From 973aec43ea54bbf95b64fbcb636403401d1ca60e Mon Sep 17 00:00:00 2001
From: Rutger Broekhoff
Date: Fri, 28 Aug 2026 18:03:05 +0200
Subject: Import from e4b104792206ee7ea64bf39c6b7d2c0c230f9d14
---
web/index.html | 52 ++++++++++++++++++++
web/script.js | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
web/style.css | 62 ++++++++++++++++++++++++
3 files changed, 263 insertions(+)
create mode 100644 web/index.html
create mode 100644 web/script.js
create mode 100644 web/style.css
(limited to 'web')
diff --git a/web/index.html b/web/index.html
new file mode 100644
index 0000000..f927d39
--- /dev/null
+++ b/web/index.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Routemon
+
Broncode
+
+
+ Systeeminformatie
+
+
+
+ | Situatiepublicatie van |
+ <onbekend> |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/script.js b/web/script.js
new file mode 100644
index 0000000..b3179b3
--- /dev/null
+++ b/web/script.js
@@ -0,0 +1,149 @@
+const dialog = document.querySelector("dialog");
+const closeButton = document.querySelector("dialog button");
+closeButton.addEventListener("click", () => {
+ dialog.close();
+});
+
+let map = L.map("map");
+
+L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
+ attribution: '© OpenStreetMap contributors',
+}).addTo(map);
+
+let layerGroup = L.layerGroup().addTo(map);
+
+document.forms["gpx-upload-form"].addEventListener("submit", (event) => {
+ event.preventDefault();
+ const gpxFileInput = document.getElementById("gpx-file-input");
+ if (gpxFileInput.files.length !== 1) {
+ alert("Voor de upload moet er exact één GPX-bestand zijn geselecteerd");
+ return;
+ }
+ load(gpxFileInput.files[0]);
+});
+
+function el(name, attrs, ...children) {
+ const node = document.createElement(name);
+ for (const [k, v] of Object.entries(attrs)) {
+ node.setAttribute(k, v);
+ }
+ node.replaceChildren(...children);
+ return node;
+}
+
+function txt(s) {
+ return document.createTextNode(s);
+}
+
+function tbl(rowcols) {
+ return el("table", {},
+ el("tbody", {},
+ ...rowcols.map((cols) =>
+ el("tr", {},
+ ...cols.map((col) => el("td", {}, col)),
+ ))));
+}
+
+async function showProblem(rsp) {
+ const problem = await rsp.json();
+
+ document.getElementById("dialog-title").innerText = problem.title;
+ if (problem.detail)
+ document.getElementById("dialog-message").innerText = problem.detail;
+ else
+ document.getElementById("dialog-message").innerText = "";
+ dialog.showModal();
+
+ let rows = [
+ [txt("HTTP-statuscode"),
+ el("a", { "href": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/" + rsp.status },
+ txt(rsp.status + " (" + rsp.statusText + ")"))],
+ [txt("Probleemtype"),
+ el("code", {}, txt(problem.type))],
+ [txt("Trace-ID"),
+ el("code", {}, txt(rsp.headers.get("X-Routemon-Trace-Id")))],
+ ];
+ if (problem.instance) {
+ rows.push([txt("Instantie"),
+ el("code", {}, txt(problem.instance))]);
+ }
+ document.getElementById("dialog-more-info").replaceChildren(tbl(rows));
+
+ return;
+}
+
+async function getSysinfo() {
+ const rsp = await fetch("http://localhost:8284/sysinfo", {
+ method: "GET",
+ headers: {
+ "Accept-Language": "nl-NL",
+ },
+ });
+ if (!rsp.ok) {
+ await showProblem(rsp);
+ return;
+ }
+
+ const res = await rsp.json();
+ document.getElementById("situation-publication-of").innerText = new Date(res.using_publication_of).toLocaleString();
+}
+
+async function load(gpxFile) {
+ const rsp = await fetch("http://localhost:8284/gpx", {
+ method: "POST",
+ headers: {
+ "Accept-Language": "nl-NL",
+ },
+ body: gpxFile,
+ });
+ if (!rsp.ok) {
+ await showProblem(rsp);
+ return;
+ }
+
+ const res = await rsp.json();
+ layerGroup.clearLayers();
+
+ let bounds = null;
+ for (const track of res.tracks) {
+ for (const segment of track.segments) {
+ const polyline = L.polyline(segment.points, { color: "blue" }).addTo(layerGroup);
+ if (bounds === null) {
+ bounds = polyline.getBounds();
+ } else {
+ bounds.extend(polyline.getBounds());
+ }
+ }
+ }
+ if (bounds !== null) {
+ map.fitBounds(bounds);
+ }
+
+ res.relevant_situations.forEach((sit) => {
+ let firstComment = sit.comments[0] || "";
+ const lfIndex = firstComment.indexOf("\n");
+ if (lfIndex !== -1) {
+ firstComment = firstComment.substring(0, lfIndex);
+ }
+ const commentEl =
+ el("div", {},
+ el("code", {}, txt(sit.id)),
+ txt(": " + firstComment));
+
+ const markerLayer = L.marker(sit.location);
+ markerLayer.addTo(layerGroup).bindPopup(commentEl);
+
+ sit.relevant_road_closures.forEach((rc) => {
+ rc.relevant_lss.forEach((ls) => {
+ const lineLayer = L.polyline(ls, {
+ color: "purple",
+ dashArray: "5, 10",
+ dashOffset: "0",
+ });
+ lineLayer.addTo(layerGroup);
+ });
+ });
+ });
+}
+
+getSysinfo();
diff --git a/web/style.css b/web/style.css
new file mode 100644
index 0000000..a96354c
--- /dev/null
+++ b/web/style.css
@@ -0,0 +1,62 @@
+html, body {
+ margin: 0px;
+ height: 100%;
+ font-family: sans-serif;
+ font-size: 14px;
+}
+
+#map { height: 100%; }
+
+.box {
+ padding: 10px;
+ background-color: white;
+ border: 2px solid #b0b0b0;
+ border-radius: 4px;
+}
+
+dialog button {
+ float: right;
+}
+
+details summary {
+ user-select: none;
+ cursor: pointer;
+}
+
+table {
+ border-collapse: collapse;
+ border: 1px solid black;
+ margin: 5px 0px;
+}
+
+th, td {
+ border: 1px solid black;
+ padding: 5px 5px;
+}
+
+form[name="gpx-upload-form"] {
+ z-index: 1000;
+ display: flex;
+ gap: 16pt;
+ position: absolute;
+ top: 10px;
+ right: 10px;
+}
+
+#sysinfo {
+ z-index: 1000;
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+}
+
+#sysinfo h3 {
+ margin: 0px 0px;
+}
+
+.help-cursor {
+ cursor: help;
+}
--
cgit v1.3