geo-types-0.7.17/.cargo_vcs_info.json0000644000000001470000000000100130460ustar { "git": { "sha1": "36b0f893c742b4e0188bbad4486c5429c09e0c1c" }, "path_in_vcs": "geo-types" }geo-types-0.7.17/CHANGES.md000064400000000000000000000314121046102023000132270ustar 00000000000000# Changes ## 0.7.17 - 2025-07-25 - Add support to `wkt!` geometry creation macro for `LINE`, `RECT`, and `TRIANGLE` geometries. - - Add `empty` convenience initializer for constructing empty geometries - ## 0.7.16 - 2025-03-24 - Add `LineString::rev_lines` to iterate segments from end to start - - Add more concise Debug output for geometries (like WKT). NOTE: because geo-types allows some representations which are not supported by standard WKT, not all debug output is valid WKT. Do not attempt to treat debug as a stable format - it's unsuitable for interacting with programmatically. See the [`wkt` crate](https://crates.io/crates/wkt) for that. - https://github.com/georust/geo/pull/1302 - Rect and Triangle's `lines` and `coords` iterators and `to_polygon` method now return ccw-oriented geometry - https://github.com/georust/geo/pull/1308 - Implement approx::UlpsEq for geo-types, allowing a new kind of approximate equality check, based on counting the number of floats between two numbers. - https://github.com/georust/geo/pull/1312 ## 0.7.15 - 2025-01-14 - Implement `RTreeObject` for `Triangle`. - Implement `AsRef` for `Point` and `Coord`. ## 0.7.14 - POSSIBLY BREAKING: Minimum supported version of Rust (MSRV) is now 1.75 - - Add rstar compatibility for MultiPolygon - Add multi-threading support to Multi\* geometries. - Feature-gated (`multithreading`), disabled by default, enabled by default when `geo-types` is used by `geo` ## 0.7.13 - POSSIBLY BREAKING: Minimum supported version of Rust (MSRV) is now 1.70 - - Add feature for rstar v0.12.0 - Add num_rings and num_interior_rings methods to polygons. ## 0.7.12 - Add `Polygon::try_exterior_mut` and `Polygon::try_interiors_mut`. - Add `wkt!` macro to define geometries at compile time. ## 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.17/Cargo.lock0000644000000245600000000000100110260ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "approx" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" dependencies = [ "num-traits", ] [[package]] name = "arbitrary" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" [[package]] name = "as-slice" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" dependencies = [ "generic-array 0.12.4", "generic-array 0.13.3", "generic-array 0.14.7", "stable_deref_trait", ] [[package]] name = "atomic-polyfill" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" dependencies = [ "critical-section", ] [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "critical-section" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" [[package]] name = "crossbeam-deque" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "generic-array" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" dependencies = [ "typenum", ] [[package]] name = "generic-array" version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" dependencies = [ "typenum", ] [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", ] [[package]] name = "geo-types" version = "0.7.17" dependencies = [ "approx", "arbitrary", "num-traits", "rayon", "rstar 0.10.0", "rstar 0.11.0", "rstar 0.12.2", "rstar 0.8.4", "rstar 0.9.3", "serde", ] [[package]] name = "hash32" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" dependencies = [ "byteorder", ] [[package]] name = "hash32" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" dependencies = [ "byteorder", ] [[package]] name = "hash32" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" dependencies = [ "byteorder", ] [[package]] name = "heapless" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422" dependencies = [ "as-slice", "generic-array 0.14.7", "hash32 0.1.1", "stable_deref_trait", ] [[package]] name = "heapless" version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" dependencies = [ "atomic-polyfill", "hash32 0.2.1", "rustc_version", "spin", "stable_deref_trait", ] [[package]] name = "heapless" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ "hash32 0.3.1", "stable_deref_trait", ] [[package]] name = "libm" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "lock_api" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", ] [[package]] name = "pdqselect" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27" [[package]] name = "proc-macro2" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "rstar" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a45c0e8804d37e4d97e55c6f258bc9ad9c5ee7b07437009dd152d764949a27c" dependencies = [ "heapless 0.6.1", "num-traits", "pdqselect", "smallvec", ] [[package]] name = "rstar" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b40f1bfe5acdab44bc63e6699c28b74f75ec43afb59f3eda01e145aff86a25fa" dependencies = [ "heapless 0.7.17", "num-traits", "smallvec", ] [[package]] name = "rstar" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f39465655a1e3d8ae79c6d9e007f4953bfc5d55297602df9dc38f9ae9f1359a" dependencies = [ "heapless 0.7.17", "num-traits", "smallvec", ] [[package]] name = "rstar" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73111312eb7a2287d229f06c00ff35b51ddee180f017ab6dec1f69d62ac098d6" dependencies = [ "heapless 0.7.17", "num-traits", "smallvec", ] [[package]] name = "rstar" version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "421400d13ccfd26dfa5858199c30a5d76f9c54e0dba7575273025b43c5175dbb" dependencies = [ "heapless 0.8.0", "num-traits", "smallvec", ] [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" dependencies = [ "lock_api", ] [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "syn" version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "typenum" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" geo-types-0.7.17/Cargo.toml0000644000000043340000000000100110460ustar # 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.75" name = "geo-types" version = "0.7.17" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false 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" [features] default = ["std"] multithreading = ["rayon"] 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_12 = [ "rstar_0_12", "approx", ] use-rstar_0_8 = [ "rstar_0_8", "approx", ] use-rstar_0_9 = [ "rstar_0_9", "approx", ] [lib] name = "geo_types" path = "src/lib.rs" [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.rayon] version = "1.10.0" optional = true [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_12] version = "0.12" 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" geo-types-0.7.17/Cargo.toml.orig000064400000000000000000000033251046102023000145260ustar 00000000000000[package] name = "geo-types" version = "0.7.17" 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.75" edition = "2021" [features] default = ["std"] std = ["approx?/std", "num-traits/std", "serde?/std"] multithreading = ["rayon"] # 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"] use-rstar_0_12 = ["rstar_0_12", "approx"] [dependencies] rayon = { version = "1.10.0", optional = true } 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 } rstar_0_12 = { package = "rstar", version = "0.12", 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.17/README.md000064400000000000000000000054321046102023000131170ustar 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 - All DE-9IM spatial predicates such as contains, crosses, and touches. 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.17/src/arbitrary.rs000064400000000000000000000075571046102023000150060ustar 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.17/src/debug.rs000064400000000000000000000344051046102023000140650ustar 00000000000000use core::fmt::{Debug, Formatter}; use crate::geometry::*; use crate::CoordNum; impl Debug for Coord { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "COORD({x:?} {y:?})", x = self.x, y = self.y) } } impl Debug for Point { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "POINT({x:?} {y:?})", x = self.x(), y = self.y()) } } impl Debug for Line { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "LINE")?; write_coord_seq(f, [self.start, self.end].iter()) } } impl Debug for LineString { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "LINESTRING")?; if self.0.is_empty() { write!(f, " ")?; } write_coord_seq(f, self.0.iter())?; Ok(()) } } impl Debug for Polygon { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "POLYGON")?; if self.exterior().0.is_empty() && self.interiors().is_empty() { write!(f, " ")?; } write_polygon_inner(f, self) } } impl Debug for MultiPoint { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "MULTIPOINT")?; if self.0.is_empty() { write!(f, " ")?; } write_coord_seq(f, self.0.iter().map(|p| &p.0)) } } impl Debug for MultiLineString { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "MULTILINESTRING")?; let mut line_strings = self.0.iter(); let Some(first) = line_strings.next() else { return write!(f, " EMPTY"); }; write!(f, "(")?; write_coord_seq(f, first.0.iter())?; for line_string in line_strings { write!(f, ",")?; write_coord_seq(f, line_string.0.iter())?; } write!(f, ")") } } impl Debug for MultiPolygon { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "MULTIPOLYGON")?; let mut polygons = self.0.iter(); let Some(first) = polygons.next() else { return write!(f, " EMPTY"); }; write!(f, "(")?; write_polygon_inner(f, first)?; for polygon in polygons { write!(f, ",")?; write_polygon_inner(f, polygon)?; } write!(f, ")") } } impl Debug for Rect { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "RECT")?; write_coord_seq(f, [self.min(), self.max()].iter()) } } impl Debug for Triangle { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "TRIANGLE")?; write_coord_seq(f, [self.0, self.1, self.2].iter()) } } impl Debug for Geometry { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { match self { Geometry::Point(inner) => inner.fmt(f), Geometry::Line(inner) => inner.fmt(f), Geometry::LineString(inner) => inner.fmt(f), Geometry::Polygon(inner) => inner.fmt(f), Geometry::MultiPoint(inner) => inner.fmt(f), Geometry::MultiLineString(inner) => inner.fmt(f), Geometry::MultiPolygon(inner) => inner.fmt(f), Geometry::GeometryCollection(inner) => inner.fmt(f), Geometry::Rect(inner) => inner.fmt(f), Geometry::Triangle(inner) => inner.fmt(f), } } } impl Debug for GeometryCollection { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "GEOMETRYCOLLECTION")?; let mut geometries = self.0.iter(); let Some(first) = geometries.next() else { return write!(f, " EMPTY"); }; write!(f, "({first:?}")?; for geometry in geometries { write!(f, ",{geometry:?}")?; } write!(f, ")") } } fn write_coord_seq<'a, T: CoordNum + 'a>( f: &mut Formatter<'_>, mut coords: impl Iterator>, ) -> core::fmt::Result { let Some(coord) = coords.next() else { write!(f, "EMPTY")?; return Ok(()); }; write!(f, "({x:?} {y:?}", x = coord.x, y = coord.y)?; for coord in coords { write!(f, ",{x:?} {y:?}", x = coord.x, y = coord.y)?; } write!(f, ")") } fn write_polygon_inner( f: &mut Formatter<'_>, polygon: &Polygon, ) -> core::fmt::Result { if polygon.exterior().0.is_empty() { let mut interiors = polygon.interiors().iter(); let Some(interior) = interiors.next() else { write!(f, "EMPTY")?; return Ok(()); }; // Invalid polygon - having interiors but no exterior! // Still, we should try to print something meaningful. write!(f, "(EMPTY,")?; write_coord_seq(f, interior.0.iter())?; for interior in interiors { write!(f, ",")?; write_coord_seq(f, interior.0.iter())?; } write!(f, ")")?; } else { write!(f, "(")?; write_coord_seq(f, polygon.exterior().0.iter())?; for interior in polygon.interiors().iter() { write!(f, ",")?; write_coord_seq(f, interior.0.iter())?; } write!(f, ")")?; } Ok(()) } #[cfg(feature = "std")] #[cfg(test)] mod tests { use super::*; #[test] fn float_coord() { let coord = Coord { x: 1.0, y: 2.0 }; assert_eq!("COORD(1.0 2.0)", format!("{coord:?}")); } #[test] fn int_coord() { let coord = Coord { x: 1, y: 2 }; assert_eq!("COORD(1 2)", format!("{coord:?}")); } #[test] fn float_point() { let point = Point::new(1.0, 2.0); assert_eq!("POINT(1.0 2.0)", format!("{point:?}")); } #[test] fn int_point() { let point = Point::new(1, 2); assert_eq!("POINT(1 2)", format!("{point:?}")); } #[test] fn line() { let line_string = Line::new((1, 2), (3, 4)); assert_eq!("LINE(1 2,3 4)", format!("{line_string:?}")); } #[test] fn line_string() { let line_string = LineString::new(vec![(1, 2).into(), (3, 4).into()]); assert_eq!("LINESTRING(1 2,3 4)", format!("{line_string:?}")); } #[test] fn line_string_with_single_element() { let line_string = LineString::new(vec![(1, 2).into()]); assert_eq!("LINESTRING(1 2)", format!("{line_string:?}")); } #[test] fn empty_line_string() { let line_string = LineString::::empty(); assert_eq!("LINESTRING EMPTY", format!("{line_string:?}")); } #[test] fn polygon_no_holes() { let polygon = wkt!(POLYGON((1 2,3 4,5 6))); assert_eq!("POLYGON((1 2,3 4,5 6,1 2))", format!("{polygon:?}")); } #[test] fn polygon_with_hole() { let polygon = wkt!(POLYGON( (1 1,10 1,10 10,1 10,1 1), (3 3,7 3,7 7,3 7,3 3) )); assert_eq!( "POLYGON((1 1,10 1,10 10,1 10,1 1),(3 3,7 3,7 7,3 7,3 3))", format!("{polygon:?}") ); } #[test] fn polygon_with_multiple_holes() { let polygon = wkt!(POLYGON( (0 0,10 0,10 10,0 10,0 0), (2 2,4 2,4 4,2 4,2 2), (6 6,8 6,8 8,6 8,6 6) )); assert_eq!( "POLYGON((0 0,10 0,10 10,0 10,0 0),(2 2,4 2,4 4,2 4,2 2),(6 6,8 6,8 8,6 8,6 6))", format!("{polygon:?}") ); } #[test] fn invalid_polygon_interior_but_no_exterior() { // Not a valid polygon, but we should still have reasonable debug output - note this is *not* valid WKT let interior = LineString::new(vec![(1, 2).into()]); let polygon = Polygon::new(LineString::empty(), vec![interior]); assert_eq!("POLYGON(EMPTY,(1 2))", format!("{polygon:?}")); } #[test] fn empty_polygon() { let polygon: Polygon = wkt!(POLYGON EMPTY); assert_eq!("POLYGON EMPTY", format!("{polygon:?}")); } #[test] fn multi_point_empty() { let multi_point: MultiPoint = wkt!(MULTIPOINT EMPTY); assert_eq!("MULTIPOINT EMPTY", format!("{multi_point:?}")); } #[test] fn multi_point_one_point() { let multi_point = wkt!(MULTIPOINT(1 2)); assert_eq!("MULTIPOINT(1 2)", format!("{multi_point:?}")); } #[test] fn multi_point_three_points() { let multi_point = wkt!(MULTIPOINT(1 2,3 4,5 6)); assert_eq!("MULTIPOINT(1 2,3 4,5 6)", format!("{multi_point:?}")); } #[test] fn multilinestring_empty() { let multi_line_string: MultiLineString = wkt!(MULTILINESTRING EMPTY); assert_eq!("MULTILINESTRING EMPTY", format!("{multi_line_string:?}")); } #[test] fn multi_line_string_one_line() { let multi_line_string = wkt!(MULTILINESTRING((1 2, 3 4, 5 6))); assert_eq!( "MULTILINESTRING((1 2,3 4,5 6))", format!("{multi_line_string:?}") ); } #[test] fn multi_line_string_multiple_lines() { let multi_line_string = wkt!(MULTILINESTRING( (1 2, 3 4, 5 6), (7 8, 9 10, 11 12) )); assert_eq!( "MULTILINESTRING((1 2,3 4,5 6),(7 8,9 10,11 12))", format!("{multi_line_string:?}") ); } #[test] fn multi_line_string_multiple_lines_with_empty() { let multi_line_string = wkt!(MULTILINESTRING( (1 2, 3 4, 5 6), EMPTY, (7 8, 9 10, 11 12) )); assert_eq!( "MULTILINESTRING((1 2,3 4,5 6),EMPTY,(7 8,9 10,11 12))", format!("{multi_line_string:?}") ); } #[test] fn multi_polygon_empty() { let multi_polygon: MultiPolygon = wkt!(MULTIPOLYGON EMPTY); assert_eq!("MULTIPOLYGON EMPTY", format!("{multi_polygon:?}")); } #[test] fn multi_polygon_one_polygon() { let multi_polygon = wkt!(MULTIPOLYGON( ((1 2, 3 4, 5 6, 1 2)) )); assert_eq!( "MULTIPOLYGON(((1 2,3 4,5 6,1 2)))", format!("{multi_polygon:?}") ); } #[test] fn multi_polygon_multiple_polygons() { let multi_polygon = wkt!(MULTIPOLYGON( ((1 2, 3 4, 5 6, 1 2)), ((7 8, 9 10, 11 12, 7 8)) )); assert_eq!( "MULTIPOLYGON(((1 2,3 4,5 6,1 2)),((7 8,9 10,11 12,7 8)))", format!("{multi_polygon:?}") ); } #[test] fn multi_polygon_with_holes() { let multi_polygon = wkt!(MULTIPOLYGON( ( (1 1, 10 1, 10 10, 1 10, 1 1) ), ( (20 20, 30 20, 30 30, 20 30, 20 20), (22 22, 28 22, 28 28, 22 28, 22 22) ) )); assert_eq!( "MULTIPOLYGON(((1 1,10 1,10 10,1 10,1 1)),((20 20,30 20,30 30,20 30,20 20),(22 22,28 22,28 28,22 28,22 22)))", format!("{multi_polygon:?}") ); } #[test] fn multi_polygon_with_holes_and_empty_polygon() { let multi_polygon = wkt!(MULTIPOLYGON( ( (1 1, 10 1, 10 10, 1 10, 1 1) ), EMPTY, ( (20 20, 30 20, 30 30, 20 30, 20 20), (22 22, 28 22, 28 28, 22 28, 22 22) ) )); assert_eq!( "MULTIPOLYGON(((1 1,10 1,10 10,1 10,1 1)),EMPTY,((20 20,30 20,30 30,20 30,20 20),(22 22,28 22,28 28,22 28,22 22)))", format!("{multi_polygon:?}") ); } #[test] fn rect() { let rect = Rect::new((1, 2), (3, 4)); assert_eq!("RECT(1 2,3 4)", format!("{rect:?}")); let rect = Rect::new((3, 4), (1, 2)); // output is always (min, max) assert_eq!("RECT(1 2,3 4)", format!("{rect:?}")); } #[test] fn triangle() { let rect = Triangle::new((1, 2).into(), (3, 4).into(), (5, 6).into()); assert_eq!("TRIANGLE(1 2,3 4,5 6)", format!("{rect:?}")); } #[test] fn geometry() { let rect = Geometry::Triangle(Triangle::new((1, 2).into(), (3, 4).into(), (5, 6).into())); assert_eq!("TRIANGLE(1 2,3 4,5 6)", format!("{rect:?}")); } #[test] fn geometry_collection() { let rect = Geometry::Triangle(Triangle::new((1, 2).into(), (3, 4).into(), (5, 6).into())); assert_eq!("TRIANGLE(1 2,3 4,5 6)", format!("{rect:?}")); } #[test] fn empty_geometry_collection() { let geometry_collection: GeometryCollection = GeometryCollection::default(); assert_eq!( "GEOMETRYCOLLECTION EMPTY", format!("{geometry_collection:?}") ); } #[test] fn geometry_collection_with_mixed_geometries() { let geometry_collection: GeometryCollection = GeometryCollection::from(vec![ Geometry::Point(Point::new(1, 2)), Geometry::Line(Line::new((1, 2), (3, 4))), Geometry::Polygon(Polygon::new( LineString::from(vec![(0, 0), (1, 0), (1, 1), (0, 0)]), vec![], )), ]); assert_eq!( "GEOMETRYCOLLECTION(POINT(1 2),LINE(1 2,3 4),POLYGON((0 0,1 0,1 1,0 0)))", format!("{geometry_collection:?}") ); } #[test] fn nested_geometry_collection() { let inner_collection: GeometryCollection = GeometryCollection::from(vec![ Geometry::Point(Point::new(5, 6)), Geometry::LineString(LineString::from(vec![(1, 2), (3, 4)])), ]); let outer_collection: GeometryCollection = GeometryCollection::from(vec![ Geometry::Point(Point::new(1, 2)), Geometry::GeometryCollection(inner_collection), ]); assert_eq!( "GEOMETRYCOLLECTION(POINT(1 2),GEOMETRYCOLLECTION(POINT(5 6),LINESTRING(1 2,3 4)))", format!("{outer_collection:?}") ); } #[test] fn geometry_collection_with_no_coordinates() { let geometry_collection: GeometryCollection = GeometryCollection::from(vec![ Geometry::Point(Point::new(0.0, 0.0)), Geometry::Polygon(Polygon::empty()), ]); assert_eq!( "GEOMETRYCOLLECTION(POINT(0.0 0.0),POLYGON EMPTY)", format!("{geometry_collection:?}") ); } } geo-types-0.7.17/src/error.rs000064400000000000000000000022601046102023000141220ustar 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.17/src/geometry/coord.rs000064400000000000000000000255301046102023000157370ustar 00000000000000use crate::{coord, CoordNum, Point}; /// 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, Hash, Default)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Coord { /// Typically, `x` is the horizontal position, or longitude for geographic coordinates, /// but its interpretation can vary across coordinate systems. pub x: T, /// Typically, `y` is the vertical position, or latitude for geographic coordinates, /// but its interpretation can vary across coordinate systems. 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))] mod approx_integration { use super::*; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; impl AbsDiffEq for Coord where T: CoordNum + AbsDiffEq, { 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) } } impl RelativeEq for Coord where T: CoordNum + RelativeEq, { #[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) } } impl UlpsEq for Coord where T: CoordNum + UlpsEq, { #[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!(), } } } #[cfg(feature = "rstar_0_12")] impl ::rstar_0_12::Point for Coord where T: ::num_traits::Float + ::rstar_0_12::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!(), } } } impl AsRef> for Coord { fn as_ref(&self) -> &Coord { self } } geo-types-0.7.17/src/geometry/geometry_collection.rs000064400000000000000000000250371046102023000207010ustar 00000000000000use crate::{CoordNum, Geometry}; use alloc::vec; use alloc::vec::Vec; 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, 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::empty() 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) } /// Returns an empty GeometryCollection pub fn empty() -> Self { Self(Vec::new()) } /// 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))] mod approx_integration { use super::*; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; impl RelativeEq for GeometryCollection where T: 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; } self.iter() .zip(other.iter()) .all(|(lhs, rhs)| lhs.relative_eq(rhs, epsilon, max_relative)) } } impl AbsDiffEq for GeometryCollection where T: CoordNum + AbsDiffEq, { 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; } self.into_iter() .zip(other) .all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon)) } } impl UlpsEq for GeometryCollection where T: CoordNum + UlpsEq, { fn default_max_ulps() -> u32 { T::default_max_ulps() } fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { if self.0.len() != other.0.len() { return false; } self.into_iter() .zip(other) .all(|(lhs, rhs)| lhs.ulps_eq(rhs, epsilon, max_ulps)) } } } #[cfg(test)] mod tests { use alloc::vec; use crate::{wkt, 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); } #[test] fn empty() { let empty = GeometryCollection::::empty(); let empty_2 = wkt! { GEOMETRYCOLLECTION EMPTY }; assert_eq!(empty, empty_2); } } geo-types-0.7.17/src/geometry/line.rs000064400000000000000000000230621046102023000155560ustar 00000000000000use crate::{Coord, CoordNum, Point}; /// 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, 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))] mod approx_integration { use super::*; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; impl RelativeEq for Line where T: 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) } } impl AbsDiffEq for Line where T: CoordNum + AbsDiffEq, { 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) } } impl UlpsEq for Line where T: CoordNum + UlpsEq, { fn default_max_ulps() -> u32 { T::default_max_ulps() } fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.start.ulps_eq(&other.start, epsilon, max_ulps) && self.end.ulps_eq(&other.end, epsilon, max_ulps) } } } #[cfg(any( feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10", feature = "rstar_0_11", feature = "rstar_0_12" ))] 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 { ::$rstar::AABB::from_corners(self.start_point(), self.end_point()) } } 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(feature = "rstar_0_12")] impl_rstar_line!(rstar_0_12); #[cfg(test)] mod test { use super::*; use crate::{coord, point}; use approx::{AbsDiffEq, RelativeEq}; #[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.17/src/geometry/line_string.rs000064400000000000000000000535751046102023000171600ustar 00000000000000use 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 [`Coord`]s, representing a path between locations. /// To be valid, a `LineString` must be empty, or have two or more coords. /// /// # 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, 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 Iterator for PointsIter<'_, 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 ExactSizeIterator for PointsIter<'_, T> { fn len(&self) -> usize { self.0.len() } } impl DoubleEndedIterator for PointsIter<'_, 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 ExactSizeIterator for CoordinatesIter<'_, T> { fn len(&self) -> usize { self.0.len() } } impl DoubleEndedIterator for CoordinatesIter<'_, T> { fn next_back(&mut self) -> Option { self.0.next_back() } } impl LineString { /// Returns a LineString with the given coordinates pub fn new(value: Vec>) -> Self { Self(value) } /// Returns an empty LineString pub fn empty() -> Self { Self::new(Vec::new()) } /// 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::{wkt, Line, LineString}; /// /// let line_string = wkt!(LINESTRING(0 0,5 0,7 9)); /// let mut lines = line_string.lines(); /// /// assert_eq!( /// Some(Line::new((0, 0), (5, 0))), /// lines.next() /// ); /// assert_eq!( /// Some(Line::new((5, 0), (7, 9))), /// lines.next() /// ); /// assert!(lines.next().is_none()); /// ``` pub fn lines(&'_ self) -> impl ExactSizeIterator> + '_ { 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)) } }) } /// Return an iterator yielding one [`Line`] for each line segment in the [`LineString`], /// starting from the **end** point of the LineString, working towards the start. /// /// Note: This is like [`Self::lines`], but the sequence **and** the orientation of /// segments are reversed. /// /// # Examples /// /// ``` /// use geo_types::{wkt, Line, LineString}; /// /// let line_string = wkt!(LINESTRING(0 0,5 0,7 9)); /// let mut lines = line_string.rev_lines(); /// /// assert_eq!( /// Some(Line::new((7, 9), (5, 0))), /// lines.next() /// ); /// assert_eq!( /// Some(Line::new((5, 0), (0, 0))), /// lines.next() /// ); /// assert!(lines.next().is_none()); /// ``` pub fn rev_lines(&'_ self) -> impl ExactSizeIterator> + '_ { self.0.windows(2).rev().map(|w| { // slice::windows(N) is guaranteed to yield a slice with exactly N elements unsafe { Line::new(*w.get_unchecked(1), *w.get_unchecked(0)) } }) } /// An iterator which yields the coordinates of a [`LineString`] as [Triangle]s pub fn triangles(&'_ self) -> impl ExactSizeIterator> + '_ { 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))] mod approx_integration { use super::*; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; impl RelativeEq for LineString where T: 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 } } impl AbsDiffEq for LineString where T: CoordNum + AbsDiffEq, { 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)) } } impl UlpsEq for LineString where T: CoordNum + UlpsEq, { fn default_max_ulps() -> u32 { T::default_max_ulps() } fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> 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.ulps_eq(&rhs, epsilon, max_ulps)) } } } #[cfg(any( feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10", feature = "rstar_0_11", feature = "rstar_0_12" ))] 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(feature = "rstar_0_12")] impl_rstar_line_string!(rstar_0_12); #[cfg(test)] mod test { use super::*; use crate::{coord, wkt}; use approx::{AbsDiffEq, RelativeEq}; #[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)); } #[test] fn empty() { let empty = LineString::::empty(); let empty_2 = wkt! { LINESTRING EMPTY }; assert_eq!(empty, empty_2); } } geo-types-0.7.17/src/geometry/mod.rs000064400000000000000000000443541046102023000154150ustar 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}; 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, 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))] mod approx_integration { use super::*; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; impl RelativeEq for Geometry where T: 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, } } } impl AbsDiffEq for Geometry where T: CoordNum + AbsDiffEq, { 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, } } } impl UlpsEq for Geometry where T: CoordNum + UlpsEq, { fn default_max_ulps() -> u32 { T::default_max_ulps() } /// Approximate equality assertion for floating point geometries based on the number of /// representable floats that fit between the two numbers being compared. /// /// "relative_eq" might be more intuitive, but it does floating point math in its error /// calculation, introducing its **own** error into the error calculation. /// /// Working with `ulps` avoids this problem. `max_ulps` means "how many floating points /// are representable that fit between these two numbers", which lets us tune how "sloppy" /// we're willing to be while avoiding any danger of floating point rounding in the /// comparison itself. /// /// # Examples /// /// ``` /// use geo_types::{Geometry, Point}; /// /// let a: Geometry = Point::new(1.0, 1.0).into(); /// let b: Geometry = Point::new(1.0 + 4.0 * f64::EPSILON, 1.0 + 4.0 * f64::EPSILON).into(); /// /// approx::assert_ulps_eq!(a, b); /// approx::assert_ulps_ne!(a, b, max_ulps=3); /// approx::assert_ulps_eq!(a, b, max_ulps=5); /// ``` /// /// # References /// /// fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { match (self, other) { (Geometry::Point(g1), Geometry::Point(g2)) => g1.ulps_eq(g2, epsilon, max_ulps), (Geometry::Line(g1), Geometry::Line(g2)) => g1.ulps_eq(g2, epsilon, max_ulps), (Geometry::LineString(g1), Geometry::LineString(g2)) => { g1.ulps_eq(g2, epsilon, max_ulps) } (Geometry::Polygon(g1), Geometry::Polygon(g2)) => g1.ulps_eq(g2, epsilon, max_ulps), (Geometry::MultiPoint(g1), Geometry::MultiPoint(g2)) => { g1.ulps_eq(g2, epsilon, max_ulps) } (Geometry::MultiLineString(g1), Geometry::MultiLineString(g2)) => { g1.ulps_eq(g2, epsilon, max_ulps) } (Geometry::MultiPolygon(g1), Geometry::MultiPolygon(g2)) => { g1.ulps_eq(g2, epsilon, max_ulps) } (Geometry::GeometryCollection(g1), Geometry::GeometryCollection(g2)) => { g1.ulps_eq(g2, epsilon, max_ulps) } (Geometry::Rect(g1), Geometry::Rect(g2)) => g1.ulps_eq(g2, epsilon, max_ulps), (Geometry::Triangle(g1), Geometry::Triangle(g2)) => { g1.ulps_eq(g2, epsilon, max_ulps) } // mismatched geometry types _ => false, } } } } #[cfg(test)] mod tests { mod approx_integration { use crate::{Geometry, Point}; #[test] fn test_abs_diff() { let g = Geometry::from(Point::new(1.0, 1.0)); let abs_diff_eq_point = Geometry::from(Point::new(1.0 + f64::EPSILON, 1.0 + f64::EPSILON)); assert_ne!(g, abs_diff_eq_point); assert_abs_diff_eq!(g, abs_diff_eq_point); let a_little_farther = Geometry::from(Point::new(1.001, 1.001)); assert_ne!(g, a_little_farther); assert_abs_diff_ne!(g, a_little_farther); assert_abs_diff_eq!(g, a_little_farther, epsilon = 1e-3); assert_abs_diff_ne!(g, a_little_farther, epsilon = 5e-4); } #[test] fn test_relative() { let g = Geometry::from(Point::new(2.0, 2.0)); let relative_eq_point = Geometry::from(Point::new( 2.0 + 2.0 * f64::EPSILON, 2.0 + 2.0 * f64::EPSILON, )); assert_ne!(g, relative_eq_point); assert_relative_eq!(g, relative_eq_point); let a_little_farther = Geometry::from(Point::new(2.001, 2.001)); assert_ne!(g, a_little_farther); assert_relative_ne!(g, a_little_farther); assert_relative_eq!(g, a_little_farther, epsilon = 1e-3); assert_relative_ne!(g, a_little_farther, epsilon = 5e-4); assert_relative_eq!(g, a_little_farther, max_relative = 5e-4); // point * 2 let far = Geometry::from(Point::new(4.0, 4.0)); assert_relative_eq!(g, far, max_relative = 1.0 / 2.0); assert_relative_ne!(g, far, max_relative = 0.49); } #[test] fn test_ulps() { let g = Geometry::from(Point::new(1.0, 1.0)); let ulps_eq_point = Geometry::from(Point::new(1.0 + f64::EPSILON, 1.0 + f64::EPSILON)); assert_ne!(g, ulps_eq_point); assert_ulps_eq!(g, ulps_eq_point); } #[test] fn test_ulps_vs_relative() { // "relative_eq" measures the difference between two floating point outputs, but to do // so involves doing its own floating point math, which introduces some of its own // error in the error calculation. // // Working with `ulps` avoids this problem. `max_ulps` means "how many floating points // are representable that fit between these two numbers", which lets us tune how "sloppy" // we're willing to be while avoiding any danger of floating point rounding in the // comparison itself. let a = 1000.000000000001; let b = 1000.0000000000008; let p1 = Point::new(a, a); let p2 = Point::new(b, b); assert_ne!(p1, p2); assert_relative_ne!(p1, p2); assert_ulps_eq!(p1, p2); } } } geo-types-0.7.17/src/geometry/multi_line_string.rs000064400000000000000000000252001046102023000203520ustar 00000000000000use crate::{CoordNum, LineString}; use alloc::vec; use alloc::vec::Vec; #[cfg(any(feature = "approx", test))] use core::iter::FromIterator; #[cfg(feature = "multithreading")] use rayon::prelude::*; /// 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, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct MultiLineString(pub Vec>); impl MultiLineString { /// Returns a MultiLineString with the given LineStrings as elements pub fn new(value: Vec>) -> Self { Self(value) } /// Returns an empty MultiLineString pub fn empty() -> Self { Self::new(Vec::new()) } /// 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::::empty().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(feature = "multithreading")] impl IntoParallelIterator for MultiLineString { type Item = LineString; type Iter = rayon::vec::IntoIter>; fn into_par_iter(self) -> Self::Iter { self.0.into_par_iter() } } #[cfg(feature = "multithreading")] impl<'a, T: CoordNum + Sync> IntoParallelIterator for &'a MultiLineString { type Item = &'a LineString; type Iter = rayon::slice::Iter<'a, LineString>; fn into_par_iter(self) -> Self::Iter { self.0.par_iter() } } #[cfg(feature = "multithreading")] impl<'a, T: CoordNum + Send + Sync> IntoParallelIterator for &'a mut MultiLineString { type Item = &'a mut LineString; type Iter = rayon::slice::IterMut<'a, LineString>; fn into_par_iter(self) -> Self::Iter { self.0.par_iter_mut() } } #[cfg(any(feature = "approx", test))] mod approx_integration { use super::*; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; impl RelativeEq for MultiLineString where T: 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)) } } impl AbsDiffEq for MultiLineString where T: CoordNum + AbsDiffEq, { 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; } self.into_iter() .zip(other) .all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon)) } } impl UlpsEq for MultiLineString where T: CoordNum + UlpsEq, { fn default_max_ulps() -> u32 { T::default_max_ulps() } fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { if self.0.len() != other.0.len() { return false; } self.into_iter() .zip(other) .all(|(lhs, rhs)| lhs.ulps_eq(rhs, epsilon, max_ulps)) } } } #[cfg(test)] mod test { use super::*; use crate::{line_string, wkt}; #[cfg(feature = "multithreading")] #[test] fn test_multithreading_linestring() { let multi: MultiLineString = wkt! { MULTILINESTRING((0 0,2 0,1 2,0 0), (10 10,12 10,11 12,10 10)) }; let mut multimut: MultiLineString = wkt! { MULTILINESTRING((0 0,2 0,1 2,0 0), (10 10,12 10,11 12,10 10)) }; multi.par_iter().for_each(|_p| ()); multimut.par_iter_mut().for_each(|_p| ()); let _ = &multi.into_par_iter().for_each(|_p| ()); let _ = &mut multimut.par_iter_mut().for_each(|_p| ()); } #[test] fn test_iter() { let multi: MultiLineString = wkt! { MULTILINESTRING((0 0,2 0,1 2,0 0), (10 10,12 10,11 12,10 10)) }; let mut first = true; for p in &multi { if first { assert_eq!(p, &wkt! { LINESTRING(0 0,2 0,1 2,0 0) }); first = false; } else { assert_eq!(p, &wkt! { LINESTRING(10 10,12 10,11 12,10 10) }); } } // Do it again to prove that `multi` wasn't `moved`. first = true; for p in &multi { if first { assert_eq!(p, &wkt! { LINESTRING(0 0,2 0,1 2,0 0) }); first = false; } else { assert_eq!(p, &wkt! { LINESTRING(10 10,12 10,11 12,10 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)] ); } } } #[test] fn empty() { let empty = MultiLineString::::empty(); let empty_2 = wkt! { MULTILINESTRING EMPTY }; assert_eq!(empty, empty_2); } } geo-types-0.7.17/src/geometry/multi_point.rs000064400000000000000000000235411046102023000171740ustar 00000000000000use crate::{CoordNum, Point}; use alloc::vec; use alloc::vec::Vec; use core::iter::FromIterator; #[cfg(feature = "multithreading")] use rayon::prelude::*; /// 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, 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() } } #[cfg(feature = "multithreading")] impl IntoParallelIterator for MultiPoint { type Item = Point; type Iter = rayon::vec::IntoIter>; fn into_par_iter(self) -> Self::Iter { self.0.into_par_iter() } } #[cfg(feature = "multithreading")] impl<'a, T: CoordNum + Sync> IntoParallelIterator for &'a MultiPoint { type Item = &'a Point; type Iter = rayon::slice::Iter<'a, Point>; fn into_par_iter(self) -> Self::Iter { self.0.par_iter() } } #[cfg(feature = "multithreading")] impl<'a, T: CoordNum + Send + Sync> IntoParallelIterator for &'a mut MultiPoint { type Item = &'a mut Point; type Iter = rayon::slice::IterMut<'a, Point>; fn into_par_iter(self) -> Self::Iter { self.0.par_iter_mut() } } impl MultiPoint { /// Returns a MultiPoint with the given Points as elements pub fn new(value: Vec>) -> Self { Self(value) } /// Returns an empty MultiPoint pub fn empty() -> Self { Self::new(Vec::new()) } pub fn len(&self) -> usize { self.0.len() } pub fn is_empty(&self) -> bool { self.0.is_empty() } 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))] mod approx_integration { use super::*; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; impl RelativeEq for MultiPoint where T: 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)) } } impl AbsDiffEq for MultiPoint where T: CoordNum + AbsDiffEq, { 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); mp_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon)) } } impl UlpsEq for MultiPoint where T: CoordNum + UlpsEq, { fn default_max_ulps() -> u32 { T::default_max_ulps() } fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { if self.0.len() != other.0.len() { return false; } let mut mp_zipper = self.into_iter().zip(other); mp_zipper.all(|(lhs, rhs)| lhs.ulps_eq(rhs, epsilon, max_ulps)) } } } #[cfg(test)] mod test { use super::*; use crate::{point, wkt}; use approx::{AbsDiffEq, RelativeEq}; #[test] fn test_iter() { let multi = wkt! { MULTIPOINT(0 0,10 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 = wkt! { MULTIPOINT(0 0,10 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 = wkt! { MULTIPOINT(0. 0.,10. 10.) }; let mut multi_x = multi.clone(); *multi_x.0[0].x_mut() += delta; assert!(multi.relative_eq(&multi_x, 1e-2, 1e-2)); assert!(multi.relative_ne(&multi_x, 1e-12, 1e-12)); let mut multi_y = multi.clone(); *multi_y.0[0].y_mut() += 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 = wkt! { MULTIPOINT(0. 0.) }; assert!(multi.relative_ne(&multi_undersized, 1., 1.)); // Over-sized but otherwise equal. let multi_oversized = wkt! { MULTIPOINT(0. 0.,10. 10.,10. 100.) }; assert!(multi.relative_ne(&multi_oversized, 1., 1.)); } #[test] fn test_abs_diff_eq() { let delta = 1e-6; let multi = wkt! { MULTIPOINT(0. 0.,10. 10.) }; let mut multi_x = multi.clone(); *multi_x.0[0].x_mut() += delta; assert!(multi.abs_diff_eq(&multi_x, 1e-2)); assert!(multi.abs_diff_ne(&multi_x, 1e-12)); let mut multi_y = multi.clone(); *multi_y.0[0].y_mut() += 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 = wkt! { MULTIPOINT(0. 0.) }; assert!(multi.abs_diff_ne(&multi_undersized, 1.)); // Over-sized but otherwise equal. let multi_oversized = wkt! { MULTIPOINT(0. 0.,10. 10.,10. 100.) }; assert!(multi.abs_diff_ne(&multi_oversized, 1.)); } #[test] fn empty() { let empty = MultiPoint::::empty(); let empty_2 = wkt! { MULTIPOINT EMPTY }; assert_eq!(empty, empty_2); } } geo-types-0.7.17/src/geometry/multi_polygon.rs000064400000000000000000000260601046102023000175310ustar 00000000000000use crate::{CoordNum, Polygon}; use alloc::vec; use alloc::vec::Vec; use core::iter::FromIterator; #[cfg(feature = "multithreading")] use rayon::prelude::*; /// 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, 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() } } #[cfg(feature = "multithreading")] impl IntoParallelIterator for MultiPolygon { type Item = Polygon; type Iter = rayon::vec::IntoIter>; fn into_par_iter(self) -> Self::Iter { self.0.into_par_iter() } } #[cfg(feature = "multithreading")] impl<'a, T: CoordNum + Sync> IntoParallelIterator for &'a MultiPolygon { type Item = &'a Polygon; type Iter = rayon::slice::Iter<'a, Polygon>; fn into_par_iter(self) -> Self::Iter { self.0.par_iter() } } #[cfg(feature = "multithreading")] impl<'a, T: CoordNum + Send + Sync> IntoParallelIterator for &'a mut MultiPolygon { type Item = &'a mut Polygon; type Iter = rayon::slice::IterMut<'a, Polygon>; fn into_par_iter(self) -> Self::Iter { self.0.par_iter_mut() } } impl MultiPolygon { /// Returns a MultiPolygon with the given Polygons as elements pub fn new(value: Vec>) -> Self { Self(value) } /// Returns an empty MultiPolygon pub fn empty() -> Self { Self(Vec::new()) } 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))] mod approx_integration { use super::*; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; impl RelativeEq for MultiPolygon where T: 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)) } } impl AbsDiffEq for MultiPolygon where T: CoordNum + AbsDiffEq, { 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); mp_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon)) } } impl UlpsEq for MultiPolygon where T: CoordNum + UlpsEq, { fn default_max_ulps() -> u32 { T::default_max_ulps() } fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { if self.0.len() != other.0.len() { return false; } let mut mp_zipper = self.into_iter().zip(other); mp_zipper.all(|(lhs, rhs)| lhs.ulps_eq(rhs, epsilon, max_ulps)) } } } #[cfg(any( feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10", feature = "rstar_0_11", feature = "rstar_0_12" ))] macro_rules! impl_rstar_multi_polygon { ($rstar:ident) => { impl $rstar::RTreeObject for MultiPolygon where T: ::num_traits::Float + ::$rstar::RTreeNum, { type Envelope = ::$rstar::AABB<$crate::Point>; fn envelope(&self) -> Self::Envelope { use ::$rstar::Envelope; self.iter() .map(|p| p.envelope()) .fold(::$rstar::AABB::new_empty(), |a, b| a.merged(&b)) } } }; } #[cfg(feature = "rstar_0_8")] impl_rstar_multi_polygon!(rstar_0_8); #[cfg(feature = "rstar_0_9")] impl_rstar_multi_polygon!(rstar_0_9); #[cfg(feature = "rstar_0_10")] impl_rstar_multi_polygon!(rstar_0_10); #[cfg(feature = "rstar_0_11")] impl_rstar_multi_polygon!(rstar_0_11); #[cfg(feature = "rstar_0_12")] impl_rstar_multi_polygon!(rstar_0_12); #[cfg(test)] mod test { use super::*; use crate::{polygon, wkt}; #[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)] ); } } } #[cfg(feature = "multithreading")] #[test] fn test_par_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 multimut = 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)], ]); multi.par_iter().for_each(|_p| ()); let _ = &multimut.par_iter_mut().for_each(|_p| ()); let _ = &multi.into_par_iter().for_each(|_p| ()); let _ = &mut multimut.par_iter_mut().for_each(|_p| ()); } #[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)] ); } } } #[test] fn empty() { let empty = MultiPolygon::::empty(); let empty_2 = wkt! { MULTIPOLYGON EMPTY }; assert_eq!(empty, empty_2); } } geo-types-0.7.17/src/geometry/point.rs000064400000000000000000000507431046102023000157660ustar 00000000000000use crate::{point, Coord, CoordFloat, CoordNum}; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; /// A single point in 2D space. /// /// # 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. /// /// # Creating a Point /// /// There are many ways to construct a point. /// ``` /// use geo_types::{coord, point, Point}; /// /// let p1 = Point::new(0., 1.); /// /// let p2 = point! { x: 1000.0, y: 2000.0 }; /// /// let p3: Point = (0., 1.).into(); /// /// let c = coord! { x: 10., y: 20. }; /// let p4: Point = c.into(); /// ``` /// /// See the `From` impl section for a complete list of conversions. /// /// ## Coordinate order for geographic points /// /// For geographic points, typically `x` corresponds to longitude and `y` to latitude. /// /// Geographic methods in the [`geo`](https://crates.io/crates/geo) crate expect this common /// lon/lat order, but different conventions exist in other coordinate systems, /// notably EPSG:4326, which uses lat/lon ordering. /// ``` /// use geo_types::{coord, point, Point}; /// /// let lon = 179.9; /// let lat = 45.0; /// let geographic_point = Point::new(lon, lat); /// ``` /// #[derive(Eq, PartialEq, Clone, Copy, 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. /// /// Typically, `x` is the horizontal position, or longitude for geographic coordinates, /// but its interpretation can vary across coordinate systems. /// /// # 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. /// /// Typically, `x` is the horizontal position, or longitude for geographic coordinates, /// but its interpretation can vary across coordinate systems. /// /// # 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 /// /// Typically, `x` is the horizontal position, or longitude for geographic coordinates, /// but its interpretation can vary across coordinate systems. /// /// # 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. /// /// Typically, `y` is the vertical position, or latitude for geographic coordinates, /// but its interpretation can vary across coordinate systems. /// /// # 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. /// /// Typically, `y` is the vertical position, or latitude for geographic coordinates, /// but its interpretation can vary across coordinate systems. /// /// # 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 /// /// Typically, `y` is the vertical position, or latitude for geographic coordinates, /// but its interpretation can vary across coordinate systems. /// /// # 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))] mod approx_integration { use super::*; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; impl RelativeEq for Point where T: 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) } } impl AbsDiffEq for Point where T: CoordNum + AbsDiffEq, { 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) } } impl UlpsEq for Point where T: CoordNum + UlpsEq, { fn default_max_ulps() -> u32 { T::default_max_ulps() } fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.0.ulps_eq(&other.0, epsilon, max_ulps) } } } #[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(feature = "rstar_0_12")] impl ::rstar_0_12::Point for Point where T: ::num_traits::Float + ::rstar_0_12::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!(), } } } impl AsRef> for Point { fn as_ref(&self) -> &Coord { &self.0 } } #[cfg(test)] mod test { use super::*; use approx::{AbsDiffEq, RelativeEq}; #[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.17/src/geometry/polygon.rs000064400000000000000000000513451046102023000163230ustar 00000000000000use crate::{CoordFloat, CoordNum, LineString, Point, Rect, Triangle}; use alloc::vec; use alloc::vec::Vec; use num_traits::{Float, Signed}; /// 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, 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, } } /// Returns an empty Polygon. /// /// `geo` represents an empty Polygon as one whose exterior is an empty LineString pub fn empty() -> Self { Self::new(LineString::empty(), vec![]) } /// 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(); } /// Fallible alternative to [`exterior_mut`](Polygon::exterior_mut). pub fn try_exterior_mut(&mut self, f: F) -> Result<(), E> where F: FnOnce(&mut LineString) -> Result<(), E>, { f(&mut self.exterior)?; self.exterior.close(); Ok(()) } /// 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(); } } /// Fallible alternative to [`interiors_mut`](Self::interiors_mut). pub fn try_interiors_mut(&mut self, f: F) -> Result<(), E> where F: FnOnce(&mut [LineString]) -> Result<(), E>, { f(&mut self.interiors)?; for interior in &mut self.interiors { interior.close(); } Ok(()) } /// 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) } /// Count the total number of rings (interior and exterior) in the polygon /// /// # Examples /// /// ``` /// use geo_types::{coord, LineString, Polygon}; /// /// let polygon = Polygon::new( /// LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.)]), /// vec![], /// ); /// /// assert_eq!(polygon.num_rings(), 1); /// /// 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)])], /// ); /// /// assert_eq!(polygon.num_rings(), 2); /// ``` pub fn num_rings(&self) -> usize { self.num_interior_rings() + 1 } /// Count the number of interior rings in the polygon /// /// # Examples /// /// ``` /// use geo_types::{coord, LineString, Polygon}; /// /// let polygon = Polygon::new( /// LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.)]), /// vec![], /// ); /// /// assert_eq!(polygon.num_interior_rings(), 0); /// /// 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)])], /// ); /// /// assert_eq!(polygon.num_interior_rings(), 1); /// ``` pub fn num_interior_rings(&self) -> usize { self.interiors.len() } } // 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))] mod approx_integration { use super::*; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; impl RelativeEq for Polygon where T: 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)) } } impl AbsDiffEq for Polygon where T: CoordNum + AbsDiffEq, { 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)) } } impl UlpsEq for Polygon where T: CoordNum + UlpsEq, { fn default_max_ulps() -> u32 { T::default_max_ulps() } fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { if !self.exterior.ulps_eq(&other.exterior, epsilon, max_ulps) { 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.ulps_eq(rhs, epsilon, max_ulps)) } } } #[cfg(any( feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10", feature = "rstar_0_11", feature = "rstar_0_12" ))] 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); #[cfg(feature = "rstar_0_12")] impl_rstar_polygon!(rstar_0_12); #[cfg(test)] mod tests { use super::*; use crate::wkt; #[test] fn empty() { let empty = Polygon::::empty(); let empty_2 = wkt! { POLYGON EMPTY }; assert_eq!(empty, empty_2); } } geo-types-0.7.17/src/geometry/rect.rs000064400000000000000000000343311046102023000155650ustar 00000000000000use crate::{coord, polygon, Coord, CoordFloat, CoordNum, Line, Polygon}; /// 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, 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. /// /// Coords are stored and returned (by iterators) in CCW order /// /// # 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. }, /// ); /// /// // Output is CCW /// assert_eq!( /// rect.to_polygon(), /// polygon![ /// (x: 1., y: 0.), /// (x: 1., y: 2.), /// (x: 0., y: 2.), /// (x: 0., y: 0.), /// (x: 1., y: 0.), /// ], /// ); /// ``` pub fn to_polygon(self) -> Polygon { polygon![ (x: self.max.x, y: self.min.y), (x: self.max.x, y: self.max.y), (x: self.min.x, y: self.max.y), (x: self.min.x, y: self.min.y), (x: self.max.x, y: self.min.y), ] } pub fn to_lines(&self) -> [Line; 4] { [ Line::new( coord! { x: self.max.x, y: self.min.y, }, coord! { x: self.max.x, y: self.max.y, }, ), Line::new( coord! { x: self.max.x, y: self.max.y, }, coord! { x: self.min.x, y: self.max.y, }, ), Line::new( coord! { x: self.min.x, y: self.max.y, }, coord! { x: self.min.x, y: self.min.y, }, ), Line::new( coord! { x: self.min.x, y: self.min.y, }, coord! { x: self.max.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))] mod approx_integration { use super::*; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; impl RelativeEq for Rect where T: 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 } } impl AbsDiffEq for Rect where T: CoordNum + AbsDiffEq, { 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 } } impl UlpsEq for Rect where T: CoordNum + UlpsEq, { fn default_max_ulps() -> u32 { T::default_max_ulps() } fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { if !self.min.ulps_eq(&other.min, epsilon, max_ulps) { return false; } if !self.max.ulps_eq(&other.max, epsilon, max_ulps) { 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.17/src/geometry/triangle.rs000064400000000000000000000150101046102023000164260ustar 00000000000000use crate::{polygon, Coord, CoordNum, Line, Point, Polygon}; use core::cmp::Ordering; /// 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. /// /// # Notes /// Irrespective of input order the resulting geometry has ccw order and its vertices are yielded in ccw order by iterators #[derive(Copy, Clone, 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 { // determine cross product of input points. NB: non-robust let orientation = Point::from(v1).cross_prod(v2.into(), v3.into()); match orientation.partial_cmp(&T::zero()) { Some(Ordering::Greater) => Self(v1, v2, v3), Some(Ordering::Less) => Self(v3, v2, v1), // we told you not to do this! _ => 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}; /// /// // Input is CW /// let triangle = Triangle::new( /// coord! { x: 0., y: 0. }, /// coord! { x: 10., y: 20. }, /// coord! { x: 20., y: -10. }, /// ); /// /// // Output is CCW /// assert_eq!( /// triangle.to_polygon(), /// polygon![ /// (x: 20., y: -10.), /// (x: 10., y: 20.), /// (x: 0., y: 0.), /// (x: 20., y: -10.), /// ], /// ); /// ``` 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))] mod approx_integration { use super::*; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; impl RelativeEq for Triangle where T: 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 } } impl AbsDiffEq for Triangle where T: CoordNum + AbsDiffEq, { 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 } } impl UlpsEq for Triangle where T: CoordNum + UlpsEq, { fn default_max_ulps() -> u32 { T::default_max_ulps() } fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { if !self.0.ulps_eq(&other.0, epsilon, max_ulps) { return false; } if !self.1.ulps_eq(&other.1, epsilon, max_ulps) { return false; } if !self.2.ulps_eq(&other.2, epsilon, max_ulps) { return false; } true } } } #[cfg(any( feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10", feature = "rstar_0_11", feature = "rstar_0_12" ))] macro_rules! impl_rstar_triangle { ($rstar:ident) => { impl ::$rstar::RTreeObject for Triangle where T: ::num_traits::Float + ::$rstar::RTreeNum, { type Envelope = ::$rstar::AABB>; fn envelope(&self) -> Self::Envelope { let bounding_rect = crate::private_utils::get_bounding_rect(self.to_array()).unwrap(); ::$rstar::AABB::from_corners(bounding_rect.min().into(), bounding_rect.max().into()) } } }; } #[cfg(feature = "rstar_0_8")] impl_rstar_triangle!(rstar_0_8); #[cfg(feature = "rstar_0_9")] impl_rstar_triangle!(rstar_0_9); #[cfg(feature = "rstar_0_10")] impl_rstar_triangle!(rstar_0_10); #[cfg(feature = "rstar_0_11")] impl_rstar_triangle!(rstar_0_11); #[cfg(feature = "rstar_0_12")] impl_rstar_triangle!(rstar_0_12); geo-types-0.7.17/src/lib.rs000064400000000000000000000300301046102023000135330ustar 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. //! - `multithreading`: Enables multi-threaded iteration over `Multi*` geometries. **Disabled** //! by default but **enabled** by `geo`'s default features. //! - `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`) //! - `use-rstar_0_12`: Allows geometry types to be inserted into [rstar] R*-trees (`rstar v0.12`) //! //! 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; use core::fmt::Debug; use num_traits::{Float, Num, NumCast}; #[cfg(feature = "serde")] #[macro_use] extern crate serde; #[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; #[macro_use] mod wkt_macro; #[cfg(feature = "arbitrary")] mod arbitrary; #[cfg(any( feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10", feature = "rstar_0_11", feature = "rstar_0_12" ))] #[doc(hidden)] pub mod private_utils; mod debug; #[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::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))); } #[cfg(feature = "rstar_0_12")] #[test] /// ensure Line's SpatialObject impl is correct fn line_test_0_12() { use rstar_0_12::primitives::Line as RStarLine; use rstar_0_12::{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.17/src/macros.rs000064400000000000000000000206261046102023000142630ustar 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::empty() }; ( $(( $($tag:tt : $val:expr),* $(,)? )),* $(,)? ) => { line_string![ $( $crate::coord! { $( $tag: $val , )* }, )* ] }; ( $($coord:expr),* $(,)? ) => { $crate::LineString::new( $crate::_alloc::vec![ $($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::empty() }; ( 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), * ], $crate::_alloc::vec![ $( $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.17/src/private_utils.rs000064400000000000000000000115241046102023000156660ustar 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.0) } 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, C: AsRef>, I: IntoIterator, { let mut iter = collection.into_iter(); if let Some(pnt) = iter.next() { let pnt = pnt.as_ref(); let mut xrange = (pnt.x, pnt.x); let mut yrange = (pnt.y, pnt.y); for pnt in iter { let (px, py) = pnt.as_ref().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 d_squared = dx * dx + dy * dy; let r = ((point.x - start.x) * dx + (point.y - start.y) * dy) / d_squared; 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) / d_squared; 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 } geo-types-0.7.17/src/wkt_macro.rs000064400000000000000000000306741046102023000147710ustar 00000000000000/// Creates a [`crate::geometry`] from a /// [WKT](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) literal. /// /// This is evaluated at compile time, so you don't need to worry about runtime errors from invalid /// WKT syntax. /// /// Note that `POINT EMPTY` is not accepted because it is not representable as a `geo_types::Point`. /// /// Note that all geometry variants are constructable with this macro. /// This includes [`LINE`](crate::Line), [`RECT`](crate::Rect), and [`TRIANGLE`](crate::Triangle), even though they are /// not standard WKT types, and will not be parsable by a standard WKT parser. /// /// ``` /// use geo_types::wkt; /// let point = wkt! { POINT(1.0 2.0) }; /// assert_eq!(point.x(), 1.0); /// assert_eq!(point.y(), 2.0); /// /// let geometry_collection = wkt! { /// GEOMETRYCOLLECTION( /// POINT(1.0 2.0), /// LINESTRING EMPTY, /// POLYGON((0.0 0.0,1.0 0.0,1.0 1.0,0.0 0.0)) /// ) /// }; /// assert_eq!(geometry_collection.len(), 3); /// ``` #[macro_export] macro_rules! wkt { // Hide distracting implementation details from the generated rustdoc. ($($wkt:tt)+) => { { $crate::wkt_internal!($($wkt)+) } }; } #[macro_export] #[doc(hidden)] macro_rules! wkt_internal { (POINT EMPTY) => { compile_error!("EMPTY points are not supported in geo-types") }; (POINT($x: literal $y: literal)) => { $crate::point!(x: $x, y: $y) }; (POINT $($tail: tt)*) => { compile_error!("Invalid POINT wkt"); }; (LINE($a_x: literal $a_y: literal,$b_x: literal $b_y: literal)) => { $crate::Line::new( $crate::coord!(x: $a_x, y: $a_y), $crate::coord!(x: $b_x, y: $b_y) ) }; (LINESTRING EMPTY) => { $crate::LineString::empty() }; (LINESTRING ($($x: literal $y: literal),+)) => { $crate::line_string![ $($crate::coord!(x: $x, y: $y)),* ] }; (LINESTRING ()) => { compile_error!("use `EMPTY` instead of () for an empty collection") }; (LINESTRING $($tail: tt)*) => { compile_error!("Invalid LINESTRING wkt"); }; (POLYGON EMPTY) => { $crate::Polygon::empty() }; (POLYGON ( $exterior_tt: tt )) => { $crate::Polygon::new($crate::wkt!(LINESTRING $exterior_tt), $crate::_alloc::vec![]) }; (POLYGON( $exterior_tt: tt, $($interiors_tt: tt),+ )) => { $crate::Polygon::new( $crate::wkt!(LINESTRING $exterior_tt), $crate::_alloc::vec![ $($crate::wkt!(LINESTRING $interiors_tt)),* ] ) }; (POLYGON ()) => { compile_error!("use `EMPTY` instead of () for an empty collection") }; (POLYGON $($tail: tt)*) => { compile_error!("Invalid POLYGON wkt"); }; (MULTIPOINT EMPTY) => { $crate::MultiPoint::empty() }; (MULTIPOINT ()) => { compile_error!("use `EMPTY` instead of () for an empty collection") }; (MULTIPOINT ($($x: literal $y: literal),* )) => { $crate::MultiPoint( $crate::_alloc::vec![$($crate::point!(x: $x, y: $y)),*] ) }; (MULTIPOINT $($tail: tt)*) => { compile_error!("Invalid MULTIPOINT wkt"); }; (MULTILINESTRING EMPTY) => { $crate::MultiLineString::empty() }; (MULTILINESTRING ()) => { compile_error!("use `EMPTY` instead of () for an empty collection") }; (MULTILINESTRING ( $($line_string_tt: tt),* )) => { $crate::MultiLineString($crate::_alloc::vec![ $($crate::wkt!(LINESTRING $line_string_tt)),* ]) }; (MULTILINESTRING $($tail: tt)*) => { compile_error!("Invalid MULTILINESTRING wkt"); }; (MULTIPOLYGON EMPTY) => { $crate::MultiPolygon::empty() }; (MULTIPOLYGON ()) => { compile_error!("use `EMPTY` instead of () for an empty collection") }; (MULTIPOLYGON ( $($polygon_tt: tt),* )) => { $crate::MultiPolygon($crate::_alloc::vec![ $($crate::wkt!(POLYGON $polygon_tt)),* ]) }; (MULTIPOLYGON $($tail: tt)*) => { compile_error!("Invalid MULTIPOLYGON wkt"); }; (GEOMETRYCOLLECTION EMPTY) => { $crate::GeometryCollection::empty() }; (GEOMETRYCOLLECTION ()) => { compile_error!("use `EMPTY` instead of () for an empty collection") }; (GEOMETRYCOLLECTION ( $($el_type:tt $el_tt: tt),* )) => { $crate::GeometryCollection($crate::_alloc::vec![ $($crate::Geometry::from($crate::wkt!($el_type $el_tt))),* ]) }; (GEOMETRYCOLLECTION $($tail: tt)*) => { compile_error!("Invalid GEOMETRYCOLLECTION wkt"); }; (RECT($a_x: literal $a_y: literal,$b_x: literal $b_y: literal)) => { $crate::Rect::new( $crate::coord!(x: $a_x, y: $a_y), $crate::coord!(x: $b_x, y: $b_y) ) }; (TRIANGLE($a_x: literal $a_y: literal,$b_x: literal $b_y: literal,$c_x: literal $c_y: literal)) => { $crate::Triangle::new( $crate::coord!(x: $a_x, y: $a_y), $crate::coord!(x: $b_x, y: $b_y), $crate::coord!(x: $c_x, y: $c_y) ) }; ($name: ident ($($tail: tt)*)) => { compile_error!("Unknown type. Must be one of POINT, LINESTRING, POLYGON, MULTIPOINT, MULTILINESTRING, MULTIPOLYGON, GEOMETRYCOLLECTION, LINE, RECT, or TRIANGLE") }; } #[cfg(test)] mod test { use crate::geometry::*; use alloc::vec; #[test] fn point() { let point = wkt! { POINT(1.0 2.0) }; assert_eq!(point.x(), 1.0); assert_eq!(point.y(), 2.0); let point = wkt! { POINT(1.0 2.0) }; assert_eq!(point.x(), 1.0); assert_eq!(point.y(), 2.0); // This (rightfully) fails to compile because geo-types doesn't support "empty" points // wkt! { POINT EMPTY } } #[test] fn line() { let line = wkt! { LINE(1.0 2.0,3.0 4.0) }; assert_eq!(line.start, coord!(x: 1.0, y: 2.0)); assert_eq!(line.end, coord!(x: 3.0, y: 4.0)); } #[test] fn empty_line_string() { let line_string: LineString = wkt! { LINESTRING EMPTY }; assert_eq!(line_string.0.len(), 0); // This (rightfully) fails to compile because its invalid wkt // wkt! { LINESTRING() } } #[test] fn line_string() { let line_string = wkt! { LINESTRING(1.0 2.0,3.0 4.0) }; assert_eq!(line_string.0.len(), 2); assert_eq!(line_string[0], coord! { x: 1.0, y: 2.0 }); } #[test] fn empty_polygon() { let polygon: Polygon = wkt! { POLYGON EMPTY }; assert_eq!(polygon.exterior().0.len(), 0); assert_eq!(polygon.interiors().len(), 0); // This (rightfully) fails to compile because its invalid wkt // wkt! { POLYGON() } } #[test] fn polygon() { let polygon = wkt! { POLYGON((1.0 2.0)) }; assert_eq!(polygon.exterior().0.len(), 1); assert_eq!(polygon.exterior().0[0], coord! { x: 1.0, y: 2.0 }); let polygon = wkt! { POLYGON((1.0 2.0,3.0 4.0)) }; // Note: an extra coord is added to close the linestring assert_eq!(polygon.exterior().0.len(), 3); assert_eq!(polygon.exterior().0[0], coord! { x: 1.0, y: 2.0 }); assert_eq!(polygon.exterior().0[1], coord! { x: 3.0, y: 4.0 }); assert_eq!(polygon.exterior().0[2], coord! { x: 1.0, y: 2.0 }); let polygon = wkt! { POLYGON((1.0 2.0), (1.1 2.1)) }; assert_eq!(polygon.exterior().0.len(), 1); assert_eq!(polygon.interiors().len(), 1); assert_eq!(polygon.exterior().0[0], coord! { x: 1.0, y: 2.0 }); assert_eq!(polygon.interiors()[0].0[0], coord! { x: 1.1, y: 2.1 }); let polygon = wkt! { POLYGON((1.0 2.0,3.0 4.0), (1.1 2.1,3.1 4.1), (1.2 2.2,3.2 4.2)) }; assert_eq!(polygon.exterior().0.len(), 3); assert_eq!(polygon.interiors().len(), 2); assert_eq!(polygon.interiors()[1][1], coord! { x: 3.2, y: 4.2 }); } #[test] fn empty_multi_point() { let multipoint: MultiPoint = wkt! { MULTIPOINT EMPTY }; assert!(multipoint.0.is_empty()); // This (rightfully) fails to compile because its invalid wkt // wkt! { MULTIPOINT() } } #[test] fn multi_point() { let multi_point = wkt! { MULTIPOINT(1.0 2.0) }; assert_eq!(multi_point.0, vec![point! { x: 1.0, y: 2.0}]); let multi_point = wkt! { MULTIPOINT(1.0 2.0,3.0 4.0) }; assert_eq!( multi_point.0, vec![point! { x: 1.0, y: 2.0}, point! { x: 3.0, y: 4.0}] ); } #[test] fn empty_multi_line_string() { let multi_line_string: MultiLineString = wkt! { MULTILINESTRING EMPTY }; assert_eq!(multi_line_string.0, vec![]); // This (rightfully) fails to compile because its invalid wkt // wkt! { MULTILINESTRING() } } #[test] fn multi_line_string() { let multi_line_string = wkt! { MULTILINESTRING ((1.0 2.0,3.0 4.0)) }; assert_eq!(multi_line_string.0.len(), 1); assert_eq!(multi_line_string.0[0].0[1], coord! { x: 3.0, y: 4.0 }); let multi_line_string = wkt! { MULTILINESTRING ((1.0 2.0,3.0 4.0),(5.0 6.0,7.0 8.0)) }; assert_eq!(multi_line_string.0.len(), 2); assert_eq!(multi_line_string.0[1].0[1], coord! { x: 7.0, y: 8.0 }); let multi_line_string = wkt! { MULTILINESTRING ((1.0 2.0,3.0 4.0),EMPTY) }; assert_eq!(multi_line_string.0.len(), 2); assert_eq!(multi_line_string.0[1].0.len(), 0); } #[test] fn empty_multi_polygon() { let multi_polygon: MultiPolygon = wkt! { MULTIPOLYGON EMPTY }; assert!(multi_polygon.0.is_empty()); // This (rightfully) fails to compile because its invalid wkt // wkt! { MULTIPOLYGON() } } #[test] fn multi_line_polygon() { let multi_polygon = wkt! { MULTIPOLYGON (((1.0 2.0))) }; assert_eq!(multi_polygon.0.len(), 1); assert_eq!(multi_polygon.0[0].exterior().0[0], coord! { x: 1.0, y: 2.0}); let multi_polygon = wkt! { MULTIPOLYGON (((1.0 2.0,3.0 4.0), (1.1 2.1,3.1 4.1), (1.2 2.2,3.2 4.2)),((1.0 2.0))) }; assert_eq!(multi_polygon.0.len(), 2); assert_eq!( multi_polygon.0[0].interiors()[1].0[0], coord! { x: 1.2, y: 2.2} ); let multi_polygon = wkt! { MULTIPOLYGON (((1.0 2.0,3.0 4.0), (1.1 2.1,3.1 4.1), (1.2 2.2,3.2 4.2)), EMPTY) }; assert_eq!(multi_polygon.0.len(), 2); assert_eq!( multi_polygon.0[0].interiors()[1].0[0], coord! { x: 1.2, y: 2.2} ); assert!(multi_polygon.0[1].exterior().0.is_empty()); } #[test] fn empty_geometry_collection() { let geometry_collection: GeometryCollection = wkt! { GEOMETRYCOLLECTION EMPTY }; assert!(geometry_collection.is_empty()); // This (rightfully) fails to compile because its invalid wkt // wkt! { MULTIPOLYGON() } } #[test] fn geometry_collection() { let geometry_collection = wkt! { GEOMETRYCOLLECTION ( POINT (40.0 10.0), LINESTRING (10.0 10.0, 20.0 20.0, 10.0 40.0), POLYGON ((40.0 40.0, 20.0 45.0, 45.0 30.0, 40.0 40.0)) ) }; assert_eq!(geometry_collection.len(), 3); let line_string = match &geometry_collection[1] { Geometry::LineString(line_string) => line_string, _ => panic!( "unexpected geometry: {geometry:?}", geometry = geometry_collection[1] ), }; assert_eq!(line_string.0[1], coord! {x: 20.0, y: 20.0 }); } #[test] fn rect() { let rect = wkt! { RECT(1.0 2.0,3.0 4.0) }; assert_eq!(rect.min(), coord!(x: 1.0, y: 2.0)); assert_eq!(rect.max(), coord!(x: 3.0, y: 4.0)); } #[test] fn triangle() { let triangle = wkt! { TRIANGLE(0.0 1.0,4.0 2.0,3.0 5.0) }; assert_eq!(triangle.0, coord!(x: 0.0, y: 1.0)); assert_eq!(triangle.1, coord!(x: 4.0, y: 2.0)); assert_eq!(triangle.2, coord!(x: 3.0, y: 5.0)); } #[test] fn other_numeric_types() { let point: Point = wkt!(POINT(1 2)); assert_eq!(point.x(), 1i32); assert_eq!(point.y(), 2i32); let point: Point = wkt!(POINT(1 2)); assert_eq!(point.x(), 1u64); assert_eq!(point.y(), 2u64); let point: Point = wkt!(POINT(1.0 2.0)); assert_eq!(point.x(), 1.0f32); assert_eq!(point.y(), 2.0f32); } }