1use 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
18impl 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 fn analyze_way_tags(&self, tags: &Tags) -> Result<Option<Self::WayProperties>, Error>;
35
36 fn way_edge_properties(
38 &self,
39 edge: H3DirectedEdge,
40 way_properties: &Self::WayProperties,
41 ) -> Result<EdgeProperties<T>, Error>;
42}
43
44pub 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}