Skip to main content

egml_core/model/geometry/
abstract_geometry.rs

1use crate::model::base::{AbstractGml, AsAbstractGml, AsAbstractGmlMut};
2use crate::model::geometry::SrsReferenceGroup;
3
4/// Base data shared by all GML geometry types ([OGC 07-036 ยง10.1.3.1](https://docs.ogc.org/is/07-036/07-036.pdf)).
5///
6/// Embeds [`AbstractGml`] and is in turn embedded by every concrete and
7/// abstract geometry.
8///
9/// The [`SrsReferenceGroup`] (`srsName`, `srsDimension`) is stored out of line
10/// in a lazily allocated box: it is almost never set on nested geometries
11/// (rings, patches, members of aggregates), so keeping it inline would cost
12/// every geometry object 24 bytes for data it does not have.
13#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
14pub struct AbstractGeometry {
15    pub abstract_gml: AbstractGml,
16    /// `None` whenever the group is empty, so equality and hashing do not
17    /// depend on whether an empty group was ever allocated.
18    srs_reference_group: Option<Box<SrsReferenceGroup>>,
19}
20
21impl AbstractGeometry {
22    /// Creates a new `AbstractGeometry` wrapping the provided GML base data.
23    pub fn new() -> Self {
24        Self {
25            abstract_gml: AbstractGml::default(),
26            srs_reference_group: None,
27        }
28    }
29
30    pub fn from_abstract_gml(abstract_gml: AbstractGml) -> Self {
31        Self {
32            abstract_gml,
33            srs_reference_group: None,
34        }
35    }
36
37    /// Applies `update` to the SRS reference group, allocating it on demand
38    /// and dropping the allocation again once the group is empty.
39    fn update_srs_reference_group(&mut self, update: impl FnOnce(&mut SrsReferenceGroup)) {
40        let group = self.srs_reference_group.get_or_insert_with(Box::default);
41        update(group);
42        if group.is_empty() {
43            self.srs_reference_group = None;
44        }
45    }
46}
47
48/// Object-safe read accessor for [`AbstractGeometry`] fields.
49pub trait AsAbstractGeometry: AsAbstractGml {
50    /// Returns a reference to the embedded [`AbstractGeometry`] base data.
51    fn abstract_geometry(&self) -> &AbstractGeometry;
52
53    /// Returns the SRS reference group, or `None` if neither attribute is set.
54    fn srs_reference_group(&self) -> Option<&SrsReferenceGroup> {
55        self.abstract_geometry().srs_reference_group.as_deref()
56    }
57
58    fn srs_name(&self) -> Option<&str> {
59        self.srs_reference_group()
60            .and_then(SrsReferenceGroup::srs_name)
61    }
62
63    fn srs_dimension(&self) -> Option<u32> {
64        self.srs_reference_group()
65            .and_then(SrsReferenceGroup::srs_dimension)
66    }
67}
68
69/// Mutable companion to [`AsAbstractGeometry`].
70pub trait AsAbstractGeometryMut: AsAbstractGeometry + AsAbstractGmlMut {
71    /// Returns a mutable reference to the embedded [`AbstractGeometry`] base data.
72    fn abstract_geometry_mut(&mut self) -> &mut AbstractGeometry;
73
74    /// Replaces the SRS reference group. An empty group is stored as `None`.
75    fn set_srs_reference_group(&mut self, srs_reference_group: SrsReferenceGroup) {
76        self.abstract_geometry_mut().srs_reference_group =
77            (!srs_reference_group.is_empty()).then(|| Box::new(srs_reference_group));
78    }
79
80    /// Removes both SRS attributes.
81    fn clear_srs_reference_group(&mut self) {
82        self.abstract_geometry_mut().srs_reference_group = None;
83    }
84
85    fn set_srs_name(&mut self, srs_name: impl Into<String>) {
86        self.abstract_geometry_mut()
87            .update_srs_reference_group(|group| group.set_srs_name(srs_name));
88    }
89
90    fn set_srs_name_opt(&mut self, srs_name: Option<String>) {
91        self.abstract_geometry_mut()
92            .update_srs_reference_group(|group| group.set_srs_name_opt(srs_name));
93    }
94
95    fn clear_srs_name(&mut self) {
96        self.abstract_geometry_mut()
97            .update_srs_reference_group(SrsReferenceGroup::clear_srs_name);
98    }
99
100    fn set_srs_dimension(&mut self, srs_dimension: u32) {
101        self.abstract_geometry_mut()
102            .update_srs_reference_group(|group| group.set_srs_dimension(srs_dimension));
103    }
104
105    fn set_srs_dimension_opt(&mut self, srs_dimension: Option<u32>) {
106        self.abstract_geometry_mut()
107            .update_srs_reference_group(|group| group.set_srs_dimension_opt(srs_dimension));
108    }
109
110    fn clear_srs_dimension(&mut self) {
111        self.abstract_geometry_mut()
112            .update_srs_reference_group(SrsReferenceGroup::clear_srs_dimension);
113    }
114}
115
116impl AsAbstractGeometry for AbstractGeometry {
117    fn abstract_geometry(&self) -> &AbstractGeometry {
118        self
119    }
120}
121
122impl AsAbstractGeometryMut for AbstractGeometry {
123    fn abstract_geometry_mut(&mut self) -> &mut AbstractGeometry {
124        self
125    }
126}
127
128#[macro_export]
129macro_rules! impl_abstract_geometry_traits {
130    ($type:ty) => {
131        $crate::impl_abstract_gml_traits!($type);
132
133        impl $crate::model::base::AsAbstractGml for $type {
134            fn abstract_gml(&self) -> &$crate::model::base::AbstractGml {
135                &<$type as $crate::model::geometry::AsAbstractGeometry>::abstract_geometry(self)
136                    .abstract_gml
137            }
138        }
139    };
140}
141
142#[macro_export]
143macro_rules! impl_abstract_geometry_mut_traits {
144    ($type:ty) => {
145        $crate::impl_abstract_gml_mut_traits!($type);
146
147        impl $crate::model::base::AsAbstractGmlMut for $type {
148            fn abstract_gml_mut(&mut self) -> &mut $crate::model::base::AbstractGml {
149                &mut <$type as $crate::model::geometry::AsAbstractGeometryMut>::abstract_geometry_mut(self)
150                    .abstract_gml
151            }
152        }
153    };
154}
155
156impl_abstract_geometry_traits!(AbstractGeometry);
157impl_abstract_geometry_mut_traits!(AbstractGeometry);
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162
163    #[test]
164    fn srs_attributes_are_not_allocated_by_default() {
165        let geometry = AbstractGeometry::new();
166        assert!(geometry.srs_reference_group().is_none());
167        assert_eq!(geometry.srs_name(), None);
168        assert_eq!(geometry.srs_dimension(), None);
169    }
170
171    #[test]
172    fn setting_srs_attributes_allocates_and_reads_back() {
173        let mut geometry = AbstractGeometry::new();
174        geometry.set_srs_name("EPSG:25832");
175        geometry.set_srs_dimension(3);
176
177        assert_eq!(geometry.srs_name(), Some("EPSG:25832"));
178        assert_eq!(geometry.srs_dimension(), Some(3));
179    }
180
181    #[test]
182    fn clearing_both_srs_attributes_drops_the_allocation() {
183        let mut geometry = AbstractGeometry::new();
184        geometry.set_srs_name("EPSG:25832");
185        geometry.set_srs_dimension(3);
186
187        geometry.clear_srs_name();
188        assert!(geometry.srs_reference_group().is_some());
189        assert_eq!(geometry.srs_dimension(), Some(3));
190
191        geometry.set_srs_dimension_opt(None);
192        assert!(geometry.srs_reference_group().is_none());
193        assert_eq!(geometry, AbstractGeometry::new());
194    }
195
196    #[test]
197    fn setting_an_empty_group_stores_none() {
198        let mut geometry = AbstractGeometry::new();
199        geometry.set_srs_name("EPSG:25832");
200
201        geometry.set_srs_reference_group(SrsReferenceGroup::new());
202
203        assert!(geometry.srs_reference_group().is_none());
204        assert_eq!(geometry, AbstractGeometry::new());
205    }
206
207    #[test]
208    fn clearing_unset_attributes_does_not_allocate() {
209        let mut geometry = AbstractGeometry::new();
210        geometry.clear_srs_name();
211        geometry.set_srs_name_opt(None);
212        assert!(geometry.srs_reference_group().is_none());
213    }
214}