<!DOCTYPE html>
<html>
<head>
<meta charset=”utf-8″ />
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<title>Wereldwijde Boot GPS – OpenSeaMap</title>
<!– Leaflet CSS en JS via CDNJS –>
<link rel=”stylesheet” href=”https://cloudflare.com” />
<script src=”https://cloudflare.com“></script>
<style>
body { font-family: Arial, sans-serif; margin: 15px; background: #f0f2f5; }
.dashboard {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
background: white;
padding: 15px;
border-radius: 8px;
text-align: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 15px;
}
.stat { font-size: 1.4em; font-weight: bold; color: #0275d8; display: block; margin-top: 5px; }
#mijn-kaart {
height: 550px;
width: 100%;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
/* CSS animatie voor een vloeiende overgang bij het draaien van de pijl */
.boot-icon-container {
transition: transform 0.5s ease-in-out;
}
</style>
</head>
<body>
<div class=”dashboard”>
<div><strong>Snelheid</strong><span id=”speed” class=”stat”>0.0 kn</span></div>
<div><strong>Koers</strong><span id=”heading” class=”stat”>–°</span></div>
<div><strong>Status</strong><span id=”method” class=”stat”>Zoeken…</span></div>
</div>
<div id=”mijn-kaart”></div>
<script>
// 1. Initialiseer de kaart met wereldwijde instellingen
var map = L.map(‘mijn-kaart’, {
worldCopyJump: true, // Zorgt dat markers op de hele wereldbol herhaald worden
center: [0, 0], // Startpunt op de evenaar totdat GPS start
zoom: 2
});
// 2. OpenStreetMap als basislaag
L.tileLayer(‘https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png‘, {
attribution: ‘© OpenStreetMap contributors’,
maxZoom: 18
}).addTo(map);
// 3. OpenSeaMap als maritieme laag over de hele wereld
L.tileLayer(‘https://openseamap.org{z}/{x}/{y}.png’, {
attribution: ‘Map data © OpenSeaMap contributors’,
minZoom: 3,
maxZoom: 18
}).addTo(map);
// 4. Een functie om het meedraaiende pijl-icoon te genereren
function maakPijlIcoon(koersGraden) {
// We gebruiken een SVG-pijl die we via CSS ‘rotate’ de juiste kant op sturen
return L.divIcon({
className: ‘boot-icon-container’,
html: `<div style=”transform: rotate(${koersGraden}deg); width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;”>
<svg width=”40″ height=”40″ viewBox=”0 0 24 24″ fill=”none” xmlns=”http://w3.org“>
<!– De cirkel rondom de pijl –>
<circle cx=”12″ cy=”12″ r=”10″ fill=”#0275d8″ fill-opacity=”0.2″ stroke=”#0275d8″ stroke-width=”2″/>
<!– De navigatiepijl zelf (wijst standaard naar het Noorden / 0 graden) –>
<path d=”M12 4L4 18L12 14L20 18L12 4Z” fill=”#0275d8″ stroke=”#ffffff” stroke-width=”1″ stroke-linejoin=”round”/>
</svg>
</div>`,
iconSize: [40, 40],
iconAnchor: [20, 20] // Het middelpunt van de pijl is het rotatiepunt
});
}
// Maak een start-marker aan met de pijl naar het noorden (0 graden)
var bootMarker = L.marker([0, 0], { icon: maakPijlIcoon(0) }).addTo(map);
// 5. GPS tracking activeren via de mobiele browser
if (navigator.geolocation) {
navigator.geolocation.watchPosition(function(pos) {
var lat = pos.coords.latitude;
var lng = pos.coords.longitude;
var speedMs = pos.coords.speed; // Snelheid in m/s
var heading = pos.coords.heading; // Koers in graden (0-360)
// Wiskundige omrekening naar knopen
var speedKnots = (speedMs && speedMs > 0) ? (speedMs * 1.94384).toFixed(1) + ” kn” : “0.0 kn”;
// Als de telefoon stilligt of geen koers doorgeeft, houden we de pijl op 0 graden
var actueleKoers = (heading !== null && heading !== undefined) ? Math.round(heading) : 0;
var headingText = (heading !== null && heading !== undefined) ? actueleKoers + “°” : “–°”;
// Dashboard updaten
document.getElementById(‘speed’).innerText = speedKnots;
document.getElementById(‘heading’).innerText = headingText;
document.getElementById(‘method’).innerText = “GPS (Live)”;
// CRUCIAL: Verplaats de marker én centreer de kaart direct op de boot
var livePos = [lat, lng];
bootMarker.setLatLng(livePos);
// Vervang het icoon met de nieuw berekende rotatiehoek
bootMarker.setIcon(maakPijlIcoon(actueleKoers));
// Zorg dat de boot exact in het midden blijft staan
map.setView(livePos, 14);
}, function(err) {
console.warn(“GPS mislukt, we vallen terug op ruwe IP-locatie.”);
haalIpLocatie();
}, {
enableHighAccuracy: true, // Essentieel om op het water de GPS-chip te forceren
maximumAge: 0,
timeout: 12000
});
} else {
haalIpLocatie();
}
// Back-up voor computers/omgevingen zonder GPS-chip
function haalIpLocatie() {
fetch(‘https://ipapi.co‘)
.then(res => res.json())
.then(data => {
if(data.latitude && data.longitude) {
document.getElementById(‘method’).innerText = “IP-Locatie”;
document.getElementById(‘speed’).innerText = “0.0 kn”;
document.getElementById(‘heading’).innerText = “–°”;
var ipPos = [data.latitude, data.longitude];
bootMarker.setLatLng(ipPos);
bootMarker.setIcon(maakPijlIcoon(0)); // IP-adres geeft geen koers, dus pijl naar het Noorden
map.setView(ipPos, 11);
}
}).catch(e => {
document.getElementById(‘method’).innerText = “Basis Kaart”;
});
}
</script>
</body>
</html>