Skip to content

Commit

Permalink
Clip the network to the boundary
Browse files Browse the repository at this point in the history
Wires up e2e, exposing a problem with the route snapper geometry
  • Loading branch information
dabreegster committed Dec 18, 2023
1 parent 4959b86 commit 9a3fd90
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 5 deletions.
17 changes: 16 additions & 1 deletion backend/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
#[macro_use]
extern crate anyhow;
#[macro_use]
extern crate log;

use std::fmt;
use std::sync::Once;

use geo::{LineString, Point};
use geo::{LineString, Point, Polygon};
use geojson::{Feature, GeoJson, Geometry};
use wasm_bindgen::prelude::*;

mod mercator;
mod neighbourhood;
mod scrape;
mod tags;

Expand Down Expand Up @@ -111,6 +114,18 @@ impl MapModel {
bytes
}

/// Takes boundary GJ polygon, returns GJ with more details
#[wasm_bindgen(js_name = analyzeNeighbourhood)]
pub fn analyze_neighbourhood(&self, input: JsValue) -> Result<String, JsValue> {
let boundary_gj: Feature = serde_wasm_bindgen::from_value(input)?;
let mut boundary_geo: Polygon = boundary_gj.try_into().map_err(err_to_js)?;
self.mercator.to_mercator_in_place(&mut boundary_geo);

let neighbourhood =
neighbourhood::Neighbourhood::new(self, boundary_geo).map_err(err_to_js)?;
Ok(serde_json::to_string(&neighbourhood.to_gj(self)).map_err(err_to_js)?)
}

fn find_edge(&self, i1: IntersectionID, i2: IntersectionID) -> &Road {
// TODO Store lookup table
for r in &self.intersections[i1.0].roads {
Expand Down
40 changes: 40 additions & 0 deletions backend/src/neighbourhood.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use std::collections::HashSet;

use anyhow::Result;
use geo::{Contains, Polygon};
use geojson::GeoJson;

use crate::{MapModel, RoadID};

pub struct Neighbourhood {
interior_roads: HashSet<RoadID>,
}

impl Neighbourhood {
pub fn new(map: &MapModel, boundary: Polygon) -> Result<Self> {
let mut interior_roads = HashSet::new();
for r in &map.roads {
if boundary.contains(&r.linestring) {
interior_roads.insert(r.id);
}
}

if interior_roads.is_empty() {
bail!("No roads inside the boundary");
}

Ok(Self { interior_roads })
}

pub fn to_gj(&self, map: &MapModel) -> GeoJson {
let mut features = Vec::new();

for r in &self.interior_roads {
let mut f = map.roads[r.0].to_gj(&map.mercator);
f.set_property("kind", "interior_road");
features.push(f);
}

GeoJson::from(features)
}
}
4 changes: 2 additions & 2 deletions web/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
{/if}
<div><button on:click={zoomToFit}>Zoom to fit</button></div>

{#if mode.mode == "network"}
{#if mode.mode == "network" && model}
<button on:click={setBoundaryMode}>Set boundary</button>
{:else if mode.mode == "set-boundary"}
<p>Draw the boundary...</p>
Expand All @@ -104,7 +104,7 @@
{:else if mode.mode == "set-boundary"}
<RouteSnapperLayer />
{:else if mode.mode == "neighbourhood"}
<NeighbourhoodLayer boundary={mode.boundary} />
<NeighbourhoodLayer {model} boundary={mode.boundary} />
{/if}
{/if}
</MapLibre>
Expand Down
30 changes: 28 additions & 2 deletions web/src/NeighbourhoodLayer.svelte
Original file line number Diff line number Diff line change
@@ -1,15 +1,41 @@
<script lang="ts">
import { MapModel } from "backend";
import type { Feature, Polygon } from "geojson";
import { FillLayer, GeoJSON } from "svelte-maplibre";
import { FillLayer, GeoJSON, LineLayer, Popup } from "svelte-maplibre";
import { constructMatchExpression, PropertiesTable } from "./common";
export let model: MapModel;
export let boundary: Feature<Polygon>;
let details = JSON.parse(model.analyzeNeighbourhood(boundary));
</script>

<GeoJSON data={boundary}>
<FillLayer
paint={{
"fill-color": "black",
"fill-opacity": 0.5,
"fill-opacity": 0.2,
}}
/>
</GeoJSON>

<GeoJSON data={details}>
<LineLayer
paint={{
"line-width": 5,
"line-color": constructMatchExpression(
["get", "kind"],
{
interior_road: "black",
},
"red"
),
}}
on:click={(e) => window.open(e.detail.features[0].properties.way, "_blank")}
hoverCursor="pointer"
>
<Popup openOn="hover" let:data>
<PropertiesTable properties={data.properties} />
</Popup>
</LineLayer>
</GeoJSON>

0 comments on commit 9a3fd90

Please sign in to comment.