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/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 a258d59a..264c3919 100644 --- a/src/groups.rs +++ b/src/groups.rs @@ -14,52 +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, Node> { + nodes_generic::(g, b) } -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_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), +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) { @@ -68,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(), @@ -78,10 +129,25 @@ 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, + }, + 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>, @@ -90,11 +156,30 @@ pub struct DenseNodes<'a> { cur_id: i64, cur_lat: i64, cur_lon: i64, + #[cfg(feature = "full-metadata")] + denseinfo: DenseInfo<'a>, + phantom: PhantomData, +} + +#[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<'_> { - 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; @@ -116,30 +201,84 @@ impl Iterator for DenseNodes<'_> { tags.insert(k, v); } tags.shrink_to_fit(); - 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")] + 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: 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), + }) + } + _ => None, + } + }; + + 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, + #[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 @@ -150,11 +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")] + make_info(&w.info, self.block), + #[cfg(not(feature = "full-metadata"))] + None, + ) }) } fn size_hint(&self) -> (usize, Option) { @@ -163,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; @@ -196,11 +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")] + make_info(&rel.info, self.block), + #[cfg(not(feature = "full-metadata"))] + None, + ) }) } fn size_hint(&self) -> (usize, Option) { @@ -235,3 +395,75 @@ 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: 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), + }) + } else { + 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 9f095fce..2c1fedd5 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 @@ -273,6 +274,80 @@ pub struct Relation { pub refs: Vec, } +/// 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, +} + +#[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) @@ -308,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>