Skip to content

Commit b5be410

Browse files
committed
feat: exposing layer metadata by get_layer_metadata
* layer_index * version * name * feature_count * extent (Defaults to 4096) Solves #466
1 parent 684e415 commit b5be410

File tree

3 files changed

+167
-42
lines changed

3 files changed

+167
-42
lines changed

src/layer.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//! This module provide the `Layer` struct.
2+
//!
3+
//! The `Layer` struct represents a layer in a vector tile, containing metadata about the layer and its features.
4+
//!
5+
//! # Types
6+
//!
7+
//! The `layer` module defines the following types:
8+
//!
9+
//! - `Layer`: Represents a layer in a vector tile, containing metadata about the layer and its features.
10+
11+
/// A structure representing a layer in a vector tile.
12+
#[derive(Debug, Clone)]
13+
pub struct Layer {
14+
/// The index of the layer in the vector tile.
15+
pub layer_index: usize,
16+
17+
/// The version of the layer.
18+
pub version: u32,
19+
20+
/// The name of the layer.
21+
pub name: String,
22+
23+
/// The number of features in the layer.
24+
pub feature_count: usize,
25+
26+
/// The extent of the layer, representing the size of the tile in pixels. Defaults to 4096.
27+
pub extent: u32,
28+
}

src/lib.rs

Lines changed: 124 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,15 @@
6060
6161
pub mod error;
6262
pub mod feature;
63+
pub mod layer;
6364

6465
mod vector_tile;
6566

6667
use feature::{Feature, Value};
6768
use geo_types::{
6869
Coord, Geometry, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,
6970
};
71+
use layer::Layer;
7072
use prost::{Message, bytes::Bytes};
7173
use vector_tile::{Tile, tile::GeomType};
7274

@@ -132,21 +134,43 @@ impl Reader {
132134
/// }
133135
/// ```
134136
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+
})
150174
}
151175

152176
/// Retrieves the features of a specific layer in the vector tile.
@@ -222,6 +246,28 @@ impl Reader {
222246
}
223247
}
224248

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+
225271
fn parse_tags(
226272
tags: &[u32],
227273
keys: &[String],
@@ -408,10 +454,11 @@ pub mod wasm {
408454

409455
use crate::feature::Value;
410456
use geojson::{Feature, GeoJson, JsonObject, JsonValue, feature::Id};
411-
use serde::Serialize;
457+
use serde::ser::{Serialize, SerializeStruct};
412458
use serde_wasm_bindgen::Serializer;
413459
use wasm_bindgen::prelude::*;
414460

461+
/// Converts a `Value` into a `serde_json::Value`.
415462
impl From<Value> for JsonValue {
416463
fn from(value: Value) -> Self {
417464
match value {
@@ -450,6 +497,28 @@ pub mod wasm {
450497
}
451498
}
452499

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+
453522
/// Reader for decoding and accessing vector tile data in WebAssembly.
454523
#[wasm_bindgen]
455524
pub struct Reader {
@@ -507,25 +576,30 @@ pub mod wasm {
507576
/// ```
508577
#[wasm_bindgen(js_name = getLayerNames)]
509578
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)
529603
}
530604

531605
/// Retrieves the features of a specific layer in the vector tile.
@@ -553,14 +627,22 @@ pub mod wasm {
553627
layer_index: usize,
554628
error_callback: Option<js_sys::Function>,
555629
) -> 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+
{
556639
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(),
564646
Err(error) => {
565647
if let Some(callback) = error_callback {
566648
callback

tests/fixtures.test.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@ const { Reader } = require('mvt-reader')
22
const { readFileSync } = require('fs')
33

44
describe('Test fixture', () => {
5+
test('032 contains layer metadata', async () => {
6+
const reader = new Reader(readFileSync('mvt-fixtures/fixtures/032/tile.mvt'))
7+
const layers = reader.getLayerMetadata()
8+
9+
expect(layers.length).toBe(1)
10+
11+
const layer = layers[0]
12+
13+
expect(layer.name).toBe('hello')
14+
expect(layer.extent).toBe(4096)
15+
expect(layer.version).toBe(2)
16+
expect(layer.feature_count).toBe(1)
17+
expect(layer.layer_index).toBe(0)
18+
})
19+
520
test('032 parsed successfully (string_value)', async () => {
621
const reader = new Reader(readFileSync('mvt-fixtures/fixtures/032/tile.mvt'))
722
expect(reader.getLayerNames()).toContain('hello')

0 commit comments

Comments
 (0)