Fun changes
Some checks failed
CI / Python (lint + test) (push) Failing after 3m38s
CI / Rust (lint + test) (push) Failing after 3m32s
CI / Frontend (lint + typecheck) (push) Failing after 4m12s
Build and publish Docker image / build-and-push (push) Failing after 4m48s

This commit is contained in:
Andras Schmelczer 2026-04-04 22:59:44 +01:00
parent cd778dd088
commit 349a6c1d53
60 changed files with 1260 additions and 2600 deletions

View file

@ -109,6 +109,8 @@ export const STACKED_GROUPS: Record<
label: string;
/** If set, use this feature's stats for the total and info popup. Otherwise sum components. */
feature?: string;
/** If set, display this feature's mean as the primary value (e.g. per-1k rate) instead of the absolute total. */
rateFeature?: string;
/** Suffix shown after the total value (e.g. "avg/yr") */
unit?: string;
/** Feature names that make up the segments */
@ -119,7 +121,8 @@ export const STACKED_GROUPS: Record<
{
label: 'Serious crime',
feature: 'Serious crime (avg/yr)',
unit: 'avg/yr',
rateFeature: 'Serious crime per 1k residents (avg/yr)',
unit: 'per 1k/yr',
components: [
'Violence and sexual offences (avg/yr)',
'Robbery (avg/yr)',
@ -130,7 +133,8 @@ export const STACKED_GROUPS: Record<
{
label: 'Minor crime',
feature: 'Minor crime (avg/yr)',
unit: 'avg/yr',
rateFeature: 'Minor crime per 1k residents (avg/yr)',
unit: 'per 1k/yr',
components: [
'Anti-social behaviour (avg/yr)',
'Criminal damage and arson (avg/yr)',
@ -179,7 +183,7 @@ export const STACKED_ENUM_GROUPS: Record<
feature: 'Property type',
components: ['Property type'],
valueOrder: ['Detached', 'Semi-Detached', 'Terraced', 'Flats/Maisonettes', 'Other'],
valueColors: ['#8b5cf6', '#3b82f6', '#14b8a6', '#f59e0b', '#6b7280'],
valueColors: ['#f97316', '#3b82f6', '#22c55e', '#ec4899', '#6b7280'],
},
{
label: 'Leasehold/Freehold',
@ -208,6 +212,60 @@ export const ENUM_PALETTE: [number, number, number][] = [
[107, 114, 128], // gray-500
];
/**
* Per-feature color overrides for enum values on the map and dashboard.
* Keys are feature names (as returned by the server), values map enum value RGB.
* Any value not listed falls back to ENUM_PALETTE by index.
*/
export const ENUM_COLOR_OVERRIDES: Record<string, Record<string, [number, number, number]>> = {
'Winning party': {
Labour: [220, 36, 31], // Labour red
Conservative: [0, 135, 220], // Conservative blue
'Liberal Democrat': [253, 187, 48], // Lib Dem gold
'Reform UK': [18, 178, 196], // Reform teal
Green: [106, 176, 35], // Green party green
'Other parties': [148, 130, 160], // muted purple
},
'Property type': {
Detached: [249, 115, 22], // orange
'Semi-Detached': [59, 130, 246], // blue
Terraced: [34, 197, 94], // green
'Flats/Maisonettes': [236, 72, 153], // pink
Other: [107, 114, 128], // gray
},
};
/**
* Build a 10-color palette for a given feature, using overrides where defined.
* Returns the default ENUM_PALETTE when no overrides exist.
*/
export function getEnumPaletteForFeature(
featureName: string | null,
values?: string[]
): [number, number, number][] {
if (!featureName || !values) return ENUM_PALETTE;
const overrides = ENUM_COLOR_OVERRIDES[featureName];
if (!overrides) return ENUM_PALETTE;
const palette: [number, number, number][] = [];
for (let i = 0; i < 10; i++) {
if (i < values.length && overrides[values[i]]) {
palette.push(overrides[values[i]]);
} else {
palette.push(ENUM_PALETTE[i % ENUM_PALETTE.length]);
}
}
return palette;
}
/** Look up override color for a specific enum value, or null if none. */
export function getEnumValueColor(
featureName: string,
valueName: string
): [number, number, number] | null {
return ENUM_COLOR_OVERRIDES[featureName]?.[valueName] ?? null;
}
/** Colors for stacked bar segments */
export const SEGMENT_COLORS = [
'#ef4444', // red-500