Skip to main content

fast_mvt/reader/
tile.rs

1use std::fmt;
2
3use buffa::MessageView as _;
4
5use super::MvtLayerRef;
6use crate::generated::vector_tile::{Tile, TileView};
7use crate::types::DEFAULT_EXTENT;
8use crate::{MvtError, MvtResult, MvtTile};
9
10impl Tile {
11    pub fn from_reader(reader: &MvtReaderRef<'_>) -> MvtResult<Self> {
12        let mut tile = reader.to_proto()?;
13        for layer in &mut tile.layers {
14            layer.extent.get_or_insert(DEFAULT_EXTENT.get());
15        }
16        Ok(tile)
17    }
18}
19
20#[derive(Clone)]
21pub struct MvtReaderRef<'a>(TileView<'a>);
22
23impl<'a> MvtReaderRef<'a> {
24    pub fn new(data: &'a [u8]) -> MvtResult<Self> {
25        let tile = TileView::decode_view(data)?;
26        for layer in &tile.layers {
27            if layer.name.is_empty() {
28                return Err(MvtError::MissingLayerName);
29            }
30            if layer.version < 1 || layer.version > 3 {
31                return Err(MvtError::UnsupportedVersion {
32                    layer: layer.name.to_string(),
33                    version: layer.version,
34                });
35            }
36        }
37        Ok(Self(tile))
38    }
39
40    #[must_use]
41    pub fn layers(&self) -> impl ExactSizeIterator<Item = MvtLayerRef<'_>> {
42        self.0.layers.iter().map(MvtLayerRef::new)
43    }
44
45    #[must_use]
46    pub fn layer_count(&self) -> usize {
47        self.0.layers.len()
48    }
49
50    pub fn to_tile(&self) -> MvtResult<MvtTile> {
51        let mut layers = Vec::with_capacity(self.0.layers.len());
52        for layer in self.layers() {
53            layers.push(layer.to_layer()?);
54        }
55        Ok(MvtTile { layers })
56    }
57
58    pub fn to_proto(&self) -> MvtResult<Tile> {
59        Ok(self.0.to_owned_message()?)
60    }
61}
62
63impl fmt::Debug for MvtReaderRef<'_> {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        for (index, layer) in self.layers().enumerate() {
66            if index > 0 {
67                writeln!(
68                    f,
69                    "============================================================="
70                )?;
71            }
72            writeln!(f, "layer: {index}")?;
73            // Each layer's `Debug` block is already newline-terminated.
74            write!(f, "{layer:?}")?;
75        }
76        Ok(())
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use geo_types::Geometry;
83
84    use super::*;
85    use crate::MvtValue;
86    use crate::generated::vector_tile::tile as proto_tile;
87    use crate::reader::MvtValueRef;
88    use crate::reader::tests::{encode_layer, first_feature};
89
90    #[test]
91    fn borrowed_api_reads_accessors_properties_and_repeated_points() {
92        let layer = proto_tile::Layer {
93            version: 3,
94            name: "places".to_string(),
95            keys: vec![
96                "string".into(),
97                "float".into(),
98                "double".into(),
99                "int".into(),
100                "uint".into(),
101                "sint".into(),
102                "bool".into(),
103                "null".into(),
104            ],
105            values: vec![
106                proto_tile::Value {
107                    string_value: Some("name".into()),
108                    ..Default::default()
109                },
110                proto_tile::Value {
111                    float_value: Some(1.25),
112                    ..Default::default()
113                },
114                proto_tile::Value {
115                    double_value: Some(2.5),
116                    ..Default::default()
117                },
118                proto_tile::Value {
119                    int_value: Some(-3),
120                    ..Default::default()
121                },
122                proto_tile::Value {
123                    uint_value: Some(4),
124                    ..Default::default()
125                },
126                proto_tile::Value {
127                    sint_value: Some(-5),
128                    ..Default::default()
129                },
130                proto_tile::Value {
131                    bool_value: Some(true),
132                    ..Default::default()
133                },
134                proto_tile::Value::default(),
135            ],
136            features: vec![proto_tile::Feature {
137                id: Some(7),
138                tags: (0_u32..8).flat_map(|idx| [idx, idx]).collect(),
139                r#type: Some(proto_tile::GeomType::Point),
140                geometry: vec![9, 2, 4, 9, 6, 8],
141            }],
142            ..Default::default()
143        };
144        let bytes = encode_layer(layer);
145        let reader = MvtReaderRef::new(&bytes).unwrap();
146        let layer = reader.layers().next().unwrap();
147
148        assert_eq!(reader.layer_count(), 1);
149        assert_eq!(layer.name(), "places");
150        assert_eq!(layer.version(), 3);
151        assert_eq!(layer.extent(), DEFAULT_EXTENT.get());
152        assert_eq!(layer.feature_count(), 1);
153        assert!(!layer.is_empty());
154        assert_eq!(
155            layer.keys(),
156            [
157                "string", "float", "double", "int", "uint", "sint", "bool", "null"
158            ]
159        );
160        assert_eq!(layer.values().len(), 8);
161
162        let feature = layer.features().next().unwrap();
163        assert_eq!(feature.id(), Some(7));
164        assert!(feature.has_properties());
165        assert_eq!(
166            feature.tags(),
167            [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7]
168        );
169        assert_eq!(feature.geometry_commands(), [9, 2, 4, 9, 6, 8]);
170        assert_eq!(feature.geom_type(), Some(proto_tile::GeomType::Point));
171        assert_eq!(feature.geom_type_value(), Some(1));
172
173        let properties = feature.properties_vec().unwrap();
174        assert_eq!(properties[0].1, MvtValueRef::String("name"));
175        assert_eq!(properties[1].1, MvtValueRef::Float(1.25));
176        assert_eq!(properties[2].1, MvtValueRef::Double(2.5));
177        assert_eq!(properties[3].1, MvtValueRef::Int(-3));
178        assert_eq!(properties[4].1, MvtValueRef::UInt(4));
179        assert_eq!(properties[5].1, MvtValueRef::SInt(-5));
180        assert_eq!(properties[6].1, MvtValueRef::Bool(true));
181        assert_eq!(properties[7].1, MvtValueRef::Null);
182        assert_eq!(
183            properties[0].1.into_owned(),
184            MvtValue::String("name".into())
185        );
186        assert_eq!(properties[7].1.into_owned(), MvtValue::Null);
187
188        let Geometry::MultiPoint(points) = feature.geometry().unwrap() else {
189            panic!("expected multipoint");
190        };
191        assert_eq!(points.0.len(), 2);
192        assert!(matches!(
193            feature.geometry().unwrap(),
194            Geometry::MultiPoint(_)
195        ));
196    }
197
198    #[test]
199    fn invalid_versions_and_geometry_types_are_errors() {
200        let layer = proto_tile::Layer {
201            version: 4,
202            name: "bad".into(),
203            ..Default::default()
204        };
205        let bytes = encode_layer(layer);
206        assert!(matches!(
207            MvtReaderRef::new(&bytes),
208            Err(MvtError::UnsupportedVersion { version: 4, .. })
209        ));
210
211        let feature = proto_tile::Feature {
212            r#type: Some(proto_tile::GeomType::Unknown),
213            geometry: vec![9, 0, 0],
214            ..Default::default()
215        };
216        let mut layer = proto_tile::Layer {
217            version: 2,
218            name: "geometry".into(),
219            ..Default::default()
220        };
221        layer.features = vec![feature];
222        let bytes = encode_layer(layer);
223        let reader = MvtReaderRef::new(&bytes).unwrap();
224        let feature = first_feature(&reader);
225        assert!(matches!(feature.geometry(), Err(MvtError::InvalidGeometry)));
226    }
227
228    #[test]
229    fn debug_separates_multiple_layers() {
230        use buffa::Message as _;
231
232        let layer = |name: &str| proto_tile::Layer {
233            version: 2,
234            name: name.into(),
235            ..Default::default()
236        };
237        let bytes = Tile {
238            layers: vec![layer("a"), layer("b")],
239        }
240        .encode_to_vec();
241        let reader = MvtReaderRef::new(&bytes).unwrap();
242        insta::assert_debug_snapshot!(reader, @"
243        layer: 0
244          name: a
245          version: 2
246          extent: 4096
247        =============================================================
248        layer: 1
249          name: b
250          version: 2
251          extent: 4096
252        ");
253    }
254}