leptos_leaflet/components/
position.rs1use leaflet::LatLng;
2
3use crate::core::IntoLatLng;
4
5#[derive(Debug, Default, Clone, Copy, PartialEq)]
12pub struct Position {
13 pub lat: f64,
14 pub lng: f64,
15}
16
17impl Position {
18 pub fn new(lat: f64, lng: f64) -> Self {
20 Self { lat, lng }
21 }
22
23 pub fn distance_haversine(&self, other: &Self) -> f64 {
27 const R: f64 = 6371e3; let phi1 = self.lat.to_radians();
29 let phi2 = other.lat.to_radians();
30 let delta_phi = (other.lat - self.lat).to_radians();
31 let delta_lambda = (other.lng - self.lng).to_radians();
32
33 let a = (delta_phi / 2.0).sin().powi(2)
34 + phi1.cos() * phi2.cos() * (delta_lambda / 2.0).sin().powi(2);
35 let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
36 R * c
37 }
38
39 #[inline]
48 pub fn inside_circle(&self, center: &Position, radius: f64) -> bool {
49 self.distance_haversine(center) < radius
50 }
51
52 #[inline]
54 pub fn inside_polygon(&self, polygon: &[Position]) -> bool {
55 let x = self.lat;
56 let y = self.lng;
57
58 let mut inside = false;
59 for i in 0..polygon.len() {
60 let j = if i == 0 { polygon.len() - 1 } else { i - 1 };
61 let xi = polygon[i].lat;
62 let yi = polygon[i].lng;
63 let xj = polygon[j].lat;
64 let yj = polygon[j].lng;
65
66 let intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
67 if intersect {
68 inside = !inside;
69 }
70 }
71
72 inside
73 }
74
75 pub fn distance(&self, other: &Self) -> f64 {
77 ((self.lat - other.lat).powi(2) + (self.lng - other.lng).powi(2)).sqrt()
78 }
79
80 pub fn is_zero(&self) -> bool {
82 self.lat.abs() <= f64::EPSILON && self.lng.abs() <= f64::EPSILON
83 }
84
85 pub fn as_lat_lng(&self) -> LatLng {
86 LatLng::new(self.lat, self.lng)
87 }
88}
89
90#[allow(unused)]
92fn winding_number(poly: &[Position], point: &Position) -> i32 {
93 let mut wn = 0;
94 let n = poly.len();
95 poly.iter().enumerate().for_each(|(i, p0)| {
96 let p1 = &poly[(i + 1) % n];
97 if p0.lng <= point.lng {
98 if p1.lng > point.lng && is_left(p0, p1, point) > 0.0 {
99 wn += 1;
100 }
101 } else if p1.lng <= point.lng && is_left(p0, p1, point) < 0.0 {
102 wn -= 1;
103 }
104 });
105 wn
106}
107
108#[allow(unused)]
110fn is_left(p0: &Position, p1: &Position, p2: &Position) -> f64 {
111 (p1.lat - p0.lat) * (p2.lng - p0.lng) - (p2.lat - p0.lat) * (p1.lng - p0.lng)
112}
113
114impl From<Position> for LatLng {
115 fn from(value: Position) -> Self {
116 LatLng::new(value.lat, value.lng)
117 }
118}
119
120impl From<&Position> for LatLng {
121 fn from(value: &Position) -> Self {
122 LatLng::new(value.lat, value.lng)
123 }
124}
125
126impl From<Position> for (f64, f64) {
127 fn from(value: Position) -> Self {
128 (value.lat, value.lng)
129 }
130}
131
132impl From<Position> for [f64; 2] {
133 fn from(value: Position) -> Self {
134 [value.lat, value.lng]
135 }
136}
137
138impl From<(f64, f64)> for Position {
139 fn from(value: (f64, f64)) -> Self {
140 Self::new(value.0, value.1)
141 }
142}
143
144impl From<[f64; 2]> for Position {
145 fn from(value: [f64; 2]) -> Self {
146 Self::new(value[0], value[1])
147 }
148}
149
150impl IntoLatLng for Position {
151 fn into_lat_lng(self) -> LatLng {
152 LatLng::new(self.lat, self.lng)
153 }
154}
155
156#[macro_export]
157macro_rules! position {
158 ($lat: expr, $lng: expr) => {
159 {
160 use leptos::prelude::*;
161 $crate::prelude::JsSignal::derive_local(move || $crate::prelude::Position::new($lat, $lng))
162 }
163 };
164}
165
166pub fn positions(positions: &[(f64, f64)]) -> Vec<Position> {
167 positions
168 .iter()
169 .map(|&(lat, lng)| Position::new(lat, lng))
170 .collect()
171}