Skip to main content

open_geocode/
record.rs

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/// One normalized geocoding record, as the builder emits it and the Pack
9/// stores it.
10#[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    /// OSM node ids of the low and high numbered anchors.
32    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/// Every record layer. Place layers are the context layers used to describe
62/// where an address is.
63#[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    /// Layers that describe the surroundings of a point (admin areas, named
112    /// places and postcodes) rather than a specific address or road.
113    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    /// The point used to place the record on a map: the point itself for point
305    /// records, the representative point for lines.
306    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}