egml-core 0.0.2-alpha.4

Core primitives and operations for processing GML data.
Documentation
use crate::model::geometry::aggregates::AggregationType;
use crate::model::geometry::primitives::{
    AbstractSurface, AsAbstractSurface, AsAbstractSurfaceMut, SurfaceProperty, TriangulatedSurface,
};
use crate::model::geometry::{DirectPosition, Envelope};
use crate::{Error, impl_abstract_surface_traits};
use nalgebra::Isometry3;
use rayon::iter::IntoParallelRefMutIterator;
use rayon::iter::ParallelIterator;

/// A topology-aware surface composed of connected [`SurfaceProperty`] members.
///
/// Corresponds to `gml:CompositeSurface` in [OGC 07-036 §11.2.2.3](https://docs.ogc.org/is/07-036/07-036.pdf).  Unlike
/// [`MultiSurface`](crate::model::geometry::aggregates::MultiSurface), a
/// `CompositeSurface` requires that its constituent surfaces share boundaries
/// coherently, forming a single connected manifold.
#[derive(Debug, Clone, PartialEq)]
pub struct CompositeSurface {
    pub(crate) abstract_surface: AbstractSurface,
    surface_member: Vec<SurfaceProperty>,
    aggregation_type: AggregationType,
}

impl CompositeSurface {
    /// Creates a new `CompositeSurface` from surface members and an aggregation type.
    ///
    /// # Errors
    ///
    /// Returns [`Error::TooFewElements`] if `surface_members` is empty.
    pub fn new(
        surface_members: impl IntoIterator<Item = SurfaceProperty>,
        aggregation_type: AggregationType,
    ) -> Result<Self, Error> {
        let surface_members: Vec<SurfaceProperty> = surface_members.into_iter().collect();
        if surface_members.is_empty() {
            return Err(Error::TooFewElements {
                geometry: "gml:CompositeSurface",
                minimum: 1,
                spec: Some("OGC 07-036 §10.5.11.4"),
                id: None,
                detail: None,
            });
        }

        Ok(CompositeSurface {
            abstract_surface: AbstractSurface::default(),
            surface_member: surface_members,
            aggregation_type,
        })
    }

    /// Returns the surface members as a slice.
    pub fn surface_member(&self) -> &[SurfaceProperty] {
        &self.surface_member
    }

    pub fn set_surface_member(&mut self, surface_members: Vec<SurfaceProperty>) {
        self.surface_member = surface_members;
    }

    pub fn push_surface_member(&mut self, member: SurfaceProperty) {
        self.surface_member.push(member);
    }

    pub fn extend_surface_members(&mut self, members: impl IntoIterator<Item = SurfaceProperty>) {
        self.surface_member.extend(members);
    }

    /// Returns the aggregation type that qualifies how members relate.
    pub fn aggregation_type(&self) -> AggregationType {
        self.aggregation_type
    }

    pub fn set_aggregation_type(&mut self, aggregation_type: AggregationType) {
        self.aggregation_type = aggregation_type;
    }
}

impl CompositeSurface {
    /// Returns the number of surface members.
    pub fn surface_member_count(&self) -> usize {
        self.surface_member.len()
    }

    /// Triangulates all surface members and merges them into a single [`TriangulatedSurface`].
    ///
    /// # Errors
    ///
    /// Returns [`Error::TriangulationFailed`] if any member cannot be triangulated.
    pub fn triangulate(&self) -> Result<TriangulatedSurface, Error> {
        let triangulated_surfaces = self
            .surface_member
            .iter()
            .flat_map(|x| x.object.as_ref())
            .map(|x| x.triangulate())
            .collect::<Result<Vec<TriangulatedSurface>, Error>>()?;

        TriangulatedSurface::from_triangulated_surfaces(triangulated_surfaces)
    }

    /// Returns the union of the bounding boxes of all surface members.
    pub fn compute_envelope(&self) -> Option<Envelope> {
        let envelopes: Vec<Envelope> = self
            .surface_member
            .iter()
            .flat_map(|x| x.object.as_ref())
            .flat_map(|x| x.compute_envelope())
            .collect::<Vec<_>>();

        Envelope::from_envelopes(&envelopes)
    }

    pub fn area_3d(&self) -> Result<f64, Error> {
        self.surface_member
            .iter()
            .map(|s| {
                s.object
                    .as_ref()
                    .ok_or_else(|| Error::UnresolvedSurfaceReference {
                        href: s.href.clone(),
                    })
                    .and_then(|kind| kind.area_3d())
            })
            .collect::<Result<Vec<f64>, Error>>()
            .map(|area_3ds| area_3ds.into_iter().sum())
    }

    pub fn apply_transform(&mut self, m: &Isometry3<f64>) {
        self.surface_member
            .par_iter_mut()
            .flat_map(|x| x.object.as_mut())
            .for_each(|x| x.apply_transform(m));
    }

    pub fn points(&self) -> Vec<&DirectPosition> {
        todo!("needs to be implemented")
    }
}

impl AsAbstractSurface for CompositeSurface {
    fn abstract_surface(&self) -> &AbstractSurface {
        &self.abstract_surface
    }
}

impl AsAbstractSurfaceMut for CompositeSurface {
    fn abstract_surface_mut(&mut self) -> &mut AbstractSurface {
        &mut self.abstract_surface
    }
}

impl_abstract_surface_traits!(CompositeSurface);