From 9c785d621925451826defa920207b1bc2f4b13b9 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Wed, 5 Jun 2024 22:29:58 +0100 Subject: [PATCH] Clean up URL code --- .../layers/areas/CensusOutputAreas.svelte | 8 ++--- src/lib/browse/layers/areas/Pollution.svelte | 8 ++--- src/lib/browse/layers/lines/CyclePaths.svelte | 31 +++++++++---------- src/lib/browse/layers/lines/PCT.svelte | 13 +++----- src/lib/browse/layers/lines/RoadSpeeds.svelte | 13 +++----- src/lib/browse/layers/points/Education.svelte | 29 ++++++++--------- src/lib/browse/layers/points/Stats19.svelte | 8 ++--- src/lib/browse/layers/url.ts | 11 +++++-- 8 files changed, 57 insertions(+), 64 deletions(-) diff --git a/src/lib/browse/layers/areas/CensusOutputAreas.svelte b/src/lib/browse/layers/areas/CensusOutputAreas.svelte index 21304122..1c61a209 100644 --- a/src/lib/browse/layers/areas/CensusOutputAreas.svelte +++ b/src/lib/browse/layers/areas/CensusOutputAreas.svelte @@ -17,7 +17,7 @@ import { colors } from "../../colors"; import OsOglLicense from "../OsOglLicense.svelte"; import SequentialLegend from "../SequentialLegend.svelte"; - import { customUrl } from "../url"; + import { customUrlState } from "../url"; let name = "census_output_areas"; let colorScale = colors.sequential_low_to_high; @@ -33,13 +33,13 @@ function stringify(x: State): string | null { return x.show ? x.kind : null; } - function parse(result: string): State { + function parse(x: string): State { return { show: true, - kind: result, + kind: x, }; } - let state = customUrl(name, defaultState, stringify, parse); + let state = customUrlState(name, defaultState, stringify, parse); // Mutually exclusive, like a radio button. We need these for checkboxes to work let showHouseholdsWithCar = $state.kind == "percent_households_with_car"; diff --git a/src/lib/browse/layers/areas/Pollution.svelte b/src/lib/browse/layers/areas/Pollution.svelte index 0d9c562b..fa029046 100644 --- a/src/lib/browse/layers/areas/Pollution.svelte +++ b/src/lib/browse/layers/areas/Pollution.svelte @@ -4,7 +4,7 @@ import { layerId } from "lib/maplibre"; import { RasterLayer, RasterTileSource } from "svelte-maplibre"; import OsOglLicense from "../OsOglLicense.svelte"; - import { customUrl } from "../url"; + import { customUrlState } from "../url"; type State = { show: boolean; @@ -19,15 +19,15 @@ function stringify(x: State): string | null { return x.show ? `${x.pollutant}/${x.opacity}` : null; } - function parse(result: string): State { - let [pollutant, opacity] = result.split("/"); + function parse(x: string): State { + let [pollutant, opacity] = x.split("/"); return { show: true, pollutant, opacity: parseInt(opacity), }; } - let state = customUrl("pollution", defaultState, stringify, parse); + let state = customUrlState("pollution", defaultState, stringify, parse); // URLs and layers found from https://uk-air.defra.gov.uk/data/wms-services // and QGIS diff --git a/src/lib/browse/layers/lines/CyclePaths.svelte b/src/lib/browse/layers/lines/CyclePaths.svelte index 0ad51eaf..e621b69d 100644 --- a/src/lib/browse/layers/lines/CyclePaths.svelte +++ b/src/lib/browse/layers/lines/CyclePaths.svelte @@ -16,12 +16,12 @@ } from "svelte-maplibre"; import { colors, denseLineWidth } from "../../colors"; import OsmLicense from "../OsmLicense.svelte"; - import { customUrl } from "../url"; + import { customUrlState } from "../url"; let name = "cycle_paths"; type State = { - group: boolean; + show: boolean; track: boolean; lane: boolean; shared_use_segregated: boolean; @@ -34,29 +34,26 @@ "shared_use_unsegregated", ] as const; let defaultState = { - group: false, + show: false, track: true, lane: true, shared_use_segregated: true, shared_use_unsegregated: true, }; function stringify(x: State): string | null { - if (!x.group) { - return null; - } - return keys.filter((c) => x[c]).join(","); + return x.show ? keys.filter((c) => x[c]).join(",") : null; } - function parse(result: string): State { + function parse(x: string): State { return { - group: true, - track: result.includes("track"), - lane: result.includes("lane"), - shared_use_segregated: result.includes("shared_use_segregated"), - shared_use_unsegregated: result.includes("shared_use_unsegregated"), + show: true, + track: x.includes("track"), + lane: x.includes("lane"), + shared_use_segregated: x.includes("shared_use_segregated"), + shared_use_unsegregated: x.includes("shared_use_unsegregated"), }; } - let state = customUrl(name, defaultState, stringify, parse); + let state = customUrlState(name, defaultState, stringify, parse); let legend = [ ["track", "Separated tracks", colors.cycle_paths.track], @@ -104,7 +101,7 @@ } - + Cycle paths @@ -142,7 +139,7 @@ -{#if $state.group} +{#if $state.show}
{#each legend as [kind, label, color]} @@ -176,7 +173,7 @@ "line-opacity": hoverStateFilter(1.0, 0.5), }} layout={{ - visibility: $state.group ? "visible" : "none", + visibility: $state.show ? "visible" : "none", }} filter={makeFilter($state)} manageHoverState diff --git a/src/lib/browse/layers/lines/PCT.svelte b/src/lib/browse/layers/lines/PCT.svelte index fd0f0adf..5867a86b 100644 --- a/src/lib/browse/layers/lines/PCT.svelte +++ b/src/lib/browse/layers/lines/PCT.svelte @@ -14,7 +14,7 @@ } from "svelte-maplibre"; import { colors, denseLineWidth } from "../../colors"; import SequentialLegend from "../SequentialLegend.svelte"; - import { customUrl } from "../url"; + import { customUrlState } from "../url"; // TODO It'd be much simpler to have one source with both attributes let nameCommute = "pct_commute"; @@ -33,20 +33,17 @@ scenario: "baseline", }; function stringify(x: State): string | null { - if (!x.show) { - return null; - } - return `${x.tripPurpose}/${x.scenario}`; + return x.show ? `${x.tripPurpose}/${x.scenario}` : null; } - function parse(result: string): State { - let [tripPurpose, scenario] = result.split("/"); + function parse(x: string): State { + let [tripPurpose, scenario] = x.split("/"); return { show: true, tripPurpose, scenario, }; } - let state = customUrl("pct", defaultState, stringify, parse); + let state = customUrlState("pct", defaultState, stringify, parse); // TODO Don't use a function and @html; do everything in Svelte? function tooltip(props: { [name: string]: any }): string { diff --git a/src/lib/browse/layers/lines/RoadSpeeds.svelte b/src/lib/browse/layers/lines/RoadSpeeds.svelte index 07ab0dbb..c464e4f6 100644 --- a/src/lib/browse/layers/lines/RoadSpeeds.svelte +++ b/src/lib/browse/layers/lines/RoadSpeeds.svelte @@ -14,7 +14,7 @@ } from "svelte-maplibre"; import { colors, denseLineWidth } from "../../colors"; import SequentialLegend from "../SequentialLegend.svelte"; - import { customUrl } from "../url"; + import { customUrlState } from "../url"; let name = "road_speeds"; let colorScale = colors.sequential_low_to_high; @@ -29,18 +29,15 @@ kind: "indicative_mph", }; function stringify(x: State): string | null { - if (!x.show) { - return null; - } - return x.kind; + return x.show ? x.kind : null; } - function parse(result: string): State { + function parse(x: string): State { return { show: true, - kind: result, + kind: x, }; } - let state = customUrl(name, defaultState, stringify, parse); + let state = customUrlState(name, defaultState, stringify, parse); let times: Record = { mf4to7: "Monday-Friday 4-7am", diff --git a/src/lib/browse/layers/points/Education.svelte b/src/lib/browse/layers/points/Education.svelte index 20de5450..e84bc89d 100644 --- a/src/lib/browse/layers/points/Education.svelte +++ b/src/lib/browse/layers/points/Education.svelte @@ -15,39 +15,36 @@ } from "svelte-maplibre"; import { colors } from "../../colors"; import OsmLicense from "../OsmLicense.svelte"; - import { customUrl } from "../url"; + import { customUrlState } from "../url"; let name = "education"; type State = { - group: boolean; + show: boolean; school: boolean; college: boolean; university: boolean; }; let keys = ["school", "college", "university"] as const; let defaultState = { - group: false, + show: false, school: true, college: true, university: true, }; function stringify(x: State): string | null { - if (!x.group) { - return null; - } - return keys.filter((c) => x[c]).join(","); + return x.show ? keys.filter((c) => x[c]).join(",") : null; } - function parse(result: string): State { + function parse(x: string): State { return { - group: true, - school: result.includes("school"), - college: result.includes("college"), - university: result.includes("university"), + show: true, + school: x.includes("school"), + college: x.includes("college"), + university: x.includes("university"), }; } - let state = customUrl(name, defaultState, stringify, parse); + let state = customUrlState(name, defaultState, stringify, parse); function makeFilter(state: State): ExpressionSpecification { let include = keys.filter((l) => state[l]); @@ -55,7 +52,7 @@ } - + Education @@ -67,7 +64,7 @@ -{#if $state.group} +{#if $state.show}
@@ -105,7 +102,7 @@ "fill-opacity": hoverStateFilter(0.7, 1.0), }} layout={{ - visibility: $state.group ? "visible" : "none", + visibility: $state.show ? "visible" : "none", }} filter={makeFilter($state)} manageHoverState diff --git a/src/lib/browse/layers/points/Stats19.svelte b/src/lib/browse/layers/points/Stats19.svelte index addfe030..89bdf10d 100644 --- a/src/lib/browse/layers/points/Stats19.svelte +++ b/src/lib/browse/layers/points/Stats19.svelte @@ -16,7 +16,7 @@ } from "svelte-maplibre"; import { colors } from "../../colors"; import OsOglLicense from "../OsOglLicense.svelte"; - import { customUrl } from "../url"; + import { customUrlState } from "../url"; let name = "stats19"; @@ -46,8 +46,8 @@ let bools = keys.filter((c) => x[c]).join(","); return `${bools}/${x.minYear}/${x.maxYear}`; } - function parse(result: string): State { - let [bools, minYear, maxYear] = result.split("/"); + function parse(x: string): State { + let [bools, minYear, maxYear] = x.split("/"); return { show: true, pedestrians: bools.includes("pedestrians"), @@ -59,7 +59,7 @@ }; } - let state = customUrl(name, defaultState, stringify, parse); + let state = customUrlState(name, defaultState, stringify, parse); $: filter = makeFilter( $state.minYear, diff --git a/src/lib/browse/layers/url.ts b/src/lib/browse/layers/url.ts index 5589bb8a..9e99ebc6 100644 --- a/src/lib/browse/layers/url.ts +++ b/src/lib/browse/layers/url.ts @@ -1,5 +1,7 @@ import { writable, type Writable } from "svelte/store"; +// Create a store to represent whether a layer should be shown or hidden. The +// state is synced as a URL query parameter. export function showHideLayer(name: string): Writable { let initialValue = new URLSearchParams(window.location.search).has(name); let store = writable(initialValue); @@ -16,11 +18,14 @@ export function showHideLayer(name: string): Writable { return store; } -export function customUrl( +// Create a store to represent some state about a layer, syncing it to a URL +// query parameter. The parameter missing is equivalent to stringify returning +// null. +export function customUrlState( name: string, defaultValue: T, - stringify: (x: T) => string | null, - parse: (x: string) => T, + stringify: (state: T) => string | null, + parse: (param: string) => T, ): Writable { let param = new URLSearchParams(window.location.search).get(name); let initialValue = param == null ? defaultValue : parse(param);