1use crate::data_structures::PriorityQueue;
2use alloc::vec::Vec;
3use core::f64::consts::SQRT_2;
4use libm::{fmax, fmin, sqrt};
5use s2json::{MValueCompatible, VectorMultiPolygon, VectorPoint, VectorPolygon};
6
7#[derive(Debug, Default, MValueCompatible, Clone)]
9pub struct PolyLabelMetadata {
10 pub distance: f64,
12}
13impl PolyLabelMetadata {
14 pub fn new(distance: f64) -> PolyLabelMetadata {
16 PolyLabelMetadata { distance }
17 }
18}
19
20#[derive(Debug, Default, Clone)]
22pub struct PolyLabelCell {
23 pub x: f64,
25 pub y: f64,
27 pub h: f64,
29 pub d: f64,
31 pub max: f64,
33}
34
35pub fn polylabels<M: Clone>(
62 polygons: &VectorMultiPolygon<M>,
63 precision: Option<f64>,
64) -> Vec<VectorPoint<PolyLabelMetadata>> {
65 polygons.iter().map(|polygon| polylabel(polygon, precision)).collect()
66}
67
68pub fn polylabel<M: Clone>(
95 polygon: &VectorPolygon<M>,
96 precision: Option<f64>,
97) -> VectorPoint<PolyLabelMetadata> {
98 let precision = precision.unwrap_or(1.0);
99 let mut min_x = f64::MAX;
101 let mut min_y = f64::MAX;
102 let mut max_x = f64::MIN;
103 let mut max_y = f64::MIN;
104
105 if polygon.is_empty() || polygon[0].is_empty() {
106 return VectorPoint::new_xy(0.0, 0.0, Some(PolyLabelMetadata::default()));
107 }
108
109 for VectorPoint { x, y, .. } in &polygon[0] {
110 if *x < min_x {
111 min_x = *x;
112 }
113 if *y < min_y {
114 min_y = *y;
115 }
116 if *x > max_x {
117 max_x = *x;
118 }
119 if *y > max_y {
120 max_y = *y;
121 }
122 }
123
124 let width = max_x - min_x;
125 let height = max_y - min_y;
126 let cell_size = fmax(precision, fmin(width, height));
127
128 if cell_size == precision {
129 return VectorPoint::new_xy(min_x, min_y, Some(PolyLabelMetadata::default()));
130 }
131
132 let mut cell_queue =
134 PriorityQueue::<PolyLabelCell>::new(|a: &PolyLabelCell, b: &PolyLabelCell| {
135 b.max.partial_cmp(&a.max).unwrap_or(core::cmp::Ordering::Equal)
136 });
137
138 let mut best_cell = get_centroid_cell(polygon);
140
141 let bbox_cell = build_cell(min_x + width / 2., min_y + height / 2., 0., polygon);
143 if bbox_cell.d > best_cell.d {
144 best_cell = bbox_cell;
145 }
146
147 let potentially_queue =
149 |x: f64,
150 y: f64,
151 h: f64,
152 best_cell: &mut PolyLabelCell,
153 cell_queue: &mut PriorityQueue<PolyLabelCell>| {
154 let cell = build_cell(x, y, h, polygon);
155 if cell.max > best_cell.d + precision {
156 cell_queue.push(cell.clone());
157 }
158 if cell.d > best_cell.d {
160 *best_cell = cell;
161 }
162 };
163
164 let mut h = cell_size / 2.;
166 let mut x = min_x;
167 while x < max_x {
168 let mut y = min_y;
169 while y < max_y {
170 potentially_queue(x + h, y + h, h, &mut best_cell, &mut cell_queue);
171 y += cell_size;
172 }
173 x += cell_size;
174 }
175
176 loop {
177 let cell = cell_queue.pop();
179 if cell.is_none() {
180 break;
181 }
182 let PolyLabelCell { max, x, y, h: ch, .. } = &cell.unwrap();
183
184 if max - best_cell.d <= precision {
186 break;
187 }
188
189 h = ch / 2.;
191 potentially_queue(x - h, y - h, h, &mut best_cell, &mut cell_queue);
192 potentially_queue(x + h, y - h, h, &mut best_cell, &mut cell_queue);
193 potentially_queue(x - h, y + h, h, &mut best_cell, &mut cell_queue);
194 potentially_queue(x + h, y + h, h, &mut best_cell, &mut cell_queue);
195 }
196
197 VectorPoint::new_xy(best_cell.x, best_cell.y, Some(PolyLabelMetadata::new(best_cell.d)))
198}
199
200fn build_cell<M: Clone>(x: f64, y: f64, h: f64, polygon: &VectorPolygon<M>) -> PolyLabelCell {
211 let d = point_to_polygon_dist(x, y, polygon);
212 PolyLabelCell { x, y, h, d, max: d + h * SQRT_2 }
213}
214
215fn point_to_polygon_dist<M: Clone>(x: f64, y: f64, polygon: &VectorPolygon<M>) -> f64 {
225 let mut inside = false;
226 let mut min_dist_sq = f64::MAX;
227
228 for ring in polygon {
229 let len = ring.len();
230 let mut j = len - 1;
231 for i in 0..len {
232 let a = &ring[i];
233 let b = &ring[j];
234
235 if (a.y > y) != (b.y > y) && x < ((b.x - a.x) * (y - a.y)) / (b.y - a.y) + a.x {
236 inside = !inside;
237 }
238
239 min_dist_sq = fmin(min_dist_sq, get_seg_dist_sq(x, y, a, b));
240 j = i; }
242 }
243
244 if min_dist_sq == 0. { 0. } else { (if inside { 1. } else { -1. }) * sqrt(min_dist_sq) }
245}
246
247fn get_centroid_cell<M: Clone>(polygon: &VectorPolygon<M>) -> PolyLabelCell {
250 let mut area = 0.;
251 let mut x = 0.;
252 let mut y = 0.;
253 let points = &polygon[0];
254
255 let len = points.len();
256 let mut j = len - 1;
257 for i in 0..len {
258 let a = &points[i];
259 let b = &points[j];
260 let f = a.x * b.y - b.x * a.y;
261 x += (a.x + b.x) * f;
262 y += (a.y + b.y) * f;
263 area += f * 3.;
264 j = i; }
266 let centroid = build_cell(x / area, y / area, 0., polygon);
267 if area == 0. || centroid.d < 0. {
268 build_cell(points[0].x, points[0].y, 0., polygon)
269 } else {
270 centroid
271 }
272}
273
274fn get_seg_dist_sq<M: Clone>(px: f64, py: f64, a: &VectorPoint<M>, b: &VectorPoint<M>) -> f64 {
277 let mut x = a.x;
278 let mut y = a.y;
279 let mut dx = b.x - x;
280 let mut dy = b.y - y;
281
282 if dx != 0. || dy != 0. {
283 let t = ((px - x) * dx + (py - y) * dy) / (dx * dx + dy * dy);
284
285 if t > 1. {
286 x = b.x;
287 y = b.y;
288 } else if t > 0. {
289 x += dx * t;
290 y += dy * t;
291 }
292 }
293
294 dx = px - x;
295 dy = py - y;
296
297 dx * dx + dy * dy
298}