You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sedona.apache.org by pa...@apache.org on 2026/06/24 04:53:15 UTC
(sedona-db) branch main updated: feat(rust/sedona-functions): add ST_NumPoints alias for ST_NPoints (#989)
This is an automated email from the ASF dual-hosted git repository.
paleolimbot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/sedona-db.git
The following commit(s) were added to refs/heads/main by this push:
new 7e8cb1c8b feat(rust/sedona-functions): add ST_NumPoints alias for ST_NPoints (#989)
7e8cb1c8b is described below
commit 7e8cb1c8b21687255ca89f6b2b69e044d62ba3e3
Author: ajaypadwal73 <51...@users.noreply.github.com>
AuthorDate: Wed Jun 24 10:23:10 2026 +0530
feat(rust/sedona-functions): add ST_NumPoints alias for ST_NPoints (#989)
---
c/sedona-geos/src/lib.rs | 1 -
c/sedona-geos/src/register.rs | 1 -
c/sedona-geos/src/st_numpoints.rs | 146 ---------------------
docs/reference/sql/st_npoints.qmd | 2 +
python/sedonadb/tests/functions/test_functions.py | 40 ++----
.../geography/test_geog_mechanical_transforms.py | 20 +--
rust/sedona-functions/src/st_points.rs | 7 +
7 files changed, 27 insertions(+), 190 deletions(-)
diff --git a/c/sedona-geos/src/lib.rs b/c/sedona-geos/src/lib.rs
index 9973f4437..29c4d293b 100644
--- a/c/sedona-geos/src/lib.rs
+++ b/c/sedona-geos/src/lib.rs
@@ -41,7 +41,6 @@ mod st_minimumclearance_line;
mod st_normalize;
mod st_nrings;
mod st_numinteriorrings;
-mod st_numpoints;
mod st_perimeter;
mod st_polygonize;
mod st_polygonize_agg;
diff --git a/c/sedona-geos/src/register.rs b/c/sedona-geos/src/register.rs
index bc48a14eb..390d7420e 100644
--- a/c/sedona-geos/src/register.rs
+++ b/c/sedona-geos/src/register.rs
@@ -70,7 +70,6 @@ pub fn scalar_kernels() -> Vec<(&'static str, Vec<ScalarKernelRef>)> {
"st_normalize" => crate::st_normalize::st_normalize_impl,
"st_nrings" => crate::st_nrings::st_nrings_impl,
"st_numinteriorrings" => crate::st_numinteriorrings::st_num_interior_rings_impl,
- "st_numpoints" => crate::st_numpoints::st_num_points_impl,
"st_overlaps" => crate::binary_predicates::st_overlaps_impl,
"st_perimeter" => crate::st_perimeter::st_perimeter_impl,
"st_polygonize" => crate::st_polygonize::st_polygonize_impl,
diff --git a/c/sedona-geos/src/st_numpoints.rs b/c/sedona-geos/src/st_numpoints.rs
deleted file mode 100644
index 4f65435e4..000000000
--- a/c/sedona-geos/src/st_numpoints.rs
+++ /dev/null
@@ -1,146 +0,0 @@
-// 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 crate::executor::GeosExecutor;
-use arrow_array::builder::Int32Builder;
-use arrow_schema::DataType;
-use datafusion_common::{error::Result, DataFusionError};
-use datafusion_expr::ColumnarValue;
-use geos::{Geom, Geometry, GeometryTypes};
-use sedona_expr::{
- item_crs::ItemCrsKernel,
- scalar_udf::{ScalarKernelRef, SedonaScalarKernel},
-};
-use sedona_schema::{datatypes::SedonaType, matchers::ArgMatcher};
-
-pub fn st_num_points_impl() -> Vec<ScalarKernelRef> {
- ItemCrsKernel::wrap_impl(STNumPoints {})
-}
-
-#[derive(Debug)]
-struct STNumPoints {}
-
-impl SedonaScalarKernel for STNumPoints {
- fn return_type(&self, args: &[SedonaType]) -> Result<Option<SedonaType>> {
- let matcher = ArgMatcher::new(
- vec![ArgMatcher::is_geometry_or_geography()],
- SedonaType::Arrow(DataType::Int32),
- );
-
- matcher.match_args(args)
- }
-
- fn invoke_batch(
- &self,
- arg_types: &[SedonaType],
- args: &[ColumnarValue],
- ) -> Result<ColumnarValue> {
- let executor = GeosExecutor::new(arg_types, args);
- let mut builder = Int32Builder::with_capacity(executor.num_iterations());
- executor.execute_wkb_void(|maybe_geom| {
- match maybe_geom {
- None => builder.append_null(),
- Some(geom) => {
- let res = invoke_scalar(&geom)?;
- match res {
- Some(n) => builder.append_value(n),
- None => builder.append_null(),
- }
- }
- }
- Ok(())
- })?;
-
- executor.finish(Arc::new(builder.finish()))
- }
-}
-
-fn invoke_scalar(geom: &Geometry) -> Result<Option<i32>> {
- match geom
- .geometry_type()
- .map_err(|e| DataFusionError::Execution(format!("Failed to get geometry type: {e}")))?
- {
- GeometryTypes::LineString => {
- let count = geom.get_num_points().map_err(|e| {
- DataFusionError::Execution(format!("Failed to get num points: {e}"))
- })?;
- Ok(Some(count as i32))
- }
- _ => Ok(None),
- }
-}
-
-#[cfg(test)]
-mod tests {
- use std::sync::Arc;
-
- use arrow_array::{ArrayRef, Int32Array};
- use arrow_schema::DataType;
- use datafusion_common::ScalarValue;
- use rstest::rstest;
- use sedona_expr::scalar_udf::SedonaScalarUDF;
- use sedona_schema::datatypes::{
- SedonaType, WKB_GEOGRAPHY, WKB_GEOGRAPHY_ITEM_CRS, WKB_GEOMETRY, WKB_GEOMETRY_ITEM_CRS,
- WKB_VIEW_GEOGRAPHY, WKB_VIEW_GEOMETRY,
- };
- use sedona_testing::compare::assert_array_equal;
- use sedona_testing::testers::ScalarUdfTester;
-
- use super::*;
-
- #[rstest]
- fn udf(
- #[values(WKB_GEOMETRY, WKB_VIEW_GEOMETRY, WKB_GEOGRAPHY, WKB_VIEW_GEOGRAPHY, WKB_GEOMETRY_ITEM_CRS.clone(), WKB_GEOGRAPHY_ITEM_CRS.clone())]
- sedona_type: SedonaType,
- ) {
- let udf = SedonaScalarUDF::from_impl("st_numpoints", st_num_points_impl());
- let tester = ScalarUdfTester::new(udf.into(), vec![sedona_type]);
- tester.assert_return_type(DataType::Int32);
- let result = tester.invoke_scalar("LINESTRING (1 2, 3 4)").unwrap();
- tester.assert_scalar_result_equals(result, 2_i32);
-
- let result = tester.invoke_scalar(ScalarValue::Null).unwrap();
- assert!(result.is_null());
-
- let input_wkt = vec![
- None,
- Some("POINT (1 2)"),
- Some("LINESTRING (0 0, 1 1, 2 2)"),
- Some("POLYGON EMPTY"),
- Some("LINESTRING(0 0, 1 1, 1 2, 2 2)"),
- Some("MULTILINESTRING ((0 0, 1 1), (2 2, 3 3))"),
- Some("POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))"),
- Some("GEOMETRYCOLLECTION (POINT (1 2), LINESTRING (0 0, 1 1))"),
- ];
-
- let expected: ArrayRef = Arc::new(Int32Array::from(vec![
- None,
- None,
- Some(3),
- None,
- Some(4),
- None,
- None,
- None,
- ]));
-
- let result = tester.invoke_wkb_array(input_wkt).unwrap();
- assert_array_equal(&result, &expected);
- }
-}
diff --git a/docs/reference/sql/st_npoints.qmd b/docs/reference/sql/st_npoints.qmd
index 9a7a3ea1d..f7543f94b 100644
--- a/docs/reference/sql/st_npoints.qmd
+++ b/docs/reference/sql/st_npoints.qmd
@@ -29,6 +29,8 @@ kernels:
Returns the total number of coordinate points in the geometry, counting all vertices across all components.
+Aliases: `ST_NumPoints`.
+
## Examples
```sql
diff --git a/python/sedonadb/tests/functions/test_functions.py b/python/sedonadb/tests/functions/test_functions.py
index cc3d9a6ad..77d9af1c0 100644
--- a/python/sedonadb/tests/functions/test_functions.py
+++ b/python/sedonadb/tests/functions/test_functions.py
@@ -2714,6 +2714,7 @@ def test_st_pointm(eng, x, y, m, expected):
],
)
def test_st_points(eng, geometry, expected, expected_n):
+ is_postgis = eng is PostGIS
eng = eng.create_or_skip()
eng.assert_query_result(
f"SELECT ST_Points({geom_or_null(geometry)})",
@@ -2723,6 +2724,13 @@ def test_st_points(eng, geometry, expected, expected_n):
f"SELECT ST_NPoints({geom_or_null(geometry)})",
expected_n,
)
+ if not is_postgis:
+ # ST_NumPoints is an alias for ST_NPoints in SedonaDB.
+ # PostGIS still treats ST_NumPoints as LineString-only despite documentation.
+ eng.assert_query_result(
+ f"SELECT ST_NumPoints({geom_or_null(geometry)})",
+ expected_n,
+ )
@pytest.mark.parametrize("eng", [SedonaDB, PostGIS])
@@ -3876,38 +3884,6 @@ def test_st_numinteriorrings_basic(eng, geom, expected):
)
-@pytest.mark.parametrize("eng", [SedonaDB, PostGIS])
-@pytest.mark.parametrize(
- ("geom", "expected"),
- [
- (None, None),
- ("POINT EMPTY", None),
- ("LINESTRING EMPTY", 0),
- ("POLYGON EMPTY", None),
- ("MULTIPOINT EMPTY", None),
- ("MULTILINESTRING EMPTY", None),
- ("MULTIPOLYGON EMPTY", None),
- ("GEOMETRYCOLLECTION EMPTY", None),
- ("POINT (1 2)", None),
- ("LINESTRING (0 0, 1 1, 2 2)", 3),
- ("LINESTRING (0 0, 1 1, 0 0)", 3),
- ("LINESTRING Z (0 0 0, 1 1 1, 2 2 2, 3 3 3)", 4),
- ("LINESTRING M (0 0 0, 1 1 1, 2 2 2, 3 3 3)", 4),
- ("LINESTRING ZM (0 0 0 2, 1 1 1 4)", 2),
- ("POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))", None),
- ("MULTILINESTRING ((0 0, 0 1, 1 1, 0 0),(0 0, 1 1))", None),
- ("GEOMETRYCOLLECTION (LINESTRING (0 0, 0 1, 1 1, 0 0))", None),
- ("POLYGON ((0 0,6 0,6 6,0 6,0 0),(2 2,4 2,4 4,2 4,2 2))", None),
- ],
-)
-def test_st_numpoints(eng, geom, expected):
- eng = eng.create_or_skip()
- eng.assert_query_result(
- f"SELECT ST_NumPoints({geom_or_null(geom)})",
- expected,
- )
-
-
@pytest.mark.parametrize("eng", [SedonaDB, PostGIS])
@pytest.mark.parametrize(
("geom", "expected"),
diff --git a/python/sedonadb/tests/geography/test_geog_mechanical_transforms.py b/python/sedonadb/tests/geography/test_geog_mechanical_transforms.py
index ba391a284..54ba4a75e 100644
--- a/python/sedonadb/tests/geography/test_geog_mechanical_transforms.py
+++ b/python/sedonadb/tests/geography/test_geog_mechanical_transforms.py
@@ -638,28 +638,28 @@ def test_st_numinteriorrings(eng, geog, expected):
("geog", "expected"),
[
pytest.param(None, None, id="null"),
- pytest.param("POINT EMPTY", None, id="point_empty"),
+ pytest.param("POINT EMPTY", 0, id="point_empty"),
pytest.param("LINESTRING EMPTY", 0, id="linestring_empty"),
- pytest.param("POLYGON EMPTY", None, id="polygon_empty"),
- pytest.param("MULTIPOINT EMPTY", None, id="multipoint_empty"),
- pytest.param("MULTILINESTRING EMPTY", None, id="multilinestring_empty"),
- pytest.param("MULTIPOLYGON EMPTY", None, id="multipolygon_empty"),
- pytest.param("GEOMETRYCOLLECTION EMPTY", None, id="geometrycollection_empty"),
- pytest.param("POINT (1 2)", None, id="point"),
+ pytest.param("POLYGON EMPTY", 0, id="polygon_empty"),
+ pytest.param("MULTIPOINT EMPTY", 0, id="multipoint_empty"),
+ pytest.param("MULTILINESTRING EMPTY", 0, id="multilinestring_empty"),
+ pytest.param("MULTIPOLYGON EMPTY", 0, id="multipolygon_empty"),
+ pytest.param("GEOMETRYCOLLECTION EMPTY", 0, id="geometrycollection_empty"),
+ pytest.param("POINT (1 2)", 1, id="point"),
pytest.param("LINESTRING (0 0, 1 1, 2 2)", 3, id="linestring"),
pytest.param("LINESTRING (0 0, 1 1, 0 0)", 3, id="linestring_closed"),
pytest.param("LINESTRING Z (0 0 0, 1 1 1, 2 2 2, 3 3 3)", 4, id="linestring_z"),
pytest.param("LINESTRING M (0 0 0, 1 1 1, 2 2 2, 3 3 3)", 4, id="linestring_m"),
pytest.param("LINESTRING ZM (0 0 0 2, 1 1 1 4)", 2, id="linestring_zm"),
- pytest.param("POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))", None, id="polygon"),
+ pytest.param("POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))", 5, id="polygon"),
pytest.param(
"MULTILINESTRING ((0 0, 0 1, 1 1, 0 0),(0 0, 1 1))",
- None,
+ 6,
id="multilinestring",
),
pytest.param(
"GEOMETRYCOLLECTION (LINESTRING (0 0, 0 1, 1 1, 0 0))",
- None,
+ 4,
id="geometrycollection",
),
],
diff --git a/rust/sedona-functions/src/st_points.rs b/rust/sedona-functions/src/st_points.rs
index 15c98e711..ca928eabf 100644
--- a/rust/sedona-functions/src/st_points.rs
+++ b/rust/sedona-functions/src/st_points.rs
@@ -115,6 +115,7 @@ pub fn st_npoints_udf() -> SedonaScalarUDF {
ItemCrsKernel::wrap_impl(vec![Arc::new(STNPoints)]),
Volatility::Immutable,
)
+ .with_aliases(vec!["st_numpoints".to_string()])
}
#[derive(Debug)]
@@ -296,6 +297,12 @@ mod tests {
assert!(st_npoints_udf.documentation().is_none());
}
+ #[test]
+ fn npoints_aliases() {
+ let udf: ScalarUDF = st_npoints_udf().into();
+ assert!(udf.aliases().contains(&"st_numpoints".to_string()));
+ }
+
#[rstest]
fn udf(#[values(WKB_GEOMETRY, WKB_GEOGRAPHY)] sedona_type: SedonaType) {
use arrow_array::UInt64Array;