egml_io/geometry/
multi_surface.rs

1use crate::error::Error;
2use quick_xml::{DeError, de};
3use std::hash::{DefaultHasher, Hash, Hasher};
4
5use crate::geometry::polygon::GmlPolygon;
6use egml_core::model::base::{AbstractGml, 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
32    #[serde(rename = "$value")]
33    pub content: Option<GmlSurfaceMemberContent>,
34}
35
36#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)]
37pub enum GmlSurfaceMemberContent {
38    Polygon(GmlPolygon),
39    CompositeSurface(GmlCompositeSurface),
40}
41
42#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)]
43#[serde(rename = "gml:CompositeSurface")]
44pub struct GmlCompositeSurface {
45    #[serde(rename = "@id", default)]
46    id: String,
47    #[serde(rename = "$value")]
48    pub surface_members: Vec<GmlCompositeSurfaceMember>,
49}
50
51#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)]
52pub struct GmlCompositeSurfaceMember {
53    #[serde(rename = "$value")]
54    pub polygon: Option<GmlPolygon>,
55}
56
57impl TryFrom<GmlSurfaceMember> for SurfaceProperty {
58    type Error = Error;
59
60    fn try_from(value: GmlSurfaceMember) -> Result<Self, Self::Error> {
61        let linear_ring: Option<LinearRing> = value
62            .content
63            .and_then(|current_content| match current_content {
64                GmlSurfaceMemberContent::Polygon(x) => Some(x),
65                GmlSurfaceMemberContent::CompositeSurface(_) => {
66                    warn!("Found composite surface member in MultiSurface. Ignoring.");
67                    None
68                }
69            })
70            .and_then(|x| x.exterior.linear_ring)
71            .and_then(|gml_ring| gml_ring.try_into().ok());
72
73        let surface_property = SurfaceProperty::new(value.href, linear_ring)?;
74        Ok(surface_property)
75    }
76}
77
78impl TryFrom<GmlMultiSurface> for MultiSurface {
79    type Error = Error;
80
81    fn try_from(value: GmlMultiSurface) -> Result<Self, Self::Error> {
82        let id: Id = value.id.clone().try_into().ok().unwrap_or_else(|| {
83            let mut hasher = DefaultHasher::new();
84            value.hash(&mut hasher);
85            Id::from_hashed_u64(hasher.finish())
86        });
87        let abstract_gml = AbstractGml::new(id);
88
89        let polygons: Vec<Polygon> = value
90            .members
91            .into_iter()
92            .flat_map(|x| x.content)
93            .flat_map(|content| match content {
94                GmlSurfaceMemberContent::CompositeSurface(composite) => composite
95                    .surface_members
96                    .into_iter()
97                    .flat_map(|member| member.polygon)
98                    .collect::<Vec<_>>(),
99                GmlSurfaceMemberContent::Polygon(polygon) => vec![polygon],
100            })
101            .flat_map(|x| {
102                let polygon_id = x.id.clone();
103                x.try_into()
104                    .map_err(|e| {
105                        warn!(
106                            "Error during parsing of gml:Polygon with id={}: {}",
107                            &polygon_id, e
108                        );
109                    })
110                    .ok()
111            })
112            .collect();
113
114        let multi_surface = MultiSurface::new(abstract_gml, polygons)?;
115        Ok(multi_surface)
116    }
117}
118
119pub fn parse_multi_surface(source_text: &str) -> Result<MultiSurface, Error> {
120    let parsed_geometry: Result<GmlMultiSurface, DeError> = de::from_str(source_text);
121    parsed_geometry?.try_into()
122}
123
124#[cfg(test)]
125mod tests {
126    use crate::parse_multi_surface;
127
128    #[test]
129    fn parsing_multi_surface_with_composite_surface_member() {
130        let source_text = "
131				<gml:MultiSurface srsDimension=\"3\">
132					<gml:surfaceMember>
133						<gml:CompositeSurface>
134							<gml:surfaceMember>
135								<gml:Polygon>
136									<gml:exterior>
137										<gml:LinearRing>
138											<gml:posList>314.531005859375 1043.4599609375 7.144559860229492 314.531005859375 1043.4599609375 2.6047298908233643 314.68798828125 1043.22998046875 2.6047298908233643 314.531005859375 1043.4599609375 7.144559860229492</gml:posList>
139										</gml:LinearRing>
140									</gml:exterior>
141								</gml:Polygon>
142							</gml:surfaceMember>
143							<gml:surfaceMember>
144								<gml:Polygon>
145									<gml:exterior>
146										<gml:LinearRing>
147											<gml:posList>314.531005859375 1043.4599609375 7.144559860229492 314.68798828125 1043.22998046875 2.6047298908233643 315.7770080566406 1041.6500244140625 2.6047298908233643 314.531005859375 1043.4599609375 7.144559860229492</gml:posList>
148										</gml:LinearRing>
149									</gml:exterior>
150								</gml:Polygon>
151							</gml:surfaceMember>
152							<gml:surfaceMember>
153								<gml:Polygon>
154									<gml:exterior>
155										<gml:LinearRing>
156											<gml:posList>314.531005859375 1043.4599609375 7.144559860229492 315.7770080566406 1041.6500244140625 2.6047298908233643 316.1080017089844 1041.1700439453125 7.144559860229492 314.531005859375 1043.4599609375 7.144559860229492</gml:posList>
157										</gml:LinearRing>
158									</gml:exterior>
159								</gml:Polygon>
160							</gml:surfaceMember>
161						</gml:CompositeSurface>
162					</gml:surfaceMember>
163				</gml:MultiSurface>
164			";
165
166        let _result = parse_multi_surface(source_text).unwrap();
167    }
168
169    #[test]
170    fn parsing_multi_surface() {
171        let source_text = "<gml:MultiSurface gml:id=\"UUID_6b33ecfa-6e08-4e8e-a4b5-e1d06540faf0\">
172              <gml:surfaceMember>
173                <gml:Polygon gml:id=\"UUID_efb8f6a5-82fa-4b21-8709-c1d93ed1e595\">
174                  <gml:exterior>
175                    <gml:LinearRing>
176                      <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>
177                    </gml:LinearRing>
178                  </gml:exterior>
179                </gml:Polygon>
180              </gml:surfaceMember>
181            </gml:MultiSurface>";
182
183        let _result = parse_multi_surface(source_text).unwrap();
184    }
185
186    #[test]
187    fn parsing_multi_surface_with_duplicate_elements() {
188        let source_text = "<gml:MultiSurface srsName=\"EPSG:25832\" srsDimension=\"3\">
189              <gml:surfaceMember>
190                <gml:Polygon gml:id=\"4018133_PG.3nRTCd4XPu47PsAAUyNv\">
191                  <gml:exterior>
192                    <gml:LinearRing gml:id=\"4018133_LR.lHfcvQUrKVl08ifcH6eO\">
193                      <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>
194                    </gml:LinearRing>
195                  </gml:exterior>
196                </gml:Polygon>
197              </gml:surfaceMember>
198            </gml:MultiSurface>";
199
200        let _result = parse_multi_surface(source_text).unwrap();
201    }
202
203    #[test]
204    fn parsing_multi_surface_with_holes() {
205        let source_text = "
206            <gml:MultiSurface srsName=\"EPSG:25832\" srsDimension=\"3\">
207              <gml:surfaceMember>
208                <gml:Polygon gml:id=\"4018106_PG.dKY9ug9ol2tsxL5bLAPz\">
209                  <gml:exterior>
210                    <gml:LinearRing gml:id=\"4018106_LR.Wqmtl1E6Yz3eVJkuGjsK\">
211                      <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>
212                    </gml:LinearRing>
213                  </gml:exterior>
214                  <gml:interior>
215                    <gml:LinearRing gml:id=\"4018106_LR.10JNDsQqif3fouy54mfv\">
216                      <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>
217                    </gml:LinearRing>
218                  </gml:interior>
219                  <gml:interior>
220                    <gml:LinearRing gml:id=\"4018106_LR.yzLlZkAQX00eXb6Xi0DZ\">
221                      <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>
222                    </gml:LinearRing>
223                  </gml:interior>
224                  <gml:interior>
225                    <gml:LinearRing gml:id=\"4018106_LR.MIkI0SEPyMQ4yblCNiF2\">
226                      <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>
227                    </gml:LinearRing>
228                  </gml:interior>
229                  <gml:interior>
230                    <gml:LinearRing gml:id=\"4018106_LR.novU6ZVfhrtxrFFh7eYQ\">
231                      <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>
232                    </gml:LinearRing>
233                  </gml:interior>
234                  <gml:interior>
235                    <gml:LinearRing gml:id=\"4018106_LR.XdJcfjsruS75wlUmTQdH\">
236                      <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>
237                    </gml:LinearRing>
238                  </gml:interior>
239                  <gml:interior>
240                    <gml:LinearRing gml:id=\"4018106_LR.wzwxsPr4Ys8dTM1bzH8T\">
241                      <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>
242                    </gml:LinearRing>
243                  </gml:interior>
244                  <gml:interior>
245                    <gml:LinearRing gml:id=\"4018106_LR.20P6FwXiq4ZJ4EAxdmJ0\">
246                      <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>
247                    </gml:LinearRing>
248                  </gml:interior>
249                  <gml:interior>
250                    <gml:LinearRing gml:id=\"4018106_LR.saIYdVUNcoK3LJkC2LDw\">
251                      <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>
252                    </gml:LinearRing>
253                  </gml:interior>
254                  <gml:interior>
255                    <gml:LinearRing gml:id=\"4018106_LR.yPDE98qtqfTYBziBsTpl\">
256                      <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>
257                    </gml:LinearRing>
258                  </gml:interior>
259                  <gml:interior>
260                    <gml:LinearRing gml:id=\"4018106_LR.XaQt7QEqeVnG2PB8D6ad\">
261                      <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>
262                    </gml:LinearRing>
263                  </gml:interior>
264                  <gml:interior>
265                    <gml:LinearRing gml:id=\"4018106_LR.kCEyyLA2tigxjpQY9cyU\">
266                      <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>
267                    </gml:LinearRing>
268                  </gml:interior>
269                  <gml:interior>
270                    <gml:LinearRing gml:id=\"4018106_LR.Wq5AG6YS8zrN5HgtFQD8\">
271                      <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>
272                    </gml:LinearRing>
273                  </gml:interior>
274                  <gml:interior>
275                    <gml:LinearRing gml:id=\"4018106_LR.aQFMEYkDQkns0ZoJ66pj\">
276                      <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>
277                    </gml:LinearRing>
278                  </gml:interior>
279                  <gml:interior>
280                    <gml:LinearRing gml:id=\"4018106_LR.tXljCrPP3Efr0mz83aTx\">
281                      <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>
282                    </gml:LinearRing>
283                  </gml:interior>
284                  <gml:interior>
285                    <gml:LinearRing gml:id=\"4018106_LR.gLnR6siy7dPwvvNX2zz0\">
286                      <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>
287                    </gml:LinearRing>
288                  </gml:interior>
289                  <gml:interior>
290                    <gml:LinearRing gml:id=\"4018106_LR.Iw6I84mlFFHQEPQCpApK\">
291                      <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>
292                    </gml:LinearRing>
293                  </gml:interior>
294                </gml:Polygon>
295              </gml:surfaceMember>
296            </gml:MultiSurface>";
297
298        let result = parse_multi_surface(source_text).unwrap();
299
300        assert_eq!(result.surface_member().len(), 1);
301    }
302
303    #[test]
304    fn parsing_multi_surface_without_id() {
305        let source_text = "<gml:MultiSurface>
306              <gml:surfaceMember>
307                <gml:Polygon>
308                  <gml:exterior>
309                    <gml:LinearRing>
310                      <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>
311                    </gml:LinearRing>
312                  </gml:exterior>
313                </gml:Polygon>
314              </gml:surfaceMember>
315            </gml:MultiSurface>";
316
317        let result = parse_multi_surface(source_text).unwrap();
318
319        assert_eq!(result.surface_member().len(), 1);
320    }
321}