1use crate::geometry::Geometry;
2use crate::metadata::GeographicalExtent;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
12pub enum KnownCityObjectType {
13 Bridge,
14 BridgePart,
15 BridgeInstallation,
16 BridgeConstructiveElement,
17 BridgeRoom,
18 BridgeFurniture,
19 Building,
20 BuildingPart,
21 BuildingInstallation,
22 BuildingConstructiveElement,
23 BuildingFurniture,
24 BuildingStorey,
25 BuildingRoom,
26 BuildingUnit,
27 CityFurniture,
28 CityObjectGroup,
29 GenericCityObject,
30 LandUse,
31 OtherConstruction,
32 PlantCover,
33 SolitaryVegetationObject,
34 TINRelief,
35 Road,
36 Railway,
37 Waterway,
38 TransportSquare,
39 Tunnel,
40 TunnelPart,
41 TunnelInstallation,
42 TunnelConstructiveElement,
43 TunnelHollowSpace,
44 TunnelFurniture,
45 WaterBody,
46}
47
48#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
97#[serde(untagged)]
98pub enum CityObjectType {
99 Known(KnownCityObjectType),
100 Extension(String),
103}
104
105#[allow(non_upper_case_globals)]
116impl CityObjectType {
117 pub const Bridge: CityObjectType = CityObjectType::Known(KnownCityObjectType::Bridge);
118 pub const BridgePart: CityObjectType = CityObjectType::Known(KnownCityObjectType::BridgePart);
119 pub const BridgeInstallation: CityObjectType =
120 CityObjectType::Known(KnownCityObjectType::BridgeInstallation);
121 pub const BridgeConstructiveElement: CityObjectType =
122 CityObjectType::Known(KnownCityObjectType::BridgeConstructiveElement);
123 pub const BridgeRoom: CityObjectType = CityObjectType::Known(KnownCityObjectType::BridgeRoom);
124 pub const BridgeFurniture: CityObjectType =
125 CityObjectType::Known(KnownCityObjectType::BridgeFurniture);
126 pub const Building: CityObjectType = CityObjectType::Known(KnownCityObjectType::Building);
127 pub const BuildingPart: CityObjectType =
128 CityObjectType::Known(KnownCityObjectType::BuildingPart);
129 pub const BuildingInstallation: CityObjectType =
130 CityObjectType::Known(KnownCityObjectType::BuildingInstallation);
131 pub const BuildingConstructiveElement: CityObjectType =
132 CityObjectType::Known(KnownCityObjectType::BuildingConstructiveElement);
133 pub const BuildingFurniture: CityObjectType =
134 CityObjectType::Known(KnownCityObjectType::BuildingFurniture);
135 pub const BuildingStorey: CityObjectType =
136 CityObjectType::Known(KnownCityObjectType::BuildingStorey);
137 pub const BuildingRoom: CityObjectType =
138 CityObjectType::Known(KnownCityObjectType::BuildingRoom);
139 pub const BuildingUnit: CityObjectType =
140 CityObjectType::Known(KnownCityObjectType::BuildingUnit);
141 pub const CityFurniture: CityObjectType =
142 CityObjectType::Known(KnownCityObjectType::CityFurniture);
143 pub const CityObjectGroup: CityObjectType =
144 CityObjectType::Known(KnownCityObjectType::CityObjectGroup);
145 pub const GenericCityObject: CityObjectType =
146 CityObjectType::Known(KnownCityObjectType::GenericCityObject);
147 pub const LandUse: CityObjectType = CityObjectType::Known(KnownCityObjectType::LandUse);
148 pub const OtherConstruction: CityObjectType =
149 CityObjectType::Known(KnownCityObjectType::OtherConstruction);
150 pub const PlantCover: CityObjectType = CityObjectType::Known(KnownCityObjectType::PlantCover);
151 pub const SolitaryVegetationObject: CityObjectType =
152 CityObjectType::Known(KnownCityObjectType::SolitaryVegetationObject);
153 pub const TINRelief: CityObjectType = CityObjectType::Known(KnownCityObjectType::TINRelief);
154 pub const Road: CityObjectType = CityObjectType::Known(KnownCityObjectType::Road);
155 pub const Railway: CityObjectType = CityObjectType::Known(KnownCityObjectType::Railway);
156 pub const Waterway: CityObjectType = CityObjectType::Known(KnownCityObjectType::Waterway);
157 pub const TransportSquare: CityObjectType =
158 CityObjectType::Known(KnownCityObjectType::TransportSquare);
159 pub const Tunnel: CityObjectType = CityObjectType::Known(KnownCityObjectType::Tunnel);
160 pub const TunnelPart: CityObjectType = CityObjectType::Known(KnownCityObjectType::TunnelPart);
161 pub const TunnelInstallation: CityObjectType =
162 CityObjectType::Known(KnownCityObjectType::TunnelInstallation);
163 pub const TunnelConstructiveElement: CityObjectType =
164 CityObjectType::Known(KnownCityObjectType::TunnelConstructiveElement);
165 pub const TunnelHollowSpace: CityObjectType =
166 CityObjectType::Known(KnownCityObjectType::TunnelHollowSpace);
167 pub const TunnelFurniture: CityObjectType =
168 CityObjectType::Known(KnownCityObjectType::TunnelFurniture);
169 pub const WaterBody: CityObjectType = CityObjectType::Known(KnownCityObjectType::WaterBody);
170}
171
172#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
173pub struct CityObject {
174 #[serde(rename = "type")]
175 pub thetype: CityObjectType,
176 #[serde(rename = "geographicalExtent")]
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub geographical_extent: Option<GeographicalExtent>,
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub attributes: Option<Value>,
181 #[serde(skip_serializing_if = "Option::is_none")]
182 pub geometry: Option<Vec<Geometry>>,
183 #[serde(skip_serializing_if = "Option::is_none")]
184 pub children: Option<Vec<String>>,
185 #[serde(skip_serializing_if = "Option::is_none")]
190 pub children_roles: Option<Vec<Option<String>>>,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub parents: Option<Vec<String>>,
193 #[serde(flatten)]
197 pub other: serde_json::Value,
198}
199
200impl CityObject {
201 pub fn new(thetype: CityObjectType) -> Self {
219 CityObject {
220 thetype,
221 geographical_extent: None,
222 attributes: None,
223 geometry: None,
224 children: None,
225 children_roles: None,
226 parents: None,
227 other: Value::Object(serde_json::Map::new()),
228 }
229 }
230
231 pub fn get_type(&self) -> CityObjectType {
232 self.thetype.clone()
233 }
234 pub(crate) fn is_toplevel(&self) -> bool {
235 match &self.parents {
236 Some(x) => {
237 if x.is_empty() {
238 return true;
239 } else {
240 return false;
241 }
242 }
243 None => return true,
244 }
245 }
246 pub(crate) fn get_children_keys(&self) -> Vec<String> {
247 let mut re: Vec<String> = Vec::new();
248 match &self.children {
249 Some(x) => {
250 for each in x {
251 re.push(each.to_string());
252 }
253 }
254 None => (),
255 }
256 re
257 }
258}
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263
264 #[test]
271 fn a_constructed_city_object_equals_its_parsed_twin() {
272 let constructed = CityObject::new(CityObjectType::Building);
273 let parsed: CityObject = serde_json::from_str(r#"{"type":"Building"}"#).unwrap();
274 assert_eq!(constructed, parsed);
275 assert_eq!(
276 serde_json::to_value(&constructed).unwrap(),
277 serde_json::json!({"type": "Building"})
278 );
279
280 let constructed = CityObject::new(CityObjectType::Extension("+Noise".into()));
283 let parsed: CityObject = serde_json::from_str(r#"{"type":"+Noise"}"#).unwrap();
284 assert_eq!(constructed, parsed);
285 }
286
287 #[test]
288 fn extension_city_object_type_roundtrips_with_its_plus() {
289 let t: CityObjectType =
290 serde_json::from_value(serde_json::json!("+NoiseCityFurnitureSegment")).unwrap();
291 assert_eq!(
292 t,
293 CityObjectType::Extension("+NoiseCityFurnitureSegment".into())
294 );
295 assert_eq!(
296 serde_json::to_value(&t).unwrap(),
297 serde_json::json!("+NoiseCityFurnitureSegment")
298 );
299 }
300
301 #[test]
302 fn known_city_object_type_is_a_unit_variant() {
303 let t: CityObjectType = serde_json::from_value(serde_json::json!("BuildingPart")).unwrap();
304 assert_eq!(t, CityObjectType::BuildingPart);
305 }
306
307 #[test]
312 fn plus_prefixed_known_name_lands_in_extension_not_the_known_variant() {
313 let t: CityObjectType = serde_json::from_value(serde_json::json!("+Building")).unwrap();
314 assert_eq!(t, CityObjectType::Extension("+Building".into()));
315 assert_ne!(t, CityObjectType::Building);
316 }
317
318 #[test]
338 fn extension_typed_city_object_roundtrips_byte_for_byte_inside_a_feature() {
339 let input = concat!(
340 r#"{"type":"CityJSONFeature","id":"id-1","#,
341 r#""CityObjects":{"id-1":{"type":"+NoiseCityFurnitureSegment","#,
342 r#""+noiseLevel":{"dB":55}}},"#,
343 r#""vertices":[]}"#
344 );
345 let parsed: crate::cityjson::CityJSONFeature = serde_json::from_str(input).unwrap();
346 assert_eq!(
347 parsed.city_objects["id-1"].thetype,
348 CityObjectType::Extension("+NoiseCityFurnitureSegment".into())
349 );
350 let reserialized = serde_json::to_string(&parsed).unwrap();
351 assert_eq!(
352 reserialized, input,
353 "an Extension-typed CityObject with an extra flattened member inside a \
354 CityJSONFeature must round-trip byte-for-byte"
355 );
356 }
357
358 #[test]
361 fn city_object_with_equal_fields_is_equal() {
362 let input = serde_json::json!({"type": "Building"});
363 let a: CityObject = serde_json::from_value(input.clone()).unwrap();
364 let b: CityObject = serde_json::from_value(input).unwrap();
365 assert_eq!(a, b);
366 let mut c = b.clone();
367 c.children = Some(vec!["id-2".to_string()]);
368 assert_ne!(a, c);
369 }
370
371 #[test]
376 fn city_object_geographical_extent_rejects_the_wrong_length() {
377 let too_short = serde_json::json!({
378 "type": "Building",
379 "geographicalExtent": [0.0, 0.0, 0.0, 1.0, 1.0]
380 });
381 assert!(serde_json::from_value::<CityObject>(too_short).is_err());
382
383 let too_long = serde_json::json!({
384 "type": "Building",
385 "geographicalExtent": [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0]
386 });
387 assert!(serde_json::from_value::<CityObject>(too_long).is_err());
388 }
389
390 #[test]
391 fn city_object_geographical_extent_round_trips_as_a_six_element_array() {
392 let input = serde_json::json!({
393 "type": "Building",
394 "geographicalExtent": [84710.1, 446846.0, -5.3, 84757.1, 446944.0, 40.9]
395 });
396 let co: CityObject = serde_json::from_value(input.clone()).unwrap();
397 assert_eq!(
398 co.geographical_extent,
399 Some([84710.1, 446846.0, -5.3, 84757.1, 446944.0, 40.9])
400 );
401 assert_eq!(serde_json::to_value(&co).unwrap(), input);
402 }
403
404 #[test]
410 fn city_object_group_children_roles_round_trips_including_null_entries() {
411 let input = serde_json::json!({
412 "type": "CityObjectGroup",
413 "children": ["building1", "building3"],
414 "children_roles": ["residential building", null]
415 });
416 let co: CityObject = serde_json::from_value(input.clone()).unwrap();
417 assert_eq!(
418 co.children_roles,
419 Some(vec![Some("residential building".to_string()), None])
420 );
421 assert_eq!(serde_json::to_value(&co).unwrap(), input);
422 }
423
424 #[test]
428 fn every_known_city_object_type_round_trips_as_its_unit_variant() {
429 let known: &[(&str, CityObjectType)] = &[
430 ("Bridge", CityObjectType::Bridge),
431 ("BridgePart", CityObjectType::BridgePart),
432 ("BridgeInstallation", CityObjectType::BridgeInstallation),
433 (
434 "BridgeConstructiveElement",
435 CityObjectType::BridgeConstructiveElement,
436 ),
437 ("BridgeRoom", CityObjectType::BridgeRoom),
438 ("BridgeFurniture", CityObjectType::BridgeFurniture),
439 ("Building", CityObjectType::Building),
440 ("BuildingPart", CityObjectType::BuildingPart),
441 ("BuildingInstallation", CityObjectType::BuildingInstallation),
442 (
443 "BuildingConstructiveElement",
444 CityObjectType::BuildingConstructiveElement,
445 ),
446 ("BuildingFurniture", CityObjectType::BuildingFurniture),
447 ("BuildingStorey", CityObjectType::BuildingStorey),
448 ("BuildingRoom", CityObjectType::BuildingRoom),
449 ("BuildingUnit", CityObjectType::BuildingUnit),
450 ("CityFurniture", CityObjectType::CityFurniture),
451 ("CityObjectGroup", CityObjectType::CityObjectGroup),
452 ("GenericCityObject", CityObjectType::GenericCityObject),
453 ("LandUse", CityObjectType::LandUse),
454 ("OtherConstruction", CityObjectType::OtherConstruction),
455 ("PlantCover", CityObjectType::PlantCover),
456 (
457 "SolitaryVegetationObject",
458 CityObjectType::SolitaryVegetationObject,
459 ),
460 ("TINRelief", CityObjectType::TINRelief),
461 ("Road", CityObjectType::Road),
462 ("Railway", CityObjectType::Railway),
463 ("Waterway", CityObjectType::Waterway),
464 ("TransportSquare", CityObjectType::TransportSquare),
465 ("Tunnel", CityObjectType::Tunnel),
466 ("TunnelPart", CityObjectType::TunnelPart),
467 ("TunnelInstallation", CityObjectType::TunnelInstallation),
468 (
469 "TunnelConstructiveElement",
470 CityObjectType::TunnelConstructiveElement,
471 ),
472 ("TunnelHollowSpace", CityObjectType::TunnelHollowSpace),
473 ("TunnelFurniture", CityObjectType::TunnelFurniture),
474 ("WaterBody", CityObjectType::WaterBody),
475 ];
476 for (name, expected) in known {
477 let parsed: CityObjectType = serde_json::from_value(serde_json::json!(name)).unwrap();
478 assert_eq!(&parsed, expected, "{name} did not parse to its own variant");
479 assert_eq!(
480 serde_json::to_value(&parsed).unwrap(),
481 serde_json::json!(name),
482 "{name} did not round-trip back to its own string"
483 );
484 }
485 }
486}