rtz_core/geo/admin/
osm.rs

1//! All of the geo-specific functions for OSM admin lookups.
2
3// This module is mostly used for cache preprocessing, which is expensive during coverage, so
4// it is not included in the coverage report.
5#![cfg(not(tarpaulin_include))]
6
7use geo::Geometry;
8use serde_json::{Map, Value};
9use std::borrow::Cow;
10
11#[cfg(feature = "self-contained")]
12use bincode::{
13    de::{BorrowDecoder, Decoder},
14    error::DecodeError,
15    BorrowDecode, Decode, Encode,
16};
17
18use crate::{
19    base::types::Float,
20    geo::shared::{get_geojson_feature_from_string, simplify_geometry, CanGetGeoJsonFeaturesFromSource, EncodableGeometry, EncodableString, HasGeometry, HasProperties, IdFeaturePair},
21};
22
23use super::shared::IsAdmin;
24
25// Constants.
26
27#[cfg(not(feature = "extrasimplified"))]
28const SIMPLIFICATION_EPSILON: Float = 0.001;
29#[cfg(feature = "extrasimplified")]
30const SIMPLIFICATION_EPSILON: Float = 0.1;
31
32// Helpers.
33
34/// Get the GeoJSON [`geojson::Feature`]s from the source.
35#[cfg(not(target_family = "wasm"))]
36pub fn get_geojson_features_from_source() -> geojson::FeatureCollection {
37    use rayon::prelude::{IntoParallelIterator, ParallelIterator};
38
39    let paths = ADDRESS.split(';').collect::<Vec<_>>();
40    let mut files = Vec::new();
41
42    for path in paths {
43        let mut path_files = std::fs::read_dir(path)
44            .unwrap()
45            .filter(|f| f.as_ref().unwrap().file_name().to_str().unwrap().ends_with(".geojson"))
46            .map(|f| f.unwrap())
47            .collect::<Vec<_>>();
48
49        files.append(&mut path_files);
50    }
51
52    let features = files
53        .into_par_iter()
54        .filter(|f| {
55            let md = f.metadata().unwrap();
56
57            md.len() != 0
58        })
59        .map(|f| {
60            let json = std::fs::read_to_string(f.path()).unwrap();
61            get_geojson_feature_from_string(&json)
62        })
63        .collect::<Vec<_>>();
64
65    geojson::FeatureCollection {
66        bbox: None,
67        features,
68        foreign_members: None,
69    }
70}
71
72/// The address of the GeoJSON file.
73///
74/// Hacking to local machine, for now.  Will create a repo at some point.
75pub static ADDRESS: &str = "D://LargeData//admin_data//admin2;D://LargeData//admin_data//admin3;D://LargeData//admin_data//admin4;D://LargeData//admin_data//admin5;D://LargeData//admin_data//admin6;D://LargeData//admin_data//admin7;D://LargeData//admin_data//admin8";
76/// The name of the timezone bincode file.
77pub static ADMIN_BINCODE_DESTINATION_NAME: &str = "osm_admins.bincode";
78/// The name of the cache bincode file.
79pub static LOOKUP_BINCODE_DESTINATION_NAME: &str = "osm_admin_lookup.bincode";
80
81// Types.
82
83/// A representation of the [OpenStreetMap](https://www.openstreetmap.org/)
84/// [geojson](https://github.com/evansiroky/timezone-boundary-builder)
85/// [`geojson::Feature`]s for administrative areas.
86#[derive(Debug)]
87#[cfg_attr(feature = "self-contained", derive(Encode))]
88pub struct OsmAdmin {
89    /// The index of the [`OsmAdmin`] in the global static cache.
90    ///
91    /// This is is not stable across builds or new data sets.  It is merely unique during a single build.
92    pub id: usize,
93
94    /// The `name` of the [`OsmAdmin`] (e.g., `Burkina Faso`).
95    pub name: EncodableString,
96    /// The `level` of the [`OsmAdmin`] (e.g., `3`).
97    pub level: usize,
98
99    /// The geometry of the [`OsmAdmin`].
100    pub geometry: EncodableGeometry,
101}
102
103#[cfg(feature = "self-contained")]
104impl Decode for OsmAdmin {
105    fn decode<D>(decoder: &mut D) -> Result<Self, DecodeError>
106    where
107        D: Decoder,
108    {
109        let id = usize::decode(decoder)?;
110        let name = EncodableString::decode(decoder)?;
111        let level = usize::decode(decoder)?;
112        let geometry = EncodableGeometry::decode(decoder)?;
113
114        Ok(OsmAdmin { id, name, level, geometry })
115    }
116}
117
118#[cfg(feature = "self-contained")]
119impl<'de> BorrowDecode<'de> for OsmAdmin
120where
121    'de: 'static,
122{
123    fn borrow_decode<D>(decoder: &mut D) -> Result<Self, DecodeError>
124    where
125        D: BorrowDecoder<'de>,
126    {
127        let id = usize::decode(decoder)?;
128        let name = EncodableString::borrow_decode(decoder)?;
129        let level = usize::decode(decoder)?;
130        let geometry = EncodableGeometry::borrow_decode(decoder)?;
131
132        Ok(OsmAdmin { id, name, level, geometry })
133    }
134}
135
136impl PartialEq for OsmAdmin {
137    fn eq(&self, other: &Self) -> bool {
138        self.id == other.id
139    }
140}
141
142impl From<IdFeaturePair> for OsmAdmin {
143    fn from(value: IdFeaturePair) -> OsmAdmin {
144        let id = value.0;
145        let properties = value.1.properties.as_ref().unwrap();
146        let geometry = value.1.geometry.as_ref().unwrap();
147
148        let name = EncodableString(Cow::Owned(properties.get("name").unwrap().as_str().unwrap().to_string()));
149        let level = properties.get("admin_level").unwrap().as_u64().unwrap() as usize;
150
151        let geometry: Geometry<Float> = geometry.value.clone().try_into().unwrap();
152        let geometry = EncodableGeometry(simplify_geometry(geometry, SIMPLIFICATION_EPSILON));
153
154        OsmAdmin { id, name, level, geometry }
155    }
156}
157
158impl IsAdmin for OsmAdmin {
159    fn name(&self) -> &str {
160        self.name.as_ref()
161    }
162}
163
164impl HasGeometry for OsmAdmin {
165    fn id(&self) -> usize {
166        self.id
167    }
168
169    fn geometry(&self) -> &Geometry<Float> {
170        &self.geometry.0
171    }
172}
173
174impl HasProperties for OsmAdmin {
175    fn properties(&self) -> Map<String, Value> {
176        let mut properties = Map::new();
177
178        properties.insert("name".to_string(), Value::String(self.name.to_string()));
179        properties.insert("level".to_string(), Value::String(self.level.to_string()));
180
181        properties
182    }
183}
184
185#[cfg(not(target_family = "wasm"))]
186impl CanGetGeoJsonFeaturesFromSource for OsmAdmin {
187    fn get_geojson_features_from_source() -> geojson::FeatureCollection {
188        get_geojson_features_from_source()
189    }
190}