Lots of frontend changes
This commit is contained in:
parent
ec29631c44
commit
555ba7cf53
38 changed files with 1508 additions and 648 deletions
|
|
@ -3,40 +3,94 @@ import type { StyleSpecification } from 'maplibre-gl';
|
|||
import { layers, namedFlavor } from '@protomaps/basemaps';
|
||||
import {
|
||||
GLYPHS_URL,
|
||||
SPRITE_URL_BASE,
|
||||
TILE_MAX_ZOOM,
|
||||
OSM_ATTRIBUTION,
|
||||
FEATURE_GRADIENT,
|
||||
DENSITY_GRADIENT,
|
||||
DENSITY_GRADIENT_DARK,
|
||||
ZOOM_TO_RESOLUTION_THRESHOLDS,
|
||||
TWEMOJI_BASE,
|
||||
POSTCODE_ZOOM_THRESHOLD,
|
||||
} from './consts';
|
||||
|
||||
// Re-export constants for backwards compatibility
|
||||
export { FEATURE_GRADIENT as GRADIENT, DENSITY_GRADIENT, POSTCODE_ZOOM_THRESHOLD } from './consts';
|
||||
export { FEATURE_GRADIENT as GRADIENT, DENSITY_GRADIENT, DENSITY_GRADIENT_DARK, POSTCODE_ZOOM_THRESHOLD } from './consts';
|
||||
|
||||
const ROAD_OPACITY = 0.4;
|
||||
|
||||
export function getMapStyle(theme: 'light' | 'dark'): StyleSpecification {
|
||||
const flavor = namedFlavor(theme);
|
||||
// Use absolute URL for tiles - required by MapLibre
|
||||
const tileUrl = `${window.location.origin}/api/tiles/{z}/{x}/{y}`;
|
||||
const baseLayers = layers('protomaps', flavor, { lang: 'en' });
|
||||
|
||||
// Reduce road layer opacity so hexagons are more visible
|
||||
const modifiedLayers = baseLayers.map((layer) => {
|
||||
if (layer.id.includes('roads_') || layer.id.includes('road_')) {
|
||||
if (layer.type === 'line') {
|
||||
return { ...layer, paint: { ...layer.paint, 'line-opacity': ROAD_OPACITY } };
|
||||
} else if (layer.type === 'fill') {
|
||||
return { ...layer, paint: { ...layer.paint, 'fill-opacity': ROAD_OPACITY } };
|
||||
}
|
||||
}
|
||||
return layer;
|
||||
});
|
||||
|
||||
return {
|
||||
version: 8,
|
||||
glyphs: GLYPHS_URL,
|
||||
sprite: `${SPRITE_URL_BASE}/${theme}`,
|
||||
sources: {
|
||||
protomaps: {
|
||||
type: 'vector',
|
||||
tiles: [tileUrl],
|
||||
maxzoom: TILE_MAX_ZOOM,
|
||||
attribution: OSM_ATTRIBUTION,
|
||||
maxzoom: POSTCODE_ZOOM_THRESHOLD,
|
||||
},
|
||||
},
|
||||
layers: layers('protomaps', flavor, { lang: 'en' }),
|
||||
layers: modifiedLayers,
|
||||
} as StyleSpecification;
|
||||
}
|
||||
|
||||
type GradientStop = { t: number; color: [number, number, number] };
|
||||
|
||||
// Oklab color space for perceptually uniform interpolation
|
||||
function srgbToLinear(c: number): number {
|
||||
const v = c / 255;
|
||||
return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
||||
}
|
||||
|
||||
function linearToSrgb(c: number): number {
|
||||
const v = c <= 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
|
||||
return Math.round(Math.max(0, Math.min(255, v * 255)));
|
||||
}
|
||||
|
||||
function rgbToOklab(rgb: [number, number, number]): [number, number, number] {
|
||||
const r = srgbToLinear(rgb[0]);
|
||||
const g = srgbToLinear(rgb[1]);
|
||||
const b = srgbToLinear(rgb[2]);
|
||||
|
||||
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
||||
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
||||
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
||||
|
||||
return [
|
||||
0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,
|
||||
1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,
|
||||
0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s,
|
||||
];
|
||||
}
|
||||
|
||||
function oklabToRgb(lab: [number, number, number]): [number, number, number] {
|
||||
const L = lab[0], a = lab[1], b = lab[2];
|
||||
|
||||
const l = Math.pow(L + 0.3963377774 * a + 0.2158037573 * b, 3);
|
||||
const m = Math.pow(L - 0.1055613458 * a - 0.0638541728 * b, 3);
|
||||
const s = Math.pow(L - 0.0894841775 * a - 1.2914855480 * b, 3);
|
||||
|
||||
return [
|
||||
linearToSrgb(+4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),
|
||||
linearToSrgb(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),
|
||||
linearToSrgb(-0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s),
|
||||
];
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -46,11 +100,14 @@ function interpolateGradient(t: number, gradient: GradientStop[]): [number, numb
|
|||
const hi = 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),
|
||||
const loLab = rgbToOklab(lo.color);
|
||||
const hiLab = rgbToOklab(hi.color);
|
||||
const interpLab: [number, number, number] = [
|
||||
loLab[0] + (hiLab[0] - loLab[0]) * frac,
|
||||
loLab[1] + (hiLab[1] - loLab[1]) * frac,
|
||||
loLab[2] + (hiLab[2] - loLab[2]) * frac,
|
||||
];
|
||||
return oklabToRgb(interpLab);
|
||||
}
|
||||
}
|
||||
return gradient[gradient.length - 1].color;
|
||||
|
|
@ -60,8 +117,8 @@ export function normalizedToColor(t: number): [number, number, number] {
|
|||
return interpolateGradient(t, FEATURE_GRADIENT);
|
||||
}
|
||||
|
||||
export function countToColor(t: number): [number, number, number] {
|
||||
return interpolateGradient(t, DENSITY_GRADIENT);
|
||||
export function countToColor(t: number, gradient: GradientStop[] = DENSITY_GRADIENT): [number, number, number] {
|
||||
return interpolateGradient(t, gradient);
|
||||
}
|
||||
|
||||
export function zoomToResolution(zoom: number): number {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue