Skip to content

Commit

Permalink
Generalize all of the everything -- let the user change the mode for
Browse files Browse the repository at this point in the history
routing and scores. #18
  • Loading branch information
dabreegster committed Aug 26, 2024
1 parent 1d14bca commit 89883e1
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 24 deletions.
9 changes: 5 additions & 4 deletions backend/src/heatmap.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use geo::{Coord, Densify, Line, LineString};
use geojson::FeatureCollection;
use graph::Mode;

use crate::{MapModel, RoadKind};

Expand All @@ -8,7 +9,7 @@ use crate::{MapModel, RoadKind};
// We could focus where footways connect to severances, but that's probably a crossing. Ideally we
// want to find footpaths parallel(ish) to severances. If we had some kind of generalized edge
// bundling...
pub fn along_severances(map: &MapModel) -> FeatureCollection {
pub fn along_severances(map: &MapModel, mode: Mode) -> FeatureCollection {
let mut requests = Vec::new();
for r in &map.graph.roads {
if map.road_kinds[r.id.0] == Some(RoadKind::Severance) {
Expand All @@ -17,14 +18,14 @@ pub fn along_severances(map: &MapModel) -> FeatureCollection {
}
}
}
calculate(map, requests)
calculate(map, mode, requests)
}

fn calculate(map: &MapModel, requests: Vec<(Coord, Coord)>) -> FeatureCollection {
fn calculate(map: &MapModel, mode: Mode, requests: Vec<(Coord, Coord)>) -> FeatureCollection {
let mut samples = Vec::new();
let mut max_score = 0.0_f64;
for (start, end) in requests {
if let Ok((mut f, fc)) = crate::route::do_route(map, start, end) {
if let Ok((mut f, fc)) = crate::route::do_route(map, start, end, mode) {
let direct = fc
.foreign_members
.as_ref()
Expand Down
10 changes: 7 additions & 3 deletions backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,17 @@ impl MapModel {
x: req.x2,
y: req.y2,
});
let (_, gj) = route::do_route(self, start, end).map_err(err_to_js)?;
let mode = Mode::parse(&req.mode).map_err(err_to_js)?;
let (_, gj) = route::do_route(self, start, end, mode).map_err(err_to_js)?;
let out = serde_json::to_string(&gj).map_err(err_to_js)?;
Ok(out)
}

#[wasm_bindgen(js_name = makeHeatmap)]
pub fn make_heatmap(&self) -> Result<String, JsValue> {
let samples = heatmap::along_severances(self);
pub fn make_heatmap(&self, mode: String) -> Result<String, JsValue> {
let mode = Mode::parse(&mode).map_err(err_to_js)?;
// TODO Different strategy for driving
let samples = heatmap::along_severances(self, mode);
let out = serde_json::to_string(&samples).map_err(err_to_js)?;
Ok(out)
}
Expand Down Expand Up @@ -150,6 +153,7 @@ pub struct CompareRouteRequest {
y1: f64,
x2: f64,
y2: f64,
mode: String,
}

fn err_to_js<E: std::fmt::Display>(err: E) -> JsValue {
Expand Down
13 changes: 9 additions & 4 deletions backend/src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ use serde::Serialize;
use crate::MapModel;

// Also returns the line of the snapped request (in WGS84)
pub fn do_route(map: &MapModel, start: Coord, end: Coord) -> Result<(Feature, FeatureCollection)> {
let start = map.graph.snap_to_road(start, Mode::Foot);
let end = map.graph.snap_to_road(end, Mode::Foot);
pub fn do_route(
map: &MapModel,
start: Coord,
end: Coord,
mode: Mode,
) -> Result<(Feature, FeatureCollection)> {
let start = map.graph.snap_to_road(start, mode);
let end = map.graph.snap_to_road(end, mode);

let route = map.graph.router[Mode::Foot].route(&map.graph, start, end)?;
let route = map.graph.router[mode].route(&map.graph, start, end)?;
let route_linestring = route.linestring(&map.graph);

let mut directions = Vec::new();
Expand Down
12 changes: 12 additions & 0 deletions web/src/PickTravelMode.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script lang="ts">
import { travelMode } from "./stores";
</script>

<label
>Mode:
<select bind:value={$travelMode}>
<option value="car">Car</option>
<option value="bicycle">Bicycle</option>
<option value="foot">Foot</option>
</select>
</label>
9 changes: 6 additions & 3 deletions web/src/RouteMode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import { MapEvents, GeoJSON, LineLayer, Marker } from "svelte-maplibre";
import Directions from "./Directions.svelte";
import { SplitComponent } from "svelte-utils/top_bar_layout";
import { model, type RouteGJ, routeA, routeB } from "./stores";
import { model, type RouteGJ, routeA, routeB, travelMode } from "./stores";
import NavBar from "./NavBar.svelte";
import { onMount } from "svelte";
import PickTravelMode from "./PickTravelMode.svelte";
// TODO or empty
let route_gj: RouteGJ | null = null;
Expand All @@ -29,6 +30,7 @@
y1: $routeA[1],
x2: $routeB[0],
y2: $routeB[1],
mode: $travelMode,
}),
);
route_err = "";
Expand All @@ -53,9 +55,10 @@
<div slot="sidebar">
<h2>Route mode</h2>
<p>
Move the <b>A</b> and <b>B</b> pins to find a walking route. (Hint: right-click
to set the first pin somewhere.)
Move the <b>A</b> and <b>B</b> pins to find a route. (Hint: right-click to
set the first pin somewhere.)
</p>
<PickTravelMode />
{#if route_err}
<p>{route_err}</p>
{/if}
Expand Down
37 changes: 27 additions & 10 deletions web/src/ScoreMode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,29 @@
maxScore,
routeA,
routeB,
travelMode,
type Position,
} from "./stores";
import NavBar from "./NavBar.svelte";
import PickTravelMode from "./PickTravelMode.svelte";
// TODO Cache
let scoreGj: FeatureCollection<LineString, { score: number }> = JSON.parse(
$model!.makeHeatmap(),
);
let highestScore = Math.round(
Math.max(...scoreGj.features.map((f) => f.properties.score)),
);
if ($maxScore > highestScore) {
$minScore = 0;
$maxScore = highestScore;
let scoreGj: FeatureCollection<LineString, { score: number }> = {
type: "FeatureCollection" as const,
features: [],
};
let highestScore = 0;
function update(_mode: string) {
scoreGj = JSON.parse($model!.makeHeatmap($travelMode));
highestScore = Math.round(
Math.max(...scoreGj.features.map((f) => f.properties.score)),
);
if ($maxScore > highestScore) {
$minScore = 0;
$maxScore = highestScore;
}
}
$: update($travelMode);
let desire_line: Feature<LineString, { score: number }> | null = null;
let route_gj: FeatureCollection | null = null;
Expand All @@ -58,6 +66,7 @@
y1: linestring[0][1],
x2: linestring[1][0],
y2: linestring[1][1],
mode: $travelMode,
}),
);
} catch (err) {
Expand Down Expand Up @@ -110,6 +119,14 @@
</label>
</fieldset>

<PickTravelMode />
{#if $travelMode != "foot"}
<p>
Note the scores are calculated in one arbitrary direction, which might
pick up one-way roads
</p>
{/if}

<hr />

<button on:click={gotoRouteMode} disabled={desire_line == null}
Expand Down
6 changes: 6 additions & 0 deletions web/src/stores.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ export let duplicateSidewalks: Writable<boolean> = urlState({
stringify: (x) => (x ? "1" : "0"),
parse: (x) => x == "1",
});
export let travelMode = urlState({
name: "travelMode",
defaultValue: "foot",
stringify: (x) => x,
parse: enumUrl(["car", "bicycle", "foot"]),
});

export type Position = [number, number];

Expand Down

0 comments on commit 89883e1

Please sign in to comment.