This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

1<!doctype html> 2<html lang="en"> 3 <head> 4 <meta charset="utf-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1" /> 6 <title>where is lassulus?</title> 7 <link rel="stylesheet" href="/leaflet/leaflet.css" /> 8 <style> 9 html, 10 body { 11 margin: 0; 12 height: 100%; 13 background: #111; 14 } 15 #map { 16 position: absolute; 17 inset: 0; 18 } 19 #info { 20 position: absolute; 21 z-index: 1000; 22 left: 0; 23 right: 0; 24 bottom: 0; 25 padding: 0.5em 1em; 26 font: 14px/1.4 monospace; 27 color: #eee; 28 background: rgba(0, 0, 0, 0.7); 29 } 30 #info a { 31 color: #9cf; 32 } 33 </style> 34 </head> 35 <body> 36 <div id="map"></div> 37 <div id="info">loading…</div> 38 <script src="/leaflet/leaflet.js"></script> 39 <script> 40 (function () { 41 var map = L.map("map", { zoomControl: true }); 42 L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { 43 maxZoom: 19, 44 attribution: 45 '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', 46 }).addTo(map); 47 48 var layer = L.layerGroup().addTo(map); 49 var info = document.getElementById("info"); 50 51 function ago(iso) { 52 var s = Math.round((Date.now() - Date.parse(iso)) / 1000); 53 if (s < 60) return s + "s"; 54 if (s < 3600) return Math.round(s / 60) + "min"; 55 if (s < 86400) return (s / 3600).toFixed(1) + "h"; 56 return (s / 86400).toFixed(1) + "d"; 57 } 58 59 // trail fades from transparent (24h ago) to solid (now); one segment per point pair 60 function render(data) { 61 layer.clearLayers(); 62 var pts = data.points; 63 if (!pts.length) { 64 info.textContent = "no location in the last 24h"; 65 return; 66 } 67 var t0 = Date.parse(pts[0].t), 68 t1 = Date.parse(pts[pts.length - 1].t) || t0 + 1; 69 for (var i = 1; i < pts.length; i++) { 70 var a = pts[i - 1], 71 b = pts[i]; 72 var age = (Date.parse(b.t) - t0) / (t1 - t0); 73 L.polyline( 74 [ 75 [a.lat, a.lon], 76 [b.lat, b.lon], 77 ], 78 { 79 color: "#e33", 80 weight: 4, 81 opacity: 0.15 + 0.85 * age, 82 }, 83 ).addTo(layer); 84 } 85 var last = pts[pts.length - 1]; 86 if (last.acc) { 87 L.circle([last.lat, last.lon], { 88 radius: last.acc, 89 color: "#39f", 90 weight: 1, 91 fillOpacity: 0.1, 92 }).addTo(layer); 93 } 94 L.marker([last.lat, last.lon]) 95 .addTo(layer) 96 .bindPopup( 97 "last seen " + 98 ago(last.t) + 99 " ago<br>" + 100 new Date(last.t).toLocaleString(), 101 ); 102 info.innerHTML = 103 "<b>lassulus</b> is <b>" + 104 last.state + 105 "</b> · last seen " + 106 ago(last.t) + 107 " ago" + 108 " · " + 109 pts.length + 110 " points in the last 24h" + 111 ' · <a href="https://www.openstreetmap.org/?mlat=' + 112 last.lat + 113 "&mlon=" + 114 last.lon + 115 "#map=16/" + 116 last.lat + 117 "/" + 118 last.lon + 119 '">osm</a>'; 120 return L.latLngBounds( 121 pts.map(function (p) { 122 return [p.lat, p.lon]; 123 }), 124 ); 125 } 126 127 var first = true; 128 function refresh() { 129 fetch("/location.json", { cache: "no-cache" }) 130 .then(function (r) { 131 return r.json(); 132 }) 133 .then(function (data) { 134 var bounds = render(data); 135 if (first && bounds) { 136 map.fitBounds(bounds.pad(0.1), { maxZoom: 16 }); 137 first = false; 138 } 139 }) 140 .catch(function (e) { 141 info.textContent = "failed to load location: " + e; 142 }); 143 } 144 refresh(); 145 setInterval(refresh, 60 * 1000); 146 })(); 147 </script> 148 </body> 149</html>