rtz_core/geo/admin/
osm.rs1use geo::Geometry;
4use serde_json::{Map, Value};
5use std::borrow::Cow;
6
7#[cfg(feature = "self-contained")]
8use bincode::{
9 de::{BorrowDecoder, Decoder},
10 error::DecodeError,
11 BorrowDecode, Decode, Encode,
12};
13
14use crate::{
15 base::types::Float,
16 geo::shared::{simplify_geometry, ConcreteVec, EncodableGeometry, EncodableString, HasGeometry, HasProperties, IdFeaturePair},
17};
18
19#[cfg(not(target_family = "wasm"))]
22use crate::geo::shared::{get_geojson_feature_from_string, CanGetGeoJsonFeaturesFromSource};
23
24use super::shared::IsAdmin;
25
26#[cfg(not(feature = "extrasimplified"))]
29const SIMPLIFICATION_EPSILON: Float = 0.001;
30#[cfg(feature = "extrasimplified")]
31const SIMPLIFICATION_EPSILON: Float = 0.1;
32
33#[cfg(not(target_family = "wasm"))]
37#[cfg_attr(coverage_nightly, coverage(off))]
38pub fn get_geojson_features_from_source() -> geojson::FeatureCollection {
39 use rayon::prelude::{IntoParallelIterator, ParallelIterator};
40
41 let admin_dirs = std::env::var("RTZ_OSM_ADMIN_DIRS")
42 .expect("RTZ_OSM_ADMIN_DIRS must be set (semicolon-separated admin GeoJSON directories) to regenerate OSM admin data");
43 let paths = admin_dirs.split(';').collect::<Vec<_>>();
44 let mut files = Vec::new();
45
46 for path in paths {
47 let mut path_files = std::fs::read_dir(path)
48 .unwrap()
49 .filter(|f| f.as_ref().unwrap().file_name().to_str().unwrap().ends_with(".geojson"))
50 .map(|f| f.unwrap())
51 .collect::<Vec<_>>();
52
53 files.append(&mut path_files);
54 }
55
56 let features = files
57 .into_par_iter()
58 .filter(|f| {
59 let md = f.metadata().unwrap();
60
61 md.len() != 0
62 })
63 .map(|f| {
64 let json = std::fs::read_to_string(f.path()).unwrap();
65 get_geojson_feature_from_string(&json)
66 })
67 .collect::<Vec<_>>();
68
69 geojson::FeatureCollection {
70 bbox: None,
71 features,
72 foreign_members: None,
73 }
74}
75
76pub static ADMIN_BINCODE_DESTINATION_NAME: &str = "osm_admins.bincode";
78pub static LOOKUP_BINCODE_DESTINATION_NAME: &str = "osm_admin_lookup.bincode";
80
81#[derive(Debug)]
87#[cfg_attr(feature = "self-contained", derive(Encode))]
88pub struct OsmAdmin {
89 pub id: usize,
93
94 pub relation_id: u64,
97
98 pub name: EncodableString,
100 pub level: usize,
102
103 pub geometry: EncodableGeometry,
105}
106
107#[cfg(feature = "self-contained")]
108impl<Context> Decode<Context> for OsmAdmin {
109 fn decode<D>(decoder: &mut D) -> Result<Self, DecodeError>
110 where
111 D: Decoder<Context = Context>,
112 {
113 let id = usize::decode(decoder)?;
114 let relation_id = u64::decode(decoder)?;
115 let name = EncodableString::decode(decoder)?;
116 let level = usize::decode(decoder)?;
117 let geometry = EncodableGeometry::decode(decoder)?;
118
119 Ok(OsmAdmin { id, relation_id, name, level, geometry })
120 }
121}
122
123#[cfg(feature = "self-contained")]
124impl<'de, Context> BorrowDecode<'de, Context> for OsmAdmin
125where
126 'de: 'static,
127{
128 fn borrow_decode<D>(decoder: &mut D) -> Result<Self, DecodeError>
129 where
130 D: BorrowDecoder<'de, Context = Context>,
131 {
132 let id = usize::decode(decoder)?;
133 let relation_id = u64::decode(decoder)?;
134 let name = EncodableString::borrow_decode(decoder)?;
135 let level = usize::decode(decoder)?;
136 let geometry = EncodableGeometry::borrow_decode(decoder)?;
137
138 Ok(OsmAdmin { id, relation_id, name, level, geometry })
139 }
140}
141
142impl PartialEq for OsmAdmin {
143 fn eq(&self, other: &Self) -> bool {
144 self.id == other.id
145 }
146}
147
148impl From<IdFeaturePair> for OsmAdmin {
149 fn from(value: IdFeaturePair) -> OsmAdmin {
150 let id = value.0;
151 let properties = value.1.properties.as_ref().unwrap();
152 let geometry = value.1.geometry.as_ref().unwrap();
153
154 let relation_id = properties.get("relation_id").and_then(|v| v.as_u64()).unwrap_or(0);
157 let name = EncodableString(Cow::Owned(properties.get("name").unwrap().as_str().unwrap().to_string()));
158 let level = properties.get("admin_level").unwrap().as_u64().unwrap() as usize;
159
160 let geometry: Geometry<Float> = geometry.value.clone().try_into().unwrap();
161 let geometry = EncodableGeometry(simplify_geometry(geometry, SIMPLIFICATION_EPSILON));
162
163 OsmAdmin { id, relation_id, name, level, geometry }
164 }
165}
166
167impl IsAdmin for OsmAdmin {
168 fn name(&self) -> &str {
169 self.name.as_ref()
170 }
171}
172
173impl HasGeometry for OsmAdmin {
174 fn id(&self) -> usize {
175 self.id
176 }
177
178 fn geometry(&self) -> &Geometry<Float> {
179 &self.geometry.0
180 }
181
182 fn reorder(items: ConcreteVec<Self>) -> ConcreteVec<Self> {
188 let mut items = items.into_iter().collect::<Vec<_>>();
189 items.sort_by_key(|admin| (admin.level, admin.relation_id));
190
191 for (index, admin) in items.iter_mut().enumerate() {
193 admin.id = index;
194 }
195
196 ConcreteVec::from(items)
197 }
198}
199
200impl HasProperties for OsmAdmin {
201 fn properties(&self) -> Map<String, Value> {
202 let mut properties = Map::new();
203
204 properties.insert("name".to_string(), Value::String(self.name.to_string()));
205 properties.insert("level".to_string(), Value::String(self.level.to_string()));
206
207 properties
208 }
209}
210
211#[cfg(not(target_family = "wasm"))]
212impl CanGetGeoJsonFeaturesFromSource for OsmAdmin {
213 fn get_geojson_features_from_source() -> geojson::FeatureCollection {
214 get_geojson_features_from_source()
215 }
216}