1use geometry_coords::CoordinateScalar;
18use geometry_cs::{CartesianFamily, CoordinateSystem};
19use geometry_model::{DynGeometry, DynKind, Linestring, Point, Segment};
20use geometry_strategy::{
21 DefaultDistance, DefaultDistanceStrategy, DistanceStrategy, PointToSegment, Pythagoras,
22};
23use geometry_tag::SameAs;
24use geometry_trait::Linestring as LinestringTrait;
25
26use crate::distance::{distance, distance_with};
27use crate::dyn_error::DynKindMismatch;
28
29const SUPPORTED: &[&[DynKind]] = &[
34 &[DynKind::Point, DynKind::Point],
35 &[DynKind::Point, DynKind::LineString], &[DynKind::LineString, DynKind::Point], ];
38
39#[allow(
53 clippy::match_same_arms,
54 reason = "The two point↔linestring arms differ by argument order; keeping them separate documents both directions."
55)]
56pub fn distance_dyn<S, Cs>(
57 a: &DynGeometry<S, Cs>,
58 b: &DynGeometry<S, Cs>,
59) -> Result<S, DynKindMismatch>
60where
61 S: CoordinateScalar,
62 Cs: CoordinateSystem + Copy,
63 Cs::Family: SameAs<CartesianFamily> + DefaultDistance<Cs::Family>,
64 DefaultDistanceStrategy<Point<S, 2, Cs>, Point<S, 2, Cs>>:
65 DistanceStrategy<Point<S, 2, Cs>, Point<S, 2, Cs>, Out = S> + Default,
66 PointToSegment<Pythagoras>:
67 DistanceStrategy<Point<S, 2, Cs>, Segment<Point<S, 2, Cs>>, Out = S>,
68{
69 use DynGeometry::{LineString, Point as PointArm};
70 match (a, b) {
71 (PointArm(p), PointArm(q)) => Ok(distance(p, q)),
72 (PointArm(p), LineString(ls)) => Ok(point_to_linestring(p, ls)),
73 (LineString(ls), PointArm(p)) => Ok(point_to_linestring(p, ls)),
74 _ => Err(DynKindMismatch {
75 got: alloc::vec![a.kind(), b.kind()],
76 expected: SUPPORTED,
77 }),
78 }
79}
80
81fn point_to_linestring<S, Cs>(p: &Point<S, 2, Cs>, ls: &Linestring<Point<S, 2, Cs>>) -> S
87where
88 S: CoordinateScalar,
89 Cs: CoordinateSystem + Copy,
90 Cs::Family: SameAs<CartesianFamily>,
91 PointToSegment<Pythagoras>:
92 DistanceStrategy<Point<S, 2, Cs>, Segment<Point<S, 2, Cs>>, Out = S>,
93{
94 let strategy = PointToSegment::<Pythagoras>::default();
95 let mut best: Option<S> = None;
96 let mut prev: Option<Point<S, 2, Cs>> = None;
97 for pt in ls.points() {
98 if let Some(start) = prev {
99 let seg = Segment::new(start, *pt);
100 let d = distance_with(p, &seg, strategy);
101 best = Some(match best {
102 Some(m) if m <= d => m,
103 _ => d,
104 });
105 }
106 prev = Some(*pt);
107 }
108 best.unwrap_or_else(|| match ls.points().next() {
112 Some(v) => distance_with(p, &Segment::new(*v, *v), strategy),
113 None => S::ZERO,
114 })
115}