From ed717aec324f98168b5e9d8824ec0b0f133286a5 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Fri, 13 Sep 2024 15:17:44 +0100 Subject: [PATCH] Start filling out some docs --- graph/src/isochrone.rs | 3 +++ graph/src/lib.rs | 21 +++++++++++++++++++-- graph/src/route.rs | 7 +++++++ graph/src/scrape.rs | 9 ++++++--- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/graph/src/isochrone.rs b/graph/src/isochrone.rs index a258598..abd2ba3 100644 --- a/graph/src/isochrone.rs +++ b/graph/src/isochrone.rs @@ -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, diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 9735778..d3a7251 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -25,14 +25,18 @@ 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, pub intersections: Vec, // 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>, pub router: EnumMap, + /// A polygon covering the study area. pub boundary_polygon: Polygon, pub gtfs: GtfsModel, @@ -40,11 +44,13 @@ pub struct Graph { pub type EdgeLocation = GeomWithData; +/// #[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, @@ -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 { @@ -75,6 +82,7 @@ impl Mode { } } +/// Represents an edge going between exactly two `Intersection`s. #[derive(Serialize, Deserialize)] pub struct Road { pub id: RoadID, @@ -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, - // 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, } +/// An intersection between one or more roads. This might represent a dead-end. #[derive(Serialize, Deserialize)] pub struct Intersection { pub id: IntersectionID, @@ -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 { @@ -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()) @@ -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) } @@ -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, @@ -215,6 +231,7 @@ pub enum GtfsSource { None, } +/// A single step along a route pub enum PathStep { Road { road: RoadID, diff --git a/graph/src/route.rs b/graph/src/route.rs index bcc5367..fb41cbf 100644 --- a/graph/src/route.rs +++ b/graph/src/route.rs @@ -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")] @@ -20,6 +22,7 @@ pub struct Router { path_calc: RefCell>, } +/// A route between two positions. pub struct Route { pub start: Position, pub end: Position, @@ -27,6 +30,8 @@ pub struct Route { } impl Router { + /// Creates a router for a mode. This is slow to calculate, as it builds a + /// contraction hierarchy. pub fn new(roads: &Vec, mode: Mode) -> Self { let mut input_graph = InputGraph::new(); let mut node_map = NodeMap::new(); @@ -92,6 +97,7 @@ impl Router { } } + /// Calculates a route between two positions. pub fn route(&self, graph: &Graph, start: Position, end: Position) -> Result { debug!("route from {start:?} to {end:?}"); if start == end { @@ -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()); diff --git a/graph/src/scrape.rs b/graph/src/scrape.rs index aa52599..29a3e9e 100644 --- a/graph/src/scrape.rs +++ b/graph/src/scrape.rs @@ -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), R: utils::osm2graph::OsmReader>( input_bytes: &[u8], osm_reader: &mut R, @@ -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");