pub mod error;
pub mod feature;
pub mod layer;
mod vector_tile;
use feature::{Feature, Value};
use geo_types::{
Coord, CoordNum, Geometry, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,
};
use layer::Layer;
use num_traits::NumCast;
use prost::{Message, bytes::Bytes};
use vector_tile::{Tile, tile::GeomType};
const DIMENSION: u32 = 2;
pub struct Reader {
tile: Tile,
}
impl Reader {
pub fn new(data: Vec<u8>) -> Result<Self, error::ParserError> {
let tile = Tile::decode(Bytes::from(data))?;
Ok(Self { tile })
}
pub fn get_layer_names(&self) -> Result<Vec<String>, error::ParserError> {
process_layers(&self.tile.layers, |layer, _| layer.name.clone())
}
pub fn get_layer_metadata(&self) -> Result<Vec<Layer>, error::ParserError> {
process_layers(&self.tile.layers, |layer, index| Layer {
layer_index: index,
version: layer.version,
name: layer.name.clone(),
feature_count: layer.features.len(),
extent: layer.extent.unwrap_or(4096),
})
}
pub fn get_features(&self, layer_index: usize) -> Result<Vec<Feature>, error::ParserError> {
self.get_features_as::<f32>(layer_index)
}
pub fn get_features_as<T: CoordNum>(&self, layer_index: usize) -> Result<Vec<Feature<T>>, error::ParserError> {
let layer = self.tile.layers.get(layer_index);
match layer {
Some(layer) => {
let mut features = Vec::with_capacity(layer.features.len());
for feature in layer.features.iter() {
if let Some(geom_type) = feature.r#type {
let geom_type = GeomType::try_from(geom_type).map_err(|_| error::ParserError::InvalidGeometry)?;
let parsed_geometry = parse_geometry::<T>(&feature.geometry, geom_type)?;
let parsed_tags = parse_tags(&feature.tags, &layer.keys, &layer.values)?;
features.push(Feature {
geometry: parsed_geometry,
id: feature.id,
properties: Some(parsed_tags),
});
}
}
Ok(features)
}
None => Ok(vec![]),
}
}
}
fn process_layers<T, F>(
layers: &[vector_tile::tile::Layer],
mut processor: F,
) -> Result<Vec<T>, error::ParserError>
where
F: FnMut(&vector_tile::tile::Layer, usize) -> T,
{
let mut results = Vec::with_capacity(layers.len());
for (index, layer) in layers.iter().enumerate() {
match layer.version {
1 | 2 => results.push(processor(layer, index)),
_ => {
return Err(error::ParserError::UnsupportedVersion {
layer_name: layer.name.clone(),
version: layer.version,
});
}
}
}
Ok(results)
}
fn parse_tags(
tags: &[u32],
keys: &[String],
values: &[vector_tile::tile::Value],
) -> Result<std::collections::HashMap<String, Value>, error::ParserError> {
let mut result = std::collections::HashMap::new();
for item in tags.chunks(2) {
if item.len() != 2
|| item[0] as usize >= keys.len()
|| item[1] as usize >= values.len()
{
return Err(error::ParserError::InvalidTags);
}
result.insert(
keys[item[0] as usize].clone(),
map_value(values[item[1] as usize].clone()),
);
}
Ok(result)
}
fn map_value(value: vector_tile::tile::Value) -> Value {
if let Some(s) = value.string_value {
return Value::String(s);
}
if let Some(f) = value.float_value {
return Value::Float(f);
}
if let Some(d) = value.double_value {
return Value::Double(d);
}
if let Some(i) = value.int_value {
return Value::Int(i);
}
if let Some(u) = value.uint_value {
return Value::UInt(u);
}
if let Some(s) = value.sint_value {
return Value::SInt(s);
}
if let Some(b) = value.bool_value {
return Value::Bool(b);
}
Value::Null
}
fn shoelace_formula<T: CoordNum>(points: &[Point<T>]) -> f32 {
let mut area: f32 = 0.0;
let n = points.len();
let mut v1 = points[n - 1];
for v2 in points.iter().take(n) {
let v2y: f32 = NumCast::from(v2.y()).unwrap_or(0.0);
let v1y: f32 = NumCast::from(v1.y()).unwrap_or(0.0);
let v2x: f32 = NumCast::from(v2.x()).unwrap_or(0.0);
let v1x: f32 = NumCast::from(v1.x()).unwrap_or(0.0);
area += (v2y - v1y) * (v2x + v1x);
v1 = *v2;
}
area * 0.5
}
fn parse_geometry<T: CoordNum>(
geometry_data: &[u32],
geom_type: GeomType,
) -> Result<Geometry<T>, error::ParserError> {
if geom_type == GeomType::Unknown {
return Err(error::ParserError::InvalidGeometry);
}
let mut coordinates: Vec<Coord<T>> = Vec::with_capacity(geometry_data.len());
let mut polygons: Vec<Polygon<T>> = Vec::new();
let mut linestrings: Vec<LineString<T>> = Vec::new();
let mut cursor: [i32; 2] = [0, 0];
let mut parameter_count: u32 = 0;
for value in geometry_data.iter() {
if parameter_count == 0 {
let command_integer = value;
let id = (command_integer & 0x7) as u8;
match id {
1 => {
parameter_count = (command_integer >> 3) * DIMENSION;
if geom_type == GeomType::Linestring && !coordinates.is_empty() {
linestrings.push(LineString::new(coordinates));
coordinates = Vec::with_capacity(geometry_data.len());
}
}
2 => {
parameter_count = (command_integer >> 3) * DIMENSION;
}
7 => {
let first_coordinate = match coordinates.first() {
Some(coord) => coord.to_owned(),
None => {
return Err(error::ParserError::InvalidGeometry);
}
};
coordinates.push(first_coordinate);
let ring = LineString::new(coordinates);
let area = shoelace_formula(&ring.clone().into_points());
if area > 0.0 {
if !linestrings.is_empty() {
polygons.push(Polygon::new(
linestrings[0].clone(),
linestrings[1..].into(),
));
linestrings = Vec::new();
}
}
linestrings.push(ring);
coordinates = Vec::with_capacity(geometry_data.len());
}
_ => (),
}
} else {
let parameter_integer = value;
let integer_value = ((parameter_integer >> 1) as i32) ^ -((parameter_integer & 1) as i32);
if parameter_count.is_multiple_of(DIMENSION) {
cursor[0] = match cursor[0].checked_add(integer_value) {
Some(result) => result,
None => i32::MAX, };
} else {
cursor[1] = match cursor[1].checked_add(integer_value) {
Some(result) => result,
None => i32::MAX, };
let x = NumCast::from(cursor[0])
.ok_or(error::ParserError::CoordinateOverflow { value: cursor[0] })?;
let y = NumCast::from(cursor[1])
.ok_or(error::ParserError::CoordinateOverflow { value: cursor[1] })?;
coordinates.push(Coord { x, y });
}
parameter_count -= 1;
}
}
match geom_type {
GeomType::Linestring => {
if !linestrings.is_empty() {
linestrings.push(LineString::new(coordinates));
return Ok(MultiLineString::new(linestrings).into());
}
Ok(LineString::new(coordinates).into())
}
GeomType::Point => Ok(
MultiPoint(
coordinates
.iter()
.map(|coord| Point::new(coord.x, coord.y))
.collect(),
)
.into(),
),
GeomType::Polygon => {
if !linestrings.is_empty() {
polygons.push(Polygon::new(
linestrings[0].clone(),
linestrings[1..].into(),
));
return Ok(MultiPolygon::new(polygons).into());
}
match polygons.first() {
Some(polygon) => Ok(polygon.to_owned().into()),
None => Err(error::ParserError::InvalidGeometry),
}
}
GeomType::Unknown => Err(error::ParserError::InvalidGeometry),
}
}
#[cfg(feature = "wasm")]
pub mod wasm {
use crate::feature::Value;
use geojson::{Feature, GeoJson, JsonObject, JsonValue, feature::Id};
use serde::ser::{Serialize, SerializeStruct};
use serde_wasm_bindgen::Serializer;
use wasm_bindgen::prelude::*;
impl From<Value> for JsonValue {
fn from(value: Value) -> Self {
match value {
Value::Null => JsonValue::Null,
Value::Bool(b) => JsonValue::from(b),
Value::Int(i) => JsonValue::from(i),
Value::UInt(u) => JsonValue::from(u),
Value::SInt(s) => JsonValue::from(s),
Value::Float(f) => JsonValue::from(f),
Value::Double(d) => JsonValue::from(d),
Value::String(s) => JsonValue::from(s),
}
}
}
impl From<super::feature::Feature> for wasm_bindgen::JsValue {
fn from(feature: super::feature::Feature) -> Self {
let properties: Option<JsonObject> = feature.properties.as_ref().map(|props| {
props
.clone()
.into_iter()
.map(|(k, v)| (k, v.into()))
.collect()
});
let geojson = GeoJson::Feature(Feature {
bbox: None,
geometry: Some(feature.get_geometry().into()),
id: feature.id.map(|id| Id::Number(id.into())),
properties,
foreign_members: None,
});
geojson.serialize(&Serializer::json_compatible()).unwrap()
}
}
impl From<super::layer::Layer> for wasm_bindgen::JsValue {
fn from(layer: super::layer::Layer) -> Self {
layer.serialize(&Serializer::json_compatible()).unwrap()
}
}
impl Serialize for super::layer::Layer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
let mut state = serializer.serialize_struct("Layer", 5)?;
state.serialize_field("layer_index", &self.layer_index)?;
state.serialize_field("version", &self.version)?;
state.serialize_field("name", &self.name)?;
state.serialize_field("feature_count", &self.feature_count)?;
state.serialize_field("extent", &self.extent)?;
state.end()
}
}
#[wasm_bindgen]
pub struct Reader {
reader: Option<super::Reader>,
}
#[wasm_bindgen]
impl Reader {
#[wasm_bindgen(constructor)]
pub fn new(data: Vec<u8>, error_callback: Option<js_sys::Function>) -> Reader {
let reader = match super::Reader::new(data) {
Ok(reader) => Some(reader),
Err(error) => {
if let Some(callback) = error_callback {
callback
.call1(&JsValue::NULL, &JsValue::from_str(&format!("{:?}", error)))
.unwrap();
}
None
}
};
Reader { reader }
}
#[wasm_bindgen(js_name = getLayerNames)]
pub fn get_layer_names(&self, error_callback: Option<js_sys::Function>) -> JsValue {
self.handle_result(|reader| reader.get_layer_names(), error_callback)
}
#[wasm_bindgen(js_name = getLayerMetadata)]
pub fn get_layer_metadata(&self, error_callback: Option<js_sys::Function>) -> JsValue {
self.handle_result(|reader| reader.get_layer_metadata(), error_callback)
}
#[wasm_bindgen(js_name = getFeatures)]
pub fn get_features(
&self,
layer_index: usize,
error_callback: Option<js_sys::Function>,
) -> JsValue {
self.handle_result(|reader| reader.get_features(layer_index), error_callback)
}
fn handle_result<T, F>(&self, operation: F, error_callback: Option<js_sys::Function>) -> JsValue
where
T: IntoIterator,
T::Item: Into<JsValue>,
F: FnOnce(&super::Reader) -> Result<T, super::error::ParserError>,
{
match &self.reader {
Some(reader) => match operation(reader) {
Ok(result) => result
.into_iter()
.map(Into::into)
.collect::<js_sys::Array>()
.into(),
Err(error) => {
if let Some(callback) = error_callback {
callback
.call1(&JsValue::NULL, &JsValue::from_str(&format!("{:?}", error)))
.unwrap();
}
JsValue::NULL
}
},
None => JsValue::NULL,
}
}
}
}