Skip to content

Commit

Permalink
Transformerでの仮のSwap X-Y、GeoJSON, Gpkg ユーティリティのリファクタリング (#153)
Browse files Browse the repository at this point in the history
## 変更内容

### 仮のTransformerで「とりあえず」の変換処理を行う
- x-y 入れかえ処理を入れておく
    - これにともない GeoJSON, GeoPackage ドライバ内での x-y 入れかえは除去する
- WGS 84 にしておく処理も追加 #155

### Gpkg, GeoJSON 周りのリファクタリング
- `nusamai-geojson` に "indexed" でないふつうのジオメトリをGeoJSON
Valueに変換する関数を追加(tiling2d sink で使う)。
    - #154 もこのPRにマージ
- Gpkg のバイナリジオメトリの構築で、 `Vec<u8>` でなく `std::io::Write` trait
を使う(`Vec<u8>`はWrite)。一時的なVecの生成も除去。

---

Closes: #125
Closes: #76
  • Loading branch information
ciscorn authored Jan 8, 2024
1 parent 63874e9 commit 4eafe35
Show file tree
Hide file tree
Showing 14 changed files with 356 additions and 205 deletions.
1 change: 1 addition & 0 deletions nusamai-citygml/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ chrono = { version = "0.4.31", features = ["serde"], default-features = false }
indexmap = { version = "2.1", features = ["serde"] }
macros = { path = "./macros" }
nusamai-geometry = { path = "../nusamai-geometry", features = ["serde"]}
nusamai-projection = { path = "../nusamai-projection"}
quick-xml = "0.31"
serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = { version = "1.0.108", optional = true }
Expand Down
23 changes: 10 additions & 13 deletions nusamai-citygml/src/geometry.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use nusamai_geometry::{MultiLineString, MultiPoint, MultiPolygon};
use nusamai_projection::crs::*;

#[derive(Debug, Clone, Copy)]
pub enum GeometryParseType {
Expand Down Expand Up @@ -35,28 +36,24 @@ pub struct GeometryRefEntry {

pub type GeometryRef = Vec<GeometryRefEntry>;

#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Debug, Default)]
pub enum CRS {
#[default]
WGS84,
JGD2011,
}

/// Geometries in a toplevel city object and its children.
/// Geometries in a city object and all its children.
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Debug, Default)]
pub struct GeometryStore {
pub crs: CRS,
/// EPSG code of the Coordinate Reference System (CRS)
pub epsg: EPSGCode,
/// Shared vertex buffer for all geometries
pub vertices: Vec<[f64; 3]>,
/// All polygons, referenced by `GeometryRef`
pub multipolygon: MultiPolygon<'static, 1, u32>,
/// All line-strings of , referenced by `GeometryRef`
pub multilinestring: MultiLineString<'static, 1, u32>,
/// All points, referenced by `GeometryRef`
pub multipoint: MultiPoint<'static, 1, u32>,
}

/// Store for collecting vertices and polygons from GML.
#[derive(Default)]
pub struct GeometryCollector {
pub(crate) struct GeometryCollector {
pub vertices: indexmap::IndexSet<[u64; 3]>,
pub multipolygon: MultiPolygon<'static, 1, u32>,
pub multilinestring: MultiLineString<'static, 1, u32>,
Expand Down Expand Up @@ -94,7 +91,7 @@ impl GeometryCollector {
]);
}
GeometryStore {
crs: CRS::JGD2011,
epsg: EPSG_JGD2011_GEOGRAPHIC_3D,
vertices,
multipolygon: self.multipolygon,
multilinestring: self.multilinestring,
Expand Down
208 changes: 176 additions & 32 deletions nusamai-geojson/src/conversion.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,91 @@
use nusamai_geometry::{MultiLineString, MultiPoint, MultiPolygon, Polygon};
use nusamai_geometry::{CoordNum, MultiLineString, MultiPoint, MultiPolygon};

/// Create a GeoJSON geometry from nusamai_citygml::CityObject's `multipolygon` geometry
pub fn multipolygon_to_geojson_geometry(
/// Create a GeoJSON MultiPolygon from `nusamai_geometry::MultiPolygon`.
pub fn multipolygon_to_geometry(mpoly: &MultiPolygon<3>) -> geojson::Geometry {
multipolygon_to_geometry_with_mapping(mpoly, |c| c.to_vec())
}

/// Create a GeoJSON MultiPolygon from vertices and indices.
pub fn indexed_multipolygon_to_geometry(
vertices: &[[f64; 3]],
mpoly: &MultiPolygon<1, u32>,
mpoly_idx: &MultiPolygon<1, u32>,
) -> geojson::Geometry {
multipolygon_to_geometry_with_mapping(mpoly_idx, |idx| vertices[idx[0] as usize].to_vec())
}

/// Create a GeoJSON MultiPolygon from `nusamai_geometry::MultiPolygon` with a mapping function.
pub fn multipolygon_to_geometry_with_mapping<const D: usize, T: CoordNum>(
mpoly: &MultiPolygon<D, T>,
mapping: impl Fn(&[T]) -> Vec<f64>,
) -> geojson::Geometry {
let ring_list: Vec<geojson::PolygonType> = mpoly
let coords: Vec<geojson::PolygonType> = mpoly
.iter()
.map(|poly| polygon_to_rings(vertices, &poly))
.map(|poly| {
poly.rings()
.map(|ls| {
ls.iter_closed()
.map(&mapping) // Get the actual coord values
.collect()
})
.collect::<Vec<_>>()
})
.collect();
geojson::Value::MultiPolygon(ring_list).into()
geojson::Value::MultiPolygon(coords).into()
}

fn polygon_to_rings(vertices: &[[f64; 3]], poly: &Polygon<1, u32>) -> geojson::PolygonType {
poly.rings()
.map(|ls| {
ls.iter_closed()
.map(|idx| vertices[idx[0] as usize].to_vec()) // Get the actual coord values
.collect()
})
.collect()
/// Create a GeoJSON MultiLineString from `nusamai_geometry::MultiLineString`.
pub fn multilinestring_to_geometry(mls: &MultiLineString<3>) -> geojson::Geometry {
multilinestring_to_geometry_with_mapping(mls, |c| c.to_vec())
}

/// Create a GeoJSON geometry from nusamai_citygml::CityObject's `multilinestring` geometry
pub fn multilinestring_to_geojson_geometry(
/// Create a GeoJSON MultiLineString from vertices and indices.
pub fn indexed_multilinestring_to_geometry(
vertices: &[[f64; 3]],
mls: &MultiLineString<1, u32>,
mls_idx: &MultiLineString<1, u32>,
) -> geojson::Geometry {
let mls_coords: Vec<geojson::LineStringType> = mls
multilinestring_to_geometry_with_mapping(mls_idx, |idx| vertices[idx[0] as usize].to_vec())
}

/// Create a GeoJSON MultiLineString from `nusamai_geometry::MultiPolygon` with a mapping function.
pub fn multilinestring_to_geometry_with_mapping<const D: usize, T: CoordNum>(
mls: &MultiLineString<D, T>,
mapping: impl Fn(&[T]) -> Vec<f64>,
) -> geojson::Geometry {
let coords = mls
.iter()
.map(|ls| {
ls.iter()
.map(|idx| vertices[idx[0] as usize].to_vec()) // Get the actual coord values
.map(|ls_idx| {
ls_idx
.iter()
.map(&mapping) // Get the actual coord values
.collect()
})
.collect();
geojson::Value::MultiLineString(mls_coords).into()
geojson::Value::MultiLineString(coords).into()
}

/// Create a GeoJSON MultiPoint from `nusamai_geometry::MultiPoint`.
pub fn multipoint_to_geometry(mpoint: &MultiPoint<3>) -> geojson::Geometry {
multipoint_to_geometry_with_mapping(mpoint, |c| c.to_vec())
}

/// Create a GeoJSON geometry from nusamai_citygml::CityObject's `multipoint` geometry
pub fn multipoint_to_geojson_geometry(
/// Create a GeoJSON MultiPoint from vertices and indices.
pub fn indexed_multipoint_to_geometry(
vertices: &[[f64; 3]],
mpoint: &MultiPoint<1, u32>,
mpoint_idx: &MultiPoint<1, u32>,
) -> geojson::Geometry {
multipoint_to_geometry_with_mapping(mpoint_idx, |idx| vertices[idx[0] as usize].to_vec())
}

/// Create a GeoJSON MultiPoint from `nusamai_geometry::MultiPoint` with a mapping function.
pub fn multipoint_to_geometry_with_mapping<const D: usize, T: CoordNum>(
mpoint: &MultiPoint<D, T>,
mapping: impl Fn(&[T]) -> Vec<f64>,
) -> geojson::Geometry {
let mpoint_coords: Vec<geojson::PointType> = mpoint
let coords = mpoint
.iter()
.map(|p| vertices[p[0] as usize].to_vec()) // Get the actual coord values
.map(&mapping) // Get the actual coord values
.collect();
geojson::Value::MultiPoint(mpoint_coords).into()
geojson::Value::MultiPoint(coords).into()
}

#[cfg(test)]
Expand All @@ -56,6 +94,64 @@ mod tests {

#[test]
fn test_multipolygon() {
let mut mpoly = MultiPolygon::<3>::new();
// 1st polygon
mpoly.add_exterior([
[0., 0., 0.],
[0., 10., 0.],
[10., 10., 0.],
[10., 0., 0.],
[0., 0., 0.], // closed
]);
// polygon
mpoly.add_exterior([
[10., 10., 0.],
[10., 20., 0.],
[20., 20., 0.],
[20., 10., 0.], // not closed
]);
mpoly.add_interior([
[15., 15., 0.],
[18., 10., 0.],
[18., 18., 0.],
[15., 18., 0.],
]);
let geom = multipolygon_to_geometry(&mpoly);
let geojson::Value::MultiPolygon(mpoly) = geom.value else {
panic!("The result is not a GeoJSON MultiPolygon");

Check warning on line 121 in nusamai-geojson/src/conversion.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-geojson/src/conversion.rs#L121

Added line #L121 was not covered by tests
};
assert_eq!(
mpoly,
vec![
vec![vec![
vec![0., 0., 0.],
vec![0., 10., 0.],
vec![10., 10., 0.],
vec![10., 0., 0.],
vec![0., 0., 0.],
]],
vec![
vec![
vec![10., 10., 0.],
vec![10., 20., 0.],
vec![20., 20., 0.],
vec![20., 10., 0.],
vec![10., 10., 0.],
],
vec![
vec![15., 15., 0.],
vec![18., 10., 0.],
vec![18., 18., 0.],
vec![15., 18., 0.],
vec![15., 15., 0.],
],
],
],
);
}

#[test]
fn test_indexed_multipolygon() {
let vertices: Vec<[f64; 3]> = vec![
// 1st polygon, exterior (vertex 0~3)
[0., 0., 111.],
Expand Down Expand Up @@ -100,7 +196,7 @@ mod tests {
// 3rd polygon
mpoly.add_exterior([[20], [21], [22], [23], [20]]);

let geojson_geometry = multipolygon_to_geojson_geometry(&vertices, &mpoly);
let geojson_geometry = indexed_multipolygon_to_geometry(&vertices, &mpoly);

assert!(geojson_geometry.bbox.is_none());
assert!(geojson_geometry.foreign_members.is_none());
Expand Down Expand Up @@ -192,6 +288,33 @@ mod tests {

#[test]
fn test_multilinestring() {
let mut mls = MultiLineString::<3>::new();
mls.add_linestring([[11., 12., 13.], [21., 22., 23.], [31., 32., 33.]]);
mls.add_linestring([[111., 112., 113.], [121., 122., 123.], [131., 132., 133.]]);

let geom = multilinestring_to_geometry(&mls);
let geojson::Value::MultiLineString(mls) = geom.value else {
panic!("The result is not a GeoJSON MultiPolygon");

Check warning on line 297 in nusamai-geojson/src/conversion.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-geojson/src/conversion.rs#L297

Added line #L297 was not covered by tests
};
assert_eq!(
mls,
vec![
vec![
vec![11., 12., 13.],
vec![21., 22., 23.],
vec![31., 32., 33.],
],
vec![
vec![111., 112., 113.],
vec![121., 122., 123.],
vec![131., 132., 133.],
],
]
);
}

#[test]
fn test_indexed_multilinestring() {
let vertices = vec![
// 1st linestring
[0., 0., 111.],
Expand All @@ -210,7 +333,7 @@ mod tests {
mls.add_linestring([[2], [3]]);
mls.add_linestring([[4], [5], [6]]);

let geojson_geometry = multilinestring_to_geojson_geometry(&vertices, &mls);
let geojson_geometry = indexed_multilinestring_to_geometry(&vertices, &mls);

assert!(geojson_geometry.bbox.is_none());
assert!(geojson_geometry.foreign_members.is_none());
Expand Down Expand Up @@ -244,13 +367,34 @@ mod tests {

#[test]
fn test_multipoint() {
let mut mpoint = MultiPoint::<3>::new();
mpoint.push(&[11., 12., 13.]);
mpoint.push(&[21., 22., 23.]);
mpoint.push(&[31., 32., 33.]);

let geom = multipoint_to_geometry(&mpoint);
let geojson::Value::MultiPoint(mpoint) = geom.value else {
panic!("The result is not a GeoJSON MultiPolygon");

Check warning on line 377 in nusamai-geojson/src/conversion.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-geojson/src/conversion.rs#L377

Added line #L377 was not covered by tests
};
assert_eq!(
mpoint,
vec![
vec![11., 12., 13.],
vec![21., 22., 23.],
vec![31., 32., 33.],
]
);
}

#[test]
fn test_indexed_multipoint() {
let vertices = vec![[0., 0., 111.], [1., 2., 222.], [3., 4., 333.]];
let mut mpoint = MultiPoint::<1, u32>::new();
mpoint.push(&[0]);
mpoint.push(&[1]);
mpoint.push(&[2]);

let geojson_geometry = multipoint_to_geojson_geometry(&vertices, &mpoint);
let geojson_geometry = indexed_multipoint_to_geometry(&vertices, &mpoint);

assert!(geojson_geometry.bbox.is_none());
assert!(geojson_geometry.foreign_members.is_none());
Expand Down
Loading

0 comments on commit 4eafe35

Please sign in to comment.