Skip to content

Commit

Permalink
Merge pull request #123 from georust/kyle/geo-traits
Browse files Browse the repository at this point in the history
Implement geo-traits for reading from WKT
  • Loading branch information
michaelkirk authored Nov 26, 2024
2 parents b2f7399 + 779e519 commit cdbff53
Show file tree
Hide file tree
Showing 10 changed files with 590 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ edition = "2021"

[dependencies]
geo-types = { version = "0.7.8", optional = true }
geo-traits = "0.2"
num-traits = "0.2"
serde = { version = "1.0", default-features = false, optional = true }
thiserror = "1.0.23"
Expand Down
194 changes: 194 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ use std::default::Default;
use std::fmt;
use std::str::FromStr;

use geo_traits::{
GeometryCollectionTrait, GeometryTrait, LineStringTrait, MultiLineStringTrait, MultiPointTrait,
MultiPolygonTrait, PointTrait, PolygonTrait,
};
use num_traits::{Float, Num, NumCast};

use crate::tokenizer::{PeekableTokens, Token, Tokens};
Expand Down Expand Up @@ -397,6 +401,196 @@ where
}
}

impl<T: WktNum> GeometryTrait for Wkt<T> {
type T = T;
type PointType<'b> = Point<T> where Self: 'b;
type LineStringType<'b> = LineString<T> where Self: 'b;
type PolygonType<'b> = Polygon<T> where Self: 'b;
type MultiPointType<'b> = MultiPoint<T> where Self: 'b;
type MultiLineStringType<'b> = MultiLineString<T> where Self: 'b;
type MultiPolygonType<'b> = MultiPolygon<T> where Self: 'b;
type GeometryCollectionType<'b> = GeometryCollection<T> where Self: 'b;
type RectType<'b> = geo_traits::UnimplementedRect<T> where Self: 'b;
type LineType<'b> = geo_traits::UnimplementedLine<T> where Self: 'b;
type TriangleType<'b> = geo_traits::UnimplementedTriangle<T> where Self: 'b;

fn dim(&self) -> geo_traits::Dimensions {
match self {
Wkt::Point(geom) => PointTrait::dim(geom),
Wkt::LineString(geom) => LineStringTrait::dim(geom),
Wkt::Polygon(geom) => PolygonTrait::dim(geom),
Wkt::MultiPoint(geom) => MultiPointTrait::dim(geom),
Wkt::MultiLineString(geom) => MultiLineStringTrait::dim(geom),
Wkt::MultiPolygon(geom) => MultiPolygonTrait::dim(geom),
Wkt::GeometryCollection(geom) => GeometryCollectionTrait::dim(geom),
}
}

fn as_type(
&self,
) -> geo_traits::GeometryType<
'_,
Point<T>,
LineString<T>,
Polygon<T>,
MultiPoint<T>,
MultiLineString<T>,
MultiPolygon<T>,
GeometryCollection<T>,
Self::RectType<'_>,
Self::TriangleType<'_>,
Self::LineType<'_>,
> {
match self {
Wkt::Point(geom) => geo_traits::GeometryType::Point(geom),
Wkt::LineString(geom) => geo_traits::GeometryType::LineString(geom),
Wkt::Polygon(geom) => geo_traits::GeometryType::Polygon(geom),
Wkt::MultiPoint(geom) => geo_traits::GeometryType::MultiPoint(geom),
Wkt::MultiLineString(geom) => geo_traits::GeometryType::MultiLineString(geom),
Wkt::MultiPolygon(geom) => geo_traits::GeometryType::MultiPolygon(geom),
Wkt::GeometryCollection(geom) => geo_traits::GeometryType::GeometryCollection(geom),
}
}
}

impl<T: WktNum> GeometryTrait for &Wkt<T> {
type T = T;
type PointType<'b> = Point<T> where Self: 'b;
type LineStringType<'b> = LineString<T> where Self: 'b;
type PolygonType<'b> = Polygon<T> where Self: 'b;
type MultiPointType<'b> = MultiPoint<T> where Self: 'b;
type MultiLineStringType<'b> = MultiLineString<T> where Self: 'b;
type MultiPolygonType<'b> = MultiPolygon<T> where Self: 'b;
type GeometryCollectionType<'b> = GeometryCollection<T> where Self: 'b;
type RectType<'b> = geo_traits::UnimplementedRect<T> where Self: 'b;
type LineType<'b> = geo_traits::UnimplementedLine<T> where Self: 'b;
type TriangleType<'b> = geo_traits::UnimplementedTriangle<T> where Self: 'b;

fn dim(&self) -> geo_traits::Dimensions {
match self {
Wkt::Point(geom) => PointTrait::dim(geom),
Wkt::LineString(geom) => LineStringTrait::dim(geom),
Wkt::Polygon(geom) => PolygonTrait::dim(geom),
Wkt::MultiPoint(geom) => MultiPointTrait::dim(geom),
Wkt::MultiLineString(geom) => MultiLineStringTrait::dim(geom),
Wkt::MultiPolygon(geom) => MultiPolygonTrait::dim(geom),
Wkt::GeometryCollection(geom) => GeometryCollectionTrait::dim(geom),
}
}

fn as_type(
&self,
) -> geo_traits::GeometryType<
'_,
Point<T>,
LineString<T>,
Polygon<T>,
MultiPoint<T>,
MultiLineString<T>,
MultiPolygon<T>,
GeometryCollection<T>,
Self::RectType<'_>,
Self::TriangleType<'_>,
Self::LineType<'_>,
> {
match self {
Wkt::Point(geom) => geo_traits::GeometryType::Point(geom),
Wkt::LineString(geom) => geo_traits::GeometryType::LineString(geom),
Wkt::Polygon(geom) => geo_traits::GeometryType::Polygon(geom),
Wkt::MultiPoint(geom) => geo_traits::GeometryType::MultiPoint(geom),
Wkt::MultiLineString(geom) => geo_traits::GeometryType::MultiLineString(geom),
Wkt::MultiPolygon(geom) => geo_traits::GeometryType::MultiPolygon(geom),
Wkt::GeometryCollection(geom) => geo_traits::GeometryType::GeometryCollection(geom),
}
}
}

// Specialized implementations on each WKT concrete type.

macro_rules! impl_specialization {
($geometry_type:ident) => {
impl<T: WktNum> GeometryTrait for $geometry_type<T> {
type T = T;
type PointType<'b> = Point<Self::T> where Self: 'b;
type LineStringType<'b> = LineString<Self::T> where Self: 'b;
type PolygonType<'b> = Polygon<Self::T> where Self: 'b;
type MultiPointType<'b> = MultiPoint<Self::T> where Self: 'b;
type MultiLineStringType<'b> = MultiLineString<Self::T> where Self: 'b;
type MultiPolygonType<'b> = MultiPolygon<Self::T> where Self: 'b;
type GeometryCollectionType<'b> = GeometryCollection<Self::T> where Self: 'b;
type RectType<'b> = geo_traits::UnimplementedRect<T> where Self: 'b;
type LineType<'b> = geo_traits::UnimplementedLine<T> where Self: 'b;
type TriangleType<'b> = geo_traits::UnimplementedTriangle<T> where Self: 'b;

fn dim(&self) -> geo_traits::Dimensions {
geo_traits::Dimensions::Xy
}

fn as_type(
&self,
) -> geo_traits::GeometryType<
'_,
Point<T>,
LineString<T>,
Polygon<T>,
MultiPoint<T>,
MultiLineString<T>,
MultiPolygon<T>,
GeometryCollection<T>,
Self::RectType<'_>,
Self::TriangleType<'_>,
Self::LineType<'_>,
> {
geo_traits::GeometryType::$geometry_type(self)
}
}

impl<'a, T: WktNum + 'a> GeometryTrait for &'a $geometry_type<T> {
type T = T;
type PointType<'b> = Point<Self::T> where Self: 'b;
type LineStringType<'b> = LineString<Self::T> where Self: 'b;
type PolygonType<'b> = Polygon<Self::T> where Self: 'b;
type MultiPointType<'b> = MultiPoint<Self::T> where Self: 'b;
type MultiLineStringType<'b> = MultiLineString<Self::T> where Self: 'b;
type MultiPolygonType<'b> = MultiPolygon<Self::T> where Self: 'b;
type GeometryCollectionType<'b> = GeometryCollection<Self::T> where Self: 'b;
type RectType<'b> = geo_traits::UnimplementedRect<T> where Self: 'b;
type LineType<'b> = geo_traits::UnimplementedLine<T> where Self: 'b;
type TriangleType<'b> = geo_traits::UnimplementedTriangle<T> where Self: 'b;

fn dim(&self) -> geo_traits::Dimensions {
geo_traits::Dimensions::Xy
}

fn as_type(
&self,
) -> geo_traits::GeometryType<
'_,
Point<T>,
LineString<T>,
Polygon<T>,
MultiPoint<T>,
MultiLineString<T>,
MultiPolygon<T>,
GeometryCollection<T>,
Self::RectType<'_>,
Self::TriangleType<'_>,
Self::LineType<'_>,
> {
geo_traits::GeometryType::$geometry_type(self)
}
}
};
}

impl_specialization!(Point);
impl_specialization!(LineString);
impl_specialization!(Polygon);
impl_specialization!(MultiPoint);
impl_specialization!(MultiLineString);
impl_specialization!(MultiPolygon);
impl_specialization!(GeometryCollection);

fn infer_geom_dimension<T: WktNum + FromStr + Default>(
tokens: &mut PeekableTokens<T>,
) -> Result<Dimension, &'static str> {
Expand Down
96 changes: 96 additions & 0 deletions src/types/coord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use geo_traits::CoordTrait;

use crate::tokenizer::{PeekableTokens, Token};
use crate::types::Dimension;
use crate::{FromTokens, WktNum};
Expand Down Expand Up @@ -96,6 +98,100 @@ where
}
}

impl<T: WktNum> CoordTrait for Coord<T> {
type T = T;

fn dim(&self) -> geo_traits::Dimensions {
match (self.z.is_some(), self.m.is_some()) {
(true, true) => geo_traits::Dimensions::Xyzm,
(true, false) => geo_traits::Dimensions::Xyz,
(false, true) => geo_traits::Dimensions::Xym,
(false, false) => geo_traits::Dimensions::Xy,
}
}

fn x(&self) -> Self::T {
self.x
}

fn y(&self) -> Self::T {
self.y
}

fn nth_or_panic(&self, n: usize) -> Self::T {
let has_z = self.z.is_some();
let has_m = self.m.is_some();
match n {
0 => self.x,
1 => self.y,
2 => {
if has_z {
self.z.unwrap()
} else if has_m {
self.m.unwrap()
} else {
panic!("n out of range")
}
}
3 => {
if has_z && has_m {
self.m.unwrap()
} else {
panic!("n out of range")
}
}
_ => panic!("n out of range"),
}
}
}

impl<T: WktNum> CoordTrait for &Coord<T> {
type T = T;

fn dim(&self) -> geo_traits::Dimensions {
match (self.z.is_some(), self.m.is_some()) {
(true, true) => geo_traits::Dimensions::Xyzm,
(true, false) => geo_traits::Dimensions::Xyz,
(false, true) => geo_traits::Dimensions::Xym,
(false, false) => geo_traits::Dimensions::Xy,
}
}

fn x(&self) -> Self::T {
self.x
}

fn y(&self) -> Self::T {
self.y
}

fn nth_or_panic(&self, n: usize) -> Self::T {
let has_z = self.z.is_some();
let has_m = self.m.is_some();
match n {
0 => self.x,
1 => self.y,
2 => {
if has_z {
self.z.unwrap()
} else if has_m {
self.m.unwrap()
} else {
panic!("n out of range")
}
}
3 => {
if has_z && has_m {
self.m.unwrap()
} else {
panic!("n out of range")
}
}
_ => panic!("n out of range"),
}
}
}

#[cfg(test)]
mod tests {
use super::Coord;
Expand Down
24 changes: 24 additions & 0 deletions src/types/geometrycollection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use geo_traits::{GeometryCollectionTrait, GeometryTrait};

use crate::tokenizer::{PeekableTokens, Token};
use crate::types::Dimension;
use crate::{FromTokens, Wkt, WktNum};
Expand Down Expand Up @@ -84,6 +86,28 @@ where
}
}

impl<T: WktNum> GeometryCollectionTrait for GeometryCollection<T> {
type T = T;
type GeometryType<'a> = &'a Wkt<T> where Self: 'a;

fn dim(&self) -> geo_traits::Dimensions {
// TODO: infer dimension from empty WKT
if self.0.is_empty() {
geo_traits::Dimensions::Xy
} else {
self.0[0].dim()
}
}

fn num_geometries(&self) -> usize {
self.0.len()
}

unsafe fn geometry_unchecked(&self, i: usize) -> Self::GeometryType<'_> {
self.0.get_unchecked(i)
}
}

#[cfg(test)]
mod tests {
use super::GeometryCollection;
Expand Down
Loading

0 comments on commit cdbff53

Please sign in to comment.