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 item to which the id refers (from the global list).
34// pub(crate) trait MapIntoItem<T> {
35//     fn map_into_item(self) -> Option<&'static T>;
36// }
37
38// impl<T> MapIntoItem<T> for Option<&u16>
39// where
40//     T: HasItemData,
41// {
42//     fn map_into_item(self) -> Option<&'static T> {
43//         let value = self?;
44
45//         let items = T::get_mem_items();
46
47//         items.get(*value as usize)
48//     }
49// }
50
51/// Trait that allows converting a [`u16`] into the items to which the ids refer (from the global list).
52pub(crate) trait MapIntoItems<T> {
53    fn map_into_items(self) -> Option<Vec<&'static T>>;
54}
55
56impl<A, T> MapIntoItems<T> for Option<A>
57where
58    A: AsRef<[Id]>,
59    T: HasItemData,
60{
61    fn map_into_items(self) -> Option<Vec<&'static T>> {
62        let value = self?;
63
64        let source = value.as_ref();
65        let items = T::get_mem_items();
66
67        let mut result = Vec::with_capacity(source.len());
68        for id in source {
69            let item = &items[*id as usize];
70            result.push(item);
71        }
72
73        Some(result)
74    }
75}
76
77/// Perform a decode of binary data.
78#[cfg(feature = "self-contained")]
79pub fn decode_binary_data<T>(data: &'static [u8]) -> T
80where
81    T: bincode::Decode + bincode::BorrowDecode<'static>,
82{
83    #[cfg(not(feature = "owned-decode"))]
84    let (value, _len): (T, usize) = bincode::borrow_decode_from_slice(data, rtz_core::geo::shared::get_global_bincode_config())
85        .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.");
86    #[cfg(feature = "owned-decode")]
87    let (value, _len): (T, usize) = bincode::decode_from_slice(data, rtz_core::geo::shared::get_global_bincode_config())
88        .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.");
89
90    value
91}
92
93/// Trait that abstracts away the primary end-user functionality of geo lookups.
94pub trait CanPerformGeoLookup: HasLookupData + HasGeometry + HasProperties
95where
96    Self: 'static,
97{
98    /// Get the cache-driven item for a given longitude (x) and latitude (y).
99    ///
100    /// Some data sources allow for multiple results, so this is a vector.
101    fn lookup(xf: Float, yf: Float) -> Vec<&'static Self> {
102        let x = xf.floor() as RoundDegree;
103        let y = yf.floor() as RoundDegree;
104
105        let Some(suggestions) = Self::get_lookup_suggestions(x, y) else {
106            return Vec::new();
107        };
108
109        suggestions.into_iter().filter(|&i| i.geometry().contains(&Coord { x: xf, y: yf })).collect()
110    }
111
112    /// Get the exact item for a given longitude (x) and latitude (y).
113    #[allow(dead_code)]
114    fn lookup_slow(xf: Float, yf: Float) -> Vec<&'static Self> {
115        Self::get_mem_items().into_iter().filter(|&i| i.geometry().contains(&Coord { x: xf, y: yf })).collect()
116    }
117
118    /// Gets the geojson representation of the memory cache.
119    fn memory_data_to_geojson() -> String {
120        let geojson = Self::get_mem_items().to_geojson();
121        geojson.to_json_value().to_string()
122    }
123
124    /// Get value from the static memory cache.
125    fn get_lookup_suggestions(x: RoundDegree, y: RoundDegree) -> Option<Vec<&'static Self>> {
126        let cache = Self::get_mem_lookup();
127        cache.get(&(x, y)).map_into_items()
128    }
129}