egml_io/geometry/
multi_surface.rs

1use crate::error::Error;
2use quick_xml::de;
3use std::hash::{DefaultHasher, Hash, Hasher};
4
5use crate::geometry::polygon::GmlPolygon;
6use egml_core::model::base::{Gml, Id};
7use egml_core::model::geometry::{LinearRing, MultiSurface, Polygon, SurfaceProperty};
8use serde::{Deserialize, Serialize};
9use tracing::warn;
10
11#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)]
12pub struct GmlMultiSurfaceProperty {
13    #[serde(rename = "$value")]
14    pub multi_surface: Option<GmlMultiSurface>,
15}
16
17#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)]
18#[serde(rename = "gml:MultiSurface")]
19pub struct GmlMultiSurface {
20    #[serde(rename = "@id", default)]
21    id: String,
22    #[serde(rename = "$value")]
23    members: Vec<GmlSurfaceMember>,
24}
25
26#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)]
27#[serde(rename = "gml:surfaceMember")]
28pub struct GmlSurfaceMember {
29    #[serde(rename = "@href", default)]
30    href: String,
31    #[serde(rename = "$value")]
32    pub polygon: Option<GmlPolygon>,
33}
34
35impl TryFrom<GmlSurfaceMember> for SurfaceProperty {
36    type Error = Error;
37
38    fn try_from(value: GmlSurfaceMember) -> Result<Self, Self::Error> {
39        let linear_ring: Option<LinearRing> = value
40            .polygon
41            .and_then(|x| x.exterior.linear_ring)
42            .and_then(|gml_ring| gml_ring.try_into().ok());
43
44        let surface_property = SurfaceProperty::new(value.href, linear_ring)?;
45        Ok(surface_property)
46    }
47}
48
49impl TryFrom<GmlMultiSurface> for MultiSurface {
50    type Error = Error;
51
52    fn try_from(value: GmlMultiSurface) -> Result<Self, Self::Error> {
53        let id: Id = value.id.clone().try_into().ok().unwrap_or_else(|| {
54            let mut hasher = DefaultHasher::new();
55            value.hash(&mut hasher);
56            Id::from_hashed_u64(hasher.finish())
57        });
58        let gml = Gml::new(id);
59
60        let polygons: Vec<Polygon> = value
61            .members
62            .into_iter()
63            .flat_map(|x| x.polygon)
64            .flat_map(|x| {
65                let polygon_id = x.id.clone();
66                x.try_into()
67                    .map_err(|e| {
68                        warn!(
69                            "Error during parsing of gml:Polygon with id={}: {}",
70                            &polygon_id, e
71                        );
72                    })
73                    .ok()
74            })
75            .collect();
76
77        let multi_surface = MultiSurface::new(gml, polygons)?;
78        Ok(multi_surface)
79    }
80}
81
82pub fn parse_multi_surface(source_text: &str) -> Result<MultiSurface, Error> {
83    let parsed_geometry: GmlMultiSurface = de::from_str(source_text)?;
84    parsed_geometry.try_into()
85}
86
87#[cfg(test)]
88mod tests {
89    use crate::parse_multi_surface;
90
91    #[test]
92    fn parsing_multi_surface() {
93        let source_text = "<gml:MultiSurface gml:id=\"UUID_6b33ecfa-6e08-4e8e-a4b5-e1d06540faf0\">
94              <gml:surfaceMember>
95                <gml:Polygon gml:id=\"UUID_efb8f6a5-82fa-4b21-8709-c1d93ed1e595\">
96                  <gml:exterior>
97                    <gml:LinearRing>
98                      <gml:posList srsDimension=\"3\">678009.7116291433 5403638.313338383 417.3480034550211 678012.5609078613 5403634.960884141 417.34658523466385 678013.7892528991 5403636.004867206 417.51938733855997 678010.9399743223 5403639.357321232 417.5208051908512 678009.7116291433 5403638.313338383 417.3480034550211</gml:posList>
99                    </gml:LinearRing>
100                  </gml:exterior>
101                </gml:Polygon>
102              </gml:surfaceMember>
103            </gml:MultiSurface>";
104
105        let _result = parse_multi_surface(source_text).unwrap();
106    }
107
108    #[test]
109    fn parsing_multi_surface_with_duplicate_elements() {
110        let source_text = "<gml:MultiSurface srsName=\"EPSG:25832\" srsDimension=\"3\">
111              <gml:surfaceMember>
112                <gml:Polygon gml:id=\"4018133_PG.3nRTCd4XPu47PsAAUyNv\">
113                  <gml:exterior>
114                    <gml:LinearRing gml:id=\"4018133_LR.lHfcvQUrKVl08ifcH6eO\">
115                      <gml:posList>678105.792 5403815.554 369.98523 678105.792 5403815.555 367.67323 678106.047 5403815.125 367.67323 678106.047 5403815.125 367.67323 678106.047 5403815.125 367.67323 678106.047 5403815.124 369.98523 678105.792 5403815.554 369.98523</gml:posList>
116                    </gml:LinearRing>
117                  </gml:exterior>
118                </gml:Polygon>
119              </gml:surfaceMember>
120            </gml:MultiSurface>";
121
122        let _result = parse_multi_surface(source_text).unwrap();
123    }
124
125    #[test]
126    fn parsing_multi_surface_with_holes() {
127        let source_text = "
128            <gml:MultiSurface srsName=\"EPSG:25832\" srsDimension=\"3\">
129              <gml:surfaceMember>
130                <gml:Polygon gml:id=\"4018106_PG.dKY9ug9ol2tsxL5bLAPz\">
131                  <gml:exterior>
132                    <gml:LinearRing gml:id=\"4018106_LR.Wqmtl1E6Yz3eVJkuGjsK\">
133                      <gml:posList>678097.805 5403801.433 367.40123 678092.938 5403810.139 367.40123 678092.938 5403810.139 370.87623 678092.032 5403811.76 370.87623 678092.032 5403811.76 377.09023 678097.805 5403801.433 377.09023 678097.805 5403801.433 367.40123</gml:posList>
134                    </gml:LinearRing>
135                  </gml:exterior>
136                  <gml:interior>
137                    <gml:LinearRing gml:id=\"4018106_LR.10JNDsQqif3fouy54mfv\">
138                      <gml:posList>678096.88 5403803.088 374.90623 678097.403 5403802.152 374.90623 678097.403 5403802.152 376.19923 678096.88 5403803.088 376.19923 678096.88 5403803.088 374.90623</gml:posList>
139                    </gml:LinearRing>
140                  </gml:interior>
141                  <gml:interior>
142                    <gml:LinearRing gml:id=\"4018106_LR.yzLlZkAQX00eXb6Xi0DZ\">
143                      <gml:posList>678096.154 5403804.386 376.19923 678096.154 5403804.386 374.90623 678096.677 5403803.45 374.90623 678096.677 5403803.45 376.19923 678096.154 5403804.386 376.19923</gml:posList>
144                    </gml:LinearRing>
145                  </gml:interior>
146                  <gml:interior>
147                    <gml:LinearRing gml:id=\"4018106_LR.MIkI0SEPyMQ4yblCNiF2\">
148                      <gml:posList>678095.438 5403805.667 376.19923 678095.438 5403805.667 374.90623 678095.961 5403804.731 374.90623 678095.961 5403804.731 376.19923 678095.438 5403805.667 376.19923</gml:posList>
149                    </gml:LinearRing>
150                  </gml:interior>
151                  <gml:interior>
152                    <gml:LinearRing gml:id=\"4018106_LR.novU6ZVfhrtxrFFh7eYQ\">
153                      <gml:posList>678097.403 5403802.152 372.05223 678097.403 5403802.152 373.34523 678096.88 5403803.088 373.34523 678096.88 5403803.088 372.05223 678097.403 5403802.152 372.05223</gml:posList>
154                    </gml:LinearRing>
155                  </gml:interior>
156                  <gml:interior>
157                    <gml:LinearRing gml:id=\"4018106_LR.XdJcfjsruS75wlUmTQdH\">
158                      <gml:posList>678096.677 5403803.45 372.05223 678096.677 5403803.45 373.34523 678096.154 5403804.386 373.34523 678096.154 5403804.386 372.05223 678096.677 5403803.45 372.05223</gml:posList>
159                    </gml:LinearRing>
160                  </gml:interior>
161                  <gml:interior>
162                    <gml:LinearRing gml:id=\"4018106_LR.wzwxsPr4Ys8dTM1bzH8T\">
163                      <gml:posList>678095.961 5403804.731 372.05223 678095.961 5403804.731 373.34523 678095.438 5403805.667 373.34523 678095.438 5403805.667 372.05223 678095.961 5403804.731 372.05223</gml:posList>
164                    </gml:LinearRing>
165                  </gml:interior>
166                  <gml:interior>
167                    <gml:LinearRing gml:id=\"4018106_LR.20P6FwXiq4ZJ4EAxdmJ0\">
168                      <gml:posList>678093.838 5403808.528 374.89423 678094.361 5403807.593 374.89423 678094.361 5403807.593 376.18723 678093.838 5403808.528 376.18723 678093.838 5403808.528 374.89423</gml:posList>
169                    </gml:LinearRing>
170                  </gml:interior>
171                  <gml:interior>
172                    <gml:LinearRing gml:id=\"4018106_LR.saIYdVUNcoK3LJkC2LDw\">
173                      <gml:posList>678093.645 5403808.873 374.89423 678093.645 5403808.873 376.18723 678093.122 5403809.809 376.18723 678093.122 5403809.809 374.89423 678093.645 5403808.873 374.89423</gml:posList>
174                    </gml:LinearRing>
175                  </gml:interior>
176                  <gml:interior>
177                    <gml:LinearRing gml:id=\"4018106_LR.yPDE98qtqfTYBziBsTpl\">
178                      <gml:posList>678093.869 5403808.474 372.04523 678094.392 5403807.538 372.04523 678094.392 5403807.538 373.33823 678093.869 5403808.474 373.33823 678093.869 5403808.474 372.04523</gml:posList>
179                    </gml:LinearRing>
180                  </gml:interior>
181                  <gml:interior>
182                    <gml:LinearRing gml:id=\"4018106_LR.XaQt7QEqeVnG2PB8D6ad\">
183                      <gml:posList>678093.153 5403809.755 373.33823 678093.153 5403809.755 372.04523 678093.676 5403808.819 372.04523 678093.676 5403808.819 373.33823 678093.153 5403809.755 373.33823</gml:posList>
184                    </gml:LinearRing>
185                  </gml:interior>
186                  <gml:interior>
187                    <gml:LinearRing gml:id=\"4018106_LR.kCEyyLA2tigxjpQY9cyU\">
188                      <gml:posList>678092.933 5403810.148 372.04523 678092.933 5403810.148 373.32523 678092.126 5403811.591 373.32523 678092.126 5403811.591 372.04523 678092.933 5403810.148 372.04523</gml:posList>
189                    </gml:LinearRing>
190                  </gml:interior>
191                  <gml:interior>
192                    <gml:LinearRing gml:id=\"4018106_LR.Wq5AG6YS8zrN5HgtFQD8\">
193                      <gml:posList>678092.126 5403811.591 376.18723 678092.126 5403811.591 374.89423 678092.933 5403810.148 374.89423 678092.933 5403810.148 376.18723 678092.126 5403811.591 376.18723</gml:posList>
194                    </gml:LinearRing>
195                  </gml:interior>
196                  <gml:interior>
197                    <gml:LinearRing gml:id=\"4018106_LR.aQFMEYkDQkns0ZoJ66pj\">
198                      <gml:posList>678095.264 5403805.978 370.34223000000003 678095.264 5403805.978 370.79823 678093.197 5403809.675 370.79823 678093.197 5403809.675 370.34223000000003 678095.264 5403805.978 370.34223000000003</gml:posList>
199                    </gml:LinearRing>
200                  </gml:interior>
201                  <gml:interior>
202                    <gml:LinearRing gml:id=\"4018106_LR.tXljCrPP3Efr0mz83aTx\">
203                      <gml:posList>678095.254 5403805.996 368.30523 678095.254 5403805.996 370.06923 678093.187 5403809.693 370.06923 678093.187 5403809.693 368.30523 678095.254 5403805.996 368.30523</gml:posList>
204                    </gml:LinearRing>
205                  </gml:interior>
206                  <gml:interior>
207                    <gml:LinearRing gml:id=\"4018106_LR.gLnR6siy7dPwvvNX2zz0\">
208                      <gml:posList>678095.558 5403805.452 370.06723 678095.558 5403805.452 368.30323 678097.625 5403801.755 368.30323 678097.625 5403801.755 370.06723 678095.558 5403805.452 370.06723</gml:posList>
209                    </gml:LinearRing>
210                  </gml:interior>
211                  <gml:interior>
212                    <gml:LinearRing gml:id=\"4018106_LR.Iw6I84mlFFHQEPQCpApK\">
213                      <gml:posList>678097.625 5403801.755 370.34223000000003 678097.625 5403801.755 370.79223 678095.558 5403805.452 370.79223 678095.558 5403805.452 370.34223000000003 678097.625 5403801.755 370.34223000000003</gml:posList>
214                    </gml:LinearRing>
215                  </gml:interior>
216                </gml:Polygon>
217              </gml:surfaceMember>
218            </gml:MultiSurface>";
219
220        let result = parse_multi_surface(source_text).unwrap();
221
222        assert_eq!(result.surface_member().len(), 1);
223    }
224
225    #[test]
226    fn parsing_multi_surface_without_id() {
227        let source_text = "<gml:MultiSurface>
228              <gml:surfaceMember>
229                <gml:Polygon>
230                  <gml:exterior>
231                    <gml:LinearRing>
232                      <gml:posList srsDimension=\"3\">678009.7116291433 5403638.313338383 417.3480034550211 678012.5609078613 5403634.960884141 417.34658523466385 678013.7892528991 5403636.004867206 417.51938733855997 678010.9399743223 5403639.357321232 417.5208051908512 678009.7116291433 5403638.313338383 417.3480034550211</gml:posList>
233                    </gml:LinearRing>
234                  </gml:exterior>
235                </gml:Polygon>
236              </gml:surfaceMember>
237            </gml:MultiSurface>";
238
239        let result = parse_multi_surface(source_text).unwrap();
240
241        assert_eq!(result.surface_member().len(), 1);
242    }
243}