Skip to main content

mlt_core/convert/
geojson.rs

1//! `GeoJSON` -like data to represent decoded MLT data with i32 coordinates
2
3use std::collections::BTreeMap;
4
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8use crate::frames::Layer;
9use crate::v01::{Id, ParsedProperty};
10use crate::{Decoder, MltResult};
11
12/// `GeoJSON` geometry with `i32` tile coordinates
13pub type Geom32 = geo_types::Geometry<i32>;
14
15/// A single `i32` coordinate (x, y)
16pub type Coord32 = geo_types::Coord<i32>;
17
18/// `GeoJSON` geometry with `i16` tile coordinates
19pub type Geom16 = geo_types::Geometry<i16>;
20
21/// A single `i16` coordinate (x, y)
22pub type Coord16 = geo_types::Coord<i16>;
23
24/// `GeoJSON` [`FeatureCollection`]
25#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
26pub struct FeatureCollection {
27    #[serde(rename = "type")]
28    pub ty: String,
29    pub features: Vec<Feature>,
30}
31
32impl FeatureCollection {
33    /// Convert decoded layers to a `GeoJSON` [`FeatureCollection`]
34    pub fn from_layers(
35        layers: &mut [Layer<'_>],
36        dec: &mut Decoder,
37    ) -> MltResult<FeatureCollection> {
38        let mut features = Vec::new();
39        for layer in layers.iter_mut() {
40            let l = layer.decoded_layer01_mut(dec)?;
41            l.decode_properties(dec)?;
42            let geom = l.geometry.as_parsed()?;
43            let ids: Option<&[Option<u64>]> = l.id.as_ref().and_then(|v| {
44                if let Id::Parsed(d) = v {
45                    Some(d.values())
46                } else {
47                    None
48                }
49            });
50            for i in 0..geom.vector_types.len() {
51                let geometry = geom.to_geojson(i)?;
52                let mut properties = BTreeMap::new();
53                for prop in &l.properties {
54                    let prop = prop.as_parsed()?;
55                    // SharedDict properties are flattened to individual properties
56                    // with names like "struct_name:child_suffix"
57                    if let ParsedProperty::SharedDict(dict) = prop {
58                        for item in &dict.items {
59                            if let Some(s) = item.get(dict, i) {
60                                let key = format!("{}{}", dict.prefix, item.suffix);
61                                properties.insert(key, Value::String(s.to_string()));
62                            }
63                        }
64                    } else if let Some(val) = prop.to_geojson(i) {
65                        properties.insert(prop.name().to_string(), val);
66                    }
67                }
68                properties.insert("_layer".into(), Value::String(l.name.to_string()));
69                properties.insert("_extent".into(), Value::Number(l.extent.into()));
70                features.push(Feature {
71                    geometry,
72                    id: ids.and_then(|v| v.get(i).copied().flatten()),
73                    properties,
74                    ty: "Feature".into(),
75                });
76            }
77        }
78        Ok(FeatureCollection {
79            features,
80            ty: "FeatureCollection".into(),
81        })
82    }
83}
84
85/// `GeoJSON` [`Feature`]
86#[derive(Debug, Clone, PartialEq, Deserialize)]
87pub struct Feature {
88    #[serde(with = "geom_serde")]
89    pub geometry: Geom32,
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub id: Option<u64>,
92    pub properties: BTreeMap<String, Value>,
93    #[serde(rename = "type")]
94    pub ty: String,
95}
96
97struct Geom32Wire<'a>(&'a Geom32);
98impl Serialize for Geom32Wire<'_> {
99    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
100        geom_serde::serialize(self.0, s)
101    }
102}
103
104/// Serialize with the preferred order of the keys
105impl Serialize for Feature {
106    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
107        use serde::ser::SerializeMap as _;
108        let len = 3 + usize::from(self.id.is_some());
109        let mut map = serializer.serialize_map(Some(len))?;
110        map.serialize_entry("type", &self.ty)?;
111        if let Some(id) = self.id {
112            map.serialize_entry("id", &id)?;
113        }
114        map.serialize_entry("properties", &self.properties)?;
115        map.serialize_entry("geometry", &Geom32Wire(&self.geometry))?;
116        map.end()
117    }
118}
119
120/// Serialize/deserialize [`Geom32`] in `GeoJSON` wire format:
121/// `{"type":"…","coordinates":…}` with `[x, y]` integer arrays.
122mod geom_serde {
123    use geo_types::{
124        Geometry, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,
125    };
126    use serde::{Deserialize, Deserializer, Serializer};
127    use serde_json::Value;
128
129    use crate::geojson::Geom32;
130
131    type Arr = [i32; 2];
132
133    fn ls_arr(ls: &LineString<i32>) -> Vec<Arr> {
134        ls.0.iter().copied().map(Into::into).collect()
135    }
136
137    fn poly_arr(poly: &Polygon<i32>) -> Vec<Vec<Arr>> {
138        std::iter::once(poly.exterior())
139            .chain(poly.interiors())
140            .map(ls_arr)
141            .collect()
142    }
143
144    fn arr_ls(v: Vec<Arr>) -> LineString<i32> {
145        LineString::from(v)
146    }
147
148    fn arr_poly(rings: Vec<Vec<Arr>>) -> Polygon<i32> {
149        let mut it = rings.into_iter();
150        let ext = it.next().map_or_else(|| LineString(vec![]), arr_ls);
151        Polygon::new(ext, it.map(arr_ls).collect())
152    }
153
154    pub fn serialize<S: Serializer>(g: &Geom32, s: S) -> Result<S::Ok, S::Error> {
155        use serde::ser::{Error, SerializeMap as _};
156
157        let mut m = s.serialize_map(Some(2))?;
158        let (ty, coords): (&str, Value) = match g {
159            Geometry::Point(p) => ("Point", serde_json::to_value(Arr::from(*p)).unwrap()),
160            Geometry::LineString(ls) => ("LineString", serde_json::to_value(ls_arr(ls)).unwrap()),
161            Geometry::Polygon(poly) => ("Polygon", serde_json::to_value(poly_arr(poly)).unwrap()),
162            Geometry::MultiPoint(mp) => (
163                "MultiPoint",
164                serde_json::to_value(mp.0.iter().copied().map(Arr::from).collect::<Vec<_>>())
165                    .unwrap(),
166            ),
167            Geometry::MultiLineString(mls) => (
168                "MultiLineString",
169                serde_json::to_value(mls.iter().map(ls_arr).collect::<Vec<_>>()).unwrap(),
170            ),
171            Geometry::MultiPolygon(mpoly) => (
172                "MultiPolygon",
173                serde_json::to_value(mpoly.iter().map(poly_arr).collect::<Vec<_>>()).unwrap(),
174            ),
175            _ => return Err(Error::custom("unsupported geometry variant")),
176        };
177        m.serialize_entry("type", ty)?;
178        m.serialize_entry("coordinates", &coords)?;
179        m.end()
180    }
181
182    pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Geom32, D::Error> {
183        use serde::de::Error as _;
184
185        fn parse<T: serde::de::DeserializeOwned, E: serde::de::Error>(v: Value) -> Result<T, E> {
186            serde_json::from_value(v).map_err(E::custom)
187        }
188
189        #[derive(Deserialize)]
190        struct Wire {
191            #[serde(rename = "type")]
192            ty: String,
193            coordinates: Value,
194        }
195
196        let Wire { ty, coordinates: c } = Wire::deserialize(d)?;
197        Ok(match ty.as_str() {
198            "Point" => Geometry::Point(Point::from(parse::<Arr, _>(c)?)),
199            "LineString" => Geometry::LineString(arr_ls(parse(c)?)),
200            "Polygon" => Geometry::Polygon(arr_poly(parse(c)?)),
201            "MultiPoint" => {
202                let v: Vec<Arr> = parse(c)?;
203                Geometry::MultiPoint(MultiPoint(v.into_iter().map(Point::from).collect()))
204            }
205            "MultiLineString" => {
206                let v: Vec<Vec<Arr>> = parse(c)?;
207                Geometry::MultiLineString(MultiLineString(v.into_iter().map(arr_ls).collect()))
208            }
209            "MultiPolygon" => {
210                let v: Vec<Vec<Vec<Arr>>> = parse(c)?;
211                Geometry::MultiPolygon(MultiPolygon(v.into_iter().map(arr_poly).collect()))
212            }
213            _ => {
214                return Err(D::Error::unknown_variant(
215                    &ty,
216                    &[
217                        "Point",
218                        "LineString",
219                        "Polygon",
220                        "MultiPoint",
221                        "MultiLineString",
222                        "MultiPolygon",
223                    ],
224                ));
225            }
226        })
227    }
228}