1#[allow(deprecated)]
5use crate::{
6 CoordFloat, Line, LineString, Point,
7 {euclidean_distance::EuclideanDistance, euclidean_length::EuclideanLength},
8};
9use std::ops::AddAssign;
10
11pub trait LineLocatePoint<T, Rhs> {
39 type Output;
40 type Rhs;
41
42 fn line_locate_point(&self, p: &Rhs) -> Self::Output;
43}
44
45impl<T> LineLocatePoint<T, Point<T>> for Line<T>
46where
47 T: CoordFloat,
48{
49 type Output = Option<T>;
50 type Rhs = Point<T>;
51
52 fn line_locate_point(&self, p: &Self::Rhs) -> Self::Output {
53 let sp: Point<_> = *p - self.start_point();
60
61 let v: Point<_> = (self.end - self.start).into();
63
64 let v_sq = v.dot(v);
66 if v_sq == T::zero() {
67 Some(T::zero())
69 } else {
70 let v_dot_sp = v.dot(sp);
72 let l = v_dot_sp / v_sq;
73 if l.is_finite() {
74 Some(l.max(T::zero()).min(T::one()))
75 } else {
76 None
77 }
78 }
79 }
80}
81
82#[allow(deprecated)]
83impl<T> LineLocatePoint<T, Point<T>> for LineString<T>
84where
85 T: CoordFloat + AddAssign,
86 Line<T>: EuclideanDistance<T, Point<T>> + EuclideanLength<T>,
87 LineString<T>: EuclideanLength<T>,
88{
89 type Output = Option<T>;
90 type Rhs = Point<T>;
91
92 fn line_locate_point(&self, p: &Self::Rhs) -> Self::Output {
93 let total_length = (*self).euclidean_length();
94 if total_length == T::zero() {
95 return Some(T::zero());
96 }
97 let mut cum_length = T::zero();
98 let mut closest_dist_to_point = T::infinity();
99 let mut fraction = T::zero();
100 for segment in self.lines() {
101 let segment_distance_to_point = segment.euclidean_distance(p);
102 let segment_length = segment.euclidean_length();
103 let segment_fraction = segment.line_locate_point(p)?; if segment_distance_to_point < closest_dist_to_point {
105 closest_dist_to_point = segment_distance_to_point;
106 fraction = (cum_length + segment_fraction * segment_length) / total_length;
107 }
108 cum_length += segment_length;
109 }
110 Some(fraction)
111 }
112}
113
114#[cfg(test)]
115mod test {
116 use super::*;
117 use crate::{coord, point};
118 use num_traits::Float;
119
120 #[test]
121 fn test_line_locate_point_line() {
122 let line = Line::new(coord! { x: -1.0, y: 0.0 }, coord! { x: 1.0, y: 0.0 });
124 let point = Point::new(0.0, 1.0);
125 assert_eq!(line.line_locate_point(&point), Some(0.5));
126
127 let point = Point::new(1.0, 1.0);
128 assert_eq!(line.line_locate_point(&point), Some(1.0));
129
130 let point = Point::new(2.0, 1.0);
131 assert_eq!(line.line_locate_point(&point), Some(1.0));
132
133 let point = Point::new(-1.0, 1.0);
134 assert_eq!(line.line_locate_point(&point), Some(0.0));
135
136 let point = Point::new(-2.0, 1.0);
137 assert_eq!(line.line_locate_point(&point), Some(0.0));
138
139 let point = Point::new(Float::nan(), 1.0);
141 assert_eq!(line.line_locate_point(&point), None);
142
143 let point = Point::new(Float::infinity(), 1.0);
144 assert_eq!(line.line_locate_point(&point), None);
145
146 let point = Point::new(Float::neg_infinity(), 1.0);
147 assert_eq!(line.line_locate_point(&point), None);
148
149 let line = Line::new(
151 coord! { x: 0.0, y: 0.0 },
152 coord! {
153 x: Float::infinity(),
154 y: 0.0,
155 },
156 );
157 let point = Point::new(1000.0, 1000.0);
158 assert_eq!(line.line_locate_point(&point), None);
159
160 let line = Line::new(
161 coord! { x: 0.0, y: 0.0 },
162 coord! {
163 x: Float::neg_infinity(),
164 y: 0.0,
165 },
166 );
167 let point = Point::new(1000.0, 1000.0);
168 assert_eq!(line.line_locate_point(&point), None);
169
170 let line = Line::new(
171 coord! { x: 0.0, y: 0.0 },
172 coord! {
173 x: Float::nan(),
174 y: 0.0,
175 },
176 );
177 let point = Point::new(1000.0, 1000.0);
178 assert_eq!(line.line_locate_point(&point), None);
179
180 let line: Line = Line::new(coord! { x: 1.0, y: 1.0 }, coord! { x: 1.0, y: 1.0 });
182 let pt = point!(x: 2.0, y: 2.0);
183 assert_eq!(line.line_locate_point(&pt), Some(0.0));
184
185 let line: Line = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 10.0, y: 0.0 });
187 let pt = Point::new(555.0, 555.0);
188 assert_eq!(line.line_locate_point(&pt), Some(1.0));
189 let pt = Point::new(10.0000001, 0.0);
190 assert_eq!(line.line_locate_point(&pt), Some(1.0));
191 let pt = Point::new(9.0, 0.001);
192 assert_eq!(line.line_locate_point(&pt), Some(0.9));
193 }
194
195 #[test]
196 fn test_line_locate_point_linestring() {
197 let ring: LineString = geo_test_fixtures::ring::<f64>();
199 let pt = point!(x: 10.0, y: 1.0);
200 assert_eq!(ring.line_locate_point(&pt), Some(0.0));
201
202 let pt = point!(x: 10.0, y: 1.0000000000000742);
203 assert_eq!(ring.line_locate_point(&pt), Some(0.9999999999999988));
204
205 let pt = point!(x: 10.0, y: 1.0);
206 assert_eq!(ring.line_locate_point(&pt), Some(0.0));
207
208 let pt = point!(x: Float::nan(), y: 1.0);
210 assert_eq!(ring.line_locate_point(&pt), None);
211
212 let pt = point!(x: Float::infinity(), y: 1.0);
213 assert_eq!(ring.line_locate_point(&pt), None);
214
215 let pt = point!(x: Float::neg_infinity(), y: 1.0);
216 assert_eq!(ring.line_locate_point(&pt), None);
217
218 let line: LineString = LineString::new(vec![
220 (0.0, 0.0).into(),
221 (1.0, 0.0).into(),
222 (1.0, 1.0).into(),
223 (0.0, 1.0).into(),
224 ]);
225 let pt = point!(x: 0.0, y: 0.5);
226 assert_eq!(line.line_locate_point(&pt), Some(0.0));
227
228 let line: LineString = LineString::new(vec![
229 (1.0, 1.0).into(),
230 (1.0, 1.0).into(),
231 (1.0, 1.0).into(),
232 ]);
233 let pt = point!(x: 2.0, y: 2.0);
234 assert_eq!(line.line_locate_point(&pt), Some(0.0));
235
236 let line: LineString = LineString::new(vec![
238 coord! { x: 1.0, y: 1.0 },
239 coord! {
240 x: Float::nan(),
241 y: 1.0,
242 },
243 coord! { x: 0.0, y: 0.0 },
244 ]);
245 let pt = point!(x: 2.0, y: 2.0);
246 assert_eq!(line.line_locate_point(&pt), None);
247
248 let line: LineString = LineString::new(vec![
249 coord! { x: 1.0, y: 1.0 },
250 coord! {
251 x: Float::infinity(),
252 y: 1.0,
253 },
254 coord! { x: 0.0, y: 0.0 },
255 ]);
256 let pt = point!(x: 2.0, y: 2.0);
257 assert_eq!(line.line_locate_point(&pt), None);
258 let line: LineString = LineString::new(vec![
259 coord! { x: 1.0, y: 1.0 },
260 coord! {
261 x: Float::neg_infinity(),
262 y: 1.0,
263 },
264 coord! { x: 0.0, y: 0.0 },
265 ]);
266 let pt = point!(x: 2.0, y: 2.0);
267 assert_eq!(line.line_locate_point(&pt), None);
268 }
269}