1pub mod primitives {
2 use std::fmt::Display;
3
4 use serde::{Deserialize, Serialize};
5
6 use crate::great_circle::distance::haversine;
7
8 #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
9 pub struct FPCoordinate {
10 pub lat: i32,
11 pub lon: i32,
12 }
13
14 impl FPCoordinate {
15 pub const fn new(lat: i32, lon: i32) -> Self {
16 Self { lat, lon }
17 }
18
19 pub const fn min() -> Self {
20 Self {
21 lat: i32::MIN,
22 lon: i32::MIN,
23 }
24 }
25
26 pub const fn max() -> Self {
27 Self {
28 lat: i32::MAX,
29 lon: i32::MAX,
30 }
31 }
32
33 pub fn new_from_lat_lon(lat: f64, lon: f64) -> Self {
34 let lat = (lat * 1000000.) as i32;
35 let lon = (lon * 1000000.) as i32;
36
37 Self { lat, lon }
38 }
39
40 pub fn to_lon_lat_pair(&self) -> (f64, f64) {
41 (self.lon as f64 / 1000000., self.lat as f64 / 1000000.)
42 }
43
44 pub fn to_lon_lat_vec(&self) -> Vec<f64> {
45 let (lon, lat) = self.to_lon_lat_pair();
46 vec![lon, lat]
47 }
48 }
49
50 impl Display for FPCoordinate {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 write!(
53 f,
54 "{:.6}, {:.6}",
55 self.lat as f64 / 1000000.,
56 self.lon as f64 / 1000000.
57 )
58 }
59 }
60
61 pub const fn cross_product(o: &FPCoordinate, a: &FPCoordinate, b: &FPCoordinate) -> i64 {
62 let first = (a.lon as i64 - o.lon as i64) * (b.lat as i64 - o.lat as i64);
64 let second = (a.lat as i64 - o.lat as i64) * (b.lon as i64 - o.lon as i64);
65 first - second
66 }
67
68 pub const fn is_clock_wise_turn(o: &FPCoordinate, a: &FPCoordinate, b: &FPCoordinate) -> bool {
69 let first = (a.lon as i64 - o.lon as i64) * (b.lat as i64 - o.lat as i64);
71 let second = (a.lat as i64 - o.lat as i64) * (b.lon as i64 - o.lon as i64);
72 first > second
73 }
74
75 pub fn distance(first: &FPCoordinate, b: &FPCoordinate) -> f64 {
76 let (lona, lata) = first.to_lon_lat_pair();
77 let (lonb, latb) = b.to_lon_lat_pair();
78 haversine(lata, lona, latb, lonb)
79 }
80
81 #[derive(Clone, Copy, Debug, PartialEq)]
82 pub struct Point {
83 pub x: f64,
84 pub y: f64,
85 }
86
87 impl Point {
88 pub fn new() -> Self {
89 Point { x: 0., y: 0. }
90 }
91 }
92
93 impl Default for Point {
94 fn default() -> Self {
95 Self::new()
96 }
97 }
98
99 pub struct Segment(pub Point, pub Point);
100
101 pub fn distance_to_segment(point: Point, segment: Segment) -> (f64, Point) {
102 let mut dx = segment.1.x - segment.0.x;
103 let mut dy = segment.1.y - segment.0.y;
104
105 if (dx == 0.) && (dy == 0.) {
106 dx = point.x - segment.0.x;
108 dy = point.y - segment.0.y;
109 return ((dx * dx + dy * dy).sqrt(), segment.0);
110 }
111
112 let t = ((point.x - segment.0.x) * dx + (point.y - segment.0.y) * dy) / (dx * dx + dy * dy);
114
115 let closest;
118 if t < 0. {
119 closest = segment.0;
120 dx = point.x - segment.0.x;
121 dy = point.y - segment.0.y;
122 } else if t > 1. {
123 closest = Point {
124 x: segment.1.x,
125 y: segment.1.y,
126 };
127 dx = point.x - segment.1.x;
128 dy = point.y - segment.1.y;
129 } else {
130 closest = Point {
131 x: segment.0.x + t * dx,
132 y: segment.0.y + t * dy,
133 };
134 dx = point.x - closest.x;
135 dy = point.y - closest.y;
136 }
137
138 ((dx * dx + dy * dy).sqrt(), closest)
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 macro_rules! assert_delta {
145 ($x:expr, $y:expr, $d:expr) => {
146 if !($x - $y < $d || $y - $x < $d) {
147 panic!();
148 }
149 };
150 }
151
152 use super::primitives::{cross_product, distance, FPCoordinate};
153 use crate::geometry::primitives::{distance_to_segment, is_clock_wise_turn, Point, Segment};
154
155 #[test]
156 fn distance_one() {
157 let p = Point { x: 1., y: 2. };
158 let s = Segment(Point { x: 0., y: 0. }, Point { x: 0., y: 10. });
159 let (distance, location) = distance_to_segment(p, s);
160 assert_eq!(distance, 1.);
161 assert_eq!(location, Point { x: 0., y: 2. });
162 }
163
164 #[test]
165 fn distance_on_line() {
166 let p = Point { x: 1., y: 2. };
167 let s = Segment(Point { x: 0., y: 0. }, Point { x: 0., y: 0. });
168 let (distance, location) = distance_to_segment(p, s);
169 assert_eq!(distance, 2.23606797749979);
170 assert_eq!(location, Point { x: 0., y: 0. });
171 }
172
173 #[test]
174 fn cross_product_ex1() {
175 let o = FPCoordinate::new(1, 1);
176 let a = FPCoordinate::new(4, 5);
177 let b = FPCoordinate::new(5, 4);
178 assert_eq!(7, cross_product(&o, &a, &b))
179 }
180
181 #[test]
182 fn cross_product_ex2() {
183 let o = FPCoordinate::new(0, 0);
184 let a = FPCoordinate::new(7, 5);
185 let b = FPCoordinate::new(17, 13);
186 assert_eq!(-6, cross_product(&o, &a, &b))
187 }
188
189 #[test]
190 fn cross_product_ex3() {
191 let o = FPCoordinate::new(0, 0);
192 let a = FPCoordinate::new(2, 2);
193 let b = FPCoordinate::new(0, -3);
194 assert_eq!(6, cross_product(&o, &a, &b))
195 }
196
197 #[test]
198 fn clock_wise_turn() {
199 let o = FPCoordinate::new_from_lat_lon(33.376756, -114.990162);
200 let a = FPCoordinate::new_from_lat_lon(33.359699, -114.945064);
201 let b = FPCoordinate::new_from_lat_lon(33.412820, -114.943641);
202
203 let cp = cross_product(&o, &a, &b);
204 assert!(cp > 0);
205 assert!(is_clock_wise_turn(&o, &a, &b));
206 }
207
208 #[test]
209 fn println() {
210 let input = FPCoordinate::new_from_lat_lon(33.359699, -114.945064);
211 let output = format!("{input}");
212 assert_eq!(output, "33.359699, -114.945064");
213 }
214
215 #[test]
216 fn println_truncated() {
217 let input = FPCoordinate::new_from_lat_lon(33.359699123456789, -114.945064127454);
218 let output = format!("{input}");
219 assert_eq!(output, "33.359699, -114.945064");
220 }
221
222 #[test]
223 fn to_lon_lat_pair_vec_equivalent() {
224 let input = FPCoordinate::new_from_lat_lon(33.359699123456789, -114.945064127454);
225 let output1 = input.to_lon_lat_pair();
226 let output2 = input.to_lon_lat_vec();
227
228 assert_eq!(output1.0, output2[0]);
229 assert_eq!(output1.1, output2[1]);
230 }
231
232 #[test]
233 fn trivial_distance_equivalent() {
234 let ny = FPCoordinate::new_from_lat_lon(40.730610, -73.935242);
235 let sf = FPCoordinate::new_from_lat_lon(37.773972, -122.431297);
236 let distance = distance(&ny, &sf);
237
238 assert_delta!(distance, 4140.175105689902, 0.0000001);
239 }
240
241 #[test]
242 fn point_self_new_equivalent() {
243 let p1 = Point::default();
244 let p2 = Point::new();
245 assert_eq!(p1, p2);
246 }
247}