Skip to content

Commit

Permalink
shapefile: Data を別ファイルとして出力できるようにする (#444)
Browse files Browse the repository at this point in the history
  • Loading branch information
nokonoko1203 authored Mar 10, 2024
1 parent a3e6586 commit 1feb0ec
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 203 deletions.
2 changes: 1 addition & 1 deletion nusamai-citygml/src/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl CityGmlElement for Code {
fn parse<R: BufRead>(&mut self, st: &mut SubTreeReader<R>) -> Result<(), ParseError> {
let code_space = st.find_codespace_attr();
let code = st.parse_text()?.to_string();
self.code = code.clone();
self.code.clone_from(&code);

if let Some(code_space) = code_space {
let base_url = st.context().source_url();
Expand Down
2 changes: 1 addition & 1 deletion nusamai/src/sink/cesiumtiles/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ pub fn slice_to_tiles<E>(

// Send tiled features
for ((z, x, y), mut sliced_feature) in sliced_tiles {
sliced_feature.materials = materials.clone();
sliced_feature.materials.clone_from(&materials);
send_feature((z, x, y), sliced_feature)?;
}
Ok(())
Expand Down
262 changes: 112 additions & 150 deletions nusamai/src/sink/shapefile/attributes.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use chrono::Datelike;
use hashbrown::HashMap;
use indexmap::IndexMap;
use shapefile::dbase::{self, Date, FieldValue, Record};

use nusamai_citygml::object::Map;
use nusamai_citygml::object::Value;
use nusamai_citygml::schema::TypeRef;
use shapefile::Shape;
Expand All @@ -12,53 +12,70 @@ pub struct FieldInfo {
size: u8,
}

pub type FieldInfoList = HashMap<String, FieldInfo>;
pub type FieldInfoMap = HashMap<String, FieldInfo>;
pub type Features = Vec<(Shape, Map)>;

pub type Features = Vec<(Shape, IndexMap<String, Value, ahash::RandomState>)>;
pub fn make_table_builder(fields: &FieldInfoMap) -> dbase::TableWriterBuilder {
let mut builder = dbase::TableWriterBuilder::new();

pub struct TableBuilder {
pub fields: FieldInfoList,
pub builder: dbase::TableWriterBuilder,
}
for (field_name, field_info) in fields {
let name = field_name.as_str().try_into().unwrap(); // FIXME: handle errors

impl TableBuilder {
pub fn new(fields: FieldInfoList) -> Self {
Self {
fields,
builder: dbase::TableWriterBuilder::new(),
match field_info.field_type {
TypeRef::String | TypeRef::Code | TypeRef::URI => {
builder = builder.add_character_field(name, field_info.size);
}
TypeRef::Integer | TypeRef::NonNegativeInteger => {
builder = builder.add_integer_field(name);
}
TypeRef::Double | TypeRef::Measure => {
builder = builder.add_float_field(name, 50, 10);
}
TypeRef::Boolean => {
builder = builder.add_logical_field(name);
}
TypeRef::Date => {
builder = builder.add_date_field(name);
}
TypeRef::Point => {
// todo
}
TypeRef::Unknown => {
// todo
}
TypeRef::Named(_) => {
// todo
}
TypeRef::JsonString(_) => {
// todo
}
TypeRef::DateTime => {
// todo
}
}
}
pub fn build(mut self) -> dbase::TableWriterBuilder {
for (field_name, field_info) in self.fields {

builder
}

pub fn fill_missing_fields(attributes: &mut Map, field_info: &FieldInfoMap) {
for (field_name, field_info) in field_info {
if !attributes.contains_key(field_name.as_str()) {
match field_info.field_type {
TypeRef::String | TypeRef::Code | TypeRef::URI => {
self.builder = self.builder.add_character_field(
field_name.as_str().try_into().unwrap(),
field_info.size,
);
attributes.insert(field_name.clone(), Value::String("".to_string()));
}
TypeRef::Integer | TypeRef::NonNegativeInteger => {
self.builder = self
.builder
.add_integer_field(field_name.as_str().try_into().unwrap());
attributes.insert(field_name.clone(), Value::Integer(0));
}
// Handle as Float
TypeRef::Double | TypeRef::Measure => {
self.builder = self.builder.add_float_field(
field_name.as_str().try_into().unwrap(),
50,
10,
);
attributes.insert(field_name.clone(), Value::Double(0.0));
}
TypeRef::Boolean => {
self.builder = self
.builder
.add_logical_field(field_name.as_str().try_into().unwrap());
attributes.insert(field_name.clone(), Value::String("".to_string()));
}
TypeRef::Date => {
self.builder = self
.builder
.add_date_field(field_name.as_str().try_into().unwrap());
attributes.insert(field_name.clone(), Value::String("".to_string()));
}
TypeRef::Point => {
// todo
Expand All @@ -77,53 +94,11 @@ impl TableBuilder {
}
}
}
self.builder
}
}

pub fn fill_missing_fields(features: &mut Features, field_info: &FieldInfoList) {
for (_, attributes) in features.iter_mut() {
for (field_name, field_info) in field_info {
if !attributes.contains_key(field_name.as_str()) {
match field_info.field_type {
TypeRef::String | TypeRef::Code | TypeRef::URI => {
attributes.insert(field_name.clone(), Value::String("".to_string()));
}
TypeRef::Integer | TypeRef::NonNegativeInteger => {
attributes.insert(field_name.clone(), Value::Integer(0));
}
TypeRef::Double | TypeRef::Measure => {
attributes.insert(field_name.clone(), Value::Double(0.0));
}
TypeRef::Boolean => {
attributes.insert(field_name.clone(), Value::String("".to_string()));
}
TypeRef::Date => {
attributes.insert(field_name.clone(), Value::String("".to_string()));
}
TypeRef::Point => {
// todo
}
TypeRef::Unknown => {
// todo
}
TypeRef::Named(_) => {
// todo
}
TypeRef::JsonString(_) => {
// todo
}
TypeRef::DateTime => {
// todo
}
}
}
}
}
}

pub fn make_field_list(features: &Features) -> FieldInfoList {
let mut fields: FieldInfoList = Default::default();
pub fn make_field_list(features: &Features) -> FieldInfoMap {
let mut fields: FieldInfoMap = Default::default();

for (_, attributes) in features {
for (field_name, field_value) in attributes {
Expand Down Expand Up @@ -188,81 +163,68 @@ pub fn make_field_list(features: &Features) -> FieldInfoList {
fields
}

pub fn prepare_shapefile_attributes(features: &Features) -> Vec<Record> {
let mut records = Vec::new();
for (_, feature_attributes) in features.iter() {
let mut record = dbase::Record::default();
pub fn attributes_to_record(attributes: Map) -> Record {
let mut record = dbase::Record::default();

for (attr_name, attr_value) in feature_attributes {
match attr_value {
Value::String(s) => {
// Shapefile string type can only store up to 255 characters.
if s.len() > 255 {
log::warn!("{} value too long, truncating to 255 characters", attr_name);
record.insert(
attr_name.into(),
FieldValue::Character(Some(s[0..255].to_owned())),
);
} else {
record.insert(attr_name.into(), FieldValue::Character(Some(s.to_owned())));
}
}
Value::Code(c) => {
// value of the code
record.insert(
attr_name.into(),
FieldValue::Character(Some(c.value().to_owned())),
);
}
Value::Integer(i) => {
record.insert(attr_name.into(), FieldValue::Integer(i.to_owned() as i32));
}
Value::NonNegativeInteger(i) => {
record.insert(attr_name.into(), FieldValue::Integer(i.to_owned() as i32));
for (attr_name, attr_value) in attributes {
match attr_value {
Value::String(s) => {
// Shapefile string type can only store up to 255 characters.
if s.len() > 255 {
log::warn!("{} value too long, truncating to 255 characters", attr_name);
record.insert(attr_name, FieldValue::Character(Some(s[0..255].to_owned())));
} else {
record.insert(attr_name, FieldValue::Character(Some(s.to_owned())));
}
// Handle as Float
Value::Double(d) => {
record.insert(
attr_name.into(),
FieldValue::Float(Some(d.to_owned() as f32)),
);
}
// Handle as Float
Value::Measure(m) => {
record.insert(
attr_name.into(),
FieldValue::Float(Some(m.value().to_owned() as f32)),
);
}
Value::Boolean(b) => {
record.insert(attr_name.into(), FieldValue::Logical(Some(b.to_owned())));
}
Value::Uri(u) => {
record.insert(
attr_name.into(),
FieldValue::Character(Some(u.value().to_string())),
);
}
Value::Date(d) => {
// Date represented as an ISO8601 string
record.insert(
attr_name.into(),
FieldValue::Date(Some(Date::new(d.day(), d.month(), d.year() as u32))),
);
}
Value::Point(_p) => {
// TODO: implement
}
Value::Array(_arr) => {
// TODO: handle multiple values
}
Value::Object(_obj) => {
// TODO: handle nested objects
}
};
}
records.push(record);
}
Value::Code(c) => {
// value of the code
record.insert(attr_name, FieldValue::Character(Some(c.value().to_owned())));
}
Value::Integer(i) => {
record.insert(attr_name, FieldValue::Integer(i.to_owned() as i32));
}
Value::NonNegativeInteger(i) => {
record.insert(attr_name, FieldValue::Integer(i.to_owned() as i32));
}
// Handle as Float
Value::Double(d) => {
record.insert(attr_name, FieldValue::Float(Some(d.to_owned() as f32)));
}
// Handle as Float
Value::Measure(m) => {
record.insert(
attr_name,
FieldValue::Float(Some(m.value().to_owned() as f32)),
);
}
Value::Boolean(b) => {
record.insert(attr_name, FieldValue::Logical(Some(b.to_owned())));
}
Value::Uri(u) => {
record.insert(
attr_name,
FieldValue::Character(Some(u.value().to_string())),
);
}
Value::Date(d) => {
// Date represented as an ISO8601 string
record.insert(
attr_name,
FieldValue::Date(Some(Date::new(d.day(), d.month(), d.year() as u32))),
);
}
Value::Point(_p) => {
// TODO: implement
}
Value::Array(_arr) => {
// TODO: handle multiple values
}
Value::Object(_obj) => {
// TODO: handle nested objects
}
};
}

records
record
}
Loading

0 comments on commit 1feb0ec

Please sign in to comment.