Skip to main content

egml_core/model/geometry/complexes/
composite_surface.rs

1use crate::model::geometry::aggregates::AggregationType;
2use crate::model::geometry::primitives::{
3    AbstractSurface, AsAbstractSurface, AsAbstractSurfaceMut, SurfaceProperty, TriangulatedSurface,
4};
5use crate::model::geometry::{DirectPosition, Envelope};
6use crate::{Error, impl_abstract_surface_traits};
7use nalgebra::Isometry3;
8use rayon::iter::IntoParallelRefMutIterator;
9use rayon::iter::ParallelIterator;
10
11/// A topology-aware surface composed of connected [`SurfaceProperty`] members.
12///
13/// Corresponds to `gml:CompositeSurface` in [OGC 07-036 §11.2.2.3](https://docs.ogc.org/is/07-036/07-036.pdf).  Unlike
14/// [`MultiSurface`](crate::model::geometry::aggregates::MultiSurface), a
15/// `CompositeSurface` requires that its constituent surfaces share boundaries
16/// coherently, forming a single connected manifold.
17#[derive(Debug, Clone, PartialEq)]
18pub struct CompositeSurface {
19    pub(crate) abstract_surface: AbstractSurface,
20    surface_member: Vec<SurfaceProperty>,
21    aggregation_type: AggregationType,
22}
23
24impl CompositeSurface {
25    /// Creates a new `CompositeSurface` from surface members and an aggregation type.
26    ///
27    /// # Errors
28    ///
29    /// Returns [`Error::TooFewElements`] if `surface_members` is empty.
30    pub fn new(
31        surface_members: impl IntoIterator<Item = SurfaceProperty>,
32        aggregation_type: AggregationType,
33    ) -> Result<Self, Error> {
34        let surface_members: Vec<SurfaceProperty> = surface_members.into_iter().collect();
35        if surface_members.is_empty() {
36            return Err(Error::TooFewElements {
37                geometry: "gml:CompositeSurface",
38                minimum: 1,
39                spec: Some("OGC 07-036 §10.5.11.4"),
40                id: None,
41                detail: None,
42            });
43        }
44
45        Ok(CompositeSurface {
46            abstract_surface: AbstractSurface::default(),
47            surface_member: surface_members,
48            aggregation_type,
49        })
50    }
51
52    /// Returns the surface members as a slice.
53    pub fn surface_member(&self) -> &[SurfaceProperty] {
54        &self.surface_member
55    }
56
57    pub fn set_surface_member(&mut self, surface_members: Vec<SurfaceProperty>) {
58        self.surface_member = surface_members;
59    }
60
61    pub fn push_surface_member(&mut self, member: SurfaceProperty) {
62        self.surface_member.push(member);
63    }
64
65    pub fn extend_surface_members(&mut self, members: impl IntoIterator<Item = SurfaceProperty>) {
66        self.surface_member.extend(members);
67    }
68
69    /// Returns the aggregation type that qualifies how members relate.
70    pub fn aggregation_type(&self) -> AggregationType {
71        self.aggregation_type
72    }
73
74    pub fn set_aggregation_type(&mut self, aggregation_type: AggregationType) {
75        self.aggregation_type = aggregation_type;
76    }
77}
78
79impl CompositeSurface {
80    /// Returns the number of surface members.
81    pub fn surface_member_count(&self) -> usize {
82        self.surface_member.len()
83    }
84
85    /// Triangulates all surface members and merges them into a single [`TriangulatedSurface`].
86    ///
87    /// # Errors
88    ///
89    /// Returns [`Error::TriangulationFailed`] if any member cannot be triangulated.
90    pub fn triangulate(&self) -> Result<TriangulatedSurface, Error> {
91        let triangulated_surfaces = self
92            .surface_member
93            .iter()
94            .flat_map(|x| x.object.as_ref())
95            .map(|x| x.triangulate())
96            .collect::<Result<Vec<TriangulatedSurface>, Error>>()?;
97
98        TriangulatedSurface::from_triangulated_surfaces(triangulated_surfaces)
99    }
100
101    /// Returns the union of the bounding boxes of all surface members.
102    pub fn compute_envelope(&self) -> Option<Envelope> {
103        let envelopes: Vec<Envelope> = self
104            .surface_member
105            .iter()
106            .flat_map(|x| x.object.as_ref())
107            .flat_map(|x| x.compute_envelope())
108            .collect::<Vec<_>>();
109
110        Envelope::from_envelopes(&envelopes)
111    }
112
113    pub fn area_3d(&self) -> Result<f64, Error> {
114        self.surface_member
115            .iter()
116            .map(|s| {
117                s.object
118                    .as_ref()
119                    .ok_or_else(|| Error::UnresolvedSurfaceReference {
120                        href: s.href.clone(),
121                    })
122                    .and_then(|kind| kind.area_3d())
123            })
124            .collect::<Result<Vec<f64>, Error>>()
125            .map(|area_3ds| area_3ds.into_iter().sum())
126    }
127
128    pub fn apply_transform(&mut self, m: &Isometry3<f64>) {
129        self.surface_member
130            .par_iter_mut()
131            .flat_map(|x| x.object.as_mut())
132            .for_each(|x| x.apply_transform(m));
133    }
134
135    pub fn points(&self) -> Vec<&DirectPosition> {
136        todo!("needs to be implemented")
137    }
138}
139
140impl AsAbstractSurface for CompositeSurface {
141    fn abstract_surface(&self) -> &AbstractSurface {
142        &self.abstract_surface
143    }
144}
145
146impl AsAbstractSurfaceMut for CompositeSurface {
147    fn abstract_surface_mut(&mut self) -> &mut AbstractSurface {
148        &mut self.abstract_surface
149    }
150}
151
152impl_abstract_surface_traits!(CompositeSurface);