geo-types-0.7.11/.cargo_vcs_info.json0000644000000001470000000000100130400ustar { "git": { "sha1": "3f9e12f19493527afaf5330bfc3209364dad8d7a" }, "path_in_vcs": "geo-types" }geo-types-0.7.11/CHANGES.md000064400000000000000000000253371046102023000132320ustar 00000000000000# Changes ## 0.7.11 * Bump rstar dependency ## 0.7.10 * Implement `From<&Line>` for `LineString` ## 0.7.9 * Return `DoubleEndedIterator` from `LineString::points` and `LineString::points_mut` * * POSSIBLY BREAKING: Minimum supported version of Rust (MSRV) is now 1.63 * Add `no_std` compatibility when the new default `std` feature is disabled * * Support `rstar` version `0.10` in feature `use-rstar_0_10`. ## 0.7.8 * Rename `Coordinate` to `Coord`; add deprecated `Coordinate` that is an alias for `Coord` * Pin `arbitrary` version to 1.1.3 until our MSRV catches up with its latest release * Add `point.x_mut()` and `point.y_mut()` methods on `Points` * Changed license field to [SPDX 2.1 license expression](https://spdx.dev/spdx-specification-21-web-version/#h.jxpfx0ykyb60) * * Fix typo in deprecated attribute, which will become a compiler error in a future version of rustc. * ## 0.7.7 * Fixed: using empty `polygon![]` macro no longer requires including `line_string!` macro ## 0.7.6 * You may now specify `Geometry` rather than `Geometry` since we've added a default trait implementation. You may still explicitly declare the numeric type as f64, or any other implementation of `CoordNum`, but this should save you some typing if you're using f64. The same change applies to `Coordinates` and all the geometry variants, like `Point`, `LineString`, etc. * * the `geometry` module now re-exports all the inner geometry variants, so you can `use geo_types::geometry::*` to concisely include `Point`, `LineString`, etc. * ## 0.7.5 * Add `split_x` and `split_y` methods on `Rect` * * Add support for `Polygon` in `RTree` * * Deprecate `GeometryCollection::from(single_geom)` in favor of `GeometryCollection::from(vec![single_geom])` * ## 0.7.4 * BREAKING: Make `Rect::to_lines` return lines in winding order for `Rect::to_polygon`. * * Note: All crates have been migrated to Rust 2021 edition. The MSRV when installing the latest dependencies has increased to 1.56. * * Macros `coord!`, `point!`, `line_string!`, and `polygon!` now support trailing commas such as `coord! { x: 181.2, y: 51.79, }` * * Internal cleanup: Explicitly declare `use-rstar_0_8` and `use-rstar_0_9` features to be explicit which rstar version is being used. For backward compatibility, the `use-rstar` feature will still enable `use-rstar_0_8`. * * Add missing size_hint() method for point and coordinate iterators on LineString * * Add ExactsizeIterator impl for Points iterator on LineString * * Extend `point!` macro to support single coordinate expression arguments `point!(coordinate)` (coordinate can be created with the `coord!` macro) * * `LineString`, `MultiPoint`, `MultiPolygon`, `Triangle`, `MultiLineString` now have a new constructor `new(...)`. `GeometryCollection` has a `new_from(...)` constructor. `GeometryCollection::new()` has been deprecated - use `GeometryCollection::default()` instead. Do not use tuple constructors like ~~`MultiPoint(...)`~~ for any of the geo-types. Use `MultiPoint::new(...)` and similar ones instead. * PRs: [MultiPolygon::new](https://github.com/georust/geo/pull/786), [MultiLineString::new](https://github.com/georust/geo/pull/784), [Triangle::new](https://github.com/georust/geo/pull/783), [GeometryCollection::new_from](https://github.com/georust/geo/pull/782), [LineString::new](https://github.com/georust/geo/pull/781), [MultiPoint::new](https://github.com/georust/geo/pull/778), [Point::from](https://github.com/georust/geo/pull/777) ## 0.7.3 * DEPRECATION: Deprecate `Point::lng`, `Point::lat`, `Point::set_lng` and `Point::set_lat` * * Support `rstar` version `0.9` in feature `use-rstar_0_9` * * `Geometry` and `GeometryCollection` now support serde. * * Add `Coordinate` iterators to LineString, regularise its iterator methods, and refactor its docs * * Add +=, -=, \*=, and /= for Point * * Note: The MSRV when installing the latest dependencies has increased to 1.55 * ## 0.7.2 * Implement `RelativeEq` and `AbsDiffEq` for fuzzy comparison of remaining Geometry Types * * Implement `From` for `LineString` * * Add optional `arbitrary` feature for integration with the [arbitrary](https://github.com/rust-fuzz/arbitrary) crate * ## 0.7.1 * Implement `Default` on `Coordinate` and `Point` structs (defaults to `(x: 0, y: 0)`) * * Add specific details about conversion failures in the newly public `geo_types::Error` * ## 0.7.0 * BREAKING: `geo_types::CoordinateType` now extends Debug and has been deprecated in favor of `geo_types::CoordNum` and `geo_types::CoordFloat` * * BREAKING: Introduce `use-rstar` feature rather than `rstar` so that `approx` dependency can be optional * * Implement `approx::{RelativeEq, AbsDiffEq}` for geo-types when using the `approx` feature * * `geo_types::LineString::num_coords` has been deprecated in favor of `geo::algorithm::coords_iter::CoordsIter::coords_count` * ## 0.6.2 * Add `into_iter`, `iter` and `iter_mut` methods for `MultiPolygon`, `MultiPoint`, and `MultiLineString` * * `Rect::new` automatically determines min/max points. Deprecates `Rect::try_new` which can no longer fail. * * Add `MultiLineString::is_closed` method * ## 0.6.1 * Add documentation on semantics (based on OGC-SFA) * * Add vector-space operations to `Coordinate` and `Point` * ## 0.6.0 * Remove `COORD_PRECISION` which was an arbitrary constant of 0.1m * * Bump rstar version to 0.8.0 * * Add `Triangle` and `Rect` to `Geometry` * * Introduce `Rect::try_new` constructor which does not panic * * Add `Rect::center` method * * Derive `Eq` for types when applicable * * * Implement `From for Polygon` * ## 0.5.0 * Update Geometry enum with iterators and TryFrom impls for primitives * https://github.com/georust/geo/pull/410 * Make geo-types Rect fields private to force users to use constructor (breaking change) * * Bump rstar dependency to 0.4 * * Fix link to `LineString` in docs * * Fix typo in Rect docs about min/max positions. * * Implement `Hash` for all types in `geo-types` * ## 0.4.3 * Introduce `point!`, `line_string!`, and `polygon!` macros. * * * Add `Rect` constructor that enforces `min.{x,y} < max.{x,y}` * ## 0.4.2 * Add `Polygon::num_coords` * ## 0.4.1 * Add `Polygon::interiors_push` - Adds an interior ring to a `Polygon` * ## 0.4.0 * Rewrite `Polygon` structure to enforce closed `LineString` rings * * Implement `Into` for `Line` * * Implement `Index` for `LineString` to get the coordinate at that position * * Bump `rstar` dependency * * Ability to construct `MultiPolygon` from `Vec` of anything that implements `Into` * * Add `new`, `is_empty`, `len` functions on `GeometryCollection` * * Tweak `Geometry` method names slightly * * Remove unnecessary references in function signatures * ## 0.3.0 * Replace the [spade](https://crates.io/crates/spade) crate with the [rstar](https://crates.io/crates/rstar) crate * * Remove unnecessary algorithm trait bounds * ## 0.2.2 * Fix misnamed `serde` feature flag. * * Add `width` and `height` helpers on `Rect`. * ## 0.2.1 * Add `to_lines` method on a `Triangle` * ## 0.2.0 * Introduce `Line::{dx, dy, slope, determinant}` methods. * * Remove unnecessary borrows in function params for `Copy` types. * * Introduce `x_y` method on `Point` and `Coordinate` * * Migrate `Line` and `LineString` to be a series of `Coordinates` (not `Points`). * * Introduce Triangle geometry type. * * Rename bounding ‘box’ to ‘rect’; move structure to geo-types. * ## 0.1.1 * Allow LineString creation from vec of two-element CoordinateType array * ## 0.1.0 * New crate with core types from `geo` * geo-types-0.7.11/Cargo.toml0000644000000035740000000000100110450ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.63" name = "geo-types" version = "0.7.11" description = "Geospatial primitive data types" documentation = "https://docs.rs/geo-types/" readme = "README.md" keywords = [ "gis", "geo", "geography", "geospatial", ] license = "MIT OR Apache-2.0" repository = "https://github.com/georust/geo" resolver = "1" [dependencies.approx] version = ">= 0.4.0, < 0.6.0" optional = true default-features = false [dependencies.arbitrary] version = "1.2.0" optional = true [dependencies.num-traits] version = "0.2" features = ["libm"] default-features = false [dependencies.rstar_0_10] version = "0.10" optional = true package = "rstar" [dependencies.rstar_0_11] version = "0.11" optional = true package = "rstar" [dependencies.rstar_0_8] version = "0.8" optional = true package = "rstar" [dependencies.rstar_0_9] version = "0.9" optional = true package = "rstar" [dependencies.serde] version = "1" features = [ "alloc", "derive", ] optional = true default-features = false [dev-dependencies.approx] version = ">= 0.4.0, < 0.6.0" [features] default = ["std"] rstar = ["rstar_0_8"] std = [ "approx?/std", "num-traits/std", "serde?/std", ] use-rstar = ["use-rstar_0_8"] use-rstar_0_10 = [ "rstar_0_10", "approx", ] use-rstar_0_11 = [ "rstar_0_11", "approx", ] use-rstar_0_8 = [ "rstar_0_8", "approx", ] use-rstar_0_9 = [ "rstar_0_9", "approx", ] geo-types-0.7.11/Cargo.toml.orig000064400000000000000000000030321046102023000145130ustar 00000000000000[package] name = "geo-types" version = "0.7.11" license = "MIT OR Apache-2.0" repository = "https://github.com/georust/geo" documentation = "https://docs.rs/geo-types/" readme = "../README.md" keywords = ["gis", "geo", "geography", "geospatial"] description = "Geospatial primitive data types" rust-version = "1.63" edition = "2021" [features] default = ["std"] std = ["approx?/std", "num-traits/std", "serde?/std"] # Prefer `use-rstar` feature rather than enabling rstar directly. # rstar integration relies on the optional approx crate, but implicit features cannot yet enable other features. # See: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#namespaced-features rstar = ["rstar_0_8"] use-rstar = ["use-rstar_0_8"] use-rstar_0_8 = ["rstar_0_8", "approx"] use-rstar_0_9 = ["rstar_0_9", "approx"] use-rstar_0_10 = ["rstar_0_10", "approx"] use-rstar_0_11 = ["rstar_0_11", "approx"] [dependencies] approx = { version = ">= 0.4.0, < 0.6.0", optional = true, default-features = false } arbitrary = { version = "1.2.0", optional = true } num-traits = { version = "0.2", default-features = false, features = ["libm"] } rstar_0_8 = { package = "rstar", version = "0.8", optional = true } rstar_0_9 = { package = "rstar", version = "0.9", optional = true } rstar_0_10 = { package = "rstar", version = "0.10", optional = true } rstar_0_11 = { package = "rstar", version = "0.11", optional = true } serde = { version = "1", optional = true, default-features = false, features = ["alloc", "derive"] } [dev-dependencies] approx = ">= 0.4.0, < 0.6.0" geo-types-0.7.11/README.md000064400000000000000000000053231046102023000131100ustar 00000000000000[![geo](https://avatars1.githubusercontent.com/u/10320338?v=4&s=50)](https://github.com/georust) [![geo on Crates.io](https://img.shields.io/crates/v/geo.svg?color=brightgreen)](https://crates.io/crates/geo) [![Coverage Status](https://img.shields.io/coverallsCoverage/github/georust/geo.svg)](https://coveralls.io/github/georust/geo?branch=trying) [![Documentation](https://img.shields.io/docsrs/geo/latest.svg)](https://docs.rs/geo) [![Discord](https://img.shields.io/discord/598002550221963289)](https://discord.gg/Fp2aape) # geo ## Geospatial Primitives, Algorithms, and Utilities ### Chat or ask questions on [Discord](https://discord.gg/Fp2aape) The `geo` crate provides geospatial primitive types such as `Point`, `LineString`, and `Polygon`, and provides algorithms and operations such as: - Area and centroid calculation - Simplification and convex hull operations - Euclidean and Haversine distance measurement - Intersection checks - Affine transforms such as rotation and translation. Please refer to [the documentation](https://docs.rs/geo) for a complete list. The primitive types also provide the basis for other functionality in the `Geo` ecosystem, including: - [Coordinate transformation and projection](https://github.com/georust/proj) - Serialization to and from [GeoJSON](https://github.com/georust/geojson) and [WKT](https://github.com/georust/wkt) - [Geocoding](https://github.com/georust/geocoding) - [Working with GPS data](https://github.com/georust/gpx) ## Example ```rust // primitives use geo::{line_string, polygon}; // algorithms use geo::ConvexHull; // An L shape let poly = polygon![ (x: 0.0, y: 0.0), (x: 4.0, y: 0.0), (x: 4.0, y: 1.0), (x: 1.0, y: 1.0), (x: 1.0, y: 4.0), (x: 0.0, y: 4.0), (x: 0.0, y: 0.0), ]; // Calculate the polygon's convex hull let hull = poly.convex_hull(); assert_eq!( hull.exterior(), &line_string![ (x: 4.0, y: 0.0), (x: 4.0, y: 1.0), (x: 1.0, y: 4.0), (x: 0.0, y: 4.0), (x: 0.0, y: 0.0), (x: 4.0, y: 0.0), ] ); ``` ## Contributing Contributions are welcome! Have a look at the [issues](https://github.com/georust/geo/issues), and open a pull request if you'd like to add an algorithm or some functionality. ## License Licensed under either of * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. geo-types-0.7.11/src/arbitrary.rs000064400000000000000000000075571046102023000150000ustar 00000000000000use crate::{ Coord, CoordFloat, Geometry, GeometryCollection, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, Rect, Triangle, }; use std::mem; impl<'a, T> arbitrary::Arbitrary<'a> for Coord where T: arbitrary::Arbitrary<'a> + CoordFloat, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { Ok(coord! { x: u.arbitrary::()?, y: u.arbitrary::()?, }) } } impl<'a, T> arbitrary::Arbitrary<'a> for Point where T: arbitrary::Arbitrary<'a> + CoordFloat, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { u.arbitrary::>().map(Self) } } impl<'a, T> arbitrary::Arbitrary<'a> for LineString where T: arbitrary::Arbitrary<'a> + CoordFloat, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { let coords = u.arbitrary::>>()?; if coords.len() < 2 { Err(arbitrary::Error::IncorrectFormat) } else { Ok(Self(coords)) } } fn size_hint(_depth: usize) -> (usize, Option) { (mem::size_of::() * 2, None) } } impl<'a, T> arbitrary::Arbitrary<'a> for Polygon where T: arbitrary::Arbitrary<'a> + CoordFloat, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { Ok(Self::new( u.arbitrary::>()?, u.arbitrary::>>()?, )) } } impl<'a, T> arbitrary::Arbitrary<'a> for MultiPoint where T: arbitrary::Arbitrary<'a> + CoordFloat, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { u.arbitrary::>>().map(Self) } } impl<'a, T> arbitrary::Arbitrary<'a> for MultiLineString where T: arbitrary::Arbitrary<'a> + CoordFloat, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { u.arbitrary::>>().map(Self) } } impl<'a, T> arbitrary::Arbitrary<'a> for MultiPolygon where T: arbitrary::Arbitrary<'a> + CoordFloat, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { u.arbitrary::>>().map(Self) } } impl<'a, T> arbitrary::Arbitrary<'a> for GeometryCollection where T: arbitrary::Arbitrary<'a> + CoordFloat, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { u.arbitrary() } } impl<'a, T> arbitrary::Arbitrary<'a> for Rect where T: arbitrary::Arbitrary<'a> + CoordFloat, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { Ok(Self::new( u.arbitrary::>()?, u.arbitrary::>()?, )) } } impl<'a, T> arbitrary::Arbitrary<'a> for Triangle where T: arbitrary::Arbitrary<'a> + CoordFloat, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { Ok(Self( u.arbitrary::>()?, u.arbitrary::>()?, u.arbitrary::>()?, )) } } impl<'a, T> arbitrary::Arbitrary<'a> for Geometry where T: arbitrary::Arbitrary<'a> + CoordFloat, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { let n = u.int_in_range(0..=8)?; Ok(match n { 0 => Self::Point(u.arbitrary()?), 1 => Self::LineString(u.arbitrary()?), 2 => Self::Polygon(u.arbitrary()?), 3 => Self::MultiPoint(u.arbitrary()?), 4 => Self::MultiLineString(u.arbitrary()?), 5 => Self::MultiPolygon(u.arbitrary()?), 6 => Self::GeometryCollection(u.arbitrary()?), 7 => Self::Triangle(u.arbitrary()?), 8 => Self::Rect(u.arbitrary()?), _ => unreachable!(), }) } } geo-types-0.7.11/src/error.rs000064400000000000000000000022601046102023000141140ustar 00000000000000use core::fmt; #[derive(Debug)] pub enum Error { MismatchedGeometry { expected: &'static str, found: &'static str, }, } #[cfg(feature = "std")] impl std::error::Error for Error {} impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::MismatchedGeometry { expected, found } => { write!(f, "Expected a {expected}, but found a {found}") } } } } #[cfg(test)] mod test { use crate::{Geometry, Point, Rect}; use alloc::string::ToString; use core::convert::TryFrom; #[test] fn error_output() { let point = Point::new(1.0, 2.0); let point_geometry = Geometry::from(point); let rect = Rect::new(Point::new(1.0, 2.0), Point::new(3.0, 4.0)); let rect_geometry = Geometry::from(rect); Point::try_from(point_geometry).expect("failed to unwrap inner enum Point"); let failure = Point::try_from(rect_geometry).unwrap_err(); assert_eq!( failure.to_string(), "Expected a geo_types::geometry::point::Point, but found a geo_types::geometry::rect::Rect" ); } } geo-types-0.7.11/src/geometry/coord.rs000064400000000000000000000231301046102023000157230ustar 00000000000000use crate::{coord, CoordNum, Point}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq, UlpsEq}; /// A lightweight struct used to store coordinates on the 2-dimensional /// Cartesian plane. /// /// Unlike `Point` (which in the future may contain additional information such /// as an envelope, a precision model, and spatial reference system /// information), a `Coord` only contains ordinate values and accessor /// methods. /// /// This type implements the [vector space] operations: /// [`Add`], [`Sub`], [`Neg`], [`Zero`], /// [`Mul`][`Mul`], and [`Div`][`Div`] traits. /// /// # Semantics /// /// This type does not represent any geospatial primitive, /// but is used in their definitions. The only requirement /// is that the coordinates it contains are valid numbers /// (for eg. not `f64::NAN`). /// /// [vector space]: //en.wikipedia.org/wiki/Vector_space #[derive(Eq, PartialEq, Clone, Copy, Debug, Hash, Default)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Coord { pub x: T, pub y: T, } #[deprecated(note = "Renamed to `geo_types::Coord` (or `geo::Coord`)")] pub type Coordinate = Coord; impl From<(T, T)> for Coord { #[inline] fn from(coords: (T, T)) -> Self { coord! { x: coords.0, y: coords.1, } } } impl From<[T; 2]> for Coord { #[inline] fn from(coords: [T; 2]) -> Self { coord! { x: coords[0], y: coords[1], } } } impl From> for Coord { #[inline] fn from(point: Point) -> Self { coord! { x: point.x(), y: point.y(), } } } impl From> for (T, T) { #[inline] fn from(coord: Coord) -> Self { (coord.x, coord.y) } } impl From> for [T; 2] { #[inline] fn from(coord: Coord) -> Self { [coord.x, coord.y] } } impl Coord { /// Returns a tuple that contains the x/horizontal & y/vertical component of the coordinate. /// /// # Examples /// /// ``` /// use geo_types::coord; /// /// let c = coord! { /// x: 40.02f64, /// y: 116.34, /// }; /// let (x, y) = c.x_y(); /// /// assert_eq!(y, 116.34); /// assert_eq!(x, 40.02f64); /// ``` #[inline] pub fn x_y(&self) -> (T, T) { (self.x, self.y) } } use core::ops::{Add, Div, Mul, Neg, Sub}; /// Negate a coordinate. /// /// # Examples /// /// ``` /// use geo_types::coord; /// /// let p = coord! { x: 1.25, y: 2.5 }; /// let q = -p; /// /// assert_eq!(q.x, -p.x); /// assert_eq!(q.y, -p.y); /// ``` impl Neg for Coord where T: CoordNum + Neg, { type Output = Self; #[inline] fn neg(self) -> Self { coord! { x: -self.x, y: -self.y, } } } /// Add two coordinates. /// /// # Examples /// /// ``` /// use geo_types::coord; /// /// let p = coord! { x: 1.25, y: 2.5 }; /// let q = coord! { x: 1.5, y: 2.5 }; /// let sum = p + q; /// /// assert_eq!(sum.x, 2.75); /// assert_eq!(sum.y, 5.0); /// ``` impl Add for Coord { type Output = Self; #[inline] fn add(self, rhs: Self) -> Self { coord! { x: self.x + rhs.x, y: self.y + rhs.y, } } } /// Subtract a coordinate from another. /// /// # Examples /// /// ``` /// use geo_types::coord; /// /// let p = coord! { x: 1.5, y: 2.5 }; /// let q = coord! { x: 1.25, y: 2.5 }; /// let diff = p - q; /// /// assert_eq!(diff.x, 0.25); /// assert_eq!(diff.y, 0.); /// ``` impl Sub for Coord { type Output = Self; #[inline] fn sub(self, rhs: Self) -> Self { coord! { x: self.x - rhs.x, y: self.y - rhs.y, } } } /// Multiply coordinate wise by a scalar. /// /// # Examples /// /// ``` /// use geo_types::coord; /// /// let p = coord! { x: 1.25, y: 2.5 }; /// let q = p * 4.; /// /// assert_eq!(q.x, 5.0); /// assert_eq!(q.y, 10.0); /// ``` impl Mul for Coord { type Output = Self; #[inline] fn mul(self, rhs: T) -> Self { coord! { x: self.x * rhs, y: self.y * rhs, } } } /// Divide coordinate wise by a scalar. /// /// # Examples /// /// ``` /// use geo_types::coord; /// /// let p = coord! { x: 5., y: 10. }; /// let q = p / 4.; /// /// assert_eq!(q.x, 1.25); /// assert_eq!(q.y, 2.5); /// ``` impl Div for Coord { type Output = Self; #[inline] fn div(self, rhs: T) -> Self { coord! { x: self.x / rhs, y: self.y / rhs, } } } use num_traits::Zero; /// Create a coordinate at the origin. /// /// # Examples /// /// ``` /// use geo_types::Coord; /// use num_traits::Zero; /// /// let p: Coord = Zero::zero(); /// /// assert_eq!(p.x, 0.); /// assert_eq!(p.y, 0.); /// ``` impl Coord { #[inline] pub fn zero() -> Self { coord! { x: T::zero(), y: T::zero(), } } } impl Zero for Coord { #[inline] fn zero() -> Self { Self::zero() } #[inline] fn is_zero(&self) -> bool { self.x.is_zero() && self.y.is_zero() } } #[cfg(any(feature = "approx", test))] impl AbsDiffEq for Coord where T::Epsilon: Copy, { type Epsilon = T::Epsilon; #[inline] fn default_epsilon() -> T::Epsilon { T::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: T::Epsilon) -> bool { T::abs_diff_eq(&self.x, &other.x, epsilon) && T::abs_diff_eq(&self.y, &other.y, epsilon) } } #[cfg(any(feature = "approx", test))] impl RelativeEq for Coord where T::Epsilon: Copy, { #[inline] fn default_max_relative() -> T::Epsilon { T::default_max_relative() } #[inline] fn relative_eq(&self, other: &Self, epsilon: T::Epsilon, max_relative: T::Epsilon) -> bool { T::relative_eq(&self.x, &other.x, epsilon, max_relative) && T::relative_eq(&self.y, &other.y, epsilon, max_relative) } } #[cfg(any(feature = "approx", test))] impl UlpsEq for Coord where T::Epsilon: Copy, { #[inline] fn default_max_ulps() -> u32 { T::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: T::Epsilon, max_ulps: u32) -> bool { T::ulps_eq(&self.x, &other.x, epsilon, max_ulps) && T::ulps_eq(&self.y, &other.y, epsilon, max_ulps) } } #[cfg(feature = "rstar_0_8")] impl ::rstar_0_8::Point for Coord where T: ::num_traits::Float + ::rstar_0_8::RTreeNum, { type Scalar = T; const DIMENSIONS: usize = 2; #[inline] fn generate(generator: impl Fn(usize) -> Self::Scalar) -> Self { coord! { x: generator(0), y: generator(1), } } #[inline] fn nth(&self, index: usize) -> Self::Scalar { match index { 0 => self.x, 1 => self.y, _ => unreachable!(), } } #[inline] fn nth_mut(&mut self, index: usize) -> &mut Self::Scalar { match index { 0 => &mut self.x, 1 => &mut self.y, _ => unreachable!(), } } } #[cfg(feature = "rstar_0_9")] impl ::rstar_0_9::Point for Coord where T: ::num_traits::Float + ::rstar_0_9::RTreeNum, { type Scalar = T; const DIMENSIONS: usize = 2; #[inline] fn generate(mut generator: impl FnMut(usize) -> Self::Scalar) -> Self { coord! { x: generator(0), y: generator(1), } } #[inline] fn nth(&self, index: usize) -> Self::Scalar { match index { 0 => self.x, 1 => self.y, _ => unreachable!(), } } #[inline] fn nth_mut(&mut self, index: usize) -> &mut Self::Scalar { match index { 0 => &mut self.x, 1 => &mut self.y, _ => unreachable!(), } } } #[cfg(feature = "rstar_0_10")] impl ::rstar_0_10::Point for Coord where T: ::num_traits::Float + ::rstar_0_10::RTreeNum, { type Scalar = T; const DIMENSIONS: usize = 2; #[inline] fn generate(mut generator: impl FnMut(usize) -> Self::Scalar) -> Self { coord! { x: generator(0), y: generator(1), } } #[inline] fn nth(&self, index: usize) -> Self::Scalar { match index { 0 => self.x, 1 => self.y, _ => unreachable!(), } } #[inline] fn nth_mut(&mut self, index: usize) -> &mut Self::Scalar { match index { 0 => &mut self.x, 1 => &mut self.y, _ => unreachable!(), } } } #[cfg(feature = "rstar_0_11")] impl ::rstar_0_11::Point for Coord where T: ::num_traits::Float + ::rstar_0_11::RTreeNum, { type Scalar = T; const DIMENSIONS: usize = 2; #[inline] fn generate(mut generator: impl FnMut(usize) -> Self::Scalar) -> Self { coord! { x: generator(0), y: generator(1), } } #[inline] fn nth(&self, index: usize) -> Self::Scalar { match index { 0 => self.x, 1 => self.y, _ => unreachable!(), } } #[inline] fn nth_mut(&mut self, index: usize) -> &mut Self::Scalar { match index { 0 => &mut self.x, 1 => &mut self.y, _ => unreachable!(), } } } geo-types-0.7.11/src/geometry/geometry_collection.rs000064400000000000000000000231121046102023000206630ustar 00000000000000use crate::{CoordNum, Geometry}; use alloc::vec; use alloc::vec::Vec; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; use core::iter::FromIterator; use core::ops::{Index, IndexMut}; /// A collection of [`Geometry`](enum.Geometry.html) types. /// /// It can be created from a `Vec` of Geometries, or from an Iterator which yields Geometries. /// /// Looping over this object yields its component **Geometry /// enum members** (_not_ the underlying geometry /// primitives), and it supports iteration and indexing as /// well as the various /// [`MapCoords`](algorithm/map_coords/index.html) /// functions, which _are_ directly applied to the /// underlying geometry primitives. /// /// # Examples /// ## Looping /// /// ``` /// use std::convert::TryFrom; /// use geo_types::{Point, point, Geometry, GeometryCollection}; /// let p = point!(x: 1.0, y: 1.0); /// let pe = Geometry::Point(p); /// let gc = GeometryCollection::new_from(vec![pe]); /// for geom in gc { /// println!("{:?}", Point::try_from(geom).unwrap().x()); /// } /// ``` /// ## Implements `iter()` /// /// ``` /// use std::convert::TryFrom; /// use geo_types::{Point, point, Geometry, GeometryCollection}; /// let p = point!(x: 1.0, y: 1.0); /// let pe = Geometry::Point(p); /// let gc = GeometryCollection::new_from(vec![pe]); /// gc.iter().for_each(|geom| println!("{:?}", geom)); /// ``` /// /// ## Mutable Iteration /// /// ``` /// use std::convert::TryFrom; /// use geo_types::{Point, point, Geometry, GeometryCollection}; /// let p = point!(x: 1.0, y: 1.0); /// let pe = Geometry::Point(p); /// let mut gc = GeometryCollection::new_from(vec![pe]); /// gc.iter_mut().for_each(|geom| { /// if let Geometry::Point(p) = geom { /// p.set_x(0.2); /// } /// }); /// let updated = gc[0].clone(); /// assert_eq!(Point::try_from(updated).unwrap().x(), 0.2); /// ``` /// /// ## Indexing /// /// ``` /// use std::convert::TryFrom; /// use geo_types::{Point, point, Geometry, GeometryCollection}; /// let p = point!(x: 1.0, y: 1.0); /// let pe = Geometry::Point(p); /// let gc = GeometryCollection::new_from(vec![pe]); /// println!("{:?}", gc[0]); /// ``` /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct GeometryCollection(pub Vec>); // Implementing Default by hand because T does not have Default restriction // todo: consider adding Default as a CoordNum requirement impl Default for GeometryCollection { fn default() -> Self { Self(Vec::new()) } } impl GeometryCollection { /// Return an empty GeometryCollection #[deprecated( note = "Will be replaced with a parametrized version in upcoming version. Use GeometryCollection::default() instead" )] pub fn new() -> Self { GeometryCollection::default() } /// DO NOT USE! /// This fn will be renamed to `new` in the upcoming version. /// This fn is not marked as deprecated because it would require extensive refactoring of the geo code. pub fn new_from(value: Vec>) -> Self { Self(value) } /// Number of geometries in this GeometryCollection pub fn len(&self) -> usize { self.0.len() } /// Is this GeometryCollection empty pub fn is_empty(&self) -> bool { self.0.is_empty() } } /// **DO NOT USE!** Deprecated since 0.7.5. /// /// Use `GeometryCollection::from(vec![geom])` instead. impl>> From for GeometryCollection { fn from(x: IG) -> Self { Self(vec![x.into()]) } } impl>> From> for GeometryCollection { fn from(geoms: Vec) -> Self { let geoms: Vec> = geoms.into_iter().map(Into::into).collect(); Self(geoms) } } /// Collect Geometries (or what can be converted to a Geometry) into a GeometryCollection impl>> FromIterator for GeometryCollection { fn from_iter>(iter: I) -> Self { Self(iter.into_iter().map(|g| g.into()).collect()) } } impl Index for GeometryCollection { type Output = Geometry; fn index(&self, index: usize) -> &Geometry { self.0.index(index) } } impl IndexMut for GeometryCollection { fn index_mut(&mut self, index: usize) -> &mut Geometry { self.0.index_mut(index) } } // structure helper for consuming iterator #[derive(Debug)] pub struct IntoIteratorHelper { iter: ::alloc::vec::IntoIter>, } // implement the IntoIterator trait for a consuming iterator. Iteration will // consume the GeometryCollection impl IntoIterator for GeometryCollection { type Item = Geometry; type IntoIter = IntoIteratorHelper; // note that into_iter() is consuming self fn into_iter(self) -> Self::IntoIter { IntoIteratorHelper { iter: self.0.into_iter(), } } } // implement Iterator trait for the helper struct, to be used by adapters impl Iterator for IntoIteratorHelper { type Item = Geometry; // just return the reference fn next(&mut self) -> Option { self.iter.next() } } // structure helper for non-consuming iterator #[derive(Debug)] pub struct IterHelper<'a, T: CoordNum> { iter: ::core::slice::Iter<'a, Geometry>, } // implement the IntoIterator trait for a non-consuming iterator. Iteration will // borrow the GeometryCollection impl<'a, T: CoordNum> IntoIterator for &'a GeometryCollection { type Item = &'a Geometry; type IntoIter = IterHelper<'a, T>; // note that into_iter() is consuming self fn into_iter(self) -> Self::IntoIter { IterHelper { iter: self.0.iter(), } } } // implement the Iterator trait for the helper struct, to be used by adapters impl<'a, T: CoordNum> Iterator for IterHelper<'a, T> { type Item = &'a Geometry; // just return the str reference fn next(&mut self) -> Option { self.iter.next() } } // structure helper for mutable non-consuming iterator #[derive(Debug)] pub struct IterMutHelper<'a, T: CoordNum> { iter: ::core::slice::IterMut<'a, Geometry>, } // implement the IntoIterator trait for a mutable non-consuming iterator. Iteration will // mutably borrow the GeometryCollection impl<'a, T: CoordNum> IntoIterator for &'a mut GeometryCollection { type Item = &'a mut Geometry; type IntoIter = IterMutHelper<'a, T>; // note that into_iter() is consuming self fn into_iter(self) -> Self::IntoIter { IterMutHelper { iter: self.0.iter_mut(), } } } // implement the Iterator trait for the helper struct, to be used by adapters impl<'a, T: CoordNum> Iterator for IterMutHelper<'a, T> { type Item = &'a mut Geometry; // just return the str reference fn next(&mut self) -> Option { self.iter.next() } } impl<'a, T: CoordNum> GeometryCollection { pub fn iter(&'a self) -> IterHelper<'a, T> { self.into_iter() } pub fn iter_mut(&'a mut self) -> IterMutHelper<'a, T> { self.into_iter() } } #[cfg(any(feature = "approx", test))] impl RelativeEq for GeometryCollection where T: AbsDiffEq + CoordNum + RelativeEq, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } /// Equality assertion within a relative limit. /// /// # Examples /// /// ``` /// use geo_types::{GeometryCollection, point}; /// /// let a = GeometryCollection::new_from(vec![point![x: 1.0, y: 2.0].into()]); /// let b = GeometryCollection::new_from(vec![point![x: 1.0, y: 2.01].into()]); /// /// approx::assert_relative_eq!(a, b, max_relative=0.1); /// approx::assert_relative_ne!(a, b, max_relative=0.0001); /// ``` #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { if self.0.len() != other.0.len() { return false; } let mut mp_zipper = self.iter().zip(other.iter()); mp_zipper.all(|(lhs, rhs)| lhs.relative_eq(rhs, epsilon, max_relative)) } } #[cfg(any(feature = "approx", test))] impl AbsDiffEq for GeometryCollection where T: AbsDiffEq + CoordNum, T::Epsilon: Copy, { type Epsilon = T; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } /// Equality assertion with an absolute limit. /// /// # Examples /// /// ``` /// use geo_types::{GeometryCollection, point}; /// /// let a = GeometryCollection::new_from(vec![point![x: 0.0, y: 0.0].into()]); /// let b = GeometryCollection::new_from(vec![point![x: 0.0, y: 0.1].into()]); /// /// approx::abs_diff_eq!(a, b, epsilon=0.1); /// approx::abs_diff_ne!(a, b, epsilon=0.001); /// ``` #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { if self.0.len() != other.0.len() { return false; } let mut mp_zipper = self.into_iter().zip(other.into_iter()); mp_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon)) } } #[cfg(test)] mod tests { use alloc::vec; use crate::{GeometryCollection, Point}; #[test] fn from_vec() { let gc = GeometryCollection::from(vec![Point::new(1i32, 2)]); let p = Point::try_from(gc[0].clone()).unwrap(); assert_eq!(p.y(), 2); } } geo-types-0.7.11/src/geometry/line.rs000064400000000000000000000216361046102023000155550ustar 00000000000000use crate::{Coord, CoordNum, Point}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; /// A line segment made up of exactly two /// [`Coord`]s. /// /// # Semantics /// /// The _interior_ and _boundary_ are defined as with a /// `LineString` with the two end points. #[derive(Eq, PartialEq, Clone, Copy, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Line { pub start: Coord, pub end: Coord, } impl Line { /// Creates a new line segment. /// /// # Examples /// /// ``` /// use geo_types::{coord, Line}; /// /// let line = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 2. }); /// /// assert_eq!(line.start, coord! { x: 0., y: 0. }); /// assert_eq!(line.end, coord! { x: 1., y: 2. }); /// ``` pub fn new(start: C, end: C) -> Self where C: Into>, { Self { start: start.into(), end: end.into(), } } /// Calculate the difference in coordinates (Δx, Δy). pub fn delta(&self) -> Coord { self.end - self.start } /// Calculate the difference in ‘x’ components (Δx). /// /// Equivalent to: /// /// ```rust /// # use geo_types::{Line, point}; /// # let line = Line::new( /// # point! { x: 4., y: -12. }, /// # point! { x: 0., y: 9. }, /// # ); /// # assert_eq!( /// # line.dx(), /// line.end.x - line.start.x /// # ); /// ``` pub fn dx(&self) -> T { self.delta().x } /// Calculate the difference in ‘y’ components (Δy). /// /// Equivalent to: /// /// ```rust /// # use geo_types::{Line, point}; /// # let line = Line::new( /// # point! { x: 4., y: -12. }, /// # point! { x: 0., y: 9. }, /// # ); /// # assert_eq!( /// # line.dy(), /// line.end.y - line.start.y /// # ); /// ``` pub fn dy(&self) -> T { self.delta().y } /// Calculate the slope (Δy/Δx). /// /// Equivalent to: /// /// ```rust /// # use geo_types::{Line, point}; /// # let line = Line::new( /// # point! { x: 4., y: -12. }, /// # point! { x: 0., y: 9. }, /// # ); /// # assert_eq!( /// # line.slope(), /// line.dy() / line.dx() /// # ); /// ``` /// /// Note that: /// /// ```rust /// # use geo_types::{Line, point}; /// # let a = point! { x: 4., y: -12. }; /// # let b = point! { x: 0., y: 9. }; /// # assert!( /// Line::new(a, b).slope() == Line::new(b, a).slope() /// # ); /// ``` pub fn slope(&self) -> T { self.dy() / self.dx() } /// Calculate the [determinant](https://en.wikipedia.org/wiki/Determinant) of the line. /// /// Equivalent to: /// /// ```rust /// # use geo_types::{Line, point}; /// # let line = Line::new( /// # point! { x: 4., y: -12. }, /// # point! { x: 0., y: 9. }, /// # ); /// # assert_eq!( /// # line.determinant(), /// line.start.x * line.end.y - line.start.y * line.end.x /// # ); /// ``` /// /// Note that: /// /// ```rust /// # use geo_types::{Line, point}; /// # let a = point! { x: 4., y: -12. }; /// # let b = point! { x: 0., y: 9. }; /// # assert!( /// Line::new(a, b).determinant() == -Line::new(b, a).determinant() /// # ); /// ``` pub fn determinant(&self) -> T { self.start.x * self.end.y - self.start.y * self.end.x } pub fn start_point(&self) -> Point { Point::from(self.start) } pub fn end_point(&self) -> Point { Point::from(self.end) } pub fn points(&self) -> (Point, Point) { (self.start_point(), self.end_point()) } } impl From<[(T, T); 2]> for Line { fn from(coord: [(T, T); 2]) -> Self { Line::new(coord[0], coord[1]) } } #[cfg(any(feature = "approx", test))] impl RelativeEq for Line where T: AbsDiffEq + CoordNum + RelativeEq, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } /// Equality assertion within a relative limit. /// /// # Examples /// /// ``` /// use geo_types::{coord, Line}; /// /// let a = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. }); /// let b = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1.001, y: 1. }); /// /// approx::assert_relative_eq!(a, b, max_relative=0.1); /// ``` #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.start.relative_eq(&other.start, epsilon, max_relative) && self.end.relative_eq(&other.end, epsilon, max_relative) } } #[cfg(any(feature = "approx", test))] impl + CoordNum> AbsDiffEq for Line { type Epsilon = T; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } /// Equality assertion with an absolute limit. /// /// # Examples /// /// ``` /// use geo_types::{coord, Line}; /// /// let a = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. }); /// let b = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1.001, y: 1. }); /// /// approx::assert_abs_diff_eq!(a, b, epsilon=0.1); /// ``` #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.start.abs_diff_eq(&other.start, epsilon) && self.end.abs_diff_eq(&other.end, epsilon) } } #[cfg(any( feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10", feature = "rstar_0_11" ))] macro_rules! impl_rstar_line { ($rstar:ident) => { impl ::$rstar::RTreeObject for Line where T: ::num_traits::Float + ::$rstar::RTreeNum, { type Envelope = ::$rstar::AABB>; fn envelope(&self) -> Self::Envelope { let bounding_rect = crate::private_utils::line_bounding_rect(*self); ::$rstar::AABB::from_corners(bounding_rect.min().into(), bounding_rect.max().into()) } } impl ::$rstar::PointDistance for Line where T: ::num_traits::Float + ::$rstar::RTreeNum, { fn distance_2(&self, point: &Point) -> T { let d = crate::private_utils::point_line_euclidean_distance(*point, *self); d.powi(2) } } }; } #[cfg(feature = "rstar_0_8")] impl_rstar_line!(rstar_0_8); #[cfg(feature = "rstar_0_9")] impl_rstar_line!(rstar_0_9); #[cfg(feature = "rstar_0_10")] impl_rstar_line!(rstar_0_10); #[cfg(feature = "rstar_0_11")] impl_rstar_line!(rstar_0_11); #[cfg(test)] mod test { use super::*; use crate::{coord, point}; #[test] fn test_abs_diff_eq() { let delta = 1e-6; let line = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. }); let line_start_x = Line::new( point! { x: 0. + delta, y: 0., }, point! { x: 1., y: 1. }, ); assert!(line.abs_diff_eq(&line_start_x, 1e-2)); assert!(line.abs_diff_ne(&line_start_x, 1e-12)); let line_start_y = Line::new( coord! { x: 0., y: 0. + delta, }, coord! { x: 1., y: 1. }, ); assert!(line.abs_diff_eq(&line_start_y, 1e-2)); assert!(line.abs_diff_ne(&line_start_y, 1e-12)); let line_end_x = Line::new( coord! { x: 0., y: 0. }, coord! { x: 1. + delta, y: 1., }, ); assert!(line.abs_diff_eq(&line_end_x, 1e-2)); assert!(line.abs_diff_ne(&line_end_x, 1e-12)); let line_end_y = Line::new( coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. + delta, }, ); assert!(line.abs_diff_eq(&line_end_y, 1e-2)); assert!(line.abs_diff_ne(&line_end_y, 1e-12)); } #[test] fn test_relative_eq() { let delta = 1e-6; let line = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. }); let line_start_x = Line::new( point! { x: 0. + delta, y: 0., }, point! { x: 1., y: 1. }, ); let line_start_y = Line::new( coord! { x: 0., y: 0. + delta, }, coord! { x: 1., y: 1. }, ); assert!(line.relative_eq(&line_start_x, 1e-2, 1e-2)); assert!(line.relative_ne(&line_start_x, 1e-12, 1e-12)); assert!(line.relative_eq(&line_start_y, 1e-2, 1e-2)); assert!(line.relative_ne(&line_start_y, 1e-12, 1e-12)); } } geo-types-0.7.11/src/geometry/line_string.rs000064400000000000000000000475661046102023000171550ustar 00000000000000#[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; use crate::{Coord, CoordNum, Line, Point, Triangle}; use alloc::vec; use alloc::vec::Vec; use core::iter::FromIterator; use core::ops::{Index, IndexMut}; /// An ordered collection of two or more [`Coord`]s, representing a /// path between locations. /// /// # Semantics /// /// 1. A [`LineString`] is _closed_ if it is empty, **or** if the first and last coordinates are the same. /// 2. The _boundary_ of a [`LineString`] is either: /// - **empty** if it is _closed_ (see **1**) **or** /// - contains the **start** and **end** coordinates. /// 3. The _interior_ is the (infinite) set of all coordinates along the [`LineString`], _not including_ the boundary. /// 4. A [`LineString`] is _simple_ if it does not intersect except **optionally** at the first and last coordinates (in which case it is also _closed_, see **1**). /// 5. A _simple_ **and** _closed_ [`LineString`] is a `LinearRing` as defined in the OGC-SFA (but is not defined as a separate type in this crate). /// /// # Validity /// /// A [`LineString`] is valid if it is either empty or /// contains 2 or more coordinates. /// /// Further, a closed [`LineString`] **must not** self-intersect. Note that its /// validity is **not** enforced, and operations and /// predicates are **undefined** on invalid `LineString`s. /// /// # Examples /// ## Creation /// /// Create a [`LineString`] by calling it directly: /// /// ``` /// use geo_types::{coord, LineString}; /// /// let line_string = LineString::new(vec![ /// coord! { x: 0., y: 0. }, /// coord! { x: 10., y: 0. }, /// ]); /// ``` /// /// Create a [`LineString`] with the [`line_string!`][`crate::line_string!`] macro: /// /// ``` /// use geo_types::line_string; /// /// let line_string = line_string![ /// (x: 0., y: 0.), /// (x: 10., y: 0.), /// ]; /// ``` /// /// By converting from a [`Vec`] of coordinate-like things: /// /// ``` /// use geo_types::LineString; /// /// let line_string: LineString = vec![(0., 0.), (10., 0.)].into(); /// ``` /// /// ``` /// use geo_types::LineString; /// /// let line_string: LineString = vec![[0., 0.], [10., 0.]].into(); /// ``` // /// Or by `collect`ing from a [`Coord`] iterator /// /// ``` /// use geo_types::{coord, LineString}; /// /// let mut coords_iter = /// vec![coord! { x: 0., y: 0. }, coord! { x: 10., y: 0. }].into_iter(); /// /// let line_string: LineString = coords_iter.collect(); /// ``` /// /// ## Iteration /// [`LineString`] provides five iterators: [`coords`](LineString::coords), [`coords_mut`](LineString::coords_mut), [`points`](LineString::points), [`lines`](LineString::lines), and [`triangles`](LineString::triangles): /// /// ``` /// use geo_types::{coord, LineString}; /// /// let line_string = LineString::new(vec![ /// coord! { x: 0., y: 0. }, /// coord! { x: 10., y: 0. }, /// ]); /// /// line_string.coords().for_each(|coord| println!("{:?}", coord)); /// /// for point in line_string.points() { /// println!("Point x = {}, y = {}", point.x(), point.y()); /// } /// ``` /// /// Note that its [`IntoIterator`] impl yields [`Coord`]s when looping: /// /// ``` /// use geo_types::{coord, LineString}; /// /// let line_string = LineString::new(vec![ /// coord! { x: 0., y: 0. }, /// coord! { x: 10., y: 0. }, /// ]); /// /// for coord in &line_string { /// println!("Coordinate x = {}, y = {}", coord.x, coord.y); /// } /// /// for coord in line_string { /// println!("Coordinate x = {}, y = {}", coord.x, coord.y); /// } /// /// ``` /// ## Decomposition /// /// You can decompose a [`LineString`] into a [`Vec`] of [`Coord`]s or [`Point`]s: /// ``` /// use geo_types::{coord, LineString, Point}; /// /// let line_string = LineString::new(vec![ /// coord! { x: 0., y: 0. }, /// coord! { x: 10., y: 0. }, /// ]); /// /// let coordinate_vec = line_string.clone().into_inner(); /// let point_vec = line_string.clone().into_points(); /// /// ``` #[derive(Eq, PartialEq, Clone, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct LineString(pub Vec>); /// A [`Point`] iterator returned by the `points` method #[derive(Debug)] pub struct PointsIter<'a, T: CoordNum + 'a>(::core::slice::Iter<'a, Coord>); impl<'a, T: CoordNum> Iterator for PointsIter<'a, T> { type Item = Point; fn next(&mut self) -> Option { self.0.next().map(|c| Point::from(*c)) } fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } } impl<'a, T: CoordNum> ExactSizeIterator for PointsIter<'a, T> { fn len(&self) -> usize { self.0.len() } } impl<'a, T: CoordNum> DoubleEndedIterator for PointsIter<'a, T> { fn next_back(&mut self) -> Option { self.0.next_back().map(|c| Point::from(*c)) } } /// A [`Coord`] iterator used by the `into_iter` method on a [`LineString`] #[derive(Debug)] pub struct CoordinatesIter<'a, T: CoordNum + 'a>(::core::slice::Iter<'a, Coord>); impl<'a, T: CoordNum> Iterator for CoordinatesIter<'a, T> { type Item = &'a Coord; fn next(&mut self) -> Option { self.0.next() } fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } } impl<'a, T: CoordNum> ExactSizeIterator for CoordinatesIter<'a, T> { fn len(&self) -> usize { self.0.len() } } impl<'a, T: CoordNum> DoubleEndedIterator for CoordinatesIter<'a, T> { fn next_back(&mut self) -> Option { self.0.next_back() } } impl LineString { /// Instantiate Self from the raw content value pub fn new(value: Vec>) -> Self { Self(value) } /// Return an iterator yielding the coordinates of a [`LineString`] as [`Point`]s #[deprecated(note = "Use points() instead")] pub fn points_iter(&self) -> PointsIter { PointsIter(self.0.iter()) } /// Return an iterator yielding the coordinates of a [`LineString`] as [`Point`]s pub fn points(&self) -> PointsIter { PointsIter(self.0.iter()) } /// Return an iterator yielding the members of a [`LineString`] as [`Coord`]s pub fn coords(&self) -> impl DoubleEndedIterator> { self.0.iter() } /// Return an iterator yielding the coordinates of a [`LineString`] as mutable [`Coord`]s pub fn coords_mut(&mut self) -> impl DoubleEndedIterator> { self.0.iter_mut() } /// Return the coordinates of a [`LineString`] as a [`Vec`] of [`Point`]s pub fn into_points(self) -> Vec> { self.0.into_iter().map(Point::from).collect() } /// Return the coordinates of a [`LineString`] as a [`Vec`] of [`Coord`]s pub fn into_inner(self) -> Vec> { self.0 } /// Return an iterator yielding one [Line] for each line segment /// in the [`LineString`]. /// /// # Examples /// /// ``` /// use geo_types::{coord, Line, LineString}; /// /// let mut coords = vec![(0., 0.), (5., 0.), (7., 9.)]; /// let line_string: LineString = coords.into_iter().collect(); /// /// let mut lines = line_string.lines(); /// assert_eq!( /// Some(Line::new( /// coord! { x: 0., y: 0. }, /// coord! { x: 5., y: 0. } /// )), /// lines.next() /// ); /// assert_eq!( /// Some(Line::new( /// coord! { x: 5., y: 0. }, /// coord! { x: 7., y: 9. } /// )), /// lines.next() /// ); /// assert!(lines.next().is_none()); /// ``` pub fn lines(&'_ self) -> impl ExactSizeIterator + Iterator> + '_ { self.0.windows(2).map(|w| { // slice::windows(N) is guaranteed to yield a slice with exactly N elements unsafe { Line::new(*w.get_unchecked(0), *w.get_unchecked(1)) } }) } /// An iterator which yields the coordinates of a [`LineString`] as [Triangle]s pub fn triangles(&'_ self) -> impl ExactSizeIterator + Iterator> + '_ { self.0.windows(3).map(|w| { // slice::windows(N) is guaranteed to yield a slice with exactly N elements unsafe { Triangle::new( *w.get_unchecked(0), *w.get_unchecked(1), *w.get_unchecked(2), ) } }) } /// Close the [`LineString`]. Specifically, if the [`LineString`] has at least one [`Coord`], and /// the value of the first [`Coord`] **does not** equal the value of the last [`Coord`], then a /// new [`Coord`] is added to the end with the value of the first [`Coord`]. pub fn close(&mut self) { if !self.is_closed() { // by definition, we treat empty LineString's as closed. debug_assert!(!self.0.is_empty()); self.0.push(self.0[0]); } } /// Return the number of coordinates in the [`LineString`]. /// /// # Examples /// /// ``` /// use geo_types::LineString; /// /// let mut coords = vec![(0., 0.), (5., 0.), (7., 9.)]; /// let line_string: LineString = coords.into_iter().collect(); /// /// # #[allow(deprecated)] /// # { /// assert_eq!(3, line_string.num_coords()); /// # } /// ``` #[deprecated(note = "Use geo::CoordsIter::coords_count instead")] pub fn num_coords(&self) -> usize { self.0.len() } /// Checks if the linestring is closed; i.e. it is /// either empty or, the first and last points are the /// same. /// /// # Examples /// /// ``` /// use geo_types::LineString; /// /// let mut coords = vec![(0., 0.), (5., 0.), (0., 0.)]; /// let line_string: LineString = coords.into_iter().collect(); /// assert!(line_string.is_closed()); /// ``` /// /// Note that we diverge from some libraries ([JTS](https://locationtech.github.io/jts/javadoc/org/locationtech/jts/geom/LinearRing.html) et al), which have a `LinearRing` type, /// separate from [`LineString`]. Those libraries treat an empty `LinearRing` as **closed** by /// definition, while treating an empty `LineString` as **open**. Since we don't have a separate /// `LinearRing` type, and use a [`LineString`] in its place, we adopt the JTS `LinearRing` `is_closed` /// behavior in all places: that is, **we consider an empty [`LineString`] as closed**. /// /// This is expected when used in the context of a [`Polygon.exterior`](crate::Polygon::exterior) and elsewhere; And there /// seems to be no reason to maintain the separate behavior for [`LineString`]s used in /// non-`LinearRing` contexts. pub fn is_closed(&self) -> bool { self.0.first() == self.0.last() } } /// Turn a [`Vec`] of [`Point`]-like objects into a [`LineString`]. impl>> From> for LineString { fn from(v: Vec) -> Self { Self(v.into_iter().map(|c| c.into()).collect()) } } impl From> for LineString { fn from(line: Line) -> Self { LineString::from(&line) } } impl From<&Line> for LineString { fn from(line: &Line) -> Self { Self(vec![line.start, line.end]) } } /// Turn an iterator of [`Point`]-like objects into a [`LineString`]. impl>> FromIterator for LineString { fn from_iter>(iter: I) -> Self { Self(iter.into_iter().map(|c| c.into()).collect()) } } /// Iterate over all the [`Coord`]s in this [`LineString`]. impl IntoIterator for LineString { type Item = Coord; type IntoIter = ::alloc::vec::IntoIter>; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl<'a, T: CoordNum> IntoIterator for &'a LineString { type Item = &'a Coord; type IntoIter = CoordinatesIter<'a, T>; fn into_iter(self) -> Self::IntoIter { CoordinatesIter(self.0.iter()) } } /// Mutably iterate over all the [`Coord`]s in this [`LineString`] impl<'a, T: CoordNum> IntoIterator for &'a mut LineString { type Item = &'a mut Coord; type IntoIter = ::core::slice::IterMut<'a, Coord>; fn into_iter(self) -> ::core::slice::IterMut<'a, Coord> { self.0.iter_mut() } } impl Index for LineString { type Output = Coord; fn index(&self, index: usize) -> &Coord { self.0.index(index) } } impl IndexMut for LineString { fn index_mut(&mut self, index: usize) -> &mut Coord { self.0.index_mut(index) } } #[cfg(any(feature = "approx", test))] impl RelativeEq for LineString where T: AbsDiffEq + CoordNum + RelativeEq, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } /// Equality assertion within a relative limit. /// /// # Examples /// /// ``` /// use geo_types::LineString; /// /// let mut coords_a = vec![(0., 0.), (5., 0.), (7., 9.)]; /// let a: LineString = coords_a.into_iter().collect(); /// /// let mut coords_b = vec![(0., 0.), (5., 0.), (7.001, 9.)]; /// let b: LineString = coords_b.into_iter().collect(); /// /// approx::assert_relative_eq!(a, b, max_relative=0.1) /// ``` /// fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { if self.0.len() != other.0.len() { return false; } let points_zipper = self.points().zip(other.points()); for (lhs, rhs) in points_zipper { if lhs.relative_ne(&rhs, epsilon, max_relative) { return false; } } true } } #[cfg(any(feature = "approx", test))] impl + CoordNum> AbsDiffEq for LineString { type Epsilon = T; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } /// Equality assertion with an absolute limit. /// /// # Examples /// /// ``` /// use geo_types::LineString; /// /// let mut coords_a = vec![(0., 0.), (5., 0.), (7., 9.)]; /// let a: LineString = coords_a.into_iter().collect(); /// /// let mut coords_b = vec![(0., 0.), (5., 0.), (7.001, 9.)]; /// let b: LineString = coords_b.into_iter().collect(); /// /// approx::assert_relative_eq!(a, b, epsilon=0.1) /// ``` fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { if self.0.len() != other.0.len() { return false; } let mut points_zipper = self.points().zip(other.points()); points_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(&rhs, epsilon)) } } #[cfg(any( feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10", feature = "rstar_0_11" ))] macro_rules! impl_rstar_line_string { ($rstar:ident) => { impl ::$rstar::RTreeObject for LineString where T: ::num_traits::Float + ::$rstar::RTreeNum, { type Envelope = ::$rstar::AABB>; fn envelope(&self) -> Self::Envelope { use num_traits::Bounded; let bounding_rect = crate::private_utils::line_string_bounding_rect(self); match bounding_rect { None => ::$rstar::AABB::from_corners( Point::new(Bounded::min_value(), Bounded::min_value()), Point::new(Bounded::max_value(), Bounded::max_value()), ), Some(b) => ::$rstar::AABB::from_corners( Point::new(b.min().x, b.min().y), Point::new(b.max().x, b.max().y), ), } } } impl ::$rstar::PointDistance for LineString where T: ::num_traits::Float + ::$rstar::RTreeNum, { fn distance_2(&self, point: &Point) -> T { let d = crate::private_utils::point_line_string_euclidean_distance(*point, self); if d == T::zero() { d } else { d.powi(2) } } } }; } #[cfg(feature = "rstar_0_8")] impl_rstar_line_string!(rstar_0_8); #[cfg(feature = "rstar_0_9")] impl_rstar_line_string!(rstar_0_9); #[cfg(feature = "rstar_0_10")] impl_rstar_line_string!(rstar_0_10); #[cfg(feature = "rstar_0_11")] impl_rstar_line_string!(rstar_0_11); #[cfg(test)] mod test { use super::*; use crate::coord; use approx::AbsDiffEq; #[test] fn test_exact_size() { // see https://github.com/georust/geo/issues/762 let first = coord! { x: 0., y: 0. }; let ls = LineString::new(vec![first, coord! { x: 10., y: 0. }]); // reference to force the `impl IntoIterator for &LineString` impl, giving a `CoordinatesIter` for c in (&ls).into_iter().rev().skip(1).rev() { assert_eq!(&first, c); } for p in ls.points().rev().skip(1).rev() { assert_eq!(Point::from(first), p); } } #[test] fn test_abs_diff_eq() { let delta = 1e-6; let coords = vec![(0., 0.), (5., 0.), (10., 10.)]; let ls: LineString = coords.into_iter().collect(); let coords_x = vec![(0., 0.), (5. + delta, 0.), (10., 10.)]; let ls_x: LineString = coords_x.into_iter().collect(); assert!(ls.abs_diff_eq(&ls_x, 1e-2)); assert!(ls.abs_diff_ne(&ls_x, 1e-12)); let coords_y = vec![(0., 0.), (5., 0. + delta), (10., 10.)]; let ls_y: LineString = coords_y.into_iter().collect(); assert!(ls.abs_diff_eq(&ls_y, 1e-2)); assert!(ls.abs_diff_ne(&ls_y, 1e-12)); // Undersized, but otherwise equal. let coords_x = vec![(0., 0.), (5., 0.)]; let ls_under: LineString = coords_x.into_iter().collect(); assert!(ls.abs_diff_ne(&ls_under, 1.)); // Oversized, but otherwise equal. let coords_x = vec![(0., 0.), (5., 0.), (10., 10.), (10., 100.)]; let ls_oversized: LineString = coords_x.into_iter().collect(); assert!(ls.abs_diff_ne(&ls_oversized, 1.)); } #[test] fn test_relative_eq() { let delta = 1e-6; let coords = vec![(0., 0.), (5., 0.), (10., 10.)]; let ls: LineString = coords.into_iter().collect(); let coords_x = vec![(0., 0.), (5. + delta, 0.), (10., 10.)]; let ls_x: LineString = coords_x.into_iter().collect(); assert!(ls.relative_eq(&ls_x, 1e-2, 1e-2)); assert!(ls.relative_ne(&ls_x, 1e-12, 1e-12)); let coords_y = vec![(0., 0.), (5., 0. + delta), (10., 10.)]; let ls_y: LineString = coords_y.into_iter().collect(); assert!(ls.relative_eq(&ls_y, 1e-2, 1e-2)); assert!(ls.relative_ne(&ls_y, 1e-12, 1e-12)); // Undersized, but otherwise equal. let coords_x = vec![(0., 0.), (5., 0.)]; let ls_under: LineString = coords_x.into_iter().collect(); assert!(ls.relative_ne(&ls_under, 1., 1.)); // Oversized, but otherwise equal. let coords_x = vec![(0., 0.), (5., 0.), (10., 10.), (10., 100.)]; let ls_oversized: LineString = coords_x.into_iter().collect(); assert!(ls.relative_ne(&ls_oversized, 1., 1.)); } #[test] fn should_be_built_from_line() { let start = coord! { x: 0, y: 0 }; let end = coord! { x: 10, y: 10 }; let line = Line::new(start, end); let expected = LineString::new(vec![start, end]); assert_eq!(expected, LineString::from(line)); let start = coord! { x: 10., y: 0.5 }; let end = coord! { x: 10000., y: 10.4 }; let line = Line::new(start, end); let expected = LineString::new(vec![start, end]); assert_eq!(expected, LineString::from(line)); } } geo-types-0.7.11/src/geometry/mod.rs000064400000000000000000000276131046102023000154060ustar 00000000000000pub(crate) mod coord; pub(crate) mod geometry_collection; pub(crate) mod line; pub(crate) mod line_string; pub(crate) mod multi_line_string; pub(crate) mod multi_point; pub(crate) mod multi_polygon; pub(crate) mod point; pub(crate) mod polygon; pub(crate) mod rect; pub(crate) mod triangle; // re-export all the geometry variants: #[allow(deprecated)] pub use coord::{Coord, Coordinate}; pub use geometry_collection::GeometryCollection; pub use line::Line; pub use line_string::LineString; pub use multi_line_string::MultiLineString; pub use multi_point::MultiPoint; pub use multi_polygon::MultiPolygon; pub use point::Point; pub use polygon::Polygon; pub use rect::Rect; pub use triangle::Triangle; use crate::{CoordNum, Error}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; use core::any::type_name; use core::convert::TryFrom; /// An enum representing any possible geometry type. /// /// All geometry variants ([`Point`], [`LineString`], etc.) can be converted to a `Geometry` using /// [`Into::into`]. Conversely, [`TryFrom::try_from`] can be used to convert a [`Geometry`] /// _back_ to one of it's specific enum members. /// /// # Example /// /// ``` /// use std::convert::TryFrom; /// use geo_types::{Point, point, Geometry, GeometryCollection}; /// let p = point!(x: 1.0, y: 1.0); /// let pe: Geometry = p.into(); /// let pn = Point::try_from(pe).unwrap(); /// ``` /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Geometry { Point(Point), Line(Line), LineString(LineString), Polygon(Polygon), MultiPoint(MultiPoint), MultiLineString(MultiLineString), MultiPolygon(MultiPolygon), GeometryCollection(GeometryCollection), Rect(Rect), Triangle(Triangle), } impl From> for Geometry { fn from(x: Point) -> Self { Self::Point(x) } } impl From> for Geometry { fn from(x: Line) -> Self { Self::Line(x) } } impl From> for Geometry { fn from(x: LineString) -> Self { Self::LineString(x) } } impl From> for Geometry { fn from(x: Polygon) -> Self { Self::Polygon(x) } } impl From> for Geometry { fn from(x: MultiPoint) -> Self { Self::MultiPoint(x) } } impl From> for Geometry { fn from(x: MultiLineString) -> Self { Self::MultiLineString(x) } } impl From> for Geometry { fn from(x: MultiPolygon) -> Self { Self::MultiPolygon(x) } } // Disabled until we remove the deprecated GeometryCollection::from(single_geom) impl. // impl From> for Geometry { // fn from(x: GeometryCollection) -> Self { // Self::GeometryCollection(x) // } // } impl From> for Geometry { fn from(x: Rect) -> Self { Self::Rect(x) } } impl From> for Geometry { fn from(x: Triangle) -> Self { Self::Triangle(x) } } impl Geometry { /// If this Geometry is a Point, then return that, else None. /// /// # Examples /// /// ``` /// use geo_types::*; /// use std::convert::TryInto; /// /// let g = Geometry::Point(Point::new(0., 0.)); /// let p2: Point = g.try_into().unwrap(); /// assert_eq!(p2, Point::new(0., 0.,)); /// ``` #[deprecated( note = "Will be removed in an upcoming version. Switch to std::convert::TryInto" )] pub fn into_point(self) -> Option> { if let Geometry::Point(x) = self { Some(x) } else { None } } /// If this Geometry is a LineString, then return that LineString, else None. #[deprecated( note = "Will be removed in an upcoming version. Switch to std::convert::TryInto" )] pub fn into_line_string(self) -> Option> { if let Geometry::LineString(x) = self { Some(x) } else { None } } /// If this Geometry is a Line, then return that Line, else None. #[deprecated( note = "Will be removed in an upcoming version. Switch to std::convert::TryInto" )] pub fn into_line(self) -> Option> { if let Geometry::Line(x) = self { Some(x) } else { None } } /// If this Geometry is a Polygon, then return that, else None. #[deprecated( note = "Will be removed in an upcoming version. Switch to std::convert::TryInto" )] pub fn into_polygon(self) -> Option> { if let Geometry::Polygon(x) = self { Some(x) } else { None } } /// If this Geometry is a MultiPoint, then return that, else None. #[deprecated( note = "Will be removed in an upcoming version. Switch to std::convert::TryInto" )] pub fn into_multi_point(self) -> Option> { if let Geometry::MultiPoint(x) = self { Some(x) } else { None } } /// If this Geometry is a MultiLineString, then return that, else None. #[deprecated( note = "Will be removed in an upcoming version. Switch to std::convert::TryInto" )] pub fn into_multi_line_string(self) -> Option> { if let Geometry::MultiLineString(x) = self { Some(x) } else { None } } /// If this Geometry is a MultiPolygon, then return that, else None. #[deprecated( note = "Will be removed in an upcoming version. Switch to std::convert::TryInto" )] pub fn into_multi_polygon(self) -> Option> { if let Geometry::MultiPolygon(x) = self { Some(x) } else { None } } } macro_rules! try_from_geometry_impl { ($($type: ident),+) => { $( /// Convert a Geometry enum into its inner type. /// /// Fails if the enum case does not match the type you are trying to convert it to. impl TryFrom> for $type { type Error = Error; fn try_from(geom: Geometry) -> Result { match geom { Geometry::$type(g) => Ok(g), other => Err(Error::MismatchedGeometry { expected: type_name::<$type>(), found: inner_type_name(other) }) } } } )+ } } try_from_geometry_impl!( Point, Line, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, // Disabled until we remove the deprecated GeometryCollection::from(single_geom) impl. // GeometryCollection, Rect, Triangle ); fn inner_type_name(geometry: Geometry) -> &'static str where T: CoordNum, { match geometry { Geometry::Point(_) => type_name::>(), Geometry::Line(_) => type_name::>(), Geometry::LineString(_) => type_name::>(), Geometry::Polygon(_) => type_name::>(), Geometry::MultiPoint(_) => type_name::>(), Geometry::MultiLineString(_) => type_name::>(), Geometry::MultiPolygon(_) => type_name::>(), Geometry::GeometryCollection(_) => type_name::>(), Geometry::Rect(_) => type_name::>(), Geometry::Triangle(_) => type_name::>(), } } #[cfg(any(feature = "approx", test))] impl RelativeEq for Geometry where T: AbsDiffEq + CoordNum + RelativeEq, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } /// Equality assertion within a relative limit. /// /// # Examples /// /// ``` /// use geo_types::{Geometry, polygon}; /// /// let a: Geometry = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7., y: 9.), (x: 0., y: 0.)].into(); /// let b: Geometry = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7.01, y: 9.), (x: 0., y: 0.)].into(); /// /// approx::assert_relative_eq!(a, b, max_relative=0.1); /// approx::assert_relative_ne!(a, b, max_relative=0.001); /// ``` /// fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { match (self, other) { (Geometry::Point(g1), Geometry::Point(g2)) => g1.relative_eq(g2, epsilon, max_relative), (Geometry::Line(g1), Geometry::Line(g2)) => g1.relative_eq(g2, epsilon, max_relative), (Geometry::LineString(g1), Geometry::LineString(g2)) => { g1.relative_eq(g2, epsilon, max_relative) } (Geometry::Polygon(g1), Geometry::Polygon(g2)) => { g1.relative_eq(g2, epsilon, max_relative) } (Geometry::MultiPoint(g1), Geometry::MultiPoint(g2)) => { g1.relative_eq(g2, epsilon, max_relative) } (Geometry::MultiLineString(g1), Geometry::MultiLineString(g2)) => { g1.relative_eq(g2, epsilon, max_relative) } (Geometry::MultiPolygon(g1), Geometry::MultiPolygon(g2)) => { g1.relative_eq(g2, epsilon, max_relative) } (Geometry::GeometryCollection(g1), Geometry::GeometryCollection(g2)) => { g1.relative_eq(g2, epsilon, max_relative) } (Geometry::Rect(g1), Geometry::Rect(g2)) => g1.relative_eq(g2, epsilon, max_relative), (Geometry::Triangle(g1), Geometry::Triangle(g2)) => { g1.relative_eq(g2, epsilon, max_relative) } (_, _) => false, } } } #[cfg(any(feature = "approx", test))] impl + CoordNum> AbsDiffEq for Geometry { type Epsilon = T; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } /// Equality assertion with an absolute limit. /// /// # Examples /// /// ``` /// use geo_types::{Geometry, polygon}; /// /// let a: Geometry = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7., y: 9.), (x: 0., y: 0.)].into(); /// let b: Geometry = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7.01, y: 9.), (x: 0., y: 0.)].into(); /// /// approx::assert_abs_diff_eq!(a, b, epsilon=0.1); /// approx::assert_abs_diff_ne!(a, b, epsilon=0.001); /// ``` fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { match (self, other) { (Geometry::Point(g1), Geometry::Point(g2)) => g1.abs_diff_eq(g2, epsilon), (Geometry::Line(g1), Geometry::Line(g2)) => g1.abs_diff_eq(g2, epsilon), (Geometry::LineString(g1), Geometry::LineString(g2)) => g1.abs_diff_eq(g2, epsilon), (Geometry::Polygon(g1), Geometry::Polygon(g2)) => g1.abs_diff_eq(g2, epsilon), (Geometry::MultiPoint(g1), Geometry::MultiPoint(g2)) => g1.abs_diff_eq(g2, epsilon), (Geometry::MultiLineString(g1), Geometry::MultiLineString(g2)) => { g1.abs_diff_eq(g2, epsilon) } (Geometry::MultiPolygon(g1), Geometry::MultiPolygon(g2)) => g1.abs_diff_eq(g2, epsilon), (Geometry::GeometryCollection(g1), Geometry::GeometryCollection(g2)) => { g1.abs_diff_eq(g2, epsilon) } (Geometry::Rect(g1), Geometry::Rect(g2)) => g1.abs_diff_eq(g2, epsilon), (Geometry::Triangle(g1), Geometry::Triangle(g2)) => g1.abs_diff_eq(g2, epsilon), (_, _) => false, } } } geo-types-0.7.11/src/geometry/multi_line_string.rs000064400000000000000000000212121046102023000203430ustar 00000000000000use crate::{CoordNum, LineString}; use alloc::vec; use alloc::vec::Vec; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; use core::iter::FromIterator; /// A collection of /// [`LineString`s](line_string/struct.LineString.html). Can /// be created from a `Vec` of `LineString`s or from an /// Iterator which yields `LineString`s. Iterating over this /// object yields the component `LineString`s. /// /// # Semantics /// /// The _boundary_ of a `MultiLineString` is obtained by /// applying the “mod 2” union rule: A `Point` is in the /// boundary of a `MultiLineString` if it is in the /// boundaries of an odd number of elements of the /// `MultiLineString`. /// /// The _interior_ of a `MultiLineString` is the union of /// the interior, and boundary of the constituent /// `LineString`s, _except_ for the boundary as defined /// above. In other words, it is the set difference of the /// boundary from the union of the interior and boundary of /// the constituents. /// /// A `MultiLineString` is _simple_ if and only if all of /// its elements are simple and the only intersections /// between any two elements occur at `Point`s that are on /// the boundaries of both elements. A `MultiLineString` is /// _closed_ if all of its elements are closed. The boundary /// of a closed `MultiLineString` is always empty. #[derive(Eq, PartialEq, Clone, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct MultiLineString(pub Vec>); impl MultiLineString { /// Instantiate Self from the raw content value pub fn new(value: Vec>) -> Self { Self(value) } /// True if the MultiLineString is empty or if all of its LineStrings are closed - see /// [`LineString::is_closed`]. /// /// # Examples /// /// ``` /// use geo_types::{MultiLineString, LineString, line_string}; /// /// let open_line_string: LineString = line_string![(x: 0., y: 0.), (x: 5., y: 0.)]; /// assert!(!MultiLineString::new(vec![open_line_string.clone()]).is_closed()); /// /// let closed_line_string: LineString = line_string![(x: 0., y: 0.), (x: 5., y: 0.), (x: 0., y: 0.)]; /// assert!(MultiLineString::new(vec![closed_line_string.clone()]).is_closed()); /// /// // MultiLineString is not closed if *any* of it's LineStrings are not closed /// assert!(!MultiLineString::new(vec![open_line_string, closed_line_string]).is_closed()); /// /// // An empty MultiLineString is closed /// assert!(MultiLineString::::new(vec![]).is_closed()); /// ``` pub fn is_closed(&self) -> bool { // Note: Unlike JTS et al, we consider an empty MultiLineString as closed. self.iter().all(LineString::is_closed) } } impl>> From for MultiLineString { fn from(ls: ILS) -> Self { Self(vec![ls.into()]) } } impl>> FromIterator for MultiLineString { fn from_iter>(iter: I) -> Self { Self(iter.into_iter().map(|ls| ls.into()).collect()) } } impl IntoIterator for MultiLineString { type Item = LineString; type IntoIter = ::alloc::vec::IntoIter>; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl<'a, T: CoordNum> IntoIterator for &'a MultiLineString { type Item = &'a LineString; type IntoIter = ::alloc::slice::Iter<'a, LineString>; fn into_iter(self) -> Self::IntoIter { (self.0).iter() } } impl<'a, T: CoordNum> IntoIterator for &'a mut MultiLineString { type Item = &'a mut LineString; type IntoIter = ::alloc::slice::IterMut<'a, LineString>; fn into_iter(self) -> Self::IntoIter { (self.0).iter_mut() } } impl MultiLineString { pub fn iter(&self) -> impl Iterator> { self.0.iter() } pub fn iter_mut(&mut self) -> impl Iterator> { self.0.iter_mut() } } #[cfg(any(feature = "approx", test))] impl RelativeEq for MultiLineString where T: AbsDiffEq + CoordNum + RelativeEq, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } /// Equality assertion within a relative limit. /// /// # Examples /// /// ``` /// use geo_types::{MultiLineString, line_string}; /// /// let a = MultiLineString::new(vec![line_string![(x: 0., y: 0.), (x: 10., y: 10.)]]); /// let b = MultiLineString::new(vec![line_string![(x: 0., y: 0.), (x: 10.01, y: 10.)]]); /// /// approx::assert_relative_eq!(a, b, max_relative=0.1); /// approx::assert_relative_ne!(a, b, max_relative=0.0001); /// ``` #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { if self.0.len() != other.0.len() { return false; } let mut mp_zipper = self.iter().zip(other.iter()); mp_zipper.all(|(lhs, rhs)| lhs.relative_eq(rhs, epsilon, max_relative)) } } #[cfg(any(feature = "approx", test))] impl AbsDiffEq for MultiLineString where T: AbsDiffEq + CoordNum, T::Epsilon: Copy, { type Epsilon = T; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } /// Equality assertion with an absolute limit. /// /// # Examples /// /// ``` /// use geo_types::{MultiLineString, line_string}; /// /// let a = MultiLineString::new(vec![line_string![(x: 0., y: 0.), (x: 10., y: 10.)]]); /// let b = MultiLineString::new(vec![line_string![(x: 0., y: 0.), (x: 10.01, y: 10.)]]); /// /// approx::abs_diff_eq!(a, b, epsilon=0.1); /// approx::abs_diff_ne!(a, b, epsilon=0.001); /// ``` #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { if self.0.len() != other.0.len() { return false; } let mut mp_zipper = self.into_iter().zip(other.into_iter()); mp_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon)) } } #[cfg(test)] mod test { use super::*; use crate::line_string; #[test] fn test_iter() { let multi: Vec> = vec![ line_string![(x: 0, y: 0), (x: 2, y: 0), (x: 1, y: 2), (x:0, y:0)], line_string![(x: 10, y: 10), (x: 12, y: 10), (x: 11, y: 12), (x:10, y:10)], ]; let multi: MultiLineString = MultiLineString::new(multi); let mut first = true; for p in &multi { if first { assert_eq!( p, &line_string![(x: 0, y: 0), (x: 2, y: 0), (x: 1, y: 2), (x:0, y:0)] ); first = false; } else { assert_eq!( p, &line_string![(x: 10, y: 10), (x: 12, y: 10), (x: 11, y: 12), (x:10, y:10)] ); } } // Do it again to prove that `multi` wasn't `moved`. first = true; for p in &multi { if first { assert_eq!( p, &line_string![(x: 0, y: 0), (x: 2, y: 0), (x: 1, y: 2), (x:0, y:0)] ); first = false; } else { assert_eq!( p, &line_string![(x: 10, y: 10), (x: 12, y: 10), (x: 11, y: 12), (x:10, y:10)] ); } } } #[test] fn test_iter_mut() { let mut multi = MultiLineString::new(vec![ line_string![(x: 0, y: 0), (x: 2, y: 0), (x: 1, y: 2), (x:0, y:0)], line_string![(x: 10, y: 10), (x: 12, y: 10), (x: 11, y: 12), (x:10, y:10)], ]); for line_string in &mut multi { for coord in line_string { coord.x += 1; coord.y += 1; } } for line_string in multi.iter_mut() { for coord in line_string { coord.x += 1; coord.y += 1; } } let mut first = true; for p in &multi { if first { assert_eq!( p, &line_string![(x: 2, y: 2), (x: 4, y: 2), (x: 3, y: 4), (x:2, y:2)] ); first = false; } else { assert_eq!( p, &line_string![(x: 12, y: 12), (x: 14, y: 12), (x: 13, y: 14), (x:12, y:12)] ); } } } } geo-types-0.7.11/src/geometry/multi_point.rs000064400000000000000000000203261046102023000171640ustar 00000000000000use crate::{CoordNum, Point}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; use alloc::vec; use alloc::vec::Vec; use core::iter::FromIterator; /// A collection of [`Point`s](struct.Point.html). Can /// be created from a `Vec` of `Point`s, or from an /// Iterator which yields `Point`s. Iterating over this /// object yields the component `Point`s. /// /// # Semantics /// /// The _interior_ and the _boundary_ are the union of the /// interior and the boundary of the constituent points. In /// particular, the boundary of a `MultiPoint` is always /// empty. /// /// # Examples /// /// Iterating over a `MultiPoint` yields the `Point`s inside. /// /// ``` /// use geo_types::{MultiPoint, Point}; /// let points: MultiPoint<_> = vec![(0., 0.), (1., 2.)].into(); /// for point in points { /// println!("Point x = {}, y = {}", point.x(), point.y()); /// } /// ``` #[derive(Eq, PartialEq, Clone, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct MultiPoint(pub Vec>); impl>> From for MultiPoint { /// Convert a single `Point` (or something which can be converted to a `Point`) into a /// one-member `MultiPoint` fn from(x: IP) -> Self { Self(vec![x.into()]) } } impl>> From> for MultiPoint { /// Convert a `Vec` of `Points` (or `Vec` of things which can be converted to a `Point`) into a /// `MultiPoint`. fn from(v: Vec) -> Self { Self(v.into_iter().map(|p| p.into()).collect()) } } impl>> FromIterator for MultiPoint { /// Collect the results of a `Point` iterator into a `MultiPoint` fn from_iter>(iter: I) -> Self { Self(iter.into_iter().map(|p| p.into()).collect()) } } /// Iterate over the `Point`s in this `MultiPoint`. impl IntoIterator for MultiPoint { type Item = Point; type IntoIter = ::alloc::vec::IntoIter>; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl<'a, T: CoordNum> IntoIterator for &'a MultiPoint { type Item = &'a Point; type IntoIter = ::alloc::slice::Iter<'a, Point>; fn into_iter(self) -> Self::IntoIter { (self.0).iter() } } impl<'a, T: CoordNum> IntoIterator for &'a mut MultiPoint { type Item = &'a mut Point; type IntoIter = ::alloc::slice::IterMut<'a, Point>; fn into_iter(self) -> Self::IntoIter { (self.0).iter_mut() } } impl MultiPoint { pub fn new(value: Vec>) -> Self { Self(value) } pub fn iter(&self) -> impl Iterator> { self.0.iter() } pub fn iter_mut(&mut self) -> impl Iterator> { self.0.iter_mut() } } #[cfg(any(feature = "approx", test))] impl RelativeEq for MultiPoint where T: AbsDiffEq + CoordNum + RelativeEq, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } /// Equality assertion within a relative limit. /// /// # Examples /// /// ``` /// use geo_types::MultiPoint; /// use geo_types::point; /// /// let a = MultiPoint::new(vec![point![x: 0., y: 0.], point![x: 10., y: 10.]]); /// let b = MultiPoint::new(vec![point![x: 0., y: 0.], point![x: 10.001, y: 10.]]); /// /// approx::assert_relative_eq!(a, b, max_relative=0.1) /// ``` #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { if self.0.len() != other.0.len() { return false; } let mut mp_zipper = self.iter().zip(other.iter()); mp_zipper.all(|(lhs, rhs)| lhs.relative_eq(rhs, epsilon, max_relative)) } } #[cfg(any(feature = "approx", test))] impl AbsDiffEq for MultiPoint where T: AbsDiffEq + CoordNum, T::Epsilon: Copy, { type Epsilon = T; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } /// Equality assertion with an absolute limit. /// /// # Examples /// /// ``` /// use geo_types::MultiPoint; /// use geo_types::point; /// /// let a = MultiPoint::new(vec![point![x: 0., y: 0.], point![x: 10., y: 10.]]); /// let b = MultiPoint::new(vec![point![x: 0., y: 0.], point![x: 10.001, y: 10.]]); /// /// approx::abs_diff_eq!(a, b, epsilon=0.1); /// ``` #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { if self.0.len() != other.0.len() { return false; } let mut mp_zipper = self.into_iter().zip(other.into_iter()); mp_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon)) } } #[cfg(test)] mod test { use super::*; use crate::point; #[test] fn test_iter() { let multi = MultiPoint::new(vec![point![x: 0, y: 0], point![x: 10, y: 10]]); let mut first = true; for p in &multi { if first { assert_eq!(p, &point![x: 0, y: 0]); first = false; } else { assert_eq!(p, &point![x: 10, y: 10]); } } // Do it again to prove that `multi` wasn't `moved`. first = true; for p in &multi { if first { assert_eq!(p, &point![x: 0, y: 0]); first = false; } else { assert_eq!(p, &point![x: 10, y: 10]); } } } #[test] fn test_iter_mut() { let mut multi = MultiPoint::new(vec![point![x: 0, y: 0], point![x: 10, y: 10]]); for point in &mut multi { point.0.x += 1; point.0.y += 1; } for point in multi.iter_mut() { point.0.x += 1; point.0.y += 1; } let mut first = true; for p in &multi { if first { assert_eq!(p, &point![x: 2, y: 2]); first = false; } else { assert_eq!(p, &point![x: 12, y: 12]); } } } #[test] fn test_relative_eq() { let delta = 1e-6; let multi = MultiPoint::new(vec![point![x: 0., y: 0.], point![x: 10., y: 10.]]); let multi_x = MultiPoint::new(vec![point![x: 0., y: 0.], point![x: 10.+delta, y: 10.]]); assert!(multi.relative_eq(&multi_x, 1e-2, 1e-2)); assert!(multi.relative_ne(&multi_x, 1e-12, 1e-12)); let multi_y = MultiPoint::new(vec![point![x: 0., y: 0.], point![x: 10., y: 10.+delta]]); assert!(multi.relative_eq(&multi_y, 1e-2, 1e-2)); assert!(multi.relative_ne(&multi_y, 1e-12, 1e-12)); // Under-sized but otherwise equal. let multi_undersized = MultiPoint::new(vec![point![x: 0., y: 0.]]); assert!(multi.relative_ne(&multi_undersized, 1., 1.)); // Over-sized but otherwise equal. let multi_oversized = MultiPoint::new(vec![ point![x: 0., y: 0.], point![x: 10., y: 10.], point![x: 10., y:100.], ]); assert!(multi.relative_ne(&multi_oversized, 1., 1.)); } #[test] fn test_abs_diff_eq() { let delta = 1e-6; let multi = MultiPoint::new(vec![point![x: 0., y: 0.], point![x: 10., y: 10.]]); let multi_x = MultiPoint::new(vec![point![x: 0., y: 0.], point![x: 10.+delta, y: 10.]]); assert!(multi.abs_diff_eq(&multi_x, 1e-2)); assert!(multi.abs_diff_ne(&multi_x, 1e-12)); let multi_y = MultiPoint::new(vec![point![x: 0., y: 0.], point![x: 10., y: 10.+delta]]); assert!(multi.abs_diff_eq(&multi_y, 1e-2)); assert!(multi.abs_diff_ne(&multi_y, 1e-12)); // Under-sized but otherwise equal. let multi_undersized = MultiPoint::new(vec![point![x: 0., y: 0.]]); assert!(multi.abs_diff_ne(&multi_undersized, 1.)); // Over-sized but otherwise equal. let multi_oversized = MultiPoint::new(vec![ point![x: 0., y: 0.], point![x: 10., y: 10.], point![x: 10., y:100.], ]); assert!(multi.abs_diff_ne(&multi_oversized, 1.)); } } geo-types-0.7.11/src/geometry/multi_polygon.rs000064400000000000000000000167071046102023000175320ustar 00000000000000use crate::{CoordNum, Polygon}; use alloc::vec; use alloc::vec::Vec; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; use core::iter::FromIterator; /// A collection of [`Polygon`s](struct.Polygon.html). Can /// be created from a `Vec` of `Polygon`s, or from an /// Iterator which yields `Polygon`s. Iterating over this /// object yields the component `Polygon`s. /// /// # Semantics /// /// The _interior_ and the _boundary_ are the union of the /// interior and the boundary of the constituent polygons. /// /// # Validity /// /// - The interiors of no two constituent polygons may intersect. /// /// - The boundaries of two (distinct) constituent polygons /// may only intersect at finitely many points. /// /// Refer to section 6.1.14 of the OGC-SFA for a formal /// definition of validity. Note that the validity is not /// enforced, but expected by the operations and /// predicates that operate on it. #[derive(Eq, PartialEq, Clone, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct MultiPolygon(pub Vec>); impl>> From for MultiPolygon { fn from(x: IP) -> Self { Self(vec![x.into()]) } } impl>> From> for MultiPolygon { fn from(x: Vec) -> Self { Self(x.into_iter().map(|p| p.into()).collect()) } } impl>> FromIterator for MultiPolygon { fn from_iter>(iter: I) -> Self { Self(iter.into_iter().map(|p| p.into()).collect()) } } impl IntoIterator for MultiPolygon { type Item = Polygon; type IntoIter = ::alloc::vec::IntoIter>; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl<'a, T: CoordNum> IntoIterator for &'a MultiPolygon { type Item = &'a Polygon; type IntoIter = ::alloc::slice::Iter<'a, Polygon>; fn into_iter(self) -> Self::IntoIter { (self.0).iter() } } impl<'a, T: CoordNum> IntoIterator for &'a mut MultiPolygon { type Item = &'a mut Polygon; type IntoIter = ::alloc::slice::IterMut<'a, Polygon>; fn into_iter(self) -> Self::IntoIter { (self.0).iter_mut() } } impl MultiPolygon { /// Instantiate Self from the raw content value pub fn new(value: Vec>) -> Self { Self(value) } pub fn iter(&self) -> impl Iterator> { self.0.iter() } pub fn iter_mut(&mut self) -> impl Iterator> { self.0.iter_mut() } } #[cfg(any(feature = "approx", test))] impl RelativeEq for MultiPolygon where T: AbsDiffEq + CoordNum + RelativeEq, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } /// Equality assertion within a relative limit. /// /// # Examples /// /// ``` /// use geo_types::{polygon, Polygon, MultiPolygon}; /// /// let a_el: Polygon = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7., y: 9.), (x: 0., y: 0.)]; /// let a = MultiPolygon::new(vec![a_el]); /// let b_el: Polygon = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7.01, y: 9.), (x: 0., y: 0.)]; /// let b = MultiPolygon::new(vec![b_el]); /// /// approx::assert_relative_eq!(a, b, max_relative=0.1); /// approx::assert_relative_ne!(a, b, max_relative=0.001); /// ``` #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { if self.0.len() != other.0.len() { return false; } let mut mp_zipper = self.iter().zip(other.iter()); mp_zipper.all(|(lhs, rhs)| lhs.relative_eq(rhs, epsilon, max_relative)) } } #[cfg(any(feature = "approx", test))] impl AbsDiffEq for MultiPolygon where T: AbsDiffEq + CoordNum, T::Epsilon: Copy, { type Epsilon = T; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } /// Equality assertion with an absolute limit. /// /// # Examples /// /// ``` /// use geo_types::{polygon, Polygon, MultiPolygon}; /// /// let a_el: Polygon = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7., y: 9.), (x: 0., y: 0.)]; /// let a = MultiPolygon::new(vec![a_el]); /// let b_el: Polygon = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7.01, y: 9.), (x: 0., y: 0.)]; /// let b = MultiPolygon::new(vec![b_el]); /// /// approx::abs_diff_eq!(a, b, epsilon=0.1); /// approx::abs_diff_ne!(a, b, epsilon=0.001); /// ``` #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { if self.0.len() != other.0.len() { return false; } let mut mp_zipper = self.into_iter().zip(other.into_iter()); mp_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon)) } } #[cfg(test)] mod test { use super::*; use crate::polygon; #[test] fn test_iter() { let multi = MultiPolygon::new(vec![ polygon![(x: 0, y: 0), (x: 2, y: 0), (x: 1, y: 2), (x:0, y:0)], polygon![(x: 10, y: 10), (x: 12, y: 10), (x: 11, y: 12), (x:10, y:10)], ]); let mut first = true; for p in &multi { if first { assert_eq!( p, &polygon![(x: 0, y: 0), (x: 2, y: 0), (x: 1, y: 2), (x:0, y:0)] ); first = false; } else { assert_eq!( p, &polygon![(x: 10, y: 10), (x: 12, y: 10), (x: 11, y: 12), (x:10, y:10)] ); } } // Do it again to prove that `multi` wasn't `moved`. first = true; for p in &multi { if first { assert_eq!( p, &polygon![(x: 0, y: 0), (x: 2, y: 0), (x: 1, y: 2), (x:0, y:0)] ); first = false; } else { assert_eq!( p, &polygon![(x: 10, y: 10), (x: 12, y: 10), (x: 11, y: 12), (x:10, y:10)] ); } } } #[test] fn test_iter_mut() { let mut multi = MultiPolygon::new(vec![ polygon![(x: 0, y: 0), (x: 2, y: 0), (x: 1, y: 2), (x:0, y:0)], polygon![(x: 10, y: 10), (x: 12, y: 10), (x: 11, y: 12), (x:10, y:10)], ]); for poly in &mut multi { poly.exterior_mut(|exterior| { for coord in exterior { coord.x += 1; coord.y += 1; } }); } for poly in multi.iter_mut() { poly.exterior_mut(|exterior| { for coord in exterior { coord.x += 1; coord.y += 1; } }); } let mut first = true; for p in &multi { if first { assert_eq!( p, &polygon![(x: 2, y: 2), (x: 4, y: 2), (x: 3, y: 4), (x:2, y:2)] ); first = false; } else { assert_eq!( p, &polygon![(x: 12, y: 12), (x: 14, y: 12), (x: 13, y: 14), (x:12, y:12)] ); } } } } geo-types-0.7.11/src/geometry/point.rs000064400000000000000000000432631046102023000157570ustar 00000000000000use crate::{point, Coord, CoordFloat, CoordNum}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; /// A single point in 2D space. /// /// Points can be created using the [`Point::new`] constructor, /// the [`point!`] macro, or from a `Coord`, two-element /// tuples, or arrays – see the `From` impl section for a /// complete list. /// /// # Semantics /// /// The _interior_ of the point is itself (a singleton set), /// and its _boundary_ is empty. A point is _valid_ if and /// only if the `Coord` is valid. /// /// # Examples /// /// ``` /// use geo_types::{coord, Point}; /// let p1: Point = (0., 1.).into(); /// let c = coord! { x: 10., y: 20. }; /// let p2: Point = c.into(); /// ``` #[derive(Eq, PartialEq, Clone, Copy, Debug, Hash, Default)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Point(pub Coord); impl From> for Point { fn from(x: Coord) -> Self { Point(x) } } impl From<(T, T)> for Point { fn from(coords: (T, T)) -> Self { Point::new(coords.0, coords.1) } } impl From<[T; 2]> for Point { fn from(coords: [T; 2]) -> Self { Point::new(coords[0], coords[1]) } } impl From> for (T, T) { fn from(point: Point) -> Self { point.0.into() } } impl From> for [T; 2] { fn from(point: Point) -> Self { point.0.into() } } impl Point { /// Creates a new point. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let p = Point::new(1.234, 2.345); /// /// assert_eq!(p.x(), 1.234); /// assert_eq!(p.y(), 2.345); /// ``` pub fn new(x: T, y: T) -> Self { point! { x: x, y: y } } /// Returns the x/horizontal component of the point. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let p = Point::new(1.234, 2.345); /// /// assert_eq!(p.x(), 1.234); /// ``` pub fn x(self) -> T { self.0.x } /// Sets the x/horizontal component of the point. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let mut p = Point::new(1.234, 2.345); /// p.set_x(9.876); /// /// assert_eq!(p.x(), 9.876); /// ``` pub fn set_x(&mut self, x: T) -> &mut Self { self.0.x = x; self } /// Returns a mutable reference to the x/horizontal component of the point /// /// # Examples /// /// ``` /// use approx::assert_relative_eq; /// use geo_types::Point; /// let mut p = Point::new(1.234, 2.345); /// let mut p_x = p.x_mut(); /// *p_x += 1.0; /// assert_relative_eq!(p.x(), 2.234); /// ``` pub fn x_mut(&mut self) -> &mut T { &mut self.0.x } /// Returns the y/vertical component of the point. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let p = Point::new(1.234, 2.345); /// /// assert_eq!(p.y(), 2.345); /// ``` pub fn y(self) -> T { self.0.y } /// Sets the y/vertical component of the point. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let mut p = Point::new(1.234, 2.345); /// p.set_y(9.876); /// /// assert_eq!(p.y(), 9.876); /// ``` pub fn set_y(&mut self, y: T) -> &mut Self { self.0.y = y; self } /// Returns a mutable reference to the x/horizontal component of the point /// /// # Examples /// /// ``` /// use approx::assert_relative_eq; /// use geo_types::Point; /// let mut p = Point::new(1.234, 2.345); /// let mut p_y = p.y_mut(); /// *p_y += 1.0; /// assert_relative_eq!(p.y(), 3.345); /// ``` pub fn y_mut(&mut self) -> &mut T { &mut self.0.y } /// Returns a tuple that contains the x/horizontal & y/vertical component of the point. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let mut p = Point::new(1.234, 2.345); /// let (x, y) = p.x_y(); /// /// assert_eq!(y, 2.345); /// assert_eq!(x, 1.234); /// ``` pub fn x_y(self) -> (T, T) { (self.0.x, self.0.y) } /// Returns the longitude/horizontal component of the point. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let p = Point::new(1.234, 2.345); /// /// assert_eq!(p.x(), 1.234); /// ``` #[deprecated = "use `Point::x` instead, it's less ambiguous"] pub fn lng(self) -> T { self.x() } /// Sets the longitude/horizontal component of the point. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let mut p = Point::new(1.234, 2.345); /// #[allow(deprecated)] /// p.set_lng(9.876); /// /// assert_eq!(p.x(), 9.876); /// ``` #[deprecated = "use `Point::set_x` instead, it's less ambiguous"] pub fn set_lng(&mut self, lng: T) -> &mut Self { self.set_x(lng) } /// Returns the latitude/vertical component of the point. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let p = Point::new(1.234, 2.345); /// /// assert_eq!(p.y(), 2.345); /// ``` #[deprecated = "use `Point::y` instead, it's less ambiguous"] pub fn lat(self) -> T { self.y() } /// Sets the latitude/vertical component of the point. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let mut p = Point::new(1.234, 2.345); /// #[allow(deprecated)] /// p.set_lat(9.876); /// /// assert_eq!(p.y(), 9.876); /// ``` #[deprecated = "use `Point::set_y` instead, it's less ambiguous"] pub fn set_lat(&mut self, lat: T) -> &mut Self { self.set_y(lat) } } impl Point { /// Returns the dot product of the two points: /// `dot = x1 * x2 + y1 * y2` /// /// # Examples /// /// ``` /// use geo_types::{point, Point}; /// /// let point = point! { x: 1.5, y: 0.5 }; /// let dot = point.dot(point! { x: 2.0, y: 4.5 }); /// /// assert_eq!(dot, 5.25); /// ``` pub fn dot(self, other: Self) -> T { self.x() * other.x() + self.y() * other.y() } /// Returns the cross product of 3 points. A positive value implies /// `self` → `point_b` → `point_c` is counter-clockwise, negative implies /// clockwise. /// /// # Note on Robustness /// /// This function is **not** robust against floating-point errors. /// The [`geo`](https://docs.rs/geo) crate /// offers robust predicates for standard numeric types using the /// [`Kernel`](https://docs.rs/geo/algorithm/kernels/trait.Kernel.html) /// trait, and these should be preferred if possible. /// /// # Examples /// /// ``` /// use geo_types::point; /// /// let point_a = point! { x: 1., y: 2. }; /// let point_b = point! { x: 3., y: 5. }; /// let point_c = point! { x: 7., y: 12. }; /// /// let cross = point_a.cross_prod(point_b, point_c); /// /// assert_eq!(cross, 2.0) /// ``` pub fn cross_prod(self, point_b: Self, point_c: Self) -> T { (point_b.x() - self.x()) * (point_c.y() - self.y()) - (point_b.y() - self.y()) * (point_c.x() - self.x()) } } impl Point { /// Converts the (x,y) components of Point to degrees /// /// # Example /// ``` /// use geo_types::Point; /// /// let p = Point::new(1.234, 2.345); /// let (x, y): (f32, f32) = p.to_degrees().x_y(); /// assert_eq!(x.round(), 71.0); /// assert_eq!(y.round(), 134.0); /// ``` pub fn to_degrees(self) -> Self { let (x, y) = self.x_y(); let x = x.to_degrees(); let y = y.to_degrees(); Point::new(x, y) } /// Converts the (x,y) components of Point to radians /// /// # Example /// ``` /// use geo_types::Point; /// /// let p = Point::new(180.0, 341.5); /// let (x, y): (f32, f32) = p.to_radians().x_y(); /// assert_eq!(x.round(), 3.0); /// assert_eq!(y.round(), 6.0); /// ``` pub fn to_radians(self) -> Self { let (x, y) = self.x_y(); let x = x.to_radians(); let y = y.to_radians(); Point::new(x, y) } } impl Neg for Point where T: CoordNum + Neg, { type Output = Self; /// Returns a point with the x and y components negated. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let p = -Point::new(-1.25, 2.5); /// /// assert_eq!(p.x(), 1.25); /// assert_eq!(p.y(), -2.5); /// ``` fn neg(self) -> Self::Output { Point::from(-self.0) } } impl Add for Point { type Output = Self; /// Add a point to the given point. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let p = Point::new(1.25, 2.5) + Point::new(1.5, 2.5); /// /// assert_eq!(p.x(), 2.75); /// assert_eq!(p.y(), 5.0); /// ``` fn add(self, rhs: Self) -> Self::Output { Point::from(self.0 + rhs.0) } } impl AddAssign for Point { /// Add a point to the given point and assign it to the original point. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let mut p = Point::new(1.25, 2.5); /// p += Point::new(1.5, 2.5); /// /// assert_eq!(p.x(), 2.75); /// assert_eq!(p.y(), 5.0); /// ``` fn add_assign(&mut self, rhs: Self) { self.0 = self.0 + rhs.0; } } impl Sub for Point { type Output = Self; /// Subtract a point from the given point. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let p = Point::new(1.25, 3.0) - Point::new(1.5, 2.5); /// /// assert_eq!(p.x(), -0.25); /// assert_eq!(p.y(), 0.5); /// ``` fn sub(self, rhs: Self) -> Self::Output { Point::from(self.0 - rhs.0) } } impl SubAssign for Point { /// Subtract a point from the given point and assign it to the original point. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let mut p = Point::new(1.25, 2.5); /// p -= Point::new(1.5, 2.5); /// /// assert_eq!(p.x(), -0.25); /// assert_eq!(p.y(), 0.0); /// ``` fn sub_assign(&mut self, rhs: Self) { self.0 = self.0 - rhs.0; } } impl Mul for Point { type Output = Self; /// Scaler multiplication of a point /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let p = Point::new(2.0, 3.0) * 2.0; /// /// assert_eq!(p.x(), 4.0); /// assert_eq!(p.y(), 6.0); /// ``` fn mul(self, rhs: T) -> Self::Output { Point::from(self.0 * rhs) } } impl MulAssign for Point { /// Scaler multiplication of a point in place /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let mut p = Point::new(2.0, 3.0); /// p *= 2.0; /// /// assert_eq!(p.x(), 4.0); /// assert_eq!(p.y(), 6.0); /// ``` fn mul_assign(&mut self, rhs: T) { self.0 = self.0 * rhs } } impl Div for Point { type Output = Self; /// Scaler division of a point /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let p = Point::new(2.0, 3.0) / 2.0; /// /// assert_eq!(p.x(), 1.0); /// assert_eq!(p.y(), 1.5); /// ``` fn div(self, rhs: T) -> Self::Output { Point::from(self.0 / rhs) } } impl DivAssign for Point { /// Scaler division of a point in place /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let mut p = Point::new(2.0, 3.0); /// p /= 2.0; /// /// assert_eq!(p.x(), 1.0); /// assert_eq!(p.y(), 1.5); /// ``` fn div_assign(&mut self, rhs: T) { self.0 = self.0 / rhs } } #[cfg(any(feature = "approx", test))] impl RelativeEq for Point where T: AbsDiffEq + CoordNum + RelativeEq, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } /// Equality assertion within a relative limit. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let a = Point::new(2.0, 3.0); /// let b = Point::new(2.0, 3.01); /// /// approx::assert_relative_eq!(a, b, max_relative=0.1) /// ``` #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.0.relative_eq(&other.0, epsilon, max_relative) } } #[cfg(any(feature = "approx", test))] impl AbsDiffEq for Point where T: AbsDiffEq + CoordNum, T::Epsilon: Copy, { type Epsilon = T::Epsilon; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } /// Equality assertion with an absolute limit. /// /// # Examples /// /// ``` /// use geo_types::Point; /// /// let a = Point::new(2.0, 3.0); /// let b = Point::new(2.0, 3.0000001); /// /// approx::assert_relative_eq!(a, b, epsilon=0.1) /// ``` #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.0.abs_diff_eq(&other.0, epsilon) } } #[cfg(feature = "rstar_0_8")] // These are required for rstar RTree impl ::rstar_0_8::Point for Point where T: ::num_traits::Float + ::rstar_0_8::RTreeNum, { type Scalar = T; const DIMENSIONS: usize = 2; fn generate(generator: impl Fn(usize) -> Self::Scalar) -> Self { Point::new(generator(0), generator(1)) } fn nth(&self, index: usize) -> Self::Scalar { match index { 0 => self.0.x, 1 => self.0.y, _ => unreachable!(), } } fn nth_mut(&mut self, index: usize) -> &mut Self::Scalar { match index { 0 => &mut self.0.x, 1 => &mut self.0.y, _ => unreachable!(), } } } #[cfg(feature = "rstar_0_9")] impl ::rstar_0_9::Point for Point where T: ::num_traits::Float + ::rstar_0_9::RTreeNum, { type Scalar = T; const DIMENSIONS: usize = 2; fn generate(mut generator: impl FnMut(usize) -> Self::Scalar) -> Self { Point::new(generator(0), generator(1)) } fn nth(&self, index: usize) -> Self::Scalar { match index { 0 => self.0.x, 1 => self.0.y, _ => unreachable!(), } } fn nth_mut(&mut self, index: usize) -> &mut Self::Scalar { match index { 0 => &mut self.0.x, 1 => &mut self.0.y, _ => unreachable!(), } } } #[cfg(feature = "rstar_0_10")] impl ::rstar_0_10::Point for Point where T: ::num_traits::Float + ::rstar_0_10::RTreeNum, { type Scalar = T; const DIMENSIONS: usize = 2; fn generate(mut generator: impl FnMut(usize) -> Self::Scalar) -> Self { Point::new(generator(0), generator(1)) } fn nth(&self, index: usize) -> Self::Scalar { match index { 0 => self.0.x, 1 => self.0.y, _ => unreachable!(), } } fn nth_mut(&mut self, index: usize) -> &mut Self::Scalar { match index { 0 => &mut self.0.x, 1 => &mut self.0.y, _ => unreachable!(), } } } #[cfg(feature = "rstar_0_11")] impl ::rstar_0_11::Point for Point where T: ::num_traits::Float + ::rstar_0_11::RTreeNum, { type Scalar = T; const DIMENSIONS: usize = 2; fn generate(mut generator: impl FnMut(usize) -> Self::Scalar) -> Self { Point::new(generator(0), generator(1)) } fn nth(&self, index: usize) -> Self::Scalar { match index { 0 => self.0.x, 1 => self.0.y, _ => unreachable!(), } } fn nth_mut(&mut self, index: usize) -> &mut Self::Scalar { match index { 0 => &mut self.0.x, 1 => &mut self.0.y, _ => unreachable!(), } } } #[cfg(test)] mod test { use super::*; use approx::AbsDiffEq; #[test] fn test_abs_diff_eq() { let delta = 1e-6; let p = Point::new(1.0, 1.0); let p_x = Point::new(1.0 - delta, 1.0); assert!(p.abs_diff_eq(&p_x, 1e-2)); assert!(p.abs_diff_ne(&p_x, 1e-12)); let p_y = Point::new(1.0, 1.0 + delta); assert!(p.abs_diff_eq(&p_y, 1e-2)); assert!(p.abs_diff_ne(&p_y, 1e-12)); let p_xy = Point::new(1.0 + delta, 1.0 - delta); assert!(p.abs_diff_eq(&p_xy, 1e-2)); assert!(p.abs_diff_ne(&p_xy, 1e-12)); let p_inf = Point::new(f64::INFINITY, 1.); assert!(p.abs_diff_ne(&p_inf, 1e-2)); } #[test] fn test_relative_eq() { let delta = 1e-6; let p = Point::new(1.0, 1.0); let p_x = Point::new(1.0 - delta, 1.0); assert!(p.relative_eq(&p_x, 1e-2, 1e-2)); assert!(p.relative_ne(&p_x, 1e-12, 1e-12)); let p_y = Point::new(1.0, 1.0 + delta); assert!(p.relative_eq(&p_y, 1e-2, 1e-2)); assert!(p.relative_ne(&p_y, 1e-12, 1e-12)); let p_xy = Point::new(1.0 + delta, 1.0 - delta); assert!(p.relative_eq(&p_xy, 1e-2, 1e-2)); assert!(p.relative_ne(&p_xy, 1e-12, 1e-12)); let p_inf = Point::new(f64::INFINITY, 1.); assert!(p.relative_ne(&p_inf, 1e-2, 1e-2)); } } geo-types-0.7.11/src/geometry/polygon.rs000064400000000000000000000423021046102023000163060ustar 00000000000000use crate::{CoordFloat, CoordNum, LineString, Point, Rect, Triangle}; use alloc::vec; use alloc::vec::Vec; use num_traits::{Float, Signed}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; /// A bounded two-dimensional area. /// /// A `Polygon`’s outer boundary (_exterior ring_) is represented by a /// [`LineString`]. It may contain zero or more holes (_interior rings_), also /// represented by `LineString`s. /// /// A `Polygon` can be created with the [`Polygon::new`] constructor or the [`polygon!`][`crate::polygon!`] macro. /// /// # Semantics /// /// The _boundary_ of the polygon is the union of the /// boundaries of the exterior and interiors. The interior /// is all the points inside the polygon (not on the /// boundary). /// /// The `Polygon` structure guarantees that all exterior and interior rings will /// be _closed_, such that the first and last `Coord` of each ring has /// the same value. /// /// # Validity /// /// - The exterior and interior rings must be valid /// `LinearRing`s (see [`LineString`]). /// /// - No two rings in the boundary may cross, and may /// intersect at a `Point` only as a tangent. In other /// words, the rings must be distinct, and for every pair of /// common points in two of the rings, there must be a /// neighborhood (a topological open set) around one that /// does not contain the other point. /// /// - The closure of the interior of the `Polygon` must /// equal the `Polygon` itself. For instance, the exterior /// may not contain a spike. /// /// - The interior of the polygon must be a connected /// point-set. That is, any two distinct points in the /// interior must admit a curve between these two that lies /// in the interior. /// /// Refer to section 6.1.11.1 of the OGC-SFA for a formal /// definition of validity. Besides the closed `LineString` /// guarantee, the `Polygon` structure does not enforce /// validity at this time. For example, it is possible to /// construct a `Polygon` that has: /// /// - fewer than 3 coordinates per `LineString` ring /// - interior rings that intersect other interior rings /// - interior rings that extend beyond the exterior ring /// /// # `LineString` closing operation /// /// Some APIs on `Polygon` result in a closing operation on a `LineString`. The /// operation is as follows: /// /// If a `LineString`’s first and last `Coord` have different values, a /// new `Coord` will be appended to the `LineString` with a value equal to /// the first `Coord`. /// /// [`LineString`]: line_string/struct.LineString.html #[derive(Eq, PartialEq, Clone, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Polygon { exterior: LineString, interiors: Vec>, } impl Polygon { /// Create a new `Polygon` with the provided exterior `LineString` ring and /// interior `LineString` rings. /// /// Upon calling `new`, the exterior and interior `LineString` rings [will /// be closed]. /// /// [will be closed]: #linestring-closing-operation /// /// # Examples /// /// Creating a `Polygon` with no interior rings: /// /// ``` /// use geo_types::{LineString, Polygon}; /// /// let polygon = Polygon::new( /// LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.)]), /// vec![], /// ); /// ``` /// /// Creating a `Polygon` with an interior ring: /// /// ``` /// use geo_types::{LineString, Polygon}; /// /// let polygon = Polygon::new( /// LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.)]), /// vec![LineString::from(vec![ /// (0.1, 0.1), /// (0.9, 0.9), /// (0.9, 0.1), /// (0.1, 0.1), /// ])], /// ); /// ``` /// /// If the first and last `Coord`s of the exterior or interior /// `LineString`s no longer match, those `LineString`s [will be closed]: /// /// ``` /// use geo_types::{coord, LineString, Polygon}; /// /// let mut polygon = Polygon::new(LineString::from(vec![(0., 0.), (1., 1.), (1., 0.)]), vec![]); /// /// assert_eq!( /// polygon.exterior(), /// &LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.),]) /// ); /// ``` pub fn new(mut exterior: LineString, mut interiors: Vec>) -> Self { exterior.close(); for interior in &mut interiors { interior.close(); } Self { exterior, interiors, } } /// Consume the `Polygon`, returning the exterior `LineString` ring and /// a vector of the interior `LineString` rings. /// /// # Examples /// /// ``` /// use geo_types::{LineString, Polygon}; /// /// let mut polygon = Polygon::new( /// LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.)]), /// vec![LineString::from(vec![ /// (0.1, 0.1), /// (0.9, 0.9), /// (0.9, 0.1), /// (0.1, 0.1), /// ])], /// ); /// /// let (exterior, interiors) = polygon.into_inner(); /// /// assert_eq!( /// exterior, /// LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.),]) /// ); /// /// assert_eq!( /// interiors, /// vec![LineString::from(vec![ /// (0.1, 0.1), /// (0.9, 0.9), /// (0.9, 0.1), /// (0.1, 0.1), /// ])] /// ); /// ``` pub fn into_inner(self) -> (LineString, Vec>) { (self.exterior, self.interiors) } /// Return a reference to the exterior `LineString` ring. /// /// # Examples /// /// ``` /// use geo_types::{LineString, Polygon}; /// /// let exterior = LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.)]); /// /// let polygon = Polygon::new(exterior.clone(), vec![]); /// /// assert_eq!(polygon.exterior(), &exterior); /// ``` pub fn exterior(&self) -> &LineString { &self.exterior } /// Execute the provided closure `f`, which is provided with a mutable /// reference to the exterior `LineString` ring. /// /// After the closure executes, the exterior `LineString` [will be closed]. /// /// # Examples /// /// ``` /// use geo_types::{coord, LineString, Polygon}; /// /// let mut polygon = Polygon::new( /// LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.)]), /// vec![], /// ); /// /// polygon.exterior_mut(|exterior| { /// exterior.0[1] = coord! { x: 1., y: 2. }; /// }); /// /// assert_eq!( /// polygon.exterior(), /// &LineString::from(vec![(0., 0.), (1., 2.), (1., 0.), (0., 0.),]) /// ); /// ``` /// /// If the first and last `Coord`s of the exterior `LineString` no /// longer match, the `LineString` [will be closed]: /// /// ``` /// use geo_types::{coord, LineString, Polygon}; /// /// let mut polygon = Polygon::new( /// LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.)]), /// vec![], /// ); /// /// polygon.exterior_mut(|exterior| { /// exterior.0[0] = coord! { x: 0., y: 1. }; /// }); /// /// assert_eq!( /// polygon.exterior(), /// &LineString::from(vec![(0., 1.), (1., 1.), (1., 0.), (0., 0.), (0., 1.),]) /// ); /// ``` /// /// [will be closed]: #linestring-closing-operation pub fn exterior_mut(&mut self, f: F) where F: FnOnce(&mut LineString), { f(&mut self.exterior); self.exterior.close(); } /// Return a slice of the interior `LineString` rings. /// /// # Examples /// /// ``` /// use geo_types::{coord, LineString, Polygon}; /// /// let interiors = vec![LineString::from(vec![ /// (0.1, 0.1), /// (0.9, 0.9), /// (0.9, 0.1), /// (0.1, 0.1), /// ])]; /// /// let polygon = Polygon::new( /// LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.)]), /// interiors.clone(), /// ); /// /// assert_eq!(interiors, polygon.interiors()); /// ``` pub fn interiors(&self) -> &[LineString] { &self.interiors } /// Execute the provided closure `f`, which is provided with a mutable /// reference to the interior `LineString` rings. /// /// After the closure executes, each of the interior `LineString`s [will be /// closed]. /// /// # Examples /// /// ``` /// use geo_types::{coord, LineString, Polygon}; /// /// let mut polygon = Polygon::new( /// LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.)]), /// vec![LineString::from(vec![ /// (0.1, 0.1), /// (0.9, 0.9), /// (0.9, 0.1), /// (0.1, 0.1), /// ])], /// ); /// /// polygon.interiors_mut(|interiors| { /// interiors[0].0[1] = coord! { x: 0.8, y: 0.8 }; /// }); /// /// assert_eq!( /// polygon.interiors(), /// &[LineString::from(vec![ /// (0.1, 0.1), /// (0.8, 0.8), /// (0.9, 0.1), /// (0.1, 0.1), /// ])] /// ); /// ``` /// /// If the first and last `Coord`s of any interior `LineString` no /// longer match, those `LineString`s [will be closed]: /// /// ``` /// use geo_types::{coord, LineString, Polygon}; /// /// let mut polygon = Polygon::new( /// LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.)]), /// vec![LineString::from(vec![ /// (0.1, 0.1), /// (0.9, 0.9), /// (0.9, 0.1), /// (0.1, 0.1), /// ])], /// ); /// /// polygon.interiors_mut(|interiors| { /// interiors[0].0[0] = coord! { x: 0.1, y: 0.2 }; /// }); /// /// assert_eq!( /// polygon.interiors(), /// &[LineString::from(vec![ /// (0.1, 0.2), /// (0.9, 0.9), /// (0.9, 0.1), /// (0.1, 0.1), /// (0.1, 0.2), /// ])] /// ); /// ``` /// /// [will be closed]: #linestring-closing-operation pub fn interiors_mut(&mut self, f: F) where F: FnOnce(&mut [LineString]), { f(&mut self.interiors); for interior in &mut self.interiors { interior.close(); } } /// Add an interior ring to the `Polygon`. /// /// The new `LineString` interior ring [will be closed]: /// /// # Examples /// /// ``` /// use geo_types::{coord, LineString, Polygon}; /// /// let mut polygon = Polygon::new( /// LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.)]), /// vec![], /// ); /// /// assert_eq!(polygon.interiors().len(), 0); /// /// polygon.interiors_push(vec![(0.1, 0.1), (0.9, 0.9), (0.9, 0.1)]); /// /// assert_eq!( /// polygon.interiors(), /// &[LineString::from(vec![ /// (0.1, 0.1), /// (0.9, 0.9), /// (0.9, 0.1), /// (0.1, 0.1), /// ])] /// ); /// ``` /// /// [will be closed]: #linestring-closing-operation pub fn interiors_push(&mut self, new_interior: impl Into>) { let mut new_interior = new_interior.into(); new_interior.close(); self.interiors.push(new_interior); } /// Wrap-around previous-vertex fn previous_vertex(&self, current_vertex: usize) -> usize where T: Float, { (current_vertex + (self.exterior.0.len() - 1) - 1) % (self.exterior.0.len() - 1) } } // used to check the sign of a vec of floats #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] enum ListSign { Empty, Positive, Negative, Mixed, } impl Polygon { /// Determine whether a Polygon is convex // For each consecutive pair of edges of the polygon (each triplet of points), // compute the z-component of the cross product of the vectors defined by the // edges pointing towards the points in increasing order. // Take the cross product of these vectors // The polygon is convex if the z-components of the cross products are either // all positive or all negative. Otherwise, the polygon is non-convex. // see: http://stackoverflow.com/a/1881201/416626 #[deprecated( since = "0.6.1", note = "Please use `geo::is_convex` on `poly.exterior()` instead" )] pub fn is_convex(&self) -> bool { let convex = self .exterior .0 .iter() .enumerate() .map(|(idx, _)| { let prev_1 = self.previous_vertex(idx); let prev_2 = self.previous_vertex(prev_1); Point::from(self.exterior[prev_2]).cross_prod( Point::from(self.exterior[prev_1]), Point::from(self.exterior[idx]), ) }) // accumulate and check cross-product result signs in a single pass // positive implies ccw convexity, negative implies cw convexity // anything else implies non-convexity .fold(ListSign::Empty, |acc, n| match (acc, n.is_positive()) { (ListSign::Empty, true) | (ListSign::Positive, true) => ListSign::Positive, (ListSign::Empty, false) | (ListSign::Negative, false) => ListSign::Negative, _ => ListSign::Mixed, }); convex != ListSign::Mixed } } impl From> for Polygon { fn from(r: Rect) -> Self { Polygon::new( vec![ (r.min().x, r.min().y), (r.max().x, r.min().y), (r.max().x, r.max().y), (r.min().x, r.max().y), (r.min().x, r.min().y), ] .into(), Vec::new(), ) } } impl From> for Polygon { fn from(t: Triangle) -> Self { Polygon::new(vec![t.0, t.1, t.2, t.0].into(), Vec::new()) } } #[cfg(any(feature = "approx", test))] impl RelativeEq for Polygon where T: AbsDiffEq + CoordNum + RelativeEq, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } /// Equality assertion within a relative limit. /// /// # Examples /// /// ``` /// use geo_types::{Polygon, polygon}; /// /// let a: Polygon = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7., y: 9.), (x: 0., y: 0.)]; /// let b: Polygon = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7.01, y: 9.), (x: 0., y: 0.)]; /// /// approx::assert_relative_eq!(a, b, max_relative=0.1); /// approx::assert_relative_ne!(a, b, max_relative=0.001); /// ``` /// fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { if !self .exterior .relative_eq(&other.exterior, epsilon, max_relative) { return false; } if self.interiors.len() != other.interiors.len() { return false; } let mut zipper = self.interiors.iter().zip(other.interiors.iter()); zipper.all(|(lhs, rhs)| lhs.relative_eq(rhs, epsilon, max_relative)) } } #[cfg(any(feature = "approx", test))] impl + CoordNum> AbsDiffEq for Polygon { type Epsilon = T; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } /// Equality assertion with an absolute limit. /// /// # Examples /// /// ``` /// use geo_types::{Polygon, polygon}; /// /// let a: Polygon = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7., y: 9.), (x: 0., y: 0.)]; /// let b: Polygon = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7.01, y: 9.), (x: 0., y: 0.)]; /// /// approx::assert_abs_diff_eq!(a, b, epsilon=0.1); /// approx::assert_abs_diff_ne!(a, b, epsilon=0.001); /// ``` fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { if !self.exterior.abs_diff_eq(&other.exterior, epsilon) { return false; } if self.interiors.len() != other.interiors.len() { return false; } let mut zipper = self.interiors.iter().zip(other.interiors.iter()); zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon)) } } #[cfg(any( feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10", feature = "rstar_0_11" ))] macro_rules! impl_rstar_polygon { ($rstar:ident) => { impl $rstar::RTreeObject for Polygon where T: ::num_traits::Float + ::$rstar::RTreeNum, { type Envelope = ::$rstar::AABB>; fn envelope(&self) -> Self::Envelope { self.exterior.envelope() } } }; } #[cfg(feature = "rstar_0_8")] impl_rstar_polygon!(rstar_0_8); #[cfg(feature = "rstar_0_9")] impl_rstar_polygon!(rstar_0_9); #[cfg(feature = "rstar_0_10")] impl_rstar_polygon!(rstar_0_10); #[cfg(feature = "rstar_0_11")] impl_rstar_polygon!(rstar_0_11); geo-types-0.7.11/src/geometry/rect.rs000064400000000000000000000326251046102023000155630ustar 00000000000000use crate::{coord, polygon, Coord, CoordFloat, CoordNum, Line, Polygon}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; /// An _axis-aligned_ bounded 2D rectangle whose area is /// defined by minimum and maximum `Coord`s. /// /// The constructors and setters ensure the maximum /// `Coord` is greater than or equal to the minimum. /// Thus, a `Rect`s width, height, and area is guaranteed to /// be greater than or equal to zero. /// /// **Note.** While `Rect` implements `MapCoords` and /// `RotatePoint` algorithmic traits, the usage is expected /// to maintain the axis alignment. In particular, only /// rotation by integer multiples of 90 degrees, will /// preserve the original shape. In other cases, the min, /// and max points are rotated or transformed, and a new /// rectangle is created (with coordinate swaps to ensure /// min < max). /// /// # Examples /// /// ``` /// use geo_types::{coord, Rect}; /// /// let rect = Rect::new( /// coord! { x: 0., y: 4.}, /// coord! { x: 3., y: 10.}, /// ); /// /// assert_eq!(3., rect.width()); /// assert_eq!(6., rect.height()); /// assert_eq!( /// coord! { x: 1.5, y: 7. }, /// rect.center() /// ); /// ``` #[derive(Eq, PartialEq, Clone, Copy, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Rect { min: Coord, max: Coord, } impl Rect { /// Creates a new rectangle from two corner coordinates. /// /// # Examples /// /// ``` /// use geo_types::{coord, Rect}; /// /// let rect = Rect::new( /// coord! { x: 10., y: 20. }, /// coord! { x: 30., y: 10. } /// ); /// assert_eq!(rect.min(), coord! { x: 10., y: 10. }); /// assert_eq!(rect.max(), coord! { x: 30., y: 20. }); /// ``` pub fn new(c1: C, c2: C) -> Self where C: Into>, { let c1 = c1.into(); let c2 = c2.into(); let (min_x, max_x) = if c1.x < c2.x { (c1.x, c2.x) } else { (c2.x, c1.x) }; let (min_y, max_y) = if c1.y < c2.y { (c1.y, c2.y) } else { (c2.y, c1.y) }; Self { min: coord! { x: min_x, y: min_y }, max: coord! { x: max_x, y: max_y }, } } #[deprecated( since = "0.6.2", note = "Use `Rect::new` instead, since `Rect::try_new` will never Error" )] #[allow(deprecated)] pub fn try_new(c1: C, c2: C) -> Result, InvalidRectCoordinatesError> where C: Into>, { Ok(Rect::new(c1, c2)) } /// Returns the minimum `Coord` of the `Rect`. /// /// # Examples /// /// ```rust /// use geo_types::{coord, Rect}; /// /// let rect = Rect::new( /// coord! { x: 5., y: 5. }, /// coord! { x: 15., y: 15. }, /// ); /// /// assert_eq!(rect.min(), coord! { x: 5., y: 5. }); /// ``` pub fn min(self) -> Coord { self.min } /// Set the `Rect`’s minimum coordinate. /// /// # Panics /// /// Panics if `min`’s x/y is greater than the maximum coordinate’s x/y. pub fn set_min(&mut self, min: C) where C: Into>, { self.min = min.into(); self.assert_valid_bounds(); } /// Returns the maximum `Coord` of the `Rect`. /// /// # Examples /// /// ```rust /// use geo_types::{coord, Rect}; /// /// let rect = Rect::new( /// coord! { x: 5., y: 5. }, /// coord! { x: 15., y: 15. }, /// ); /// /// assert_eq!(rect.max(), coord! { x: 15., y: 15. }); /// ``` pub fn max(self) -> Coord { self.max } /// Set the `Rect`’s maximum coordinate. /// /// # Panics /// /// Panics if `max`’s x/y is less than the minimum coordinate’s x/y. pub fn set_max(&mut self, max: C) where C: Into>, { self.max = max.into(); self.assert_valid_bounds(); } /// Returns the width of the `Rect`. /// /// # Examples /// /// ```rust /// use geo_types::{coord, Rect}; /// /// let rect = Rect::new( /// coord! { x: 5., y: 5. }, /// coord! { x: 15., y: 15. }, /// ); /// /// assert_eq!(rect.width(), 10.); /// ``` pub fn width(self) -> T { self.max().x - self.min().x } /// Returns the height of the `Rect`. /// /// # Examples /// /// ```rust /// use geo_types::{coord, Rect}; /// /// let rect = Rect::new( /// coord! { x: 5., y: 5. }, /// coord! { x: 15., y: 15. }, /// ); /// /// assert_eq!(rect.height(), 10.); /// ``` pub fn height(self) -> T { self.max().y - self.min().y } /// Create a `Polygon` from the `Rect`. /// /// # Examples /// /// ```rust /// use geo_types::{coord, Rect, polygon}; /// /// let rect = Rect::new( /// coord! { x: 0., y: 0. }, /// coord! { x: 1., y: 2. }, /// ); /// /// assert_eq!( /// rect.to_polygon(), /// polygon![ /// (x: 0., y: 0.), /// (x: 0., y: 2.), /// (x: 1., y: 2.), /// (x: 1., y: 0.), /// (x: 0., y: 0.), /// ], /// ); /// ``` pub fn to_polygon(self) -> Polygon { polygon![ (x: self.min.x, y: self.min.y), (x: self.min.x, y: self.max.y), (x: self.max.x, y: self.max.y), (x: self.max.x, y: self.min.y), (x: self.min.x, y: self.min.y), ] } pub fn to_lines(&self) -> [Line; 4] { [ Line::new( coord! { x: self.min.x, y: self.min.y, }, coord! { x: self.min.x, y: self.max.y, }, ), Line::new( coord! { x: self.min.x, y: self.max.y, }, coord! { x: self.max.x, y: self.max.y, }, ), Line::new( coord! { x: self.max.x, y: self.max.y, }, coord! { x: self.max.x, y: self.min.y, }, ), Line::new( coord! { x: self.max.x, y: self.min.y, }, coord! { x: self.min.x, y: self.min.y, }, ), ] } /// Split a rectangle into two rectangles along the X-axis with equal widths. /// /// # Examples /// /// ``` /// let rect = geo_types::Rect::new( /// geo_types::coord! { x: 0., y: 0. }, /// geo_types::coord! { x: 4., y: 4. }, /// ); /// /// let [rect1, rect2] = rect.split_x(); /// /// assert_eq!( /// geo_types::Rect::new( /// geo_types::coord! { x: 0., y: 0. }, /// geo_types::coord! { x: 2., y: 4. }, /// ), /// rect1, /// ); /// assert_eq!( /// geo_types::Rect::new( /// geo_types::coord! { x: 2., y: 0. }, /// geo_types::coord! { x: 4., y: 4. }, /// ), /// rect2, /// ); /// ``` pub fn split_x(self) -> [Rect; 2] { let two = T::one() + T::one(); let mid_x = self.min().x + self.width() / two; [ Rect::new(self.min(), coord! { x: mid_x, y: self.max().y }), Rect::new(coord! { x: mid_x, y: self.min().y }, self.max()), ] } /// Split a rectangle into two rectangles along the Y-axis with equal heights. /// /// # Examples /// /// ``` /// let rect = geo_types::Rect::new( /// geo_types::coord! { x: 0., y: 0. }, /// geo_types::coord! { x: 4., y: 4. }, /// ); /// /// let [rect1, rect2] = rect.split_y(); /// /// assert_eq!( /// geo_types::Rect::new( /// geo_types::coord! { x: 0., y: 0. }, /// geo_types::coord! { x: 4., y: 2. }, /// ), /// rect1, /// ); /// assert_eq!( /// geo_types::Rect::new( /// geo_types::coord! { x: 0., y: 2. }, /// geo_types::coord! { x: 4., y: 4. }, /// ), /// rect2, /// ); /// ``` pub fn split_y(self) -> [Rect; 2] { let two = T::one() + T::one(); let mid_y = self.min().y + self.height() / two; [ Rect::new(self.min(), coord! { x: self.max().x, y: mid_y }), Rect::new(coord! { x: self.min().x, y: mid_y }, self.max()), ] } fn assert_valid_bounds(&self) { if !self.has_valid_bounds() { panic!("{}", RECT_INVALID_BOUNDS_ERROR); } } fn has_valid_bounds(&self) -> bool { self.min.x <= self.max.x && self.min.y <= self.max.y } } impl Rect { /// Returns the center `Coord` of the `Rect`. /// /// # Examples /// /// ```rust /// use geo_types::{coord, Rect}; /// /// let rect = Rect::new( /// coord! { x: 5., y: 5. }, /// coord! { x: 15., y: 15. }, /// ); /// /// assert_eq!(rect.center(), coord! { x: 10., y: 10. }); /// ``` pub fn center(self) -> Coord { let two = T::one() + T::one(); coord! { x: (self.max.x + self.min.x) / two, y: (self.max.y + self.min.y) / two, } } } static RECT_INVALID_BOUNDS_ERROR: &str = "Failed to create Rect: 'min' coordinate's x/y value must be smaller or equal to the 'max' x/y value"; #[cfg(any(feature = "approx", test))] impl RelativeEq for Rect where T: AbsDiffEq + CoordNum + RelativeEq, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } /// Equality assertion within a relative limit. /// /// # Examples /// /// ``` /// use geo_types::Rect; /// /// let a = Rect::new((0.0, 0.0), (10.0, 10.0)); /// let b = Rect::new((0.0, 0.0), (10.01, 10.0)); /// /// approx::assert_relative_eq!(a, b, max_relative=0.1); /// approx::assert_relative_ne!(a, b, max_relative=0.0001); /// ``` #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { if !self.min.relative_eq(&other.min, epsilon, max_relative) { return false; } if !self.max.relative_eq(&other.max, epsilon, max_relative) { return false; } true } } #[cfg(any(feature = "approx", test))] impl AbsDiffEq for Rect where T: AbsDiffEq + CoordNum, T::Epsilon: Copy, { type Epsilon = T; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } /// Equality assertion with an absolute limit. /// /// # Examples /// /// ``` /// use geo_types::{point, Rect}; /// /// let a = Rect::new((0.0, 0.0), (10.0, 10.0)); /// let b = Rect::new((0.0, 0.0), (10.01, 10.0)); /// /// approx::abs_diff_eq!(a, b, epsilon=0.1); /// approx::abs_diff_ne!(a, b, epsilon=0.001); /// ``` #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { if !self.min.abs_diff_eq(&other.min, epsilon) { return false; } if !self.max.abs_diff_eq(&other.max, epsilon) { return false; } true } } #[deprecated( since = "0.6.2", note = "Use `Rect::new` instead, since `Rect::try_new` will never Error" )] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct InvalidRectCoordinatesError; #[cfg(feature = "std")] #[allow(deprecated)] impl std::error::Error for InvalidRectCoordinatesError {} #[allow(deprecated)] impl core::fmt::Display for InvalidRectCoordinatesError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{RECT_INVALID_BOUNDS_ERROR}") } } #[cfg(test)] mod test { use super::*; use crate::coord; #[test] fn rect() { let rect = Rect::new((10, 10), (20, 20)); assert_eq!(rect.min, coord! { x: 10, y: 10 }); assert_eq!(rect.max, coord! { x: 20, y: 20 }); let rect = Rect::new((20, 20), (10, 10)); assert_eq!(rect.min, coord! { x: 10, y: 10 }); assert_eq!(rect.max, coord! { x: 20, y: 20 }); let rect = Rect::new((10, 20), (20, 10)); assert_eq!(rect.min, coord! { x: 10, y: 10 }); assert_eq!(rect.max, coord! { x: 20, y: 20 }); } #[test] fn rect_width() { let rect = Rect::new((10, 10), (20, 20)); assert_eq!(rect.width(), 10); } #[test] fn rect_height() { let rect = Rect::new((10., 10.), (20., 20.)); assert_relative_eq!(rect.height(), 10.); } #[test] fn rect_center() { assert_relative_eq!( Rect::new((0., 10.), (10., 90.)).center(), Coord::from((5., 50.)) ); assert_relative_eq!( Rect::new((-42., -42.), (42., 42.)).center(), Coord::from((0., 0.)) ); assert_relative_eq!( Rect::new((0., 0.), (0., 0.)).center(), Coord::from((0., 0.)) ); } } geo-types-0.7.11/src/geometry/triangle.rs000064400000000000000000000101071046102023000164220ustar 00000000000000use crate::{polygon, Coord, CoordNum, Line, Polygon}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; /// A bounded 2D area whose three vertices are defined by /// `Coord`s. The semantics and validity are that of /// the equivalent [`Polygon`]; in addition, the three /// vertices must not be collinear and they must be distinct. #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Triangle(pub Coord, pub Coord, pub Coord); impl Triangle { /// Instantiate Self from the raw content value pub fn new(v1: Coord, v2: Coord, v3: Coord) -> Self { Self(v1, v2, v3) } pub fn to_array(&self) -> [Coord; 3] { [self.0, self.1, self.2] } pub fn to_lines(&self) -> [Line; 3] { [ Line::new(self.0, self.1), Line::new(self.1, self.2), Line::new(self.2, self.0), ] } /// Create a `Polygon` from the `Triangle`. /// /// # Examples /// /// ```rust /// use geo_types::{coord, Triangle, polygon}; /// /// let triangle = Triangle::new( /// coord! { x: 0., y: 0. }, /// coord! { x: 10., y: 20. }, /// coord! { x: 20., y: -10. }, /// ); /// /// assert_eq!( /// triangle.to_polygon(), /// polygon![ /// (x: 0., y: 0.), /// (x: 10., y: 20.), /// (x: 20., y: -10.), /// (x: 0., y: 0.), /// ], /// ); /// ``` pub fn to_polygon(self) -> Polygon { polygon![self.0, self.1, self.2, self.0] } } impl> + Copy, T: CoordNum> From<[IC; 3]> for Triangle { fn from(array: [IC; 3]) -> Self { Self(array[0].into(), array[1].into(), array[2].into()) } } #[cfg(any(feature = "approx", test))] impl RelativeEq for Triangle where T: AbsDiffEq + CoordNum + RelativeEq, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } /// Equality assertion within a relative limit. /// /// # Examples /// /// ``` /// use geo_types::{point, Triangle}; /// /// let a = Triangle::new((0.0, 0.0).into(), (10.0, 10.0).into(), (0.0, 5.0).into()); /// let b = Triangle::new((0.0, 0.0).into(), (10.01, 10.0).into(), (0.0, 5.0).into()); /// /// approx::assert_relative_eq!(a, b, max_relative=0.1); /// approx::assert_relative_ne!(a, b, max_relative=0.0001); /// ``` #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { if !self.0.relative_eq(&other.0, epsilon, max_relative) { return false; } if !self.1.relative_eq(&other.1, epsilon, max_relative) { return false; } if !self.2.relative_eq(&other.2, epsilon, max_relative) { return false; } true } } #[cfg(any(feature = "approx", test))] impl AbsDiffEq for Triangle where T: AbsDiffEq + CoordNum, T::Epsilon: Copy, { type Epsilon = T; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } /// Equality assertion with an absolute limit. /// /// # Examples /// /// ``` /// use geo_types::{point, Triangle}; /// /// let a = Triangle::new((0.0, 0.0).into(), (10.0, 10.0).into(), (0.0, 5.0).into()); /// let b = Triangle::new((0.0, 0.0).into(), (10.01, 10.0).into(), (0.0, 5.0).into()); /// /// approx::abs_diff_eq!(a, b, epsilon=0.1); /// approx::abs_diff_ne!(a, b, epsilon=0.001); /// ``` #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { if !self.0.abs_diff_eq(&other.0, epsilon) { return false; } if !self.1.abs_diff_eq(&other.1, epsilon) { return false; } if !self.2.abs_diff_eq(&other.2, epsilon) { return false; } true } } geo-types-0.7.11/src/lib.rs000064400000000000000000000262701046102023000135400ustar 00000000000000#![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_debug_implementations)] #![doc(html_logo_url = "https://raw.githubusercontent.com/georust/meta/master/logo/logo.png")] //! The `geo-types` library defines geometric types for the [GeoRust] 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 additionally provides geospatial algorithms. //! //! ## Geometries //! //! - **[`Point`]**: A single point represented by one [`Coord`] //! - **[`MultiPoint`]**: A collection of [`Point`]s //! - **[`Line`]**: A line segment represented by two [`Coord`]s //! - **[`LineString`]**: A series of contiguous line segments represented by two or more //! [`Coord`]s //! - **[`MultiLineString`]**: A collection of [`LineString`]s //! - **[`Polygon`]**: A bounded area represented by one [`LineString`] exterior ring, and zero or //! more [`LineString`] interior rings //! - **[`MultiPolygon`]**: A collection of [`Polygon`]s //! - **[`Rect`]**: An axis-aligned bounded rectangle represented by minimum and maximum //! [`Coord`]s //! - **[`Triangle`]**: A bounded area represented by three [`Coord`] vertices //! - **[`GeometryCollection`]**: A collection of [`Geometry`]s //! - **[`Geometry`]**: An enumeration of all geometry types, excluding [`Coord`] //! //! ## Coordinates and Numeric Types //! //! - **[`Coord`]**: A two-dimensional coordinate. All geometry types are composed of [`Coord`]s, though [`Coord`] itself is not a [`Geometry`] type. See [`Point`] for a single coordinate geometry. //! //! By default, coordinates are 64-bit floating point numbers, but this is generic, and you may specify any numeric type that implements [`CoordNum`] or [`CoordFloat`]. As well as [`f64`], this includes common numeric types like [`f32`], [`i32`], [`i64`], etc. //! //! ```rust //! use geo_types::Point; //! //! // Geometries are f64 by default //! let point: Point = Point::new(1.0, 2.0); //! assert_eq!(std::mem::size_of::(), 64 * 2 / 8); //! //! // You can be explicit about the numeric type. //! let f64_point: Point = Point::new(1.0, 2.0); //! assert_eq!(std::mem::size_of::>(), 64 * 2 / 8); //! //! // Or specify some non-default numeric type //! let f32_point: Point = Point::new(1.0, 2.0); //! assert_eq!(std::mem::size_of::>(), 32 * 2 / 8); //! //! // Integer geometries are supported too, though not all //! // algorithms will be implemented for all numeric types. //! let i32_point: Point = Point::new(1, 2); //! assert_eq!(std::mem::size_of::>(), 32 * 2 / 8); //! ``` //! //! # Semantics //! //! The geospatial types provided here aim to adhere to the [OpenGIS Simple feature access][OGC-SFA] //! standards. Thus, the types here are inter-operable with other implementations of the standards: //! [JTS], [GEOS], etc. //! //! # Features //! //! The following optional [Cargo features] are available: //! //! - `std`: Enables use of the full `std` library. Enabled by default. //! - `approx`: Allows geometry types to be checked for approximate equality with [approx] //! - `arbitrary`: Allows geometry types to be created from unstructured input with [arbitrary] //! - `serde`: Allows geometry types to be serialized and deserialized with [Serde] //! - `use-rstar_0_8`: Allows geometry types to be inserted into [rstar] R*-trees (`rstar v0.8`) //! - `use-rstar_0_9`: Allows geometry types to be inserted into [rstar] R*-trees (`rstar v0.9`) //! - `use-rstar_0_10`: Allows geometry types to be inserted into [rstar] R*-trees (`rstar v0.10`) //! - `use-rstar_0_11`: Allows geometry types to be inserted into [rstar] R*-trees (`rstar v0.11`) //! //! This library can be used in `#![no_std]` environments if the default `std` feature is disabled. At //! the moment, the `arbitrary` and `use-rstar_0_8` features require `std`. This may change in a //! future release. //! //! [approx]: https://github.com/brendanzab/approx //! [arbitrary]: https://github.com/rust-fuzz/arbitrary //! [Cargo features]: https://doc.rust-lang.org/cargo/reference/features.html //! [GeoRust]: https://georust.org //! [GEOS]: https://trac.osgeo.org/geos //! [JTS]: https://github.com/locationtech/jts //! [OGC-SFA]: https://www.ogc.org/standards/sfa //! [rstar]: https://github.com/Stoeoef/rstar //! [Serde]: https://serde.rs/ extern crate alloc; extern crate num_traits; use core::fmt::Debug; use num_traits::{Float, Num, NumCast}; #[cfg(feature = "serde")] #[macro_use] extern crate serde; #[cfg(feature = "rstar_0_8")] extern crate rstar_0_8; #[cfg(test)] #[macro_use] extern crate approx; #[deprecated(since = "0.7.0", note = "use `CoordFloat` or `CoordNum` instead")] pub trait CoordinateType: Num + Copy + NumCast + PartialOrd + Debug {} #[allow(deprecated)] impl CoordinateType for T {} /// For algorithms which can use both integer **and** floating point `Point`s/`Coord`s /// /// Floats (`f32` and `f64`) and Integers (`u8`, `i32` etc.) implement this. /// /// For algorithms which only make sense for floating point, like area or length calculations, /// see [CoordFloat](trait.CoordFloat.html). #[allow(deprecated)] pub trait CoordNum: CoordinateType + Debug {} #[allow(deprecated)] impl CoordNum for T {} /// For algorithms which can only use floating point `Point`s/`Coord`s, like area or length calculations pub trait CoordFloat: CoordNum + Float {} impl CoordFloat for T {} pub mod geometry; pub use geometry::*; pub use geometry::line_string::PointsIter; #[allow(deprecated)] pub use geometry::rect::InvalidRectCoordinatesError; mod error; pub use error::Error; #[macro_use] mod macros; #[cfg(feature = "arbitrary")] mod arbitrary; #[cfg(any( feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10", feature = "rstar_0_11" ))] #[doc(hidden)] pub mod private_utils; #[doc(hidden)] pub mod _alloc { //! Needed to access these types from `alloc` in macros when the std feature is //! disabled and the calling context is missing `extern crate alloc`. These are //! _not_ meant for public use. pub use ::alloc::boxed::Box; pub use ::alloc::vec; } #[cfg(test)] mod tests { use alloc::vec; use super::*; use core::convert::TryFrom; #[test] fn type_test() { let c = coord! { x: 40.02f64, y: 116.34, }; let p = Point::from(c); let Point(c2) = p; assert_eq!(c, c2); assert_relative_eq!(c.x, c2.x); assert_relative_eq!(c.y, c2.y); let p: Point = (0f32, 1f32).into(); assert_relative_eq!(p.x(), 0.); assert_relative_eq!(p.y(), 1.); } #[test] fn convert_types() { let p: Point = Point::new(0., 0.); let p1 = p; let g: Geometry = p.into(); let p2 = Point::try_from(g).unwrap(); assert_eq!(p1, p2); } #[test] fn polygon_new_test() { let exterior = LineString::new(vec![ coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. }, coord! { x: 1., y: 0. }, coord! { x: 0., y: 0. }, ]); let interiors = vec![LineString::new(vec![ coord! { x: 0.1, y: 0.1 }, coord! { x: 0.9, y: 0.9 }, coord! { x: 0.9, y: 0.1 }, coord! { x: 0.1, y: 0.1 }, ])]; let p = Polygon::new(exterior.clone(), interiors.clone()); assert_eq!(p.exterior(), &exterior); assert_eq!(p.interiors(), &interiors[..]); } #[test] fn iters() { let _: MultiPoint<_> = vec![(0., 0.), (1., 2.)].into(); let _: MultiPoint<_> = vec![(0., 0.), (1., 2.)].into_iter().collect(); let mut l1: LineString<_> = vec![(0., 0.), (1., 2.)].into(); assert_eq!(l1[1], coord! { x: 1., y: 2. }); // index into linestring let _: LineString<_> = vec![(0., 0.), (1., 2.)].into_iter().collect(); // index mutably into a linestring l1[0] = coord! { x: 1., y: 1. }; assert_eq!(l1, vec![(1., 1.), (1., 2.)].into()); } #[test] fn test_coordinate_types() { let p: Point = Point::new(0, 0); assert_eq!(p.x(), 0u8); let p: Point = Point::new(1_000_000, 0); assert_eq!(p.x(), 1_000_000i64); } #[cfg(feature = "rstar_0_8")] #[test] /// ensure Line's SpatialObject impl is correct fn line_test() { use rstar_0_8::primitives::Line as RStarLine; use rstar_0_8::{PointDistance, RTreeObject}; let rl = RStarLine::new(Point::new(0.0, 0.0), Point::new(5.0, 5.0)); let l = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 5., y: 5. }); assert_eq!(rl.envelope(), l.envelope()); // difference in 15th decimal place assert_relative_eq!(26.0, rl.distance_2(&Point::new(4.0, 10.0))); assert_relative_eq!(25.999999999999996, l.distance_2(&Point::new(4.0, 10.0))); } #[cfg(feature = "rstar_0_9")] #[test] /// ensure Line's SpatialObject impl is correct fn line_test_0_9() { use rstar_0_9::primitives::Line as RStarLine; use rstar_0_9::{PointDistance, RTreeObject}; let rl = RStarLine::new(Point::new(0.0, 0.0), Point::new(5.0, 5.0)); let l = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 5., y: 5. }); assert_eq!(rl.envelope(), l.envelope()); // difference in 15th decimal place assert_relative_eq!(26.0, rl.distance_2(&Point::new(4.0, 10.0))); assert_relative_eq!(25.999999999999996, l.distance_2(&Point::new(4.0, 10.0))); } #[cfg(feature = "rstar_0_10")] #[test] /// ensure Line's SpatialObject impl is correct fn line_test_0_10() { use rstar_0_10::primitives::Line as RStarLine; use rstar_0_10::{PointDistance, RTreeObject}; let rl = RStarLine::new(Point::new(0.0, 0.0), Point::new(5.0, 5.0)); let l = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 5., y: 5. }); assert_eq!(rl.envelope(), l.envelope()); // difference in 15th decimal place assert_relative_eq!(26.0, rl.distance_2(&Point::new(4.0, 10.0))); assert_relative_eq!(25.999999999999996, l.distance_2(&Point::new(4.0, 10.0))); } #[cfg(feature = "rstar_0_11")] #[test] /// ensure Line's SpatialObject impl is correct fn line_test_0_11() { use rstar_0_11::primitives::Line as RStarLine; use rstar_0_11::{PointDistance, RTreeObject}; let rl = RStarLine::new(Point::new(0.0, 0.0), Point::new(5.0, 5.0)); let l = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 5., y: 5. }); assert_eq!(rl.envelope(), l.envelope()); // difference in 15th decimal place assert_relative_eq!(26.0, rl.distance_2(&Point::new(4.0, 10.0))); assert_relative_eq!(25.999999999999996, l.distance_2(&Point::new(4.0, 10.0))); } #[test] fn test_rects() { let r = Rect::new(coord! { x: -1., y: -1. }, coord! { x: 1., y: 1. }); let p: Polygon<_> = r.into(); assert_eq!( p, Polygon::new( vec![(-1., -1.), (1., -1.), (1., 1.), (-1., 1.), (-1., -1.)].into(), vec![] ) ); } } geo-types-0.7.11/src/macros.rs000064400000000000000000000211571046102023000142550ustar 00000000000000/// Creates a [`Point`] from the given coordinates. /// /// ```txt /// point! { x: , y: } /// point!() /// ``` /// /// # Examples /// /// Creating a [`Point`], supplying x/y values: /// /// ``` /// use geo_types::{point, coord}; /// /// let p = point! { x: 181.2, y: 51.79 }; /// /// assert_eq!(p.x(), 181.2); /// assert_eq!(p.y(), 51.79); /// /// let p = point!(coord! { x: 181.2, y: 51.79 }); /// /// assert_eq!(p.x(), 181.2); /// assert_eq!(p.y(), 51.79); /// ``` /// /// [`Point`]: ./struct.Point.html #[macro_export] macro_rules! point { ( $($tag:tt : $val:expr),* $(,)? ) => { $crate::point! ( $crate::coord! { $( $tag: $val , )* } ) }; ( $coord:expr $(,)? ) => { $crate::Point::from($coord) }; } /// Creates a [`Coord`] from the given scalars. /// /// ```txt /// coord! { x: , y: } /// ``` /// /// # Examples /// /// Creating a [`Coord`], supplying x/y values: /// /// ``` /// use geo_types::coord; /// /// let c = coord! { x: 181.2, y: 51.79 }; /// /// assert_eq!(c, geo_types::coord! { x: 181.2, y: 51.79 }); /// ``` /// /// [`Coord`]: ./struct.Coord.html #[macro_export] macro_rules! coord { (x: $x:expr, y: $y:expr $(,)* ) => { $crate::Coord { x: $x, y: $y } }; } /// Creates a [`LineString`] containing the given coordinates. /// /// ```txt /// line_string![Coord OR (x: , y: ), …] /// ``` /// /// # Examples /// /// Creating a [`LineString`], supplying x/y values: /// /// ``` /// use geo_types::line_string; /// /// let ls = line_string![ /// (x: -21.95156, y: 64.1446), /// (x: -21.951, y: 64.14479), /// (x: -21.95044, y: 64.14527), /// (x: -21.951445, y: 64.145508), /// ]; /// /// assert_eq!(ls[1], geo_types::coord! { /// x: -21.951, /// y: 64.14479 /// }); /// ``` /// /// Creating a [`LineString`], supplying [`Coord`]s: /// /// ``` /// use geo_types::line_string; /// /// let coord1 = geo_types::coord! { /// x: -21.95156, /// y: 64.1446, /// }; /// let coord2 = geo_types::coord! { /// x: -21.951, /// y: 64.14479, /// }; /// let coord3 = geo_types::coord! { /// x: -21.95044, /// y: 64.14527, /// }; /// let coord4 = geo_types::coord! { /// x: -21.951445, /// y: 64.145508, /// }; /// /// let ls = line_string![coord1, coord2, coord3, coord4]; /// /// assert_eq!( /// ls[1], /// geo_types::coord! { /// x: -21.951, /// y: 64.14479 /// } /// ); /// ``` /// /// [`Coord`]: ./struct.Coord.html /// [`LineString`]: ./line_string/struct.LineString.html #[macro_export] macro_rules! line_string { () => { $crate::LineString::new(vec![]) }; ( $(( $($tag:tt : $val:expr),* $(,)? )),* $(,)? ) => { line_string![ $( $crate::coord! { $( $tag: $val , )* }, )* ] }; ( $($coord:expr),* $(,)? ) => { $crate::LineString::new( <[_]>::into_vec( $crate::_alloc::Box::new( [$($coord), *] ) ) ) }; } /// Creates a [`Polygon`] containing the given coordinates. /// /// ```txt /// polygon![Coord OR (x: , y: ), …] /// /// // or /// /// polygon!( /// exterior: [Coord OR (x: , y: ), …], /// interiors: [ /// [Coord OR (x: , y: ), …], /// … /// ], /// ) /// ``` /// /// # Examples /// /// Creating a [`Polygon`] without interior rings, supplying x/y values: /// /// ``` /// use geo_types::polygon; /// /// let poly = polygon![ /// (x: -111., y: 45.), /// (x: -111., y: 41.), /// (x: -104., y: 41.), /// (x: -104., y: 45.), /// ]; /// /// assert_eq!( /// poly.exterior()[1], /// geo_types::coord! { x: -111., y: 41. }, /// ); /// ``` /// /// Creating a [`Polygon`], supplying x/y values: /// /// ``` /// use geo_types::polygon; /// /// let poly = polygon!( /// exterior: [ /// (x: -111., y: 45.), /// (x: -111., y: 41.), /// (x: -104., y: 41.), /// (x: -104., y: 45.), /// ], /// interiors: [ /// [ /// (x: -110., y: 44.), /// (x: -110., y: 42.), /// (x: -105., y: 42.), /// (x: -105., y: 44.), /// ], /// ], /// ); /// /// assert_eq!( /// poly.exterior()[1], /// geo_types::coord! { x: -111., y: 41. }, /// ); /// ``` /// /// [`Coord`]: ./struct.Coord.html /// [`Polygon`]: ./struct.Polygon.html #[macro_export] macro_rules! polygon { () => { $crate::Polygon::new($crate::line_string![], vec![]) }; ( exterior: [ $(( $($exterior_tag:tt : $exterior_val:expr),* $(,)? )),* $(,)? ], interiors: [ $([ $(( $($interior_tag:tt : $interior_val:expr),* $(,)? )),* $(,)? ]),* $(,)? ] $(,)? ) => { polygon!( exterior: [ $( $crate::coord! { $( $exterior_tag: $exterior_val , )* }, )* ], interiors: [ $([ $($crate::coord! { $( $interior_tag: $interior_val , )* }),* ]),* ], ) }; ( exterior: [ $($exterior_coord:expr),* $(,)? ], interiors: [ $([ $($interior_coord:expr),* $(,)? ]),* $(,)? ] $(,)? ) => { $crate::Polygon::new( $crate::line_string![ $($exterior_coord), * ], <[_]>::into_vec( $crate::_alloc::Box::new( [ $( $crate::line_string![$($interior_coord),*] ), * ] ) ) ) }; ( $(( $($tag:tt : $val:expr),* $(,)? )),* $(,)? ) => { polygon![ $($crate::coord! { $( $tag: $val , )* }),* ] }; ( $($coord:expr),* $(,)? ) => { $crate::Polygon::new( $crate::line_string![$($coord,)*], $crate::_alloc::vec![], ) }; } #[cfg(test)] mod test { #[test] fn test_point() { let p = point! { x: 1.2, y: 3.4 }; assert_eq!(p.x(), 1.2); assert_eq!(p.y(), 3.4); let p = point! { x: 1.2, y: 3.4, }; assert_eq!(p.x(), 1.2); assert_eq!(p.y(), 3.4); let p = point!(coord! { x: 1.2, y: 3.4 }); assert_eq!(p.x(), 1.2); assert_eq!(p.y(), 3.4); let p = point!(coord! { x: 1.2, y: 3.4 },); assert_eq!(p.x(), 1.2); assert_eq!(p.y(), 3.4); } #[test] fn test_line() { let ls = line_string![(x: -1.2f32, y: 3.4f32)]; assert_eq!(ls[0], coord! { x: -1.2, y: 3.4 }); let ls = line_string![ (x: -1.2f32, y: 3.4f32), ]; assert_eq!(ls[0], coord! { x: -1.2, y: 3.4 }); let ls = line_string![( x: -1.2f32, y: 3.4f32, )]; assert_eq!(ls[0], coord! { x: -1.2, y: 3.4 }); let ls = line_string![ (x: -1.2f32, y: 3.4f32), (x: -5.6, y: 7.8), ]; assert_eq!(ls[0], coord! { x: -1.2, y: 3.4 }); assert_eq!(ls[1], coord! { x: -5.6, y: 7.8 }); } #[test] fn test_polygon() { let p = polygon!( exterior: [(x: 1, y: 2)], interiors: [[(x: 3, y: 4)]] ); assert_eq!(p.exterior()[0], coord! { x: 1, y: 2 }); assert_eq!(p.interiors()[0][0], coord! { x: 3, y: 4 }); let p = polygon!( exterior: [(x: 1, y: 2)], interiors: [[(x: 3, y: 4)]], ); assert_eq!(p.exterior()[0], coord! { x: 1, y: 2 }); assert_eq!(p.interiors()[0][0], coord! { x: 3, y: 4 }); let p = polygon!( exterior: [(x: 1, y: 2, )], interiors: [[(x: 3, y: 4, )]], ); assert_eq!(p.exterior()[0], coord! { x: 1, y: 2 }); assert_eq!(p.interiors()[0][0], coord! { x: 3, y: 4 }); let p = polygon!( exterior: [(x: 1, y: 2, ), ], interiors: [[(x: 3, y: 4, ), ]], ); assert_eq!(p.exterior()[0], coord! { x: 1, y: 2 }); assert_eq!(p.interiors()[0][0], coord! { x: 3, y: 4 }); let p = polygon!( exterior: [(x: 1, y: 2, ), ], interiors: [[(x: 3, y: 4, ), ], ], ); assert_eq!(p.exterior()[0], coord! { x: 1, y: 2 }); assert_eq!(p.interiors()[0][0], coord! { x: 3, y: 4 }); } } geo-types-0.7.11/src/private_utils.rs000064400000000000000000000114311046102023000156550ustar 00000000000000// To implement RStar’s traits in the geo-types crates, we need to access to a // few geospatial algorithms, which are included in this hidden module. This // hidden module is public so the geo crate can reuse these algorithms to // prevent duplication. These functions are _not_ meant for public consumption. use crate::{Coord, CoordFloat, CoordNum, Line, LineString, Point, Rect}; pub fn line_string_bounding_rect(line_string: &LineString) -> Option> where T: CoordNum, { get_bounding_rect(line_string.coords().cloned()) } pub fn line_bounding_rect(line: Line) -> Rect where T: CoordNum, { Rect::new(line.start, line.end) } pub fn get_bounding_rect(collection: I) -> Option> where T: CoordNum, I: IntoIterator>, { let mut iter = collection.into_iter(); if let Some(pnt) = iter.next() { let mut xrange = (pnt.x, pnt.x); let mut yrange = (pnt.y, pnt.y); for pnt in iter { let (px, py) = pnt.x_y(); xrange = get_min_max(px, xrange.0, xrange.1); yrange = get_min_max(py, yrange.0, yrange.1); } return Some(Rect::new( coord! { x: xrange.0, y: yrange.0, }, coord! { x: xrange.1, y: yrange.1, }, )); } None } fn get_min_max(p: T, min: T, max: T) -> (T, T) { if p > max { (min, p) } else if p < min { (p, max) } else { (min, max) } } pub fn line_segment_distance(point: C, start: C, end: C) -> T where T: CoordFloat, C: Into>, { let point = point.into(); let start = start.into(); let end = end.into(); if start == end { return line_euclidean_length(Line::new(point, start)); } let dx = end.x - start.x; let dy = end.y - start.y; let r = ((point.x - start.x) * dx + (point.y - start.y) * dy) / (dx.powi(2) + dy.powi(2)); if r <= T::zero() { return line_euclidean_length(Line::new(point, start)); } if r >= T::one() { return line_euclidean_length(Line::new(point, end)); } let s = ((start.y - point.y) * dx - (start.x - point.x) * dy) / (dx * dx + dy * dy); s.abs() * dx.hypot(dy) } pub fn line_euclidean_length(line: Line) -> T where T: CoordFloat, { line.dx().hypot(line.dy()) } pub fn point_line_string_euclidean_distance(p: Point, l: &LineString) -> T where T: CoordFloat, { // No need to continue if the point is on the LineString, or it's empty if line_string_contains_point(l, p) || l.0.is_empty() { return T::zero(); } l.lines() .map(|line| line_segment_distance(p.0, line.start, line.end)) .fold(T::max_value(), |accum, val| accum.min(val)) } pub fn point_line_euclidean_distance(p: C, l: Line) -> T where T: CoordFloat, C: Into>, { line_segment_distance(p.into(), l.start, l.end) } pub fn point_contains_point(p1: Point, p2: Point) -> bool where T: CoordFloat, { let distance = line_euclidean_length(Line::new(p1, p2)).to_f32().unwrap(); approx::relative_eq!(distance, 0.0) } pub fn line_string_contains_point(line_string: &LineString, point: Point) -> bool where T: CoordFloat, { // LineString without points if line_string.0.is_empty() { return false; } // LineString with one point equal p if line_string.0.len() == 1 { return point_contains_point(Point::from(line_string[0]), point); } // check if point is a vertex if line_string.0.contains(&point.0) { return true; } for line in line_string.lines() { // This is a duplicate of the line-contains-point logic in the "intersects" module let tx = if line.dx() == T::zero() { None } else { Some((point.x() - line.start.x) / line.dx()) }; let ty = if line.dy() == T::zero() { None } else { Some((point.y() - line.start.y) / line.dy()) }; let contains = match (tx, ty) { (None, None) => { // Degenerate line point.0 == line.start } (Some(t), None) => { // Horizontal line point.y() == line.start.y && T::zero() <= t && t <= T::one() } (None, Some(t)) => { // Vertical line point.x() == line.start.x && T::zero() <= t && t <= T::one() } (Some(t_x), Some(t_y)) => { // All other lines (t_x - t_y).abs() <= T::epsilon() && T::zero() <= t_x && t_x <= T::one() } }; if contains { return true; } } false }