Skip to main content

egml_io/geometry/primitives/
polygon_patch.rs

1use crate::Error;
2use crate::primitives::abstract_ring_property::GmlRingProperty;
3use egml_core::model::geometry::primitives::{PolygonPatch, RingPropertyKind};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
7pub struct GmlPolygonPatch {
8    #[serde(rename(serialize = "gml:exterior", deserialize = "exterior"))]
9    pub exterior: Option<GmlRingProperty>,
10
11    #[serde(
12        rename(serialize = "gml:interior", deserialize = "interior"),
13        default,
14        skip_serializing_if = "Vec::is_empty"
15    )]
16    pub interior: Vec<GmlRingProperty>,
17}
18
19impl TryFrom<GmlPolygonPatch> for PolygonPatch {
20    type Error = Error;
21
22    fn try_from(value: GmlPolygonPatch) -> Result<Self, Self::Error> {
23        let exterior: Option<RingPropertyKind> =
24            value.exterior.map(|x| x.try_into()).transpose()?;
25
26        let interior: Vec<RingPropertyKind> = value
27            .interior
28            .into_iter()
29            .map(|x| x.try_into())
30            .collect::<Result<Vec<_>, _>>()?;
31
32        Ok(PolygonPatch::new(Default::default(), exterior, interior))
33    }
34}
35
36impl From<&PolygonPatch> for GmlPolygonPatch {
37    fn from(patch: &PolygonPatch) -> Self {
38        let exterior = patch.exterior().map(ring_property_kind_to_gml);
39        let interior = patch
40            .interior()
41            .iter()
42            .map(ring_property_kind_to_gml)
43            .collect();
44        Self { exterior, interior }
45    }
46}
47
48fn ring_property_kind_to_gml(kind: &RingPropertyKind) -> GmlRingProperty {
49    match kind {
50        RingPropertyKind::LinearRing(lr) => GmlRingProperty::from(lr),
51        RingPropertyKind::RingKind(_) => todo!("Ring serialization is not yet implemented"),
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use crate::primitives::GmlPolygonPatch;
58    use egml_core::model::geometry::DirectPosition;
59    use egml_core::model::geometry::primitives::{
60        AbstractRing, AbstractSurfacePatch, LinearRing, PolygonPatch, RingPropertyKind,
61    };
62    use quick_xml::{DeError, de, se};
63
64    #[test]
65    fn deserialize_polygon_patch() {
66        let xml_document = b"<gml:PolygonPatch>
67                    <gml:exterior>
68                        <gml:LinearRing>
69                            <gml:posList>350.54400634765625 972.9130249023438 0.11999999731779099 350.5414201635045 968.6025425887852 0.11999999731779099 350.54400634765625 968.6025096366793 0.11999999731779099 350.54400634765625 972.9130249023438 0.11999999731779099</gml:posList>
70                        </gml:LinearRing>
71                    </gml:exterior>
72                </gml:PolygonPatch>";
73
74        let parsed_result: Result<GmlPolygonPatch, DeError> =
75            de::from_reader(xml_document.as_ref());
76        let parsed_gml = parsed_result.expect("parsing should work");
77        let polygon_patch: PolygonPatch = parsed_gml.try_into().unwrap();
78
79        let exterior: &RingPropertyKind = polygon_patch.exterior().expect("should be set");
80        match exterior {
81            RingPropertyKind::LinearRing(x) => {
82                assert_eq!(x.points().len(), 3);
83            }
84            _ => panic!("should be linear ring"),
85        }
86    }
87
88    #[test]
89    fn deserialize_polygon_patch_with_interior_rings() {
90        let xml_document = b"<gml:PolygonPatch>
91                    <gml:exterior>
92                        <gml:LinearRing>
93                            <gml:posList>350.54400634765625 972.9130249023438 0.11999999731779099 350.5414201635045 968.6025425887852 0.11999999731779099 350.54400634765625 968.6025096366793 0.11999999731779099 350.54400634765625 972.9130249023438 0.11999999731779099</gml:posList>
94                        </gml:LinearRing>
95                    </gml:exterior>
96                    <gml:interior>
97                        <gml:LinearRing>
98                            <gml:posList>350.54400634765625 972.9130249023438 0.11999999731779099 350.5414201635045 968.6025425887852 0.11999999731779099 350.54400634765625 968.6025096366793 0.11999999731779099 350.54400634765625 972.9130249023438 0.11999999731779099</gml:posList>
99                        </gml:LinearRing>
100                    </gml:interior>
101                    <gml:interior>
102                        <gml:LinearRing>
103                            <gml:posList>350.54400634765625 972.9130249023438 0.11999999731779099 350.5414201635045 968.6025425887852 0.11999999731779099 350.54400634765625 968.6025096366793 0.11999999731779099 350.54400634765625 972.9130249023438 0.11999999731779099</gml:posList>
104                        </gml:LinearRing>
105                    </gml:interior>
106                </gml:PolygonPatch>";
107
108        let parsed_result: Result<GmlPolygonPatch, DeError> =
109            de::from_reader(xml_document.as_ref());
110        let parsed_gml = parsed_result.expect("parsing should work");
111        let polygon_patch: PolygonPatch = parsed_gml.try_into().unwrap();
112
113        assert_eq!(polygon_patch.interior().len(), 2);
114
115        let exterior: &RingPropertyKind = polygon_patch.exterior().expect("should be set");
116        match exterior {
117            RingPropertyKind::LinearRing(x) => {
118                assert_eq!(x.points().len(), 3);
119            }
120            _ => panic!("should be linear ring"),
121        }
122    }
123
124    fn make_polygon_patch() -> PolygonPatch {
125        let points = vec![
126            DirectPosition::new(0.0, 0.0, 0.0).unwrap(),
127            DirectPosition::new(1.0, 0.0, 0.0).unwrap(),
128            DirectPosition::new(0.0, 1.0, 0.0).unwrap(),
129        ];
130        let ring = LinearRing::new(AbstractRing::default(), points).unwrap();
131        PolygonPatch::new(
132            AbstractSurfacePatch::default(),
133            Some(RingPropertyKind::LinearRing(ring)),
134            vec![],
135        )
136    }
137
138    #[test]
139    fn serialize_polygon_patch_writes_gml_tags() {
140        let patch = make_polygon_patch();
141        let gml = GmlPolygonPatch::from(&patch);
142        let xml = se::to_string_with_root("gml:PolygonPatch", &gml).unwrap();
143
144        assert!(xml.contains("<gml:PolygonPatch"));
145        assert!(xml.contains("<gml:exterior"));
146        assert!(xml.contains("<gml:LinearRing"));
147        assert!(xml.contains("<gml:posList"));
148    }
149
150    #[test]
151    fn round_trip_polygon_patch_from_xml() {
152        let input_xml = "<gml:PolygonPatch>\
153            <gml:exterior><gml:LinearRing><gml:posList srsDimension=\"3\">0 0 0 1 0 0 0 1 0 0 0 0</gml:posList></gml:LinearRing></gml:exterior>\
154            </gml:PolygonPatch>";
155
156        let gml: GmlPolygonPatch = de::from_reader(input_xml.as_bytes()).unwrap();
157        let patch: PolygonPatch = gml.try_into().unwrap();
158        let output_xml =
159            se::to_string_with_root("gml:PolygonPatch", &GmlPolygonPatch::from(&patch)).unwrap();
160
161        assert_eq!(input_xml, output_xml);
162    }
163}