1use 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
18impl 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#[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#[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!(admins.len() > 300_000, "admin item count implausibly low: {}", admins.len());
98 }
99
100 #[test]
101 fn can_get_lookup() {
102 let cache = OsmAdmin::get_mem_lookup();
103 assert_eq!(cache.len(), 64_800);
104 }
105
106 #[test]
107 fn can_get_from_lookup() {
108 let lookup = OsmAdmin::get_lookup_suggestions(-121, 46).unwrap();
109 assert!(lookup.len() >= 10, "too few lookup suggestions: {}", lookup.len());
112 }
113
114 #[test]
115 fn can_perform_exact_lookup() {
116 assert_eq!(OsmAdmin::lookup_slow(-177.0, -15.0).len(), 0);
117
118 let admins = OsmAdmin::lookup_slow(-121.0, 46.0);
122 let names = admins.iter().map(|a| a.name.as_ref()).collect::<Vec<&str>>();
123 assert!(names.contains(&"United States"), "expected United States in {names:?}");
124 assert!(names.contains(&"Washington"), "expected Washington in {names:?}");
125
126 assert_eq!(OsmAdmin::lookup_slow(179.9968, -67.0959).len(), 0);
127 }
128
129 #[test]
134 fn lookup_returns_admins_broadest_first() {
135 for (lng, lat) in [(-87.62, 41.88), (2.35, 48.86), (139.69, 35.68), (-74.006, 40.7128)] {
137 for (path, admins) in [("lookup", OsmAdmin::lookup(lng, lat)), ("lookup_slow", OsmAdmin::lookup_slow(lng, lat))] {
138 let levels = admins.iter().map(|a| a.level).collect::<Vec<usize>>();
139
140 assert!(levels.len() > 1, "({lng}, {lat}) via {path} should hit nested admins, got {levels:?}");
141 assert!(levels.is_sorted(), "({lng}, {lat}) via {path} returned levels out of order: {levels:?}");
142 }
143 }
144 }
145
146 #[test]
147 fn can_access_lookup() {
148 let cache = OsmAdmin::get_mem_lookup();
149
150 let tzs = cache.get(&(-177, -15)).map_into_items().unwrap() as Vec<&OsmAdmin>;
151 assert_eq!(tzs.len(), 0);
152
153 let tzs = cache.get(&(-121, 46)).map_into_items().unwrap() as Vec<&OsmAdmin>;
156 assert!(tzs.len() >= 10, "too few items in cell (-121, 46): {}", tzs.len());
157 let names = tzs.iter().map(|t| t.name.as_ref()).collect::<Vec<&str>>();
158 assert!(names.contains(&"United States"), "expected United States in {names:?}");
159
160 let tzs = cache.get(&(-87, 38)).map_into_items().unwrap() as Vec<&OsmAdmin>;
161 assert!(tzs.len() >= 10, "too few items in cell (-87, 38): {}", tzs.len());
162 }
163
164 #[test]
165 fn can_verify_lookup_assisted_accuracy() {
166 (0..100).into_par_iter().for_each(|_| {
167 let x = rand::random::<Float>() * 360.0 - 180.0;
168 let y = rand::random::<Float>() * 180.0 - 90.0;
169 let full = OsmAdmin::lookup_slow(x, y);
170 let lookup_assisted = OsmAdmin::lookup(x, y);
171
172 assert_eq!(
173 full.into_iter().map(|t| t.id).collect::<Vec<_>>(),
174 lookup_assisted.into_iter().map(|t| t.id).collect::<Vec<_>>(),
175 "({}, {})",
176 x,
177 y
178 );
179 });
180 }
181}