From 3fde2ea4289112443f39bd6c22067c0286a47005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Fri, 20 Dec 2019 15:59:42 +0000 Subject: [PATCH] Revise and update docs --- geo-types/src/lib.rs | 8 ++ geo-types/src/line_string.rs | 25 +++- geo-types/src/point.rs | 15 +-- .../algorithm/chamberlain_duquette_area.rs | 2 +- geo/src/algorithm/closest_point.rs | 12 +- geo/src/algorithm/from_postgis.rs | 1 + geo/src/algorithm/map_coords.rs | 108 +++++++++--------- geo/src/algorithm/mod.rs | 16 +-- geo/src/algorithm/rotate.rs | 2 +- geo/src/algorithm/translate.rs | 4 +- geo/src/lib.rs | 39 +++++++ 11 files changed, 150 insertions(+), 82 deletions(-) diff --git a/geo-types/src/lib.rs b/geo-types/src/lib.rs index ac90c64f2d..c105b3ef12 100644 --- a/geo-types/src/lib.rs +++ b/geo-types/src/lib.rs @@ -1,3 +1,11 @@ +#![doc(html_logo_url = "https://raw.githubusercontent.com/georust/meta/master/logo/logo.png")] +//! The `geo-types` library provides geospatial primitive types and traits to the [`GeoRust`](https://github.com/georust) +//! crate ecosystem. +//! +//! In most cases, you will only need to use this crate if you're a crate author and want compatibility +//! with other `GeoRust` crates. Otherwise, the [`geo`](https://crates.io/crates/geo) crate re-exports these types and +//! provides geospatial algorithms, while the [`geojson`](https://crates.io/crates/geojson) crate allows serialising +//! and de-serialising `geo-types` primitives to GeoJSON. extern crate num_traits; #[cfg(feature = "serde")] diff --git a/geo-types/src/line_string.rs b/geo-types/src/line_string.rs index bd76369e8c..26df827830 100644 --- a/geo-types/src/line_string.rs +++ b/geo-types/src/line_string.rs @@ -66,12 +66,28 @@ use std::ops::{Index, IndexMut}; /// } /// ``` /// +/// You can also iterate over the coordinates in the `LineString` as `Point`s: +/// +/// ``` +/// use geo_types::{LineString, Coordinate}; +/// +/// let line_string = LineString(vec![ +/// Coordinate { x: 0., y: 0. }, +/// Coordinate { x: 10., y: 0. }, +/// ]); +/// +/// for point in line_string.points_iter() { +/// println!("Point x = {}, y = {}", point.x(), point.y()); +/// } +/// ``` + #[derive(PartialEq, Clone, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct LineString(pub Vec>) where T: CoordinateType; +/// A `Point` iterator returned by the `points_iter` method pub struct PointsIter<'a, T: CoordinateType + 'a>(::std::slice::Iter<'a, Coordinate>); impl<'a, T: CoordinateType> Iterator for PointsIter<'a, T> { @@ -89,15 +105,17 @@ impl<'a, T: CoordinateType> DoubleEndedIterator for PointsIter<'a, T> { } impl LineString { + /// Return an iterator yielding the coordinates of a `LineString` as `Point`s pub fn points_iter(&self) -> PointsIter { PointsIter(self.0.iter()) } + /// Return the coordinates of a `LineString` as a `Vec` of `Point`s pub fn into_points(self) -> Vec> { self.0.into_iter().map(Point).collect() } - /// Return an `Line` iterator that yields one `Line` for each line segment + /// Return an iterator yielding one `Line` for each line segment /// in the `LineString`. /// /// # Examples @@ -126,6 +144,7 @@ impl LineString { }) } + /// An iterator which yields the coordinates of a `LineString` as `Triangle`s pub fn triangles<'a>(&'a self) -> impl ExactSizeIterator + Iterator> + 'a { self.0.windows(3).map(|w| { // slice::windows(N) is guaranteed to yield a slice with exactly N elements @@ -166,14 +185,14 @@ impl LineString { } } -/// Turn a `Vec` of `Point`-ish objects into a `LineString`. +/// Turn a `Vec` of `Point`-like objects into a `LineString`. impl>> From> for LineString { fn from(v: Vec) -> Self { LineString(v.into_iter().map(|c| c.into()).collect()) } } -/// Turn a `Point`-ish iterator into a `LineString`. +/// Turn an iterator of `Point`-like objects into a `LineString`. impl>> FromIterator for LineString { fn from_iter>(iter: I) -> Self { LineString(iter.into_iter().map(|c| c.into()).collect()) diff --git a/geo-types/src/point.rs b/geo-types/src/point.rs index 46c9adede9..cfa02201b4 100644 --- a/geo-types/src/point.rs +++ b/geo-types/src/point.rs @@ -6,7 +6,8 @@ use std::ops::Sub; /// A single point in 2D space. /// -/// Points can be created using the `new(x, y)` constructor, or from a `Coordinate` or pair of points. +/// Points can be created using the `new(x, y)` constructor, the `point!` macro, a `Coordinate`, or from +/// two-element tuples or arrays – see the `From` impl section for a complete list. /// /// # Examples /// @@ -34,6 +35,12 @@ impl From<(T, T)> for Point { } } +impl From<[T; 2]> for Point { + fn from(coords: [T; 2]) -> Point { + Point::new(coords[0], coords[1]) + } +} + impl Point where T: CoordinateType, @@ -379,9 +386,3 @@ where } } } - -impl From<[T; 2]> for Point { - fn from(coords: [T; 2]) -> Point { - Point::new(coords[0], coords[1]) - } -} diff --git a/geo/src/algorithm/chamberlain_duquette_area.rs b/geo/src/algorithm/chamberlain_duquette_area.rs index 9443f765c1..4870adf63f 100644 --- a/geo/src/algorithm/chamberlain_duquette_area.rs +++ b/geo/src/algorithm/chamberlain_duquette_area.rs @@ -1,7 +1,7 @@ use crate::{CoordinateType, LineString, Polygon, EQUATORIAL_EARTH_RADIUS}; use num_traits::Float; -/// Signed approximate geodesic area of a geometry. +/// Calculate the signed approximate geodesic area of a `Geometry`. /// /// # Units /// diff --git a/geo/src/algorithm/closest_point.rs b/geo/src/algorithm/closest_point.rs index bce4740410..20a3f64cae 100644 --- a/geo/src/algorithm/closest_point.rs +++ b/geo/src/algorithm/closest_point.rs @@ -3,15 +3,15 @@ use crate::{Closest, Line, LineString, MultiLineString, MultiPoint, MultiPolygon use num_traits::Float; use std::iter; -/// Find the closest point between two objects, where the other object is -/// assumed to be a `Point` by default. +/// Find the closest `Point` between a given geometry and an input `Point`. +/// The closest point may intersect the geometry, be a single +/// point, or be indeterminate, as indicated by the value of the returned enum. /// /// # Examples /// -/// Here's a simple example where we've got a horizontal line which goes -/// through `(-50, 0) -> (50, 0)` and want to find the closest point to -/// `(0, 100)`. If you draw it out on paper the point on the line which is -/// closest to `(0, 100)` will be the origin. +/// We have a horizontal line which goes through `(-50, 0) -> (50, 0)`, +/// and want to find the closest point to the point `(0, 100)`. +/// Drawn on paper, the point on the line which is closest to `(0, 100)` is the origin (0, 0). /// /// ```rust /// # use geo::algorithm::closest_point::ClosestPoint; diff --git a/geo/src/algorithm/from_postgis.rs b/geo/src/algorithm/from_postgis.rs index a4c6762f00..c81add1f14 100644 --- a/geo/src/algorithm/from_postgis.rs +++ b/geo/src/algorithm/from_postgis.rs @@ -5,6 +5,7 @@ use crate::{ use postgis; use postgis::ewkb::{GeometryCollectionT, GeometryT}; +#[cfg_attr(docsrs, doc(cfg(feature = "postgis")))] /// Creates geometry from a PostGIS type. /// /// Note that PostGIS databases can store data under any spatial diff --git a/geo/src/algorithm/map_coords.rs b/geo/src/algorithm/map_coords.rs index f854faf95a..ec885466f0 100644 --- a/geo/src/algorithm/map_coords.rs +++ b/geo/src/algorithm/map_coords.rs @@ -17,7 +17,7 @@ pub trait MapCoords { /// use geo::algorithm::map_coords::MapCoords; /// /// let p1 = Point::new(10., 20.); - /// let p2 = p1.map_coords(&|&(x, y)| (x+1000., y*2.)); + /// let p2 = p1.map_coords(|&(x, y)| (x+1000., y*2.)); /// /// assert_eq!(p2, Point::new(1010., 40.)); /// ``` @@ -29,11 +29,11 @@ pub trait MapCoords { /// # use geo::algorithm::map_coords::MapCoords; /// /// let p1: Point = Point::new(10.0f32, 20.0f32); - /// let p2: Point = p1.map_coords(&|&(x, y)| (x as f64, y as f64)); + /// let p2: Point = p1.map_coords(|&(x, y)| (x as f64, y as f64)); /// /// assert_eq!(p2, Point::new(10.0f64, 20.0f64)); /// ``` - fn map_coords(&self, func: &dyn Fn(&(T, T)) -> (NT, NT)) -> Self::Output + fn map_coords(&self, func: impl Fn(&(T, T)) -> (NT, NT) + Copy) -> Self::Output where T: CoordinateType, NT: CoordinateType; @@ -52,13 +52,13 @@ pub trait TryMapCoords { /// use geo::algorithm::map_coords::TryMapCoords; /// /// let p1 = Point::new(10., 20.); - /// let p2 = p1.try_map_coords(&|&(x, y)| Ok((x+1000., y*2.))).unwrap(); + /// let p2 = p1.try_map_coords(|&(x, y)| Ok((x+1000., y*2.))).unwrap(); /// /// assert_eq!(p2, Point::new(1010., 40.)); /// ``` fn try_map_coords( &self, - func: &dyn Fn(&(T, T)) -> Result<(NT, NT), Box>, + func: impl Fn(&(T, T)) -> Result<(NT, NT), Box> + Copy, ) -> Result> where T: CoordinateType, @@ -76,11 +76,11 @@ pub trait MapCoordsInplace { /// use geo::algorithm::map_coords::MapCoordsInplace; /// /// let mut p = Point::new(10., 20.); - /// p.map_coords_inplace(&|&(x, y)| (x+1000., y*2.)); + /// p.map_coords_inplace(|&(x, y)| (x+1000., y*2.)); /// /// assert_eq!(p, Point::new(1010., 40.)); /// ``` - fn map_coords_inplace(&mut self, func: &dyn Fn(&(T, T)) -> (T, T)) + fn map_coords_inplace(&mut self, func: impl Fn(&(T, T)) -> (T, T) + Copy) where T: CoordinateType; } @@ -88,7 +88,7 @@ pub trait MapCoordsInplace { impl MapCoords for Point { type Output = Point; - fn map_coords(&self, func: &dyn Fn(&(T, T)) -> (NT, NT)) -> Self::Output { + fn map_coords(&self, func: impl Fn(&(T, T)) -> (NT, NT) + Copy) -> Self::Output { let new_point = func(&(self.0.x, self.0.y)); Point::new(new_point.0, new_point.1) } @@ -99,7 +99,7 @@ impl TryMapCoords for Point { fn try_map_coords( &self, - func: &dyn Fn(&(T, T)) -> Result<(NT, NT), Box>, + func: impl Fn(&(T, T)) -> Result<(NT, NT), Box>, ) -> Result> { let new_point = func(&(self.0.x, self.0.y))?; Ok(Point::new(new_point.0, new_point.1)) @@ -107,7 +107,7 @@ impl TryMapCoords for Point { } impl MapCoordsInplace for Point { - fn map_coords_inplace(&mut self, func: &dyn Fn(&(T, T)) -> (T, T)) { + fn map_coords_inplace(&mut self, func: impl Fn(&(T, T)) -> (T, T)) { let new_point = func(&(self.0.x, self.0.y)); self.0.x = new_point.0; self.0.y = new_point.1; @@ -117,7 +117,7 @@ impl MapCoordsInplace for Point { impl MapCoords for Line { type Output = Line; - fn map_coords(&self, func: &dyn Fn(&(T, T)) -> (NT, NT)) -> Self::Output { + fn map_coords(&self, func: impl Fn(&(T, T)) -> (NT, NT) + Copy) -> Self::Output { Line::new( self.start_point().map_coords(func).0, self.end_point().map_coords(func).0, @@ -130,7 +130,7 @@ impl TryMapCoords for Line { fn try_map_coords( &self, - func: &dyn Fn(&(T, T)) -> Result<(NT, NT), Box>, + func: impl Fn(&(T, T)) -> Result<(NT, NT), Box> + Copy, ) -> Result> { Ok(Line::new( self.start_point().try_map_coords(func)?.0, @@ -140,7 +140,7 @@ impl TryMapCoords for Line { } impl MapCoordsInplace for Line { - fn map_coords_inplace(&mut self, func: &dyn Fn(&(T, T)) -> (T, T)) { + fn map_coords_inplace(&mut self, func: impl Fn(&(T, T)) -> (T, T)) { let new_start = func(&(self.start.x, self.start.y)); self.start.x = new_start.0; self.start.y = new_start.1; @@ -154,7 +154,7 @@ impl MapCoordsInplace for Line { impl MapCoords for LineString { type Output = LineString; - fn map_coords(&self, func: &dyn Fn(&(T, T)) -> (NT, NT)) -> Self::Output { + fn map_coords(&self, func: impl Fn(&(T, T)) -> (NT, NT) + Copy) -> Self::Output { LineString::from( self.points_iter() .map(|p| p.map_coords(func)) @@ -168,7 +168,7 @@ impl TryMapCoords for LineString Result<(NT, NT), Box>, + func: impl Fn(&(T, T)) -> Result<(NT, NT), Box> + Copy, ) -> Result> { Ok(LineString::from( self.points_iter() @@ -179,7 +179,7 @@ impl TryMapCoords for LineString MapCoordsInplace for LineString { - fn map_coords_inplace(&mut self, func: &dyn Fn(&(T, T)) -> (T, T)) { + fn map_coords_inplace(&mut self, func: impl Fn(&(T, T)) -> (T, T)) { for p in &mut self.0 { let new_coords = func(&(p.x, p.y)); p.x = new_coords.0; @@ -191,7 +191,7 @@ impl MapCoordsInplace for LineString { impl MapCoords for Polygon { type Output = Polygon; - fn map_coords(&self, func: &dyn Fn(&(T, T)) -> (NT, NT)) -> Self::Output { + fn map_coords(&self, func: impl Fn(&(T, T)) -> (NT, NT) + Copy) -> Self::Output { Polygon::new( self.exterior().map_coords(func), self.interiors() @@ -207,7 +207,7 @@ impl TryMapCoords for Polygon { fn try_map_coords( &self, - func: &dyn Fn(&(T, T)) -> Result<(NT, NT), Box>, + func: impl Fn(&(T, T)) -> Result<(NT, NT), Box> + Copy, ) -> Result> { Ok(Polygon::new( self.exterior().try_map_coords(func)?, @@ -220,7 +220,7 @@ impl TryMapCoords for Polygon { } impl MapCoordsInplace for Polygon { - fn map_coords_inplace(&mut self, func: &dyn Fn(&(T, T)) -> (T, T)) { + fn map_coords_inplace(&mut self, func: impl Fn(&(T, T)) -> (T, T) + Copy) { self.exterior_mut(|line_string| { line_string.map_coords_inplace(func); }); @@ -236,7 +236,7 @@ impl MapCoordsInplace for Polygon { impl MapCoords for MultiPoint { type Output = MultiPoint; - fn map_coords(&self, func: &dyn Fn(&(T, T)) -> (NT, NT)) -> Self::Output { + fn map_coords(&self, func: impl Fn(&(T, T)) -> (NT, NT) + Copy) -> Self::Output { MultiPoint(self.0.iter().map(|p| p.map_coords(func)).collect()) } } @@ -246,7 +246,7 @@ impl TryMapCoords for MultiPoint Result<(NT, NT), Box>, + func: impl Fn(&(T, T)) -> Result<(NT, NT), Box> + Copy, ) -> Result> { Ok(MultiPoint( self.0 @@ -258,7 +258,7 @@ impl TryMapCoords for MultiPoint MapCoordsInplace for MultiPoint { - fn map_coords_inplace(&mut self, func: &dyn Fn(&(T, T)) -> (T, T)) { + fn map_coords_inplace(&mut self, func: impl Fn(&(T, T)) -> (T, T) + Copy) { for p in &mut self.0 { p.map_coords_inplace(func); } @@ -268,7 +268,7 @@ impl MapCoordsInplace for MultiPoint { impl MapCoords for MultiLineString { type Output = MultiLineString; - fn map_coords(&self, func: &dyn Fn(&(T, T)) -> (NT, NT)) -> Self::Output { + fn map_coords(&self, func: impl Fn(&(T, T)) -> (NT, NT) + Copy) -> Self::Output { MultiLineString(self.0.iter().map(|l| l.map_coords(func)).collect()) } } @@ -278,7 +278,7 @@ impl TryMapCoords for MultiLineStr fn try_map_coords( &self, - func: &dyn Fn(&(T, T)) -> Result<(NT, NT), Box>, + func: impl Fn(&(T, T)) -> Result<(NT, NT), Box> + Copy, ) -> Result> { Ok(MultiLineString( self.0 @@ -290,7 +290,7 @@ impl TryMapCoords for MultiLineStr } impl MapCoordsInplace for MultiLineString { - fn map_coords_inplace(&mut self, func: &dyn Fn(&(T, T)) -> (T, T)) { + fn map_coords_inplace(&mut self, func: impl Fn(&(T, T)) -> (T, T) + Copy) { for p in &mut self.0 { p.map_coords_inplace(func); } @@ -300,7 +300,7 @@ impl MapCoordsInplace for MultiLineString { impl MapCoords for MultiPolygon { type Output = MultiPolygon; - fn map_coords(&self, func: &dyn Fn(&(T, T)) -> (NT, NT)) -> Self::Output { + fn map_coords(&self, func: impl Fn(&(T, T)) -> (NT, NT) + Copy) -> Self::Output { MultiPolygon(self.0.iter().map(|p| p.map_coords(func)).collect()) } } @@ -310,7 +310,7 @@ impl TryMapCoords for MultiPolygon fn try_map_coords( &self, - func: &dyn Fn(&(T, T)) -> Result<(NT, NT), Box>, + func: impl Fn(&(T, T)) -> Result<(NT, NT), Box> + Copy, ) -> Result> { Ok(MultiPolygon( self.0 @@ -322,7 +322,7 @@ impl TryMapCoords for MultiPolygon } impl MapCoordsInplace for MultiPolygon { - fn map_coords_inplace(&mut self, func: &dyn Fn(&(T, T)) -> (T, T)) { + fn map_coords_inplace(&mut self, func: impl Fn(&(T, T)) -> (T, T) + Copy) { for p in &mut self.0 { p.map_coords_inplace(func); } @@ -332,7 +332,7 @@ impl MapCoordsInplace for MultiPolygon { impl MapCoords for Geometry { type Output = Geometry; - fn map_coords(&self, func: &dyn Fn(&(T, T)) -> (NT, NT)) -> Self::Output { + fn map_coords(&self, func: impl Fn(&(T, T)) -> (NT, NT) + Copy) -> Self::Output { match *self { Geometry::Point(ref x) => Geometry::Point(x.map_coords(func)), Geometry::Line(ref x) => Geometry::Line(x.map_coords(func)), @@ -351,7 +351,7 @@ impl TryMapCoords for Geometry fn try_map_coords( &self, - func: &dyn Fn(&(T, T)) -> Result<(NT, NT), Box>, + func: impl Fn(&(T, T)) -> Result<(NT, NT), Box> + Copy, ) -> Result> { match *self { Geometry::Point(ref x) => Ok(Geometry::Point(x.try_map_coords(func)?)), @@ -371,7 +371,7 @@ impl TryMapCoords for Geometry } impl MapCoordsInplace for Geometry { - fn map_coords_inplace(&mut self, func: &dyn Fn(&(T, T)) -> (T, T)) { + fn map_coords_inplace(&mut self, func: impl Fn(&(T, T)) -> (T, T) + Copy) { match *self { Geometry::Point(ref mut x) => x.map_coords_inplace(func), Geometry::Line(ref mut x) => x.map_coords_inplace(func), @@ -388,7 +388,7 @@ impl MapCoordsInplace for Geometry { impl MapCoords for GeometryCollection { type Output = GeometryCollection; - fn map_coords(&self, func: &dyn Fn(&(T, T)) -> (NT, NT)) -> Self::Output { + fn map_coords(&self, func: impl Fn(&(T, T)) -> (NT, NT) + Copy) -> Self::Output { GeometryCollection(self.0.iter().map(|g| g.map_coords(func)).collect()) } } @@ -398,7 +398,7 @@ impl TryMapCoords for GeometryColl fn try_map_coords( &self, - func: &dyn Fn(&(T, T)) -> Result<(NT, NT), Box>, + func: impl Fn(&(T, T)) -> Result<(NT, NT), Box> + Copy, ) -> Result> { Ok(GeometryCollection( self.0 @@ -410,7 +410,7 @@ impl TryMapCoords for GeometryColl } impl MapCoordsInplace for GeometryCollection { - fn map_coords_inplace(&mut self, func: &dyn Fn(&(T, T)) -> (T, T)) { + fn map_coords_inplace(&mut self, func: impl Fn(&(T, T)) -> (T, T) + Copy) { for p in &mut self.0 { p.map_coords_inplace(func); } @@ -420,7 +420,7 @@ impl MapCoordsInplace for GeometryCollection { impl MapCoords for Rect { type Output = Rect; - fn map_coords(&self, func: &dyn Fn(&(T, T)) -> (NT, NT)) -> Self::Output { + fn map_coords(&self, func: impl Fn(&(T, T)) -> (NT, NT) + Copy) -> Self::Output { let new_min = func(&self.min().x_y()); let new_max = func(&self.max().x_y()); @@ -442,7 +442,7 @@ impl TryMapCoords for Rect { fn try_map_coords( &self, - func: &dyn Fn(&(T, T)) -> Result<(NT, NT), Box>, + func: impl Fn(&(T, T)) -> Result<(NT, NT), Box>, ) -> Result> { let new_min = func(&(self.min().x, self.min().y))?; let new_max = func(&(self.max().x, self.max().y))?; @@ -461,7 +461,7 @@ impl TryMapCoords for Rect { } impl MapCoordsInplace for Rect { - fn map_coords_inplace(&mut self, func: &dyn Fn(&(T, T)) -> (T, T)) { + fn map_coords_inplace(&mut self, func: impl Fn(&(T, T)) -> (T, T)) { let new_min = func(&self.min().x_y()); let new_max = func(&self.max().x_y()); @@ -479,7 +479,7 @@ mod test { #[test] fn point() { let p = Point::new(10., 10.); - let new_p = p.map_coords(&|&(x, y)| (x + 10., y + 100.)); + let new_p = p.map_coords(|&(x, y)| (x + 10., y + 100.)); assert_relative_eq!(new_p.x(), 20.); assert_relative_eq!(new_p.y(), 110.); } @@ -487,7 +487,7 @@ mod test { #[test] fn point_inplace() { let mut p2 = Point::new(10f32, 10f32); - p2.map_coords_inplace(&|&(x, y)| (x + 10., y + 100.)); + p2.map_coords_inplace(|&(x, y)| (x + 10., y + 100.)); assert_relative_eq!(p2.x(), 20.); assert_relative_eq!(p2.y(), 110.); } @@ -495,7 +495,7 @@ mod test { #[test] fn rect_inplace() { let mut rect = Rect::new((10, 10), (20, 20)); - rect.map_coords_inplace(&|&(x, y)| (x + 10, y + 20)); + rect.map_coords_inplace(|&(x, y)| (x + 10, y + 20)); assert_eq!(rect.min(), Coordinate { x: 20, y: 30 }); assert_eq!(rect.max(), Coordinate { x: 30, y: 40 }); } @@ -504,7 +504,7 @@ mod test { #[should_panic] fn rect_inplace_panic() { let mut rect = Rect::new((10, 10), (20, 20)); - rect.map_coords_inplace(&|&(x, y)| { + rect.map_coords_inplace(|&(x, y)| { if x < 15 && y < 15 { (x, y) } else { @@ -516,7 +516,7 @@ mod test { #[test] fn rect_map_coords() { let rect = Rect::new((10, 10), (20, 20)); - let another_rect = rect.map_coords(&|&(x, y)| (x + 10, y + 20)); + let another_rect = rect.map_coords(|&(x, y)| (x + 10, y + 20)); assert_eq!(another_rect.min(), Coordinate { x: 20, y: 30 }); assert_eq!(another_rect.max(), Coordinate { x: 30, y: 40 }); } @@ -524,7 +524,7 @@ mod test { #[test] fn rect_try_map_coords() { let rect = Rect::new((10, 10), (20, 20)); - let result = rect.try_map_coords(&|&(x, y)| Ok((x + 10, y + 20))); + let result = rect.try_map_coords(|&(x, y)| Ok((x + 10, y + 20))); assert!(result.is_ok()); } @@ -532,7 +532,7 @@ mod test { #[should_panic] fn rect_try_map_coords_panic() { let rect = Rect::new((10, 10), (20, 20)); - let _ = rect.try_map_coords(&|&(x, y)| { + let _ = rect.try_map_coords(|&(x, y)| { if x < 15 && y < 15 { Ok((x, y)) } else { @@ -545,7 +545,7 @@ mod test { fn line() { let line = Line::from([(0., 0.), (1., 2.)]); assert_eq!( - line.map_coords(&|&(x, y)| (x * 2., y)), + line.map_coords(|&(x, y)| (x * 2., y)), Line::from([(0., 0.), (2., 2.)]) ); } @@ -553,7 +553,7 @@ mod test { #[test] fn linestring() { let line1: LineString = LineString::from(vec![(0., 0.), (1., 2.)]); - let line2 = line1.map_coords(&|&(x, y)| (x + 10., y - 100.)); + let line2 = line1.map_coords(|&(x, y)| (x + 10., y - 100.)); assert_eq!(line2.0[0], Coordinate::from((10., -100.))); assert_eq!(line2.0[1], Coordinate::from((11., -98.))); } @@ -569,7 +569,7 @@ mod test { ])]; let p = Polygon::new(exterior, interiors); - let p2 = p.map_coords(&|&(x, y)| (x + 10., y - 100.)); + let p2 = p.map_coords(|&(x, y)| (x + 10., y - 100.)); let exterior2 = LineString::from(vec![(10., -100.), (11., -99.), (11., -100.), (10., -100.)]); @@ -591,7 +591,7 @@ mod test { let mp = MultiPoint(vec![p1, p2]); assert_eq!( - mp.map_coords(&|&(x, y)| (x + 10., y + 100.)), + mp.map_coords(|&(x, y)| (x + 10., y + 100.)), MultiPoint(vec![Point::new(20., 110.), Point::new(10., 0.)]) ); } @@ -601,7 +601,7 @@ mod test { let line1: LineString = LineString::from(vec![(0., 0.), (1., 2.)]); let line2: LineString = LineString::from(vec![(-1., 0.), (0., 0.), (1., 2.)]); let mline = MultiLineString(vec![line1, line2]); - let mline2 = mline.map_coords(&|&(x, y)| (x + 10., y - 100.)); + let mline2 = mline.map_coords(|&(x, y)| (x + 10., y - 100.)); assert_eq!( mline2, MultiLineString(vec![ @@ -640,7 +640,7 @@ mod test { ]; let mp = MultiPolygon(vec![poly1, poly2]); - let mp2 = mp.map_coords(&|&(x, y)| (x * 2., y + 100.)); + let mp2 = mp.map_coords(|&(x, y)| (x * 2., y + 100.)); assert_eq!(mp2.0.len(), 2); assert_eq!( mp2.0[0], @@ -683,7 +683,7 @@ mod test { let gc = GeometryCollection(vec![p1, line1]); assert_eq!( - gc.map_coords(&|&(x, y)| (x + 10., y + 100.)), + gc.map_coords(|&(x, y)| (x + 10., y + 100.)), GeometryCollection(vec![ Geometry::Point(Point::new(20., 110.)), Geometry::LineString(LineString::from(vec![(10., 100.), (11., 102.)])), @@ -694,7 +694,7 @@ mod test { #[test] fn convert_type() { let p1: Point = Point::new(1., 2.); - let p2: Point = p1.map_coords(&|&(x, y)| (x as f32, y as f32)); + let p2: Point = p1.map_coords(|&(x, y)| (x as f32, y as f32)); assert_relative_eq!(p2.x(), 1f32); assert_relative_eq!(p2.y(), 2f32); } @@ -723,9 +723,9 @@ mod test { Point::new(3.0, 3.0), ] .into(); - let bad = bad_ls.try_map_coords(&|&(x, y)| f(x, y)); + let bad = bad_ls.try_map_coords(|&(x, y)| f(x, y)); assert!(bad.is_err()); - let good = good_ls.try_map_coords(&|&(x, y)| f(x, y)); + let good = good_ls.try_map_coords(|&(x, y)| f(x, y)); assert!(good.is_ok()); assert_eq!( good.unwrap(), diff --git a/geo/src/algorithm/mod.rs b/geo/src/algorithm/mod.rs index 1d600d0b89..a21402a21a 100644 --- a/geo/src/algorithm/mod.rs +++ b/geo/src/algorithm/mod.rs @@ -6,15 +6,15 @@ pub mod bearing; pub mod bounding_rect; /// Calculate the centroid of a `Geometry`. pub mod centroid; -/// Signed approximate geodesic area of a geometry. +/// Calculate the signed approximate geodesic area of a `Geometry`. pub mod chamberlain_duquette_area; -/// Calculate the minimum distance between two `Geometries`. +/// Calculate the closest `Point` between a `Geometry` and an input `Point`. pub mod closest_point; /// Determine whether `Geometry` `A` is completely enclosed by `Geometry` `B`. pub mod contains; /// Calculate the convex hull of a `Geometry`. pub mod convexhull; -/// Calculate the Euclidean distance between two `Geometries`. +/// Calculate the minimum Euclidean distance between two `Geometries`. pub mod euclidean_distance; /// Calculate the length of a planar line between two `Geometries`. pub mod euclidean_length; @@ -25,7 +25,10 @@ pub mod frechet_distance; /// Produces a `Geometry` from PostGIS. #[cfg(feature = "postgis-integration")] pub mod from_postgis; -/// Calculate a new Point given a distance and a bearing. +/// Convert a `Geometry` into a PostGIS. +#[cfg(feature = "postgis-integration")] +pub mod to_postgis; +/// Calculate a destination `Point`, given a distance and a bearing. pub mod haversine_destination; /// Calculate the Haversine distance between two `Geometries`. pub mod haversine_distance; @@ -41,7 +44,7 @@ pub mod map_coords; pub mod orient; /// Helper functions for the "fast path" variant of the Polygon-Polygon Euclidean distance method. pub(crate) mod polygon_distance_fast_path; -/// Coordinate projections and transformations using [PROJ](http://proj4.org) v5.0.x. +/// Coordinate projections and transformations using the current stable version of [PROJ](http://proj.org). #[cfg(feature = "use-proj")] pub mod proj; /// Rotate a `Geometry` around either its centroid or a `Point` by an angle given in degrees. @@ -50,9 +53,6 @@ pub mod rotate; pub mod simplify; /// Simplify `Geometries` using the Visvalingam-Whyatt algorithm. Includes a topology-preserving variant. pub mod simplifyvw; -/// Convert `Geometries` into PostGIS types. -#[cfg(feature = "postgis-integration")] -pub mod to_postgis; /// Translate a `Geometry` along the given offsets. pub mod translate; /// Calculate the Vincenty distance between two `Point`s. diff --git a/geo/src/algorithm/rotate.rs b/geo/src/algorithm/rotate.rs index f5662401df..c01178cf2d 100644 --- a/geo/src/algorithm/rotate.rs +++ b/geo/src/algorithm/rotate.rs @@ -124,7 +124,7 @@ where fn rotate_around_point(&self, angle: T, point: Point) -> Self { let (sin_theta, cos_theta) = angle.to_radians().sin_cos(); let (x0, y0) = point.x_y(); - self.map_coords(&|&(x, y)| rotate_inner(x, y, x0, y0, sin_theta, cos_theta).x_y()) + self.map_coords(|&(x, y)| rotate_inner(x, y, x0, y0, sin_theta, cos_theta).x_y()) } } diff --git a/geo/src/algorithm/translate.rs b/geo/src/algorithm/translate.rs index 4f6a88a48a..2f2b924a71 100644 --- a/geo/src/algorithm/translate.rs +++ b/geo/src/algorithm/translate.rs @@ -40,11 +40,11 @@ where G: MapCoords + MapCoordsInplace, { fn translate(&self, xoff: T, yoff: T) -> Self { - self.map_coords(&|&(x, y)| (x + xoff, y + yoff)) + self.map_coords(|&(x, y)| (x + xoff, y + yoff)) } fn translate_inplace(&mut self, xoff: T, yoff: T) { - self.map_coords_inplace(&|&(x, y)| (x + xoff, y + yoff)) + self.map_coords_inplace(|&(x, y)| (x + xoff, y + yoff)) } } diff --git a/geo/src/lib.rs b/geo/src/lib.rs index d83e305d06..da553d7339 100644 --- a/geo/src/lib.rs +++ b/geo/src/lib.rs @@ -1,3 +1,42 @@ +#![doc(html_logo_url = "https://raw.githubusercontent.com/georust/meta/master/logo/logo.png")] +//! The `geo` crate provides geospatial primitive types such as `Coordinate`, `Point`, `LineString`, and `Polygon` as +//! well as their `Multi–` equivalents, and provides algorithms and operations such as: +//! - Area and centroid calculation +//! - Simplification and convex hull operations +//! - Distance measurement +//! - Intersection checks +//! - Transformation to and from PostGIS types +//! - Affine transforms such as rotation and translation. +//! +//! The primitive types also provide the basis for other functionality in the `Geo` ecosystem, including: +//! - Serialization to and from [GeoJSON](https://docs.rs/geojson) and [WKT](https://docs.rs/wkt) +//! - [Coordinate transformation and projection](https://docs.rs/proj) +//! - [Geocoding](https://docs.rs/geocoding) +//! - [Working with GPS data](https://docs.rs/gpx) +//! +//! …allowing these crates to interoperate; GeoJSON can readily be read from a file, deserialised, transformed +//! to a local datum, modified, transformed back to `WGS84`, and serialised back to GeoJSON. +//! +//! Operations available for primitive types can be found in the `algorithm` module, along with +//! comprehensive usage examples. +//! +//! While `Geo` is primarily intended to operate on **planar** geometries, some other useful algorithms are +//! provided: Haversine, Frechet, and Vincenty distances, as well as Chamberlain-Duquette area. +//! +//! ## Optional Features (these can be activated in your `cargo.toml`) +//! The following optional features are available: +//! - `from-postgis`: convert `Geometry` types to and from [`PostGIS`](https://docs.rs/postgis) types. +//! - `use-proj`: enable coordinate conversion and transformation of `Point` geometries using the [`proj`](https://docs.rs/proj) crate +//! - `use-serde`: enable serialisation of geometries using `serde`. +//! +//! ## GeoJSON +//! If you wish to read or write `GeoJSON`, use the [`geojson`](https://docs.rs/geojson) crate, with the `geo-types` feature activated. +//! This provides fallible conversions **to** `geo-types` primitives such as `Point` and `Polygon` from `geojson` `Value` +//! structs using the standard [`TryFrom`](https://doc.rust-lang.org/stable/std/convert/trait.TryFrom.html) +//! and [`TryInto`](https://doc.rust-lang.org/stable/std/convert/trait.TryInto.html) traits, +//! and conversion **from** `geo-types` primitives to `geojson` +//! `Value` structs using the [`From`](https://doc.rust-lang.org/stable/std/convert/trait.TryFrom.html) trait. + extern crate geo_types; extern crate num_traits; #[cfg(feature = "use-serde")]