osmio/
lib.rs

1//! Read and write OpenStreetMap files
2//!
3//! # Reading files
4//!
5//! ```no_run
6//! use osmio::prelude::*;
7//!
8//! let mut reader = osmio::read_pbf("path/to/filename.osm.pbf")?;
9//! for obj in reader.objects() {
10//!     // ...
11//! }
12//! # Ok::<(), anyhow::Error>(())
13//! ```
14extern crate byteorder;
15extern crate chrono;
16extern crate flate2;
17extern crate protobuf;
18extern crate quick_xml;
19extern crate xml as xml_rs;
20#[macro_use]
21extern crate derive_builder;
22extern crate anyhow;
23extern crate bzip2;
24extern crate serde;
25extern crate serde_json;
26extern crate smallvec;
27extern crate smol_str;
28
29use serde::{Deserialize, Serialize};
30use std::collections::HashMap;
31use std::convert::{TryFrom, TryInto};
32use std::fmt;
33use std::fmt::Debug;
34use std::fmt::Display;
35use std::fs::File;
36use std::io::{BufReader, Read, Write};
37use std::iter::{ExactSizeIterator, Iterator};
38use std::path::Path;
39use std::str::FromStr;
40use utils::{epoch_to_iso, iso_to_epoch};
41
42use anyhow::Result;
43
44#[macro_use]
45pub mod utils;
46
47pub mod arcpbf;
48pub mod pbf;
49pub mod stringpbf;
50pub mod xml;
51//pub mod opl;
52pub mod osc;
53
54pub mod obj_types;
55
56#[cfg(test)]
57mod tests;
58
59pub mod changesets;
60
61/// Type that stores the OSM Id
62pub type ObjId = i64;
63
64/// How many nanodegrees are represented by each unit in [`Lat::inner()`].
65/// We use the same internal precision as OpenStreetMap.org, 100 nanodegrees.
66pub const COORD_PRECISION_NANOS: i32 = 100;
67
68/// Number of internal units (as returned from [`Lat::inner`]) in one degree.
69///
70/// See [`COORD_PRECISION_NANOS`].
71pub const COORD_SCALE_FACTOR: f64 = (1_000_000_000 / COORD_PRECISION_NANOS) as f64;
72
73pub mod prelude {
74    //! Useful things for osmio
75    pub use crate::OSMObj;
76    pub use crate::OSMObjectType;
77    pub use crate::OSMReader;
78    pub use crate::{Node, Relation, Way};
79}
80
81pub fn lat_lon_inner_to_degrees(inner: i32) -> f64 {
82    inner as f64 / COORD_SCALE_FACTOR
83}
84
85macro_rules! lat_lon_impl {
86    ($lat_or_lon: ident) => {
87        /// Latitude and Longitude are stored internally as a 32-bit signed integer, in units
88        /// of [`COORD_PRECISION_NANOS`].
89        ///
90        /// This gives us 7 decimal places of precision - the same precision that OSM uses.
91        ///
92        /// ```
93        /// use std::convert::TryFrom;
94        /// use osmio::Lat;
95        /// let lat = Lat::try_from(1.0).unwrap();
96        /// let float_lat: f64 = lat.into();
97        /// assert_eq!(float_lat, 1.);
98        /// ```
99        #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
100        pub struct $lat_or_lon(i32);
101
102        impl $lat_or_lon {
103            /// Build a Lat/Lon from it's inner representation, which is `degrees * 1e7`.
104            ///
105            /// ```
106            /// use osmio::Lat;
107            /// // build a Lat for 1.2345678 degrees South
108            /// let lat = Lat::from_inner(12345678);
109            /// ```
110            pub fn from_inner(inner: i32) -> Self {
111                Self(inner)
112            }
113
114            pub fn inner(&self) -> i32 {
115                self.0
116            }
117
118            /// Returns the number of degrees as a 64-bit float.
119            ///
120            /// Note: The actual precision is [`COORD_PRECISION_NANOS`], which is less than
121            /// 64-bits. It is derived from an inner i32 representation, which mirrors the
122            /// precision used by OpenStreetMap.org
123            pub fn degrees(&self) -> f64 {
124                lat_lon_inner_to_degrees(self.0)
125            }
126        }
127
128        impl Display for $lat_or_lon {
129            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130                // TODO: fix precision
131                Display::fmt(&self.degrees(), f)
132            }
133        }
134
135        impl Debug for $lat_or_lon {
136            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137                // TODO: fix precision
138                Debug::fmt(&self.degrees(), f)
139            }
140        }
141
142        /// ```rust
143        ///  use osmio::Lat;
144        ///  use std::str::FromStr;
145        ///
146        ///  let lat = Lat::from_str("1.23").unwrap();
147        ///  assert_eq!(1.23, lat.degrees());
148        ///  assert_eq!(12300000, lat.inner());
149        ///
150        ///  assert!(Lat::from_str("-180.0").is_ok());
151        ///  assert!(Lat::from_str("xxxx").is_err());
152        ///  assert!(Lat::from_str("600.0").is_err());
153        /// ```
154        impl FromStr for $lat_or_lon {
155            type Err = ParseLatLonError;
156
157            fn from_str(s: &str) -> Result<Self, Self::Err> {
158                match (f64::from_str(s)? * COORD_SCALE_FACTOR).round() {
159                    x if x > (i32::MAX as f64) => Err(ParseLatLonError::TooLarge(x)),
160                    x if x < (i32::MIN as f64) => Err(ParseLatLonError::TooSmall(x)),
161                    x => Ok(Self(x as i32)),
162                }
163            }
164        }
165
166        impl From<$lat_or_lon> for f64 {
167            fn from(val: $lat_or_lon) -> f64 {
168                val.degrees()
169            }
170        }
171
172        impl TryFrom<f64> for $lat_or_lon {
173            type Error = ParseLatLonError;
174            fn try_from(val: f64) -> Result<$lat_or_lon, Self::Error> {
175                match (val * COORD_SCALE_FACTOR).round() {
176                    x if x > (i32::MAX as f64) => Err(ParseLatLonError::TooLarge(x)),
177                    x if x < (i32::MIN as f64) => Err(ParseLatLonError::TooSmall(x)),
178                    x => Ok(Self(x as i32)),
179                }
180            }
181        }
182
183        impl TryFrom<f32> for $lat_or_lon {
184            type Error = ParseLatLonError;
185            fn try_from(val: f32) -> Result<$lat_or_lon, Self::Error> {
186                $lat_or_lon::try_from(val as f64)
187            }
188        }
189    };
190}
191
192// Latitude
193lat_lon_impl!(Lat);
194
195// Longitude
196lat_lon_impl!(Lon);
197
198/// An error while trying to parse a string into a [`Lat`] or [`Lon`]
199#[derive(Debug)]
200pub enum ParseLatLonError {
201    /// Number was not parseable as a Float, wraps the underlying [`std::num::ParseFloatError`].
202    ParseFloatError(std::num::ParseFloatError),
203
204    /// The parsed float was too large to fit into our Lat/Lon representation.
205    ///
206    /// This should never happen for "normal" Lats and Lons, i.e. those between (-90..90) or
207    /// (-180..+180), respectively.
208    TooLarge(f64),
209
210    /// The parsed float was too small to fit into our Lat/Lon representation.
211    ///
212    /// This should never happen for "normal" Lats and Lons, i.e. those between (-90..90) or
213    /// (-180..+180), respectively.
214    TooSmall(f64),
215}
216
217impl std::error::Error for ParseLatLonError {
218    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
219        if let Self::ParseFloatError(inner) = self {
220            Some(inner)
221        } else {
222            None
223        }
224    }
225}
226
227impl Display for ParseLatLonError {
228    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229        match self {
230            Self::ParseFloatError(inner) => Display::fmt(inner, f),
231            Self::TooLarge(float) => write!(f, "{} is too large to represent as a Lat/Lon", float),
232            Self::TooSmall(float) => write!(f, "{} is too small to represent as a Lat/Lon", float),
233        }
234    }
235}
236
237impl From<std::num::ParseFloatError> for ParseLatLonError {
238    fn from(err: std::num::ParseFloatError) -> Self {
239        ParseLatLonError::ParseFloatError(err)
240    }
241}
242
243/// Timestamps can be stored as an ISO formatted string, or number of seconds since unix epoch
244///
245/// In XML files, timestamps are represented as ISO strings, and in PBF files, as integer seconds
246/// since the epoch
247#[derive(Debug, Clone, Eq, Serialize, Deserialize)]
248pub enum TimestampFormat {
249    ISOString(String),
250    EpochNunber(i64),
251}
252
253impl TimestampFormat {
254    pub fn to_iso_string(&self) -> String {
255        match self {
256            TimestampFormat::ISOString(s) => s.clone(),
257            TimestampFormat::EpochNunber(t) => epoch_to_iso(*t as i32),
258        }
259    }
260
261    pub fn to_epoch_number(&self) -> i64 {
262        match self {
263            TimestampFormat::ISOString(s) => iso_to_epoch(s) as i64,
264            &TimestampFormat::EpochNunber(t) => t,
265        }
266    }
267}
268
269impl<T> From<T> for TimestampFormat
270where
271    T: Into<i64>,
272{
273    fn from(v: T) -> Self {
274        TimestampFormat::EpochNunber(v.into())
275    }
276}
277
278impl std::str::FromStr for TimestampFormat {
279    type Err = String;
280
281    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
282        let date: i64 = chrono::DateTime::parse_from_rfc3339(s)
283            .map_err(|_| "invalid date")?
284            .timestamp();
285        Ok(TimestampFormat::EpochNunber(date))
286    }
287}
288
289impl fmt::Display for TimestampFormat {
290    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
291        write!(f, "{}", self.to_iso_string())
292    }
293}
294
295impl std::cmp::PartialOrd for TimestampFormat {
296    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
297        match (self, other) {
298            (TimestampFormat::ISOString(a), TimestampFormat::ISOString(b)) => a.partial_cmp(b),
299            (TimestampFormat::EpochNunber(a), TimestampFormat::EpochNunber(b)) => a.partial_cmp(b),
300            (a, b) => a.to_epoch_number().partial_cmp(&b.to_epoch_number()),
301        }
302    }
303}
304impl std::cmp::PartialEq for TimestampFormat {
305    fn eq(&self, other: &Self) -> bool {
306        match (self, other) {
307            (TimestampFormat::ISOString(a), TimestampFormat::ISOString(b)) => a.eq(b),
308            (TimestampFormat::EpochNunber(a), TimestampFormat::EpochNunber(b)) => a.eq(b),
309            (a, b) => a.to_epoch_number().eq(&b.to_epoch_number()),
310        }
311    }
312}
313
314/// The basic metadata fields all OSM objects share
315pub trait OSMObjBase: PartialEq + Debug + Clone {
316    fn id(&self) -> ObjId;
317    fn set_id(&mut self, val: impl Into<ObjId>);
318    fn version(&self) -> Option<u32>;
319    fn set_version(&mut self, val: impl Into<Option<u32>>);
320    fn deleted(&self) -> bool;
321    fn set_deleted(&mut self, val: bool);
322    fn changeset_id(&self) -> Option<u32>;
323    fn set_changeset_id(&mut self, val: impl Into<Option<u32>>);
324    fn timestamp(&self) -> &Option<TimestampFormat>;
325    fn set_timestamp(&mut self, val: impl Into<Option<TimestampFormat>>);
326    fn uid(&self) -> Option<u32>;
327    fn set_uid(&mut self, val: impl Into<Option<u32>>);
328    fn user(&self) -> Option<&str>;
329    fn set_user<'a>(&mut self, val: impl Into<Option<&'a str>>);
330
331    fn tags<'a>(&'a self) -> Box<dyn ExactSizeIterator<Item = (&'a str, &'a str)> + 'a>;
332    fn tag(&self, key: impl AsRef<str>) -> Option<&str>;
333    fn has_tag(&self, key: impl AsRef<str>) -> bool {
334        self.tag(key).is_some()
335    }
336    fn num_tags(&self) -> usize {
337        self.tags().count()
338    }
339    // The tags of this object, as a JSON string
340    fn tags_json_string(&self) -> String {
341        if self.untagged() {
342            return "{}".to_string();
343        }
344        let mut t = serde_json::Map::new();
345        for (k, v) in self.tags() {
346            t.insert(k.to_string(), serde_json::Value::String(v.to_string()));
347        }
348        serde_json::Value::Object(t).to_string()
349    }
350
351    /// True iff this object has tags
352    fn tagged(&self) -> bool {
353        !self.untagged()
354    }
355    /// True iff this object has no tags
356    fn untagged(&self) -> bool {
357        self.num_tags() == 0
358    }
359
360    fn set_tag(&mut self, key: impl AsRef<str>, value: impl Into<String>);
361    fn unset_tag(&mut self, key: impl AsRef<str>);
362
363    fn strip_metadata(&mut self) {
364        self.set_uid(None);
365        self.set_user(None);
366        self.set_changeset_id(None);
367    }
368
369    fn object_type(&self) -> OSMObjectType;
370}
371
372/// A Node
373pub trait Node: OSMObjBase {
374    /// Latitude & Longitude of the node (if it's set)
375    fn lat_lon(&self) -> Option<(Lat, Lon)>;
376
377    /// Latitude & Longitude of the node as `f64` (if it's set)
378    fn lat_lon_f64(&self) -> Option<(f64, f64)> {
379        self.lat_lon().map(|(lat, lon)| (lat.into(), lon.into()))
380    }
381    /// True iff this node has latitude & longitude set
382    fn has_lat_lon(&self) -> bool {
383        self.lat_lon().is_some()
384    }
385
386    /// Remove the lat & lon for this node
387    fn unset_lat_lon(&mut self) {
388        self.set_lat_lon_direct(None);
389    }
390
391    /// Directly set the lat & lon of the node, see `set_lat_lon()` as a more convienient method.
392    fn set_lat_lon_direct(&mut self, loc: Option<(Lat, Lon)>);
393
394    /// Set the Latitude & Longitude.
395    ///
396    /// The type signature is complicated so you can convert from f64
397    /// ```rust
398    /// use osmio::Node;
399    /// # use osmio::obj_types::StringNodeBuilder;
400    /// # let mut node = StringNodeBuilder::default()._id(1).build().unwrap();
401    ///
402    /// // It can convert from f64
403    /// node.set_lat_lon((0.0_f64, 0.0_f64));
404    /// assert_eq!(node.lat_lon_f64().unwrap(), (0., 0.));
405    ///
406    /// // .. or from f32
407    /// node.set_lat_lon((0.0_f32, 0.0_f32));
408    /// assert_eq!(node.lat_lon_f64().unwrap(), (0., 0.));
409    ///
410    /// // You can set to None too
411    /// node.set_lat_lon(None as Option<(f64, f64)>);
412    /// assert!(node.lat_lon_f64().is_none());
413    /// ```
414    fn set_lat_lon<LL, L1, L2>(&mut self, loc: LL) -> Result<(), ParseLatLonError>
415    where
416        L1: TryInto<Lat>,
417        L2: TryInto<Lon>,
418        LL: Into<Option<(L1, L2)>>,
419        ParseLatLonError: From<<L1 as TryInto<Lat>>::Error>,
420        ParseLatLonError: From<<L2 as TryInto<Lon>>::Error>,
421    {
422        let ll: Option<(L1, L2)> = loc.into();
423        match ll {
424            None => self.set_lat_lon_direct(None),
425            Some((l1, l2)) => {
426                let l1: Lat = l1.try_into()?;
427                let l2: Lon = l2.try_into()?;
428                self.set_lat_lon_direct(Some((l1, l2)));
429            }
430        }
431        Ok(())
432    }
433}
434
435/// A Way
436pub trait Way: OSMObjBase {
437    /// List of node ids in this way
438    fn nodes(&self) -> &[ObjId];
439
440    /// Alias for Way::nodes().
441    fn nids(&self) -> &[ObjId] {
442        self.nodes()
443    }
444    /// Number of nodes.
445    fn num_nodes(&self) -> usize;
446    /// Return node id at this position
447    fn node(&self, idx: usize) -> Option<ObjId>;
448    fn set_nodes(&mut self, nodes: impl IntoIterator<Item = impl Into<ObjId>>);
449
450    /// A Way which begins and ends with the same Node is considered "closed".
451    ///
452    /// A closed way that also has an `area=yes` tag should be interpreted as an area.
453    ///
454    /// This method only compares Node ID's, and does no location based comparison.
455    fn is_closed(&self) -> bool {
456        match (self.nodes().first(), self.nodes().last()) {
457            (Some(a), Some(b)) => a == b,
458            _ => false,
459        }
460    }
461
462    /// When `is_area` is true, the Way should be interpreted as a 2-D shape rather than a 1-D
463    /// linestring. Uses OSM convention to detect “areas”.
464    fn is_area(&self) -> bool {
465        // Generally any closed way represents an area the `area=yes` tag should also be present,
466        // but sometimes it's `area=highway` or other things. In the interest of accepting all
467        // plausible input, we assume it's an area unless explicitly marked otherwise.
468        //
469        // See also: https://taginfo.openstreetmap.org/keys/area#values
470        self.is_closed() && self.tag("area") != Some("no")
471    }
472}
473
474/// A Relation
475pub trait Relation: OSMObjBase {
476    fn members<'a>(
477        &'a self,
478    ) -> Box<dyn ExactSizeIterator<Item = (OSMObjectType, ObjId, &'a str)> + 'a>;
479    fn set_members(
480        &mut self,
481        members: impl IntoIterator<Item = (OSMObjectType, ObjId, impl Into<String>)>,
482    );
483}
484
485/// A Node, Way or Relation
486#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
487pub enum OSMObjectType {
488    Node,
489    Way,
490    Relation,
491}
492
493impl OSMObjectType {
494    pub fn name_short(&self) -> &str {
495        match self {
496            OSMObjectType::Node => "n",
497            OSMObjectType::Way => "w",
498            OSMObjectType::Relation => "r",
499        }
500    }
501    pub fn name_long(&self) -> &str {
502        match self {
503            OSMObjectType::Node => "node",
504            OSMObjectType::Way => "way",
505            OSMObjectType::Relation => "relation",
506        }
507    }
508}
509
510impl std::fmt::Debug for OSMObjectType {
511    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
512        write!(f, "{}", self.name_short())
513    }
514}
515
516impl std::fmt::Display for OSMObjectType {
517    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
518        write!(f, "{}", self.name_long())
519    }
520}
521
522impl TryFrom<char> for OSMObjectType {
523    type Error = String;
524
525    fn try_from(c: char) -> Result<Self, Self::Error> {
526        match c {
527            'n' => Ok(OSMObjectType::Node),
528            'w' => Ok(OSMObjectType::Way),
529            'r' => Ok(OSMObjectType::Relation),
530            _ => Err(format!("Cannot convert {} to OSMObjectType", c)),
531        }
532    }
533}
534
535impl std::str::FromStr for OSMObjectType {
536    type Err = String;
537
538    fn from_str(s: &str) -> Result<Self, Self::Err> {
539        match s {
540            "n" | "node" => Ok(OSMObjectType::Node),
541            "w" | "way" => Ok(OSMObjectType::Way),
542            "r" | "relation" | "rel" => Ok(OSMObjectType::Relation),
543            _ => Err(format!("Cannot convert {} to OSMObjectType", s)),
544        }
545    }
546}
547
548/// Something which could be any one of the possible OSM objects
549pub trait OSMObj: OSMObjBase {
550    /// The type of the Node type
551    type Node: Node;
552    /// The type of the Way type
553    type Way: Way;
554    /// The type of the Relation type
555    type Relation: Relation;
556
557    fn into_node(self) -> Option<Self::Node>;
558    fn into_way(self) -> Option<Self::Way>;
559    fn into_relation(self) -> Option<Self::Relation>;
560
561    fn as_node(&self) -> Option<&Self::Node>;
562    fn as_way(&self) -> Option<&Self::Way>;
563    fn as_relation(&self) -> Option<&Self::Relation>;
564
565    fn as_node_mut(&mut self) -> Option<&mut Self::Node>;
566    fn as_way_mut(&mut self) -> Option<&mut Self::Way>;
567    fn as_relation_mut(&mut self) -> Option<&mut Self::Relation>;
568
569    fn is_node(&self) -> bool {
570        self.object_type() == OSMObjectType::Node
571    }
572    fn is_way(&self) -> bool {
573        self.object_type() == OSMObjectType::Way
574    }
575    fn is_relation(&self) -> bool {
576        self.object_type() == OSMObjectType::Relation
577    }
578}
579
580/// A Generic reader that reads OSM objects
581pub trait OSMReader {
582    /// The underlying `std::io::Read`.
583    type R: Read;
584    /// The type of OSM Object that this returns
585    type Obj: OSMObj;
586
587    /// Create this reader from a `std::io::Read`.
588    fn new(_: Self::R) -> Self;
589
590    #[allow(unused_variables)]
591    fn set_sorted_assumption(&mut self, sorted_assumption: bool) {}
592    fn get_sorted_assumption(&mut self) -> bool {
593        false
594    }
595
596    fn assume_sorted(&mut self) {
597        self.set_sorted_assumption(true);
598    }
599    fn assume_unsorted(&mut self) {
600        self.set_sorted_assumption(false);
601    }
602
603    /// Convert to the underlying reader
604    fn into_inner(self) -> Self::R;
605
606    /// Reference to the inner
607    fn inner(&self) -> &Self::R;
608
609    /// Returns the next OSM Object in this reader
610    fn next(&mut self) -> Option<Self::Obj>;
611
612    /// Returns an iterator over the objects in this reader.
613    fn objects(&mut self) -> OSMObjectIterator<'_, Self>
614    where
615        Self: Sized,
616    {
617        OSMObjectIterator { inner: self }
618    }
619
620    fn nodes(&mut self) -> Box<dyn Iterator<Item = <<Self as OSMReader>::Obj as OSMObj>::Node> + '_>
621    where
622        Self: Sized,
623    {
624        Box::new(self.objects().filter_map(|o| o.into_node()))
625    }
626
627    fn ways(&mut self) -> Box<dyn Iterator<Item = <<Self as OSMReader>::Obj as OSMObj>::Way> + '_>
628    where
629        Self: Sized,
630    {
631        Box::new(self.objects().filter_map(|o| o.into_way()))
632    }
633
634    fn relations(
635        &mut self,
636    ) -> Box<dyn Iterator<Item = <<Self as OSMReader>::Obj as OSMObj>::Relation> + '_>
637    where
638        Self: Sized,
639    {
640        Box::new(self.objects().filter_map(|o| o.into_relation()))
641    }
642
643    //fn nodes_locations<'a>(&'a mut self) -> Box<Iterator<Item=(ObjId, Lat, Lon)>+'a> where Self:Sized {
644    //    Box::new(self.nodes().filter_map(|n| if n.deleted || n.lat.is_none() { None } else { Some((n.id, n.lat.unwrap(), n.lon.unwrap())) } ))
645    //}
646
647    //fn ways<'a>(&'a mut self) -> Box<Iterator<Item=Way>+'a> where Self:Sized {
648    //    if self.get_sorted_assumption() {
649    //        Box::new(self.objects().take_while(|o| (o.is_node() || o.is_way())).filter_map(|o| o.into_way()))
650    //    } else {
651    //        Box::new(self.objects().filter_map(|o| o.into_way()))
652    //    }
653    //}
654
655    //fn relations<'a>(&'a mut self) -> Box<Iterator<Item=Relation>+'a> where Self:Sized {
656    //    Box::new(self.objects().filter_map(|o| o.into_relation()))
657    //}
658}
659
660/// Something that produces OSMObjects
661///
662/// Created by `OSMReader::objects`
663pub struct OSMObjectIterator<'a, R>
664where
665    R: OSMReader + 'a,
666{
667    inner: &'a mut R,
668}
669
670impl<'a, R> OSMObjectIterator<'a, R>
671where
672    R: OSMReader,
673{
674    pub fn inner(&self) -> &R {
675        self.inner
676    }
677}
678
679impl<'a, R> Iterator for OSMObjectIterator<'a, R>
680where
681    R: OSMReader,
682{
683    type Item = R::Obj;
684
685    fn next(&mut self) -> Option<Self::Item> {
686        self.inner.next()
687    }
688}
689
690/// An error when trying to write from an OSMWriter
691#[derive(Debug)]
692pub enum OSMWriteError {
693    FormatDoesntSupportHeaders,
694    AlreadyStarted,
695    AlreadyClosed,
696    OPLWrite(::std::io::Error),
697    XMLWriteXMLError(quick_xml::Error),
698    XMLWriteIOError(::std::io::Error),
699}
700impl std::fmt::Display for OSMWriteError {
701    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
702        write!(f, "{:?}", self)
703    }
704}
705impl std::error::Error for OSMWriteError {}
706
707/// A generic writer for OSM objects.
708pub trait OSMWriter<W: Write> {
709    /// Create a writer from an underying writer
710    fn new(_: W) -> Self;
711
712    /// Close this writer, cannot write any more objects.
713    /// Some fileformats have certain 'end of file' things. After you write those, you cannot write
714    /// any more OSM objects. e.g. an XML file format will require that you close your root XML
715    /// tag.
716    /// After calling this method, you cannot add any more OSM objects to this writer, and
717    /// `is_open` will return `false`.
718    fn close(&mut self) -> Result<(), OSMWriteError>;
719
720    /// Return true iff this writer is not closed.
721    /// If open you should be able to continue to write objects to it. If closed you cannot write
722    /// any more OSM objects to it.
723    fn is_open(&self) -> bool;
724
725    /// Write an OSM object to this.
726    fn write_obj(&mut self, obj: &impl OSMObj) -> Result<(), OSMWriteError>;
727
728    /// Convert back to the underlying writer object
729    fn into_inner(self) -> W;
730
731    fn set_header(&mut self, _key_value: (&str, &str)) -> Result<(), OSMWriteError> {
732        todo!("set_header not done yet")
733    }
734
735    /// Create a new OSMWriter, consume all the objects from an OSMObj iterator source, and then
736    /// close this source. Returns this OSMWriter.
737    fn from_iter<I: Iterator<Item = impl OSMObj>>(writer: W, iter: I) -> Self
738    where
739        Self: Sized,
740    {
741        let mut writer = Self::new(writer);
742
743        // FIXME return the results of these operations?
744        for obj in iter {
745            writer.write_obj(&obj).unwrap();
746        }
747        writer.close().unwrap();
748
749        writer
750    }
751}
752
753/// The version string of this library.
754///
755/// calls the "CARGO_PKG_VERSION"
756pub fn version<'a>() -> &'a str {
757    option_env!("CARGO_PKG_VERSION").unwrap_or("unknown-non-cargo-build")
758}
759
760/// Opens a PBF filename
761pub fn read_pbf(filename: impl AsRef<Path>) -> Result<pbf::PBFReader<BufReader<File>>> {
762    pbf::PBFReader::from_filename(filename)
763}
764
765/// Opens a bzip2 filename
766pub fn read_xml(
767    filename: impl AsRef<Path>,
768) -> Result<xml::XMLReader<bzip2::read::MultiBzDecoder<std::fs::File>>> {
769    xml::from_filename_bz2(filename)
770}