petern48 commented on code in PR #183:
URL: https://github.com/apache/sedona-db/pull/183#discussion_r2404668960


##########
rust/sedona-functions/src/st_azimuth.rs:
##########
@@ -0,0 +1,66 @@
+// 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 arrow_schema::DataType;
+use datafusion_expr::{scalar_doc_sections::DOC_SECTION_OTHER, Documentation, 
Volatility};
+use sedona_expr::scalar_udf::SedonaScalarUDF;
+use sedona_schema::{datatypes::SedonaType, matchers::ArgMatcher};
+
+/// ST_Azimuth() scalar UDF stub
+///
+/// Stub function for azimuth calculation between two points.
+pub fn st_azimuth_udf() -> SedonaScalarUDF {
+    SedonaScalarUDF::new_stub(
+        "st_azimuth",
+        ArgMatcher::new(
+            vec![
+                ArgMatcher::is_geometry_or_geography(),
+                ArgMatcher::is_geometry_or_geography(),
+            ],
+            SedonaType::Arrow(DataType::Float64),
+        ),
+        Volatility::Immutable,
+        Some(st_azimuth_doc()),
+    )
+}
+
+fn st_azimuth_doc() -> Documentation {
+    Documentation::builder(
+        DOC_SECTION_OTHER,
+        "Returns the azimuth (a clockwise angle measured from north) in 
radians from geomA to geomB",
+        "ST_Azimuth (A: Geometry, B: Geometry)",
+    )
+    .with_argument("geomA", "geometry: Start point geometry")
+    .with_argument("geomB", "geometry: End point geometry")
+    .with_sql_example(
+        "SELECT ST_Azimuth(\n            ST_Point(0, 0),\n            
ST_Point(1, 1)\n        )",

Review Comment:
   ```suggestion
           "SELECT ST_Azimuth(ST_Point(0, 0), ST_Point(1, 1))",
   ```
   Let's have the example all on one line, like we how it is in the rest of the 
other functions.



##########
rust/sedona-functions/src/st_azimuth.rs:
##########
@@ -0,0 +1,66 @@
+// 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 arrow_schema::DataType;
+use datafusion_expr::{scalar_doc_sections::DOC_SECTION_OTHER, Documentation, 
Volatility};
+use sedona_expr::scalar_udf::SedonaScalarUDF;
+use sedona_schema::{datatypes::SedonaType, matchers::ArgMatcher};
+
+/// ST_Azimuth() scalar UDF stub
+///
+/// Stub function for azimuth calculation between two points.
+pub fn st_azimuth_udf() -> SedonaScalarUDF {
+    SedonaScalarUDF::new_stub(
+        "st_azimuth",
+        ArgMatcher::new(
+            vec![
+                ArgMatcher::is_geometry_or_geography(),
+                ArgMatcher::is_geometry_or_geography(),

Review Comment:
   ```suggestion
                   ArgMatcher::is_geometry(),
                   ArgMatcher::is_geometry(),
   ```
   
   Then, we can reduce this to geometry only for now, since we don't want to 
call the geometry implementation on geography objects.



##########
rust/sedona-functions/src/st_azimuth.rs:
##########
@@ -0,0 +1,66 @@
+// 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 arrow_schema::DataType;
+use datafusion_expr::{scalar_doc_sections::DOC_SECTION_OTHER, Documentation, 
Volatility};
+use sedona_expr::scalar_udf::SedonaScalarUDF;
+use sedona_schema::{datatypes::SedonaType, matchers::ArgMatcher};
+
+/// ST_Azimuth() scalar UDF stub
+///
+/// Stub function for azimuth calculation between two points.
+pub fn st_azimuth_udf() -> SedonaScalarUDF {
+    SedonaScalarUDF::new_stub(

Review Comment:
   Since `ST_Azimuth` is simple enough to be implemented manually, we should 
move the entire implementation that you wrote inside of `sedona-geo` to this 
file in `sedona-functions`. No need for this stub function. `sedona-geo` is 
really for functions where we need to use the `geo_generic_alg` package to call 
more complicated algorithms. You can see an example implementation in 
[st_isempty.rs](https://github.com/apache/sedona-db/blob/main/rust/sedona-functions/src/st_isempty.rs).
 It should be as simple as copy-pasting over the implementation and modifying 
this function a bit to use `SedonaScalarUDF::new` instead of 
`SedonaScalarUDF::new_stub`.



##########
rust/sedona-geo/src/st_azimuth.rs:
##########
@@ -0,0 +1,174 @@
+// 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_traits::{CoordTrait, GeometryTrait, GeometryType, PointTrait};
+use sedona_expr::scalar_udf::{ScalarKernelRef, SedonaScalarKernel};
+use sedona_functions::executor::WkbExecutor;
+use sedona_schema::{datatypes::SedonaType, matchers::ArgMatcher};
+use wkb::reader::Wkb;
+
+/// ST_Azimuth() implementation
+pub fn st_azimuth_impl() -> ScalarKernelRef {
+    Arc::new(STAzimuth {})
+}
+
+#[derive(Debug)]
+struct STAzimuth {}
+
+impl SedonaScalarKernel for STAzimuth {
+    fn return_type(&self, args: &[SedonaType]) -> Result<Option<SedonaType>> {
+        let matcher = ArgMatcher::new(
+            vec![ArgMatcher::is_geometry(), 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_wkb_void(|maybe_start, maybe_end| {
+            match (maybe_start, maybe_end) {
+                (Some(start), Some(end)) => match invoke_scalar(start, end)? {
+                    Some(angle) => builder.append_value(angle),
+                    None => builder.append_null(),
+                },
+                _ => builder.append_null(),
+            }
+
+            Ok(())
+        })?;
+
+        executor.finish(Arc::new(builder.finish()))
+    }
+}
+
+fn invoke_scalar(start: &Wkb, end: &Wkb) -> Result<Option<f64>> {
+    match (start.as_type(), end.as_type()) {
+        (GeometryType::Point(start_point), GeometryType::Point(end_point)) => {
+            match (start_point.coord(), end_point.coord()) {
+                // If both geometries are non-empty points, calculate the angle
+                (Some(start_coord), Some(end_coord)) => Ok(calc_azimuth(
+                    start_coord.x(),
+                    start_coord.y(),
+                    end_coord.x(),
+                    end_coord.y(),
+                )),
+                // If either of the points is empty, the result is NULL
+                _ => Ok(None),
+            }
+        }
+        _ => Err(datafusion_common::error::DataFusionError::Execution(
+            "ST_Azimuth expects both arguments to be POINT geometries".into(),
+        )),
+    }
+}
+
+// Note: When the two points are completely coincident, PostGIS's ST_Azimuth()
+//       returns NULL. However, this returns 0.0.

Review Comment:
   Let's definitely add a test for this case.
   
   But first, we should confirm what the desired behavior is. I suspect this is 
a just something that was missed in the original Sedona, and maybe we should 
follow PostGIS behavior here instead. There doesn't seem to be any discussion 
about this in the [original Sedona 
PR](https://github.com/apache/sedona/pull/449). @jiayuasu was this difference 
intentional or maybe just something that was missed? Shall we fix it in Sedona 
as well?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to