Skip to main content

egml_core/model/geometry/aggregates/
multi_surface.rs

1use crate::error::Error;
2use crate::model::base::HasAssociationAttributes;
3use crate::model::common::{
4    ApplyTransform, ComputeEnvelope, IterGeometries, Triangulate, Triangulation,
5};
6use crate::model::geometry::aggregates::{
7    AbstractGeometricAggregate, AsAbstractGeometricAggregate, AsAbstractGeometricAggregateMut,
8};
9use crate::model::geometry::primitives::AbstractSurfaceProperty;
10use crate::model::geometry::primitives::TriangulatedSurface;
11use crate::model::geometry::refs::AbstractGeometryKindRef;
12use crate::model::geometry::{DirectPosition, Envelope};
13use crate::{
14    impl_abstract_geometric_aggregate_mut_traits, impl_abstract_geometric_aggregate_traits,
15    impl_has_geometry_type,
16};
17use nalgebra::{Isometry3, Rotation3, Scale3, Transform3, Vector3};
18use rayon::prelude::*;
19
20/// An unordered collection of [`SurfaceKind`] members.
21///
22/// Corresponds to `gml:MultiSurface` in [OGC 07-036 §11.3.4.1](https://docs.ogc.org/is/07-036/07-036.pdf).
23#[derive(Debug, Clone, PartialEq)]
24pub struct MultiSurface {
25    pub abstract_geometric_aggregate: AbstractGeometricAggregate,
26    surface_member: Vec<AbstractSurfaceProperty>,
27}
28
29impl MultiSurface {
30    /// Creates a new `MultiSurface` from a list of surface members.
31    ///
32    /// # Errors
33    ///
34    /// Returns [`Error::TooFewElements`] if `members` is empty.
35    pub fn new(members: impl IntoIterator<Item = AbstractSurfaceProperty>) -> Result<Self, Error> {
36        let members: Vec<AbstractSurfaceProperty> = members.into_iter().collect();
37        Self::validate(&members)?;
38
39        Ok(Self {
40            abstract_geometric_aggregate: AbstractGeometricAggregate::default(),
41            surface_member: members,
42        })
43    }
44
45    pub fn from_abstract_geometric_aggregate(
46        abstract_geometric_aggregate: AbstractGeometricAggregate,
47        members: impl IntoIterator<Item = AbstractSurfaceProperty>,
48    ) -> Result<Self, Error> {
49        let members: Vec<AbstractSurfaceProperty> = members.into_iter().collect();
50        Self::validate(&members)?;
51
52        Ok(Self {
53            abstract_geometric_aggregate,
54            surface_member: members,
55        })
56    }
57
58    fn validate(members: &[AbstractSurfaceProperty]) -> Result<(), Error> {
59        if members.is_empty() {
60            return Err(Error::TooFewElements {
61                geometry: "gml:MultiSurface",
62                minimum: 1,
63                spec: Some("OGC 07-036 §11.3.4.1"),
64                id: None,
65                detail: None,
66            });
67        }
68        Ok(())
69    }
70
71    /// Returns the surface members as a slice.
72    pub fn surface_member(&self) -> &[AbstractSurfaceProperty] {
73        &self.surface_member
74    }
75
76    /// Replaces the surface members.
77    ///
78    /// # Errors
79    ///
80    /// Returns [`Error::TooFewElements`] if `val` is empty.
81    pub fn set_surface_member(&mut self, val: Vec<AbstractSurfaceProperty>) -> Result<(), Error> {
82        Self::validate(&val)?;
83
84        self.surface_member = val;
85        Ok(())
86    }
87
88    pub fn push_surface_member(&mut self, member: AbstractSurfaceProperty) {
89        self.surface_member.push(member);
90    }
91
92    pub fn extend_surface_members(
93        &mut self,
94        members: impl IntoIterator<Item = AbstractSurfaceProperty>,
95    ) {
96        self.surface_member.extend(members);
97    }
98}
99
100impl AsAbstractGeometricAggregate for MultiSurface {
101    fn abstract_geometric_aggregate(&self) -> &AbstractGeometricAggregate {
102        &self.abstract_geometric_aggregate
103    }
104}
105
106impl AsAbstractGeometricAggregateMut for MultiSurface {
107    fn abstract_geometric_aggregate_mut(&mut self) -> &mut AbstractGeometricAggregate {
108        &mut self.abstract_geometric_aggregate
109    }
110}
111
112impl_abstract_geometric_aggregate_traits!(MultiSurface);
113impl_abstract_geometric_aggregate_mut_traits!(MultiSurface);
114impl_has_geometry_type!(MultiSurface, MultiSurface);
115
116impl MultiSurface {
117    pub fn points(&self) -> Vec<&DirectPosition> {
118        self.surface_member
119            .iter()
120            .flat_map(|x| x.object())
121            .fold(Vec::new(), |mut acc, x| {
122                acc.extend(x.points().iter());
123                acc
124            })
125    }
126
127    /// Returns the total 3D area_3d of all surface members.
128    ///
129    /// # Errors
130    ///
131    /// Returns [`Error::UnresolvedSurfaceReference`] if any member carries only an
132    /// xlink:href that has not been resolved into an inline object.
133    /// Propagates any error from [`SurfaceKind::area_3d`].
134    pub fn area_3d(&self) -> Result<f64, Error> {
135        self.surface_member
136            .iter()
137            .map(|s| {
138                s.object()
139                    .ok_or_else(|| Error::UnresolvedSurfaceReference {
140                        href: s.href().as_ref().map(|h| h.to_string()),
141                    })
142                    .and_then(|kind| kind.area_3d())
143            })
144            .collect::<Result<Vec<f64>, Error>>()
145            .map(|area_3ds| area_3ds.into_iter().sum())
146    }
147}
148
149impl Triangulate for MultiSurface {
150    /// Members that fail to triangulate individually (e.g. a degenerate ring) are
151    /// skipped rather than failing the whole aggregate; see their errors via
152    /// [`Triangulation::skipped`].
153    ///
154    /// # Errors
155    ///
156    /// Returns [`Error::TooFewElements`] if no member could be triangulated.
157    fn triangulate(&self) -> Result<Triangulation, Error> {
158        let mut surfaces = Vec::new();
159        let mut skipped = Vec::new();
160
161        for member in self.surface_member.iter().flat_map(|x| x.object()) {
162            match member.triangulate() {
163                Ok(triangulation) => {
164                    let (surface, nested_skipped) = triangulation.into_parts();
165                    surfaces.push(surface);
166                    skipped.extend(nested_skipped);
167                }
168                Err(error) => {
169                    skipped.push(error);
170                }
171            }
172        }
173
174        let combined = TriangulatedSurface::from_triangulated_surfaces(surfaces)?;
175        Ok(Triangulation::new(combined, skipped))
176    }
177}
178
179impl ApplyTransform for MultiSurface {
180    fn apply_transform(&mut self, transform: Transform3<f64>) {
181        self.surface_member.par_iter_mut().for_each(|p| {
182            if let Some(x) = p.object_mut() {
183                x.apply_transform(transform);
184            }
185        });
186    }
187
188    fn apply_isometry(&mut self, isometry: Isometry3<f64>) {
189        self.surface_member.par_iter_mut().for_each(|p| {
190            if let Some(x) = p.object_mut() {
191                x.apply_isometry(isometry);
192            }
193        });
194    }
195
196    fn apply_translation(&mut self, vector: Vector3<f64>) {
197        self.surface_member.par_iter_mut().for_each(|p| {
198            if let Some(x) = p.object_mut() {
199                x.apply_translation(vector);
200            }
201        });
202    }
203
204    fn apply_rotation(&mut self, rotation: Rotation3<f64>) {
205        self.surface_member.par_iter_mut().for_each(|p| {
206            if let Some(x) = p.object_mut() {
207                x.apply_rotation(rotation);
208            }
209        });
210    }
211
212    fn apply_scale(&mut self, scale: Scale3<f64>) {
213        self.surface_member.par_iter_mut().for_each(|p| {
214            if let Some(x) = p.object_mut() {
215                x.apply_scale(scale);
216            }
217        });
218    }
219}
220
221impl ComputeEnvelope for MultiSurface {
222    /// Returns the union of the bounding boxes of all surface members.
223    fn compute_envelope(&self) -> Option<Envelope> {
224        let envelopes: Vec<Envelope> = self
225            .surface_member
226            .iter()
227            .flat_map(|x| x.object())
228            .flat_map(|x| x.compute_envelope())
229            .collect();
230
231        Envelope::from_envelopes(&envelopes)
232    }
233}
234
235impl IterGeometries for MultiSurface {
236    fn iter_geometries(&self) -> Box<dyn Iterator<Item = AbstractGeometryKindRef<'_>> + '_> {
237        Box::new(
238            std::iter::once(self.into()).chain(
239                self.surface_member
240                    .iter()
241                    .filter_map(|x| x.object())
242                    .flat_map(|x| x.iter_geometries()),
243            ),
244        )
245    }
246}
247
248#[cfg(test)]
249mod tests {
250    use super::*;
251    use crate::Error;
252    use crate::model::geometry::DirectPosition;
253    use crate::model::geometry::primitives::{
254        AbstractRingKind, AbstractRingProperty, AbstractSurfaceKind, LinearRing,
255    };
256
257    fn unit_square_polygon(z: f64) -> AbstractSurfaceKind {
258        let ring = LinearRing::new([
259            DirectPosition::new(0.0, 0.0, z).unwrap(),
260            DirectPosition::new(1.0, 0.0, z).unwrap(),
261            DirectPosition::new(1.0, 1.0, z).unwrap(),
262            DirectPosition::new(0.0, 1.0, z).unwrap(),
263        ])
264        .unwrap();
265        let polygon = crate::model::geometry::primitives::Polygon::new(
266            Some(AbstractRingProperty::from_object(
267                AbstractRingKind::LinearRing(ring),
268            )),
269            vec![],
270        )
271        .unwrap();
272        AbstractSurfaceKind::Polygon(polygon)
273    }
274
275    #[test]
276    fn area_3d_two_unit_squares() {
277        let multi_surface = MultiSurface::new([
278            AbstractSurfaceProperty::from_object(unit_square_polygon(0.0)),
279            AbstractSurfaceProperty::from_object(unit_square_polygon(1.0)),
280        ])
281        .unwrap();
282        assert!((multi_surface.area_3d().unwrap() - 2.0).abs() < 1e-10);
283    }
284
285    #[test]
286    fn iter_geometries_yields_self_each_polygon_and_each_ring() {
287        let multi_surface = MultiSurface::new([
288            AbstractSurfaceProperty::from_object(unit_square_polygon(0.0)),
289            AbstractSurfaceProperty::from_object(unit_square_polygon(1.0)),
290        ])
291        .unwrap();
292
293        // self (1) + 2 polygons (2) + 2 rings, one per polygon (2) = 5
294        assert_eq!(multi_surface.iter_geometries().count(), 5);
295    }
296
297    #[test]
298    fn area_3d_unresolved_surface_reference() {
299        let multi_surface = MultiSurface::new([AbstractSurfaceProperty::from_href(
300            "urn:example:surface-1".into(),
301        )])
302        .unwrap();
303        assert_eq!(
304            multi_surface.area_3d(),
305            Err(Error::UnresolvedSurfaceReference {
306                href: Some("urn:example:surface-1".to_string())
307            })
308        );
309    }
310}