Skip to content

Commit

Permalink
Start filling out some docs
Browse files Browse the repository at this point in the history
  • Loading branch information
dabreegster committed Sep 13, 2024
1 parent 96fe25d commit ed717ae
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 5 deletions.
3 changes: 3 additions & 0 deletions graph/src/isochrone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ use crate::{Graph, IntersectionID, Mode, RoadID};

impl Graph {
// TODO Doesn't account for start/end distance along roads
/// From a list of start intersections, floods out the graph for a mode until `end_time` is
/// reached. Returns the time needed to reach each road within that range. This query is not
/// precise about positions along a road.
pub fn get_costs(
&self,
starts: Vec<IntersectionID>,
Expand Down
21 changes: 19 additions & 2 deletions graph/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,32 @@ pub use self::timer::Timer;
pub use crate::gtfs::GtfsModel;
use crate::gtfs::{StopID, TripID};

/// A study area imported from OpenStreetMap.
#[derive(Serialize, Deserialize)]
pub struct Graph {
pub roads: Vec<Road>,
pub intersections: Vec<Intersection>,
// All geometry stored in worldspace, including rtrees
/// `Graph` stores all geometry in a Mercator projection for the study area. This field helps
/// translation to/from WGS84.
pub mercator: Mercator,
pub closest_road: EnumMap<Mode, RTree<EdgeLocation>>,
pub router: EnumMap<Mode, Router>,
/// A polygon covering the study area.
pub boundary_polygon: Polygon,

pub gtfs: GtfsModel,
}

pub type EdgeLocation = GeomWithData<LineString, RoadID>;

///
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct RoadID(pub usize);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct IntersectionID(pub usize);

/// How can a `Road` be crossed by a particular `Mode`?
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub enum Direction {
Forwards,
Expand All @@ -53,6 +59,7 @@ pub enum Direction {
None,
}

/// The graph structure is expressed for each of these different modes of travel.
// TODO Justify why PublicTransit isn't captured here
#[derive(Clone, Copy, Enum, Debug, Serialize, Deserialize)]
pub enum Mode {
Expand All @@ -75,6 +82,7 @@ impl Mode {
}
}

/// Represents an edge going between exactly two `Intersection`s.
#[derive(Serialize, Deserialize)]
pub struct Road {
pub id: RoadID,
Expand All @@ -89,14 +97,17 @@ pub struct Road {
pub linestring: LineString,

// A simplified view of who can access a road. All might be None (buses, trains ignored)
/// Per mode, what direction is this road traversable?
pub access: EnumMap<Mode, Direction>,

// Meters/second, for cars
/// For cars, the speed limit in meters/second
pub max_speed: f64,

/// The bus stops associated with this road
pub stops: Vec<StopID>,
}

/// An intersection between one or more roads. This might represent a dead-end.
#[derive(Serialize, Deserialize)]
pub struct Intersection {
pub id: IntersectionID,
Expand Down Expand Up @@ -143,6 +154,7 @@ impl Graph {
Ok(out)
}

/// Find the Road going from `i1` to `i2` or vice versa. Panics if neither exists.
pub fn find_edge(&self, i1: IntersectionID, i2: IntersectionID) -> &Road {
// TODO Store lookup table
for r in &self.intersections[i1.0].roads {
Expand All @@ -154,6 +166,8 @@ impl Graph {
panic!("no road from {i1:?} to {i2:?} or vice versa");
}

/// Given a point (in Mercator) and mode, snap to a position along some road that mode can
/// cross.
pub fn snap_to_road(&self, pt: Coord, mode: Mode) -> Position {
let r = self.closest_road[mode]
.nearest_neighbor(&pt.into())
Expand All @@ -175,10 +189,12 @@ impl Graph {
}

impl Road {
/// Can this mode cross this road in the forwards direction?
pub fn allows_forwards(&self, mode: Mode) -> bool {
matches!(self.access[mode], Direction::Forwards | Direction::Both)
}

/// Can this mode cross this road in the backwards direction?
pub fn allows_backwards(&self, mode: Mode) -> bool {
matches!(self.access[mode], Direction::Backwards | Direction::Both)
}
Expand All @@ -201,7 +217,7 @@ impl Road {
}
}

/// A position along a road, along with the closer intersection
/// A position along a road, along with the closest intersection
#[derive(Clone, Debug, Copy, PartialEq)]
pub struct Position {
pub road: RoadID,
Expand All @@ -215,6 +231,7 @@ pub enum GtfsSource {
None,
}

/// A single step along a route
pub enum PathStep {
Road {
road: RoadID,
Expand Down
7 changes: 7 additions & 0 deletions graph/src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use utils::{deserialize_nodemap, LineSplit, NodeMap};
use crate::costs::cost;
use crate::{Graph, IntersectionID, Mode, PathStep, Position, Road};

/// Calculates optimal routes for one mode. This structure uses contraction hierarchies to
/// calculate routes very quickly. They are slower to construct, but fast to query.
#[derive(Serialize, Deserialize)]
pub struct Router {
#[serde(deserialize_with = "deserialize_nodemap")]
Expand All @@ -20,13 +22,16 @@ pub struct Router {
path_calc: RefCell<Option<PathCalculator>>,
}

/// A route between two positions.
pub struct Route {
pub start: Position,
pub end: Position,
pub steps: Vec<PathStep>,
}

impl Router {
/// Creates a router for a mode. This is slow to calculate, as it builds a
/// contraction hierarchy.
pub fn new(roads: &Vec<Road>, mode: Mode) -> Self {
let mut input_graph = InputGraph::new();
let mut node_map = NodeMap::new();
Expand Down Expand Up @@ -92,6 +97,7 @@ impl Router {
}
}

/// Calculates a route between two positions.
pub fn route(&self, graph: &Graph, start: Position, end: Position) -> Result<Route> {
debug!("route from {start:?} to {end:?}");
if start == end {
Expand Down Expand Up @@ -179,6 +185,7 @@ impl Router {
}

impl Route {
/// Renders a route as a linestring (in Mercator), with precise positions at the start and end.
pub fn linestring(&self, graph: &Graph) -> LineString {
let mut pts = Vec::new();
debug!("turning {} steps into linestring", self.steps.len());
Expand Down
9 changes: 6 additions & 3 deletions graph/src/scrape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ use crate::{
};

impl Graph {
/// input_bytes: Bytes of an osm.pbf or osm.xml string
/// osm_reader: To scrape OSM elements
/// modify_roads: Runs before any routing structures are calculated. Use to modify access per mode.
/// Constructs a graph from OpenStreetMap data.
///
/// - `input_bytes`: Bytes of an osm.pbf or osm.xml file
/// - `osm_reader`: A callback for every OSM element read, to extract non-graph data
/// - `modify_roads`: Runs before any routing structures are calculated. Use to modify access per mode.
pub fn new<F: FnOnce(&mut Vec<Road>), R: utils::osm2graph::OsmReader>(
input_bytes: &[u8],
osm_reader: &mut R,
Expand Down Expand Up @@ -113,6 +115,7 @@ impl Graph {
})
}

/// Adds in GTFS data to the current graph. This only makes sense to call once.
pub async fn setup_gtfs(&mut self, source: GtfsSource, timer: &mut Timer) -> Result<()> {
timer.push("setting up GTFS");
timer.step("parse");
Expand Down

0 comments on commit ed717ae

Please sign in to comment.