Skip to content

Commit

Permalink
Detect what each auto boundary is bordered by, and try filtering based
Browse files Browse the repository at this point in the history
on it
  • Loading branch information
dabreegster committed Dec 3, 2024
1 parent f95f972 commit 15522f1
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 4 deletions.
19 changes: 18 additions & 1 deletion backend/src/auto_boundaries.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use geo::{Area, Coord, LineString, Polygon};
use geo::{Area, Coord, Intersects, LineString, Polygon};
use geojson::FeatureCollection;
use i_float::f64_point::F64Point;
use i_overlay::core::fill_rule::FillRule;
Expand All @@ -11,6 +11,7 @@ impl MapModel {
pub fn render_auto_boundaries(&self) -> FeatureCollection {
let mut features = Vec::new();
let mut severances = Vec::new();
let mut road_severances = Vec::new();

for road in &self.roads {
if road.tags.is_any(
Expand All @@ -32,6 +33,7 @@ impl MapModel {
features.push(f);

severances.push(road.linestring.clone());
road_severances.push(road.linestring.clone());
}
}

Expand All @@ -52,8 +54,16 @@ impl MapModel {
}

for polygon in split_polygon(self.mercator.to_mercator(&self.boundary_wgs84), severances) {
// TODO This is expensive; could this info somehow be retained?
let touches_big_road = boundary_touches_any(&polygon, &road_severances);
let touches_railway = boundary_touches_any(&polygon, &self.railways);
let touches_waterway = boundary_touches_any(&polygon, &self.waterways);

let mut f = self.mercator.to_wgs84_gj(&polygon);
f.set_property("kind", "area");
f.set_property("touches_big_road", touches_big_road);
f.set_property("touches_railway", touches_railway);
f.set_property("touches_waterway", touches_waterway);
// Convert from m^2 to km^2. Use unsigned area to ignore polygon orientation.
f.set_property("area_km2", polygon.unsigned_area() / 1_000_000.0);
features.push(f);
Expand Down Expand Up @@ -103,3 +113,10 @@ fn to_geo_linestring(pts: Vec<F64Point>) -> LineString {
.collect(),
)
}

fn boundary_touches_any(polygon: &Polygon, linestrings: &Vec<LineString>) -> bool {
// TODO At least consider an rtree to prune!
linestrings
.iter()
.any(|ls| ls.intersects(polygon.exterior()))
}
4 changes: 3 additions & 1 deletion backend/src/scrape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ pub fn scrape_osm(
node_ids.into_iter().map(|n| node_mapping[&n]).collect(),
));
}
} else if tags.is_any("natural", vec!["water", "coastline"]) {
} else if tags.is_any("natural", vec!["water", "coastline"])
|| tags.is("waterway", "dock")
{
// If the entire area is inside the study area, the LineString will be closed. If
// it intersects the study area, then it might not be.
node_ids.retain(|n| node_mapping.contains_key(n));
Expand Down
26 changes: 24 additions & 2 deletions web/src/AutoBoundariesMode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
hoverStateFilter,
type LayerClickInfo,
} from "svelte-maplibre";
import type { ExpressionSpecification } from "maplibre-gl";
import { Link, Popup, layerId } from "./common";
import { isLine, isPolygon } from "svelte-utils/map";
import { SplitComponent } from "svelte-utils/top_bar_layout";
Expand All @@ -15,6 +16,7 @@
let gj = JSON.parse($app!.renderAutoBoundaries());
let minArea = 0;
let removeNonRoad = true;
function add(e: CustomEvent<LayerClickInfo>) {
let name = window.prompt("What do you want to name the neighbourhood?");
Expand Down Expand Up @@ -49,6 +51,17 @@
JSON.stringify(gj, null, " "),
);
}
function makeFilter(
minArea: number,
removeNonRoad: boolean,
): ExpressionSpecification {
let x: ExpressionSpecification = ["all", isPolygon, [">=", ["get", "area_km2"], minArea]];
if (removeNonRoad) {
x.push(["get", "touches_big_road"]);
}
return x;
}
</script>

<SplitComponent>
Expand Down Expand Up @@ -85,13 +98,18 @@
Minimum area (km²)
<input type="number" bind:value={minArea} min="0" max="1" step="0.01" />
</label>

<label>
<input type="checkbox" bind:checked={removeNonRoad} />
Remove areas not touching a big road
</label>
</div>

<div slot="map">
<GeoJSON data={gj} generateId>
<FillLayer
{...layerId("auto-boundaries-areas")}
filter={["all", isPolygon, [">=", ["get", "area_km2"], minArea]]}
filter={makeFilter(minArea, removeNonRoad)}
manageHoverState
paint={{
"fill-color": [
Expand All @@ -115,7 +133,11 @@
hoverCursor="pointer"
>
<Popup openOn="hover" let:props>
Area: {props.area_km2.toFixed(5)} km²
<p>Area: {props.area_km2.toFixed(1)} km²</p>
<p>
Borders roads = {props.touches_big_road}, railway = {props.touches_railway},
water = {props.touches_waterway}
</p>
</Popup>
</FillLayer>

Expand Down

0 comments on commit 15522f1

Please sign in to comment.