feat(map): distinct marker icons for specialist vehicles
Crane, Motorbike and Pick-Up now render their own white SVG silhouette inside the department-coloured pin (via fn_live_positions' new `vehicle_type` field). Specialists keep full size + colour even when parked so they stay legible and stand out from the field-service swarm. All other vehicles (field-service + unassigned) are unchanged — they fall through to the existing arrow/square/dot marker. Icon-only change; no popup/clustering/filter behaviour altered. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ade9705345
commit
38fd7551f9
1 changed files with 38 additions and 6 deletions
40
index.html
40
index.html
|
|
@ -269,6 +269,15 @@
|
|||
}
|
||||
.veh-marker.offline .veh-plate { color: var(--muted); }
|
||||
|
||||
/* Specialist vehicle icon (crane / motorbike / pick-up) — white silhouette
|
||||
centred in the department-coloured pin. */
|
||||
.veh-type { display: grid; place-items: center; width: 22px; height: 22px; }
|
||||
.veh-type svg { width: 20px; height: 20px; display: block; }
|
||||
/* Keep specialists full-size + circular even when parked so the icon stays
|
||||
legible (overrides the parked half-size square). */
|
||||
.veh-marker.has-type.parked .veh-pin { transform: scale(1); border-radius: 50%; }
|
||||
.veh-marker.has-type.offline .veh-type svg { opacity: .85; }
|
||||
|
||||
/* ── Persistent POI marker (Fireside HQ) ───────────────────────────── */
|
||||
/* Cluster bubble (zoomed-out): amber circle + white count, tiered by size.
|
||||
Click zooms to expand into the individual pins. */
|
||||
|
|
@ -753,15 +762,29 @@ function upsertClusterMarker(id, count, coords) {
|
|||
}
|
||||
}
|
||||
|
||||
// Specialist vehicle types get their own marker icon (white silhouette inside
|
||||
// the department-coloured pin). All other types (field-service + unassigned)
|
||||
// fall through to the default arrow/square/dot marker, unchanged. Keys must
|
||||
// match fn_live_positions' `vehicle_type` exactly (migration 16).
|
||||
const SPECIALIST_ICONS = {
|
||||
'Crane': '<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 21V6"/><path d="M3 6h18"/><path d="M9 3l3 3 3-3"/><path d="M18 6v4"/><path d="M17 10h2v1.6h-2z"/><path d="M8 21h8"/></svg>',
|
||||
'Motorbike': '<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="5.5" cy="16" r="3.3"/><circle cx="18.5" cy="16" r="3.3"/><path d="M5.5 16h5l3-5h3.5"/><path d="M10.5 16 14 11"/><path d="M14.5 8H18l1 3"/></svg>',
|
||||
'Pick-Up': '<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 15V8h7l3 3h8v4"/><path d="M2 15h2m5 0h6m4 0h2"/><circle cx="6.5" cy="16.5" r="1.9"/><circle cx="17.5" cy="16.5" r="1.9"/></svg>',
|
||||
};
|
||||
|
||||
function upsertLiveMarker(p, coords, feature) {
|
||||
const state = vehicleState(p);
|
||||
// Active (moving now) = full department colour. Parked (reported within 24h)
|
||||
// = a PASTEL of that department colour. Offline (>24h silent) = grey. Lets
|
||||
// high-level viewers read fleet activity by department at a glance.
|
||||
const ccColor = colorForCostCentre(p.cost_centre);
|
||||
const color = state === 'offline' ? OFFLINE_COLOR
|
||||
const typeIcon = SPECIALIST_ICONS[p && p.vehicle_type];
|
||||
let color = state === 'offline' ? OFFLINE_COLOR
|
||||
: state === 'parked' ? pastelColor(ccColor)
|
||||
: ccColor;
|
||||
// Specialists keep the full department colour (not the parked pastel) so the
|
||||
// white icon stays legible and they stand out from the field-service swarm.
|
||||
if (typeIcon && state !== 'offline') color = ccColor;
|
||||
const speed = Number(p.speed || 0);
|
||||
const dir = Number(p.direction || 0);
|
||||
let m = liveMarkers.get(p.imei);
|
||||
|
|
@ -788,19 +811,28 @@ function upsertLiveMarker(p, coords, feature) {
|
|||
el.classList.add('veh-marker');
|
||||
el.classList.remove('active', 'parked', 'offline');
|
||||
el.classList.add(state);
|
||||
el.classList.toggle('has-type', !!typeIcon);
|
||||
const pin = el.querySelector('.veh-pin');
|
||||
pin.style.setProperty('--c', color);
|
||||
// Direction arrow only for vehicles moving now. Parked = a clean pastel
|
||||
// square (no arrow, no dot). Idling/offline keep the neutral dot.
|
||||
// Specialist vehicles (crane/motorbike/pick-up) show their own icon instead of
|
||||
// the heading arrow. Everything else: direction arrow only when moving now,
|
||||
// a clean pastel square when parked, a neutral dot when idling/offline.
|
||||
const glyph = el.querySelector('.glyph');
|
||||
if (state === 'active' && speed > 0) {
|
||||
if (typeIcon) {
|
||||
glyph.className = 'glyph veh-type';
|
||||
glyph.style.removeProperty('--dir');
|
||||
glyph.innerHTML = typeIcon;
|
||||
} else if (state === 'active' && speed > 0) {
|
||||
glyph.className = 'glyph veh-arrow';
|
||||
glyph.innerHTML = '';
|
||||
glyph.style.setProperty('--dir', dir + 'deg');
|
||||
} else if (state === 'parked') {
|
||||
glyph.className = 'glyph'; // empty — just the pastel square
|
||||
glyph.innerHTML = '';
|
||||
glyph.style.removeProperty('--dir');
|
||||
} else {
|
||||
glyph.className = 'glyph idle-dot';
|
||||
glyph.innerHTML = '';
|
||||
glyph.style.removeProperty('--dir');
|
||||
}
|
||||
el.querySelector('.veh-plate').textContent = plateTail(p.vehicle_number);
|
||||
|
|
|
|||
Loading…
Reference in a new issue