1use std::collections::BTreeMap;
2
3use geojson::{Geometry, GeometryValue};
4use serde::{Deserialize, Serialize, Serializer, ser::SerializeStruct};
5
6use crate::{labels, util::geo::point_lon_lat};
7
8#[derive(Debug, Clone, PartialEq)]
11pub enum Record {
12 Address(AddressRecord),
13 Interpolation(InterpolationRecord),
14 Street(StreetRecord),
15 Postcode(PostcodeRecord),
16 Place(PlaceLayer, PlaceRecord),
17}
18
19#[derive(Debug, Clone, PartialEq)]
20pub struct AddressRecord {
21 pub address: AddressComponents,
22 pub geometry: Geometry,
23 pub location_precision: LocationPrecision,
24 pub source: SourceProvenance,
25}
26
27#[derive(Debug, Clone, PartialEq)]
28pub struct InterpolationRecord {
29 pub address: InterpolationAddressComponents,
30 pub interpolation: InterpolationRange,
31 pub anchor_node_ids: [i64; 2],
33 pub geometry: Geometry,
34 pub representative_point: [f64; 2],
35 pub source: SourceProvenance,
36}
37
38#[derive(Debug, Clone, PartialEq)]
39pub struct StreetRecord {
40 pub name: String,
41 pub geometry: Geometry,
42 pub representative_point: [f64; 2],
43 pub source: SourceProvenance,
44}
45
46#[derive(Debug, Clone, PartialEq)]
47pub struct PostcodeRecord {
48 pub postcode: String,
49 pub geometry: Geometry,
50 pub source: DerivedSourceProvenance,
51}
52
53#[derive(Debug, Clone, PartialEq)]
54pub struct PlaceRecord {
55 pub name: String,
56 pub place_type: String,
57 pub geometry: Geometry,
58 pub source: SourceProvenance,
59}
60
61#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
64#[serde(rename_all = "snake_case")]
65pub enum Layer {
66 Address,
67 Country,
68 District,
69 Interpolation,
70 Locality,
71 Neighbourhood,
72 Place,
73 Postcode,
74 Region,
75 Street,
76}
77
78impl Layer {
79 pub const ALL: [Layer; 10] = [
80 Layer::Address,
81 Layer::Country,
82 Layer::District,
83 Layer::Interpolation,
84 Layer::Locality,
85 Layer::Neighbourhood,
86 Layer::Place,
87 Layer::Postcode,
88 Layer::Region,
89 Layer::Street,
90 ];
91
92 pub const fn as_str(self) -> &'static str {
93 match self {
94 Layer::Address => "address",
95 Layer::Country => "country",
96 Layer::District => "district",
97 Layer::Interpolation => "interpolation",
98 Layer::Locality => "locality",
99 Layer::Neighbourhood => "neighbourhood",
100 Layer::Place => "place",
101 Layer::Postcode => "postcode",
102 Layer::Region => "region",
103 Layer::Street => "street",
104 }
105 }
106
107 pub fn parse(value: &str) -> Option<Self> {
108 Self::ALL.into_iter().find(|layer| layer.as_str() == value)
109 }
110
111 pub const fn is_context(self) -> bool {
114 matches!(
115 self,
116 Layer::Country
117 | Layer::District
118 | Layer::Locality
119 | Layer::Neighbourhood
120 | Layer::Place
121 | Layer::Postcode
122 | Layer::Region
123 )
124 }
125
126 pub const fn place_layer(self) -> Option<PlaceLayer> {
127 match self {
128 Layer::Country => Some(PlaceLayer::Country),
129 Layer::Region => Some(PlaceLayer::Region),
130 Layer::District => Some(PlaceLayer::District),
131 Layer::Place => Some(PlaceLayer::Place),
132 Layer::Locality => Some(PlaceLayer::Locality),
133 Layer::Neighbourhood => Some(PlaceLayer::Neighbourhood),
134 _ => None,
135 }
136 }
137}
138
139#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
140pub enum PlaceLayer {
141 Country,
142 Region,
143 District,
144 Place,
145 Locality,
146 Neighbourhood,
147}
148
149impl PlaceLayer {
150 pub const fn as_str(self) -> &'static str {
151 Layer::from_place(self).as_str()
152 }
153}
154
155impl Layer {
156 pub const fn from_place(layer: PlaceLayer) -> Self {
157 match layer {
158 PlaceLayer::Country => Layer::Country,
159 PlaceLayer::Region => Layer::Region,
160 PlaceLayer::District => Layer::District,
161 PlaceLayer::Place => Layer::Place,
162 PlaceLayer::Locality => Layer::Locality,
163 PlaceLayer::Neighbourhood => Layer::Neighbourhood,
164 }
165 }
166}
167
168impl From<PlaceLayer> for Layer {
169 fn from(layer: PlaceLayer) -> Self {
170 Layer::from_place(layer)
171 }
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
175pub struct AddressComponents {
176 pub number: String,
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub street: Option<String>,
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub place: Option<String>,
181 #[serde(skip_serializing_if = "Option::is_none")]
182 pub unit: Option<String>,
183 #[serde(skip_serializing_if = "Option::is_none")]
184 pub locality: Option<String>,
185 #[serde(skip_serializing_if = "Option::is_none")]
186 pub region: Option<String>,
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub postcode: Option<String>,
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub country: Option<String>,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
194pub struct InterpolationAddressComponents {
195 #[serde(skip_serializing_if = "Option::is_none")]
196 pub street: Option<String>,
197 #[serde(skip_serializing_if = "Option::is_none")]
198 pub place: Option<String>,
199 #[serde(skip_serializing_if = "Option::is_none")]
200 pub locality: Option<String>,
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub region: Option<String>,
203 #[serde(skip_serializing_if = "Option::is_none")]
204 pub postcode: Option<String>,
205 #[serde(skip_serializing_if = "Option::is_none")]
206 pub country: Option<String>,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
210pub struct InterpolationRange {
211 #[serde(rename = "type")]
212 pub kind: String,
213 pub start: u32,
214 pub end: u32,
215 pub step: u32,
216}
217
218#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
219#[serde(rename_all = "snake_case")]
220pub enum LocationPrecision {
221 Point,
222 Centroid,
223}
224
225pub fn point_geometry(lon: f64, lat: f64) -> Geometry {
226 Geometry::new(GeometryValue::Point {
227 coordinates: vec![lon, lat].into(),
228 })
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
232pub struct SourceProvenance {
233 pub dataset: String,
234 pub object_type: OsmObjectType,
235 pub object_id: i64,
236 #[serde(skip_serializing_if = "Option::is_none")]
237 pub tags: Option<BTreeMap<String, String>>,
238}
239
240#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
241pub struct DerivedSourceProvenance {
242 pub dataset: String,
243 pub derived_from: String,
244 pub record_count: u64,
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
248pub struct RejectedRecord {
249 pub reason: String,
250 #[serde(skip_serializing_if = "Option::is_none")]
251 pub layer_hint: Option<String>,
252 pub source: SourceProvenance,
253}
254
255#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
256#[serde(rename_all = "snake_case")]
257pub enum OsmObjectType {
258 Node,
259 Way,
260 Relation,
261}
262
263impl Record {
264 pub const fn layer(&self) -> Layer {
265 match self {
266 Record::Address(_) => Layer::Address,
267 Record::Interpolation(_) => Layer::Interpolation,
268 Record::Street(_) => Layer::Street,
269 Record::Postcode(_) => Layer::Postcode,
270 Record::Place(layer, _) => Layer::from_place(*layer),
271 }
272 }
273
274 pub fn id(&self) -> String {
275 match self {
276 Record::Address(record) => record.id(),
277 Record::Interpolation(record) => record.id(),
278 Record::Street(record) => record.id(),
279 Record::Postcode(record) => record.id(),
280 Record::Place(_, record) => record.id(),
281 }
282 }
283
284 pub fn label(&self) -> String {
285 match self {
286 Record::Address(record) => record.label(),
287 Record::Interpolation(record) => record.label(),
288 Record::Street(record) => record.label(),
289 Record::Postcode(record) => record.label(),
290 Record::Place(_, record) => record.label(),
291 }
292 }
293
294 pub fn geometry(&self) -> &Geometry {
295 match self {
296 Record::Address(record) => &record.geometry,
297 Record::Interpolation(record) => &record.geometry,
298 Record::Street(record) => &record.geometry,
299 Record::Postcode(record) => &record.geometry,
300 Record::Place(_, record) => &record.geometry,
301 }
302 }
303
304 pub fn display_point(&self) -> Option<[f64; 2]> {
307 match self {
308 Record::Address(record) => point_lon_lat(&record.geometry),
309 Record::Interpolation(record) => Some(record.representative_point),
310 Record::Street(record) => Some(record.representative_point),
311 Record::Postcode(record) => point_lon_lat(&record.geometry),
312 Record::Place(_, record) => point_lon_lat(&record.geometry),
313 }
314 }
315}
316
317impl From<AddressRecord> for Record {
318 fn from(record: AddressRecord) -> Self {
319 Record::Address(record)
320 }
321}
322
323impl From<InterpolationRecord> for Record {
324 fn from(record: InterpolationRecord) -> Self {
325 Record::Interpolation(record)
326 }
327}
328
329impl From<StreetRecord> for Record {
330 fn from(record: StreetRecord) -> Self {
331 Record::Street(record)
332 }
333}
334
335impl From<PostcodeRecord> for Record {
336 fn from(record: PostcodeRecord) -> Self {
337 Record::Postcode(record)
338 }
339}
340
341impl Serialize for Record {
342 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
343 match self {
344 Record::Address(record) => record.serialize(serializer),
345 Record::Interpolation(record) => record.serialize(serializer),
346 Record::Street(record) => record.serialize(serializer),
347 Record::Postcode(record) => record.serialize(serializer),
348 Record::Place(_, record) => record.serialize(serializer),
349 }
350 }
351}
352
353impl AddressRecord {
354 pub const fn location_precision(&self) -> LocationPrecision {
355 self.location_precision
356 }
357
358 pub fn id(&self) -> String {
359 labels::osm_record_id(self.source.object_type, self.source.object_id)
360 }
361
362 pub fn name(&self) -> String {
363 labels::address_name(&self.address)
364 }
365
366 pub fn label(&self) -> String {
367 labels::address_label(&self.address)
368 }
369}
370
371impl Serialize for AddressRecord {
372 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
373 let mut state = serializer.serialize_struct("AddressRecord", 7)?;
374 state.serialize_field("id", &self.id())?;
375 state.serialize_field("label", &self.label())?;
376 state.serialize_field("name", &self.name())?;
377 state.serialize_field("address", &self.address)?;
378 state.serialize_field("geometry", &self.geometry)?;
379 state.serialize_field("location_precision", &self.location_precision)?;
380 state.serialize_field("source", &self.source)?;
381 state.end()
382 }
383}
384
385impl InterpolationRecord {
386 pub fn id(&self) -> String {
387 labels::interpolation_record_id(
388 self.source.object_id,
389 self.anchor_node_ids[0],
390 self.anchor_node_ids[1],
391 )
392 }
393
394 pub fn name(&self) -> String {
395 labels::interpolation_name(&self.address)
396 }
397
398 pub fn label(&self) -> String {
399 labels::interpolation_label(&self.name(), &self.interpolation, &self.address)
400 }
401}
402
403impl Serialize for InterpolationRecord {
404 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
405 let anchor_ids = self
406 .anchor_node_ids
407 .map(|node_id| labels::osm_record_id(OsmObjectType::Node, node_id));
408 let mut state = serializer.serialize_struct("InterpolationRecord", 9)?;
409 state.serialize_field("id", &self.id())?;
410 state.serialize_field("label", &self.label())?;
411 state.serialize_field("name", &self.name())?;
412 state.serialize_field("address", &self.address)?;
413 state.serialize_field("interpolation", &self.interpolation)?;
414 state.serialize_field("anchor_ids", &anchor_ids)?;
415 state.serialize_field("geometry", &self.geometry)?;
416 state.serialize_field("representative_point", &self.representative_point)?;
417 state.serialize_field("source", &self.source)?;
418 state.end()
419 }
420}
421
422impl StreetRecord {
423 pub fn id(&self) -> String {
424 labels::osm_record_id(self.source.object_type, self.source.object_id)
425 }
426
427 pub fn label(&self) -> String {
428 self.name.clone()
429 }
430}
431
432impl Serialize for StreetRecord {
433 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
434 let mut state = serializer.serialize_struct("StreetRecord", 6)?;
435 state.serialize_field("id", &self.id())?;
436 state.serialize_field("label", &self.label())?;
437 state.serialize_field("name", &self.name)?;
438 state.serialize_field("geometry", &self.geometry)?;
439 state.serialize_field("representative_point", &self.representative_point)?;
440 state.serialize_field("source", &self.source)?;
441 state.end()
442 }
443}
444
445impl PostcodeRecord {
446 pub fn id(&self) -> String {
447 labels::derived_postcode_id(&self.postcode)
448 }
449
450 pub fn name(&self) -> String {
451 self.postcode.clone()
452 }
453
454 pub fn label(&self) -> String {
455 self.postcode.clone()
456 }
457}
458
459impl Serialize for PostcodeRecord {
460 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
461 let mut state = serializer.serialize_struct("PostcodeRecord", 6)?;
462 state.serialize_field("id", &self.id())?;
463 state.serialize_field("label", &self.label())?;
464 state.serialize_field("name", &self.name())?;
465 state.serialize_field("postcode", &self.postcode)?;
466 state.serialize_field("geometry", &self.geometry)?;
467 state.serialize_field("source", &self.source)?;
468 state.end()
469 }
470}
471
472impl PlaceRecord {
473 pub fn id(&self) -> String {
474 labels::place_record_id(
475 self.source.object_type,
476 self.source.object_id,
477 &self.place_type,
478 )
479 }
480
481 pub fn label(&self) -> String {
482 self.name.clone()
483 }
484}
485
486impl Serialize for PlaceRecord {
487 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
488 let mut state = serializer.serialize_struct("PlaceRecord", 6)?;
489 state.serialize_field("id", &self.id())?;
490 state.serialize_field("label", &self.label())?;
491 state.serialize_field("name", &self.name)?;
492 state.serialize_field("place_type", &self.place_type)?;
493 state.serialize_field("geometry", &self.geometry)?;
494 state.serialize_field("source", &self.source)?;
495 state.end()
496 }
497}
498
499impl SourceProvenance {
500 pub fn osm(object_type: OsmObjectType, object_id: i64) -> Self {
501 Self {
502 dataset: "osm".to_string(),
503 object_type,
504 object_id,
505 tags: None,
506 }
507 }
508
509 pub fn osm_with_tags(
510 object_type: OsmObjectType,
511 object_id: i64,
512 tags: BTreeMap<String, String>,
513 ) -> Self {
514 Self {
515 dataset: "osm".to_string(),
516 object_type,
517 object_id,
518 tags: Some(tags),
519 }
520 }
521}
522
523pub const DERIVED_FROM_ADDRESS_RECORDS: &str = "accepted_address_records";
524
525impl DerivedSourceProvenance {
526 pub fn osm_address_records(record_count: u64) -> Self {
527 Self {
528 dataset: "osm".to_string(),
529 derived_from: DERIVED_FROM_ADDRESS_RECORDS.to_string(),
530 record_count,
531 }
532 }
533}