egml-io 0.0.2-alpha.1

IO operations for processing GML data.
Documentation
use crate::Error;
use crate::primitives::GmlSurfaceKind;
use egml_core::model::geometry::primitives::{SurfaceKind, SurfaceProperty};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct GmlSurfaceProperty {
    #[serde(rename = "@href", skip_serializing_if = "Option::is_none")]
    pub href: Option<String>,

    #[serde(rename = "$value", skip_serializing_if = "Option::is_none")]
    pub content: Option<GmlSurfaceKind>,
}

impl TryFrom<GmlSurfaceProperty> for SurfaceProperty {
    type Error = Error;

    fn try_from(item: GmlSurfaceProperty) -> Result<Self, Self::Error> {
        if item.href.is_some() && item.content.is_none() {
            return Err(Error::UnsupportedXLink());
        }

        let surface: SurfaceKind = item
            .content
            .ok_or(Error::MissingSurfaceKind(
                "parsing the GmlSurfaceProperty".to_string(),
            ))?
            .try_into()?;

        Ok(SurfaceProperty::new(surface))
    }
}

impl From<&SurfaceProperty> for GmlSurfaceProperty {
    fn from(prop: &SurfaceProperty) -> Self {
        Self {
            href: None,
            content: Some(GmlSurfaceKind::from(&prop.content)),
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::primitives::GmlSurfaceProperty;
    use egml_core::model::geometry::DirectPosition;
    use egml_core::model::geometry::primitives::{
        AbstractRing, AbstractSurface, LinearRing, Polygon, RingPropertyKind, SurfaceKind,
        SurfaceProperty,
    };
    use quick_xml::{de, se};

    fn make_surface_property() -> SurfaceProperty {
        let points = vec![
            DirectPosition::new(0.0, 0.0, 0.0).unwrap(),
            DirectPosition::new(1.0, 0.0, 0.0).unwrap(),
            DirectPosition::new(0.0, 1.0, 0.0).unwrap(),
        ];
        let ring = LinearRing::new(AbstractRing::default(), points).unwrap();
        let polygon = Polygon::new(
            AbstractSurface::default(),
            Some(RingPropertyKind::LinearRing(ring)),
            vec![],
        )
        .unwrap();
        SurfaceProperty::new(SurfaceKind::Polygon(polygon))
    }

    #[test]
    fn deserialize_surface_property_with_xlink() {
        let xml_document = b"\
              <gml:surfaceMember xlink:href=\"#DEBY_LOD2_59772_4becb506-d53b-44ca-a483-e6a3d238b4c2_2_poly\"/>
              <gml:surfaceMember xlink:href=\"#DEBY_LOD2_59772_be3462c3-9865-467b-829d-76e6b9b692e7_2_poly\"/>
              <gml:surfaceMember xlink:href=\"#DEBY_LOD2_59772_c0aae462-3f4b-4062-80bb-8cd04768ab1a_2_poly\"/>";

        let parsed_geometry: GmlSurfaceProperty = de::from_reader(xml_document.as_ref()).unwrap();

        assert_eq!(
            parsed_geometry.href,
            Some("#DEBY_LOD2_59772_4becb506-d53b-44ca-a483-e6a3d238b4c2_2_poly".to_string())
        );
    }

    #[test]
    fn serialize_surface_property_writes_polygon_element() {
        let prop = make_surface_property();
        let gml = GmlSurfaceProperty::from(&prop);
        let xml = se::to_string_with_root("gml:surfaceMember", &gml).unwrap();

        assert!(xml.contains("<gml:Polygon"));
        assert!(xml.contains("<gml:exterior"));
        assert!(xml.contains("<gml:LinearRing"));
    }

    #[test]
    fn round_trip_surface_property_from_xml() {
        let input_xml = "<gml:surfaceMember>\
            <gml:Polygon><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></gml:Polygon>\
            </gml:surfaceMember>";

        let gml: GmlSurfaceProperty = de::from_reader(input_xml.as_bytes()).unwrap();
        let output_xml = se::to_string_with_root("gml:surfaceMember", &gml).unwrap();

        assert_eq!(input_xml, output_xml);
    }
}