Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consolidate tools into a single command-line interface #753

Merged
merged 7 commits into from
Sep 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ jobs:
working-directory: parking_mapper
run: cargo build --release ${features}

- name: Build other tools
run: cargo build --release --bin importer --bin one_step_import --bin geojson_to_osmosis --bin pick_geofabrik --bin clip_osm --bin import_grid2demand
- name: Build the CLI tool
run: cargo build --release --bin cli

- name: Download system data
run: cargo run --release --bin updater -- --minimal
Expand Down
75 changes: 72 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
members = [
"abstio",
"abstutil",
"cli",
"collisions",
"convert_osm",
"fifteen_min",
Expand Down
2 changes: 1 addition & 1 deletion abstutil/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl CmdArgs {
}
}

fn from_args(raw: Vec<String>) -> CmdArgs {
pub fn from_args(raw: Vec<String>) -> CmdArgs {
let mut args = CmdArgs {
kv: HashMap::new(),
bits: HashSet::new(),
Expand Down
24 changes: 24 additions & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "cli"
version = "0.1.0"
edition = "2018"

[dependencies]
aabb-quadtree = "0.1.0"
abstio = { path = "../abstio" }
abstutil = { path = "../abstutil" }
anyhow = "1.0.38"
csv = "1.1.4"
geo = "0.18.0"
geojson = { version = "0.22.0", features = ["geo-types"] }
geom = { path = "../geom" }
importer = { path = "../importer" }
log = "0.4.14"
map_model = { path = "../map_model" }
osmio = "0.4.0"
rand = "0.8.3"
rand_xorshift = "0.3.0"
serde = "1.0.123"
sim = { path = "../sim" }
structopt = "0.3.23"
tokio = { version = "1.1.1", features = ["full"] }
Original file line number Diff line number Diff line change
@@ -1,40 +1,22 @@
//! A tool to modify each person's schedule from an existing scenario in various ways.
//!
//! `--add_return_trips`: For people with only a single trip, add a return trip back home sometime
//! 4-12 hours later.
//! `--add_lunch_trips`: Before a person's final trip back home, insert a round-trip to a nearby
//! cafe or restaurant.
//!
//! These tools aren't very smart about detecting if a scenario already has these extra trips added
//! in; be careful about running this on the correct input. It modifies the given `--input` binary
//! scenario in-place.

#[macro_use]
extern crate log;

use rand::prelude::SliceRandom;
use rand::{Rng, SeedableRng};
use rand_xorshift::XorShiftRng;

use abstutil::{prettyprint_usize, CmdArgs, Timer};
use abstutil::{prettyprint_usize, Timer};
use geom::{Distance, Duration, FindClosest};
use map_model::{AmenityType, BuildingID, Map};
use sim::{IndividTrip, Scenario, TripEndpoint, TripMode, TripPurpose};

fn main() {
let mut args = CmdArgs::new();
let input = args.required("--input");
let should_add_return_trips = args.enabled("--add_return_trips");
let should_add_lunch_trips = args.enabled("--add_lunch_trips");
let rng_seed: u64 = args
.optional_parse("--rng_seed", |s| s.parse())
.unwrap_or(42);
args.done();

pub fn run(
input_scenario: String,
should_add_return_trips: bool,
should_add_lunch_trips: bool,
rng_seed: u64,
) {
let mut rng = XorShiftRng::seed_from_u64(rng_seed);
let mut timer = Timer::new("augment scenario");

let mut scenario: Scenario = abstio::must_read_object(input, &mut timer);
let mut scenario: Scenario = abstio::must_read_object(input_scenario, &mut timer);
let map = Map::load_synchronously(scenario.map_name.path(), &mut timer);

if should_add_return_trips {
Expand Down
12 changes: 1 addition & 11 deletions importer/src/bin/clip_osm.rs → cli/src/clip_osm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,9 @@ use geo::{LineString, Point, Polygon};
use osmio::obj_types::ArcOSMObj;
use osmio::{Node, OSMObj, OSMObjBase, OSMObjectType, OSMReader, OSMWriter, Relation, Way};

use abstutil::CmdArgs;
use geom::LonLat;

/// Clips an .osm.pbf specified by `--pbf` using the Osmosis boundary polygon specified by
/// `--clip`, writing the result as .osm.xml to `--out`. This is a simple Rust port of `osmconvert
/// large_map.osm -B=clipping.poly --complete-ways -o=smaller_map.osm`.
fn main() -> Result<()> {
let mut args = CmdArgs::new();
let pbf_path = args.required("--pbf");
let clip_path = args.required("--clip");
let out_path = args.required("--out");
args.done();

pub fn run(pbf_path: String, clip_path: String, out_path: String) -> Result<()> {
let boundary_pts = LonLat::read_osmosis_polygon(&clip_path)?;
let raw_pts: Vec<(f64, f64)> = boundary_pts
.into_iter()
Expand Down
70 changes: 5 additions & 65 deletions importer/src/bin/generate_houses.rs → cli/src/generate_houses.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,18 @@
//! Procedurally generates houses along empty residential roads of a map. Writes a GeoJSON file
//! with the results if the number of houses is at least `--num_required`. This can be used to
//! autodetect if a map probably already has houses filled out in OSM.

use std::collections::HashSet;

use aabb_quadtree::QuadTree;
use geojson::{Feature, FeatureCollection, GeoJson};
use rand::{Rng, SeedableRng};
use rand_xorshift::XorShiftRng;

use abstutil::{CmdArgs, Timer};
use abstutil::Timer;
use geom::{Distance, Polygon};
use map_model::{osm, Map};

fn main() {
pub fn run(map: String, num_required: usize, rng_seed: u64, output: String) {
let mut timer = Timer::new("generate houses");
let mut args = CmdArgs::new();
let num_required = args.required("--num_required").parse::<usize>().unwrap();
let out = args.required("--out");
let mut rng = XorShiftRng::seed_from_u64(args.required("--rng_seed").parse::<u64>().unwrap());
let map = if let Some(path) = args.optional("--map") {
Map::load_synchronously(path, &mut timer)
} else {
import_map(
args.required("--osm"),
args.optional("--clip"),
!args.enabled("--drive_on_left"),
&mut timer,
)
};
args.done();
let mut rng = XorShiftRng::seed_from_u64(rng_seed);
let map = Map::load_synchronously(map, &mut timer);

let houses = generate_buildings_on_empty_residential_roads(&map, &mut rng, &mut timer);
if houses.len() <= num_required {
Expand All @@ -55,7 +38,7 @@ fn main() {
features,
foreign_members: None,
});
abstio::write_json(out, &geojson);
abstio::write_json(output, &geojson);
}

fn generate_buildings_on_empty_residential_roads(
Expand Down Expand Up @@ -175,46 +158,3 @@ fn rand_dist(rng: &mut XorShiftRng, low: f64, high: f64) -> Distance {
assert!(high > low);
Distance::meters(rng.gen_range(low..high))
}

fn import_map(
osm_input: String,
clip: Option<String>,
drive_on_right: bool,
timer: &mut Timer,
) -> Map {
let raw = convert_osm::convert(
convert_osm::Options {
osm_input,
name: abstio::MapName::new("zz", "oneshot", "procgen"),

clip,
map_config: map_model::MapConfig {
driving_side: if drive_on_right {
map_model::DrivingSide::Right
} else {
map_model::DrivingSide::Left
},
bikes_can_use_bus_lanes: true,
inferred_sidewalks: true,
street_parking_spot_length: Distance::meters(8.0),
},

onstreet_parking: convert_osm::OnstreetParking::JustOSM,
public_offstreet_parking: convert_osm::PublicOffstreetParking::None,
private_offstreet_parking: convert_osm::PrivateOffstreetParking::FixedPerBldg(1),
include_railroads: true,
extra_buildings: None,
skip_local_roads: false,
},
timer,
);
map_model::Map::create_from_raw(
raw,
map_model::RawToMapOptions {
build_ch: false,
consolidate_all_intersections: false,
keep_bldg_tags: false,
},
timer,
)
}
16 changes: 16 additions & 0 deletions cli/src/geojson_to_osmosis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use anyhow::Result;

use geom::LonLat;

pub fn run(path: String) -> Result<()> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed to take a file as a path, instead of read from STDIN. Much easier to use this as a library call from one_step_import.rs.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small nit/reminder: Path/PathBuffer are nice and should probably be preferred for this kind of thing... though maybe this method has sufficient interop to make that an undesirably cascading change.

let buffer = std::fs::read_to_string(path)?;
for (idx, points) in LonLat::parse_geojson_polygons(buffer)?
.into_iter()
.enumerate()
{
let path = format!("boundary{}.poly", idx);
LonLat::write_osmosis_polygon(&path, &points)?;
println!("Wrote {}", path);
}
Ok(())
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
use anyhow::{anyhow, bail, Result};
use serde::Deserialize;

use abstutil::{prettyprint_usize, CmdArgs, Timer};
use abstutil::{prettyprint_usize, Timer};
use geom::{Duration, LonLat, Time};
use map_model::Map;
use sim::{ExternalPerson, ExternalTrip, ExternalTripEndpoint, Scenario, TripMode, TripPurpose};

/// Import a scenario from https:/asu-trans-ai-lab/grid2demand.
fn main() -> Result<()> {
let mut args = CmdArgs::new();
let csv_path = args.required("--input");
let map = args.required("--map");
args.done();

pub fn run(csv_path: String, map: String) -> Result<()> {
let mut timer = Timer::new("import grid2demand");
timer.start("parse CSV");
let people = parse_trips(csv_path)?;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
use serde::Deserialize;

use abstutil::{prettyprint_usize, CmdArgs, Timer};
use abstutil::{prettyprint_usize, Timer};
use map_model::Map;
use sim::{ExternalPerson, Scenario};

fn main() {
let mut args = CmdArgs::new();
let map = args.required("--map");
let input = args.required("--input");
let skip_problems = args.enabled("--skip_problems");
args.done();

pub fn run(input: String, map: String, skip_problems: bool) {
let mut timer = Timer::new("import traffic demand data");
let map = Map::load_synchronously(map, &mut timer);
let input: Input = abstio::read_json(input, &mut timer);
Expand Down
Loading