diff --git a/book/src/trafficsim/travel_demand.md b/book/src/trafficsim/travel_demand.md index 8ebfde75fa..9ded47c0d0 100644 --- a/book/src/trafficsim/travel_demand.md +++ b/book/src/trafficsim/travel_demand.md @@ -130,3 +130,4 @@ modify the mode for some people (change 50% of all driving trips between 7 and - - - +- diff --git a/collisions/src/lib.rs b/collisions/src/lib.rs index f36bf6a07e..765ed57697 100644 --- a/collisions/src/lib.rs +++ b/collisions/src/lib.rs @@ -78,3 +78,63 @@ pub fn import_stats19(input: ExtraShapes, source_url: &str) -> CollisionDataset } data } + +/// Import data from Seattle GeoData +/// (https://data-seattlecitygis.opendata.arcgis.com/datasets/5b5c745e0f1f48e7a53acec63a0022ab_0). +/// Any parsing errors will skip the row and log a warning. +pub fn import_seattle(input: ExtraShapes, source_url: &str) -> CollisionDataset { + let mut data = CollisionDataset { + source_url: source_url.to_string(), + collisions: Vec::new(), + }; + for shape in input.shapes { + if shape.points.len() != 1 { + warn!("One row had >1 point: {:?}", shape); + continue; + } + let time = match parse_incdttm(&shape.attributes["INCDTTM"]) { + Some(time) => time, + None => { + warn!("Couldn't parse time {}", shape.attributes["INCDTTM"]); + continue; + } + }; + let severity = match shape + .attributes + .get("SEVERITYCODE") + .cloned() + .unwrap_or_else(String::new) + .as_ref() + { + "1" | "0" => Severity::Slight, + "2b" | "2" => Severity::Serious, + "3" => Severity::Fatal, + x => { + warn!("Unknown severity {}", x); + continue; + } + }; + data.collisions.push(Collision { + location: shape.points[0], + time, + severity, + }); + } + data +} + +// INCDTTM is something like "11/12/2019 7:30:00 AM" +fn parse_incdttm(x: &str) -> Option { + let parts = x.split(" ").collect::>(); + if parts.len() != 3 { + return None; + } + let time = Duration::parse(parts[1]).ok()?; + if parts[2] == "AM" { + Some(time) + } else if parts[2] == "PM" { + Some(time + Duration::hours(12)) + } else { + None + } +} diff --git a/data/MANIFEST.json b/data/MANIFEST.json index a79b2b00da..9838fb801c 100644 --- a/data/MANIFEST.json +++ b/data/MANIFEST.json @@ -128,6 +128,14 @@ "checksum": "350bd9e59bf2af4e885a7c0741e6ee6b", "size_bytes": 102846513 }, + "data/input/seattle/collisions.bin": { + "checksum": "5c403fc190bc43e028a34d6d54a5d6bf", + "size_bytes": 4457539 + }, + "data/input/seattle/collisions.kml": { + "checksum": "16e26b4a07d2918780e09c95202ba9ac", + "size_bytes": 433758033 + }, "data/input/seattle/footways.bin": { "checksum": "09c6aca2ad37d007c6bd40b91886ed95", "size_bytes": 5536891 diff --git a/importer/src/seattle.rs b/importer/src/seattle.rs index 94db9f0167..d8567abff9 100644 --- a/importer/src/seattle.rs +++ b/importer/src/seattle.rs @@ -54,6 +54,23 @@ fn input(config: &ImporterConfiguration, timer: &mut abstutil::Timer) { "input/seattle/google_transit/", "http://metro.kingcounty.gov/gtfs/google_transit.zip", ); + + // From + // https://data-seattlecitygis.opendata.arcgis.com/datasets/5b5c745e0f1f48e7a53acec63a0022ab_0 + download( + config, + "input/seattle/collisions.kml", + "https://opendata.arcgis.com/datasets/5b5c745e0f1f48e7a53acec63a0022ab_0.kml", + ); + + // This is a little expensive, so delete data/input/seattle/collisions.bin to regenerate this. + if !abstutil::file_exists("data/input/seattle/collisions.bin") { + let shapes = kml::load("data/input/seattle/collisions.kml", &bounds, true, timer).unwrap(); + let collisions = collisions::import_seattle( + shapes, + "https://data-seattlecitygis.opendata.arcgis.com/datasets/5b5c745e0f1f48e7a53acec63a0022ab_0"); + abstutil::write_binary("data/input/seattle/collisions.bin".to_string(), &collisions); + } } pub fn osm_to_raw(name: &str, timer: &mut abstutil::Timer, config: &ImporterConfiguration) {