Skip to content

Commit

Permalink
Upfront filter out roads with no access for any profile. #17
Browse files Browse the repository at this point in the history
  • Loading branch information
dabreegster committed Oct 25, 2024
1 parent a61f9d1 commit 17da1bb
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 22 deletions.
36 changes: 23 additions & 13 deletions graph/src/muv_profiles.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,57 @@
use std::time::Duration;

use geo::{EuclideanLength, LineString};
use muv_osm::{AccessLevel, TMode};
use utils::Tags;

use crate::{Direction, Road};
use crate::Direction;

// TODO Separate profiles like this will repeat work parsing!

pub fn muv_car_profile() -> (String, Box<dyn Fn(&Road) -> (Direction, Duration)>) {
pub fn muv_car_profile() -> (
String,
Box<dyn Fn(&Tags, &LineString) -> (Direction, Duration)>,
) {
(
"car".to_string(),
Box::new(|road| {
let access = calculate_access(&road.osm_tags, TMode::Motorcar);
Box::new(|tags, linestring| {
let access = calculate_access(tags, TMode::Motorcar);
let cost =
Duration::from_secs_f64(road.length_meters / calculate_max_speed(&road.osm_tags));
Duration::from_secs_f64(linestring.euclidean_length() / calculate_max_speed(tags));
(access, cost)
}),
)
}

pub fn muv_bicycle_profile() -> (String, Box<dyn Fn(&Road) -> (Direction, Duration)>) {
pub fn muv_bicycle_profile() -> (
String,
Box<dyn Fn(&Tags, &LineString) -> (Direction, Duration)>,
) {
(
"bicycle".to_string(),
Box::new(|road| {
let access = calculate_access(&road.osm_tags, TMode::Bicycle);
Box::new(|tags, linestring| {
let access = calculate_access(tags, TMode::Bicycle);
// TODO Use elevation and other more detailed things
// 10 mph
let max_bicycle_speed = 4.4704;
let cost = Duration::from_secs_f64(road.length_meters / max_bicycle_speed);
let cost = Duration::from_secs_f64(linestring.euclidean_length() / max_bicycle_speed);
(access, cost)
}),
)
}

pub fn muv_pedestrian_profile() -> (String, Box<dyn Fn(&Road) -> (Direction, Duration)>) {
pub fn muv_pedestrian_profile() -> (
String,
Box<dyn Fn(&Tags, &LineString) -> (Direction, Duration)>,
) {
(
"foot".to_string(),
Box::new(|road| {
let access = calculate_access(&road.osm_tags, TMode::Foot);
Box::new(|tags, linestring| {
let access = calculate_access(tags, TMode::Foot);
// TODO Use elevation and other more detailed things
// 3 mph
let max_foot_speed = 1.34112;
let cost = Duration::from_secs_f64(road.length_meters / max_foot_speed);
let cost = Duration::from_secs_f64(linestring.euclidean_length() / max_foot_speed);
(access, cost)
}),
)
Expand Down
31 changes: 22 additions & 9 deletions graph/src/scrape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use std::collections::BTreeMap;
use std::time::Duration;

use anyhow::Result;
use geo::EuclideanLength;
use geo::{EuclideanLength, LineString};
use utils::Tags;

use crate::gtfs::GtfsModel;
use crate::route::Router;
Expand All @@ -13,23 +14,34 @@ impl Graph {
///
/// - `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
/// - `profiles`: A list of named profiles. Each one assigns an access direction and cost to
/// each Road.
/// - `profiles`: A list of named profiles. Each one assigns an access direction and cost,
/// given OSM tags and a Euclidean center-line. If every profile assigns `Direction::None`,
/// then the Road is completely excluded from the graph.
pub fn new<R: utils::osm2graph::OsmReader>(
input_bytes: &[u8],
osm_reader: &mut R,
profiles: Vec<(String, Box<dyn Fn(&Road) -> (Direction, Duration)>)>,
profiles: Vec<(
String,
Box<dyn Fn(&Tags, &LineString) -> (Direction, Duration)>,
)>,
timer: &mut Timer,
) -> Result<Graph> {
timer.step("parse OSM and split graph");

let graph = utils::osm2graph::Graph::new(
input_bytes,
// Don't do any filtering by profile yet
// TODO Actually, see if any profile accepts it. But can we avoid calling the profiles
// twice?
|tags| {
tags.has("highway") && !tags.is("highway", "proposed") && !tags.is("area", "yes")
if !tags.has("highway") || tags.is("highway", "proposed") || tags.is("area", "yes")
{
return false;
}
// Make sure at least one profile allows access
// TODO It's weird to pass in an empty linestring
// TODO It's inefficient to call the profiles twice
let empty = LineString::new(Vec::new());
profiles
.iter()
.any(|(_, profile)| profile(tags, &empty).0 != Direction::None)
},
osm_reader,
)?;
Expand Down Expand Up @@ -73,7 +85,7 @@ impl Graph {
let mut access = Vec::new();
let mut cost = Vec::new();
for (_, profile) in &profiles {
let (dir, c) = profile(road);
let (dir, c) = profile(&road.osm_tags, &road.linestring);
access.push(dir);
cost.push(c);
}
Expand All @@ -90,6 +102,7 @@ impl Graph {

profile_names.insert(name, ProfileID(idx));
}
timer.pop();

Ok(Graph {
roads,
Expand Down

0 comments on commit 17da1bb

Please sign in to comment.