Skip to main content

geo_types/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![warn(missing_debug_implementations)]
3#![doc(html_logo_url = "https://raw.githubusercontent.com/georust/meta/master/logo/logo.png")]
4//! The `geo-types` library defines geometric types for the [GeoRust] ecosystem.
5//!
6//! In most cases, you will only need to use this crate if you’re a crate author and want
7//! compatibility with other GeoRust crates. Otherwise, the [`geo`](https://crates.io/crates/geo)
8//! crate re-exports these types and additionally provides geospatial algorithms.
9//!
10//! ## Geometries
11//!
12//! - **[`Point`]**: A single point represented by one [`Coord`]
13//! - **[`MultiPoint`]**: A collection of [`Point`]s
14//! - **[`Line`]**: A line segment represented by two [`Coord`]s
15//! - **[`LineString`]**: A series of contiguous line segments represented by two or more
16//!   [`Coord`]s
17//! - **[`MultiLineString`]**: A collection of [`LineString`]s
18//! - **[`Polygon`]**: A bounded area represented by one [`LineString`] exterior ring, and zero or
19//!   more [`LineString`] interior rings
20//! - **[`MultiPolygon`]**: A collection of [`Polygon`]s
21//! - **[`Rect`]**: An axis-aligned bounded rectangle represented by minimum and maximum
22//!   [`Coord`]s
23//! - **[`Triangle`]**: A bounded area represented by three [`Coord`] vertices
24//! - **[`GeometryCollection`]**: A collection of [`Geometry`]s
25//! - **[`Geometry`]**: An enumeration of all geometry types, excluding [`Coord`]
26//!
27//! ## Coordinates and Numeric Types
28//!
29//! - **[`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.
30//!
31//! 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.
32//!
33//! ```rust
34//! use geo_types::Point;
35//!
36//! // Geometries are f64 by default
37//! let point: Point = Point::new(1.0, 2.0);
38//! assert_eq!(std::mem::size_of::<Point>(), 64 * 2 / 8);
39//!
40//! // You can be explicit about the numeric type.
41//! let f64_point: Point<f64> = Point::new(1.0, 2.0);
42//! assert_eq!(std::mem::size_of::<Point<f64>>(), 64 * 2 / 8);
43//!
44//! // Or specify some non-default numeric type
45//! let f32_point: Point<f32> = Point::new(1.0, 2.0);
46//! assert_eq!(std::mem::size_of::<Point<f32>>(), 32 * 2 / 8);
47//!
48//! // Integer geometries are supported too, though not all
49//! // algorithms will be implemented for all numeric types.
50//! let i32_point: Point<i32> = Point::new(1, 2);
51//! assert_eq!(std::mem::size_of::<Point<i32>>(), 32 * 2 / 8);
52//! ```
53//!
54//! # Semantics
55//!
56//! The geospatial types provided here aim to adhere to the [OpenGIS Simple feature access][OGC-SFA]
57//! standards. Thus, the types here are inter-operable with other implementations of the standards:
58//! [JTS], [GEOS], etc.
59//!
60//! # Features
61//!
62//! The following optional [Cargo features] are available:
63//!
64//! - `std`: Enables use of the full `std` library. Enabled by default.
65//! - `multithreading`: Enables multi-threaded iteration over `Multi*` geometries. **Disabled**
66//!   by default but **enabled** by `geo`'s default features.
67//! - `approx`: Allows geometry types to be checked for approximate equality with [approx]
68//! - `arbitrary`: Allows geometry types to be created from unstructured input with [arbitrary]
69//! - `serde`: Allows geometry types to be serialized and deserialized with [Serde]
70//! - `use-rstar_0_8`: Allows geometry types to be inserted into [rstar] R*-trees (`rstar v0.8`)
71//! - `use-rstar_0_9`: Allows geometry types to be inserted into [rstar] R*-trees (`rstar v0.9`)
72//! - `use-rstar_0_10`: Allows geometry types to be inserted into [rstar] R*-trees (`rstar v0.10`)
73//! - `use-rstar_0_11`: Allows geometry types to be inserted into [rstar] R*-trees (`rstar v0.11`)
74//! - `use-rstar_0_12`: Allows geometry types to be inserted into [rstar] R*-trees (`rstar v0.12`)
75//! - `rstar_0_13`: Allows geometry types to be inserted into [rstar] R*-trees (`rstar v0.13`)
76//!
77//! This library can be used in `#![no_std]` environments if the default `std` feature is disabled. At
78//! the moment, the `arbitrary` and `use-rstar_0_8` features require `std`. This may change in a
79//! future release.
80//!
81//! [approx]: https://github.com/brendanzab/approx
82//! [arbitrary]: https://github.com/rust-fuzz/arbitrary
83//! [Cargo features]: https://doc.rust-lang.org/cargo/reference/features.html
84//! [GeoRust]: https://georust.org
85//! [GEOS]: https://trac.osgeo.org/geos
86//! [JTS]: https://github.com/locationtech/jts
87//! [OGC-SFA]: https://www.ogc.org/standards/sfa
88//! [rstar]: https://github.com/Stoeoef/rstar
89//! [Serde]: https://serde.rs/
90extern crate alloc;
91
92use core::fmt::Debug;
93use num_traits::{Float, Num, NumCast};
94
95#[cfg(feature = "serde")]
96#[macro_use]
97extern crate serde;
98
99#[cfg(test)]
100#[macro_use]
101extern crate approx;
102
103#[deprecated(since = "0.7.0", note = "use `CoordFloat` or `CoordNum` instead")]
104pub trait CoordinateType: Num + Copy + NumCast + PartialOrd + Debug {}
105#[allow(deprecated)]
106impl<T: Num + Copy + NumCast + PartialOrd + Debug> CoordinateType for T {}
107
108/// For algorithms which can use both integer **and** floating point `Point`s/`Coord`s
109///
110/// Floats (`f32` and `f64`) and Integers (`u8`, `i32` etc.) implement this.
111///
112/// For algorithms which only make sense for floating point, like area or length calculations,
113/// see [CoordFloat](trait.CoordFloat.html).
114#[allow(deprecated)]
115pub trait CoordNum: CoordinateType + Debug {}
116#[allow(deprecated)]
117impl<T: CoordinateType + Debug> CoordNum for T {}
118
119/// For algorithms which can only use floating point `Point`s/`Coord`s, like area or length calculations
120pub trait CoordFloat: CoordNum + Float {}
121impl<T: CoordNum + Float> CoordFloat for T {}
122
123pub mod geometry;
124pub use geometry::*;
125
126pub use geometry::line_string::PointsIter;
127
128#[allow(deprecated)]
129pub use geometry::rect::InvalidRectCoordinatesError;
130
131mod error;
132pub use error::Error;
133
134#[macro_use]
135mod macros;
136
137#[macro_use]
138mod wkt_macro;
139
140#[cfg(feature = "arbitrary")]
141mod arbitrary;
142
143#[cfg(any(
144    feature = "rstar_0_8",
145    feature = "rstar_0_9",
146    feature = "rstar_0_10",
147    feature = "rstar_0_11",
148    feature = "rstar_0_12",
149    feature = "rstar_0_13"
150))]
151#[doc(hidden)]
152pub mod private_utils;
153
154mod debug;
155
156#[doc(hidden)]
157pub mod _alloc {
158    //! Needed to access these types from `alloc` in macros when the std feature is
159    //! disabled and the calling context is missing `extern crate alloc`. These are
160    //! _not_ meant for public use.
161    pub use ::alloc::vec;
162}
163
164// Some gymnastics to help migrate people off our old feature flag naming conventions
165#[allow(unused, non_camel_case_types, missing_debug_implementations)]
166mod deprecated_feature_flags {
167    #[cfg_attr(
168        not(feature = "__allow_deprecated_features"),
169        deprecated(
170            since = "0.7.18",
171            note = "The `use-rstar` feature has been renamed to simply `rstar`. Use the `rstar` feature instead."
172        )
173    )]
174    pub struct UseRstar;
175
176    #[cfg_attr(
177        not(feature = "__allow_deprecated_features"),
178        deprecated(
179            since = "0.7.18",
180            note = "The `use-rstar_0_8` feature has been renamed to simply `rstar_0_8`. Use the `rstar_0_8` feature instead."
181        )
182    )]
183    pub struct UseRstar_0_8;
184
185    #[cfg_attr(
186        not(feature = "__allow_deprecated_features"),
187        deprecated(
188            since = "0.7.18",
189            note = "The `use-rstar_0_9` feature has been renamed to simply `rstar_0_9`. Use the `rstar_0_9` feature instead."
190        )
191    )]
192    pub struct UseRstar_0_9;
193
194    #[cfg_attr(
195        not(feature = "__allow_deprecated_features"),
196        deprecated(
197            since = "0.7.18",
198            note = "The `use-rstar_0_10` feature has been renamed to simply `rstar_0_10`. Use the `rstar_0_10` feature instead."
199        )
200    )]
201    pub struct UseRstar_0_10;
202
203    #[cfg_attr(
204        not(feature = "__allow_deprecated_features"),
205        deprecated(
206            since = "0.7.18",
207            note = "The `use-rstar_0_11` feature has been renamed to simply `rstar_0_11`. Use the `rstar_0_11` feature instead."
208        )
209    )]
210    pub struct UseRstar_0_11;
211
212    #[cfg_attr(
213        not(feature = "__allow_deprecated_features"),
214        deprecated(
215            since = "0.7.18",
216            note = "The `use-rstar_0_12` feature has been renamed to simply `rstar_0_12`. Use the `rstar_0_12` feature instead."
217        )
218    )]
219    pub struct UseRstar_0_12;
220}
221
222#[cfg(feature = "use-rstar")]
223pub use deprecated_feature_flags::UseRstar;
224
225#[cfg(feature = "use-rstar_0_8")]
226pub use deprecated_feature_flags::UseRstar_0_8;
227
228#[cfg(feature = "use-rstar_0_9")]
229pub use deprecated_feature_flags::UseRstar_0_9;
230
231#[cfg(feature = "use-rstar_0_10")]
232pub use deprecated_feature_flags::UseRstar_0_10;
233
234#[cfg(feature = "use-rstar_0_11")]
235pub use deprecated_feature_flags::UseRstar_0_11;
236
237#[cfg(feature = "use-rstar_0_12")]
238pub use deprecated_feature_flags::UseRstar_0_12;
239
240#[cfg(test)]
241mod tests {
242    use alloc::vec;
243
244    use super::*;
245    use core::convert::TryFrom;
246
247    #[test]
248    fn type_test() {
249        let c = coord! {
250            x: 40.02f64,
251            y: 116.34,
252        };
253
254        let p = Point::from(c);
255
256        let Point(c2) = p;
257        assert_eq!(c, c2);
258        assert_relative_eq!(c.x, c2.x);
259        assert_relative_eq!(c.y, c2.y);
260
261        let p: Point<f32> = (0f32, 1f32).into();
262        assert_relative_eq!(p.x(), 0.);
263        assert_relative_eq!(p.y(), 1.);
264    }
265
266    #[test]
267    fn convert_types() {
268        let p: Point<f32> = Point::new(0., 0.);
269        let p1 = p;
270        let g: Geometry<f32> = p.into();
271        let p2 = Point::try_from(g).unwrap();
272        assert_eq!(p1, p2);
273    }
274
275    #[test]
276    fn polygon_new_test() {
277        let exterior = LineString::new(vec![
278            coord! { x: 0., y: 0. },
279            coord! { x: 1., y: 1. },
280            coord! { x: 1., y: 0. },
281            coord! { x: 0., y: 0. },
282        ]);
283        let interiors = vec![LineString::new(vec![
284            coord! { x: 0.1, y: 0.1 },
285            coord! { x: 0.9, y: 0.9 },
286            coord! { x: 0.9, y: 0.1 },
287            coord! { x: 0.1, y: 0.1 },
288        ])];
289        let p = Polygon::new(exterior.clone(), interiors.clone());
290
291        assert_eq!(p.exterior(), &exterior);
292        assert_eq!(p.interiors(), &interiors[..]);
293    }
294
295    #[test]
296    fn iters() {
297        let _: MultiPoint<_> = vec![(0., 0.), (1., 2.)].into();
298        let _: MultiPoint<_> = vec![(0., 0.), (1., 2.)].into_iter().collect();
299
300        let mut l1: LineString<_> = vec![(0., 0.), (1., 2.)].into();
301        assert_eq!(l1[1], coord! { x: 1., y: 2. }); // index into linestring
302        let _: LineString<_> = vec![(0., 0.), (1., 2.)].into_iter().collect();
303
304        // index mutably into a linestring
305        l1[0] = coord! { x: 1., y: 1. };
306        assert_eq!(l1, vec![(1., 1.), (1., 2.)].into());
307    }
308
309    #[test]
310    fn test_coordinate_types() {
311        let p: Point<u8> = Point::new(0, 0);
312        assert_eq!(p.x(), 0u8);
313
314        let p: Point<i64> = Point::new(1_000_000, 0);
315        assert_eq!(p.x(), 1_000_000i64);
316    }
317
318    #[cfg(feature = "rstar_0_8")]
319    #[test]
320    /// ensure Line's SpatialObject impl is correct
321    fn line_test() {
322        use rstar_0_8::primitives::Line as RStarLine;
323        use rstar_0_8::{PointDistance, RTreeObject};
324
325        let rl = RStarLine::new(Point::new(0.0, 0.0), Point::new(5.0, 5.0));
326        let l = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 5., y: 5. });
327        assert_eq!(rl.envelope(), l.envelope());
328        // difference in 15th decimal place
329        assert_relative_eq!(26.0, rl.distance_2(&Point::new(4.0, 10.0)));
330        assert_relative_eq!(25.999999999999996, l.distance_2(&Point::new(4.0, 10.0)));
331    }
332
333    #[cfg(feature = "rstar_0_9")]
334    #[test]
335    /// ensure Line's SpatialObject impl is correct
336    fn line_test_0_9() {
337        use rstar_0_9::primitives::Line as RStarLine;
338        use rstar_0_9::{PointDistance, RTreeObject};
339
340        let rl = RStarLine::new(Point::new(0.0, 0.0), Point::new(5.0, 5.0));
341        let l = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 5., y: 5. });
342        assert_eq!(rl.envelope(), l.envelope());
343        // difference in 15th decimal place
344        assert_relative_eq!(26.0, rl.distance_2(&Point::new(4.0, 10.0)));
345        assert_relative_eq!(25.999999999999996, l.distance_2(&Point::new(4.0, 10.0)));
346    }
347
348    #[cfg(feature = "rstar_0_10")]
349    #[test]
350    /// ensure Line's SpatialObject impl is correct
351    fn line_test_0_10() {
352        use rstar_0_10::primitives::Line as RStarLine;
353        use rstar_0_10::{PointDistance, RTreeObject};
354
355        let rl = RStarLine::new(Point::new(0.0, 0.0), Point::new(5.0, 5.0));
356        let l = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 5., y: 5. });
357        assert_eq!(rl.envelope(), l.envelope());
358        // difference in 15th decimal place
359        assert_relative_eq!(26.0, rl.distance_2(&Point::new(4.0, 10.0)));
360        assert_relative_eq!(25.999999999999996, l.distance_2(&Point::new(4.0, 10.0)));
361    }
362
363    #[cfg(feature = "rstar_0_11")]
364    #[test]
365    /// ensure Line's SpatialObject impl is correct
366    fn line_test_0_11() {
367        use rstar_0_11::primitives::Line as RStarLine;
368        use rstar_0_11::{PointDistance, RTreeObject};
369
370        let rl = RStarLine::new(Point::new(0.0, 0.0), Point::new(5.0, 5.0));
371        let l = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 5., y: 5. });
372        assert_eq!(rl.envelope(), l.envelope());
373        // difference in 15th decimal place
374        assert_relative_eq!(26.0, rl.distance_2(&Point::new(4.0, 10.0)));
375        assert_relative_eq!(25.999999999999996, l.distance_2(&Point::new(4.0, 10.0)));
376    }
377
378    #[cfg(feature = "rstar_0_12")]
379    #[test]
380    /// ensure Line's SpatialObject impl is correct
381    fn line_test_0_12() {
382        use rstar_0_12::primitives::Line as RStarLine;
383        use rstar_0_12::{PointDistance, RTreeObject};
384
385        let rl = RStarLine::new(Point::new(0.0, 0.0), Point::new(5.0, 5.0));
386        let l = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 5., y: 5. });
387        assert_eq!(rl.envelope(), l.envelope());
388        // difference in 15th decimal place
389        assert_relative_eq!(26.0, rl.distance_2(&Point::new(4.0, 10.0)));
390        assert_relative_eq!(25.999999999999996, l.distance_2(&Point::new(4.0, 10.0)));
391    }
392
393    #[cfg(feature = "rstar_0_13")]
394    #[test]
395    /// ensure Line's SpatialObject impl is correct
396    fn line_test_0_13() {
397        use rstar_0_13::primitives::Line as RStarLine;
398        use rstar_0_13::{PointDistance, RTreeObject};
399
400        let rl = RStarLine::new(Point::new(0.0, 0.0), Point::new(5.0, 5.0));
401        let l = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 5., y: 5. });
402        assert_eq!(rl.envelope(), l.envelope());
403        // difference in 15th decimal place
404        assert_relative_eq!(26.0, rl.distance_2(&Point::new(4.0, 10.0)));
405        assert_relative_eq!(25.999999999999996, l.distance_2(&Point::new(4.0, 10.0)));
406    }
407
408    #[test]
409    fn test_rects() {
410        let r = Rect::new(coord! { x: -1., y: -1. }, coord! { x: 1., y: 1. });
411        let p: Polygon<_> = r.into();
412        assert_eq!(
413            p,
414            Polygon::new(
415                vec![(-1., -1.), (1., -1.), (1., 1.), (-1., 1.), (-1., -1.)].into(),
416                vec![]
417            )
418        );
419    }
420}