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