mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Add vector_tile crate for decoding vector tiles
This commit is contained in:
parent
b887aadcc0
commit
6c4ab27bb1
19
libs/vector_tile/Cargo.toml
Normal file
19
libs/vector_tile/Cargo.toml
Normal 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
13
libs/vector_tile/build.rs
Normal 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
1
libs/vector_tile/spec
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit c8d178da65cce0ea474e3d99e3368970ce217093
|
||||
185
libs/vector_tile/src/encoding.rs
Normal file
185
libs/vector_tile/src/encoding.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
99
libs/vector_tile/src/geometry.rs
Normal file
99
libs/vector_tile/src/geometry.rs
Normal 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 }
|
||||
}
|
||||
}
|
||||
9
libs/vector_tile/src/lib.rs
Normal file
9
libs/vector_tile/src/lib.rs
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
mod protos;
|
||||
mod encoding;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub mod tile;
|
||||
pub mod geometry;
|
||||
2
libs/vector_tile/src/protos/.gitignore
vendored
Normal file
2
libs/vector_tile/src/protos/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.rs
|
||||
!mod.rs
|
||||
2
libs/vector_tile/src/protos/mod.rs
Normal file
2
libs/vector_tile/src/protos/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
#[path = "vector_tile.rs"]
|
||||
pub mod vector_tile;
|
||||
16
libs/vector_tile/src/tests.rs
Normal file
16
libs/vector_tile/src/tests.rs
Normal 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);
|
||||
}
|
||||
79
libs/vector_tile/src/tile.rs
Normal file
79
libs/vector_tile/src/tile.rs
Normal 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 }
|
||||
}
|
||||
}
|
||||
BIN
libs/vector_tile/test_data/europe.pbf
Normal file
BIN
libs/vector_tile/test_data/europe.pbf
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user