summaryrefslogtreecommitdiff
path: root/modules/subd-buien/index.html
diff options
context:
space:
mode:
Diffstat (limited to 'modules/subd-buien/index.html')
-rw-r--r--modules/subd-buien/index.html279
1 files changed, 279 insertions, 0 deletions
diff --git a/modules/subd-buien/index.html b/modules/subd-buien/index.html
new file mode 100644
index 0000000..03c56e8
--- /dev/null
+++ b/modules/subd-buien/index.html
@@ -0,0 +1,279 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Buien</title>
+<script>
+var stamps = null;
+var currentIndex = -1;
+var imageMap = new Map(); // stamp => (Image | false); false means 'still downloading'
+var pendingImage = null; // null | [idx, stamp]
+
+var dbgpageload = new Date();
+
+function dbgnow() {
+ return new Date() - dbgpageload;
+}
+
+function renderStamp(s) {
+ function zeropad2(num) {
+ if (num < 10) return "0" + num;
+ else return num.toString();
+ }
+
+ var date = new Date(Date.UTC(
+ parseInt(s.slice(0, 4), 10),
+ parseInt(s.slice(5, 7), 10) - 1,
+ parseInt(s.slice(8, 10), 10),
+ parseInt(s.slice(11, 13), 10),
+ parseInt(s.slice(14, 16), 10),
+ parseInt(s.slice(17, 19), 10)
+ ));
+ return date.toLocaleString("nl-NL", {timeZone: "Europe/Amsterdam"});
+ return date.getFullYear() + "-" +
+ zeropad2(date.getMonth() + 1) + "-" +
+ zeropad2(date.getDate()) + " " +
+ zeropad2(date.getHours()) + ":" +
+ zeropad2(date.getMinutes()) + ":" +
+ zeropad2(date.getSeconds());
+}
+
+function getStamps() {
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState == 4) {
+ if (xhr.status != 200){
+ alert("Error getting list of radar frames from server!");
+ } else {
+ stamps = xhr.response;
+ if (currentIndex == -1) {
+ doGotoLast();
+ }
+ }
+ }
+ };
+ xhr.open("GET", "/api/list/json");
+ xhr.responseType = "json";
+ xhr.send();
+}
+
+// direction: -1 to download earlier frames first; 1 to download later frames first.
+// Returns a list of indices in stamps.
+function determineImageSetAround(idx, direction) {
+ if (stamps.length == 0) return [];
+ if (idx < 0) idx = 0;
+ if (idx >= stamps.length) idx = stamps.length - 1;
+
+ var res = [];
+
+ var forwardPeek = 20;
+ var backwardPeek = 10;
+ var sizeLimit = 10;
+
+ function loopcheck(start, end, incr) {
+ for (var i = start; i != end; i += incr) {
+ if (res.length >= sizeLimit) return;
+ if (!imageMap.has(stamps[i])) res.push(i);
+ }
+ }
+
+ switch (direction) {
+ case -1:
+ loopcheck(idx, Math.max(-1, idx - forwardPeek - 1), -1);
+ loopcheck(Math.min(stamps.length, idx + 1), Math.min(stamps.length, idx + backwardPeek + 1), 1);
+ break;
+
+ case 1:
+ default:
+ loopcheck(idx, Math.min(stamps.length, idx + forwardPeek + 1), 1);
+ loopcheck(Math.max(-1, idx - 1), Math.max(-1, idx - backwardPeek - 1), -1);
+ break;
+ }
+
+ return res;
+}
+
+function readUint64LE(buffer, offset) {
+ var arr = new DataView(buffer, offset, 8);
+ var hi = arr.getUint32(4, true);
+ if (hi >= (1 << (53 - 32))) {
+ throw new Error("readUint64LE: Doesn't fit in Number");
+ }
+ return arr.getUint32(0, true) + (hi << 32);
+}
+
+// cb : (err) -> ()
+function downloadImages(wantedindices, cb) {
+ var indices = [];
+ for (var i = 0; i < wantedindices.length; i++) {
+ if (!imageMap.has(stamps[wantedindices[i]])) {
+ indices.push(wantedindices[i]);
+ imageMap.set(stamps[wantedindices[i]], false);
+ }
+ }
+
+ // console.log(dbgnow(), "Downloading:", indices);
+
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function(){
+ if (xhr.readyState == 4) {
+ if (xhr.status != 200){
+ cb("Server error while downloading images");
+ } else {
+ var buffer = xhr.response;
+
+ var numLoaded = 0, numTotal = 0;
+
+ var cursor = 0;
+ for (var i = 0; cursor < buffer.byteLength; i++) {
+ var pnglen = readUint64LE(buffer, cursor);
+ if (!(imageMap.get(stamps[indices[i]]) instanceof Image)) {
+ var arr = new Uint8Array(buffer, cursor + 8, pnglen);
+ var blob = new Blob([arr], { type: "image/png" })
+ var url = URL.createObjectURL(blob);
+ var image = new Image();
+ numTotal++;
+ image.onload = function () {
+ URL.revokeObjectURL(url);
+ numLoaded++;
+ if (numLoaded >= numTotal) {
+ // console.log(dbgnow(), "Done:", indices);
+ cb(null);
+ }
+ };
+ image.src = url;
+ imageMap.set(stamps[indices[i]], image);
+ }
+ cursor += 8 + pnglen;
+ }
+ }
+ }
+ };
+ xhr.open("GET", "/api/images?s=" + indices.map(i => stamps[i]).join(","));
+ xhr.responseType = "arraybuffer";
+ xhr.send();
+}
+
+function showPendingImage() {
+ if (pendingImage != null) {
+ var idx = pendingImage[0], stamp = pendingImage[1];
+ pendingImage = null;
+ showImage(idx, stamp);
+ }
+}
+
+function showImage(idx, stamp) {
+ // console.log("showImage(" + idx + ", '" + stamp + "') -> " + imageMap.has(stamp));
+ if (imageMap.has(stamp)) {
+ var image = imageMap.get(stamp);
+ if (image === false) {
+ pendingImage = [idx, stamp];
+ return;
+ }
+
+ var cvs = document.getElementById("imgcvs");
+ var ctx = cvs.getContext("2d");
+ ctx.drawImage(image, 0, 0);
+
+ var dateelem = document.getElementById("imgdate");
+ dateelem.innerHTML = "";
+ dateelem.appendChild(document.createTextNode(renderStamp(stamp)));
+
+ pendingImage = null;
+ } else {
+ pendingImage = [idx, stamp];
+
+ downloadImages(determineImageSetAround(idx), function (err) {
+ if (err) {
+ alert("Error getting images from server:\n" + err);
+ pendingImage = null;
+ } else {
+ showPendingImage();
+ }
+ });
+ }
+}
+
+function gotoIndex(idx) {
+ if (stamps == null || stamps.length == 0) { currentIndex = -1; return; }
+ if (idx < 0) idx = 0;
+ if (idx >= stamps.length) idx = stamps.length - 1;
+
+ var dir = idx == currentIndex - 1 ? -1
+ : idx == currentIndex + 1 ? 1
+ : 0;
+
+ currentIndex = idx;
+
+ // console.log(dbgnow(), "Goto " + currentIndex);
+
+ if (dir != 0) {
+ var missing = false;
+ for (var i = 1; i <= 8; i++) {
+ if (currentIndex + i * dir < 0 || currentIndex + i * dir >= stamps.length) break;
+ if (!imageMap.has(stamps[currentIndex + i * dir])) { missing = true; break; }
+ }
+
+ if (missing) {
+ downloadImages(determineImageSetAround(currentIndex, dir), function () {
+ showPendingImage();
+ });
+ }
+ }
+
+ showImage(currentIndex, stamps[currentIndex]);
+}
+
+function doGotoFirst() {
+ if (stamps == null) return;
+ gotoIndex(0);
+}
+
+function doGotoOffset(off) {
+ if (stamps == null) return;
+ var newIndex = Math.min(stamps.length - 1, Math.max(0, currentIndex + off));
+ gotoIndex(newIndex);
+}
+
+function doGotoLast() {
+ if (stamps == null) return;
+ gotoIndex(stamps.length > 0 ? stamps.length - 1 : 0);
+}
+
+window.addEventListener("load", function () {
+ getStamps();
+});
+
+window.addEventListener("keydown", function (ev) {
+ if (ev.key == "ArrowLeft") { doGotoOffset(-1); ev.preventDefault(); }
+ else if (ev.key == "ArrowRight") { doGotoOffset(1); ev.preventDefault(); }
+});
+</script>
+<style>
+button.wide {
+ width: 40px;
+ margin-right: 20px;
+}
+</style>
+</head>
+<body>
+<noscript><b>This page requires JavaScript to do anything.</b></noscript>
+
+<h1>Buien</h1>
+
+<p>Warning: This page can load a lot of images; one frame is between 30KB and 70KB.
+One day (288 frames) is about 14MB.</p>
+
+<canvas id="imgcvs" width="550" height="512">Your browser does not support the <code>&lt;canvas&gt;</code> element. Get a newer/different browser.</canvas>
+<br>
+<span id="imgdate"></span><br>
+<button class="wide" onclick="doGotoFirst()">&lt;&lt;</button>
+<button class="wide" onclick="doGotoOffset(-288)">-1d</button>
+<button class="wide" onclick="doGotoOffset(-12)">-1h</button>
+<button class="wide" onclick="doGotoOffset(-1)">&lt;</button>
+<button class="wide" onclick="doGotoOffset(1)">&gt;</button>
+<button class="wide" onclick="doGotoOffset(12)">+1h</button>
+<button class="wide" onclick="doGotoOffset(288)">+1d</button>
+<button class="wide" onclick="doGotoLast()">&gt;&gt;</button>
+</body>
+</html>