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 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}