1use crate::error::{OxiGdalError, Result};
6use core::fmt;
7use serde::{Deserialize, Serialize};
8
9#[cfg(feature = "std")]
10use std::vec::Vec;
11
12#[cfg(all(not(feature = "std"), feature = "alloc"))]
13use alloc::vec::Vec;
14
15#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
17pub struct Coordinate {
18 pub x: f64,
20 pub y: f64,
22 pub z: Option<f64>,
24 pub m: Option<f64>,
26}
27
28impl Coordinate {
29 #[must_use]
31 pub const fn new_2d(x: f64, y: f64) -> Self {
32 Self {
33 x,
34 y,
35 z: None,
36 m: None,
37 }
38 }
39
40 #[must_use]
42 pub const fn new_3d(x: f64, y: f64, z: f64) -> Self {
43 Self {
44 x,
45 y,
46 z: Some(z),
47 m: None,
48 }
49 }
50
51 #[must_use]
53 pub const fn new_2dm(x: f64, y: f64, m: f64) -> Self {
54 Self {
55 x,
56 y,
57 z: None,
58 m: Some(m),
59 }
60 }
61
62 #[must_use]
64 pub const fn new_3dm(x: f64, y: f64, z: f64, m: f64) -> Self {
65 Self {
66 x,
67 y,
68 z: Some(z),
69 m: Some(m),
70 }
71 }
72
73 #[must_use]
75 pub const fn has_z(&self) -> bool {
76 self.z.is_some()
77 }
78
79 #[must_use]
81 pub const fn has_m(&self) -> bool {
82 self.m.is_some()
83 }
84
85 #[must_use]
87 pub const fn dimensions(&self) -> u8 {
88 let mut dims = 2;
89 if self.z.is_some() {
90 dims += 1;
91 }
92 if self.m.is_some() {
93 dims += 1;
94 }
95 dims
96 }
97
98 #[must_use]
100 pub const fn x(&self) -> f64 {
101 self.x
102 }
103
104 #[must_use]
106 pub const fn y(&self) -> f64 {
107 self.y
108 }
109
110 #[must_use]
112 pub const fn z(&self) -> Option<f64> {
113 self.z
114 }
115
116 #[must_use]
118 pub const fn m(&self) -> Option<f64> {
119 self.m
120 }
121}
122
123#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
125pub enum Geometry {
126 Point(Point),
128 LineString(LineString),
130 Polygon(Polygon),
132 MultiPoint(MultiPoint),
134 MultiLineString(MultiLineString),
136 MultiPolygon(MultiPolygon),
138 GeometryCollection(GeometryCollection),
140}
141
142impl Geometry {
143 #[must_use]
145 pub fn has_z(&self) -> bool {
146 match self {
147 Self::Point(p) => p.coord.has_z(),
148 Self::LineString(ls) => ls.coords.iter().any(|c| c.has_z()),
149 Self::Polygon(poly) => {
150 poly.exterior.coords.iter().any(|c| c.has_z())
151 || poly
152 .interiors
153 .iter()
154 .any(|r| r.coords.iter().any(|c| c.has_z()))
155 }
156 Self::MultiPoint(mp) => mp.points.iter().any(|p| p.coord.has_z()),
157 Self::MultiLineString(mls) => mls
158 .line_strings
159 .iter()
160 .any(|ls| ls.coords.iter().any(|c| c.has_z())),
161 Self::MultiPolygon(mpoly) => mpoly.polygons.iter().any(|poly| {
162 poly.exterior.coords.iter().any(|c| c.has_z())
163 || poly
164 .interiors
165 .iter()
166 .any(|r| r.coords.iter().any(|c| c.has_z()))
167 }),
168 Self::GeometryCollection(gc) => gc.geometries.iter().any(|g| g.has_z()),
169 }
170 }
171
172 #[must_use]
174 pub fn has_m(&self) -> bool {
175 match self {
176 Self::Point(p) => p.coord.has_m(),
177 Self::LineString(ls) => ls.coords.iter().any(|c| c.has_m()),
178 Self::Polygon(poly) => {
179 poly.exterior.coords.iter().any(|c| c.has_m())
180 || poly
181 .interiors
182 .iter()
183 .any(|r| r.coords.iter().any(|c| c.has_m()))
184 }
185 Self::MultiPoint(mp) => mp.points.iter().any(|p| p.coord.has_m()),
186 Self::MultiLineString(mls) => mls
187 .line_strings
188 .iter()
189 .any(|ls| ls.coords.iter().any(|c| c.has_m())),
190 Self::MultiPolygon(mpoly) => mpoly.polygons.iter().any(|poly| {
191 poly.exterior.coords.iter().any(|c| c.has_m())
192 || poly
193 .interiors
194 .iter()
195 .any(|r| r.coords.iter().any(|c| c.has_m()))
196 }),
197 Self::GeometryCollection(gc) => gc.geometries.iter().any(|g| g.has_m()),
198 }
199 }
200
201 #[must_use]
206 pub fn z_values(&self) -> Option<Vec<f64>> {
207 if !self.has_z() {
208 return None;
209 }
210 let mut out = Vec::new();
211 Self::collect_z_from(self, &mut out);
212 Some(out)
213 }
214
215 #[must_use]
220 pub fn m_values(&self) -> Option<Vec<f64>> {
221 if !self.has_m() {
222 return None;
223 }
224 let mut out = Vec::new();
225 Self::collect_m_from(self, &mut out);
226 Some(out)
227 }
228
229 fn collect_z_from(geom: &Geometry, out: &mut Vec<f64>) {
230 match geom {
231 Self::Point(p) => out.push(p.coord.z.unwrap_or(0.0)),
232 Self::LineString(ls) => {
233 for c in &ls.coords {
234 out.push(c.z.unwrap_or(0.0));
235 }
236 }
237 Self::Polygon(poly) => {
238 for c in &poly.exterior.coords {
239 out.push(c.z.unwrap_or(0.0));
240 }
241 for ring in &poly.interiors {
242 for c in &ring.coords {
243 out.push(c.z.unwrap_or(0.0));
244 }
245 }
246 }
247 Self::MultiPoint(mp) => {
248 for p in &mp.points {
249 out.push(p.coord.z.unwrap_or(0.0));
250 }
251 }
252 Self::MultiLineString(mls) => {
253 for ls in &mls.line_strings {
254 for c in &ls.coords {
255 out.push(c.z.unwrap_or(0.0));
256 }
257 }
258 }
259 Self::MultiPolygon(mpoly) => {
260 for poly in &mpoly.polygons {
261 for c in &poly.exterior.coords {
262 out.push(c.z.unwrap_or(0.0));
263 }
264 for ring in &poly.interiors {
265 for c in &ring.coords {
266 out.push(c.z.unwrap_or(0.0));
267 }
268 }
269 }
270 }
271 Self::GeometryCollection(gc) => {
272 for g in &gc.geometries {
273 Self::collect_z_from(g, out);
274 }
275 }
276 }
277 }
278
279 fn collect_m_from(geom: &Geometry, out: &mut Vec<f64>) {
280 match geom {
281 Self::Point(p) => out.push(p.coord.m.unwrap_or(0.0)),
282 Self::LineString(ls) => {
283 for c in &ls.coords {
284 out.push(c.m.unwrap_or(0.0));
285 }
286 }
287 Self::Polygon(poly) => {
288 for c in &poly.exterior.coords {
289 out.push(c.m.unwrap_or(0.0));
290 }
291 for ring in &poly.interiors {
292 for c in &ring.coords {
293 out.push(c.m.unwrap_or(0.0));
294 }
295 }
296 }
297 Self::MultiPoint(mp) => {
298 for p in &mp.points {
299 out.push(p.coord.m.unwrap_or(0.0));
300 }
301 }
302 Self::MultiLineString(mls) => {
303 for ls in &mls.line_strings {
304 for c in &ls.coords {
305 out.push(c.m.unwrap_or(0.0));
306 }
307 }
308 }
309 Self::MultiPolygon(mpoly) => {
310 for poly in &mpoly.polygons {
311 for c in &poly.exterior.coords {
312 out.push(c.m.unwrap_or(0.0));
313 }
314 for ring in &poly.interiors {
315 for c in &ring.coords {
316 out.push(c.m.unwrap_or(0.0));
317 }
318 }
319 }
320 }
321 Self::GeometryCollection(gc) => {
322 for g in &gc.geometries {
323 Self::collect_m_from(g, out);
324 }
325 }
326 }
327 }
328
329 #[must_use]
331 pub const fn geometry_type(&self) -> &'static str {
332 match self {
333 Self::Point(_) => "Point",
334 Self::LineString(_) => "LineString",
335 Self::Polygon(_) => "Polygon",
336 Self::MultiPoint(_) => "MultiPoint",
337 Self::MultiLineString(_) => "MultiLineString",
338 Self::MultiPolygon(_) => "MultiPolygon",
339 Self::GeometryCollection(_) => "GeometryCollection",
340 }
341 }
342
343 #[must_use]
345 pub fn is_empty(&self) -> bool {
346 match self {
347 Self::Point(p) => p.coord.x.is_nan() || p.coord.y.is_nan(),
348 Self::LineString(ls) => ls.coords.is_empty(),
349 Self::Polygon(p) => p.exterior.coords.is_empty(),
350 Self::MultiPoint(mp) => mp.points.is_empty(),
351 Self::MultiLineString(mls) => mls.line_strings.is_empty(),
352 Self::MultiPolygon(mp) => mp.polygons.is_empty(),
353 Self::GeometryCollection(gc) => gc.geometries.is_empty(),
354 }
355 }
356
357 #[must_use]
359 pub fn bounds(&self) -> Option<(f64, f64, f64, f64)> {
360 match self {
361 Self::Point(p) => {
362 if p.coord.x.is_nan() || p.coord.y.is_nan() {
363 None
364 } else {
365 Some((p.coord.x, p.coord.y, p.coord.x, p.coord.y))
366 }
367 }
368 Self::LineString(ls) => ls.bounds(),
369 Self::Polygon(p) => p.bounds(),
370 Self::MultiPoint(mp) => mp.bounds(),
371 Self::MultiLineString(mls) => mls.bounds(),
372 Self::MultiPolygon(mp) => mp.bounds(),
373 Self::GeometryCollection(gc) => gc.bounds(),
374 }
375 }
376}
377
378#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
380pub struct Point {
381 pub coord: Coordinate,
383}
384
385impl Point {
386 #[must_use]
388 pub const fn new(x: f64, y: f64) -> Self {
389 Self {
390 coord: Coordinate::new_2d(x, y),
391 }
392 }
393
394 #[must_use]
396 pub const fn new_3d(x: f64, y: f64, z: f64) -> Self {
397 Self {
398 coord: Coordinate::new_3d(x, y, z),
399 }
400 }
401
402 #[must_use]
404 pub const fn from_coord(coord: Coordinate) -> Self {
405 Self { coord }
406 }
407
408 #[must_use]
410 pub const fn x(&self) -> f64 {
411 self.coord.x
412 }
413
414 #[must_use]
416 pub const fn y(&self) -> f64 {
417 self.coord.y
418 }
419
420 #[must_use]
422 pub const fn z(&self) -> Option<f64> {
423 self.coord.z
424 }
425}
426
427#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
429pub struct LineString {
430 pub coords: Vec<Coordinate>,
432}
433
434impl LineString {
435 pub fn new(coords: Vec<Coordinate>) -> Result<Self> {
437 if coords.len() < 2 {
438 return Err(OxiGdalError::InvalidParameter {
439 parameter: "coords",
440 message: "LineString must have at least 2 coordinates".to_string(),
441 });
442 }
443 Ok(Self { coords })
444 }
445
446 #[must_use]
448 pub const fn empty() -> Self {
449 Self { coords: Vec::new() }
450 }
451
452 pub fn push(&mut self, coord: Coordinate) {
454 self.coords.push(coord);
455 }
456
457 #[must_use]
459 pub fn len(&self) -> usize {
460 self.coords.len()
461 }
462
463 #[must_use]
465 pub fn is_empty(&self) -> bool {
466 self.coords.is_empty()
467 }
468
469 #[must_use]
471 pub fn bounds(&self) -> Option<(f64, f64, f64, f64)> {
472 if self.coords.is_empty() {
473 return None;
474 }
475
476 let mut min_x = f64::INFINITY;
477 let mut min_y = f64::INFINITY;
478 let mut max_x = f64::NEG_INFINITY;
479 let mut max_y = f64::NEG_INFINITY;
480
481 for coord in &self.coords {
482 min_x = min_x.min(coord.x);
483 min_y = min_y.min(coord.y);
484 max_x = max_x.max(coord.x);
485 max_y = max_y.max(coord.y);
486 }
487
488 Some((min_x, min_y, max_x, max_y))
489 }
490
491 #[must_use]
493 pub fn coords(&self) -> &[Coordinate] {
494 &self.coords
495 }
496
497 pub fn points(&self) -> impl Iterator<Item = &Coordinate> {
499 self.coords.iter()
500 }
501}
502
503#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
505pub struct Polygon {
506 pub exterior: LineString,
508 pub interiors: Vec<LineString>,
510}
511
512impl Polygon {
513 pub fn new(exterior: LineString, interiors: Vec<LineString>) -> Result<Self> {
515 if exterior.coords.len() < 4 {
516 return Err(OxiGdalError::InvalidParameter {
517 parameter: "exterior",
518 message: "Polygon exterior ring must have at least 4 coordinates".to_string(),
519 });
520 }
521
522 let first = &exterior.coords[0];
524 let last = &exterior.coords[exterior.coords.len() - 1];
525 if (first.x - last.x).abs() > f64::EPSILON || (first.y - last.y).abs() > f64::EPSILON {
526 return Err(OxiGdalError::InvalidParameter {
527 parameter: "exterior",
528 message: "Polygon exterior ring must be closed".to_string(),
529 });
530 }
531
532 for interior in &interiors {
534 if interior.coords.len() < 4 {
535 return Err(OxiGdalError::InvalidParameter {
536 parameter: "interiors",
537 message: "Polygon interior ring must have at least 4 coordinates".to_string(),
538 });
539 }
540
541 let first = &interior.coords[0];
542 let last = &interior.coords[interior.coords.len() - 1];
543 if (first.x - last.x).abs() > f64::EPSILON || (first.y - last.y).abs() > f64::EPSILON {
544 return Err(OxiGdalError::InvalidParameter {
545 parameter: "interiors",
546 message: "Polygon interior ring must be closed".to_string(),
547 });
548 }
549 }
550
551 Ok(Self {
552 exterior,
553 interiors,
554 })
555 }
556
557 #[must_use]
559 pub const fn empty() -> Self {
560 Self {
561 exterior: LineString::empty(),
562 interiors: Vec::new(),
563 }
564 }
565
566 #[must_use]
568 pub fn bounds(&self) -> Option<(f64, f64, f64, f64)> {
569 self.exterior.bounds()
570 }
571
572 #[must_use]
574 pub fn exterior(&self) -> &LineString {
575 &self.exterior
576 }
577
578 #[must_use]
580 pub fn interiors(&self) -> &[LineString] {
581 &self.interiors
582 }
583}
584
585#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
587pub struct MultiPoint {
588 pub points: Vec<Point>,
590}
591
592impl MultiPoint {
593 #[must_use]
595 pub const fn new(points: Vec<Point>) -> Self {
596 Self { points }
597 }
598
599 #[must_use]
601 pub const fn empty() -> Self {
602 Self { points: Vec::new() }
603 }
604
605 pub fn push(&mut self, point: Point) {
607 self.points.push(point);
608 }
609
610 #[must_use]
612 pub fn bounds(&self) -> Option<(f64, f64, f64, f64)> {
613 if self.points.is_empty() {
614 return None;
615 }
616
617 let mut min_x = f64::INFINITY;
618 let mut min_y = f64::INFINITY;
619 let mut max_x = f64::NEG_INFINITY;
620 let mut max_y = f64::NEG_INFINITY;
621
622 for point in &self.points {
623 min_x = min_x.min(point.coord.x);
624 min_y = min_y.min(point.coord.y);
625 max_x = max_x.max(point.coord.x);
626 max_y = max_y.max(point.coord.y);
627 }
628
629 Some((min_x, min_y, max_x, max_y))
630 }
631}
632
633#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
635pub struct MultiLineString {
636 pub line_strings: Vec<LineString>,
638}
639
640impl MultiLineString {
641 #[must_use]
643 pub const fn new(line_strings: Vec<LineString>) -> Self {
644 Self { line_strings }
645 }
646
647 #[must_use]
649 pub const fn empty() -> Self {
650 Self {
651 line_strings: Vec::new(),
652 }
653 }
654
655 pub fn push(&mut self, line_string: LineString) {
657 self.line_strings.push(line_string);
658 }
659
660 #[must_use]
662 pub fn bounds(&self) -> Option<(f64, f64, f64, f64)> {
663 if self.line_strings.is_empty() {
664 return None;
665 }
666
667 let mut min_x = f64::INFINITY;
668 let mut min_y = f64::INFINITY;
669 let mut max_x = f64::NEG_INFINITY;
670 let mut max_y = f64::NEG_INFINITY;
671
672 for ls in &self.line_strings {
673 if let Some((x_min, y_min, x_max, y_max)) = ls.bounds() {
674 min_x = min_x.min(x_min);
675 min_y = min_y.min(y_min);
676 max_x = max_x.max(x_max);
677 max_y = max_y.max(y_max);
678 }
679 }
680
681 if min_x.is_infinite() {
682 None
683 } else {
684 Some((min_x, min_y, max_x, max_y))
685 }
686 }
687}
688
689#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
691pub struct MultiPolygon {
692 pub polygons: Vec<Polygon>,
694}
695
696impl MultiPolygon {
697 #[must_use]
699 pub const fn new(polygons: Vec<Polygon>) -> Self {
700 Self { polygons }
701 }
702
703 #[must_use]
705 pub const fn empty() -> Self {
706 Self {
707 polygons: Vec::new(),
708 }
709 }
710
711 pub fn push(&mut self, polygon: Polygon) {
713 self.polygons.push(polygon);
714 }
715
716 #[must_use]
718 pub fn bounds(&self) -> Option<(f64, f64, f64, f64)> {
719 if self.polygons.is_empty() {
720 return None;
721 }
722
723 let mut min_x = f64::INFINITY;
724 let mut min_y = f64::INFINITY;
725 let mut max_x = f64::NEG_INFINITY;
726 let mut max_y = f64::NEG_INFINITY;
727
728 for poly in &self.polygons {
729 if let Some((x_min, y_min, x_max, y_max)) = poly.bounds() {
730 min_x = min_x.min(x_min);
731 min_y = min_y.min(y_min);
732 max_x = max_x.max(x_max);
733 max_y = max_y.max(y_max);
734 }
735 }
736
737 if min_x.is_infinite() {
738 None
739 } else {
740 Some((min_x, min_y, max_x, max_y))
741 }
742 }
743}
744
745#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
747pub struct GeometryCollection {
748 pub geometries: Vec<Geometry>,
750}
751
752impl GeometryCollection {
753 #[must_use]
755 pub const fn new(geometries: Vec<Geometry>) -> Self {
756 Self { geometries }
757 }
758
759 #[must_use]
761 pub const fn empty() -> Self {
762 Self {
763 geometries: Vec::new(),
764 }
765 }
766
767 pub fn push(&mut self, geometry: Geometry) {
769 self.geometries.push(geometry);
770 }
771
772 #[must_use]
774 pub fn bounds(&self) -> Option<(f64, f64, f64, f64)> {
775 if self.geometries.is_empty() {
776 return None;
777 }
778
779 let mut min_x = f64::INFINITY;
780 let mut min_y = f64::INFINITY;
781 let mut max_x = f64::NEG_INFINITY;
782 let mut max_y = f64::NEG_INFINITY;
783
784 for geom in &self.geometries {
785 if let Some((x_min, y_min, x_max, y_max)) = geom.bounds() {
786 min_x = min_x.min(x_min);
787 min_y = min_y.min(y_min);
788 max_x = max_x.max(x_max);
789 max_y = max_y.max(y_max);
790 }
791 }
792
793 if min_x.is_infinite() {
794 None
795 } else {
796 Some((min_x, min_y, max_x, max_y))
797 }
798 }
799}
800
801impl fmt::Display for Coordinate {
804 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
805 write!(f, "{} {}", self.x, self.y)?;
806 if let Some(z) = self.z {
807 write!(f, " {z}")?;
808 }
809 Ok(())
810 }
811}
812
813impl fmt::Display for Point {
814 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
815 write!(f, "POINT({})", self.coord)
816 }
817}
818
819fn fmt_ring(f: &mut fmt::Formatter<'_>, ring: &LineString) -> fmt::Result {
821 write!(f, "(")?;
822 for (i, c) in ring.coords.iter().enumerate() {
823 if i > 0 {
824 write!(f, ", ")?;
825 }
826 write!(f, "{c}")?;
827 }
828 write!(f, ")")
829}
830
831impl fmt::Display for LineString {
832 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
833 write!(f, "LINESTRING")?;
834 fmt_ring(f, self)
835 }
836}
837
838impl fmt::Display for Polygon {
839 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
840 write!(f, "POLYGON(")?;
841 fmt_ring(f, &self.exterior)?;
842 for interior in &self.interiors {
843 write!(f, ", ")?;
844 fmt_ring(f, interior)?;
845 }
846 write!(f, ")")
847 }
848}
849
850impl fmt::Display for MultiPoint {
851 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
852 write!(f, "MULTIPOINT(")?;
853 for (i, p) in self.points.iter().enumerate() {
854 if i > 0 {
855 write!(f, ", ")?;
856 }
857 write!(f, "({} {})", p.coord.x, p.coord.y)?;
858 }
859 write!(f, ")")
860 }
861}
862
863impl fmt::Display for MultiLineString {
864 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
865 write!(f, "MULTILINESTRING(")?;
866 for (i, ls) in self.line_strings.iter().enumerate() {
867 if i > 0 {
868 write!(f, ", ")?;
869 }
870 fmt_ring(f, ls)?;
871 }
872 write!(f, ")")
873 }
874}
875
876impl fmt::Display for MultiPolygon {
877 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
878 write!(f, "MULTIPOLYGON(")?;
879 for (i, poly) in self.polygons.iter().enumerate() {
880 if i > 0 {
881 write!(f, ", ")?;
882 }
883 write!(f, "(")?;
884 fmt_ring(f, &poly.exterior)?;
885 for interior in &poly.interiors {
886 write!(f, ", ")?;
887 fmt_ring(f, interior)?;
888 }
889 write!(f, ")")?;
890 }
891 write!(f, ")")
892 }
893}
894
895impl fmt::Display for Geometry {
896 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
897 match self {
898 Self::Point(p) => write!(f, "{p}"),
899 Self::LineString(ls) => write!(f, "{ls}"),
900 Self::Polygon(poly) => write!(f, "{poly}"),
901 Self::MultiPoint(mp) => write!(f, "{mp}"),
902 Self::MultiLineString(mls) => write!(f, "{mls}"),
903 Self::MultiPolygon(mpoly) => write!(f, "{mpoly}"),
904 Self::GeometryCollection(gc) => {
905 write!(f, "GEOMETRYCOLLECTION(")?;
906 for (i, g) in gc.geometries.iter().enumerate() {
907 if i > 0 {
908 write!(f, ", ")?;
909 }
910 write!(f, "{g}")?;
911 }
912 write!(f, ")")
913 }
914 }
915 }
916}
917
918#[cfg(feature = "std")]
921mod wkb {
922 use std::io::Write;
927
928 pub(crate) const WKB_POINT: u32 = 1;
930 pub(crate) const WKB_LINESTRING: u32 = 2;
931 pub(crate) const WKB_POLYGON: u32 = 3;
932 pub(crate) const WKB_MULTIPOINT: u32 = 4;
933 pub(crate) const WKB_MULTILINESTRING: u32 = 5;
934 pub(crate) const WKB_MULTIPOLYGON: u32 = 6;
935 pub(crate) const WKB_GEOMETRYCOLLECTION: u32 = 7;
936
937 pub(crate) const WKB_Z_OFFSET: u32 = 1000;
939
940 pub(crate) fn write_header<W: Write>(w: &mut W, wkb_type: u32) -> std::io::Result<()> {
942 w.write_all(&[0x01])?; w.write_all(&wkb_type.to_le_bytes())
944 }
945
946 pub(crate) fn write_f64<W: Write>(w: &mut W, v: f64) -> std::io::Result<()> {
948 w.write_all(&v.to_le_bytes())
949 }
950
951 pub(crate) fn write_u32<W: Write>(w: &mut W, v: u32) -> std::io::Result<()> {
953 w.write_all(&v.to_le_bytes())
954 }
955}
956
957#[cfg(feature = "std")]
958impl Geometry {
959 #[must_use]
968 pub fn to_wkb(&self) -> Vec<u8> {
969 let mut buf = Vec::new();
970 if self.write_wkb(&mut buf).is_err() {
972 unreachable!("Vec<u8> writes are infallible");
974 }
975 buf
976 }
977
978 pub fn write_wkb<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
985 match self {
986 Self::Point(p) => write_point_wkb(w, p),
987 Self::LineString(ls) => write_linestring_wkb(w, ls),
988 Self::Polygon(poly) => write_polygon_wkb(w, poly),
989 Self::MultiPoint(mp) => write_multipoint_wkb(w, mp),
990 Self::MultiLineString(mls) => write_multilinestring_wkb(w, mls),
991 Self::MultiPolygon(mpoly) => write_multipolygon_wkb(w, mpoly),
992 Self::GeometryCollection(gc) => write_geometrycollection_wkb(w, gc),
993 }
994 }
995}
996
997#[cfg(feature = "std")]
1001fn write_point_wkb<W: std::io::Write>(w: &mut W, p: &Point) -> std::io::Result<()> {
1002 use wkb::{WKB_POINT, WKB_Z_OFFSET};
1003 let has_z = p.coord.z.is_some();
1004 let wkb_type = if has_z {
1005 WKB_POINT + WKB_Z_OFFSET
1006 } else {
1007 WKB_POINT
1008 };
1009 wkb::write_header(w, wkb_type)?;
1010 wkb::write_f64(w, p.coord.x)?;
1011 wkb::write_f64(w, p.coord.y)?;
1012 if let Some(z) = p.coord.z {
1013 wkb::write_f64(w, z)?;
1014 }
1015 Ok(())
1016}
1017
1018#[cfg(feature = "std")]
1020fn write_linestring_wkb<W: std::io::Write>(w: &mut W, ls: &LineString) -> std::io::Result<()> {
1021 wkb::write_header(w, wkb::WKB_LINESTRING)?;
1022 let count = ls.coords.len() as u32;
1023 wkb::write_u32(w, count)?;
1024 for coord in &ls.coords {
1025 wkb::write_f64(w, coord.x)?;
1026 wkb::write_f64(w, coord.y)?;
1027 }
1028 Ok(())
1029}
1030
1031#[cfg(feature = "std")]
1033fn write_ring_wkb<W: std::io::Write>(w: &mut W, ring: &LineString) -> std::io::Result<()> {
1034 let count = ring.coords.len() as u32;
1035 wkb::write_u32(w, count)?;
1036 for coord in &ring.coords {
1037 wkb::write_f64(w, coord.x)?;
1038 wkb::write_f64(w, coord.y)?;
1039 }
1040 Ok(())
1041}
1042
1043#[cfg(feature = "std")]
1047fn write_polygon_wkb<W: std::io::Write>(w: &mut W, poly: &Polygon) -> std::io::Result<()> {
1048 wkb::write_header(w, wkb::WKB_POLYGON)?;
1049 let ring_count = 1u32 + poly.interiors.len() as u32;
1050 wkb::write_u32(w, ring_count)?;
1051 write_ring_wkb(w, &poly.exterior)?;
1052 for interior in &poly.interiors {
1053 write_ring_wkb(w, interior)?;
1054 }
1055 Ok(())
1056}
1057
1058#[cfg(feature = "std")]
1062fn write_multipoint_wkb<W: std::io::Write>(w: &mut W, mp: &MultiPoint) -> std::io::Result<()> {
1063 wkb::write_header(w, wkb::WKB_MULTIPOINT)?;
1064 wkb::write_u32(w, mp.points.len() as u32)?;
1065 for point in &mp.points {
1066 write_point_wkb(w, point)?;
1067 }
1068 Ok(())
1069}
1070
1071#[cfg(feature = "std")]
1075fn write_multilinestring_wkb<W: std::io::Write>(
1076 w: &mut W,
1077 mls: &MultiLineString,
1078) -> std::io::Result<()> {
1079 wkb::write_header(w, wkb::WKB_MULTILINESTRING)?;
1080 wkb::write_u32(w, mls.line_strings.len() as u32)?;
1081 for ls in &mls.line_strings {
1082 write_linestring_wkb(w, ls)?;
1083 }
1084 Ok(())
1085}
1086
1087#[cfg(feature = "std")]
1091fn write_multipolygon_wkb<W: std::io::Write>(
1092 w: &mut W,
1093 mpoly: &MultiPolygon,
1094) -> std::io::Result<()> {
1095 wkb::write_header(w, wkb::WKB_MULTIPOLYGON)?;
1096 wkb::write_u32(w, mpoly.polygons.len() as u32)?;
1097 for poly in &mpoly.polygons {
1098 write_polygon_wkb(w, poly)?;
1099 }
1100 Ok(())
1101}
1102
1103#[cfg(feature = "std")]
1107fn write_geometrycollection_wkb<W: std::io::Write>(
1108 w: &mut W,
1109 gc: &GeometryCollection,
1110) -> std::io::Result<()> {
1111 wkb::write_header(w, wkb::WKB_GEOMETRYCOLLECTION)?;
1112 wkb::write_u32(w, gc.geometries.len() as u32)?;
1113 for geom in &gc.geometries {
1114 geom.write_wkb(w)?;
1115 }
1116 Ok(())
1117}
1118
1119#[cfg(test)]
1122mod tests {
1123 use super::*;
1124
1125 #[test]
1126 fn test_coordinate_2d() {
1127 let coord = Coordinate::new_2d(1.0, 2.0);
1128 assert_eq!(coord.x, 1.0);
1129 assert_eq!(coord.y, 2.0);
1130 assert!(!coord.has_z());
1131 assert!(!coord.has_m());
1132 assert_eq!(coord.dimensions(), 2);
1133 }
1134
1135 #[test]
1136 fn test_coordinate_3d() {
1137 let coord = Coordinate::new_3d(1.0, 2.0, 3.0);
1138 assert_eq!(coord.x, 1.0);
1139 assert_eq!(coord.y, 2.0);
1140 assert_eq!(coord.z, Some(3.0));
1141 assert!(coord.has_z());
1142 assert!(!coord.has_m());
1143 assert_eq!(coord.dimensions(), 3);
1144 }
1145
1146 #[test]
1147 fn test_point() {
1148 let point = Point::new(1.0, 2.0);
1149 assert_eq!(point.coord.x, 1.0);
1150 assert_eq!(point.coord.y, 2.0);
1151 }
1152
1153 #[test]
1154 fn test_linestring() {
1155 let coords = vec![
1156 Coordinate::new_2d(0.0, 0.0),
1157 Coordinate::new_2d(1.0, 1.0),
1158 Coordinate::new_2d(2.0, 0.0),
1159 ];
1160 let ls = LineString::new(coords).ok();
1161 assert!(ls.is_some());
1162 let ls = ls.expect("linestring creation failed");
1163 assert_eq!(ls.len(), 3);
1164 assert!(!ls.is_empty());
1165
1166 let bounds = ls.bounds();
1167 assert!(bounds.is_some());
1168 let (min_x, min_y, max_x, max_y) = bounds.expect("bounds calculation failed");
1169 assert_eq!(min_x, 0.0);
1170 assert_eq!(min_y, 0.0);
1171 assert_eq!(max_x, 2.0);
1172 assert_eq!(max_y, 1.0);
1173 }
1174
1175 #[test]
1176 fn test_linestring_invalid() {
1177 let coords = vec![Coordinate::new_2d(0.0, 0.0)];
1178 let result = LineString::new(coords);
1179 assert!(result.is_err());
1180 }
1181
1182 #[test]
1183 fn test_polygon() {
1184 let exterior_coords = vec![
1185 Coordinate::new_2d(0.0, 0.0),
1186 Coordinate::new_2d(1.0, 0.0),
1187 Coordinate::new_2d(1.0, 1.0),
1188 Coordinate::new_2d(0.0, 1.0),
1189 Coordinate::new_2d(0.0, 0.0),
1190 ];
1191 let exterior = LineString::new(exterior_coords).ok();
1192 assert!(exterior.is_some());
1193 let exterior = exterior.expect("linestring creation failed");
1194
1195 let poly = Polygon::new(exterior, vec![]);
1196 assert!(poly.is_ok());
1197 }
1198
1199 #[test]
1200 fn test_polygon_not_closed() {
1201 let exterior_coords = vec![
1202 Coordinate::new_2d(0.0, 0.0),
1203 Coordinate::new_2d(1.0, 0.0),
1204 Coordinate::new_2d(1.0, 1.0),
1205 Coordinate::new_2d(0.0, 1.0),
1206 ];
1207 let exterior = LineString::new(exterior_coords).ok();
1208 assert!(exterior.is_some());
1209 let exterior = exterior.expect("linestring creation failed");
1210
1211 let result = Polygon::new(exterior, vec![]);
1212 assert!(result.is_err());
1213 }
1214
1215 #[test]
1218 #[cfg(feature = "std")]
1219 fn test_wkb_point_2d() {
1220 let geom = Geometry::Point(Point::new(1.0_f64, 2.0_f64));
1222 let wkb = geom.to_wkb();
1223
1224 assert_eq!(wkb[0], 0x01, "byte-order marker");
1225 assert_eq!(&wkb[1..5], &1u32.to_le_bytes(), "wkb_type must be 1");
1226 assert_eq!(wkb.len(), 21, "1 + 4 + 8 + 8 = 21 bytes for 2D point");
1227
1228 let x = f64::from_le_bytes(wkb[5..13].try_into().expect("8 bytes"));
1229 let y = f64::from_le_bytes(wkb[13..21].try_into().expect("8 bytes"));
1230 assert_eq!(x, 1.0_f64);
1231 assert_eq!(y, 2.0_f64);
1232 }
1233
1234 #[test]
1235 #[cfg(feature = "std")]
1236 fn test_wkb_point_3d() {
1237 let geom = Geometry::Point(Point::new_3d(1.0_f64, 2.0_f64, 3.0_f64));
1239 let wkb = geom.to_wkb();
1240
1241 assert_eq!(wkb[0], 0x01);
1242 assert_eq!(&wkb[1..5], &1001u32.to_le_bytes(), "wkb_type must be 1001");
1243 assert_eq!(wkb.len(), 29, "1 + 4 + 8 + 8 + 8 = 29 bytes for 3D point");
1244
1245 let z = f64::from_le_bytes(wkb[21..29].try_into().expect("8 bytes"));
1246 assert_eq!(z, 3.0_f64);
1247 }
1248
1249 #[test]
1250 #[cfg(feature = "std")]
1251 fn test_wkb_write_equals_to_wkb() {
1252 let geom = Geometry::Point(Point::new(5.0_f64, 10.0_f64));
1253 let a = geom.to_wkb();
1254 let mut b = Vec::new();
1255 geom.write_wkb(&mut b).expect("Vec<u8> write cannot fail");
1256 assert_eq!(a, b);
1257 }
1258
1259 #[test]
1260 #[cfg(feature = "std")]
1261 fn test_wkb_linestring() {
1262 let coords = vec![
1263 Coordinate::new_2d(0.0, 0.0),
1264 Coordinate::new_2d(1.0, 1.0),
1265 Coordinate::new_2d(2.0, 0.0),
1266 ];
1267 let ls = LineString::new(coords).expect("valid linestring");
1268 let geom = Geometry::LineString(ls);
1269 let wkb = geom.to_wkb();
1270
1271 assert_eq!(wkb[0], 0x01);
1274 assert_eq!(&wkb[1..5], &2u32.to_le_bytes(), "wkb_type must be 2");
1275
1276 let count = u32::from_le_bytes(wkb[5..9].try_into().expect("4 bytes"));
1277 assert_eq!(count, 3, "three coordinates");
1278 assert_eq!(wkb.len(), 1 + 4 + 4 + 3 * 16, "total length");
1279 }
1280
1281 #[test]
1282 #[cfg(feature = "std")]
1283 fn test_wkb_polygon_simple() {
1284 let exterior_coords = vec![
1285 Coordinate::new_2d(0.0, 0.0),
1286 Coordinate::new_2d(1.0, 0.0),
1287 Coordinate::new_2d(1.0, 1.0),
1288 Coordinate::new_2d(0.0, 1.0),
1289 Coordinate::new_2d(0.0, 0.0),
1290 ];
1291 let exterior = LineString::new(exterior_coords).expect("valid exterior");
1292 let poly = Polygon::new(exterior, vec![]).expect("valid polygon");
1293 let geom = Geometry::Polygon(poly);
1294 let wkb = geom.to_wkb();
1295
1296 assert_eq!(wkb[0], 0x01);
1297 assert_eq!(&wkb[1..5], &3u32.to_le_bytes(), "wkb_type must be 3");
1298
1299 let ring_count = u32::from_le_bytes(wkb[5..9].try_into().expect("4 bytes"));
1300 assert_eq!(ring_count, 1, "one exterior ring");
1301 let point_count = u32::from_le_bytes(wkb[9..13].try_into().expect("4 bytes"));
1303 assert_eq!(point_count, 5);
1304 }
1305
1306 #[test]
1307 #[cfg(feature = "std")]
1308 fn test_wkb_geometry_collection() {
1309 let gc = GeometryCollection::new(vec![
1310 Geometry::Point(Point::new(0.0, 0.0)),
1311 Geometry::Point(Point::new(1.0, 1.0)),
1312 ]);
1313 let geom = Geometry::GeometryCollection(gc);
1314 let wkb = geom.to_wkb();
1315
1316 assert_eq!(wkb[0], 0x01);
1317 assert_eq!(&wkb[1..5], &7u32.to_le_bytes(), "wkb_type must be 7");
1318
1319 let member_count = u32::from_le_bytes(wkb[5..9].try_into().expect("4 bytes"));
1320 assert_eq!(member_count, 2, "two member geometries");
1321 }
1322
1323 #[test]
1324 #[cfg(feature = "std")]
1325 fn test_wkb_multipoint() {
1326 let mp = MultiPoint::new(vec![Point::new(0.0, 0.0), Point::new(3.0, 4.0)]);
1327 let geom = Geometry::MultiPoint(mp);
1328 let wkb = geom.to_wkb();
1329
1330 assert_eq!(&wkb[1..5], &4u32.to_le_bytes(), "wkb_type must be 4");
1331 let count = u32::from_le_bytes(wkb[5..9].try_into().expect("4 bytes"));
1332 assert_eq!(count, 2);
1333 }
1334
1335 #[test]
1336 fn test_display_geometry_point_wkt() {
1337 let p = Geometry::Point(Point::new(1.0, 2.0));
1338 assert_eq!(p.to_string(), "POINT(1 2)");
1339 }
1340
1341 #[test]
1342 fn test_display_geometry_point_3d() {
1343 let p = Geometry::Point(Point::new_3d(1.0, 2.0, 3.0));
1344 assert_eq!(p.to_string(), "POINT(1 2 3)");
1345 }
1346
1347 #[test]
1348 fn test_display_linestring() {
1349 let coords = vec![Coordinate::new_2d(0.0, 0.0), Coordinate::new_2d(1.0, 1.0)];
1350 let ls = Geometry::LineString(LineString::new(coords).expect("valid"));
1351 assert_eq!(ls.to_string(), "LINESTRING(0 0, 1 1)");
1352 }
1353
1354 #[test]
1355 fn test_display_geometry_collection() {
1356 let gc = Geometry::GeometryCollection(GeometryCollection::new(vec![
1357 Geometry::Point(Point::new(0.0, 0.0)),
1358 Geometry::Point(Point::new(1.0, 1.0)),
1359 ]));
1360 assert_eq!(gc.to_string(), "GEOMETRYCOLLECTION(POINT(0 0), POINT(1 1))");
1361 }
1362}