Skip to main content

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