Skip to main content

pbf_craft/models/
mod.rs

1//! Element models for OpenStreetMap data.
2//!
3//! The three OSM element types are [`crate::models::Node`], [`crate::models::Way`] and [`crate::models::Relation`], which all share the
4//! common metadata fields of [`crate::models::ElementBase`] (id, version, timestamp, user, changeset id,
5//! visible flag and tags) and are carried polymorphically by the [`crate::models::Element`] enum.
6//!
7//! # Units
8//!
9//! Coordinates are stored as **integer nanodegrees** — the raw unit used by the PBF format
10//! (1e9 nanodegrees = 1 degree). This avoids floating-point precision loss on round-trips.
11//! Divide by `1e9` to obtain degrees. [`crate::models::Bound`] fields use the same unit.
12//!
13//! # The `visible` flag and metadata defaults
14//!
15//! Per the PBF spec the `visible` flag is assumed `true` when absent. All element types
16//! therefore default `visible` to `true`, and `timestamp`/`user` are `Option`s that are
17//! `None` when the source data carries no such metadata. `version`/`changeset_id` default to
18//! `-1` (the convention used by osmosis for "no version"/"no changeset").
19use std::str::FromStr;
20
21use chrono::{DateTime, Utc};
22use serde::{Deserialize, Serialize};
23
24/// A bounding box from the PBF file header.
25///
26/// Coordinates are in integer **nanodegrees** (1e9 per degree). `origin` is the data source
27/// string recorded in the header.
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct Bound {
30    pub left: i64,
31    pub right: i64,
32    pub top: i64,
33    pub bottom: i64,
34    pub origin: String,
35}
36
37/// The user associated with an element (a mapper account name and id).
38#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
39pub struct OsmUser {
40    pub id: i32,
41    pub name: String,
42}
43
44/// A polymorphic OSM element: either a [`Node`], a [`Way`] or a [`Relation`].
45///
46/// Serialized with a `type` tag (`"node"`, `"way"`, `"relation"`).
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(tag = "type")]
49pub enum Element {
50    Node(Node),
51    Way(Way),
52    Relation(Relation),
53}
54
55impl Element {
56    /// Returns the element's `(type, id)` pair.
57    pub fn get_meta(&self) -> (ElementType, i64) {
58        match self {
59            Element::Node(e) => (ElementType::Node, e.id),
60            Element::Way(e) => (ElementType::Way, e.id),
61            Element::Relation(e) => (ElementType::Relation, e.id),
62        }
63    }
64}
65
66/// The type of an OSM element.
67///
68/// Can be parsed from the lowercase strings `"node"`, `"way"` and `"relation"` via
69/// [`FromStr`].
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
71pub enum ElementType {
72    Node,
73    Way,
74    Relation,
75}
76
77impl FromStr for ElementType {
78    type Err = anyhow::Error;
79
80    fn from_str(s: &str) -> Result<Self, Self::Err> {
81        match s {
82            "node" => Ok(ElementType::Node),
83            "way" => Ok(ElementType::Way),
84            "relation" => Ok(ElementType::Relation),
85            _ => Err(anyhow!("Illegal element_type: {}", s)),
86        }
87    }
88}
89
90/// Common metadata shared by [`Node`], [`Way`] and [`Relation`].
91///
92/// See the [module docs](self) for the default values (`visible = true`,
93/// `version = changeset_id = -1`, `timestamp`/`user` = `None`).
94#[derive(Debug)]
95pub struct ElementBase {
96    pub id: i64,
97    pub version: i32,
98    pub timestamp: Option<DateTime<Utc>>,
99    pub user: Option<OsmUser>,
100    pub changeset_id: i64,
101    pub visible: bool,
102    pub tags: Vec<Tag>,
103}
104
105// `visible` defaults to true: the PBF spec states the flag "MUST be assumed to be true" when
106// absent, and a derived `Default` would yield `false` for the `bool`, silently marking every
107// freshly-created element as deleted on write.
108impl Default for ElementBase {
109    fn default() -> Self {
110        Self {
111            id: 0,
112            version: -1,
113            timestamp: None,
114            user: None,
115            changeset_id: -1,
116            visible: true,
117            tags: Vec::new(),
118        }
119    }
120}
121
122impl ElementBase {
123    /// Creates base metadata for an element with only an id and tags (no version, timestamp
124    /// or user information).
125    pub fn new_with_tags(id: i64, tags: Vec<Tag>) -> Self {
126        Self {
127            id,
128            tags,
129            visible: true,
130            ..Default::default()
131        }
132    }
133}
134
135/// A `key=value` pair attached to an element.
136#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
137pub struct Tag {
138    pub key: String,
139    pub value: String,
140}
141
142/// An OSM node: a point with a coordinate.
143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
144pub struct Node {
145    pub id: i64,
146    pub version: i32,
147    pub timestamp: Option<DateTime<Utc>>,
148    pub user: Option<OsmUser>,
149    pub changeset_id: i64,
150    /// Latitude in integer **nanodegrees** (divide by 1e9 for degrees).
151    pub latitude: i64,
152    /// Longitude in integer **nanodegrees** (divide by 1e9 for degrees).
153    pub longitude: i64,
154    /// `false` marks a deleted/historical object; defaults to `true` (see module docs).
155    pub visible: bool,
156    pub tags: Vec<Tag>,
157}
158
159// See the comment on `ElementBase::default()`: `visible` must default to true, not to the
160// derived `bool` default of false.
161impl Default for Node {
162    fn default() -> Self {
163        Self {
164            id: 0,
165            version: -1,
166            timestamp: None,
167            user: None,
168            changeset_id: -1,
169            latitude: 0,
170            longitude: 0,
171            visible: true,
172            tags: Vec::new(),
173        }
174    }
175}
176
177impl From<ElementBase> for Node {
178    fn from(el: ElementBase) -> Self {
179        Self {
180            id: el.id,
181            version: el.version,
182            timestamp: el.timestamp,
183            user: el.user,
184            changeset_id: el.changeset_id,
185            visible: el.visible,
186            tags: el.tags,
187            latitude: 0,
188            longitude: 0,
189        }
190    }
191}
192
193/// An OSM way: an ordered list of node references ([`WayNode`]s).
194#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
195pub struct Way {
196    pub id: i64,
197    pub version: i32,
198    pub timestamp: Option<DateTime<Utc>>,
199    pub user: Option<OsmUser>,
200    pub changeset_id: i64,
201    /// `false` marks a deleted/historical object; defaults to `true` (see module docs).
202    pub visible: bool,
203    pub tags: Vec<Tag>,
204    /// The way's nodes in order. Coordinates are present only when the file declares the
205    /// `LocationsOnWays` feature.
206    pub way_nodes: Vec<WayNode>,
207}
208
209// `visible` defaults to true — see `ElementBase::default()`.
210impl Default for Way {
211    fn default() -> Self {
212        Self {
213            id: 0,
214            version: -1,
215            timestamp: None,
216            user: None,
217            changeset_id: -1,
218            visible: true,
219            tags: Vec::new(),
220            way_nodes: Vec::new(),
221        }
222    }
223}
224
225impl From<ElementBase> for Way {
226    fn from(el: ElementBase) -> Self {
227        Self {
228            id: el.id,
229            version: el.version,
230            timestamp: el.timestamp,
231            user: el.user,
232            changeset_id: el.changeset_id,
233            visible: el.visible,
234            tags: el.tags,
235            way_nodes: Vec::new(),
236        }
237    }
238}
239
240/// A reference to a node within a [`Way`], optionally carrying the node's coordinates.
241///
242/// Coordinates are in integer **nanodegrees** and are only populated when the PBF file
243/// carries node locations on ways (`LocationsOnWays` optional feature).
244#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
245pub struct WayNode {
246    /// The referenced node's id.
247    pub id: i64,
248    pub latitude: Option<i64>,
249    pub longitude: Option<i64>,
250}
251
252impl WayNode {
253    /// Creates a node reference without coordinates.
254    pub fn new_without_coords(id: i64) -> Self {
255        Self {
256            id,
257            latitude: None,
258            longitude: None,
259        }
260    }
261
262    /// Creates a node reference with coordinates (in integer nanodegrees).
263    pub fn new(id: i64, latitude: i64, longitude: i64) -> Self {
264        Self {
265            id,
266            latitude: Some(latitude),
267            longitude: Some(longitude),
268        }
269    }
270}
271
272/// An OSM relation: a set of typed member references ([`RelationMember`]s).
273#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
274pub struct Relation {
275    pub id: i64,
276    pub version: i32,
277    pub timestamp: Option<DateTime<Utc>>,
278    pub user: Option<OsmUser>,
279    pub changeset_id: i64,
280    /// `false` marks a deleted/historical object; defaults to `true` (see module docs).
281    pub visible: bool,
282    pub tags: Vec<Tag>,
283    pub members: Vec<RelationMember>,
284}
285
286// `visible` defaults to true — see `ElementBase::default()`.
287impl Default for Relation {
288    fn default() -> Self {
289        Self {
290            id: 0,
291            version: -1,
292            timestamp: None,
293            user: None,
294            changeset_id: -1,
295            visible: true,
296            tags: Vec::new(),
297            members: Vec::new(),
298        }
299    }
300}
301
302impl From<ElementBase> for Relation {
303    fn from(el: ElementBase) -> Self {
304        Self {
305            id: el.id,
306            version: el.version,
307            timestamp: el.timestamp,
308            user: el.user,
309            changeset_id: el.changeset_id,
310            visible: el.visible,
311            tags: el.tags,
312            members: Vec::new(),
313        }
314    }
315}
316
317/// A member of a [`Relation`]: a typed reference to another element plus a role.
318#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
319pub struct RelationMember {
320    /// The referenced element's id.
321    pub member_id: i64,
322    /// The referenced element's type.
323    pub member_type: ElementType,
324    /// The member's role within the relation (e.g. `"outer"`, `"inner"`).
325    pub role: String,
326}
327
328/// Common accessors implemented by [`Node`], [`Way`] and [`Relation`].
329pub trait BasicElement: Clone {
330    fn get_element_type() -> ElementType;
331    fn get_id(&self) -> i64;
332    fn get_version(&self) -> i32;
333    fn get_timestamp(&self) -> Option<DateTime<Utc>>;
334    fn get_changeset_id(&self) -> i64;
335    fn is_visible(&self) -> bool;
336    fn get_tags(&self) -> &Vec<Tag>;
337    fn get_user(&self) -> Option<&OsmUser>;
338}
339
340impl BasicElement for Node {
341    fn get_element_type() -> ElementType {
342        ElementType::Node
343    }
344
345    fn get_id(&self) -> i64 {
346        self.id
347    }
348
349    fn get_version(&self) -> i32 {
350        self.version
351    }
352
353    fn get_timestamp(&self) -> Option<DateTime<Utc>> {
354        self.timestamp
355    }
356
357    fn get_changeset_id(&self) -> i64 {
358        self.changeset_id
359    }
360
361    fn is_visible(&self) -> bool {
362        self.visible
363    }
364
365    fn get_tags(&self) -> &Vec<Tag> {
366        &self.tags
367    }
368
369    fn get_user(&self) -> Option<&OsmUser> {
370        self.user.as_ref()
371    }
372}
373
374impl BasicElement for Way {
375    fn get_element_type() -> ElementType {
376        ElementType::Way
377    }
378
379    fn get_id(&self) -> i64 {
380        self.id
381    }
382
383    fn get_version(&self) -> i32 {
384        self.version
385    }
386
387    fn get_timestamp(&self) -> Option<DateTime<Utc>> {
388        self.timestamp
389    }
390
391    fn get_changeset_id(&self) -> i64 {
392        self.changeset_id
393    }
394
395    fn is_visible(&self) -> bool {
396        self.visible
397    }
398
399    fn get_tags(&self) -> &Vec<Tag> {
400        &self.tags
401    }
402
403    fn get_user(&self) -> Option<&OsmUser> {
404        self.user.as_ref()
405    }
406}
407
408impl BasicElement for Relation {
409    fn get_element_type() -> ElementType {
410        ElementType::Relation
411    }
412
413    fn get_id(&self) -> i64 {
414        self.id
415    }
416
417    fn get_version(&self) -> i32 {
418        self.version
419    }
420
421    fn get_timestamp(&self) -> Option<DateTime<Utc>> {
422        self.timestamp
423    }
424
425    fn get_changeset_id(&self) -> i64 {
426        self.changeset_id
427    }
428
429    fn is_visible(&self) -> bool {
430        self.visible
431    }
432
433    fn get_tags(&self) -> &Vec<Tag> {
434        &self.tags
435    }
436
437    fn get_user(&self) -> Option<&OsmUser> {
438        self.user.as_ref()
439    }
440}