Skip to content

Commit 853e332

Browse files
committed
feat(expr-geo): add line_locate_point kernel (geo::LineLocatePoint)
1 parent f69a342 commit 853e332

2 files changed

Lines changed: 82 additions & 0 deletions

File tree

rust/geoarrow-expr-geo/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod distance;
1717
mod interior_point;
1818
mod intersects;
1919
mod length;
20+
mod line_locate_point;
2021
mod minimum_rotated_rect;
2122
mod relate;
2223
mod simplify;
@@ -38,6 +39,7 @@ pub use distance::{euclidean_distance, frechet_distance, hausdorff_distance};
3839
pub use interior_point::interior_point;
3940
pub use intersects::intersects;
4041
pub use length::{euclidean_length, geodesic_length, haversine_length, rhumb_length};
42+
pub use line_locate_point::line_locate_point;
4143
pub use minimum_rotated_rect::minimum_rotated_rect;
4244
pub use relate::relate_boolean;
4345
pub use simplify::{simplify, simplify_vw, simplify_vw_preserve};
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use arrow_array::Float64Array;
2+
use geo::LineLocatePoint;
3+
use geoarrow_array::{GeoArrowArray, GeoArrowArrayAccessor};
4+
use geoarrow_schema::error::GeoArrowResult;
5+
6+
use crate::util::downcast::downcast_geoarrow_array_two_args;
7+
use crate::util::{check_same_len, map_pair_to_f64};
8+
9+
/// Element-wise: locate each point along its paired line, as the fraction of the
10+
/// line's length at the nearest point on that line.
11+
///
12+
/// `geo` implements this for a `Line` or `LineString` with a `Point` argument only.
13+
/// Any other pairing gives a null row, which a caller cannot tell apart from a
14+
/// null input.
15+
pub fn line_locate_point(
16+
lines: &dyn GeoArrowArray,
17+
points: &dyn GeoArrowArray,
18+
) -> GeoArrowResult<Float64Array> {
19+
check_same_len(lines, points)?;
20+
downcast_geoarrow_array_two_args!(lines, points, line_locate_point_impl)
21+
}
22+
23+
fn line_locate_point_impl<'a>(
24+
lines: &'a impl GeoArrowArrayAccessor<'a>,
25+
points: &'a impl GeoArrowArrayAccessor<'a>,
26+
) -> GeoArrowResult<Float64Array> {
27+
map_pair_to_f64(lines, points, |line, point| match (line, point) {
28+
(geo::Geometry::LineString(line), geo::Geometry::Point(point)) => {
29+
line.line_locate_point(point)
30+
}
31+
(geo::Geometry::Line(line), geo::Geometry::Point(point)) => line.line_locate_point(point),
32+
_ => None,
33+
})
34+
}
35+
36+
#[cfg(test)]
37+
mod test {
38+
use arrow_array::Array;
39+
use geo::{Geometry, LineString, point};
40+
41+
use super::*;
42+
use crate::test_util::geometry_array;
43+
44+
fn line(coords: Vec<(f64, f64)>) -> Option<Geometry> {
45+
Some(Geometry::LineString(LineString::from(coords)))
46+
}
47+
48+
#[test]
49+
fn locate_midpoint_and_nulls() {
50+
let lines = geometry_array(vec![
51+
line(vec![(0.0, 0.0), (10.0, 0.0)]),
52+
Some(Geometry::from(point!(x: 0., y: 0.))),
53+
None,
54+
line(vec![(1.0, 1.0), (1.0, 1.0)]),
55+
]);
56+
let points = geometry_array(vec![
57+
Some(Geometry::from(point!(x: 5., y: 5.))),
58+
Some(Geometry::from(point!(x: 1., y: 1.))),
59+
Some(Geometry::from(point!(x: 1., y: 1.))),
60+
Some(Geometry::from(point!(x: 5., y: 5.))),
61+
]);
62+
63+
let result = line_locate_point(&lines, &points).unwrap();
64+
assert_eq!(result.value(0), 0.5);
65+
assert!(result.is_null(1));
66+
assert!(result.is_null(2));
67+
// geo measures a zero-length line as Some(0.0), thus not a null row.
68+
assert_eq!(result.value(3), 0.0);
69+
}
70+
71+
#[test]
72+
fn length_mismatch_errors() {
73+
let lines = geometry_array(vec![line(vec![(0.0, 0.0), (1.0, 0.0)])]);
74+
let points = geometry_array(vec![
75+
Some(Geometry::from(point!(x: 0., y: 0.))),
76+
Some(Geometry::from(point!(x: 1., y: 1.))),
77+
]);
78+
assert!(line_locate_point(&lines, &points).is_err());
79+
}
80+
}

0 commit comments

Comments
 (0)