diff --git a/Cargo.toml b/Cargo.toml index 4634dd1..bce59ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,11 +12,14 @@ edition = "2018" [features] default = ["geo-types"] +geo-traits = ["dep:geo-traits", "dep:bytemuck"] [dependencies] -serde = { version="~1.0", features = ["derive"] } +serde = { version = "~1.0", features = ["derive"] } serde_json = "~1.0" geo-types = { version = "0.7.13", features = ["serde"], optional = true } +geo-traits = { version = "0.2.0", optional = true } +bytemuck = { version = "1", features = ["derive"], optional = true } thiserror = "1.0.20" log = "0.4.17" @@ -39,4 +42,3 @@ harness = false [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] - diff --git a/src/geo_traits_impl/coord.rs b/src/geo_traits_impl/coord.rs new file mode 100644 index 0000000..127b639 --- /dev/null +++ b/src/geo_traits_impl/coord.rs @@ -0,0 +1,47 @@ +use super::PointType; +use geo_traits::Dimensions; + +impl geo_traits::CoordTrait for PointType { + type T = f64; + + fn dim(&self) -> Dimensions { + match self.0.len() { + 0 | 1 => panic!("Position must have at least 2 dimensions"), + 2 => Dimensions::Xy, + 3 => Dimensions::Xyz, + _ => Dimensions::Unknown(self.0.len()), + } + } + + fn x(&self) -> Self::T { + self.0[0] + } + + fn y(&self) -> Self::T { + self.0[1] + } + + fn nth_or_panic(&self, n: usize) -> Self::T { + self.0[n] + } +} + +impl geo_traits::CoordTrait for &PointType { + type T = f64; + + fn dim(&self) -> Dimensions { + PointType::dim(self) + } + + fn x(&self) -> Self::T { + PointType::x(self) + } + + fn y(&self) -> Self::T { + PointType::y(self) + } + + fn nth_or_panic(&self, n: usize) -> Self::T { + PointType::nth_or_panic(self, n) + } +} diff --git a/src/geo_traits_impl/geometry.rs b/src/geo_traits_impl/geometry.rs new file mode 100644 index 0000000..f986124 --- /dev/null +++ b/src/geo_traits_impl/geometry.rs @@ -0,0 +1,490 @@ +use super::{ + GeometryCollectionType, LineStringType, MultiLineStringType, MultiPointType, MultiPolygonType, + PointType, PolygonType, +}; +use bytemuck::TransparentWrapper; +use geo_traits::{ + Dimensions, GeometryCollectionTrait, LineStringTrait, MultiLineStringTrait, MultiPointTrait, + MultiPolygonTrait, PointTrait, PolygonTrait, UnimplementedLine, UnimplementedRect, + UnimplementedTriangle, +}; + +impl geo_traits::GeometryTrait for crate::Value { + type T = f64; + type PointType<'a> = PointType; + type LineStringType<'a> = LineStringType; + type PolygonType<'a> = PolygonType; + type MultiPointType<'a> = MultiPointType; + type MultiLineStringType<'a> = MultiLineStringType; + type MultiPolygonType<'a> = MultiPolygonType; + type GeometryCollectionType<'a> = GeometryCollectionType; + type RectType<'a> = UnimplementedRect; + type TriangleType<'a> = UnimplementedTriangle; + type LineType<'a> = UnimplementedLine; + + fn as_type( + &self, + ) -> geo_traits::GeometryType< + '_, + Self::PointType<'_>, + Self::LineStringType<'_>, + Self::PolygonType<'_>, + Self::MultiPointType<'_>, + Self::MultiLineStringType<'_>, + Self::MultiPolygonType<'_>, + Self::GeometryCollectionType<'_>, + Self::RectType<'_>, + Self::TriangleType<'_>, + Self::LineType<'_>, + > { + match self { + crate::Value::Point(p) => geo_traits::GeometryType::Point(PointType::wrap_ref(p)), + crate::Value::LineString(ls) => { + geo_traits::GeometryType::LineString(LineStringType::wrap_ref(ls)) + } + crate::Value::Polygon(p) => geo_traits::GeometryType::Polygon(PolygonType::wrap_ref(p)), + crate::Value::MultiPoint(mp) => { + geo_traits::GeometryType::MultiPoint(MultiPointType::wrap_ref(mp)) + } + crate::Value::MultiLineString(mls) => { + geo_traits::GeometryType::MultiLineString(MultiLineStringType::wrap_ref(mls)) + } + crate::Value::MultiPolygon(mp) => { + geo_traits::GeometryType::MultiPolygon(MultiPolygonType::wrap_ref(mp)) + } + crate::Value::GeometryCollection(gc) => { + geo_traits::GeometryType::GeometryCollection(GeometryCollectionType::wrap_ref(gc)) + } + } + } + + fn dim(&self) -> Dimensions { + match self { + crate::Value::Point(ref p) => PointType::wrap_ref(p).dim(), + crate::Value::LineString(ref ls) => LineStringType::wrap_ref(ls).dim(), + crate::Value::Polygon(ref p) => PolygonType::wrap_ref(p).dim(), + crate::Value::MultiPoint(ref mp) => MultiPointType::wrap_ref(mp).dim(), + crate::Value::MultiLineString(ref mls) => MultiLineStringType::wrap_ref(mls).dim(), + crate::Value::MultiPolygon(ref mp) => MultiPolygonType::wrap_ref(mp).dim(), + crate::Value::GeometryCollection(ref gc) => GeometryCollectionType::wrap_ref(gc).dim(), + } + } +} + +impl geo_traits::GeometryTrait for &crate::Value { + type T = f64; + type PointType<'b> + = PointType + where + Self: 'b; + type LineStringType<'b> + = LineStringType + where + Self: 'b; + type PolygonType<'b> + = PolygonType + where + Self: 'b; + type MultiPointType<'b> + = MultiPointType + where + Self: 'b; + type MultiLineStringType<'b> + = MultiLineStringType + where + Self: 'b; + type MultiPolygonType<'b> + = MultiPolygonType + where + Self: 'b; + type GeometryCollectionType<'b> + = GeometryCollectionType + where + Self: 'b; + type RectType<'b> + = UnimplementedRect + where + Self: 'b; + type TriangleType<'b> + = UnimplementedTriangle + where + Self: 'b; + type LineType<'b> + = UnimplementedLine + where + Self: 'b; + + fn dim(&self) -> Dimensions { + crate::Value::dim(self) + } + + fn as_type( + &self, + ) -> geo_traits::GeometryType< + '_, + Self::PointType<'_>, + Self::LineStringType<'_>, + Self::PolygonType<'_>, + Self::MultiPointType<'_>, + Self::MultiLineStringType<'_>, + Self::MultiPolygonType<'_>, + Self::GeometryCollectionType<'_>, + Self::RectType<'_>, + Self::TriangleType<'_>, + Self::LineType<'_>, + > { + crate::Value::as_type(self) + } +} + +impl geo_traits::GeometryTrait for crate::Geometry { + type T = f64; + type PointType<'b> = PointType; + type LineStringType<'b> = LineStringType; + type PolygonType<'b> = PolygonType; + type MultiPointType<'b> = MultiPointType; + type MultiLineStringType<'b> = MultiLineStringType; + type MultiPolygonType<'b> = MultiPolygonType; + type GeometryCollectionType<'b> = GeometryCollectionType; + type RectType<'b> = UnimplementedRect; + type TriangleType<'b> = UnimplementedTriangle; + type LineType<'b> = UnimplementedLine; + + fn dim(&self) -> Dimensions { + self.value.dim() + } + + fn as_type( + &self, + ) -> geo_traits::GeometryType< + '_, + Self::PointType<'_>, + Self::LineStringType<'_>, + Self::PolygonType<'_>, + Self::MultiPointType<'_>, + Self::MultiLineStringType<'_>, + Self::MultiPolygonType<'_>, + Self::GeometryCollectionType<'_>, + Self::RectType<'_>, + Self::TriangleType<'_>, + Self::LineType<'_>, + > { + self.value.as_type() + } +} + +impl geo_traits::GeometryTrait for &crate::Geometry { + type T = f64; + type PointType<'b> + = PointType + where + Self: 'b; + type LineStringType<'b> + = LineStringType + where + Self: 'b; + type PolygonType<'b> + = PolygonType + where + Self: 'b; + type MultiPointType<'b> + = MultiPointType + where + Self: 'b; + type MultiLineStringType<'b> + = MultiLineStringType + where + Self: 'b; + type MultiPolygonType<'b> + = MultiPolygonType + where + Self: 'b; + type GeometryCollectionType<'b> + = GeometryCollectionType + where + Self: 'b; + type RectType<'b> + = UnimplementedRect + where + Self: 'b; + type TriangleType<'b> + = UnimplementedTriangle + where + Self: 'b; + type LineType<'b> + = UnimplementedLine + where + Self: 'b; + + fn as_type( + &self, + ) -> geo_traits::GeometryType< + '_, + Self::PointType<'_>, + Self::LineStringType<'_>, + Self::PolygonType<'_>, + Self::MultiPointType<'_>, + Self::MultiLineStringType<'_>, + Self::MultiPolygonType<'_>, + Self::GeometryCollectionType<'_>, + Self::RectType<'_>, + Self::TriangleType<'_>, + Self::LineType<'_>, + > { + crate::Geometry::as_type(self) + } + + fn dim(&self) -> Dimensions { + crate::Geometry::dim(self) + } +} + +impl geo_traits::GeometryTrait for crate::Feature { + type T = f64; + type PointType<'b> = PointType; + type LineStringType<'b> = LineStringType; + type PolygonType<'b> = PolygonType; + type MultiPointType<'b> = MultiPointType; + type MultiLineStringType<'b> = MultiLineStringType; + type MultiPolygonType<'b> = MultiPolygonType; + type GeometryCollectionType<'b> = GeometryCollectionType; + type RectType<'b> = UnimplementedRect; + type TriangleType<'b> = UnimplementedTriangle; + type LineType<'b> = UnimplementedLine; + + fn as_type( + &self, + ) -> geo_traits::GeometryType< + '_, + Self::PointType<'_>, + Self::LineStringType<'_>, + Self::PolygonType<'_>, + Self::MultiPointType<'_>, + Self::MultiLineStringType<'_>, + Self::MultiPolygonType<'_>, + Self::GeometryCollectionType<'_>, + Self::RectType<'_>, + Self::TriangleType<'_>, + Self::LineType<'_>, + > { + match self.geometry { + Some(ref g) => g.as_type(), + None => panic!("GeoJSON feature has no geometry"), + } + } + + fn dim(&self) -> Dimensions { + match self.geometry { + Some(ref g) => g.dim(), + None => panic!("GeoJSON feature has no geometry"), + } + } +} + +impl geo_traits::GeometryTrait for &crate::Feature { + type T = f64; + type PointType<'b> + = PointType + where + Self: 'b; + type LineStringType<'b> + = LineStringType + where + Self: 'b; + type PolygonType<'b> + = PolygonType + where + Self: 'b; + type MultiPointType<'b> + = MultiPointType + where + Self: 'b; + type MultiLineStringType<'b> + = MultiLineStringType + where + Self: 'b; + type MultiPolygonType<'b> + = MultiPolygonType + where + Self: 'b; + type GeometryCollectionType<'b> + = GeometryCollectionType + where + Self: 'b; + type RectType<'b> + = UnimplementedRect + where + Self: 'b; + type TriangleType<'b> + = UnimplementedTriangle + where + Self: 'b; + type LineType<'b> + = UnimplementedLine + where + Self: 'b; + + fn as_type( + &self, + ) -> geo_traits::GeometryType< + '_, + Self::PointType<'_>, + Self::LineStringType<'_>, + Self::PolygonType<'_>, + Self::MultiPointType<'_>, + Self::MultiLineStringType<'_>, + Self::MultiPolygonType<'_>, + Self::GeometryCollectionType<'_>, + Self::RectType<'_>, + Self::TriangleType<'_>, + Self::LineType<'_>, + > { + crate::Feature::as_type(self) + } + + fn dim(&self) -> Dimensions { + crate::Feature::dim(self) + } +} + +impl geo_traits::GeometryTrait for crate::GeoJson { + type T = f64; + type PointType<'b> + = PointType + where + Self: 'b; + type LineStringType<'b> + = LineStringType + where + Self: 'b; + type PolygonType<'b> + = PolygonType + where + Self: 'b; + type MultiPointType<'b> + = MultiPointType + where + Self: 'b; + type MultiLineStringType<'b> + = MultiLineStringType + where + Self: 'b; + type MultiPolygonType<'b> + = MultiPolygonType + where + Self: 'b; + type GeometryCollectionType<'b> + = GeometryCollectionType + where + Self: 'b; + type RectType<'b> + = UnimplementedRect + where + Self: 'b; + type TriangleType<'b> + = UnimplementedTriangle + where + Self: 'b; + type LineType<'b> + = UnimplementedLine + where + Self: 'b; + + fn dim(&self) -> Dimensions { + match self { + crate::GeoJson::Feature(f) => f.dim(), + crate::GeoJson::FeatureCollection(fc) => fc.dim(), + crate::GeoJson::Geometry(g) => g.dim(), + } + } + + fn as_type( + &self, + ) -> geo_traits::GeometryType< + '_, + Self::PointType<'_>, + Self::LineStringType<'_>, + Self::PolygonType<'_>, + Self::MultiPointType<'_>, + Self::MultiLineStringType<'_>, + Self::MultiPolygonType<'_>, + Self::GeometryCollectionType<'_>, + Self::RectType<'_>, + Self::TriangleType<'_>, + Self::LineType<'_>, + > { + match self { + crate::GeoJson::Feature(f) => f.as_type(), + crate::GeoJson::FeatureCollection(_fc) => { + unimplemented!("TODO") + } + crate::GeoJson::Geometry(g) => g.as_type(), + } + } +} + +impl geo_traits::GeometryTrait for &crate::GeoJson { + type T = f64; + type PointType<'b> + = PointType + where + Self: 'b; + type LineStringType<'b> + = LineStringType + where + Self: 'b; + type PolygonType<'b> + = PolygonType + where + Self: 'b; + type MultiPointType<'b> + = MultiPointType + where + Self: 'b; + type MultiLineStringType<'b> + = MultiLineStringType + where + Self: 'b; + type MultiPolygonType<'b> + = MultiPolygonType + where + Self: 'b; + type GeometryCollectionType<'b> + = GeometryCollectionType + where + Self: 'b; + type RectType<'b> + = UnimplementedRect + where + Self: 'b; + type TriangleType<'b> + = UnimplementedTriangle + where + Self: 'b; + type LineType<'b> + = UnimplementedLine + where + Self: 'b; + + fn dim(&self) -> Dimensions { + crate::GeoJson::dim(self) + } + + fn as_type( + &self, + ) -> geo_traits::GeometryType< + '_, + Self::PointType<'_>, + Self::LineStringType<'_>, + Self::PolygonType<'_>, + Self::MultiPointType<'_>, + Self::MultiLineStringType<'_>, + Self::MultiPolygonType<'_>, + Self::GeometryCollectionType<'_>, + Self::RectType<'_>, + Self::TriangleType<'_>, + Self::LineType<'_>, + > { + crate::GeoJson::as_type(self) + } +} diff --git a/src/geo_traits_impl/geometry_collection.rs b/src/geo_traits_impl/geometry_collection.rs new file mode 100644 index 0000000..249b572 --- /dev/null +++ b/src/geo_traits_impl/geometry_collection.rs @@ -0,0 +1,122 @@ +use super::GeometryCollectionType; +use geo_traits::{Dimensions, GeometryTrait}; + +impl geo_traits::GeometryCollectionTrait for GeometryCollectionType { + type T = f64; + type GeometryType<'b> + = &'b crate::Geometry + where + Self: 'b; + + fn dim(&self) -> Dimensions { + self.geometry(0).map_or(Dimensions::Unknown(0), |g| g.dim()) + } + + fn geometries( + &self, + ) -> impl DoubleEndedIterator + ExactSizeIterator> { + self.0.iter() + } + + fn geometry(&self, i: usize) -> Option> { + self.0.get(i) + } + + unsafe fn geometry_unchecked(&self, i: usize) -> Self::GeometryType<'_> { + self.0.get_unchecked(i) + } + + fn num_geometries(&self) -> usize { + self.0.len() + } +} + +impl<'a> geo_traits::GeometryCollectionTrait for &'a GeometryCollectionType { + type T = f64; + type GeometryType<'b> + = &'b crate::Geometry + where + Self: 'b; + + fn dim(&self) -> Dimensions { + GeometryCollectionType::dim(self) + } + + fn geometries( + &self, + ) -> impl DoubleEndedIterator + ExactSizeIterator> { + GeometryCollectionType::geometries(self) + } + + fn geometry(&self, i: usize) -> Option> { + GeometryCollectionType::geometry(self, i) + } + + unsafe fn geometry_unchecked(&self, i: usize) -> Self::GeometryType<'_> { + GeometryCollectionType::geometry_unchecked(self, i) + } + + fn num_geometries(&self) -> usize { + GeometryCollectionType::num_geometries(self) + } +} + +impl geo_traits::GeometryCollectionTrait for crate::FeatureCollection { + type T = f64; + type GeometryType<'b> = &'b crate::Feature; + + fn dim(&self) -> Dimensions { + self.features + .first() + .and_then(|f| f.geometry.as_ref()) + .map_or(Dimensions::Unknown(0), |g| g.dim()) + } + + fn geometries( + &self, + ) -> impl DoubleEndedIterator + ExactSizeIterator> { + self.features.iter() + } + + fn geometry(&self, i: usize) -> Option> { + self.features.get(i) + } + + unsafe fn geometry_unchecked(&self, i: usize) -> Self::GeometryType<'_> { + self.features.get_unchecked(i) + } + + fn num_geometries(&self) -> usize { + self.features.len() + } +} + +impl<'a> geo_traits::GeometryCollectionTrait for &'a crate::FeatureCollection { + type T = f64; + type GeometryType<'b> + = &'b crate::Feature + where + Self: 'b; + + fn dim(&self) -> Dimensions { + crate::FeatureCollection::dim(self) + } + + fn geometries( + &self, + ) -> impl DoubleEndedIterator + ExactSizeIterator> { + crate::FeatureCollection::geometries(self) + } + + fn geometry(&self, i: usize) -> Option> { + crate::FeatureCollection::geometry(self, i) + } + + unsafe fn geometry_unchecked(&self, i: usize) -> Self::GeometryType<'_> { + crate::FeatureCollection::geometry_unchecked(self, i) + } + + fn num_geometries(&self) -> usize { + crate::FeatureCollection::num_geometries(self) + } +} diff --git a/src/geo_traits_impl/line_string.rs b/src/geo_traits_impl/line_string.rs new file mode 100644 index 0000000..7d8eecb --- /dev/null +++ b/src/geo_traits_impl/line_string.rs @@ -0,0 +1,51 @@ +use super::{LineStringType, PointType}; +use bytemuck::TransparentWrapper; +use geo_traits::{Dimensions, LineStringTrait, PointTrait}; + +impl LineStringTrait for LineStringType { + type T = f64; + type CoordType<'b> + = &'b PointType + where + Self: 'b; + + fn num_coords(&self) -> usize { + self.0.len() + } + + fn coord(&self, i: usize) -> Option> { + Some(PointType::wrap_ref(&self.0[i])) + } + + unsafe fn coord_unchecked(&self, i: usize) -> Self::CoordType<'_> { + self.coord(i).unwrap() + } + + fn dim(&self) -> Dimensions { + self.coord(0).map_or(Dimensions::Unknown(0), |p| p.dim()) + } +} + +impl geo_traits::LineStringTrait for &LineStringType { + type T = f64; + type CoordType<'b> + = &'b PointType + where + Self: 'b; + + fn num_coords(&self) -> usize { + LineStringType::num_coords(self) + } + + fn coord(&self, i: usize) -> Option> { + LineStringType::coord(self, i) + } + + unsafe fn coord_unchecked(&self, i: usize) -> Self::CoordType<'_> { + LineStringType::coord_unchecked(self, i) + } + + fn dim(&self) -> Dimensions { + LineStringType::dim(self) + } +} diff --git a/src/geo_traits_impl/mod.rs b/src/geo_traits_impl/mod.rs new file mode 100644 index 0000000..83af361 --- /dev/null +++ b/src/geo_traits_impl/mod.rs @@ -0,0 +1,57 @@ +mod coord; +mod geometry; +mod geometry_collection; +mod line_string; +mod multi_line_string; +mod multi_point; +mod multi_polygon; +mod point; +mod polygon; + +// These structures are needed because we can't implement traits on types like +// `geojson::PointType` because they are just type aliases of raw types like +// `Vec`. + +#[derive(bytemuck::TransparentWrapper)] +#[repr(transparent)] +pub struct PointType(crate::Position); + +#[derive(bytemuck::TransparentWrapper)] +#[repr(transparent)] +pub struct LineStringType(crate::LineStringType); + +#[derive(bytemuck::TransparentWrapper)] +#[repr(transparent)] +pub struct PolygonType(crate::PolygonType); + +#[derive(bytemuck::TransparentWrapper)] +#[repr(transparent)] +pub struct MultiPointType(Vec); + +#[derive(bytemuck::TransparentWrapper)] +#[repr(transparent)] +pub struct MultiLineStringType(Vec); + +#[derive(bytemuck::TransparentWrapper)] +#[repr(transparent)] +pub struct MultiPolygonType(Vec); + +#[derive(bytemuck::TransparentWrapper)] +#[repr(transparent)] +pub struct GeometryCollectionType(Vec); + +#[cfg(test)] +mod test { + #[test] + fn test_implementation() { + let geojson_str = include_str!("../../tests/fixtures/countries.geojson"); + let geojson = geojson_str.parse::().unwrap(); + let area = area(geojson); + assert_eq!(area, 0.0); + } + + // Example to demonstrate usage of geo-traits + fn area(_g: impl geo_traits::GeometryTrait) -> f64 { + 0. + } +} diff --git a/src/geo_traits_impl/multi_line_string.rs b/src/geo_traits_impl/multi_line_string.rs new file mode 100644 index 0000000..f1d753e --- /dev/null +++ b/src/geo_traits_impl/multi_line_string.rs @@ -0,0 +1,64 @@ +use super::{LineStringType, MultiLineStringType}; +use bytemuck::TransparentWrapper; +use geo_traits::{Dimensions, LineStringTrait}; + +impl geo_traits::MultiLineStringTrait for MultiLineStringType { + type T = f64; + type LineStringType<'b> + = &'b LineStringType + where + Self: 'b; + + fn num_line_strings(&self) -> usize { + self.0.len() + } + + fn dim(&self) -> Dimensions { + self.line_string(0) + .map_or(Dimensions::Unknown(0), |ls| ls.dim()) + } + + fn line_string(&self, i: usize) -> Option> { + self.0.get(i).map(LineStringType::wrap_ref) + } + + unsafe fn line_string_unchecked(&self, i: usize) -> Self::LineStringType<'_> { + LineStringType::wrap_ref(self.0.get_unchecked(i)) + } + + fn line_strings( + &self, + ) -> impl DoubleEndedIterator + ExactSizeIterator> { + self.0.iter().map(LineStringType::wrap_ref) + } +} + +impl<'a> geo_traits::MultiLineStringTrait for &'a MultiLineStringType { + type T = f64; + type LineStringType<'b> + = &'b LineStringType + where + Self: 'b; + + fn num_line_strings(&self) -> usize { + MultiLineStringType::num_line_strings(self) + } + + fn dim(&self) -> Dimensions { + MultiLineStringType::dim(self) + } + + fn line_string(&self, i: usize) -> Option> { + MultiLineStringType::line_string(self, i) + } + + unsafe fn line_string_unchecked(&self, i: usize) -> Self::LineStringType<'_> { + MultiLineStringType::line_string_unchecked(self, i) + } + + fn line_strings( + &self, + ) -> impl DoubleEndedIterator + ExactSizeIterator> { + MultiLineStringType::line_strings(self) + } +} diff --git a/src/geo_traits_impl/multi_point.rs b/src/geo_traits_impl/multi_point.rs new file mode 100644 index 0000000..968cd62 --- /dev/null +++ b/src/geo_traits_impl/multi_point.rs @@ -0,0 +1,59 @@ +use super::{MultiPointType, PointType}; +use bytemuck::TransparentWrapper; +use geo_traits::{Dimensions, PointTrait}; + +impl geo_traits::MultiPointTrait for MultiPointType { + type T = f64; + type PointType<'b> + = &'b PointType + where + Self: 'b; + + fn num_points(&self) -> usize { + self.0.len() + } + + fn dim(&self) -> Dimensions { + self.point(0).map_or(Dimensions::Unknown(0), |p| p.dim()) + } + + fn point(&self, i: usize) -> Option> { + self.0.get(i).map(PointType::wrap_ref) + } + + unsafe fn point_unchecked(&self, i: usize) -> Self::PointType<'_> { + PointType::wrap_ref(self.0.get_unchecked(i)) + } + + fn points(&self) -> impl DoubleEndedIterator + ExactSizeIterator> { + self.0.iter().map(PointType::wrap_ref) + } +} + +impl<'a> geo_traits::MultiPointTrait for &'a MultiPointType { + type T = f64; + type PointType<'b> + = &'b PointType + where + Self: 'b; + + fn num_points(&self) -> usize { + MultiPointType::num_points(self) + } + + fn dim(&self) -> Dimensions { + MultiPointType::dim(self) + } + + fn point(&self, i: usize) -> Option> { + MultiPointType::point(self, i) + } + + unsafe fn point_unchecked(&self, i: usize) -> Self::PointType<'_> { + MultiPointType::point_unchecked(self, i) + } + + fn points(&self) -> impl DoubleEndedIterator + ExactSizeIterator> { + MultiPointType::points(self) + } +} diff --git a/src/geo_traits_impl/multi_polygon.rs b/src/geo_traits_impl/multi_polygon.rs new file mode 100644 index 0000000..4740a60 --- /dev/null +++ b/src/geo_traits_impl/multi_polygon.rs @@ -0,0 +1,63 @@ +use super::{MultiPolygonType, PolygonType}; +use bytemuck::TransparentWrapper; +use geo_traits::{Dimensions, PolygonTrait}; + +impl geo_traits::MultiPolygonTrait for MultiPolygonType { + type T = f64; + type PolygonType<'b> + = &'b PolygonType + where + Self: 'b; + + fn num_polygons(&self) -> usize { + self.0.len() + } + + fn dim(&self) -> Dimensions { + self.polygon(0).map_or(Dimensions::Unknown(0), |p| p.dim()) + } + + fn polygon(&self, i: usize) -> Option> { + self.0.get(i).map(PolygonType::wrap_ref) + } + + unsafe fn polygon_unchecked(&self, i: usize) -> Self::PolygonType<'_> { + PolygonType::wrap_ref(self.0.get_unchecked(i)) + } + + fn polygons( + &self, + ) -> impl DoubleEndedIterator + ExactSizeIterator> { + self.0.iter().map(PolygonType::wrap_ref) + } +} + +impl<'a> geo_traits::MultiPolygonTrait for &'a MultiPolygonType { + type T = f64; + type PolygonType<'b> + = &'b PolygonType + where + Self: 'b; + + fn num_polygons(&self) -> usize { + MultiPolygonType::num_polygons(self) + } + + fn dim(&self) -> Dimensions { + MultiPolygonType::dim(self) + } + + fn polygon(&self, i: usize) -> Option> { + MultiPolygonType::polygon(self, i) + } + + unsafe fn polygon_unchecked(&self, i: usize) -> Self::PolygonType<'_> { + MultiPolygonType::polygon_unchecked(self, i) + } + + fn polygons( + &self, + ) -> impl DoubleEndedIterator + ExactSizeIterator> { + MultiPolygonType::polygons(self) + } +} diff --git a/src/geo_traits_impl/point.rs b/src/geo_traits_impl/point.rs new file mode 100644 index 0000000..e6bc236 --- /dev/null +++ b/src/geo_traits_impl/point.rs @@ -0,0 +1,34 @@ +use super::PointType; +use geo_traits::{Dimensions, PointTrait}; + +impl PointTrait for PointType { + type T = f64; + type CoordType<'b> + = &'b PointType + where + Self: 'b; + + fn coord(&self) -> Option> { + Some(self) + } + + fn dim(&self) -> Dimensions { + ::dim(self) + } +} + +impl geo_traits::PointTrait for &PointType { + type T = f64; + type CoordType<'b> + = &'b PointType + where + Self: 'b; + + fn coord(&self) -> Option> { + PointType::coord(self) + } + + fn dim(&self) -> Dimensions { + PointType::dim(self) + } +} diff --git a/src/geo_traits_impl/polygon.rs b/src/geo_traits_impl/polygon.rs new file mode 100644 index 0000000..2fa3a9f --- /dev/null +++ b/src/geo_traits_impl/polygon.rs @@ -0,0 +1,68 @@ +use super::{LineStringType, PolygonType}; +use bytemuck::TransparentWrapper; +use geo_traits::{Dimensions, LineStringTrait}; + +impl geo_traits::PolygonTrait for PolygonType { + type T = f64; + type RingType<'b> + = &'b LineStringType + where + Self: 'b; + + fn exterior(&self) -> Option> { + self.0.first().map(LineStringType::wrap_ref) + } + + fn num_interiors(&self) -> usize { + self.0.len() - 1 + } + + fn interior(&self, i: usize) -> Option> { + self.0.get(i + 1).map(LineStringType::wrap_ref) + } + + fn dim(&self) -> Dimensions { + self.exterior() + .map_or(Dimensions::Unknown(0), |ls| ls.dim()) + } + + unsafe fn interior_unchecked(&self, i: usize) -> Self::RingType<'_> { + LineStringType::wrap_ref(self.0.get_unchecked(i + 1)) + } + + fn interiors(&self) -> impl DoubleEndedIterator + ExactSizeIterator> { + self.0.iter().skip(1).map(LineStringType::wrap_ref) + } +} + +impl<'a> geo_traits::PolygonTrait for &'a PolygonType { + type T = f64; + type RingType<'b> + = &'b LineStringType + where + Self: 'b; + + fn exterior(&self) -> Option> { + PolygonType::exterior(self) + } + + fn num_interiors(&self) -> usize { + PolygonType::num_interiors(self) + } + + fn interior(&self, i: usize) -> Option> { + PolygonType::interior(self, i) + } + + fn dim(&self) -> Dimensions { + PolygonType::dim(self) + } + + unsafe fn interior_unchecked(&self, i: usize) -> Self::RingType<'_> { + PolygonType::interior_unchecked(self, i) + } + + fn interiors(&self) -> impl DoubleEndedIterator + ExactSizeIterator> { + PolygonType::interiors(self) + } +} diff --git a/src/lib.rs b/src/lib.rs index b164a8d..4fcb29a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -408,6 +408,9 @@ pub use crate::errors::{Error, Result}; #[cfg(feature = "geo-types")] mod conversion; +#[cfg(feature = "geo-traits")] +mod geo_traits_impl; + /// Build your struct from GeoJSON using [`serde`] pub mod de;