gistools/geometry/tools/lines/
along.rs1use crate::geometry::{bearing, destination};
2use libm::hypot;
3use s2json::{
4 Feature, Features, Geometry, GetXY, LineString, LineStringGeometry, NewXY, VectorFeature,
5 VectorGeometry, VectorLineString, VectorLineStringGeometry,
6};
7
8pub trait Along<P: NewXY> {
32 fn along_line(&self, distance: f64, radius: Option<f64>) -> P;
34}
35
36impl<P: GetXY + NewXY, Q: GetXY> Along<P> for &[Q] {
39 fn along_line(&self, distance: f64, radius: Option<f64>) -> P {
40 along_line(self, distance, radius)
41 }
42}
43
44impl<M, P: Clone + Default, D: Clone + Default, Q: NewXY> Along<Q> for Feature<M, P, D> {
47 fn along_line(&self, distance: f64, radius: Option<f64>) -> Q {
48 self.geometry.along_line(distance, radius)
49 }
50}
51impl<M: Clone + Default, Q: NewXY> Along<Q> for Geometry<M> {
52 fn along_line(&self, distance: f64, radius: Option<f64>) -> Q {
53 match self {
54 Geometry::LineString(g) => g.along_line(distance, radius),
55 _ => Q::new_xy(f64::NAN, f64::NAN),
56 }
57 }
58}
59impl<M: Clone + Default, Q: NewXY> Along<Q> for LineStringGeometry<M> {
60 fn along_line(&self, distance: f64, radius: Option<f64>) -> Q {
61 self.coordinates.along_line(distance, radius)
62 }
63}
64impl<Q: NewXY> Along<Q> for LineString {
65 fn along_line(&self, distance: f64, radius: Option<f64>) -> Q {
66 along_line(self, distance, radius)
67 }
68}
69
70impl<M, P: Clone + Default, D: Clone + Default, Q: NewXY> Along<Q> for VectorFeature<M, P, D> {
73 fn along_line(&self, distance: f64, radius: Option<f64>) -> Q {
74 self.geometry.along_line(distance, radius)
75 }
76}
77impl<M: Clone + Default, Q: NewXY> Along<Q> for VectorGeometry<M> {
78 fn along_line(&self, distance: f64, radius: Option<f64>) -> Q {
79 match self {
80 VectorGeometry::LineString(g) => g.along_line(distance, radius),
81 _ => Q::new_xy(f64::NAN, f64::NAN),
82 }
83 }
84}
85impl<M: Clone + Default, Q: NewXY> Along<Q> for VectorLineStringGeometry<M> {
86 fn along_line(&self, distance: f64, radius: Option<f64>) -> Q {
87 self.coordinates.along_line(distance, radius)
88 }
89}
90impl<M: Clone + Default, Q: NewXY> Along<Q> for VectorLineString<M> {
91 fn along_line(&self, distance: f64, radius: Option<f64>) -> Q {
92 along_line(self, distance, radius)
93 }
94}
95
96impl<M, P: Clone + Default, D: Clone + Default, Q: NewXY> Along<Q> for Features<M, P, D> {
99 fn along_line(&self, distance: f64, radius: Option<f64>) -> Q {
100 match self {
101 Features::Feature(f) => f.along_line(distance, radius),
102 Features::VectorFeature(f) => f.along_line(distance, radius),
103 }
104 }
105}
106
107pub fn along_line<P: GetXY, Q: NewXY>(coords: &[P], distance: f64, radius: Option<f64>) -> Q {
111 let mut travelled = 0.;
112 for i in 0..coords.len() {
113 if distance >= travelled && i == coords.len() - 1 {
114 break;
115 } else if travelled >= distance {
116 let overshot = distance - travelled;
117 if overshot == 0. {
118 return Q::new_xy(coords[i].x(), coords[i].y());
119 } else {
120 let direction = bearing(&coords[i], &coords[i - 1]) - 180.;
121 let interpolated = destination(&coords[i], overshot, direction, radius);
122 return interpolated;
123 }
124 } else {
125 travelled +=
126 hypot(coords[i + 1].x() - coords[i].x(), coords[i + 1].y() - coords[i].y());
127 }
128 }
129 let last = coords.len() - 1;
130 Q::new_xy(coords[last].x(), coords[last].y())
131}