1use crate::error::ServiceError;
39
40pub fn encode_varint(mut value: u64) -> Vec<u8> {
46 let mut buf = Vec::with_capacity(10);
47 loop {
48 if value < 0x80 {
49 buf.push(value as u8);
50 return buf;
51 }
52 buf.push((value as u8 & 0x7F) | 0x80);
53 value >>= 7;
54 }
55}
56
57pub fn encode_zigzag(value: i64) -> u64 {
62 ((value << 1) ^ (value >> 63)) as u64
63}
64
65pub fn decode_zigzag(value: u64) -> i64 {
67 ((value >> 1) as i64) ^ (-((value & 1) as i64))
68}
69
70fn encode_tag(field: u32, wire_type: u8) -> Vec<u8> {
72 encode_varint(((field << 3) | wire_type as u32) as u64)
73}
74
75pub(crate) fn encode_varint_field(field: u32, value: u64) -> Vec<u8> {
77 let mut buf = encode_tag(field, 0);
78 buf.extend_from_slice(&encode_varint(value));
79 buf
80}
81
82pub(crate) fn encode_len_delimited(field: u32, data: &[u8]) -> Vec<u8> {
84 let mut buf = encode_tag(field, 2);
85 buf.extend_from_slice(&encode_varint(data.len() as u64));
86 buf.extend_from_slice(data);
87 buf
88}
89
90pub(crate) fn encode_string_field(field: u32, s: &str) -> Vec<u8> {
92 encode_len_delimited(field, s.as_bytes())
93}
94
95#[derive(Debug, Clone, PartialEq, Eq)]
101pub enum MvtGeometryType {
102 Unknown = 0,
104 Point = 1,
106 LineString = 2,
108 Polygon = 3,
110}
111
112impl MvtGeometryType {
113 fn as_u64(&self) -> u64 {
114 match self {
115 Self::Unknown => 0,
116 Self::Point => 1,
117 Self::LineString => 2,
118 Self::Polygon => 3,
119 }
120 }
121}
122
123#[derive(Debug, Clone)]
131pub enum MvtValue {
132 String(String),
134 Float(f32),
136 Double(f64),
138 Int(i64),
140 UInt(u64),
142 Sint(i64),
144 Bool(bool),
146}
147
148impl MvtValue {
149 pub fn encode(&self) -> Vec<u8> {
151 match self {
152 Self::String(s) => encode_string_field(1, s),
153 Self::Float(v) => {
154 let mut buf = encode_tag(2, 5);
156 buf.extend_from_slice(&v.to_le_bytes());
157 buf
158 }
159 Self::Double(v) => {
160 let mut buf = encode_tag(3, 1);
162 buf.extend_from_slice(&v.to_le_bytes());
163 buf
164 }
165 Self::Int(v) => encode_varint_field(4, *v as u64),
166 Self::UInt(v) => encode_varint_field(5, *v),
167 Self::Sint(v) => encode_varint_field(6, encode_zigzag(*v)),
168 Self::Bool(v) => encode_varint_field(7, u64::from(*v)),
169 }
170 }
171}
172
173#[derive(Debug, Clone)]
182pub struct MvtFeature {
183 pub id: Option<u64>,
185 pub geometry_type: MvtGeometryType,
187 pub geometry: Vec<i32>,
189 pub tags: Vec<u32>,
191}
192
193impl MvtFeature {
194 pub fn encode(&self) -> Vec<u8> {
196 let mut buf = Vec::new();
197
198 if let Some(id) = self.id {
200 buf.extend_from_slice(&encode_varint_field(1, id));
201 }
202
203 if !self.tags.is_empty() {
205 let mut packed = Vec::with_capacity(self.tags.len() * 2);
206 for &tag in &self.tags {
207 packed.extend_from_slice(&encode_varint(tag as u64));
208 }
209 buf.extend_from_slice(&encode_len_delimited(2, &packed));
210 }
211
212 buf.extend_from_slice(&encode_varint_field(3, self.geometry_type.as_u64()));
214
215 if !self.geometry.is_empty() {
217 let mut packed = Vec::with_capacity(self.geometry.len() * 2);
218 for &cmd in &self.geometry {
219 packed.extend_from_slice(&encode_varint(encode_zigzag(cmd as i64)));
220 }
221 buf.extend_from_slice(&encode_len_delimited(4, &packed));
222 }
223
224 buf
225 }
226}
227
228pub struct MvtLayer {
236 pub name: String,
238 pub version: u32,
240 pub extent: u32,
242 pub keys: Vec<String>,
244 pub values: Vec<MvtValue>,
246 pub features: Vec<MvtFeature>,
248}
249
250impl MvtLayer {
251 pub fn new(name: impl Into<String>) -> Self {
253 Self {
254 name: name.into(),
255 version: 2,
256 extent: 4096,
257 keys: Vec::new(),
258 values: Vec::new(),
259 features: Vec::new(),
260 }
261 }
262
263 pub fn with_extent(name: impl Into<String>, extent: u32) -> Self {
265 Self {
266 extent,
267 ..Self::new(name)
268 }
269 }
270
271 pub fn key_index(&mut self, key: &str) -> u32 {
275 if let Some(i) = self.keys.iter().position(|k| k == key) {
276 return i as u32;
277 }
278 self.keys.push(key.to_string());
279 (self.keys.len() - 1) as u32
280 }
281
282 pub fn value_index(&mut self, value: MvtValue) -> u32 {
286 self.values.push(value);
287 (self.values.len() - 1) as u32
288 }
289
290 pub fn add_feature(&mut self, feature: MvtFeature) {
292 self.features.push(feature);
293 }
294
295 pub fn encode(&self) -> Vec<u8> {
297 let mut buf = Vec::new();
298
299 buf.extend_from_slice(&encode_varint_field(15, self.version as u64));
301
302 buf.extend_from_slice(&encode_string_field(1, &self.name));
304
305 for feature in &self.features {
307 buf.extend_from_slice(&encode_len_delimited(2, &feature.encode()));
308 }
309
310 for key in &self.keys {
312 buf.extend_from_slice(&encode_string_field(3, key));
313 }
314
315 for value in &self.values {
317 buf.extend_from_slice(&encode_len_delimited(4, &value.encode()));
318 }
319
320 buf.extend_from_slice(&encode_varint_field(5, self.extent as u64));
322
323 buf
324 }
325
326 pub fn feature_count(&self) -> usize {
328 self.features.len()
329 }
330}
331
332pub fn move_to(dx: i32, dy: i32) -> Vec<i32> {
343 vec![9, dx, dy]
345}
346
347pub fn line_to(coords: &[(i32, i32)]) -> Vec<i32> {
354 let count = coords.len() as i32;
355 let mut cmds = Vec::with_capacity(1 + coords.len() * 2);
357 cmds.push((count << 3) | 2);
358 for &(dx, dy) in coords {
359 cmds.push(dx);
360 cmds.push(dy);
361 }
362 cmds
363}
364
365pub fn close_path() -> Vec<i32> {
369 vec![15]
370}
371
372pub fn point_geometry(dx: i32, dy: i32) -> Vec<i32> {
374 move_to(dx, dy)
375}
376
377pub fn linestring_geometry(coords: &[(i32, i32)]) -> Vec<i32> {
382 if coords.is_empty() {
383 return Vec::new();
384 }
385 let mut cmds = move_to(coords[0].0, coords[0].1);
386 if coords.len() > 1 {
387 cmds.extend_from_slice(&line_to(&coords[1..]));
388 }
389 cmds
390}
391
392pub fn polygon_ring_geometry(coords: &[(i32, i32)]) -> Vec<i32> {
396 if coords.is_empty() {
397 return Vec::new();
398 }
399 let mut cmds = move_to(coords[0].0, coords[0].1);
400 if coords.len() > 1 {
401 cmds.extend_from_slice(&line_to(&coords[1..]));
402 }
403 cmds.extend_from_slice(&close_path());
404 cmds
405}
406
407pub struct MvtTile {
416 pub layers: Vec<MvtLayer>,
418}
419
420impl MvtTile {
421 pub fn new() -> Self {
423 Self { layers: Vec::new() }
424 }
425
426 pub fn add_layer(&mut self, layer: MvtLayer) {
428 self.layers.push(layer);
429 }
430
431 pub fn encode(&self) -> Vec<u8> {
435 let mut buf = Vec::new();
436 for layer in &self.layers {
437 buf.extend_from_slice(&encode_len_delimited(3, &layer.encode()));
439 }
440 buf
441 }
442
443 pub fn total_feature_count(&self) -> usize {
445 self.layers.iter().map(|l| l.feature_count()).sum()
446 }
447}
448
449impl Default for MvtTile {
450 fn default() -> Self {
451 Self::new()
452 }
453}
454
455pub fn scale_to_tile(lon: f64, lat: f64, tile_bbox: [f64; 4], extent: u32) -> (i32, i32) {
464 let [west, south, east, north] = tile_bbox;
465 let x_raw = (lon - west) / (east - west) * extent as f64;
466 let y_raw = (north - lat) / (north - south) * extent as f64;
467 let x = x_raw as i32;
468 let y = y_raw as i32;
469 let max = extent as i32 - 1;
470 (x.clamp(0, max), y.clamp(0, max))
471}
472
473pub fn delta_encode(coords: &[(i32, i32)]) -> Vec<(i32, i32)> {
478 let mut deltas = Vec::with_capacity(coords.len());
479 let mut cursor = (0i32, 0i32);
480 for &(x, y) in coords {
481 deltas.push((x - cursor.0, y - cursor.1));
482 cursor = (x, y);
483 }
484 deltas
485}
486
487pub struct MvtLayerBuilder {
495 layer: MvtLayer,
496 tile_bbox: [f64; 4],
497}
498
499impl MvtLayerBuilder {
500 pub fn new(name: impl Into<String>, tile_bbox: [f64; 4], extent: u32) -> Self {
502 Self {
503 layer: MvtLayer::with_extent(name, extent),
504 tile_bbox,
505 }
506 }
507
508 pub fn add_point(
513 &mut self,
514 lon: f64,
515 lat: f64,
516 id: Option<u64>,
517 properties: &[(&str, MvtValue)],
518 ) -> Result<(), ServiceError> {
519 let (px, py) = scale_to_tile(lon, lat, self.tile_bbox, self.layer.extent);
520 let geom = point_geometry(px, py);
521
522 let mut tags = Vec::with_capacity(properties.len() * 2);
523 for (key, value) in properties {
524 let ki = self.layer.key_index(key);
525 let vi = self.layer.value_index(value.clone());
526 tags.push(ki);
527 tags.push(vi);
528 }
529
530 self.layer.add_feature(MvtFeature {
531 id,
532 geometry_type: MvtGeometryType::Point,
533 geometry: geom,
534 tags,
535 });
536 Ok(())
537 }
538
539 pub fn add_linestring(
541 &mut self,
542 coords: &[(f64, f64)],
543 id: Option<u64>,
544 properties: &[(&str, MvtValue)],
545 ) -> Result<(), ServiceError> {
546 if coords.is_empty() {
547 return Err(ServiceError::InvalidParameter(
548 "coords".into(),
549 "linestring must have at least one coordinate".into(),
550 ));
551 }
552
553 let pixel_coords: Vec<(i32, i32)> = coords
554 .iter()
555 .map(|&(lon, lat)| scale_to_tile(lon, lat, self.tile_bbox, self.layer.extent))
556 .collect();
557 let deltas = delta_encode(&pixel_coords);
558 let geom = linestring_geometry(&deltas);
559
560 let mut tags = Vec::with_capacity(properties.len() * 2);
561 for (key, value) in properties {
562 let ki = self.layer.key_index(key);
563 let vi = self.layer.value_index(value.clone());
564 tags.push(ki);
565 tags.push(vi);
566 }
567
568 self.layer.add_feature(MvtFeature {
569 id,
570 geometry_type: MvtGeometryType::LineString,
571 geometry: geom,
572 tags,
573 });
574 Ok(())
575 }
576
577 pub fn add_polygon(
579 &mut self,
580 ring: &[(f64, f64)],
581 id: Option<u64>,
582 properties: &[(&str, MvtValue)],
583 ) -> Result<(), ServiceError> {
584 if ring.len() < 3 {
585 return Err(ServiceError::InvalidParameter(
586 "ring".into(),
587 "polygon ring must have at least 3 coordinates".into(),
588 ));
589 }
590
591 let pixel_coords: Vec<(i32, i32)> = ring
592 .iter()
593 .map(|&(lon, lat)| scale_to_tile(lon, lat, self.tile_bbox, self.layer.extent))
594 .collect();
595 let deltas = delta_encode(&pixel_coords);
596 let geom = polygon_ring_geometry(&deltas);
597
598 let mut tags = Vec::with_capacity(properties.len() * 2);
599 for (key, value) in properties {
600 let ki = self.layer.key_index(key);
601 let vi = self.layer.value_index(value.clone());
602 tags.push(ki);
603 tags.push(vi);
604 }
605
606 self.layer.add_feature(MvtFeature {
607 id,
608 geometry_type: MvtGeometryType::Polygon,
609 geometry: geom,
610 tags,
611 });
612 Ok(())
613 }
614
615 pub fn build(self) -> MvtLayer {
617 self.layer
618 }
619}
620
621#[cfg(test)]
622mod tests {
623 use super::*;
624
625 #[test]
628 fn test_encode_varint_zero() {
629 assert_eq!(encode_varint(0), vec![0x00]);
630 }
631
632 #[test]
633 fn test_encode_varint_one() {
634 assert_eq!(encode_varint(1), vec![0x01]);
635 }
636
637 #[test]
638 fn test_encode_varint_127() {
639 assert_eq!(encode_varint(127), vec![0x7F]);
640 }
641
642 #[test]
643 fn test_encode_varint_128() {
644 assert_eq!(encode_varint(128), vec![0x80, 0x01]);
646 }
647
648 #[test]
649 fn test_encode_varint_300() {
650 assert_eq!(encode_varint(300), vec![0xAC, 0x02]);
653 }
654
655 #[test]
656 fn test_encode_varint_large() {
657 let enc = encode_varint(16384);
659 assert_eq!(enc.len(), 3);
660 }
661
662 #[test]
663 fn test_encode_varint_max_u32() {
664 let enc = encode_varint(u32::MAX as u64);
665 assert!(!enc.is_empty());
666 assert!(enc.len() <= 5);
667 }
668
669 #[test]
672 fn test_encode_zigzag_zero() {
673 assert_eq!(encode_zigzag(0), 0);
674 }
675
676 #[test]
677 fn test_encode_zigzag_minus_one() {
678 assert_eq!(encode_zigzag(-1), 1);
679 }
680
681 #[test]
682 fn test_encode_zigzag_plus_one() {
683 assert_eq!(encode_zigzag(1), 2);
684 }
685
686 #[test]
687 fn test_encode_zigzag_minus_two() {
688 assert_eq!(encode_zigzag(-2), 3);
689 }
690
691 #[test]
692 fn test_encode_zigzag_plus_two() {
693 assert_eq!(encode_zigzag(2), 4);
694 }
695
696 #[test]
697 fn test_decode_zigzag_roundtrip() {
698 for v in [-100i64, -1, 0, 1, 100, 4095, -4096] {
699 assert_eq!(
700 decode_zigzag(encode_zigzag(v)),
701 v,
702 "roundtrip failed for {}",
703 v
704 );
705 }
706 }
707
708 #[test]
711 fn test_move_to_encoding() {
712 let cmds = move_to(10, 20);
713 assert_eq!(cmds, vec![9, 10, 20]);
715 }
716
717 #[test]
718 fn test_move_to_origin() {
719 let cmds = move_to(0, 0);
720 assert_eq!(cmds, vec![9, 0, 0]);
721 }
722
723 #[test]
724 fn test_close_path_encoding() {
725 let cmds = close_path();
726 assert_eq!(cmds, vec![15]);
728 }
729
730 #[test]
731 fn test_line_to_two_points() {
732 let cmds = line_to(&[(5, 3), (10, 8)]);
733 assert_eq!(cmds[0], 18, "command integer for LineTo count=2");
735 assert_eq!(cmds[1], 5);
736 assert_eq!(cmds[2], 3);
737 assert_eq!(cmds[3], 10);
738 assert_eq!(cmds[4], 8);
739 assert_eq!(cmds.len(), 5);
740 }
741
742 #[test]
743 fn test_line_to_one_point() {
744 let cmds = line_to(&[(7, 7)]);
745 assert_eq!(cmds[0], 10);
747 assert_eq!(cmds.len(), 3);
748 }
749
750 #[test]
751 fn test_polygon_ring_geometry_has_close_path() {
752 let ring = [(0, 0), (100, 0), (100, 100), (0, 100)];
753 let cmds = polygon_ring_geometry(&ring);
754 assert_eq!(*cmds.last().expect("non-empty"), 15);
756 }
757
758 #[test]
759 fn test_linestring_geometry_starts_with_move_to() {
760 let coords = [(5, 10), (15, 20), (25, 30)];
761 let cmds = linestring_geometry(&coords);
762 assert_eq!(cmds[0], 9, "should start with MoveTo");
764 assert_eq!(cmds[1], 5);
765 assert_eq!(cmds[2], 10);
766 }
767
768 #[test]
771 fn test_mvt_layer_new_defaults() {
772 let layer = MvtLayer::new("roads");
773 assert_eq!(layer.name, "roads");
774 assert_eq!(layer.version, 2);
775 assert_eq!(layer.extent, 4096);
776 assert!(layer.keys.is_empty());
777 assert!(layer.values.is_empty());
778 assert!(layer.features.is_empty());
779 }
780
781 #[test]
782 fn test_mvt_layer_key_index_insert() {
783 let mut layer = MvtLayer::new("test");
784 let i0 = layer.key_index("name");
785 let i1 = layer.key_index("type");
786 assert_eq!(i0, 0);
787 assert_eq!(i1, 1);
788 assert_eq!(layer.keys.len(), 2);
789 }
790
791 #[test]
792 fn test_mvt_layer_key_index_dedup() {
793 let mut layer = MvtLayer::new("test");
794 let i0 = layer.key_index("name");
795 let i1 = layer.key_index("name");
796 assert_eq!(i0, i1, "duplicate key should return same index");
797 assert_eq!(layer.keys.len(), 1);
798 }
799
800 #[test]
801 fn test_mvt_layer_value_index_append() {
802 let mut layer = MvtLayer::new("test");
803 let i0 = layer.value_index(MvtValue::String("road".into()));
804 let i1 = layer.value_index(MvtValue::Int(42));
805 assert_eq!(i0, 0);
806 assert_eq!(i1, 1);
807 }
808
809 #[test]
810 fn test_mvt_layer_encode_non_empty() {
811 let mut layer = MvtLayer::new("buildings");
812 layer.add_feature(MvtFeature {
813 id: Some(1),
814 geometry_type: MvtGeometryType::Point,
815 geometry: move_to(100, 200),
816 tags: vec![],
817 });
818 let encoded = layer.encode();
819 assert!(!encoded.is_empty(), "encoded layer should not be empty");
820 }
821
822 #[test]
825 fn test_mvt_feature_encode_has_geometry_type() {
826 let f = MvtFeature {
827 id: None,
828 geometry_type: MvtGeometryType::Point,
829 geometry: move_to(0, 0),
830 tags: vec![],
831 };
832 let encoded = f.encode();
833 assert!(
836 encoded.windows(2).any(|w| w[0] == 0x18),
837 "geometry type field (0x18) not found in encoded feature"
838 );
839 }
840
841 #[test]
842 fn test_mvt_feature_encode_with_id() {
843 let f = MvtFeature {
844 id: Some(42),
845 geometry_type: MvtGeometryType::LineString,
846 geometry: vec![],
847 tags: vec![],
848 };
849 let encoded = f.encode();
850 assert!(
852 encoded.first() == Some(&0x08),
853 "id field tag (0x08) should be first byte"
854 );
855 }
856
857 #[test]
858 fn test_mvt_feature_encode_with_tags() {
859 let f = MvtFeature {
860 id: None,
861 geometry_type: MvtGeometryType::Polygon,
862 geometry: vec![],
863 tags: vec![0, 0, 1, 1],
864 };
865 let encoded = f.encode();
866 assert!(!encoded.is_empty());
867 assert!(
869 encoded.contains(&0x12),
870 "tags field (0x12) should be present"
871 );
872 }
873
874 #[test]
877 fn test_mvt_tile_empty_encode() {
878 let tile = MvtTile::new();
879 let encoded = tile.encode();
880 assert!(
881 encoded.is_empty(),
882 "empty tile should encode to empty bytes"
883 );
884 }
885
886 #[test]
887 fn test_mvt_tile_wraps_layer_in_field3() {
888 let mut tile = MvtTile::new();
889 let layer = MvtLayer::new("test");
890 tile.add_layer(layer);
891 let encoded = tile.encode();
892 assert!(!encoded.is_empty());
893 assert_eq!(
895 encoded[0], 0x1A,
896 "tile should start with layer field tag 0x1A"
897 );
898 }
899
900 #[test]
901 fn test_mvt_tile_multiple_layers() {
902 let mut tile = MvtTile::new();
903 tile.add_layer(MvtLayer::new("roads"));
904 tile.add_layer(MvtLayer::new("buildings"));
905 assert_eq!(tile.layers.len(), 2);
906 let encoded = tile.encode();
907 let count = encoded.windows(1).filter(|w| w[0] == 0x1A).count();
909 assert_eq!(count, 2, "should have 2 layer field tags");
910 }
911
912 #[test]
913 fn test_mvt_tile_total_feature_count() {
914 let mut tile = MvtTile::new();
915 let mut layer1 = MvtLayer::new("a");
916 layer1.add_feature(MvtFeature {
917 id: None,
918 geometry_type: MvtGeometryType::Point,
919 geometry: move_to(0, 0),
920 tags: vec![],
921 });
922 let mut layer2 = MvtLayer::new("b");
923 layer2.add_feature(MvtFeature {
924 id: None,
925 geometry_type: MvtGeometryType::Point,
926 geometry: move_to(10, 10),
927 tags: vec![],
928 });
929 layer2.add_feature(MvtFeature {
930 id: None,
931 geometry_type: MvtGeometryType::Point,
932 geometry: move_to(20, 20),
933 tags: vec![],
934 });
935 tile.add_layer(layer1);
936 tile.add_layer(layer2);
937 assert_eq!(tile.total_feature_count(), 3);
938 }
939
940 #[test]
943 fn test_scale_to_tile_origin() {
944 let bbox = [-10.0f64, -10.0, 10.0, 10.0];
945 let (x, y) = scale_to_tile(-10.0, 10.0, bbox, 4096);
946 assert_eq!(x, 0);
947 assert_eq!(y, 0);
948 }
949
950 #[test]
951 fn test_scale_to_tile_max_corner() {
952 let bbox = [0.0f64, 0.0, 1.0, 1.0];
953 let (x, y) = scale_to_tile(1.0, 1.0, bbox, 4096);
955 assert_eq!(x, 4095);
956 assert_eq!(y, 0);
957 }
958
959 #[test]
960 fn test_scale_to_tile_clamps_negative() {
961 let bbox = [0.0f64, 0.0, 1.0, 1.0];
962 let (x, y) = scale_to_tile(-1.0, 2.0, bbox, 4096);
963 assert_eq!(x, 0);
964 assert_eq!(y, 0);
965 }
966
967 #[test]
968 fn test_scale_to_tile_clamps_overflow() {
969 let bbox = [0.0f64, 0.0, 1.0, 1.0];
970 let (x, y) = scale_to_tile(2.0, -1.0, bbox, 4096);
971 assert_eq!(x, 4095);
972 assert_eq!(y, 4095);
973 }
974
975 #[test]
976 fn test_scale_to_tile_center() {
977 let bbox = [-180.0f64, -85.0, 180.0, 85.0];
978 let (x, y) = scale_to_tile(0.0, 0.0, bbox, 4096);
979 assert_eq!(x, 2048);
980 assert!((y - 2048).abs() <= 2, "y={}", y);
982 }
983
984 #[test]
987 fn test_delta_encode_basic() {
988 let coords = [(10, 20), (15, 25), (5, 30)];
989 let deltas = delta_encode(&coords);
990 assert_eq!(deltas[0], (10, 20));
991 assert_eq!(deltas[1], (5, 5));
992 assert_eq!(deltas[2], (-10, 5));
993 }
994
995 #[test]
996 fn test_delta_encode_empty() {
997 let deltas = delta_encode(&[]);
998 assert!(deltas.is_empty());
999 }
1000
1001 #[test]
1004 fn test_layer_builder_add_point() {
1005 let bbox = [-180.0f64, -85.0, 180.0, 85.0];
1006 let mut builder = MvtLayerBuilder::new("poi", bbox, 4096);
1007 builder
1008 .add_point(
1009 0.0,
1010 0.0,
1011 Some(1),
1012 &[("name", MvtValue::String("origin".into()))],
1013 )
1014 .expect("add_point should succeed");
1015 let layer = builder.build();
1016 assert_eq!(layer.feature_count(), 1);
1017 assert_eq!(layer.keys.len(), 1);
1018 assert_eq!(layer.keys[0], "name");
1019 }
1020
1021 #[test]
1022 fn test_layer_builder_add_linestring() {
1023 let bbox = [-180.0f64, -85.0, 180.0, 85.0];
1024 let mut builder = MvtLayerBuilder::new("roads", bbox, 4096);
1025 let coords = [(-10.0f64, 20.0), (0.0, 30.0), (10.0, 40.0)];
1026 builder
1027 .add_linestring(
1028 &coords,
1029 None,
1030 &[("highway", MvtValue::String("motorway".into()))],
1031 )
1032 .expect("add_linestring should succeed");
1033 let layer = builder.build();
1034 assert_eq!(layer.feature_count(), 1);
1035 let f = &layer.features[0];
1036 assert_eq!(f.geometry_type, MvtGeometryType::LineString);
1037 }
1038
1039 #[test]
1040 fn test_layer_builder_add_linestring_empty_error() {
1041 let bbox = [-180.0f64, -85.0, 180.0, 85.0];
1042 let mut builder = MvtLayerBuilder::new("roads", bbox, 4096);
1043 let result = builder.add_linestring(&[], None, &[]);
1044 assert!(result.is_err(), "empty linestring should return error");
1045 }
1046
1047 #[test]
1048 fn test_layer_builder_add_polygon() {
1049 let bbox = [-1.0f64, -1.0, 1.0, 1.0];
1050 let mut builder = MvtLayerBuilder::new("buildings", bbox, 4096);
1051 let ring = [(-0.5f64, -0.5), (0.5, -0.5), (0.5, 0.5), (-0.5, 0.5)];
1052 builder
1053 .add_polygon(&ring, Some(99), &[("height", MvtValue::Int(10))])
1054 .expect("add_polygon should succeed");
1055 let layer = builder.build();
1056 let f = &layer.features[0];
1057 assert_eq!(f.geometry_type, MvtGeometryType::Polygon);
1058 assert_eq!(f.id, Some(99));
1059 assert_eq!(*f.geometry.last().expect("non-empty"), 15);
1061 }
1062
1063 #[test]
1066 fn test_full_roundtrip_tile_encode_non_empty() {
1067 let bbox = [-180.0f64, -90.0, 180.0, 90.0];
1068 let mut builder = MvtLayerBuilder::new("countries", bbox, 4096);
1069 builder
1070 .add_point(
1071 139.6917,
1072 35.6895,
1073 Some(1),
1074 &[
1075 ("name", MvtValue::String("Tokyo".into())),
1076 ("pop", MvtValue::Int(13_960_000)),
1077 ],
1078 )
1079 .expect("add Tokyo point");
1080 builder
1081 .add_point(
1082 -0.1276,
1083 51.5074,
1084 Some(2),
1085 &[
1086 ("name", MvtValue::String("London".into())),
1087 ("pop", MvtValue::Int(8_982_000)),
1088 ],
1089 )
1090 .expect("add London point");
1091
1092 let layer = builder.build();
1093 assert_eq!(layer.feature_count(), 2);
1094
1095 let mut tile = MvtTile::new();
1096 tile.add_layer(layer);
1097
1098 let encoded = tile.encode();
1099 assert!(!encoded.is_empty(), "encoded tile must not be empty");
1100 assert_eq!(encoded[0], 0x1A);
1102 }
1103
1104 #[test]
1105 fn test_mvt_value_string_encode() {
1106 let v = MvtValue::String("hello".into());
1107 let enc = v.encode();
1108 assert_eq!(enc[0], 0x0A);
1110 assert_eq!(enc[1], 5);
1111 }
1112
1113 #[test]
1114 fn test_mvt_value_bool_true_encode() {
1115 let v = MvtValue::Bool(true);
1116 let enc = v.encode();
1117 assert!(!enc.is_empty());
1118 }
1119
1120 #[test]
1121 fn test_mvt_value_double_encode_length() {
1122 let v = MvtValue::Double(std::f64::consts::PI);
1123 let enc = v.encode();
1124 assert!(enc.len() >= 9);
1126 }
1127}