diff --git a/Cargo.lock b/Cargo.lock index 6cf3a49..0c9d7ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -319,7 +319,7 @@ dependencies = [ [[package]] name = "tinymvt" -version = "0.1.0" +version = "0.0.1" dependencies = [ "ahash", "indexmap", diff --git a/Cargo.toml b/Cargo.toml index 2b1eaa6..31f2a12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,9 @@ [package] name = "tinymvt" -version = "0.1.0" +version = "0.0.1" edition = "2021" +license = "MIT" +description = "Simple library for serializing Mapbox Vector Tile (MVT) with minimal dependencies." [dependencies] ahash = "0.8.11" diff --git a/README.md b/README.md index ed497ee..80f133a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,17 @@ -# tinymvt +# tinymvt-rs [![codecov](https://codecov.io/gh/MIERUNE/tinymvt/graph/badge.svg?token=HSPd9MRmxC)](https://codecov.io/gh/MIERUNE/tinymvt) +![Crates.io Version](https://img.shields.io/crates/v/tinymvt) -A simple Rust library for serializing Mapbox Vector Tile (MVT) with few dependencies. +A minimal Rust utility library for serializing Mapbox Vector Tiles (MVT). License: MIT + +Includes the following utilities: + +- Protobuf ([prost](https://github.com/tokio-rs/prost)) data types for MVT +- Geometry encoder +- Tags encoder +- Conversion between Web Mercator and geographic coordinates +- Conversion between linear tile IDs (PMTiles-compliant Hilbert id) and XYZ tile IDs + diff --git a/examples/demo.rs b/examples/demo.rs index cf28c14..5ef891e 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -3,8 +3,10 @@ use std::fs; use prost::Message; use tinymvt::geometry::GeometryEncoder; use tinymvt::tag::{TagsEncoder, Value}; -use tinymvt::vector_tile::tile::{Feature, GeomType, Layer}; -use tinymvt::vector_tile::Tile; +use tinymvt::vector_tile::{ + tile::{Feature, GeomType, Layer}, + Tile, +}; fn main() { let extent = 4096; @@ -25,12 +27,12 @@ fn main() { [extent - 900, 900], ]); - tags_enc.add("foo", 10.into()); - tags_enc.add("bar", 20.5.into()); + tags_enc.add("foo", 10); + tags_enc.add("bar", 20.5); Feature { id: Some(1), - tags: tags_enc.flush_tags(), + tags: tags_enc.take_tags(), r#type: Some(GeomType::Point as i32), geometry: geom_enc.into_vec(), } @@ -56,13 +58,13 @@ fn main() { tags_enc.add("sint", Value::SInt(-10)); tags_enc.add("int", Value::Int(10)); tags_enc.add("string", Value::String("string".to_string())); - tags_enc.add("float", 10.5f32.into()); - tags_enc.add("double", 10.5f64.into()); + tags_enc.add("float", 10.5f32); + tags_enc.add("double", 10.5f64); tags_enc.add("bool", Value::Bool(true)); Feature { id: Some(2), - tags: tags_enc.flush_tags(), + tags: tags_enc.take_tags(), r#type: Some(GeomType::Linestring as i32), geometry: geom_enc.into_vec(), } @@ -79,12 +81,12 @@ fn main() { geom_enc.add_ring([[2200, 2200], [2300, 2200], [2300, 2300], [2200, 2300]]); geom_enc.add_ring([[2300, 2300], [2400, 2300], [2400, 2400], [2300, 2400]]); - tags_enc.add("fizz", 10.into()); - tags_enc.add("buzz", 20.5.into()); + tags_enc.add("fizz", 10); + tags_enc.add("buzz", 20.5); Feature { id: Some(3), - tags: tags_enc.flush_tags(), + tags: tags_enc.take_tags(), r#type: Some(GeomType::Polygon as i32), geometry: geom_enc.into_vec(), } diff --git a/src/geometry.rs b/src/geometry.rs index a832586..7661292 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -7,16 +7,16 @@ const GEOM_COMMAND_CLOSE_PATH: u32 = 7; const GEOM_COMMAND_MOVE_TO_WITH_COUNT1: u32 = 1 << 3 | GEOM_COMMAND_MOVE_TO; const GEOM_COMMAND_CLOSE_PATH_WITH_COUNT1: u32 = 1 << 3 | GEOM_COMMAND_CLOSE_PATH; +/// Utility for encoding MVT geometries. pub struct GeometryEncoder { buf: Vec, prev_x: i16, prev_y: i16, } -/// Utility for encoding MVT geometries. impl GeometryEncoder { - // TODO: with_capacity pub fn new() -> Self { + // TODO: with_capacity? Self { buf: Vec::new(), prev_x: 0, @@ -24,6 +24,7 @@ impl GeometryEncoder { } } + /// Consumes the encoder and returns the encoded geometry. #[inline] pub fn into_vec(self) -> Vec { self.buf diff --git a/src/tag.rs b/src/tag.rs index 7e258a0..0af7cc0 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -1,5 +1,3 @@ -//! Tag (attribute) encoder for MVT. - use ahash::RandomState; use indexmap::IndexSet; @@ -18,8 +16,14 @@ impl TagsEncoder { Default::default() } + /// Adds a key-value pair for the current feature. + #[inline] + pub fn add(&mut self, key: &str, value: impl Into) { + self.add_inner(key, value.into()); + } + #[inline] - pub fn add(&mut self, key: &str, value: Value) { + fn add_inner(&mut self, key: &str, value: Value) { let key_idx = match self.keys.get_index_of(key) { None => self.keys.insert_full(key.to_string()).0, Some(idx) => idx, @@ -31,11 +35,13 @@ impl TagsEncoder { self.tags.extend([key_idx as u32, value_idx as u32]); } + /// Takes the key-value index buffer for the current feature. #[inline] - pub fn flush_tags(&mut self) -> Vec { + pub fn take_tags(&mut self) -> Vec { std::mem::take(&mut self.tags) } + /// Consumes the encoder and returns the keys and values for a layer. #[inline] pub fn into_keys_and_values(self) -> (Vec, Vec) { let keys = self.keys.into_iter().collect(); @@ -48,12 +54,7 @@ impl TagsEncoder { } } -pub struct KeysAndValues { - pub keys: Vec, - pub values: Vec, -} - -/// Wrapper for MVT values +/// Comperative wrapper for the MVT values. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Value { String(String), @@ -162,34 +163,34 @@ mod test { #[test] fn tags_encoder() { let mut encoder = TagsEncoder::new(); - encoder.add("k0", "v0".into()); - encoder.add("k0", "v0".into()); - encoder.add("k1", "v0".into()); - encoder.add("k1", "v1".into()); - assert_eq!(encoder.flush_tags(), [0, 0, 0, 0, 1, 0, 1, 1]); - - encoder.add("k0", "v0".into()); - encoder.add("k0", "v2".into()); - encoder.add("k1", "v2".into()); - encoder.add("k2", "v0".to_string().into()); - encoder.add("k1", "v1".into()); - encoder.add("k1", "v1".to_string().into()); - assert_eq!(encoder.flush_tags(), [0, 0, 0, 2, 1, 2, 2, 0, 1, 1, 1, 1]); - - encoder.add("k1", 10i32.into()); - encoder.add("k2", 10.5f64.into()); - encoder.add("k3", 10u32.into()); - encoder.add("k3", (-10i32).into()); - encoder.add("k3", true.into()); - encoder.add("k3", 1.into()); - encoder.add("k2", 10.5f32.into()); - encoder.add("k4", 10.5f64.into()); - encoder.add("k3", (-10i64).into()); - encoder.add("k3", 10u64.into()); + encoder.add("k0", "v0"); + encoder.add("k0", "v0"); + encoder.add("k1", "v0"); + encoder.add("k1", "v1"); + assert_eq!(encoder.take_tags(), [0, 0, 0, 0, 1, 0, 1, 1]); + + encoder.add("k0", "v0"); + encoder.add("k0", "v2"); + encoder.add("k1", "v2"); + encoder.add("k2", "v0".to_string()); + encoder.add("k1", "v1"); + encoder.add("k1", "v1".to_string()); + assert_eq!(encoder.take_tags(), [0, 0, 0, 2, 1, 2, 2, 0, 1, 1, 1, 1]); + + encoder.add("k1", 10i32); + encoder.add("k2", 10.5f64); + encoder.add("k3", 10u32); + encoder.add("k3", -10i32); + encoder.add("k3", true); + encoder.add("k3", 1); + encoder.add("k2", 10.5f32); + encoder.add("k4", 10.5f64); + encoder.add("k3", -10i64); + encoder.add("k3", 10u64); encoder.add("k5", Value::Int(11)); - encoder.add("k5", 12i64.into()); + encoder.add("k5", 12i64); assert_eq!( - encoder.flush_tags(), + encoder.take_tags(), [1, 3, 2, 4, 3, 3, 3, 5, 3, 6, 3, 7, 2, 8, 4, 4, 3, 5, 3, 3, 5, 9, 5, 10] ); diff --git a/src/vector_tile.rs b/src/vector_tile.rs index 65ed831..a32f330 100644 --- a/src/vector_tile.rs +++ b/src/vector_tile.rs @@ -72,17 +72,7 @@ pub mod tile { pub extent: ::core::option::Option, } /// GeomType is described in section 4.3.4 of the specification - #[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - PartialOrd, - Ord, - ::prost::Enumeration - )] + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] pub enum GeomType { Unknown = 0, diff --git a/tests/make_tile.rs b/tests/make_tile.rs index 02ec0cd..a5541cd 100644 --- a/tests/make_tile.rs +++ b/tests/make_tile.rs @@ -3,8 +3,10 @@ use std::fs; use prost::Message; use tinymvt::geometry::GeometryEncoder; use tinymvt::tag::{TagsEncoder, Value}; -use tinymvt::vector_tile::tile::{Feature, GeomType, Layer}; -use tinymvt::vector_tile::Tile; +use tinymvt::vector_tile::{ + tile::{Feature, GeomType, Layer}, + Tile, +}; #[test] fn make_tile() { @@ -27,12 +29,12 @@ fn make_tile() { [extent - 900, 900], ]); - tags_enc.add("foo", 10.into()); - tags_enc.add("bar", 20.5.into()); + tags_enc.add("foo", 10); + tags_enc.add("bar", 20.5); Feature { id: Some(1), - tags: tags_enc.flush_tags(), + tags: tags_enc.take_tags(), r#type: Some(GeomType::Point as i32), geometry: geom_enc.into_vec(), } @@ -59,13 +61,13 @@ fn make_tile() { tags_enc.add("sint", Value::SInt(-10)); tags_enc.add("int", Value::Int(10)); tags_enc.add("string", Value::String("string".to_string())); - tags_enc.add("float", 10.5f32.into()); - tags_enc.add("double", 10.5f64.into()); + tags_enc.add("float", 10.5f32); + tags_enc.add("double", 10.5f64); tags_enc.add("bool", Value::Bool(true)); Feature { id: Some(2), - tags: tags_enc.flush_tags(), + tags: tags_enc.take_tags(), r#type: Some(GeomType::Linestring as i32), geometry: geom_enc.into_vec(), } @@ -84,12 +86,12 @@ fn make_tile() { geom_enc.add_ring([[2200, 2200], [2300, 2200], [2300, 2300], [2200, 2300]]); geom_enc.add_ring([[2300, 2300], [2400, 2300], [2400, 2400], [2300, 2400]]); - tags_enc.add("fizz", 10.into()); - tags_enc.add("buzz", 20.5.into()); + tags_enc.add("fizz", 10); + tags_enc.add("buzz", 20.5); Feature { id: Some(3), - tags: tags_enc.flush_tags(), + tags: tags_enc.take_tags(), r#type: Some(GeomType::Polygon as i32), geometry: geom_enc.into_vec(), }