From bbc1ab02c9fc7294937ffa44427ba2291f37ede8 Mon Sep 17 00:00:00 2001 From: Jocelyn Jaubert Date: Mon, 21 Apr 2025 19:53:54 +0200 Subject: [PATCH 1/3] Add new feature 'full-metadata' to dump all available metadata from pbf file With cfg='full-metadata' enabled, these fields are available in an 'info' field of Node/Way/Relation object: - version - timestamp, as an i64 number, in milliseconds since 1970 epoch - changeset - user id - user name - visible, set to true by default This is greatly inspired from PR #TeXitoi/osmpbfreader-rs#40, with the addition of more fields. --- Cargo.toml | 2 + src/groups.rs | 106 +++++++++++++++++++++++++++++++++++++++++++++++++ src/objects.rs | 28 +++++++++++++ 3 files changed, 136 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 0dae4393..09557d96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,8 @@ edition = "2021" [features] # Enables serde serialization/deserialization for all OpenStreetMap data structures serde = ["dep:serde", "flat_map/serde1", "smartstring/serde"] +# Enables dumping of full metadata from pbf file +full-metadata = [] [dependencies] byteorder = "1.3" diff --git a/src/groups.rs b/src/groups.rs index a258d59a..e70e88b9 100644 --- a/src/groups.rs +++ b/src/groups.rs @@ -60,6 +60,8 @@ impl Iterator for SimpleNodes<'_> { decimicro_lat: make_lat(n.lat(), self.block), decimicro_lon: make_lon(n.lon(), self.block), tags: make_tags(&n.keys, &n.vals, self.block), + #[cfg(feature = "full-metadata")] + info: make_info(&n.info, self.block), }) } fn size_hint(&self) -> (usize, Option) { @@ -78,6 +80,20 @@ pub fn dense_nodes<'a>(group: &'a PrimitiveGroup, block: &'a PrimitiveBlock) -> cur_id: 0, cur_lat: 0, cur_lon: 0, + #[cfg(feature = "full-metadata")] + denseinfo: DenseInfo { + block, + version: dense.denseinfo.version.iter(), + dtimestamp: dense.denseinfo.timestamp.iter(), + dchangeset: dense.denseinfo.changeset.iter(), + duid: dense.denseinfo.uid.iter(), + duser_sid: dense.denseinfo.user_sid.iter(), + visible: dense.denseinfo.visible.iter(), + cur_timestamp: 0, + cur_changeset: 0, + cur_uid: 0, + cur_user_sid: 0, + }, } } @@ -90,6 +106,24 @@ pub struct DenseNodes<'a> { cur_id: i64, cur_lat: i64, cur_lon: i64, + #[cfg(feature = "full-metadata")] + denseinfo: DenseInfo<'a>, +} + +#[cfg(feature = "full-metadata")] +pub struct DenseInfo<'a> { + block: &'a PrimitiveBlock, + version: slice::Iter<'a, i32>, + dtimestamp: slice::Iter<'a, i64>, + dchangeset: slice::Iter<'a, i64>, + duid: slice::Iter<'a, i32>, + duser_sid: slice::Iter<'a, i32>, + visible: slice::Iter<'a, bool>, + // Note that version and visible are not delta-coded + cur_timestamp: i64, + cur_changeset: i64, + cur_uid: i32, + cur_user_sid: i32, } impl Iterator for DenseNodes<'_> { @@ -116,11 +150,55 @@ impl Iterator for DenseNodes<'_> { tags.insert(k, v); } tags.shrink_to_fit(); + + #[cfg(feature = "full-metadata")] + let info = { + match ( + self.denseinfo.version.next(), + self.denseinfo.dtimestamp.next(), + self.denseinfo.dchangeset.next(), + self.denseinfo.duid.next(), + self.denseinfo.duser_sid.next(), + self.denseinfo.visible.next(), + ) { + ( + Some(&version), + Some(&dtimestamp), + Some(&dchangeset), + Some(&duid), + Some(&duser_sid), + visible, + ) => { + self.denseinfo.cur_timestamp += dtimestamp; + self.denseinfo.cur_changeset += dchangeset; + self.denseinfo.cur_uid += duid; + self.denseinfo.cur_user_sid += duser_sid; + + let user = Some(make_string( + self.denseinfo.cur_user_sid as usize, + self.denseinfo.block, + )); + + Some(Info { + version: Some(version), + timestamp: Some(self.denseinfo.cur_timestamp), + changeset: Some(self.denseinfo.cur_changeset), + uid: Some(self.denseinfo.cur_uid), + user, + visible: *visible.unwrap_or(&true), + }) + } + _ => None, + } + }; + Some(Node { id: NodeId(self.cur_id), decimicro_lat: make_lat(self.cur_lat, self.block), decimicro_lon: make_lon(self.cur_lon, self.block), tags, + #[cfg(feature = "full-metadata")] + info: info, }) } } @@ -154,6 +232,8 @@ impl Iterator for Ways<'_> { id: WayId(w.id()), nodes, tags: make_tags(&w.keys, &w.vals, self.block), + #[cfg(feature = "full-metadata")] + info: make_info(&w.info, self.block), } }) } @@ -200,6 +280,8 @@ impl Iterator for Relations<'_> { id: RelationId(rel.id()), refs, tags: make_tags(&rel.keys, &rel.vals, self.block), + #[cfg(feature = "full-metadata")] + info: make_info(&rel.info, self.block), } }) } @@ -235,3 +317,27 @@ fn make_tags(keys: &[u32], vals: &[u32], b: &PrimitiveBlock) -> Tags { tags.shrink_to_fit(); tags } + +#[cfg(feature = "full-metadata")] +use protobuf::MessageField; + +#[cfg(feature = "full-metadata")] +fn make_info(info: &MessageField, b: &PrimitiveBlock) -> Option { + if info.has_timestamp() { + let user = if let Some(user_sid) = info.user_sid { + Some(make_string(user_sid as usize, b)) + } else { + None + }; + Some(Info { + version: info.version, + timestamp: info.timestamp, + changeset: info.changeset, + uid: info.uid, + user, + visible: info.visible.unwrap_or(true), + }) + } else { + None + } +} diff --git a/src/objects.rs b/src/objects.rs index 9f095fce..2b78a162 100644 --- a/src/objects.rs +++ b/src/objects.rs @@ -209,6 +209,9 @@ pub struct Node { pub decimicro_lat: i32, /// The longitude in decimicro degrees (10⁻⁷ degrees). pub decimicro_lon: i32, + #[cfg(feature = "full-metadata")] + /// Additional metadata + pub info: Option, } impl Node { @@ -234,6 +237,9 @@ pub struct Way { pub tags: Tags, /// The ordered list of nodes as id. pub nodes: Vec, + #[cfg(feature = "full-metadata")] + /// Additional metadata + pub info: Option, } impl Way { @@ -271,6 +277,28 @@ pub struct Relation { pub tags: Tags, /// Members of the relation. pub refs: Vec, + #[cfg(feature = "full-metadata")] + /// Additional metadata + pub info: Option, +} + +/// Additional metadata about a Node, Way or Relation. +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Info { + /// The version of the object. + pub version: Option, + /// The timestamp when the object was last modified. + pub timestamp: Option, + /// The changeset id of the last modification. + pub changeset: Option, + /// The user id of the last user who modified this object. + pub uid: Option, + /// The user name of the last user who modified this object. + pub user: Option, + /// Wether the object should be considered a currently valid object. Being false hints to it + /// being a historic version that is not uptodate anymore. Defaults to true. + pub visible: bool, } impl ::std::convert::From for OsmId { From 1ccefd6573f15cc8cbd53f3cbfd510c21474614a Mon Sep 17 00:00:00 2001 From: Jocelyn Jaubert Date: Fri, 25 Apr 2025 22:26:31 +0200 Subject: [PATCH 2/3] Use NonZero in struct Info to lower memory usage --- src/groups.rs | 18 ++++++++++-------- src/objects.rs | 9 +++++---- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/groups.rs b/src/groups.rs index e70e88b9..3a208c0e 100644 --- a/src/groups.rs +++ b/src/groups.rs @@ -14,6 +14,8 @@ use std::borrow::Cow; use std::convert::From; use std::iter::Chain; use std::iter::Map; +#[cfg(feature = "full-metadata")] +use std::num::NonZero; use std::slice; pub_iterator_type! { @@ -180,10 +182,10 @@ impl Iterator for DenseNodes<'_> { )); Some(Info { - version: Some(version), - timestamp: Some(self.denseinfo.cur_timestamp), - changeset: Some(self.denseinfo.cur_changeset), - uid: Some(self.denseinfo.cur_uid), + version: NonZero::new(version), + timestamp: NonZero::new(self.denseinfo.cur_timestamp), + changeset: NonZero::new(self.denseinfo.cur_changeset), + uid: NonZero::new(self.denseinfo.cur_uid), user, visible: *visible.unwrap_or(&true), }) @@ -330,10 +332,10 @@ fn make_info(info: &MessageField, b: &PrimitiveBlock) -> Option None }; Some(Info { - version: info.version, - timestamp: info.timestamp, - changeset: info.changeset, - uid: info.uid, + version: NonZero::new(info.version.unwrap_or(0)), + timestamp: NonZero::new(info.timestamp.unwrap_or(0)), + changeset: NonZero::new(info.changeset.unwrap_or(0)), + uid: NonZero::new(info.uid.unwrap_or(0)), user, visible: info.visible.unwrap_or(true), }) diff --git a/src/objects.rs b/src/objects.rs index 2b78a162..ca7576a2 100644 --- a/src/objects.rs +++ b/src/objects.rs @@ -13,6 +13,7 @@ use serde::{Deserialize, Serialize}; use smartstring::alias::String; use std::iter::FromIterator; +use std::num::NonZero; use std::ops::{Deref, DerefMut}; /// Tags represents the features of the objects. See the @@ -287,13 +288,13 @@ pub struct Relation { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Info { /// The version of the object. - pub version: Option, + pub version: Option>, /// The timestamp when the object was last modified. - pub timestamp: Option, + pub timestamp: Option>, /// The changeset id of the last modification. - pub changeset: Option, + pub changeset: Option>, /// The user id of the last user who modified this object. - pub uid: Option, + pub uid: Option>, /// The user name of the last user who modified this object. pub user: Option, /// Wether the object should be considered a currently valid object. Being false hints to it From 218406220dc80410bfcb6f87cf28834ca812f023 Mon Sep 17 00:00:00 2001 From: Jocelyn Jaubert Date: Thu, 24 Apr 2025 22:03:01 +0200 Subject: [PATCH 3/3] Change how metadata are fetched from pbf file New functions are added to get full metadata: iter_with_metadata() and par_iter_with_metadata(), with same behaviour than iter() and par_iter(). They return ObjInfo objects, which is a node/way/relation with an optional Info field. --- src/blobs.rs | 41 +++++++++ src/blocks.rs | 14 +++ src/groups.rs | 232 +++++++++++++++++++++++++++++++++++++------------ src/objects.rs | 85 ++++++++++++++++-- src/reader.rs | 34 ++++++++ 5 files changed, 343 insertions(+), 63 deletions(-) diff --git a/src/blobs.rs b/src/blobs.rs index 5d6e7fa8..e4c9f78f 100644 --- a/src/blobs.rs +++ b/src/blobs.rs @@ -9,12 +9,16 @@ use std::iter; +#[cfg(feature = "full-metadata")] +use crate::blocks::OsmObjInfos as OsmBlockObjInfos; use crate::blocks::{ self, Nodes as BlockNodes, OsmObjs as OsmBlockObjs, Relations as BlockRelations, Ways as BlockWays, }; use crate::fileformat::Blob; use crate::objects::OsmObj; +#[cfg(feature = "full-metadata")] +use crate::objects::OsmObjInfo; use crate::osmformat::PrimitiveBlock; macro_rules! wrap { @@ -40,6 +44,8 @@ macro_rules! wrap { } wrap!(OsmBlobObjs, OsmBlockObjs => OsmObj); +#[cfg(feature = "full-metadata")] +wrap!(OsmBlobObjInfos, OsmBlockObjInfos => OsmObjInfo); wrap!(OsmBlobRelations, BlockRelations => super::Relation); wrap!(OsmBlobWays, BlockWays => super::Way); wrap!(OsmBlobNodes, BlockNodes => super::Node); @@ -73,6 +79,41 @@ pub fn result_blob_into_iter(result: crate::Result) -> OsmObjs`. +#[cfg(feature = "full-metadata")] +pub struct OsmObjInfos(OsmObjInfosImpl); + +#[allow(clippy::type_complexity)] +#[cfg(feature = "full-metadata")] +enum OsmObjInfosImpl { + OkIter(iter::Map::Item) -> crate::Result<::Item>>), + ErrIter(iter::Once::Item>>), +} + +#[cfg(feature = "full-metadata")] +impl Iterator for OsmObjInfos { + type Item = crate::Result<::Item>; + fn next(&mut self) -> Option { + match self.0 { + OsmObjInfosImpl::OkIter(ref mut iter) => iter.next(), + OsmObjInfosImpl::ErrIter(ref mut iter) => iter.next(), + } + } +} + +/// Transforms a `Result` into a `Iterator>`. +#[cfg(feature = "full-metadata")] +pub fn result_blob_into_iter_with_metadata( + result: crate::Result, +) -> OsmObjInfos { + match result.and_then(|b| crate::reader::primitive_block_from_blob(&b)) { + Ok(block) => OsmObjInfos(OsmObjInfosImpl::OkIter( + OsmBlobObjInfos::new(block, blocks::iter_with_metadata).map(Ok), + )), + Err(e) => OsmObjInfos(OsmObjInfosImpl::ErrIter(iter::once(Err(e)))), + } +} + /// Transforms a `Result` into a `Iterator>`. pub fn result_blob_into_node_iter(result: crate::Result) -> OsmObjs { match result.and_then(|b| crate::reader::primitive_block_from_blob(&b)) { diff --git a/src/blocks.rs b/src/blocks.rs index a1769115..c5b89005 100644 --- a/src/blocks.rs +++ b/src/blocks.rs @@ -8,6 +8,8 @@ //! Iterators of OpenStreetMap objects from a block. use crate::groups; +#[cfg(feature = "full-metadata")] +use crate::objects::OsmObjInfo; use crate::objects::{Node, OsmObj, Relation, Way}; use crate::osmformat::PrimitiveBlock; use pub_iterator_type::pub_iterator_type; @@ -22,6 +24,18 @@ pub fn iter(block: &PrimitiveBlock) -> OsmObjs { OsmObjs(Box::new(block.primitivegroup.iter().flat_map(f))) } +#[cfg(feature = "full-metadata")] +pub_iterator_type! { + #[doc="Iterator on the `OsmObjInfo` of a `PrimitiveBlock`."] + OsmObjInfos['a] = Box + 'a> +} + +#[cfg(feature = "full-metadata")] +pub fn iter_with_metadata(block: &PrimitiveBlock) -> OsmObjInfos { + let f = move |g| groups::iter_with_metadata(g, block); + OsmObjInfos(Box::new(block.primitivegroup.iter().flat_map(f))) +} + pub_iterator_type! { #[doc="Iterator on the `Node` of a `PrimitiveBlock`."] Nodes['a] = Box + 'a> diff --git a/src/groups.rs b/src/groups.rs index 3a208c0e..264c3919 100644 --- a/src/groups.rs +++ b/src/groups.rs @@ -14,56 +14,96 @@ use std::borrow::Cow; use std::convert::From; use std::iter::Chain; use std::iter::Map; +use std::marker::PhantomData; #[cfg(feature = "full-metadata")] use std::num::NonZero; use std::slice; pub_iterator_type! { #[doc="Iterator on the `OsmObj` of a `PrimitiveGroup`."] - OsmObjs['a] = Chain, fn(Node) -> OsmObj>, - Map, fn(Way) -> OsmObj>>, - Map, fn(Relation) -> OsmObj>> + OsmObjs['a] = Chain, fn(Node) -> OsmObj>, + Map, fn(Way) -> OsmObj>>, + Map, fn(Relation) -> OsmObj>> } pub fn iter<'a>(g: &'a PrimitiveGroup, b: &'a PrimitiveBlock) -> OsmObjs<'a> { let iter = nodes(g, b) .map(From::from as fn(Node) -> OsmObj) - .chain(ways(g, b).map(From::from as fn(Way) -> OsmObj)) + .chain(ways_generic::(g, b).map(From::from as fn(Way) -> OsmObj)) .chain(relations(g, b).map(From::from as fn(Relation) -> OsmObj)); OsmObjs(iter) } +#[cfg(feature = "full-metadata")] +pub_iterator_type! { + #[doc="Iterator on the `OsmObjInfo` of a `PrimitiveGroup`."] + OsmObjInfos['a] = Chain, fn(NodeInfo) -> OsmObjInfo>, + Map, fn(WayInfo) -> OsmObjInfo>>, + Map, fn(RelationInfo) -> OsmObjInfo>> +} + +#[cfg(feature = "full-metadata")] +pub fn iter_with_metadata<'a>(g: &'a PrimitiveGroup, b: &'a PrimitiveBlock) -> OsmObjInfos<'a> { + let iter = nodes_generic::(g, b) + .map(From::from as fn(NodeInfo) -> OsmObjInfo) + .chain(ways_generic::(g, b).map(From::from as fn(WayInfo) -> OsmObjInfo)) + .chain( + relations_generic::(g, b) + .map(From::from as fn(RelationInfo) -> OsmObjInfo), + ); + OsmObjInfos(iter) +} + pub_iterator_type! { #[doc="Iterator on the `Node` of a `PrimitiveGroup`."] - Nodes['a] = Chain, DenseNodes<'a>> + Nodes['a, N] = Chain, DenseNodes<'a, N>> where N: NodeT } -pub fn nodes<'a>(g: &'a PrimitiveGroup, b: &'a PrimitiveBlock) -> Nodes<'a> { - Nodes(simple_nodes(g, b).chain(dense_nodes(g, b))) +pub fn nodes<'a>(g: &'a PrimitiveGroup, b: &'a PrimitiveBlock) -> Nodes<'a, Node> { + nodes_generic::(g, b) +} + +pub fn nodes_generic<'a, N: NodeT>(g: &'a PrimitiveGroup, b: &'a PrimitiveBlock) -> Nodes<'a, N> { + Nodes::(simple_nodes_generic::(g, b).chain(dense_nodes_generic::(g, b))) } pub fn simple_nodes<'a>(group: &'a PrimitiveGroup, block: &'a PrimitiveBlock) -> SimpleNodes<'a> { - SimpleNodes { + simple_nodes_generic::(group, block) +} + +pub fn simple_nodes_generic<'a, N: NodeT>( + group: &'a PrimitiveGroup, + block: &'a PrimitiveBlock, +) -> SimpleNodes<'a, N> { + SimpleNodes:: { iter: group.nodes.iter(), block, + phantom: PhantomData, } } -pub struct SimpleNodes<'a> { +pub struct SimpleNodes<'a, N: NodeT = Node> { iter: slice::Iter<'a, osmformat::Node>, block: &'a PrimitiveBlock, + phantom: PhantomData, } -impl Iterator for SimpleNodes<'_> { - type Item = Node; - fn next(&mut self) -> Option { - self.iter.next().map(|n| Node { - id: NodeId(n.id()), - decimicro_lat: make_lat(n.lat(), self.block), - decimicro_lon: make_lon(n.lon(), self.block), - tags: make_tags(&n.keys, &n.vals, self.block), - #[cfg(feature = "full-metadata")] - info: make_info(&n.info, self.block), +impl Iterator for SimpleNodes<'_, N> { + type Item = N; + fn next(&mut self) -> Option { + self.iter.next().map(|n| { + N::get_node( + Node { + id: NodeId(n.id()), + decimicro_lat: make_lat(n.lat(), self.block), + decimicro_lon: make_lon(n.lon(), self.block), + tags: make_tags(&n.keys, &n.vals, self.block), + }, + #[cfg(feature = "full-metadata")] + make_info(&n.info, self.block), + #[cfg(not(feature = "full-metadata"))] + None, + ) }) } fn size_hint(&self) -> (usize, Option) { @@ -72,8 +112,15 @@ impl Iterator for SimpleNodes<'_> { } pub fn dense_nodes<'a>(group: &'a PrimitiveGroup, block: &'a PrimitiveBlock) -> DenseNodes<'a> { + dense_nodes_generic::(group, block) +} + +pub fn dense_nodes_generic<'a, N: NodeT>( + group: &'a PrimitiveGroup, + block: &'a PrimitiveBlock, +) -> DenseNodes<'a, N> { let dense = &group.dense; - DenseNodes { + DenseNodes:: { block, dids: dense.id.iter(), dlats: dense.lat.iter(), @@ -96,10 +143,11 @@ pub fn dense_nodes<'a>(group: &'a PrimitiveGroup, block: &'a PrimitiveBlock) -> cur_uid: 0, cur_user_sid: 0, }, + phantom: PhantomData, } } -pub struct DenseNodes<'a> { +pub struct DenseNodes<'a, N: NodeT = Node> { block: &'a PrimitiveBlock, dids: slice::Iter<'a, i64>, dlats: slice::Iter<'a, i64>, @@ -110,6 +158,7 @@ pub struct DenseNodes<'a> { cur_lon: i64, #[cfg(feature = "full-metadata")] denseinfo: DenseInfo<'a>, + phantom: PhantomData, } #[cfg(feature = "full-metadata")] @@ -128,9 +177,9 @@ pub struct DenseInfo<'a> { cur_user_sid: i32, } -impl Iterator for DenseNodes<'_> { - type Item = Node; - fn next(&mut self) -> Option { +impl Iterator for DenseNodes<'_, N> { + type Item = N; + fn next(&mut self) -> Option { match (self.dids.next(), self.dlats.next(), self.dlons.next()) { (Some(&did), Some(&dlat), Some(&dlon)) => { self.cur_id += did; @@ -194,32 +243,42 @@ impl Iterator for DenseNodes<'_> { } }; - Some(Node { - id: NodeId(self.cur_id), - decimicro_lat: make_lat(self.cur_lat, self.block), - decimicro_lon: make_lon(self.cur_lon, self.block), - tags, + Some(N::get_node( + Node { + id: NodeId(self.cur_id), + decimicro_lat: make_lat(self.cur_lat, self.block), + decimicro_lon: make_lon(self.cur_lon, self.block), + tags, + }, #[cfg(feature = "full-metadata")] - info: info, - }) + info, + #[cfg(not(feature = "full-metadata"))] + None, + )) } } -pub fn ways<'a>(group: &'a PrimitiveGroup, block: &'a PrimitiveBlock) -> Ways<'a> { - Ways { +pub fn ways<'a>(group: &'a PrimitiveGroup, block: &'a PrimitiveBlock) -> Ways<'a, Way> { + ways_generic::(group, block) +} + +fn ways_generic<'a, W: WayT>(group: &'a PrimitiveGroup, block: &'a PrimitiveBlock) -> Ways<'a, W> { + Ways:: { iter: group.ways.iter(), block, + phantom: PhantomData, } } -pub struct Ways<'a> { +pub struct Ways<'a, W: WayT = Way> { iter: slice::Iter<'a, osmformat::Way>, block: &'a PrimitiveBlock, + phantom: PhantomData, } -impl Iterator for Ways<'_> { - type Item = Way; - fn next(&mut self) -> Option { +impl Iterator for Ways<'_, W> { + type Item = W; + fn next(&mut self) -> Option { self.iter.next().map(|w| { let mut n = 0; let nodes = w @@ -230,13 +289,17 @@ impl Iterator for Ways<'_> { NodeId(n) }) .collect(); - Way { - id: WayId(w.id()), - nodes, - tags: make_tags(&w.keys, &w.vals, self.block), + W::get_way( + Way { + id: WayId(w.id()), + nodes, + tags: make_tags(&w.keys, &w.vals, self.block), + }, #[cfg(feature = "full-metadata")] - info: make_info(&w.info, self.block), - } + make_info(&w.info, self.block), + #[cfg(not(feature = "full-metadata"))] + None, + ) }) } fn size_hint(&self) -> (usize, Option) { @@ -245,19 +308,28 @@ impl Iterator for Ways<'_> { } pub fn relations<'a>(group: &'a PrimitiveGroup, block: &'a PrimitiveBlock) -> Relations<'a> { - Relations { + relations_generic::(group, block) +} + +pub fn relations_generic<'a, R: RelationT>( + group: &'a PrimitiveGroup, + block: &'a PrimitiveBlock, +) -> Relations<'a, R> { + Relations:: { iter: group.relations.iter(), block, + phantom: PhantomData, } } -pub struct Relations<'a> { +pub struct Relations<'a, R: RelationT = Relation> { iter: slice::Iter<'a, osmformat::Relation>, block: &'a PrimitiveBlock, + phantom: PhantomData, } -impl Iterator for Relations<'_> { - type Item = Relation; - fn next(&mut self) -> Option { +impl Iterator for Relations<'_, R> { + type Item = R; + fn next(&mut self) -> Option { use osmformat::relation::MemberType::{NODE, RELATION, WAY}; self.iter.next().map(|rel| { let mut m = 0; @@ -278,13 +350,17 @@ impl Iterator for Relations<'_> { }) }) .collect(); - Relation { - id: RelationId(rel.id()), - refs, - tags: make_tags(&rel.keys, &rel.vals, self.block), + R::get_relation( + Relation { + id: RelationId(rel.id()), + refs, + tags: make_tags(&rel.keys, &rel.vals, self.block), + }, #[cfg(feature = "full-metadata")] - info: make_info(&rel.info, self.block), - } + make_info(&rel.info, self.block), + #[cfg(not(feature = "full-metadata"))] + None, + ) }) } fn size_hint(&self) -> (usize, Option) { @@ -343,3 +419,51 @@ fn make_info(info: &MessageField, b: &PrimitiveBlock) -> Option None } } + +pub trait NodeT { + fn get_node(node: Node, info: Option) -> Self; +} +impl NodeT for Node { + fn get_node(node: Node, _info: Option) -> Self { + node + } +} + +#[cfg(feature = "full-metadata")] +impl NodeT for NodeInfo { + fn get_node(node: Node, info: Option) -> Self { + NodeInfo { node, info } + } +} + +pub trait WayT { + fn get_way(way: Way, info: Option) -> Self; +} +impl WayT for Way { + fn get_way(way: Way, _info: Option) -> Self { + way + } +} + +#[cfg(feature = "full-metadata")] +impl WayT for WayInfo { + fn get_way(way: Way, info: Option) -> Self { + WayInfo { way, info } + } +} + +pub trait RelationT { + fn get_relation(relation: Relation, info: Option) -> Self; +} +impl RelationT for Relation { + fn get_relation(relation: Relation, _info: Option) -> Self { + relation + } +} + +#[cfg(feature = "full-metadata")] +impl RelationT for RelationInfo { + fn get_relation(relation: Relation, info: Option) -> Self { + RelationInfo { relation, info } + } +} diff --git a/src/objects.rs b/src/objects.rs index ca7576a2..2c1fedd5 100644 --- a/src/objects.rs +++ b/src/objects.rs @@ -210,9 +210,6 @@ pub struct Node { pub decimicro_lat: i32, /// The longitude in decimicro degrees (10⁻⁷ degrees). pub decimicro_lon: i32, - #[cfg(feature = "full-metadata")] - /// Additional metadata - pub info: Option, } impl Node { @@ -238,9 +235,6 @@ pub struct Way { pub tags: Tags, /// The ordered list of nodes as id. pub nodes: Vec, - #[cfg(feature = "full-metadata")] - /// Additional metadata - pub info: Option, } impl Way { @@ -278,9 +272,6 @@ pub struct Relation { pub tags: Tags, /// Members of the relation. pub refs: Vec, - #[cfg(feature = "full-metadata")] - /// Additional metadata - pub info: Option, } /// Additional metadata about a Node, Way or Relation. @@ -302,6 +293,61 @@ pub struct Info { pub visible: bool, } +#[derive(Debug, PartialEq, PartialOrd, Clone)] +#[cfg(feature = "full-metadata")] +/// Node with Additional metadata +pub struct NodeInfo { + /// Node + pub node: Node, + /// Additional metadata + pub info: Option, +} + +#[derive(Debug, PartialEq, PartialOrd, Clone)] +#[cfg(feature = "full-metadata")] +/// Way with Additional metadata +pub struct WayInfo { + /// Way + pub way: Way, + /// Additional metadata + pub info: Option, +} + +#[derive(Debug, PartialEq, PartialOrd, Clone)] +#[cfg(feature = "full-metadata")] +/// Relation with Additional metadata +pub struct RelationInfo { + /// Relation + pub relation: Relation, + /// Additional metadata + pub info: Option, +} + +/// An OpenStreetMap object with metadata +#[derive(Debug, PartialEq, PartialOrd, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg(feature = "full-metadata")] +pub enum OsmObjInfo { + /// A node + Node(NodeInfo), + /// A way + Way(WayInfo), + /// A relation + Relation(RelationInfo), +} + +#[cfg(feature = "full-metadata")] +impl OsmObjInfo { + /// Returns the tags of the object. + pub fn tags(&self) -> &Tags { + match *self { + OsmObjInfo::Node(ref node) => &node.node.tags, + OsmObjInfo::Way(ref way) => &way.way.tags, + OsmObjInfo::Relation(ref rel) => &rel.relation.tags, + } + } +} + impl ::std::convert::From for OsmId { fn from(n: NodeId) -> Self { OsmId::Node(n) @@ -337,3 +383,24 @@ impl ::std::convert::From for OsmObj { OsmObj::Relation(r) } } + +#[cfg(feature = "full-metadata")] +impl ::std::convert::From for OsmObjInfo { + fn from(n: NodeInfo) -> Self { + OsmObjInfo::Node(n) + } +} + +#[cfg(feature = "full-metadata")] +impl ::std::convert::From for OsmObjInfo { + fn from(w: WayInfo) -> Self { + OsmObjInfo::Way(w) + } +} + +#[cfg(feature = "full-metadata")] +impl ::std::convert::From for OsmObjInfo { + fn from(r: RelationInfo) -> Self { + OsmObjInfo::Relation(r) + } +} diff --git a/src/reader.rs b/src/reader.rs index 93cba57a..2b4e24db 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -70,6 +70,15 @@ impl OsmPbfReader { Iter(self.blobs().flat_map(blobs::result_blob_into_iter)) } + /// Returns an iterator on the OsmObjInfo of the pbf file. + #[cfg(feature = "full-metadata")] + pub fn iter_with_metadata(&mut self) -> IterWithMetadata { + IterWithMetadata( + self.blobs() + .flat_map(blobs::result_blob_into_iter_with_metadata), + ) + } + /// Returns a parallel iterator on the OsmObj of the pbf file. /// /// Several threads decode in parallel the file. The memory and @@ -88,6 +97,15 @@ impl OsmPbfReader { ParIter(self.blobs().par_flat_map(blobs::result_blob_into_iter)) } + /// Returns a parallel iterator on the OsmObjInfo of the pbf file. + #[cfg(feature = "full-metadata")] + pub fn par_iter_with_metadata(&mut self) -> ParIterWithMetadata<'_, R> { + ParIterWithMetadata( + self.blobs() + .par_flat_map(blobs::result_blob_into_iter_with_metadata), + ) + } + /// Returns an iterator on the Node of the pbf file. pub fn iter_nodes(&mut self) -> NodeIter { NodeIter(self.blobs().flat_map(blobs::result_blob_into_node_iter)) @@ -332,6 +350,13 @@ pub_iterator_type! { where R: io::Read + 'a } +#[cfg(feature = "full-metadata")] +pub_iterator_type! { + #[doc="Iterator on the `OsmObjInfo` of the pbf file."] + IterWithMetadata['a, R] = iter::FlatMap, blobs::OsmObjInfos, fn(Result) -> blobs::OsmObjInfos> + where R: io::Read + 'a +} + pub_iterator_type! { #[doc="Parallel iterator on the `OsmObj` of the pbf file."] ParIter['a, R] = par_map::FlatMap, @@ -340,6 +365,15 @@ pub_iterator_type! { where R: io::Read + 'a } +#[cfg(feature = "full-metadata")] +pub_iterator_type! { + #[doc="Parallel iterator on the `OsmObjInfo` of the pbf file."] + ParIterWithMetadata['a, R] = par_map::FlatMap, + blobs::OsmObjInfos, + fn(Result) -> blobs::OsmObjInfos> + where R: io::Read + 'a +} + pub_iterator_type! { #[doc="Iterator on the `OsmObj` of the pbf file."] NodeIter['a, R] = iter::FlatMap, blobs::OsmObjs, fn(Result) -> blobs::OsmObjs>