Skip to content

Commit 2146954

Browse files
osipovartemeadgbear
authored andcommitted
Add ST_DIMENSION (#321)
* Add ST_MakePolygon, st_polygon * Add docs * Add ST_dimention * Add license
1 parent ab7a91e commit 2146954

File tree

5 files changed

+254
-1
lines changed

5 files changed

+254
-1
lines changed
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
use std::any::Any;
19+
use std::sync::{Arc, OnceLock};
20+
21+
use crate::datafusion::functions::geospatial::data_types::{
22+
any_single_geometry_type_input, parse_to_native_array,
23+
};
24+
use arrow::array::UInt8Builder;
25+
use arrow_schema::DataType;
26+
use datafusion::logical_expr::scalar_doc_sections::DOC_SECTION_OTHER;
27+
use datafusion::logical_expr::{ColumnarValue, Documentation, ScalarUDFImpl, Signature};
28+
use datafusion_common::{DataFusionError, Result};
29+
use geoarrow::array::AsNativeArray;
30+
use geoarrow::datatypes::NativeType;
31+
use geoarrow::scalar::Geometry;
32+
use geoarrow::trait_::ArrayAccessor;
33+
34+
#[derive(Debug)]
35+
pub struct GeomDimension {
36+
signature: Signature,
37+
}
38+
39+
impl GeomDimension {
40+
pub fn new() -> Self {
41+
Self {
42+
signature: any_single_geometry_type_input(),
43+
}
44+
}
45+
}
46+
47+
static DOCUMENTATION: OnceLock<Documentation> = OnceLock::new();
48+
49+
impl ScalarUDFImpl for GeomDimension {
50+
fn as_any(&self) -> &dyn Any {
51+
self
52+
}
53+
54+
fn name(&self) -> &'static str {
55+
"st_dimension"
56+
}
57+
58+
fn signature(&self) -> &Signature {
59+
&self.signature
60+
}
61+
62+
fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
63+
Ok(DataType::UInt8)
64+
}
65+
66+
fn invoke(&self, args: &[ColumnarValue]) -> Result<ColumnarValue> {
67+
dim_impl(args)
68+
}
69+
70+
fn documentation(&self) -> Option<&Documentation> {
71+
Some(DOCUMENTATION.get_or_init(|| {
72+
Documentation::builder(
73+
DOC_SECTION_OTHER,
74+
"Return the coordinate dimension of the geometry value.",
75+
"ST_Dimension(geometry)",
76+
)
77+
.with_argument("g1", "geometry")
78+
.build()
79+
}))
80+
}
81+
}
82+
83+
macro_rules! build_output_array {
84+
($value:expr, $size:expr) => {{
85+
let mut output_array = UInt8Builder::with_capacity($size);
86+
for _ in 0..$size {
87+
output_array.append_value($value);
88+
}
89+
Ok(ColumnarValue::Array(Arc::new(output_array.finish())))
90+
}};
91+
}
92+
93+
fn dim_impl(args: &[ColumnarValue]) -> Result<ColumnarValue> {
94+
let array = ColumnarValue::values_to_arrays(args)?
95+
.into_iter()
96+
.next()
97+
.ok_or_else(|| {
98+
DataFusionError::Execution("Expected only one argument in ST_Dimension".to_string())
99+
})?;
100+
101+
let native_array = parse_to_native_array(&array)?;
102+
let native_array_ref = native_array.as_ref();
103+
let array_size = native_array_ref.len();
104+
105+
match native_array.data_type() {
106+
NativeType::Point(_, _) | NativeType::MultiPoint(_, _) => {
107+
build_output_array!(0, array_size)
108+
}
109+
NativeType::LineString(_, _) | NativeType::MultiLineString(_, _) => {
110+
build_output_array!(1, array_size)
111+
}
112+
NativeType::Polygon(_, _) | NativeType::MultiPolygon(_, _) | NativeType::Rect(_) => {
113+
build_output_array!(2, array_size)
114+
}
115+
NativeType::Geometry(_) | NativeType::GeometryCollection(_, _) => {
116+
let array_ref = native_array.as_ref();
117+
let arr = array_ref.as_geometry();
118+
let mut output_array = UInt8Builder::with_capacity(native_array.len());
119+
for geom in arr.iter() {
120+
let dim = match geom {
121+
Some(g) => match g {
122+
Geometry::Point(_) | Geometry::MultiPoint(_) => 0,
123+
Geometry::LineString(_) | Geometry::MultiLineString(_) => 1,
124+
Geometry::Polygon(_) | Geometry::MultiPolygon(_) | Geometry::Rect(_) => 2,
125+
Geometry::GeometryCollection(_) => {
126+
return Err(DataFusionError::Execution(
127+
"Unsupported geometry type".to_string(),
128+
))
129+
}
130+
},
131+
None => {
132+
return Err(DataFusionError::Execution(
133+
"Null geometry found".to_string(),
134+
))
135+
}
136+
};
137+
output_array.append_value(dim);
138+
}
139+
Ok(ColumnarValue::Array(Arc::new(output_array.finish())))
140+
}
141+
}
142+
}
143+
144+
#[cfg(test)]
145+
mod tests {
146+
use super::*;
147+
use arrow_array::cast::AsArray;
148+
use arrow_array::types::UInt8Type;
149+
use arrow_array::ArrayRef;
150+
use datafusion::logical_expr::ColumnarValue;
151+
use geo_types::{line_string, point, polygon};
152+
use geoarrow::array::LineStringBuilder;
153+
use geoarrow::array::{CoordType, PointBuilder, PolygonBuilder};
154+
use geoarrow::datatypes::Dimension;
155+
use geoarrow::ArrayBase;
156+
157+
#[test]
158+
#[allow(clippy::unwrap_used)]
159+
fn test_dim() {
160+
let dim = Dimension::XY;
161+
let ct = CoordType::Separated;
162+
163+
let args: [(ArrayRef, u8); 3] = [
164+
(
165+
{
166+
let data = vec![
167+
line_string![(x: 0., y: 0.), (x: 1., y: 0.), (x: 1., y: 1.), (x: 0., y: 1.), (x: 0., y: 0.)],
168+
line_string![(x: 2., y: 2.), (x: 3., y: 2.), (x: 3., y: 3.), (x: 2., y: 3.), (x: 2., y: 2.)],
169+
];
170+
let array =
171+
LineStringBuilder::from_line_strings(&data, dim, ct, Arc::default())
172+
.finish();
173+
array.to_array_ref()
174+
},
175+
1,
176+
),
177+
(
178+
{
179+
let data = [point! {x: 0., y: 0.}, point! {x: 1., y: 1.}];
180+
let array =
181+
PointBuilder::from_points(data.iter(), dim, ct, Arc::default()).finish();
182+
array.to_array_ref()
183+
},
184+
0,
185+
),
186+
(
187+
{
188+
let data = vec![
189+
polygon![(x: 3.3, y: 30.4), (x: 1.7, y: 24.6), (x: 13.4, y: 25.1), (x: 14.4, y: 31.0),(x:3.3,y:30.4)],
190+
polygon![(x: 3.3, y: 30.4), (x: 1.7, y: 24.6), (x: 13.4, y: 25.1), (x: 14.4, y: 31.0),(x:3.3,y:30.4)],
191+
];
192+
let array =
193+
PolygonBuilder::from_polygons(&data, dim, ct, Arc::default()).finish();
194+
array.to_array_ref()
195+
},
196+
2,
197+
),
198+
];
199+
200+
for (array, exp) in args {
201+
let args = vec![ColumnarValue::Array(array.clone())];
202+
let dim_fn = GeomDimension::new();
203+
let result = dim_fn.invoke_batch(&args, 2).unwrap().to_array(2).unwrap();
204+
let result = result.as_primitive::<UInt8Type>();
205+
assert_eq!(result.value(0), exp);
206+
}
207+
}
208+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
mod dim;
19+
20+
use datafusion::prelude::SessionContext;
21+
22+
/// Register all provided [geo] functions for constructing geometries
23+
pub fn register_udfs(ctx: &SessionContext) {
24+
ctx.register_udf(dim::GeomDimension::new().into());
25+
}

crates/runtime/src/datafusion/functions/geospatial/constructors/line_string.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ impl ScalarUDFImpl for MakeLine {
9595
Some(DOCUMENTATION.get_or_init(|| {
9696
Documentation::builder(
9797
DOC_SECTION_OTHER,
98-
"Returns a geometry t that represents a line connecting the points in the input objects.",
98+
"Returns a geometry that represents a line connecting the points in the input objects.",
9999
"ST_MakeLine(ST_POINT(-71.104, 42.315), ST_POINT(-71.103, 42.312))",
100100
)
101101
.with_related_udf("st_makeline")

crates/runtime/src/datafusion/functions/geospatial/data_types.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use crate::datafusion::functions::geospatial::error::{
2222
};
2323
use arrow_array::ArrayRef;
2424
use datafusion::error::DataFusionError;
25+
use datafusion::logical_expr::{Signature, Volatility};
2526
use geoarrow::array::{
2627
CoordType, GeometryArray, LineStringArray, PointArray, PolygonArray, RectArray,
2728
};
@@ -38,6 +39,23 @@ pub const LINE_STRING_TYPE: NativeType =
3839
NativeType::LineString(CoordType::Separated, Dimension::XY);
3940
pub const POLYGON_2D_TYPE: NativeType = NativeType::Polygon(CoordType::Separated, Dimension::XY);
4041

42+
#[must_use]
43+
pub fn any_single_geometry_type_input() -> Signature {
44+
Signature::uniform(
45+
1,
46+
vec![
47+
POINT2D_TYPE.into(),
48+
POINT3D_TYPE.into(),
49+
BOX2D_TYPE.into(),
50+
BOX3D_TYPE.into(),
51+
LINE_STRING_TYPE.into(),
52+
POLYGON_2D_TYPE.into(),
53+
GEOMETRY_TYPE.into(),
54+
],
55+
Volatility::Immutable,
56+
)
57+
}
58+
4159
/// This will not cast a `PointArray` to a `GeometryArray`
4260
pub fn parse_to_native_array(array: &ArrayRef) -> GeoDataFusionResult<Arc<dyn NativeArray>> {
4361
let data_type = array.data_type();

crates/runtime/src/datafusion/functions/geospatial/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18+
pub mod accessors;
1819
pub mod constructors;
1920
pub mod data_types;
2021
pub mod error;
@@ -23,4 +24,5 @@ use datafusion::prelude::SessionContext;
2324

2425
pub fn register_udfs(ctx: &SessionContext) {
2526
constructors::register_udfs(ctx);
27+
accessors::register_udfs(ctx);
2628
}

0 commit comments

Comments
 (0)