Live GPS N/S E/W, UTC/LT, Compass & history. Offline ready.
CONTAINER →← BULK CARRIER
—
N
Compass
—
Distance
—
Time To Go
—
Initial Brg
—
Final Brg
—
Departure
—
Source
—
DEFAULT: Offline All-In-One Map (OSM)Always works
Map loading
OPTIONAL: Google Map (needs internet)
Google Map - online only
}
function finalBearing(a,b,c,d){return (initBearing(c,d,a,b)+180)%360;}
function fillDMS(lat,lon,acc){
const la=decToDms(lat,true);const lo=decToDms(lon,false);
document.getElementById('curLatD').value=la.deg;document.getElementById('curLatM').value=la.min;document.getElementById('curLatS').value=la.sec.toFixed(3);document.getElementById('curLatH').value=la.hem;
document.getElementById('curLonD').value=lo.deg;document.getElementById('curLonM').value=lo.min;document.getElementById('curLonS').value=lo.sec.toFixed(3);document.getElementById('curLonH').value=lo.hem;
document.getElementById('posBadge').textContent='Live GPS ±'+Math.round(acc)+'m';document.getElementById('posBadge').style.background='#dff0e4';
document.getElementById('gpsAcc').textContent='Fix: '+acc.toFixed(1)+'m';
document.getElementById('timeObtained').value=new Date().toISOString().slice(0,16);
lastGPS={lat,lon,acc};}
function useLiveGPS(){
const b=document.getElementById('gpsBtn');const err=document.getElementById('err');
err.style.display='block';err.textContent='Requesting GPS...';
if(!navigator.geolocation){err.textContent='❌ No GPS support';return;}
b.textContent='Locating...';
navigator.geolocation.getCurrentPosition(p=>{fillDMS(p.coords.latitude,p.coords.longitude,p.coords.accuracy);b.textContent='📍 Live GPS ✓';err.style.display='none';plotCurrentOnly();},e=>{
let msg='GPS Error: '+e.message;
if(e.code===1) msg='❌ Permission DENIED - Settings > Chrome > Location > Allow';
if(e.code===2) msg='❌ GPS unavailable - Turn ON Location';
if(e.code===3) msg='❌ Timeout - Go open deck';
err.textContent=msg;b.textContent='📍 Live GPS';
},{enableHighAccuracy:true,timeout:20000,maximumAge:0});
}
function toggleWatchGPS(){
const b=document.getElementById('watchBtn');const err=document.getElementById('err');
if(watchId!==null){navigator.geolocation.clearWatch(watchId);watchId=null;b.textContent='Watch OFF';b.classList.remove('watch-on');err.style.display='none';return;}
if(!navigator.geolocation){err.style.display='block';err.textContent='No GPS';return;}
b.textContent='Watch ON...';b.classList.add('watch-on');err.style.display='block';err.textContent='Watch ON - Tracking...';
watchId=navigator.geolocation.watchPosition(p=>{fillDMS(p.coords.latitude,p.coords.longitude,p.coords.accuracy);b.textContent='Watch ON ('+Math.round(p.coords.accuracy)+'m) LIVE';err.textContent='Watch LIVE ±'+Math.round(p.coords.accuracy)+'m';plotCurrentOnly();},e=>{err.textContent='Watch Error: '+e.message;},{enableHighAccuracy:true,maximumAge:1000,timeout:20000});
}
function showErr(m){const er=document.getElementById('err');er.textContent=m;er.style.display='block';setTimeout(()=>er.style.display='none',5000);}
let gmap=null,gCurMarker=null,gDestMarker=null,gLine=null,gVisible=false;
let lastPlot=null;
function toggleGMap(){
const w=document.getElementById('gmapWrap'); const b=document.getElementById('gToggle');
if(!gVisible){ w.style.display='block'; b.textContent='Hide Google Map'; gVisible=true; initGMap(); setTimeout(()=>{ if(gmap) gmap.invalidateSize(); if(lastPlot) plotBothG(lastPlot.a,lastPlot.b,lastPlot.c,lastPlot.d,lastPlot.dist); },300); }
else{ w.style.display='none'; b.textContent='Show Google Map'; gVisible=false; }
}
function initGMap(){ if(gmap) return; try{ gmap=L.map('gmap',{zoomControl:false}).setView([11.7,43.2],5); L.control.zoom({position:'bottomright'}).addTo(gmap); L.tileLayer('https://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',{maxZoom:20, subdomains:['mt0','mt1','mt2','mt3']}).addTo(gmap); document.getElementById('gmapInfo').textContent='Google Map ready (online)'; }catch(e){ document.getElementById('gmap').innerHTML='
Google Map needs internet
'; } }
function plotBothG(a,b,c,d,dist){ if(!gmap) return; try{ if(gCurMarker) gmap.removeLayer(gCurMarker); if(gDestMarker) gmap.removeLayer(gDestMarker); if(gLine) gmap.removeLayer(gLine); gCurMarker=L.marker([a,b],{icon:L.divIcon({html:'',iconSize:[18,18],className:''})}).addTo(gmap); gDestMarker=L.marker([c,d],{icon:L.divIcon({html:'',iconSize:[18,18],className:''})}).addTo(gmap); gLine=L.polyline([[a,b],[c,d]],{color:'#ff7a00',weight:3,dashArray:'8,8'}).addTo(gmap); gmap.fitBounds([[a,b],[c,d]],{padding:[40,40]});}catch(e){} }
function centerGMap(){ if(gCurMarker&&gmap) gmap.setView(gCurMarker.getLatLng(),10); }
function initMap(){if(map)return;try{map=L.map('map',{zoomControl:false}).setView([11.7,43.2],5);L.control.zoom({position:'bottomright'}).addTo(map);L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{maxZoom:18}).addTo(map);document.getElementById('mapInfo').textContent='Map ready';}catch(e){document.getElementById('map').innerHTML='
OFFLINE MODE Map needs online first
';}setTimeout(()=>{if(map)try{map.invalidateSize()}catch(e){}},500);}
function plotCurrentOnly(){initMap();const lat=dmsToDec(curLatD.value,curLatM.value,curLatS.value,curLatH.value);const lon=dmsToDec(curLonD.value,curLonM.value,curLonS.value,curLonH.value);if(isNaN(lat))return;if(typeof L==='undefined')return;if(curMarker)map.removeLayer(curMarker);curMarker=L.marker([lat,lon],{icon:L.divIcon({html:'',iconSize:[18,18],className:''})}).addTo(map);map.setView([lat,lon],9);}
function plotBoth(a,b,c,d,dist){initMap();if(typeof L==='undefined')return;if(curMarker)map.removeLayer(curMarker);if(destMarker)map.removeLayer(destMarker);if(line)map.removeLayer(line);curMarker=L.marker([a,b],{icon:L.divIcon({html:'