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

jiayu 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 924aa6c  feat: Implement native Rust ST_Centroid and ST_Length 
functions (#33)
924aa6c is described below

commit 924aa6c3d3f519b7877c12389a36f0d58baf4f09
Author: Feng Zhang <[email protected]>
AuthorDate: Mon Sep 8 12:21:54 2025 -0700

    feat: Implement native Rust ST_Centroid and ST_Length functions (#33)
    
    * Implement the st_centroid udf function using rust geo-algo
    
    * Add st_length support in rust geo algo
    
    * rever the changes (for testing)
    
    * revert change to the test bench base
    
    * revert git ignore
    
    * fix pre-commit lint
    
    * fix python tests and clippy errors
    
    * fix pre-commit lint
    
    * ignore python cache
    
    * refactor st_length and st_centroid
    
    * fix st_length tests
    
    * revert changes to functional tests
    
    * fix the st_length implementation
    
    * use geo_generic_alg::EuclideanLength
    
    * free up disk space in rust.yaml build process
    
    * fix pre-commit lint
    
    * switch to use LengthMeasurableExt
    
    * replaced manual disk cleanup with jlumbroso/free-disk-space action
    
    * do not remove large-packages
    
    * use write_wkb_empty_point
    
    * address pr review comments
    
    * refactor how is_empty is implemented
    
    * remove free disk task from the ci build
    
    * change to use SedonaGeometryError
---
 .github/workflows/rust.yml              |   9 +-
 .gitignore                              |   3 +
 Cargo.lock                              | 276 +++++++++++++++++---------------
 benchmarks/test_functions.py            |  17 ++
 rust/sedona-functions/src/lib.rs        |   2 +-
 rust/sedona-functions/src/register.rs   |   1 +
 rust/sedona-functions/src/st_isempty.rs |  33 ++--
 rust/sedona-geo/src/lib.rs              |   2 +
 rust/sedona-geo/src/register.rs         |   7 +-
 rust/sedona-geo/src/st_centroid.rs      | 139 ++++++++++++++++
 rust/sedona-geo/src/st_length.rs        | 115 +++++++++++++
 rust/sedona-geometry/src/is_empty.rs    | 151 +++++++++++++++++
 rust/sedona-geometry/src/lib.rs         |   1 +
 13 files changed, 605 insertions(+), 151 deletions(-)

diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 7069839..27b2885 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -51,6 +51,11 @@ jobs:
     runs-on: ubuntu-latest
     env:
       CARGO_INCREMENTAL: 0
+      # Reduce debug info to save disk space
+      CARGO_PROFILE_DEV_DEBUG: 1
+      CARGO_PROFILE_TEST_DEBUG: 1
+      # Limit parallel compilation to reduce memory pressure
+      CARGO_BUILD_JOBS: 2
     steps:
       - uses: actions/checkout@v4
         with:
@@ -97,7 +102,7 @@ jobs:
       - uses: Swatinem/rust-cache@v2
         with:
           # Update this key to force a new cache
-          prefix-key: "rust-${{ matrix.name }}-v1"
+          prefix-key: "rust-${{ matrix.name }}-v2"
 
       - name: Install dependencies
         shell: bash
@@ -112,6 +117,8 @@ jobs:
         if: matrix.name == 'test'
         run: |
           cargo test --workspace --all-targets --all-features
+          # Clean up intermediate build artifacts to free disk space
+          cargo clean -p sedona-s2geography
 
       - name: Doctests
         if: matrix.name == 'test'
diff --git a/.gitignore b/.gitignore
index a7e0e92..72eaab6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,3 +36,6 @@ coverage/
 
 # documentation
 site/
+
+# Python cache files
+__pycache__
diff --git a/Cargo.lock b/Cargo.lock
index 4b0eb37..999f2c0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -539,9 +539,9 @@ checksum = 
"c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
 
 [[package]]
 name = "aws-config"
-version = "1.8.5"
+version = "1.8.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "c478f5b10ce55c9a33f87ca3404ca92768b144fc1bfdede7c0121214a8283a25"
+checksum = "8bc1b40fb26027769f16960d2f4a6bc20c4bb755d403e552c8c1a73af433c246"
 dependencies = [
  "aws-credential-types",
  "aws-runtime",
@@ -569,9 +569,9 @@ dependencies = [
 
 [[package]]
 name = "aws-credential-types"
-version = "1.2.5"
+version = "1.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1541072f81945fa1251f8795ef6c92c4282d74d59f88498ae7d4bf00f0ebdad9"
+checksum = "d025db5d9f52cbc413b167136afb3d8aeea708c0d8884783cf6253be5e22f6f2"
 dependencies = [
  "aws-smithy-async",
  "aws-smithy-runtime-api",
@@ -628,9 +628,9 @@ dependencies = [
 
 [[package]]
 name = "aws-sdk-sso"
-version = "1.81.0"
+version = "1.83.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "79ede098271e3471036c46957cba2ba30888f53bda2515bf04b560614a30a36e"
+checksum = "643cd43af212d2a1c4dedff6f044d7e1961e5d9e7cfe773d70f31d9842413886"
 dependencies = [
  "aws-credential-types",
  "aws-runtime",
@@ -650,9 +650,9 @@ dependencies = [
 
 [[package]]
 name = "aws-sdk-ssooidc"
-version = "1.83.0"
+version = "1.84.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "0b49e8fe57ff100a2f717abfa65bdd94e39702fa5ab3f60cddc6ac7784010c68"
+checksum = "20ec4a95bd48e0db7a424356a161f8d87bd6a4f0af37204775f0da03d9e39fc3"
 dependencies = [
  "aws-credential-types",
  "aws-runtime",
@@ -672,9 +672,9 @@ dependencies = [
 
 [[package]]
 name = "aws-sdk-sts"
-version = "1.84.0"
+version = "1.85.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "91abcdbfb48c38a0419eb75e0eac772a4783a96750392680e4f3c25a8a0535b9"
+checksum = "410309ad0df4606bc721aff0d89c3407682845453247213a0ccc5ff8801ee107"
 dependencies = [
  "aws-credential-types",
  "aws-runtime",
@@ -748,9 +748,9 @@ dependencies = [
 
 [[package]]
 name = "aws-smithy-http-client"
-version = "1.1.0"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "4fdbad9bd9dbcc6c5e68c311a841b54b70def3ca3b674c42fbebb265980539f8"
+checksum = "147e8eea63a40315d704b97bf9bc9b8c1402ae94f89d5ad6f7550d963309da1b"
 dependencies = [
  "aws-smithy-async",
  "aws-smithy-runtime-api",
@@ -772,9 +772,9 @@ dependencies = [
 
 [[package]]
 name = "aws-smithy-json"
-version = "0.61.4"
+version = "0.61.5"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "a16e040799d29c17412943bdbf488fd75db04112d0c0d4b9290bacf5ae0014b9"
+checksum = "eaa31b350998e703e9826b2104dd6f63be0508666e1aba88137af060e8944047"
 dependencies = [
  "aws-smithy-types",
 ]
@@ -800,9 +800,9 @@ dependencies = [
 
 [[package]]
 name = "aws-smithy-runtime"
-version = "1.9.0"
+version = "1.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "a3d57c8b53a72d15c8e190475743acf34e4996685e346a3448dd54ef696fc6e0"
+checksum = "d3946acbe1ead1301ba6862e712c7903ca9bb230bdf1fbd1b5ac54158ef2ab1f"
 dependencies = [
  "aws-smithy-async",
  "aws-smithy-http",
@@ -975,9 +975,9 @@ dependencies = [
 
 [[package]]
 name = "bitflags"
-version = "2.9.3"
+version = "2.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d"
+checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
 
 [[package]]
 name = "blake2"
@@ -1117,10 +1117,11 @@ checksum = 
"37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
 
 [[package]]
 name = "cc"
-version = "1.2.34"
+version = "1.2.36"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc"
+checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54"
 dependencies = [
+ "find-msvc-tools",
  "jobserver",
  "libc",
  "shlex",
@@ -1157,7 +1158,7 @@ dependencies = [
  "iana-time-zone",
  "num-traits",
  "serde",
- "windows-link",
+ "windows-link 0.1.3",
 ]
 
 [[package]]
@@ -1210,9 +1211,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.46"
+version = "4.5.47"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57"
+checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -1220,9 +1221,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.46"
+version = "4.5.47"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41"
+checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6"
 dependencies = [
  "anstream",
  "anstyle",
@@ -1232,9 +1233,9 @@ dependencies = [
 
 [[package]]
 name = "clap_derive"
-version = "4.5.45"
+version = "4.5.47"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6"
+checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -1274,9 +1275,9 @@ checksum = 
"b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
 
 [[package]]
 name = "comfy-table"
-version = "7.1.4"
+version = "7.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "4a65ebfec4fb190b6f90e944a817d60499ee0744e582530e2c9900a22e591d9a"
+checksum = "3f8e18d0dca9578507f13f9803add0df13362b02c501c1c17734f0dbb52eaf0b"
 dependencies = [
  "crossterm",
  "unicode-segmentation",
@@ -1448,14 +1449,15 @@ checksum = 
"d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
 
 [[package]]
 name = "crossterm"
-version = "0.28.1"
+version = "0.29.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
+checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
 dependencies = [
  "bitflags",
  "crossterm_winapi",
+ "document-features",
  "parking_lot",
- "rustix 0.38.44",
+ "rustix 1.0.8",
  "winapi",
 ]
 
@@ -2263,9 +2265,9 @@ dependencies = [
 
 [[package]]
 name = "deranged"
-version = "0.4.0"
+version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
+checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc"
 dependencies = [
  "powerfmt",
 ]
@@ -2299,7 +2301,7 @@ dependencies = [
  "libc",
  "option-ext",
  "redox_users",
- "windows-sys 0.60.2",
+ "windows-sys 0.61.0",
 ]
 
 [[package]]
@@ -2313,6 +2315,15 @@ dependencies = [
  "syn 2.0.106",
 ]
 
+[[package]]
+name = "document-features"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
+dependencies = [
+ "litrs",
+]
+
 [[package]]
 name = "dunce"
 version = "1.0.5"
@@ -2415,6 +2426,12 @@ dependencies = [
  "windows-sys 0.60.2",
 ]
 
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d"
+
 [[package]]
 name = "fixedbitset"
 version = "0.5.7"
@@ -2610,7 +2627,7 @@ dependencies = [
 [[package]]
 name = "geo-generic-alg"
 version = "0.1.0"
-source = 
"git+https://github.com/wherobots/geo.git?branch=generic-alg#04c929eb1d63b8738165c019095f9f625f8ecde7";
+source = 
"git+https://github.com/wherobots/geo.git?branch=generic-alg#ebc0ca413d20c0106f832c59eebda386691c1456";
 dependencies = [
  "earcutr",
  "float_next_after",
@@ -2645,7 +2662,7 @@ dependencies = [
 [[package]]
 name = "geo-traits"
 version = "0.2.0"
-source = 
"git+https://github.com/wherobots/geo.git?branch=generic-alg#04c929eb1d63b8738165c019095f9f625f8ecde7";
+source = 
"git+https://github.com/wherobots/geo.git?branch=generic-alg#ebc0ca413d20c0106f832c59eebda386691c1456";
 dependencies = [
  "geo-types",
 ]
@@ -2662,7 +2679,7 @@ dependencies = [
 [[package]]
 name = "geo-traits-ext"
 version = "0.1.0"
-source = 
"git+https://github.com/wherobots/geo.git?branch=generic-alg#04c929eb1d63b8738165c019095f9f625f8ecde7";
+source = 
"git+https://github.com/wherobots/geo.git?branch=generic-alg#ebc0ca413d20c0106f832c59eebda386691c1456";
 dependencies = [
  "approx",
  "geo-traits 0.2.0",
@@ -2674,7 +2691,7 @@ dependencies = [
 [[package]]
 name = "geo-types"
 version = "0.7.16"
-source = 
"git+https://github.com/wherobots/geo.git?branch=generic-alg#04c929eb1d63b8738165c019095f9f625f8ecde7";
+source = 
"git+https://github.com/wherobots/geo.git?branch=generic-alg#ebc0ca413d20c0106f832c59eebda386691c1456";
 dependencies = [
  "approx",
  "num-traits",
@@ -2740,7 +2757,7 @@ dependencies = [
  "js-sys",
  "libc",
  "r-efi",
- "wasi 0.14.2+wasi-0.2.4",
+ "wasi 0.14.4+wasi-0.2.4",
  "wasm-bindgen",
 ]
 
@@ -2987,7 +3004,7 @@ dependencies = [
  "libc",
  "percent-encoding",
  "pin-project-lite",
- "socket2 0.6.0",
+ "socket2",
  "tokio",
  "tower-service",
  "tracing",
@@ -3327,9 +3344,9 @@ dependencies = [
 
 [[package]]
 name = "js-sys"
-version = "0.3.77"
+version = "0.3.78"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738"
 dependencies = [
  "once_cell",
  "wasm-bindgen",
@@ -3506,18 +3523,18 @@ dependencies = [
 
 [[package]]
 name = "libz-rs-sys"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221"
+checksum = "840db8cf39d9ec4dd794376f38acc40d0fc65eec2a8f484f7fd375b84602becd"
 dependencies = [
  "zlib-rs",
 ]
 
 [[package]]
 name = "link-cplusplus"
-version = "1.0.10"
+version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212"
+checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82"
 dependencies = [
  "cc",
 ]
@@ -3540,6 +3557,12 @@ version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
 
+[[package]]
+name = "litrs"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
+
 [[package]]
 name = "lock_api"
 version = "0.4.13"
@@ -3552,9 +3575,9 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.27"
+version = "0.4.28"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
 
 [[package]]
 name = "lru"
@@ -4041,9 +4064,9 @@ dependencies = [
 
 [[package]]
 name = "potential_utf"
-version = "0.1.2"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
+checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a"
 dependencies = [
  "zerovec",
 ]
@@ -4217,9 +4240,9 @@ dependencies = [
 
 [[package]]
 name = "quinn"
-version = "0.11.8"
+version = "0.11.9"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8"
+checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
 dependencies = [
  "bytes",
  "cfg_aliases",
@@ -4228,7 +4251,7 @@ dependencies = [
  "quinn-udp",
  "rustc-hash 2.1.1",
  "rustls",
- "socket2 0.5.10",
+ "socket2",
  "thiserror 2.0.16",
  "tokio",
  "tracing",
@@ -4237,9 +4260,9 @@ dependencies = [
 
 [[package]]
 name = "quinn-proto"
-version = "0.11.12"
+version = "0.11.13"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e"
+checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
 dependencies = [
  "bytes",
  "getrandom 0.3.3",
@@ -4258,16 +4281,16 @@ dependencies = [
 
 [[package]]
 name = "quinn-udp"
-version = "0.5.13"
+version = "0.5.14"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970"
+checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
 dependencies = [
  "cfg_aliases",
  "libc",
  "once_cell",
- "socket2 0.5.10",
+ "socket2",
  "tracing",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
 ]
 
 [[package]]
@@ -4744,9 +4767,9 @@ checksum = 
"94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
 [[package]]
 name = "security-framework"
-version = "3.3.0"
+version = "3.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c"
+checksum = "60b369d18893388b345804dc0007963c99b7d665ae71d275812d828c6f089640"
 dependencies = [
  "bitflags",
  "core-foundation",
@@ -4757,9 +4780,9 @@ dependencies = [
 
 [[package]]
 name = "security-framework-sys"
-version = "2.14.0"
+version = "2.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
+checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0"
 dependencies = [
  "core-foundation-sys",
  "libc",
@@ -5315,16 +5338,6 @@ version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b"
 
-[[package]]
-name = "socket2"
-version = "0.5.10"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
-dependencies = [
- "libc",
- "windows-sys 0.52.0",
-]
-
 [[package]]
 name = "socket2"
 version = "0.6.0"
@@ -5556,9 +5569,9 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.3.41"
+version = "0.3.43"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
+checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031"
 dependencies = [
  "deranged",
  "num-conv",
@@ -5570,15 +5583,15 @@ dependencies = [
 
 [[package]]
 name = "time-core"
-version = "0.1.4"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
+checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
 
 [[package]]
 name = "time-macros"
-version = "0.2.22"
+version = "0.2.24"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
+checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
 dependencies = [
  "num-conv",
  "time-core",
@@ -5643,7 +5656,7 @@ dependencies = [
  "pin-project-lite",
  "signal-hook-registry",
  "slab",
- "socket2 0.6.0",
+ "socket2",
  "tokio-macros",
  "windows-sys 0.59.0",
 ]
@@ -5798,9 +5811,9 @@ checksum = 
"e78122066b0cb818b8afd08f7ed22f7fdbc3e90815035726f0840d0d26c0747a"
 
 [[package]]
 name = "twox-hash"
-version = "2.1.1"
+version = "2.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "8b907da542cbced5261bd3256de1b3a1bf340a3d37f93425a07362a1d687de56"
+checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
 
 [[package]]
 name = "typed-arena"
@@ -5836,9 +5849,9 @@ checksum = 
"1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
 
 [[package]]
 name = "typewit"
-version = "1.13.0"
+version = "1.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "4dd91acc53c592cb800c11c83e8e7ee1d48378d05cfa33b5474f5f80c5b236bf"
+checksum = "4c98488b93df24b7c794d6a58c4198d7a2abde676324beaca84f7fb5b39c0811"
 
 [[package]]
 name = "unicode-ident"
@@ -5902,9 +5915,9 @@ checksum = 
"06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 
 [[package]]
 name = "uuid"
-version = "1.18.0"
+version = "1.18.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be"
+checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
 dependencies = [
  "getrandom 0.3.3",
  "js-sys",
@@ -5957,30 +5970,31 @@ checksum = 
"ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
 
 [[package]]
 name = "wasi"
-version = "0.14.2+wasi-0.2.4"
+version = "0.14.4+wasi-0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
+checksum = "88a5f4a424faf49c3c2c344f166f0662341d470ea185e939657aaff130f0ec4a"
 dependencies = [
- "wit-bindgen-rt",
+ "wit-bindgen",
 ]
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.100"
+version = "0.2.101"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b"
 dependencies = [
  "cfg-if",
  "once_cell",
  "rustversion",
  "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.100"
+version = "0.2.101"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
+checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb"
 dependencies = [
  "bumpalo",
  "log",
@@ -5992,9 +6006,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.50"
+version = "0.4.51"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
+checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe"
 dependencies = [
  "cfg-if",
  "js-sys",
@@ -6005,9 +6019,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.100"
+version = "0.2.101"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -6015,9 +6029,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.100"
+version = "0.2.101"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -6028,9 +6042,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.100"
+version = "0.2.101"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1"
 dependencies = [
  "unicode-ident",
 ]
@@ -6050,9 +6064,9 @@ dependencies = [
 
 [[package]]
 name = "web-sys"
-version = "0.3.77"
+version = "0.3.78"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
+checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -6098,11 +6112,11 @@ checksum = 
"ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
 name = "winapi-util"
-version = "0.1.10"
+version = "0.1.11"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
 dependencies = [
- "windows-sys 0.60.2",
+ "windows-sys 0.61.0",
 ]
 
 [[package]]
@@ -6119,7 +6133,7 @@ checksum = 
"c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
 dependencies = [
  "windows-implement",
  "windows-interface",
- "windows-link",
+ "windows-link 0.1.3",
  "windows-result",
  "windows-strings",
 ]
@@ -6152,13 +6166,19 @@ version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
 
+[[package]]
+name = "windows-link"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
+
 [[package]]
 name = "windows-result"
 version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
 dependencies = [
- "windows-link",
+ "windows-link 0.1.3",
 ]
 
 [[package]]
@@ -6167,7 +6187,7 @@ version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
 dependencies = [
- "windows-link",
+ "windows-link 0.1.3",
 ]
 
 [[package]]
@@ -6197,6 +6217,15 @@ dependencies = [
  "windows-targets 0.53.3",
 ]
 
+[[package]]
+name = "windows-sys"
+version = "0.61.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa"
+dependencies = [
+ "windows-link 0.2.0",
+]
+
 [[package]]
 name = "windows-targets"
 version = "0.52.6"
@@ -6219,7 +6248,7 @@ version = "0.53.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
 dependencies = [
- "windows-link",
+ "windows-link 0.1.3",
  "windows_aarch64_gnullvm 0.53.0",
  "windows_aarch64_msvc 0.53.0",
  "windows_i686_gnu 0.53.0",
@@ -6336,13 +6365,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "wit-bindgen-rt"
-version = "0.39.0"
+name = "wit-bindgen"
+version = "0.45.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
-dependencies = [
- "bitflags",
-]
+checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36"
 
 [[package]]
 name = "wkb"
@@ -6452,18 +6478,18 @@ dependencies = [
 
 [[package]]
 name = "zerocopy"
-version = "0.8.26"
+version = "0.8.27"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
+checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
 dependencies = [
  "zerocopy-derive",
 ]
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.8.26"
+version = "0.8.27"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
+checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -6532,9 +6558,9 @@ dependencies = [
 
 [[package]]
 name = "zlib-rs"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a"
+checksum = "2f06ae92f42f5e5c42443fd094f245eb656abf56dd7cce9b8b263236565e00f2"
 
 [[package]]
 name = "zstd"
@@ -6556,9 +6582,9 @@ dependencies = [
 
 [[package]]
 name = "zstd-sys"
-version = "2.0.15+zstd.1.5.7"
+version = "2.0.16+zstd.1.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237"
+checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
 dependencies = [
  "cc",
  "pkg-config",
diff --git a/benchmarks/test_functions.py b/benchmarks/test_functions.py
index 8be56d7..a36de82 100644
--- a/benchmarks/test_functions.py
+++ b/benchmarks/test_functions.py
@@ -115,3 +115,20 @@ class TestBenchFunctions(TestBenchBase):
             eng.execute_and_collect(f"SELECT ST_GeometryType(geom1) from 
{table}")
 
         benchmark(queries)
+
+    @pytest.mark.parametrize("eng", [SedonaDB, PostGIS, DuckDB])
+    @pytest.mark.parametrize(
+        "table",
+        [
+            "segments_large",
+            "collections_simple",
+            "collections_complex",
+        ],
+    )
+    def test_st_length(self, benchmark, eng, table):
+        eng = self._get_eng(eng)
+
+        def queries():
+            eng.execute_and_collect(f"SELECT ST_Length(geom1) from {table}")
+
+        benchmark(queries)
diff --git a/rust/sedona-functions/src/lib.rs b/rust/sedona-functions/src/lib.rs
index 1041fb1..6a791f2 100644
--- a/rust/sedona-functions/src/lib.rs
+++ b/rust/sedona-functions/src/lib.rs
@@ -37,7 +37,7 @@ mod st_geomfromwkb;
 mod st_geomfromwkt;
 mod st_haszm;
 pub mod st_intersection_aggr;
-mod st_isempty;
+pub mod st_isempty;
 mod st_length;
 mod st_perimeter;
 mod st_point;
diff --git a/rust/sedona-functions/src/register.rs 
b/rust/sedona-functions/src/register.rs
index 4db04da..48639d2 100644
--- a/rust/sedona-functions/src/register.rs
+++ b/rust/sedona-functions/src/register.rs
@@ -123,6 +123,7 @@ pub mod stubs {
     pub use crate::predicates::*;
     pub use crate::referencing::*;
     pub use crate::st_area::st_area_udf;
+    pub use crate::st_centroid::st_centroid_udf;
     pub use crate::st_length::st_length_udf;
     pub use crate::st_perimeter::st_perimeter_udf;
     pub use crate::st_setsrid::st_set_crs_with_engine_udf;
diff --git a/rust/sedona-functions/src/st_isempty.rs 
b/rust/sedona-functions/src/st_isempty.rs
index 7069f54..d41a08b 100644
--- a/rust/sedona-functions/src/st_isempty.rs
+++ b/rust/sedona-functions/src/st_isempty.rs
@@ -23,12 +23,8 @@ use datafusion_common::error::Result;
 use datafusion_expr::{
     scalar_doc_sections::DOC_SECTION_OTHER, ColumnarValue, Documentation, 
Volatility,
 };
-use geo_traits::{
-    GeometryCollectionTrait, GeometryTrait, LineStringTrait, 
MultiLineStringTrait, MultiPointTrait,
-    MultiPolygonTrait, PointTrait, PolygonTrait,
-};
-use sedona_common::sedona_internal_err;
 use sedona_expr::scalar_udf::{ArgMatcher, SedonaScalarKernel, SedonaScalarUDF};
+use sedona_geometry::is_empty::is_geometry_empty;
 use sedona_schema::datatypes::SedonaType;
 use wkb::reader::Wkb;
 
@@ -87,25 +83,16 @@ impl SedonaScalarKernel for STIsEmpty {
     }
 }
 
+pub fn is_wkb_empty(item: &Wkb) -> Result<bool> {
+    invoke_scalar(item)
+}
+
 fn invoke_scalar(item: &Wkb) -> Result<bool> {
-    match item.as_type() {
-        geo_traits::GeometryType::Point(point) => Ok(point.coord().is_none()),
-        geo_traits::GeometryType::LineString(linestring) => 
Ok(linestring.num_coords() == 0),
-        geo_traits::GeometryType::Polygon(polygon) => {
-            Ok(polygon.num_interiors() == 0 && polygon.exterior().is_none())
-        }
-        geo_traits::GeometryType::MultiPoint(multipoint) => 
Ok(multipoint.num_points() == 0),
-        geo_traits::GeometryType::MultiLineString(multilinestring) => {
-            Ok(multilinestring.num_line_strings() == 0)
-        }
-        geo_traits::GeometryType::MultiPolygon(multipolygon) => {
-            Ok(multipolygon.num_polygons() == 0)
-        }
-        geo_traits::GeometryType::GeometryCollection(geometrycollection) => {
-            Ok(geometrycollection.num_geometries() == 0)
-        }
-        _ => sedona_internal_err!("Invalid geometry type"),
-    }
+    is_geometry_empty(item).map_err(|e| {
+        datafusion_common::error::DataFusionError::Execution(format!(
+            "Failed to check if geometry is empty: {e}"
+        ))
+    })
 }
 
 #[cfg(test)]
diff --git a/rust/sedona-geo/src/lib.rs b/rust/sedona-geo/src/lib.rs
index edb0959..24c6408 100644
--- a/rust/sedona-geo/src/lib.rs
+++ b/rust/sedona-geo/src/lib.rs
@@ -17,8 +17,10 @@
 pub mod centroid;
 pub mod register;
 mod st_area;
+mod st_centroid;
 mod st_intersection_aggr;
 mod st_intersects;
+mod st_length;
 mod st_line_interpolate_point;
 mod st_union_aggr;
 pub mod to_geo;
diff --git a/rust/sedona-geo/src/register.rs b/rust/sedona-geo/src/register.rs
index 5904961..9db2c32 100644
--- a/rust/sedona-geo/src/register.rs
+++ b/rust/sedona-geo/src/register.rs
@@ -20,12 +20,17 @@ use sedona_expr::scalar_udf::ScalarKernelRef;
 use crate::st_intersection_aggr::st_intersection_aggr_impl;
 use crate::st_line_interpolate_point::st_line_interpolate_point_impl;
 use crate::st_union_aggr::st_union_aggr_impl;
-use crate::{st_area::st_area_impl, st_intersects::st_intersects_impl};
+use crate::{
+    st_area::st_area_impl, st_centroid::st_centroid_impl, 
st_intersects::st_intersects_impl,
+    st_length::st_length_impl,
+};
 
 pub fn scalar_kernels() -> Vec<(&'static str, ScalarKernelRef)> {
     vec![
         ("st_intersects", st_intersects_impl()),
         ("st_area", st_area_impl()),
+        ("st_centroid", st_centroid_impl()),
+        ("st_length", st_length_impl()),
         ("st_lineinterpolatepoint", st_line_interpolate_point_impl()),
     ]
 }
diff --git a/rust/sedona-geo/src/st_centroid.rs 
b/rust/sedona-geo/src/st_centroid.rs
new file mode 100644
index 0000000..0b4a623
--- /dev/null
+++ b/rust/sedona-geo/src/st_centroid.rs
@@ -0,0 +1,139 @@
+// 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::sync::Arc;
+
+use arrow_array::builder::BinaryBuilder;
+use datafusion_common::{error::Result, exec_err};
+use datafusion_expr::ColumnarValue;
+use geo_generic_alg::Centroid;
+use sedona_expr::scalar_udf::{ArgMatcher, ScalarKernelRef, SedonaScalarKernel};
+use sedona_functions::executor::WkbExecutor;
+use sedona_geometry::is_empty::is_geometry_empty;
+use sedona_schema::datatypes::{SedonaType, WKB_GEOMETRY};
+use wkb::reader::Wkb;
+
+use geo_traits::Dimensions;
+use sedona_geometry::wkb_factory::{self, WKB_MIN_PROBABLE_BYTES};
+
+/// ST_Centroid() implementation using centroid extraction
+pub fn st_centroid_impl() -> ScalarKernelRef {
+    Arc::new(STCentroid {})
+}
+
+#[derive(Debug)]
+struct STCentroid {}
+
+impl SedonaScalarKernel for STCentroid {
+    fn return_type(&self, args: &[SedonaType]) -> Result<Option<SedonaType>> {
+        let matcher = ArgMatcher::new(vec![ArgMatcher::is_geometry()], 
WKB_GEOMETRY);
+
+        matcher.match_args(args)
+    }
+
+    fn invoke_batch(
+        &self,
+        arg_types: &[SedonaType],
+        args: &[ColumnarValue],
+    ) -> Result<ColumnarValue> {
+        let executor = WkbExecutor::new(arg_types, args);
+        let mut builder =
+            BinaryBuilder::with_capacity(executor.num_iterations(), 
WKB_MIN_PROBABLE_BYTES);
+        executor.execute_wkb_void(|maybe_wkb| {
+            match maybe_wkb {
+                Some(wkb) => {
+                    let centroid_wkb = invoke_scalar(&wkb)?;
+                    builder.append_value(&centroid_wkb);
+                }
+                _ => builder.append_null(),
+            }
+
+            Ok(())
+        })?;
+
+        executor.finish(Arc::new(builder.finish()))
+    }
+}
+
+fn invoke_scalar(wkb: &Wkb) -> Result<Vec<u8>> {
+    // Check for empty geometries first - they should return POINT EMPTY
+    if is_geometry_empty(wkb).map_err(|e| {
+        datafusion_common::error::DataFusionError::Execution(format!(
+            "Failed to check if geometry is empty: {e}"
+        ))
+    })? {
+        let mut empty_point_wkb = Vec::new();
+        wkb_factory::write_wkb_empty_point(&mut empty_point_wkb, 
Dimensions::Xy)
+            .map_err(|e| 
datafusion_common::error::DataFusionError::External(Box::new(e)))?;
+        return Ok(empty_point_wkb);
+    }
+
+    // Use Centroid trait directly on WKB, similar to how st_area uses 
unsigned_area()
+    if let Some(centroid_point) = wkb.centroid() {
+        // Extract coordinates from the centroid point
+        let x = centroid_point.x();
+        let y = centroid_point.y();
+
+        wkb_factory::wkb_point((x, y))
+            .map_err(|e| 
datafusion_common::error::DataFusionError::External(Box::new(e)))
+    } else {
+        // This should not happen for non-empty geometries - this indicates an 
error
+        exec_err!("Failed to compute centroid for geometry")
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rstest::rstest;
+    use sedona_functions::register::stubs::st_centroid_udf;
+    use sedona_schema::datatypes::{WKB_GEOMETRY, WKB_VIEW_GEOMETRY};
+    use sedona_testing::compare::assert_array_equal;
+    use sedona_testing::create::create_array;
+    use sedona_testing::testers::ScalarUdfTester;
+
+    use super::*;
+
+    #[rstest]
+    fn udf(#[values(WKB_GEOMETRY, WKB_VIEW_GEOMETRY)] sedona_type: SedonaType) 
{
+        let mut udf = st_centroid_udf();
+        udf.add_kernel(st_centroid_impl());
+        let tester = ScalarUdfTester::new(udf.into(), vec![sedona_type]);
+
+        assert_eq!(tester.return_type().unwrap(), WKB_GEOMETRY);
+
+        // Test with a polygon
+        let result = tester
+            .invoke_scalar("POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))")
+            .unwrap();
+        tester.assert_scalar_result_equals(result, "POINT (1 1)");
+
+        // Test with array
+        let input_wkt = vec![
+            Some("POINT(1 2)"),
+            None,
+            Some("POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))"),
+        ];
+        let result_array = tester.invoke_wkb_array(input_wkt).unwrap();
+        assert_array_equal(
+            &result_array,
+            &create_array(
+                &[Some("POINT (1 2)"), None, Some("POINT (1 1)")],
+                &WKB_GEOMETRY,
+            ),
+        );
+    }
+}
diff --git a/rust/sedona-geo/src/st_length.rs b/rust/sedona-geo/src/st_length.rs
new file mode 100644
index 0000000..b80869f
--- /dev/null
+++ b/rust/sedona-geo/src/st_length.rs
@@ -0,0 +1,115 @@
+// 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::sync::Arc;
+
+use arrow_array::builder::Float64Builder;
+use arrow_schema::DataType;
+use datafusion_common::error::Result;
+use datafusion_expr::ColumnarValue;
+use geo_generic_alg::algorithm::{line_measures::Euclidean, 
LengthMeasurableExt};
+use sedona_expr::scalar_udf::{ArgMatcher, ScalarKernelRef, SedonaScalarKernel};
+use sedona_functions::executor::WkbExecutor;
+use sedona_schema::datatypes::SedonaType;
+use wkb::reader::Wkb;
+
+/// ST_Length() implementation using [LengthMeasurableExt] with Euclidean 
metric
+pub fn st_length_impl() -> ScalarKernelRef {
+    Arc::new(STLength {})
+}
+
+#[derive(Debug)]
+struct STLength {}
+
+impl SedonaScalarKernel for STLength {
+    fn return_type(&self, args: &[SedonaType]) -> Result<Option<SedonaType>> {
+        let matcher = ArgMatcher::new(
+            vec![ArgMatcher::is_geometry()],
+            SedonaType::Arrow(DataType::Float64),
+        );
+
+        matcher.match_args(args)
+    }
+
+    fn invoke_batch(
+        &self,
+        arg_types: &[SedonaType],
+        args: &[ColumnarValue],
+    ) -> Result<ColumnarValue> {
+        let executor = WkbExecutor::new(arg_types, args);
+        let mut builder = 
Float64Builder::with_capacity(executor.num_iterations());
+        executor.execute_wkb_void(|maybe_wkb| {
+            match maybe_wkb {
+                Some(wkb) => {
+                    builder.append_value(invoke_scalar(&wkb)?);
+                }
+                _ => builder.append_null(),
+            }
+
+            Ok(())
+        })?;
+
+        executor.finish(Arc::new(builder.finish()))
+    }
+}
+
+fn invoke_scalar(wkb: &Wkb) -> Result<f64> {
+    Ok(wkb.length_ext(&Euclidean))
+}
+
+#[cfg(test)]
+mod tests {
+    use arrow_array::{create_array, ArrayRef};
+    use datafusion_common::scalar::ScalarValue;
+    use rstest::rstest;
+    use sedona_functions::register::stubs::st_length_udf;
+    use sedona_schema::datatypes::{WKB_GEOMETRY, WKB_VIEW_GEOMETRY};
+    use sedona_testing::testers::ScalarUdfTester;
+
+    use super::*;
+
+    #[rstest]
+    fn udf(#[values(WKB_GEOMETRY, WKB_VIEW_GEOMETRY)] sedona_type: SedonaType) 
{
+        let mut udf = st_length_udf();
+        udf.add_kernel(st_length_impl());
+        let tester = ScalarUdfTester::new(udf.into(), vec![sedona_type]);
+
+        assert_eq!(
+            tester.return_type().unwrap(),
+            SedonaType::Arrow(DataType::Float64)
+        );
+
+        // Test with a line string
+        assert_eq!(
+            tester
+                .invoke_wkb_scalar(Some("LINESTRING (0 0, 3 4)"))
+                .unwrap(),
+            ScalarValue::Float64(Some(5.0))
+        );
+
+        let input_wkt = vec![
+            Some("POINT(1 2)"), // Point should have 0 length
+            None,
+            Some("LINESTRING (0 0, 3 4)"),      // Should have length 5
+            Some("LINESTRING (0 0, 1 0, 1 1)"), // Should have length 2
+            Some("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))"), // Polygon should 
have 0 length per OGC standard
+        ];
+        let expected: ArrayRef =
+            create_array!(Float64, [Some(0.0), None, Some(5.0), Some(2.0), 
Some(0.0)]);
+        assert_eq!(&tester.invoke_wkb_array(input_wkt).unwrap(), &expected);
+    }
+}
diff --git a/rust/sedona-geometry/src/is_empty.rs 
b/rust/sedona-geometry/src/is_empty.rs
new file mode 100644
index 0000000..ad3f7d5
--- /dev/null
+++ b/rust/sedona-geometry/src/is_empty.rs
@@ -0,0 +1,151 @@
+// 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 crate::error::SedonaGeometryError;
+use geo_traits::{
+    GeometryCollectionTrait, GeometryTrait, LineStringTrait, 
MultiLineStringTrait, MultiPointTrait,
+    MultiPolygonTrait, PointTrait, PolygonTrait,
+};
+
+pub fn is_geometry_empty<G: GeometryTrait<T = f64>>(
+    geometry: &G,
+) -> Result<bool, SedonaGeometryError> {
+    match geometry.as_type() {
+        geo_traits::GeometryType::Point(point) => Ok(point.coord().is_none()),
+        geo_traits::GeometryType::LineString(linestring) => 
Ok(linestring.num_coords() == 0),
+        geo_traits::GeometryType::Polygon(polygon) => {
+            Ok(polygon.num_interiors() == 0 && polygon.exterior().is_none())
+        }
+        geo_traits::GeometryType::MultiPoint(multipoint) => 
Ok(multipoint.num_points() == 0),
+        geo_traits::GeometryType::MultiLineString(multilinestring) => {
+            Ok(multilinestring.num_line_strings() == 0)
+        }
+        geo_traits::GeometryType::MultiPolygon(multipolygon) => {
+            Ok(multipolygon.num_polygons() == 0)
+        }
+        geo_traits::GeometryType::GeometryCollection(geometrycollection) => {
+            Ok(geometrycollection.num_geometries() == 0)
+        }
+        _ => Err(SedonaGeometryError::Invalid(
+            "Invalid geometry type".to_string(),
+        )),
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use std::str::FromStr;
+    use wkb::reader::read_wkb;
+    use wkb::writer::write_geometry;
+    use wkt::Wkt;
+
+    fn create_wkb_bytes_from_wkt(wkt_str: &str) -> Vec<u8> {
+        let wkt: Wkt = Wkt::from_str(wkt_str).unwrap();
+        let mut wkb_bytes = vec![];
+        write_geometry(&mut wkb_bytes, &wkt, 
wkb::Endianness::LittleEndian).unwrap();
+        wkb_bytes
+    }
+
+    #[test]
+    fn test_is_geometry_empty_points() {
+        // Empty point
+        let empty_point_bytes = create_wkb_bytes_from_wkt("POINT EMPTY");
+        let empty_point = read_wkb(&empty_point_bytes).unwrap();
+        assert!(is_geometry_empty(&empty_point).unwrap());
+
+        // Non-empty point
+        let point_bytes = create_wkb_bytes_from_wkt("POINT (1 2)");
+        let point = read_wkb(&point_bytes).unwrap();
+        assert!(!is_geometry_empty(&point).unwrap());
+    }
+
+    #[test]
+    fn test_is_geometry_empty_linestrings() {
+        // Empty linestring
+        let empty_linestring_bytes = create_wkb_bytes_from_wkt("LINESTRING 
EMPTY");
+        let empty_linestring = read_wkb(&empty_linestring_bytes).unwrap();
+        assert!(is_geometry_empty(&empty_linestring).unwrap());
+
+        // Non-empty linestring
+        let linestring_bytes = create_wkb_bytes_from_wkt("LINESTRING (1 2, 2 
2)");
+        let linestring = read_wkb(&linestring_bytes).unwrap();
+        assert!(!is_geometry_empty(&linestring).unwrap());
+    }
+
+    #[test]
+    fn test_is_geometry_empty_polygons() {
+        // Empty polygon
+        let empty_polygon_bytes = create_wkb_bytes_from_wkt("POLYGON EMPTY");
+        let empty_polygon = read_wkb(&empty_polygon_bytes).unwrap();
+        assert!(is_geometry_empty(&empty_polygon).unwrap());
+
+        // Non-empty polygon
+        let polygon_bytes = create_wkb_bytes_from_wkt("POLYGON ((0 0, 1 0, 1 
1, 0 1, 0 0))");
+        let polygon = read_wkb(&polygon_bytes).unwrap();
+        assert!(!is_geometry_empty(&polygon).unwrap());
+    }
+
+    #[test]
+    fn test_is_geometry_empty_multigeometries() {
+        // Empty multipoint
+        let empty_multipoint_bytes = create_wkb_bytes_from_wkt("MULTIPOINT 
EMPTY");
+        let empty_multipoint = read_wkb(&empty_multipoint_bytes).unwrap();
+        assert!(is_geometry_empty(&empty_multipoint).unwrap());
+
+        // Non-empty multipoint
+        let multipoint_bytes = create_wkb_bytes_from_wkt("MULTIPOINT ((0 0))");
+        let multipoint = read_wkb(&multipoint_bytes).unwrap();
+        assert!(!is_geometry_empty(&multipoint).unwrap());
+
+        // Empty multilinestring
+        let empty_multilinestring_bytes = 
create_wkb_bytes_from_wkt("MULTILINESTRING EMPTY");
+        let empty_multilinestring = 
read_wkb(&empty_multilinestring_bytes).unwrap();
+        assert!(is_geometry_empty(&empty_multilinestring).unwrap());
+
+        // Non-empty multilinestring
+        let multilinestring_bytes =
+            create_wkb_bytes_from_wkt("MULTILINESTRING ((0 0, 1 0), (1 1, 0 
1))");
+        let multilinestring = read_wkb(&multilinestring_bytes).unwrap();
+        assert!(!is_geometry_empty(&multilinestring).unwrap());
+
+        // Empty multipolygon
+        let empty_multipolygon_bytes = create_wkb_bytes_from_wkt("MULTIPOLYGON 
EMPTY");
+        let empty_multipolygon = read_wkb(&empty_multipolygon_bytes).unwrap();
+        assert!(is_geometry_empty(&empty_multipolygon).unwrap());
+
+        // Non-empty multipolygon
+        let multipolygon_bytes =
+            create_wkb_bytes_from_wkt("MULTIPOLYGON (((0 0, 1 0, 1 1, 0 1, 0 
0)))");
+        let multipolygon = read_wkb(&multipolygon_bytes).unwrap();
+        assert!(!is_geometry_empty(&multipolygon).unwrap());
+    }
+
+    #[test]
+    fn test_is_geometry_empty_collections() {
+        // Empty collection
+        let empty_collection_bytes = 
create_wkb_bytes_from_wkt("GEOMETRYCOLLECTION EMPTY");
+        let empty_collection = read_wkb(&empty_collection_bytes).unwrap();
+        assert!(is_geometry_empty(&empty_collection).unwrap());
+
+        // Non-empty collection
+        let collection_bytes =
+            create_wkb_bytes_from_wkt("GEOMETRYCOLLECTION (POINT (1 2), 
LINESTRING (1 2, 2 2))");
+        let collection = read_wkb(&collection_bytes).unwrap();
+        assert!(!is_geometry_empty(&collection).unwrap());
+    }
+}
diff --git a/rust/sedona-geometry/src/lib.rs b/rust/sedona-geometry/src/lib.rs
index 14fd096..65cc593 100644
--- a/rust/sedona-geometry/src/lib.rs
+++ b/rust/sedona-geometry/src/lib.rs
@@ -19,6 +19,7 @@ pub mod bounding_box;
 pub mod bounds;
 pub mod error;
 pub mod interval;
+pub mod is_empty;
 pub mod point_count;
 pub mod transform;
 pub mod types;

Reply via email to