1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
use std::collections::BTreeMap; use std::error::Error; use serde::{Deserialize, Serialize}; use abstutil::{prettyprint_usize, Timer}; use geom::{GPSBounds, LonLat}; #[derive(Serialize, Deserialize)] pub struct ExtraShapes { pub shapes: Vec<ExtraShape>, } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ExtraShape { pub points: Vec<LonLat>, pub attributes: BTreeMap<String, String>, } pub fn load( path: &str, gps_bounds: &GPSBounds, require_all_pts_in_bounds: bool, timer: &mut Timer, ) -> Result<ExtraShapes, Box<dyn Error>> { timer.start(format!("read {}", path)); let bytes = abstutil::slurp_file(path)?; let raw_string = std::str::from_utf8(&bytes)?; let tree = roxmltree::Document::parse(raw_string)?; timer.stop(format!("read {}", path)); let mut shapes = Vec::new(); let mut skipped_count = 0; let mut kv = BTreeMap::new(); timer.start("scrape objects"); recurse( tree.root(), &mut shapes, &mut skipped_count, &mut kv, gps_bounds, require_all_pts_in_bounds, )?; timer.stop("scrape objects"); timer.note(format!( "Got {} shapes from {} and skipped {} shapes", prettyprint_usize(shapes.len()), path, prettyprint_usize(skipped_count) )); Ok(ExtraShapes { shapes }) } fn recurse( node: roxmltree::Node, shapes: &mut Vec<ExtraShape>, skipped_count: &mut usize, kv: &mut BTreeMap<String, String>, gps_bounds: &GPSBounds, require_all_pts_in_bounds: bool, ) -> Result<(), Box<dyn Error>> { for child in node.children() { recurse( child, shapes, skipped_count, kv, gps_bounds, require_all_pts_in_bounds, )?; } if node.tag_name().name() == "SimpleData" { let key = node.attribute("name").unwrap().to_string(); let value = node .text() .map(|x| x.to_string()) .unwrap_or_else(String::new); kv.insert(key, value); } else if node.tag_name().name() == "coordinates" { let mut any_oob = false; let mut any_ok = false; let mut pts: Vec<LonLat> = Vec::new(); if let Some(txt) = node.text() { for pair in txt.split(' ') { if let Some(pt) = parse_pt(pair) { pts.push(pt); if gps_bounds.contains(pt) { any_ok = true; } else { any_oob = true; } } else { return Err(format!("Malformed coordinates: {}", pair).into()); } } } if any_ok && (!any_oob || !require_all_pts_in_bounds) { let attributes = std::mem::replace(kv, BTreeMap::new()); shapes.push(ExtraShape { points: pts, attributes, }); } else { *skipped_count += 1; } } Ok(()) } fn parse_pt(input: &str) -> Option<LonLat> { let coords: Vec<&str> = input.split(',').collect(); if coords.len() != 2 { return None; } match (coords[0].parse::<f64>(), coords[1].parse::<f64>()) { (Ok(lon), Ok(lat)) => Some(LonLat::new(lon, lat)), _ => None, } }