h3ron_graph/io/
osm.rs

1//! Support for OpenStreetMap data formats
2
3use std::io::BufReader;
4use std::ops::Add;
5use std::path::Path;
6
7use geo_types::{Coord, LineString};
8pub use osmpbfreader;
9use osmpbfreader::{OsmPbfReader, Tags};
10
11use h3ron::collections::HashMap;
12use h3ron::iter::continuous_cells_to_edges;
13use h3ron::H3DirectedEdge;
14
15use crate::error::Error;
16use crate::graph::{H3EdgeGraph, H3EdgeGraphBuilder};
17
18/// hide errors in the io error to avoid having osmpbfreader in the public api.
19impl From<osmpbfreader::Error> for Error {
20    fn from(g_err: osmpbfreader::Error) -> Self {
21        Self::IOError(std::io::Error::new(std::io::ErrorKind::Other, g_err))
22    }
23}
24
25pub struct EdgeProperties<T> {
26    pub is_bidirectional: bool,
27    pub weight: T,
28}
29
30pub trait WayAnalyzer<T> {
31    type WayProperties;
32
33    /// analyze the tags of an Way and return `Some` when this way should be used
34    fn analyze_way_tags(&self, tags: &Tags) -> Result<Option<Self::WayProperties>, Error>;
35
36    /// return the weight for a single `H3Edge`
37    fn way_edge_properties(
38        &self,
39        edge: H3DirectedEdge,
40        way_properties: &Self::WayProperties,
41    ) -> Result<EdgeProperties<T>, Error>;
42}
43
44/// Builds [`H3EdgeGraph`] instances from .osm.pbf files.
45pub struct OsmPbfH3EdgeGraphBuilder<
46    T: PartialOrd + PartialEq + Add + Copy + Sync + Send,
47    WA: WayAnalyzer<T>,
48> {
49    h3_resolution: u8,
50    way_analyzer: WA,
51    graph: H3EdgeGraph<T>,
52}
53
54impl<T, WA> OsmPbfH3EdgeGraphBuilder<T, WA>
55where
56    T: PartialOrd + PartialEq + Add + Copy + Send + Sync,
57    WA: WayAnalyzer<T>,
58{
59    pub fn new(h3_resolution: u8, way_analyzer: WA) -> Self {
60        Self {
61            h3_resolution,
62            way_analyzer,
63            graph: H3EdgeGraph::new(h3_resolution),
64        }
65    }
66
67    pub fn read_pbf(&mut self, pbf_path: &Path) -> Result<(), Error> {
68        let pbf_file = BufReader::new(std::fs::File::open(pbf_path)?);
69        let mut pbf = OsmPbfReader::new(pbf_file);
70        let mut nodeid_coordinates: HashMap<_, _> = Default::default();
71        for obj_result in pbf.iter() {
72            let obj = obj_result?;
73            match obj {
74                osmpbfreader::OsmObj::Node(node) => {
75                    let coordinate = Coord {
76                        x: node.lon(),
77                        y: node.lat(),
78                    };
79                    nodeid_coordinates.insert(node.id, coordinate);
80                }
81                osmpbfreader::OsmObj::Way(way) => {
82                    if let Some(way_props) = self.way_analyzer.analyze_way_tags(&way.tags)? {
83                        let coordinates: Vec<_> = way
84                            .nodes
85                            .iter()
86                            .filter_map(|node_id| nodeid_coordinates.get(node_id).copied())
87                            .collect();
88                        if coordinates.len() >= 2 {
89                            let h3indexes: Vec<_> =
90                                h3ron::line(&LineString::from(coordinates), self.h3_resolution)?
91                                    .into();
92
93                            for edge_result in continuous_cells_to_edges(h3indexes) {
94                                let edge = edge_result?;
95                                let edge_props =
96                                    self.way_analyzer.way_edge_properties(edge, &way_props)?;
97
98                                self.graph.add_edge(edge, edge_props.weight)?;
99                                if edge_props.is_bidirectional {
100                                    self.graph.add_edge(edge.reversed()?, edge_props.weight)?;
101                                }
102                            }
103                        }
104                    }
105                }
106                osmpbfreader::OsmObj::Relation(_) => {}
107            }
108        }
109        Ok(())
110    }
111}
112
113impl<T, WA> H3EdgeGraphBuilder<T> for OsmPbfH3EdgeGraphBuilder<T, WA>
114where
115    T: PartialOrd + PartialEq + Add + Copy + Send + Sync,
116    WA: WayAnalyzer<T>,
117{
118    fn build_graph(self) -> std::result::Result<H3EdgeGraph<T>, Error> {
119        Ok(self.graph)
120    }
121}