Skip to content

Commit 15522f1

Browse files
committed
Detect what each auto boundary is bordered by, and try filtering based
on it
1 parent f95f972 commit 15522f1

File tree

3 files changed

+45
-4
lines changed

3 files changed

+45
-4
lines changed

backend/src/auto_boundaries.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use geo::{Area, Coord, LineString, Polygon};
1+
use geo::{Area, Coord, Intersects, LineString, Polygon};
22
use geojson::FeatureCollection;
33
use i_float::f64_point::F64Point;
44
use i_overlay::core::fill_rule::FillRule;
@@ -11,6 +11,7 @@ impl MapModel {
1111
pub fn render_auto_boundaries(&self) -> FeatureCollection {
1212
let mut features = Vec::new();
1313
let mut severances = Vec::new();
14+
let mut road_severances = Vec::new();
1415

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

3435
severances.push(road.linestring.clone());
36+
road_severances.push(road.linestring.clone());
3537
}
3638
}
3739

@@ -52,8 +54,16 @@ impl MapModel {
5254
}
5355

5456
for polygon in split_polygon(self.mercator.to_mercator(&self.boundary_wgs84), severances) {
57+
// TODO This is expensive; could this info somehow be retained?
58+
let touches_big_road = boundary_touches_any(&polygon, &road_severances);
59+
let touches_railway = boundary_touches_any(&polygon, &self.railways);
60+
let touches_waterway = boundary_touches_any(&polygon, &self.waterways);
61+
5562
let mut f = self.mercator.to_wgs84_gj(&polygon);
5663
f.set_property("kind", "area");
64+
f.set_property("touches_big_road", touches_big_road);
65+
f.set_property("touches_railway", touches_railway);
66+
f.set_property("touches_waterway", touches_waterway);
5767
// Convert from m^2 to km^2. Use unsigned area to ignore polygon orientation.
5868
f.set_property("area_km2", polygon.unsigned_area() / 1_000_000.0);
5969
features.push(f);
@@ -103,3 +113,10 @@ fn to_geo_linestring(pts: Vec<F64Point>) -> LineString {
103113
.collect(),
104114
)
105115
}
116+
117+
fn boundary_touches_any(polygon: &Polygon, linestrings: &Vec<LineString>) -> bool {
118+
// TODO At least consider an rtree to prune!
119+
linestrings
120+
.iter()
121+
.any(|ls| ls.intersects(polygon.exterior()))
122+
}

backend/src/scrape.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ pub fn scrape_osm(
6161
node_ids.into_iter().map(|n| node_mapping[&n]).collect(),
6262
));
6363
}
64-
} else if tags.is_any("natural", vec!["water", "coastline"]) {
64+
} else if tags.is_any("natural", vec!["water", "coastline"])
65+
|| tags.is("waterway", "dock")
66+
{
6567
// If the entire area is inside the study area, the LineString will be closed. If
6668
// it intersects the study area, then it might not be.
6769
node_ids.retain(|n| node_mapping.contains_key(n));

web/src/AutoBoundariesMode.svelte

+24-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
hoverStateFilter,
88
type LayerClickInfo,
99
} from "svelte-maplibre";
10+
import type { ExpressionSpecification } from "maplibre-gl";
1011
import { Link, Popup, layerId } from "./common";
1112
import { isLine, isPolygon } from "svelte-utils/map";
1213
import { SplitComponent } from "svelte-utils/top_bar_layout";
@@ -15,6 +16,7 @@
1516
1617
let gj = JSON.parse($app!.renderAutoBoundaries());
1718
let minArea = 0;
19+
let removeNonRoad = true;
1820
1921
function add(e: CustomEvent<LayerClickInfo>) {
2022
let name = window.prompt("What do you want to name the neighbourhood?");
@@ -49,6 +51,17 @@
4951
JSON.stringify(gj, null, " "),
5052
);
5153
}
54+
55+
function makeFilter(
56+
minArea: number,
57+
removeNonRoad: boolean,
58+
): ExpressionSpecification {
59+
let x: ExpressionSpecification = ["all", isPolygon, [">=", ["get", "area_km2"], minArea]];
60+
if (removeNonRoad) {
61+
x.push(["get", "touches_big_road"]);
62+
}
63+
return x;
64+
}
5265
</script>
5366

5467
<SplitComponent>
@@ -85,13 +98,18 @@
8598
Minimum area (km²)
8699
<input type="number" bind:value={minArea} min="0" max="1" step="0.01" />
87100
</label>
101+
102+
<label>
103+
<input type="checkbox" bind:checked={removeNonRoad} />
104+
Remove areas not touching a big road
105+
</label>
88106
</div>
89107

90108
<div slot="map">
91109
<GeoJSON data={gj} generateId>
92110
<FillLayer
93111
{...layerId("auto-boundaries-areas")}
94-
filter={["all", isPolygon, [">=", ["get", "area_km2"], minArea]]}
112+
filter={makeFilter(minArea, removeNonRoad)}
95113
manageHoverState
96114
paint={{
97115
"fill-color": [
@@ -115,7 +133,11 @@
115133
hoverCursor="pointer"
116134
>
117135
<Popup openOn="hover" let:props>
118-
Area: {props.area_km2.toFixed(5)} km²
136+
<p>Area: {props.area_km2.toFixed(1)} km²</p>
137+
<p>
138+
Borders roads = {props.touches_big_road}, railway = {props.touches_railway},
139+
water = {props.touches_waterway}
140+
</p>
119141
</Popup>
120142
</FillLayer>
121143

0 commit comments

Comments
 (0)