Skip to content

Commit

Permalink
Start a mode to look for OSM ways that might need sidewalk=separate
Browse files Browse the repository at this point in the history
  • Loading branch information
dabreegster committed Aug 11, 2024
1 parent b31a5eb commit 521c167
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 28 deletions.
38 changes: 38 additions & 0 deletions backend/src/fix_osm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use geo::Intersects;
use geojson::GeoJson;
use rstar::{RTree, RTreeObject};

use crate::MapModel;

pub fn find_separate_sidewalks(map: &MapModel) -> GeoJson {
let footways = RTree::bulk_load(
map.roads
.iter()
.filter(|r| r.tags.is("highway", "footway"))
.map(|r| r.linestring.clone())
.collect(),
);

let mut features = Vec::new();
'ROAD: for r in &map.roads {
if r.tags.is("highway", "footway")
|| r.tags.is("sidewalk", "separate")
|| r.tags.is("sidewalk:left", "separate")
|| r.tags.is("sidewalk:right", "separate")
|| r.tags.is("sidewalk:both", "separate")
{
continue;
}

// Along this road, if we project away, do we hit a footway?
for line in crate::heatmap::make_perpendicular_offsets(&r.linestring, 25.0, 15.0) {
for footway in footways.locate_in_envelope_intersecting(&line.envelope()) {
if footway.intersects(&line) {
features.push(r.to_gj(&map.mercator));
continue 'ROAD;
}
}
}
}
GeoJson::from(features)
}
2 changes: 1 addition & 1 deletion backend/src/heatmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ fn calculate(map: &mut MapModel, requests: Vec<CompareRouteRequest>) -> FeatureC
}

// TODO canvas_geometry needs this too
fn make_perpendicular_offsets(
pub fn make_perpendicular_offsets(
linestring: &LineString,
walk_every_m: f64,
project_away_m: f64,
Expand Down
8 changes: 8 additions & 0 deletions backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use wasm_bindgen::prelude::*;

use crate::profiles::Profile;

mod fix_osm;
mod heatmap;
mod profiles;
mod route;
Expand Down Expand Up @@ -174,6 +175,13 @@ impl MapModel {
vec![b.min().x, b.min().y, b.max().x, b.max().y]
}

#[wasm_bindgen(js_name = findSeparateSidewalks)]
pub fn find_separate_sidewalks(&self) -> Result<String, JsValue> {
let out =
serde_json::to_string(&fix_osm::find_separate_sidewalks(self)).map_err(err_to_js)?;
Ok(out)
}

fn find_edge(&self, i1: IntersectionID, i2: IntersectionID) -> &Road {
// TODO Store lookup table
for r in &self.intersections[i1.0].roads {
Expand Down
61 changes: 35 additions & 26 deletions web/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import DebugMode from "./DebugMode.svelte";
import RouteMode from "./RouteMode.svelte";
import ScoreMode from "./ScoreMode.svelte";
import OsmSeparateSidewalksMode from "./OsmSeparateSidewalksMode.svelte";
import NetworkLayer from "./NetworkLayer.svelte";
import {
map as mapStore,
Expand Down Expand Up @@ -96,31 +97,33 @@
<hr />
<div><button on:click={zoomToFit}>Zoom to fit</button></div>

<Legend
rows={[
["Footway (ground, outdoors)", kindToColor.Footway],
["Indoors footway", kindToColor.Indoors],
["Footway not on the ground", kindToColor.BridgeOrTunnel],
[
"Street with vehicle traffic (maybe with a sidewalk, maybe not)",
kindToColor.WithTraffic,
],
["Crossing", kindToColor.Crossing],
["Severance", kindToColor.Severance],
]}
/>
<div>
<label>
<input type="checkbox" bind:checked={showSeverances} />
Show severances
</label>
</div>
<div>
<label>
Network opacity:
<input type="range" min="0" max="100" bind:value={opacity} />
</label>
</div>
{#if $mode.kind != "osm-separate-sidewalks"}
<Legend
rows={[
["Footway (ground, outdoors)", kindToColor.Footway],
["Indoors footway", kindToColor.Indoors],
["Footway not on the ground", kindToColor.BridgeOrTunnel],
[
"Street with vehicle traffic (maybe with a sidewalk, maybe not)",
kindToColor.WithTraffic,
],
["Crossing", kindToColor.Crossing],
["Severance", kindToColor.Severance],
]}
/>
<div>
<label>
<input type="checkbox" bind:checked={showSeverances} />
Show severances
</label>
</div>
<div>
<label>
Network opacity:
<input type="range" min="0" max="100" bind:value={opacity} />
</label>
</div>
{/if}
{/if}
</div>
<div slot="main" style="position:relative; width: 100%; height: 100vh;">
Expand All @@ -146,14 +149,20 @@
<FillLayer paint={{ "fill-color": "black", "fill-opacity": 0.3 }} />
</GeoJSON>

<NetworkLayer show={$mode.kind != "debug"} {showSeverances} {opacity} />
<NetworkLayer
show={$mode.kind != "debug" && $mode.kind != "osm-separate-sidewalks"}
{showSeverances}
{opacity}
/>

{#if $mode.kind == "route"}
<RouteMode route_a={$mode.route_a} route_b={$mode.route_b} />
{:else if $mode.kind == "score"}
<ScoreMode />
{:else if $mode.kind == "debug"}
<DebugMode {showSeverances} {opacity} />
{:else if $mode.kind == "osm-separate-sidewalks"}
<OsmSeparateSidewalksMode />
{/if}
{/if}
</MapLibre>
Expand Down
8 changes: 8 additions & 0 deletions web/src/NavBar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
>
</li>

<li>
<button
on:click={() => ($mode = { kind: "osm-separate-sidewalks" })}
disabled={$mode.kind == "osm-separate-sidewalks"}
>Fix OSM sidewalk=separate</button
>
</li>

<li>
<button
on:click={() => ($mode = { kind: "debug" })}
Expand Down
42 changes: 42 additions & 0 deletions web/src/OsmSeparateSidewalksMode.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script lang="ts">
import { GeoJSON, hoverStateFilter, LineLayer } from "svelte-maplibre";
import { SplitComponent } from "svelte-utils/top_bar_layout";
import { PropertiesTable, notNull } from "svelte-utils";
import { Popup } from "svelte-utils/map";
import { model } from "./stores";
import NavBar from "./NavBar.svelte";
</script>

<SplitComponent>
<div slot="top"><NavBar /></div>
<div slot="sidebar">
<h2>Fix OSM sidewalk=separate mode</h2>
<p>
This shows roads that seem to have a parallel sidewalk. Click one to open
OSM, then (if appropriate) add <b>sidewalk=separate</b> (or a <b>:left</b>
/ <b>:right</b> variant).
</p>
</div>
<div slot="map">
<GeoJSON
data={JSON.parse(notNull($model).findSeparateSidewalks())}
generateId
>
<LineLayer
paint={{
"line-width": hoverStateFilter(5, 7),
"line-color": "red",
}}
manageHoverState
on:click={(e) =>
window.open(notNull(e.detail.features[0].properties).way, "_blank")}
hoverCursor="pointer"
>
<Popup openOn="hover" let:props>
<h2>Classified as {props.kind}</h2>
<PropertiesTable properties={props} />
</Popup>
</LineLayer>
</GeoJSON>
</div>
</SplitComponent>
3 changes: 2 additions & 1 deletion web/src/stores.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export type Mode =
| { kind: "title" }
| { kind: "score" }
| { kind: "route"; route_a: [number, number]; route_b: [number, number] }
| { kind: "debug" };
| { kind: "debug" }
| { kind: "osm-separate-sidewalks" };
export interface RouteGJ extends FeatureCollection {
direct_length: number;
route_length: number;
Expand Down

0 comments on commit 521c167

Please sign in to comment.