1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use crate::euclidean_distance::EuclideanDistance;
use crate::{LineString, Point};
use num_traits::{Float, FromPrimitive};
pub trait FrechetDistance<T, Rhs = Self> {
fn frechet_distance(&self, rhs: &Rhs) -> T;
}
impl<T> FrechetDistance<T, LineString<T>> for LineString<T>
where
T: Float + FromPrimitive,
{
fn frechet_distance(&self, ls: &LineString<T>) -> T {
if self.num_coords() != 0 && ls.num_coords() != 0 {
let mut data = Data {
cache: vec![vec![T::nan(); ls.num_coords()]; self.num_coords()],
ls_a: self,
ls_b: ls,
};
data.compute(self.num_coords() - 1, ls.num_coords() - 1)
} else {
T::zero()
}
}
}
struct Data<'a, T>
where
T: Float + FromPrimitive,
{
cache: Vec<Vec<T>>,
ls_a: &'a LineString<T>,
ls_b: &'a LineString<T>,
}
impl<'a, T> Data<'a, T>
where
T: Float + FromPrimitive,
{
fn compute(&mut self, i: usize, j: usize) -> T {
if self.cache[i][j].is_nan() {
let eucl = Point::from(self.ls_a[i]).euclidean_distance(&Point::from(self.ls_b[j]));
self.cache[i][j] = match (i, j) {
(0, 0) => eucl,
(_, 0) => self.compute(i - 1, 0).max(eucl),
(0, _) => self.compute(0, j - 1).max(eucl),
(_, _) => ((self.compute(i - 1, j).min(self.compute(i - 1, j - 1)))
.min(self.compute(i, j - 1)))
.max(eucl),
};
}
self.cache[i][j]
}
}
#[cfg(test)]
mod test {
use crate::algorithm::frechet_distance::FrechetDistance;
use crate::euclidean_distance::EuclideanDistance;
use crate::LineString;
#[test]
fn test_single_point_in_linestring() {
let ls_a = LineString::from(vec![(1., 1.)]);
let ls_b = LineString::from(vec![(0., 2.)]);
assert_relative_eq!(
(ls_a.clone().into_points())[0].euclidean_distance(&(&ls_b.clone().into_points())[0]),
ls_a.frechet_distance(&ls_b)
);
}
#[test]
fn test_identical_linestrings() {
let ls_a = LineString::from(vec![(1., 1.), (2., 1.), (2., 2.)]);
let ls_b = LineString::from(vec![(1., 1.), (2., 1.), (2., 2.)]);
assert_relative_eq!(0., ls_a.frechet_distance(&ls_b));
}
#[test]
fn different_dimensions_linestrings() {
let ls_a = LineString::from(vec![(1., 1.)]);
let ls_b = LineString::from(vec![(2., 2.), (0., 1.)]);
assert_relative_eq!(2f64.sqrt(), ls_a.frechet_distance(&ls_b));
}
#[test]
fn test_frechet_1() {
let ls_a = LineString::from(vec![(1., 1.), (2., 1.)]);
let ls_b = LineString::from(vec![(2., 2.), (2., 3.)]);
assert_relative_eq!(2., ls_a.frechet_distance(&ls_b));
}
#[test]
fn test_frechet_2() {
let ls_a = LineString::from(vec![(1., 1.), (2., 1.), (2., 2.)]);
let ls_b = LineString::from(vec![(2., 2.), (0., 1.), (2., 4.)]);
assert_relative_eq!(2., ls_a.frechet_distance(&ls_b));
}
}