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

paleolimbot 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 1f4506b2 fix(c/sedona-geos): Support export of geometries with M 
values from GEOS (#640)
1f4506b2 is described below

commit 1f4506b258d4eefc331f8c1c0198ed1694c9dbe3
Author: Dewey Dunnington <[email protected]>
AuthorDate: Thu Feb 19 08:50:48 2026 -0600

    fix(c/sedona-geos): Support export of geometries with M values from GEOS 
(#640)
---
 Cargo.lock                                        | 61 +++++------------------
 Cargo.toml                                        | 47 +++++++----------
 c/sedona-geos/src/geos_to_wkb.rs                  | 20 +++++++-
 c/sedona-geos/src/wkb_to_geos.rs                  | 18 +++----
 python/sedonadb/tests/functions/test_functions.py |  4 +-
 5 files changed, 61 insertions(+), 89 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index a13a037a..74239ac0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -181,7 +181,7 @@ version = "1.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
 dependencies = [
- "windows-sys 0.60.2",
+ "windows-sys 0.61.2",
 ]
 
 [[package]]
@@ -192,7 +192,7 @@ checksum = 
"291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
 dependencies = [
  "anstyle",
  "once_cell_polyfill",
- "windows-sys 0.60.2",
+ "windows-sys 0.61.2",
 ]
 
 [[package]]
@@ -2419,7 +2419,7 @@ dependencies = [
  "libc",
  "option-ext",
  "redox_users",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
 ]
 
 [[package]]
@@ -2525,7 +2525,7 @@ source = 
"registry+https://github.com/rust-lang/crates.io-index";
 checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
 dependencies = [
  "libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.61.2",
 ]
 
 [[package]]
@@ -2885,20 +2885,21 @@ dependencies = [
 
 [[package]]
 name = "geos"
-version = "10.0.0"
-source = 
"git+https://github.com/georust/geos.git?rev=47afbad2483e489911ddb456417808340e9342c3#47afbad2483e489911ddb456417808340e9342c3";
+version = "11.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "b236fab6ae800ae29f31e473433e0fd61a07dacdab29644b5e6fd69c835921b9"
 dependencies = [
  "geo-types",
  "geos-sys",
  "libc",
- "num",
  "wkt 0.10.3",
 ]
 
 [[package]]
 name = "geos-sys"
-version = "2.0.6"
-source = 
"git+https://github.com/georust/geos.git?rev=47afbad2483e489911ddb456417808340e9342c3#47afbad2483e489911ddb456417808340e9342c3";
+version = "2.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "582778505a1ec6d017d0382e947eb592b5f623479b6f1b1f2adf506934e84f89"
 dependencies = [
  "libc",
  "pkg-config",
@@ -3914,20 +3915,6 @@ dependencies = [
  "minimal-lexical",
 ]
 
-[[package]]
-name = "num"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
-dependencies = [
- "num-bigint",
- "num-complex",
- "num-integer",
- "num-iter",
- "num-rational",
- "num-traits",
-]
-
 [[package]]
 name = "num-bigint"
 version = "0.4.6"
@@ -3963,28 +3950,6 @@ dependencies = [
  "num-traits",
 ]
 
-[[package]]
-name = "num-iter"
-version = "0.1.45"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
-dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-rational"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
-dependencies = [
- "num-bigint",
- "num-integer",
- "num-traits",
-]
-
 [[package]]
 name = "num-traits"
 version = "0.2.19"
@@ -4913,7 +4878,7 @@ dependencies = [
  "errno",
  "libc",
  "linux-raw-sys",
- "windows-sys 0.52.0",
+ "windows-sys 0.61.2",
 ]
 
 [[package]]
@@ -6068,7 +6033,7 @@ dependencies = [
  "getrandom 0.4.1",
  "once_cell",
  "rustix",
- "windows-sys 0.52.0",
+ "windows-sys 0.61.2",
 ]
 
 [[package]]
@@ -6704,7 +6669,7 @@ version = "0.1.11"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
 dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.61.2",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index ce62c751..32099667 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -60,22 +60,21 @@ keywords = ["geospatial", "gis", "spatial", "datafusion", 
"arrow"]
 categories = ["science::geo", "database"]
 
 [workspace.dependencies]
-approx = "0.5"
 adbc_core = ">=0.22.0"
 adbc_ffi = ">=0.22.0"
-lru = "0.16"
+approx = "0.5"
 arrow = { version = "57.0.0", features = ["prettyprint", "ffi", "chrono-tz"] }
 arrow-array = { version = "57.0.0" }
+arrow-buffer = { version = "57.0.0" }
 arrow-cast = { version = "57.0.0" }
 arrow-data = { version = "57.0.0" }
 arrow-ipc = { version = "57.0.0" }
 arrow-json = { version = "57.0.0" }
 arrow-schema = { version = "57.0.0" }
-arrow-buffer = { version = "57.0.0" }
 async-trait = { version = "0.1.87" }
 bytemuck = "1.25"
-bytes = "1.11"
 byteorder = "1"
+bytes = "1.11"
 chrono = { version = "0.4.41", default-features = false }
 comfy-table = { version = "7.2" }
 criterion = { version = "0.8", features = ["html_reports"] }
@@ -94,36 +93,26 @@ datafusion-physical-plan = { version = "51.0.0" }
 datafusion-pruning = { version = "51.0.0" }
 dirs = "6.0.0"
 env_logger = "0.11"
-log = "^0.4"
 fastrand = "2.0"
+float_next_after = "2"
 futures = "0.3"
-pin-project-lite = "0.2"
+geo = "0.31.0"
+geo-index = { version = "0.3.3", features = ["use-geo_0_31"] }
+geo-traits = "0.3.0"
+geo-types = "0.7.17"
+geojson = "0.24.2"
+geos = { version = "11.0.0", features = ["geo", "v3_12_0"] }
 glam = "0.32.0"
-object_store = { version = "0.12.4", default-features = false }
-float_next_after = "2"
-num-traits = { version = "0.2", default-features = false, features = ["libm"] }
-mimalloc = { version = "0.1", default-features = false }
 libmimalloc-sys = { version = "0.1", default-features = false }
+log = "^0.4"
+lru = "0.16"
+mimalloc = { version = "0.1", default-features = false }
+num-traits = { version = "0.2", default-features = false, features = ["libm"] }
+object_store = { version = "0.12.4", default-features = false }
 once_cell = "1.20"
-
-geos = { git="https://github.com/georust/geos.git";, 
rev="47afbad2483e489911ddb456417808340e9342c3", features = ["geo", "v3_12_0"] }
-
-geo-types = "0.7.17"
-geo-traits = "0.3.0"
-geo = "0.31.0"
-geojson = "0.24.2"
-
-geo-index = { version = "0.3.3", features = ["use-geo_0_31"] }
-
-wkb = "0.9.2"
-wkt = "0.14.0"
-
 parking_lot = "0.12"
-parquet = { version = "57.0.0", default-features = false, features = [
-    "arrow",
-    "async",
-    "object_store",
-] }
+parquet = { version = "57.0.0", default-features = false, features = ["arrow", 
"async", "object_store"] }
+pin-project-lite = "0.2"
 rand = "0.10"
 regex = "1.12"
 rstest = "0.26.1"
@@ -134,6 +123,8 @@ tempfile = { version = "3"}
 thiserror = { version = "2" }
 tokio = { version = "1.48", features = ["macros", "rt", "sync"] }
 url = "2.5.7"
+wkb = "0.9.2"
+wkt = "0.14.0"
 
 # Workspace path dependencies for internal crates
 sedona = { version = "0.3.0", path = "rust/sedona" }
diff --git a/c/sedona-geos/src/geos_to_wkb.rs b/c/sedona-geos/src/geos_to_wkb.rs
index f60fd897..c8d26038 100644
--- a/c/sedona-geos/src/geos_to_wkb.rs
+++ b/c/sedona-geos/src/geos_to_wkb.rs
@@ -19,7 +19,8 @@ use std::io::Write;
 use byteorder::{LittleEndian, WriteBytesExt};
 use datafusion_common::{error::Result, DataFusionError};
 use geo_traits::Dimensions;
-use geos::{Geom, Geometry, GeometryTypes};
+use geos::{CoordType, Geom, Geometry, GeometryTypes};
+use sedona_common::sedona_internal_err;
 use sedona_geometry::wkb_factory::{
     write_wkb_geometrycollection_header, write_wkb_linestring_header,
     write_wkb_multilinestring_header, write_wkb_multipoint_header, 
write_wkb_multipolygon_header,
@@ -265,8 +266,16 @@ fn write_coord_seq(
     dim: Dimensions,
     writer: &mut impl Write,
 ) -> Result<()> {
+    let coord_type = match dim {
+        Dimensions::Xy => CoordType::XY,
+        Dimensions::Xyz => CoordType::XYZ,
+        Dimensions::Xym => CoordType::XYM,
+        Dimensions::Xyzm => CoordType::XYZM,
+        _ => return sedona_internal_err!("Unexpected dimensions {dim:?}"),
+    };
+
     let coords = coord_seq
-        .as_buffer(Some(dim.size()))
+        .as_buffer(Some(coord_type))
         .map_err(|e| DataFusionError::Execution(format!("Failed to get coord 
seq buffer: {e}")))?;
 
     // Cast Vec<f64> to &[u8] so we can write the bytes directly to the writer 
buffer
@@ -339,6 +348,13 @@ mod tests {
         test_wkb_round_trip("LINESTRING Z (0 0 10, 1 1 11, 2 2 12)");
     }
 
+    #[test]
+    fn test_write_linestring_xym() {
+        test_wkb_round_trip("LINESTRING M (0 0 0, 1 1 1)");
+        test_wkb_round_trip("LINESTRING M (0 0 0, 1 1 1, 2 2 2)");
+        test_wkb_round_trip("LINESTRING M (0 0 10, 1 1 11, 2 2 12)");
+    }
+
     #[test]
     fn test_write_linestring_xyzm() {
         test_wkb_round_trip("LINESTRING ZM (0 0 1 2, 1 1 3 4)");
diff --git a/c/sedona-geos/src/wkb_to_geos.rs b/c/sedona-geos/src/wkb_to_geos.rs
index 9bd83507..91d75b71 100644
--- a/c/sedona-geos/src/wkb_to_geos.rs
+++ b/c/sedona-geos/src/wkb_to_geos.rs
@@ -18,7 +18,7 @@ use std::cell::RefCell;
 
 use byteorder::{BigEndian, ByteOrder, LittleEndian};
 use geo_traits::*;
-use geos::GResult;
+use geos::{CoordType, GResult};
 use wkb::{reader::*, Endianness};
 
 /// A factory for converting WKB to GEOS geometries.
@@ -215,11 +215,11 @@ fn create_coord_sequence_from_raw_parts(
     num_coords: usize,
     scratch: &mut Vec<f64>,
 ) -> GResult<geos::CoordSeq> {
-    let (has_z, has_m, dim_size) = match dim {
-        Dimension::Xy => (false, false, 2),
-        Dimension::Xyz => (true, false, 3),
-        Dimension::Xym => (false, true, 3),
-        Dimension::Xyzm => (true, true, 4),
+    let (coord_type, dim_size) = match dim {
+        Dimension::Xy => (CoordType::XY, 2),
+        Dimension::Xyz => (CoordType::XYZ, 3),
+        Dimension::Xym => (CoordType::XYM, 3),
+        Dimension::Xyzm => (CoordType::XYZM, 4),
     };
     let num_ordinates = dim_size * num_coords;
 
@@ -233,7 +233,7 @@ fn create_coord_sequence_from_raw_parts(
         {
             let coords_f64 =
                 unsafe { &*core::ptr::slice_from_raw_parts(ptr as *const f64, 
num_ordinates) };
-            geos::CoordSeq::new_from_buffer(coords_f64, num_coords, has_z, 
has_m)
+            geos::CoordSeq::new_from_buffer(coords_f64, num_coords, coord_type)
         }
 
         // On platforms without unaligned memory access support, we need to 
copy the data to the
@@ -249,7 +249,7 @@ fn create_coord_sequence_from_raw_parts(
                     scratch.as_mut_ptr() as *mut u8,
                     num_ordinates * std::mem::size_of::<f64>(),
                 );
-                geos::CoordSeq::new_from_buffer(scratch.as_slice(), 
num_coords, has_z, has_m)
+                geos::CoordSeq::new_from_buffer(scratch.as_slice(), 
num_coords, coord_type)
             }
         }
     } else {
@@ -262,7 +262,7 @@ fn create_coord_sequence_from_raw_parts(
                 save_f64_to_scratch::<LittleEndian>(scratch, buf, 
num_ordinates);
             }
         }
-        geos::CoordSeq::new_from_buffer(scratch.as_slice(), num_coords, has_z, 
has_m)
+        geos::CoordSeq::new_from_buffer(scratch.as_slice(), num_coords, 
coord_type)
     }
 }
 
diff --git a/python/sedonadb/tests/functions/test_functions.py 
b/python/sedonadb/tests/functions/test_functions.py
index 904a3875..fbbda82c 100644
--- a/python/sedonadb/tests/functions/test_functions.py
+++ b/python/sedonadb/tests/functions/test_functions.py
@@ -1174,10 +1174,10 @@ def test_st_unaryunion(eng, geom, expected):
 @pytest.mark.parametrize(
     ("geom", "expected"),
     [
-        # Skip M tests because geos rust isn't capable of writing XYM 
geometries yet
-        # https://github.com/apache/sedona-db/issues/481
+        ("POINT M EMPTY", "POINT M EMPTY"),
         ("POINT Z EMPTY", "POINT Z EMPTY"),
         ("POINT ZM EMPTY", "POINT ZM EMPTY"),
+        ("POINT M (0 1 2)", "POINT M(0 1 2)"),
         ("POINT Z (0 0 0)", "POINT Z(0 0 0)"),
         ("POINT ZM (1 2 3 4)", "POINT ZM(1 2 3 4)"),
         ("LINESTRING Z (0 0 0, 1 1 1)", "LINESTRING Z(0 0 0,1 1 1)"),

Reply via email to