Implement the two public TripEndpoint methods in synthpop, but without needing to drag in SidewalkSpot and DrivingGoal

This commit is contained in:
Dustin Carlino 2022-01-28 12:24:58 +00:00
parent 3f8689b9ea
commit 9845d9cae1
5 changed files with 215 additions and 165 deletions

View File

@ -462,45 +462,15 @@ impl SidewalkSpot {
// Recall sidewalks are bidirectional. // Recall sidewalks are bidirectional.
pub fn start_at_border(i: IntersectionID, map: &Map) -> Option<SidewalkSpot> { pub fn start_at_border(i: IntersectionID, map: &Map) -> Option<SidewalkSpot> {
let lanes = map Some(SidewalkSpot {
.get_i(i) sidewalk_pos: TripEndpoint::start_walking_at_border(i, map)?,
.get_outgoing_lanes(map, PathConstraints::Pedestrian); connection: SidewalkPOI::Border(i),
if !lanes.is_empty() { })
return Some(SidewalkSpot {
sidewalk_pos: Position::start(lanes[0]),
connection: SidewalkPOI::Border(i),
});
}
map.get_i(i)
.get_incoming_lanes(map, PathConstraints::Pedestrian)
.get(0)
.map(|l| SidewalkSpot {
sidewalk_pos: Position::end(*l, map),
connection: SidewalkPOI::Border(i),
})
} }
pub fn end_at_border(i: IntersectionID, map: &Map) -> Option<SidewalkSpot> { pub fn end_at_border(i: IntersectionID, map: &Map) -> Option<SidewalkSpot> {
if let Some(l) = map
.get_i(i)
.get_incoming_lanes(map, PathConstraints::Pedestrian)
.get(0)
{
return Some(SidewalkSpot {
sidewalk_pos: Position::end(*l, map),
connection: SidewalkPOI::Border(i),
});
}
let lanes = map
.get_i(i)
.get_outgoing_lanes(map, PathConstraints::Pedestrian);
if lanes.is_empty() {
return None;
}
Some(SidewalkSpot { Some(SidewalkSpot {
sidewalk_pos: Position::start(lanes[0]), sidewalk_pos: TripEndpoint::end_walking_at_border(i, map)?,
connection: SidewalkPOI::Border(i), connection: SidewalkPOI::Border(i),
}) })
} }

View File

@ -223,7 +223,7 @@ impl TripSpec {
} else { } else {
PathConstraints::Bike PathConstraints::Bike
}; };
let goal = to.driving_goal(constraints, map)?; let goal = driving_goal(to, constraints, map)?;
match from { match from {
TripEndpoint::Bldg(start_bldg) => { TripEndpoint::Bldg(start_bldg) => {
if mode == TripMode::Drive { if mode == TripMode::Drive {
@ -266,12 +266,12 @@ impl TripSpec {
} }
} }
TripMode::Walk => TripSpec::JustWalking { TripMode::Walk => TripSpec::JustWalking {
start: from.start_sidewalk_spot(map)?, start: start_sidewalk_spot(from, map)?,
goal: to.end_sidewalk_spot(map)?, goal: end_sidewalk_spot(to, map)?,
}, },
TripMode::Transit => { TripMode::Transit => {
let start = from.start_sidewalk_spot(map)?; let start = start_sidewalk_spot(from, map)?;
let goal = to.end_sidewalk_spot(map)?; let goal = end_sidewalk_spot(to, map)?;
if let Some((stop1, maybe_stop2, route)) = if let Some((stop1, maybe_stop2, route)) =
map.should_use_transit(start.sidewalk_pos, goal.sidewalk_pos) map.should_use_transit(start.sidewalk_pos, goal.sidewalk_pos)
{ {
@ -291,3 +291,47 @@ impl TripSpec {
}) })
} }
} }
fn start_sidewalk_spot(endpt: TripEndpoint, map: &Map) -> Result<SidewalkSpot> {
match endpt {
TripEndpoint::Bldg(b) => Ok(SidewalkSpot::building(b, map)),
TripEndpoint::Border(i) => SidewalkSpot::start_at_border(i, map)
.ok_or_else(|| anyhow!("can't start walking from {}", i)),
TripEndpoint::SuddenlyAppear(pos) => Ok(SidewalkSpot::suddenly_appear(pos, map)),
}
}
fn end_sidewalk_spot(endpt: TripEndpoint, map: &Map) -> Result<SidewalkSpot> {
match endpt {
TripEndpoint::Bldg(b) => Ok(SidewalkSpot::building(b, map)),
TripEndpoint::Border(i) => {
SidewalkSpot::end_at_border(i, map).ok_or_else(|| anyhow!("can't end walking at {}", i))
}
TripEndpoint::SuddenlyAppear(_) => unreachable!(),
}
}
fn driving_goal(
endpt: TripEndpoint,
constraints: PathConstraints,
map: &Map,
) -> Result<DrivingGoal> {
match endpt {
TripEndpoint::Bldg(b) => Ok(DrivingGoal::ParkNear(b)),
// TODO Duplicates some logic from TripEndpoint::pos
TripEndpoint::Border(i) => map
.get_i(i)
.some_incoming_road(map)
.and_then(|dr| {
let lanes = dr.lanes(constraints, map);
if lanes.is_empty() {
None
} else {
// TODO ideally could use any
Some(DrivingGoal::Border(dr.dst_i(map), lanes[0]))
}
})
.ok_or_else(|| anyhow!("can't end at {} for {:?}", i, constraints)),
TripEndpoint::SuddenlyAppear(_) => unreachable!(),
}
}

View File

@ -1,21 +1,18 @@
use std::collections::{BTreeMap, HashSet, VecDeque}; use std::collections::{BTreeMap, HashSet, VecDeque};
use anyhow::Result;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use rand::{Rng, SeedableRng}; use rand::{Rng, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use abstutil::{prettyprint_usize, Counter, Timer}; use abstutil::{prettyprint_usize, Counter, Timer};
use geom::{Distance, Pt2D, Speed}; use geom::{Distance, Speed};
use map_model::{ use map_model::{BuildingID, Map, OffstreetParking, RoadID};
BuildingID, Map, OffstreetParking, PathConstraints, PathRequest, Position, RoadID,
};
use synthpop::{PersonSpec, Scenario, TripEndpoint, TripMode}; use synthpop::{PersonSpec, Scenario, TripEndpoint, TripMode};
use crate::make::fork_rng; use crate::make::fork_rng;
use crate::{ use crate::{
DrivingGoal, ParkingSpot, SidewalkSpot, Sim, StartTripArgs, TripInfo, Vehicle, VehicleSpec, ParkingSpot, Sim, StartTripArgs, TripInfo, Vehicle, VehicleSpec, VehicleType, BIKE_LENGTH,
VehicleType, BIKE_LENGTH, MAX_CAR_LENGTH, MIN_CAR_LENGTH, MAX_CAR_LENGTH, MIN_CAR_LENGTH,
}; };
impl Sim { impl Sim {
@ -366,111 +363,3 @@ pub fn count_parked_cars_per_bldg(scenario: &Scenario) -> Counter<BuildingID> {
} }
per_bldg per_bldg
} }
/*impl TripEndpoint {
/// Figure out a single PathRequest that goes between two TripEndpoints. Assume a single mode
/// the entire time -- no walking to a car before driving, for instance. The result probably
/// won't be exactly what would happen on a real trip between the endpoints because of this
/// assumption.
pub fn path_req(
from: TripEndpoint,
to: TripEndpoint,
mode: TripMode,
map: &Map,
) -> Option<PathRequest> {
let start = from.pos(mode, true, map)?;
let end = to.pos(mode, false, map)?;
Some(match mode {
TripMode::Walk | TripMode::Transit => PathRequest::walking(start, end),
TripMode::Bike => PathRequest::vehicle(start, end, PathConstraints::Bike),
// Only cars leaving from a building might turn out from the driveway in a special way
TripMode::Drive => {
if matches!(from, TripEndpoint::Bldg(_)) {
PathRequest::leave_from_driveway(start, end, PathConstraints::Car, map)
} else {
PathRequest::vehicle(start, end, PathConstraints::Car)
}
}
})
}
fn start_sidewalk_spot(&self, map: &Map) -> Result<SidewalkSpot> {
match self {
TripEndpoint::Bldg(b) => Ok(SidewalkSpot::building(*b, map)),
TripEndpoint::Border(i) => SidewalkSpot::start_at_border(*i, map)
.ok_or_else(|| anyhow!("can't start walking from {}", i)),
TripEndpoint::SuddenlyAppear(pos) => Ok(SidewalkSpot::suddenly_appear(*pos, map)),
}
}
fn end_sidewalk_spot(&self, map: &Map) -> Result<SidewalkSpot> {
match self {
TripEndpoint::Bldg(b) => Ok(SidewalkSpot::building(*b, map)),
TripEndpoint::Border(i) => SidewalkSpot::end_at_border(*i, map)
.ok_or_else(|| anyhow!("can't end walking at {}", i)),
TripEndpoint::SuddenlyAppear(_) => unreachable!(),
}
}
fn driving_goal(&self, constraints: PathConstraints, map: &Map) -> Result<DrivingGoal> {
match self {
TripEndpoint::Bldg(b) => Ok(DrivingGoal::ParkNear(*b)),
TripEndpoint::Border(i) => map
.get_i(*i)
.some_incoming_road(map)
.and_then(|dr| {
let lanes = dr.lanes(constraints, map);
if lanes.is_empty() {
None
} else {
// TODO ideally could use any
Some(DrivingGoal::Border(dr.dst_i(map), lanes[0]))
}
})
.ok_or_else(|| anyhow!("can't end at {} for {:?}", i, constraints)),
TripEndpoint::SuddenlyAppear(_) => unreachable!(),
}
}
fn pos(self, mode: TripMode, from: bool, map: &Map) -> Option<Position> {
match mode {
TripMode::Walk | TripMode::Transit => (if from {
self.start_sidewalk_spot(map)
} else {
self.end_sidewalk_spot(map)
})
.ok()
.map(|spot| spot.sidewalk_pos),
TripMode::Drive | TripMode::Bike => {
if from {
match self {
// Fall through and use DrivingGoal also to start.
TripEndpoint::Bldg(_) => {}
TripEndpoint::Border(i) => {
return map.get_i(i).some_outgoing_road(map).and_then(|dr| {
dr.lanes(mode.to_constraints(), map)
.get(0)
.map(|l| Position::start(*l))
});
}
TripEndpoint::SuddenlyAppear(pos) => {
return Some(pos);
}
}
}
self.driving_goal(mode.to_constraints(), map)
.ok()
.and_then(|goal| goal.goal_pos(mode.to_constraints(), map))
}
}
}
/// Returns a point representing where this endpoint is.
pub fn pt(&self, map: &Map) -> Pt2D {
match self {
TripEndpoint::Bldg(b) => map.get_b(*b).polygon.center(),
TripEndpoint::Border(i) => map.get_i(*i).polygon.center(),
TripEndpoint::SuddenlyAppear(pos) => pos.pt(map),
}
}
}*/

154
synthpop/src/endpoint.rs Normal file
View File

@ -0,0 +1,154 @@
use geom::Pt2D;
use map_model::{BuildingID, IntersectionID, Map, PathConstraints, PathRequest, Position};
use serde::{Deserialize, Serialize};
use crate::TripMode;
/// Specifies where a trip begins or ends.
#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
pub enum TripEndpoint {
Bldg(BuildingID),
Border(IntersectionID),
/// Used for interactive spawning, tests, etc. For now, only valid as a trip's start.
SuddenlyAppear(Position),
}
impl TripEndpoint {
/// Returns a point representing where this endpoint is.
pub fn pt(self, map: &Map) -> Pt2D {
match self {
TripEndpoint::Bldg(b) => map.get_b(b).polygon.center(),
TripEndpoint::Border(i) => map.get_i(i).polygon.center(),
TripEndpoint::SuddenlyAppear(pos) => pos.pt(map),
}
}
/// Figure out a single PathRequest that goes between two TripEndpoints. Assume a single mode
/// the entire time -- no walking to a car before driving, for instance. The result probably
/// won't be exactly what would happen on a real trip between the endpoints because of this
/// assumption.
pub fn path_req(
from: TripEndpoint,
to: TripEndpoint,
mode: TripMode,
map: &Map,
) -> Option<PathRequest> {
let start = from.pos(mode, true, map)?;
let end = to.pos(mode, false, map)?;
Some(match mode {
TripMode::Walk | TripMode::Transit => PathRequest::walking(start, end),
TripMode::Bike => PathRequest::vehicle(start, end, PathConstraints::Bike),
// Only cars leaving from a building might turn out from the driveway in a special way
TripMode::Drive => {
if matches!(from, TripEndpoint::Bldg(_)) {
PathRequest::leave_from_driveway(start, end, PathConstraints::Car, map)
} else {
PathRequest::vehicle(start, end, PathConstraints::Car)
}
}
})
}
fn pos(self, mode: TripMode, from: bool, map: &Map) -> Option<Position> {
match mode {
TripMode::Walk | TripMode::Transit => self.sidewalk_pos(map, from),
TripMode::Drive | TripMode::Bike => {
let constraints = mode.to_constraints();
if from {
match self {
// Fall through
TripEndpoint::Bldg(_) => {}
TripEndpoint::Border(i) => {
return map.get_i(i).some_outgoing_road(map).and_then(|dr| {
dr.lanes(constraints, map)
.get(0)
.map(|l| Position::start(*l))
});
}
TripEndpoint::SuddenlyAppear(pos) => {
return Some(pos);
}
}
}
match self {
TripEndpoint::Bldg(b) => match constraints {
PathConstraints::Car => {
let driving_lane = map.find_driving_lane_near_building(b);
let sidewalk_pos = map.get_b(b).sidewalk_pos;
if driving_lane.road == sidewalk_pos.lane().road {
Some(sidewalk_pos.equiv_pos(driving_lane, map))
} else {
Some(Position::start(driving_lane))
}
}
PathConstraints::Bike => Some(map.get_b(b).biking_connection(map)?.0),
PathConstraints::Bus
| PathConstraints::Train
| PathConstraints::Pedestrian => {
unreachable!()
}
},
TripEndpoint::Border(i) => {
map.get_i(i).some_incoming_road(map).and_then(|dr| {
let lanes = dr.lanes(constraints, map);
if lanes.is_empty() {
None
} else {
// TODO ideally could use any
Some(Position::end(lanes[0], map))
}
})
}
TripEndpoint::SuddenlyAppear(_) => unreachable!(),
}
}
}
}
fn sidewalk_pos(self, map: &Map, from: bool) -> Option<Position> {
match self {
TripEndpoint::Bldg(b) => Some(map.get_b(b).sidewalk_pos),
TripEndpoint::Border(i) => {
if from {
TripEndpoint::start_walking_at_border(i, map)
} else {
TripEndpoint::end_walking_at_border(i, map)
}
}
TripEndpoint::SuddenlyAppear(pos) => Some(pos),
}
}
// Recall sidewalks are bidirectional.
pub fn start_walking_at_border(i: IntersectionID, map: &Map) -> Option<Position> {
let lanes = map
.get_i(i)
.get_outgoing_lanes(map, PathConstraints::Pedestrian);
if !lanes.is_empty() {
return Some(Position::start(lanes[0]));
}
map.get_i(i)
.get_incoming_lanes(map, PathConstraints::Pedestrian)
.get(0)
.map(|l| Position::end(*l, map))
}
pub fn end_walking_at_border(i: IntersectionID, map: &Map) -> Option<Position> {
if let Some(l) = map
.get_i(i)
.get_incoming_lanes(map, PathConstraints::Pedestrian)
.get(0)
{
return Some(Position::end(*l, map));
}
let lanes = map
.get_i(i)
.get_outgoing_lanes(map, PathConstraints::Pedestrian);
if lanes.is_empty() {
return None;
}
Some(Position::start(lanes[0]))
}
}

View File

@ -6,25 +6,18 @@ extern crate log;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use abstutil::{deserialize_usize, serialize_usize}; use abstutil::{deserialize_usize, serialize_usize};
use map_model::{BuildingID, IntersectionID, PathConstraints, Position}; use map_model::PathConstraints;
pub use self::endpoint::TripEndpoint;
pub use self::external::{ExternalPerson, ExternalTrip, ExternalTripEndpoint, MapBorders}; pub use self::external::{ExternalPerson, ExternalTrip, ExternalTripEndpoint, MapBorders};
pub use self::modifier::ScenarioModifier; pub use self::modifier::ScenarioModifier;
pub use self::scenario::{IndividTrip, PersonSpec, Scenario, TripPurpose}; pub use self::scenario::{IndividTrip, PersonSpec, Scenario, TripPurpose};
mod endpoint;
mod external; mod external;
mod modifier; mod modifier;
mod scenario; mod scenario;
/// Specifies where a trip begins or ends.
#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
pub enum TripEndpoint {
Bldg(BuildingID),
Border(IntersectionID),
/// Used for interactive spawning, tests, etc. For now, only valid as a trip's start.
SuddenlyAppear(Position),
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)] #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)]
pub enum TripMode { pub enum TripMode {
Walk, Walk,