diff --git a/Cargo.toml b/Cargo.toml index e10b64a76..b96fd5755 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ byteorder = "1" gdal = { version = "0.15", optional = true } geo = "0.26" geodesy = { version = "0.10", optional = true } -geos = { version = "8.3", features = ["v3_8_0", "geo"], optional = true } +geos = { version = "8.3", features = ["v3_10_0", "geo"], optional = true } geozero = { version = "0.9.4", features = ["with-wkb"], optional = true } itertools = "0.11" # Note: geo has a hard dependency on rstar, so there's no point in feature flagging it diff --git a/js/Cargo.lock b/js/Cargo.lock index bb5c5127d..12eb5888a 100644 --- a/js/Cargo.lock +++ b/js/Cargo.lock @@ -22,9 +22,9 @@ checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "approx" @@ -37,9 +37,9 @@ dependencies = [ [[package]] name = "arrow2" -version = "0.17.2" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15ae0428d69ab31d7b2adad22a752d6f11fef2e901d2262d0cad4f5cb08b7093" +checksum = "e44f27e89e3edd8738a07c5e2c881efaa25e69be97a816d2df051685d460670c" dependencies = [ "ahash", "bytemuck", @@ -110,7 +110,7 @@ checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.27", ] [[package]] @@ -223,9 +223,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" +checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" [[package]] name = "earcutr" @@ -239,9 +239,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "ethnum" @@ -435,9 +435,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" @@ -484,9 +484,9 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", "libm", @@ -539,18 +539,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.29" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" dependencies = [ "proc-macro2", ] @@ -609,34 +609,34 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.164" +version = "1.0.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +checksum = "3b88756493a5bd5e5395d53baa70b194b05764ab85b59e43e4b8f4e1192fa9b1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.164" +version = "1.0.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +checksum = "6e5c3a298c7f978e53536f95a63bdc4c4a64550582f31a0359a9afda6aede62e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.27", ] [[package]] @@ -660,9 +660,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "spin" @@ -698,9 +698,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.23" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -724,29 +724,29 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.27", ] [[package]] name = "time" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" +checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" dependencies = [ "itoa", "libc", @@ -764,24 +764,24 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" dependencies = [ "time-core", ] [[package]] name = "unicode-ident" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "uuid" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ "getrandom", "wasm-bindgen", @@ -820,7 +820,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.27", "wasm-bindgen-shared", ] @@ -854,7 +854,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/src/array/coord/combined/geos.rs b/src/array/coord/combined/geos.rs new file mode 100644 index 000000000..bd50b77cf --- /dev/null +++ b/src/array/coord/combined/geos.rs @@ -0,0 +1,14 @@ +use crate::array::CoordBuffer; +use crate::error::{GeoArrowError, Result}; +use geos::CoordSeq; + +impl TryFrom for CoordSeq<'_> { + type Error = GeoArrowError; + + fn try_from(value: CoordBuffer) -> Result { + match value { + CoordBuffer::Separated(cb) => cb.try_into(), + CoordBuffer::Interleaved(cb) => cb.try_into(), + } + } +} diff --git a/src/array/coord/combined/mod.rs b/src/array/coord/combined/mod.rs index 797d74b75..4464fc007 100644 --- a/src/array/coord/combined/mod.rs +++ b/src/array/coord/combined/mod.rs @@ -1,4 +1,6 @@ mod array; +#[cfg(feature = "geos")] +mod geos; mod mutable; pub use array::CoordBuffer; diff --git a/src/array/coord/interleaved/geos.rs b/src/array/coord/interleaved/geos.rs new file mode 100644 index 000000000..4ce862762 --- /dev/null +++ b/src/array/coord/interleaved/geos.rs @@ -0,0 +1,17 @@ +use crate::array::InterleavedCoordBuffer; +use crate::error::{GeoArrowError, Result}; +use crate::GeometryArrayTrait; +use geos::CoordSeq; + +impl TryFrom for CoordSeq<'_> { + type Error = GeoArrowError; + + fn try_from(value: InterleavedCoordBuffer) -> Result { + Ok(CoordSeq::new_from_buffer( + value.coords.as_slice(), + value.len(), + false, + false, + )?) + } +} diff --git a/src/array/coord/interleaved/mod.rs b/src/array/coord/interleaved/mod.rs index 9ab4522f2..18719ef5f 100644 --- a/src/array/coord/interleaved/mod.rs +++ b/src/array/coord/interleaved/mod.rs @@ -1,4 +1,6 @@ mod array; +#[cfg(feature = "geos")] +mod geos; mod mutable; pub use array::InterleavedCoordBuffer; diff --git a/src/array/coord/separated/geos.rs b/src/array/coord/separated/geos.rs new file mode 100644 index 000000000..cdbff1edd --- /dev/null +++ b/src/array/coord/separated/geos.rs @@ -0,0 +1,16 @@ +use crate::array::SeparatedCoordBuffer; +use crate::error::{GeoArrowError, Result}; +use geos::CoordSeq; + +impl TryFrom for CoordSeq<'_> { + type Error = GeoArrowError; + + fn try_from(value: SeparatedCoordBuffer) -> Result { + Ok(CoordSeq::new_from_arrays( + value.x.as_slice(), + value.y.as_slice(), + None, + None, + )?) + } +} diff --git a/src/array/coord/separated/mod.rs b/src/array/coord/separated/mod.rs index b3ac0d61f..118ee5121 100644 --- a/src/array/coord/separated/mod.rs +++ b/src/array/coord/separated/mod.rs @@ -1,4 +1,6 @@ mod array; +#[cfg(feature = "geos")] +mod geos; mod mutable; pub use array::SeparatedCoordBuffer; diff --git a/src/array/linestring/array.rs b/src/array/linestring/array.rs index 80242bb27..bfbb495f6 100644 --- a/src/array/linestring/array.rs +++ b/src/array/linestring/array.rs @@ -239,7 +239,7 @@ impl LineStringArray { /// Returns the value at slot `i` as a GEOS geometry. #[cfg(feature = "geos")] pub fn value_as_geos(&self, i: usize) -> geos::Geometry { - (&self.value_as_geo(i)).try_into().unwrap() + self.value(i).try_into().unwrap() } /// Gets the value at slot `i` as a GEOS geometry, additionally checking the validity bitmap @@ -249,7 +249,7 @@ impl LineStringArray { return None; } - self.get_as_geo(i).as_ref().map(|g| g.try_into().unwrap()) + Some(self.value_as_geos(i)) } /// Iterator over GEOS geometry objects diff --git a/src/array/linestring/mutable.rs b/src/array/linestring/mutable.rs index da4918bf3..345e6be66 100644 --- a/src/array/linestring/mutable.rs +++ b/src/array/linestring/mutable.rs @@ -262,6 +262,29 @@ impl TryFrom> for MutableLineStringArray { } } +// #[cfg(feature = "geos")] +// impl TryFrom>>> for MutableLineStringArray { +// type Error = GeoArrowError; +// fn try_from(value: Vec>) -> std::result::Result { +// let length = value.len(); +// let geos_linestring_objects: Vec> = value +// .iter() +// .map(|geom| { +// geom.map(|geom| GEOSLineString::new_unchecked(std::borrow::Cow::Owned(geom))) +// }) +// .collect(); +// let (coord_capacity, geom_capacity) = first_pass( +// geos_linestring_objects.iter().map(|item| item.as_ref()), +// length, +// ); +// Ok(second_pass( +// geos_linestring_objects.iter().map(|item| item.as_ref()), +// coord_capacity, +// geom_capacity, +// )) +// } +// } + /// LineString and MultiPoint have the same layout, so enable conversions between the two to change /// the semantic type impl From> for MutableMultiPointArray { diff --git a/src/array/multilinestring/array.rs b/src/array/multilinestring/array.rs index c1a6101f6..30c610b37 100644 --- a/src/array/multilinestring/array.rs +++ b/src/array/multilinestring/array.rs @@ -277,37 +277,35 @@ impl MultiLineStringArray { ZipValidity::new_with_validity(self.iter_geo_values(), self.validity()) } - // GEOS from not implemented for MultiLineString I suppose - // - // /// Returns the value at slot `i` as a GEOS geometry. - // #[cfg(feature = "geos")] - // pub fn value_as_geos(&self, i: usize) -> geos::Geometry { - // (&self.value_as_geo(i)).try_into().unwrap() - // } - - // /// Gets the value at slot `i` as a GEOS geometry, additionally checking the validity bitmap - // #[cfg(feature = "geos")] - // pub fn get_as_geos(&self, i: usize) -> Option { - // if self.is_null(i) { - // return None; - // } - - // self.get_as_geo(i).as_ref().map(|g| g.try_into().unwrap()) - // } - - // /// Iterator over GEOS geometry objects - // #[cfg(feature = "geos")] - // pub fn iter_geos_values(&self) -> impl Iterator + '_ { - // (0..self.len()).map(|i| self.value_as_geos(i)) - // } - - // /// Iterator over GEOS geometry objects, taking validity into account - // #[cfg(feature = "geos")] - // pub fn iter_geos( - // &self, - // ) -> ZipValidity + '_, BitmapIter> { - // ZipValidity::new_with_validity(self.iter_geos_values(), self.validity()) - // } + /// Returns the value at slot `i` as a GEOS geometry. + #[cfg(feature = "geos")] + pub fn value_as_geos(&self, i: usize) -> geos::Geometry { + self.value(i).try_into().unwrap() + } + + /// Gets the value at slot `i` as a GEOS geometry, additionally checking the validity bitmap + #[cfg(feature = "geos")] + pub fn get_as_geos(&self, i: usize) -> Option { + if self.is_null(i) { + return None; + } + + Some(self.value_as_geos(i)) + } + + /// Iterator over GEOS geometry objects + #[cfg(feature = "geos")] + pub fn iter_geos_values(&self) -> impl Iterator + '_ { + (0..self.len()).map(|i| self.value_as_geos(i)) + } + + /// Iterator over GEOS geometry objects, taking validity into account + #[cfg(feature = "geos")] + pub fn iter_geos( + &self, + ) -> ZipValidity + '_, BitmapIter> { + ZipValidity::new_with_validity(self.iter_geos_values(), self.validity()) + } } impl TryFrom<&ListArray> for MultiLineStringArray { diff --git a/src/array/multipoint/array.rs b/src/array/multipoint/array.rs index 255a353db..0f9d7e9b5 100644 --- a/src/array/multipoint/array.rs +++ b/src/array/multipoint/array.rs @@ -231,37 +231,35 @@ impl MultiPointArray { ZipValidity::new_with_validity(self.iter_geo_values(), self.validity()) } - // GEOS from not implemented for MultiPoint?!? - // - // /// Returns the value at slot `i` as a GEOS geometry. - // #[cfg(feature = "geos")] - // pub fn value_as_geos(&self, i: usize) -> geos::Geometry { - // (&self.value_as_geo(i)).try_into().unwrap() - // } - - // /// Gets the value at slot `i` as a GEOS geometry, additionally checking the validity bitmap - // #[cfg(feature = "geos")] - // pub fn get_as_geos(&self, i: usize) -> Option { - // if self.is_null(i) { - // return None; - // } - - // self.get_as_geo(i).as_ref().map(|g| g.try_into().unwrap()) - // } - - // /// Iterator over GEOS geometry objects - // #[cfg(feature = "geos")] - // pub fn iter_geos_values(&self) -> impl Iterator + '_ { - // (0..self.len()).map(|i| self.value_as_geos(i)) - // } - - // /// Iterator over GEOS geometry objects, taking validity into account - // #[cfg(feature = "geos")] - // pub fn iter_geos( - // &self, - // ) -> ZipValidity + '_, BitmapIter> { - // ZipValidity::new_with_validity(self.iter_geos_values(), self.validity()) - // } + /// Returns the value at slot `i` as a GEOS geometry. + #[cfg(feature = "geos")] + pub fn value_as_geos(&self, i: usize) -> geos::Geometry { + self.value(i).try_into().unwrap() + } + + /// Gets the value at slot `i` as a GEOS geometry, additionally checking the validity bitmap + #[cfg(feature = "geos")] + pub fn get_as_geos(&self, i: usize) -> Option { + if self.is_null(i) { + return None; + } + + Some(self.value_as_geos(i)) + } + + /// Iterator over GEOS geometry objects + #[cfg(feature = "geos")] + pub fn iter_geos_values(&self) -> impl Iterator + '_ { + (0..self.len()).map(|i| self.value_as_geos(i)) + } + + /// Iterator over GEOS geometry objects, taking validity into account + #[cfg(feature = "geos")] + pub fn iter_geos( + &self, + ) -> ZipValidity + '_, BitmapIter> { + ZipValidity::new_with_validity(self.iter_geos_values(), self.validity()) + } } impl TryFrom<&ListArray> for MultiPointArray { diff --git a/src/array/multipolygon/array.rs b/src/array/multipolygon/array.rs index aed865cc9..a27e129c5 100644 --- a/src/array/multipolygon/array.rs +++ b/src/array/multipolygon/array.rs @@ -311,37 +311,35 @@ impl MultiPolygonArray { ZipValidity::new_with_validity(self.iter_geo_values(), self.validity()) } - // GEOS from not implemented for MultiLineString I suppose - // - // /// Returns the value at slot `i` as a GEOS geometry. - // #[cfg(feature = "geos")] - // pub fn value_as_geos(&self, i: usize) -> geos::Geometry { - // (&self.value_as_geo(i)).try_into().unwrap() - // } - - // /// Gets the value at slot `i` as a GEOS geometry, additionally checking the validity bitmap - // #[cfg(feature = "geos")] - // pub fn get_as_geos(&self, i: usize) -> Option { - // if self.is_null(i) { - // return None; - // } - - // self.get_as_geo(i).as_ref().map(|g| g.try_into().unwrap()) - // } - - // /// Iterator over GEOS geometry objects - // #[cfg(feature = "geos")] - // pub fn iter_geos_values(&self) -> impl Iterator + '_ { - // (0..self.len()).map(|i| self.value_as_geos(i)) - // } - - // /// Iterator over GEOS geometry objects, taking validity into account - // #[cfg(feature = "geos")] - // pub fn iter_geos( - // &self, - // ) -> ZipValidity + '_, BitmapIter> { - // ZipValidity::new_with_validity(self.iter_geos_values(), self.validity()) - // } + /// Returns the value at slot `i` as a GEOS geometry. + #[cfg(feature = "geos")] + pub fn value_as_geos(&self, i: usize) -> geos::Geometry { + self.value(i).try_into().unwrap() + } + + /// Gets the value at slot `i` as a GEOS geometry, additionally checking the validity bitmap + #[cfg(feature = "geos")] + pub fn get_as_geos(&self, i: usize) -> Option { + if self.is_null(i) { + return None; + } + + Some(self.value_as_geos(i)) + } + + /// Iterator over GEOS geometry objects + #[cfg(feature = "geos")] + pub fn iter_geos_values(&self) -> impl Iterator + '_ { + (0..self.len()).map(|i| self.value_as_geos(i)) + } + + /// Iterator over GEOS geometry objects, taking validity into account + #[cfg(feature = "geos")] + pub fn iter_geos( + &self, + ) -> ZipValidity + '_, BitmapIter> { + ZipValidity::new_with_validity(self.iter_geos_values(), self.validity()) + } } impl TryFrom<&ListArray> for MultiPolygonArray { diff --git a/src/array/point/array.rs b/src/array/point/array.rs index cbeda435a..32fec3434 100644 --- a/src/array/point/array.rs +++ b/src/array/point/array.rs @@ -194,7 +194,7 @@ impl PointArray { /// Returns the value at slot `i` as a GEOS geometry. #[cfg(feature = "geos")] pub fn value_as_geos(&self, i: usize) -> geos::Geometry { - (&self.value_as_geo(i)).try_into().unwrap() + self.value(i).try_into().unwrap() } /// Gets the value at slot `i` as a GEOS geometry, additionally checking the validity bitmap @@ -204,7 +204,7 @@ impl PointArray { return None; } - self.get_as_geo(i).as_ref().map(|g| g.try_into().unwrap()) + Some(self.value_as_geos(i)) } /// Iterator over GEOS geometry objects diff --git a/src/array/polygon/array.rs b/src/array/polygon/array.rs index 862d7ff1c..2f5f7957a 100644 --- a/src/array/polygon/array.rs +++ b/src/array/polygon/array.rs @@ -275,7 +275,7 @@ impl PolygonArray { /// Returns the value at slot `i` as a GEOS geometry. #[cfg(feature = "geos")] pub fn value_as_geos(&self, i: usize) -> geos::Geometry { - (&self.value_as_geo(i)).try_into().unwrap() + self.value(i).try_into().unwrap() } /// Gets the value at slot `i` as a GEOS geometry, additionally checking the validity bitmap @@ -285,7 +285,7 @@ impl PolygonArray { return None; } - self.get_as_geo(i).as_ref().map(|g| g.try_into().unwrap()) + Some(self.value_as_geos(i)) } /// Iterator over GEOS geometry objects diff --git a/src/error.rs b/src/error.rs index 7c22bfec5..9cad963fa 100644 --- a/src/error.rs +++ b/src/error.rs @@ -29,6 +29,10 @@ pub enum GeoArrowError { #[error(transparent)] FailedToConvergeError(#[from] geo::vincenty_distance::FailedToConvergeError), + + #[cfg(feature = "geos")] + #[error(transparent)] + GeosError(#[from] geos::Error), } pub type Result = std::result::Result; diff --git a/src/io/native/geos/coord.rs b/src/io/native/geos/coord.rs new file mode 100644 index 000000000..e3c18b9d9 --- /dev/null +++ b/src/io/native/geos/coord.rs @@ -0,0 +1,61 @@ +use std::borrow::Cow; + +use geos::CoordSeq; + +use crate::geo_traits::{CoordTrait, PointTrait}; + +#[derive(Clone)] +pub struct GEOSCoord<'a> { + /// The underlying coordinate sequence + coord_seq: Cow<'a, CoordSeq<'a>>, + + /// The offset into the buffer where this coordinate is located + /// + /// Note that this does not have to be immediately after the WKB header! For a `WKBPoint`, the + /// `Point` is immediately after the header, but the `Point` also appears in other geometry + /// types. I.e. the `WKBLineString` has a header, then the number of points, then a sequence of + /// `Point` objects. + offset: usize, +} + +impl<'a> GEOSCoord<'a> { + #[allow(dead_code)] + pub fn new_owned(coord_seq: CoordSeq<'a>, offset: usize) -> Self { + Self { + coord_seq: Cow::Owned(coord_seq), + offset, + } + } + + #[allow(dead_code)] + pub fn new_borrowed(coord_seq: &'a CoordSeq<'a>, offset: usize) -> Self { + Self { + coord_seq: Cow::Borrowed(coord_seq), + offset, + } + } +} + +impl<'a> CoordTrait for GEOSCoord<'a> { + type T = f64; + + fn x(&self) -> Self::T { + self.coord_seq.get_x(self.offset).unwrap() + } + + fn y(&self) -> Self::T { + self.coord_seq.get_y(self.offset).unwrap() + } +} + +impl<'a> PointTrait for GEOSCoord<'a> { + type T = f64; + + fn x(&self) -> Self::T { + self.coord_seq.get_x(self.offset).unwrap() + } + + fn y(&self) -> Self::T { + self.coord_seq.get_y(self.offset).unwrap() + } +} diff --git a/src/io/native/geos/geos_cow.rs b/src/io/native/geos/geos_cow.rs new file mode 100644 index 000000000..f8e602343 --- /dev/null +++ b/src/io/native/geos/geos_cow.rs @@ -0,0 +1,94 @@ +use std::borrow::Borrow; +use std::ops::Deref; + +pub enum GEOSCowGeometry<'a, B: ?Sized + 'a> +where + B: ToOwned, +{ + /// Borrowed data. + #[allow(dead_code)] + Borrowed(&'a B), + + /// Owned data. + #[allow(dead_code)] + Owned(::Owned), +} + +impl Clone for GEOSCowGeometry<'_, B> { + fn clone(&self) -> Self { + match *self { + GEOSCowGeometry::Borrowed(b) => GEOSCowGeometry::Borrowed(b), + GEOSCowGeometry::Owned(ref o) => { + let b: &B = o.borrow(); + GEOSCowGeometry::Owned(b.to_owned()) + } + } + } + + fn clone_from(&mut self, source: &Self) { + match (self, source) { + (&mut GEOSCowGeometry::Owned(ref mut dest), GEOSCowGeometry::Owned(o)) => { + o.borrow().clone_into(dest) + } + (t, s) => *t = s.clone(), + } + } +} + +impl GEOSCowGeometry<'_, B> { + /// Extracts the owned data. + /// + /// Clones the data if it is not already owned. + /// + /// # Examples + /// + /// Calling `into_owned` on a `Cow::Borrowed` returns a clone of the borrowed data: + /// + /// ``` + /// use std::borrow::Cow; + /// + /// let s = "Hello world!"; + /// let cow = Cow::Borrowed(s); + /// + /// assert_eq!( + /// cow.into_owned(), + /// String::from(s) + /// ); + /// ``` + /// + /// Calling `into_owned` on a `Cow::Owned` returns the owned data. The data is moved out of the + /// `Cow` without being cloned. + /// + /// ``` + /// use std::borrow::Cow; + /// + /// let s = "Hello world!"; + /// let cow: Cow<'_, str> = Cow::Owned(String::from(s)); + /// + /// assert_eq!( + /// cow.into_owned(), + /// String::from(s) + /// ); + /// ``` + #[allow(dead_code)] + pub fn into_owned(self) -> ::Owned { + match self { + GEOSCowGeometry::Borrowed(borrowed) => borrowed.to_owned(), + GEOSCowGeometry::Owned(owned) => owned, + } + } +} + +impl Deref for GEOSCowGeometry<'_, B> +where + B::Owned: Borrow, +{ + type Target = B; + + fn deref(&self) -> &B { + match *self { + GEOSCowGeometry::Borrowed(borrowed) => borrowed, + GEOSCowGeometry::Owned(ref owned) => owned.borrow(), + } + } +} diff --git a/src/io/native/geos/linestring.rs b/src/io/native/geos/linestring.rs new file mode 100644 index 000000000..ac064809e --- /dev/null +++ b/src/io/native/geos/linestring.rs @@ -0,0 +1,70 @@ +use geos::{Geom, GeometryTypes}; + +use crate::error::Result; +use crate::geo_traits::LineStringTrait; +use crate::io::native::geos::point::GEOSPoint; +use std::borrow::Cow; +use std::iter::Cloned; +use std::slice::Iter; + +#[derive(Clone)] +pub struct GEOSLineString<'a>(Cow<'a, geos::Geometry<'a>>); + +impl<'a> GEOSLineString<'a> { + pub fn new_unchecked(geom: Cow<'a, geos::Geometry<'a>>) -> Self { + Self(geom) + } + + pub fn try_new(geom: Cow<'a, geos::Geometry<'a>>) -> Result { + // TODO: make Err + assert!(matches!(geom.geometry_type(), GeometryTypes::Point)); + + Ok(Self(geom)) + } +} + +impl<'a> LineStringTrait<'a> for GEOSLineString<'a> { + type T = f64; + type ItemType = GEOSPoint<'a>; + type Iter = Cloned>; + + fn num_coords(&self) -> usize { + self.0.get_num_points().unwrap() + } + + fn coord(&self, i: usize) -> Option { + if i > (self.num_coords()) { + return None; + } + + let point = self.0.get_point_n(i).unwrap(); + Some(GEOSPoint::new_owned(point)) + } + + fn coords(&'a self) -> Self::Iter { + todo!() + } +} + +impl<'a> LineStringTrait<'a> for &GEOSLineString<'a> { + type T = f64; + type ItemType = GEOSPoint<'a>; + type Iter = Cloned>; + + fn num_coords(&self) -> usize { + self.0.get_num_points().unwrap() + } + + fn coord(&self, i: usize) -> Option { + if i > (self.num_coords()) { + return None; + } + + let point = self.0.get_point_n(i).unwrap(); + Some(GEOSPoint::new_owned(point)) + } + + fn coords(&'a self) -> Self::Iter { + todo!() + } +} diff --git a/src/io/native/geos/mod.rs b/src/io/native/geos/mod.rs new file mode 100644 index 000000000..a0f9516e9 --- /dev/null +++ b/src/io/native/geos/mod.rs @@ -0,0 +1,5 @@ +pub mod coord; +pub mod geos_cow; +pub mod linestring; +pub mod multipoint; +pub mod point; diff --git a/src/io/native/geos/multipoint.rs b/src/io/native/geos/multipoint.rs new file mode 100644 index 000000000..617483229 --- /dev/null +++ b/src/io/native/geos/multipoint.rs @@ -0,0 +1,65 @@ +use crate::error::Result; +use crate::geo_traits::MultiPointTrait; +use crate::io::native::geos::point::GEOSBorrowedPoint; +use geos::{Geom, GeometryTypes}; +use std::iter::Cloned; +use std::slice::Iter; + +#[derive(Clone)] +pub struct GEOSMultiPoint<'a, 'b>(&'b geos::Geometry<'a>); + +impl<'a, 'b> GEOSMultiPoint<'a, 'b> { + #[allow(dead_code)] + pub fn try_new(geom: &'a geos::Geometry<'a>) -> Result { + // TODO: make Err + assert!(matches!(geom.geometry_type(), GeometryTypes::MultiPoint)); + + Ok(Self(geom)) + } +} + +impl<'a, 'b: 'a> MultiPointTrait<'a> for GEOSMultiPoint<'a, 'b> { + type T = f64; + type ItemType = GEOSBorrowedPoint<'a, 'b>; + type Iter = Cloned>; + + fn num_points(&self) -> usize { + self.0.get_num_geometries().unwrap() + } + + fn point(&self, i: usize) -> Option { + if i > self.num_points() { + return None; + } + + let point = self.0.get_geometry_n(i).unwrap(); + Some(GEOSBorrowedPoint::new_unchecked(point)) + } + + fn points(&'a self) -> Self::Iter { + todo!() + } +} + +// impl<'b, 'a> MultiPointTrait<'a> for &GEOSMultiPoint<'a,'b> { +// type T = f64; +// type ItemType = GEOSBorrowedPoint<'a, 'b>; +// type Iter = Cloned>; + +// fn num_points(&self) -> usize { +// self.0.get_num_geometries().unwrap() +// } + +// fn point(&self, i: usize) -> Option { +// if i > self.num_points() { +// return None; +// } + +// let point = self.0.get_geometry_n(i).unwrap(); +// Some(GEOSBorrowedPoint::new_unchecked(point)) +// } + +// fn points(&'a self) -> Self::Iter { +// todo!() +// } +// } diff --git a/src/io/native/geos/point.rs b/src/io/native/geos/point.rs new file mode 100644 index 000000000..12dd657cf --- /dev/null +++ b/src/io/native/geos/point.rs @@ -0,0 +1,124 @@ +use std::borrow::Cow; + +use geos::{Geom, GeometryTypes}; + +use crate::error::Result; +use crate::geo_traits::{CoordTrait, PointTrait}; + +#[derive(Clone)] +pub struct GEOSPoint<'a>(Cow<'a, geos::Geometry<'a>>); + +impl<'a> GEOSPoint<'a> { + // pub fn new_unchecked(geom: &'a geos::Geometry<'a>) -> Self { + // Self(geom) + // } + + // pub fn try_new(geom: &'a geos::Geometry<'a>) -> Result { + // // TODO: make Err + // assert!(matches!(geom.geometry_type(), GeometryTypes::Point)); + + // Ok(Self(geom)) + // } + + pub fn new_owned(geom: geos::Geometry<'a>) -> Self { + Self(Cow::Owned(geom)) + } + + pub fn new_borrowed(geom: &'a geos::Geometry<'a>) -> Self { + Self(Cow::Borrowed(geom)) + } +} + +impl<'a> PointTrait for GEOSPoint<'a> { + type T = f64; + + fn x(&self) -> Self::T { + self.0.get_x().unwrap() + } + + fn y(&self) -> Self::T { + self.0.get_y().unwrap() + } +} + +impl<'a> PointTrait for &GEOSPoint<'a> { + type T = f64; + + fn x(&self) -> Self::T { + self.0.get_x().unwrap() + } + + fn y(&self) -> Self::T { + self.0.get_y().unwrap() + } +} + +impl<'a> CoordTrait for GEOSPoint<'a> { + type T = f64; + + fn x(&self) -> Self::T { + self.0.get_x().unwrap() + } + + fn y(&self) -> Self::T { + self.0.get_y().unwrap() + } +} + +impl<'a> CoordTrait for &GEOSPoint<'a> { + type T = f64; + + fn x(&self) -> Self::T { + self.0.get_x().unwrap() + } + + fn y(&self) -> Self::T { + self.0.get_y().unwrap() + } +} + +pub struct GEOSBorrowedPoint<'a, 'b>(geos::ConstGeometry<'a, 'b>); + +// TODO: +impl<'a, 'b> Clone for GEOSBorrowedPoint<'a, 'b> { + fn clone(&self) -> Self { + todo!() + } +} + +impl<'a, 'b> GEOSBorrowedPoint<'a, 'b> { + pub fn new_unchecked(geom: geos::ConstGeometry<'a, 'b>) -> Self { + Self(geom) + } + + pub fn try_new(geom: geos::ConstGeometry<'a, 'b>) -> Result { + // TODO: make Err + assert!(matches!(geom.geometry_type(), GeometryTypes::Point)); + + Ok(Self(geom)) + } +} + +impl<'a, 'b> PointTrait for GEOSBorrowedPoint<'a, 'b> { + type T = f64; + + fn x(&self) -> Self::T { + self.0.get_x().unwrap() + } + + fn y(&self) -> Self::T { + self.0.get_y().unwrap() + } +} + +impl<'a, 'b> PointTrait for &GEOSBorrowedPoint<'a, 'b> { + type T = f64; + + fn x(&self) -> Self::T { + self.0.get_x().unwrap() + } + + fn y(&self) -> Self::T { + self.0.get_y().unwrap() + } +} diff --git a/src/io/native/mod.rs b/src/io/native/mod.rs index e98c05e40..22c8661c3 100644 --- a/src/io/native/mod.rs +++ b/src/io/native/mod.rs @@ -1 +1,3 @@ +#[cfg(feature = "geos")] +pub mod geos; pub mod wkb; diff --git a/src/io/native/wkb/linestring.rs b/src/io/native/wkb/linestring.rs index 37c00f1ac..eec2bd84b 100644 --- a/src/io/native/wkb/linestring.rs +++ b/src/io/native/wkb/linestring.rs @@ -71,7 +71,7 @@ impl<'a> LineStringTrait<'a> for WKBLineString<'a> { } fn coord(&self, i: usize) -> Option { - if i > (self.num_points) { + if i > (self.num_coords()) { return None; } @@ -98,7 +98,7 @@ impl<'a> LineStringTrait<'a> for &WKBLineString<'a> { } fn coord(&self, i: usize) -> Option { - if i > (self.num_points) { + if i > (self.num_coords()) { return None; } diff --git a/src/scalar/binary/geos.rs b/src/scalar/binary/geos.rs new file mode 100644 index 000000000..925c58340 --- /dev/null +++ b/src/scalar/binary/geos.rs @@ -0,0 +1,21 @@ +use crate::error::{GeoArrowError, Result}; +use crate::scalar::WKB; +use arrow2::types::Offset; + +impl<'b, O: Offset> TryFrom> for geos::Geometry<'b> { + type Error = GeoArrowError; + + fn try_from(value: WKB<'_, O>) -> Result> { + geos::Geometry::try_from(&value) + } +} + +impl<'a, 'b, O: Offset> TryFrom<&'a WKB<'_, O>> for geos::Geometry<'b> { + type Error = GeoArrowError; + + fn try_from(value: &'a WKB<'_, O>) -> Result> { + Ok(geos::Geometry::new_from_wkb( + value.arr.value(value.geom_index), + )?) + } +} diff --git a/src/scalar/binary/mod.rs b/src/scalar/binary/mod.rs index 3c1b125a6..5d573808d 100644 --- a/src/scalar/binary/mod.rs +++ b/src/scalar/binary/mod.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "geos")] +mod geos; mod scalar; pub use scalar::WKB; diff --git a/src/scalar/linestring/geos.rs b/src/scalar/linestring/geos.rs new file mode 100644 index 000000000..94d05a75f --- /dev/null +++ b/src/scalar/linestring/geos.rs @@ -0,0 +1,40 @@ +use crate::error::{GeoArrowError, Result}; +use crate::scalar::LineString; +use crate::GeometryArrayTrait; +use arrow2::types::Offset; + +impl<'b, O: Offset> TryFrom> for geos::Geometry<'b> { + type Error = GeoArrowError; + + fn try_from(value: LineString<'_, O>) -> Result> { + geos::Geometry::try_from(&value) + } +} + +impl<'a, 'b, O: Offset> TryFrom<&'a LineString<'_, O>> for geos::Geometry<'b> { + type Error = GeoArrowError; + + fn try_from(value: &'a LineString<'_, O>) -> Result> { + let (start, end) = value.geom_offsets.start_end(value.geom_index); + + let mut sliced_coords = value.coords.clone(); + sliced_coords.slice(start, end - start); + + Ok(geos::Geometry::create_line_string( + sliced_coords.try_into()?, + )?) + } +} + +impl<'b, O: Offset> LineString<'_, O> { + pub fn to_geos_linear_ring(&self) -> Result> { + let (start, end) = self.geom_offsets.start_end(self.geom_index); + + let mut sliced_coords = self.coords.clone(); + sliced_coords.slice(start, end - start); + + Ok(geos::Geometry::create_linear_ring( + sliced_coords.try_into()?, + )?) + } +} diff --git a/src/scalar/linestring/mod.rs b/src/scalar/linestring/mod.rs index c19aa106c..e463ba642 100644 --- a/src/scalar/linestring/mod.rs +++ b/src/scalar/linestring/mod.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "geos")] +mod geos; mod scalar; pub use scalar::LineString; diff --git a/src/scalar/multilinestring/geos.rs b/src/scalar/multilinestring/geos.rs new file mode 100644 index 000000000..ed921e25c --- /dev/null +++ b/src/scalar/multilinestring/geos.rs @@ -0,0 +1,28 @@ +use crate::error::GeoArrowError; +use crate::geo_traits::MultiLineStringTrait; +use crate::scalar::MultiLineString; +use arrow2::types::Offset; + +impl<'b, O: Offset> TryFrom> for geos::Geometry<'b> { + type Error = GeoArrowError; + + fn try_from(value: MultiLineString<'_, O>) -> Result, Self::Error> { + geos::Geometry::try_from(&value) + } +} + +impl<'a, 'b, O: Offset> TryFrom<&'a MultiLineString<'_, O>> for geos::Geometry<'b> { + type Error = GeoArrowError; + + fn try_from(value: &'a MultiLineString<'_, O>) -> Result, Self::Error> { + let num_lines = value.num_lines(); + let mut geos_geoms = Vec::with_capacity(num_lines); + + for line_idx in 0..num_lines { + let line = value.line(line_idx).unwrap(); + geos_geoms.push(line.try_into()?); + } + + Ok(geos::Geometry::create_multiline_string(geos_geoms)?) + } +} diff --git a/src/scalar/multilinestring/mod.rs b/src/scalar/multilinestring/mod.rs index e379509c4..753c2f7f6 100644 --- a/src/scalar/multilinestring/mod.rs +++ b/src/scalar/multilinestring/mod.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "geos")] +mod geos; mod scalar; pub use scalar::MultiLineString; diff --git a/src/scalar/multipoint/geos.rs b/src/scalar/multipoint/geos.rs new file mode 100644 index 000000000..19274b8b2 --- /dev/null +++ b/src/scalar/multipoint/geos.rs @@ -0,0 +1,28 @@ +use crate::error::GeoArrowError; +use crate::geo_traits::MultiPointTrait; +use crate::scalar::MultiPoint; +use arrow2::types::Offset; + +impl<'b, O: Offset> TryFrom> for geos::Geometry<'b> { + type Error = GeoArrowError; + + fn try_from(value: MultiPoint<'_, O>) -> Result, Self::Error> { + geos::Geometry::try_from(&value) + } +} + +impl<'a, 'b, O: Offset> TryFrom<&'a MultiPoint<'_, O>> for geos::Geometry<'b> { + type Error = GeoArrowError; + + fn try_from(value: &'a MultiPoint<'_, O>) -> Result, Self::Error> { + let num_points = value.num_points(); + let mut geos_geoms = Vec::with_capacity(num_points); + + for point_idx in 0..num_points { + let point = value.point(point_idx).unwrap(); + geos_geoms.push(point.try_into()?); + } + + Ok(geos::Geometry::create_multipoint(geos_geoms)?) + } +} diff --git a/src/scalar/multipoint/mod.rs b/src/scalar/multipoint/mod.rs index 6401f1af1..32b21eb06 100644 --- a/src/scalar/multipoint/mod.rs +++ b/src/scalar/multipoint/mod.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "geos")] +mod geos; mod scalar; pub use scalar::MultiPoint; diff --git a/src/scalar/multipolygon/geos.rs b/src/scalar/multipolygon/geos.rs new file mode 100644 index 000000000..0e9ada1df --- /dev/null +++ b/src/scalar/multipolygon/geos.rs @@ -0,0 +1,28 @@ +use crate::error::GeoArrowError; +use crate::geo_traits::MultiPolygonTrait; +use crate::scalar::MultiPolygon; +use arrow2::types::Offset; + +impl<'b, O: Offset> TryFrom> for geos::Geometry<'b> { + type Error = GeoArrowError; + + fn try_from(value: MultiPolygon<'_, O>) -> Result, Self::Error> { + geos::Geometry::try_from(&value) + } +} + +impl<'a, 'b, O: Offset> TryFrom<&'a MultiPolygon<'_, O>> for geos::Geometry<'b> { + type Error = GeoArrowError; + + fn try_from(value: &'a MultiPolygon<'_, O>) -> Result, Self::Error> { + let num_polygons = value.num_polygons(); + let mut geos_geoms = Vec::with_capacity(num_polygons); + + for polygon_idx in 0..num_polygons { + let polygon = value.polygon(polygon_idx).unwrap(); + geos_geoms.push(polygon.try_into()?); + } + + Ok(geos::Geometry::create_multipolygon(geos_geoms)?) + } +} diff --git a/src/scalar/multipolygon/mod.rs b/src/scalar/multipolygon/mod.rs index 7d5ecf997..421e3c5a0 100644 --- a/src/scalar/multipolygon/mod.rs +++ b/src/scalar/multipolygon/mod.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "geos")] +mod geos; mod scalar; pub use scalar::MultiPolygon; diff --git a/src/scalar/point/geos.rs b/src/scalar/point/geos.rs new file mode 100644 index 000000000..40ee275b1 --- /dev/null +++ b/src/scalar/point/geos.rs @@ -0,0 +1,25 @@ +use crate::error::GeoArrowError; +use crate::geo_traits::PointTrait; +use crate::scalar::Point; +use geos::{CoordDimensions, CoordSeq}; + +impl<'b> TryFrom> for geos::Geometry<'b> { + type Error = GeoArrowError; + + fn try_from(value: Point<'_>) -> Result, Self::Error> { + geos::Geometry::try_from(&value) + } +} + +impl<'a, 'b> TryFrom<&'a Point<'_>> for geos::Geometry<'b> { + type Error = GeoArrowError; + + fn try_from(value: &'a Point<'_>) -> Result, Self::Error> { + let mut coord_seq = + CoordSeq::new(1, CoordDimensions::TwoD).expect("failed to create CoordSeq"); + coord_seq.set_x(0, value.x())?; + coord_seq.set_y(0, value.y())?; + + Ok(geos::Geometry::create_point(coord_seq)?) + } +} diff --git a/src/scalar/point/mod.rs b/src/scalar/point/mod.rs index 247dae076..d19d0863b 100644 --- a/src/scalar/point/mod.rs +++ b/src/scalar/point/mod.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "geos")] +mod geos; mod scalar; pub use scalar::Point; diff --git a/src/scalar/polygon/geos.rs b/src/scalar/polygon/geos.rs new file mode 100644 index 000000000..bdfc13fee --- /dev/null +++ b/src/scalar/polygon/geos.rs @@ -0,0 +1,30 @@ +use crate::error::GeoArrowError; +use crate::geo_traits::PolygonTrait; +use crate::scalar::Polygon; +use arrow2::types::Offset; + +impl<'b, O: Offset> TryFrom> for geos::Geometry<'b> { + type Error = GeoArrowError; + + fn try_from(value: Polygon<'_, O>) -> Result, Self::Error> { + geos::Geometry::try_from(&value) + } +} + +impl<'a, 'b, O: Offset> TryFrom<&'a Polygon<'_, O>> for geos::Geometry<'b> { + type Error = GeoArrowError; + + fn try_from(value: &'a Polygon<'_, O>) -> Result, Self::Error> { + let exterior = value.exterior().to_geos_linear_ring()?; + let num_interiors = value.num_interiors(); + + let mut interiors = Vec::with_capacity(num_interiors); + + for interior_idx in 0..num_interiors { + let interior = value.interior(interior_idx).unwrap(); + interiors.push(interior.to_geos_linear_ring()?); + } + + Ok(geos::Geometry::create_polygon(exterior, interiors)?) + } +} diff --git a/src/scalar/polygon/mod.rs b/src/scalar/polygon/mod.rs index 6965d1ebe..720e78289 100644 --- a/src/scalar/polygon/mod.rs +++ b/src/scalar/polygon/mod.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "geos")] +mod geos; mod scalar; pub use scalar::Polygon;