feat(street-level): one colour per imagery source

As more sources arrive, each one reads the same everywhere it appears: its
PROVIDERS chip, coverage lines, overview points, image cones and a single
legend swatch. The palette lives in one place, PROVIDER_COLORS in
src/layers/streetLevel/policy.js: Mapillary green (#05cb63), Panoramax
purple, Google Street View blue.

Mapillary drops its age dimming and magenta panoramas: lines and overview
points are Mapillary green, cones are green too and 360° images keep their
ring shape; the 360°/FLAT filter still separates them, and the selected
sequence stays GEV cyan.

The provider contract loses `legend`: `colors.coverage` is the source's one
colour, and the core composes the legend as one swatch per active source
(its name, its colour) followed by Selected. The chip group takes an
optional per-chip colour as a `--chip-color` CSS variable, written only
when it changes; Street Level chips show it as a swatch dot and, while on,
as the chip's border, text and a faint fill.

The feature gate checks the legend has one swatch per source plus Selected
and that the Mapillary chip carries #05cb63.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Opus 5.5 committed 2026-09-29 00:03:00 +01:00
1 parent d5da45eacb
commit 0f2cac2ca2
21 files changed
+188 -99

No files matched your search

@@ -15,7 +15,6 @@ import {
COVERAGE_MOVE_DEBOUNCE_MS,
COVERAGE_OVERVIEW_MAX_TILES,
COVERAGE_OVERVIEW_POINT_PX,
COVERAGE_RECENT_DAYS,
PICK_PREFIX,
} from './policy.js';
@@ -34,15 +33,11 @@ const PER_TILE_SEQUENCE_CAP = Math.floor(
COVERAGE_MAX_SEQUENCES / COVERAGE_MAX_TILES,
);
/** Colour for a sequence: brand green, dimmer with age, magenta for panoramas. */
function sequenceColor(sequence, { selected = false, now = Date.now() } = {}) {
if (selected) return Cesium.Color.fromCssColorString(COLORS.selected);
if (sequence.isPano)
return Cesium.Color.fromCssColorString(COLORS.pano).withAlpha(0.9);
const ageDays = (now - (sequence.capturedAt || 0)) / 86_400_000;
return ageDays <= COVERAGE_RECENT_DAYS
? Cesium.Color.fromCssColorString(COLORS.coverage).withAlpha(0.92)
: Cesium.Color.fromCssColorString(COLORS.coverageOld).withAlpha(0.7);
/** Colour for a sequence: Mapillary green, GEV cyan while selected. */
function sequenceColor({ selected = false } = {}) {
return selected
? Cesium.Color.fromCssColorString(COLORS.selected)
: Cesium.Color.fromCssColorString(COLORS.coverage).withAlpha(0.92);
}
/**
@@ -82,14 +77,13 @@ export function createCoverage({ state, source }) {
);
}
function sequenceInstance(sequence, geometry, now) {
function sequenceInstance(sequence, geometry) {
return new Cesium.GeometryInstance({
geometry,
id: `${PICK_PREFIX.sequence}${sequence.id}`,
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
sequenceColor(sequence, {
now,
sequenceColor({
selected: sequence.id === state.sequence.selectedId,
}),
),
@@ -103,7 +97,6 @@ export function createCoverage({ state, source }) {
* bare-earth heights for every sequence whose heights are cached.
*/
function buildSequencePrimitives(sequences) {
const now = Date.now();
const ground = terrainMode() ? state.context.groundCaster : null;
const draped = [];
const cast = [];
@@ -129,7 +122,6 @@ export function createCoverage({ state, source }) {
vertexFormat: Cesium.PolylineColorAppearance.VERTEX_FORMAT,
arcType: Cesium.ArcType.NONE,
}),
now,
),
);
else
@@ -140,7 +132,6 @@ export function createCoverage({ state, source }) {
positions,
width: COVERAGE_LINE_WIDTH_PX,
}),
now,
),
);
}
@@ -234,13 +225,12 @@ export function createCoverage({ state, source }) {
const green = Cesium.Color.fromCssColorString(COLORS.coverage).withAlpha(
0.85,
);
const pink = Cesium.Color.fromCssColorString(COLORS.pano).withAlpha(0.9);
let count = 0;
for (const point of points) {
if (!passesImageryFilter(point, filter())) continue;
collection.add({
position: Cesium.Cartesian3.fromDegrees(point.lon, point.lat),
color: point.isPano ? pink : green,
color: green,
pixelSize: COVERAGE_OVERVIEW_POINT_PX,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
});
@@ -519,7 +509,7 @@ export function createCoverage({ state, source }) {
primitive.getGeometryInstanceAttributes(instanceId);
if (attributes)
attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(
sequenceColor(sequence, { selected }),
sequenceColor({ selected }),
attributes.color,
);
} catch {