This repository has no description
4.3 kB
149 lines
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 '© <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>