1 use abstutil::{prettyprint_usize, slurp_file, Tags, Timer};
2 use geom::{GPSBounds, LonLat, Pt2D};
3 use map_model::osm::{NodeID, OsmID, RelationID, WayID};
4 use std::collections::BTreeMap;
5 use std::error::Error;
6
7 // References to missing objects are just filtered out.
8 // Per https://wiki.openstreetmap.org/wiki/OSM_XML#Certainties_and_Uncertainties, we assume
9 // elements come in order: nodes, ways, then relations.
10 //
11 // TODO Filter out visible=false
12 // TODO NodeID, WayID, RelationID are nice. Plumb forward through map_model.
13 // TODO Replicate IDs in each object, and change members to just hold a reference to the object
14 // (which is guaranteed to exist).
15
16 pub struct Document {
17 pub gps_bounds: GPSBounds,
18 pub nodes: BTreeMap<NodeID, Node>,
19 pub ways: BTreeMap<WayID, Way>,
20 pub relations: BTreeMap<RelationID, Relation>,
21 }
22
23 pub struct Node {
24 pub pt: Pt2D,
25 pub tags: Tags,
26 }
27
28 pub struct Way {
29 // Duplicates geometry, because it's convenient
30 pub nodes: Vec<NodeID>,
31 pub pts: Vec<Pt2D>,
32 pub tags: Tags,
33 }
34
35 pub struct Relation {
36 pub tags: Tags,
37 // Role, member
38 pub members: Vec<(String, OsmID)>,
39 }
40
read( path: &str, input_gps_bounds: &GPSBounds, timer: &mut Timer, ) -> Result<Document, Box<dyn Error>>41 pub fn read(
42 path: &str,
43 input_gps_bounds: &GPSBounds,
44 timer: &mut Timer,
45 ) -> Result<Document, Box<dyn Error>> {
46 timer.start(format!("read {}", path));
47 let bytes = slurp_file(path)?;
48 let raw_string = std::str::from_utf8(&bytes)?;
49 let tree = roxmltree::Document::parse(raw_string)?;
50 timer.stop(format!("read {}", path));
51
52 let mut doc = Document {
53 gps_bounds: input_gps_bounds.clone(),
54 nodes: BTreeMap::new(),
55 ways: BTreeMap::new(),
56 relations: BTreeMap::new(),
57 };
58
59 timer.start("scrape objects");
60 for obj in tree.descendants() {
61 if !obj.is_element() {
62 continue;
63 }
64 match obj.tag_name().name() {
65 "bounds" => {
66 // If we weren't provided with GPSBounds, use this.
67 if doc.gps_bounds != GPSBounds::new() {
68 continue;
69 }
70 doc.gps_bounds.update(LonLat::new(
71 obj.attribute("minlon").unwrap().parse::<f64>().unwrap(),
72 obj.attribute("minlat").unwrap().parse::<f64>().unwrap(),
73 ));
74 doc.gps_bounds.update(LonLat::new(
75 obj.attribute("maxlon").unwrap().parse::<f64>().unwrap(),
76 obj.attribute("maxlat").unwrap().parse::<f64>().unwrap(),
77 ));
78 }
79 "node" => {
80 if doc.gps_bounds == GPSBounds::new() {
81 timer.warn(
82 "No clipping polygon provided and the .osm is missing a <bounds> element, \
83 so figuring out the bounds manually."
84 .to_string(),
85 );
86 doc.gps_bounds = scrape_bounds(&tree);
87 }
88
89 let id = NodeID(obj.attribute("id").unwrap().parse::<i64>().unwrap());
90 if doc.nodes.contains_key(&id) {
91 return Err(format!("Duplicate {}, your .osm is corrupt", id).into());
92 }
93 let pt = Pt2D::from_gps(
94 LonLat::new(
95 obj.attribute("lon").unwrap().parse::<f64>().unwrap(),
96 obj.attribute("lat").unwrap().parse::<f64>().unwrap(),
97 ),
98 &doc.gps_bounds,
99 );
100 let tags = read_tags(obj);
101 doc.nodes.insert(id, Node { pt, tags });
102 }
103 "way" => {
104 let id = WayID(obj.attribute("id").unwrap().parse::<i64>().unwrap());
105 if doc.ways.contains_key(&id) {
106 return Err(format!("Duplicate {}, your .osm is corrupt", id).into());
107 }
108 let tags = read_tags(obj);
109
110 let mut nodes = Vec::new();
111 let mut pts = Vec::new();
112 for child in obj.children() {
113 if child.tag_name().name() == "nd" {
114 let n = NodeID(child.attribute("ref").unwrap().parse::<i64>().unwrap());
115 // Just skip missing nodes
116 if let Some(ref node) = doc.nodes.get(&n) {
117 nodes.push(n);
118 pts.push(node.pt);
119 }
120 }
121 }
122 if !nodes.is_empty() {
123 doc.ways.insert(id, Way { tags, nodes, pts });
124 }
125 }
126 "relation" => {
127 let id = RelationID(obj.attribute("id").unwrap().parse::<i64>().unwrap());
128 if doc.relations.contains_key(&id) {
129 return Err(format!("Duplicate {}, your .osm is corrupt", id).into());
130 }
131 let tags = read_tags(obj);
132 let mut members = Vec::new();
133 for child in obj.children() {
134 if child.tag_name().name() == "member" {
135 let member = match child.attribute("type").unwrap() {
136 "node" => {
137 let n =
138 NodeID(child.attribute("ref").unwrap().parse::<i64>().unwrap());
139 if !doc.nodes.contains_key(&n) {
140 continue;
141 }
142 OsmID::Node(n)
143 }
144 "way" => {
145 let w =
146 WayID(child.attribute("ref").unwrap().parse::<i64>().unwrap());
147 if !doc.ways.contains_key(&w) {
148 continue;
149 }
150 OsmID::Way(w)
151 }
152 "relation" => {
153 let r = RelationID(
154 child.attribute("ref").unwrap().parse::<i64>().unwrap(),
155 );
156 if !doc.relations.contains_key(&r) {
157 continue;
158 }
159 OsmID::Relation(r)
160 }
161 _ => continue,
162 };
163 members.push((child.attribute("role").unwrap().to_string(), member));
164 }
165 }
166 doc.relations.insert(id, Relation { tags, members });
167 }
168 _ => {}
169 }
170 }
171 timer.stop("scrape objects");
172 timer.note(format!(
173 "Found {} nodes, {} ways, {} relations",
174 prettyprint_usize(doc.nodes.len()),
175 prettyprint_usize(doc.ways.len()),
176 prettyprint_usize(doc.relations.len())
177 ));
178
179 Ok(doc)
180 }
181
read_tags(obj: roxmltree::Node) -> Tags182 fn read_tags(obj: roxmltree::Node) -> Tags {
183 let mut tags = Tags::new(BTreeMap::new());
184 for child in obj.children() {
185 if child.tag_name().name() == "tag" {
186 let key = child.attribute("k").unwrap();
187 // Filter out really useless data
188 if key.starts_with("tiger:") || key.starts_with("old_name:") {
189 continue;
190 }
191 tags.insert(key, child.attribute("v").unwrap());
192 }
193 }
194 tags
195 }
196
scrape_bounds(doc: &roxmltree::Document) -> GPSBounds197 fn scrape_bounds(doc: &roxmltree::Document) -> GPSBounds {
198 let mut b = GPSBounds::new();
199 for obj in doc.descendants() {
200 if obj.is_element() && obj.tag_name().name() == "node" {
201 b.update(LonLat::new(
202 obj.attribute("lon").unwrap().parse::<f64>().unwrap(),
203 obj.attribute("lat").unwrap().parse::<f64>().unwrap(),
204 ));
205 }
206 }
207 b
208 }
209