egml_core/resolver/
geometry_resolver.rs1use crate::model::base::{AsAbstractGml, Id};
2use crate::model::common::IterGeometries;
3use crate::model::geometry::refs::AbstractGeometryKindRef;
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Default)]
13pub struct GeometryResolver<'a> {
14 by_id: HashMap<Id, AbstractGeometryKindRef<'a>>,
15}
16
17impl<'a> GeometryResolver<'a> {
18 pub fn new() -> Self {
20 Self::default()
21 }
22
23 pub fn build<T: IterGeometries>(root: &'a T) -> Self {
41 let mut resolver = Self::new();
42 resolver.insert_root(root);
43 resolver
44 }
45
46 pub fn insert_root<T: IterGeometries>(&mut self, root: &'a T) {
74 self.extend(root.iter_geometries());
75 }
76
77 pub fn insert(&mut self, geometry: AbstractGeometryKindRef<'a>) -> bool {
85 match geometry.id() {
86 Some(id) => {
87 self.by_id.insert(id.clone(), geometry);
88 true
89 }
90 None => false,
91 }
92 }
93
94 pub fn extend(&mut self, geometries: impl IntoIterator<Item = AbstractGeometryKindRef<'a>>) {
96 for geometry in geometries {
97 self.insert(geometry);
98 }
99 }
100
101 pub fn resolve(&self, id: &Id) -> Option<AbstractGeometryKindRef<'a>> {
103 self.by_id.get(id).copied()
104 }
105
106 pub fn resolve_as<T>(&self, id: &Id) -> Option<T>
134 where
135 T: TryFrom<AbstractGeometryKindRef<'a>>,
136 {
137 self.resolve(id)
138 .and_then(|geometry| T::try_from(geometry).ok())
139 }
140
141 pub fn contains(&self, id: &Id) -> bool {
143 self.by_id.contains_key(id)
144 }
145
146 pub fn ids(&self) -> impl Iterator<Item = &Id> {
148 self.by_id.keys()
149 }
150
151 pub fn iter(&self) -> impl Iterator<Item = (&Id, &AbstractGeometryKindRef<'a>)> {
154 self.by_id.iter()
155 }
156
157 pub fn len(&self) -> usize {
158 self.by_id.len()
159 }
160
161 pub fn is_empty(&self) -> bool {
162 self.by_id.is_empty()
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 use crate::model::base::AsAbstractGmlMut;
170 use crate::model::geometry::DirectPosition;
171 use crate::model::geometry::primitives::{
172 AbstractRingKind, AbstractRingProperty, LinearRing, Point, Polygon,
173 };
174
175 fn ring_with_id(id: &str) -> LinearRing {
176 let mut ring = LinearRing::new([
177 DirectPosition::new(0.0, 0.0, 0.0).unwrap(),
178 DirectPosition::new(1.0, 0.0, 0.0).unwrap(),
179 DirectPosition::new(0.0, 1.0, 0.0).unwrap(),
180 ])
181 .unwrap();
182 ring.set_id(Id::try_from(id).expect("valid id"));
183 ring
184 }
185
186 #[test]
187 fn build_indexes_self_and_descendants() {
188 let mut polygon = Polygon::new(
189 Some(AbstractRingProperty::from_object(
190 AbstractRingKind::LinearRing(ring_with_id("ring-1")),
191 )),
192 [],
193 )
194 .unwrap();
195 polygon.set_id(Id::try_from("polygon-1").expect("valid id"));
196
197 let resolver = GeometryResolver::build(&polygon);
198
199 assert_eq!(resolver.len(), 2);
200 assert!(resolver.contains(&Id::try_from("polygon-1").expect("valid id")));
201 assert!(resolver.contains(&Id::try_from("ring-1").expect("valid id")));
202 }
203
204 #[test]
205 fn resolve_unknown_id_yields_none() {
206 let polygon = Polygon::new(None, []).unwrap();
207 let resolver = GeometryResolver::build(&polygon);
208
209 assert!(
210 resolver
211 .resolve(&Id::try_from("missing").expect("valid id"))
212 .is_none()
213 );
214 }
215
216 #[test]
217 fn resolve_as_downcasts_to_concrete_type() {
218 let mut polygon = Polygon::new(
219 Some(AbstractRingProperty::from_object(
220 AbstractRingKind::LinearRing(ring_with_id("ring-1")),
221 )),
222 [],
223 )
224 .unwrap();
225 polygon.set_id(Id::try_from("polygon-1").expect("valid id"));
226
227 let resolver = GeometryResolver::build(&polygon);
228
229 let ring: Option<&LinearRing> =
230 resolver.resolve_as(&Id::try_from("ring-1").expect("valid id"));
231 assert!(ring.is_some());
232
233 let wrong: Option<&Point> = resolver.resolve_as(&Id::try_from("ring-1").expect("valid id"));
235 assert!(wrong.is_none());
236 }
237
238 #[test]
239 fn insert_root_adds_a_second_independent_tree_to_the_same_resolver() {
240 let mut first_polygon = Polygon::new(
241 Some(AbstractRingProperty::from_object(
242 AbstractRingKind::LinearRing(ring_with_id("ring-1")),
243 )),
244 [],
245 )
246 .unwrap();
247 first_polygon.set_id(Id::try_from("polygon-1").expect("valid id"));
248
249 let mut second_polygon = Polygon::new(
250 Some(AbstractRingProperty::from_object(
251 AbstractRingKind::LinearRing(ring_with_id("ring-2")),
252 )),
253 [],
254 )
255 .unwrap();
256 second_polygon.set_id(Id::try_from("polygon-2").expect("valid id"));
257
258 let mut resolver = GeometryResolver::new();
259 resolver.insert_root(&first_polygon);
260 resolver.insert_root(&second_polygon);
261
262 assert_eq!(resolver.len(), 4);
264 assert!(resolver.contains(&Id::try_from("polygon-1").expect("valid id")));
265 assert!(resolver.contains(&Id::try_from("polygon-2").expect("valid id")));
266 assert!(resolver.contains(&Id::try_from("ring-1").expect("valid id")));
267 assert!(resolver.contains(&Id::try_from("ring-2").expect("valid id")));
268 }
269
270 #[test]
271 fn insert_skips_geometry_without_id() {
272 let point = Point::new(DirectPosition::new(1.0, 2.0, 3.0).unwrap());
273 let mut resolver = GeometryResolver::new();
274
275 let inserted = resolver.insert((&point).into());
276
277 assert!(!inserted);
278 assert!(resolver.is_empty());
279 }
280
281 #[test]
282 fn insert_duplicate_id_overwrites_last_wins() {
283 let mut first = Point::new(DirectPosition::new(1.0, 2.0, 3.0).unwrap());
284 first.set_id(Id::try_from("dup").expect("valid id"));
285 let mut second = Point::new(DirectPosition::new(4.0, 5.0, 6.0).unwrap());
286 second.set_id(Id::try_from("dup").expect("valid id"));
287
288 let mut resolver = GeometryResolver::new();
289 resolver.insert((&first).into());
290 resolver.insert((&second).into());
291
292 assert_eq!(resolver.len(), 1);
293 let resolved: &Point = resolver
294 .resolve_as(&Id::try_from("dup").expect("valid id"))
295 .unwrap();
296 assert_eq!(resolved.pos().x(), 4.0);
297 }
298
299 #[test]
300 fn ids_and_iter_expose_all_entries() {
301 let mut polygon = Polygon::new(
302 Some(AbstractRingProperty::from_object(
303 AbstractRingKind::LinearRing(ring_with_id("ring-1")),
304 )),
305 [],
306 )
307 .unwrap();
308 polygon.set_id(Id::try_from("polygon-1").expect("valid id"));
309
310 let resolver = GeometryResolver::build(&polygon);
311
312 let ids: Vec<&Id> = resolver.ids().collect();
313 assert_eq!(ids.len(), 2);
314 assert!(ids.contains(&&Id::try_from("polygon-1").expect("valid id")));
315 assert!(ids.contains(&&Id::try_from("ring-1").expect("valid id")));
316
317 assert_eq!(resolver.iter().count(), 2);
318 }
319}