diff --git a/book/src/dev/api.md b/book/src/dev/api.md index 4e00e158fa..139d3cc1dc 100644 --- a/book/src/dev/api.md +++ b/book/src/dev/api.md @@ -70,6 +70,8 @@ are missing, etc. A summary of the commands available so far: [PermanentMapEdits](https://dabreegster.github.io/abstreet/rustdoc/map_model/struct.PermanentMapEdits.html) in JSON format. This is the same as the files in `data/player/edits/`. The edits will remain as you call `/sim/reset`, but get reset with `/sim/load`. + - **GET /map/get-edit-road-command?id=123**: Returns an object that can be + modified and then added to map edits. ## Working with the map model diff --git a/headless/examples/abst_helpers.py b/headless/examples/abst_helpers.py index 47c22a8ca0..1fd6316f7b 100644 --- a/headless/examples/abst_helpers.py +++ b/headless/examples/abst_helpers.py @@ -3,11 +3,13 @@ import statistics # Returns Results -def run_sim(args, modifiers=[]): +def run_sim(args, modifiers=[], edits=None): requests.post(args.api + '/sim/load', json={ 'load': 'data/system/scenarios/{}/weekday.bin'.format(args.map_name), 'modifiers': modifiers, }) + if edits: + requests.post(args.api + '/map/set-edits', json=edits) requests.get(args.api + '/sim/goto-time', params={'t': '{}:00:00'.format(args.hours)}) raw_trips = requests.get( @@ -49,6 +51,13 @@ class Results: slower.append(after_dt - before_dt) print('{:,} trips faster, average {:.1f}s savings'.format( - len(faster), statistics.mean(faster))) + len(faster), avg(faster))) print('{:,} trips slower, average {:.1f}s loss'.format( - len(slower), statistics.mean(slower))) + len(slower), avg(slower))) + + +def avg(data): + if data: + return statistics.mean(data) + else: + return 0.0 diff --git a/headless/examples/cap_experiment.py b/headless/examples/cap_experiment.py index 82a69c9a43..2ff6d51cab 100755 --- a/headless/examples/cap_experiment.py +++ b/headless/examples/cap_experiment.py @@ -21,13 +21,32 @@ def main(): parser.add_argument('--api', default='http://localhost:1234') parser.add_argument('--map_name', default='montlake') parser.add_argument('--hours', type=int, default=24) + parser.add_argument('--cap_pct', type=int, default=80) args = parser.parse_args() print('Simulating {} hours of data/system/scenarios/{}/weekday.bin'.format(args.hours, args.map_name)) print('') baseline = abst_helpers.run_sim(args) + busiest_road, thruput = find_busiest_road(args) - # Find the road with the most car traffic in any one hour period + # Cap that road + edits = requests.get(args.api + '/map/get-edits').json() + cmd = requests.get(args.api + '/map/get-edit-road-command', + params={'id': busiest_road}).json() + cmd['ChangeRoad']['new']['access_restrictions']['cap_vehicles_per_hour'] = int( + (args.cap_pct / 100.0) * thruput) + edits['commands'].append(cmd) + + # See what happened + print('Rerunning with {}% cap on that road'.format(args.cap_pct)) + print('') + experiment = abst_helpers.run_sim(args, edits=edits) + busiest_road, thruput = find_busiest_road(args) + baseline.compare(experiment) + + +# Find the road with the most car traffic in any one hour period +def find_busiest_road(args): thruput = requests.get( args.api + '/data/get-road-thruput').json()['counts'] max_key = None @@ -39,6 +58,7 @@ def main(): max_value = count print('Busiest road is #{}, with {} cars crossing during hour {}'.format( max_key[0], max_value, max_key[1])) + return (max_key[0], max_value) if __name__ == '__main__': diff --git a/headless/src/main.rs b/headless/src/main.rs index 44fef7cfb2..2f7966f25b 100644 --- a/headless/src/main.rs +++ b/headless/src/main.rs @@ -274,6 +274,12 @@ fn handle_command( apply_edits(map); Ok(format!("loaded edits")) } + "/map/get-edit-road-command" => { + let r = RoadID(params["id"].parse::()?); + Ok(abstutil::to_json( + &map.edit_road_cmd(r, |_| {}).to_perma(map), + )) + } _ => Err("Unknown command".into()), } } diff --git a/map_model/src/edits/perma.rs b/map_model/src/edits/perma.rs index 72e6c7f065..d01aabecd8 100644 --- a/map_model/src/edits/perma.rs +++ b/map_model/src/edits/perma.rs @@ -22,7 +22,7 @@ pub struct PermanentMapEdits { } #[derive(Serialize, Deserialize, Clone)] -pub(crate) enum PermanentEditIntersection { +pub enum PermanentEditIntersection { StopSign { #[serde( serialize_with = "serialize_btreemap", @@ -35,7 +35,7 @@ pub(crate) enum PermanentEditIntersection { } #[derive(Serialize, Deserialize, Clone)] -pub(crate) enum PermanentEditCmd { +pub enum PermanentEditCmd { ChangeRoad { r: OriginalRoad, new: EditRoad, @@ -54,7 +54,7 @@ pub(crate) enum PermanentEditCmd { } impl EditCmd { - pub(crate) fn to_perma(&self, map: &Map) -> PermanentEditCmd { + pub fn to_perma(&self, map: &Map) -> PermanentEditCmd { match self { EditCmd::ChangeRoad { r, new, old } => PermanentEditCmd::ChangeRoad { r: map.get_r(*r).orig_id,