|
60 | 60 |
|
61 | 61 | pub mod error;
|
62 | 62 | pub mod feature;
|
| 63 | +pub mod layer; |
63 | 64 |
|
64 | 65 | mod vector_tile;
|
65 | 66 |
|
66 | 67 | use feature::{Feature, Value};
|
67 | 68 | use geo_types::{
|
68 | 69 | Coord, Geometry, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,
|
69 | 70 | };
|
| 71 | +use layer::Layer; |
70 | 72 | use prost::{Message, bytes::Bytes};
|
71 | 73 | use vector_tile::{Tile, tile::GeomType};
|
72 | 74 |
|
@@ -132,21 +134,43 @@ impl Reader {
|
132 | 134 | /// }
|
133 | 135 | /// ```
|
134 | 136 | pub fn get_layer_names(&self) -> Result<Vec<String>, error::ParserError> {
|
135 |
| - let mut layer_names = Vec::with_capacity(self.tile.layers.len()); |
136 |
| - for layer in self.tile.layers.iter() { |
137 |
| - match layer.version { |
138 |
| - 1 | 2 => { |
139 |
| - layer_names.push(layer.name.clone()); |
140 |
| - } |
141 |
| - _ => { |
142 |
| - return Err(error::ParserError::new(error::VersionError::new( |
143 |
| - layer.name.clone(), |
144 |
| - layer.version, |
145 |
| - ))); |
146 |
| - } |
147 |
| - } |
148 |
| - } |
149 |
| - Ok(layer_names) |
| 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 | + }) |
150 | 174 | }
|
151 | 175 |
|
152 | 176 | /// Retrieves the features of a specific layer in the vector tile.
|
@@ -222,6 +246,28 @@ impl Reader {
|
222 | 246 | }
|
223 | 247 | }
|
224 | 248 |
|
| 249 | +fn process_layers<T, F>( |
| 250 | + layers: &[vector_tile::tile::Layer], |
| 251 | + mut processor: F, |
| 252 | +) -> Result<Vec<T>, error::ParserError> |
| 253 | +where |
| 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 | + |
225 | 271 | fn parse_tags(
|
226 | 272 | tags: &[u32],
|
227 | 273 | keys: &[String],
|
@@ -408,10 +454,11 @@ pub mod wasm {
|
408 | 454 |
|
409 | 455 | use crate::feature::Value;
|
410 | 456 | use geojson::{Feature, GeoJson, JsonObject, JsonValue, feature::Id};
|
411 |
| - use serde::Serialize; |
| 457 | + use serde::ser::{Serialize, SerializeStruct}; |
412 | 458 | use serde_wasm_bindgen::Serializer;
|
413 | 459 | use wasm_bindgen::prelude::*;
|
414 | 460 |
|
| 461 | + /// Converts a `Value` into a `serde_json::Value`. |
415 | 462 | impl From<Value> for JsonValue {
|
416 | 463 | fn from(value: Value) -> Self {
|
417 | 464 | match value {
|
@@ -450,6 +497,28 @@ pub mod wasm {
|
450 | 497 | }
|
451 | 498 | }
|
452 | 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 | + |
453 | 522 | /// Reader for decoding and accessing vector tile data in WebAssembly.
|
454 | 523 | #[wasm_bindgen]
|
455 | 524 | pub struct Reader {
|
@@ -507,25 +576,30 @@ pub mod wasm {
|
507 | 576 | /// ```
|
508 | 577 | #[wasm_bindgen(js_name = getLayerNames)]
|
509 | 578 | pub fn get_layer_names(&self, error_callback: Option<js_sys::Function>) -> JsValue {
|
510 |
| - match &self.reader { |
511 |
| - Some(reader) => match reader.get_layer_names() { |
512 |
| - Ok(layer_names) => JsValue::from( |
513 |
| - layer_names |
514 |
| - .into_iter() |
515 |
| - .map(JsValue::from) |
516 |
| - .collect::<js_sys::Array>(), |
517 |
| - ), |
518 |
| - Err(error) => { |
519 |
| - if let Some(callback) = error_callback { |
520 |
| - callback |
521 |
| - .call1(&JsValue::NULL, &JsValue::from_str(&format!("{:?}", error))) |
522 |
| - .unwrap(); |
523 |
| - } |
524 |
| - JsValue::NULL |
525 |
| - } |
526 |
| - }, |
527 |
| - None => JsValue::NULL, |
528 |
| - } |
| 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) |
529 | 603 | }
|
530 | 604 |
|
531 | 605 | /// Retrieves the features of a specific layer in the vector tile.
|
@@ -553,14 +627,22 @@ pub mod wasm {
|
553 | 627 | layer_index: usize,
|
554 | 628 | error_callback: Option<js_sys::Function>,
|
555 | 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 | + { |
556 | 639 | match &self.reader {
|
557 |
| - Some(reader) => match reader.get_features(layer_index) { |
558 |
| - Ok(features) => JsValue::from( |
559 |
| - features |
560 |
| - .into_iter() |
561 |
| - .map(JsValue::from) |
562 |
| - .collect::<js_sys::Array>(), |
563 |
| - ), |
| 640 | + Some(reader) => match operation(reader) { |
| 641 | + Ok(result) => result |
| 642 | + .into_iter() |
| 643 | + .map(Into::into) |
| 644 | + .collect::<js_sys::Array>() |
| 645 | + .into(), |
564 | 646 | Err(error) => {
|
565 | 647 | if let Some(callback) = error_callback {
|
566 | 648 | callback
|
|
0 commit comments