Skip to content

Commit

Permalink
Simplify Distance implementation, and get rid of AvoidMainRoads. Move
Browse files Browse the repository at this point in the history
some filtering logic into the LTS definition.
  • Loading branch information
dabreegster committed Nov 6, 2023
1 parent 8717d73 commit 1533e57
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 100 deletions.
9 changes: 8 additions & 1 deletion examples/seattle/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@
"origins_path": "",
"destinations_path": ""
},
"cost": "AvoidMainRoads",
"cost": {
"ByLTS": {
"lts1": 1.0,
"lts2": 1.3,
"lts3": 2.0,
"lts4": 3.0
}
},
"uptake": "Identity",
"lts": "BikeOttawa"
}
43 changes: 43 additions & 0 deletions lts/src/allowed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use crate::Tags;

pub fn is_cycling_allowed(tags: &Tags, msgs: &mut Vec<String>) -> bool {
if !tags.has("highway") && !tags.has("bicycle") {
msgs.push("Way doesn't have a highway or bicycle tag".into());
return false;
}

if tags.is("bicycle", "no") {
msgs.push("Cycling not permitted due to bicycle=no".into());
return false;
}

if tags.is("access", "no") {
// TODO There are exceptions for bicycle
msgs.push("Cycling not permitted due to access=no".into());
return false;
}

if tags.is_any(
"highway",
vec!["motorway", "motorway_link", "proposed", "construction"],
) {
msgs.push(format!(
"Cycling not permitted due to highway={}",
tags.get("highway").unwrap()
));
return false;
}

if tags.is_any("highway", vec!["footway", "path"])
&& tags.is("footway", "sidewalk")
&& !tags.is("bicycle", "yes")
{
msgs.push(format!(
"Cycling not permitted on highway={}, when footway=sidewalk and bicycle=yes is missing",
tags.get("highway").unwrap()
));
return false;
}

true
}
46 changes: 2 additions & 44 deletions lts/src/bike_ottawa.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{parse, Tags, LTS};
use crate::{is_cycling_allowed, parse, Tags, LTS};

// The below is adapted from https://raw.githubusercontent.com/BikeOttawa/stressmodel/master/stressmodel.js, MIT licensed
// TODO Ask about differences: maxspeed parsing, highway=construction
Expand All @@ -7,7 +7,7 @@ use crate::{parse, Tags, LTS};
pub fn bike_ottawa(tags: &Tags) -> (LTS, Vec<String>) {
let mut msgs = Vec::new();

if !is_biking_permitted(&tags, &mut msgs) {
if !is_cycling_allowed(&tags, &mut msgs) {
return (LTS::NotAllowed, msgs);
}

Expand All @@ -28,48 +28,6 @@ pub fn bike_ottawa(tags: &Tags) -> (LTS, Vec<String>) {
(LTS::NotAllowed, msgs)
}

fn is_biking_permitted(tags: &Tags, msgs: &mut Vec<String>) -> bool {
if !tags.has("highway") && !tags.has("bicycle") {
msgs.push("Way doesn't have a highway or bicycle tag".into());
return false;
}

if tags.is("bicycle", "no") {
msgs.push("Cycling not permitted due to bicycle=no".into());
return false;
}

if tags.is("access", "no") {
// TODO There are exceptions for bicycle
msgs.push("Cycling not permitted due to access=no".into());
return false;
}

if tags.is_any(
"highway",
vec!["motorway", "motorway_link", "proposed", "construction"],
) {
msgs.push(format!(
"Cycling not permitted due to highway={}",
tags.get("highway").unwrap()
));
return false;
}

if tags.is_any("highway", vec!["footway", "path"])
&& tags.is("footway", "sidewalk")
&& !tags.is("bicycle", "yes")
{
msgs.push(format!(
"Cycling not permitted on highway={}, when footway=sidewalk and bicycle=yes is missing",
tags.get("highway").unwrap()
));
return false;
}

true
}

fn is_separate_path(tags: &Tags, msgs: &mut Vec<String>) -> bool {
if tags.is_any("highway", vec!["cycleway", "path"]) {
msgs.push(format!(
Expand Down
2 changes: 2 additions & 0 deletions lts/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod allowed;
mod bike_ottawa;
mod parse;
mod speed_limit_only;
Expand All @@ -9,6 +10,7 @@ mod wasm;

use serde_repr::{Deserialize_repr, Serialize_repr};

pub use allowed::is_cycling_allowed;
pub use bike_ottawa::bike_ottawa;
pub use speed_limit_only::speed_limit_only;
pub use tags::Tags;
Expand Down
34 changes: 13 additions & 21 deletions lts/src/speed_limit_only.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,19 @@
use crate::{Tags, LTS};
use crate::{is_cycling_allowed, parse, Tags, LTS};

pub fn speed_limit_only(tags: &Tags) -> (LTS, Vec<String>) {
let msgs = vec!["Only looking at maxspeed".into()];
// TODO Handle bicycle=no, on things like highway=footway
let mut msgs = vec!["Only looking at maxspeed".into()];

// TODO Use parse::get_maxspeed_mph
if let Some(mph) = tags
.get("maxspeed")
.and_then(|x| x.trim_end_matches(" mph").parse::<usize>().ok())
{
if mph <= 20 {
return (LTS::LTS2, msgs);
}
if mph >= 40 {
return (LTS::LTS4, msgs);
}
// Between 20 and 40
return (LTS::LTS3, msgs);
if !is_cycling_allowed(tags, &mut msgs) {
return (LTS::NotAllowed, msgs);
}

/*if tags.is("highway", "residential") {
return LTS::LTS1;
}*/

(LTS::NotAllowed, msgs)
let mph = parse::get_maxspeed_mph(tags, &mut msgs);
if mph <= 20 {
return (LTS::LTS2, msgs);
}
if mph >= 40 {
return (LTS::LTS4, msgs);
}
// Between 20 and 40
(LTS::LTS3, msgs)
}
5 changes: 2 additions & 3 deletions od2net/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,9 @@ pub enum ODPattern {

#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum CostFunction {
/// Just find the shortest distance path
/// Just find the most direct path, minimizing distance. This is equivalent to ByLTS with all
/// weights set to 1.
Distance,
/// Heavily penalize main roads
AvoidMainRoads,
/// Multiply distance by a factor for each LTS classification
ByLTS {
lts1: f64,
Expand Down
30 changes: 1 addition & 29 deletions od2net/src/plugins/cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use lts::LTS;
pub fn calculate_batch(cost: &CostFunction, input_batch: Vec<&Edge>) -> Vec<Option<usize>> {
match cost {
CostFunction::Distance => input_batch.into_iter().map(distance).collect(),
CostFunction::AvoidMainRoads => input_batch.into_iter().map(avoid_main_roads).collect(),
CostFunction::OsmHighwayType(ref weights) => input_batch
.into_iter()
.map(|e| osm_highway_type(e, weights))
Expand All @@ -31,34 +30,7 @@ pub fn calculate_batch(cost: &CostFunction, input_batch: Vec<&Edge>) -> Vec<Opti
}

fn distance(edge: &Edge) -> Option<usize> {
// TODO Match the lts.ts definition
if edge.tags.is("bicycle", "no")
|| edge.tags.is("highway", "motorway")
|| edge.tags.is("highway", "proposed")
{
return None;
}

Some(edge.length_meters.round() as usize)
}

fn avoid_main_roads(edge: &Edge) -> Option<usize> {
// TODO Match the lts.ts definition
if edge.tags.is("bicycle", "no")
|| edge.tags.is("highway", "motorway")
|| edge.tags.is("highway", "proposed")
{
return None;
}

// TODO Reframe this to just penalize by LTS?
let penalty = if edge.tags.is("highway", "residential") || edge.tags.is("highway", "cycleway") {
1.0
} else {
5.0
};

Some((penalty * edge.length_meters).round() as usize)
by_lts(edge, 1.0, 1.0, 1.0, 1.0)
}

fn osm_highway_type(edge: &Edge, weights: &HashMap<String, f64>) -> Option<usize> {
Expand Down
1 change: 0 additions & 1 deletion viewer/src/CostFunction.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
Cost function:
<select bind:value={costChoice}>
<option value="Distance">Distance</option>
<option value="AvoidMainRoads">Avoid main roads</option>
<option value="ByLTS">Weight per LTS</option>
<option value="OsmHighwayType">Set a weight per OSM highway type</option>
</select>
Expand Down
5 changes: 4 additions & 1 deletion wasm-od2net/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ impl JsNetwork {
#[wasm_bindgen(js_name = updateCostFunction)]
pub fn update_cost_function(&mut self, input: JsValue) -> Result<(), JsValue> {
let cost: CostFunction = serde_wasm_bindgen::from_value(input)?;
info!("Changing cost to {}", serde_json::to_string(&cost).map_err(err_to_js)?);
info!(
"Changing cost to {}",
serde_json::to_string(&cost).map_err(err_to_js)?
);
self.last_cost = cost;
self.network.recalculate_cost(&self.last_cost);
// Doesn't touch the CH, because this is only meant to be used in the edge cost app, which
Expand Down

0 comments on commit 1533e57

Please sign in to comment.