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::{get_geojson_feature_from_string, simplify_geometry, CanGetGeoJsonFeaturesFromSource, EncodableGeometry, EncodableString, HasGeometry, HasProperties, IdFeaturePair},
17};
18
19use super::shared::IsAdmin;
20
21#[cfg(not(feature = "extrasimplified"))]
24const SIMPLIFICATION_EPSILON: Float = 0.001;
25#[cfg(feature = "extrasimplified")]
26const SIMPLIFICATION_EPSILON: Float = 0.1;
27
28#[cfg(not(target_family = "wasm"))]
32#[cfg_attr(coverage_nightly, coverage(off))]
33pub fn get_geojson_features_from_source() -> geojson::FeatureCollection {
34 use rayon::prelude::{IntoParallelIterator, ParallelIterator};
35
36 let admin_dirs = std::env::var("RTZ_OSM_ADMIN_DIRS")
37 .expect("RTZ_OSM_ADMIN_DIRS must be set (semicolon-separated admin GeoJSON directories) to regenerate OSM admin data");
38 let paths = admin_dirs.split(';').collect::<Vec<_>>();
39 let mut files = Vec::new();
40
41 for path in paths {
42 let mut path_files = std::fs::read_dir(path)
43 .unwrap()
44 .filter(|f| f.as_ref().unwrap().file_name().to_str().unwrap().ends_with(".geojson"))
45 .map(|f| f.unwrap())
46 .collect::<Vec<_>>();
47
48 files.append(&mut path_files);
49 }
50
51 let features = files
52 .into_par_iter()
53 .filter(|f| {
54 let md = f.metadata().unwrap();
55
56 md.len() != 0
57 })
58 .map(|f| {
59 let json = std::fs::read_to_string(f.path()).unwrap();
60 get_geojson_feature_from_string(&json)
61 })
62 .collect::<Vec<_>>();
63
64 geojson::FeatureCollection {
65 bbox: None,
66 features,
67 foreign_members: None,
68 }
69}
70
71pub static ADMIN_BINCODE_DESTINATION_NAME: &str = "osm_admins.bincode";
73pub static LOOKUP_BINCODE_DESTINATION_NAME: &str = "osm_admin_lookup.bincode";
75
76#[derive(Debug)]
82#[cfg_attr(feature = "self-contained", derive(Encode))]
83pub struct OsmAdmin {
84 pub id: usize,
88
89 pub relation_id: u64,
92
93 pub name: EncodableString,
95 pub level: usize,
97
98 pub geometry: EncodableGeometry,
100}
101
102#[cfg(feature = "self-contained")]
103impl<Context> Decode<Context> for OsmAdmin {
104 fn decode<D>(decoder: &mut D) -> Result<Self, DecodeError>
105 where
106 D: Decoder<Context = Context>,
107 {
108 let id = usize::decode(decoder)?;
109 let relation_id = u64::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, relation_id, name, level, geometry })
115 }
116}
117
118#[cfg(feature = "self-contained")]
119impl<'de, Context> BorrowDecode<'de, Context> for OsmAdmin
120where
121 'de: 'static,
122{
123 fn borrow_decode<D>(decoder: &mut D) -> Result<Self, DecodeError>
124 where
125 D: BorrowDecoder<'de, Context = Context>,
126 {
127 let id = usize::decode(decoder)?;
128 let relation_id = u64::decode(decoder)?;
129 let name = EncodableString::borrow_decode(decoder)?;
130 let level = usize::decode(decoder)?;
131 let geometry = EncodableGeometry::borrow_decode(decoder)?;
132
133 Ok(OsmAdmin { id, relation_id, name, level, geometry })
134 }
135}
136
137impl PartialEq for OsmAdmin {
138 fn eq(&self, other: &Self) -> bool {
139 self.id == other.id
140 }
141}
142
143impl From<IdFeaturePair> for OsmAdmin {
144 fn from(value: IdFeaturePair) -> OsmAdmin {
145 let id = value.0;
146 let properties = value.1.properties.as_ref().unwrap();
147 let geometry = value.1.geometry.as_ref().unwrap();
148
149 let relation_id = properties.get("relation_id").and_then(|v| v.as_u64()).unwrap_or(0);
152 let name = EncodableString(Cow::Owned(properties.get("name").unwrap().as_str().unwrap().to_string()));
153 let level = properties.get("admin_level").unwrap().as_u64().unwrap() as usize;
154
155 let geometry: Geometry<Float> = geometry.value.clone().try_into().unwrap();
156 let geometry = EncodableGeometry(simplify_geometry(geometry, SIMPLIFICATION_EPSILON));
157
158 OsmAdmin { id, relation_id, name, level, geometry }
159 }
160}
161
162impl IsAdmin for OsmAdmin {
163 fn name(&self) -> &str {
164 self.name.as_ref()
165 }
166}
167
168impl HasGeometry for OsmAdmin {
169 fn id(&self) -> usize {
170 self.id
171 }
172
173 fn geometry(&self) -> &Geometry<Float> {
174 &self.geometry.0
175 }
176}
177
178impl HasProperties for OsmAdmin {
179 fn properties(&self) -> Map<String, Value> {
180 let mut properties = Map::new();
181
182 properties.insert("name".to_string(), Value::String(self.name.to_string()));
183 properties.insert("level".to_string(), Value::String(self.level.to_string()));
184
185 properties
186 }
187}
188
189#[cfg(not(target_family = "wasm"))]
190impl CanGetGeoJsonFeaturesFromSource for OsmAdmin {
191 fn get_geojson_features_from_source() -> geojson::FeatureCollection {
192 get_geojson_features_from_source()
193 }
194}