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(¢roid_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;