ecitygml-io 0.0.2-alpha.5

IO operations for processing CityGML data.
Documentation
use crate::Error;
use crate::gml::codec::vegetation::abstract_vegetation_object::{
    deserialize_abstract_vegetation_object, serialize_abstract_vegetation_object,
};
use crate::gml::util::CityGmlElement;
use ecitygml_core::model::vegetation::values::{
    SolitaryVegetationObjectClassValue, SolitaryVegetationObjectFunctionValue,
    SolitaryVegetationObjectUsageValue, SpeciesValue,
};
use ecitygml_core::model::vegetation::{AsAbstractVegetationObject, SolitaryVegetationObject};
use egml::io::codec::basic::{GmlCode, GmlMeasure};
use egml::io::util::{
    Formatting, XmlNode, XmlNodeContent, extract_xml_element_spans, serialize_inner,
};
use egml::model::basic_types::Code;
use quick_xml::de;
use serde::{Deserialize, Serialize};

pub fn deserialize_solitary_vegetation_object(
    xml_document: &[u8],
) -> Result<SolitaryVegetationObject, Error> {
    let spans = extract_xml_element_spans(xml_document)?;
    let abstract_vegetation_object = deserialize_abstract_vegetation_object(xml_document, &spans)?;
    let parsed =
        de::from_reader::<_, GmlSolitaryVegetationObject>(xml_document).map_err(Error::from)?;
    let mut solitary_vegetation_object =
        SolitaryVegetationObject::from_abstract_vegetation_object(abstract_vegetation_object);

    solitary_vegetation_object.set_class_opt(
        parsed
            .class
            .map(Code::from)
            .map(SolitaryVegetationObjectClassValue::from),
    );
    solitary_vegetation_object.set_functions(
        parsed
            .functions
            .into_iter()
            .map(Code::from)
            .map(SolitaryVegetationObjectFunctionValue::from)
            .collect(),
    );
    solitary_vegetation_object.set_usages(
        parsed
            .usages
            .into_iter()
            .map(Code::from)
            .map(SolitaryVegetationObjectUsageValue::from)
            .collect(),
    );
    solitary_vegetation_object
        .set_species_opt(parsed.species.map(Code::from).map(SpeciesValue::from));
    solitary_vegetation_object.set_height_opt(parsed.height.map(Into::into));
    solitary_vegetation_object.set_trunk_diameter_opt(parsed.trunk_diameter.map(Into::into));
    solitary_vegetation_object.set_crown_diameter_opt(parsed.crown_diameter.map(Into::into));
    solitary_vegetation_object
        .set_root_ball_diameter_opt(parsed.root_ball_diameter.map(Into::into));
    solitary_vegetation_object
        .set_max_root_ball_depth_opt(parsed.max_root_ball_depth.map(Into::into));

    Ok(solitary_vegetation_object)
}

pub fn serialize_solitary_vegetation_object(
    solitary_vegetation_object: &SolitaryVegetationObject,
    formatting: Formatting,
) -> Result<XmlNode, Error> {
    let mut parts = serialize_abstract_vegetation_object(
        solitary_vegetation_object.abstract_vegetation_object(),
        formatting,
    )?;

    if let Some(raw) = serialize_inner(
        GmlSolitaryVegetationObject::from(solitary_vegetation_object),
        formatting,
    )? {
        parts.content.push(XmlNodeContent::Raw(raw));
    }

    Ok(XmlNode::new(
        CityGmlElement::SolitaryVegetationObject.into(),
        parts,
    ))
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct GmlSolitaryVegetationObject {
    #[serde(
        rename(serialize = "veg:class", deserialize = "class"),
        skip_serializing_if = "Option::is_none"
    )]
    pub class: Option<GmlCode>,

    #[serde(rename(serialize = "veg:function", deserialize = "function"), default)]
    pub functions: Vec<GmlCode>,

    #[serde(rename(serialize = "veg:usage", deserialize = "usage"), default)]
    pub usages: Vec<GmlCode>,

    #[serde(
        rename(serialize = "veg:species", deserialize = "species"),
        skip_serializing_if = "Option::is_none"
    )]
    pub species: Option<GmlCode>,

    #[serde(
        rename(serialize = "veg:height", deserialize = "height"),
        skip_serializing_if = "Option::is_none"
    )]
    pub height: Option<GmlMeasure>,

    #[serde(
        rename(serialize = "veg:trunkDiameter", deserialize = "trunkDiameter"),
        skip_serializing_if = "Option::is_none"
    )]
    pub trunk_diameter: Option<GmlMeasure>,

    #[serde(
        rename(serialize = "veg:crownDiameter", deserialize = "crownDiameter"),
        skip_serializing_if = "Option::is_none"
    )]
    pub crown_diameter: Option<GmlMeasure>,

    #[serde(
        rename(serialize = "veg:rootBallDiameter", deserialize = "rootBallDiameter"),
        skip_serializing_if = "Option::is_none"
    )]
    pub root_ball_diameter: Option<GmlMeasure>,

    #[serde(
        rename(serialize = "veg:maxRootBallDepth", deserialize = "maxRootBallDepth"),
        skip_serializing_if = "Option::is_none"
    )]
    pub max_root_ball_depth: Option<GmlMeasure>,
}

impl From<&SolitaryVegetationObject> for GmlSolitaryVegetationObject {
    fn from(item: &SolitaryVegetationObject) -> Self {
        Self {
            class: item
                .class()
                .map(SolitaryVegetationObjectClassValue::code)
                .map(Into::into),
            functions: item
                .functions()
                .iter()
                .map(SolitaryVegetationObjectFunctionValue::code)
                .map(Into::into)
                .collect(),
            usages: item
                .usages()
                .iter()
                .map(SolitaryVegetationObjectUsageValue::code)
                .map(Into::into)
                .collect(),
            species: item.species().map(SpeciesValue::code).map(Into::into),
            height: item.height().map(Into::into),
            trunk_diameter: item.trunk_diameter().map(Into::into),
            crown_diameter: item.crown_diameter().map(Into::into),
            root_ball_diameter: item.root_ball_diameter().map(Into::into),
            max_root_ball_depth: item.max_root_ball_depth().map(Into::into),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ecitygml_core::model::core::{
        AsAbstractCityObject, AsAbstractFeature, AsAbstractOccupiedSpace, AsAbstractThematicSurface,
    };
    use egml::model::base::Id;

    #[test]
    fn test_deserialize_basic_solitary_vegetation_object() {
        let xml_document = b"<veg:SolitaryVegetationObject gml:id=\"UUID_cd516eff-0302-379f-a635-791ebe618098\">
      <genericAttribute>
        <gen:StringAttribute>
          <gen:name>opendrive_roadObject_type</gen:name>
          <gen:value>TREE</gen:value>
        </gen:StringAttribute>
      </genericAttribute>
      <lod1ImplicitRepresentation>
        <ImplicitGeometry>
          <transformationMatrix>-0.9092656859988072 -0.4162161845304895 0.0 0.0 0.4162161845304895 -0.9092656859988072 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0</transformationMatrix>
          <referencePoint>
            <gml:Point>
              <gml:pos srsDimension=\"3\">678133.7625855505 5403776.088934055 367.0738274934606</gml:pos>
            </gml:Point>
          </referencePoint>
        </ImplicitGeometry>
      </lod1ImplicitRepresentation>
      <veg:height uom=\"#m\">12.1966</veg:height>
      <veg:trunkDiameter uom=\"#m\">0.4</veg:trunkDiameter>
    </veg:SolitaryVegetationObject>";

        let solitary_vegetation_object =
            deserialize_solitary_vegetation_object(xml_document).expect("should work");

        assert_eq!(
            solitary_vegetation_object.feature_id(),
            &Id::try_from("UUID_cd516eff-0302-379f-a635-791ebe618098").expect("should work")
        );
        assert!(
            solitary_vegetation_object
                .lod1_implicit_representation()
                .is_some()
        );
        assert_eq!(solitary_vegetation_object.generic_attributes().len(), 1);

        assert_eq!(
            solitary_vegetation_object
                .height()
                .expect("height should be read")
                .value,
            12.1966
        );
        assert_eq!(
            solitary_vegetation_object
                .height()
                .expect("height should be read")
                .uom,
            "#m"
        );
        assert_eq!(
            solitary_vegetation_object
                .trunk_diameter()
                .expect("trunk_diameter should be read")
                .value,
            0.4
        );
        assert_eq!(
            solitary_vegetation_object
                .trunk_diameter()
                .expect("trunk_diameter should be read")
                .uom,
            "#m"
        );
    }
}