1use std::collections::{BTreeMap, HashMap};
4
5use fast_mvt::{MvtLayer, MvtLayerRef, MvtReaderRef, MvtValue, MvtValueRef};
6use serde_json::Value;
7
8use crate::geojson::{Feature, FeatureCollection};
9use crate::tile::{PropValue, TileFeature, TileLayer};
10use crate::{MltError, MltResult};
11
12fn read_mvt_layers(data: &[u8]) -> MltResult<Vec<MvtLayer>> {
14 let layers = MvtReaderRef::new(data)?.to_tile()?.layers;
15 if layers.iter().any(|layer| layer.name.is_empty()) {
16 return Err(MltError::MissingLayerName);
17 }
18 Ok(layers)
19}
20
21pub fn mvt_to_feature_collection(data: impl AsRef<[u8]>) -> MltResult<FeatureCollection> {
23 let mut features = Vec::new();
24
25 for layer in read_mvt_layers(data.as_ref())? {
26 for feat in layer.features {
27 let mut properties = feat
28 .properties
29 .into_iter()
30 .map(|(k, v)| Ok((k, Value::try_from(v)?)))
31 .collect::<MltResult<BTreeMap<_, _>>>()?;
32 properties.insert("_layer".into(), Value::String(layer.name.clone()));
33 properties.insert("_extent".into(), Value::Number(layer.extent.get().into()));
34 features.push(Feature {
35 geometry: feat.geometry,
36 z: None,
37 id: feat.id,
38 properties,
39 ty: "Feature".into(),
40 });
41 }
42 }
43
44 Ok(FeatureCollection {
45 features,
46 ty: "FeatureCollection".into(),
47 })
48}
49
50pub fn mvt_to_tile_layers(data: impl AsRef<[u8]>) -> MltResult<Vec<TileLayer>> {
57 MvtReaderRef::new(data.as_ref())?
58 .layers()
59 .map(tile_layer_from_ref)
60 .collect()
61}
62
63fn tile_layer_from_ref(layer: MvtLayerRef<'_>) -> MltResult<TileLayer> {
65 let name = layer.name();
66 if name.is_empty() {
67 return Err(MltError::MissingLayerName);
68 }
69
70 let mut col_names: Vec<String> = Vec::new();
72 let mut col_index: HashMap<&str, usize> = HashMap::new();
73 let mut col_types: Vec<InferredType> = Vec::new();
74 let mut values: Vec<(usize, MvtValueRef<'_>)> = Vec::new();
76 let mut feature_ends: Vec<usize> = Vec::with_capacity(layer.feature_count());
77
78 for feat in layer.features() {
79 for prop in feat.properties() {
80 let (key, value) = prop?;
81 let idx = if let Some(&idx) = col_index.get(key) {
82 idx
83 } else {
84 let idx = col_names.len();
85 col_names.push(key.to_string());
86 col_index.insert(key, idx);
87 col_types.push(InferredType::Unknown);
88 idx
89 };
90 let slot = &mut col_types[idx];
92 *slot = slot.merge(InferredType::from_mvt(value));
93 values.push((idx, value));
94 }
95 feature_ends.push(values.len());
96 }
97
98 for t in &mut col_types {
100 if *t == InferredType::Unknown {
101 *t = InferredType::Str;
102 }
103 }
104
105 let mut tile_features = Vec::with_capacity(layer.feature_count());
107 let mut start = 0;
108 for (feat, &end) in layer.features().zip(&feature_ends) {
109 let mut properties: Vec<PropValue> = col_types.iter().map(|t| t.typed_null()).collect();
111 for &(idx, value) in &values[start..end] {
112 if !matches!(value, MvtValueRef::Null) {
113 properties[idx] = col_types[idx].convert(value.into_owned());
114 }
115 }
116 start = end;
117 tile_features.push(TileFeature {
118 id: feat.id(),
119 geometry: feat.geometry()?,
120 properties,
121 #[cfg(feature = "unstable-v2")]
122 m_values: Vec::new(),
123 #[cfg(feature = "unstable-v2")]
124 nested: Vec::new(),
125 #[cfg(feature = "unstable-v2")]
126 z: Vec::new(),
127 });
128 }
129
130 TileLayer::from_parts(name, layer.extent(), col_names, tile_features)
131}
132
133impl TryFrom<MvtLayer> for TileLayer {
134 type Error = MltError;
135
136 fn try_from(layer: MvtLayer) -> Result<Self, Self::Error> {
137 if layer.name.is_empty() {
138 return Err(MltError::MissingLayerName);
139 }
140
141 let mut col_names: Vec<String> = Vec::new();
143 let mut col_index: HashMap<String, usize> = HashMap::new();
144 let mut col_types: Vec<InferredType> = Vec::new();
145
146 for feat in &layer.features {
147 for (key, val) in &feat.properties {
148 let idx = *col_index.entry(key.clone()).or_insert_with(|| {
149 let i = col_names.len();
150 col_names.push(key.clone());
151 col_types.push(InferredType::Unknown);
152 i
153 });
154 let slot = &mut col_types[idx];
155 *slot = slot.merge(InferredType::from_mvt(as_value_ref(val)));
156 }
157 }
158
159 for t in &mut col_types {
161 if *t == InferredType::Unknown {
162 *t = InferredType::Str;
163 }
164 }
165
166 let mut tile_features = Vec::with_capacity(layer.features.len());
168 for feat in layer.features {
169 let mut properties: Vec<PropValue> = col_types.iter().map(|t| t.typed_null()).collect();
171 for (key, val) in feat.properties {
172 if let Some(&idx) = col_index.get(&key)
173 && !matches!(val, MvtValue::Null)
174 {
175 properties[idx] = col_types[idx].convert(val);
176 }
177 }
178 tile_features.push(TileFeature {
179 id: feat.id,
180 geometry: feat.geometry,
181 properties,
182 #[cfg(feature = "unstable-v2")]
183 m_values: Vec::new(),
184 #[cfg(feature = "unstable-v2")]
185 nested: Vec::new(),
186 #[cfg(feature = "unstable-v2")]
187 z: Vec::new(),
188 });
189 }
190
191 Self::from_parts(layer.name, layer.extent.get(), col_names, tile_features)
192 }
193}
194
195fn as_value_ref(value: &MvtValue) -> MvtValueRef<'_> {
197 match value {
198 MvtValue::String(s) => MvtValueRef::String(s),
199 MvtValue::Float(f) => MvtValueRef::Float(*f),
200 MvtValue::Double(f) => MvtValueRef::Double(*f),
201 MvtValue::Int(i) => MvtValueRef::Int(*i),
202 MvtValue::UInt(u) => MvtValueRef::UInt(*u),
203 MvtValue::SInt(i) => MvtValueRef::SInt(*i),
204 MvtValue::Bool(b) => MvtValueRef::Bool(*b),
205 MvtValue::Null => MvtValueRef::Null,
206 }
207}
208
209#[derive(Debug, Clone, Copy, PartialEq, Eq)]
211enum InferredType {
212 Unknown,
213 Bool,
214 I64,
215 U64,
216 F32,
217 F64,
218 Str,
219}
220
221impl InferredType {
222 fn from_mvt(val: MvtValueRef<'_>) -> Self {
223 match val {
224 MvtValueRef::Bool(_) => Self::Bool,
225 MvtValueRef::Int(_) | MvtValueRef::SInt(_) => Self::I64,
226 MvtValueRef::UInt(_) => Self::U64,
227 MvtValueRef::Float(_) => Self::F32,
228 MvtValueRef::Double(_) => Self::F64,
229 MvtValueRef::String(_) => Self::Str,
230 MvtValueRef::Null => Self::Unknown,
231 }
232 }
233
234 fn merge(self, other: Self) -> Self {
236 if self == Self::Unknown {
237 return other;
238 }
239 if other == Self::Unknown || self == other {
240 return self;
241 }
242 if matches!(
243 (self, other),
244 (Self::I64, Self::U64) | (Self::U64, Self::I64)
245 ) {
246 return Self::I64;
247 }
248 if matches!(
249 (self, other),
250 (Self::F32, Self::F64) | (Self::F64, Self::F32)
251 ) {
252 return Self::F64;
253 }
254 Self::Str
255 }
256
257 fn typed_null(self) -> PropValue {
258 match self {
259 Self::Unknown | Self::Str => PropValue::Str(None),
260 Self::Bool => PropValue::Bool(None),
261 Self::I64 => PropValue::I64(None),
262 Self::U64 => PropValue::U64(None),
263 Self::F32 => PropValue::F32(None),
264 Self::F64 => PropValue::F64(None),
265 }
266 }
267
268 fn convert(self, val: MvtValue) -> PropValue {
270 match (self, val) {
271 (_, MvtValue::Null) => self.typed_null(),
272 (Self::Bool, MvtValue::Bool(b)) => PropValue::Bool(Some(b)),
273 (Self::I64, MvtValue::Int(i) | MvtValue::SInt(i)) => PropValue::I64(Some(i)),
274 (Self::I64, MvtValue::UInt(u)) if i64::try_from(u).is_ok() => {
275 #[expect(clippy::cast_possible_wrap, reason = "checked above")]
277 PropValue::I64(Some(u as i64))
278 }
279 (Self::U64, MvtValue::UInt(u)) => PropValue::U64(Some(u)),
280 (Self::F32, MvtValue::Float(f)) => PropValue::F32(Some(f)),
281 (Self::F64, MvtValue::Double(f)) => PropValue::F64(Some(f)),
282 (Self::F64, MvtValue::Float(f)) => PropValue::F64(Some(f64::from(f))),
283 (_, MvtValue::String(s)) => PropValue::Str(Some(s)),
284 (_, MvtValue::Bool(b)) => PropValue::Str(Some(b.to_string())),
286 (_, MvtValue::Int(i) | MvtValue::SInt(i)) => PropValue::Str(Some(i.to_string())),
287 (_, MvtValue::UInt(u)) => PropValue::Str(Some(u.to_string())),
288 (_, MvtValue::Float(f)) => PropValue::Str(Some(f.to_string())),
289 (_, MvtValue::Double(f)) => PropValue::Str(Some(f.to_string())),
290 }
291 }
292}
293
294#[cfg(test)]
295mod tests {
296 use std::num::NonZeroU32;
297
298 use rstest::rstest;
299
300 use super::*;
301
302 const POINT_AT_1_1: &[u64] = &[9, 2, 2];
303
304 #[rstest]
305 #[case::key_index_past_the_key_list(vec![5, 0], "MVT error: invalid key index 5")]
306 #[case::value_index_past_the_value_list(vec![0, 9], "MVT error: invalid value index 9")]
307 #[case::odd_tag_count(vec![0], "MVT error: invalid feature tags length: 1")]
308 fn malformed_tags_are_reported(#[case] tags: Vec<u32>, #[case] expected: &str) {
309 assert_eq!(
310 mvt_to_tile_layers(mvt_with_tags(&tags))
311 .expect_err("malformed tags must error")
312 .to_string(),
313 expected
314 );
315 }
316
317 #[rstest]
318 #[case::signed_then_unsigned(
319 vec![MvtValue::SInt(-1), MvtValue::UInt(7)],
320 vec![PropValue::I64(Some(-1)), PropValue::I64(Some(7))],
321 )]
322 #[case::unsigned_then_signed(
323 vec![MvtValue::UInt(7), MvtValue::SInt(-1)],
324 vec![PropValue::I64(Some(7)), PropValue::I64(Some(-1))],
325 )]
326 #[case::int_then_sint(
327 vec![MvtValue::Int(i64::MIN), MvtValue::SInt(3)],
328 vec![PropValue::I64(Some(i64::MIN)), PropValue::I64(Some(3))],
329 )]
330 #[case::unsigned_only(
331 vec![MvtValue::UInt(7), MvtValue::UInt(u64::MAX)],
332 vec![PropValue::U64(Some(7)), PropValue::U64(Some(u64::MAX))],
333 )]
334 #[case::float_only(
335 vec![MvtValue::Float(1.5)],
336 vec![PropValue::F32(Some(1.5))],
337 )]
338 #[case::float_then_double(
339 vec![MvtValue::Float(1.5), MvtValue::Double(2.5)],
340 vec![PropValue::F64(Some(1.5)), PropValue::F64(Some(2.5))],
341 )]
342 #[case::double_then_float(
343 vec![MvtValue::Double(2.5), MvtValue::Float(1.5)],
344 vec![PropValue::F64(Some(2.5)), PropValue::F64(Some(1.5))],
345 )]
346 #[case::bool_only(
347 vec![MvtValue::Bool(true), MvtValue::Bool(false)],
348 vec![PropValue::Bool(Some(true)), PropValue::Bool(Some(false))],
349 )]
350 #[case::string_only(
351 vec![MvtValue::String("x".into())],
352 vec![PropValue::Str(Some("x".into()))],
353 )]
354 #[case::bool_then_int(
355 vec![MvtValue::Bool(true), MvtValue::Int(5)],
356 vec![PropValue::Str(Some("true".into())), PropValue::Str(Some("5".into()))],
357 )]
358 #[case::double_then_string(
359 vec![MvtValue::Double(1.5), MvtValue::String("x".into())],
360 vec![PropValue::Str(Some("1.5".into())), PropValue::Str(Some("x".into()))],
361 )]
362 #[case::null_between_bools(
363 vec![MvtValue::Bool(true), MvtValue::Null, MvtValue::Bool(false)],
364 vec![
365 PropValue::Bool(Some(true)),
366 PropValue::Bool(None),
367 PropValue::Bool(Some(false)),
368 ],
369 )]
370 #[case::null_only(
371 vec![MvtValue::Null],
372 vec![PropValue::Str(None)],
373 )]
374 fn a_column_takes_the_type_that_holds_every_value(
375 #[case] values: Vec<MvtValue>,
376 #[case] expected: Vec<PropValue>,
377 ) {
378 assert_eq!(single_column(&values), expected);
379 }
380
381 #[test]
382 fn an_unsigned_value_past_i64_max_clashes_with_its_signed_column() {
383 let data = mvt_with_values(&[MvtValue::SInt(-1), MvtValue::UInt(u64::MAX)]);
384 assert_eq!(
385 mvt_to_tile_layers(&data)
386 .expect_err("the string fallback leaves the column mixed")
387 .to_string(),
388 "property 0 kind mismatch: expected I64, got Str"
389 );
390 }
391
392 #[test]
393 fn missing_properties_become_typed_nulls() {
394 let messages: Vec<Vec<u8>> = [
395 MvtValue::Int(1),
396 MvtValue::UInt(2),
397 MvtValue::Float(1.5),
398 MvtValue::Double(2.5),
399 MvtValue::Bool(true),
400 MvtValue::String("x".into()),
401 ]
402 .iter()
403 .map(value_message)
404 .collect();
405 let every_key: Vec<u32> = (0..6).flat_map(|i| [i, i]).collect();
406 let data = mvt_tile(&[layer_body(
407 b"l",
408 4096,
409 &[
410 b"i".as_slice(),
411 b"u".as_slice(),
412 b"f".as_slice(),
413 b"d".as_slice(),
414 b"b".as_slice(),
415 b"s".as_slice(),
416 ],
417 &messages,
418 &[(None, &every_key, POINT_AT_1_1), (None, &[], POINT_AT_1_1)],
419 )]);
420
421 let layers = both_paths(&data);
422 let [layer] = &layers[..] else {
423 panic!("expected one layer, got {}", layers.len())
424 };
425 assert_eq!(layer.property_names(), ["i", "u", "f", "d", "b", "s"]);
426 assert_eq!(
427 layer.features()[0].properties(),
428 [
429 PropValue::I64(Some(1)),
430 PropValue::U64(Some(2)),
431 PropValue::F32(Some(1.5)),
432 PropValue::F64(Some(2.5)),
433 PropValue::Bool(Some(true)),
434 PropValue::Str(Some("x".into())),
435 ]
436 );
437 assert_eq!(
438 layer.features()[1].properties(),
439 [
440 PropValue::I64(None),
441 PropValue::U64(None),
442 PropValue::F32(None),
443 PropValue::F64(None),
444 PropValue::Bool(None),
445 PropValue::Str(None),
446 ]
447 );
448 }
449
450 #[test]
451 fn column_order_follows_first_use_not_the_key_list() {
452 let messages: Vec<Vec<u8>> = [
453 MvtValue::String("1".into()),
454 MvtValue::String("2".into()),
455 MvtValue::String("3".into()),
456 ]
457 .iter()
458 .map(value_message)
459 .collect();
460 let data = mvt_tile(&[layer_body(
461 b"l",
462 4096,
463 &[b"b".as_slice(), b"a".as_slice(), b"c".as_slice()],
464 &messages,
465 &[
466 (None, &[2, 2, 0, 0], POINT_AT_1_1),
467 (None, &[1, 1], POINT_AT_1_1),
468 ],
469 )]);
470
471 let layers = both_paths(&data);
472 let [layer] = &layers[..] else {
473 panic!("expected one layer, got {}", layers.len())
474 };
475 assert_eq!(layer.property_names(), ["c", "b", "a"]);
476 assert_eq!(
477 layer.features()[0].properties(),
478 [
479 PropValue::Str(Some("3".into())),
480 PropValue::Str(Some("1".into())),
481 PropValue::Str(None),
482 ]
483 );
484 assert_eq!(
485 layer.features()[1].properties(),
486 [
487 PropValue::Str(None),
488 PropValue::Str(None),
489 PropValue::Str(Some("2".into())),
490 ]
491 );
492 }
493
494 #[test]
495 fn each_layer_keeps_its_own_name_extent_and_columns() {
496 let layers = both_paths(&two_layer_tile());
497 assert_eq!(
498 layers
499 .iter()
500 .map(|layer| (
501 layer.name(),
502 layer.extent().get(),
503 layer.property_names().to_vec()
504 ))
505 .collect::<Vec<_>>(),
506 [
507 ("roads", 4096, vec!["n".to_string()]),
508 ("water", 512, vec!["z".to_string()]),
509 ]
510 );
511 assert_eq!(
512 layers[0].features()[0].properties(),
513 [PropValue::Str(Some("a".into()))]
514 );
515 assert_eq!(
516 layers[1].features()[0].properties(),
517 [PropValue::I64(Some(3))]
518 );
519 }
520
521 #[test]
522 fn a_layer_without_features_has_no_columns() {
523 let data = mvt_tile(&[layer_body(b"l", 4096, &[], &[], &[])]);
524 let layers = both_paths(&data);
525 let [layer] = &layers[..] else {
526 panic!("expected one layer, got {}", layers.len())
527 };
528 let no_names: [&str; 0] = [];
529 assert_eq!(layer.property_names(), no_names);
530 assert_eq!(layer.feature_count(), 0);
531 }
532
533 #[test]
534 fn feature_ids_survive_both_conversions() {
535 let data = mvt_tile(&[layer_body(
536 b"l",
537 4096,
538 &[],
539 &[],
540 &[(Some(7), &[], POINT_AT_1_1), (None, &[], POINT_AT_1_1)],
541 )]);
542 let layers = both_paths(&data);
543 assert_eq!(
544 layers[0]
545 .features()
546 .iter()
547 .map(TileFeature::id)
548 .collect::<Vec<_>>(),
549 [Some(7), None]
550 );
551 }
552
553 #[test]
554 fn a_zero_extent_is_rejected() {
555 let data = mvt_tile(&[layer_body(b"l", 0, &[], &[], &[])]);
556 assert_eq!(
557 mvt_to_tile_layers(&data)
558 .expect_err("zero extent must error")
559 .to_string(),
560 "invalid extent: 0"
561 );
562 }
563
564 #[test]
565 fn a_truncated_geometry_is_reported() {
566 let data = mvt_tile(&[layer_body(b"l", 4096, &[], &[], &[(None, &[], &[9])])]);
567 assert_eq!(
568 mvt_to_tile_layers(&data)
569 .expect_err("truncated geometry must error")
570 .to_string(),
571 "MVT error: invalid geometry command stream"
572 );
573 }
574
575 #[test]
576 fn a_layer_without_a_name_is_rejected() {
577 let unnamed = mvt_layer(b"", &[vec![]], b"k", &[]);
578 assert_eq!(
579 mvt_to_tile_layers(&unnamed)
580 .expect_err("borrowed conversion must error")
581 .to_string(),
582 "MVT error: missing required layer name"
583 );
584 assert_eq!(
585 mvt_to_feature_collection(&unnamed)
586 .expect_err("feature collection must error")
587 .to_string(),
588 "MVT error: missing required layer name"
589 );
590
591 let hand_built = MvtLayer {
592 name: String::new(),
593 extent: NonZeroU32::new(4096).expect("non-zero"),
594 features: Vec::new(),
595 };
596 assert_eq!(
597 TileLayer::try_from(hand_built)
598 .expect_err("owned conversion must error")
599 .to_string(),
600 "missing layer name"
601 );
602 }
603
604 #[test]
605 fn unreadable_tile_bytes_are_reported() {
606 assert!(matches!(
607 mvt_to_tile_layers([0xff_u8]),
608 Err(MltError::Mvt(_))
609 ));
610 assert!(matches!(
611 mvt_to_feature_collection([0xff_u8]),
612 Err(MltError::Mvt(_))
613 ));
614 }
615
616 #[test]
617 fn an_empty_tile_holds_no_layers_and_no_features() {
618 assert_eq!(
619 mvt_to_tile_layers([0_u8; 0]).expect("convert"),
620 Vec::<TileLayer>::new()
621 );
622 insta::assert_snapshot!(
623 serde_json::to_string(&mvt_to_feature_collection([0_u8; 0]).expect("convert"))
624 .expect("serialize"),
625 @r#"{"type":"FeatureCollection","features":[]}"#
626 );
627 }
628
629 #[test]
630 fn every_feature_carries_its_own_layer_name_and_extent() {
631 insta::assert_snapshot!(
632 serde_json::to_string(&mvt_to_feature_collection(two_layer_tile()).expect("convert"))
633 .expect("serialize"),
634 @r#"{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"_extent":4096,"_layer":"roads","n":"a"},"geometry":{"type":"Point","coordinates":[1,1]}},{"type":"Feature","properties":{"_extent":512,"_layer":"water","z":3},"geometry":{"type":"Point","coordinates":[1,1]}}]}"#
635 );
636 }
637
638 #[test]
639 fn a_non_finite_double_cannot_become_geojson() {
640 assert_eq!(
641 mvt_to_feature_collection(mvt_with_values(&[MvtValue::Double(f64::NAN)]))
642 .expect_err("NaN must error")
643 .to_string(),
644 "MVT JSON value error: non-finite float cannot be represented as JSON"
645 );
646 }
647
648 fn single_column(values: &[MvtValue]) -> Vec<PropValue> {
650 let data = mvt_with_values(values);
651 let layers = both_paths(&data);
652 let [layer] = &layers[..] else {
653 panic!("expected one layer, got {}", layers.len())
654 };
655 assert_eq!(layer.property_names(), ["k"]);
656 layer
657 .features()
658 .iter()
659 .map(|feat| feat.properties()[0].clone())
660 .collect()
661 }
662
663 fn both_paths(data: &[u8]) -> Vec<TileLayer> {
665 let from_ref = mvt_to_tile_layers(data).expect("borrowed conversion");
666 let owned: Vec<TileLayer> = MvtReaderRef::new(data)
667 .expect("read")
668 .to_tile()
669 .expect("to_tile")
670 .layers
671 .into_iter()
672 .map(TileLayer::try_from)
673 .collect::<MltResult<_>>()
674 .expect("owned conversion");
675 assert_eq!(from_ref, owned);
676 from_ref
677 }
678
679 fn two_layer_tile() -> Vec<u8> {
681 mvt_tile(&[
682 layer_body(
683 b"roads",
684 4096,
685 &[b"n".as_slice()],
686 &[value_message(&MvtValue::String("a".into()))],
687 &[(None, &[0, 0], POINT_AT_1_1)],
688 ),
689 layer_body(
690 b"water",
691 512,
692 &[b"z".as_slice()],
693 &[value_message(&MvtValue::Int(3))],
694 &[(None, &[0, 0], POINT_AT_1_1)],
695 ),
696 ])
697 }
698
699 fn field(number: u32, wire: u32) -> u8 {
700 u8::try_from((number << 3) | wire).expect("small field number")
701 }
702
703 fn varint(mut value: u64, out: &mut Vec<u8>) {
704 loop {
705 let byte = u8::try_from(value & 0x7f).expect("masked");
706 value >>= 7;
707 if value == 0 {
708 out.push(byte);
709 return;
710 }
711 out.push(byte | 0x80);
712 }
713 }
714
715 fn packed(number: u32, values: &[u64], out: &mut Vec<u8>) {
716 let mut body = Vec::new();
717 for value in values {
718 varint(*value, &mut body);
719 }
720 out.push(field(number, 2));
721 varint(u64::try_from(body.len()).expect("small"), out);
722 out.extend(&body);
723 }
724
725 fn bytes(number: u32, body: &[u8], out: &mut Vec<u8>) {
726 out.push(field(number, 2));
727 varint(u64::try_from(body.len()).expect("small"), out);
728 out.extend(body);
729 }
730
731 fn value_message(value: &MvtValue) -> Vec<u8> {
733 let mut out = Vec::new();
734 match value {
735 MvtValue::String(s) => bytes(1, s.as_bytes(), &mut out),
736 MvtValue::Float(f) => {
737 out.push(field(2, 5));
738 out.extend(f.to_le_bytes());
739 }
740 MvtValue::Double(f) => {
741 out.push(field(3, 1));
742 out.extend(f.to_le_bytes());
743 }
744 MvtValue::Int(i) => {
745 out.push(field(4, 0));
746 varint(i.cast_unsigned(), &mut out);
747 }
748 MvtValue::UInt(u) => {
749 out.push(field(5, 0));
750 varint(*u, &mut out);
751 }
752 MvtValue::SInt(i) => {
753 out.push(field(6, 0));
754 varint(zigzag::ZigZag::encode(*i), &mut out);
755 }
756 MvtValue::Bool(b) => {
757 out.push(field(7, 0));
758 varint(u64::from(*b), &mut out);
759 }
760 MvtValue::Null => {}
762 }
763 out
764 }
765
766 fn mvt_with_values(values: &[MvtValue]) -> Vec<u8> {
768 let tags: Vec<Vec<u32>> = (0..values.len())
769 .map(|i| vec![0, u32::try_from(i).expect("small")])
770 .collect();
771 let messages: Vec<Vec<u8>> = values.iter().map(value_message).collect();
772 mvt_layer(b"l", &tags, b"k", &messages)
773 }
774
775 fn mvt_with_tags(tags: &[u32]) -> Vec<u8> {
777 mvt_layer(b"l", &[tags.to_vec()], b"k", &[])
778 }
779
780 fn mvt_layer(
782 name: &[u8],
783 feature_tags: &[Vec<u32>],
784 key: &[u8],
785 values: &[Vec<u8>],
786 ) -> Vec<u8> {
787 let features: Vec<(Option<u64>, &[u32], &[u64])> = feature_tags
788 .iter()
789 .map(|tags| (None, tags.as_slice(), POINT_AT_1_1))
790 .collect();
791 mvt_tile(&[layer_body(name, 4096, &[key], values, &features)])
792 }
793
794 fn mvt_tile(layers: &[Vec<u8>]) -> Vec<u8> {
795 let mut tile = Vec::new();
796 for layer in layers {
797 bytes(3, layer, &mut tile);
798 }
799 tile
800 }
801
802 fn layer_body(
804 name: &[u8],
805 extent: u32,
806 keys: &[&[u8]],
807 values: &[Vec<u8>],
808 features: &[(Option<u64>, &[u32], &[u64])],
809 ) -> Vec<u8> {
810 let mut layer = Vec::new();
811 layer.push(field(15, 0));
812 varint(2, &mut layer); bytes(1, name, &mut layer);
814 for (id, tags, geometry) in features {
815 let mut feat = Vec::new();
816 if let Some(id) = id {
817 feat.push(field(1, 0));
818 varint(*id, &mut feat);
819 }
820 packed(
821 2,
822 &tags.iter().copied().map(u64::from).collect::<Vec<_>>(),
823 &mut feat,
824 );
825 feat.push(field(3, 0));
826 varint(1, &mut feat); packed(4, geometry, &mut feat);
828 bytes(2, &feat, &mut layer);
829 }
830 for key in keys {
831 bytes(3, key, &mut layer);
832 }
833 for value in values {
834 bytes(4, value, &mut layer);
835 }
836 layer.push(field(5, 0));
837 varint(u64::from(extent), &mut layer);
838 layer
839 }
840}