Skip to content

Commit

Permalink
Port a walking LTS mapping from https://github.com/acteng/edge_level_…
Browse files Browse the repository at this point in the history
  • Loading branch information
dabreegster committed Oct 30, 2024
1 parent 83a1b6d commit 9862131
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 4 deletions.
2 changes: 2 additions & 0 deletions lts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod speed_limit_only;
mod tags;
#[cfg(test)]
mod tests;
mod walking;
#[cfg(target_arch = "wasm32")]
mod wasm;

Expand All @@ -14,6 +15,7 @@ pub use allowed::is_cycling_allowed;
pub use bike_ottawa::bike_ottawa;
pub use speed_limit_only::speed_limit_only;
pub use tags::Tags;
pub use walking::walking;

#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
Expand Down
105 changes: 105 additions & 0 deletions lts/src/walking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use crate::{parse, Tags, LTS};

/// Ported from
/// https://github.com/acteng/edge_level_walkability_function/blob/main/walking-lts-prototyping.ipynb
pub fn walking(tags: &Tags) -> (LTS, Vec<String>) {
let mut msgs = Vec::new();

let speed_mph = parse::get_maxspeed_mph(tags, &mut msgs);

if !can_traverse_on_foot(tags) || is_sidewalk(tags) || tags.is("highway", "elevator") {
return (LTS::NotAllowed, msgs);
}

if is_separate_footpath(tags) {
(LTS::LTS1, msgs)
} else if is_pleasant_road(tags, speed_mph) || tags.is("highway", "steps") {
(LTS::LTS2, msgs)
} else if !tags.is_any("highway", vec!["motorway", "motorway_link"])
&& is_quite_unpleasant_road(tags, speed_mph)
{
(LTS::LTS3, msgs)
} else if tags.is_any(
"highway",
vec!["motorway", "motorway_link", "trunk", "trunk_link"],
) || speed_mph > 40
{
(LTS::LTS4, msgs)
} else {
// TODO What cases are these?
(LTS::NotAllowed, msgs)
}
}

fn can_traverse_on_foot(tags: &Tags) -> bool {
if !tags.has("highway") || tags.is("foot", "no") {
return false;
}
if tags.is("access", "no") && !tags.is_any("foot", vec!["yes", "designated", "permissive"]) {
return false;
}
if tags.is_any("highway", vec!["motorway", "motorway_link", "proposed"]) {
return false;
}
true
}

fn is_separate_footpath(tags: &Tags) -> bool {
if tags.is_any("highway", vec!["pedestrian", "path", "living_street"]) {
return true;
}
if tags.is("highway", "footway")
&& !tags.is_any("footway", vec!["crossing", "link", "traffic_island"])
{
return true;
}
if tags.is("highway", "cycleway") && tags.is("footway", "designated") {
return true;
}
false
}

fn is_pleasant_road(tags: &Tags, speed_mph: usize) -> bool {
if tags.is_any(
"highway",
vec![
"service",
"alley",
"driveway",
"parking_aisle",
"residential",
"bridleway",
"corridor",
"track",
"tertiary",
],
) && speed_mph <= 20
{
return true;
}
if tags.is("highway", "footway")
&& tags.is_any("footway", vec!["crossing", "link", "traffic_island"])
{
return true;
}
false
}

// TODO Logic here seems wrong
fn is_quite_unpleasant_road(tags: &Tags, speed_mph: usize) -> bool {
let big_highway_type = tags.is_any(
"highway",
vec!["trunk", "trunk_link", "primary", "primary_link"],
);
if !big_highway_type && speed_mph <= 40 {
return true;
}
if big_highway_type && speed_mph <= 20 {
return true;
}
false
}

fn is_sidewalk(tags: &Tags) -> bool {
tags.is("highway", "footway") && tags.is("footway", "sidewalk")
}
4 changes: 3 additions & 1 deletion lts/src/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use wasm_bindgen::prelude::*;

use crate::{bike_ottawa, speed_limit_only, Tags, LTS};
use crate::{bike_ottawa, speed_limit_only, walking, Tags, LTS};

#[derive(Deserialize)]
struct Input {
Expand All @@ -29,6 +29,8 @@ pub fn calculate(input: JsValue) -> Result<JsValue, JsValue> {
speed_limit_only::speed_limit_only(&tags)
} else if input.method == "bike_ottawa" {
bike_ottawa::bike_ottawa(&tags)
} else if input.method == "walking" {
walking::walking(&tags)
} else {
(
LTS::NotAllowed,
Expand Down
1 change: 1 addition & 0 deletions od2net/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ pub enum Uptake {
pub enum LtsMapping {
SpeedLimitOnly,
BikeOttawa,
Walking,
/// Run this command to calculate LTS. STDIN will contain a JSON array of objects, each with
/// OSM tags representing one segment. The output must be an equally sized JSON array of
/// numbers 0-4, representing the resulting LTS.
Expand Down
4 changes: 4 additions & 0 deletions od2net/src/plugins/lts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ pub fn calculate_lts_batch(lts: &LtsMapping, tags_batch: Vec<&Tags>) -> Vec<LTS>
.into_iter()
.map(|tags| lts::bike_ottawa(tags).0)
.collect(),
LtsMapping::Walking => tags_batch
.into_iter()
.map(|tags| lts::walking(tags).0)
.collect(),
LtsMapping::ExternalCommand(command) => external_command(command, tags_batch).unwrap(),
}
}
Expand Down
5 changes: 2 additions & 3 deletions od2net/src/plugins/uptake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,12 @@ mod tests {
// #' uptake_pct_govtarget_school2(3.51, 1.11)
// #' [1] 0.05584607

// #' # pcycle = exp(1.953)/(1 + exp(1.953)) = .8758, or 87.58%.
// #' uptake_pct_godutch_school2(3.51, 1.11)
// #' # pcycle = exp(1.953)/(1 + exp(1.953)) = .8758, or 87.58%.
// #' uptake_pct_godutch_school2(3.51, 1.11)
// #' [1] 0.875
#[test]
fn test_school() {
assert!((pct_gov_target_school(3.51 * 1000.0, 1.11) - 0.05584607).abs() < 1e-4);
assert!((pct_go_dutch_school(3.51 * 1000.0, 1.11) - 0.8758).abs() < 1e-4);
}

}

0 comments on commit 9862131

Please sign in to comment.