Add off-map vehicle corner indicators
This commit is contained in:
parent
b0b630ceb2
commit
c683d82c51
48
kort7/app.js
48
kort7/app.js
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue