mvt_reader/
lib.rs

1//! # mvt-reader
2//!
3//! `mvt-reader` is a Rust library for decoding and reading Mapbox vector tiles.
4//!
5//! It provides the `Reader` struct, which allows you to read vector tiles and access their layers and features.
6//!
7//! # Usage
8//!
9//! To use the `mvt-reader` library in your Rust project, add the following to your `Cargo.toml` file:
10//!
11//! ```toml
12//! [dependencies]
13//! mvt-reader = "2.1.0-alpha.1"
14//! ```
15//!
16//! Then, you can import and use the library in your code:
17//!
18//! ```rust
19//! use mvt_reader::{Reader, error::{ParserError}};
20//!
21//! fn main() -> Result<(), ParserError> {
22//!   // Read a vector tile from file or data
23//!   let data = vec![/* Vector tile data */];
24//!   let reader = Reader::new(data)?;
25//!
26//!   // Get layer names
27//!   let layer_names = reader.get_layer_names()?;
28//!   for name in layer_names {
29//!     println!("Layer: {}", name);
30//!   }
31//!
32//!   // Get features for a specific layer
33//!   let layer_index = 0;
34//!   let features = reader.get_features(layer_index)?;
35//!   for feature in features {
36//!     todo!()
37//!   }
38//!
39//!   Ok(())
40//! }
41//! ```
42//!
43//! # Features
44//!
45//! The `mvt-reader` library provides the following features:
46//!
47//! - `wasm`: Enables the compilation of the library as a WebAssembly module, allowing usage in JavaScript/TypeScript projects.
48//!
49//! To enable the `wasm` feature, add the following to your `Cargo.toml` file:
50//!
51//! ```toml
52//! [dependencies.mvt-reader]
53//! version = "2.1.0-alpha.1"
54//! features = ["wasm"]
55//! ```
56//!
57//! # License
58//!
59//! This project is licensed under the [MIT License](https://github.com/codeart1st/mvt-reader/blob/main/LICENSE).
60
61pub mod error;
62pub mod feature;
63pub mod layer;
64
65mod vector_tile;
66
67use feature::{Feature, Value};
68use geo_types::{
69  Coord, Geometry, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,
70};
71use layer::Layer;
72use prost::{Message, bytes::Bytes};
73use vector_tile::{Tile, tile::GeomType};
74
75/// The dimension used for the vector tile.
76const DIMENSION: u32 = 2;
77
78/// Reader for decoding and accessing vector tile data.
79pub struct Reader {
80  tile: Tile,
81}
82
83impl Reader {
84  /// Creates a new `Reader` instance with the provided vector tile data.
85  ///
86  /// # Arguments
87  ///
88  /// * `data` - The vector tile data as a byte vector.
89  ///
90  /// # Returns
91  ///
92  /// A result containing the `Reader` instance if successful, or a `DecodeError` if decoding the vector tile data fails.
93  ///
94  /// # Examples
95  ///
96  /// ```
97  /// use mvt_reader::Reader;
98  ///
99  /// let data = vec![/* Vector tile data */];
100  /// let reader = Reader::new(data);
101  /// ```
102  pub fn new(data: Vec<u8>) -> Result<Self, error::ParserError> {
103    match Tile::decode(Bytes::from(data)) {
104      Ok(tile) => Ok(Self { tile }),
105      Err(error) => Err(error::ParserError::new(error::DecodeError::new(Box::new(
106        error,
107      )))),
108    }
109  }
110
111  /// Retrieves the names of the layers in the vector tile.
112  ///
113  /// # Returns
114  ///
115  /// A result containing a vector of layer names if successful, or a `ParserError` if there is an error parsing the tile.
116  ///
117  /// # Examples
118  ///
119  /// ```
120  /// use mvt_reader::Reader;
121  ///
122  /// let data = vec![/* Vector tile data */];
123  /// let reader = Reader::new(data).unwrap();
124  ///
125  /// match reader.get_layer_names() {
126  ///   Ok(layer_names) => {
127  ///     for name in layer_names {
128  ///       println!("Layer: {}", name);
129  ///     }
130  ///   }
131  ///   Err(error) => {
132  ///     todo!();
133  ///   }
134  /// }
135  /// ```
136  pub fn get_layer_names(&self) -> Result<Vec<String>, error::ParserError> {
137    process_layers(&self.tile.layers, |layer, _| layer.name.clone())
138  }
139
140  /// Retrieves metadata about the layers in the vector tile.
141  ///
142  /// # Returns
143  ///
144  /// A result containing a vector of `Layer` structs if successful, or a `ParserError` if there is an error parsing the tile.
145  ///
146  /// # Examples
147  ///
148  /// ```
149  /// use mvt_reader::Reader;
150  ///
151  /// let data = vec![/* Vector tile data */];
152  /// let reader = Reader::new(data).unwrap();
153  ///
154  /// match reader.get_layer_metadata() {
155  ///   Ok(layers) => {
156  ///     for layer in layers {
157  ///       println!("Layer: {}", layer.name);
158  ///       println!("Extent: {}", layer.extent);
159  ///     }
160  ///   }
161  ///   Err(error) => {
162  ///     todo!();
163  ///   }
164  /// }
165  /// ```
166  pub fn get_layer_metadata(&self) -> Result<Vec<Layer>, error::ParserError> {
167    process_layers(&self.tile.layers, |layer, index| Layer {
168      layer_index: index,
169      version: layer.version,
170      name: layer.name.clone(),
171      feature_count: layer.features.len(),
172      extent: layer.extent.unwrap_or(4096),
173    })
174  }
175
176  /// Retrieves the features of a specific layer in the vector tile.
177  ///
178  /// # Arguments
179  ///
180  /// * `layer_index` - The index of the layer.
181  ///
182  /// # Returns
183  ///
184  /// A result containing a vector of features if successful, or a `ParserError` if there is an error parsing the tile or accessing the layer.
185  ///
186  /// # Examples
187  ///
188  /// ```
189  /// use mvt_reader::Reader;
190  ///
191  /// let data = vec![/* Vector tile data */];
192  /// let reader = Reader::new(data).unwrap();
193  ///
194  /// match reader.get_features(0) {
195  ///   Ok(features) => {
196  ///     for feature in features {
197  ///       todo!();
198  ///     }
199  ///   }
200  ///   Err(error) => {
201  ///     todo!();
202  ///   }
203  /// }
204  /// ```
205  pub fn get_features(&self, layer_index: usize) -> Result<Vec<Feature>, error::ParserError> {
206    let layer = self.tile.layers.get(layer_index);
207    match layer {
208      Some(layer) => {
209        let mut features = Vec::with_capacity(layer.features.len());
210        for feature in layer.features.iter() {
211          if let Some(geom_type) = feature.r#type {
212            match GeomType::try_from(geom_type) {
213              Ok(geom_type) => {
214                let parsed_geometry = match parse_geometry(&feature.geometry, geom_type) {
215                  Ok(parsed_geometry) => parsed_geometry,
216                  Err(error) => {
217                    return Err(error);
218                  }
219                };
220
221                let parsed_tags = match parse_tags(&feature.tags, &layer.keys, &layer.values) {
222                  Ok(parsed_tags) => parsed_tags,
223                  Err(error) => {
224                    return Err(error);
225                  }
226                };
227
228                features.push(Feature {
229                  geometry: parsed_geometry,
230                  id: feature.id,
231                  properties: Some(parsed_tags),
232                });
233              }
234              Err(error) => {
235                return Err(error::ParserError::new(error::DecodeError::new(Box::new(
236                  error,
237                ))));
238              }
239            }
240          }
241        }
242        Ok(features)
243      }
244      None => Ok(vec![]),
245    }
246  }
247}
248
249fn process_layers<T, F>(
250  layers: &[vector_tile::tile::Layer],
251  mut processor: F,
252) -> Result<Vec<T>, error::ParserError>
253where
254  F: FnMut(&vector_tile::tile::Layer, usize) -> T,
255{
256  let mut results = Vec::with_capacity(layers.len());
257  for (index, layer) in layers.iter().enumerate() {
258    match layer.version {
259      1 | 2 => results.push(processor(layer, index)),
260      _ => {
261        return Err(error::ParserError::new(error::VersionError::new(
262          layer.name.clone(),
263          layer.version,
264        )));
265      }
266    }
267  }
268  Ok(results)
269}
270
271fn parse_tags(
272  tags: &[u32],
273  keys: &[String],
274  values: &[vector_tile::tile::Value],
275) -> Result<std::collections::HashMap<String, Value>, error::ParserError> {
276  let mut result = std::collections::HashMap::new();
277  for item in tags.chunks(2) {
278    if item.len() != 2
279      || item[0] >= keys.len().try_into().unwrap()
280      || item[1] >= values.len().try_into().unwrap()
281    {
282      return Err(error::ParserError::new(error::TagsError::new()));
283    }
284    result.insert(
285      keys[item[0] as usize].clone(),
286      map_value(values[item[1] as usize].clone()),
287    );
288  }
289  Ok(result)
290}
291
292fn map_value(value: vector_tile::tile::Value) -> Value {
293  if let Some(s) = value.string_value {
294    return Value::String(s);
295  }
296  if let Some(f) = value.float_value {
297    return Value::Float(f);
298  }
299  if let Some(d) = value.double_value {
300    return Value::Double(d);
301  }
302  if let Some(i) = value.int_value {
303    return Value::Int(i);
304  }
305  if let Some(u) = value.uint_value {
306    return Value::UInt(u);
307  }
308  if let Some(s) = value.sint_value {
309    return Value::SInt(s);
310  }
311  if let Some(b) = value.bool_value {
312    return Value::Bool(b);
313  }
314  Value::Null
315}
316
317fn shoelace_formula(points: &[Point<f32>]) -> f32 {
318  let mut area: f32 = 0.0;
319  let n = points.len();
320  let mut v1 = points[n - 1];
321  for v2 in points.iter().take(n) {
322    area += (v2.y() - v1.y()) * (v2.x() + v1.x());
323    v1 = *v2;
324  }
325  area * 0.5
326}
327
328fn parse_geometry(
329  geometry_data: &[u32],
330  geom_type: GeomType,
331) -> Result<Geometry<f32>, error::ParserError> {
332  if geom_type == GeomType::Unknown {
333    return Err(error::ParserError::new(error::GeometryError::new()));
334  }
335
336  // worst case capacity to prevent reallocation. not needed to be exact.
337  let mut coordinates: Vec<Coord<f32>> = Vec::with_capacity(geometry_data.len());
338  let mut polygons: Vec<Polygon<f32>> = Vec::new();
339  let mut linestrings: Vec<LineString<f32>> = Vec::new();
340
341  let mut cursor: [i32; 2] = [0, 0];
342  let mut parameter_count: u32 = 0;
343
344  for value in geometry_data.iter() {
345    if parameter_count == 0 {
346      let command_integer = value;
347      let id = (command_integer & 0x7) as u8;
348      match id {
349        1 => {
350          // MoveTo
351          parameter_count = (command_integer >> 3) * DIMENSION;
352          if geom_type == GeomType::Linestring && !coordinates.is_empty() {
353            linestrings.push(LineString::new(coordinates));
354            // start with a new linestring
355            coordinates = Vec::with_capacity(geometry_data.len());
356          }
357        }
358        2 => {
359          // LineTo
360          parameter_count = (command_integer >> 3) * DIMENSION;
361        }
362        7 => {
363          // ClosePath
364          let first_coordinate = match coordinates.first() {
365            Some(coord) => coord.to_owned(),
366            None => {
367              return Err(error::ParserError::new(error::GeometryError::new()));
368            }
369          };
370          coordinates.push(first_coordinate);
371
372          let ring = LineString::new(coordinates);
373
374          let area = shoelace_formula(&ring.clone().into_points());
375
376          if area > 0.0 {
377            // exterior ring
378            if !linestrings.is_empty() {
379              // finish previous geometry
380              polygons.push(Polygon::new(
381                linestrings[0].clone(),
382                linestrings[1..].into(),
383              ));
384              linestrings = Vec::new();
385            }
386          }
387
388          linestrings.push(ring);
389          // start a new sequence
390          coordinates = Vec::with_capacity(geometry_data.len());
391        }
392        _ => (),
393      }
394    } else {
395      let parameter_integer = value;
396      let integer_value = ((parameter_integer >> 1) as i32) ^ -((parameter_integer & 1) as i32);
397      if parameter_count % DIMENSION == 0 {
398        cursor[0] = match cursor[0].checked_add(integer_value) {
399          Some(result) => result,
400          None => i32::MAX, // clip value
401        };
402      } else {
403        cursor[1] = match cursor[1].checked_add(integer_value) {
404          Some(result) => result,
405          None => i32::MAX, // clip value
406        };
407        coordinates.push(Coord {
408          x: cursor[0] as f32,
409          y: cursor[1] as f32,
410        });
411      }
412      parameter_count -= 1;
413    }
414  }
415
416  match geom_type {
417    GeomType::Linestring => {
418      // the last linestring is in coordinates vec
419      if !linestrings.is_empty() {
420        linestrings.push(LineString::new(coordinates));
421        return Ok(MultiLineString::new(linestrings).into());
422      }
423      Ok(LineString::new(coordinates).into())
424    }
425    GeomType::Point => Ok(
426      MultiPoint(
427        coordinates
428          .iter()
429          .map(|coord| Point::new(coord.x, coord.y))
430          .collect(),
431      )
432      .into(),
433    ),
434    GeomType::Polygon => {
435      if !linestrings.is_empty() {
436        // finish pending polygon
437        polygons.push(Polygon::new(
438          linestrings[0].clone(),
439          linestrings[1..].into(),
440        ));
441        return Ok(MultiPolygon::new(polygons).into());
442      }
443      match polygons.first() {
444        Some(polygon) => Ok(polygon.to_owned().into()),
445        None => Err(error::ParserError::new(error::GeometryError::new())),
446      }
447    }
448    GeomType::Unknown => Err(error::ParserError::new(error::GeometryError::new())),
449  }
450}
451
452#[cfg(feature = "wasm")]
453pub mod wasm {
454
455  use crate::feature::Value;
456  use geojson::{Feature, GeoJson, JsonObject, JsonValue, feature::Id};
457  use serde::ser::{Serialize, SerializeStruct};
458  use serde_wasm_bindgen::Serializer;
459  use wasm_bindgen::prelude::*;
460
461  /// Converts a `Value` into a `serde_json::Value`.
462  impl From<Value> for JsonValue {
463    fn from(value: Value) -> Self {
464      match value {
465        Value::Null => JsonValue::Null,
466        Value::Bool(b) => JsonValue::from(b),
467        Value::Int(i) => JsonValue::from(i),
468        Value::UInt(u) => JsonValue::from(u),
469        Value::SInt(s) => JsonValue::from(s),
470        Value::Float(f) => JsonValue::from(f),
471        Value::Double(d) => JsonValue::from(d),
472        Value::String(s) => JsonValue::from(s),
473      }
474    }
475  }
476
477  /// Converts a `super::feature::Feature` into a `wasm_bindgen::JsValue`.
478  impl From<super::feature::Feature> for wasm_bindgen::JsValue {
479    fn from(feature: super::feature::Feature) -> Self {
480      let properties: Option<JsonObject> = feature.properties.as_ref().map(|props| {
481        props
482          .clone()
483          .into_iter()
484          .map(|(k, v)| (k, v.into()))
485          .collect()
486      });
487
488      let geojson = GeoJson::Feature(Feature {
489        bbox: None,
490        geometry: Some(feature.get_geometry().into()),
491        id: feature.id.map(|id| Id::Number(id.into())),
492        properties,
493        foreign_members: None,
494      });
495
496      geojson.serialize(&Serializer::json_compatible()).unwrap()
497    }
498  }
499
500  /// Converts a `super::layer::Layer` into a `wasm_bindgen::JsValue`.
501  impl From<super::layer::Layer> for wasm_bindgen::JsValue {
502    fn from(layer: super::layer::Layer) -> Self {
503      layer.serialize(&Serializer::json_compatible()).unwrap()
504    }
505  }
506
507  impl Serialize for super::layer::Layer {
508    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
509    where
510      S: serde::ser::Serializer,
511    {
512      let mut state = serializer.serialize_struct("Layer", 5)?;
513      state.serialize_field("layer_index", &self.layer_index)?;
514      state.serialize_field("version", &self.version)?;
515      state.serialize_field("name", &self.name)?;
516      state.serialize_field("feature_count", &self.feature_count)?;
517      state.serialize_field("extent", &self.extent)?;
518      state.end()
519    }
520  }
521
522  /// Reader for decoding and accessing vector tile data in WebAssembly.
523  #[wasm_bindgen]
524  pub struct Reader {
525    reader: Option<super::Reader>,
526  }
527
528  #[wasm_bindgen]
529  impl Reader {
530    /// Creates a new `Reader` instance with the provided vector tile data.
531    ///
532    /// # Arguments
533    ///
534    /// * `data` - The vector tile data as a `Vec<u8>`.
535    /// * `error_callback` - An optional JavaScript callback function to handle errors. It should accept a single parameter which will contain the error message as a string.
536    ///
537    /// # Examples
538    ///
539    /// ```
540    /// let tileData = getVectorTileData();
541    /// let reader = new Reader(tileData, handleErrors);
542    /// ```
543    #[wasm_bindgen(constructor)]
544    pub fn new(data: Vec<u8>, error_callback: Option<js_sys::Function>) -> Reader {
545      let reader = match super::Reader::new(data) {
546        Ok(reader) => Some(reader),
547        Err(error) => {
548          if let Some(callback) = error_callback {
549            callback
550              .call1(&JsValue::NULL, &JsValue::from_str(&format!("{:?}", error)))
551              .unwrap();
552          }
553          None
554        }
555      };
556      Reader { reader }
557    }
558
559    /// Retrieves the layer names present in the vector tile.
560    ///
561    /// # Arguments
562    ///
563    /// * `error_callback` - An optional JavaScript callback function to handle errors. It should accept a single parameter which will contain the error message as a string.
564    ///
565    /// # Returns
566    ///
567    /// A JavaScript array containing the layer names as strings.
568    ///
569    /// # Examples
570    ///
571    /// ```
572    /// let layerNames = reader.getLayerNames(handleErrors);
573    /// for (let i = 0; i < layerNames.length; i++) {
574    ///   console.log(layerNames[i]);
575    /// }
576    /// ```
577    #[wasm_bindgen(js_name = getLayerNames)]
578    pub fn get_layer_names(&self, error_callback: Option<js_sys::Function>) -> JsValue {
579      self.handle_result(|reader| reader.get_layer_names(), error_callback)
580    }
581
582    /// Retrieves the layer metadata present in the vector tile.
583    ///
584    /// # Arguments
585    ///
586    /// * `error_callback` - An optional JavaScript callback function to handle errors. It should accept a single parameter which will contain the error message as a string.
587    ///
588    /// # Returns
589    ///
590    /// A JavaScript array containing the layer metadata as objects.
591    ///
592    /// # Examples
593    ///
594    /// ```
595    /// let layers = reader.getLayerMetadata(handleErrors);
596    /// for (let i = 0; i < layers.length; i++) {
597    ///   console.log(layers[i].name);
598    /// }
599    /// ```
600    #[wasm_bindgen(js_name = getLayerMetadata)]
601    pub fn get_layer_metadata(&self, error_callback: Option<js_sys::Function>) -> JsValue {
602      self.handle_result(|reader| reader.get_layer_metadata(), error_callback)
603    }
604
605    /// Retrieves the features of a specific layer in the vector tile.
606    ///
607    /// # Arguments
608    ///
609    /// * `layer_index` - The index of the layer to retrieve features from.
610    /// * `error_callback` - An optional JavaScript callback function to handle errors. It should accept a single parameter which will contain the error message as a string.
611    ///
612    /// # Returns
613    ///
614    /// A JavaScript array containing the features as GeoJSON objects.
615    ///
616    /// # Examples
617    ///
618    /// ```
619    /// let features = reader.getFeatures(0, handleErrors);
620    /// for (let i = 0; i < features.length; i++) {
621    ///   console.log(features[i]);
622    /// }
623    /// ```
624    #[wasm_bindgen(js_name = getFeatures)]
625    pub fn get_features(
626      &self,
627      layer_index: usize,
628      error_callback: Option<js_sys::Function>,
629    ) -> JsValue {
630      self.handle_result(|reader| reader.get_features(layer_index), error_callback)
631    }
632
633    fn handle_result<T, F>(&self, operation: F, error_callback: Option<js_sys::Function>) -> JsValue
634    where
635      T: IntoIterator,
636      T::Item: Into<JsValue>,
637      F: FnOnce(&super::Reader) -> Result<T, super::error::ParserError>,
638    {
639      match &self.reader {
640        Some(reader) => match operation(reader) {
641          Ok(result) => result
642            .into_iter()
643            .map(Into::into)
644            .collect::<js_sys::Array>()
645            .into(),
646          Err(error) => {
647            if let Some(callback) = error_callback {
648              callback
649                .call1(&JsValue::NULL, &JsValue::from_str(&format!("{:?}", error)))
650                .unwrap();
651            }
652            JsValue::NULL
653          }
654        },
655        None => JsValue::NULL,
656      }
657    }
658  }
659}