rtzlib/geo/admin/
osm.rs

1//! The [OpenStreetMap](https://www.openstreetmap.org/) admin lookup module.
2
3use std::{collections::HashMap, sync::OnceLock};
4
5use rtz_core::geo::{
6    admin::osm::OsmAdmin,
7    shared::{ConcreteVec, EncodableIds, RoundLngLat},
8};
9
10use crate::{
11    geo::shared::{HasItemData, HasLookupData},
12    CanPerformGeoLookup,
13};
14
15#[cfg(feature = "self-contained")]
16use include_bytes_aligned::include_bytes_aligned;
17
18// Trait impls.
19
20impl HasItemData for OsmAdmin {
21    fn get_mem_items() -> &'static ConcreteVec<OsmAdmin> {
22        static TIMEZONES: OnceLock<ConcreteVec<OsmAdmin>> = OnceLock::new();
23
24        #[cfg(feature = "self-contained")]
25        {
26            TIMEZONES.get_or_init(|| crate::geo::shared::decode_binary_data(ADMIN_BINCODE))
27        }
28
29        #[cfg(not(feature = "self-contained"))]
30        {
31            use rtz_core::geo::{admin::osm::get_geojson_features_from_source, shared::get_items_from_features};
32
33            TIMEZONES.get_or_init(|| {
34                let features = get_geojson_features_from_source();
35
36                get_items_from_features(features)
37            })
38        }
39    }
40}
41
42impl HasLookupData for OsmAdmin {
43    type Lookup = EncodableIds;
44
45    fn get_mem_lookup() -> &'static HashMap<RoundLngLat, Self::Lookup> {
46        static CACHE: OnceLock<HashMap<RoundLngLat, EncodableIds>> = OnceLock::new();
47
48        #[cfg(feature = "self-contained")]
49        {
50            CACHE.get_or_init(|| crate::geo::shared::decode_binary_data(LOOKUP_BINCODE))
51        }
52
53        #[cfg(not(feature = "self-contained"))]
54        {
55            use rtz_core::geo::shared::get_lookup_from_geometries;
56
57            CACHE.get_or_init(|| {
58                let cache = get_lookup_from_geometries(OsmAdmin::get_mem_items());
59
60                cache
61            })
62        }
63    }
64}
65
66impl CanPerformGeoLookup for OsmAdmin {}
67
68// Statics.
69
70#[cfg(all(host_family_unix, feature = "self-contained"))]
71static ADMIN_BINCODE: &[u8] = include_bytes_aligned!(8, "../../../../assets/osm_admins.bincode");
72#[cfg(all(host_family_windows, feature = "self-contained"))]
73static ADMIN_BINCODE: &[u8] = include_bytes_aligned!(8, "..\\..\\..\\..\\assets\\osm_admins.bincode");
74
75#[cfg(all(host_family_unix, feature = "self-contained"))]
76static LOOKUP_BINCODE: &[u8] = include_bytes_aligned!(8, "../../../../assets/osm_admin_lookup.bincode");
77#[cfg(all(host_family_windows, feature = "self-contained"))]
78static LOOKUP_BINCODE: &[u8] = include_bytes_aligned!(8, "..\\..\\..\\..\\assets\\osm_admin_lookup.bincode");
79
80// Tests.
81
82#[cfg(test)]
83mod tests {
84    use crate::geo::shared::{CanPerformGeoLookup, HasItemData, MapIntoItems};
85
86    use super::*;
87    use pretty_assertions::assert_eq;
88    use rayon::prelude::{IntoParallelIterator, ParallelIterator};
89    use rtz_core::base::types::Float;
90
91    #[test]
92    fn can_get_timezones() {
93        let admins = OsmAdmin::get_mem_items();
94        assert_eq!(admins.len(), 318111);
95    }
96
97    #[test]
98    fn can_get_lookup() {
99        let cache = OsmAdmin::get_mem_lookup();
100        assert_eq!(cache.len(), 64_800);
101    }
102
103    #[test]
104    fn can_get_from_lookup() {
105        let lookup = OsmAdmin::get_lookup_suggestions(-121, 46).unwrap();
106        assert_eq!(lookup.len(), 20);
107    }
108
109    #[test]
110    fn can_perform_exact_lookup() {
111        assert_eq!(OsmAdmin::lookup_slow(-177.0, -15.0).len(), 0);
112        assert_eq!(OsmAdmin::lookup_slow(-121.0, 46.0)[0].name.as_ref(), "United States");
113
114        assert_eq!(OsmAdmin::lookup_slow(179.9968, -67.0959).len(), 0);
115    }
116
117    #[test]
118    fn can_access_lookup() {
119        let cache = OsmAdmin::get_mem_lookup();
120
121        let tzs = cache.get(&(-177, -15)).map_into_items().unwrap() as Vec<&OsmAdmin>;
122        assert_eq!(tzs.len(), 0);
123
124        let tzs = cache.get(&(-121, 46)).map_into_items().unwrap() as Vec<&OsmAdmin>;
125        assert_eq!(tzs.len(), 20);
126
127        let tz = cache.get(&(-121, 46)).map_into_items().unwrap()[0] as &OsmAdmin;
128        assert_eq!(tz.name.as_ref(), "United States");
129
130        let tzs = cache.get(&(-87, 38)).map_into_items().unwrap() as Vec<&OsmAdmin>;
131        assert_eq!(tzs.len(), 58);
132    }
133
134    #[test]
135    fn can_verify_lookup_assisted_accuracy() {
136        (0..100).into_par_iter().for_each(|_| {
137            let x = rand::random::<Float>() * 360.0 - 180.0;
138            let y = rand::random::<Float>() * 180.0 - 90.0;
139            let full = OsmAdmin::lookup_slow(x, y);
140            let lookup_assisted = OsmAdmin::lookup(x, y);
141
142            assert_eq!(
143                full.into_iter().map(|t| t.id).collect::<Vec<_>>(),
144                lookup_assisted.into_iter().map(|t| t.id).collect::<Vec<_>>(),
145                "({}, {})",
146                x,
147                y
148            );
149        });
150    }
151}