Skip to content

Commit

Permalink
nusamai-geosjon: トップレベル都市オブジェクトを変換(ジオメトリのみ) (#79)
Browse files Browse the repository at this point in the history
close #62

- 従来のコードは、 `nusamai-geometry` の Geometryを直接扱って、変換していた
- → 「トップレベル都市オブジェクト」を入力として、GeoJSONへの変換処理を行う
  - #78 で用意されたものを利用している

`Geometries` にある `multipolygon`, `multilinestring`, `multipoint`
、それぞれをFeatureにしている。

- [x] 変換処理の書き換え
  - `nusamai-geojson/src/conversion.rs`
  - `nusamai-geojson/src/lib.rs`
- [x] CityGMLファイルをパースしてGeoJSONを出力するexampleの追加
  - `nusamai-geojson/examples/gml2geojson.rs`
- その他
  - [x] Tauri側の暫定処理を、この変更に合わせて修正: `app/src-tauri/*`
- [x] いにしえのパースexampleの削除: `nusamai-geojson/examples/citygml_polygons.rs`

属性はこのPRでは考慮していない。

トップレベル都市オブジェクトの ` cityobj: FeatureOrData<'a>` を扱う必要がある。

別途対応する #46
  • Loading branch information
sorami authored and ciscorn committed Dec 27, 2023
1 parent a5f4fda commit 396f1a1
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 25 deletions.
40 changes: 38 additions & 2 deletions app/src-tauri/src/example.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,50 @@
//! デモ用
//! nusamai-geojson の exmaple/gml2geojson を元にした、暫定的な処理
//! nusamai-geojson の exmaple/gml2geojson を元にした、暫定的な処理
use citygml::{CityGMLElement, CityGMLReader, ParseError, SubTreeReader};
use nusamai_geojson::toplevel_cityobj_to_geojson_features;
use nusamai_plateau::TopLevelCityObject;
use citygml::{CityGMLElement, CityGMLReader, ParseError, SubTreeReader};
use nusamai_geojson::toplevel_cityobj_to_geojson_features;
use nusamai_plateau::TopLevelCityObject;
use std::fs;
use std::io::BufRead;
use std::io::BufRead;
use std::io::BufWriter;

fn toplevel_dispatcher<R: BufRead>(
st: &mut SubTreeReader<R>,
) -> Result<Vec<TopLevelCityObject>, ParseError> {
let mut cityobjs: Vec<TopLevelCityObject> = vec![];

match st.parse_children(|st| match st.current_path() {
b"core:cityObjectMember" => {
let mut cityobj: nusamai_plateau::models::TopLevelCityObject = Default::default();
cityobj.parse(st)?;
let geometries = st.collect_geometries();

if let Some(root) = cityobj.into_object() {
let obj = TopLevelCityObject { root, geometries };
cityobjs.push(obj);
}

Ok(())
}
b"gml:boundedBy" | b"app:appearanceMember" => {
st.skip_current_element()?;
Ok(())
}
other => Err(ParseError::SchemaViolation(format!(
"Unrecognized element {}",
String::from_utf8_lossy(other)
))),
}) {
Ok(_) => Ok(cityobjs),
Err(e) => {
println!("Err: {:?}", e);
Err(e)

fn toplevel_dispatcher<R: BufRead>(
st: &mut SubTreeReader<R>,
) -> Result<Vec<TopLevelCityObject>, ParseError> {
Expand Down Expand Up @@ -47,8 +84,7 @@ pub fn citygml_to_geojson(input_path: &str, output_path: &str) {
let reader = std::io::BufReader::new(std::fs::File::open(input_path).unwrap());
let mut xml_reader = quick_xml::NsReader::from_reader(reader);

let context = citygml::ParseContext::default();
let cityobjs = match CityGMLReader::new(context).start_root(&mut xml_reader) {
let cityobjs = match CityGMLReader::new().start_root(&mut xml_reader) {
Ok(mut st) => match toplevel_dispatcher(&mut st) {
Ok(items) => items,
Err(e) => panic!("Err: {:?}", e),
Expand Down
4 changes: 2 additions & 2 deletions nusamai-geojson/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ edition = "2021"
geojson = "0.24.1"
nusamai-geometry = { path = "../nusamai-geometry" }
nusamai-plateau = { path = "../nusamai-plateau" }
citygml = {path = "../nusamai-plateau/citygml" }
serde_json = "1.0.108"

[dev-dependencies]
citygml = {path = "../nusamai-plateau/citygml" }
clap = { version = "4.4.8", features = ["derive"] }
quick-xml = "0.31.0"
serde_json = "1.0.108"
6 changes: 3 additions & 3 deletions nusamai-geojson/examples/gml2geojson.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use citygml::{CityGMLElement, CityGMLReader, ParseError, SubTreeReader};
use clap::Parser;
use nusamai_geojson::toplevel_cityobj_to_geojson_features;
use nusamai_plateau::models::CityObject;
use nusamai_plateau::TopLevelCityObject;
use std::fs;
use std::io::BufRead;
Expand All @@ -21,7 +22,7 @@ fn toplevel_dispatcher<R: BufRead>(

match st.parse_children(|st| match st.current_path() {
b"core:cityObjectMember" => {
let mut cityobj: nusamai_plateau::models::TopLevelCityObject = Default::default();
let mut cityobj: CityObject = Default::default();
cityobj.parse(st)?;
let geometries = st.collect_geometries();

Expand Down Expand Up @@ -55,8 +56,7 @@ fn main() {
let reader = std::io::BufReader::new(std::fs::File::open(args.filename).unwrap());
let mut xml_reader = quick_xml::NsReader::from_reader(reader);

let context = citygml::ParseContext::default();
let cityobjs = match CityGMLReader::new(context).start_root(&mut xml_reader) {
let cityobjs = match CityGMLReader::new().start_root(&mut xml_reader) {
Ok(mut st) => match toplevel_dispatcher(&mut st) {
Ok(items) => items,
Err(e) => panic!("Err: {:?}", e),
Expand Down
23 changes: 5 additions & 18 deletions nusamai-geojson/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,28 @@
mod conversion;
use citygml::attribute_to_json;

use conversion::{
multilinestring_to_geojson_geometry, multipoint_to_geojson_geometry,
multipolygon_to_geojson_geometry,
};
use nusamai_plateau::TopLevelCityObject;
use serde_json::Value;

fn extract_attributes(obj: &TopLevelCityObject) -> serde_json::Map<String, Value> {
let mut attributes = serde_json::Map::new();

if let citygml::ObjectValue::FeatureOrData(fod) = &obj.root {
let a = attribute_to_json(fod);
attributes = a.as_object().unwrap().clone();
}
attributes
}

/// Create GeoJSON features from a TopLevelCityObject
/// Each feature for MultiPolygon, MultiLineString, and MultiPoint will be created (if it exists)
// TODO: Handle properties (`obj.root` -> `geojson::Feature.properties`)
// TODO: We may want to traverse the tree and create features for each semantic child in the future
pub fn toplevel_cityobj_to_geojson_features(obj: &TopLevelCityObject) -> Vec<geojson::Feature> {
let mut geojson_features: Vec<geojson::Feature> = vec![];
let attributes = extract_attributes(obj);

if !obj.geometries.multipolygon.is_empty() {
let mpoly_geojson_geom = multipolygon_to_geojson_geometry(
&obj.geometries.vertices,
&obj.geometries.multipolygon,
);

let mpoly_geojson_feat = geojson::Feature {
bbox: None,
geometry: Some(mpoly_geojson_geom),
id: None,
properties: Some(attributes.clone().into_iter().collect()),
properties: None, // TODO: from `obj.root`
foreign_members: None,
};
geojson_features.push(mpoly_geojson_feat);
Expand All @@ -50,7 +37,7 @@ pub fn toplevel_cityobj_to_geojson_features(obj: &TopLevelCityObject) -> Vec<geo
bbox: None,
geometry: Some(mls_geojson_geom),
id: None,
properties: Some(attributes.clone().into_iter().collect()),
properties: None, // TODO: from `obj.root``
foreign_members: None,
};
geojson_features.push(mls_geojson_feat);
Expand All @@ -63,7 +50,7 @@ pub fn toplevel_cityobj_to_geojson_features(obj: &TopLevelCityObject) -> Vec<geo
bbox: None,
geometry: Some(mpoint_geojson_geom),
id: None,
properties: Some(attributes.clone().into_iter().collect()),
properties: None, // TODO: from `obj.root``
foreign_members: None,
};
geojson_features.push(mpoint_geojson_feat);
Expand Down Expand Up @@ -103,7 +90,7 @@ mod tests {
let geojson_features = toplevel_cityobj_to_geojson_features(&obj);
assert_eq!(geojson_features.len(), 1);

let mpoly_geojson = geojson_features.first().unwrap();
let mpoly_geojson = geojson_features.get(0).unwrap();
assert!(mpoly_geojson.bbox.is_none());
assert!(mpoly_geojson.foreign_members.is_none());
if let geojson::Value::MultiPolygon(rings_list) =
Expand Down

0 comments on commit 396f1a1

Please sign in to comment.