This is an automated email from the ASF dual-hosted git repository.

kontinuation pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/sedona-db.git


The following commit(s) were added to refs/heads/main by this push:
     new 2051517  feat: Add sedona-geo-traits-ext to sedona-db (#194)
2051517 is described below

commit 2051517207918263194925d9dc6792446e94e2f4
Author: Kristin Cowalcijk <[email protected]>
AuthorDate: Thu Oct 9 13:35:58 2025 +0800

    feat: Add sedona-geo-traits-ext to sedona-db (#194)
    
    This is part of the forked dependency elimination plan: 
https://github.com/apache/sedona-db/pull/165. This PR depends on 
https://github.com/apache/sedona-db/pull/193.
    
    This PR moves geo-traits-ext from wherobots/geo to sedona-db and renamed it 
to sedona-geo-traits-ext. Currently, it is a standalone crate and can be 
compiled using `cd rust/sedona-geo-traits-ext && cargo build`. We'll update the 
Cargo.toml files in the final step to make it live.
---
 Cargo.lock                                         |  10 +-
 rust/sedona-geo-traits-ext/Cargo.toml              |  42 ++
 rust/sedona-geo-traits-ext/README.md               |  37 ++
 rust/sedona-geo-traits-ext/src/coord.rs            |  69 +++
 rust/sedona-geo-traits-ext/src/geometry.rs         | 365 ++++++++++++++
 .../src/geometry_collection.rs                     | 113 +++++
 rust/sedona-geo-traits-ext/src/lib.rs              |  64 +++
 rust/sedona-geo-traits-ext/src/line.rs             | 140 ++++++
 rust/sedona-geo-traits-ext/src/line_string.rs      | 234 +++++++++
 .../sedona-geo-traits-ext/src/multi_line_string.rs | 123 +++++
 rust/sedona-geo-traits-ext/src/multi_point.rs      | 168 +++++++
 rust/sedona-geo-traits-ext/src/multi_polygon.rs    | 113 +++++
 rust/sedona-geo-traits-ext/src/point.rs            | 113 +++++
 rust/sedona-geo-traits-ext/src/polygon.rs          | 125 +++++
 rust/sedona-geo-traits-ext/src/rect.rs             | 331 ++++++++++++
 rust/sedona-geo-traits-ext/src/triangle.rs         | 200 ++++++++
 rust/sedona-geo-traits-ext/src/type_tag.rs         |  66 +++
 rust/sedona-geo-traits-ext/src/wkb_ext.rs          | 557 +++++++++++++++++++++
 rust/sedona-geo-traits-ext/tests/wkb_ext_tests.rs  | 230 +++++++++
 19 files changed, 3098 insertions(+), 2 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 93db6d4..d9a3c0a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2659,8 +2659,9 @@ dependencies = [
 
 [[package]]
 name = "geo-types"
-version = "0.7.16"
-source = 
"git+https://github.com/wherobots/geo.git?branch=generic-alg#66ff85949a82549b0d28fb2d4fae01e3ea19ca83";
+version = "0.7.17"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "75a4dcd69d35b2c87a7c83bce9af69fd65c9d68d3833a0ded568983928f3fc99"
 dependencies = [
  "approx",
  "num-traits",
@@ -6583,3 +6584,8 @@ dependencies = [
  "cc",
  "pkg-config",
 ]
+
+[[patch.unused]]
+name = "geo-types"
+version = "0.7.16"
+source = 
"git+https://github.com/wherobots/geo.git?branch=generic-alg#66ff85949a82549b0d28fb2d4fae01e3ea19ca83";
diff --git a/rust/sedona-geo-traits-ext/Cargo.toml 
b/rust/sedona-geo-traits-ext/Cargo.toml
new file mode 100644
index 0000000..9a2a465
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/Cargo.toml
@@ -0,0 +1,42 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+[package]
+name = "sedona-geo-traits-ext"
+version = "0.2.0"
+authors = ["Apache Sedona <[email protected]>"]
+license = "Apache-2.0"
+homepage = "https://github.com/apache/sedona-db";
+repository = "https://github.com/apache/sedona-db";
+description = "geo-traits extended for implementing generic algorithms"
+readme = "README.md"
+edition = "2021"
+
+[workspace]
+
+[dependencies]
+geo-traits = "0.3.0"
+geo-types = "0.7.17"
+num-traits = { version = "0.2", default-features = false, features = ["libm"] }
+wkb = "0.9.1"
+byteorder = "1"
+
+[dev-dependencies]
+wkt = "0.14.0"
+rstest = "0.24.0"
+
+[patch.crates-io]
+wkb = { git = "https://github.com/georust/wkb.git";, rev = 
"130eb0c2b343bc9299aeafba6d34c2a6e53f3b6a" }
diff --git a/rust/sedona-geo-traits-ext/README.md 
b/rust/sedona-geo-traits-ext/README.md
new file mode 100644
index 0000000..ce5ab0d
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/README.md
@@ -0,0 +1,37 @@
+<!-- Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License. -->
+
+# Geo-Traits Extended
+
+This crate extends the `geo-traits` crate with additional traits and
+implementations. The goal is to provide a set of traits that are useful for
+implementing algorithms in `geo-generic-alg` crate. Most of the methods are
+inspired by the `geo-types` crate, but are implemented as traits for the
+`geo-traits` types. Some methods returns concrete types defined in `geo-types`,
+these methods are only for computing tiny, intermediate results during
+algorithm execution. By adding methods in `geo-types` to `geo-traits-ext`,
+we can port algorithms in `geo` crate for concrete `geo-types` types to generic
+`geo-traits-ext` types more easily.
+
+`geo-traits-ext` traits also has an associated `Tag` type to workaround the
+single orphan rule in Rust. For instance, we cannot write blanket `AreaTrait`
+implementations for both `LineStringTrait` and `PolygonTrait` because we
+cannot show that there would be no type implementing both `LineStringTrait` and
+`PolygonTrait`. By adding an associated `Tag` type, we can write blanket
+implementations for `AreaTrait<LineStringTag>` and `AreaTrait<PolygonTag>`, 
since
+`AreaTrait<LineStringTag>` and `AreaTrait<PolygonTag>` are different types.
+Please refer to the source code of `sedona-geo-generic-alg` for more details.
diff --git a/rust/sedona-geo-traits-ext/src/coord.rs 
b/rust/sedona-geo-traits-ext/src/coord.rs
new file mode 100644
index 0000000..cd7472d
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/src/coord.rs
@@ -0,0 +1,69 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//! Extend CoordTrait traits for the `geo-traits` crate
+
+use geo_traits::{CoordTrait, UnimplementedCoord};
+use geo_types::{Coord, CoordNum};
+
+use crate::{CoordTag, GeoTraitExtWithTypeTag};
+
+/// Extension methods that bridge [`CoordTrait`] with concrete `geo-types` 
helpers.
+pub trait CoordTraitExt: CoordTrait + GeoTraitExtWithTypeTag<Tag = CoordTag>
+where
+    <Self as CoordTrait>::T: CoordNum,
+{
+    #[inline]
+    /// Converts this coordinate into the concrete [`geo_types::Coord`].
+    fn geo_coord(&self) -> Coord<Self::T> {
+        Coord {
+            x: self.x(),
+            y: self.y(),
+        }
+    }
+}
+
+impl<T> CoordTraitExt for Coord<T>
+where
+    T: CoordNum,
+{
+    fn geo_coord(&self) -> Coord<T> {
+        *self
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for Coord<T> {
+    type Tag = CoordTag;
+}
+
+impl<T> CoordTraitExt for &Coord<T>
+where
+    T: CoordNum,
+{
+    fn geo_coord(&self) -> Coord<T> {
+        **self
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for &Coord<T> {
+    type Tag = CoordTag;
+}
+
+impl<T> CoordTraitExt for UnimplementedCoord<T> where T: CoordNum {}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for UnimplementedCoord<T> {
+    type Tag = CoordTag;
+}
diff --git a/rust/sedona-geo-traits-ext/src/geometry.rs 
b/rust/sedona-geo-traits-ext/src/geometry.rs
new file mode 100644
index 0000000..d665cbb
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/src/geometry.rs
@@ -0,0 +1,365 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+// Extend GeometryTrait traits for the `geo-traits` crate
+
+use core::{borrow::Borrow, panic};
+
+use geo_traits::*;
+use geo_types::*;
+
+use crate::*;
+
+#[allow(clippy::type_complexity)]
+/// Extension trait that augments [`geo_traits::GeometryTrait`] with Sedona's
+/// additional helpers and type tagging support.
+///
+/// The trait adds accessors that mirror the behavior of `geo-types::Geometry`
+/// while keeping the code ergonomic when working through trait objects.
+/// Implementations must also opt into [`GeoTraitExtWithTypeTag`] so geometries
+/// can be introspected using [`GeometryTag`].
+pub trait GeometryTraitExt: GeometryTrait + GeoTraitExtWithTypeTag<Tag = 
GeometryTag>
+where
+    <Self as GeometryTrait>::T: CoordNum,
+{
+    /// Extension-aware point type exposed by this geometry.
+    type PointTypeExt<'a>: 'a + PointTraitExt<T = <Self as GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Extension-aware line string type exposed by this geometry.
+    type LineStringTypeExt<'a>: 'a + LineStringTraitExt<T = <Self as 
GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Extension-aware polygon type exposed by this geometry.
+    type PolygonTypeExt<'a>: 'a + PolygonTraitExt<T = <Self as 
GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Extension-aware multi point type exposed by this geometry.
+    type MultiPointTypeExt<'a>: 'a + MultiPointTraitExt<T = <Self as 
GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Extension-aware multi line string type exposed by this geometry.
+    type MultiLineStringTypeExt<'a>: 'a + MultiLineStringTraitExt<T = <Self as 
GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Extension-aware multi polygon type exposed by this geometry.
+    type MultiPolygonTypeExt<'a>: 'a + MultiPolygonTraitExt<T = <Self as 
GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Extension-aware triangle type exposed by this geometry.
+    type TriangleTypeExt<'a>: 'a + TriangleTraitExt<T = <Self as 
GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Extension-aware rectangle type exposed by this geometry.
+    type RectTypeExt<'a>: 'a + RectTraitExt<T = <Self as GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Extension-aware line type exposed by this geometry.
+    type LineTypeExt<'a>: 'a + LineTraitExt<T = <Self as GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    // Note that we don't have a GeometryCollectionTypeExt here, because it 
would introduce recursive GATs
+    // such as 
G::GeometryCollectionTypeExt::GeometryTypeExt::GeometryCollectionTypeExt::... 
and easily
+    // trigger a Rust compiler bug: 
https://github.com/rust-lang/rust/issues/128887 and 
https://github.com/rust-lang/rust/issues/131960.
+    // See also https://github.com/geoarrow/geoarrow-rs/issues/1339.
+    //
+    // Although this could be worked around by not implementing generic 
functions using trait-based approach and use
+    // function-based approach instead, see 
https://github.com/geoarrow/geoarrow-rs/pull/956 and 
https://github.com/georust/wkb/pull/77,
+    // we are not certain if there will be other issues caused by recursive 
GATs in the future. So we decided to completely get rid
+    // of recursive GATs.
+
+    /// Reference type yielded when iterating over nested geometries inside a 
collection.
+    type InnerGeometryRef<'a>: 'a + Borrow<Self>
+    where
+        Self: 'a;
+
+    /// Returns true if this geometry is a GeometryCollection
+    #[inline]
+    fn is_collection(&self) -> bool {
+        matches!(self.as_type(), GeometryType::GeometryCollection(_))
+    }
+
+    /// Returns the number of geometries inside this GeometryCollection
+    #[inline]
+    fn num_geometries_ext(&self) -> usize {
+        let GeometryType::GeometryCollection(gc) = self.as_type() else {
+            panic!("Not a GeometryCollection");
+        };
+        gc.num_geometries()
+    }
+
+    /// Cast this geometry to a [`GeometryTypeExt`] enum, which allows for 
downcasting to a specific
+    /// type. This does not work when the geometry is a GeometryCollection. 
Please use `is_collection`
+    /// to check if the geometry is NOT a GeometryCollection first before 
calling this method.
+    fn as_type_ext(
+        &self,
+    ) -> GeometryTypeExt<
+        '_,
+        Self::PointTypeExt<'_>,
+        Self::LineStringTypeExt<'_>,
+        Self::PolygonTypeExt<'_>,
+        Self::MultiPointTypeExt<'_>,
+        Self::MultiLineStringTypeExt<'_>,
+        Self::MultiPolygonTypeExt<'_>,
+        Self::RectTypeExt<'_>,
+        Self::TriangleTypeExt<'_>,
+        Self::LineTypeExt<'_>,
+    >;
+
+    /// Returns a geometry by index, or None if the index is out of bounds. 
This method only works with
+    /// GeometryCollection. Please use `is_collection` to check if the 
geometry is a GeometryCollection first before
+    /// calling this method.
+    fn geometry_ext(&self, i: usize) -> Option<Self::InnerGeometryRef<'_>>;
+
+    /// Returns a geometry by index without bounds checking. This method only 
works with GeometryCollection.
+    /// Please use `is_collection` to check if the geometry is a 
GeometryCollection first before calling this method.
+    ///
+    /// # Safety
+    /// The caller must ensure that `i` is a valid index less than the number 
of geometries.
+    /// Otherwise, this function may cause undefined behavior.
+    unsafe fn geometry_unchecked_ext(&self, i: usize) -> 
Self::InnerGeometryRef<'_>;
+
+    /// Returns an iterator over the geometries in this GeometryCollection. 
This method only works with
+    /// GeometryCollection. Please use `is_collection` to check if the 
geometry is a GeometryCollection first before
+    /// calling this method.
+    fn geometries_ext(&self) -> impl Iterator<Item = 
Self::InnerGeometryRef<'_>>;
+}
+
+#[derive(Debug)]
+/// Borrowed view into a concrete geometry type implementing the extension 
traits.
+pub enum GeometryTypeExt<'a, P, LS, Y, MP, ML, MY, R, TT, L>
+where
+    P: PointTraitExt,
+    LS: LineStringTraitExt,
+    Y: PolygonTraitExt,
+    MP: MultiPointTraitExt,
+    ML: MultiLineStringTraitExt,
+    MY: MultiPolygonTraitExt,
+    R: RectTraitExt,
+    TT: TriangleTraitExt,
+    L: LineTraitExt,
+    <P as GeometryTrait>::T: CoordNum,
+    <LS as GeometryTrait>::T: CoordNum,
+    <Y as GeometryTrait>::T: CoordNum,
+    <MP as GeometryTrait>::T: CoordNum,
+    <ML as GeometryTrait>::T: CoordNum,
+    <MY as GeometryTrait>::T: CoordNum,
+    <R as GeometryTrait>::T: CoordNum,
+    <TT as GeometryTrait>::T: CoordNum,
+    <L as GeometryTrait>::T: CoordNum,
+{
+    Point(&'a P),
+    LineString(&'a LS),
+    Polygon(&'a Y),
+    MultiPoint(&'a MP),
+    MultiLineString(&'a ML),
+    MultiPolygon(&'a MY),
+    Rect(&'a R),
+    Triangle(&'a TT),
+    Line(&'a L),
+}
+
+#[macro_export]
+/// Forwards [`GeometryTraitExt`] associated types and methods to the
+/// underlying [`geo_traits::GeometryTrait`] implementation while retaining the
+/// extension trait wrappers.
+macro_rules! forward_geometry_trait_ext_funcs {
+    ($t:ty) => {
+        type PointTypeExt<'__g_inner>
+            = <Self as GeometryTrait>::PointType<'__g_inner>
+        where
+            Self: '__g_inner;
+
+        type LineStringTypeExt<'__g_inner>
+            = <Self as GeometryTrait>::LineStringType<'__g_inner>
+        where
+            Self: '__g_inner;
+
+        type PolygonTypeExt<'__g_inner>
+            = <Self as GeometryTrait>::PolygonType<'__g_inner>
+        where
+            Self: '__g_inner;
+
+        type MultiPointTypeExt<'__g_inner>
+            = <Self as GeometryTrait>::MultiPointType<'__g_inner>
+        where
+            Self: '__g_inner;
+
+        type MultiLineStringTypeExt<'__g_inner>
+            = <Self as GeometryTrait>::MultiLineStringType<'__g_inner>
+        where
+            Self: '__g_inner;
+
+        type MultiPolygonTypeExt<'__g_inner>
+            = <Self as GeometryTrait>::MultiPolygonType<'__g_inner>
+        where
+            Self: '__g_inner;
+
+        type RectTypeExt<'__g_inner>
+            = <Self as GeometryTrait>::RectType<'__g_inner>
+        where
+            Self: '__g_inner;
+
+        type TriangleTypeExt<'__g_inner>
+            = <Self as GeometryTrait>::TriangleType<'__g_inner>
+        where
+            Self: '__g_inner;
+
+        type LineTypeExt<'__g_inner>
+            = <Self as GeometryTrait>::LineType<'__g_inner>
+        where
+            Self: '__g_inner;
+
+        fn as_type_ext(
+            &self,
+        ) -> GeometryTypeExt<
+            '_,
+            Self::PointTypeExt<'_>,
+            Self::LineStringTypeExt<'_>,
+            Self::PolygonTypeExt<'_>,
+            Self::MultiPointTypeExt<'_>,
+            Self::MultiLineStringTypeExt<'_>,
+            Self::MultiPolygonTypeExt<'_>,
+            Self::RectTypeExt<'_>,
+            Self::TriangleTypeExt<'_>,
+            Self::LineTypeExt<'_>,
+        > {
+            match self.as_type() {
+                GeometryType::Point(p) => GeometryTypeExt::Point(p),
+                GeometryType::LineString(ls) => 
GeometryTypeExt::LineString(ls),
+                GeometryType::Polygon(p) => GeometryTypeExt::Polygon(p),
+                GeometryType::MultiPoint(mp) => 
GeometryTypeExt::MultiPoint(mp),
+                GeometryType::MultiLineString(mls) => 
GeometryTypeExt::MultiLineString(mls),
+                GeometryType::MultiPolygon(mp) => 
GeometryTypeExt::MultiPolygon(mp),
+                GeometryType::GeometryCollection(_) => {
+                    panic!("GeometryCollection is not supported in 
GeometryTraitExt::as_type_ext")
+                }
+                GeometryType::Rect(r) => GeometryTypeExt::Rect(r),
+                GeometryType::Triangle(t) => GeometryTypeExt::Triangle(t),
+                GeometryType::Line(l) => GeometryTypeExt::Line(l),
+            }
+        }
+    };
+}
+
+impl<T> GeometryTraitExt for Geometry<T>
+where
+    T: CoordNum,
+{
+    forward_geometry_trait_ext_funcs!(T);
+
+    type InnerGeometryRef<'a>
+        = &'a Geometry<T>
+    where
+        Self: 'a;
+
+    fn geometry_ext(&self, i: usize) -> Option<&Geometry<T>> {
+        let GeometryType::GeometryCollection(gc) = self.as_type() else {
+            panic!("Not a GeometryCollection");
+        };
+        gc.geometry(i)
+    }
+
+    unsafe fn geometry_unchecked_ext(&self, i: usize) -> &Geometry<T> {
+        let GeometryType::GeometryCollection(gc) = self.as_type() else {
+            panic!("Not a GeometryCollection");
+        };
+        gc.geometry_unchecked(i)
+    }
+
+    fn geometries_ext(&self) -> impl Iterator<Item = &Geometry<T>> {
+        let GeometryType::GeometryCollection(gc) = self.as_type() else {
+            panic!("Not a GeometryCollection");
+        };
+        gc.geometries()
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for Geometry<T> {
+    type Tag = GeometryTag;
+}
+
+impl<'a, T> GeometryTraitExt for &'a Geometry<T>
+where
+    T: CoordNum,
+{
+    forward_geometry_trait_ext_funcs!(T);
+
+    type InnerGeometryRef<'b>
+        = &'a Geometry<T>
+    where
+        Self: 'b;
+
+    fn geometry_ext(&self, i: usize) -> Option<&'a Geometry<T>> {
+        let g = *self;
+        g.geometry_ext(i)
+    }
+
+    unsafe fn geometry_unchecked_ext(&self, i: usize) -> &'a Geometry<T> {
+        let g = *self;
+        g.geometry_unchecked_ext(i)
+    }
+
+    fn geometries_ext(&self) -> impl Iterator<Item = &'a Geometry<T>> {
+        let g = *self;
+        g.geometries_ext()
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for &Geometry<T> {
+    type Tag = GeometryTag;
+}
+
+impl<T> GeometryTraitExt for UnimplementedGeometry<T>
+where
+    T: CoordNum,
+{
+    forward_geometry_trait_ext_funcs!(T);
+
+    type InnerGeometryRef<'a>
+        = &'a UnimplementedGeometry<T>
+    where
+        Self: 'a;
+
+    fn geometry_ext(&self, _i: usize) -> Option<Self::InnerGeometryRef<'_>> {
+        unimplemented!()
+    }
+
+    unsafe fn geometry_unchecked_ext(&self, _i: usize) -> 
Self::InnerGeometryRef<'_> {
+        unimplemented!()
+    }
+
+    fn geometries_ext(&self) -> impl Iterator<Item = 
Self::InnerGeometryRef<'_>> {
+        unimplemented!();
+
+        // For making the type checker happy
+        #[allow(unreachable_code)]
+        core::iter::empty()
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for UnimplementedGeometry<T> {
+    type Tag = GeometryTag;
+}
diff --git a/rust/sedona-geo-traits-ext/src/geometry_collection.rs 
b/rust/sedona-geo-traits-ext/src/geometry_collection.rs
new file mode 100644
index 0000000..50f6c6f
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/src/geometry_collection.rs
@@ -0,0 +1,113 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+// Extend GeometryCollectionTrait traits for the `geo-traits` crate
+
+use geo_traits::{GeometryCollectionTrait, GeometryTrait, 
UnimplementedGeometryCollection};
+use geo_types::{CoordNum, GeometryCollection};
+
+use crate::{GeoTraitExtWithTypeTag, GeometryCollectionTag, GeometryTraitExt};
+
+/// Extension trait that enriches [`geo_traits::GeometryCollectionTrait`] with
+/// Sedona-specific conveniences.
+///
+/// The trait exposes accessor methods that return geometry values wrapped in
+/// [`GeometryTraitExt`], enabling downstream consumers to leverage the unified
+/// extension API regardless of the backing geometry type.
+pub trait GeometryCollectionTraitExt:
+    GeometryCollectionTrait + GeoTraitExtWithTypeTag<Tag = 
GeometryCollectionTag>
+where
+    <Self as GeometryTrait>::T: CoordNum,
+{
+    /// Extension-aware geometry type yielded by accessor methods.
+    type GeometryTypeExt<'a>: 'a + GeometryTraitExt<T = <Self as 
GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Returns the geometry at index `i`, wrapped with [`GeometryTraitExt`].
+    fn geometry_ext(&self, i: usize) -> Option<Self::GeometryTypeExt<'_>>;
+
+    /// Returns a geometry by index without bounds checking.
+    ///
+    /// # Safety
+    /// The caller must ensure that `i` is a valid index less than the number 
of geometries.
+    /// Otherwise, this function may cause undefined behavior.
+    unsafe fn geometry_unchecked_ext(&self, i: usize) -> 
Self::GeometryTypeExt<'_>;
+
+    /// Iterates over all geometries in the collection with extension wrappers 
applied.
+    fn geometries_ext(&self) -> impl Iterator<Item = 
Self::GeometryTypeExt<'_>>;
+}
+
+#[macro_export]
+/// Forwards [`GeometryCollectionTraitExt`] methods to the underlying
+/// [`geo_traits::GeometryCollectionTrait`] implementation while preserving the
+/// extension trait wrappers.
+macro_rules! forward_geometry_collection_trait_ext_funcs {
+    () => {
+        type GeometryTypeExt<'__gc_inner>
+            = <Self as GeometryCollectionTrait>::GeometryType<'__gc_inner>
+        where
+            Self: '__gc_inner;
+
+        #[inline]
+        fn geometry_ext(&self, i: usize) -> Option<Self::GeometryTypeExt<'_>> {
+            <Self as GeometryCollectionTrait>::geometry(self, i)
+        }
+
+        #[inline]
+        unsafe fn geometry_unchecked_ext(&self, i: usize) -> 
Self::GeometryTypeExt<'_> {
+            unsafe { <Self as 
GeometryCollectionTrait>::geometry_unchecked(self, i) }
+        }
+
+        #[inline]
+        fn geometries_ext(&self) -> impl Iterator<Item = 
Self::GeometryTypeExt<'_>> {
+            <Self as GeometryCollectionTrait>::geometries(self)
+        }
+    };
+}
+
+impl<T> GeometryCollectionTraitExt for GeometryCollection<T>
+where
+    T: CoordNum,
+{
+    forward_geometry_collection_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for GeometryCollection<T> {
+    type Tag = GeometryCollectionTag;
+}
+
+impl<T> GeometryCollectionTraitExt for &GeometryCollection<T>
+where
+    T: CoordNum,
+{
+    forward_geometry_collection_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for &GeometryCollection<T> {
+    type Tag = GeometryCollectionTag;
+}
+
+impl<T> GeometryCollectionTraitExt for UnimplementedGeometryCollection<T>
+where
+    T: CoordNum,
+{
+    forward_geometry_collection_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for 
UnimplementedGeometryCollection<T> {
+    type Tag = GeometryCollectionTag;
+}
diff --git a/rust/sedona-geo-traits-ext/src/lib.rs 
b/rust/sedona-geo-traits-ext/src/lib.rs
new file mode 100644
index 0000000..12729e7
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/src/lib.rs
@@ -0,0 +1,64 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//! Extended traits for the `geo-traits` crate
+//!
+//! This crate extends the `geo-traits` crate with additional traits and
+//! implementations. The goal is to provide a set of traits that are useful for
+//! implementing algorithms on top of the `geo` crate. Most of the methods are
+//! inspired by the `geo-types` crate, but are implemented as traits on the
+//! `geo-traits` types. Some methods returns concrete types defined in 
`geo-types`,
+//! these methods are only for computing tiny, intermediate results during
+//! algorithm execution.
+//!
+//! The crate is designed to support migration of the `geo` crate to use the
+//! traits defined in `geo-traits` by providing generic implementations of the
+//! geospatial algorithms, rather than implementing algorithms on concrete 
types
+//! defined in `geo-types`.
+//!
+//! The crate is currently under active development and the API is subject to
+//! change.
+
+pub use coord::CoordTraitExt;
+pub use geometry::{GeometryTraitExt, GeometryTypeExt};
+pub use geometry_collection::GeometryCollectionTraitExt;
+pub use line::LineTraitExt;
+pub use line_string::LineStringTraitExt;
+pub use multi_line_string::MultiLineStringTraitExt;
+pub use multi_point::MultiPointTraitExt;
+pub use multi_polygon::MultiPolygonTraitExt;
+pub use point::PointTraitExt;
+pub use polygon::PolygonTraitExt;
+pub use rect::RectTraitExt;
+pub use triangle::TriangleTraitExt;
+
+mod coord;
+mod geometry;
+mod geometry_collection;
+mod line;
+mod line_string;
+mod multi_line_string;
+mod multi_point;
+mod multi_polygon;
+mod point;
+mod polygon;
+mod rect;
+mod triangle;
+
+pub use type_tag::*;
+mod type_tag;
+
+pub mod wkb_ext;
diff --git a/rust/sedona-geo-traits-ext/src/line.rs 
b/rust/sedona-geo-traits-ext/src/line.rs
new file mode 100644
index 0000000..94665a1
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/src/line.rs
@@ -0,0 +1,140 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+// Extend LineTrait traits for the `geo-traits` crate
+
+use geo_traits::{GeometryTrait, LineTrait, UnimplementedLine};
+use geo_types::{CoordNum, Line};
+
+use crate::{CoordTraitExt, GeoTraitExtWithTypeTag, LineTag};
+
+/// Extra helpers for [`LineTrait`] implementers that mirror `geo-types` APIs.
+pub trait LineTraitExt: LineTrait + GeoTraitExtWithTypeTag<Tag = LineTag>
+where
+    <Self as GeometryTrait>::T: CoordNum,
+{
+    type CoordTypeExt<'a>: 'a + CoordTraitExt<T = <Self as GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Returns the start coordinate as an extension trait instance.
+    fn start_ext(&self) -> Self::CoordTypeExt<'_>;
+    /// Returns the end coordinate as an extension trait instance.
+    fn end_ext(&self) -> Self::CoordTypeExt<'_>;
+    /// Returns both start and end coordinates in a fixed-size array.
+    fn coords_ext(&self) -> [Self::CoordTypeExt<'_>; 2];
+
+    #[inline]
+    /// Returns the start coordinate converted to [`geo_types::Coord`].
+    fn start_coord(&self) -> geo_types::Coord<<Self as GeometryTrait>::T> {
+        self.start_ext().geo_coord()
+    }
+
+    #[inline]
+    /// Returns the end coordinate converted to [`geo_types::Coord`].
+    fn end_coord(&self) -> geo_types::Coord<<Self as GeometryTrait>::T> {
+        self.end_ext().geo_coord()
+    }
+
+    #[inline]
+    /// Returns the line converted to a [`geo_types::Line`].
+    fn geo_line(&self) -> Line<<Self as GeometryTrait>::T> {
+        Line::new(self.start_coord(), self.end_coord())
+    }
+}
+
+#[macro_export]
+/// Forwards [`LineTraitExt`] methods to an underlying [`LineTrait`] 
implementation.
+macro_rules! forward_line_trait_ext_funcs {
+    () => {
+        type CoordTypeExt<'__l_inner>
+            = <Self as LineTrait>::CoordType<'__l_inner>
+        where
+            Self: '__l_inner;
+
+        #[inline]
+        fn start_ext(&self) -> Self::CoordTypeExt<'_> {
+            <Self as LineTrait>::start(self)
+        }
+
+        #[inline]
+        fn end_ext(&self) -> Self::CoordTypeExt<'_> {
+            <Self as LineTrait>::end(self)
+        }
+
+        #[inline]
+        fn coords_ext(&self) -> [Self::CoordTypeExt<'_>; 2] {
+            [self.start_ext(), self.end_ext()]
+        }
+    };
+}
+
+impl<T> LineTraitExt for Line<T>
+where
+    T: CoordNum,
+{
+    forward_line_trait_ext_funcs!();
+
+    fn start_coord(&self) -> geo_types::Coord<T> {
+        self.start
+    }
+
+    fn end_coord(&self) -> geo_types::Coord<T> {
+        self.end
+    }
+
+    fn geo_line(&self) -> geo_types::Line<T> {
+        *self
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for Line<T> {
+    type Tag = LineTag;
+}
+
+impl<T> LineTraitExt for &Line<T>
+where
+    T: CoordNum,
+{
+    forward_line_trait_ext_funcs!();
+
+    fn start_coord(&self) -> geo_types::Coord<T> {
+        self.start
+    }
+
+    fn end_coord(&self) -> geo_types::Coord<T> {
+        self.end
+    }
+
+    fn geo_line(&self) -> geo_types::Line<T> {
+        **self
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for &Line<T> {
+    type Tag = LineTag;
+}
+
+impl<T> LineTraitExt for UnimplementedLine<T>
+where
+    T: CoordNum,
+{
+    forward_line_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for UnimplementedLine<T> {
+    type Tag = LineTag;
+}
diff --git a/rust/sedona-geo-traits-ext/src/line_string.rs 
b/rust/sedona-geo-traits-ext/src/line_string.rs
new file mode 100644
index 0000000..86c07c9
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/src/line_string.rs
@@ -0,0 +1,234 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+// Extend LineStringTrait traits for the `geo-traits` crate
+
+use geo_traits::{GeometryTrait, LineStringTrait, UnimplementedLineString};
+use geo_types::{Coord, CoordNum, Line, LineString, Triangle};
+
+use crate::{CoordTraitExt, GeoTraitExtWithTypeTag, LineStringTag};
+
+/// Additional convenience methods for [`LineStringTrait`] implementers that 
mirror `geo-types`.
+pub trait LineStringTraitExt:
+    LineStringTrait + GeoTraitExtWithTypeTag<Tag = LineStringTag>
+where
+    <Self as GeometryTrait>::T: CoordNum,
+{
+    type CoordTypeExt<'a>: 'a + CoordTraitExt<T = <Self as GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Returns the coordinate at the provided index.
+    fn coord_ext(&self, i: usize) -> Option<Self::CoordTypeExt<'_>>;
+
+    /// Returns a coordinate by index without bounds checking.
+    ///
+    /// # Safety
+    /// The caller must ensure that `i` is a valid index less than the number 
of coordinates.
+    /// Otherwise, this function may cause undefined behavior.
+    unsafe fn coord_unchecked_ext(&self, i: usize) -> Self::CoordTypeExt<'_>;
+
+    /// Returns an iterator over all coordinates as extension trait instances.
+    fn coords_ext(&self) -> impl Iterator<Item = Self::CoordTypeExt<'_>>;
+
+    /// Returns a coordinate by index without bounds checking.
+    ///
+    /// # Safety
+    /// The caller must ensure that `i` is a valid index less than the number 
of coordinates.
+    /// Otherwise, this function may cause undefined behavior.
+    #[inline]
+    unsafe fn geo_coord_unchecked(&self, i: usize) -> Coord<Self::T> {
+        self.coord_unchecked_ext(i).geo_coord()
+    }
+
+    /// Return an iterator yielding one [`Line`] for each line segment
+    /// in the [`LineString`][`geo_types::LineString`].
+    #[inline]
+    fn lines(&'_ self) -> impl ExactSizeIterator<Item = Line<<Self as 
GeometryTrait>::T>> + '_ {
+        let num_coords = self.num_coords();
+        (0..num_coords.saturating_sub(1)).map(|i| unsafe {
+            let coord1 = self.geo_coord_unchecked(i);
+            let coord2 = self.geo_coord_unchecked(i + 1);
+            Line::new(coord1, coord2)
+        })
+    }
+
+    /// Return an iterator yielding one [`Line`] for each line segment in the 
[`LineString`][`geo_types::LineString`],
+    /// starting from the **end** point of the LineString, working towards the 
start.
+    ///
+    /// Note: This is like [`Self::lines`], but the sequence **and** the 
orientation of
+    /// segments are reversed.
+    #[inline]
+    fn rev_lines(&'_ self) -> impl ExactSizeIterator<Item = Line<<Self as 
GeometryTrait>::T>> + '_ {
+        let num_coords = self.num_coords();
+        (1..num_coords).rev().map(|i| unsafe {
+            let coord1 = self.geo_coord_unchecked(i);
+            let coord2 = self.geo_coord_unchecked(i - 1);
+            Line::new(coord2, coord1)
+        })
+    }
+
+    /// An iterator which yields the coordinates of a 
[`LineString`][`geo_types::LineString`] as [Triangle]s
+    #[inline]
+    fn triangles(
+        &'_ self,
+    ) -> impl ExactSizeIterator<Item = Triangle<<Self as GeometryTrait>::T>> + 
'_ {
+        let num_coords = self.num_coords();
+        let end = num_coords.saturating_sub(2);
+        (0..end).map(|i| unsafe {
+            let coord1 = self.geo_coord_unchecked(i);
+            let coord2 = self.geo_coord_unchecked(i + 1);
+            let coord3 = self.geo_coord_unchecked(i + 2);
+            Triangle::new(coord1, coord2, coord3)
+        })
+    }
+
+    /// Returns an iterator yielding the coordinates of this line string as 
[`geo_types::Coord`] values.
+    #[inline]
+    fn coord_iter(&self) -> impl Iterator<Item = Coord<<Self as 
GeometryTrait>::T>> {
+        self.coords_ext().map(|c| c.geo_coord())
+    }
+
+    #[inline]
+    /// Returns true when the line string is closed (its first and last 
coordinates are equal).
+    fn is_closed(&self) -> bool {
+        let num_coords = self.num_coords();
+        if num_coords <= 1 {
+            true
+        } else {
+            let (first, last) = unsafe {
+                (
+                    self.geo_coord_unchecked(0),
+                    self.geo_coord_unchecked(num_coords - 1),
+                )
+            };
+            first == last
+        }
+    }
+}
+
+#[macro_export]
+/// Forwards [`LineStringTraitExt`] methods to an underlying 
[`LineStringTrait`] implementation.
+macro_rules! forward_line_string_trait_ext_funcs {
+    () => {
+        type CoordTypeExt<'__l_inner>
+            = <Self as LineStringTrait>::CoordType<'__l_inner>
+        where
+            Self: '__l_inner;
+
+        #[inline]
+        fn coord_ext(&self, i: usize) -> Option<Self::CoordTypeExt<'_>> {
+            <Self as LineStringTrait>::coord(self, i)
+        }
+
+        #[inline]
+        unsafe fn coord_unchecked_ext(&self, i: usize) -> 
Self::CoordTypeExt<'_> {
+            <Self as LineStringTrait>::coord_unchecked(self, i)
+        }
+
+        #[inline]
+        fn coords_ext(&self) -> impl Iterator<Item = Self::CoordTypeExt<'_>> {
+            <Self as LineStringTrait>::coords(self)
+        }
+    };
+}
+
+impl<T> LineStringTraitExt for LineString<T>
+where
+    T: CoordNum,
+{
+    forward_line_string_trait_ext_funcs!();
+
+    unsafe fn geo_coord_unchecked(&self, i: usize) -> Coord<Self::T> {
+        *self.0.get_unchecked(i)
+    }
+
+    // Delegate to the `geo-types` implementation for less performance overhead
+    fn lines(&'_ self) -> impl ExactSizeIterator<Item = Line<<Self as 
GeometryTrait>::T>> + '_ {
+        self.lines()
+    }
+
+    fn rev_lines(&'_ self) -> impl ExactSizeIterator<Item = Line<<Self as 
GeometryTrait>::T>> + '_ {
+        self.rev_lines()
+    }
+
+    fn triangles(
+        &'_ self,
+    ) -> impl ExactSizeIterator<Item = Triangle<<Self as GeometryTrait>::T>> + 
'_ {
+        self.triangles()
+    }
+
+    fn is_closed(&self) -> bool {
+        self.is_closed()
+    }
+
+    fn coord_iter(&self) -> impl Iterator<Item = Coord<<Self as 
GeometryTrait>::T>> {
+        self.0.iter().copied()
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for LineString<T> {
+    type Tag = LineStringTag;
+}
+
+impl<T> LineStringTraitExt for &LineString<T>
+where
+    T: CoordNum,
+{
+    forward_line_string_trait_ext_funcs!();
+
+    unsafe fn geo_coord_unchecked(&self, i: usize) -> Coord<Self::T> {
+        *self.0.get_unchecked(i)
+    }
+
+    // Delegate to the `geo-types` implementation for less performance overhead
+    fn lines(&'_ self) -> impl ExactSizeIterator<Item = Line<<Self as 
GeometryTrait>::T>> + '_ {
+        (*self).lines()
+    }
+
+    fn rev_lines(&'_ self) -> impl ExactSizeIterator<Item = Line<<Self as 
GeometryTrait>::T>> + '_ {
+        (*self).rev_lines()
+    }
+
+    fn triangles(
+        &'_ self,
+    ) -> impl ExactSizeIterator<Item = Triangle<<Self as GeometryTrait>::T>> + 
'_ {
+        (*self).triangles()
+    }
+
+    fn is_closed(&self) -> bool {
+        (*self).is_closed()
+    }
+
+    fn coord_iter(&self) -> impl Iterator<Item = Coord<<Self as 
GeometryTrait>::T>> {
+        self.0.iter().copied()
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for &LineString<T> {
+    type Tag = LineStringTag;
+}
+
+impl<T> LineStringTraitExt for UnimplementedLineString<T>
+where
+    T: CoordNum,
+{
+    forward_line_string_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for UnimplementedLineString<T> {
+    type Tag = LineStringTag;
+}
diff --git a/rust/sedona-geo-traits-ext/src/multi_line_string.rs 
b/rust/sedona-geo-traits-ext/src/multi_line_string.rs
new file mode 100644
index 0000000..af4f371
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/src/multi_line_string.rs
@@ -0,0 +1,123 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+// Extend MultiLineStringTrait traits for the `geo-traits` crate
+
+use geo_traits::{GeometryTrait, MultiLineStringTrait, 
UnimplementedMultiLineString};
+use geo_types::{CoordNum, MultiLineString};
+
+use crate::{GeoTraitExtWithTypeTag, LineStringTraitExt, MultiLineStringTag};
+
+/// Extension trait that layers additional ergonomics on
+/// [`geo_traits::MultiLineStringTrait`].
+///
+/// implementers gain access to extension-aware iterators and helper methods
+/// that mirror the behavior of `geo-types::MultiLineString`, while still being
+/// consumable through the trait abstractions provided by `geo-traits`.
+pub trait MultiLineStringTraitExt:
+    MultiLineStringTrait + GeoTraitExtWithTypeTag<Tag = MultiLineStringTag>
+where
+    <Self as GeometryTrait>::T: CoordNum,
+{
+    /// Extension-friendly line string type returned by accessor methods.
+    type LineStringTypeExt<'a>: 'a + LineStringTraitExt<T = <Self as 
GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Returns the line string at index `i` with the extension trait applied.
+    ///
+    /// This is analogous to [`geo_traits::MultiLineStringTrait::line_string`]
+    /// but ensures the result implements [`LineStringTraitExt`].
+    fn line_string_ext(&self, i: usize) -> Option<Self::LineStringTypeExt<'_>>;
+
+    /// Returns a line string by index without bounds checking.
+    ///
+    /// # Safety
+    /// The caller must ensure that `i` is a valid index less than the number 
of line strings.
+    /// Otherwise, this function may cause undefined behavior.
+    unsafe fn line_string_unchecked_ext(&self, i: usize) -> 
Self::LineStringTypeExt<'_>;
+
+    /// Iterates over all line strings with extension-aware wrappers applied.
+    fn line_strings_ext(&self) -> impl Iterator<Item = 
Self::LineStringTypeExt<'_>>;
+
+    /// Returns `true` when the multi line string is empty or every component 
is closed.
+    #[inline]
+    fn is_closed(&self) -> bool {
+        // Note: Unlike JTS et al, we consider an empty MultiLineString as 
closed.
+        self.line_strings_ext().all(|ls| ls.is_closed())
+    }
+}
+
+#[macro_export]
+/// Forwards [`MultiLineStringTraitExt`] methods to the underlying
+/// [`geo_traits::MultiLineStringTrait`] implementation while keeping the
+/// extension trait wrappers intact.
+macro_rules! forward_multi_line_string_trait_ext_funcs {
+    () => {
+        type LineStringTypeExt<'__l_inner>
+            = <Self as MultiLineStringTrait>::InnerLineStringType<'__l_inner>
+        where
+            Self: '__l_inner;
+
+        #[inline]
+        fn line_string_ext(&self, i: usize) -> 
Option<Self::LineStringTypeExt<'_>> {
+            <Self as MultiLineStringTrait>::line_string(self, i)
+        }
+
+        #[inline]
+        unsafe fn line_string_unchecked_ext(&self, i: usize) -> 
Self::LineStringTypeExt<'_> {
+            <Self as MultiLineStringTrait>::line_string_unchecked(self, i)
+        }
+
+        #[inline]
+        fn line_strings_ext(&self) -> impl Iterator<Item = 
Self::LineStringTypeExt<'_>> {
+            <Self as MultiLineStringTrait>::line_strings(self)
+        }
+    };
+}
+
+impl<T> MultiLineStringTraitExt for MultiLineString<T>
+where
+    T: CoordNum,
+{
+    forward_multi_line_string_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for MultiLineString<T> {
+    type Tag = MultiLineStringTag;
+}
+
+impl<T> MultiLineStringTraitExt for &MultiLineString<T>
+where
+    T: CoordNum,
+{
+    forward_multi_line_string_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for &MultiLineString<T> {
+    type Tag = MultiLineStringTag;
+}
+
+impl<T> MultiLineStringTraitExt for UnimplementedMultiLineString<T>
+where
+    T: CoordNum,
+{
+    forward_multi_line_string_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for UnimplementedMultiLineString<T> {
+    type Tag = MultiLineStringTag;
+}
diff --git a/rust/sedona-geo-traits-ext/src/multi_point.rs 
b/rust/sedona-geo-traits-ext/src/multi_point.rs
new file mode 100644
index 0000000..bdd40c8
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/src/multi_point.rs
@@ -0,0 +1,168 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+// Extend MultiPointTrait traits for the `geo-traits` crate
+
+use geo_traits::{GeometryTrait, MultiPointTrait, UnimplementedMultiPoint};
+use geo_types::{Coord, CoordNum, MultiPoint};
+
+use crate::{CoordTraitExt, GeoTraitExtWithTypeTag, MultiPointTag, 
PointTraitExt};
+
+/// Extension trait that augments [`geo_traits::MultiPointTrait`] with richer
+/// ergonomics and accessors.
+///
+/// The trait keeps parity with the APIs provided by `geo-types::MultiPoint`
+/// while still working with trait objects that only implement
+/// [`geo_traits::MultiPointTrait`]. It also wires the geometry up with a
+/// [`MultiPointTag`](crate::MultiPointTag) so the type can participate in the
+/// shared `GeoTraitExtWithTypeTag` machinery.
+pub trait MultiPointTraitExt:
+    MultiPointTrait + GeoTraitExtWithTypeTag<Tag = MultiPointTag>
+where
+    <Self as GeometryTrait>::T: CoordNum,
+{
+    /// Extension-aware point type returned from accessors on this multi point.
+    type PointTypeExt<'a>: 'a + PointTraitExt<T = <Self as GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Returns the point at index `i`, wrapped in the extension trait.
+    ///
+    /// This mirrors [`geo_traits::MultiPointTrait::point`] but guarantees the
+    /// returned point implements [`PointTraitExt`].
+    fn point_ext(&self, i: usize) -> Option<Self::PointTypeExt<'_>>;
+
+    /// Returns a point by index without bounds checking.
+    ///
+    /// # Safety
+    /// The caller must ensure that `i` is a valid index less than the number 
of points.
+    /// Otherwise, this function may cause undefined behavior.
+    unsafe fn point_unchecked_ext(&self, i: usize) -> Self::PointTypeExt<'_>;
+
+    /// Returns a coordinate by index without bounds checking.
+    ///
+    /// # Safety
+    /// The caller must ensure that `i` is a valid index less than the number 
of points.
+    /// Otherwise, this function may cause undefined behavior.
+    /// Returns the coordinate at index `i` without bounds checking.
+    ///
+    /// This helper is primarily used by iterator adapters that need direct
+    /// coordinate access while still honoring the [`PointTraitExt`] 
abstraction.
+    ///
+    /// # Safety
+    /// The caller must ensure that `i` is a valid index less than the number 
of points.
+    /// Otherwise, this function may cause undefined behavior.
+    #[inline]
+    unsafe fn geo_coord_unchecked(&self, i: usize) -> Option<Coord<<Self as 
GeometryTrait>::T>> {
+        let point = unsafe { self.point_unchecked_ext(i) };
+        point.coord_ext().map(|c| c.geo_coord())
+    }
+
+    /// Returns an iterator over all points, each wrapped in [`PointTraitExt`].
+    fn points_ext(&self) -> impl DoubleEndedIterator<Item = 
Self::PointTypeExt<'_>>;
+
+    /// Iterates over the coordinates contained in this multi point.
+    ///
+    /// For trait-based implementations this is derived from
+    /// [`points_ext`](Self::points_ext), while concrete 
`geo-types::MultiPoint`
+    /// instances provide a specialized iterator that avoids intermediate
+    /// allocations.
+    #[inline]
+    fn coord_iter(&self) -> impl DoubleEndedIterator<Item = Coord<<Self as 
GeometryTrait>::T>> {
+        self.points_ext().flat_map(|p| p.geo_coord())
+    }
+}
+
+#[macro_export]
+/// Forwards [`MultiPointTraitExt`] methods to the underlying
+/// [`geo_traits::MultiPointTrait`] implementation while maintaining the
+/// extension trait wrappers.
+macro_rules! forward_multi_point_trait_ext_funcs {
+    () => {
+        type PointTypeExt<'__l_inner>
+            = <Self as MultiPointTrait>::InnerPointType<'__l_inner>
+        where
+            Self: '__l_inner;
+
+        #[inline]
+        fn point_ext(&self, i: usize) -> Option<Self::PointTypeExt<'_>> {
+            <Self as MultiPointTrait>::point(self, i)
+        }
+
+        #[inline]
+        unsafe fn point_unchecked_ext(&self, i: usize) -> 
Self::PointTypeExt<'_> {
+            <Self as MultiPointTrait>::point_unchecked(self, i)
+        }
+
+        #[inline]
+        fn points_ext(&self) -> impl DoubleEndedIterator<Item = 
Self::PointTypeExt<'_>> {
+            <Self as MultiPointTrait>::points(self)
+        }
+    };
+}
+
+impl<T> MultiPointTraitExt for MultiPoint<T>
+where
+    T: CoordNum,
+{
+    forward_multi_point_trait_ext_funcs!();
+
+    /// Specialized coordinate accessor for `geo_types::MultiPoint`.
+    unsafe fn geo_coord_unchecked(&self, i: usize) -> Option<Coord<T>> {
+        Some(self.0.get_unchecked(i).0)
+    }
+
+    // Specialized implementation for geo_types::MultiPoint to reduce 
performance overhead
+    fn coord_iter(&self) -> impl DoubleEndedIterator<Item = Coord<<Self as 
GeometryTrait>::T>> {
+        self.0.iter().map(|p| p.0)
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for MultiPoint<T> {
+    type Tag = MultiPointTag;
+}
+
+impl<T> MultiPointTraitExt for &MultiPoint<T>
+where
+    T: CoordNum,
+{
+    forward_multi_point_trait_ext_funcs!();
+
+    /// Specialized coordinate accessor for `&geo_types::MultiPoint`.
+    unsafe fn geo_coord_unchecked(&self, i: usize) -> Option<Coord<T>> {
+        Some(self.0.get_unchecked(i).0)
+    }
+
+    // Specialized implementation for geo_types::MultiPoint to reduce 
performance overhead
+    fn coord_iter(&self) -> impl DoubleEndedIterator<Item = Coord<<Self as 
GeometryTrait>::T>> {
+        self.0.iter().map(|p| p.0)
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for &MultiPoint<T> {
+    type Tag = MultiPointTag;
+}
+
+impl<T> MultiPointTraitExt for UnimplementedMultiPoint<T>
+where
+    T: CoordNum,
+{
+    forward_multi_point_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for UnimplementedMultiPoint<T> {
+    type Tag = MultiPointTag;
+}
diff --git a/rust/sedona-geo-traits-ext/src/multi_polygon.rs 
b/rust/sedona-geo-traits-ext/src/multi_polygon.rs
new file mode 100644
index 0000000..9b90afa
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/src/multi_polygon.rs
@@ -0,0 +1,113 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+// Extend MultiPolygonTrait traits for the `geo-traits` crate
+
+use geo_traits::{GeometryTrait, MultiPolygonTrait, UnimplementedMultiPolygon};
+use geo_types::{CoordNum, MultiPolygon};
+
+use crate::{GeoTraitExtWithTypeTag, MultiPolygonTag, PolygonTraitExt};
+
+/// Extension trait that enriches [`geo_traits::MultiPolygonTrait`] with Sedona
+/// conveniences.
+///
+/// Implementations can expose polygon members through the
+/// [`PolygonTraitExt`] abstraction, ensuring consistent access to exterior and
+/// interior rings regardless of the backing geometry type.
+pub trait MultiPolygonTraitExt:
+    MultiPolygonTrait + GeoTraitExtWithTypeTag<Tag = MultiPolygonTag>
+where
+    <Self as GeometryTrait>::T: CoordNum,
+{
+    /// Extension-aware polygon type yielded by accessor methods.
+    type PolygonTypeExt<'a>: 'a + PolygonTraitExt<T = <Self as 
GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Returns the polygon at index `i`, wrapped with [`PolygonTraitExt`].
+    fn polygon_ext(&self, i: usize) -> Option<Self::PolygonTypeExt<'_>>;
+
+    /// Returns a polygon by index without bounds checking.
+    ///
+    /// # Safety
+    /// The caller must ensure that `i` is a valid index less than the number 
of polygons.
+    /// Otherwise, this function may cause undefined behavior.
+    unsafe fn polygon_unchecked_ext(&self, i: usize) -> 
Self::PolygonTypeExt<'_>;
+
+    /// Iterates over all polygon members, each wrapped with the extension 
trait.
+    fn polygons_ext(&self) -> impl Iterator<Item = Self::PolygonTypeExt<'_>>;
+}
+
+#[macro_export]
+/// Forwards [`MultiPolygonTraitExt`] methods to the underlying
+/// [`geo_traits::MultiPolygonTrait`] implementation while preserving the
+/// extension trait wrappers.
+macro_rules! forward_multi_polygon_trait_ext_funcs {
+    () => {
+        type PolygonTypeExt<'__l_inner>
+            = <Self as MultiPolygonTrait>::InnerPolygonType<'__l_inner>
+        where
+            Self: '__l_inner;
+
+        #[inline]
+        fn polygon_ext(&self, i: usize) -> Option<Self::PolygonTypeExt<'_>> {
+            <Self as MultiPolygonTrait>::polygon(self, i)
+        }
+
+        #[inline]
+        unsafe fn polygon_unchecked_ext(&self, i: usize) -> 
Self::PolygonTypeExt<'_> {
+            <Self as MultiPolygonTrait>::polygon_unchecked(self, i)
+        }
+
+        #[inline]
+        fn polygons_ext(&self) -> impl Iterator<Item = 
Self::PolygonTypeExt<'_>> {
+            <Self as MultiPolygonTrait>::polygons(self)
+        }
+    };
+}
+
+impl<T> MultiPolygonTraitExt for MultiPolygon<T>
+where
+    T: CoordNum,
+{
+    forward_multi_polygon_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for MultiPolygon<T> {
+    type Tag = MultiPolygonTag;
+}
+
+impl<T> MultiPolygonTraitExt for &MultiPolygon<T>
+where
+    T: CoordNum,
+{
+    forward_multi_polygon_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for &MultiPolygon<T> {
+    type Tag = MultiPolygonTag;
+}
+
+impl<T> MultiPolygonTraitExt for UnimplementedMultiPolygon<T>
+where
+    T: CoordNum,
+{
+    forward_multi_polygon_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for UnimplementedMultiPolygon<T> {
+    type Tag = MultiPolygonTag;
+}
diff --git a/rust/sedona-geo-traits-ext/src/point.rs 
b/rust/sedona-geo-traits-ext/src/point.rs
new file mode 100644
index 0000000..3548961
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/src/point.rs
@@ -0,0 +1,113 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+// Extend PointTrait traits for the `geo-traits` crate
+
+use geo_traits::{CoordTrait, GeometryTrait, PointTrait, UnimplementedPoint};
+use geo_types::{Coord, CoordNum, Point};
+
+use crate::{CoordTraitExt, GeoTraitExtWithTypeTag, PointTag};
+
+/// Extension methods that expose `geo-types` conveniences for [`PointTrait`] 
implementers.
+pub trait PointTraitExt: PointTrait + GeoTraitExtWithTypeTag<Tag = PointTag>
+where
+    <Self as GeometryTrait>::T: CoordNum,
+{
+    type CoordTypeExt<'a>: 'a + CoordTraitExt<T = <Self as GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Returns the underlying coordinate view for this point, if available.
+    fn coord_ext(&self) -> Option<Self::CoordTypeExt<'_>>;
+
+    #[inline]
+    /// Converts the point into a concrete [`geo_types::Point`].
+    fn geo_point(&self) -> Option<Point<<Self as GeometryTrait>::T>> {
+        self.coord_ext()
+            .map(|coord| Point::new(coord.x(), coord.y()))
+    }
+
+    #[inline]
+    /// Converts the point into a concrete [`geo_types::Coord`].
+    fn geo_coord(&self) -> Option<Coord<<Self as GeometryTrait>::T>> {
+        self.coord_ext().map(|coord| coord.geo_coord())
+    }
+}
+
+#[macro_export]
+/// Forwards [`PointTraitExt`] methods to the wrapped [`PointTrait`] 
implementation.
+macro_rules! forward_point_trait_ext_funcs {
+    () => {
+        type CoordTypeExt<'__l_inner>
+            = <Self as PointTrait>::CoordType<'__l_inner>
+        where
+            Self: '__l_inner;
+
+        #[inline]
+        fn coord_ext(&self) -> Option<Self::CoordTypeExt<'_>> {
+            <Self as PointTrait>::coord(self)
+        }
+    };
+}
+
+impl<T> PointTraitExt for Point<T>
+where
+    T: CoordNum,
+{
+    forward_point_trait_ext_funcs!();
+
+    fn geo_point(&self) -> Option<Point<T>> {
+        Some(*self)
+    }
+
+    fn geo_coord(&self) -> Option<Coord<T>> {
+        Some(self.0)
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for Point<T> {
+    type Tag = PointTag;
+}
+
+impl<T> PointTraitExt for &Point<T>
+where
+    T: CoordNum,
+{
+    forward_point_trait_ext_funcs!();
+
+    fn geo_point(&self) -> Option<Point<T>> {
+        Some(**self)
+    }
+
+    fn geo_coord(&self) -> Option<Coord<T>> {
+        Some(self.0)
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for &Point<T> {
+    type Tag = PointTag;
+}
+
+impl<T> PointTraitExt for UnimplementedPoint<T>
+where
+    T: CoordNum,
+{
+    forward_point_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for UnimplementedPoint<T> {
+    type Tag = PointTag;
+}
diff --git a/rust/sedona-geo-traits-ext/src/polygon.rs 
b/rust/sedona-geo-traits-ext/src/polygon.rs
new file mode 100644
index 0000000..52a00a5
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/src/polygon.rs
@@ -0,0 +1,125 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+// Extend PolygonTrait traits for the `geo-traits` crate
+
+use geo_traits::{GeometryTrait, PolygonTrait, UnimplementedPolygon};
+use geo_types::{CoordNum, Polygon};
+
+use crate::{GeoTraitExtWithTypeTag, LineStringTraitExt, PolygonTag};
+
+/// Extension trait that augments [`geo_traits::PolygonTrait`] with
+/// extension-aware accessors over exterior and interior rings.
+///
+/// Implementations are able to return ring references that also implement
+/// [`LineStringTraitExt`], bringing parity with `geo-types::Polygon` while
+/// remaining ergonomic through trait objects.
+pub trait PolygonTraitExt: PolygonTrait + GeoTraitExtWithTypeTag<Tag = 
PolygonTag>
+where
+    <Self as GeometryTrait>::T: CoordNum,
+{
+    /// Type of ring returned from accessor methods, wrapped with
+    /// [`LineStringTraitExt`].
+    type RingTypeExt<'a>: 'a + LineStringTraitExt<T = <Self as 
GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Returns the exterior ring with the extension trait applied.
+    fn exterior_ext(&self) -> Option<Self::RingTypeExt<'_>>;
+
+    /// Iterates over each interior ring with the extension trait applied.
+    fn interiors_ext(
+        &self,
+    ) -> impl DoubleEndedIterator + ExactSizeIterator<Item = 
Self::RingTypeExt<'_>>;
+
+    /// Returns the `i`th interior ring, if present, wrapped with the 
extension trait.
+    fn interior_ext(&self, i: usize) -> Option<Self::RingTypeExt<'_>>;
+
+    /// Returns an interior ring by index without bounds checking.
+    ///
+    /// # Safety
+    /// The caller must ensure that `i` is a valid index less than the number 
of interior rings.
+    /// Otherwise, this function may cause undefined behavior.
+    unsafe fn interior_unchecked_ext(&self, i: usize) -> Self::RingTypeExt<'_>;
+}
+
+#[macro_export]
+/// Forwards [`PolygonTraitExt`] methods to the underlying
+/// [`geo_traits::PolygonTrait`] implementation while preserving the extension
+/// trait wrappers.
+macro_rules! forward_polygon_trait_ext_funcs {
+    () => {
+        type RingTypeExt<'__l_inner>
+            = <Self as PolygonTrait>::RingType<'__l_inner>
+        where
+            Self: '__l_inner;
+
+        #[inline]
+        fn exterior_ext(&self) -> Option<Self::RingTypeExt<'_>> {
+            <Self as PolygonTrait>::exterior(self)
+        }
+
+        #[inline]
+        fn interiors_ext(
+            &self,
+        ) -> impl DoubleEndedIterator + ExactSizeIterator<Item = 
Self::RingTypeExt<'_>> {
+            <Self as PolygonTrait>::interiors(self)
+        }
+
+        #[inline]
+        fn interior_ext(&self, i: usize) -> Option<Self::RingTypeExt<'_>> {
+            <Self as PolygonTrait>::interior(self, i)
+        }
+
+        #[inline]
+        unsafe fn interior_unchecked_ext(&self, i: usize) -> 
Self::RingTypeExt<'_> {
+            <Self as PolygonTrait>::interior_unchecked(self, i)
+        }
+    };
+}
+
+impl<T> PolygonTraitExt for Polygon<T>
+where
+    T: CoordNum,
+{
+    forward_polygon_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for Polygon<T> {
+    type Tag = PolygonTag;
+}
+
+impl<T> PolygonTraitExt for &Polygon<T>
+where
+    T: CoordNum,
+{
+    forward_polygon_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for &Polygon<T> {
+    type Tag = PolygonTag;
+}
+
+impl<T> PolygonTraitExt for UnimplementedPolygon<T>
+where
+    T: CoordNum,
+{
+    forward_polygon_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for UnimplementedPolygon<T> {
+    type Tag = PolygonTag;
+}
diff --git a/rust/sedona-geo-traits-ext/src/rect.rs 
b/rust/sedona-geo-traits-ext/src/rect.rs
new file mode 100644
index 0000000..335179d
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/src/rect.rs
@@ -0,0 +1,331 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+// Extend RectTrait traits for the `geo-traits` crate
+
+use geo_traits::{CoordTrait, GeometryTrait, RectTrait, UnimplementedRect};
+use geo_types::{coord, Coord, CoordFloat, CoordNum, Line, LineString, Polygon, 
Rect};
+use num_traits::One;
+
+use crate::{CoordTraitExt, GeoTraitExtWithTypeTag, RectTag};
+
+static RECT_INVALID_BOUNDS_ERROR: &str = "Failed to create Rect: 'min' 
coordinate's x/y value must be smaller or equal to the 'max' x/y value";
+
+/// Extension trait that augments [`geo_traits::RectTrait`] with additional
+/// helpers for working with axis-aligned bounding boxes.
+pub trait RectTraitExt: RectTrait + GeoTraitExtWithTypeTag<Tag = RectTag>
+where
+    <Self as GeometryTrait>::T: CoordNum,
+{
+    /// Extension-aware coordinate type returned from accessors.
+    type CoordTypeExt<'a>: 'a + CoordTraitExt<T = <Self as GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Returns the minimum corner using the extension trait wrapper.
+    fn min_ext(&self) -> Self::CoordTypeExt<'_>;
+
+    /// Returns the maximum corner using the extension trait wrapper.
+    fn max_ext(&self) -> Self::CoordTypeExt<'_>;
+
+    #[inline]
+    /// Returns the minimum corner as a `geo-types::Coord`.
+    fn min_coord(&self) -> Coord<<Self as GeometryTrait>::T> {
+        self.min_ext().geo_coord()
+    }
+
+    #[inline]
+    /// Returns the maximum corner as a `geo-types::Coord`.
+    fn max_coord(&self) -> Coord<<Self as GeometryTrait>::T> {
+        self.max_ext().geo_coord()
+    }
+
+    #[inline]
+    /// Constructs a [`geo_types::Rect`] from the extension trait accessors.
+    fn geo_rect(&self) -> Rect<<Self as GeometryTrait>::T> {
+        Rect::new(self.min_coord(), self.max_coord())
+    }
+
+    #[inline]
+    /// Returns the width of the rectangle.
+    fn width(&self) -> <Self as GeometryTrait>::T {
+        self.max().x() - self.min().x()
+    }
+
+    #[inline]
+    /// Returns the height of the rectangle.
+    fn height(&self) -> <Self as GeometryTrait>::T {
+        self.max().y() - self.min().y()
+    }
+
+    /// Converts the rectangle into a polygon with four corners.
+    fn to_polygon(&self) -> Polygon<<Self as GeometryTrait>::T>
+    where
+        <Self as GeometryTrait>::T: Clone,
+    {
+        let min_coord = self.min_coord();
+        let max_coord = self.max_coord();
+
+        let min_x = min_coord.x;
+        let min_y = min_coord.y;
+        let max_x = max_coord.x;
+        let max_y = max_coord.y;
+
+        let line_string = LineString::new(vec![
+            Coord { x: min_x, y: min_y },
+            Coord { x: min_x, y: max_y },
+            Coord { x: max_x, y: max_y },
+            Coord { x: max_x, y: min_y },
+            Coord { x: min_x, y: min_y },
+        ]);
+
+        Polygon::new(line_string, vec![])
+    }
+
+    /// Returns the four outer edges as line segments.
+    fn to_lines(&self) -> [Line<<Self as GeometryTrait>::T>; 4] {
+        let min_coord = self.min_coord();
+        let max_coord = self.max_coord();
+        [
+            Line::new(
+                coord! {
+                    x: max_coord.x,
+                    y: min_coord.y,
+                },
+                coord! {
+                    x: max_coord.x,
+                    y: max_coord.y,
+                },
+            ),
+            Line::new(
+                coord! {
+                    x: max_coord.x,
+                    y: max_coord.y,
+                },
+                coord! {
+                    x: min_coord.x,
+                    y: max_coord.y,
+                },
+            ),
+            Line::new(
+                coord! {
+                    x: min_coord.x,
+                    y: max_coord.y,
+                },
+                coord! {
+                    x: min_coord.x,
+                    y: min_coord.y,
+                },
+            ),
+            Line::new(
+                coord! {
+                    x: min_coord.x,
+                    y: min_coord.y,
+                },
+                coord! {
+                    x: max_coord.x,
+                    y: min_coord.y,
+                },
+            ),
+        ]
+    }
+
+    /// Converts the rectangle into a closed line string in counter-clockwise 
order.
+    fn to_line_string(&self) -> LineString<<Self as GeometryTrait>::T>
+    where
+        <Self as GeometryTrait>::T: Clone,
+    {
+        let min_coord = self.min_coord();
+        let max_coord = self.max_coord();
+
+        let min_x = min_coord.x;
+        let min_y = min_coord.y;
+        let max_x = max_coord.x;
+        let max_y = max_coord.y;
+
+        LineString::new(vec![
+            Coord { x: min_x, y: min_y },
+            Coord { x: min_x, y: max_y },
+            Coord { x: max_x, y: max_y },
+            Coord { x: max_x, y: min_y },
+            Coord { x: min_x, y: min_y },
+        ])
+    }
+
+    #[inline]
+    /// Returns `true` if the rectangle has non-decreasing bounds.
+    fn has_valid_bounds(&self) -> bool {
+        let min_coord = self.min_coord();
+        let max_coord = self.max_coord();
+        min_coord.x <= max_coord.x && min_coord.y <= max_coord.y
+    }
+
+    #[inline]
+    /// Panics when the rectangle bounds are invalid.
+    fn assert_valid_bounds(&self) {
+        if !self.has_valid_bounds() {
+            panic!("{}", RECT_INVALID_BOUNDS_ERROR);
+        }
+    }
+
+    #[inline]
+    /// Returns `true` if `coord` lies inside or on the rectangle boundary.
+    fn contains_point(&self, coord: &Coord<<Self as GeometryTrait>::T>) -> bool
+    where
+        <Self as GeometryTrait>::T: PartialOrd,
+    {
+        let min_coord = self.min_coord();
+        let max_coord = self.max_coord();
+
+        let min_x = min_coord.x;
+        let min_y = min_coord.y;
+        let max_x = max_coord.x;
+        let max_y = max_coord.y;
+
+        (min_x <= coord.x && coord.x <= max_x) && (min_y <= coord.y && coord.y 
<= max_y)
+    }
+
+    #[inline]
+    /// Returns `true` if `rect` is fully contained within `self`.
+    fn contains_rect(&self, rect: &Self) -> bool
+    where
+        <Self as GeometryTrait>::T: PartialOrd,
+    {
+        let self_min = self.min_coord();
+        let self_max = self.max_coord();
+        let other_min = rect.min_coord();
+        let other_max = rect.max_coord();
+
+        let self_min_x = self_min.x;
+        let self_min_y = self_min.y;
+        let self_max_x = self_max.x;
+        let self_max_y = self_max.y;
+
+        let other_min_x = other_min.x;
+        let other_min_y = other_min.y;
+        let other_max_x = other_max.x;
+        let other_max_y = other_max.y;
+
+        (self_min_x <= other_min_x && other_max_x <= self_max_x)
+            && (self_min_y <= other_min_y && other_max_y <= self_max_y)
+    }
+
+    #[inline]
+    /// Returns the rectangle centroid as a coordinate.
+    fn center(&self) -> Coord<<Self as GeometryTrait>::T>
+    where
+        <Self as GeometryTrait>::T: CoordFloat,
+    {
+        let two = <Self as GeometryTrait>::T::one() + <Self as 
GeometryTrait>::T::one();
+        coord! {
+            x: (self.max_coord().x + self.min_coord().x) / two,
+            y: (self.max_coord().y + self.min_coord().y) / two,
+        }
+    }
+}
+
+#[macro_export]
+/// Forwards [`RectTraitExt`] methods to the underlying
+/// [`geo_traits::RectTrait`] implementation while keeping the extension trait
+/// wrappers intact.
+macro_rules! forward_rect_trait_ext_funcs {
+    () => {
+        type CoordTypeExt<'__l_inner>
+            = <Self as RectTrait>::CoordType<'__l_inner>
+        where
+            Self: '__l_inner;
+
+        fn min_ext(&self) -> Self::CoordTypeExt<'_> {
+            <Self as RectTrait>::min(self)
+        }
+
+        fn max_ext(&self) -> Self::CoordTypeExt<'_> {
+            <Self as RectTrait>::max(self)
+        }
+    };
+}
+
+impl<T> RectTraitExt for Rect<T>
+where
+    T: CoordNum,
+{
+    forward_rect_trait_ext_funcs!();
+
+    fn min_coord(&self) -> Coord<T> {
+        Rect::min(*self)
+    }
+
+    fn max_coord(&self) -> Coord<T> {
+        Rect::max(*self)
+    }
+
+    fn geo_rect(&self) -> Rect<T> {
+        *self
+    }
+
+    fn to_lines(&self) -> [Line<<Self as GeometryTrait>::T>; 4] {
+        self.to_lines()
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for Rect<T> {
+    type Tag = RectTag;
+}
+
+impl<T> RectTraitExt for &Rect<T>
+where
+    T: CoordNum,
+{
+    forward_rect_trait_ext_funcs!();
+
+    fn min_coord(&self) -> Coord<T> {
+        Rect::min(**self)
+    }
+
+    fn max_coord(&self) -> Coord<T> {
+        Rect::max(**self)
+    }
+
+    fn geo_rect(&self) -> Rect<T> {
+        **self
+    }
+
+    fn to_polygon(&self) -> Polygon<<Self as GeometryTrait>::T>
+    where
+        <Self as GeometryTrait>::T: Clone,
+    {
+        (*self).to_polygon()
+    }
+
+    fn to_lines(&self) -> [Line<<Self as GeometryTrait>::T>; 4] {
+        (*self).to_lines()
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for &Rect<T> {
+    type Tag = RectTag;
+}
+
+impl<T> RectTraitExt for UnimplementedRect<T>
+where
+    T: CoordNum,
+{
+    forward_rect_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for UnimplementedRect<T> {
+    type Tag = RectTag;
+}
diff --git a/rust/sedona-geo-traits-ext/src/triangle.rs 
b/rust/sedona-geo-traits-ext/src/triangle.rs
new file mode 100644
index 0000000..08fb868
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/src/triangle.rs
@@ -0,0 +1,200 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+// Extend TriangleTrait traits for the `geo-traits` crate
+
+use geo_traits::{GeometryTrait, TriangleTrait, UnimplementedTriangle};
+use geo_types::{polygon, Coord, CoordNum, Line, Polygon, Triangle};
+
+use crate::{CoordTraitExt, GeoTraitExtWithTypeTag, TriangleTag};
+
+/// Extension trait that augments [`geo_traits::TriangleTrait`] with convenient
+/// coordinate accessors and adapters.
+pub trait TriangleTraitExt: TriangleTrait + GeoTraitExtWithTypeTag<Tag = 
TriangleTag>
+where
+    <Self as GeometryTrait>::T: CoordNum,
+{
+    /// Extension-aware coordinate type returned from triangle accessors.
+    type CoordTypeExt<'a>: 'a + CoordTraitExt<T = <Self as GeometryTrait>::T>
+    where
+        Self: 'a;
+
+    /// Returns the first vertex with the extension trait applied.
+    fn first_ext(&self) -> Self::CoordTypeExt<'_>;
+    /// Returns the second vertex with the extension trait applied.
+    fn second_ext(&self) -> Self::CoordTypeExt<'_>;
+    /// Returns the third vertex with the extension trait applied.
+    fn third_ext(&self) -> Self::CoordTypeExt<'_>;
+    /// Returns all three vertices as extension-aware coordinates.
+    fn coords_ext(&self) -> [Self::CoordTypeExt<'_>; 3];
+
+    #[inline]
+    /// Returns the first vertex as a `geo-types::Coord`.
+    fn first_coord(&self) -> Coord<<Self as GeometryTrait>::T> {
+        self.first_ext().geo_coord()
+    }
+
+    #[inline]
+    /// Returns the second vertex as a `geo-types::Coord`.
+    fn second_coord(&self) -> Coord<<Self as GeometryTrait>::T> {
+        self.second_ext().geo_coord()
+    }
+
+    #[inline]
+    /// Returns the third vertex as a `geo-types::Coord`.
+    fn third_coord(&self) -> Coord<<Self as GeometryTrait>::T> {
+        self.third_ext().geo_coord()
+    }
+
+    #[inline]
+    /// Returns the triangle vertices as an array of coordinates.
+    fn to_array(&self) -> [Coord<<Self as GeometryTrait>::T>; 3] {
+        [self.first_coord(), self.second_coord(), self.third_coord()]
+    }
+
+    #[inline]
+    /// Returns the three edges as line segments in traversal order.
+    fn to_lines(&self) -> [Line<<Self as GeometryTrait>::T>; 3] {
+        [
+            Line::new(self.first_coord(), self.second_coord()),
+            Line::new(self.second_coord(), self.third_coord()),
+            Line::new(self.third_coord(), self.first_coord()),
+        ]
+    }
+
+    #[inline]
+    /// Converts the triangle into a polygon whose shell walks the triangle 
vertices.
+    fn to_polygon(&self) -> Polygon<<Self as GeometryTrait>::T> {
+        polygon![
+            self.first_coord(),
+            self.second_coord(),
+            self.third_coord(),
+            self.first_coord(),
+        ]
+    }
+
+    #[inline]
+    /// Iterates over the triangle vertices as coordinates.
+    fn coord_iter(&self) -> impl Iterator<Item = Coord<<Self as 
GeometryTrait>::T>> {
+        [self.first_coord(), self.second_coord(), 
self.third_coord()].into_iter()
+    }
+}
+
+#[macro_export]
+/// Forwards [`TriangleTraitExt`] methods to the underlying
+/// [`geo_traits::TriangleTrait`] implementation while returning extension 
trait
+/// wrappers.
+macro_rules! forward_triangle_trait_ext_funcs {
+    () => {
+        type CoordTypeExt<'__l_inner>
+            = <Self as TriangleTrait>::CoordType<'__l_inner>
+        where
+            Self: '__l_inner;
+
+        #[inline]
+        fn first_ext(&self) -> Self::CoordTypeExt<'_> {
+            <Self as TriangleTrait>::first(self)
+        }
+
+        #[inline]
+        fn second_ext(&self) -> Self::CoordTypeExt<'_> {
+            <Self as TriangleTrait>::second(self)
+        }
+
+        #[inline]
+        fn third_ext(&self) -> Self::CoordTypeExt<'_> {
+            <Self as TriangleTrait>::third(self)
+        }
+
+        #[inline]
+        fn coords_ext(&self) -> [Self::CoordTypeExt<'_>; 3] {
+            [self.first_ext(), self.second_ext(), self.third_ext()]
+        }
+    };
+}
+
+impl<T> TriangleTraitExt for Triangle<T>
+where
+    T: CoordNum,
+{
+    forward_triangle_trait_ext_funcs!();
+
+    fn first_coord(&self) -> Coord<<Self as GeometryTrait>::T> {
+        self.0
+    }
+
+    fn second_coord(&self) -> Coord<<Self as GeometryTrait>::T> {
+        self.1
+    }
+
+    fn third_coord(&self) -> Coord<<Self as GeometryTrait>::T> {
+        self.2
+    }
+
+    fn to_array(&self) -> [Coord<<Self as GeometryTrait>::T>; 3] {
+        self.to_array()
+    }
+
+    fn to_lines(&self) -> [Line<<Self as GeometryTrait>::T>; 3] {
+        self.to_lines()
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for Triangle<T> {
+    type Tag = TriangleTag;
+}
+
+impl<T> TriangleTraitExt for &Triangle<T>
+where
+    T: CoordNum,
+{
+    forward_triangle_trait_ext_funcs!();
+
+    fn first_coord(&self) -> Coord<<Self as GeometryTrait>::T> {
+        self.0
+    }
+
+    fn second_coord(&self) -> Coord<<Self as GeometryTrait>::T> {
+        self.1
+    }
+
+    fn third_coord(&self) -> Coord<<Self as GeometryTrait>::T> {
+        self.2
+    }
+
+    fn to_array(&self) -> [Coord<<Self as GeometryTrait>::T>; 3] {
+        (*self).to_array()
+    }
+
+    fn to_lines(&self) -> [Line<<Self as GeometryTrait>::T>; 3] {
+        (*self).to_lines()
+    }
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for &Triangle<T> {
+    type Tag = TriangleTag;
+}
+
+impl<T> TriangleTraitExt for UnimplementedTriangle<T>
+where
+    T: CoordNum,
+{
+    forward_triangle_trait_ext_funcs!();
+}
+
+impl<T: CoordNum> GeoTraitExtWithTypeTag for UnimplementedTriangle<T> {
+    type Tag = TriangleTag;
+}
diff --git a/rust/sedona-geo-traits-ext/src/type_tag.rs 
b/rust/sedona-geo-traits-ext/src/type_tag.rs
new file mode 100644
index 0000000..f6b1fdf
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/src/type_tag.rs
@@ -0,0 +1,66 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//! Geometry type tags for dispatching algorithm traits to the corresponding 
implementation
+
+/// Marker trait implemented by all geometry type tags used for dispatch.
+pub trait GeoTypeTag {}
+
+/// Tag that identifies coordinate-like values.
+pub struct CoordTag;
+/// Tag that identifies point-like geometries.
+pub struct PointTag;
+/// Tag that identifies line-string-like geometries.
+pub struct LineStringTag;
+/// Tag that identifies polygon-like geometries.
+pub struct PolygonTag;
+/// Tag that identifies multi-point-like geometries.
+pub struct MultiPointTag;
+/// Tag that identifies multi-line-string-like geometries.
+pub struct MultiLineStringTag;
+/// Tag that identifies multi-polygon-like geometries.
+pub struct MultiPolygonTag;
+/// Tag that identifies geometry-collection-like geometries.
+pub struct GeometryCollectionTag;
+/// Tag that identifies generic geometry values.
+pub struct GeometryTag;
+/// Tag that identifies line-segment-like geometries.
+pub struct LineTag;
+/// Tag that identifies rectangle-like geometries.
+pub struct RectTag;
+/// Tag that identifies triangle-like geometries.
+pub struct TriangleTag;
+
+impl GeoTypeTag for CoordTag {}
+impl GeoTypeTag for PointTag {}
+impl GeoTypeTag for LineStringTag {}
+impl GeoTypeTag for PolygonTag {}
+impl GeoTypeTag for MultiPointTag {}
+impl GeoTypeTag for MultiLineStringTag {}
+impl GeoTypeTag for MultiPolygonTag {}
+impl GeoTypeTag for GeometryCollectionTag {}
+impl GeoTypeTag for GeometryTag {}
+impl GeoTypeTag for LineTag {}
+impl GeoTypeTag for RectTag {}
+impl GeoTypeTag for TriangleTag {}
+
+/// Helper trait implemented by extension traits to expose their geometry tag.
+/// Each geometry type could only implement this trait once, so each geometry 
type
+/// has one unique tag. This helps us work around the single-orphan rule of 
Rust
+/// trait system and help us smoothly refactor the existing algorithms in 
georust/geo.
+pub trait GeoTraitExtWithTypeTag {
+    type Tag: GeoTypeTag;
+}
diff --git a/rust/sedona-geo-traits-ext/src/wkb_ext.rs 
b/rust/sedona-geo-traits-ext/src/wkb_ext.rs
new file mode 100644
index 0000000..4c84dbe
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/src/wkb_ext.rs
@@ -0,0 +1,557 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+use std::marker::PhantomData;
+
+use crate::*;
+use byteorder::{BigEndian, ByteOrder, LittleEndian};
+use geo_traits::{
+    GeometryCollectionTrait, GeometryTrait, GeometryType, LineStringTrait, 
MultiLineStringTrait,
+    MultiPointTrait, MultiPolygonTrait, PointTrait, PolygonTrait,
+};
+use geo_types::{Coord as GeoCoord, Line};
+use wkb::reader::{
+    Coord, Dimension, GeometryCollection, LineString, LinearRing, 
MultiLineString, MultiPoint,
+    MultiPolygon, Point, Polygon, Wkb,
+};
+use wkb::Endianness;
+
+// ┌──────────────────────────────────────────────────────────┐
+// │ Coord                                                    │
+// └──────────────────────────────────────────────────────────┘
+impl CoordTraitExt for Coord<'_> {
+    #[inline]
+    fn geo_coord(&self) -> geo_types::Coord<f64> {
+        let coord_slice = self.coord_slice();
+        unsafe {
+            let x_bytes = std::slice::from_raw_parts(coord_slice.as_ptr(), 8);
+            let y_bytes = 
std::slice::from_raw_parts(coord_slice.as_ptr().add(8), 8);
+            match self.byte_order() {
+                Endianness::BigEndian => {
+                    let x = BigEndian::read_f64(x_bytes);
+                    let y = BigEndian::read_f64(y_bytes);
+                    geo_types::Coord { x, y }
+                }
+                Endianness::LittleEndian => {
+                    let x = LittleEndian::read_f64(x_bytes);
+                    let y = LittleEndian::read_f64(y_bytes);
+                    geo_types::Coord { x, y }
+                }
+            }
+        }
+    }
+}
+
+impl GeoTraitExtWithTypeTag for Coord<'_> {
+    type Tag = CoordTag;
+}
+
+// ┌──────────────────────────────────────────────────────────┐
+// │ Point                                                    │
+// └──────────────────────────────────────────────────────────┘
+
+impl PointTraitExt for Point<'_> {
+    forward_point_trait_ext_funcs!();
+}
+
+impl GeoTraitExtWithTypeTag for Point<'_> {
+    type Tag = PointTag;
+}
+
+impl PointTraitExt for &Point<'_> {
+    forward_point_trait_ext_funcs!();
+}
+
+impl GeoTraitExtWithTypeTag for &Point<'_> {
+    type Tag = PointTag;
+}
+
+// ┌──────────────────────────────────────────────────────────┐
+// │ LineString                                               │
+// └──────────────────────────────────────────────────────────┘
+
+impl LineStringTraitExt for LineString<'_> {
+    forward_line_string_trait_ext_funcs!();
+
+    #[inline(always)]
+    fn lines(&'_ self) -> impl ExactSizeIterator<Item = Line<f64>> + '_ {
+        let buf = self.coords_slice();
+        let dim_size = dimension_size(self.dimension());
+        let num_coords = self.num_coords();
+        match self.byte_order() {
+            Endianness::LittleEndian => {
+                EndianLineIter::LE(LineIter::new(buf, num_coords, dim_size))
+            }
+            Endianness::BigEndian => EndianLineIter::BE(LineIter::new(buf, 
num_coords, dim_size)),
+        }
+    }
+
+    #[inline(always)]
+    fn coord_iter(&self) -> impl Iterator<Item = GeoCoord<f64>> {
+        let buf = self.coords_slice();
+        let dim_size = dimension_size(self.dimension());
+        let num_coords = self.num_coords();
+        match self.byte_order() {
+            Endianness::LittleEndian => {
+                EndianCoordIter::LE(CoordIter::new(buf, num_coords, dim_size))
+            }
+            Endianness::BigEndian => EndianCoordIter::BE(CoordIter::new(buf, 
num_coords, dim_size)),
+        }
+    }
+}
+
+impl GeoTraitExtWithTypeTag for LineString<'_> {
+    type Tag = LineStringTag;
+}
+
+impl LineStringTraitExt for &LineString<'_> {
+    forward_line_string_trait_ext_funcs!();
+
+    #[inline(always)]
+    fn lines(&'_ self) -> impl ExactSizeIterator<Item = Line<f64>> + '_ {
+        (*self).lines()
+    }
+
+    #[inline(always)]
+    fn coord_iter(&self) -> impl Iterator<Item = GeoCoord<f64>> {
+        (*self).coord_iter()
+    }
+}
+
+impl GeoTraitExtWithTypeTag for &LineString<'_> {
+    type Tag = LineStringTag;
+}
+
+// ┌──────────────────────────────────────────────────────────┐
+// │ LinearRing                                               │
+// └──────────────────────────────────────────────────────────┘
+
+impl LineStringTraitExt for LinearRing<'_> {
+    forward_line_string_trait_ext_funcs!();
+
+    #[inline(always)]
+    fn lines(&'_ self) -> impl ExactSizeIterator<Item = Line<f64>> + '_ {
+        let buf = self.coords_slice();
+        let dim_size = dimension_size(self.dimension());
+        let num_coords = self.num_coords();
+        match self.byte_order() {
+            Endianness::LittleEndian => {
+                EndianLineIter::LE(LineIter::new(buf, num_coords, dim_size))
+            }
+            Endianness::BigEndian => EndianLineIter::BE(LineIter::new(buf, 
num_coords, dim_size)),
+        }
+    }
+
+    #[inline(always)]
+    fn coord_iter(&self) -> impl Iterator<Item = GeoCoord<f64>> {
+        let buf = self.coords_slice();
+        let dim_size = dimension_size(self.dimension());
+        let num_coords = self.num_coords();
+        match self.byte_order() {
+            Endianness::LittleEndian => {
+                EndianCoordIter::LE(CoordIter::new(buf, num_coords, dim_size))
+            }
+            Endianness::BigEndian => EndianCoordIter::BE(CoordIter::new(buf, 
num_coords, dim_size)),
+        }
+    }
+}
+
+impl GeoTraitExtWithTypeTag for LinearRing<'_> {
+    type Tag = LineStringTag;
+}
+
+impl LineStringTraitExt for &LinearRing<'_> {
+    forward_line_string_trait_ext_funcs!();
+
+    #[inline(always)]
+    fn lines(&'_ self) -> impl ExactSizeIterator<Item = Line<f64>> + '_ {
+        (*self).lines()
+    }
+
+    #[inline(always)]
+    fn coord_iter(&self) -> impl Iterator<Item = GeoCoord<f64>> {
+        (*self).coord_iter()
+    }
+}
+
+impl GeoTraitExtWithTypeTag for &LinearRing<'_> {
+    type Tag = LineStringTag;
+}
+
+// ┌──────────────────────────────────────────────────────────┐
+// │ Polygon                                                  │
+// └──────────────────────────────────────────────────────────┘
+
+impl PolygonTraitExt for Polygon<'_> {
+    forward_polygon_trait_ext_funcs!();
+}
+
+impl GeoTraitExtWithTypeTag for Polygon<'_> {
+    type Tag = PolygonTag;
+}
+
+impl PolygonTraitExt for &Polygon<'_> {
+    forward_polygon_trait_ext_funcs!();
+}
+
+impl GeoTraitExtWithTypeTag for &Polygon<'_> {
+    type Tag = PolygonTag;
+}
+
+// ┌──────────────────────────────────────────────────────────┐
+// │ MultiPoint                                               │
+// └──────────────────────────────────────────────────────────┘
+
+impl MultiPointTraitExt for MultiPoint<'_> {
+    forward_multi_point_trait_ext_funcs!();
+}
+
+impl GeoTraitExtWithTypeTag for MultiPoint<'_> {
+    type Tag = MultiPointTag;
+}
+
+impl<'a, 'b> MultiPointTraitExt for &'b MultiPoint<'a>
+where
+    'a: 'b,
+{
+    forward_multi_point_trait_ext_funcs!();
+}
+
+impl<'a, 'b> GeoTraitExtWithTypeTag for &'b MultiPoint<'a>
+where
+    'a: 'b,
+{
+    type Tag = MultiPointTag;
+}
+
+// ┌──────────────────────────────────────────────────────────┐
+// │ MultiLineString                                          │
+// └──────────────────────────────────────────────────────────┘
+
+impl MultiLineStringTraitExt for MultiLineString<'_> {
+    forward_multi_line_string_trait_ext_funcs!();
+}
+
+impl GeoTraitExtWithTypeTag for MultiLineString<'_> {
+    type Tag = MultiLineStringTag;
+}
+
+impl<'a, 'b> MultiLineStringTraitExt for &'b MultiLineString<'a>
+where
+    'a: 'b,
+{
+    forward_multi_line_string_trait_ext_funcs!();
+}
+
+impl<'a, 'b> GeoTraitExtWithTypeTag for &'b MultiLineString<'a>
+where
+    'a: 'b,
+{
+    type Tag = MultiLineStringTag;
+}
+
+// ┌──────────────────────────────────────────────────────────┐
+// │ MultiPolygon                                             │
+// └──────────────────────────────────────────────────────────┘
+
+impl MultiPolygonTraitExt for MultiPolygon<'_> {
+    forward_multi_polygon_trait_ext_funcs!();
+}
+
+impl GeoTraitExtWithTypeTag for MultiPolygon<'_> {
+    type Tag = MultiPolygonTag;
+}
+
+impl<'a, 'b> MultiPolygonTraitExt for &'b MultiPolygon<'a>
+where
+    'a: 'b,
+{
+    forward_multi_polygon_trait_ext_funcs!();
+}
+
+impl<'a, 'b> GeoTraitExtWithTypeTag for &'b MultiPolygon<'a>
+where
+    'a: 'b,
+{
+    type Tag = MultiPolygonTag;
+}
+
+// ┌──────────────────────────────────────────────────────────┐
+// │ GeometryCollection                                       │
+// └──────────────────────────────────────────────────────────┘
+
+impl GeometryCollectionTraitExt for GeometryCollection<'_> {
+    forward_geometry_collection_trait_ext_funcs!();
+}
+
+impl GeoTraitExtWithTypeTag for GeometryCollection<'_> {
+    type Tag = GeometryCollectionTag;
+}
+
+// ┌──────────────────────────────────────────────────────────┐
+// │ Wkb/Geometry                                             │
+// └──────────────────────────────────────────────────────────┘
+
+impl<'a> GeometryTraitExt for Wkb<'a> {
+    forward_geometry_trait_ext_funcs!(f64);
+
+    type InnerGeometryRef<'b>
+        = &'b Wkb<'a>
+    where
+        Self: 'b;
+
+    #[inline]
+    fn geometry_ext(&self, i: usize) -> Option<Self::InnerGeometryRef<'_>> {
+        let GeometryType::GeometryCollection(gc) = self.as_type() else {
+            return None;
+        };
+        gc.geometry(i)
+    }
+
+    #[inline]
+    unsafe fn geometry_unchecked_ext(&self, i: usize) -> 
Self::InnerGeometryRef<'_> {
+        let GeometryType::GeometryCollection(gc) = self.as_type() else {
+            panic!("Called geometry_unchecked_ext on a non-GeometryCollection 
geometry");
+        };
+        gc.geometry_unchecked(i)
+    }
+
+    #[inline]
+    fn geometries_ext(&self) -> impl Iterator<Item = 
Self::InnerGeometryRef<'_>> {
+        let GeometryType::GeometryCollection(gc) = self.as_type() else {
+            panic!("Called geometries_ext on a non-GeometryCollection 
geometry");
+        };
+        gc.geometries()
+    }
+}
+
+impl<'a, 'b> GeometryTraitExt for &'b Wkb<'a>
+where
+    'a: 'b,
+{
+    forward_geometry_trait_ext_funcs!(f64);
+
+    type InnerGeometryRef<'c>
+        = &'b Wkb<'a>
+    where
+        Self: 'c;
+
+    #[inline]
+    fn geometry_ext(&self, i: usize) -> Option<Self::InnerGeometryRef<'_>> {
+        (*self).geometry_ext(i)
+    }
+
+    #[inline]
+    unsafe fn geometry_unchecked_ext(&self, i: usize) -> 
Self::InnerGeometryRef<'_> {
+        (*self).geometry_unchecked_ext(i)
+    }
+
+    #[inline]
+    fn geometries_ext(&self) -> impl Iterator<Item = 
Self::InnerGeometryRef<'_>> {
+        (*self).geometries_ext()
+    }
+}
+
+impl GeoTraitExtWithTypeTag for Wkb<'_> {
+    type Tag = GeometryTag;
+}
+
+impl<'a, 'b> GeoTraitExtWithTypeTag for &'b Wkb<'a>
+where
+    'a: 'b,
+{
+    type Tag = GeometryTag;
+}
+
+// ┌──────────────────────────────────────────────────────────┐
+// │ Iterators                                                │
+// └──────────────────────────────────────────────────────────┘
+
+/// Iterator over coordinates in a WKB buffer using a compile-time endianness.
+pub struct CoordIter<'a, B: ByteOrder> {
+    buf: &'a [u8],
+    current_offset: usize,
+    remaining: usize,
+    dim_size: usize,
+    _marker: PhantomData<B>,
+}
+
+impl<'a, B: ByteOrder> CoordIter<'a, B> {
+    #[inline]
+    /// Creates a new coordinate iterator over the provided buffer.
+    pub fn new(buf: &'a [u8], num_coords: usize, dim_size: usize) -> Self {
+        Self {
+            buf,
+            current_offset: 0,
+            remaining: num_coords,
+            dim_size,
+            _marker: PhantomData,
+        }
+    }
+}
+
+impl<B: ByteOrder> Iterator for CoordIter<'_, B> {
+    type Item = GeoCoord<f64>;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.remaining == 0 {
+            return None;
+        }
+
+        // SAFETY: We're reading raw memory from the buffer at calculated 
offsets.
+        // This assumes the buffer contains valid data and offsets are within 
bounds.
+        let coord = unsafe {
+            let x_bytes = 
std::slice::from_raw_parts(self.buf.as_ptr().add(self.current_offset), 8);
+            let y_bytes =
+                
std::slice::from_raw_parts(self.buf.as_ptr().add(self.current_offset + 8), 8);
+            let x = B::read_f64(x_bytes);
+            let y = B::read_f64(y_bytes);
+            GeoCoord { x, y }
+        };
+
+        self.current_offset += self.dim_size * 8;
+        self.remaining -= 1;
+        Some(coord)
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.remaining, Some(self.remaining))
+    }
+}
+
+impl<B: ByteOrder> ExactSizeIterator for CoordIter<'_, B> {}
+
+/// Iterator over line segments derived from sequential coordinates in a WKB 
buffer.
+pub struct LineIter<'a, B: ByteOrder> {
+    coord_iter: CoordIter<'a, B>,
+    prev_coord: Option<GeoCoord<f64>>,
+}
+
+impl<'a, B: ByteOrder> LineIter<'a, B> {
+    #[inline]
+    /// Creates a new line iterator over the provided buffer.
+    pub fn new(buf: &'a [u8], num_coords: usize, dim_size: usize) -> Self {
+        Self {
+            coord_iter: CoordIter::new(buf, num_coords, dim_size),
+            prev_coord: None,
+        }
+    }
+}
+
+impl<B: ByteOrder> Iterator for LineIter<'_, B> {
+    type Item = Line<f64>;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        let current_coord = self.coord_iter.next()?;
+
+        match self.prev_coord {
+            Some(prev_coord) => {
+                let line = Line::new(prev_coord, current_coord);
+                self.prev_coord = Some(current_coord);
+                Some(line)
+            }
+            None => {
+                // Grab the next coordinate to form the first line segment
+                let next_coord = self.coord_iter.next()?;
+                let line = Line::new(current_coord, next_coord);
+                self.prev_coord = Some(next_coord);
+                Some(line)
+            }
+        }
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let (min, max) = self.coord_iter.size_hint();
+        (min.saturating_sub(1), max.map(|m| m.saturating_sub(1)))
+    }
+}
+
+impl<B: ByteOrder> ExactSizeIterator for LineIter<'_, B> {}
+
+/// Wrapper around [`CoordIter`] that selects the concrete endianness at 
runtime.
+///
+/// The dispatch in the iterator methods is static and can be inlined by the
+/// compiler, so callers do not pay the cost of dynamic allocation.
+pub enum EndianCoordIter<'a> {
+    LE(CoordIter<'a, LittleEndian>),
+    BE(CoordIter<'a, BigEndian>),
+}
+
+impl Iterator for EndianCoordIter<'_> {
+    type Item = GeoCoord<f64>;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        // We rely on compiler optimization to hoist the match out of the 
loop, so that
+        // there's no performance overhead of checking the endianness inside 
the loop.
+        match self {
+            EndianCoordIter::LE(iter) => iter.next(),
+            EndianCoordIter::BE(iter) => iter.next(),
+        }
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        match self {
+            EndianCoordIter::LE(iter) => iter.size_hint(),
+            EndianCoordIter::BE(iter) => iter.size_hint(),
+        }
+    }
+}
+
+/// Wrapper around [`LineIter`] that selects the concrete endianness at 
runtime.
+pub enum EndianLineIter<'a> {
+    LE(LineIter<'a, LittleEndian>),
+    BE(LineIter<'a, BigEndian>),
+}
+
+impl Iterator for EndianLineIter<'_> {
+    type Item = Line<f64>;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        match self {
+            EndianLineIter::LE(iter) => iter.next(),
+            EndianLineIter::BE(iter) => iter.next(),
+        }
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        match self {
+            EndianLineIter::LE(iter) => iter.size_hint(),
+            EndianLineIter::BE(iter) => iter.size_hint(),
+        }
+    }
+}
+
+impl ExactSizeIterator for EndianLineIter<'_> {}
+
+// ┌──────────────────────────────────────────────────────────┐
+// │ Utils.                                                   │
+// └──────────────────────────────────────────────────────────┘
+/// Returns the dimensionality (number of ordinates) represented by a WKB 
[`Dimension`].
+fn dimension_size(dim: Dimension) -> usize {
+    match dim {
+        Dimension::Xy => 2,
+        Dimension::Xyz | Dimension::Xym => 3,
+        Dimension::Xyzm => 4,
+    }
+}
diff --git a/rust/sedona-geo-traits-ext/tests/wkb_ext_tests.rs 
b/rust/sedona-geo-traits-ext/tests/wkb_ext_tests.rs
new file mode 100644
index 0000000..775d68c
--- /dev/null
+++ b/rust/sedona-geo-traits-ext/tests/wkb_ext_tests.rs
@@ -0,0 +1,230 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Tests for the WKB extension traits implemented in `wkb_ext`.
+
+use geo_traits::GeometryTrait;
+use rstest::rstest;
+use sedona_geo_traits_ext::*;
+use std::str::FromStr;
+use wkb::{reader::Wkb, Endianness};
+use wkt::Wkt;
+
+/// Helper to create WKB from WKT string using the wkb writer
+fn wkb_from_wkt(wkt_str: &str) -> Vec<u8> {
+    wkb_from_wkt_with_endianness(wkt_str, wkb::Endianness::LittleEndian)
+}
+
+/// Helper to create WKB from WKT string using the wkb writer
+fn wkb_from_wkt_with_endianness(wkt_str: &str, endianness: wkb::Endianness) -> 
Vec<u8> {
+    let geometry = Wkt::<f64>::from_str(wkt_str).unwrap();
+    let mut buf = Vec::new();
+    let options = wkb::writer::WriteOptions { endianness };
+    wkb::writer::write_geometry(&mut buf, &geometry, &options).unwrap();
+    buf
+}
+
+#[rstest]
+fn test_geo_coord(
+    #[values(Endianness::LittleEndian, Endianness::BigEndian)] endianness: 
Endianness,
+) {
+    let buf = wkb_from_wkt_with_endianness("POINT (1.0 2.0)", endianness);
+    let wkb = Wkb::try_new(&buf).unwrap();
+    let geo_traits::GeometryType::Point(pt) = wkb.as_type() else {
+        panic!("expected point")
+    };
+    let coord = pt.geo_coord().unwrap();
+    assert_eq!(coord.x, 1.0);
+    assert_eq!(coord.y, 2.0);
+
+    let buf = wkb_from_wkt_with_endianness("POINT EMPTY", endianness);
+    let wkb = Wkb::try_new(&buf).unwrap();
+    let geo_traits::GeometryType::Point(pt) = wkb.as_type() else {
+        panic!("expected point")
+    };
+    let coord = pt.geo_coord();
+    assert!(coord.is_none());
+}
+
+#[rstest]
+fn test_linestring_iterators(
+    #[values(Endianness::LittleEndian, Endianness::BigEndian)] endianness: 
Endianness,
+) {
+    let buf = wkb_from_wkt_with_endianness("LINESTRING(0 0, 1 1, 2 1.5)", 
endianness);
+    let wkb = Wkb::try_new(&buf).unwrap();
+    let GeometryTypeExt::LineString(ls) = wkb.as_type_ext() else {
+        panic!("expected linestring")
+    };
+
+    let coords = &[(0.0, 0.0), (1.0, 1.0), (2.0, 1.5)];
+    let v: Vec<_> = ls.coord_iter().collect();
+    assert_eq!(v.len(), coords.len());
+    for (got, (ex_x, ex_y)) in v.iter().zip(coords.iter()) {
+        assert!((got.x - ex_x).abs() < 1e-9);
+        assert!((got.y - ex_y).abs() < 1e-9);
+    }
+    let segs: Vec<_> = ls.lines().collect();
+    assert_eq!(segs.len(), coords.len() - 1);
+    assert_eq!(segs[0].start.x, 0.0);
+    assert_eq!(segs[0].end.x, 1.0);
+
+    // Empty linestring
+    let buf = wkb_from_wkt_with_endianness("LINESTRING EMPTY", endianness);
+    let wkb = Wkb::try_new(&buf).unwrap();
+    let GeometryTypeExt::LineString(ls) = wkb.as_type_ext() else {
+        panic!("expected linestring")
+    };
+    assert_eq!(ls.coord_iter().count(), 0);
+    assert_eq!(ls.lines().count(), 0);
+}
+
+#[test]
+fn test_geometry_collection_ext() {
+    let buf = wkb_from_wkt("GEOMETRYCOLLECTION(POINT(0 0), POINT(1 1))");
+    let wkb = Wkb::try_new(&buf).unwrap();
+
+    // GeometryTraitExt is implemented for Wkb in wkb_ext. Use those helpers.
+    assert!(wkb.is_collection());
+    assert_eq!(wkb.num_geometries_ext(), 2);
+
+    let child0 = wkb.geometry_ext(0).unwrap();
+    let GeometryTypeExt::Point(_) = child0.as_type_ext() else {
+        panic!("child0 expected point");
+    };
+
+    // Iterate via geometries_ext
+    let types: Vec<_> = wkb
+        .geometries_ext()
+        .map(|g| match g.as_type_ext() {
+            GeometryTypeExt::Point(_) => "P",
+            _ => "?",
+        })
+        .collect();
+    assert_eq!(types, vec!["P", "P"]);
+}
+
+#[test]
+fn test_linestring_rev_lines() {
+    // Empty linestring
+    let buf = wkb_from_wkt("LINESTRING EMPTY");
+    let wkb = Wkb::try_new(&buf).unwrap();
+    let geo_traits::GeometryType::LineString(ls) = wkb.as_type() else {
+        panic!("expected linestring")
+    };
+    assert_eq!(ls.rev_lines().count(), 0);
+
+    // Two-point linestring: 1 segment
+    let buf = wkb_from_wkt("LINESTRING(0 0, 1 1)");
+    let wkb = Wkb::try_new(&buf).unwrap();
+    let geo_traits::GeometryType::LineString(ls) = wkb.as_type() else {
+        panic!("expected linestring")
+    };
+    let forward: Vec<_> = ls.lines().collect();
+    let reverse: Vec<_> = ls.rev_lines().collect();
+    assert_eq!(forward.len(), 1);
+    assert_eq!(reverse.len(), 1);
+    assert_eq!(forward[0].start.x, reverse[0].start.x);
+    assert_eq!(forward[0].end.x, reverse[0].end.x);
+
+    // Multi-point linestring: rev_lines should produce segments in reverse 
order
+    let buf = wkb_from_wkt("LINESTRING(0 0, 2 0, 2 2, 0 2)");
+    let wkb = Wkb::try_new(&buf).unwrap();
+    let geo_traits::GeometryType::LineString(ls) = wkb.as_type() else {
+        panic!("expected linestring")
+    };
+    let forward: Vec<_> = ls.lines().collect();
+    let reverse: Vec<_> = ls.rev_lines().collect();
+    assert_eq!(forward.len(), 3);
+    assert_eq!(reverse.len(), 3);
+    for i in 0..forward.len() {
+        let f_rev = &forward[forward.len() - 1 - i];
+        let r = &reverse[i];
+        assert_eq!(f_rev.start.x, r.start.x);
+        assert_eq!(f_rev.end.x, r.end.x);
+    }
+}
+
+#[test]
+fn test_linestring_is_closed() {
+    // Empty line string is considered closed
+    let buf = wkb_from_wkt("LINESTRING EMPTY");
+    let wkb = Wkb::try_new(&buf).unwrap();
+    let geo_traits::GeometryType::LineString(ls) = wkb.as_type() else {
+        panic!("expected linestring")
+    };
+    assert!(ls.is_closed());
+
+    // Non-closed line string
+    let buf = wkb_from_wkt("LINESTRING(0 0, 1 0, 2 0)");
+    let wkb = Wkb::try_new(&buf).unwrap();
+    let geo_traits::GeometryType::LineString(ls) = wkb.as_type() else {
+        panic!("expected linestring")
+    };
+    assert!(!ls.is_closed());
+
+    // Closed linestring (square ring) with repeated first/last
+    let buf = wkb_from_wkt("LINESTRING(0 0, 1 0, 1 1, 0 1, 0 0)");
+    let wkb = Wkb::try_new(&buf).unwrap();
+    let geo_traits::GeometryType::LineString(ls) = wkb.as_type() else {
+        panic!("expected linestring")
+    };
+    assert!(ls.is_closed());
+}
+
+#[test]
+fn test_linestring_triangles() {
+    // Empty - no triangles
+    let buf = wkb_from_wkt("LINESTRING EMPTY");
+    let wkb = Wkb::try_new(&buf).unwrap();
+    let geo_traits::GeometryType::LineString(ls) = wkb.as_type() else {
+        panic!("expected linestring")
+    };
+    assert_eq!(ls.triangles().count(), 0);
+
+    // Two points - no triangles (need at least 3)
+    let buf = wkb_from_wkt("LINESTRING(0 0, 1 1)");
+    let wkb = Wkb::try_new(&buf).unwrap();
+    let geo_traits::GeometryType::LineString(ls) = wkb.as_type() else {
+        panic!("expected linestring")
+    };
+    assert_eq!(ls.triangles().count(), 0);
+
+    // Three points - one triangle
+    let buf = wkb_from_wkt("LINESTRING(0 0, 1 0, 1 1)");
+    let wkb = Wkb::try_new(&buf).unwrap();
+    let geo_traits::GeometryType::LineString(ls) = wkb.as_type() else {
+        panic!("expected linestring")
+    };
+    assert_eq!(ls.triangles().count(), 1);
+
+    // Four points - two triangles
+    let buf = wkb_from_wkt("LINESTRING(0 0, 2 0, 2 2, 0 2)");
+    let wkb = Wkb::try_new(&buf).unwrap();
+    let geo_traits::GeometryType::LineString(ls) = wkb.as_type() else {
+        panic!("expected linestring")
+    };
+    assert_eq!(ls.triangles().count(), 2);
+
+    // Single point - degenerate case
+    let buf = wkb_from_wkt("LINESTRING(5 5)");
+    let wkb = Wkb::try_new(&buf).unwrap();
+    let geo_traits::GeometryType::LineString(ls) = wkb.as_type() else {
+        panic!("expected linestring")
+    };
+    assert_eq!(ls.triangles().count(), 0);
+    assert_eq!(ls.lines().len(), 0);
+}

Reply via email to