241 lines
4.7 KiB
HTML
241 lines
4.7 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%;
|
|
}
|
|
|
|
.car{
|
|
width:24px;
|
|
height:24px;
|
|
transform-origin:12px 12px;
|
|
}
|
|
|
|
.car svg{
|
|
width:24px;
|
|
height:24px;
|
|
}
|
|
|
|
</style>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div id="map"></div>
|
|
|
|
<script>
|
|
|
|
/* --------- 10 koordinatsæt --------- */
|
|
|
|
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(color){
|
|
|
|
return L.divIcon({
|
|
className:"",
|
|
iconSize:[24,24],
|
|
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>
|
|
`
|
|
});
|
|
|
|
}
|
|
|
|
/* --------- 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;
|
|
|
|
}
|
|
|
|
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 trails={};
|
|
const uncertainty={};
|
|
|
|
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(COLORS[name])}
|
|
).addTo(map);
|
|
|
|
trails[name]=L.polyline(
|
|
[[p.lat,p.lon]],
|
|
{color:COLORS[name],weight:3}
|
|
).addTo(map);
|
|
|
|
/* usikkerhed kun for grøn bil */
|
|
|
|
if(name==="Bil 2"){
|
|
|
|
uncertainty[name]=L.circle(
|
|
[p.lat,p.lon],
|
|
{
|
|
radius:1200,
|
|
color:"#00c853",
|
|
fillColor:"#00c853",
|
|
fillOpacity:0.15,
|
|
weight:1
|
|
}
|
|
).addTo(map);
|
|
|
|
}
|
|
|
|
markers[name].bindPopup(name);
|
|
|
|
}
|
|
|
|
/* --------- animation --------- */
|
|
|
|
let frame=0;
|
|
let start=performance.now();
|
|
|
|
const STEP=1500;
|
|
|
|
function lerp(a,b,t){
|
|
return a+(b-a)*t;
|
|
}
|
|
|
|
function smooth(t){
|
|
return t*t*(3-2*t);
|
|
}
|
|
|
|
function tick(now){
|
|
|
|
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);
|
|
|
|
markers[name].setLatLng([lat,lon]);
|
|
setHeading(markers[name],hdg);
|
|
|
|
if(uncertainty[name]){
|
|
uncertainty[name].setLatLng([lat,lon]);
|
|
}
|
|
|
|
}
|
|
|
|
if(t>=1){
|
|
|
|
frame++;
|
|
start=now;
|
|
|
|
for(const name of cars){
|
|
|
|
const p=FRAMES[frame][name];
|
|
trails[name].addLatLng([p.lat,p.lon]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
requestAnimationFrame(tick);
|
|
|
|
}
|
|
|
|
requestAnimationFrame(tick);
|
|
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|
|
|