Skip to main content

rtzlib/geo/
shared.rs

1//! Shared functionality for geo operations in the `rtz` crate.
2
3// Traits.
4
5use geo::{Contains, Coord};
6use rtz_core::{
7    base::types::Float,
8    geo::shared::{ConcreteVec, HasGeometry, HasProperties, Id, RoundDegree, RoundLngLat, ToGeoJson},
9};
10use std::collections::HashMap;
11
12/// Trait that abstracts away getting the in-memory items.
13pub trait HasItemData
14where
15    Self: Sized,
16{
17    /// Gets the items from the in-memory cache for the given type.
18    fn get_mem_items() -> &'static ConcreteVec<Self>;
19}
20
21/// Trait that abstracts away getting the in-memory timezones / cache.
22pub trait HasLookupData: HasItemData
23where
24    Self: Sized,
25{
26    /// The type to which the lookup hash table resolves.
27    type Lookup: AsRef<[Id]>;
28
29    /// Gets the lookup hash table from the in-memory cache for the given type.
30    fn get_mem_lookup() -> &'static HashMap<RoundLngLat, Self::Lookup>;
31}
32
33/// Trait that allows converting a [`u16`] into the items to which the ids refer (from the global list).
34pub(crate) trait MapIntoItems<T> {
35    fn map_into_items(self) -> Option<Vec<&'static T>>;
36}
37
38impl<A, T> MapIntoItems<T> for Option<A>
39where
40    A: AsRef<[Id]>,
41    T: HasItemData,
42{
43    fn map_into_items(self) -> Option<Vec<&'static T>> {
44        let value = self?;
45
46        let source = value.as_ref();
47        let items = T::get_mem_items();
48
49        let mut result = Vec::with_capacity(source.len());
50        for id in source {
51            let item = &items[*id as usize];
52            result.push(item);
53        }
54
55        Some(result)
56    }
57}
58
59/// Perform a decode of binary data.
60#[cfg(feature = "self-contained")]
61pub fn decode_binary_data<T>(data: &'static [u8]) -> T
62where
63    T: bincode::Decode<()> + bincode::BorrowDecode<'static, ()>,
64{
65    // INVARIANT: this is the single selector of borrow-vs-owned decode. With `owned-decode` off we
66    // borrow directly over the embedded bytes, which is what makes `EncodableGeometry`'s leak-on-drop
67    // correct (see its `Drop` in `rtz-core`). Do not introduce a second, differently-gated decode of
68    // geometry data, or the two can desync and free static memory (UB).
69    #[cfg(not(feature = "owned-decode"))]
70    let (value, _len): (T, usize) = bincode::borrow_decode_from_slice(data, rtz_core::geo::shared::get_global_bincode_config())
71        .expect("Could not decode binary data: try rebuilding with `force-rebuild` due to a likely precision difference between the generated assets and the current build.");
72    #[cfg(feature = "owned-decode")]
73    let (value, _len): (T, usize) = bincode::decode_from_slice(data, rtz_core::geo::shared::get_global_bincode_config())
74        .expect("Could not decode binary data: try rebuilding with `force-rebuild` due to a likely precision difference between the generated assets and the current build.");
75
76    value
77}
78
79/// Trait that abstracts away the primary end-user functionality of geo lookups.
80pub trait CanPerformGeoLookup: HasLookupData + HasGeometry + HasProperties
81where
82    Self: 'static,
83{
84    /// Get the cache-driven item for a given longitude (x) and latitude (y).
85    ///
86    /// Some data sources allow for multiple results, so this is a vector.
87    fn lookup(xf: Float, yf: Float) -> Vec<&'static Self> {
88        let x = xf.floor() as RoundDegree;
89        let y = yf.floor() as RoundDegree;
90
91        let Some(suggestions) = Self::get_lookup_suggestions(x, y) else {
92            return Vec::new();
93        };
94
95        suggestions.into_iter().filter(|&i| i.geometry().contains(&Coord { x: xf, y: yf })).collect()
96    }
97
98    /// Get the exact item for a given longitude (x) and latitude (y).
99    #[allow(dead_code)]
100    fn lookup_slow(xf: Float, yf: Float) -> Vec<&'static Self> {
101        Self::get_mem_items().into_iter().filter(|&i| i.geometry().contains(&Coord { x: xf, y: yf })).collect()
102    }
103
104    /// Gets the geojson representation of the memory cache.
105    fn memory_data_to_geojson() -> String {
106        let geojson = Self::get_mem_items().to_geojson();
107        geojson.to_string()
108    }
109
110    /// Get value from the static memory cache.
111    fn get_lookup_suggestions(x: RoundDegree, y: RoundDegree) -> Option<Vec<&'static Self>> {
112        let cache = Self::get_mem_lookup();
113        cache.get(&(x, y)).map_into_items()
114    }
115}