Add vector_tile crate for decoding vector tiles

This commit is contained in:
Maximilian Ammann 2021-11-28 20:30:00 +01:00
parent b887aadcc0
commit 6c4ab27bb1
11 changed files with 425 additions and 0 deletions

View File

@ -0,0 +1,19 @@
[package]
name = "vector-tile"
version = "0.1.0"
description = "A library decoding vector tiles"
readme = "README.md"
categories = ["encoding"]
edition = "2021"
build = "build.rs"
[dependencies]
log = "0.4"
num-traits = "0.2"
pointy = "0.3"
protobuf = "2.25"
[build-dependencies]
protobuf-codegen-pure = "2.25"

13
libs/vector_tile/build.rs Normal file
View File

@ -0,0 +1,13 @@
extern crate protobuf_codegen_pure;
use std::path::PathBuf;
fn main() {
let out_path = PathBuf::from("src/protos");
protobuf_codegen_pure::Codegen::new()
.out_dir(out_path)
.inputs(&["spec/2.1/vector_tile.proto"])
.include("spec/2.1")
.run()
.expect("Codegen failed.");
}

1
libs/vector_tile/spec Submodule

@ -0,0 +1 @@
Subproject commit c8d178da65cce0ea474e3d99e3368970ce217093

View File

@ -0,0 +1,185 @@
use std::collections::HashMap;
use crate::geometry::{
Geometry, GeometryLineString, GeometryPoint, GeometryPolygon, MultiPoint, Point,
};
use crate::protos::vector_tile::{
Tile as ProtoTile, Tile_Feature as ProtoFeature, Tile_GeomType as ProtoGeomType, Tile_GeomType,
Tile_Layer as ProtoLayer, Tile_Value as ProtoValue,
};
use crate::tile::{Feature, Layer, PropertyValue, Tile};
pub trait Decode<T> {
fn decode(self) -> T;
}
/// Decode a PropertyValue
impl Decode<PropertyValue> for ProtoValue {
fn decode(self) -> PropertyValue {
if self.has_bool_value() {
PropertyValue::BoolValue(self.get_bool_value())
} else if self.has_string_value() {
PropertyValue::StringValue(String::from(self.get_string_value()))
} else if self.has_float_value() {
PropertyValue::FloatValue(self.get_float_value())
} else if self.has_int_value() {
PropertyValue::IntValue(self.get_int_value())
} else if self.has_sint_value() {
PropertyValue::SIntValue(self.get_sint_value())
} else if self.has_uint_value() {
PropertyValue::UIntValue(self.get_uint_value())
} else if self.has_double_value() {
PropertyValue::DoubleValue(self.get_double_value())
} else {
PropertyValue::Unknown
}
}
}
/// Decode a list of PropertyValues
impl Decode<Vec<PropertyValue>> for Vec<ProtoValue> {
fn decode(self) -> Vec<PropertyValue> {
self.into_iter().map(|value| value.decode()).collect()
}
}
const CMD_MOVE_TO: u32 = 1;
const CMD_LINE_TO: u32 = 2;
const CMD_CLOSE_PATH: u32 = 7;
const CMD_MOVE_TO_PARAMETERS: usize = 2;
const CMD_LINE_TO_PARAMETERS: usize = 2;
const CMD_CLOSE_PATH_PARAMETERS: usize = 0;
trait ZigZag {
/// Encodes a value to zigzag
fn zigzag(self) -> i32;
/// Decodes a value from zigzag encoding
fn zagzig(self) -> i32;
}
impl ZigZag for u32 {
fn zigzag(self) -> i32 {
((self << 1) ^ (self >> 31)) as i32
}
fn zagzig(self) -> i32 {
((self >> 1) as i32 ^ (-((self & 1) as i32)))
}
}
impl Decode<GeometryPoint> for Vec<u32> {
fn decode(self) -> GeometryPoint {
let mut points = vec![];
let mut i = 0;
while i < self.len() - 1 {
let command = self[i] & 0x7;
if command != CMD_MOVE_TO {
// FIXME: ERROR
}
let count = (self[i] >> 3) as usize;
i += 1;
for parameter in 0..count {
points.push(Point::new(self[i + parameter].zagzig(), self[i + parameter + 1].zagzig()));
}
i += count * CMD_MOVE_TO_PARAMETERS;
}
if points.len() == 1 {
GeometryPoint::Point(points.remove(0))
} else if points.len() > 1 {
GeometryPoint::MultiPoint(MultiPoint::new(points))
} else {
GeometryPoint::Point(Point::new(0, 0)); // point is at the origin
}
}
}
impl Decode<GeometryLineString> for Vec<u32> {
fn decode(self) -> GeometryLineString {
i += count * match command {
CMD_MOVE_TO => CMD_MOVE_TO_PARAMETERS,
CMD_LINE_TO => CMD_LINE_TO_PARAMETERS,
CMD_CLOSE_PATH => CMD_CLOSE_PATH_PARAMETERS,
_ => 0,
};
}
}
impl Decode<GeometryPolygon> for Vec<u32> {
fn decode(self) -> GeometryPolygon {
GeometryPolygon::Unknown
}
}
/// Decode a Geometry
impl Decode<Geometry> for ProtoFeature {
fn decode(self) -> Geometry {
match &self.get_field_type() {
Tile_GeomType::UNKNOWN => Geometry::Unknown,
Tile_GeomType::POINT => Geometry::GeometryPoint(self.geometry.decode()),
Tile_GeomType::LINESTRING => Geometry::GeometryLineString(self.geometry.decode()),
Tile_GeomType::POLYGON => Geometry::GeometryPolygon(self.geometry.decode()),
}
}
}
/// Decode a Feature
impl Decode<Feature> for (&ProtoLayer, ProtoFeature) {
fn decode(self) -> Feature {
let (layer, feature) = self;
let mut properties = HashMap::new();
for chunk in feature.tags.chunks(2) {
let key = chunk[0];
let value = chunk[1];
let keys = &layer.keys;
if let Some(actualKey) = keys.get(key as usize) {
let values = &layer.values;
if let Some(actualValue) = values.get(value as usize) {
properties.insert(actualKey.clone(), actualValue.clone().decode());
}
}
}
let geometry = feature.clone().decode(); // FIXME: Inefficient clone
Feature::new(feature, geometry, properties)
}
}
/// Decode a Layer
impl Decode<Layer> for ProtoLayer {
fn decode(mut self) -> Layer {
// FIXME: Order of features is changed here
let mut features = Vec::new();
while let Some(feature) = self.features.pop() {
features.push((&self, feature).decode())
}
Layer::new(self, features)
}
}
/// Decode a whole Tile
impl Decode<Tile> for ProtoTile {
fn decode(mut self) -> Tile {
// FIXME: Order of layers is changed here
let mut layers = Vec::new();
while let Some(layer) = self.layers.pop() {
layers.push(layer.decode())
}
Tile::new(self, layers)
}
}

View File

@ -0,0 +1,99 @@
use num_traits::Num;
type Number = i32;
#[derive(Debug)]
pub enum GeometryPoint {
Point(Point),
MultiPoint(MultiPoint),
Unknown,
}
#[derive(Debug)]
pub struct MultiPoint {
points: Vec<Point>,
}
#[derive(Debug)]
pub struct Point {
x: Number,
y: Number,
}
#[derive(Debug)]
pub enum GeometryLineString {
LineString(LineString),
MultiLineString(MultiLineString),
Unknown,
}
#[derive(Debug)]
pub struct MultiLineString {
lines: Vec<LineString>,
}
#[derive(Debug)]
pub struct LineString {
points: Vec<Point>,
}
#[derive(Debug)]
pub enum GeometryPolygon {
Polygon(Polygon),
MultiLineString(MultiPolygon),
Unknown,
}
#[derive(Debug)]
pub struct Polygon {
points: Vec<Point>,
}
#[derive(Debug)]
pub struct MultiPolygon {
polygons: Vec<Polygon>,
}
#[derive(Debug)]
pub enum Geometry {
GeometryPoint(GeometryPoint),
GeometryLineString(GeometryLineString),
GeometryPolygon(GeometryPolygon),
Unknown,
}
impl Point {
pub(crate) fn new(x: Number, y: Number) -> Self {
Self { x, y }
}
}
impl MultiPoint {
pub(crate) fn new(points: Vec<Point>) -> Self {
Self { points }
}
}
impl LineString {
pub(crate) fn new(points: Vec<Point>) -> Self {
Self { points }
}
}
impl MultiLineString {
pub(crate) fn new(lines: Vec<LineString>) -> Self {
Self { lines }
}
}
impl Polygon {
pub(crate) fn new(points: Vec<Point>) -> Self {
Self { points }
}
}
impl MultiPolygon {
pub(crate) fn new(polygons: Vec<Polygon>) -> Self {
Self { polygons }
}
}

View File

@ -0,0 +1,9 @@
mod protos;
mod encoding;
#[cfg(test)]
mod tests;
pub mod tile;
pub mod geometry;

View File

@ -0,0 +1,2 @@
*.rs
!mod.rs

View File

@ -0,0 +1,2 @@
#[path = "vector_tile.rs"]
pub mod vector_tile;

View File

@ -0,0 +1,16 @@
use std::fs::File;
use std::io::BufReader;
use crate::encoding::Decode;
use protobuf::Message;
use crate::protos::vector_tile::Tile;
#[test]
fn it_works() {
let mut f = File::open("libs/vector_tile/test_data/europe.pbf").expect("no file found");
//let mut f = File::open("test_data/europe.pbf").expect("no file found");
let mut reader = BufReader::new(f);
let x = Tile::parse_from_reader(&mut reader).unwrap().decode();
println!("{:#?}", x);
}

View File

@ -0,0 +1,79 @@
use std::collections::HashMap;
use crate::geometry::Geometry;
use crate::protos::vector_tile::{
Tile as ProtoTile, Tile_Feature, Tile_Feature as ProtoFeature, Tile_Layer as ProtoLayer,
};
#[derive(Debug)]
pub struct Tile {
internal: ProtoTile,
layers: Vec<Layer>,
}
#[derive(Debug)]
pub struct Layer {
internal: ProtoLayer,
features: Vec<Feature>,
}
#[derive(Debug)]
pub struct Feature {
internal: ProtoFeature,
geometry: Geometry,
properties: HashMap<String, PropertyValue>,
}
#[derive(Debug)]
pub enum PropertyValue {
StringValue(String),
FloatValue(f32),
DoubleValue(f64),
IntValue(i64),
UIntValue(u64),
SIntValue(i64),
BoolValue(bool),
Unknown,
}
impl Feature {
pub(crate) fn new(
internal: ProtoFeature,
geometry: Geometry,
properties: HashMap<String, PropertyValue>,
) -> Self {
Feature {
internal,
geometry,
properties,
}
}
pub fn id(&self) -> u64 {
self.internal.get_id()
}
}
impl Layer {
pub(crate) fn new(internal: ProtoLayer, features: Vec<Feature>) -> Self {
Layer { internal, features }
}
pub fn extend(&self) -> u32 {
self.internal.get_extent()
}
pub fn version(&self) -> u32 {
self.internal.get_version()
}
pub fn name(&self) -> &str {
self.internal.get_name()
}
}
impl Tile {
pub(crate) fn new(internal: ProtoTile, layers: Vec<Layer>) -> Self {
Tile { internal, layers }
}
}

Binary file not shown.