-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1e29f41
commit 49a4439
Showing
4 changed files
with
194 additions
and
158 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
use std::fmt; | ||
|
||
use anyhow::Result; | ||
use geo::{LineString, Point, Polygon}; | ||
use geojson::{Feature, GeoJson, Geometry}; | ||
use rstar::{primitives::GeomWithData, RTree}; | ||
use serde::Serialize; | ||
use utils::{Mercator, Tags}; | ||
|
||
pub struct Graph { | ||
pub roads: Vec<Road>, | ||
pub intersections: Vec<Intersection>, | ||
// All geometry stored in worldspace, including rtrees | ||
pub mercator: Mercator, | ||
pub closest_intersection: RTree<IntersectionLocation>, | ||
pub boundary_polygon: Polygon, | ||
} | ||
|
||
pub type IntersectionLocation = GeomWithData<[f64; 2], IntersectionID>; | ||
|
||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] | ||
pub struct RoadID(pub usize); | ||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize)] | ||
pub struct IntersectionID(pub usize); | ||
|
||
impl fmt::Display for RoadID { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "Road #{}", self.0) | ||
} | ||
} | ||
|
||
impl fmt::Display for IntersectionID { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "Intersection #{}", self.0) | ||
} | ||
} | ||
|
||
#[derive(Clone, Copy, Debug)] | ||
pub enum Direction { | ||
Forwards, | ||
Backwards, | ||
Both, | ||
None, | ||
} | ||
|
||
#[derive(Clone, Copy)] | ||
pub enum Mode { | ||
Car, | ||
Bicycle, | ||
Foot, | ||
} | ||
|
||
pub struct Road { | ||
pub id: RoadID, | ||
pub src_i: IntersectionID, | ||
pub dst_i: IntersectionID, | ||
pub way: osm_reader::WayID, | ||
pub node1: osm_reader::NodeID, | ||
pub node2: osm_reader::NodeID, | ||
pub linestring: LineString, | ||
pub tags: Tags, | ||
|
||
// A simplified view of who can access a road. All might be None (buses, trains ignored) | ||
// TODO enum map? | ||
pub access_car: Direction, | ||
pub access_bicycle: Direction, | ||
pub access_foot: Direction, | ||
} | ||
|
||
pub struct Intersection { | ||
pub id: IntersectionID, | ||
#[allow(dead_code)] | ||
pub node: osm_reader::NodeID, | ||
pub point: Point, | ||
pub roads: Vec<RoadID>, | ||
} | ||
|
||
impl Graph { | ||
/// Call with bytes of an osm.pbf or osm.xml string | ||
pub fn new(input_bytes: &[u8]) -> Result<Graph> { | ||
// TODO make a method there | ||
crate::scrape::scrape_osm(input_bytes) | ||
} | ||
|
||
/// Returns a GeoJSON string. Just shows the full network | ||
pub fn render(&self) -> Result<String> { | ||
let mut features = Vec::new(); | ||
|
||
for r in &self.roads { | ||
features.push(r.to_gj(&self.mercator)); | ||
} | ||
|
||
let gj = GeoJson::from(features); | ||
let out = serde_json::to_string(&gj)?; | ||
Ok(out) | ||
} | ||
|
||
/// Return a polygon covering the world, minus a hole for the boundary, in WGS84 | ||
pub fn get_inverted_boundary(&self) -> Result<String> { | ||
let (boundary, _) = self.mercator.to_wgs84(&self.boundary_polygon).into_inner(); | ||
let polygon = Polygon::new( | ||
LineString::from(vec![ | ||
(180.0, 90.0), | ||
(-180.0, 90.0), | ||
(-180.0, -90.0), | ||
(180.0, -90.0), | ||
(180.0, 90.0), | ||
]), | ||
vec![boundary], | ||
); | ||
let f = Feature::from(Geometry::from(&polygon)); | ||
let out = serde_json::to_string(&f)?; | ||
Ok(out) | ||
} | ||
|
||
pub fn roads_per_intersection( | ||
&self, | ||
i: IntersectionID, | ||
mode: Mode, | ||
) -> impl Iterator<Item = &Road> { | ||
self.intersections[i.0] | ||
.roads | ||
.iter() | ||
.map(|r| &self.roads[r.0]) | ||
.filter(move |r| r.allows_forwards(mode) || r.allows_backwards(mode)) | ||
} | ||
} | ||
|
||
impl Road { | ||
pub fn allows_forwards(&self, mode: Mode) -> bool { | ||
let dir = match mode { | ||
Mode::Car => self.access_car, | ||
Mode::Bicycle => self.access_bicycle, | ||
Mode::Foot => self.access_foot, | ||
}; | ||
matches!(dir, Direction::Forwards | Direction::Both) | ||
} | ||
|
||
pub fn allows_backwards(&self, mode: Mode) -> bool { | ||
let dir = match mode { | ||
Mode::Car => self.access_car, | ||
Mode::Bicycle => self.access_bicycle, | ||
Mode::Foot => self.access_foot, | ||
}; | ||
matches!(dir, Direction::Backwards | Direction::Both) | ||
} | ||
|
||
pub fn to_gj(&self, mercator: &Mercator) -> Feature { | ||
let mut f = Feature::from(Geometry::from(&mercator.to_wgs84(&self.linestring))); | ||
// TODO Rethink most of this -- it's debug info | ||
f.set_property("id", self.id.0); | ||
f.set_property("way", self.way.to_string()); | ||
f.set_property("node1", self.node1.to_string()); | ||
f.set_property("node2", self.node2.to_string()); | ||
for (k, v) in &self.tags.0 { | ||
f.set_property(k, v.to_string()); | ||
} | ||
f.set_property("access_car", format!("{:?}", self.access_car)); | ||
f.set_property("access_bicycle", format!("{:?}", self.access_bicycle)); | ||
f.set_property("access_foot", format!("{:?}", self.access_foot)); | ||
f | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.