Refactor map page

This commit is contained in:
Andras Schmelczer 2026-02-07 14:34:17 +00:00
parent 29d048ffd4
commit d4d79f0d99
17 changed files with 1014 additions and 878 deletions

View file

@ -4,7 +4,7 @@ const INITIAL_RETRY_MS = 1000;
const MAX_RETRY_MS = 10000;
// Error handling utilities
export function isAbortError(error: unknown): boolean {
function isAbortError(error: unknown): boolean {
return error instanceof Error && error.name === 'AbortError';
}

View file

@ -1,13 +1,6 @@
import type { ViewState } from '../types';
// =============================================================================
// Map Bounds & Zoom
// =============================================================================
/** Geographic bounds constraining map panning [west, south, east, north] */
export const MAP_BOUNDS: [number, number, number, number] = [-9.5, 49, 5, 57];
/** Minimum zoom level (can't zoom out further) */
export const MAP_MIN_ZOOM = 5.5;
/** Maximum zoom level for tile fetching (map extrapolates beyond this) */
@ -21,12 +14,6 @@ export const INITIAL_VIEW_STATE: ViewState = {
pitch: 0,
};
// =============================================================================
// Zoom Thresholds
// =============================================================================
/** Zoom level at which we switch from H3 hexagons to postcode polygons */
export const POSTCODE_ZOOM_THRESHOLD = 15;
/**
* Zoom to H3 resolution mapping thresholds.
@ -40,6 +27,8 @@ export const ZOOM_TO_RESOLUTION_THRESHOLDS = [
{ maxZoom: 13, resolution: 9 },
{ maxZoom: Infinity, resolution: 10 },
] as const;
export const POSTCODE_ZOOM_THRESHOLD = 15;
// =============================================================================
// Color Gradients

View file

@ -1,9 +1,4 @@
import type { FeatureMeta } from '../types';
export interface FeatureGroup {
name: string;
features: FeatureMeta[];
}
import type { FeatureMeta, FeatureGroup } from '../types';
export function groupFeaturesByCategory(features: FeatureMeta[]): FeatureGroup[] {
const groups: FeatureGroup[] = [];

View file

@ -35,13 +35,15 @@ export function getMapStyle(theme: 'light' | 'dark'): StyleSpecification {
} as StyleSpecification;
}
export function normalizedToColor(t: number): [number, number, number] {
if (t <= 0) return FEATURE_GRADIENT[0].color;
if (t >= 1) return FEATURE_GRADIENT[FEATURE_GRADIENT.length - 1].color;
type GradientStop = { t: number; color: [number, number, number] };
for (let i = 0; i < FEATURE_GRADIENT.length - 1; i++) {
const lo = FEATURE_GRADIENT[i];
const hi = FEATURE_GRADIENT[i + 1];
function interpolateGradient(t: number, gradient: GradientStop[]): [number, number, number] {
if (t <= 0) return gradient[0].color;
if (t >= 1) return gradient[gradient.length - 1].color;
for (let i = 0; i < gradient.length - 1; i++) {
const lo = gradient[i];
const hi = gradient[i + 1];
if (t >= lo.t && t <= hi.t) {
const frac = (t - lo.t) / (hi.t - lo.t);
return [
@ -51,26 +53,15 @@ export function normalizedToColor(t: number): [number, number, number] {
];
}
}
return FEATURE_GRADIENT[FEATURE_GRADIENT.length - 1].color;
return gradient[gradient.length - 1].color;
}
export function normalizedToColor(t: number): [number, number, number] {
return interpolateGradient(t, FEATURE_GRADIENT);
}
export function countToColor(t: number): [number, number, number] {
if (t <= 0) return DENSITY_GRADIENT[0].color;
if (t >= 1) return DENSITY_GRADIENT[DENSITY_GRADIENT.length - 1].color;
for (let i = 0; i < DENSITY_GRADIENT.length - 1; i++) {
const lo = DENSITY_GRADIENT[i];
const hi = DENSITY_GRADIENT[i + 1];
if (t >= lo.t && t <= hi.t) {
const frac = (t - lo.t) / (hi.t - lo.t);
return [
Math.round(lo.color[0] + (hi.color[0] - lo.color[0]) * frac),
Math.round(lo.color[1] + (hi.color[1] - lo.color[1]) * frac),
Math.round(lo.color[2] + (hi.color[2] - lo.color[2]) * frac),
];
}
}
return DENSITY_GRADIENT[DENSITY_GRADIENT.length - 1].color;
return interpolateGradient(t, DENSITY_GRADIENT);
}
export function zoomToResolution(zoom: number): number {

View file

@ -1,12 +1,5 @@
import type { FeatureMeta, FeatureFilters, ViewState } from '../types';
export const DEFAULT_VIEW: ViewState = {
longitude: -1.5,
latitude: 53.5,
zoom: 6,
pitch: 0,
};
export function parseUrlState(): {
viewState?: ViewState;
filters?: FeatureFilters;