kort/kort3.html

263 lines
5.0 KiB
HTML

<!doctype html>
<html lang="da">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>GPS demo</title>
<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>
<style>
html,body{
height:100%;
margin:0;
}
#map{
height:100%;
}
.hud{
position:absolute;
top:10px;
left:10px;
z-index:1000;
background:white;
padding:10px;
border-radius:8px;
border:1px solid #ccc;
font-family:sans-serif;
}
.car{
width:24px;
height:24px;
transform-origin:12px 12px;
}
.car svg{
width:24px;
height:24px;
}
.car-label{
position:relative;
top:-2px;
left:26px;
font-size:12px;
background:white;
padding:2px 6px;
border-radius:10px;
border:1px solid #ccc;
}
</style>
</head>
<body>
<div id="map"></div>
<div class="hud">
<button id="play">Play</button>
<button id="pause">Pause</button>
<button id="reset">Reset</button>
</div>
<script>
/* --------- DATA --------- */
const FRAMES=[
{"Bil 1":{lat:55.6761,lon:12.5683},"Bil 2":{lat:55.6748,lon:12.5650},"Bil 3":{lat:55.6780,lon:12.5600},"Bil 4":{lat:55.6728,lon:12.5710},"Bil 5":{lat:55.6795,lon:12.5845}},
{"Bil 1":{lat:55.6762,lon:12.5720},"Bil 2":{lat:55.6760,lon:12.5670},"Bil 3":{lat:55.6772,lon:12.5603},"Bil 4":{lat:55.6736,lon:12.5698},"Bil 5":{lat:55.6793,lon:12.5815}},
{"Bil 1":{lat:55.6763,lon:12.5758},"Bil 2":{lat:55.6772,lon:12.5692},"Bil 3":{lat:55.6765,lon:12.5610},"Bil 4":{lat:55.6746,lon:12.5688},"Bil 5":{lat:55.6790,lon:12.5784}},
{"Bil 1":{lat:55.6764,lon:12.5798},"Bil 2":{lat:55.6785,lon:12.5716},"Bil 3":{lat:55.6758,lon:12.5620},"Bil 4":{lat:55.6757,lon:12.5682},"Bil 5":{lat:55.6786,lon:12.5753}},
{"Bil 1":{lat:55.6766,lon:12.5837},"Bil 2":{lat:55.6796,lon:12.5744},"Bil 3":{lat:55.6752,lon:12.5634},"Bil 4":{lat:55.6769,lon:12.5682},"Bil 5":{lat:55.6781,lon:12.5722}},
{"Bil 1":{lat:55.6768,lon:12.5875},"Bil 2":{lat:55.6804,lon:12.5776},"Bil 3":{lat:55.6747,lon:12.5652},"Bil 4":{lat:55.6781,lon:12.5688},"Bil 5":{lat:55.6776,lon:12.5692}},
{"Bil 1":{lat:55.6770,lon:12.5912},"Bil 2":{lat:55.6810,lon:12.5808},"Bil 3":{lat:55.6744,lon:12.5673},"Bil 4":{lat:55.6792,lon:12.5700},"Bil 5":{lat:55.6771,lon:12.5662}},
{"Bil 1":{lat:55.6772,lon:12.5948},"Bil 2":{lat:55.6812,lon:12.5842},"Bil 3":{lat:55.6742,lon:12.5696},"Bil 4":{lat:55.6800,lon:12.5716},"Bil 5":{lat:55.6766,lon:12.5634}},
{"Bil 1":{lat:55.6775,lon:12.5982},"Bil 2":{lat:55.6811,lon:12.5877},"Bil 3":{lat:55.6742,lon:12.5720},"Bil 4":{lat:55.6806,lon:12.5736},"Bil 5":{lat:55.6761,lon:12.5608}},
{"Bil 1":{lat:55.6778,lon:12.6016},"Bil 2":{lat:55.6808,lon:12.5910},"Bil 3":{lat:55.6744,lon:12.5742},"Bil 4":{lat:55.6809,lon:12.5758},"Bil 5":{lat:55.6756,lon:12.5584}}
];
/* --------- FARVER --------- */
const COLORS={
"Bil 1":"#ff0000",
"Bil 2":"#00c853",
"Bil 3":"#2979ff",
"Bil 4":"#2979ff",
"Bil 5":"#2979ff"
};
/* --------- KORT --------- */
const map=L.map("map").setView([55.6761,12.5683],13);
L.tileLayer(
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
{attribution:"© OpenStreetMap"}
).addTo(map);
/* --------- IKON --------- */
function carIcon(label,color){
return L.divIcon({
className:"",
iconSize:[1,1],
iconAnchor:[12,12],
html:`
<div class="car" data-role="car">
<svg viewBox="0 0 24 24">
<path
d="M12 2 L20 22 L12 18 L4 22 Z"
fill="${color}"
stroke="#ffffff"
stroke-width="1.5"
/>
</svg>
</div>
<div class="car-label">${label}</div>
`
});
}
/* --------- BEARING --------- */
function bearing(p0,p1){
const dy=p1.lat-p0.lat;
const dx=p1.lon-p0.lon;
const a=Math.atan2(dx,dy)*180/Math.PI;
return (a+360)%360;
}
/* --------- ROTATION --------- */
function setHeading(marker,deg){
const el=marker.getElement();
if(!el)return;
const car=el.querySelector("[data-role=car]");
car.style.transform=`rotate(${deg}deg)`;
}
/* --------- MARKERS --------- */
const markers={};
const cars=Object.keys(FRAMES[0]);
for(const name of cars){
const p=FRAMES[0][name];
markers[name]=L.marker(
[p.lat,p.lon],
{icon:carIcon(name,COLORS[name])}
).addTo(map);
}
/* --------- ANIMATION --------- */
let frame=0;
let start=performance.now();
let running=true;
const STEP=1500;
function lerp(a,b,t){
return a+(b-a)*t;
}
/* smooth GPS-agtig easing */
function smooth(t){
return t*t*(3-2*t);
}
function tick(now){
if(!running)return;
const next=frame+1;
if(next>=FRAMES.length)return;
let t=(now-start)/STEP;
if(t>1)t=1;
t=smooth(t);
for(const name of cars){
const p0=FRAMES[frame][name];
const p1=FRAMES[next][name];
const lat=lerp(p0.lat,p1.lat,t);
const lon=lerp(p0.lon,p1.lon,t);
const hdg=bearing(p0,p1);
const m=markers[name];
m.setLatLng([lat,lon]);
setHeading(m,hdg);
}
if(t>=1){
frame++;
start=now;
}
requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
/* --------- UI --------- */
play.onclick=()=>{
running=true;
requestAnimationFrame(tick);
};
pause.onclick=()=>{
running=false;
};
reset.onclick=()=>{
frame=0;
start=performance.now();
running=true;
requestAnimationFrame(tick);
};
</script>
</body>
</html>