Skip to main content

cjseq/
cityjson.rs

1use crate::appearance::{Appearance, MaterialObject, TextureObject};
2use crate::city_object::CityObject;
3use crate::error::Result;
4use crate::geometry::GeometryTemplates;
5use crate::metadata::{Metadata, Transform};
6use serde::{Deserialize, Serialize};
7use serde_json::{json, Value};
8use std::collections::HashMap;
9
10/// Slice a document-level appearance palette down to the entries one feature
11/// uses, in the order that feature now refers to them.
12///
13/// `oldnew` maps each index used by the feature onto its dense position in the
14/// new palette; `update_material`/`update_texture` build it by handing out
15/// consecutive new indices, so its values are exactly `0..oldnew.len()` and
16/// sorting by them reproduces the palette.
17///
18/// This replaces a `resize(len, json!(null))`-then-overwrite loop, which only
19/// worked because every slot was subsequently overwritten: the `null` filler
20/// was never a valid palette entry, and now that the palette is typed it is
21/// not even representable.
22fn reindex<T: Clone>(all: &[T], oldnew: &HashMap<usize, usize>) -> Vec<T> {
23    let mut pairs: Vec<(usize, usize)> = oldnew.iter().map(|(o, n)| (*n, *o)).collect();
24    pairs.sort_unstable();
25    pairs.into_iter().map(|(_, old)| all[old].clone()).collect()
26}
27
28#[derive(Clone)]
29pub enum SortingStrategy {
30    Random,
31    Lexicographical,
32    Morton,  //-- TODO implement Morton sorting
33    Hilbert, //-- TODO implement Hilbert sorting
34}
35
36#[derive(Serialize, Deserialize, Debug, Clone)]
37pub struct CityJSON {
38    #[serde(rename = "type")]
39    pub thetype: CityJSONType,
40    pub version: String,
41    pub transform: Transform,
42    #[serde(rename = "CityObjects")]
43    pub city_objects: HashMap<String, CityObject>,
44    pub vertices: Vec<Vec<i64>>,
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub metadata: Option<Metadata>,
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub appearance: Option<Appearance>,
49    #[serde(rename = "geometry-templates")]
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub geometry_templates: Option<GeometryTemplates>,
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub extensions: Option<Value>,
54    #[serde(flatten)]
55    pub other: serde_json::Value,
56    #[serde(skip)]
57    sorted_ids: Vec<String>,
58    #[serde(skip)]
59    transform_correction: Option<Transform>,
60}
61
62/// Hand-written rather than derived: `sorted_ids` and `transform_correction`
63/// are `#[serde(skip)]` internal bookkeeping, not part of the CityJSON
64/// document. `sorted_ids` in particular is populated by iterating
65/// `city_objects` (a `HashMap`, whose iteration order is not guaranteed
66/// stable across independently-constructed maps) and can be further
67/// reordered by `sort_cjfeatures(SortingStrategy::Random)`, so a derived
68/// `PartialEq` that included it would make `==` spuriously `false` for two
69/// values representing the identical document. Excluding it here is what
70/// makes equality mean "same document", which is the only sense of equality
71/// this crate (or its callers) should ever want. This does not violate the
72/// "never hand-write" constraint, which is about `Serialize`/`Deserialize`
73/// -- a hand-written `PartialEq` cannot corrupt output.
74impl PartialEq for CityJSON {
75    fn eq(&self, other: &Self) -> bool {
76        self.thetype == other.thetype
77            && self.version == other.version
78            && self.transform == other.transform
79            && self.city_objects == other.city_objects
80            && self.vertices == other.vertices
81            && self.metadata == other.metadata
82            && self.appearance == other.appearance
83            && self.geometry_templates == other.geometry_templates
84            && self.extensions == other.extensions
85            && self.other == other.other
86    }
87}
88
89impl CityJSON {
90    /// Create a new CityJSON instance with default values.
91    pub fn new() -> Self {
92        let co: HashMap<String, CityObject> = HashMap::new();
93        let v: Vec<Vec<i64>> = Vec::new();
94        let tr = Transform::new();
95        CityJSON {
96            thetype: CityJSONType::CityJSON,
97            version: "2.0".to_string(),
98            transform: tr,
99            city_objects: co,
100            vertices: v,
101            metadata: None,
102            appearance: None,
103            geometry_templates: None,
104            extensions: None,
105            other: json!(null),
106            sorted_ids: vec![],
107            transform_correction: None,
108        }
109    }
110    /// Create a new CityJSON instance from a string.
111    pub fn from_str(s: &str) -> Result<Self> {
112        let mut cjj: CityJSON = serde_json::from_str(s)?;
113        //-- check if CO exists, then add them to the sorted_ids
114        for (key, co) in &cjj.city_objects {
115            if co.is_toplevel() {
116                cjj.sorted_ids.push(key.clone());
117            }
118        }
119        Ok(cjj)
120    }
121
122    /// Get the "first line" (aka metadata or header) of a CityJSONSeq
123    pub fn get_metadata(&self) -> Self {
124        let co: HashMap<String, CityObject> = HashMap::new();
125        let v: Vec<Vec<i64>> = Vec::new();
126        let mut cj0 = CityJSON {
127            thetype: self.thetype.clone(),
128            version: self.version.clone(),
129            transform: self.transform.clone(),
130            metadata: self.metadata.clone(),
131            city_objects: co,
132            vertices: v,
133            appearance: None,
134            geometry_templates: self.geometry_templates.clone(),
135            other: self.other.clone(),
136            extensions: self.extensions.clone(),
137            sorted_ids: vec![],
138            transform_correction: None,
139        };
140        //-- if geometry-templates have material/textures then these need to be
141        //-- added to 1st line (metadata)
142        match &self.geometry_templates {
143            Some(x) => {
144                let mut gts2: GeometryTemplates = x.clone();
145                let mut m_oldnew: HashMap<usize, usize> = HashMap::new();
146                let mut t_oldnew: HashMap<usize, usize> = HashMap::new();
147                let mut t_v_oldnew: HashMap<usize, usize> = HashMap::new();
148                for g in &mut gts2.templates {
149                    g.update_material(&mut m_oldnew);
150                    g.update_texture(&mut t_oldnew, &mut t_v_oldnew, 0);
151                }
152                //-- "slice" materials
153                if self.appearance.is_some() {
154                    let a = self.appearance.as_ref().unwrap();
155                    let mut acjf: Appearance = Appearance::new();
156                    acjf.default_theme_material = a.default_theme_material.clone();
157                    acjf.default_theme_texture = a.default_theme_texture.clone();
158                    if let Some(am) = a.materials.as_ref() {
159                        acjf.materials = Some(reindex(am, &m_oldnew));
160                    }
161                    if let Some(at) = a.textures.as_ref() {
162                        acjf.textures = Some(reindex(at, &t_oldnew));
163                    }
164                    if let Some(atv) = a.vertices_texture.as_ref() {
165                        acjf.vertices_texture = Some(reindex(atv, &t_v_oldnew));
166                    }
167                    cj0.appearance = Some(acjf);
168                }
169            }
170            None => (),
171        }
172        cj0
173    }
174    /// Getter for the features in a CityJSON dataset.
175    /// Starts at 0, and return Option::None if the index is out of bounds.
176    pub fn get_cjfeature(&self, i: usize) -> Option<CityJSONFeature> {
177        let i2 = self.sorted_ids.get(i);
178        if i2.is_none() {
179            return None;
180        }
181        let obj = self.city_objects.get(i2.unwrap());
182        if obj.is_none() {
183            return None;
184        }
185        let co = obj.unwrap();
186        //-- the other lines
187        let mut cjf = CityJSONFeature::new();
188        let mut co2: CityObject = co.clone();
189        let mut g_vi_oldnew: HashMap<usize, usize> = HashMap::new();
190        let mut m_oldnew: HashMap<usize, usize> = HashMap::new();
191        let mut t_oldnew: HashMap<usize, usize> = HashMap::new();
192        let mut t_v_oldnew: HashMap<usize, usize> = HashMap::new();
193        match &mut co2.geometry {
194            Some(x) => {
195                for g in x.iter_mut() {
196                    g.update_geometry_boundaries(&mut g_vi_oldnew);
197                    g.update_material(&mut m_oldnew);
198                    g.update_texture(&mut t_oldnew, &mut t_v_oldnew, 0);
199                }
200            }
201            None => (),
202        }
203        cjf.add_co(self.sorted_ids[i].clone(), co2);
204        cjf.id = self.sorted_ids[i].to_string();
205        //-- TODO: to fix: children-of-children?
206        //-- process all the children (only one-level lower)
207        for childkey in co.get_children_keys() {
208            let coc = self.city_objects.get(&childkey).unwrap();
209            let mut coc2: CityObject = coc.clone();
210            match &mut coc2.geometry {
211                Some(x) => {
212                    for g in x.iter_mut() {
213                        g.update_geometry_boundaries(&mut g_vi_oldnew);
214                        g.update_material(&mut m_oldnew);
215                        g.update_texture(&mut t_oldnew, &mut t_v_oldnew, 0);
216                    }
217                }
218                None => (),
219            }
220            cjf.add_co(childkey.clone(), coc2);
221        }
222        //-- "slice" geometry vertices
223        let allvertices = &self.vertices;
224        let mut g_new_vertices: Vec<Vec<i64>> = Vec::new();
225        g_new_vertices.resize(g_vi_oldnew.len(), vec![]);
226        for (old, new) in &g_vi_oldnew {
227            g_new_vertices[*new] = allvertices[*old].clone();
228        }
229        cjf.vertices = g_new_vertices;
230        //-- "slice" materials
231        if self.appearance.is_some() {
232            let a = self.appearance.as_ref().unwrap();
233            let mut acjf: Appearance = Appearance::new();
234            acjf.default_theme_material = a.default_theme_material.clone();
235            acjf.default_theme_texture = a.default_theme_texture.clone();
236            if let Some(am) = a.materials.as_ref() {
237                acjf.materials = Some(reindex(am, &m_oldnew));
238            }
239            if let Some(at) = a.textures.as_ref() {
240                acjf.textures = Some(reindex(at, &t_oldnew));
241            }
242            if let Some(atv) = a.vertices_texture.as_ref() {
243                acjf.vertices_texture = Some(reindex(atv, &t_v_oldnew));
244            }
245            cjf.appearance = Some(acjf);
246        }
247        Some(cjf)
248    }
249    /// Used when many CityJSONSeq are used, the "transform" can
250    /// be modified (the new value is a "correction").
251    pub fn add_transform_correction(&mut self, t: Transform) {
252        self.transform_correction = Some(t);
253    }
254    pub fn add_cjfeature(&mut self, cjf: &mut CityJSONFeature) {
255        let mut m_oldnew: HashMap<usize, usize> = HashMap::new();
256        let mut t_oldnew: HashMap<usize, usize> = HashMap::new();
257        let mut t_v_oldnew: HashMap<usize, usize> = HashMap::new();
258        let g_offset = self.vertices.len();
259        let mut t_offset = 0;
260        if let Some(cjf_app) = &cjf.appearance {
261            if let Some(cjf_mat) = &cjf_app.materials {
262                for (i, m) in cjf_mat.iter().enumerate() {
263                    m_oldnew.insert(i, self.add_material(m.clone()));
264                }
265            }
266            if let Some(cjf_tex) = &cjf_app.textures {
267                for (i, m) in cjf_tex.iter().enumerate() {
268                    t_oldnew.insert(i, self.add_texture(m.clone()));
269                }
270            }
271            if let Some(cjf_v_tex) = &cjf_app.vertices_texture {
272                t_offset = cjf_v_tex.len();
273                self.add_vertices_texture(cjf_v_tex.clone());
274            }
275        }
276
277        for (key, co) in &mut cjf.city_objects {
278            //-- boundaries
279            if let Some(ref mut geoms) = &mut co.geometry {
280                for g in geoms.iter_mut() {
281                    //-- boundaries
282                    g.offset_geometry_boundaries(g_offset);
283                    // g.update_geometry_boundaries(&mut g_oldnew, g_offset);
284                    //-- material
285                    g.update_material(&mut m_oldnew);
286                    //-- texture
287                    g.update_texture(&mut t_oldnew, &mut t_v_oldnew, t_offset);
288                }
289            }
290            //-- update the collected json object by adding the CityObjects
291            self.add_co(key.to_string(), co.clone());
292        }
293        //-- add the new vertices
294        self.add_vertices(&mut cjf.vertices);
295        //-- add the CO id to the list
296        self.sorted_ids.push(cjf.id.clone());
297    }
298    pub fn remove_duplicate_vertices(&mut self) {
299        // let totalinput = self.vertices.len();
300        let mut h: HashMap<String, usize> = HashMap::new();
301        let mut newids: HashMap<usize, usize> = HashMap::new();
302        let mut newvertices: Vec<Vec<i64>> = Vec::new();
303        for (i, v) in self.vertices.iter().enumerate() {
304            // println!("{:?}", v);
305            let k = format!("{} {} {}", v[0], v[1], v[2]);
306            match h.get(&k) {
307                Some(x) => {
308                    let _ = newids.insert(i, *x);
309                }
310                None => {
311                    newids.insert(i, newvertices.len());
312                    h.insert(k.clone(), newvertices.len());
313                    newvertices.push(v.clone());
314                }
315            }
316        }
317        //-- update indices
318        let cos = &mut self.city_objects;
319        for (_key, co) in cos.iter_mut() {
320            match &mut co.geometry {
321                Some(x) => {
322                    for g in x.iter_mut() {
323                        g.update_geometry_boundaries(&mut newids);
324                    }
325                }
326                None => (),
327            }
328        }
329        //-- replace the vertices, innit?
330        self.vertices = newvertices;
331    }
332    pub fn update_geographicalextent(&mut self) {
333        if let Some(m) = &mut self.metadata {
334            if let Some(ref mut ge) = m.geographical_extent {
335                let mut mins: Vec<i64> = vec![i64::MAX, i64::MAX, i64::MAX];
336                let mut maxs: Vec<i64> = vec![i64::MIN, i64::MIN, i64::MIN];
337                for v in &self.vertices {
338                    for i in 0..3 {
339                        if v[i] < mins[i] {
340                            mins[i] = v[i];
341                        }
342                        if v[i] > maxs[i] {
343                            maxs[i] = v[i];
344                        }
345                    }
346                }
347                *ge = [
348                    mins[0] as f64 * self.transform.scale[0] + self.transform.translate[0],
349                    mins[1] as f64 * self.transform.scale[1] + self.transform.translate[1],
350                    mins[2] as f64 * self.transform.scale[2] + self.transform.translate[2],
351                    maxs[0] as f64 * self.transform.scale[0] + self.transform.translate[0],
352                    maxs[1] as f64 * self.transform.scale[1] + self.transform.translate[1],
353                    maxs[2] as f64 * self.transform.scale[2] + self.transform.translate[2],
354                ];
355            }
356        }
357    }
358    pub fn update_transform(&mut self) {
359        let mut newvertices: Vec<Vec<i64>> = Vec::new();
360        let mut mins: Vec<i64> = vec![i64::MAX, i64::MAX, i64::MAX];
361        //-- find min-xyz
362        for v in &self.vertices {
363            for i in 0..3 {
364                if v[i] < mins[i] {
365                    mins[i] = v[i];
366                }
367            }
368        }
369        //-- subtract the mins from each vertex
370        for v in &self.vertices {
371            let v: Vec<i64> = vec![v[0] - mins[0], v[1] - mins[1], v[2] - mins[2]];
372            newvertices.push(v);
373        }
374        //-- replace the vertices, innit?
375        self.vertices = newvertices;
376        //-- update the transform/translate
377        let ttx = (mins[0] as f64 * self.transform.scale[0]) + self.transform.translate[0];
378        let tty = (mins[1] as f64 * self.transform.scale[1]) + self.transform.translate[1];
379        let ttz = (mins[2] as f64 * self.transform.scale[2]) + self.transform.translate[2];
380        self.transform.translate = vec![ttx, tty, ttz];
381    }
382    pub fn number_of_city_objects(&self) -> usize {
383        let mut total: usize = 0;
384        for (_key, co) in &self.city_objects {
385            if co.is_toplevel() {
386                total += 1;
387            }
388        }
389        total
390    }
391    /// When getting the CityJSONFeatures, this controls the order in which
392    /// they are returned. By default they are returned in the order they were added.
393    pub fn sort_cjfeatures(&mut self, ss: SortingStrategy) {
394        self.sorted_ids.clear();
395        match ss {
396            SortingStrategy::Random => {
397                for (key, co) in &self.city_objects {
398                    if co.is_toplevel() {
399                        self.sorted_ids.push(key.clone());
400                    }
401                }
402            }
403            SortingStrategy::Lexicographical => {
404                for (key, co) in &self.city_objects {
405                    if co.is_toplevel() {
406                        self.sorted_ids.push(key.clone());
407                    }
408                }
409                self.sorted_ids.sort();
410            }
411            _ => todo!(),
412        }
413    }
414    fn add_co(&mut self, id: String, co: CityObject) {
415        self.city_objects.insert(id.clone(), co);
416    }
417    fn add_vertices(&mut self, vs: &mut Vec<Vec<i64>>) {
418        if self.transform_correction.is_none() {
419            self.vertices.append(vs);
420        } else {
421            //-- the transfrom correction needs to be applied
422            let c = self.transform_correction.as_ref().unwrap();
423            for v in vs {
424                let cx: i64 = (((v[0] as f64 * c.scale[0]) + c.translate[0]
425                    - self.transform.translate[0])
426                    / self.transform.scale[0])
427                    .round() as i64;
428                let cy: i64 = (((v[1] as f64 * c.scale[1]) + c.translate[1]
429                    - self.transform.translate[1])
430                    / self.transform.scale[1])
431                    .round() as i64;
432                let cz: i64 = (((v[2] as f64 * c.scale[2]) + c.translate[2]
433                    - self.transform.translate[2])
434                    / self.transform.scale[2])
435                    .round() as i64;
436                self.vertices.push(vec![cx, cy, cz]);
437            }
438        }
439    }
440    fn add_vertices_texture(&mut self, vs: Vec<[f64; 2]>) {
441        match &mut self.appearance {
442            Some(x) => x.add_vertices_texture(vs),
443            None => {
444                let mut a: Appearance = Appearance::new();
445                a.add_vertices_texture(vs);
446                self.appearance = Some(a);
447            }
448        };
449    }
450    fn add_material(&mut self, jm: MaterialObject) -> usize {
451        let re = match &mut self.appearance {
452            Some(x) => x.add_material(jm),
453            None => {
454                let mut a: Appearance = Appearance::new();
455                let re = a.add_material(jm);
456                self.appearance = Some(a);
457                re
458            }
459        };
460        re
461    }
462    fn add_texture(&mut self, jm: TextureObject) -> usize {
463        let re = match &mut self.appearance {
464            Some(x) => x.add_texture(jm),
465            None => {
466                let mut a: Appearance = Appearance::new();
467                let re = a.add_texture(jm);
468                self.appearance = Some(a);
469                re
470            }
471        };
472        re
473    }
474}
475
476#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
477pub struct CityJSONFeature {
478    #[serde(rename = "type")]
479    pub thetype: CityJSONFeatureType,
480    pub id: String,
481    #[serde(rename = "CityObjects")]
482    pub city_objects: HashMap<String, CityObject>,
483    pub vertices: Vec<Vec<i64>>,
484    #[serde(skip_serializing_if = "Option::is_none")]
485    pub appearance: Option<Appearance>,
486}
487impl CityJSONFeature {
488    pub fn new() -> Self {
489        let co: HashMap<String, CityObject> = HashMap::new();
490        let v: Vec<Vec<i64>> = Vec::new();
491        CityJSONFeature {
492            thetype: CityJSONFeatureType::CityJSONFeature,
493            id: "".to_string(),
494            city_objects: co,
495            vertices: v,
496            appearance: None,
497        }
498    }
499    pub fn from_str(s: &str) -> Result<Self> {
500        let cjf: CityJSONFeature = serde_json::from_str(s)?;
501        Ok(cjf)
502    }
503    pub fn add_co(&mut self, id: String, co: CityObject) {
504        self.city_objects.insert(id, co);
505    }
506    pub fn centroid(&self) -> Vec<f64> {
507        let mut totals: Vec<f64> = vec![0., 0., 0.];
508        for v in &self.vertices {
509            for i in 0..3 {
510                totals[i] += v[i] as f64;
511            }
512        }
513        for i in 0..3 {
514            totals[i] /= self.vertices.len() as f64;
515        }
516        return totals;
517    }
518}
519
520/// Collects a base CityJSON metadata and a vector of CityJSONFeatures
521/// into a complete CityJSON object
522pub fn cjseq_to_cj(mut base_cj: CityJSON, features: Vec<CityJSONFeature>) -> CityJSON {
523    for mut feature in features {
524        base_cj.add_cjfeature(&mut feature);
525    }
526
527    base_cj.remove_duplicate_vertices();
528    base_cj.update_transform();
529    base_cj.update_geographicalextent();
530
531    base_cj
532}
533
534/// The `type` member of a [`CityJSON`] object. The spec fixes its value to
535/// the single string `"CityJSON"` (§ 1 CityJSON Object); unlike City Object
536/// and semantic-surface types, CityJSON does not define an Extension
537/// document type, so there is no `Extension` variant here -- a document
538/// declaring any other `type` value fails to deserialize.
539#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
540pub enum CityJSONType {
541    CityJSON,
542}
543
544/// The `type` member of a [`CityJSONFeature`] object. The spec fixes its
545/// value to the single string `"CityJSONFeature"` (§ 7.2 Text sequences and
546/// streaming with CityJSONFeature); as with [`CityJSONType`], there is no
547/// Extension variant.
548#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
549pub enum CityJSONFeatureType {
550    CityJSONFeature,
551}
552
553#[cfg(test)]
554mod tests {
555    use super::*;
556
557    /// Every public type in this crate derives `PartialEq`; `CityJSON` and
558    /// `CityJSONFeature` were missing it.
559    #[test]
560    fn cityjson_feature_with_equal_fields_is_equal() {
561        let input = serde_json::json!({
562            "type": "CityJSONFeature",
563            "id": "id-1",
564            "CityObjects": {},
565            "vertices": []
566        });
567        let a: CityJSONFeature = serde_json::from_value(input.clone()).unwrap();
568        let b: CityJSONFeature = serde_json::from_value(input).unwrap();
569        assert_eq!(a, b);
570        let mut c = b.clone();
571        c.id = "id-2".to_string();
572        assert_ne!(a, c);
573    }
574
575    #[test]
576    fn cityjson_with_equal_fields_is_equal() {
577        let a = CityJSON::new();
578        let b = CityJSON::new();
579        assert_eq!(a, b);
580        let mut c = CityJSON::new();
581        c.version = "1.1".to_string();
582        assert_ne!(a, c);
583    }
584
585    /// `sorted_ids` and `transform_correction` are `#[serde(skip)]` internal
586    /// bookkeeping, not part of the document: `sorted_ids` is populated by
587    /// `from_str` iterating `city_objects` (a `HashMap`, whose iteration
588    /// order is not guaranteed stable across independently-constructed
589    /// maps), and `sort_cjfeatures(Random)` shuffles it further. Two
590    /// `CityJSON`s parsed from the identical string represent the identical
591    /// document and must compare equal regardless of what order that
592    /// bookkeeping happened to land in. A fixture with a single top-level
593    /// CityObject cannot exercise this -- `sorted_ids` would trivially have
594    /// one, unambiguous order either way -- so this uses five.
595    #[test]
596    fn cityjson_parsed_twice_from_the_same_string_is_equal_regardless_of_hashmap_order() {
597        let input = serde_json::json!({
598            "type": "CityJSON",
599            "version": "2.0",
600            "transform": {"scale": [1.0, 1.0, 1.0], "translate": [0.0, 0.0, 0.0]},
601            "CityObjects": {
602                "id-a": {"type": "Building"},
603                "id-b": {"type": "Building"},
604                "id-c": {"type": "Building"},
605                "id-d": {"type": "Building"},
606                "id-e": {"type": "Building"}
607            },
608            "vertices": []
609        })
610        .to_string();
611        let a = CityJSON::from_str(&input).unwrap();
612        let b = CityJSON::from_str(&input).unwrap();
613        assert_eq!(
614            a, b,
615            "the same document parsed twice must be equal, independent of internal \
616             bookkeeping order"
617        );
618    }
619
620    #[test]
621    fn cityjson_type_round_trips() {
622        let t: CityJSONType = serde_json::from_value(serde_json::json!("CityJSON")).unwrap();
623        assert_eq!(t, CityJSONType::CityJSON);
624        assert_eq!(
625            serde_json::to_value(&t).unwrap(),
626            serde_json::json!("CityJSON")
627        );
628    }
629
630    #[test]
631    fn cityjson_type_rejects_any_other_value() {
632        assert!(
633            serde_json::from_value::<CityJSONType>(serde_json::json!("CityJSONFeature")).is_err()
634        );
635        assert!(serde_json::from_value::<CityJSONType>(serde_json::json!("+CityJSON")).is_err());
636    }
637
638    #[test]
639    fn cityjson_feature_type_round_trips() {
640        let t: CityJSONFeatureType =
641            serde_json::from_value(serde_json::json!("CityJSONFeature")).unwrap();
642        assert_eq!(t, CityJSONFeatureType::CityJSONFeature);
643        assert_eq!(
644            serde_json::to_value(&t).unwrap(),
645            serde_json::json!("CityJSONFeature")
646        );
647    }
648
649    #[test]
650    fn cityjson_feature_type_rejects_any_other_value() {
651        assert!(
652            serde_json::from_value::<CityJSONFeatureType>(serde_json::json!("CityJSON")).is_err()
653        );
654    }
655}