Skip to content

Commit

Permalink
Use Geozero for raw Mapbox Vector Tile (MVT) decoding (#73)
Browse files Browse the repository at this point in the history
practically this code does not really change the performance of the software, only improves the simplicity of the code.
  • Loading branch information
kylerchin authored Jun 21, 2024
1 parent c519b4d commit 3bff75f
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 237 deletions.
2 changes: 1 addition & 1 deletion galileo-mvt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ exclude = [

[features]
default = []
generate_proto = []

[dependencies]
bytes = "1.5.0"
Expand All @@ -25,6 +24,7 @@ log = "0.4.20"
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
nalgebra = { version = "0.32", features = ["serde-serialize"] }
geozero = { version = "0.13.0", features = ["with-mvt"] }

[build-dependencies]
prost-build = "0.12.2"
7 changes: 0 additions & 7 deletions galileo-mvt/build.rs

This file was deleted.

7 changes: 0 additions & 7 deletions galileo-mvt/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use prost::DecodeError;
use thiserror::Error;

#[derive(Debug, Clone, Error)]
Expand All @@ -9,9 +8,3 @@ pub enum GalileoMvtError {
#[error("{0}")]
Generic(String),
}

impl From<DecodeError> for GalileoMvtError {
fn from(value: DecodeError) -> Self {
Self::Proto(value.to_string())
}
}
173 changes: 32 additions & 141 deletions galileo-mvt/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,137 +1,17 @@
use crate::error::GalileoMvtError;
use crate::vector_tile::tile::GeomType;
use bytes::Buf;
use galileo_types::cartesian::{CartesianClosedContour, CartesianPoint2d, Winding};
use galileo_types::impls::{ClosedContour, Contour, Polygon};
use geozero::mvt::tile::GeomType;
use geozero::mvt::Message as GeozeroMessage;
use geozero::mvt::Tile;
use nalgebra::Point2;
use prost::Message;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt::{Display, Formatter};

pub mod error;

#[cfg(feature = "generate_proto")]
mod vector_tile {
include!(concat!(env!("OUT_DIR"), "/vector_tile.rs"));
}

#[cfg(not(feature = "generate_proto"))]
mod vector_tile {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Tile {
#[prost(message, repeated, tag = "3")]
pub layers: ::prost::alloc::vec::Vec<tile::Layer>,
}
/// Nested message and enum types in `Tile`.
pub mod tile {
/// Variant type encoding
/// The use of values is described in section 4.1 of the specification
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Value {
/// Exactly one of these values must be present in a valid message
#[prost(string, optional, tag = "1")]
pub string_value: ::core::option::Option<::prost::alloc::string::String>,
#[prost(float, optional, tag = "2")]
pub float_value: ::core::option::Option<f32>,
#[prost(double, optional, tag = "3")]
pub double_value: ::core::option::Option<f64>,
#[prost(int64, optional, tag = "4")]
pub int_value: ::core::option::Option<i64>,
#[prost(uint64, optional, tag = "5")]
pub uint_value: ::core::option::Option<u64>,
#[prost(sint64, optional, tag = "6")]
pub sint_value: ::core::option::Option<i64>,
#[prost(bool, optional, tag = "7")]
pub bool_value: ::core::option::Option<bool>,
}
/// Features are described in section 4.2 of the specification
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Feature {
#[prost(uint64, optional, tag = "1", default = "0")]
pub id: ::core::option::Option<u64>,
/// Tags of this feature are encoded as repeated pairs of
/// integers.
/// A detailed description of tags is located in sections
/// 4.2 and 4.4 of the specification
#[prost(uint32, repeated, tag = "2")]
pub tags: ::prost::alloc::vec::Vec<u32>,
/// The type of geometry stored in this feature.
#[prost(enumeration = "GeomType", optional, tag = "3", default = "Unknown")]
pub r#type: ::core::option::Option<i32>,
/// Contains a stream of commands and parameters (vertices).
/// A detailed description on geometry encoding is located in
/// section 4.3 of the specification.
#[prost(uint32, repeated, tag = "4")]
pub geometry: ::prost::alloc::vec::Vec<u32>,
}
/// Layers are described in section 4.1 of the specification
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Layer {
/// Any compliant implementation must first read the version
/// number encoded in this message and choose the correct
/// implementation for this version number before proceeding to
/// decode other parts of this message.
#[prost(uint32, required, tag = "15", default = "1")]
pub version: u32,
#[prost(string, required, tag = "1")]
pub name: ::prost::alloc::string::String,
/// The actual features in this tile.
#[prost(message, repeated, tag = "2")]
pub features: ::prost::alloc::vec::Vec<Feature>,
/// Dictionary encoding for keys
#[prost(string, repeated, tag = "3")]
pub keys: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
/// Dictionary encoding for values
#[prost(message, repeated, tag = "4")]
pub values: ::prost::alloc::vec::Vec<Value>,
/// Although this is an "optional" field it is required by the specification.
/// See <https://github.com/mapbox/vector-tile-spec/issues/47>
#[prost(uint32, optional, tag = "5", default = "4096")]
pub extent: ::core::option::Option<u32>,
}
/// GeomType is described in section 4.3.4 of the specification
#[derive(
Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration,
)]
#[repr(i32)]
pub enum GeomType {
Unknown = 0,
Point = 1,
Linestring = 2,
Polygon = 3,
}
impl GeomType {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
GeomType::Unknown => "UNKNOWN",
GeomType::Point => "POINT",
GeomType::Linestring => "LINESTRING",
GeomType::Polygon => "POLYGON",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"UNKNOWN" => Some(Self::Unknown),
"POINT" => Some(Self::Point),
"LINESTRING" => Some(Self::Linestring),
"POLYGON" => Some(Self::Polygon),
_ => None,
}
}
}
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MvtTile {
pub layers: Vec<MvtLayer>,
Expand Down Expand Up @@ -187,23 +67,18 @@ pub enum MvtGeometry {
Polygon(Vec<Polygon<Point>>),
}

impl From<Option<i32>> for GeomType {
fn from(value: Option<i32>) -> Self {
match value {
Some(1) => GeomType::Point,
Some(2) => GeomType::Linestring,
Some(3) => GeomType::Polygon,
_ => GeomType::Unknown,
}
}
}

impl MvtTile {
pub fn decode<B>(buffer: B, skip_recoverable_errors: bool) -> Result<MvtTile, GalileoMvtError>
where
B: Buf,
{
let pb = vector_tile::Tile::decode(buffer)?;
let pb = Tile::decode(buffer);

if let Err(e) = pb {
return Err(GalileoMvtError::Proto(e.to_string()));
}

let pb = pb.unwrap();

let mut layers = vec![];
for layer in pb.layers.into_iter() {
Expand Down Expand Up @@ -233,10 +108,10 @@ impl MvtTile {

impl MvtLayer {
fn decode(
pb_layer: vector_tile::tile::Layer,
pb_layer: geozero::mvt::tile::Layer,
skip_recoverable_errors: bool,
) -> Result<Self, GalileoMvtError> {
let vector_tile::tile::Layer {
let geozero::mvt::tile::Layer {
name,
keys,
values,
Expand Down Expand Up @@ -289,7 +164,7 @@ impl MvtLayer {
}

impl MvtValue {
fn decode(pb_value: vector_tile::tile::Value) -> Result<MvtValue, GalileoMvtError> {
fn decode(pb_value: geozero::mvt::tile::Value) -> Result<MvtValue, GalileoMvtError> {
let mut present_types = 0;
let mut value = MvtValue::Unknown;

Expand Down Expand Up @@ -340,20 +215,36 @@ impl MvtValue {
}
}

pub fn number_to_geomtype(number: i32) -> GeomType {
match number {
1 => GeomType::Point,
2 => GeomType::Linestring,
3 => GeomType::Polygon,
_ => GeomType::Unknown,
}
}

pub fn opt_number_to_geomtype(number: Option<i32>) -> GeomType {
match number {
Some(number) => number_to_geomtype(number),
None => GeomType::Unknown,
}
}

impl MvtFeature {
fn decode(
pb_feature: vector_tile::tile::Feature,
pb_feature: geozero::mvt::tile::Feature,
extent: u32,
keys: &[String],
values: &[MvtValue],
) -> Result<MvtFeature, GalileoMvtError> {
let vector_tile::tile::Feature {
let geozero::mvt::tile::Feature {
id,
tags,
r#type,
geometry,
} = pb_feature;
let pb_type = r#type.into();
let pb_type = opt_number_to_geomtype(r#type);
let properties = Self::decode_properties(tags, keys, values)?;
let geometry = Self::decode_geometry(pb_type, geometry, extent)?;

Expand Down
80 changes: 0 additions & 80 deletions galileo-mvt/src/vector_tile.proto

This file was deleted.

2 changes: 1 addition & 1 deletion galileo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ quick_cache = "0.4"
futures-intrusive = "0.5"
geojson = { version = "0.24", optional = true }
raw-window-handle = { version = "0.6", optional = true }
geozero = "0.13.0"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
wgpu = { version = "0.19", optional = true }
Expand Down Expand Up @@ -102,7 +103,6 @@ lazy_static = "1.4"
geo = "0.27"
csv = "1.3"
geo-types = "0.7"
geozero = "0.12"
las = { version = "0.8", features = ["laz"] }
anyhow = "1.0"
geojson = "0.24"
Expand Down

0 comments on commit 3bff75f

Please sign in to comment.