extern crate byteorder;
extern crate chrono;
extern crate flate2;
extern crate protobuf;
extern crate quick_xml;
extern crate xml as xml_rs;
#[macro_use]
extern crate derive_builder;
extern crate anyhow;
extern crate bzip2;
extern crate serde;
extern crate serde_json;
extern crate smallvec;
extern crate smol_str;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::fmt::Debug;
use std::fmt::Display;
use std::fs::File;
use std::io::{BufReader, Read, Write};
use std::iter::{ExactSizeIterator, Iterator};
use std::path::Path;
use std::str::FromStr;
use utils::{epoch_to_iso, iso_to_epoch};
use anyhow::Result;
#[macro_use]
pub mod utils;
pub mod arcpbf;
pub mod pbf;
pub mod stringpbf;
pub mod xml;
pub mod osc;
pub mod obj_types;
#[cfg(test)]
mod tests;
pub mod changesets;
pub type ObjId = i64;
pub const COORD_PRECISION_NANOS: i32 = 100;
pub const COORD_SCALE_FACTOR: f64 = (1_000_000_000 / COORD_PRECISION_NANOS) as f64;
pub mod prelude {
pub use crate::OSMObj;
pub use crate::OSMObjectType;
pub use crate::OSMReader;
pub use crate::{Node, Relation, Way};
}
pub fn lat_lon_inner_to_degrees(inner: i32) -> f64 {
inner as f64 / COORD_SCALE_FACTOR
}
macro_rules! lat_lon_impl {
($lat_or_lon: ident) => {
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct $lat_or_lon(i32);
impl $lat_or_lon {
pub fn from_inner(inner: i32) -> Self {
Self(inner)
}
pub fn inner(&self) -> i32 {
self.0
}
pub fn degrees(&self) -> f64 {
lat_lon_inner_to_degrees(self.0)
}
}
impl Display for $lat_or_lon {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.degrees(), f)
}
}
impl Debug for $lat_or_lon {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.degrees(), f)
}
}
impl FromStr for $lat_or_lon {
type Err = ParseLatLonError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match (f64::from_str(s)? * COORD_SCALE_FACTOR).round() {
x if x > (i32::MAX as f64) => Err(ParseLatLonError::TooLarge(x)),
x if x < (i32::MIN as f64) => Err(ParseLatLonError::TooSmall(x)),
x => Ok(Self(x as i32)),
}
}
}
impl From<$lat_or_lon> for f64 {
fn from(val: $lat_or_lon) -> f64 {
val.degrees()
}
}
impl TryFrom<f64> for $lat_or_lon {
type Error = ParseLatLonError;
fn try_from(val: f64) -> Result<$lat_or_lon, Self::Error> {
match (val * COORD_SCALE_FACTOR).round() {
x if x > (i32::MAX as f64) => Err(ParseLatLonError::TooLarge(x)),
x if x < (i32::MIN as f64) => Err(ParseLatLonError::TooSmall(x)),
x => Ok(Self(x as i32)),
}
}
}
impl TryFrom<f32> for $lat_or_lon {
type Error = ParseLatLonError;
fn try_from(val: f32) -> Result<$lat_or_lon, Self::Error> {
$lat_or_lon::try_from(val as f64)
}
}
};
}
lat_lon_impl!(Lat);
lat_lon_impl!(Lon);
#[derive(Debug)]
pub enum ParseLatLonError {
ParseFloatError(std::num::ParseFloatError),
TooLarge(f64),
TooSmall(f64),
}
impl std::error::Error for ParseLatLonError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
if let Self::ParseFloatError(inner) = self {
Some(inner)
} else {
None
}
}
}
impl Display for ParseLatLonError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ParseFloatError(inner) => Display::fmt(inner, f),
Self::TooLarge(float) => write!(f, "{} is too large to represent as a Lat/Lon", float),
Self::TooSmall(float) => write!(f, "{} is too small to represent as a Lat/Lon", float),
}
}
}
impl From<std::num::ParseFloatError> for ParseLatLonError {
fn from(err: std::num::ParseFloatError) -> Self {
ParseLatLonError::ParseFloatError(err)
}
}
#[derive(Debug, Clone, Eq, Serialize, Deserialize)]
pub enum TimestampFormat {
ISOString(String),
EpochNunber(i64),
}
impl TimestampFormat {
pub fn to_iso_string(&self) -> String {
match self {
TimestampFormat::ISOString(s) => s.clone(),
TimestampFormat::EpochNunber(t) => epoch_to_iso(*t as i32),
}
}
pub fn to_epoch_number(&self) -> i64 {
match self {
TimestampFormat::ISOString(s) => iso_to_epoch(s) as i64,
&TimestampFormat::EpochNunber(t) => t,
}
}
}
impl<T> From<T> for TimestampFormat
where
T: Into<i64>,
{
fn from(v: T) -> Self {
TimestampFormat::EpochNunber(v.into())
}
}
impl std::str::FromStr for TimestampFormat {
type Err = String;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let date: i64 = chrono::DateTime::parse_from_rfc3339(s)
.map_err(|_| "invalid date")?
.timestamp();
Ok(TimestampFormat::EpochNunber(date))
}
}
impl fmt::Display for TimestampFormat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_iso_string())
}
}
impl std::cmp::PartialOrd for TimestampFormat {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match (self, other) {
(TimestampFormat::ISOString(a), TimestampFormat::ISOString(b)) => a.partial_cmp(b),
(TimestampFormat::EpochNunber(a), TimestampFormat::EpochNunber(b)) => a.partial_cmp(b),
(a, b) => a.to_epoch_number().partial_cmp(&b.to_epoch_number()),
}
}
}
impl std::cmp::PartialEq for TimestampFormat {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(TimestampFormat::ISOString(a), TimestampFormat::ISOString(b)) => a.eq(b),
(TimestampFormat::EpochNunber(a), TimestampFormat::EpochNunber(b)) => a.eq(b),
(a, b) => a.to_epoch_number().eq(&b.to_epoch_number()),
}
}
}
pub trait OSMObjBase: PartialEq + Debug + Clone {
fn id(&self) -> ObjId;
fn set_id(&mut self, val: impl Into<ObjId>);
fn version(&self) -> Option<u32>;
fn set_version(&mut self, val: impl Into<Option<u32>>);
fn deleted(&self) -> bool;
fn set_deleted(&mut self, val: bool);
fn changeset_id(&self) -> Option<u32>;
fn set_changeset_id(&mut self, val: impl Into<Option<u32>>);
fn timestamp(&self) -> &Option<TimestampFormat>;
fn set_timestamp(&mut self, val: impl Into<Option<TimestampFormat>>);
fn uid(&self) -> Option<u32>;
fn set_uid(&mut self, val: impl Into<Option<u32>>);
fn user(&self) -> Option<&str>;
fn set_user<'a>(&mut self, val: impl Into<Option<&'a str>>);
fn tags<'a>(&'a self) -> Box<dyn ExactSizeIterator<Item = (&'a str, &'a str)> + 'a>;
fn tag(&self, key: impl AsRef<str>) -> Option<&str>;
fn has_tag(&self, key: impl AsRef<str>) -> bool {
self.tag(key).is_some()
}
fn num_tags(&self) -> usize {
self.tags().count()
}
fn tags_json_string(&self) -> String {
if self.untagged() {
return "{}".to_string();
}
let mut t = serde_json::Map::new();
for (k, v) in self.tags() {
t.insert(k.to_string(), serde_json::Value::String(v.to_string()));
}
serde_json::Value::Object(t).to_string()
}
fn tagged(&self) -> bool {
!self.untagged()
}
fn untagged(&self) -> bool {
self.num_tags() == 0
}
fn set_tag(&mut self, key: impl AsRef<str>, value: impl Into<String>);
fn unset_tag(&mut self, key: impl AsRef<str>);
fn strip_metadata(&mut self) {
self.set_uid(None);
self.set_user(None);
self.set_changeset_id(None);
}
fn object_type(&self) -> OSMObjectType;
}
pub trait Node: OSMObjBase {
fn lat_lon(&self) -> Option<(Lat, Lon)>;
fn lat_lon_f64(&self) -> Option<(f64, f64)> {
self.lat_lon().map(|(lat, lon)| (lat.into(), lon.into()))
}
fn has_lat_lon(&self) -> bool {
self.lat_lon().is_some()
}
fn unset_lat_lon(&mut self) {
self.set_lat_lon_direct(None);
}
fn set_lat_lon_direct(&mut self, loc: Option<(Lat, Lon)>);
fn set_lat_lon<LL, L1, L2>(&mut self, loc: LL) -> Result<(), ParseLatLonError>
where
L1: TryInto<Lat>,
L2: TryInto<Lon>,
LL: Into<Option<(L1, L2)>>,
ParseLatLonError: From<<L1 as TryInto<Lat>>::Error>,
ParseLatLonError: From<<L2 as TryInto<Lon>>::Error>,
{
let ll: Option<(L1, L2)> = loc.into();
match ll {
None => self.set_lat_lon_direct(None),
Some((l1, l2)) => {
let l1: Lat = l1.try_into()?;
let l2: Lon = l2.try_into()?;
self.set_lat_lon_direct(Some((l1, l2)));
}
}
Ok(())
}
}
pub trait Way: OSMObjBase {
fn nodes(&self) -> &[ObjId];
fn nids(&self) -> &[ObjId] {
self.nodes()
}
fn num_nodes(&self) -> usize;
fn node(&self, idx: usize) -> Option<ObjId>;
fn set_nodes(&mut self, nodes: impl IntoIterator<Item = impl Into<ObjId>>);
fn is_closed(&self) -> bool {
match (self.nodes().first(), self.nodes().last()) {
(Some(a), Some(b)) => a == b,
_ => false,
}
}
fn is_area(&self) -> bool {
self.is_closed() && self.tag("area") != Some("no")
}
}
pub trait Relation: OSMObjBase {
fn members<'a>(
&'a self,
) -> Box<dyn ExactSizeIterator<Item = (OSMObjectType, ObjId, &'a str)> + 'a>;
fn set_members(
&mut self,
members: impl IntoIterator<Item = (OSMObjectType, ObjId, impl Into<String>)>,
);
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum OSMObjectType {
Node,
Way,
Relation,
}
impl OSMObjectType {
pub fn name_short(&self) -> &str {
match self {
OSMObjectType::Node => "n",
OSMObjectType::Way => "w",
OSMObjectType::Relation => "r",
}
}
pub fn name_long(&self) -> &str {
match self {
OSMObjectType::Node => "node",
OSMObjectType::Way => "way",
OSMObjectType::Relation => "relation",
}
}
}
impl std::fmt::Debug for OSMObjectType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(f, "{}", self.name_short())
}
}
impl std::fmt::Display for OSMObjectType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(f, "{}", self.name_long())
}
}
impl TryFrom<char> for OSMObjectType {
type Error = String;
fn try_from(c: char) -> Result<Self, Self::Error> {
match c {
'n' => Ok(OSMObjectType::Node),
'w' => Ok(OSMObjectType::Way),
'r' => Ok(OSMObjectType::Relation),
_ => Err(format!("Cannot convert {} to OSMObjectType", c)),
}
}
}
impl std::str::FromStr for OSMObjectType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"n" | "node" => Ok(OSMObjectType::Node),
"w" | "way" => Ok(OSMObjectType::Way),
"r" | "relation" | "rel" => Ok(OSMObjectType::Relation),
_ => Err(format!("Cannot convert {} to OSMObjectType", s)),
}
}
}
pub trait OSMObj: OSMObjBase {
type Node: Node;
type Way: Way;
type Relation: Relation;
fn into_node(self) -> Option<Self::Node>;
fn into_way(self) -> Option<Self::Way>;
fn into_relation(self) -> Option<Self::Relation>;
fn as_node(&self) -> Option<&Self::Node>;
fn as_way(&self) -> Option<&Self::Way>;
fn as_relation(&self) -> Option<&Self::Relation>;
fn as_node_mut(&mut self) -> Option<&mut Self::Node>;
fn as_way_mut(&mut self) -> Option<&mut Self::Way>;
fn as_relation_mut(&mut self) -> Option<&mut Self::Relation>;
fn is_node(&self) -> bool {
self.object_type() == OSMObjectType::Node
}
fn is_way(&self) -> bool {
self.object_type() == OSMObjectType::Way
}
fn is_relation(&self) -> bool {
self.object_type() == OSMObjectType::Relation
}
}
pub trait OSMReader {
type R: Read;
type Obj: OSMObj;
fn new(_: Self::R) -> Self;
#[allow(unused_variables)]
fn set_sorted_assumption(&mut self, sorted_assumption: bool) {}
fn get_sorted_assumption(&mut self) -> bool {
false
}
fn assume_sorted(&mut self) {
self.set_sorted_assumption(true);
}
fn assume_unsorted(&mut self) {
self.set_sorted_assumption(false);
}
fn into_inner(self) -> Self::R;
fn inner(&self) -> &Self::R;
fn next(&mut self) -> Option<Self::Obj>;
fn objects(&mut self) -> OSMObjectIterator<'_, Self>
where
Self: Sized,
{
OSMObjectIterator { inner: self }
}
fn nodes(&mut self) -> Box<dyn Iterator<Item = <<Self as OSMReader>::Obj as OSMObj>::Node> + '_>
where
Self: Sized,
{
Box::new(self.objects().filter_map(|o| o.into_node()))
}
fn ways(&mut self) -> Box<dyn Iterator<Item = <<Self as OSMReader>::Obj as OSMObj>::Way> + '_>
where
Self: Sized,
{
Box::new(self.objects().filter_map(|o| o.into_way()))
}
fn relations(
&mut self,
) -> Box<dyn Iterator<Item = <<Self as OSMReader>::Obj as OSMObj>::Relation> + '_>
where
Self: Sized,
{
Box::new(self.objects().filter_map(|o| o.into_relation()))
}
}
pub struct OSMObjectIterator<'a, R>
where
R: OSMReader + 'a,
{
inner: &'a mut R,
}
impl<'a, R> OSMObjectIterator<'a, R>
where
R: OSMReader,
{
pub fn inner(&self) -> &R {
self.inner
}
}
impl<'a, R> Iterator for OSMObjectIterator<'a, R>
where
R: OSMReader,
{
type Item = R::Obj;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
#[derive(Debug)]
pub enum OSMWriteError {
FormatDoesntSupportHeaders,
AlreadyStarted,
AlreadyClosed,
OPLWrite(::std::io::Error),
XMLWriteXMLError(quick_xml::Error),
XMLWriteIOError(::std::io::Error),
}
impl std::fmt::Display for OSMWriteError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for OSMWriteError {}
pub trait OSMWriter<W: Write> {
fn new(_: W) -> Self;
fn close(&mut self) -> Result<(), OSMWriteError>;
fn is_open(&self) -> bool;
fn write_obj(&mut self, obj: &impl OSMObj) -> Result<(), OSMWriteError>;
fn into_inner(self) -> W;
fn set_header(&mut self, _key_value: (&str, &str)) -> Result<(), OSMWriteError> {
todo!("set_header not done yet")
}
fn from_iter<I: Iterator<Item = impl OSMObj>>(writer: W, iter: I) -> Self
where
Self: Sized,
{
let mut writer = Self::new(writer);
for obj in iter {
writer.write_obj(&obj).unwrap();
}
writer.close().unwrap();
writer
}
}
pub fn version<'a>() -> &'a str {
option_env!("CARGO_PKG_VERSION").unwrap_or("unknown-non-cargo-build")
}
pub fn read_pbf(filename: impl AsRef<Path>) -> Result<pbf::PBFReader<BufReader<File>>> {
pbf::PBFReader::from_filename(filename)
}
pub fn read_xml(
filename: impl AsRef<Path>,
) -> Result<xml::XMLReader<bzip2::read::MultiBzDecoder<std::fs::File>>> {
xml::from_filename_bz2(filename)
}