1use crate::coords_iter::CoordsIter;
2use crate::{CoordFloat, EuclideanLength, Line, LineString, Point};
3use std::ops::AddAssign;
4
5pub trait LineInterpolatePoint<F: CoordFloat> {
34 type Output;
35
36 fn line_interpolate_point(&self, fraction: F) -> Self::Output;
37}
38
39impl<T> LineInterpolatePoint<T> for Line<T>
40where
41 T: CoordFloat,
42{
43 type Output = Option<Point<T>>;
44
45 fn line_interpolate_point(&self, fraction: T) -> Self::Output {
46 if (fraction >= T::zero()) && (fraction <= T::one()) {
47 let diff = self.end - self.start;
49 let r = self.start + diff * (fraction);
50 if r.x.is_finite() && r.y.is_finite() {
51 Some(r.into())
52 } else {
53 None
54 }
55 } else if fraction < T::zero() {
56 self.line_interpolate_point(T::zero())
58 } else if fraction > T::one() {
59 self.line_interpolate_point(T::one())
61 } else {
62 debug_assert!(fraction.is_nan());
64 None
65 }
66 }
67}
68
69impl<T> LineInterpolatePoint<T> for LineString<T>
70where
71 T: CoordFloat + AddAssign + std::fmt::Debug,
72 Line<T>: EuclideanLength<T>,
73 LineString<T>: EuclideanLength<T>,
74{
75 type Output = Option<Point<T>>;
76
77 fn line_interpolate_point(&self, fraction: T) -> Self::Output {
78 if (fraction >= T::zero()) && (fraction <= T::one()) {
79 let total_length = self.euclidean_length();
81 let fractional_length = total_length * fraction;
82 let mut cum_length = T::zero();
83 for segment in self.lines() {
84 let length = segment.euclidean_length();
85 if cum_length + length >= fractional_length {
86 let segment_fraction = (fractional_length - cum_length) / length;
87 return segment.line_interpolate_point(segment_fraction);
88 }
89 cum_length += length;
90 }
91 debug_assert!(fractional_length.is_nan() || (self.coords_count() == 0));
94 None
95 } else if fraction < T::zero() {
96 self.line_interpolate_point(T::zero())
98 } else if fraction > T::one() {
99 self.line_interpolate_point(T::one())
101 } else {
102 debug_assert!(fraction.is_nan());
104 None
105 }
106 }
107}
108
109#[cfg(test)]
110mod test {
111
112 use super::*;
113 use crate::{coord, point};
114 use crate::{ClosestPoint, LineLocatePoint};
115 use num_traits::Float;
116
117 #[test]
118 fn test_line_interpolate_point_line() {
119 let line = Line::new(coord! { x: -1.0, y: 0.0 }, coord! { x: 1.0, y: 0.0 });
120 assert_eq!(
122 line.line_interpolate_point(-1.0),
123 Some(point!(x: -1.0, y: 0.0))
124 );
125 assert_eq!(
126 line.line_interpolate_point(0.5),
127 Some(point!(x: 0.0, y: 0.0))
128 );
129 assert_eq!(
130 line.line_interpolate_point(0.75),
131 Some(point!(x: 0.5, y: 0.0))
132 );
133 assert_eq!(
134 line.line_interpolate_point(0.0),
135 Some(point!(x: -1.0, y: 0.0))
136 );
137 assert_eq!(
138 line.line_interpolate_point(1.0),
139 Some(point!(x: 1.0, y: 0.0))
140 );
141 assert_eq!(
142 line.line_interpolate_point(2.0),
143 Some(point!(x: 1.0, y: 0.0))
144 );
145
146 assert_eq!(line.line_interpolate_point(Float::nan()), None);
148 assert_eq!(
149 line.line_interpolate_point(Float::infinity()),
150 Some(line.end_point())
151 );
152 assert_eq!(
153 line.line_interpolate_point(Float::neg_infinity()),
154 Some(line.start_point())
155 );
156
157 let line = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 1.0, y: 1.0 });
158 assert_eq!(
159 line.line_interpolate_point(0.5),
160 Some(point!(x: 0.5, y: 0.5))
161 );
162
163 let line = Line::new(
165 coord! {
166 x: Float::nan(),
167 y: 0.0,
168 },
169 coord! { x: 1.0, y: 1.0 },
170 );
171 assert_eq!(line.line_interpolate_point(0.5), None);
172
173 let line = Line::new(
174 coord! {
175 x: Float::infinity(),
176 y: 0.0,
177 },
178 coord! { x: 1.0, y: 1.0 },
179 );
180 assert_eq!(line.line_interpolate_point(0.5), None);
181
182 let line = Line::new(
183 coord! { x: 0.0, y: 0.0 },
184 coord! {
185 x: 1.0,
186 y: Float::infinity(),
187 },
188 );
189 assert_eq!(line.line_interpolate_point(0.5), None);
190
191 let line = Line::new(
192 coord! {
193 x: Float::neg_infinity(),
194 y: 0.0,
195 },
196 coord! { x: 1.0, y: 1.0 },
197 );
198 assert_eq!(line.line_interpolate_point(0.5), None);
199
200 let line = Line::new(
201 coord! { x: 0.0, y: 0.0 },
202 coord! {
203 x: 1.0,
204 y: Float::neg_infinity(),
205 },
206 );
207 assert_eq!(line.line_interpolate_point(0.5), None);
208 }
209
210 #[test]
211 fn test_line_interpolate_point_linestring() {
212 let linestring: LineString = vec![[-1.0, 0.0], [0.0, 0.0], [1.0, 0.0]].into();
214 assert_eq!(
215 linestring.line_interpolate_point(0.0),
216 Some(point!(x: -1.0, y: 0.0))
217 );
218 assert_eq!(
219 linestring.line_interpolate_point(0.5),
220 Some(point!(x: 0.0, y: 0.0))
221 );
222 assert_eq!(
223 linestring.line_interpolate_point(1.0),
224 Some(point!(x: 1.0, y: 0.0))
225 );
226 assert_eq!(
227 linestring.line_interpolate_point(1.0),
228 linestring.line_interpolate_point(2.0)
229 );
230 assert_eq!(
231 linestring.line_interpolate_point(0.0),
232 linestring.line_interpolate_point(-2.0)
233 );
234
235 assert_eq!(
237 linestring.line_interpolate_point(Float::infinity()),
238 linestring.points().last()
239 );
240 assert_eq!(
241 linestring.line_interpolate_point(Float::neg_infinity()),
242 linestring.points().next()
243 );
244 assert_eq!(linestring.line_interpolate_point(Float::nan()), None);
245
246 let linestring: LineString = vec![[-1.0, 0.0], [0.0, 0.0], [0.0, 1.0]].into();
247 assert_eq!(
248 linestring.line_interpolate_point(0.5),
249 Some(point!(x: 0.0, y: 0.0))
250 );
251 assert_eq!(
252 linestring.line_interpolate_point(1.5),
253 Some(point!(x: 0.0, y: 1.0))
254 );
255
256 let linestring: LineString = vec![[-1.0, 0.0], [0.0, Float::nan()], [0.0, 1.0]].into();
258 assert_eq!(linestring.line_interpolate_point(0.5), None);
259 assert_eq!(linestring.line_interpolate_point(1.5), None);
260 assert_eq!(linestring.line_interpolate_point(-1.0), None);
261
262 let linestring: LineString = vec![[-1.0, 0.0], [0.0, Float::infinity()], [0.0, 1.0]].into();
263 assert_eq!(linestring.line_interpolate_point(0.5), None);
264 assert_eq!(linestring.line_interpolate_point(1.5), None);
265 assert_eq!(linestring.line_interpolate_point(-1.0), None);
266
267 let linestring: LineString =
268 vec![[-1.0, 0.0], [0.0, Float::neg_infinity()], [0.0, 1.0]].into();
269 assert_eq!(linestring.line_interpolate_point(0.5), None);
270 assert_eq!(linestring.line_interpolate_point(1.5), None);
271 assert_eq!(linestring.line_interpolate_point(-1.0), None);
272
273 let coords: Vec<Point> = Vec::new();
275 let linestring: LineString = coords.into();
276 assert_eq!(linestring.line_interpolate_point(0.5), None);
277 }
278
279 #[test]
280 fn test_matches_closest_point() {
281 let linestring: LineString = vec![[-1.0, 0.0], [0.5, 1.0], [1.0, 2.0]].into();
284 let pt = point!(x: 0.7, y: 0.7);
285 let frac = linestring
286 .line_locate_point(&pt)
287 .expect("Should result in fraction between 0 and 1");
288 let interpolated_point = linestring
289 .line_interpolate_point(frac)
290 .expect("Shouldn't return None");
291 let closest_point = linestring.closest_point(&pt);
292 match closest_point {
293 crate::Closest::SinglePoint(p) => assert_eq!(interpolated_point, p),
294 _ => panic!("The closest point should be a SinglePoint"), };
296 }
297}