diff --git a/abstutil/src/cli.rs b/abstutil/src/cli.rs new file mode 100644 index 0000000000..44f29dcdb8 --- /dev/null +++ b/abstutil/src/cli.rs @@ -0,0 +1,63 @@ +use std::collections::{HashMap, HashSet}; + +pub struct CmdArgs { + kv: HashMap, + bits: HashSet, + free: Vec, +} + +impl CmdArgs { + pub fn new() -> CmdArgs { + let mut args = CmdArgs { + kv: HashMap::new(), + bits: HashSet::new(), + free: Vec::new(), + }; + + for arg in std::env::args().skip(1) { + let parts: Vec<&str> = arg.split('=').collect(); + if parts.len() == 1 { + if arg.starts_with("--") { + args.bits.insert(arg[2..].to_string()); + } else { + args.free.push(arg); + } + } else if parts.len() == 2 { + args.kv.insert(parts[0].to_string(), parts[1].to_string()); + } else { + panic!("Weird argument {}", arg); + } + } + + args + } + + pub fn required(&mut self, key: &str) -> String { + if let Some(value) = self.kv.remove(key) { + value + } else { + panic!("Missing required arg {}", key); + } + } + + pub fn optional(&mut self, key: &str) -> Option { + self.kv.remove(key) + } + + pub fn enabled(&mut self, key: &str) -> bool { + self.bits.remove(key) + } + + // TODO Drop? + pub fn done(&mut self) { + if !self.kv.is_empty() { + panic!("Unused arguments: {:?}", self.kv); + } + if !self.bits.is_empty() { + panic!("Unused arguments: {:?}", self.bits); + } + if !self.free.is_empty() { + panic!("Unused free arguments: {:?}", self.free); + } + } +} diff --git a/abstutil/src/lib.rs b/abstutil/src/lib.rs index 1e6e715f3e..e6cadee76d 100644 --- a/abstutil/src/lib.rs +++ b/abstutil/src/lib.rs @@ -1,3 +1,4 @@ +mod cli; mod clone; mod collections; mod error; @@ -6,6 +7,7 @@ mod logs; mod random; mod time; +pub use crate::cli::CmdArgs; pub use crate::clone::Cloneable; pub use crate::collections::{ contains_duplicates, retain_btreemap, wraparound_get, Counter, MultiMap, diff --git a/convert_osm/Cargo.toml b/convert_osm/Cargo.toml index 6efccd2720..3f19efedfb 100644 --- a/convert_osm/Cargo.toml +++ b/convert_osm/Cargo.toml @@ -12,4 +12,3 @@ gtfs = { path = "../gtfs" } kml = { path = "../kml" } osm-xml = "0.6.2" map_model = { path = "../map_model" } -structopt = "0.2.18" diff --git a/convert_osm/src/lib.rs b/convert_osm/src/lib.rs index a1c3606db3..fa959e14c0 100644 --- a/convert_osm/src/lib.rs +++ b/convert_osm/src/lib.rs @@ -8,41 +8,15 @@ use geom::{Distance, FindClosest, Line, PolyLine, Pt2D}; use kml::ExtraShapes; use map_model::{raw_data, LaneID, OffstreetParking, Position, LANE_THICKNESS}; use std::collections::BTreeMap; -use structopt::StructOpt; -#[derive(StructOpt, Debug)] -#[structopt(name = "convert_osm")] pub struct Flags { - /// OSM XML file to read - #[structopt(long = "osm")] pub osm: String, - - /// ExtraShapes file with blockface, produced using the kml crate. Optional. - #[structopt(long = "parking_shapes", default_value = "")] - pub parking_shapes: String, - - /// ExtraShapes file with street signs, produced using the kml crate. Optional. - #[structopt(long = "street_signs", default_value = "")] - pub street_signs: String, - - /// KML file with offstreet parking info. Optional. - #[structopt(long = "offstreet_parking", default_value = "")] - pub offstreet_parking: String, - - /// GTFS directory. Optional. - #[structopt(long = "gtfs", default_value = "")] - pub gtfs: String, - - /// Neighborhood GeoJSON path. Optional. - #[structopt(long = "neighborhoods", default_value = "")] - pub neighborhoods: String, - - /// Osmosis clipping polgon. Optional. - #[structopt(long = "clip", default_value = "")] - pub clip: String, - - /// Output .bin path - #[structopt(long = "output")] + pub parking_shapes: Option, + pub street_signs: Option, + pub offstreet_parking: Option, + pub gtfs: Option, + pub neighborhoods: Option, + pub clip: Option, pub output: String, } @@ -56,24 +30,24 @@ pub fn convert(flags: &Flags, timer: &mut abstutil::Timer) -> raw_data::Map { check_orig_ids(&map); - if !flags.parking_shapes.is_empty() { - use_parking_hints(&mut map, &flags.parking_shapes, timer); + if let Some(ref path) = flags.parking_shapes { + use_parking_hints(&mut map, path, timer); } - if !flags.street_signs.is_empty() { - use_street_signs(&mut map, &flags.street_signs, timer); + if let Some(ref path) = flags.street_signs { + use_street_signs(&mut map, path, timer); } - if !flags.offstreet_parking.is_empty() { - use_offstreet_parking(&mut map, &flags.offstreet_parking, timer); + if let Some(ref path) = flags.offstreet_parking { + use_offstreet_parking(&mut map, path, timer); } - if !flags.gtfs.is_empty() { + if let Some(ref path) = flags.gtfs { timer.start("load GTFS"); - map.bus_routes = gtfs::load(&flags.gtfs).unwrap(); + map.bus_routes = gtfs::load(path).unwrap(); timer.stop("load GTFS"); } - if !flags.neighborhoods.is_empty() { + if let Some(ref path) = flags.neighborhoods { timer.start("convert neighborhood polygons"); - neighborhoods::convert(&flags.neighborhoods, map.name.clone(), &map.gps_bounds); + neighborhoods::convert(path, map.name.clone(), &map.gps_bounds); timer.stop("convert neighborhood polygons"); } diff --git a/convert_osm/src/main.rs b/convert_osm/src/main.rs index b4bf5ccba1..1f3c53ae69 100644 --- a/convert_osm/src/main.rs +++ b/convert_osm/src/main.rs @@ -1,8 +1,20 @@ +use abstutil::CmdArgs; use convert_osm::{convert, Flags}; -use structopt::StructOpt; fn main() { - let flags = Flags::from_args(); + let mut args = CmdArgs::new(); + let flags = Flags { + osm: args.required("--osm"), + parking_shapes: args.optional("--parking_shapes"), + street_signs: args.optional("--street_signs"), + offstreet_parking: args.optional("--offstreet_parking"), + gtfs: args.optional("--gtfs"), + neighborhoods: args.optional("--neighborhoods"), + clip: args.optional("--clip"), + output: args.required("--output"), + }; + args.done(); + let mut timer = abstutil::Timer::new(&format!("generate {}", flags.output)); let map = convert(&flags, &mut timer); println!("writing to {}", flags.output); diff --git a/convert_osm/src/osm.rs b/convert_osm/src/osm.rs index e9f9e659eb..08f0abe6a3 100644 --- a/convert_osm/src/osm.rs +++ b/convert_osm/src/osm.rs @@ -8,7 +8,7 @@ use std::io::{BufRead, BufReader}; pub fn extract_osm( osm_path: &str, - maybe_clip_path: &str, + maybe_clip_path: &Option, timer: &mut Timer, ) -> ( raw_data::Map, @@ -27,15 +27,15 @@ pub fn extract_osm( ); done(timer); - let mut map = if maybe_clip_path.is_empty() { + let mut map = if let Some(ref path) = maybe_clip_path { + read_osmosis_polygon(path) + } else { let mut m = raw_data::Map::blank(abstutil::basename(osm_path)); for node in doc.nodes.values() { m.gps_bounds.update(LonLat::new(node.lon, node.lat)); } m.boundary_polygon = m.gps_bounds.to_bounds().get_rectangle(); m - } else { - read_osmosis_polygon(maybe_clip_path) }; let mut id_to_way: HashMap> = HashMap::new(); diff --git a/tests/src/map_conversion.rs b/tests/src/map_conversion.rs index 3065f9adbb..a6068213c1 100644 --- a/tests/src/map_conversion.rs +++ b/tests/src/map_conversion.rs @@ -7,12 +7,12 @@ pub fn run(t: &mut TestRunner) { t.run_slow("convert_osm_twice", |_| { let flags = convert_osm::Flags { osm: "../data/input/montlake.osm".to_string(), - parking_shapes: "../data/shapes/blockface.bin".to_string(), - street_signs: "../data/shapes/street_signs.bin".to_string(), - offstreet_parking: "../data/input/offstreet_parking.kml".to_string(), - gtfs: "../data/input/google_transit_2018_18_08".to_string(), - neighborhoods: "../data/input/neighborhoods.geojson".to_string(), - clip: abstutil::path_polygon("montlake"), + parking_shapes: Some("../data/shapes/blockface.bin".to_string()), + street_signs: Some("../data/shapes/street_signs.bin".to_string()), + offstreet_parking: Some("../data/input/offstreet_parking.kml".to_string()), + gtfs: Some("../data/input/google_transit_2018_18_08".to_string()), + neighborhoods: Some("../data/input/neighborhoods.geojson".to_string()), + clip: Some(abstutil::path_polygon("montlake")), output: "convert_osm_twice.bin".to_string(), };