1use regex::Regex;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
11pub struct GeographyValue {
12 pub latitude: f64,
13 pub longitude: f64,
14}
15
16impl std::hash::Hash for GeographyValue {
17 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
18 self.latitude.to_bits().hash(state);
19 self.longitude.to_bits().hash(state);
20 }
21}
22
23impl GeographyValue {
24 pub fn new(latitude: f64, longitude: f64) -> Self {
25 GeographyValue {
26 latitude,
27 longitude,
28 }
29 }
30
31 pub fn distance(&self, other: &GeographyValue) -> f64 {
33 const EARTH_RADIUS_KM: f64 = 6371.0;
34 const DEG_TO_RAD: f64 = std::f64::consts::PI / 180.0;
35
36 let lat1 = self.latitude * DEG_TO_RAD;
37 let lat2 = other.latitude * DEG_TO_RAD;
38 let delta_lat = (other.latitude - self.latitude) * DEG_TO_RAD;
39 let delta_lon = (other.longitude - self.longitude) * DEG_TO_RAD;
40
41 let a = (delta_lat / 2.0).sin().powi(2)
42 + lat1.cos() * lat2.cos() * (delta_lon / 2.0).sin().powi(2);
43 let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
44
45 EARTH_RADIUS_KM * c
46 }
47
48 pub fn bearing(&self, other: &GeographyValue) -> f64 {
50 const DEG_TO_RAD: f64 = std::f64::consts::PI / 180.0;
51 const RAD_TO_DEG: f64 = 180.0 / std::f64::consts::PI;
52
53 let lat1 = self.latitude * DEG_TO_RAD;
54 let lat2 = other.latitude * DEG_TO_RAD;
55 let delta_lon = (other.longitude - self.longitude) * DEG_TO_RAD;
56
57 let y = delta_lon.sin() * lat2.cos();
58 let x = lat1.cos() * lat2.sin() - lat1.sin() * lat2.cos() * delta_lon.cos();
59
60 let bearing = y.atan2(x) * RAD_TO_DEG;
61 (bearing + 360.0) % 360.0
62 }
63
64 pub fn in_bbox(&self, min_lat: f64, max_lat: f64, min_lon: f64, max_lon: f64) -> bool {
66 self.latitude >= min_lat
67 && self.latitude <= max_lat
68 && self.longitude >= min_lon
69 && self.longitude <= max_lon
70 }
71
72 pub fn is_valid(&self) -> bool {
74 self.latitude >= -90.0
75 && self.latitude <= 90.0
76 && self.longitude >= -180.0
77 && self.longitude <= 180.0
78 }
79}
80
81impl Default for GeographyValue {
82 fn default() -> Self {
83 GeographyValue {
84 latitude: 0.0,
85 longitude: 0.0,
86 }
87 }
88}
89
90impl GeographyValue {
91 pub fn estimated_size(&self) -> usize {
93 std::mem::size_of::<Self>()
94 }
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
99pub struct LineStringValue {
100 pub points: Vec<GeographyValue>,
101}
102
103impl LineStringValue {
104 pub fn new(points: Vec<GeographyValue>) -> Self {
105 LineStringValue { points }
106 }
107
108 pub fn length(&self) -> f64 {
110 self.points.windows(2).map(|w| w[0].distance(&w[1])).sum()
111 }
112
113 pub fn num_points(&self) -> usize {
115 self.points.len()
116 }
117
118 pub fn is_closed(&self) -> bool {
120 if self.points.len() < 2 {
121 return false;
122 }
123 let first = &self.points[0];
124 let last = &self.points[self.points.len() - 1];
125 (first.latitude - last.latitude).abs() < 1e-9
126 && (first.longitude - last.longitude).abs() < 1e-9
127 }
128
129 pub fn is_ring(&self) -> bool {
131 self.points.len() >= 4 && self.is_closed()
132 }
133
134 pub fn centroid(&self) -> Option<GeographyValue> {
136 if self.points.is_empty() {
137 return None;
138 }
139 let (sum_lat, sum_lon) = self.points.iter().fold((0.0, 0.0), |(lat, lon), p| {
140 (lat + p.latitude, lon + p.longitude)
141 });
142 Some(GeographyValue::new(
143 sum_lat / self.points.len() as f64,
144 sum_lon / self.points.len() as f64,
145 ))
146 }
147
148 pub fn start_point(&self) -> Option<&GeographyValue> {
150 self.points.first()
151 }
152
153 pub fn end_point(&self) -> Option<&GeographyValue> {
155 self.points.last()
156 }
157
158 pub fn estimated_size(&self) -> usize {
160 std::mem::size_of::<Self>() + self.points.len() * std::mem::size_of::<GeographyValue>()
161 }
162
163 pub fn is_valid(&self) -> bool {
165 self.points.iter().all(|p| p.is_valid())
166 }
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
171pub struct PolygonValue {
172 pub exterior: LineStringValue,
173 pub holes: Vec<LineStringValue>,
174}
175
176impl PolygonValue {
177 pub fn new(exterior: LineStringValue, holes: Vec<LineStringValue>) -> Self {
178 PolygonValue { exterior, holes }
179 }
180
181 pub fn from_ring(ring: LineStringValue) -> Self {
182 PolygonValue {
183 exterior: ring,
184 holes: Vec::new(),
185 }
186 }
187
188 pub fn area(&self) -> f64 {
190 let exterior_area = self.ring_area(&self.exterior);
191 let holes_area: f64 = self.holes.iter().map(|h| self.ring_area(h)).sum();
192 (exterior_area - holes_area).abs()
193 }
194
195 fn ring_area(&self, ring: &LineStringValue) -> f64 {
197 if ring.points.len() < 4 {
198 return 0.0;
199 }
200
201 const EARTH_RADIUS_KM: f64 = 6371.0;
202 const DEG_TO_RAD: f64 = std::f64::consts::PI / 180.0;
203
204 let mut sum = 0.0;
205 let n = ring.points.len() - 1; for i in 0..n {
208 let j = (i + 1) % n;
209 let lat_i = ring.points[i].latitude * DEG_TO_RAD;
210 let lon_i = ring.points[i].longitude * DEG_TO_RAD;
211 let lon_j = ring.points[j].longitude * DEG_TO_RAD;
212
213 sum += (lon_j - lon_i) * lat_i.sin();
214 }
215
216 (sum.abs() * EARTH_RADIUS_KM * EARTH_RADIUS_KM) / 2.0
217 }
218
219 pub fn perimeter(&self) -> f64 {
221 let exterior_perimeter = self.exterior.length();
222 let holes_perimeter: f64 = self.holes.iter().map(|h| h.length()).sum();
223 exterior_perimeter + holes_perimeter
224 }
225
226 pub fn contains_point(&self, point: &GeographyValue) -> bool {
228 self.point_in_ring(point, &self.exterior)
229 && !self.holes.iter().any(|h| self.point_in_ring(point, h))
230 }
231
232 fn point_in_ring(&self, point: &GeographyValue, ring: &LineStringValue) -> bool {
234 if ring.points.len() < 4 {
235 return false;
236 }
237
238 let mut inside = false;
239 let n = ring.points.len() - 1;
240
241 for i in 0..n {
242 let j = (i + 1) % n;
243 let pi = &ring.points[i];
244 let pj = &ring.points[j];
245
246 if ((pi.latitude > point.latitude) != (pj.latitude > point.latitude))
247 && (point.longitude
248 < (pj.longitude - pi.longitude) * (point.latitude - pi.latitude)
249 / (pj.latitude - pi.latitude)
250 + pi.longitude)
251 {
252 inside = !inside;
253 }
254 }
255
256 inside
257 }
258
259 pub fn centroid(&self) -> Option<GeographyValue> {
261 self.exterior.centroid()
262 }
263
264 pub fn bounding_box(&self) -> Option<(f64, f64, f64, f64)> {
266 if self.exterior.points.is_empty() {
267 return None;
268 }
269
270 let mut min_lat = f64::MAX;
271 let mut max_lat = f64::MIN;
272 let mut min_lon = f64::MAX;
273 let mut max_lon = f64::MIN;
274
275 for p in &self.exterior.points {
276 min_lat = min_lat.min(p.latitude);
277 max_lat = max_lat.max(p.latitude);
278 min_lon = min_lon.min(p.longitude);
279 max_lon = max_lon.max(p.longitude);
280 }
281
282 Some((min_lat, max_lat, min_lon, max_lon))
283 }
284
285 pub fn estimated_size(&self) -> usize {
287 let mut size = std::mem::size_of::<Self>();
288 size += self.exterior.estimated_size();
289 for hole in &self.holes {
290 size += hole.estimated_size();
291 }
292 size
293 }
294
295 pub fn is_valid(&self) -> bool {
297 self.exterior.is_valid()
298 && self.exterior.is_closed()
299 && self.holes.iter().all(|h| h.is_valid() && h.is_closed())
300 }
301}
302
303#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
305pub struct MultiPointValue {
306 pub points: Vec<GeographyValue>,
307}
308
309impl MultiPointValue {
310 pub fn new(points: Vec<GeographyValue>) -> Self {
311 MultiPointValue { points }
312 }
313
314 pub fn num_points(&self) -> usize {
316 self.points.len()
317 }
318
319 pub fn centroid(&self) -> Option<GeographyValue> {
321 if self.points.is_empty() {
322 return None;
323 }
324 let (sum_lat, sum_lon) = self.points.iter().fold((0.0, 0.0), |(lat, lon), p| {
325 (lat + p.latitude, lon + p.longitude)
326 });
327 Some(GeographyValue::new(
328 sum_lat / self.points.len() as f64,
329 sum_lon / self.points.len() as f64,
330 ))
331 }
332
333 pub fn estimated_size(&self) -> usize {
335 std::mem::size_of::<Self>() + self.points.len() * std::mem::size_of::<GeographyValue>()
336 }
337
338 pub fn is_valid(&self) -> bool {
340 self.points.iter().all(|p| p.is_valid())
341 }
342}
343
344#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
346pub struct MultiLineStringValue {
347 pub linestrings: Vec<LineStringValue>,
348}
349
350impl MultiLineStringValue {
351 pub fn new(linestrings: Vec<LineStringValue>) -> Self {
352 MultiLineStringValue { linestrings }
353 }
354
355 pub fn num_linestrings(&self) -> usize {
357 self.linestrings.len()
358 }
359
360 pub fn length(&self) -> f64 {
362 self.linestrings.iter().map(|ls| ls.length()).sum()
363 }
364
365 pub fn estimated_size(&self) -> usize {
367 std::mem::size_of::<Self>()
368 + self
369 .linestrings
370 .iter()
371 .map(|ls| ls.estimated_size())
372 .sum::<usize>()
373 }
374
375 pub fn is_valid(&self) -> bool {
377 self.linestrings.iter().all(|ls| ls.is_valid())
378 }
379}
380
381#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
383pub struct MultiPolygonValue {
384 pub polygons: Vec<PolygonValue>,
385}
386
387impl MultiPolygonValue {
388 pub fn new(polygons: Vec<PolygonValue>) -> Self {
389 MultiPolygonValue { polygons }
390 }
391
392 pub fn num_polygons(&self) -> usize {
394 self.polygons.len()
395 }
396
397 pub fn area(&self) -> f64 {
399 self.polygons.iter().map(|p| p.area()).sum()
400 }
401
402 pub fn contains_point(&self, point: &GeographyValue) -> bool {
404 self.polygons.iter().any(|p| p.contains_point(point))
405 }
406
407 pub fn estimated_size(&self) -> usize {
409 std::mem::size_of::<Self>()
410 + self
411 .polygons
412 .iter()
413 .map(|p| p.estimated_size())
414 .sum::<usize>()
415 }
416
417 pub fn is_valid(&self) -> bool {
419 self.polygons.iter().all(|p| p.is_valid())
420 }
421}
422
423#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
425pub enum Geography {
426 Point(GeographyValue),
427 LineString(LineStringValue),
428 Polygon(PolygonValue),
429 MultiPoint(MultiPointValue),
430 MultiLineString(MultiLineStringValue),
431 MultiPolygon(MultiPolygonValue),
432}
433
434impl std::hash::Hash for Geography {
435 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
436 match self {
437 Geography::Point(p) => {
438 0u8.hash(state);
439 p.hash(state);
440 }
441 Geography::LineString(ls) => {
442 1u8.hash(state);
443 ls.points.hash(state);
444 }
445 Geography::Polygon(p) => {
446 2u8.hash(state);
447 p.exterior.points.hash(state);
448 p.holes.len().hash(state);
449 }
450 Geography::MultiPoint(mp) => {
451 3u8.hash(state);
452 mp.points.hash(state);
453 }
454 Geography::MultiLineString(mls) => {
455 4u8.hash(state);
456 mls.linestrings.len().hash(state);
457 for ls in &mls.linestrings {
458 ls.points.hash(state);
459 }
460 }
461 Geography::MultiPolygon(mp) => {
462 5u8.hash(state);
463 mp.polygons.len().hash(state);
464 for p in &mp.polygons {
465 p.exterior.points.hash(state);
466 }
467 }
468 }
469 }
470}
471
472impl Geography {
473 pub fn from_wkt(wkt: &str) -> Result<Self, String> {
475 let wkt_upper = wkt.trim().to_uppercase();
476
477 if wkt_upper.starts_with("POINT") {
478 Self::parse_point_wkt(wkt.trim())
479 } else if wkt_upper.starts_with("LINESTRING") {
480 Self::parse_linestring_wkt(wkt.trim())
481 } else if wkt_upper.starts_with("POLYGON") {
482 Self::parse_polygon_wkt(wkt.trim())
483 } else if wkt_upper.starts_with("MULTIPOINT") {
484 Self::parse_multipoint_wkt(wkt.trim())
485 } else if wkt_upper.starts_with("MULTILINESTRING") {
486 Self::parse_multilinestring_wkt(wkt.trim())
487 } else if wkt_upper.starts_with("MULTIPOLYGON") {
488 Self::parse_multipolygon_wkt(wkt.trim())
489 } else {
490 Err(format!("Unsupported WKT format: {}", wkt))
491 }
492 }
493
494 pub fn to_wkt(&self) -> String {
496 match self {
497 Geography::Point(p) => format!("POINT({} {})", p.longitude, p.latitude),
498 Geography::LineString(ls) => {
499 let coords: Vec<String> = ls
500 .points
501 .iter()
502 .map(|p| format!("{} {}", p.longitude, p.latitude))
503 .collect();
504 format!("LINESTRING({})", coords.join(", "))
505 }
506 Geography::Polygon(p) => {
507 let mut rings: Vec<String> = Vec::new();
508 let exterior_coords: Vec<String> = p
509 .exterior
510 .points
511 .iter()
512 .map(|pt| format!("{} {}", pt.longitude, pt.latitude))
513 .collect();
514 rings.push(format!("({})", exterior_coords.join(", ")));
515 for hole in &p.holes {
516 let hole_coords: Vec<String> = hole
517 .points
518 .iter()
519 .map(|pt| format!("{} {}", pt.longitude, pt.latitude))
520 .collect();
521 rings.push(format!("({})", hole_coords.join(", ")));
522 }
523 format!("POLYGON({})", rings.join(", "))
524 }
525 Geography::MultiPoint(mp) => {
526 let coords: Vec<String> = mp
527 .points
528 .iter()
529 .map(|p| format!("({} {})", p.longitude, p.latitude))
530 .collect();
531 format!("MULTIPOINT({})", coords.join(", "))
532 }
533 Geography::MultiLineString(mls) => {
534 let linestrings: Vec<String> = mls
535 .linestrings
536 .iter()
537 .map(|ls| {
538 let coords: Vec<String> = ls
539 .points
540 .iter()
541 .map(|p| format!("{} {}", p.longitude, p.latitude))
542 .collect();
543 format!("({})", coords.join(", "))
544 })
545 .collect();
546 format!("MULTILINESTRING({})", linestrings.join(", "))
547 }
548 Geography::MultiPolygon(mp) => {
549 let polygons: Vec<String> = mp
550 .polygons
551 .iter()
552 .map(|p| {
553 let mut rings: Vec<String> = Vec::new();
554 let exterior_coords: Vec<String> = p
555 .exterior
556 .points
557 .iter()
558 .map(|pt| format!("{} {}", pt.longitude, pt.latitude))
559 .collect();
560 rings.push(format!("({})", exterior_coords.join(", ")));
561 for hole in &p.holes {
562 let hole_coords: Vec<String> = hole
563 .points
564 .iter()
565 .map(|pt| format!("{} {}", pt.longitude, pt.latitude))
566 .collect();
567 rings.push(format!("({})", hole_coords.join(", ")));
568 }
569 format!("({})", rings.join(", "))
570 })
571 .collect();
572 format!("MULTIPOLYGON({})", polygons.join(", "))
573 }
574 }
575 }
576
577 fn parse_point_wkt(wkt: &str) -> Result<Self, String> {
578 let re = Regex::new(r"(?i)POINT\s*\(\s*([-\d.]+)\s+([-\d.]+)\s*\)")
579 .map_err(|_| "Invalid regular expression".to_string())?;
580
581 if let Some(caps) = re.captures(wkt) {
582 let lon = caps
583 .get(1)
584 .ok_or("Missing longitude coordinate")?
585 .as_str()
586 .parse::<f64>()
587 .map_err(|_| "Invalid longitude format")?;
588 let lat = caps
589 .get(2)
590 .ok_or("Missing latitude coordinate")?
591 .as_str()
592 .parse::<f64>()
593 .map_err(|_| "Invalid latitude format")?;
594 return Ok(Geography::Point(GeographyValue::new(lat, lon)));
595 }
596
597 Err("Invalid POINT WKT format".to_string())
598 }
599
600 fn parse_linestring_wkt(wkt: &str) -> Result<Self, String> {
601 let re = Regex::new(r"(?i)LINESTRING\s*\(([^)]+)\)")
602 .map_err(|_| "Invalid regular expression".to_string())?;
603
604 if let Some(caps) = re.captures(wkt) {
605 let coords_str = caps.get(1).ok_or("Missing coordinates")?.as_str();
606 let points = Self::parse_coordinate_pairs(coords_str)?;
607 if points.len() < 2 {
608 return Err("LineString must have at least 2 points".to_string());
609 }
610 return Ok(Geography::LineString(LineStringValue::new(points)));
611 }
612
613 Err("Invalid LINESTRING WKT format".to_string())
614 }
615
616 fn parse_polygon_wkt(wkt: &str) -> Result<Self, String> {
617 let re = Regex::new(r"(?i)POLYGON\s*\((.+)\)\s*$")
618 .map_err(|_| "Invalid regular expression".to_string())?;
619
620 if let Some(caps) = re.captures(wkt) {
621 let content = caps.get(1).ok_or("Missing polygon content")?.as_str();
622 let rings = Self::parse_rings(content)?;
623
624 if rings.is_empty() {
625 return Err("Polygon must have at least one ring".to_string());
626 }
627
628 let exterior = rings.into_iter().next().unwrap();
629 return Ok(Geography::Polygon(PolygonValue::from_ring(exterior)));
630 }
631
632 Err("Invalid POLYGON WKT format".to_string())
633 }
634
635 fn parse_multipoint_wkt(wkt: &str) -> Result<Self, String> {
636 let re = Regex::new(r"(?i)MULTIPOINT\s*\(([^)]*)\)")
637 .map_err(|_| "Invalid regular expression".to_string())?;
638
639 if let Some(caps) = re.captures(wkt) {
640 let coords_str = caps.get(1).ok_or("Missing coordinates")?.as_str();
641 if coords_str.trim().is_empty() {
642 return Ok(Geography::MultiPoint(MultiPointValue::default()));
643 }
644 let points = Self::parse_coordinate_pairs(coords_str)?;
645 return Ok(Geography::MultiPoint(MultiPointValue::new(points)));
646 }
647
648 Err("Invalid MULTIPOINT WKT format".to_string())
649 }
650
651 fn parse_multilinestring_wkt(wkt: &str) -> Result<Self, String> {
652 let re = Regex::new(r"(?i)MULTILINESTRING\s*\((.+)\)\s*$")
653 .map_err(|_| "Invalid regular expression".to_string())?;
654
655 if let Some(caps) = re.captures(wkt) {
656 let content = caps
657 .get(1)
658 .ok_or("Missing multilinestring content")?
659 .as_str();
660 let rings = Self::parse_rings(content)?;
661
662 let linestrings: Vec<LineStringValue> =
663 rings.into_iter().filter(|r| r.points.len() >= 2).collect();
664
665 return Ok(Geography::MultiLineString(MultiLineStringValue::new(
666 linestrings,
667 )));
668 }
669
670 Err("Invalid MULTILINESTRING WKT format".to_string())
671 }
672
673 fn parse_multipolygon_wkt(wkt: &str) -> Result<Self, String> {
674 let re = Regex::new(r"(?i)MULTIPOLYGON\s*\((.+)\)\s*$")
675 .map_err(|_| "Invalid regular expression".to_string())?;
676
677 if let Some(caps) = re.captures(wkt) {
678 let content = caps.get(1).ok_or("Missing multipolygon content")?.as_str();
679 let polygons = Self::parse_multipolygon_content(content)?;
680
681 if polygons.is_empty() {
682 return Ok(Geography::MultiPolygon(MultiPolygonValue::default()));
683 }
684
685 return Ok(Geography::MultiPolygon(MultiPolygonValue::new(polygons)));
686 }
687
688 Err("Invalid MULTIPOLYGON WKT format".to_string())
689 }
690
691 fn parse_coordinate_pairs(s: &str) -> Result<Vec<GeographyValue>, String> {
692 let mut points = Vec::new();
693 let pairs: Vec<&str> = s.split(',').collect();
694
695 for pair in pairs {
696 let pair = pair.trim();
697 if pair.is_empty() {
698 continue;
699 }
700
701 let coords: Vec<&str> = pair.split_whitespace().collect();
702 if coords.len() < 2 {
703 return Err(format!("Invalid coordinate pair: {}", pair));
704 }
705
706 let lon = coords[0]
707 .parse::<f64>()
708 .map_err(|_| format!("Invalid longitude: {}", coords[0]))?;
709 let lat = coords[1]
710 .parse::<f64>()
711 .map_err(|_| format!("Invalid latitude: {}", coords[1]))?;
712
713 points.push(GeographyValue::new(lat, lon));
714 }
715
716 Ok(points)
717 }
718
719 fn parse_rings(s: &str) -> Result<Vec<LineStringValue>, String> {
720 let mut rings = Vec::new();
721 let mut depth = 0;
722 let mut current = String::new();
723
724 for c in s.chars() {
725 match c {
726 '(' => {
727 depth += 1;
728 if depth > 1 {
729 current.push(c);
730 }
731 }
732 ')' => {
733 depth -= 1;
734 if depth > 0 {
735 current.push(c);
736 } else if !current.trim().is_empty() {
737 let points = Self::parse_coordinate_pairs(¤t)?;
738 if !points.is_empty() {
739 rings.push(LineStringValue::new(points));
740 }
741 current.clear();
742 }
743 }
744 _ => {
745 if depth > 0 {
746 current.push(c);
747 }
748 }
749 }
750 }
751
752 Ok(rings)
753 }
754
755 fn parse_multipolygon_content(s: &str) -> Result<Vec<PolygonValue>, String> {
756 let mut polygons = Vec::new();
757 let mut depth = 0;
758 let mut polygon_content = String::new();
759 let mut in_polygon = false;
760
761 for c in s.chars() {
762 match c {
763 '(' => {
764 depth += 1;
765 if depth == 2 {
766 in_polygon = true;
767 }
768 if in_polygon {
769 polygon_content.push(c);
770 }
771 }
772 ')' => {
773 if in_polygon {
774 polygon_content.push(c);
775 }
776 depth -= 1;
777 if depth == 1 && in_polygon {
778 let rings = Self::parse_rings(&polygon_content)?;
779 if !rings.is_empty() {
780 let exterior = rings.into_iter().next().unwrap();
781 polygons.push(PolygonValue::from_ring(exterior));
782 }
783 polygon_content.clear();
784 in_polygon = false;
785 }
786 }
787 _ => {
788 if in_polygon {
789 polygon_content.push(c);
790 }
791 }
792 }
793 }
794
795 Ok(polygons)
796 }
797
798 pub fn geometry_type(&self) -> &'static str {
800 match self {
801 Geography::Point(_) => "Point",
802 Geography::LineString(_) => "LineString",
803 Geography::Polygon(_) => "Polygon",
804 Geography::MultiPoint(_) => "MultiPoint",
805 Geography::MultiLineString(_) => "MultiLineString",
806 Geography::MultiPolygon(_) => "MultiPolygon",
807 }
808 }
809
810 pub fn centroid(&self) -> Option<GeographyValue> {
812 match self {
813 Geography::Point(p) => Some(p.clone()),
814 Geography::LineString(ls) => ls.centroid(),
815 Geography::Polygon(p) => p.centroid(),
816 Geography::MultiPoint(mp) => mp.centroid(),
817 Geography::MultiLineString(mls) => {
818 let all_points: Vec<&GeographyValue> =
819 mls.linestrings.iter().flat_map(|ls| &ls.points).collect();
820 if all_points.is_empty() {
821 return None;
822 }
823 let (sum_lat, sum_lon) = all_points.iter().fold((0.0, 0.0), |(lat, lon), p| {
824 (lat + p.latitude, lon + p.longitude)
825 });
826 Some(GeographyValue::new(
827 sum_lat / all_points.len() as f64,
828 sum_lon / all_points.len() as f64,
829 ))
830 }
831 Geography::MultiPolygon(mp) => {
832 let centroids: Vec<GeographyValue> =
833 mp.polygons.iter().filter_map(|p| p.centroid()).collect();
834 if centroids.is_empty() {
835 return None;
836 }
837 let (sum_lat, sum_lon) = centroids.iter().fold((0.0, 0.0), |(lat, lon), p| {
838 (lat + p.latitude, lon + p.longitude)
839 });
840 Some(GeographyValue::new(
841 sum_lat / centroids.len() as f64,
842 sum_lon / centroids.len() as f64,
843 ))
844 }
845 }
846 }
847
848 pub fn is_valid(&self) -> bool {
850 match self {
851 Geography::Point(p) => p.is_valid(),
852 Geography::LineString(ls) => ls.is_valid(),
853 Geography::Polygon(p) => p.is_valid(),
854 Geography::MultiPoint(mp) => mp.is_valid(),
855 Geography::MultiLineString(mls) => mls.is_valid(),
856 Geography::MultiPolygon(mp) => mp.is_valid(),
857 }
858 }
859
860 pub fn estimated_size(&self) -> usize {
862 match self {
863 Geography::Point(p) => p.estimated_size(),
864 Geography::LineString(ls) => ls.estimated_size(),
865 Geography::Polygon(p) => p.estimated_size(),
866 Geography::MultiPoint(mp) => mp.estimated_size(),
867 Geography::MultiLineString(mls) => mls.estimated_size(),
868 Geography::MultiPolygon(mp) => mp.estimated_size(),
869 }
870 }
871
872 pub fn bounding_box(&self) -> Option<(f64, f64, f64, f64)> {
874 match self {
875 Geography::Point(p) => Some((p.latitude, p.latitude, p.longitude, p.longitude)),
876 Geography::LineString(ls) => {
877 if ls.points.is_empty() {
878 return None;
879 }
880 let mut min_lat = f64::MAX;
881 let mut max_lat = f64::MIN;
882 let mut min_lon = f64::MAX;
883 let mut max_lon = f64::MIN;
884 for p in &ls.points {
885 min_lat = min_lat.min(p.latitude);
886 max_lat = max_lat.max(p.latitude);
887 min_lon = min_lon.min(p.longitude);
888 max_lon = max_lon.max(p.longitude);
889 }
890 Some((min_lat, max_lat, min_lon, max_lon))
891 }
892 Geography::Polygon(p) => p.bounding_box(),
893 Geography::MultiPoint(mp) => {
894 if mp.points.is_empty() {
895 return None;
896 }
897 let mut min_lat = f64::MAX;
898 let mut max_lat = f64::MIN;
899 let mut min_lon = f64::MAX;
900 let mut max_lon = f64::MIN;
901 for p in &mp.points {
902 min_lat = min_lat.min(p.latitude);
903 max_lat = max_lat.max(p.latitude);
904 min_lon = min_lon.min(p.longitude);
905 max_lon = max_lon.max(p.longitude);
906 }
907 Some((min_lat, max_lat, min_lon, max_lon))
908 }
909 Geography::MultiLineString(mls) => {
910 let all_points: Vec<&GeographyValue> =
911 mls.linestrings.iter().flat_map(|ls| &ls.points).collect();
912 if all_points.is_empty() {
913 return None;
914 }
915 let mut min_lat = f64::MAX;
916 let mut max_lat = f64::MIN;
917 let mut min_lon = f64::MAX;
918 let mut max_lon = f64::MIN;
919 for p in all_points {
920 min_lat = min_lat.min(p.latitude);
921 max_lat = max_lat.max(p.latitude);
922 min_lon = min_lon.min(p.longitude);
923 max_lon = max_lon.max(p.longitude);
924 }
925 Some((min_lat, max_lat, min_lon, max_lon))
926 }
927 Geography::MultiPolygon(mp) => {
928 let all_points: Vec<&GeographyValue> = mp
929 .polygons
930 .iter()
931 .flat_map(|p| p.exterior.points.iter())
932 .collect();
933 if all_points.is_empty() {
934 return None;
935 }
936 let mut min_lat = f64::MAX;
937 let mut max_lat = f64::MIN;
938 let mut min_lon = f64::MAX;
939 let mut max_lon = f64::MIN;
940 for p in all_points {
941 min_lat = min_lat.min(p.latitude);
942 max_lat = max_lat.max(p.latitude);
943 min_lon = min_lon.min(p.longitude);
944 max_lon = max_lon.max(p.longitude);
945 }
946 Some((min_lat, max_lat, min_lon, max_lon))
947 }
948 }
949 }
950}
951
952impl std::fmt::Display for Geography {
953 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
954 write!(f, "{}", self.to_wkt())
955 }
956}
957
958#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
964#[serde(tag = "type", rename_all = "PascalCase")]
965pub enum GeoJsonGeometry {
966 Point {
967 coordinates: Vec<f64>,
968 },
969 LineString {
970 coordinates: Vec<Vec<f64>>,
971 },
972 Polygon {
973 coordinates: Vec<Vec<Vec<f64>>>,
974 },
975 MultiPoint {
976 coordinates: Vec<Vec<f64>>,
977 },
978 MultiLineString {
979 coordinates: Vec<Vec<Vec<f64>>>,
980 },
981 MultiPolygon {
982 coordinates: Vec<Vec<Vec<Vec<f64>>>>,
983 },
984}
985
986#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
988pub struct GeoJsonFeature {
989 #[serde(rename = "type")]
990 pub type_: String,
991 pub geometry: Option<GeoJsonGeometry>,
992 pub properties: serde_json::Map<String, serde_json::Value>,
993}
994
995#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
997pub struct GeoJsonFeatureCollection {
998 #[serde(rename = "type")]
999 pub type_: String,
1000 pub features: Vec<GeoJsonFeature>,
1001}
1002
1003impl Geography {
1004 pub fn to_geojson(&self) -> GeoJsonGeometry {
1006 match self {
1007 Geography::Point(p) => GeoJsonGeometry::Point {
1008 coordinates: vec![p.longitude, p.latitude],
1009 },
1010 Geography::LineString(ls) => GeoJsonGeometry::LineString {
1011 coordinates: ls
1012 .points
1013 .iter()
1014 .map(|p| vec![p.longitude, p.latitude])
1015 .collect(),
1016 },
1017 Geography::Polygon(poly) => GeoJsonGeometry::Polygon {
1018 coordinates: {
1019 let mut rings = Vec::new();
1020 rings.push(
1021 poly.exterior
1022 .points
1023 .iter()
1024 .map(|p| vec![p.longitude, p.latitude])
1025 .collect(),
1026 );
1027 for hole in &poly.holes {
1028 rings.push(
1029 hole.points
1030 .iter()
1031 .map(|p| vec![p.longitude, p.latitude])
1032 .collect(),
1033 );
1034 }
1035 rings
1036 },
1037 },
1038 Geography::MultiPoint(mp) => GeoJsonGeometry::MultiPoint {
1039 coordinates: mp
1040 .points
1041 .iter()
1042 .map(|p| vec![p.longitude, p.latitude])
1043 .collect(),
1044 },
1045 Geography::MultiLineString(mls) => GeoJsonGeometry::MultiLineString {
1046 coordinates: mls
1047 .linestrings
1048 .iter()
1049 .map(|ls| {
1050 ls.points
1051 .iter()
1052 .map(|p| vec![p.longitude, p.latitude])
1053 .collect()
1054 })
1055 .collect(),
1056 },
1057 Geography::MultiPolygon(mp) => GeoJsonGeometry::MultiPolygon {
1058 coordinates: mp
1059 .polygons
1060 .iter()
1061 .map(|poly| {
1062 let mut rings: Vec<Vec<Vec<f64>>> = Vec::new();
1063 rings.push(
1064 poly.exterior
1065 .points
1066 .iter()
1067 .map(|p| vec![p.longitude, p.latitude])
1068 .collect(),
1069 );
1070 for hole in &poly.holes {
1071 rings.push(
1072 hole.points
1073 .iter()
1074 .map(|p| vec![p.longitude, p.latitude])
1075 .collect(),
1076 );
1077 }
1078 rings
1079 })
1080 .collect(),
1081 },
1082 }
1083 }
1084
1085 pub fn from_geojson(geojson: &GeoJsonGeometry) -> Result<Self, String> {
1087 match geojson {
1088 GeoJsonGeometry::Point { coordinates } => {
1089 if coordinates.len() < 2 {
1090 return Err("Point must have at least 2 coordinates".to_string());
1091 }
1092 Ok(Geography::Point(GeographyValue::new(
1093 coordinates[1],
1094 coordinates[0],
1095 )))
1096 }
1097 GeoJsonGeometry::LineString { coordinates } => {
1098 let points: Result<Vec<_>, _> = coordinates
1099 .iter()
1100 .map(|c| {
1101 if c.len() < 2 {
1102 Err("LineString coordinate must have at least 2 values".to_string())
1103 } else {
1104 Ok(GeographyValue::new(c[1], c[0]))
1105 }
1106 })
1107 .collect();
1108 Ok(Geography::LineString(LineStringValue::new(points?)))
1109 }
1110 GeoJsonGeometry::Polygon { coordinates } => {
1111 if coordinates.is_empty() {
1112 return Err("Polygon must have at least one ring".to_string());
1113 }
1114 let exterior = parse_ring(&coordinates[0])?;
1115 let holes: Result<Vec<_>, _> = coordinates[1..]
1116 .iter()
1117 .map(|ring| parse_ring(ring))
1118 .collect();
1119 Ok(Geography::Polygon(PolygonValue::new(exterior, holes?)))
1120 }
1121 GeoJsonGeometry::MultiPoint { coordinates } => {
1122 let points: Result<Vec<_>, _> = coordinates
1123 .iter()
1124 .map(|c| {
1125 if c.len() < 2 {
1126 Err("MultiPoint coordinate must have at least 2 values".to_string())
1127 } else {
1128 Ok(GeographyValue::new(c[1], c[0]))
1129 }
1130 })
1131 .collect();
1132 Ok(Geography::MultiPoint(MultiPointValue::new(points?)))
1133 }
1134 GeoJsonGeometry::MultiLineString { coordinates } => {
1135 let linestrings: Result<Vec<_>, _> =
1136 coordinates.iter().map(|ring| parse_ring(ring)).collect();
1137 Ok(Geography::MultiLineString(MultiLineStringValue::new(
1138 linestrings?,
1139 )))
1140 }
1141 GeoJsonGeometry::MultiPolygon { coordinates } => {
1142 let polygons: Result<Vec<_>, _> = coordinates
1143 .iter()
1144 .map(|poly_coords| {
1145 if poly_coords.is_empty() {
1146 return Err(
1147 "MultiPolygon polygon must have at least one ring".to_string()
1148 );
1149 }
1150 let exterior = parse_ring(&poly_coords[0])?;
1151 let holes: Result<Vec<_>, _> = poly_coords[1..]
1152 .iter()
1153 .map(|ring| parse_ring(ring))
1154 .collect();
1155 Ok(PolygonValue::new(exterior, holes?))
1156 })
1157 .collect();
1158 Ok(Geography::MultiPolygon(MultiPolygonValue::new(polygons?)))
1159 }
1160 }
1161 }
1162
1163 pub fn to_geojson_string(&self) -> String {
1165 serde_json::to_string(&self.to_geojson()).unwrap_or_default()
1166 }
1167
1168 pub fn from_geojson_string(json: &str) -> Result<Self, String> {
1170 let geojson: GeoJsonGeometry =
1171 serde_json::from_str(json).map_err(|e| format!("Invalid GeoJSON: {}", e))?;
1172 Self::from_geojson(&geojson)
1173 }
1174}
1175
1176fn parse_ring(coords: &[Vec<f64>]) -> Result<LineStringValue, String> {
1177 let points: Result<Vec<_>, _> = coords
1178 .iter()
1179 .map(|c| {
1180 if c.len() < 2 {
1181 Err("Ring coordinate must have at least 2 values".to_string())
1182 } else {
1183 Ok(GeographyValue::new(c[1], c[0]))
1184 }
1185 })
1186 .collect();
1187 Ok(LineStringValue::new(points?))
1188}
1189
1190#[cfg(test)]
1191mod tests {
1192 use super::*;
1193
1194 #[test]
1195 fn test_point_wkt_parsing() {
1196 let wkt = "POINT(116.4 39.9)";
1197 let geo = Geography::from_wkt(wkt).unwrap();
1198 assert!(matches!(geo, Geography::Point(_)));
1199 if let Geography::Point(p) = geo {
1200 assert!((p.longitude - 116.4).abs() < 1e-6);
1201 assert!((p.latitude - 39.9).abs() < 1e-6);
1202 }
1203 }
1204
1205 #[test]
1206 fn test_linestring_wkt_parsing() {
1207 let wkt = "LINESTRING(116.4 39.9, 121.5 31.2, 113.3 23.1)";
1208 let geo = Geography::from_wkt(wkt).unwrap();
1209 assert!(matches!(geo, Geography::LineString(_)));
1210 if let Geography::LineString(ls) = geo {
1211 assert_eq!(ls.points.len(), 3);
1212 assert!(ls.length() > 1000.0);
1213 }
1214 }
1215
1216 #[test]
1217 fn test_polygon_wkt_parsing() {
1218 let wkt = "POLYGON((116.0 40.0, 117.0 40.0, 117.0 39.0, 116.0 39.0, 116.0 40.0))";
1219 let geo = Geography::from_wkt(wkt).unwrap();
1220 assert!(matches!(geo, Geography::Polygon(_)));
1221 }
1222
1223 #[test]
1224 fn test_linestring_length() {
1225 let ls = LineStringValue::new(vec![
1226 GeographyValue::new(39.9, 116.4),
1227 GeographyValue::new(31.2, 121.5),
1228 ]);
1229 let length = ls.length();
1230 assert!(length > 1000.0);
1231 assert!(length < 1500.0);
1232 }
1233
1234 #[test]
1235 fn test_polygon_contains_point() {
1236 let polygon = PolygonValue::new(
1237 LineStringValue::new(vec![
1238 GeographyValue::new(40.0, 116.0),
1239 GeographyValue::new(40.0, 117.0),
1240 GeographyValue::new(39.0, 117.0),
1241 GeographyValue::new(39.0, 116.0),
1242 GeographyValue::new(40.0, 116.0),
1243 ]),
1244 vec![],
1245 );
1246 let point_inside = GeographyValue::new(39.5, 116.5);
1247 let point_outside = GeographyValue::new(50.0, 120.0);
1248
1249 assert!(polygon.contains_point(&point_inside));
1250 assert!(!polygon.contains_point(&point_outside));
1251 }
1252
1253 #[test]
1254 fn test_wkt_roundtrip() {
1255 let wkt = "POINT(116.4 39.9)";
1256 let geo = Geography::from_wkt(wkt).unwrap();
1257 let roundtrip = geo.to_wkt();
1258 let geo2 = Geography::from_wkt(&roundtrip).unwrap();
1259 assert_eq!(geo, geo2);
1260 }
1261
1262 #[test]
1263 fn test_centroid() {
1264 let ls = LineStringValue::new(vec![
1265 GeographyValue::new(0.0, 0.0),
1266 GeographyValue::new(10.0, 10.0),
1267 ]);
1268 let centroid = ls.centroid().unwrap();
1269 assert!((centroid.latitude - 5.0).abs() < 1e-6);
1270 assert!((centroid.longitude - 5.0).abs() < 1e-6);
1271 }
1272
1273 #[test]
1274 fn test_is_valid() {
1275 let valid_point = GeographyValue::new(45.0, 90.0);
1276 assert!(valid_point.is_valid());
1277
1278 let invalid_point = GeographyValue::new(100.0, 200.0);
1279 assert!(!invalid_point.is_valid());
1280 }
1281
1282 #[test]
1283 fn test_multipoint_wkt_parsing() {
1284 let wkt = "MULTIPOINT(116.4 39.9, 121.5 31.2)";
1285 let geo = Geography::from_wkt(wkt).unwrap();
1286 assert!(matches!(geo, Geography::MultiPoint(_)));
1287 if let Geography::MultiPoint(mp) = geo {
1288 assert_eq!(mp.points.len(), 2);
1289 }
1290 }
1291
1292 #[test]
1293 fn test_multilinestring_wkt_parsing() {
1294 let wkt = "MULTILINESTRING((116.4 39.9, 121.5 31.2), (113.3 23.1, 114.1 22.5))";
1295 let geo = Geography::from_wkt(wkt).unwrap();
1296 assert!(matches!(geo, Geography::MultiLineString(_)));
1297 if let Geography::MultiLineString(mls) = geo {
1298 assert_eq!(mls.linestrings.len(), 2);
1299 }
1300 }
1301
1302 #[test]
1303 fn test_geojson_point() {
1304 let point = Geography::Point(GeographyValue::new(39.9, 116.4));
1305 let geojson = point.to_geojson();
1306 assert!(matches!(geojson, GeoJsonGeometry::Point { .. }));
1307 if let GeoJsonGeometry::Point { coordinates } = geojson {
1308 assert_eq!(coordinates, vec![116.4, 39.9]);
1309 }
1310 }
1311
1312 #[test]
1313 fn test_geojson_linestring() {
1314 let ls = Geography::LineString(LineStringValue::new(vec![
1315 GeographyValue::new(39.9, 116.4),
1316 GeographyValue::new(31.2, 121.5),
1317 ]));
1318 let geojson = ls.to_geojson();
1319 assert!(matches!(geojson, GeoJsonGeometry::LineString { .. }));
1320 if let GeoJsonGeometry::LineString { coordinates } = geojson {
1321 assert_eq!(coordinates.len(), 2);
1322 assert_eq!(coordinates[0], vec![116.4, 39.9]);
1323 }
1324 }
1325
1326 #[test]
1327 fn test_geojson_polygon() {
1328 let polygon = PolygonValue::new(
1329 LineStringValue::new(vec![
1330 GeographyValue::new(40.0, 116.0),
1331 GeographyValue::new(40.0, 117.0),
1332 GeographyValue::new(39.0, 117.0),
1333 GeographyValue::new(39.0, 116.0),
1334 GeographyValue::new(40.0, 116.0),
1335 ]),
1336 vec![],
1337 );
1338 let geo = Geography::Polygon(polygon);
1339 let geojson = geo.to_geojson();
1340 assert!(matches!(geojson, GeoJsonGeometry::Polygon { .. }));
1341 }
1342
1343 #[test]
1344 fn test_geojson_roundtrip() {
1345 let point = Geography::Point(GeographyValue::new(39.9, 116.4));
1346 let geojson = point.to_geojson();
1347 let parsed = Geography::from_geojson(&geojson).unwrap();
1348 assert_eq!(point, parsed);
1349
1350 let ls = Geography::LineString(LineStringValue::new(vec![
1351 GeographyValue::new(39.9, 116.4),
1352 GeographyValue::new(31.2, 121.5),
1353 ]));
1354 let geojson = ls.to_geojson();
1355 let parsed = Geography::from_geojson(&geojson).unwrap();
1356 assert_eq!(ls, parsed);
1357 }
1358
1359 #[test]
1360 fn test_geojson_string() {
1361 let point = Geography::Point(GeographyValue::new(39.9, 116.4));
1362 let json_str = point.to_geojson_string();
1363 assert!(json_str.contains("\"type\":\"Point\""));
1364 assert!(json_str.contains("\"coordinates\""));
1365
1366 let parsed = Geography::from_geojson_string(&json_str).unwrap();
1367 assert_eq!(point, parsed);
1368 }
1369}