|
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