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>
+
+
+ + +

+

+
+ Meer informatie + +
+ +
+ + + + + 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