Skip to content

Commit

Permalink
Generate pmtiles for both roads and OAs from the Rust tool. Only handle
Browse files Browse the repository at this point in the history
pmtiles in the web app.

Reasoning: OAs are 2GB as GJ for all of England. Handling two possible
formats is unnecessary complexity. tippecanoe is fast to run for small
development areas anyway.
  • Loading branch information
dabreegster committed Aug 24, 2024
1 parent 96c12c6 commit 2d41140
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 81 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ pavement_parking/inputs
pavement_parking/web/public/summaries.geojson
pavement_parking/web/public/pavements.geojson
pavement_parking/web/public/pavements.pmtiles
pavement_parking/web/public/out.geojson
pavement_parking/web/public/output_areas.geojson
pavement_parking/web/public/output_areas.pmtiles
19 changes: 3 additions & 16 deletions pavement_parking/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,6 @@
1. Run `./get_boundaries.sh` once to generate `inputs/boundaries.geojson`
2. Modify (as needed) and run `./car_ownership.sh` to generate `inputs/car_ownership.geojson`
3. Acquire the OS input and run `cargo run --release path/to/trn_ntwk_roadlink.gpkg`
4. (Optionally / for large inputs) Turn that into pmtiles:

```
time tippecanoe web/public/pavements.geojson \
--force \
--generate-ids \
-l pavements \
-Z10 -z11 \
--drop-densest-as-needed \
--extend-zooms-if-still-dropping \
-o web/public/pavements.pmtiles
```

5. Set up the web app: `cd web; npm i`
6. Run it: `npm run dev`
7. Visit <http://localhost:5173/will-it-fit/index.html?data=/pavement.pmtiles> or <http://localhost:5173/will-it-fit/index.html?data=/out.geojson>
4. Set up the web app: `cd web; npm i`
5. Run it: `npm run dev`
6. Visit <http://localhost:5173/will-it-fit/index.html?data=/pavement.pmtiles>
32 changes: 31 additions & 1 deletion pavement_parking/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::io::BufWriter;
use std::process::Command;

use anyhow::Result;
use anyhow::{bail, Result};
use fs_err::File;
use gdal::vector::LayerAccess;
use gdal::Dataset;
Expand Down Expand Up @@ -56,6 +57,16 @@ fn main() -> Result<()> {
census_areas.write_output(output_areas_output_path)?;

println!("\n\nWrote '{summaries_output_path}', '{pavements_output_path}' and '{output_areas_output_path}'");

tippecanoe(
&pavements_output_path,
&pavements_output_path.replace("geojson", "pmtiles"),
)?;
tippecanoe(
&output_areas_output_path,
&output_areas_output_path.replace("geojson", "pmtiles"),
)?;

Ok(())
}

Expand All @@ -77,3 +88,22 @@ fn handle_road(

Ok(())
}

fn tippecanoe(input: &str, output: &str) -> Result<()> {
let mut cmd = Command::new("tippecanoe");
cmd.arg(input)
.arg("-o")
.arg(output)
.arg("--force") // Overwrite existing output
.arg("--generate-ids")
.arg("-l")
.arg("pavements")
.arg("-Z10")
.arg("-z11")
.arg("--drop-densest-as-needed");
println!("Running: {cmd:?}");
if !cmd.status()?.success() {
bail!("tippecanoe failed");
}
Ok(())
}
27 changes: 7 additions & 20 deletions pavement_parking/web/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
import "@picocss/pico/css/pico.jade.min.css";
import { Layout } from "svelte-utils/two_column_layout";
import {
GeoJSON,
VectorTileSource,
MapLibre,
MapEvents,
type LngLatBoundsLike,
Expand All @@ -26,16 +24,12 @@
let params = new URLSearchParams(window.location.search);
let pavementsUrl = params.get("data") || "";
let summaryUrl = pavementsUrl.replace(
pavementsUrl.endsWith(".geojson")
? "pavements.geojson"
: "pavements.pmtiles",
"pavements.pmtiles",
"summaries.geojson",
);
let censusUrl = pavementsUrl.replace(
pavementsUrl.endsWith(".geojson")
? "pavements.geojson"
: "pavements.pmtiles",
"output_areas.geojson",
"pavements.pmtiles",
"output_areas.pmtiles",
);
let map: Map;
Expand Down Expand Up @@ -83,6 +77,8 @@
{:else}
<p>Zoom in more to see roads</p>
{/if}
{:else if show == "census-area" && zoom < 10}
<p>Zoom in more to see Output Areas</p>
{/if}
</div>

Expand All @@ -95,16 +91,7 @@
>
<MapEvents on:zoom={onZoom} />

{#if pavementsUrl.endsWith(".geojson")}
<GeoJSON data={pavementsUrl} generateId>
<PavementLayers {show} {roadFilters} sourceLayer={undefined} />
</GeoJSON>
{:else if pavementsUrl.endsWith(".pmtiles")}
<VectorTileSource url={`pmtiles://${pavementsUrl}`}>
<PavementLayers {show} {roadFilters} sourceLayer="pavements" />
</VectorTileSource>
{/if}

<PavementLayers {show} url={pavementsUrl} {roadFilters} />
<SummaryLayers {show} url={summaryUrl} />
<OutputAreas {show} url={censusUrl} />
</MapLibre>
Expand All @@ -113,7 +100,7 @@
{:else}
<p>
Data source not specified. If you're developing locally, try <a
href="index.html?data=/pavements.geojson"
href="index.html?data=/pavements.pmtiles"
>
this
</a>
Expand Down
7 changes: 4 additions & 3 deletions pavement_parking/web/src/OutputAreas.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import {
GeoJSON,
VectorTileSource,
LineLayer,
Popup,
hoverStateFilter,
Expand Down Expand Up @@ -37,8 +37,9 @@
// TS gets confused by the maplibre expression, so typecast
</script>

<GeoJSON data={url} generateId>
<VectorTileSource url={`pmtiles://${url}`}>
<FillLayer
sourceLayer="pavements"
layout={{ visibility: show == "census-area" ? "visible" : "none" }}
manageHoverState
paint={{
Expand Down Expand Up @@ -72,4 +73,4 @@
"line-color": "black",
}}
/>
</GeoJSON>
</VectorTileSource>
87 changes: 47 additions & 40 deletions pavement_parking/web/src/PavementLayers.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
<script lang="ts">
import { constructMatchExpression } from "svelte-utils/map";
import { LineLayer, Popup, hoverStateFilter } from "svelte-maplibre";
import {
LineLayer,
Popup,
VectorTileSource,
hoverStateFilter,
} from "svelte-maplibre";
import { colors, scenarios, type Mode, type Filters } from "./types";
import type { ExpressionSpecification } from "maplibre-gl";
export let show: Mode;
export let url: string;
export let roadFilters: Filters;
export let sourceLayer: string | undefined;
function makeFilter(f: Filters): ExpressionSpecification {
let ratings = Object.entries(f.showRatings)
Expand All @@ -28,45 +33,47 @@
}
</script>

<LineLayer
{sourceLayer}
filter={makeFilter(roadFilters)}
layout={{ visibility: show == "roads" ? "visible" : "none" }}
manageHoverState
paint={{
"line-width": hoverStateFilter(5, 10),
"line-color": constructMatchExpression(
["get", `rating_${roadFilters.scenario}`],
colors,
"black",
),
}}
beforeId="Road numbers"
>
<Popup openOn="hover" let:data>
{#if data?.properties}
<h1>{data.properties.name || "Unnamed road"}</h1>
<p>Class: {data.properties.class} road</p>
<p>Direction: {data.properties.direction}</p>
<p>Length: {data.properties.length} meters</p>
<VectorTileSource url={`pmtiles://${url}`}>
<LineLayer
sourceLayer="pavements"
filter={makeFilter(roadFilters)}
layout={{ visibility: show == "roads" ? "visible" : "none" }}
manageHoverState
paint={{
"line-width": hoverStateFilter(5, 10),
"line-color": constructMatchExpression(
["get", `rating_${roadFilters.scenario}`],
colors,
"black",
),
}}
beforeId="Road numbers"
>
<Popup openOn="hover" let:data>
{#if data?.properties}
<h1>{data.properties.name || "Unnamed road"}</h1>
<p>Class: {data.properties.class} road</p>
<p>Direction: {data.properties.direction}</p>
<p>Length: {data.properties.length} meters</p>

<hr />
<hr />

<p>
Carriageway width: {data.properties.road_average_width} average, {data
.properties.road_minimum_width} minimum
</p>
<p>Pavement average width: {data.properties.pavement_average_width}</p>

<hr />

{#each scenarios as scenario}
<p>
In scenario {scenario}, rating is {data.properties[
`rating_${scenario}`
]}
Carriageway width: {data.properties.road_average_width} average, {data
.properties.road_minimum_width} minimum
</p>
{/each}
{/if}
</Popup>
</LineLayer>
<p>Pavement average width: {data.properties.pavement_average_width}</p>

<hr />

{#each scenarios as scenario}
<p>
In scenario {scenario}, rating is {data.properties[
`rating_${scenario}`
]}
</p>
{/each}
{/if}
</Popup>
</LineLayer>
</VectorTileSource>

0 comments on commit 2d41140

Please sign in to comment.