Import GTFS for São Paulo

This commit is contained in:
Dustin Carlino 2022-04-14 15:18:59 +01:00
parent 9ffab3c976
commit d386948a13
10 changed files with 132 additions and 35 deletions

View File

@ -529,13 +529,15 @@ fn finish_app_setup(
}
}
Mode::SomethingElse => {
let start_time = setup.start_time.unwrap_or(Duration::hours(6));
// Not attempting to keep the primary and secondary simulations synchronized at the
// same time yet. Just handle this one startup case, so we can switch maps without
// constantly flopping day/night mode.
if let Some(ref mut secondary) = app.secondary {
secondary.sim.timed_step(
&secondary.map,
Duration::hours(6),
start_time,
&mut None,
&mut Timer::throwaway(),
);
@ -546,7 +548,7 @@ fn finish_app_setup(
SandboxMode::async_new(
app,
GameplayMode::Freeform(app.primary.map.get_name().clone()),
jump_to_time_upon_startup(Duration::hours(6)),
jump_to_time_upon_startup(start_time),
)
}
Mode::TutorialIntro => sandbox::gameplay::Tutorial::start(ctx, app),

View File

@ -34,7 +34,7 @@ impl PlayScenario {
modifiers: Vec<ScenarioModifier>,
) -> Box<dyn GameplayState> {
URLManager::update_url_free_param(
// For dynamiclly generated scenarios like "random" and "home_to_work", this winds up
// For dynamically generated scenarios like "random" and "home_to_work", this winds up
// making up a filename that doesn't actually exist. But if you pass that in, it winds
// up working, because we call abstio::parse_scenario_path() on the other side.
abstio::path_scenario(app.primary.map.get_name(), name)

View File

@ -50,6 +50,10 @@ pub fn import(map: &mut RawMap) -> Result<()> {
}
// Scrape all shape data. Map from shape_id to points and the sequence number
//
// If this file is missing, one idea is to just draw straight lines between stops. We only use
// the shape currently to pick an entry/exit border, so this could be a half-reasonable
// workaround.
let mut raw_shapes: HashMap<ShapeID, Vec<(Pt2D, usize)>> = HashMap::new();
for rec in csv::Reader::from_reader(File::open(map.name.city.input_path("gtfs/shapes.txt"))?)
.deserialize()
@ -189,6 +193,8 @@ struct Route {
route_id: RouteID,
route_short_name: String,
route_long_name: String,
// Missing from São Paulo
#[serde(default)]
route_desc: String,
route_type: usize,
}

View File

@ -70,6 +70,56 @@
"uncompressed_size_bytes": 5608139,
"compressed_size_bytes": 1056533
},
"data/input/br/sao_paulo/gtfs/agency.txt": {
"checksum": "3dd694b14c7bacfa33d8ad774db99100",
"uncompressed_size_bytes": 155,
"compressed_size_bytes": 139
},
"data/input/br/sao_paulo/gtfs/calendar.txt": {
"checksum": "21622d9185a6a3e5d0775712640f222c",
"uncompressed_size_bytes": 451,
"compressed_size_bytes": 150
},
"data/input/br/sao_paulo/gtfs/fare_attributes.txt": {
"checksum": "9e8ca239a9545d1b75cf87b7af027efc",
"uncompressed_size_bytes": 371,
"compressed_size_bytes": 169
},
"data/input/br/sao_paulo/gtfs/fare_rules.txt": {
"checksum": "04104cc98ef5504e37717a7cab49edf4",
"uncompressed_size_bytes": 206440,
"compressed_size_bytes": 16286
},
"data/input/br/sao_paulo/gtfs/frequencies.txt": {
"checksum": "d92632062fa60f01449c5091fd45398f",
"uncompressed_size_bytes": 1651443,
"compressed_size_bytes": 140539
},
"data/input/br/sao_paulo/gtfs/routes.txt": {
"checksum": "cb359e55a553bc9a462fbcbb08ae5574",
"uncompressed_size_bytes": 111291,
"compressed_size_bytes": 18029
},
"data/input/br/sao_paulo/gtfs/shapes.txt": {
"checksum": "a1f5500e7a79f96776589f4f4fbcd38e",
"uncompressed_size_bytes": 58972562,
"compressed_size_bytes": 15500119
},
"data/input/br/sao_paulo/gtfs/stop_times.txt": {
"checksum": "11ef62b51bb8d23aac88cba96a8def7b",
"uncompressed_size_bytes": 4900105,
"compressed_size_bytes": 958512
},
"data/input/br/sao_paulo/gtfs/stops.txt": {
"checksum": "5191a36f2d3f8ef45b435b380e890921",
"uncompressed_size_bytes": 2133889,
"compressed_size_bytes": 594221
},
"data/input/br/sao_paulo/gtfs/trips.txt": {
"checksum": "f2eb999c99e5ae82a4cfc3f7ed6a0121",
"uncompressed_size_bytes": 130243,
"compressed_size_bytes": 26310
},
"data/input/br/sao_paulo/osm/aricanduva.osm": {
"checksum": "3708fb4be649c4f16d1de7f7c99369b6",
"uncompressed_size_bytes": 128106393,
@ -91,19 +141,19 @@
"compressed_size_bytes": 649718446
},
"data/input/br/sao_paulo/raw_maps/aricanduva.bin": {
"checksum": "a7443e903c5ebb8a9ef7eebbf3a625b1",
"uncompressed_size_bytes": 34483916,
"compressed_size_bytes": 8619214
"checksum": "27dff0b36d49f5fed136e794ad9ed258",
"uncompressed_size_bytes": 34864623,
"compressed_size_bytes": 8850565
},
"data/input/br/sao_paulo/raw_maps/center.bin": {
"checksum": "accfa249216653e27a47d13e1f1fd7e4",
"uncompressed_size_bytes": 9473758,
"compressed_size_bytes": 2496742
"checksum": "1344afc8a37b52cffc81b79c32e99506",
"uncompressed_size_bytes": 10230115,
"compressed_size_bytes": 2926542
},
"data/input/br/sao_paulo/raw_maps/sao_miguel_paulista.bin": {
"checksum": "3fe087174603b33c01b10cca95c516c5",
"uncompressed_size_bytes": 591404,
"compressed_size_bytes": 145386
"checksum": "befa29b3a81ff1e63c2d817044efeff4",
"uncompressed_size_bytes": 798773,
"compressed_size_bytes": 246669
},
"data/input/ca/ca/osm/plateau.osm": {
"checksum": "d41d8cd98f00b204e9800998ecf8427e",
@ -3106,24 +3156,24 @@
"compressed_size_bytes": 9015011
},
"data/system/br/sao_paulo/maps/aricanduva.bin": {
"checksum": "ddd45366409816df6c0c581d14055559",
"uncompressed_size_bytes": 54363220,
"compressed_size_bytes": 20757520
"checksum": "3cda5656c2d6969d2c9fadd6657038ca",
"uncompressed_size_bytes": 54515170,
"compressed_size_bytes": 20816515
},
"data/system/br/sao_paulo/maps/center.bin": {
"checksum": "69b08ea00b97339ddc3d79745dd5cd2d",
"uncompressed_size_bytes": 18742932,
"compressed_size_bytes": 7122663
"checksum": "209300c85a3095771b09647e67f560fa",
"uncompressed_size_bytes": 18882279,
"compressed_size_bytes": 7158757
},
"data/system/br/sao_paulo/maps/sao_miguel_paulista.bin": {
"checksum": "9144373938117fe60b0db641bda5ecf2",
"uncompressed_size_bytes": 958882,
"compressed_size_bytes": 326208
"checksum": "441e7c85870d58d54b415d4a0688e727",
"uncompressed_size_bytes": 986925,
"compressed_size_bytes": 335105
},
"data/system/br/sao_paulo/prebaked_results/sao_miguel_paulista/Full.bin": {
"checksum": "565cc8549ce2b275cab59ccb2189532e",
"uncompressed_size_bytes": 27726150,
"compressed_size_bytes": 9401792
"checksum": "ccea922412a35fa314392b26eb269337",
"uncompressed_size_bytes": 30436191,
"compressed_size_bytes": 9398838
},
"data/system/br/sao_paulo/scenarios/sao_miguel_paulista/Full.bin": {
"checksum": "541fdf1f2ba80b4b6cb88f36b186a707",

View File

@ -77,6 +77,8 @@ pub fn config_for_map(name: &MapName) -> convert_osm::Options {
Some("http://metro.kingcounty.gov/GTFS/google_transit.zip".to_string())
} else if name.city == CityName::new("us", "san_francisco") {
Some("https://gtfs.sfmta.com/transitdata/google_transit.zip".to_string())
} else if name.city == CityName::new("br", "sao_paulo") {
Some("https://github.com/transitland/gtfs-archives-not-hosted-elsewhere/blob/master/sao-paulo-sptrans.zip?raw=true".to_string())
} else {
None
},

View File

@ -23,7 +23,7 @@ pub async fn download(config: &ImporterConfiguration, output: String, url: &str)
println!("- Missing {}, so downloading {}", output, url);
abstio::download_to_file(url, None, tmp).await.unwrap();
if url.ends_with(".zip") {
if url.contains(".zip") {
let unzip_to = if output.ends_with('/') {
output
} else {
@ -34,7 +34,7 @@ pub async fn download(config: &ImporterConfiguration, output: String, url: &str)
println!("- Unzipping into {}", unzip_to);
must_run_cmd(Command::new(&config.unzip).arg(tmp).arg("-d").arg(unzip_to));
fs_err::remove_file(tmp).unwrap();
} else if url.ends_with(".gz") {
} else if url.contains(".gz") {
println!("- Gunzipping");
fs_err::rename(tmp, format!("{}.gz", output)).unwrap();

View File

@ -200,7 +200,16 @@ fn create_route(
&snapper.train_outgoing_borders
};
match borders.closest_pt(exit_pt, border_snap_threshold) {
Some((l, _)) => Some(l),
Some((lane, _)) => {
// Edge case: the last stop is on the same road as the border. We can't lane-change
// suddenly, so match the lane in that case.
let last_stop_lane = map.get_ts(*stops.last().unwrap()).driving_pos.lane();
Some(if lane.road == last_stop_lane.road {
last_stop_lane
} else {
lane
})
}
None => bail!(
"Couldn't find a {:?} border near end {}",
route.route_type,
@ -238,6 +247,15 @@ fn create_route(
req
);
}
if req.start.lane().road == req.end.lane().road
&& req.start.dist_along() > req.end.dist_along()
{
bail!(
"Two consecutive stops are on the same road, but they travel backwards: {}",
req
);
}
if let Err(err) = map.pathfind(req) {
bail!("Created the route, but pathfinding failed: {}", err);
}

View File

@ -158,6 +158,9 @@ impl PathV2 {
// At the simulation layer, we may need to block intermediate lanes to exit a driveway,
// so reflect that cost here. The high cost should only be worth it when the v2 path
// requires that up-front turn from certain lanes.
//
// TODO This is only valid if we were leaving from a driveway! This is making some
// buses warp after making a stop.
let idx_dist = (start_lane_idx - (l.offset as isize)).abs();
let cost = 100 * idx_dist as usize;
let fake_turn = TurnID {

View File

@ -34,14 +34,19 @@ pub(crate) struct Car {
impl Car {
/// Assumes the current head of the path is the thing to cross.
pub fn crossing_state(&self, start_dist: Distance, start_time: Time, map: &Map) -> CarState {
let dist_int = DistanceInterval::new_driving(
start_dist,
if self.router.last_step() {
let end_dist = if self.router.last_step() {
self.router.get_end_dist()
} else {
self.router.head().get_polyline(map).length()
},
};
if end_dist < start_dist {
panic!(
"{} trying to make a crossing_state from {} to {} at {}. Something's very wrong",
self.vehicle.id, start_dist, end_dist, start_time
);
}
let dist_int = DistanceInterval::new_driving(start_dist, end_dist);
self.crossing_state_with_end_dist(dist_int, start_time, map)
}

View File

@ -102,6 +102,8 @@ impl TransitSimState {
});
continue;
}
// TODO Why're we calculating these again? Use bus_route.all_path_requests(), so
// that all the nice checks in the map_model layer are preserved here
let req = PathRequest::vehicle(
stop1.driving_pos,
map.get_ts(bus_route.stops[idx + 1]).driving_pos,
@ -112,6 +114,15 @@ impl TransitSimState {
if path.is_empty() {
panic!("Empty path between stops?! {}", path.get_req());
}
if stop1.driving_pos != path.get_req().start {
panic!(
"{} will warp from {} to {}",
bus_route.long_name,
stop1.driving_pos,
path.get_req().start,
);
}
stops.push(Stop {
id: stop1.id,
driving_pos: stop1.driving_pos,