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 id: feat.id,
37 properties,
38 ty: "Feature".into(),
39 });
40 }
41 }
42
43 Ok(FeatureCollection {
44 features,
45 ty: "FeatureCollection".into(),
46 })
47}
48
49pub fn mvt_to_tile_layers(data: impl AsRef<[u8]>) -> MltResult<Vec<TileLayer>> {
56 MvtReaderRef::new(data.as_ref())?
57 .layers()
58 .map(tile_layer_from_ref)
59 .collect()
60}
61
62fn tile_layer_from_ref(layer: MvtLayerRef<'_>) -> MltResult<TileLayer> {
64 let name = layer.name();
65 if name.is_empty() {
66 return Err(MltError::MissingLayerName);
67 }
68
69 let mut col_names: Vec<String> = Vec::new();
71 let mut col_index: HashMap<&str, usize> = HashMap::new();
72 let mut col_types: Vec<InferredType> = Vec::new();
73 let mut values: Vec<(usize, MvtValueRef<'_>)> = Vec::new();
75 let mut feature_ends: Vec<usize> = Vec::with_capacity(layer.feature_count());
76
77 for feat in layer.features() {
78 for prop in feat.properties() {
79 let (key, value) = prop?;
80 let idx = if let Some(&idx) = col_index.get(key) {
81 idx
82 } else {
83 let idx = col_names.len();
84 col_names.push(key.to_string());
85 col_index.insert(key, idx);
86 col_types.push(InferredType::Unknown);
87 idx
88 };
89 let slot = &mut col_types[idx];
91 *slot = slot.merge(InferredType::from_mvt(value));
92 values.push((idx, value));
93 }
94 feature_ends.push(values.len());
95 }
96
97 for t in &mut col_types {
99 if *t == InferredType::Unknown {
100 *t = InferredType::Str;
101 }
102 }
103
104 let mut tile_features = Vec::with_capacity(layer.feature_count());
106 let mut start = 0;
107 for (feat, &end) in layer.features().zip(&feature_ends) {
108 let mut properties: Vec<PropValue> = col_types.iter().map(|t| t.typed_null()).collect();
110 for &(idx, value) in &values[start..end] {
111 if !matches!(value, MvtValueRef::Null) {
112 properties[idx] = col_types[idx].convert(value.into_owned());
113 }
114 }
115 start = end;
116 tile_features.push(TileFeature {
117 id: feat.id(),
118 geometry: feat.geometry()?,
119 properties,
120 #[cfg(feature = "unstable-v2")]
121 m_values: Vec::new(),
122 #[cfg(feature = "unstable-v2")]
123 nested: Vec::new(),
124 });
125 }
126
127 TileLayer::from_parts(name, layer.extent(), col_names, tile_features)
128}
129
130impl TryFrom<MvtLayer> for TileLayer {
131 type Error = MltError;
132
133 fn try_from(layer: MvtLayer) -> Result<Self, Self::Error> {
134 if layer.name.is_empty() {
135 return Err(MltError::MissingLayerName);
136 }
137
138 let mut col_names: Vec<String> = Vec::new();
140 let mut col_index: HashMap<String, usize> = HashMap::new();
141 let mut col_types: Vec<InferredType> = Vec::new();
142
143 for feat in &layer.features {
144 for (key, val) in &feat.properties {
145 let idx = *col_index.entry(key.clone()).or_insert_with(|| {
146 let i = col_names.len();
147 col_names.push(key.clone());
148 col_types.push(InferredType::Unknown);
149 i
150 });
151 let slot = &mut col_types[idx];
152 *slot = slot.merge(InferredType::from_mvt(as_value_ref(val)));
153 }
154 }
155
156 for t in &mut col_types {
158 if *t == InferredType::Unknown {
159 *t = InferredType::Str;
160 }
161 }
162
163 let mut tile_features = Vec::with_capacity(layer.features.len());
165 for feat in layer.features {
166 let mut properties: Vec<PropValue> = col_types.iter().map(|t| t.typed_null()).collect();
168 for (key, val) in feat.properties {
169 if let Some(&idx) = col_index.get(&key)
170 && !matches!(val, MvtValue::Null)
171 {
172 properties[idx] = col_types[idx].convert(val);
173 }
174 }
175 tile_features.push(TileFeature {
176 id: feat.id,
177 geometry: feat.geometry,
178 properties,
179 #[cfg(feature = "unstable-v2")]
180 m_values: Vec::new(),
181 #[cfg(feature = "unstable-v2")]
182 nested: Vec::new(),
183 });
184 }
185
186 Self::from_parts(layer.name, layer.extent.get(), col_names, tile_features)
187 }
188}
189
190fn as_value_ref(value: &MvtValue) -> MvtValueRef<'_> {
192 match value {
193 MvtValue::String(s) => MvtValueRef::String(s),
194 MvtValue::Float(f) => MvtValueRef::Float(*f),
195 MvtValue::Double(f) => MvtValueRef::Double(*f),
196 MvtValue::Int(i) => MvtValueRef::Int(*i),
197 MvtValue::UInt(u) => MvtValueRef::UInt(*u),
198 MvtValue::SInt(i) => MvtValueRef::SInt(*i),
199 MvtValue::Bool(b) => MvtValueRef::Bool(*b),
200 MvtValue::Null => MvtValueRef::Null,
201 }
202}
203
204#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206enum InferredType {
207 Unknown,
208 Bool,
209 I64,
210 U64,
211 F32,
212 F64,
213 Str,
214}
215
216impl InferredType {
217 fn from_mvt(val: MvtValueRef<'_>) -> Self {
218 match val {
219 MvtValueRef::Bool(_) => Self::Bool,
220 MvtValueRef::Int(_) | MvtValueRef::SInt(_) => Self::I64,
221 MvtValueRef::UInt(_) => Self::U64,
222 MvtValueRef::Float(_) => Self::F32,
223 MvtValueRef::Double(_) => Self::F64,
224 MvtValueRef::String(_) => Self::Str,
225 MvtValueRef::Null => Self::Unknown,
226 }
227 }
228
229 fn merge(self, other: Self) -> Self {
231 if self == Self::Unknown {
232 return other;
233 }
234 if other == Self::Unknown || self == other {
235 return self;
236 }
237 if matches!(
238 (self, other),
239 (Self::I64, Self::U64) | (Self::U64, Self::I64)
240 ) {
241 return Self::I64;
242 }
243 if matches!(
244 (self, other),
245 (Self::F32, Self::F64) | (Self::F64, Self::F32)
246 ) {
247 return Self::F64;
248 }
249 Self::Str
250 }
251
252 fn typed_null(self) -> PropValue {
253 match self {
254 Self::Unknown | Self::Str => PropValue::Str(None),
255 Self::Bool => PropValue::Bool(None),
256 Self::I64 => PropValue::I64(None),
257 Self::U64 => PropValue::U64(None),
258 Self::F32 => PropValue::F32(None),
259 Self::F64 => PropValue::F64(None),
260 }
261 }
262
263 fn convert(self, val: MvtValue) -> PropValue {
265 match (self, val) {
266 (_, MvtValue::Null) => self.typed_null(),
267 (Self::Bool, MvtValue::Bool(b)) => PropValue::Bool(Some(b)),
268 (Self::I64, MvtValue::Int(i) | MvtValue::SInt(i)) => PropValue::I64(Some(i)),
269 (Self::I64, MvtValue::UInt(u)) if i64::try_from(u).is_ok() => {
270 #[expect(clippy::cast_possible_wrap, reason = "checked above")]
272 PropValue::I64(Some(u as i64))
273 }
274 (Self::U64, MvtValue::UInt(u)) => PropValue::U64(Some(u)),
275 (Self::F32, MvtValue::Float(f)) => PropValue::F32(Some(f)),
276 (Self::F64, MvtValue::Double(f)) => PropValue::F64(Some(f)),
277 (Self::F64, MvtValue::Float(f)) => PropValue::F64(Some(f64::from(f))),
278 (_, MvtValue::String(s)) => PropValue::Str(Some(s)),
279 (_, v) => PropValue::Str(Some(format!("{v:?}"))),
281 }
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288
289 #[test]
290 fn malformed_tags_are_reported() {
291 for (tags, expected) in [
292 (&[5, 0][..], "invalid key index 5"),
293 (&[0, 9][..], "invalid value index 9"),
294 (&[0][..], "invalid feature tags length: 1"),
295 ] {
296 let err = mvt_to_tile_layers(mvt_with_tags(tags))
297 .expect_err("malformed tags must error")
298 .to_string();
299 assert!(err.contains(expected), "got {err:?}, wanted {expected:?}");
300 }
301 }
302
303 fn mvt_with_tags(tags: &[u32]) -> Vec<u8> {
305 fn field(number: u32, wire: u32) -> u8 {
306 u8::try_from((number << 3) | wire).expect("small field number")
307 }
308 fn varint(mut value: u64, out: &mut Vec<u8>) {
309 loop {
310 let byte = u8::try_from(value & 0x7f).expect("masked");
311 value >>= 7;
312 if value == 0 {
313 out.push(byte);
314 return;
315 }
316 out.push(byte | 0x80);
317 }
318 }
319 fn packed(number: u32, values: &[u64], out: &mut Vec<u8>) {
320 let mut body = Vec::new();
321 for value in values {
322 varint(*value, &mut body);
323 }
324 out.push(field(number, 2));
325 varint(u64::try_from(body.len()).expect("small"), out);
326 out.extend(&body);
327 }
328 fn bytes(number: u32, body: &[u8], out: &mut Vec<u8>) {
329 out.push(field(number, 2));
330 varint(u64::try_from(body.len()).expect("small"), out);
331 out.extend(body);
332 }
333
334 let mut feat = Vec::new();
335 packed(
336 2,
337 &tags.iter().copied().map(u64::from).collect::<Vec<_>>(),
338 &mut feat,
339 );
340 feat.push(field(3, 0));
341 varint(1, &mut feat); packed(4, &[9, 2, 2], &mut feat); let mut layer = Vec::new();
345 layer.push(field(15, 0));
346 varint(2, &mut layer); bytes(1, b"l", &mut layer); bytes(2, &feat, &mut layer); bytes(3, b"k", &mut layer); layer.push(field(5, 0));
351 varint(4096, &mut layer); let mut tile = Vec::new();
354 bytes(3, &layer, &mut tile);
355 tile
356 }
357}