Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MVT Sink の初期実装 #163

Merged
merged 20 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
- [`macros`](./nusamai-plateau/citygml/macros/) — パーサ導出用の proc macros
- 変換先形式のためのライブラリ:
- [`nusamai-3dtiles`](./nusamai-3dtiles/) — 3D Tiles
- [`nusamai-mvt`](./nusamai-mvt/) — Mapbox Vector Tile (MVT)
- [`nusamai-mvt`](./nusamai-mvt/) — Mapbox Vector Tiles (MVT)
- [`nusamai-gpkg`](./nusamai-gpkg/) — GeoPackage
- [`nusamai-gltf`](./nusamai-gltf/) — glTF
- [`nusamai-geojson`](./nusamai-geojson/) — GeoJSON
Expand Down
6 changes: 3 additions & 3 deletions app/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
use nusamai::pipeline::Canceller;
use nusamai::sink::DataSinkProvider;
use nusamai::sink::{
geojson::GeoJsonSinkProvider, gpkg::GpkgSinkProvider, serde::SerdeSinkProvider,
tiling2d::Tiling2DSinkProvider,
geojson::GeoJsonSinkProvider, gpkg::GpkgSinkProvider, mvt::MVTSinkProvider,
serde::SerdeSinkProvider,
};
use nusamai::source::citygml::CityGMLSourceProvider;
use nusamai::source::DataSourceProvider;
Expand Down Expand Up @@ -52,7 +52,7 @@
"GeoJSON" => Box::new(GeoJsonSinkProvider {}),
"GeoPackage" => Box::new(GpkgSinkProvider {}),
"Serde" => Box::new(SerdeSinkProvider {}),
"Tiling2D" => Box::new(Tiling2DSinkProvider {}),
"Vector Tiles" => Box::new(MVTSinkProvider {}),

Check warning on line 55 in app/src-tauri/src/main.rs

View check run for this annotation

Codecov / codecov/patch

app/src-tauri/src/main.rs#L55

Added line #L55 was not covered by tests
_ => {
log::error!("Unknown filetype: {}", filetype);
return;
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/settings.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const fileTypeOptions = ['GeoJSON', 'GeoPackage', 'Serde', 'Tiling2D'];
const fileTypeOptions = ['GeoJSON', 'GeoPackage', 'Serde', 'Vector Tiles'];

const crsOptions = [
{ value: 'EPSG:6678', label: 'JGD2011 / Japan Plane Rectangular CS X' },
Expand Down
2 changes: 1 addition & 1 deletion nusamai-citygml/src/geometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub enum GeometryType {
}

#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Debug, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub struct GeometryRefEntry {
#[serde(rename = "type")]
pub ty: GeometryType,
Expand Down
6 changes: 3 additions & 3 deletions nusamai-citygml/src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,22 @@ pub struct CityObject {
pub geometries: geometry::GeometryStore,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Feature {
pub typename: Cow<'static, str>,
pub id: Option<String>,
pub attributes: Map,
pub geometries: Option<GeometryRef>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Data {
pub typename: Cow<'static, str>,
pub attributes: Map,
}

/// Nodes for the "Object" representation of the city object.
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum Value {
String(String),
Code(Code),
Expand Down
8 changes: 4 additions & 4 deletions nusamai-citygml/src/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
}
}

#[derive(Debug, serde::Serialize, serde::Deserialize, Default, PartialEq, Eq)]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default, PartialEq, Eq)]
pub struct URI(String);

impl URI {
Expand All @@ -56,7 +56,7 @@
}
}

#[derive(Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Code {
value: String,
code: String,
Expand Down Expand Up @@ -210,7 +210,7 @@
}
}

#[derive(Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct Measure {
pub value: f64,
// pub uom: Option<String>,
Expand Down Expand Up @@ -262,7 +262,7 @@
}
}

#[derive(Debug, Default, Deserialize, Serialize, PartialEq)]
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]

Check warning on line 265 in nusamai-citygml/src/values.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-citygml/src/values.rs#L265

Added line #L265 was not covered by tests
pub struct Point {
// TODO
}
Expand Down
2 changes: 1 addition & 1 deletion nusamai-geojson/src/conversion.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use nusamai_geometry::{CoordNum, MultiLineString, MultiPoint, MultiPolygon};

/// Create a GeoJSON MultiPolygon from `nusamai_geometry::MultiPolygon`.
pub fn multipolygon_to_geometry(mpoly: &MultiPolygon<3>) -> geojson::Geometry {
pub fn multipolygon_to_geometry<const D: usize>(mpoly: &MultiPolygon<D>) -> geojson::Geometry {
multipolygon_to_geometry_with_mapping(mpoly, |c| c.to_vec())
}

Expand Down
20 changes: 14 additions & 6 deletions nusamai-geometry/src/compact/linestring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ impl<'a, const D: usize, T: CoordNum> LineString<'a, D, T> {
}

/// Reverses the coordinates in the LineString.
pub fn reverse(&mut self) {
pub fn reverse_inplace(&mut self) {
let len = self.coords.len();
if len > 0 {
let data = self.coords.to_mut();
Expand All @@ -98,7 +98,7 @@ impl<'a, const D: usize, T: CoordNum> LineString<'a, D, T> {
}

/// Reverses the winding order of the coordinates in the ring, preserving the first coordinate.
pub fn ring_reverse(&mut self) {
pub fn reverse_ring_inplace(&mut self) {
let len = self.coords.len();
if len > D {
let data = self.coords.to_mut();
Expand All @@ -118,6 +118,11 @@ impl<'a, T: CoordNum> LineString<'a, 2, T> {
self.signed_ring_area() > 0.0
}

/// Returns true if the ring is clockwise.
pub fn is_cw(&self) -> bool {
self.signed_ring_area() < 0.0
}

/// Calculates the area of this LineString as a ring.
pub fn ring_area(&self) -> f64 {
self.signed_ring_area().abs()
Expand Down Expand Up @@ -255,28 +260,31 @@ mod tests {
fn test_winding_order() {
let line = LineString2::from_raw(vec![0.0, 0.0, 3.0, 0.0, 3.0, 3.0, 0.0, 3.0].into());
assert!(line.is_ccw());
assert!(!line.is_cw());

let line = LineString2::from_raw(vec![0.0, 0.0, 0.0, 3.0, 3.0, 3.0, 3.0, 0.0].into());
assert!(!line.is_ccw());
assert!(line.is_cw());

let line = LineString2::from_raw(vec![0.0, 0.0, 0.0, 0.0].into());
assert!(!line.is_ccw());
assert!(!line.is_cw());
}

#[test]
fn test_reverse() {
let mut line = LineString2::from_raw(
vec![0.0, 0.0, 3.0, 0.0, 3.0, 1.0, 3.0, 3.0, 1.0, 3.0, 0.0, 3.0].into(),
);
line.reverse();
line.reverse_inplace();
assert_eq!(
line.coords(),
vec![0.0, 3.0, 1.0, 3.0, 3.0, 3.0, 3.0, 1.0, 3.0, 0.0, 0.0, 0.0]
);

let mut line =
LineString2::from_raw(vec![0.0, 0.0, 3.0, 0.0, 6.0, 0.0, 6.0, 3.0, 3.0, 3.0].into());
line.reverse();
line.reverse_inplace();
assert_eq!(
line.coords(),
vec![3.0, 3.0, 6.0, 3.0, 6.0, 0.0, 3.0, 0.0, 0.0, 0.0]
Expand All @@ -288,15 +296,15 @@ mod tests {
let mut line = LineString2::from_raw(
vec![0.0, 0.0, 3.0, 0.0, 3.0, 1.0, 3.0, 3.0, 1.0, 3.0, 0.0, 3.0].into(),
);
line.ring_reverse();
line.reverse_ring_inplace();
assert_eq!(
line.coords(),
vec![0.0, 0.0, 0.0, 3.0, 1.0, 3.0, 3.0, 3.0, 3.0, 1.0, 3.0, 0.0]
);

let mut line =
LineString2::from_raw(vec![0.0, 0.0, 3.0, 0.0, 6.0, 0.0, 6.0, 3.0, 3.0, 3.0].into());
line.ring_reverse();
line.reverse_ring_inplace();
assert_eq!(
line.coords(),
vec![0.0, 0.0, 3.0, 3.0, 6.0, 3.0, 6.0, 0.0, 3.0, 0.0]
Expand Down
11 changes: 8 additions & 3 deletions nusamai-geometry/src/compact/multi_polygon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,14 +205,19 @@ impl<'a, const D: usize, T: CoordNum> MultiPolygon<'a, D, T> {
}

/// Create a new MultiPolygon by applying the given transformation to all coordinates.
pub fn transform(&self, f: impl Fn(&[T; D]) -> [T; D]) -> Self {
Self {
pub fn transform<const D2: usize, T2: CoordNum>(
&self,
f: impl Fn(&[T; D]) -> [T2; D2],
) -> MultiPolygon<D2, T2> {
MultiPolygon {
all_coords: self
.all_coords
.chunks_exact(D)
.flat_map(|v| f(&v.try_into().unwrap()))
.collect(),
..self.clone()
coords_spans: self.coords_spans.clone(),
all_hole_indices: self.all_hole_indices.clone(),
holes_spans: self.holes_spans.clone(),
}
}

Expand Down
21 changes: 18 additions & 3 deletions nusamai-geometry/src/compact/polygon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,28 @@ impl<'a, const D: usize, T: CoordNum> Polygon<'a, D, T> {
.to_mut()
.push((self.coords.len() / D) as u32);
}
let head = self.coords.len();
self.coords.to_mut().extend(iter.into_iter().flatten());

// remove closing point if exists
let tail = self.coords.len();
if tail > head + 2 * D && self.coords[head..head + D] == self.coords[tail - D..] {
self.coords.to_mut().truncate(tail - D);
}
}

/// Create a new Polygon by applying the given transformation to all coordinates.
pub fn transform(&self, f: impl Fn(&[T; D]) -> [T; D]) -> Self {
Self {
pub fn transform<const D2: usize, T2: CoordNum>(
&self,
f: impl Fn(&[T; D]) -> [T2; D2],
) -> Polygon<D2, T2> {
Polygon {
coords: self
.coords
.chunks_exact(D)
.flat_map(|v| f(&v.try_into().unwrap()))
.collect(),
..self.clone()
hole_indices: self.hole_indices.clone(),
}
}

Expand Down Expand Up @@ -234,6 +244,11 @@ mod tests {
polygon.add_ring([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]);
assert_eq!(polygon.exterior().len(), 3);
assert_eq!(polygon.interiors().count(), 1);

let mut polygon = Polygon2::new();
polygon.add_ring([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [0.0, 0.0]]);
assert_eq!(polygon.exterior().len(), 3);
assert_eq!(polygon.interiors().count(), 0);
}

#[test]
Expand Down
4 changes: 4 additions & 0 deletions nusamai-mvt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ version = "0.1.0"
edition = "2021"

[dependencies]
prost = "0.12.3"

[build-dependencies]
prost-build = "0.12.3"
17 changes: 17 additions & 0 deletions nusamai-mvt/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use std::env;
use std::io::Result;
use std::path::Path;

fn main() -> Result<()> {
if !Path::new("src/vector_tile.rs").exists() {
let prev_out_dir = env::var("OUT_DIR");

env::set_var("OUT_DIR", "src/");
prost_build::compile_protos(&["src/vector_tile.proto"], &["src"])?;

if let Ok(prev) = prev_out_dir {
env::set_var("OUT_DIR", prev);
}
}
Ok(())
}
68 changes: 68 additions & 0 deletions nusamai-mvt/src/geometry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const GEOM_COMMAND_MOVE_TO: u32 = 1;
const GEOM_COMMAND_LINE_TO: u32 = 2;
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;

pub struct GeometryEncoder {
buf: Vec<u32>,
prev_x: i16,
prev_y: i16,
}

impl GeometryEncoder {
// TODO: with_capacity
pub fn new() -> Self {
Self {
buf: Vec::new(),
prev_x: 0,
prev_y: 0,
}
}

pub fn into_vec(self) -> Vec<u32> {
self.buf
}

pub fn add_ring(&mut self, mut iter: impl Iterator<Item = [i16; 2]>) {
let Some([first_x, first_y]) = iter.next() else {
return;

Check warning on line 30 in nusamai-mvt/src/geometry.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-mvt/src/geometry.rs#L30

Added line #L30 was not covered by tests
};
let dx = (first_x - self.prev_x) as i32;
let dy = (first_y - self.prev_y) as i32;
(self.prev_x, self.prev_y) = (first_x, first_y);

// move to
self.buf.push(GEOM_COMMAND_MOVE_TO_WITH_COUNT1);
self.buf.push(((dx << 1) ^ (dx >> 31)) as u32);
self.buf.push(((dy << 1) ^ (dy >> 31)) as u32);

// line to
let lineto_cmd_pos = self.buf.len();
self.buf.push(GEOM_COMMAND_LINE_TO); // length will be updated later
let mut count = 0;
for [x, y] in iter {
let dx = (x - self.prev_x) as i32;
let dy = (y - self.prev_y) as i32;
(self.prev_x, self.prev_y) = (x, y);

if dx != 0 || dy != 0 {
self.buf.push(((dx << 1) ^ (dx >> 31)) as u32);
self.buf.push(((dy << 1) ^ (dy >> 31)) as u32);
count += 1;
}
}
assert!(count >= 2);
self.buf[lineto_cmd_pos] = GEOM_COMMAND_LINE_TO | count << 3;

// close path
self.buf.push(GEOM_COMMAND_CLOSE_PATH_WITH_COUNT1);
}
}

impl Default for GeometryEncoder {
fn default() -> Self {
Self::new()
}

Check warning on line 67 in nusamai-mvt/src/geometry.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-mvt/src/geometry.rs#L65-L67

Added lines #L65 - L67 were not covered by tests
}
4 changes: 4 additions & 0 deletions nusamai-mvt/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
pub mod geometry;
pub mod tileid;
pub mod vector_tile;
pub mod webmercator;

pub type TileZXY = (u8, u32, u32);
1 change: 1 addition & 0 deletions nusamai-mvt/src/tileid/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod hilbert;

/// Tile ID calculation method
#[derive(Clone, Copy, Debug)]

Check warning on line 4 in nusamai-mvt/src/tileid/mod.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-mvt/src/tileid/mod.rs#L4

Added line #L4 was not covered by tests
pub enum TileIdMethod {
/// Tile ID based on Hilbert curve (compliant with PMTiles)
Hilbert,
Expand Down
Loading