1use crate::geometry::{
53 Feature, FeatureCollection, Geometry, LineString, MultiLineString, MultiPoint, MultiPolygon,
54 Point, Polygon, PropertyValue,
55};
56use rustial_math::TileId;
57use std::collections::HashMap;
58use std::fmt;
59
60#[derive(Debug, Clone)]
66pub struct MvtDecodeOptions {
67 pub layer_filter: Vec<String>,
69}
70
71impl Default for MvtDecodeOptions {
72 fn default() -> Self {
73 Self {
74 layer_filter: Vec::new(),
75 }
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq)]
81pub enum MvtError {
82 TruncatedPayload,
84 UnsupportedWireType(u8),
86 InvalidGeometryCommand(u32),
88 DecodeError(String),
90}
91
92impl fmt::Display for MvtError {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 match self {
95 MvtError::TruncatedPayload => write!(f, "truncated MVT payload"),
96 MvtError::UnsupportedWireType(wt) => {
97 write!(f, "unsupported protobuf wire type: {wt}")
98 }
99 MvtError::InvalidGeometryCommand(cmd) => {
100 write!(f, "invalid MVT geometry command: {cmd}")
101 }
102 MvtError::DecodeError(msg) => write!(f, "MVT decode error: {msg}"),
103 }
104 }
105}
106
107impl std::error::Error for MvtError {}
108
109pub type DecodedVectorTile = HashMap<String, FeatureCollection>;
111
112pub fn decode_mvt(
118 bytes: &[u8],
119 tile_id: &TileId,
120 options: &MvtDecodeOptions,
121) -> Result<DecodedVectorTile, MvtError> {
122 let tile_bounds = tile_geo_bounds(tile_id);
123 let mut result = DecodedVectorTile::new();
124 let mut reader = PbReader::new(bytes);
125
126 while reader.remaining() > 0 {
127 let (field_number, wire_type) = reader.read_tag()?;
128 match (field_number, wire_type) {
129 (3, WIRE_LEN) => {
131 let layer_bytes = reader.read_bytes()?;
132 match decode_layer(layer_bytes, &tile_bounds, options) {
133 Ok(Some((name, features))) => {
134 result
135 .entry(name)
136 .or_insert_with(|| FeatureCollection { features: Vec::new() })
137 .features
138 .extend(features.features);
139 }
140 Ok(None) => {} Err(e) => {
142 log::warn!("skipping malformed MVT layer: {e}");
143 }
144 }
145 }
146 _ => {
147 reader.skip_field(wire_type)?;
148 }
149 }
150 }
151
152 Ok(result)
153}
154
155struct TileGeoBounds {
161 west: f64,
162 south: f64,
163 east: f64,
164 north: f64,
165}
166
167fn tile_geo_bounds(tile: &TileId) -> TileGeoBounds {
168 let n = (1u64 << tile.zoom) as f64;
169 let west = tile.x as f64 / n * 360.0 - 180.0;
170 let east = (tile.x as f64 + 1.0) / n * 360.0 - 180.0;
171
172 let north_rad = std::f64::consts::PI * (1.0 - 2.0 * tile.y as f64 / n);
173 let south_rad = std::f64::consts::PI * (1.0 - 2.0 * (tile.y as f64 + 1.0) / n);
174
175 let north = north_rad.sinh().atan().to_degrees();
176 let south = south_rad.sinh().atan().to_degrees();
177
178 TileGeoBounds {
179 west,
180 south,
181 east,
182 north,
183 }
184}
185
186#[inline]
188fn tile_coord_to_geo(
189 x: i32,
190 y: i32,
191 extent: u32,
192 bounds: &TileGeoBounds,
193) -> rustial_math::GeoCoord {
194 let extent_f = extent as f64;
195 let lon = bounds.west + (x as f64 / extent_f) * (bounds.east - bounds.west);
196 let lat = bounds.north + (y as f64 / extent_f) * (bounds.south - bounds.north);
197 rustial_math::GeoCoord::from_lat_lon(lat, lon)
198}
199
200fn decode_layer(
205 bytes: &[u8],
206 bounds: &TileGeoBounds,
207 options: &MvtDecodeOptions,
208) -> Result<Option<(String, FeatureCollection)>, MvtError> {
209 let mut reader = PbReader::new(bytes);
210 let mut name = String::new();
211 let mut keys: Vec<String> = Vec::new();
212 let mut values: Vec<PropertyValue> = Vec::new();
213 let mut features_bytes: Vec<&[u8]> = Vec::new();
214 let mut extent: u32 = 4096;
215
216 while reader.remaining() > 0 {
217 let (field_number, wire_type) = reader.read_tag()?;
218 match (field_number, wire_type) {
219 (1, WIRE_LEN) => {
221 name = reader.read_string()?;
222 }
223 (2, WIRE_LEN) => {
225 features_bytes.push(reader.read_bytes()?);
226 }
227 (3, WIRE_LEN) => {
229 keys.push(reader.read_string()?);
230 }
231 (4, WIRE_LEN) => {
233 let val_bytes = reader.read_bytes()?;
234 values.push(decode_value(val_bytes)?);
235 }
236 (5, WIRE_VARINT) => {
238 extent = reader.read_varint()? as u32;
239 if extent == 0 {
240 extent = 4096;
241 }
242 }
243 (15, WIRE_VARINT) => {
245 let _ = reader.read_varint()?;
246 }
247 _ => {
248 reader.skip_field(wire_type)?;
249 }
250 }
251 }
252
253 if !options.layer_filter.is_empty() && !options.layer_filter.iter().any(|f| f == &name) {
255 return Ok(None);
256 }
257
258 let mut features = Vec::with_capacity(features_bytes.len());
259 for feat_bytes in features_bytes {
260 match decode_feature(feat_bytes, &keys, &values, extent, bounds) {
261 Ok(feature) => features.push(feature),
262 Err(e) => {
263 log::debug!("skipping malformed MVT feature in layer '{name}': {e}");
264 }
265 }
266 }
267
268 Ok(Some((name, FeatureCollection { features })))
269}
270
271fn decode_value(bytes: &[u8]) -> Result<PropertyValue, MvtError> {
276 let mut reader = PbReader::new(bytes);
277 let mut result = PropertyValue::Null;
278
279 while reader.remaining() > 0 {
280 let (field_number, wire_type) = reader.read_tag()?;
281 match (field_number, wire_type) {
282 (1, WIRE_LEN) => {
284 result = PropertyValue::String(reader.read_string()?);
285 }
286 (2, WIRE_32) => {
288 result = PropertyValue::Number(reader.read_fixed32_f32()? as f64);
289 }
290 (3, WIRE_64) => {
292 result = PropertyValue::Number(reader.read_fixed64_f64()?);
293 }
294 (4, WIRE_VARINT) => {
296 result = PropertyValue::Number(reader.read_varint()? as f64);
297 }
298 (5, WIRE_VARINT) => {
300 result = PropertyValue::Number(reader.read_varint()? as f64);
301 }
302 (6, WIRE_VARINT) => {
304 let raw = reader.read_varint()?;
305 let decoded = zigzag_decode(raw);
306 result = PropertyValue::Number(decoded as f64);
307 }
308 (7, WIRE_VARINT) => {
310 result = PropertyValue::Bool(reader.read_varint()? != 0);
311 }
312 _ => {
313 reader.skip_field(wire_type)?;
314 }
315 }
316 }
317
318 Ok(result)
319}
320
321const GEOM_UNKNOWN: u32 = 0;
327const GEOM_POINT: u32 = 1;
328const GEOM_LINESTRING: u32 = 2;
329const GEOM_POLYGON: u32 = 3;
330
331fn decode_feature(
332 bytes: &[u8],
333 keys: &[String],
334 values: &[PropertyValue],
335 extent: u32,
336 bounds: &TileGeoBounds,
337) -> Result<Feature, MvtError> {
338 let mut reader = PbReader::new(bytes);
339 let mut geom_type: u32 = GEOM_UNKNOWN;
340 let mut geometry_bytes: &[u8] = &[];
341 let mut tags_bytes: &[u8] = &[];
342 let mut feature_id: Option<u64> = None;
343
344 while reader.remaining() > 0 {
345 let (field_number, wire_type) = reader.read_tag()?;
346 match (field_number, wire_type) {
347 (1, WIRE_VARINT) => {
349 feature_id = Some(reader.read_varint()?);
350 }
351 (2, WIRE_LEN) => {
353 tags_bytes = reader.read_bytes()?;
354 }
355 (3, WIRE_VARINT) => {
357 geom_type = reader.read_varint()? as u32;
358 }
359 (4, WIRE_LEN) => {
361 geometry_bytes = reader.read_bytes()?;
362 }
363 _ => {
364 reader.skip_field(wire_type)?;
365 }
366 }
367 }
368
369 let geometry = decode_geometry(geom_type, geometry_bytes, extent, bounds)?;
370 let properties = decode_tags(tags_bytes, keys, values)?;
371
372 let mut props = properties;
373 if let Some(id) = feature_id {
374 props.insert(
375 "$id".to_owned(),
376 PropertyValue::Number(id as f64),
377 );
378 }
379
380 Ok(Feature {
381 geometry,
382 properties: props,
383 })
384}
385
386fn decode_tags(
387 bytes: &[u8],
388 keys: &[String],
389 values: &[PropertyValue],
390) -> Result<HashMap<String, PropertyValue>, MvtError> {
391 let mut props = HashMap::new();
392 if bytes.is_empty() {
393 return Ok(props);
394 }
395
396 let mut reader = PbReader::new(bytes);
397 while reader.remaining() > 0 {
398 let key_idx = reader.read_varint()? as usize;
399 if reader.remaining() == 0 {
400 break;
401 }
402 let val_idx = reader.read_varint()? as usize;
403
404 if let (Some(key), Some(val)) = (keys.get(key_idx), values.get(val_idx)) {
405 props.insert(key.clone(), val.clone());
406 }
407 }
408
409 Ok(props)
410}
411
412const CMD_MOVE_TO: u32 = 1;
418const CMD_LINE_TO: u32 = 2;
419const CMD_CLOSE_PATH: u32 = 7;
420
421fn decode_geometry(
422 geom_type: u32,
423 bytes: &[u8],
424 extent: u32,
425 bounds: &TileGeoBounds,
426) -> Result<Geometry, MvtError> {
427 let commands = decode_commands(bytes)?;
428 let rings = commands_to_rings(&commands, extent, bounds);
429
430 match geom_type {
431 GEOM_POINT => geometry_from_points(rings),
432 GEOM_LINESTRING => geometry_from_linestrings(rings),
433 GEOM_POLYGON => geometry_from_polygons(rings),
434 _ => Err(MvtError::DecodeError(format!(
435 "unknown geometry type: {geom_type}"
436 ))),
437 }
438}
439
440struct GeomCommand {
441 id: u32,
442 params: Vec<(i32, i32)>,
443}
444
445fn decode_commands(bytes: &[u8]) -> Result<Vec<GeomCommand>, MvtError> {
446 let mut reader = PbReader::new(bytes);
447 let mut commands = Vec::new();
448 let mut cursor_x: i32 = 0;
449 let mut cursor_y: i32 = 0;
450
451 while reader.remaining() > 0 {
452 let cmd_int = reader.read_varint()? as u32;
453 let cmd_id = cmd_int & 0x7;
454 let cmd_count = cmd_int >> 3;
455
456 if cmd_id == CMD_CLOSE_PATH {
457 commands.push(GeomCommand {
458 id: CMD_CLOSE_PATH,
459 params: Vec::new(),
460 });
461 continue;
462 }
463
464 if cmd_id != CMD_MOVE_TO && cmd_id != CMD_LINE_TO {
465 return Err(MvtError::InvalidGeometryCommand(cmd_int));
466 }
467
468 let mut params = Vec::with_capacity(cmd_count as usize);
469 for _ in 0..cmd_count {
470 if reader.remaining() < 2 {
471 break;
472 }
473 let dx = zigzag_decode(reader.read_varint()?) as i32;
474 let dy = zigzag_decode(reader.read_varint()?) as i32;
475 cursor_x += dx;
476 cursor_y += dy;
477 params.push((cursor_x, cursor_y));
478 }
479
480 commands.push(GeomCommand {
481 id: cmd_id,
482 params,
483 });
484 }
485
486 Ok(commands)
487}
488
489fn commands_to_rings(
490 commands: &[GeomCommand],
491 extent: u32,
492 bounds: &TileGeoBounds,
493) -> Vec<Vec<rustial_math::GeoCoord>> {
494 let mut rings: Vec<Vec<rustial_math::GeoCoord>> = Vec::new();
495 let mut current_ring: Vec<rustial_math::GeoCoord> = Vec::new();
496
497 for cmd in commands {
498 match cmd.id {
499 CMD_MOVE_TO => {
500 if !current_ring.is_empty() {
501 rings.push(std::mem::take(&mut current_ring));
502 }
503 for &(x, y) in &cmd.params {
504 current_ring.push(tile_coord_to_geo(x, y, extent, bounds));
505 }
506 }
507 CMD_LINE_TO => {
508 for &(x, y) in &cmd.params {
509 current_ring.push(tile_coord_to_geo(x, y, extent, bounds));
510 }
511 }
512 CMD_CLOSE_PATH => {
513 if let Some(&first) = current_ring.first() {
514 current_ring.push(first);
515 }
516 rings.push(std::mem::take(&mut current_ring));
517 }
518 _ => {}
519 }
520 }
521
522 if !current_ring.is_empty() {
523 rings.push(current_ring);
524 }
525
526 rings
527}
528
529fn geometry_from_points(
530 rings: Vec<Vec<rustial_math::GeoCoord>>,
531) -> Result<Geometry, MvtError> {
532 let points: Vec<Point> = rings
533 .into_iter()
534 .flat_map(|ring| ring.into_iter().map(|coord| Point { coord }))
535 .collect();
536
537 match points.len() {
538 0 => Err(MvtError::DecodeError("empty point geometry".into())),
539 1 => Ok(Geometry::Point(points.into_iter().next().expect("len==1"))),
540 _ => Ok(Geometry::MultiPoint(MultiPoint { points })),
541 }
542}
543
544fn geometry_from_linestrings(
545 rings: Vec<Vec<rustial_math::GeoCoord>>,
546) -> Result<Geometry, MvtError> {
547 let lines: Vec<LineString> = rings
548 .into_iter()
549 .filter(|r| r.len() >= 2)
550 .map(|coords| LineString { coords })
551 .collect();
552
553 match lines.len() {
554 0 => Err(MvtError::DecodeError("empty linestring geometry".into())),
555 1 => Ok(Geometry::LineString(
556 lines.into_iter().next().expect("len==1"),
557 )),
558 _ => Ok(Geometry::MultiLineString(MultiLineString { lines })),
559 }
560}
561
562fn geometry_from_polygons(
563 rings: Vec<Vec<rustial_math::GeoCoord>>,
564) -> Result<Geometry, MvtError> {
565 if rings.is_empty() {
566 return Err(MvtError::DecodeError("empty polygon geometry".into()));
567 }
568
569 let mut polygons: Vec<Polygon> = Vec::new();
570
571 for ring in rings {
572 if ring.len() < 4 {
573 continue;
574 }
575
576 let area = signed_ring_area(&ring);
577
578 if area > 0.0 {
579 polygons.push(Polygon {
583 exterior: ring,
584 interiors: Vec::new(),
585 });
586 } else if area < 0.0 {
587 if let Some(last) = polygons.last_mut() {
589 last.interiors.push(ring);
590 } else {
591 let mut reversed = ring;
593 reversed.reverse();
594 polygons.push(Polygon {
595 exterior: reversed,
596 interiors: Vec::new(),
597 });
598 }
599 }
600 }
602
603 match polygons.len() {
604 0 => Err(MvtError::DecodeError(
605 "no valid polygon rings found".into(),
606 )),
607 1 => Ok(Geometry::Polygon(
608 polygons.into_iter().next().expect("len==1"),
609 )),
610 _ => Ok(Geometry::MultiPolygon(MultiPolygon { polygons })),
611 }
612}
613
614fn signed_ring_area(ring: &[rustial_math::GeoCoord]) -> f64 {
618 let mut area = 0.0f64;
619 let n = ring.len();
620 if n < 3 {
621 return 0.0;
622 }
623 for i in 0..n {
624 let j = (i + 1) % n;
625 area += ring[i].lon * ring[j].lat;
627 area -= ring[j].lon * ring[i].lat;
628 }
629 area / 2.0
630}
631
632const WIRE_VARINT: u8 = 0;
638const WIRE_64: u8 = 1;
639const WIRE_LEN: u8 = 2;
640const WIRE_32: u8 = 5;
641
642struct PbReader<'a> {
644 data: &'a [u8],
645 pos: usize,
646}
647
648impl<'a> PbReader<'a> {
649 fn new(data: &'a [u8]) -> Self {
650 Self { data, pos: 0 }
651 }
652
653 #[inline]
654 fn remaining(&self) -> usize {
655 self.data.len().saturating_sub(self.pos)
656 }
657
658 fn read_byte(&mut self) -> Result<u8, MvtError> {
659 if self.pos >= self.data.len() {
660 return Err(MvtError::TruncatedPayload);
661 }
662 let b = self.data[self.pos];
663 self.pos += 1;
664 Ok(b)
665 }
666
667 fn read_varint(&mut self) -> Result<u64, MvtError> {
668 let mut result: u64 = 0;
669 let mut shift: u32 = 0;
670 loop {
671 let b = self.read_byte()?;
672 result |= ((b & 0x7F) as u64) << shift;
673 if b & 0x80 == 0 {
674 return Ok(result);
675 }
676 shift += 7;
677 if shift >= 64 {
678 return Err(MvtError::DecodeError("varint too long".into()));
679 }
680 }
681 }
682
683 fn read_tag(&mut self) -> Result<(u32, u8), MvtError> {
684 let varint = self.read_varint()? as u32;
685 let field_number = varint >> 3;
686 let wire_type = (varint & 0x7) as u8;
687 Ok((field_number, wire_type))
688 }
689
690 fn read_bytes(&mut self) -> Result<&'a [u8], MvtError> {
691 let len = self.read_varint()? as usize;
692 if self.pos + len > self.data.len() {
693 return Err(MvtError::TruncatedPayload);
694 }
695 let slice = &self.data[self.pos..self.pos + len];
696 self.pos += len;
697 Ok(slice)
698 }
699
700 fn read_string(&mut self) -> Result<String, MvtError> {
701 let bytes = self.read_bytes()?;
702 String::from_utf8(bytes.to_vec())
703 .map_err(|e| MvtError::DecodeError(format!("invalid UTF-8: {e}")))
704 }
705
706 fn read_fixed32_f32(&mut self) -> Result<f32, MvtError> {
707 if self.remaining() < 4 {
708 return Err(MvtError::TruncatedPayload);
709 }
710 let bytes = [
711 self.data[self.pos],
712 self.data[self.pos + 1],
713 self.data[self.pos + 2],
714 self.data[self.pos + 3],
715 ];
716 self.pos += 4;
717 Ok(f32::from_le_bytes(bytes))
718 }
719
720 fn read_fixed64_f64(&mut self) -> Result<f64, MvtError> {
721 if self.remaining() < 8 {
722 return Err(MvtError::TruncatedPayload);
723 }
724 let mut bytes = [0u8; 8];
725 bytes.copy_from_slice(&self.data[self.pos..self.pos + 8]);
726 self.pos += 8;
727 Ok(f64::from_le_bytes(bytes))
728 }
729
730 fn skip_field(&mut self, wire_type: u8) -> Result<(), MvtError> {
731 match wire_type {
732 WIRE_VARINT => {
733 let _ = self.read_varint()?;
734 }
735 WIRE_64 => {
736 if self.remaining() < 8 {
737 return Err(MvtError::TruncatedPayload);
738 }
739 self.pos += 8;
740 }
741 WIRE_LEN => {
742 let _ = self.read_bytes()?;
743 }
744 WIRE_32 => {
745 if self.remaining() < 4 {
746 return Err(MvtError::TruncatedPayload);
747 }
748 self.pos += 4;
749 }
750 other => return Err(MvtError::UnsupportedWireType(other)),
751 }
752 Ok(())
753 }
754}
755
756#[inline]
758fn zigzag_decode(n: u64) -> i64 {
759 ((n >> 1) as i64) ^ -((n & 1) as i64)
760}
761
762#[cfg(test)]
767mod tests {
768 use super::*;
769
770 #[test]
773 fn zigzag_decode_positive() {
774 assert_eq!(zigzag_decode(0), 0);
775 assert_eq!(zigzag_decode(2), 1);
776 assert_eq!(zigzag_decode(4), 2);
777 assert_eq!(zigzag_decode(100), 50);
778 }
779
780 #[test]
781 fn zigzag_decode_negative() {
782 assert_eq!(zigzag_decode(1), -1);
783 assert_eq!(zigzag_decode(3), -2);
784 assert_eq!(zigzag_decode(5), -3);
785 assert_eq!(zigzag_decode(99), -50);
786 }
787
788 #[test]
791 fn pb_reader_read_varint_single_byte() {
792 let data = [0x08]; let mut reader = PbReader::new(&data);
794 assert_eq!(reader.read_varint().unwrap(), 8);
795 }
796
797 #[test]
798 fn pb_reader_read_varint_multi_byte() {
799 let data = [0xAC, 0x02]; let mut reader = PbReader::new(&data);
801 assert_eq!(reader.read_varint().unwrap(), 300);
802 }
803
804 #[test]
805 fn pb_reader_read_tag() {
806 let data = [26];
808 let mut reader = PbReader::new(&data);
809 let (field, wire) = reader.read_tag().unwrap();
810 assert_eq!(field, 3);
811 assert_eq!(wire, WIRE_LEN);
812 }
813
814 #[test]
815 fn pb_reader_read_string() {
816 let data = [5, b'h', b'e', b'l', b'l', b'o'];
818 let mut reader = PbReader::new(&data);
819 assert_eq!(reader.read_string().unwrap(), "hello");
820 }
821
822 #[test]
823 fn pb_reader_truncated_payload() {
824 let data = [0xFF]; let mut reader = PbReader::new(&data);
826 assert!(reader.read_varint().is_err());
827 }
828
829 #[test]
832 fn tile_geo_bounds_zoom_0() {
833 let bounds = tile_geo_bounds(&TileId::new(0, 0, 0));
834 assert!((bounds.west - (-180.0)).abs() < 1e-6);
835 assert!((bounds.east - 180.0).abs() < 1e-6);
836 assert!(bounds.north > 85.0);
837 assert!(bounds.south < -85.0);
838 }
839
840 #[test]
841 fn tile_geo_bounds_higher_zoom() {
842 let bounds = tile_geo_bounds(&TileId::new(1, 0, 0));
843 assert!((bounds.west - (-180.0)).abs() < 1e-6);
844 assert!((bounds.east - 0.0).abs() < 1e-6);
845 assert!(bounds.north > 85.0);
846 assert!(bounds.south > -1.0); }
848
849 #[test]
852 fn signed_ring_area_ccw_positive() {
853 use rustial_math::GeoCoord;
854 let ring = vec![
856 GeoCoord::from_lat_lon(0.0, 0.0),
857 GeoCoord::from_lat_lon(0.0, 1.0),
858 GeoCoord::from_lat_lon(1.0, 1.0),
859 GeoCoord::from_lat_lon(1.0, 0.0),
860 GeoCoord::from_lat_lon(0.0, 0.0),
861 ];
862 let area = signed_ring_area(&ring);
863 assert!(area > 0.0, "CCW ring should have positive area, got {area}");
864 }
865
866 #[test]
867 fn signed_ring_area_cw_negative() {
868 use rustial_math::GeoCoord;
869 let ring = vec![
871 GeoCoord::from_lat_lon(0.0, 0.0),
872 GeoCoord::from_lat_lon(1.0, 0.0),
873 GeoCoord::from_lat_lon(1.0, 1.0),
874 GeoCoord::from_lat_lon(0.0, 1.0),
875 GeoCoord::from_lat_lon(0.0, 0.0),
876 ];
877 let area = signed_ring_area(&ring);
878 assert!(area < 0.0, "CW ring should have negative area, got {area}");
879 }
880
881 #[test]
884 fn tile_coord_to_geo_corners() {
885 let bounds = tile_geo_bounds(&TileId::new(0, 0, 0));
886 let nw = tile_coord_to_geo(0, 0, 4096, &bounds);
887 assert!((nw.lon - (-180.0)).abs() < 0.01);
888 assert!(nw.lat > 85.0);
889
890 let se = tile_coord_to_geo(4096, 4096, 4096, &bounds);
891 assert!((se.lon - 180.0).abs() < 0.01);
892 assert!(se.lat < -85.0);
893 }
894
895 fn encode_varint(mut val: u64) -> Vec<u8> {
898 let mut buf = Vec::new();
899 loop {
900 let mut byte = (val & 0x7F) as u8;
901 val >>= 7;
902 if val != 0 {
903 byte |= 0x80;
904 }
905 buf.push(byte);
906 if val == 0 {
907 break;
908 }
909 }
910 buf
911 }
912
913 fn encode_tag(field_number: u32, wire_type: u8) -> Vec<u8> {
914 encode_varint(((field_number as u64) << 3) | wire_type as u64)
915 }
916
917 fn encode_len_delimited(field_number: u32, data: &[u8]) -> Vec<u8> {
918 let mut buf = encode_tag(field_number, WIRE_LEN);
919 buf.extend(encode_varint(data.len() as u64));
920 buf.extend_from_slice(data);
921 buf
922 }
923
924 fn encode_string_field(field_number: u32, s: &str) -> Vec<u8> {
925 encode_len_delimited(field_number, s.as_bytes())
926 }
927
928 fn encode_varint_field(field_number: u32, val: u64) -> Vec<u8> {
929 let mut buf = encode_tag(field_number, WIRE_VARINT);
930 buf.extend(encode_varint(val));
931 buf
932 }
933
934 fn zigzag_encode(n: i32) -> u32 {
935 ((n << 1) ^ (n >> 31)) as u32
936 }
937
938 fn encode_geometry_commands(commands: &[(u32, &[(i32, i32)])]) -> Vec<u8> {
939 let mut buf = Vec::new();
940 for &(cmd_id, params) in commands {
941 let count = if cmd_id == CMD_CLOSE_PATH {
942 1u32
943 } else {
944 params.len() as u32
945 };
946 buf.extend(encode_varint(((count as u64) << 3) | cmd_id as u64));
947 for &(dx, dy) in params {
948 buf.extend(encode_varint(zigzag_encode(dx) as u64));
949 buf.extend(encode_varint(zigzag_encode(dy) as u64));
950 }
951 }
952 buf
953 }
954
955 fn build_mvt_value_string(s: &str) -> Vec<u8> {
956 encode_string_field(1, s)
957 }
958
959 fn build_mvt_value_double(v: f64) -> Vec<u8> {
960 let mut buf = encode_tag(3, WIRE_64);
961 buf.extend(v.to_le_bytes());
962 buf
963 }
964
965 fn build_mvt_feature(
966 id: Option<u64>,
967 geom_type: u32,
968 tags: &[u32],
969 geometry: &[u8],
970 ) -> Vec<u8> {
971 let mut buf = Vec::new();
972 if let Some(id) = id {
973 buf.extend(encode_varint_field(1, id));
974 }
975 if !tags.is_empty() {
976 let mut tags_buf = Vec::new();
977 for &t in tags {
978 tags_buf.extend(encode_varint(t as u64));
979 }
980 buf.extend(encode_len_delimited(2, &tags_buf));
981 }
982 buf.extend(encode_varint_field(3, geom_type as u64));
983 buf.extend(encode_len_delimited(4, geometry));
984 buf
985 }
986
987 fn build_mvt_layer(
988 name: &str,
989 features: &[Vec<u8>],
990 keys: &[&str],
991 values: &[Vec<u8>],
992 extent: u32,
993 ) -> Vec<u8> {
994 let mut buf = Vec::new();
995 buf.extend(encode_string_field(1, name));
996 for feat in features {
997 buf.extend(encode_len_delimited(2, feat));
998 }
999 for key in keys {
1000 buf.extend(encode_string_field(3, key));
1001 }
1002 for val in values {
1003 buf.extend(encode_len_delimited(4, val));
1004 }
1005 buf.extend(encode_varint_field(5, extent as u64));
1006 buf.extend(encode_varint_field(15, 2)); buf
1008 }
1009
1010 fn build_mvt_tile(layers: &[Vec<u8>]) -> Vec<u8> {
1011 let mut buf = Vec::new();
1012 for layer in layers {
1013 buf.extend(encode_len_delimited(3, layer));
1014 }
1015 buf
1016 }
1017
1018 #[test]
1021 fn decode_empty_tile() {
1022 let bytes = build_mvt_tile(&[]);
1023 let result = decode_mvt(&bytes, &TileId::new(0, 0, 0), &MvtDecodeOptions::default());
1024 assert!(result.is_ok());
1025 assert!(result.unwrap().is_empty());
1026 }
1027
1028 #[test]
1029 fn decode_point_feature() {
1030 let geom = encode_geometry_commands(&[(CMD_MOVE_TO, &[(2048, 2048)])]);
1032 let feature = build_mvt_feature(Some(42), GEOM_POINT, &[], &geom);
1033 let layer = build_mvt_layer("points", &[feature], &[], &[], 4096);
1034 let tile = build_mvt_tile(&[layer]);
1035
1036 let tile_id = TileId::new(0, 0, 0);
1037 let result = decode_mvt(&tile, &tile_id, &MvtDecodeOptions::default()).unwrap();
1038
1039 assert_eq!(result.len(), 1);
1040 let features = &result["points"];
1041 assert_eq!(features.len(), 1);
1042
1043 match &features.features[0].geometry {
1044 Geometry::Point(pt) => {
1045 assert!(pt.coord.lon.abs() < 1.0);
1047 assert!(pt.coord.lat.abs() < 1.0);
1048 }
1049 other => panic!("expected Point, got {}", other.type_name()),
1050 }
1051
1052 let id_prop = features.features[0].property("$id");
1054 assert_eq!(id_prop.and_then(|v| v.as_f64()), Some(42.0));
1055 }
1056
1057 #[test]
1058 fn decode_linestring_feature() {
1059 let geom = encode_geometry_commands(&[
1060 (CMD_MOVE_TO, &[(0, 0)]),
1061 (CMD_LINE_TO, &[(4096, 0), (0, 4096)]),
1062 ]);
1063 let feature = build_mvt_feature(None, GEOM_LINESTRING, &[], &geom);
1064 let layer = build_mvt_layer("roads", &[feature], &[], &[], 4096);
1065 let tile = build_mvt_tile(&[layer]);
1066
1067 let tile_id = TileId::new(0, 0, 0);
1068 let result = decode_mvt(&tile, &tile_id, &MvtDecodeOptions::default()).unwrap();
1069
1070 let features = &result["roads"];
1071 assert_eq!(features.len(), 1);
1072 match &features.features[0].geometry {
1073 Geometry::LineString(ls) => {
1074 assert_eq!(ls.coords.len(), 3);
1075 }
1076 other => panic!("expected LineString, got {}", other.type_name()),
1077 }
1078 }
1079
1080 #[test]
1081 fn decode_polygon_feature() {
1082 let geom = encode_geometry_commands(&[
1084 (CMD_MOVE_TO, &[(0, 0)]),
1085 (CMD_LINE_TO, &[(4096, 0), (0, 4096), (-4096, 0)]),
1086 (CMD_CLOSE_PATH, &[]),
1087 ]);
1088 let feature = build_mvt_feature(None, GEOM_POLYGON, &[], &geom);
1089 let layer = build_mvt_layer("water", &[feature], &[], &[], 4096);
1090 let tile = build_mvt_tile(&[layer]);
1091
1092 let tile_id = TileId::new(0, 0, 0);
1093 let result = decode_mvt(&tile, &tile_id, &MvtDecodeOptions::default()).unwrap();
1094
1095 let features = &result["water"];
1096 assert_eq!(features.len(), 1);
1097 match &features.features[0].geometry {
1098 Geometry::Polygon(poly) => {
1099 assert!(poly.exterior.len() >= 4, "polygon should have 4+ vertices");
1100 assert!(poly.interiors.is_empty());
1101 }
1102 other => panic!("expected Polygon, got {}", other.type_name()),
1103 }
1104 }
1105
1106 #[test]
1107 fn decode_feature_with_properties() {
1108 let geom = encode_geometry_commands(&[(CMD_MOVE_TO, &[(100, 100)])]);
1109 let keys = &["name", "population"];
1110 let values = &[
1111 build_mvt_value_string("Springfield"),
1112 build_mvt_value_double(12345.0),
1113 ];
1114 let feature = build_mvt_feature(None, GEOM_POINT, &[0, 0, 1, 1], &geom);
1116 let layer = build_mvt_layer("places", &[feature], keys, values, 4096);
1117 let tile = build_mvt_tile(&[layer]);
1118
1119 let tile_id = TileId::new(1, 0, 0);
1120 let result = decode_mvt(&tile, &tile_id, &MvtDecodeOptions::default()).unwrap();
1121
1122 let features = &result["places"];
1123 assert_eq!(features.len(), 1);
1124 let props = &features.features[0].properties;
1125 assert_eq!(
1126 props.get("name").and_then(|v| v.as_str()),
1127 Some("Springfield")
1128 );
1129 assert_eq!(
1130 props.get("population").and_then(|v| v.as_f64()),
1131 Some(12345.0)
1132 );
1133 }
1134
1135 #[test]
1136 fn decode_with_layer_filter() {
1137 let geom = encode_geometry_commands(&[(CMD_MOVE_TO, &[(100, 100)])]);
1138 let feat = build_mvt_feature(None, GEOM_POINT, &[], &geom);
1139 let layer_a = build_mvt_layer("water", &[feat.clone()], &[], &[], 4096);
1140 let layer_b = build_mvt_layer("roads", &[feat], &[], &[], 4096);
1141 let tile = build_mvt_tile(&[layer_a, layer_b]);
1142
1143 let options = MvtDecodeOptions {
1144 layer_filter: vec!["water".into()],
1145 };
1146 let tile_id = TileId::new(0, 0, 0);
1147 let result = decode_mvt(&tile, &tile_id, &options).unwrap();
1148
1149 assert_eq!(result.len(), 1);
1150 assert!(result.contains_key("water"));
1151 assert!(!result.contains_key("roads"));
1152 }
1153
1154 #[test]
1155 fn decode_multi_point_feature() {
1156 let geom = encode_geometry_commands(&[(CMD_MOVE_TO, &[(100, 100), (200, 200)])]);
1157 let feature = build_mvt_feature(None, GEOM_POINT, &[], &geom);
1158 let layer = build_mvt_layer("multi", &[feature], &[], &[], 4096);
1159 let tile = build_mvt_tile(&[layer]);
1160
1161 let tile_id = TileId::new(0, 0, 0);
1162 let result = decode_mvt(&tile, &tile_id, &MvtDecodeOptions::default()).unwrap();
1163 match &result["multi"].features[0].geometry {
1164 Geometry::MultiPoint(mp) => {
1165 assert_eq!(mp.points.len(), 2);
1166 }
1167 other => panic!("expected MultiPoint, got {}", other.type_name()),
1168 }
1169 }
1170
1171 #[test]
1172 fn mvt_error_display() {
1173 assert!(MvtError::TruncatedPayload.to_string().contains("truncated"));
1174 assert!(MvtError::UnsupportedWireType(6).to_string().contains("6"));
1175 assert!(MvtError::InvalidGeometryCommand(99)
1176 .to_string()
1177 .contains("99"));
1178 }
1179}