Skip to content

Commit

Permalink
Wire up the route-snapper
Browse files Browse the repository at this point in the history
  • Loading branch information
dabreegster committed Dec 18, 2023
1 parent 4c35926 commit 58561da
Show file tree
Hide file tree
Showing 14 changed files with 528 additions and 3 deletions.
20 changes: 20 additions & 0 deletions backend/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ geo = "0.27.0"
geojson = { git = "https://github.com/georust/geojson", features = ["geo-types"] }
log = "0.4.20"
osm-reader = { git = "https://github.com/a-b-street/osm-reader" }
route-snapper-graph = { git = "https://github.com/dabreegster/route_snapper", branch = "no_osm2streets" }
rstar = { version = "0.11.0" }
serde = "1.0.188"
serde_json = "1.0.105"
serde-wasm-bindgen = "0.6.0"
wasm-bindgen = "0.2.87"
web-sys = { version = "0.3.64", features = ["console"] }
bincode = "1.3.3"
29 changes: 29 additions & 0 deletions backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,35 @@ impl MapModel {
Ok(out)
}

#[wasm_bindgen(js_name = toRouteSnapper)]
pub fn to_route_snapper(&self) -> Vec<u8> {
use route_snapper_graph::{Edge, NodeID, RouteSnapperMap};

let mut nodes = Vec::new();
for i in &self.intersections {
nodes.push(self.mercator.to_wgs84(i.point.into()));
}

let mut edges = Vec::new();
for r in &self.roads {
let mut linestring = r.linestring.clone();
linestring.map_coords_in_place(|c| self.mercator.to_wgs84(c));

edges.push(Edge {
node1: NodeID(r.src_i.0 as u32),
node2: NodeID(r.dst_i.0 as u32),
geometry: linestring,
// Isn't serialized, doesn't matter
length_meters: 0.0,
name: r.tags.get("name").cloned(),
});
}

let graph = RouteSnapperMap { nodes, edges };
let bytes = bincode::serialize(&graph).unwrap();
bytes
}

fn find_edge(&self, i1: IntersectionID, i2: IntersectionID) -> &Road {
// TODO Store lookup table
for r in &self.intersections[i1.0].roads {
Expand Down
2 changes: 2 additions & 0 deletions backend/src/mercator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,6 @@ impl Mercator {
+ (self.wgs84_bounds.height() * (self.height - pt.y) / self.height);
Coord { x, y }
}

// TODO Take anything that can do mapcoords
}
6 changes: 6 additions & 0 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@mapbox/mapbox-gl-draw": "^1.4.3",
"@turf/bbox": "^6.5.0",
"@types/geojson": "^7946.0.13",
"route-snapper": "0.2.5-alpha",
"svelte-maplibre": "^0.7.3"
}
}
64 changes: 63 additions & 1 deletion web/src/App.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,33 @@
<script lang="ts">
import turfBbox from "@turf/bbox";
import { MapModel } from "backend";
import type { Feature, Polygon } from "geojson";
import type { Map } from "maplibre-gl";
import { MapLibre } from "svelte-maplibre";
import { Layout } from "./common";
import { RouteTool } from "./common/route_tool";
import RouteSnapperLayer from "./common/RouteSnapperLayer.svelte";
import MapLoader from "./MapLoader.svelte";
import NeighbourhoodLayer from "./NeighbourhoodLayer.svelte";
import NetworkLayer from "./NetworkLayer.svelte";
type Mode =
| {
mode: "network";
}
| {
mode: "set-boundary";
}
| {
mode: "neighbourhood";
boundary: Feature<Polygon>;
};
let mode = {
mode: "network",
};
let model: MapModel | undefined = undefined;
let route_tool: RouteTool | undefined = undefined;
let map: Map;
function zoomToFit() {
Expand All @@ -24,8 +44,35 @@
}
console.log("New map model loaded");
zoomToFit();
route_tool = new RouteTool(map, model.toRouteSnapper());
}
$: gotModel(model);
function setBoundaryMode() {
mode = {
mode: "set-boundary",
};
route_tool.startArea();
route_tool.addEventListenerSuccess((feature) => {
mode = {
mode: "neighbourhood",
boundary: feature,
};
route_tool.clearEventListeners();
});
route_tool.addEventListenerFailure(() => {
mode = {
mode: "network",
};
route_tool.clearEventListeners();
});
}
function reset() {
mode = {
mode: "network",
};
}
</script>

<Layout>
Expand All @@ -34,6 +81,15 @@
<MapLoader {map} bind:model />
{/if}
<div><button on:click={zoomToFit}>Zoom to fit</button></div>

{#if mode.mode == "network"}
<button on:click={setBoundaryMode}>Set boundary</button>
{:else if mode.mode == "set-boundary"}
<p>Draw the boundary...</p>
{:else if mode.mode == "neighbourhood"}
<button on:click={reset}>Reset</button>
<p>Analyze and edit now</p>
{/if}
</div>
<div slot="main" style="position:relative; width: 100%; height: 100vh;">
<MapLibre
Expand All @@ -43,7 +99,13 @@
bind:map
>
{#if model}
<NetworkLayer {model} />
{#if mode.mode == "network"}
<NetworkLayer {model} />
{:else if mode.mode == "set-boundary"}
<RouteSnapperLayer />
{:else if mode.mode == "neighbourhood"}
<NeighbourhoodLayer boundary={mode.boundary} />
{/if}
{/if}
</MapLibre>
</div>
Expand Down
2 changes: 2 additions & 0 deletions web/src/MapLoader.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts">
import init, { MapModel } from "backend";
import type { Map } from "maplibre-gl";
import init2 from "route-snapper";
import { onMount } from "svelte";
import { Loading, OverpassSelector } from "./common";
Expand All @@ -13,6 +14,7 @@
onMount(async () => {
await init();
await init2();
// When running locally if a vite public/ directory is set up, load from that for speed
try {
Expand Down
15 changes: 15 additions & 0 deletions web/src/NeighbourhoodLayer.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script lang="ts">
import type { Feature, Polygon } from "geojson";
import { FillLayer, GeoJSON } from "svelte-maplibre";
export let boundary: Feature<Polygon>;
</script>

<GeoJSON data={boundary}>
<FillLayer
paint={{
"fill-color": "black",
"fill-opacity": 0.5,
}}
/>
</GeoJSON>
49 changes: 49 additions & 0 deletions web/src/common/RouteSnapperLayer.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script lang="ts">
import type { Feature } from "geojson";
import { CircleLayer, FillLayer, GeoJSON, LineLayer } from "svelte-maplibre";
import {
constructMatchExpression,
isLine,
isPoint,
isPolygon,
} from "./index";
import { routeToolGj } from "./stores";
const circleRadiusPixels = 10;
</script>

<GeoJSON data={$routeToolGj}>
<CircleLayer
filter={isPoint}
paint={{
"circle-color": constructMatchExpression(
["get", "type"],
{
"snapped-waypoint": "red",
"free-waypoint": "blue",
},
"black"
),
"circle-opacity": ["case", ["has", "hovered"], 0.5, 1.0],
"circle-radius": constructMatchExpression(
["get", "type"],
{ node: circleRadiusPixels / 2.0 },
circleRadiusPixels
),
}}
/>
<LineLayer
filter={isLine}
paint={{
"line-color": ["case", ["get", "snapped"], "red", "blue"],
"line-width": 2.5,
}}
/>
<FillLayer
filter={isPolygon}
paint={{
"fill-color": "black",
"fill-opacity": 0.5,
}}
/>
</GeoJSON>
21 changes: 20 additions & 1 deletion web/src/common/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
import type { DataDrivenPropertyValueSpecification } from "maplibre-gl";
import type {
DataDrivenPropertyValueSpecification,
ExpressionSpecification,
} from "maplibre-gl";

export { default as Layout } from "./Layout.svelte";
export { default as Legend } from "./Legend.svelte";
export { default as Loading } from "./Loading.svelte";
export { default as OverpassSelector } from "./OverpassSelector.svelte";
export { default as PropertiesTable } from "./PropertiesTable.svelte";

export const isPolygon: ExpressionSpecification = [
"==",
["geometry-type"],
"Polygon",
];
export const isLine: ExpressionSpecification = [
"==",
["geometry-type"],
"LineString",
];
export const isPoint: ExpressionSpecification = [
"==",
["geometry-type"],
"Point",
];

export function constructMatchExpression<OutputType>(
getter: any[],
map: { [name: string]: OutputType },
Expand Down
Loading

0 comments on commit 58561da

Please sign in to comment.