1use crate::error::{Error, Result};
62use crate::geometry::{Coord, Geometry, GeometryType, LineString, Polygon};
63
64const BYTE_ORDER_LE: u8 = 1;
65
66const WKB_POINT: u32 = 1;
67const WKB_LINESTRING: u32 = 2;
68const WKB_POLYGON: u32 = 3;
69const WKB_MULTIPOINT: u32 = 4;
70const WKB_MULTILINESTRING: u32 = 5;
71const WKB_MULTIPOLYGON: u32 = 6;
72const WKB_GEOMETRYCOLLECTION: u32 = 7;
73
74impl Geometry {
75 pub fn to_wkb(&self) -> Vec<u8> {
77 let mut out = Vec::with_capacity(estimated_size(self));
78 write_geometry(self, &mut out);
79 out
80 }
81
82 pub fn write_wkb_into(&self, buf: &mut Vec<u8>) {
85 buf.clear();
86 buf.reserve(estimated_size(self));
87 write_geometry(self, buf);
88 }
89}
90
91fn estimated_size(g: &Geometry) -> usize {
92 let coord_count = count_coords(g);
95 5 + 4 + coord_count * 16
96}
97
98fn count_coords(g: &Geometry) -> usize {
99 match g {
100 Geometry::Point(_) => 1,
101 Geometry::LineString(ls) => ls.coords.len(),
102 Geometry::Polygon(p) => {
103 p.exterior.coords.len() + p.holes.iter().map(|h| h.coords.len()).sum::<usize>()
104 }
105 Geometry::MultiPoint(v) => v.len(),
106 Geometry::MultiLineString(v) => v.iter().map(|ls| ls.coords.len()).sum(),
107 Geometry::MultiPolygon(v) => v
108 .iter()
109 .map(|p| {
110 p.exterior.coords.len() + p.holes.iter().map(|h| h.coords.len()).sum::<usize>()
111 })
112 .sum(),
113 Geometry::GeometryCollection(v) => v.iter().map(count_coords).sum(),
114 Geometry::Empty(_) => 0,
115 }
116}
117
118fn write_geometry(g: &Geometry, out: &mut Vec<u8>) {
119 match g {
120 Geometry::Point(c) => write_point(c, out),
121 Geometry::LineString(ls) => write_linestring(ls, out),
122 Geometry::Polygon(p) => write_polygon(p, out),
123 Geometry::MultiPoint(v) => write_multipoint(v, out),
124 Geometry::MultiLineString(v) => write_multilinestring(v, out),
125 Geometry::MultiPolygon(v) => write_multipolygon(v, out),
126 Geometry::GeometryCollection(v) => write_collection(v, out),
127 Geometry::Empty(t) => write_empty(*t, out),
128 }
129}
130
131fn write_preamble(out: &mut Vec<u8>, type_code: u32) {
132 out.push(BYTE_ORDER_LE);
133 out.extend_from_slice(&type_code.to_le_bytes());
134}
135
136fn write_xy(c: &Coord, out: &mut Vec<u8>) {
137 out.extend_from_slice(&c.x.to_le_bytes());
138 out.extend_from_slice(&c.y.to_le_bytes());
139}
140
141fn write_point(c: &Coord, out: &mut Vec<u8>) {
142 write_preamble(out, WKB_POINT);
143 write_xy(c, out);
144}
145
146fn write_linestring(ls: &LineString, out: &mut Vec<u8>) {
147 write_preamble(out, WKB_LINESTRING);
148 out.extend_from_slice(&(ls.coords.len() as u32).to_le_bytes());
149 for c in &ls.coords {
150 write_xy(c, out);
151 }
152}
153
154fn write_polygon(p: &Polygon, out: &mut Vec<u8>) {
155 write_preamble(out, WKB_POLYGON);
156 let n_rings = 1 + p.holes.len();
157 out.extend_from_slice(&(n_rings as u32).to_le_bytes());
158 write_ring(&p.exterior, out);
159 for h in &p.holes {
160 write_ring(h, out);
161 }
162}
163
164fn write_ring(ls: &LineString, out: &mut Vec<u8>) {
165 out.extend_from_slice(&(ls.coords.len() as u32).to_le_bytes());
166 for c in &ls.coords {
167 write_xy(c, out);
168 }
169}
170
171fn write_multipoint(pts: &[Coord], out: &mut Vec<u8>) {
172 write_preamble(out, WKB_MULTIPOINT);
173 out.extend_from_slice(&(pts.len() as u32).to_le_bytes());
174 for c in pts {
175 write_point(c, out);
176 }
177}
178
179fn write_multilinestring(parts: &[LineString], out: &mut Vec<u8>) {
180 write_preamble(out, WKB_MULTILINESTRING);
181 out.extend_from_slice(&(parts.len() as u32).to_le_bytes());
182 for ls in parts {
183 write_linestring(ls, out);
184 }
185}
186
187fn write_multipolygon(polys: &[Polygon], out: &mut Vec<u8>) {
188 write_preamble(out, WKB_MULTIPOLYGON);
189 out.extend_from_slice(&(polys.len() as u32).to_le_bytes());
190 for p in polys {
191 write_polygon(p, out);
192 }
193}
194
195fn write_collection(geoms: &[Geometry], out: &mut Vec<u8>) {
196 write_preamble(out, WKB_GEOMETRYCOLLECTION);
197 out.extend_from_slice(&(geoms.len() as u32).to_le_bytes());
198 for g in geoms {
199 write_geometry(g, out);
200 }
201}
202
203fn write_empty(t: GeometryType, out: &mut Vec<u8>) {
204 match t {
205 GeometryType::Point => {
206 write_preamble(out, WKB_POINT);
208 out.extend_from_slice(&f64::NAN.to_le_bytes());
209 out.extend_from_slice(&f64::NAN.to_le_bytes());
210 }
211 GeometryType::LineString => write_preamble_with_zero_count(out, WKB_LINESTRING),
212 GeometryType::Polygon => write_preamble_with_zero_count(out, WKB_POLYGON),
213 GeometryType::MultiPoint => write_preamble_with_zero_count(out, WKB_MULTIPOINT),
214 GeometryType::MultiLineString => write_preamble_with_zero_count(out, WKB_MULTILINESTRING),
215 GeometryType::MultiPolygon => write_preamble_with_zero_count(out, WKB_MULTIPOLYGON),
216 GeometryType::GeometryCollection => {
217 write_preamble_with_zero_count(out, WKB_GEOMETRYCOLLECTION)
218 }
219 }
220}
221
222fn write_preamble_with_zero_count(out: &mut Vec<u8>, type_code: u32) {
223 write_preamble(out, type_code);
224 out.extend_from_slice(&0u32.to_le_bytes());
225}
226
227const TYPE_LOW_MASK: u32 = 0xFF;
234const ISO_Z_FLAG: u32 = 0x8000_0000;
236const ISO_M_FLAG: u32 = 0x4000_0000;
238
239impl Geometry {
240 pub fn from_wkb(bytes: &[u8]) -> Result<Self> {
248 let mut c = WkbCursor::new(bytes);
249 decode_geometry(&mut c)
250 }
251}
252
253pub fn bbox_from_bytes(bytes: &[u8]) -> Option<[f64; 4]> {
260 let mut c = WkbCursor::new(bytes);
261 let mut acc = BboxAcc::default();
262 walk_bbox(&mut c, &mut acc).ok()?;
263 acc.finish()
264}
265
266#[derive(Default)]
267struct BboxAcc {
268 have_any: bool,
269 xmin: f64,
270 ymin: f64,
271 xmax: f64,
272 ymax: f64,
273}
274
275impl BboxAcc {
276 fn add(&mut self, x: f64, y: f64) {
277 if !x.is_finite() || !y.is_finite() {
278 return;
279 }
280 if !self.have_any {
281 self.have_any = true;
282 self.xmin = x;
283 self.ymin = y;
284 self.xmax = x;
285 self.ymax = y;
286 } else {
287 if x < self.xmin {
288 self.xmin = x;
289 }
290 if y < self.ymin {
291 self.ymin = y;
292 }
293 if x > self.xmax {
294 self.xmax = x;
295 }
296 if y > self.ymax {
297 self.ymax = y;
298 }
299 }
300 }
301
302 fn finish(self) -> Option<[f64; 4]> {
303 if self.have_any {
304 Some([self.xmin, self.ymin, self.xmax, self.ymax])
305 } else {
306 None
307 }
308 }
309}
310
311struct WkbCursor<'a> {
315 bytes: &'a [u8],
316 pos: usize,
317}
318
319impl<'a> WkbCursor<'a> {
320 fn new(bytes: &'a [u8]) -> Self {
321 Self { bytes, pos: 0 }
322 }
323
324 fn need(&self, n: usize) -> Result<()> {
325 if self.pos + n > self.bytes.len() {
326 Err(Error::malformed(format!(
327 "WKB truncated at byte {}: need {} more, have {}",
328 self.pos,
329 n,
330 self.bytes.len().saturating_sub(self.pos)
331 )))
332 } else {
333 Ok(())
334 }
335 }
336
337 fn read_u8(&mut self) -> Result<u8> {
338 self.need(1)?;
339 let v = self.bytes[self.pos];
340 self.pos += 1;
341 Ok(v)
342 }
343
344 fn read_u32(&mut self, le: bool) -> Result<u32> {
345 self.need(4)?;
346 let s = &self.bytes[self.pos..self.pos + 4];
347 let arr: [u8; 4] = s.try_into().unwrap();
348 self.pos += 4;
349 Ok(if le {
350 u32::from_le_bytes(arr)
351 } else {
352 u32::from_be_bytes(arr)
353 })
354 }
355
356 fn read_f64(&mut self, le: bool) -> Result<f64> {
357 self.need(8)?;
358 let s = &self.bytes[self.pos..self.pos + 8];
359 let arr: [u8; 8] = s.try_into().unwrap();
360 self.pos += 8;
361 Ok(if le {
362 f64::from_le_bytes(arr)
363 } else {
364 f64::from_be_bytes(arr)
365 })
366 }
367
368 fn read_preamble(&mut self) -> Result<(bool, u32, bool, bool)> {
371 let bo = self.read_u8()?;
372 let le = match bo {
373 0 => false,
374 1 => true,
375 other => {
376 return Err(Error::malformed(format!(
377 "WKB byte-order byte {other}; expected 0 or 1"
378 )))
379 }
380 };
381 let raw_type = self.read_u32(le)?;
382 let iso_has_z = raw_type & ISO_Z_FLAG != 0;
384 let iso_has_m = raw_type & ISO_M_FLAG != 0;
385 let base = raw_type & TYPE_LOW_MASK;
386 let (base, range_has_z, range_has_m) = if (1001..=1007).contains(&raw_type) {
387 (raw_type - 1000, true, false)
388 } else if (2001..=2007).contains(&raw_type) {
389 (raw_type - 2000, false, true)
390 } else if (3001..=3007).contains(&raw_type) {
391 (raw_type - 3000, true, true)
392 } else {
393 (base, false, false)
394 };
395 Ok((le, base, iso_has_z || range_has_z, iso_has_m || range_has_m))
396 }
397}
398
399fn decode_geometry(c: &mut WkbCursor) -> Result<Geometry> {
400 let (le, base, has_z, has_m) = c.read_preamble()?;
401 if has_z || has_m {
402 return Err(Error::unsupported(
403 "WKB Z/M geometries (v0.1 is 2D-only)".to_string(),
404 ));
405 }
406 match base {
407 1 => decode_point(c, le),
408 2 => decode_linestring(c, le),
409 3 => decode_polygon(c, le),
410 4 => decode_multipoint(c, le),
411 5 => decode_multilinestring(c, le),
412 6 => decode_multipolygon(c, le),
413 7 => decode_collection(c, le),
414 other => Err(Error::unsupported(format!(
415 "WKB geometry type code {other}"
416 ))),
417 }
418}
419
420fn decode_point(c: &mut WkbCursor, le: bool) -> Result<Geometry> {
421 let x = c.read_f64(le)?;
422 let y = c.read_f64(le)?;
423 if x.is_nan() && y.is_nan() {
424 return Ok(Geometry::Empty(GeometryType::Point));
425 }
426 Ok(Geometry::Point(Coord::xy(x, y)))
427}
428
429fn decode_linestring_coords(c: &mut WkbCursor, le: bool) -> Result<Vec<Coord>> {
430 let n = c.read_u32(le)? as usize;
431 let mut coords = Vec::with_capacity(n);
432 for _ in 0..n {
433 let x = c.read_f64(le)?;
434 let y = c.read_f64(le)?;
435 coords.push(Coord::xy(x, y));
436 }
437 Ok(coords)
438}
439
440fn decode_linestring(c: &mut WkbCursor, le: bool) -> Result<Geometry> {
441 let coords = decode_linestring_coords(c, le)?;
442 if coords.is_empty() {
443 Ok(Geometry::Empty(GeometryType::LineString))
444 } else {
445 Ok(Geometry::LineString(LineString::new(coords)))
446 }
447}
448
449fn decode_polygon(c: &mut WkbCursor, le: bool) -> Result<Geometry> {
450 let n_rings = c.read_u32(le)? as usize;
451 if n_rings == 0 {
452 return Ok(Geometry::Empty(GeometryType::Polygon));
453 }
454 let mut rings: Vec<LineString> = Vec::with_capacity(n_rings);
455 for _ in 0..n_rings {
456 rings.push(LineString::new(decode_linestring_coords(c, le)?));
457 }
458 let exterior = rings.remove(0);
459 Ok(Geometry::Polygon(Polygon::new(exterior, rings)))
460}
461
462fn decode_multipoint(c: &mut WkbCursor, le: bool) -> Result<Geometry> {
463 let _ = le;
466 let n = c.read_u32_after_check(le)?;
467 if n == 0 {
468 return Ok(Geometry::Empty(GeometryType::MultiPoint));
469 }
470 let mut pts = Vec::with_capacity(n);
471 for _ in 0..n {
472 match decode_geometry(c)? {
473 Geometry::Point(p) => pts.push(p),
474 Geometry::Empty(GeometryType::Point) => pts.push(Coord::xy(f64::NAN, f64::NAN)),
475 other => {
476 return Err(Error::malformed(format!(
477 "MultiPoint child must be Point, got {:?}",
478 other.type_of()
479 )))
480 }
481 }
482 }
483 Ok(Geometry::MultiPoint(pts))
484}
485
486fn decode_multilinestring(c: &mut WkbCursor, le: bool) -> Result<Geometry> {
487 let n = c.read_u32_after_check(le)?;
488 if n == 0 {
489 return Ok(Geometry::Empty(GeometryType::MultiLineString));
490 }
491 let mut parts = Vec::with_capacity(n);
492 for _ in 0..n {
493 match decode_geometry(c)? {
494 Geometry::LineString(ls) => parts.push(ls),
495 Geometry::Empty(GeometryType::LineString) => parts.push(LineString::default()),
496 other => {
497 return Err(Error::malformed(format!(
498 "MultiLineString child must be LineString, got {:?}",
499 other.type_of()
500 )))
501 }
502 }
503 }
504 Ok(Geometry::MultiLineString(parts))
505}
506
507fn decode_multipolygon(c: &mut WkbCursor, le: bool) -> Result<Geometry> {
508 let n = c.read_u32_after_check(le)?;
509 if n == 0 {
510 return Ok(Geometry::Empty(GeometryType::MultiPolygon));
511 }
512 let mut polys = Vec::with_capacity(n);
513 for _ in 0..n {
514 match decode_geometry(c)? {
515 Geometry::Polygon(p) => polys.push(p),
516 Geometry::Empty(GeometryType::Polygon) => polys.push(Polygon::default()),
517 other => {
518 return Err(Error::malformed(format!(
519 "MultiPolygon child must be Polygon, got {:?}",
520 other.type_of()
521 )))
522 }
523 }
524 }
525 Ok(Geometry::MultiPolygon(polys))
526}
527
528fn decode_collection(c: &mut WkbCursor, le: bool) -> Result<Geometry> {
529 let n = c.read_u32_after_check(le)?;
530 if n == 0 {
531 return Ok(Geometry::Empty(GeometryType::GeometryCollection));
532 }
533 let mut v = Vec::with_capacity(n);
534 for _ in 0..n {
535 v.push(decode_geometry(c)?);
536 }
537 Ok(Geometry::GeometryCollection(v))
538}
539
540impl<'a> WkbCursor<'a> {
541 fn read_u32_after_check(&mut self, le: bool) -> Result<usize> {
544 Ok(self.read_u32(le)? as usize)
545 }
546}
547
548fn walk_bbox(c: &mut WkbCursor, acc: &mut BboxAcc) -> Result<()> {
553 let (le, base, has_z, has_m) = c.read_preamble()?;
554 if has_z || has_m {
557 return Err(Error::unsupported("WKB Z/M (v0.1 is 2D-only)".to_string()));
558 }
559 match base {
560 1 => {
561 let x = c.read_f64(le)?;
562 let y = c.read_f64(le)?;
563 acc.add(x, y);
565 }
566 2 => walk_xy_array(c, le, acc)?,
567 3 => {
568 let n_rings = c.read_u32(le)? as usize;
569 for _ in 0..n_rings {
570 walk_xy_array(c, le, acc)?;
571 }
572 }
573 4..=7 => {
574 let n = c.read_u32(le)? as usize;
575 for _ in 0..n {
576 walk_bbox(c, acc)?;
577 }
578 }
579 other => {
580 return Err(Error::unsupported(format!(
581 "WKB geometry type code {other}"
582 )))
583 }
584 }
585 Ok(())
586}
587
588fn walk_xy_array(c: &mut WkbCursor, le: bool, acc: &mut BboxAcc) -> Result<()> {
589 let n = c.read_u32(le)? as usize;
590 for _ in 0..n {
591 let x = c.read_f64(le)?;
592 let y = c.read_f64(le)?;
593 acc.add(x, y);
594 }
595 Ok(())
596}
597
598#[cfg(test)]
599mod decoder_tests {
600 use super::*;
601
602 fn round_trip(g: &Geometry) {
603 let bytes = g.to_wkb();
604 let back = Geometry::from_wkb(&bytes).expect("decode");
605 assert_eq!(&back, g, "round-trip mismatch for {g:?}");
606 }
607
608 #[test]
609 fn round_trip_point() {
610 round_trip(&Geometry::Point(Coord::xy(1.0, 2.0)));
611 round_trip(&Geometry::Point(Coord::xy(-180.0, 90.0)));
612 }
613
614 #[test]
615 fn round_trip_point_empty() {
616 let g = Geometry::Empty(GeometryType::Point);
617 let bytes = g.to_wkb();
618 let back = Geometry::from_wkb(&bytes).unwrap();
619 assert_eq!(back, g);
621 }
622
623 #[test]
624 fn round_trip_linestring() {
625 let ls = LineString::new(vec![
626 Coord::xy(0.0, 0.0),
627 Coord::xy(1.0, 1.0),
628 Coord::xy(2.0, 0.0),
629 ]);
630 round_trip(&Geometry::LineString(ls));
631 }
632
633 #[test]
634 fn round_trip_polygon_with_hole() {
635 let p = Polygon::new(
636 LineString::new(vec![
637 Coord::xy(0.0, 0.0),
638 Coord::xy(10.0, 0.0),
639 Coord::xy(10.0, 10.0),
640 Coord::xy(0.0, 10.0),
641 Coord::xy(0.0, 0.0),
642 ]),
643 vec![LineString::new(vec![
644 Coord::xy(2.0, 2.0),
645 Coord::xy(4.0, 2.0),
646 Coord::xy(4.0, 4.0),
647 Coord::xy(2.0, 4.0),
648 Coord::xy(2.0, 2.0),
649 ])],
650 );
651 round_trip(&Geometry::Polygon(p));
652 }
653
654 #[test]
655 fn round_trip_multipoint() {
656 round_trip(&Geometry::MultiPoint(vec![
657 Coord::xy(0.0, 0.0),
658 Coord::xy(1.0, 2.0),
659 Coord::xy(-3.0, 4.5),
660 ]));
661 }
662
663 #[test]
664 fn round_trip_multilinestring() {
665 round_trip(&Geometry::MultiLineString(vec![
666 LineString::new(vec![Coord::xy(0.0, 0.0), Coord::xy(1.0, 1.0)]),
667 LineString::new(vec![
668 Coord::xy(2.0, 2.0),
669 Coord::xy(3.0, 3.0),
670 Coord::xy(4.0, 4.0),
671 ]),
672 ]));
673 }
674
675 #[test]
676 fn round_trip_multipolygon() {
677 let p1 = Polygon::new(
678 LineString::new(vec![
679 Coord::xy(0.0, 0.0),
680 Coord::xy(1.0, 0.0),
681 Coord::xy(1.0, 1.0),
682 Coord::xy(0.0, 0.0),
683 ]),
684 vec![],
685 );
686 let p2 = Polygon::new(
687 LineString::new(vec![
688 Coord::xy(10.0, 10.0),
689 Coord::xy(11.0, 10.0),
690 Coord::xy(11.0, 11.0),
691 Coord::xy(10.0, 10.0),
692 ]),
693 vec![],
694 );
695 round_trip(&Geometry::MultiPolygon(vec![p1, p2]));
696 }
697
698 #[test]
699 fn round_trip_geometry_collection() {
700 round_trip(&Geometry::GeometryCollection(vec![
701 Geometry::Point(Coord::xy(1.0, 2.0)),
702 Geometry::LineString(LineString::new(vec![
703 Coord::xy(0.0, 0.0),
704 Coord::xy(1.0, 1.0),
705 ])),
706 ]));
707 }
708
709 #[test]
710 fn round_trip_empty_collection_shapes() {
711 for t in [
712 GeometryType::LineString,
713 GeometryType::Polygon,
714 GeometryType::MultiPoint,
715 GeometryType::MultiLineString,
716 GeometryType::MultiPolygon,
717 GeometryType::GeometryCollection,
718 ] {
719 round_trip(&Geometry::Empty(t));
720 }
721 }
722
723 #[test]
724 fn decode_be_encoded_point() {
725 let mut buf = vec![0u8]; buf.extend_from_slice(&1u32.to_be_bytes());
728 buf.extend_from_slice(&3.0f64.to_be_bytes());
729 buf.extend_from_slice(&4.0f64.to_be_bytes());
730 let g = Geometry::from_wkb(&buf).unwrap();
731 assert_eq!(g, Geometry::Point(Coord::xy(3.0, 4.0)));
732 }
733
734 #[test]
735 fn decode_mixed_endian_collection() {
736 let mut buf = Vec::new();
738 buf.push(1u8);
739 buf.extend_from_slice(&7u32.to_le_bytes()); buf.extend_from_slice(&2u32.to_le_bytes()); buf.push(0u8);
743 buf.extend_from_slice(&1u32.to_be_bytes());
744 buf.extend_from_slice(&1.0f64.to_be_bytes());
745 buf.extend_from_slice(&2.0f64.to_be_bytes());
746 buf.push(1u8);
748 buf.extend_from_slice(&1u32.to_le_bytes());
749 buf.extend_from_slice(&3.0f64.to_le_bytes());
750 buf.extend_from_slice(&4.0f64.to_le_bytes());
751
752 let g = Geometry::from_wkb(&buf).unwrap();
753 match g {
754 Geometry::GeometryCollection(v) => {
755 assert_eq!(v.len(), 2);
756 assert_eq!(v[0], Geometry::Point(Coord::xy(1.0, 2.0)));
757 assert_eq!(v[1], Geometry::Point(Coord::xy(3.0, 4.0)));
758 }
759 _ => panic!("expected GeometryCollection"),
760 }
761 }
762
763 #[test]
764 fn truncated_input_errors_cleanly() {
765 let truncated = &[1u8, 1, 0, 0, 0]; assert!(Geometry::from_wkb(truncated).is_err());
767 }
768
769 #[test]
770 fn z_variant_errors_as_unsupported() {
771 let mut buf = vec![1u8];
773 buf.extend_from_slice(&(1u32 | ISO_Z_FLAG).to_le_bytes());
774 buf.extend_from_slice(&1.0f64.to_le_bytes());
775 buf.extend_from_slice(&2.0f64.to_le_bytes());
776 buf.extend_from_slice(&3.0f64.to_le_bytes()); let err = Geometry::from_wkb(&buf).unwrap_err();
778 assert!(matches!(err, Error::Unsupported(_)));
779
780 let mut buf = vec![1u8];
782 buf.extend_from_slice(&1001u32.to_le_bytes());
783 let err = Geometry::from_wkb(&buf).unwrap_err();
784 assert!(matches!(err, Error::Unsupported(_)));
785 }
786
787 #[test]
788 fn bad_byte_order_byte_errors() {
789 let buf = [9u8, 1, 0, 0, 0]; assert!(Geometry::from_wkb(&buf).is_err());
791 }
792}
793
794#[cfg(test)]
795mod bbox_walker_tests {
796 use super::*;
797
798 fn bbox_via_walker(g: &Geometry) -> Option<[f64; 4]> {
799 bbox_from_bytes(&g.to_wkb())
800 }
801
802 #[test]
803 fn bbox_walker_matches_geometry_bbox_for_all_variants() {
804 let cases: Vec<Geometry> = vec![
805 Geometry::Point(Coord::xy(5.0, 7.0)),
806 Geometry::LineString(LineString::new(vec![
807 Coord::xy(0.0, 0.0),
808 Coord::xy(10.0, -5.0),
809 Coord::xy(2.0, 8.0),
810 ])),
811 Geometry::Polygon(Polygon::new(
812 LineString::new(vec![
813 Coord::xy(0.0, 0.0),
814 Coord::xy(10.0, 0.0),
815 Coord::xy(10.0, 10.0),
816 Coord::xy(0.0, 10.0),
817 Coord::xy(0.0, 0.0),
818 ]),
819 vec![],
820 )),
821 Geometry::MultiPoint(vec![Coord::xy(-1.0, -1.0), Coord::xy(1.0, 1.0)]),
822 Geometry::MultiLineString(vec![
823 LineString::new(vec![Coord::xy(0.0, 0.0), Coord::xy(5.0, 5.0)]),
824 LineString::new(vec![Coord::xy(100.0, -100.0), Coord::xy(101.0, -99.0)]),
825 ]),
826 Geometry::GeometryCollection(vec![
827 Geometry::Point(Coord::xy(-50.0, -50.0)),
828 Geometry::Point(Coord::xy(50.0, 50.0)),
829 ]),
830 ];
831 for g in cases {
832 assert_eq!(
833 bbox_via_walker(&g),
834 g.bbox(),
835 "walker disagrees with Geometry::bbox for {g:?}"
836 );
837 }
838 }
839
840 #[test]
841 fn bbox_walker_returns_none_for_empty_geoms() {
842 for t in [
843 GeometryType::Point,
844 GeometryType::LineString,
845 GeometryType::Polygon,
846 GeometryType::MultiPolygon,
847 GeometryType::GeometryCollection,
848 ] {
849 assert!(bbox_via_walker(&Geometry::Empty(t)).is_none(), "{t:?}");
850 }
851 }
852
853 #[test]
854 fn bbox_walker_returns_none_for_malformed_input() {
855 assert!(bbox_from_bytes(&[1u8, 1, 0, 0, 0]).is_none()); assert!(bbox_from_bytes(&[]).is_none()); assert!(bbox_from_bytes(&[1, 99, 99, 99, 99]).is_none()); }
859}
860
861#[cfg(test)]
862mod tests {
863 use super::*;
864
865 #[test]
866 fn point_wkb_byte_exact() {
867 let g = Geometry::Point(Coord::xy(1.0, 2.0));
868 let wkb = g.to_wkb();
869 assert_eq!(wkb.len(), 21);
871 assert_eq!(wkb[0], 1); assert_eq!(u32::from_le_bytes(wkb[1..5].try_into().unwrap()), 1); assert_eq!(f64::from_le_bytes(wkb[5..13].try_into().unwrap()), 1.0);
874 assert_eq!(f64::from_le_bytes(wkb[13..21].try_into().unwrap()), 2.0);
875 }
876
877 #[test]
878 fn point_empty_uses_nan() {
879 let wkb = Geometry::Empty(GeometryType::Point).to_wkb();
880 assert_eq!(wkb.len(), 21);
881 assert!(f64::from_le_bytes(wkb[5..13].try_into().unwrap()).is_nan());
882 assert!(f64::from_le_bytes(wkb[13..21].try_into().unwrap()).is_nan());
883 }
884
885 #[test]
886 fn linestring_wkb_layout() {
887 let ls = LineString::new(vec![
888 Coord::xy(0.0, 0.0),
889 Coord::xy(1.0, 1.0),
890 Coord::xy(2.0, 0.0),
891 ]);
892 let wkb = Geometry::LineString(ls).to_wkb();
893 assert_eq!(wkb.len(), 57);
895 assert_eq!(wkb[0], 1);
896 assert_eq!(u32::from_le_bytes(wkb[1..5].try_into().unwrap()), 2);
897 assert_eq!(u32::from_le_bytes(wkb[5..9].try_into().unwrap()), 3);
898 assert_eq!(f64::from_le_bytes(wkb[9..17].try_into().unwrap()), 0.0);
900 assert_eq!(f64::from_le_bytes(wkb[17..25].try_into().unwrap()), 0.0);
901 assert_eq!(f64::from_le_bytes(wkb[41..49].try_into().unwrap()), 2.0);
903 assert_eq!(f64::from_le_bytes(wkb[49..57].try_into().unwrap()), 0.0);
904 }
905
906 #[test]
907 fn polygon_with_hole() {
908 let exterior = LineString::new(vec![
909 Coord::xy(0.0, 0.0),
910 Coord::xy(10.0, 0.0),
911 Coord::xy(10.0, 10.0),
912 Coord::xy(0.0, 10.0),
913 Coord::xy(0.0, 0.0),
914 ]);
915 let hole = LineString::new(vec![
916 Coord::xy(2.0, 2.0),
917 Coord::xy(4.0, 2.0),
918 Coord::xy(4.0, 4.0),
919 Coord::xy(2.0, 4.0),
920 Coord::xy(2.0, 2.0),
921 ]);
922 let p = Polygon::new(exterior, vec![hole]);
923 let wkb = Geometry::Polygon(p).to_wkb();
924 assert_eq!(wkb.len(), 5 + 4 + 4 + 80 + 4 + 80);
926 assert_eq!(u32::from_le_bytes(wkb[1..5].try_into().unwrap()), 3);
927 assert_eq!(u32::from_le_bytes(wkb[5..9].try_into().unwrap()), 2); assert_eq!(u32::from_le_bytes(wkb[9..13].try_into().unwrap()), 5); assert_eq!(u32::from_le_bytes(wkb[93..97].try_into().unwrap()), 5);
931 }
932
933 #[test]
934 fn multipoint_each_point_carries_own_header() {
935 let g = Geometry::MultiPoint(vec![Coord::xy(0.0, 0.0), Coord::xy(1.0, 1.0)]);
936 let wkb = g.to_wkb();
937 assert_eq!(wkb.len(), 5 + 4 + 42);
939 assert_eq!(u32::from_le_bytes(wkb[1..5].try_into().unwrap()), 4);
940 assert_eq!(u32::from_le_bytes(wkb[5..9].try_into().unwrap()), 2);
941 assert_eq!(wkb[9], 1);
943 assert_eq!(u32::from_le_bytes(wkb[10..14].try_into().unwrap()), 1);
944 }
945
946 #[test]
947 fn multilinestring_wkb_layout() {
948 let g = Geometry::MultiLineString(vec![
949 LineString::new(vec![Coord::xy(0.0, 0.0), Coord::xy(1.0, 1.0)]),
950 LineString::new(vec![
951 Coord::xy(2.0, 2.0),
952 Coord::xy(3.0, 3.0),
953 Coord::xy(4.0, 4.0),
954 ]),
955 ]);
956 let wkb = g.to_wkb();
957 assert_eq!(wkb[0], 1);
958 assert_eq!(u32::from_le_bytes(wkb[1..5].try_into().unwrap()), 5);
959 assert_eq!(u32::from_le_bytes(wkb[5..9].try_into().unwrap()), 2);
960 assert_eq!(wkb[9], 1);
962 assert_eq!(u32::from_le_bytes(wkb[10..14].try_into().unwrap()), 2);
963 assert_eq!(u32::from_le_bytes(wkb[14..18].try_into().unwrap()), 2); }
965
966 #[test]
967 fn empty_linestring_wkb() {
968 let wkb = Geometry::Empty(GeometryType::LineString).to_wkb();
969 assert_eq!(wkb.len(), 5 + 4);
970 assert_eq!(u32::from_le_bytes(wkb[1..5].try_into().unwrap()), 2);
971 assert_eq!(u32::from_le_bytes(wkb[5..9].try_into().unwrap()), 0);
972 }
973
974 #[test]
975 fn empty_polygon_and_multipolygon() {
976 for (t, code) in [
977 (GeometryType::Polygon, 3),
978 (GeometryType::MultiPolygon, 6),
979 (GeometryType::MultiLineString, 5),
980 (GeometryType::MultiPoint, 4),
981 (GeometryType::GeometryCollection, 7),
982 ] {
983 let wkb = Geometry::Empty(t).to_wkb();
984 assert_eq!(wkb.len(), 5 + 4, "empty {t:?}");
985 assert_eq!(u32::from_le_bytes(wkb[1..5].try_into().unwrap()), code);
986 assert_eq!(u32::from_le_bytes(wkb[5..9].try_into().unwrap()), 0);
987 }
988 }
989
990 #[test]
991 fn geometry_collection_nests_correctly() {
992 let g = Geometry::GeometryCollection(vec![
993 Geometry::Point(Coord::xy(1.0, 2.0)),
994 Geometry::LineString(LineString::new(vec![
995 Coord::xy(0.0, 0.0),
996 Coord::xy(1.0, 1.0),
997 ])),
998 ]);
999 let wkb = g.to_wkb();
1000 assert_eq!(u32::from_le_bytes(wkb[1..5].try_into().unwrap()), 7);
1001 assert_eq!(u32::from_le_bytes(wkb[5..9].try_into().unwrap()), 2);
1002 assert_eq!(wkb[9], 1);
1004 assert_eq!(u32::from_le_bytes(wkb[10..14].try_into().unwrap()), 1); assert_eq!(wkb[30], 1);
1007 assert_eq!(u32::from_le_bytes(wkb[31..35].try_into().unwrap()), 2); }
1009
1010 #[test]
1011 fn write_wkb_into_clears_buffer() {
1012 let mut scratch = vec![0xFFu8; 100];
1013 Geometry::Point(Coord::xy(5.0, 6.0)).write_wkb_into(&mut scratch);
1014 assert_eq!(scratch.len(), 21);
1015 assert_eq!(scratch[0], 1);
1016 }
1017}