1#[derive(Debug, Clone, PartialEq)]
8pub struct Point {
9 pub coordinates: Vec<f64>,
10}
11
12impl Point {
13 pub fn new(coordinates: Vec<f64>) -> Self {
14 Self { coordinates }
15 }
16
17 pub fn dimension(&self) -> usize {
18 self.coordinates.len()
19 }
20
21 pub fn distance_squared(&self, other: &Point) -> f64 {
22 self.coordinates
23 .iter()
24 .zip(other.coordinates.iter())
25 .map(|(a, b)| (a - b).powi(2))
26 .sum()
27 }
28
29 pub fn distance(&self, other: &Point) -> f64 {
30 self.distance_squared(other).sqrt()
31 }
32}
33
34#[derive(Debug, Clone, PartialEq)]
36pub struct Rectangle {
37 pub min_bounds: Vec<f64>,
38 pub max_bounds: Vec<f64>,
39}
40
41impl Rectangle {
42 pub fn new(min_bounds: Vec<f64>, max_bounds: Vec<f64>) -> Self {
43 assert_eq!(min_bounds.len(), max_bounds.len());
44 Self {
45 min_bounds,
46 max_bounds,
47 }
48 }
49
50 pub fn dimension(&self) -> usize {
51 self.min_bounds.len()
52 }
53
54 pub fn contains(&self, point: &Point) -> bool {
55 point
56 .coordinates
57 .iter()
58 .enumerate()
59 .all(|(i, &coord)| coord >= self.min_bounds[i] && coord <= self.max_bounds[i])
60 }
61
62 pub fn intersects(&self, other: &Rectangle) -> bool {
63 self.min_bounds.iter().enumerate().all(|(i, &min)| {
64 min <= other.max_bounds[i] && self.max_bounds[i] >= other.min_bounds[i]
65 })
66 }
67
68 pub fn area(&self) -> f64 {
69 self.min_bounds
70 .iter()
71 .zip(self.max_bounds.iter())
72 .map(|(min, max)| max - min)
73 .product()
74 }
75
76 pub fn expand_to_include(&mut self, point: &Point) {
77 for (i, &coord) in point.coordinates.iter().enumerate() {
78 if coord < self.min_bounds[i] {
79 self.min_bounds[i] = coord;
80 }
81 if coord > self.max_bounds[i] {
82 self.max_bounds[i] = coord;
83 }
84 }
85 }
86
87 pub fn union(&self, other: &Rectangle) -> Rectangle {
88 let min_bounds = self
89 .min_bounds
90 .iter()
91 .zip(other.min_bounds.iter())
92 .map(|(a, b)| a.min(*b))
93 .collect();
94
95 let max_bounds = self
96 .max_bounds
97 .iter()
98 .zip(other.max_bounds.iter())
99 .map(|(a, b)| a.max(*b))
100 .collect();
101
102 Rectangle::new(min_bounds, max_bounds)
103 }
104}
105
106pub struct KdTree {
108 root: Option<Box<KdNode>>,
109 dimension: usize,
110}
111
112#[derive(Debug)]
113struct KdNode {
114 point: Point,
115 data: usize, split_dimension: usize,
117 left: Option<Box<KdNode>>,
118 right: Option<Box<KdNode>>,
119}
120
121impl KdTree {
122 pub fn new(dimension: usize) -> Self {
123 Self {
124 root: None,
125 dimension,
126 }
127 }
128
129 pub fn from_points(points: Vec<(Point, usize)>) -> Self {
130 if points.is_empty() {
131 return Self::new(0);
132 }
133
134 let dimension = points[0].0.dimension();
135 let root = Self::build_tree(points, 0, dimension);
136
137 Self {
138 root: Some(root),
139 dimension,
140 }
141 }
142
143 fn build_tree(mut points: Vec<(Point, usize)>, depth: usize, dimension: usize) -> Box<KdNode> {
144 let split_dim = depth % dimension;
145
146 points.sort_by(|a, b| {
148 a.0.coordinates[split_dim]
149 .partial_cmp(&b.0.coordinates[split_dim])
150 .expect("operation should succeed")
151 });
152
153 let median = points.len() / 2;
154 let (point, data) = points.remove(median);
155
156 let left = if median > 0 {
157 Some(Self::build_tree(
158 points[..median].to_vec(),
159 depth + 1,
160 dimension,
161 ))
162 } else {
163 None
164 };
165
166 let right = if median < points.len() {
167 Some(Self::build_tree(
168 points[median..].to_vec(),
169 depth + 1,
170 dimension,
171 ))
172 } else {
173 None
174 };
175
176 Box::new(KdNode {
177 point,
178 data,
179 split_dimension: split_dim,
180 left,
181 right,
182 })
183 }
184
185 pub fn insert(&mut self, point: Point, data: usize) {
186 if self.root.is_none() {
187 self.dimension = point.dimension();
188 }
189
190 self.root = Some(Self::insert_recursive(
191 self.root.take(),
192 point,
193 data,
194 0,
195 self.dimension,
196 ));
197 }
198
199 fn insert_recursive(
200 node: Option<Box<KdNode>>,
201 point: Point,
202 data: usize,
203 depth: usize,
204 dimension: usize,
205 ) -> Box<KdNode> {
206 if let Some(mut existing) = node {
207 let split_dim = depth % dimension;
208
209 if point.coordinates[split_dim] <= existing.point.coordinates[split_dim] {
210 existing.left = Some(Self::insert_recursive(
211 existing.left.take(),
212 point,
213 data,
214 depth + 1,
215 dimension,
216 ));
217 } else {
218 existing.right = Some(Self::insert_recursive(
219 existing.right.take(),
220 point,
221 data,
222 depth + 1,
223 dimension,
224 ));
225 }
226
227 existing
228 } else {
229 Box::new(KdNode {
230 point,
231 data,
232 split_dimension: depth % dimension,
233 left: None,
234 right: None,
235 })
236 }
237 }
238
239 pub fn nearest_neighbor(&self, query: &Point) -> Option<(Point, usize, f64)> {
240 self.root.as_ref().map(|root| {
241 let mut best = (
242 root.point.clone(),
243 root.data,
244 query.distance_squared(&root.point),
245 );
246 Self::nearest_recursive(root, query, &mut best, 0);
247 (best.0, best.1, best.2.sqrt())
248 })
249 }
250
251 fn nearest_recursive(
252 node: &KdNode,
253 query: &Point,
254 best: &mut (Point, usize, f64),
255 _depth: usize,
256 ) {
257 let distance_sq = query.distance_squared(&node.point);
258 if distance_sq < best.2 {
259 *best = (node.point.clone(), node.data, distance_sq);
260 }
261
262 let split_dim = node.split_dimension;
263 let diff = query.coordinates[split_dim] - node.point.coordinates[split_dim];
264
265 let (primary, secondary) = if diff <= 0.0 {
266 (&node.left, &node.right)
267 } else {
268 (&node.right, &node.left)
269 };
270
271 if let Some(child) = primary {
272 Self::nearest_recursive(child, query, best, _depth + 1);
273 }
274
275 if diff * diff < best.2 {
277 if let Some(child) = secondary {
278 Self::nearest_recursive(child, query, best, _depth + 1);
279 }
280 }
281 }
282
283 pub fn range_query(&self, range: &Rectangle) -> Vec<(Point, usize)> {
284 let mut results = Vec::new();
285 if let Some(root) = &self.root {
286 Self::range_recursive(root, range, &mut results);
287 }
288 results
289 }
290
291 fn range_recursive(node: &KdNode, range: &Rectangle, results: &mut Vec<(Point, usize)>) {
292 if range.contains(&node.point) {
293 results.push((node.point.clone(), node.data));
294 }
295
296 let split_dim = node.split_dimension;
297
298 if range.min_bounds[split_dim] <= node.point.coordinates[split_dim] {
299 if let Some(left) = &node.left {
300 Self::range_recursive(left, range, results);
301 }
302 }
303
304 if range.max_bounds[split_dim] >= node.point.coordinates[split_dim] {
305 if let Some(right) = &node.right {
306 Self::range_recursive(right, range, results);
307 }
308 }
309 }
310}
311
312pub struct RTree {
314 root: Option<Box<RNode>>,
315 max_entries: usize,
316 #[allow(dead_code)]
317 min_entries: usize,
318}
319
320#[derive(Debug)]
321struct RNode {
322 bounds: Rectangle,
323 entries: Vec<REntry>,
324 is_leaf: bool,
325}
326
327#[derive(Debug)]
328enum REntry {
329 Leaf {
330 bounds: Rectangle,
331 data: usize,
332 },
333 #[allow(dead_code)]
334 Internal {
335 bounds: Rectangle,
336 child: Box<RNode>,
337 },
338}
339
340impl RTree {
341 pub fn new(max_entries: usize) -> Self {
342 let min_entries = max_entries / 2;
343 Self {
344 root: None,
345 max_entries,
346 min_entries,
347 }
348 }
349
350 pub fn insert(&mut self, bounds: Rectangle, data: usize) {
351 let entry = REntry::Leaf {
352 bounds: bounds.clone(),
353 data,
354 };
355
356 if self.root.is_none() {
357 self.root = Some(Box::new(RNode {
358 bounds,
359 entries: vec![entry],
360 is_leaf: true,
361 }));
362 } else {
363 self.insert_recursive(entry);
364 }
365 }
366
367 fn insert_recursive(&mut self, entry: REntry) {
368 if let Some(root) = &mut self.root {
370 if root.is_leaf && root.entries.len() < self.max_entries {
371 root.bounds = root.bounds.union(entry.bounds());
372 root.entries.push(entry);
373 }
374 }
375 }
376
377 pub fn query(&self, query_bounds: &Rectangle) -> Vec<usize> {
378 let mut results = Vec::new();
379 if let Some(root) = &self.root {
380 Self::query_recursive(root, query_bounds, &mut results);
381 }
382 results
383 }
384
385 fn query_recursive(node: &RNode, query_bounds: &Rectangle, results: &mut Vec<usize>) {
386 if !node.bounds.intersects(query_bounds) {
387 return;
388 }
389
390 for entry in &node.entries {
391 match entry {
392 REntry::Leaf { bounds, data } => {
393 if bounds.intersects(query_bounds) {
394 results.push(*data);
395 }
396 }
397 REntry::Internal { bounds, child } => {
398 if bounds.intersects(query_bounds) {
399 Self::query_recursive(child, query_bounds, results);
400 }
401 }
402 }
403 }
404 }
405}
406
407impl REntry {
408 fn bounds(&self) -> &Rectangle {
409 match self {
410 REntry::Leaf { bounds, .. } => bounds,
411 REntry::Internal { bounds, .. } => bounds,
412 }
413 }
414}
415
416pub struct QuadTree {
418 root: QuadNode,
419 max_points: usize,
420 max_depth: usize,
421}
422
423#[derive(Debug)]
424struct QuadNode {
425 bounds: Rectangle,
426 points: Vec<(Point, usize)>,
427 children: Option<[Box<QuadNode>; 4]>,
428 depth: usize,
429}
430
431impl QuadTree {
432 pub fn new(bounds: Rectangle, max_points: usize, max_depth: usize) -> Self {
433 assert_eq!(bounds.dimension(), 2, "QuadTree only supports 2D");
434
435 Self {
436 root: QuadNode {
437 bounds,
438 points: Vec::new(),
439 children: None,
440 depth: 0,
441 },
442 max_points,
443 max_depth,
444 }
445 }
446
447 pub fn insert(&mut self, point: Point, data: usize) {
448 assert_eq!(point.dimension(), 2, "QuadTree only supports 2D points");
449 self.root
450 .insert(point, data, self.max_points, self.max_depth);
451 }
452
453 pub fn query(&self, query_bounds: &Rectangle) -> Vec<(Point, usize)> {
454 let mut results = Vec::new();
455 self.root.query(query_bounds, &mut results);
456 results
457 }
458
459 pub fn nearest_neighbor(
460 &self,
461 query: &Point,
462 max_distance: Option<f64>,
463 ) -> Option<(Point, usize, f64)> {
464 self.root.nearest_neighbor(query, max_distance)
465 }
466}
467
468impl QuadNode {
469 fn insert(&mut self, point: Point, data: usize, max_points: usize, max_depth: usize) {
470 if !self.bounds.contains(&point) {
471 return;
472 }
473
474 if self.children.is_none() {
475 self.points.push((point, data));
476
477 if self.points.len() > max_points && self.depth < max_depth {
478 self.subdivide();
479
480 let points = std::mem::take(&mut self.points);
482 for (p, d) in points {
483 self.insert_into_children(p, d, max_points, max_depth);
484 }
485 }
486 } else {
487 self.insert_into_children(point, data, max_points, max_depth);
488 }
489 }
490
491 fn subdivide(&mut self) {
492 let mid_x = (self.bounds.min_bounds[0] + self.bounds.max_bounds[0]) / 2.0;
493 let mid_y = (self.bounds.min_bounds[1] + self.bounds.max_bounds[1]) / 2.0;
494
495 let nw = Rectangle::new(
496 vec![self.bounds.min_bounds[0], mid_y],
497 vec![mid_x, self.bounds.max_bounds[1]],
498 );
499 let ne = Rectangle::new(
500 vec![mid_x, mid_y],
501 vec![self.bounds.max_bounds[0], self.bounds.max_bounds[1]],
502 );
503 let sw = Rectangle::new(
504 vec![self.bounds.min_bounds[0], self.bounds.min_bounds[1]],
505 vec![mid_x, mid_y],
506 );
507 let se = Rectangle::new(
508 vec![mid_x, self.bounds.min_bounds[1]],
509 vec![self.bounds.max_bounds[0], mid_y],
510 );
511
512 self.children = Some([
513 Box::new(QuadNode {
514 bounds: nw,
515 points: Vec::new(),
516 children: None,
517 depth: self.depth + 1,
518 }),
519 Box::new(QuadNode {
520 bounds: ne,
521 points: Vec::new(),
522 children: None,
523 depth: self.depth + 1,
524 }),
525 Box::new(QuadNode {
526 bounds: sw,
527 points: Vec::new(),
528 children: None,
529 depth: self.depth + 1,
530 }),
531 Box::new(QuadNode {
532 bounds: se,
533 points: Vec::new(),
534 children: None,
535 depth: self.depth + 1,
536 }),
537 ]);
538 }
539
540 fn insert_into_children(
541 &mut self,
542 point: Point,
543 data: usize,
544 max_points: usize,
545 max_depth: usize,
546 ) {
547 if let Some(children) = &mut self.children {
548 for child in children.iter_mut() {
549 child.insert(point.clone(), data, max_points, max_depth);
550 }
551 }
552 }
553
554 fn query(&self, query_bounds: &Rectangle, results: &mut Vec<(Point, usize)>) {
555 if !self.bounds.intersects(query_bounds) {
556 return;
557 }
558
559 for (point, data) in &self.points {
560 if query_bounds.contains(point) {
561 results.push((point.clone(), *data));
562 }
563 }
564
565 if let Some(children) = &self.children {
566 for child in children.iter() {
567 child.query(query_bounds, results);
568 }
569 }
570 }
571
572 fn nearest_neighbor(
573 &self,
574 query: &Point,
575 max_distance: Option<f64>,
576 ) -> Option<(Point, usize, f64)> {
577 let mut best: Option<(Point, usize, f64)> = None;
578 let max_dist_sq = max_distance.map(|d| d * d);
579
580 for (point, data) in &self.points {
582 let dist_sq = query.distance_squared(point);
583
584 if let Some(max_sq) = max_dist_sq {
585 if dist_sq > max_sq {
586 continue;
587 }
588 }
589
590 if best.is_none() || dist_sq < best.as_ref().expect("operation should succeed").2 {
591 best = Some((point.clone(), *data, dist_sq));
592 }
593 }
594
595 if let Some(children) = &self.children {
597 for child in children.iter() {
598 if let Some(child_best) = child.nearest_neighbor(query, max_distance) {
599 if best.is_none()
600 || child_best.2 * child_best.2
601 < best.as_ref().expect("operation should succeed").2
602 {
603 best = Some((child_best.0, child_best.1, child_best.2 * child_best.2));
604 }
605 }
606 }
607 }
608
609 best.map(|(p, d, dist_sq)| (p, d, dist_sq.sqrt()))
610 }
611}
612
613pub struct OctTree {
615 root: OctNode,
616 max_points: usize,
617 max_depth: usize,
618}
619
620#[derive(Debug)]
621struct OctNode {
622 bounds: Rectangle,
623 points: Vec<(Point, usize)>,
624 children: Option<[Box<OctNode>; 8]>,
625 depth: usize,
626}
627
628impl OctTree {
629 pub fn new(bounds: Rectangle, max_points: usize, max_depth: usize) -> Self {
630 assert_eq!(bounds.dimension(), 3, "OctTree only supports 3D");
631
632 Self {
633 root: OctNode {
634 bounds,
635 points: Vec::new(),
636 children: None,
637 depth: 0,
638 },
639 max_points,
640 max_depth,
641 }
642 }
643
644 pub fn insert(&mut self, point: Point, data: usize) {
645 assert_eq!(point.dimension(), 3, "OctTree only supports 3D points");
646 self.root
647 .insert(point, data, self.max_points, self.max_depth);
648 }
649
650 pub fn query(&self, query_bounds: &Rectangle) -> Vec<(Point, usize)> {
651 let mut results = Vec::new();
652 self.root.query(query_bounds, &mut results);
653 results
654 }
655}
656
657impl OctNode {
658 fn insert(&mut self, point: Point, data: usize, max_points: usize, max_depth: usize) {
659 if !self.bounds.contains(&point) {
660 return;
661 }
662
663 if self.children.is_none() {
664 self.points.push((point, data));
665
666 if self.points.len() > max_points && self.depth < max_depth {
667 self.subdivide();
668
669 let points = std::mem::take(&mut self.points);
671 for (p, d) in points {
672 self.insert_into_children(p, d, max_points, max_depth);
673 }
674 }
675 } else {
676 self.insert_into_children(point, data, max_points, max_depth);
677 }
678 }
679
680 fn subdivide(&mut self) {
681 let mid_x = (self.bounds.min_bounds[0] + self.bounds.max_bounds[0]) / 2.0;
682 let mid_y = (self.bounds.min_bounds[1] + self.bounds.max_bounds[1]) / 2.0;
683 let mid_z = (self.bounds.min_bounds[2] + self.bounds.max_bounds[2]) / 2.0;
684
685 let mut children = Vec::with_capacity(8);
687
688 for &z_low in &[true, false] {
689 for &y_low in &[true, false] {
690 for &x_low in &[true, false] {
691 let min_bounds = vec![
692 if x_low {
693 self.bounds.min_bounds[0]
694 } else {
695 mid_x
696 },
697 if y_low {
698 self.bounds.min_bounds[1]
699 } else {
700 mid_y
701 },
702 if z_low {
703 self.bounds.min_bounds[2]
704 } else {
705 mid_z
706 },
707 ];
708 let max_bounds = vec![
709 if x_low {
710 mid_x
711 } else {
712 self.bounds.max_bounds[0]
713 },
714 if y_low {
715 mid_y
716 } else {
717 self.bounds.max_bounds[1]
718 },
719 if z_low {
720 mid_z
721 } else {
722 self.bounds.max_bounds[2]
723 },
724 ];
725
726 children.push(Box::new(OctNode {
727 bounds: Rectangle::new(min_bounds, max_bounds),
728 points: Vec::new(),
729 children: None,
730 depth: self.depth + 1,
731 }));
732 }
733 }
734 }
735
736 self.children = Some(children.try_into().expect("operation should succeed"));
737 }
738
739 fn insert_into_children(
740 &mut self,
741 point: Point,
742 data: usize,
743 max_points: usize,
744 max_depth: usize,
745 ) {
746 if let Some(children) = &mut self.children {
747 for child in children.iter_mut() {
748 child.insert(point.clone(), data, max_points, max_depth);
749 }
750 }
751 }
752
753 fn query(&self, query_bounds: &Rectangle, results: &mut Vec<(Point, usize)>) {
754 if !self.bounds.intersects(query_bounds) {
755 return;
756 }
757
758 for (point, data) in &self.points {
759 if query_bounds.contains(point) {
760 results.push((point.clone(), *data));
761 }
762 }
763
764 if let Some(children) = &self.children {
765 for child in children.iter() {
766 child.query(query_bounds, results);
767 }
768 }
769 }
770}
771
772pub struct SpatialHash {
774 grid: std::collections::HashMap<(i32, i32), Vec<(Point, usize)>>,
775 cell_size: f64,
776 bounds: Rectangle,
777}
778
779impl SpatialHash {
780 pub fn new(bounds: Rectangle, cell_size: f64) -> Self {
781 assert_eq!(bounds.dimension(), 2, "SpatialHash only supports 2D");
782
783 Self {
784 grid: std::collections::HashMap::new(),
785 cell_size,
786 bounds,
787 }
788 }
789
790 fn hash_point(&self, point: &Point) -> (i32, i32) {
791 let x =
792 ((point.coordinates[0] - self.bounds.min_bounds[0]) / self.cell_size).floor() as i32;
793 let y =
794 ((point.coordinates[1] - self.bounds.min_bounds[1]) / self.cell_size).floor() as i32;
795 (x, y)
796 }
797
798 pub fn insert(&mut self, point: Point, data: usize) {
799 let hash = self.hash_point(&point);
800 self.grid.entry(hash).or_default().push((point, data));
801 }
802
803 pub fn query_radius(&self, center: &Point, radius: f64) -> Vec<(Point, usize)> {
804 let mut results = Vec::new();
805 let radius_sq = radius * radius;
806
807 let cells_to_check = ((radius / self.cell_size).ceil() as i32) + 1;
809 let center_hash = self.hash_point(center);
810
811 for dx in -cells_to_check..=cells_to_check {
812 for dy in -cells_to_check..=cells_to_check {
813 let hash = (center_hash.0 + dx, center_hash.1 + dy);
814
815 if let Some(points) = self.grid.get(&hash) {
816 for (point, data) in points {
817 if center.distance_squared(point) <= radius_sq {
818 results.push((point.clone(), *data));
819 }
820 }
821 }
822 }
823 }
824
825 results
826 }
827
828 pub fn clear(&mut self) {
829 self.grid.clear();
830 }
831
832 pub fn stats(&self) -> SpatialHashStats {
833 let total_points: usize = self.grid.values().map(|v| v.len()).sum();
834 let occupied_cells = self.grid.len();
835 let max_points_per_cell = self.grid.values().map(|v| v.len()).max().unwrap_or(0);
836 let avg_points_per_cell = if occupied_cells > 0 {
837 total_points as f64 / occupied_cells as f64
838 } else {
839 0.0
840 };
841
842 SpatialHashStats {
843 total_points,
844 occupied_cells,
845 max_points_per_cell,
846 avg_points_per_cell,
847 cell_size: self.cell_size,
848 }
849 }
850}
851
852#[derive(Debug, Clone)]
853pub struct SpatialHashStats {
854 pub total_points: usize,
855 pub occupied_cells: usize,
856 pub max_points_per_cell: usize,
857 pub avg_points_per_cell: f64,
858 pub cell_size: f64,
859}
860
861pub mod geographic {
863 use super::{Point, Rectangle};
864 use std::f64::consts::PI;
865
866 #[derive(Debug, Clone, PartialEq)]
868 pub enum CoordinateSystem {
869 LatLon,
871 UTM { zone: u8, hemisphere: Hemisphere },
873 WebMercator,
875 EPSG(u32),
877 }
878
879 #[derive(Debug, Clone, PartialEq)]
880 pub enum Hemisphere {
881 North,
882 South,
883 }
884
885 #[derive(Debug, Clone, PartialEq)]
887 pub struct GeoPoint {
888 pub latitude: f64,
889 pub longitude: f64,
890 pub altitude: Option<f64>,
891 pub coordinate_system: CoordinateSystem,
892 }
893
894 impl GeoPoint {
895 pub fn new(latitude: f64, longitude: f64) -> Self {
897 Self {
898 latitude,
899 longitude,
900 altitude: None,
901 coordinate_system: CoordinateSystem::LatLon,
902 }
903 }
904
905 pub fn with_altitude(latitude: f64, longitude: f64, altitude: f64) -> Self {
907 Self {
908 latitude,
909 longitude,
910 altitude: Some(altitude),
911 coordinate_system: CoordinateSystem::LatLon,
912 }
913 }
914
915 pub fn with_coordinate_system(mut self, coord_sys: CoordinateSystem) -> Self {
917 self.coordinate_system = coord_sys;
918 self
919 }
920
921 pub fn haversine_distance(&self, other: &GeoPoint) -> f64 {
923 const EARTH_RADIUS_M: f64 = 6_371_000.0;
924
925 let lat1_rad = self.latitude.to_radians();
926 let lat2_rad = other.latitude.to_radians();
927 let dlat_rad = (other.latitude - self.latitude).to_radians();
928 let dlon_rad = (other.longitude - self.longitude).to_radians();
929
930 let a = (dlat_rad / 2.0).sin().powi(2)
931 + lat1_rad.cos() * lat2_rad.cos() * (dlon_rad / 2.0).sin().powi(2);
932
933 let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
934 EARTH_RADIUS_M * c
935 }
936
937 pub fn bearing_to(&self, other: &GeoPoint) -> f64 {
939 let lat1_rad = self.latitude.to_radians();
940 let lat2_rad = other.latitude.to_radians();
941 let dlon_rad = (other.longitude - self.longitude).to_radians();
942
943 let y = dlon_rad.sin() * lat2_rad.cos();
944 let x =
945 lat1_rad.cos() * lat2_rad.sin() - lat1_rad.sin() * lat2_rad.cos() * dlon_rad.cos();
946
947 let bearing_rad = y.atan2(x);
948 (bearing_rad.to_degrees() + 360.0) % 360.0
949 }
950
951 pub fn destination_point(&self, distance_m: f64, bearing_deg: f64) -> GeoPoint {
953 const EARTH_RADIUS_M: f64 = 6_371_000.0;
954
955 let lat1_rad = self.latitude.to_radians();
956 let lon1_rad = self.longitude.to_radians();
957 let bearing_rad = bearing_deg.to_radians();
958 let angular_distance = distance_m / EARTH_RADIUS_M;
959
960 let lat2_rad = (lat1_rad.sin() * angular_distance.cos()
961 + lat1_rad.cos() * angular_distance.sin() * bearing_rad.cos())
962 .asin();
963
964 let lon2_rad = lon1_rad
965 + (bearing_rad.sin() * angular_distance.sin() * lat1_rad.cos())
966 .atan2(angular_distance.cos() - lat1_rad.sin() * lat2_rad.sin());
967
968 GeoPoint::new(lat2_rad.to_degrees(), lon2_rad.to_degrees())
969 }
970
971 pub fn to_point(&self) -> Point {
973 if let Some(alt) = self.altitude {
974 Point::new(vec![self.longitude, self.latitude, alt])
975 } else {
976 Point::new(vec![self.longitude, self.latitude])
977 }
978 }
979
980 pub fn is_within_bounds(&self, bounds: &GeoBounds) -> bool {
982 self.latitude >= bounds.south
983 && self.latitude <= bounds.north
984 && self.longitude >= bounds.west
985 && self.longitude <= bounds.east
986 }
987
988 pub fn to_web_mercator(&self) -> Point {
990 const EARTH_RADIUS: f64 = 6_378_137.0;
991
992 let x = self.longitude.to_radians() * EARTH_RADIUS;
993 let y = ((PI / 4.0 + self.latitude.to_radians() / 2.0).tan().ln()) * EARTH_RADIUS;
994
995 Point::new(vec![x, y])
996 }
997 }
998
999 #[derive(Debug, Clone, PartialEq)]
1001 pub struct GeoBounds {
1002 pub north: f64,
1003 pub south: f64,
1004 pub east: f64,
1005 pub west: f64,
1006 }
1007
1008 impl GeoBounds {
1009 pub fn new(north: f64, south: f64, east: f64, west: f64) -> Self {
1011 Self {
1012 north,
1013 south,
1014 east,
1015 west,
1016 }
1017 }
1018
1019 pub fn from_center_radius(center: &GeoPoint, radius_m: f64) -> Self {
1021 const EARTH_RADIUS_M: f64 = 6_371_000.0;
1022
1023 let lat_offset = (radius_m / EARTH_RADIUS_M).to_degrees();
1024 let lon_offset =
1025 (radius_m / (EARTH_RADIUS_M * center.latitude.to_radians().cos())).to_degrees();
1026
1027 Self {
1028 north: center.latitude + lat_offset,
1029 south: center.latitude - lat_offset,
1030 east: center.longitude + lon_offset,
1031 west: center.longitude - lon_offset,
1032 }
1033 }
1034
1035 pub fn contains(&self, point: &GeoPoint) -> bool {
1037 point.is_within_bounds(self)
1038 }
1039
1040 pub fn intersects(&self, other: &GeoBounds) -> bool {
1042 !(self.east < other.west
1043 || self.west > other.east
1044 || self.north < other.south
1045 || self.south > other.north)
1046 }
1047
1048 pub fn area_square_meters(&self) -> f64 {
1050 const EARTH_RADIUS_M: f64 = 6_371_000.0;
1051
1052 let lat_range_rad = (self.north - self.south).to_radians();
1053 let lon_range_rad = (self.east - self.west).to_radians();
1054 let avg_lat_rad = ((self.north + self.south) / 2.0).to_radians();
1055
1056 EARTH_RADIUS_M.powi(2) * lat_range_rad * lon_range_rad * avg_lat_rad.cos()
1057 }
1058
1059 pub fn to_rectangle(&self) -> Rectangle {
1061 Rectangle::new(vec![self.west, self.south], vec![self.east, self.north])
1062 }
1063 }
1064
1065 pub struct GeoUtils;
1067
1068 impl GeoUtils {
1069 pub fn deg_to_rad(degrees: f64) -> f64 {
1071 degrees * PI / 180.0
1072 }
1073
1074 pub fn rad_to_deg(radians: f64) -> f64 {
1076 radians * 180.0 / PI
1077 }
1078
1079 pub fn normalize_longitude(lon: f64) -> f64 {
1081 let mut normalized = lon % 360.0;
1082 if normalized > 180.0 {
1083 normalized -= 360.0;
1084 } else if normalized < -180.0 {
1085 normalized += 360.0;
1086 }
1087 normalized
1088 }
1089
1090 pub fn normalize_latitude(lat: f64) -> f64 {
1092 lat.clamp(-90.0, 90.0)
1093 }
1094
1095 pub fn centroid(points: &[GeoPoint]) -> Option<GeoPoint> {
1097 if points.is_empty() {
1098 return None;
1099 }
1100
1101 let mut x_sum = 0.0;
1102 let mut y_sum = 0.0;
1103 let mut z_sum = 0.0;
1104
1105 for point in points {
1106 let lat_rad = point.latitude.to_radians();
1107 let lon_rad = point.longitude.to_radians();
1108
1109 x_sum += lat_rad.cos() * lon_rad.cos();
1110 y_sum += lat_rad.cos() * lon_rad.sin();
1111 z_sum += lat_rad.sin();
1112 }
1113
1114 let count = points.len() as f64;
1115 let x_avg = x_sum / count;
1116 let y_avg = y_sum / count;
1117 let z_avg = z_sum / count;
1118
1119 let lon_rad = y_avg.atan2(x_avg);
1120 let hyp = (x_avg * x_avg + y_avg * y_avg).sqrt();
1121 let lat_rad = z_avg.atan2(hyp);
1122
1123 Some(GeoPoint::new(lat_rad.to_degrees(), lon_rad.to_degrees()))
1124 }
1125
1126 pub fn polygon_area(vertices: &[GeoPoint]) -> f64 {
1128 if vertices.len() < 3 {
1129 return 0.0;
1130 }
1131
1132 const EARTH_RADIUS_M: f64 = 6_371_000.0;
1133 let mut area = 0.0;
1134
1135 for i in 0..vertices.len() {
1136 let j = (i + 1) % vertices.len();
1137 let lat1 = vertices[i].latitude.to_radians();
1138 let lat2 = vertices[j].latitude.to_radians();
1139 let lon_diff = (vertices[j].longitude - vertices[i].longitude).to_radians();
1140
1141 area += lon_diff * (2.0 + lat1.sin() + lat2.sin());
1142 }
1143
1144 (area.abs() / 2.0) * EARTH_RADIUS_M.powi(2)
1145 }
1146
1147 pub fn point_in_polygon(point: &GeoPoint, polygon: &[GeoPoint]) -> bool {
1149 if polygon.len() < 3 {
1150 return false;
1151 }
1152
1153 let mut inside = false;
1154 let mut j = polygon.len() - 1;
1155
1156 for i in 0..polygon.len() {
1157 if ((polygon[i].latitude > point.latitude)
1158 != (polygon[j].latitude > point.latitude))
1159 && (point.longitude
1160 < (polygon[j].longitude - polygon[i].longitude)
1161 * (point.latitude - polygon[i].latitude)
1162 / (polygon[j].latitude - polygon[i].latitude)
1163 + polygon[i].longitude)
1164 {
1165 inside = !inside;
1166 }
1167 j = i;
1168 }
1169
1170 inside
1171 }
1172
1173 pub fn closest_point_on_line(
1175 point: &GeoPoint,
1176 line_start: &GeoPoint,
1177 line_end: &GeoPoint,
1178 ) -> GeoPoint {
1179 let lat_start = line_start.latitude.to_radians();
1180 let lon_start = line_start.longitude.to_radians();
1181 let lat_end = line_end.latitude.to_radians();
1182 let lon_end = line_end.longitude.to_radians();
1183 let lat_point = point.latitude.to_radians();
1184 let lon_point = point.longitude.to_radians();
1185
1186 let x1 = lat_start.cos() * lon_start.cos();
1188 let y1 = lat_start.cos() * lon_start.sin();
1189 let z1 = lat_start.sin();
1190
1191 let x2 = lat_end.cos() * lon_end.cos();
1192 let y2 = lat_end.cos() * lon_end.sin();
1193 let z2 = lat_end.sin();
1194
1195 let x0 = lat_point.cos() * lon_point.cos();
1196 let y0 = lat_point.cos() * lon_point.sin();
1197 let z0 = lat_point.sin();
1198
1199 let dot_product = x0 * (x2 - x1) + y0 * (y2 - y1) + z0 * (z2 - z1);
1201 let line_length_sq = (x2 - x1).powi(2) + (y2 - y1).powi(2) + (z2 - z1).powi(2);
1202
1203 let t = if line_length_sq == 0.0 {
1204 0.0
1205 } else {
1206 (dot_product / line_length_sq).clamp(0.0, 1.0)
1207 };
1208
1209 let closest_x = x1 + t * (x2 - x1);
1210 let closest_y = y1 + t * (y2 - y1);
1211 let closest_z = z1 + t * (z2 - z1);
1212
1213 let closest_lon = closest_y.atan2(closest_x).to_degrees();
1215 let closest_lat = closest_z
1216 .atan2((closest_x.powi(2) + closest_y.powi(2)).sqrt())
1217 .to_degrees();
1218
1219 GeoPoint::new(closest_lat, closest_lon)
1220 }
1221
1222 pub fn great_circle_distance(point1: &GeoPoint, point2: &GeoPoint) -> f64 {
1224 point1.haversine_distance(point2)
1225 }
1226
1227 pub fn decimal_to_dms(decimal: f64) -> (i32, i32, f64) {
1229 let degrees = decimal.trunc() as i32;
1230 let minutes_float = (decimal.abs() - degrees.abs() as f64) * 60.0;
1231 let minutes = minutes_float.trunc() as i32;
1232 let seconds = (minutes_float - minutes as f64) * 60.0;
1233 (degrees, minutes, seconds)
1234 }
1235
1236 pub fn dms_to_decimal(degrees: i32, minutes: i32, seconds: f64) -> f64 {
1238 let sign = if degrees < 0 { -1.0 } else { 1.0 };
1239 degrees.abs() as f64 + minutes as f64 / 60.0 + seconds / 3600.0 * sign
1240 }
1241 }
1242
1243 #[allow(non_snake_case)]
1244 #[cfg(test)]
1245 mod geo_tests {
1246 use super::*;
1247
1248 #[test]
1249 fn test_geopoint_haversine_distance() {
1250 let london = GeoPoint::new(51.5074, -0.1278);
1251 let paris = GeoPoint::new(48.8566, 2.3522);
1252
1253 let distance = london.haversine_distance(&paris);
1254 assert!((distance - 344_000.0).abs() < 10_000.0);
1256 }
1257
1258 #[test]
1259 fn test_geopoint_bearing() {
1260 let start = GeoPoint::new(51.0, 0.0);
1261 let end = GeoPoint::new(52.0, 1.0);
1262
1263 let bearing = start.bearing_to(&end);
1264 assert!((0.0..360.0).contains(&bearing));
1265 }
1266
1267 #[test]
1268 fn test_geopoint_destination() {
1269 let start = GeoPoint::new(51.0, 0.0);
1270 let destination = start.destination_point(100_000.0, 90.0); assert!((destination.latitude - start.latitude).abs() < 0.1);
1274 assert!(destination.longitude > start.longitude);
1275 }
1276
1277 #[test]
1278 fn test_geobounds() {
1279 let bounds = GeoBounds::new(52.0, 50.0, 2.0, 0.0);
1280 let point_inside = GeoPoint::new(51.0, 1.0);
1281 let point_outside = GeoPoint::new(53.0, 3.0);
1282
1283 assert!(bounds.contains(&point_inside));
1284 assert!(!bounds.contains(&point_outside));
1285 }
1286
1287 #[test]
1288 fn test_geoutils_centroid() {
1289 let points = vec![
1290 GeoPoint::new(0.0, 0.0),
1291 GeoPoint::new(1.0, 0.0),
1292 GeoPoint::new(0.0, 1.0),
1293 GeoPoint::new(1.0, 1.0),
1294 ];
1295
1296 let centroid = GeoUtils::centroid(&points).expect("operation should succeed");
1297 assert!((centroid.latitude - 0.5).abs() < 0.1);
1298 assert!((centroid.longitude - 0.5).abs() < 0.1);
1299 }
1300
1301 #[test]
1302 fn test_point_in_polygon() {
1303 let polygon = vec![
1304 GeoPoint::new(0.0, 0.0),
1305 GeoPoint::new(2.0, 0.0),
1306 GeoPoint::new(2.0, 2.0),
1307 GeoPoint::new(0.0, 2.0),
1308 ];
1309
1310 let inside_point = GeoPoint::new(1.0, 1.0);
1311 let outside_point = GeoPoint::new(3.0, 3.0);
1312
1313 assert!(GeoUtils::point_in_polygon(&inside_point, &polygon));
1314 assert!(!GeoUtils::point_in_polygon(&outside_point, &polygon));
1315 }
1316
1317 #[test]
1318 fn test_normalize_coordinates() {
1319 assert_eq!(GeoUtils::normalize_longitude(380.0), 20.0);
1320 assert_eq!(GeoUtils::normalize_longitude(-200.0), 160.0);
1321 assert_eq!(GeoUtils::normalize_latitude(100.0), 90.0);
1322 assert_eq!(GeoUtils::normalize_latitude(-100.0), -90.0);
1323 }
1324
1325 #[test]
1326 fn test_dms_conversion() {
1327 let decimal = 51.5074;
1328 let (degrees, minutes, seconds) = GeoUtils::decimal_to_dms(decimal);
1329 let converted_back = GeoUtils::dms_to_decimal(degrees, minutes, seconds);
1330
1331 assert!((decimal - converted_back).abs() < 0.0001);
1332 }
1333 }
1334}
1335
1336#[allow(non_snake_case)]
1337#[cfg(test)]
1338mod tests {
1339 use super::*;
1340
1341 #[test]
1342 fn test_point_operations() {
1343 let p1 = Point::new(vec![1.0, 2.0, 3.0]);
1344 let p2 = Point::new(vec![4.0, 5.0, 6.0]);
1345
1346 assert_eq!(p1.dimension(), 3);
1347 assert_eq!(p1.distance_squared(&p2), 27.0); assert_eq!(p1.distance(&p2), 27.0_f64.sqrt());
1349 }
1350
1351 #[test]
1352 fn test_rectangle_operations() {
1353 let rect = Rectangle::new(vec![0.0, 0.0], vec![10.0, 10.0]);
1354 let point_inside = Point::new(vec![5.0, 5.0]);
1355 let point_outside = Point::new(vec![15.0, 15.0]);
1356
1357 assert!(rect.contains(&point_inside));
1358 assert!(!rect.contains(&point_outside));
1359 assert_eq!(rect.area(), 100.0);
1360
1361 let other_rect = Rectangle::new(vec![5.0, 5.0], vec![15.0, 15.0]);
1362 assert!(rect.intersects(&other_rect));
1363
1364 let union = rect.union(&other_rect);
1365 assert_eq!(union.min_bounds, vec![0.0, 0.0]);
1366 assert_eq!(union.max_bounds, vec![15.0, 15.0]);
1367 }
1368
1369 #[test]
1370 fn test_kdtree() {
1371 let points = vec![
1372 (Point::new(vec![2.0, 3.0]), 0),
1373 (Point::new(vec![5.0, 4.0]), 1),
1374 (Point::new(vec![9.0, 6.0]), 2),
1375 (Point::new(vec![4.0, 7.0]), 3),
1376 (Point::new(vec![8.0, 1.0]), 4),
1377 (Point::new(vec![7.0, 2.0]), 5),
1378 ];
1379
1380 let kd_tree = KdTree::from_points(points);
1381
1382 let query = Point::new(vec![5.0, 5.0]);
1383 let result = kd_tree.nearest_neighbor(&query);
1384
1385 assert!(result.is_some());
1386 let (_, data, _) = result.expect("operation should succeed");
1387 assert!(data <= 5);
1389
1390 let range = Rectangle::new(vec![0.0, 0.0], vec![6.0, 6.0]);
1392 let range_results = kd_tree.range_query(&range);
1393 assert!(!range_results.is_empty());
1394 }
1395
1396 #[test]
1397 fn test_quadtree() {
1398 let bounds = Rectangle::new(vec![0.0, 0.0], vec![100.0, 100.0]);
1399 let mut quad_tree = QuadTree::new(bounds, 4, 6);
1400
1401 quad_tree.insert(Point::new(vec![10.0, 10.0]), 0);
1403 quad_tree.insert(Point::new(vec![20.0, 20.0]), 1);
1404 quad_tree.insert(Point::new(vec![80.0, 80.0]), 2);
1405 quad_tree.insert(Point::new(vec![90.0, 90.0]), 3);
1406
1407 let query_bounds = Rectangle::new(vec![5.0, 5.0], vec![25.0, 25.0]);
1409 let results = quad_tree.query(&query_bounds);
1410
1411 assert_eq!(results.len(), 2); let query_point = Point::new(vec![15.0, 15.0]);
1415 let nearest = quad_tree.nearest_neighbor(&query_point, None);
1416 assert!(nearest.is_some());
1417 }
1418
1419 #[test]
1420 fn test_spatial_hash() {
1421 let bounds = Rectangle::new(vec![0.0, 0.0], vec![100.0, 100.0]);
1422 let mut spatial_hash = SpatialHash::new(bounds, 10.0);
1423
1424 spatial_hash.insert(Point::new(vec![15.0, 15.0]), 0);
1426 spatial_hash.insert(Point::new(vec![25.0, 25.0]), 1);
1427 spatial_hash.insert(Point::new(vec![85.0, 85.0]), 2);
1428
1429 let center = Point::new(vec![20.0, 20.0]);
1431 let results = spatial_hash.query_radius(¢er, 10.0);
1432
1433 assert!(!results.is_empty());
1434
1435 let stats = spatial_hash.stats();
1436 assert_eq!(stats.total_points, 3);
1437 }
1438
1439 #[test]
1440 fn test_octree() {
1441 let bounds = Rectangle::new(vec![0.0, 0.0, 0.0], vec![100.0, 100.0, 100.0]);
1442 let mut oct_tree = OctTree::new(bounds, 4, 6);
1443
1444 oct_tree.insert(Point::new(vec![10.0, 10.0, 10.0]), 0);
1446 oct_tree.insert(Point::new(vec![20.0, 20.0, 20.0]), 1);
1447 oct_tree.insert(Point::new(vec![80.0, 80.0, 80.0]), 2);
1448
1449 let query_bounds = Rectangle::new(vec![5.0, 5.0, 5.0], vec![25.0, 25.0, 25.0]);
1451 let results = oct_tree.query(&query_bounds);
1452
1453 assert_eq!(results.len(), 2); }
1455}