Early drafts of web tracker, not complete
This commit is contained in:
parent
82db6c9a38
commit
67e1a78995
2 changed files with 212 additions and 0 deletions
157
tools/livetrack/fieldtest_map.html
Normal file
157
tools/livetrack/fieldtest_map.html
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Fieldtest Positions</title>
|
||||
|
||||
<!-- Leaflet (swap to your local staged copies if preferred)
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> -->
|
||||
<link rel="stylesheet" href="https://ryzdesk/lib/leaflet.css">
|
||||
<script src="https://ryzdesk/lib/leaflet.js"></script>
|
||||
|
||||
<style>
|
||||
html, body { height: 100%; margin: 0; }
|
||||
#map { height: 100%; width: 100%; }
|
||||
.legend {
|
||||
position: absolute; z-index: 1000; background: rgba(255,255,255,0.85);
|
||||
padding: 8px; margin: 10px; border-radius: 6px; font-family: sans-serif;
|
||||
font-size: 13px;
|
||||
}
|
||||
.node-label {
|
||||
background: rgba(255,255,255,0.85);
|
||||
border: 1px solid rgba(0,0,0,0.25);
|
||||
color: #000;
|
||||
font-weight: 700;
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.25);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="legend">
|
||||
WS: <span id="ws_state">connecting</span><br>
|
||||
Last: <span id="last_msg">—</span>
|
||||
</div>
|
||||
<div id="map"></div>
|
||||
|
||||
<script>
|
||||
// --- Map init (center near your sample points; adjust as desired)
|
||||
const map = L.map('map').setView([44.93655, -123.02185], 17);
|
||||
|
||||
// Basemap: replace with your own tile server if you have one
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 20,
|
||||
attribution: '© OpenStreetMap contributors'
|
||||
}).addTo(map);
|
||||
|
||||
// Node colors (bright, readable)
|
||||
const nodeStyle = {
|
||||
B: { color: '#00e5ff' }, // cyan-ish
|
||||
C: { color: '#ffea00' }, // yellow
|
||||
D: { color: '#ff3d00' }, // orange/red
|
||||
E: { color: '#76ff03' }, // green
|
||||
};
|
||||
// One persistent "current" marker per node (bright + labeled)
|
||||
const currentMarkers = {}; // node -> L.CircleMarker
|
||||
|
||||
function addFadingDot(node, lat, lon) {
|
||||
const style = nodeStyle[node] || { color: '#ffffff' };
|
||||
|
||||
// Bright at birth
|
||||
const m = L.circleMarker([lat, lon], {
|
||||
radius: 7,
|
||||
color: style.color,
|
||||
weight: 2,
|
||||
fillColor: style.color,
|
||||
fillOpacity: 1.0,
|
||||
opacity: 1.0
|
||||
}).addTo(map);
|
||||
|
||||
// Fade steps: 5s -> 2/3, 10s -> 1/3, 15s -> remove
|
||||
setTimeout(() => {
|
||||
m.setStyle({ fillOpacity: 0.66, opacity: 0.66 });
|
||||
}, 5000);
|
||||
|
||||
setTimeout(() => {
|
||||
m.setStyle({ fillOpacity: 0.33, opacity: 0.33 });
|
||||
}, 10000);
|
||||
|
||||
setTimeout(() => {
|
||||
map.removeLayer(m);
|
||||
}, 15000);
|
||||
}
|
||||
|
||||
function upsertCurrentMarker(node, lat, lon) {
|
||||
const style = nodeStyle[node] || { color: '#ffffff' };
|
||||
|
||||
if (!currentMarkers[node]) {
|
||||
const m = L.circleMarker([lat, lon], {
|
||||
radius: 9,
|
||||
color: style.color,
|
||||
weight: 2,
|
||||
fillColor: style.color,
|
||||
fillOpacity: 1.0,
|
||||
opacity: 1.0
|
||||
}).addTo(map);
|
||||
|
||||
// Permanent label next to the current marker
|
||||
m.bindTooltip(node, {
|
||||
permanent: true,
|
||||
direction: 'right',
|
||||
offset: [10, 0],
|
||||
className: 'node-label'
|
||||
});
|
||||
|
||||
currentMarkers[node] = m;
|
||||
} else {
|
||||
currentMarkers[node].setLatLng([lat, lon]);
|
||||
// reassert brightness in case you later add effects
|
||||
currentMarkers[node].setStyle({ fillOpacity: 1.0, opacity: 1.0 });
|
||||
}
|
||||
}
|
||||
function parseMsg(line) {
|
||||
// "C,44.936454,-123.021923,65.40"
|
||||
const parts = line.trim().split(',');
|
||||
if (parts.length < 4) return null;
|
||||
const node = parts[0];
|
||||
const lat = parseFloat(parts[1]);
|
||||
const lon = parseFloat(parts[2]);
|
||||
const alt = parseFloat(parts[3]); // not used yet, but keep it
|
||||
if (!node || Number.isNaN(lat) || Number.isNaN(lon)) return null;
|
||||
return { node, lat, lon, alt };
|
||||
}
|
||||
|
||||
// --- WebSocket
|
||||
const wsStateEl = document.getElementById('ws_state');
|
||||
const lastMsgEl = document.getElementById('last_msg');
|
||||
|
||||
//const wsUrl = `ws://${location.hostname}:3000/stream`;
|
||||
//const ws = new WebSocket(wsUrl);
|
||||
|
||||
const wsScheme = (location.protocol === 'https:') ? 'wss' : 'ws';
|
||||
const wsUrl = (wsScheme === 'ws')
|
||||
? `ws://${location.hostname}:3000/stream`
|
||||
: `wss://${location.host}/stream`;
|
||||
|
||||
const ws = new WebSocket(wsUrl);
|
||||
//const ws = new WebSocket('ws://localhost:3000/stream'); // temporary test
|
||||
|
||||
ws.onopen = () => wsStateEl.textContent = 'open';
|
||||
ws.onclose = () => wsStateEl.textContent = 'closed';
|
||||
ws.onerror = () => wsStateEl.textContent = 'error';
|
||||
|
||||
ws.onmessage = (ev) => {
|
||||
const line = ev.data;
|
||||
lastMsgEl.textContent = line;
|
||||
|
||||
const msg = parseMsg(line);
|
||||
if (!msg) return;
|
||||
|
||||
addFadingDot(msg.node, msg.lat, msg.lon);
|
||||
upsertCurrentMarker(msg.node, msg.lat, msg.lon);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue