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 .unwrap()
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().unwrap().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() || child_best.2 * child_best.2 < best.as_ref().unwrap().2 {
600 best = Some((child_best.0, child_best.1, child_best.2 * child_best.2));
601 }
602 }
603 }
604 }
605
606 best.map(|(p, d, dist_sq)| (p, d, dist_sq.sqrt()))
607 }
608}
609
610pub struct OctTree {
612 root: OctNode,
613 max_points: usize,
614 max_depth: usize,
615}
616
617#[derive(Debug)]
618struct OctNode {
619 bounds: Rectangle,
620 points: Vec<(Point, usize)>,
621 children: Option<[Box<OctNode>; 8]>,
622 depth: usize,
623}
624
625impl OctTree {
626 pub fn new(bounds: Rectangle, max_points: usize, max_depth: usize) -> Self {
627 assert_eq!(bounds.dimension(), 3, "OctTree only supports 3D");
628
629 Self {
630 root: OctNode {
631 bounds,
632 points: Vec::new(),
633 children: None,
634 depth: 0,
635 },
636 max_points,
637 max_depth,
638 }
639 }
640
641 pub fn insert(&mut self, point: Point, data: usize) {
642 assert_eq!(point.dimension(), 3, "OctTree only supports 3D points");
643 self.root
644 .insert(point, data, self.max_points, self.max_depth);
645 }
646
647 pub fn query(&self, query_bounds: &Rectangle) -> Vec<(Point, usize)> {
648 let mut results = Vec::new();
649 self.root.query(query_bounds, &mut results);
650 results
651 }
652}
653
654impl OctNode {
655 fn insert(&mut self, point: Point, data: usize, max_points: usize, max_depth: usize) {
656 if !self.bounds.contains(&point) {
657 return;
658 }
659
660 if self.children.is_none() {
661 self.points.push((point, data));
662
663 if self.points.len() > max_points && self.depth < max_depth {
664 self.subdivide();
665
666 let points = std::mem::take(&mut self.points);
668 for (p, d) in points {
669 self.insert_into_children(p, d, max_points, max_depth);
670 }
671 }
672 } else {
673 self.insert_into_children(point, data, max_points, max_depth);
674 }
675 }
676
677 fn subdivide(&mut self) {
678 let mid_x = (self.bounds.min_bounds[0] + self.bounds.max_bounds[0]) / 2.0;
679 let mid_y = (self.bounds.min_bounds[1] + self.bounds.max_bounds[1]) / 2.0;
680 let mid_z = (self.bounds.min_bounds[2] + self.bounds.max_bounds[2]) / 2.0;
681
682 let mut children = Vec::with_capacity(8);
684
685 for &z_low in &[true, false] {
686 for &y_low in &[true, false] {
687 for &x_low in &[true, false] {
688 let min_bounds = vec![
689 if x_low {
690 self.bounds.min_bounds[0]
691 } else {
692 mid_x
693 },
694 if y_low {
695 self.bounds.min_bounds[1]
696 } else {
697 mid_y
698 },
699 if z_low {
700 self.bounds.min_bounds[2]
701 } else {
702 mid_z
703 },
704 ];
705 let max_bounds = vec![
706 if x_low {
707 mid_x
708 } else {
709 self.bounds.max_bounds[0]
710 },
711 if y_low {
712 mid_y
713 } else {
714 self.bounds.max_bounds[1]
715 },
716 if z_low {
717 mid_z
718 } else {
719 self.bounds.max_bounds[2]
720 },
721 ];
722
723 children.push(Box::new(OctNode {
724 bounds: Rectangle::new(min_bounds, max_bounds),
725 points: Vec::new(),
726 children: None,
727 depth: self.depth + 1,
728 }));
729 }
730 }
731 }
732
733 self.children = Some(children.try_into().unwrap());
734 }
735
736 fn insert_into_children(
737 &mut self,
738 point: Point,
739 data: usize,
740 max_points: usize,
741 max_depth: usize,
742 ) {
743 if let Some(children) = &mut self.children {
744 for child in children.iter_mut() {
745 child.insert(point.clone(), data, max_points, max_depth);
746 }
747 }
748 }
749
750 fn query(&self, query_bounds: &Rectangle, results: &mut Vec<(Point, usize)>) {
751 if !self.bounds.intersects(query_bounds) {
752 return;
753 }
754
755 for (point, data) in &self.points {
756 if query_bounds.contains(point) {
757 results.push((point.clone(), *data));
758 }
759 }
760
761 if let Some(children) = &self.children {
762 for child in children.iter() {
763 child.query(query_bounds, results);
764 }
765 }
766 }
767}
768
769pub struct SpatialHash {
771 grid: std::collections::HashMap<(i32, i32), Vec<(Point, usize)>>,
772 cell_size: f64,
773 bounds: Rectangle,
774}
775
776impl SpatialHash {
777 pub fn new(bounds: Rectangle, cell_size: f64) -> Self {
778 assert_eq!(bounds.dimension(), 2, "SpatialHash only supports 2D");
779
780 Self {
781 grid: std::collections::HashMap::new(),
782 cell_size,
783 bounds,
784 }
785 }
786
787 fn hash_point(&self, point: &Point) -> (i32, i32) {
788 let x =
789 ((point.coordinates[0] - self.bounds.min_bounds[0]) / self.cell_size).floor() as i32;
790 let y =
791 ((point.coordinates[1] - self.bounds.min_bounds[1]) / self.cell_size).floor() as i32;
792 (x, y)
793 }
794
795 pub fn insert(&mut self, point: Point, data: usize) {
796 let hash = self.hash_point(&point);
797 self.grid.entry(hash).or_default().push((point, data));
798 }
799
800 pub fn query_radius(&self, center: &Point, radius: f64) -> Vec<(Point, usize)> {
801 let mut results = Vec::new();
802 let radius_sq = radius * radius;
803
804 let cells_to_check = ((radius / self.cell_size).ceil() as i32) + 1;
806 let center_hash = self.hash_point(center);
807
808 for dx in -cells_to_check..=cells_to_check {
809 for dy in -cells_to_check..=cells_to_check {
810 let hash = (center_hash.0 + dx, center_hash.1 + dy);
811
812 if let Some(points) = self.grid.get(&hash) {
813 for (point, data) in points {
814 if center.distance_squared(point) <= radius_sq {
815 results.push((point.clone(), *data));
816 }
817 }
818 }
819 }
820 }
821
822 results
823 }
824
825 pub fn clear(&mut self) {
826 self.grid.clear();
827 }
828
829 pub fn stats(&self) -> SpatialHashStats {
830 let total_points: usize = self.grid.values().map(|v| v.len()).sum();
831 let occupied_cells = self.grid.len();
832 let max_points_per_cell = self.grid.values().map(|v| v.len()).max().unwrap_or(0);
833 let avg_points_per_cell = if occupied_cells > 0 {
834 total_points as f64 / occupied_cells as f64
835 } else {
836 0.0
837 };
838
839 SpatialHashStats {
840 total_points,
841 occupied_cells,
842 max_points_per_cell,
843 avg_points_per_cell,
844 cell_size: self.cell_size,
845 }
846 }
847}
848
849#[derive(Debug, Clone)]
850pub struct SpatialHashStats {
851 pub total_points: usize,
852 pub occupied_cells: usize,
853 pub max_points_per_cell: usize,
854 pub avg_points_per_cell: f64,
855 pub cell_size: f64,
856}
857
858pub mod geographic {
860 use super::{Point, Rectangle};
861 use std::f64::consts::PI;
862
863 #[derive(Debug, Clone, PartialEq)]
865 pub enum CoordinateSystem {
866 LatLon,
868 UTM { zone: u8, hemisphere: Hemisphere },
870 WebMercator,
872 EPSG(u32),
874 }
875
876 #[derive(Debug, Clone, PartialEq)]
877 pub enum Hemisphere {
878 North,
879 South,
880 }
881
882 #[derive(Debug, Clone, PartialEq)]
884 pub struct GeoPoint {
885 pub latitude: f64,
886 pub longitude: f64,
887 pub altitude: Option<f64>,
888 pub coordinate_system: CoordinateSystem,
889 }
890
891 impl GeoPoint {
892 pub fn new(latitude: f64, longitude: f64) -> Self {
894 Self {
895 latitude,
896 longitude,
897 altitude: None,
898 coordinate_system: CoordinateSystem::LatLon,
899 }
900 }
901
902 pub fn with_altitude(latitude: f64, longitude: f64, altitude: f64) -> Self {
904 Self {
905 latitude,
906 longitude,
907 altitude: Some(altitude),
908 coordinate_system: CoordinateSystem::LatLon,
909 }
910 }
911
912 pub fn with_coordinate_system(mut self, coord_sys: CoordinateSystem) -> Self {
914 self.coordinate_system = coord_sys;
915 self
916 }
917
918 pub fn haversine_distance(&self, other: &GeoPoint) -> f64 {
920 const EARTH_RADIUS_M: f64 = 6_371_000.0;
921
922 let lat1_rad = self.latitude.to_radians();
923 let lat2_rad = other.latitude.to_radians();
924 let dlat_rad = (other.latitude - self.latitude).to_radians();
925 let dlon_rad = (other.longitude - self.longitude).to_radians();
926
927 let a = (dlat_rad / 2.0).sin().powi(2)
928 + lat1_rad.cos() * lat2_rad.cos() * (dlon_rad / 2.0).sin().powi(2);
929
930 let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
931 EARTH_RADIUS_M * c
932 }
933
934 pub fn bearing_to(&self, other: &GeoPoint) -> f64 {
936 let lat1_rad = self.latitude.to_radians();
937 let lat2_rad = other.latitude.to_radians();
938 let dlon_rad = (other.longitude - self.longitude).to_radians();
939
940 let y = dlon_rad.sin() * lat2_rad.cos();
941 let x =
942 lat1_rad.cos() * lat2_rad.sin() - lat1_rad.sin() * lat2_rad.cos() * dlon_rad.cos();
943
944 let bearing_rad = y.atan2(x);
945 (bearing_rad.to_degrees() + 360.0) % 360.0
946 }
947
948 pub fn destination_point(&self, distance_m: f64, bearing_deg: f64) -> GeoPoint {
950 const EARTH_RADIUS_M: f64 = 6_371_000.0;
951
952 let lat1_rad = self.latitude.to_radians();
953 let lon1_rad = self.longitude.to_radians();
954 let bearing_rad = bearing_deg.to_radians();
955 let angular_distance = distance_m / EARTH_RADIUS_M;
956
957 let lat2_rad = (lat1_rad.sin() * angular_distance.cos()
958 + lat1_rad.cos() * angular_distance.sin() * bearing_rad.cos())
959 .asin();
960
961 let lon2_rad = lon1_rad
962 + (bearing_rad.sin() * angular_distance.sin() * lat1_rad.cos())
963 .atan2(angular_distance.cos() - lat1_rad.sin() * lat2_rad.sin());
964
965 GeoPoint::new(lat2_rad.to_degrees(), lon2_rad.to_degrees())
966 }
967
968 pub fn to_point(&self) -> Point {
970 if let Some(alt) = self.altitude {
971 Point::new(vec![self.longitude, self.latitude, alt])
972 } else {
973 Point::new(vec![self.longitude, self.latitude])
974 }
975 }
976
977 pub fn is_within_bounds(&self, bounds: &GeoBounds) -> bool {
979 self.latitude >= bounds.south
980 && self.latitude <= bounds.north
981 && self.longitude >= bounds.west
982 && self.longitude <= bounds.east
983 }
984
985 pub fn to_web_mercator(&self) -> Point {
987 const EARTH_RADIUS: f64 = 6_378_137.0;
988
989 let x = self.longitude.to_radians() * EARTH_RADIUS;
990 let y = ((PI / 4.0 + self.latitude.to_radians() / 2.0).tan().ln()) * EARTH_RADIUS;
991
992 Point::new(vec![x, y])
993 }
994 }
995
996 #[derive(Debug, Clone, PartialEq)]
998 pub struct GeoBounds {
999 pub north: f64,
1000 pub south: f64,
1001 pub east: f64,
1002 pub west: f64,
1003 }
1004
1005 impl GeoBounds {
1006 pub fn new(north: f64, south: f64, east: f64, west: f64) -> Self {
1008 Self {
1009 north,
1010 south,
1011 east,
1012 west,
1013 }
1014 }
1015
1016 pub fn from_center_radius(center: &GeoPoint, radius_m: f64) -> Self {
1018 const EARTH_RADIUS_M: f64 = 6_371_000.0;
1019
1020 let lat_offset = (radius_m / EARTH_RADIUS_M).to_degrees();
1021 let lon_offset =
1022 (radius_m / (EARTH_RADIUS_M * center.latitude.to_radians().cos())).to_degrees();
1023
1024 Self {
1025 north: center.latitude + lat_offset,
1026 south: center.latitude - lat_offset,
1027 east: center.longitude + lon_offset,
1028 west: center.longitude - lon_offset,
1029 }
1030 }
1031
1032 pub fn contains(&self, point: &GeoPoint) -> bool {
1034 point.is_within_bounds(self)
1035 }
1036
1037 pub fn intersects(&self, other: &GeoBounds) -> bool {
1039 !(self.east < other.west
1040 || self.west > other.east
1041 || self.north < other.south
1042 || self.south > other.north)
1043 }
1044
1045 pub fn area_square_meters(&self) -> f64 {
1047 const EARTH_RADIUS_M: f64 = 6_371_000.0;
1048
1049 let lat_range_rad = (self.north - self.south).to_radians();
1050 let lon_range_rad = (self.east - self.west).to_radians();
1051 let avg_lat_rad = ((self.north + self.south) / 2.0).to_radians();
1052
1053 EARTH_RADIUS_M.powi(2) * lat_range_rad * lon_range_rad * avg_lat_rad.cos()
1054 }
1055
1056 pub fn to_rectangle(&self) -> Rectangle {
1058 Rectangle::new(vec![self.west, self.south], vec![self.east, self.north])
1059 }
1060 }
1061
1062 pub struct GeoUtils;
1064
1065 impl GeoUtils {
1066 pub fn deg_to_rad(degrees: f64) -> f64 {
1068 degrees * PI / 180.0
1069 }
1070
1071 pub fn rad_to_deg(radians: f64) -> f64 {
1073 radians * 180.0 / PI
1074 }
1075
1076 pub fn normalize_longitude(lon: f64) -> f64 {
1078 let mut normalized = lon % 360.0;
1079 if normalized > 180.0 {
1080 normalized -= 360.0;
1081 } else if normalized < -180.0 {
1082 normalized += 360.0;
1083 }
1084 normalized
1085 }
1086
1087 pub fn normalize_latitude(lat: f64) -> f64 {
1089 lat.clamp(-90.0, 90.0)
1090 }
1091
1092 pub fn centroid(points: &[GeoPoint]) -> Option<GeoPoint> {
1094 if points.is_empty() {
1095 return None;
1096 }
1097
1098 let mut x_sum = 0.0;
1099 let mut y_sum = 0.0;
1100 let mut z_sum = 0.0;
1101
1102 for point in points {
1103 let lat_rad = point.latitude.to_radians();
1104 let lon_rad = point.longitude.to_radians();
1105
1106 x_sum += lat_rad.cos() * lon_rad.cos();
1107 y_sum += lat_rad.cos() * lon_rad.sin();
1108 z_sum += lat_rad.sin();
1109 }
1110
1111 let count = points.len() as f64;
1112 let x_avg = x_sum / count;
1113 let y_avg = y_sum / count;
1114 let z_avg = z_sum / count;
1115
1116 let lon_rad = y_avg.atan2(x_avg);
1117 let hyp = (x_avg * x_avg + y_avg * y_avg).sqrt();
1118 let lat_rad = z_avg.atan2(hyp);
1119
1120 Some(GeoPoint::new(lat_rad.to_degrees(), lon_rad.to_degrees()))
1121 }
1122
1123 pub fn polygon_area(vertices: &[GeoPoint]) -> f64 {
1125 if vertices.len() < 3 {
1126 return 0.0;
1127 }
1128
1129 const EARTH_RADIUS_M: f64 = 6_371_000.0;
1130 let mut area = 0.0;
1131
1132 for i in 0..vertices.len() {
1133 let j = (i + 1) % vertices.len();
1134 let lat1 = vertices[i].latitude.to_radians();
1135 let lat2 = vertices[j].latitude.to_radians();
1136 let lon_diff = (vertices[j].longitude - vertices[i].longitude).to_radians();
1137
1138 area += lon_diff * (2.0 + lat1.sin() + lat2.sin());
1139 }
1140
1141 (area.abs() / 2.0) * EARTH_RADIUS_M.powi(2)
1142 }
1143
1144 pub fn point_in_polygon(point: &GeoPoint, polygon: &[GeoPoint]) -> bool {
1146 if polygon.len() < 3 {
1147 return false;
1148 }
1149
1150 let mut inside = false;
1151 let mut j = polygon.len() - 1;
1152
1153 for i in 0..polygon.len() {
1154 if ((polygon[i].latitude > point.latitude)
1155 != (polygon[j].latitude > point.latitude))
1156 && (point.longitude
1157 < (polygon[j].longitude - polygon[i].longitude)
1158 * (point.latitude - polygon[i].latitude)
1159 / (polygon[j].latitude - polygon[i].latitude)
1160 + polygon[i].longitude)
1161 {
1162 inside = !inside;
1163 }
1164 j = i;
1165 }
1166
1167 inside
1168 }
1169
1170 pub fn closest_point_on_line(
1172 point: &GeoPoint,
1173 line_start: &GeoPoint,
1174 line_end: &GeoPoint,
1175 ) -> GeoPoint {
1176 let lat_start = line_start.latitude.to_radians();
1177 let lon_start = line_start.longitude.to_radians();
1178 let lat_end = line_end.latitude.to_radians();
1179 let lon_end = line_end.longitude.to_radians();
1180 let lat_point = point.latitude.to_radians();
1181 let lon_point = point.longitude.to_radians();
1182
1183 let x1 = lat_start.cos() * lon_start.cos();
1185 let y1 = lat_start.cos() * lon_start.sin();
1186 let z1 = lat_start.sin();
1187
1188 let x2 = lat_end.cos() * lon_end.cos();
1189 let y2 = lat_end.cos() * lon_end.sin();
1190 let z2 = lat_end.sin();
1191
1192 let x0 = lat_point.cos() * lon_point.cos();
1193 let y0 = lat_point.cos() * lon_point.sin();
1194 let z0 = lat_point.sin();
1195
1196 let dot_product = x0 * (x2 - x1) + y0 * (y2 - y1) + z0 * (z2 - z1);
1198 let line_length_sq = (x2 - x1).powi(2) + (y2 - y1).powi(2) + (z2 - z1).powi(2);
1199
1200 let t = if line_length_sq == 0.0 {
1201 0.0
1202 } else {
1203 (dot_product / line_length_sq).clamp(0.0, 1.0)
1204 };
1205
1206 let closest_x = x1 + t * (x2 - x1);
1207 let closest_y = y1 + t * (y2 - y1);
1208 let closest_z = z1 + t * (z2 - z1);
1209
1210 let closest_lon = closest_y.atan2(closest_x).to_degrees();
1212 let closest_lat = closest_z
1213 .atan2((closest_x.powi(2) + closest_y.powi(2)).sqrt())
1214 .to_degrees();
1215
1216 GeoPoint::new(closest_lat, closest_lon)
1217 }
1218
1219 pub fn great_circle_distance(point1: &GeoPoint, point2: &GeoPoint) -> f64 {
1221 point1.haversine_distance(point2)
1222 }
1223
1224 pub fn decimal_to_dms(decimal: f64) -> (i32, i32, f64) {
1226 let degrees = decimal.trunc() as i32;
1227 let minutes_float = (decimal.abs() - degrees.abs() as f64) * 60.0;
1228 let minutes = minutes_float.trunc() as i32;
1229 let seconds = (minutes_float - minutes as f64) * 60.0;
1230 (degrees, minutes, seconds)
1231 }
1232
1233 pub fn dms_to_decimal(degrees: i32, minutes: i32, seconds: f64) -> f64 {
1235 let sign = if degrees < 0 { -1.0 } else { 1.0 };
1236 degrees.abs() as f64 + minutes as f64 / 60.0 + seconds / 3600.0 * sign
1237 }
1238 }
1239
1240 #[allow(non_snake_case)]
1241 #[cfg(test)]
1242 mod geo_tests {
1243 use super::*;
1244
1245 #[test]
1246 fn test_geopoint_haversine_distance() {
1247 let london = GeoPoint::new(51.5074, -0.1278);
1248 let paris = GeoPoint::new(48.8566, 2.3522);
1249
1250 let distance = london.haversine_distance(&paris);
1251 assert!((distance - 344_000.0).abs() < 10_000.0);
1253 }
1254
1255 #[test]
1256 fn test_geopoint_bearing() {
1257 let start = GeoPoint::new(51.0, 0.0);
1258 let end = GeoPoint::new(52.0, 1.0);
1259
1260 let bearing = start.bearing_to(&end);
1261 assert!(bearing >= 0.0 && bearing < 360.0);
1262 }
1263
1264 #[test]
1265 fn test_geopoint_destination() {
1266 let start = GeoPoint::new(51.0, 0.0);
1267 let destination = start.destination_point(100_000.0, 90.0); assert!((destination.latitude - start.latitude).abs() < 0.1);
1271 assert!(destination.longitude > start.longitude);
1272 }
1273
1274 #[test]
1275 fn test_geobounds() {
1276 let bounds = GeoBounds::new(52.0, 50.0, 2.0, 0.0);
1277 let point_inside = GeoPoint::new(51.0, 1.0);
1278 let point_outside = GeoPoint::new(53.0, 3.0);
1279
1280 assert!(bounds.contains(&point_inside));
1281 assert!(!bounds.contains(&point_outside));
1282 }
1283
1284 #[test]
1285 fn test_geoutils_centroid() {
1286 let points = vec![
1287 GeoPoint::new(0.0, 0.0),
1288 GeoPoint::new(1.0, 0.0),
1289 GeoPoint::new(0.0, 1.0),
1290 GeoPoint::new(1.0, 1.0),
1291 ];
1292
1293 let centroid = GeoUtils::centroid(&points).unwrap();
1294 assert!((centroid.latitude - 0.5).abs() < 0.1);
1295 assert!((centroid.longitude - 0.5).abs() < 0.1);
1296 }
1297
1298 #[test]
1299 fn test_point_in_polygon() {
1300 let polygon = vec![
1301 GeoPoint::new(0.0, 0.0),
1302 GeoPoint::new(2.0, 0.0),
1303 GeoPoint::new(2.0, 2.0),
1304 GeoPoint::new(0.0, 2.0),
1305 ];
1306
1307 let inside_point = GeoPoint::new(1.0, 1.0);
1308 let outside_point = GeoPoint::new(3.0, 3.0);
1309
1310 assert!(GeoUtils::point_in_polygon(&inside_point, &polygon));
1311 assert!(!GeoUtils::point_in_polygon(&outside_point, &polygon));
1312 }
1313
1314 #[test]
1315 fn test_normalize_coordinates() {
1316 assert_eq!(GeoUtils::normalize_longitude(380.0), 20.0);
1317 assert_eq!(GeoUtils::normalize_longitude(-200.0), 160.0);
1318 assert_eq!(GeoUtils::normalize_latitude(100.0), 90.0);
1319 assert_eq!(GeoUtils::normalize_latitude(-100.0), -90.0);
1320 }
1321
1322 #[test]
1323 fn test_dms_conversion() {
1324 let decimal = 51.5074;
1325 let (degrees, minutes, seconds) = GeoUtils::decimal_to_dms(decimal);
1326 let converted_back = GeoUtils::dms_to_decimal(degrees, minutes, seconds);
1327
1328 assert!((decimal - converted_back).abs() < 0.0001);
1329 }
1330 }
1331}
1332
1333#[allow(non_snake_case)]
1334#[cfg(test)]
1335mod tests {
1336 use super::*;
1337
1338 #[test]
1339 fn test_point_operations() {
1340 let p1 = Point::new(vec![1.0, 2.0, 3.0]);
1341 let p2 = Point::new(vec![4.0, 5.0, 6.0]);
1342
1343 assert_eq!(p1.dimension(), 3);
1344 assert_eq!(p1.distance_squared(&p2), 27.0); assert_eq!(p1.distance(&p2), 27.0_f64.sqrt());
1346 }
1347
1348 #[test]
1349 fn test_rectangle_operations() {
1350 let rect = Rectangle::new(vec![0.0, 0.0], vec![10.0, 10.0]);
1351 let point_inside = Point::new(vec![5.0, 5.0]);
1352 let point_outside = Point::new(vec![15.0, 15.0]);
1353
1354 assert!(rect.contains(&point_inside));
1355 assert!(!rect.contains(&point_outside));
1356 assert_eq!(rect.area(), 100.0);
1357
1358 let other_rect = Rectangle::new(vec![5.0, 5.0], vec![15.0, 15.0]);
1359 assert!(rect.intersects(&other_rect));
1360
1361 let union = rect.union(&other_rect);
1362 assert_eq!(union.min_bounds, vec![0.0, 0.0]);
1363 assert_eq!(union.max_bounds, vec![15.0, 15.0]);
1364 }
1365
1366 #[test]
1367 fn test_kdtree() {
1368 let points = vec![
1369 (Point::new(vec![2.0, 3.0]), 0),
1370 (Point::new(vec![5.0, 4.0]), 1),
1371 (Point::new(vec![9.0, 6.0]), 2),
1372 (Point::new(vec![4.0, 7.0]), 3),
1373 (Point::new(vec![8.0, 1.0]), 4),
1374 (Point::new(vec![7.0, 2.0]), 5),
1375 ];
1376
1377 let kd_tree = KdTree::from_points(points);
1378
1379 let query = Point::new(vec![5.0, 5.0]);
1380 let result = kd_tree.nearest_neighbor(&query);
1381
1382 assert!(result.is_some());
1383 let (_, data, _) = result.unwrap();
1384 assert!(data <= 5);
1386
1387 let range = Rectangle::new(vec![0.0, 0.0], vec![6.0, 6.0]);
1389 let range_results = kd_tree.range_query(&range);
1390 assert!(!range_results.is_empty());
1391 }
1392
1393 #[test]
1394 fn test_quadtree() {
1395 let bounds = Rectangle::new(vec![0.0, 0.0], vec![100.0, 100.0]);
1396 let mut quad_tree = QuadTree::new(bounds, 4, 6);
1397
1398 quad_tree.insert(Point::new(vec![10.0, 10.0]), 0);
1400 quad_tree.insert(Point::new(vec![20.0, 20.0]), 1);
1401 quad_tree.insert(Point::new(vec![80.0, 80.0]), 2);
1402 quad_tree.insert(Point::new(vec![90.0, 90.0]), 3);
1403
1404 let query_bounds = Rectangle::new(vec![5.0, 5.0], vec![25.0, 25.0]);
1406 let results = quad_tree.query(&query_bounds);
1407
1408 assert_eq!(results.len(), 2); let query_point = Point::new(vec![15.0, 15.0]);
1412 let nearest = quad_tree.nearest_neighbor(&query_point, None);
1413 assert!(nearest.is_some());
1414 }
1415
1416 #[test]
1417 fn test_spatial_hash() {
1418 let bounds = Rectangle::new(vec![0.0, 0.0], vec![100.0, 100.0]);
1419 let mut spatial_hash = SpatialHash::new(bounds, 10.0);
1420
1421 spatial_hash.insert(Point::new(vec![15.0, 15.0]), 0);
1423 spatial_hash.insert(Point::new(vec![25.0, 25.0]), 1);
1424 spatial_hash.insert(Point::new(vec![85.0, 85.0]), 2);
1425
1426 let center = Point::new(vec![20.0, 20.0]);
1428 let results = spatial_hash.query_radius(¢er, 10.0);
1429
1430 assert!(!results.is_empty());
1431
1432 let stats = spatial_hash.stats();
1433 assert_eq!(stats.total_points, 3);
1434 }
1435
1436 #[test]
1437 fn test_octree() {
1438 let bounds = Rectangle::new(vec![0.0, 0.0, 0.0], vec![100.0, 100.0, 100.0]);
1439 let mut oct_tree = OctTree::new(bounds, 4, 6);
1440
1441 oct_tree.insert(Point::new(vec![10.0, 10.0, 10.0]), 0);
1443 oct_tree.insert(Point::new(vec![20.0, 20.0, 20.0]), 1);
1444 oct_tree.insert(Point::new(vec![80.0, 80.0, 80.0]), 2);
1445
1446 let query_bounds = Rectangle::new(vec![5.0, 5.0, 5.0], vec![25.0, 25.0, 25.0]);
1448 let results = oct_tree.query(&query_bounds);
1449
1450 assert_eq!(results.len(), 2); }
1452}