Skip to content

Commit ccae1b8

Browse files
committed
Plumb through the study area boundary
1 parent 93cac52 commit ccae1b8

9 files changed

+70
-31
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
*.swp
22
target/
33
node_modules/
4-
# Local symlink to avoid hitting od2net.org
4+
# Local symlinks to avoid hitting od2net.org
55
web/public/osm
6+
web/public/boundaries

backend/src/auto_boundaries.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ impl MapModel {
5151
severances.push(linestring.clone());
5252
}
5353

54-
// TODO The boundary is imprecise, messing this process up
55-
for polygon in split_polygon(self.boundary_polygon.clone(), severances) {
54+
for polygon in split_polygon(self.mercator.to_mercator(&self.boundary_wgs84), severances) {
5655
let mut f = self.mercator.to_wgs84_gj(&polygon);
5756
f.set_property("kind", "area");
5857
features.push(f);

backend/src/lib.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,21 @@ pub struct LTN {
4545
impl LTN {
4646
/// Call with bytes of an osm.pbf or osm.xml string
4747
#[wasm_bindgen(constructor)]
48-
pub fn new(input_bytes: &[u8], study_area_name: Option<String>) -> Result<LTN, JsValue> {
48+
pub fn new(
49+
input_bytes: &[u8],
50+
boundary_input: JsValue,
51+
study_area_name: Option<String>,
52+
) -> Result<LTN, JsValue> {
4953
// Panics shouldn't happen, but if they do, console.log them.
5054
console_error_panic_hook::set_once();
5155
START.call_once(|| {
5256
console_log::init_with_level(log::Level::Info).unwrap();
5357
});
5458

55-
let map = MapModel::new(input_bytes, study_area_name).map_err(err_to_js)?;
59+
let boundary: Feature = serde_wasm_bindgen::from_value(boundary_input)?;
60+
let polygon = boundary.try_into().map_err(err_to_js)?;
61+
62+
let map = MapModel::new(input_bytes, polygon, study_area_name).map_err(err_to_js)?;
5663
Ok(LTN {
5764
map,
5865
neighbourhood: None,

backend/src/map_model.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use geo::{
66
Closest, ClosestPoint, Coord, Euclidean, Length, Line, LineInterpolatePoint, LineLocatePoint,
77
LineString, Point, Polygon,
88
};
9-
use geojson::{Feature, FeatureCollection, GeoJson};
9+
use geojson::{Feature, FeatureCollection, GeoJson, Geometry};
1010
use rstar::{primitives::GeomWithData, RTree, AABB};
1111
use serde::Serialize;
1212
use utils::{Mercator, Tags};
@@ -20,7 +20,7 @@ pub struct MapModel {
2020
// All geometry stored in worldspace, including rtrees
2121
pub mercator: Mercator,
2222
pub study_area_name: Option<String>,
23-
pub boundary_polygon: Polygon,
23+
pub boundary_wgs84: Polygon,
2424
pub closest_road: RTree<GeomWithData<LineString, RoadID>>,
2525

2626
// Only those acting as severances; above or belowground don't count
@@ -87,8 +87,12 @@ pub struct Intersection {
8787

8888
impl MapModel {
8989
/// Call with bytes of an osm.pbf or osm.xml string
90-
pub fn new(input_bytes: &[u8], study_area_name: Option<String>) -> Result<MapModel> {
91-
crate::scrape::scrape_osm(input_bytes, study_area_name)
90+
pub fn new(
91+
input_bytes: &[u8],
92+
boundary_wgs84: Polygon,
93+
study_area_name: Option<String>,
94+
) -> Result<MapModel> {
95+
crate::scrape::scrape_osm(input_bytes, boundary_wgs84, study_area_name)
9296
}
9397

9498
pub fn get_r(&self, r: RoadID) -> &Road {
@@ -355,7 +359,7 @@ impl MapModel {
355359

356360
gj.features.extend(self.boundaries.values().cloned());
357361

358-
let mut f = self.mercator.to_wgs84_gj(&self.boundary_polygon);
362+
let mut f = Feature::from(Geometry::from(&self.boundary_wgs84));
359363
f.set_property("kind", "study_area_boundary");
360364
gj.features.push(f);
361365

@@ -486,7 +490,6 @@ impl MapModel {
486490

487491
/// Return a polygon covering the world, minus a hole for the boundary, in WGS84
488492
pub fn invert_boundary(&self) -> Polygon {
489-
let (boundary, _) = self.mercator.to_wgs84(&self.boundary_polygon).into_inner();
490493
Polygon::new(
491494
LineString::from(vec![
492495
(180.0, 90.0),
@@ -495,7 +498,7 @@ impl MapModel {
495498
(180.0, -90.0),
496499
(180.0, 90.0),
497500
]),
498-
vec![boundary],
501+
vec![self.boundary_wgs84.exterior().clone()],
499502
)
500503
}
501504

backend/src/scrape.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
use std::collections::{BTreeMap, BTreeSet, HashMap};
22

33
use anyhow::Result;
4-
use geo::{Coord, LineString};
4+
use geo::{Coord, LineString, Polygon};
55
use osm_reader::{Element, NodeID};
66
use rstar::{primitives::GeomWithData, RTree};
77
use utils::Tags;
88

99
use crate::{Direction, FilterKind, Intersection, IntersectionID, MapModel, Road, RoadID, Router};
1010

11-
pub fn scrape_osm(input_bytes: &[u8], study_area_name: Option<String>) -> Result<MapModel> {
11+
pub fn scrape_osm(
12+
input_bytes: &[u8],
13+
boundary_wgs84: Polygon,
14+
study_area_name: Option<String>,
15+
) -> Result<MapModel> {
1216
info!("Parsing {} bytes of OSM data", input_bytes.len());
1317
// This doesn't use osm2graph's helper, because it needs to scrape more things from OSM
1418
let mut node_mapping = HashMap::new();
@@ -142,7 +146,7 @@ pub fn scrape_osm(input_bytes: &[u8], study_area_name: Option<String>) -> Result
142146
roads,
143147
intersections,
144148
mercator: graph.mercator,
145-
boundary_polygon: graph.boundary_polygon,
149+
boundary_wgs84,
146150
study_area_name,
147151
closest_road,
148152

backend/src/tests.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use anyhow::Result;
22
use geo::Polygon;
3-
use geojson::FeatureCollection;
3+
use geojson::{Feature, FeatureCollection};
44

55
use crate::{MapModel, Neighbourhood};
66

@@ -34,7 +34,13 @@ fn test_example(study_area_name: &str, savefile_name: &str, neighbourhood_name:
3434
// TODO web/public/osm must be symlinked to local PBF copies
3535
fn get_gj(study_area_name: &str, savefile_name: &str, neighbourhood_name: &str) -> Result<String> {
3636
let input_bytes = std::fs::read(format!("../web/public/osm/{study_area_name}.pbf"))?;
37-
let mut map = MapModel::new(&input_bytes, Some(study_area_name.to_string()))?;
37+
let boundary: Feature = std::fs::read_to_string(format!(
38+
"../web/public/boundaries/{study_area_name}.geojson"
39+
))?
40+
.parse()?;
41+
let polygon = boundary.try_into()?;
42+
43+
let mut map = MapModel::new(&input_bytes, polygon, Some(study_area_name.to_string()))?;
3844

3945
let savefile: FeatureCollection =
4046
std::fs::read_to_string(format!("../tests/{savefile_name}.geojson"))?.parse()?;

web/package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/src/title/NewProjectMode.svelte

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<script lang="ts">
2+
import type { Feature, Polygon } from "geojson";
23
import { LTN } from "backend";
34
import { onMount } from "svelte";
45
import { Loading } from "svelte-utils";
@@ -30,10 +31,14 @@
3031
exampleAreas = await resp.json();
3132
});
3233
33-
function gotXml(e: CustomEvent<string>) {
34+
function gotXml(e: CustomEvent<{ xml: string; boundary: Feature<Polygon> }>) {
3435
loading = "Loading OSM";
3536
try {
36-
$app = new LTN(new TextEncoder().encode(e.detail), undefined);
37+
$app = new LTN(
38+
new TextEncoder().encode(e.detail.xml),
39+
e.detail.boundary,
40+
undefined,
41+
);
3742
$projectName = `ltn_${newProjectName}`;
3843
afterProjectLoaded();
3944
// No savefile to load. Create it immediately with just the boundary

web/src/title/loader.ts

+25-11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { get } from "svelte/store";
22
import { LngLat } from "maplibre-gl";
33
import { LTN } from "backend";
4-
import type { Feature } from "geojson";
4+
import type { Feature, Polygon } from "geojson";
55
import { overpassQueryForPolygon } from "svelte-utils/overpass";
66
import { RouteTool } from "route-snapper-ts";
77
import {
@@ -28,10 +28,16 @@ export async function loadFromLocalStorage(key: string) {
2828
let gj = JSON.parse(window.localStorage.getItem(key)!);
2929

3030
console.time("get OSM input");
31-
let buffer = await getOsmInput(gj);
31+
let [buffer, boundary] = await getOsmInput(gj);
3232
console.timeEnd("get OSM input");
3333
console.time("load");
34-
app.set(new LTN(new Uint8Array(buffer), gj.study_area_name || undefined));
34+
app.set(
35+
new LTN(
36+
new Uint8Array(buffer),
37+
boundary,
38+
gj.study_area_name || undefined,
39+
),
40+
);
3541
// TODO Rename savefile -> project? Or combine this call with the constructor?
3642
get(app)!.loadSavefile(gj);
3743
console.timeEnd("load");
@@ -43,24 +49,32 @@ export async function loadFromLocalStorage(key: string) {
4349
}
4450
}
4551

46-
// Either from a pre-hosted pbf file or from Overpass
47-
async function getOsmInput(gj: any): Promise<ArrayBuffer> {
52+
// Returns OSM input and the boundary polygon, either from a pre-hosted pbf
53+
// file or from Overpass.
54+
async function getOsmInput(gj: any): Promise<[ArrayBuffer, Feature<Polygon>]> {
4855
if (gj.study_area_name) {
49-
let url = get(useLocalVite)
56+
let url1 = get(useLocalVite)
5057
? `/osm/${gj.study_area_name}.pbf`
5158
: `https://assets.od2net.org/severance_pbfs/${gj.study_area_name}.pbf`;
52-
console.log(`Grabbing ${url}`);
53-
let resp = await fetch(url);
54-
let bytes = await resp.arrayBuffer();
55-
return bytes;
59+
console.log(`Grabbing ${url1}`);
60+
let resp1 = await fetch(url1);
61+
let bytes = await resp1.arrayBuffer();
62+
63+
let url2 = get(useLocalVite)
64+
? `/boundaries/${gj.study_area_name}.geojson`
65+
: `https://assets.od2net.org/boundaries/${gj.study_area_name}.geojson`;
66+
let resp2 = await fetch(url2);
67+
let boundary = await resp2.json();
68+
69+
return [bytes, boundary];
5670
} else {
5771
console.log(`Grabbing from Overpass`);
5872
let study_area_boundary = gj.features.find(
5973
(f: Feature) => f.properties!.kind == "study_area_boundary",
6074
)!;
6175
let resp = await fetch(overpassQueryForPolygon(study_area_boundary));
6276
let bytes = await resp.arrayBuffer();
63-
return bytes;
77+
return [bytes, study_area_boundary];
6478
}
6579
}
6680

0 commit comments

Comments
 (0)