1use std::fmt;
2use std::error;
3use std::cmp::{Ord, Ordering, PartialEq, Eq, PartialOrd};
4use std::collections::BinaryHeap;
5use std::fmt::Debug;
6use std::ops::Add;
7
8pub enum CutRatioResult {
10 Begin,
12 Medium(f64),
16 End,
18}
19
20impl PartialEq for CutRatioResult {
21 fn eq(&self, other: &Self) -> bool {
22 match (self, other) {
23 (Self::Begin, Self::Begin) => true,
24 (Self::Medium(s), Self::Medium(o)) => s.eq(o),
25 (Self::End, Self::End) => true,
26 _ => false,
27 }
28 }
29}
30
31impl Eq for CutRatioResult {}
32
33impl PartialOrd for CutRatioResult {
34 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
35 Some(self.cmp(other))
36 }
37}
38
39impl Ord for CutRatioResult {
40 fn cmp(&self, other: &Self) -> Ordering {
41 match (self, other) {
42 (Self::Begin, Self::Begin) => Ordering::Equal,
43 (Self::Begin, _) => Ordering::Less,
44 (Self::Medium(_), Self::Begin) => Ordering::Greater,
45 (Self::Medium(s), Self::Medium(o)) => s.total_cmp(o),
46 (Self::Medium(_), Self::End) => Ordering::Less,
47 (Self::End, Self::End) => Ordering::Equal,
48 (Self::End, _) => Ordering::Greater,
49 }
50 }
51}
52
53pub struct DistanceToSegmentResult<P, D>
55where
56 P: Copy,
57 D: Copy + PartialOrd + Add<Output = D>,
58{
59 pub cut_ratio: CutRatioResult,
60 pub cut_point: P,
61 pub distance: D,
62}
63
64pub trait PolySplit<D>
66where
67 Self: Copy,
68 D: Copy + PartialOrd + Add<Output = D>,
69{
70 fn distance_to_point(&self, point: &Self) -> D;
76 fn distance_to_segment(&self, segment: (&Self, &Self)) -> DistanceToSegmentResult<Self, D>;
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
85#[non_exhaustive]
86pub enum PolySplitErrorKind {
87 InvalidPolyline,
88 InvalidPoints,
89 PointFarAway,
90 CannotSplit,
91}
92
93#[derive(Debug)]
94pub struct PolySplitError {
95 pub(super) kind: PolySplitErrorKind,
96 pub message: String,
97}
98
99impl PolySplitError {
100 pub fn kind(&self) -> &PolySplitErrorKind {
101 &self.kind
102 }
103}
104
105impl fmt::Display for PolySplitError {
106 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107 write!(f, "{}", self.message)
108 }
109}
110
111impl error::Error for PolySplitError {}
112
113pub type Result<T> = std::result::Result<T, PolySplitError>;
114
115struct CutPoint<P>
116where P: std::fmt::Debug {
117 segment_index: usize,
118 cut_ratio: CutRatioResult,
119 cut_point: P,
120}
121
122struct Vertex<D> {
123 point_index: usize,
124 cut_point_index: usize,
125 distance_to: D,
126}
127
128#[derive(Copy, Clone, PartialEq)]
129struct State<D> {
130 distance_total: D,
131 position: usize,
132}
133
134impl<D: PartialOrd> Eq for State<D> {
135}
136
137impl<D: Copy + PartialOrd> Ord for State<D> {
139 fn cmp(&self, other: &Self) -> Ordering {
140 other.distance_total
144 .partial_cmp(&self.distance_total)
145 .unwrap()
146 .then_with(|| self.position.cmp(&other.position))
147 }
148}
149
150impl<D: Copy + PartialOrd> PartialOrd for State<D> {
152 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
153 Some(self.cmp(other))
154 }
155}
156
157pub fn polyline_split<P, D>(
181 polyline: &[P],
182 points: &[P],
183 distance_threshold: Option<D>,
184) -> Result<Vec<Vec<P>>>
185where
186 P: PolySplit<D> + std::fmt::Debug,
187 D: Copy + PartialOrd + Add<Output = D>,
188{
189 if polyline.len() <= 1 {
190 return Err(PolySplitError{
191 kind: PolySplitErrorKind::InvalidPolyline,
192 message: "polyline has not enough points".to_string(),
193 });
194 }
195
196 if points.len() <= 1 {
197 return Err(PolySplitError{
198 kind: PolySplitErrorKind::InvalidPoints,
199 message: "number of points are not enough".to_string(),
200 });
201 }
202
203 let segments_len = polyline.len() - 1;
204 let points_len = points.len();
205
206 let mut cut_points: Vec<CutPoint<P>> = Vec::new();
208
209 for segment_index in 0..segments_len {
210 let segment_a = &polyline[segment_index];
211 let segment_b = &polyline[segment_index + 1];
212
213 let mut is_start_added = false;
214 let mut is_end_added = false;
215
216 for point in points.iter() {
217 let psd: DistanceToSegmentResult<P, D> = point.distance_to_segment((segment_a, segment_b));
218 if let Some(dt) = distance_threshold {
219 if psd.distance > dt {
220 continue;
221 }
222 }
223
224 match psd.cut_ratio {
225 CutRatioResult::Begin => {
226 if segment_index == 0 && !is_start_added {
227 cut_points.push(CutPoint {
228 segment_index,
229 cut_ratio: psd.cut_ratio,
230 cut_point: *segment_a,
231 });
232
233 is_start_added = true;
234 }
235 }
236
237 CutRatioResult::End => {
238 if !is_end_added {
239 cut_points.push(CutPoint {
240 segment_index,
241 cut_ratio: psd.cut_ratio,
242 cut_point: *segment_b,
243 });
244
245 is_end_added = true;
246 }
247 },
248
249 _ => {
250 cut_points.push(CutPoint {
251 segment_index,
252 cut_ratio: psd.cut_ratio,
253 cut_point: psd.cut_point,
254 });
255 }
256 }
257 }
258 }
259
260 cut_points.sort_unstable_by(|a, b| {
261 match a.segment_index.cmp(&b.segment_index) {
262 Ordering::Equal => a.cut_ratio.partial_cmp(&b.cut_ratio).unwrap(),
263 v => v,
264 }
265 });
266
267 let mut vertexes: Vec<Vertex<D>> = Vec::new();
269 let mut edges: Vec<(usize, usize)> = Vec::new();
270
271 let mut last_reachable_cut_point_index = 0;
272
273 for (point_index, point) in points.iter().enumerate() {
274 let start_position = vertexes.len();
275 let mut first_match_cut_point_index = None;
276
277 for (cut_point_index, cut_point) in cut_points.iter().enumerate().skip(last_reachable_cut_point_index) {
278 let distance_to = point.distance_to_point(&cut_point.cut_point);
279 if let Some(dt) = distance_threshold {
280 if distance_to > dt {
281 continue;
282 }
283 }
284
285 if first_match_cut_point_index.is_none() {
286 first_match_cut_point_index = Some(cut_point_index);
287 }
288
289 vertexes.push(Vertex {
290 point_index,
291 cut_point_index,
292 distance_to,
293 });
294 }
295
296 let end_position = vertexes.len();
297 if start_position == end_position {
298 return Err(PolySplitError{
299 kind: PolySplitErrorKind::PointFarAway,
300 message: "point has no closest segments".to_string(),
301 });
302 }
303
304 edges.push((start_position, end_position));
305 last_reachable_cut_point_index = first_match_cut_point_index.unwrap_or_default();
306 }
307
308 let vertexes_len = vertexes.len();
310 let mut dist: Vec<Option<D>> = (0..vertexes_len).map(|_| None).collect();
311 let mut prev: Vec<Option<usize>> = (0..vertexes_len).map(|_| None).collect();
312 let mut priority_queue = BinaryHeap::new();
313
314 for idx in edges[0].0..edges[0].1 {
315 let vertex = &vertexes[idx];
316
317 dist[idx] = Some(vertex.distance_to);
318 prev[idx] = None;
319 priority_queue.push(State {
320 distance_total: vertex.distance_to,
321 position: idx,
322 });
323 }
324
325 let mut destination = None;
327 while let Some(State { distance_total, position }) = priority_queue.pop() {
328 let current_vertex = &vertexes[position];
329
330 if current_vertex.point_index + 1 == points_len {
332 destination = Some(position);
333 break;
334 }
335
336 if let Some(d) = dist[position] {
338 if distance_total > d {
339 continue;
340 }
341 }
342
343 let (from_idx, to_idx) = edges[current_vertex.point_index + 1];
345 for idx in from_idx..to_idx {
346 let neighbour_vertex = &vertexes[idx];
347
348 if current_vertex.cut_point_index > neighbour_vertex.cut_point_index {
349 continue;
350 }
351
352 let relaxed_distance_total = distance_total + neighbour_vertex.distance_to;
353 if dist[idx].map_or(true, |d| d > relaxed_distance_total) {
354 dist[idx] = Some(relaxed_distance_total);
355 prev[idx] = Some(position);
356 priority_queue.push(State {
357 distance_total: relaxed_distance_total,
358 position: idx,
359 });
360 }
361 }
362 }
363
364 let mut path = Vec::new();
366 while let Some(idx) = destination {
367 path.push(idx);
368 destination = prev[idx];
369 }
370
371 if path.is_empty() {
372 return Err(PolySplitError{
373 kind: PolySplitErrorKind::CannotSplit,
374 message: "cannot split polyline".to_string(),
375 });
376 }
377
378 path.reverse();
379
380 let mut segments: Vec<_> = Vec::with_capacity(segments_len);
382 let mut current_vertex = &vertexes[path[0]];
383 let mut current_cut_point = &cut_points[current_vertex.cut_point_index];
384
385 for next_idx in path[1..].iter() {
386 let next_vertex: &Vertex<D> = &vertexes[*next_idx];
387 let next_cut_point = &cut_points[next_vertex.cut_point_index];
388 let mut segment: Vec<_> = Vec::new();
389
390 if !matches!(current_cut_point.cut_ratio, CutRatioResult::End) {
391 segment.push(current_cut_point.cut_point);
392 }
393
394 for segment_idx in current_cut_point.segment_index..next_cut_point.segment_index {
395 segment.push(polyline[segment_idx + 1]);
396 }
397
398 if !matches!(next_cut_point.cut_ratio, CutRatioResult::Begin) {
399 segment.push(next_cut_point.cut_point);
400 }
401
402 if segment.len() == 1 {
405 segment.push(segment[0]);
406 }
407
408 segments.push(segment);
409 current_vertex = next_vertex;
410 current_cut_point = &cut_points[current_vertex.cut_point_index];
411 }
412
413 Ok(segments)
414}