Add off-map vehicle corner indicators

This commit is contained in:
Morten V. Christiansen 2026-04-05 12:30:03 +02:00
parent b0b630ceb2
commit c683d82c51
2 changed files with 102 additions and 0 deletions

View File

@ -18,6 +18,7 @@ L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
const statusEl = document.getElementById("status"); const statusEl = document.getElementById("status");
const alertsEl = document.getElementById("presenceAlerts"); const alertsEl = document.getElementById("presenceAlerts");
const offmapIndicatorsEl = document.getElementById("offmapIndicators");
const playBtn = document.getElementById("playBtn"); const playBtn = document.getElementById("playBtn");
const pauseBtn = document.getElementById("pauseBtn"); const pauseBtn = document.getElementById("pauseBtn");
const resetBtn = document.getElementById("resetBtn"); const resetBtn = document.getElementById("resetBtn");
@ -54,6 +55,34 @@ function fmtTs(ts) {
return `${ts}s`; return `${ts}s`;
} }
function getDirectionInfo(lat, lon, bounds) {
const centerLat = (bounds.getSouth() + bounds.getNorth()) / 2;
const centerLon = (bounds.getWest() + bounds.getEast()) / 2;
const dLat = lat - centerLat;
const dLon = lon - centerLon;
const vertical = Math.abs(dLat) > 0.01 ? (dLat > 0 ? "N" : "S") : "";
const horizontal = Math.abs(dLon) > 0.01 ? (dLon > 0 ? "Ø" : "V") : "";
const label = `${vertical}${horizontal}` || "Nær kanten";
const arrowMap = {
"N": "↑",
"S": "↓",
"Ø": "→",
"V": "←",
"NØ": "↗",
"NV": "↖",
"SØ": "↘",
"SV": "↙",
"Nær kanten": "•"
};
return {
label,
arrow: arrowMap[label] || "•"
};
}
function popupHtml(name, state) { function popupHtml(name, state) {
return ` return `
<div style="font:13px/1.4 system-ui,sans-serif;min-width:190px;"> <div style="font:13px/1.4 system-ui,sans-serif;min-width:190px;">
@ -396,6 +425,7 @@ function updateAlerts() {
if (alertVehicles.length === 0) { if (alertVehicles.length === 0) {
alertsEl.innerHTML = ""; alertsEl.innerHTML = "";
offmapIndicatorsEl.innerHTML = "";
return; return;
} }
@ -416,6 +446,24 @@ function updateAlerts() {
</div> </div>
`; `;
}).join(""); }).join("");
const bounds = map.getBounds();
offmapIndicatorsEl.innerHTML = alertVehicles.map(v => {
const direction = getDirectionInfo(v.displayLat, v.displayLon, bounds);
const subtitle = v.presenceState === "covers"
? "Usikkerheden dækker hele viewporten"
: `Mulig tilstedeværelse fra ${direction.label}`;
return `
<div class="offmap-indicator ${v.presenceState === "covers" ? "covers" : ""}" data-vehicle="${v.name}">
<div class="offmap-arrow" style="background:${v.color};">${direction.arrow}</div>
<div class="offmap-meta">
<strong>${v.name}</strong>
<span>${subtitle}</span>
</div>
</div>
`;
}).join("");
} }
function updateStatus() { function updateStatus() {

View File

@ -60,6 +60,58 @@
.alert-item strong { display:block; margin-bottom:2px; } .alert-item strong { display:block; margin-bottom:2px; }
.offmap-indicators {
position: absolute;
right: 14px;
bottom: 14px;
z-index: 1000;
display: grid;
gap: 8px;
max-width: 260px;
pointer-events: none;
}
.offmap-indicator {
display: grid;
grid-template-columns: auto 1fr;
gap: 10px;
align-items: center;
padding: 9px 11px;
border-radius: 12px;
background: rgba(18, 18, 18, 0.86);
color: #fff;
border: 1px solid rgba(255,255,255,0.12);
box-shadow: 0 6px 18px rgba(0,0,0,0.22);
font: 12px/1.3 system-ui, sans-serif;
}
.offmap-indicator.covers {
background: rgba(0, 72, 36, 0.9);
border-color: rgba(76, 175, 80, 0.55);
}
.offmap-arrow {
width: 26px;
height: 26px;
border-radius: 999px;
display: grid;
place-items: center;
font-size: 16px;
font-weight: 700;
color: #fff;
box-shadow: inset 0 0 0 2px rgba(255,255,255,0.18);
}
.offmap-meta strong {
display: block;
margin-bottom: 2px;
}
.offmap-meta span {
display: block;
opacity: 0.82;
}
.car { .car {
width: 24px; width: 24px;
height: 24px; height: 24px;
@ -88,6 +140,8 @@
<div class="alerts" id="presenceAlerts"></div> <div class="alerts" id="presenceAlerts"></div>
</div> </div>
<div class="offmap-indicators" id="offmapIndicators"></div>
<script src="data.js"></script> <script src="data.js"></script>
<script src="app.js"></script> <script src="app.js"></script>
</body> </body>