Skip to main content

egml_core/model/feature/
abstract_feature.rs

1use crate::model::base::{AbstractGml, AsAbstractGml, AsAbstractGmlMut};
2use crate::model::common::ApplyTransform;
3use crate::model::feature::bounding_shape::BoundingShape;
4use crate::model::geometry::Envelope;
5use nalgebra::{Isometry3, Rotation3, Scale3, Transform3, Vector3};
6
7/// Base class for all GML features ([OGC 07-036 ยง9.3.1](https://docs.ogc.org/is/07-036/07-036.pdf), `gml:AbstractFeatureType`).
8///
9/// Extends [`AbstractGml`] with an optional bounding envelope.  All concrete
10/// feature types defined in GML application schemas embed `AbstractFeature`.
11#[derive(Debug, Clone, PartialEq, Default)]
12pub struct AbstractFeature {
13    /// Base GML object data (id, name).
14    pub abstract_gml: AbstractGml,
15    /// Optional precomputed bounding shape.
16    bounded_by: Option<BoundingShape>,
17}
18
19impl AbstractFeature {
20    /// Creates a new `AbstractFeature` with the given GML base data and no bounding envelope.
21    ///
22    /// # Examples
23    ///
24    /// ```rust
25    /// use egml_core::model::base::AbstractGml;
26    /// use egml_core::model::feature::AbstractFeature;
27    /// use crate::egml_core::model::feature::AsAbstractFeature;
28    ///
29    /// let feature = AbstractFeature::new();
30    /// assert!(feature.bounded_by().is_none());
31    /// ```
32    pub fn new() -> Self {
33        Self {
34            abstract_gml: AbstractGml::default(),
35            bounded_by: None,
36        }
37    }
38
39    pub fn from_abstract_gml(abstract_gml: AbstractGml) -> Self {
40        Self {
41            abstract_gml,
42            bounded_by: None,
43        }
44    }
45}
46
47impl ApplyTransform for AbstractFeature {
48    fn apply_transform(&mut self, transform: Transform3<f64>) {
49        if let Some(bounding_shape) = self.bounded_by.as_mut() {
50            bounding_shape.apply_transform(transform);
51        }
52    }
53
54    fn apply_isometry(&mut self, isometry: Isometry3<f64>) {
55        if let Some(bounding_shape) = self.bounded_by.as_mut() {
56            bounding_shape.apply_isometry(isometry);
57        }
58    }
59
60    fn apply_translation(&mut self, vector: Vector3<f64>) {
61        if let Some(bounding_shape) = self.bounded_by.as_mut() {
62            bounding_shape.apply_translation(vector);
63        }
64    }
65
66    fn apply_rotation(&mut self, rotation: Rotation3<f64>) {
67        if let Some(bounding_shape) = self.bounded_by.as_mut() {
68            bounding_shape.apply_rotation(rotation);
69        }
70    }
71
72    fn apply_scale(&mut self, scale: Scale3<f64>) {
73        if let Some(bounding_shape) = self.bounded_by.as_mut() {
74            bounding_shape.apply_scale(scale);
75        }
76    }
77}
78
79/// Object-safe read accessor for [`AbstractFeature`] fields.
80pub trait AsAbstractFeature: AsAbstractGml {
81    /// Returns a reference to the embedded [`AbstractFeature`] base data.
82    fn abstract_feature(&self) -> &AbstractFeature;
83
84    fn bounded_by(&self) -> Option<&BoundingShape> {
85        self.abstract_feature().bounded_by.as_ref()
86    }
87}
88
89/// Mutable companion to [`AsAbstractFeature`].
90pub trait AsAbstractFeatureMut: AsAbstractFeature + AsAbstractGmlMut {
91    /// Returns a mutable reference to the embedded [`AbstractFeature`] base data.
92    fn abstract_feature_mut(&mut self) -> &mut AbstractFeature;
93
94    fn set_bounded_by(&mut self, bounded_by: Option<BoundingShape>) {
95        self.abstract_feature_mut().bounded_by = bounded_by;
96    }
97
98    fn bounded_by_mut(&mut self) -> &mut Option<BoundingShape> {
99        &mut self.abstract_feature_mut().bounded_by
100    }
101
102    fn set_bounding_shape_from_envelope(&mut self, envelope: Option<Envelope>) {
103        let bounding_shape = envelope.map(BoundingShape::new);
104        <Self as AsAbstractFeatureMut>::abstract_feature_mut(self).bounded_by = bounding_shape;
105    }
106}
107
108impl AsAbstractFeature for AbstractFeature {
109    fn abstract_feature(&self) -> &AbstractFeature {
110        self
111    }
112}
113
114impl AsAbstractFeatureMut for AbstractFeature {
115    fn abstract_feature_mut(&mut self) -> &mut AbstractFeature {
116        self
117    }
118}
119
120#[macro_export]
121macro_rules! impl_abstract_feature_traits {
122    ($type:ty) => {
123        $crate::impl_abstract_gml_traits!($type);
124
125        impl $crate::model::base::AsAbstractGml for $type {
126            fn abstract_gml(&self) -> &$crate::model::base::AbstractGml {
127                &<$type as $crate::model::feature::AsAbstractFeature>::abstract_feature(self)
128                    .abstract_gml
129            }
130        }
131    };
132}
133
134#[macro_export]
135macro_rules! impl_abstract_feature_mut_traits {
136    ($type:ty) => {
137        $crate::impl_abstract_gml_mut_traits!($type);
138
139        impl $crate::model::base::AsAbstractGmlMut for $type {
140            fn abstract_gml_mut(&mut self) -> &mut $crate::model::base::AbstractGml {
141                &mut <$type as $crate::model::feature::AsAbstractFeatureMut>::abstract_feature_mut(
142                    self,
143                )
144                .abstract_gml
145            }
146        }
147    };
148}
149
150impl_abstract_feature_traits!(AbstractFeature);
151impl_abstract_feature_mut_traits!(AbstractFeature);