const apiBaseUrl = "https://routemon-api.fautchen.eu"; // const apiBaseUrl = "http://localhost:8284"; const dialog = document.querySelector("dialog"); const closeButton = document.querySelector("dialog button"); closeButton.addEventListener("click", () => { dialog.close(); }); function handlePeriodLimitPresetSelInput(selEl, inputEl) { selEl.addEventListener("input", (event) => { if (selEl.value === "custom") { inputEl.classList.remove("hidden"); } else { inputEl.classList.add("hidden"); } }); } function getPeriodLimitPresetSelInput(selEl, inputEl) { const relativeDateTime = (durationArgs) => Temporal.Now.plainDateTimeISO().add(durationArgs); let durationArgs = {}; switch (selEl.value) { case "-1m": return relativeDateTime({ months: -1 }); case "-2w": return relativeDateTime({ weeks: -2 }); case "-1w": return relativeDateTime({ weeks: -1 }); case "0": return relativeDateTime({ seconds: 0 }); case "+1w": return relativeDateTime({ weeks: 1 }); case "+2w": return relativeDateTime({ weeks: 2 }); case "+1m": return relativeDateTime({ months: 1 }); case "custom": return Temporal.PlainDateTime.from(inputEl.value); } } handlePeriodLimitPresetSelInput( document.getElementById("check-period-begin-preset-select"), document.getElementById("check-period-begin-input")); handlePeriodLimitPresetSelInput( document.getElementById("check-period-end-preset-select"), document.getElementById("check-period-end-input")); window.addEventListener("load", (event) => { const oneWeek = Temporal.Duration.from({ weeks: 1 }); const now = Temporal.Now.plainDateTimeISO(); document.getElementById("check-period-begin-input").value = now.subtract(oneWeek).toString({ smallestUnit: "minute", }); document.getElementById("check-period-end-input").value = now.add(oneWeek).toString({ smallestUnit: "minute", }); }); 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) { showDialog({ title: "Geen bestand geselecteerd", detail: "Voor de upload moet er exact één GPX-bestand zijn geselecteerd", moreInfoEl: null, }); return; } const tzidHere = Temporal.Now.timeZoneId(); const cpBegin = getPeriodLimitPresetSelInput(document.getElementById("check-period-begin-preset-select"), document.getElementById("check-period-begin-input")).toZonedDateTime(tzidHere).toInstant(); const cpEnd = getPeriodLimitPresetSelInput(document.getElementById("check-period-end-preset-select"), document.getElementById("check-period-end-input")).toZonedDateTime(tzidHere).toInstant(); load({ gpxFile: gpxFileInput.files[0], checkPeriod: { begin: cpBegin, end: cpEnd, }, }); }); 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)), )))); } function showDialog({ title, detail, moreInfoEl }) { document.getElementById("dialog-title").innerText = title; document.getElementById("dialog-message").innerText = detail !== null ? detail : ""; if (moreInfoEl !== null) { document.getElementById("dialog-more-info").replaceChildren(moreInfoEl); document.getElementById("dialog-more-info-container").classList.remove("hidden"); } else { document.getElementById("dialog-more-info").replaceChildren(); document.getElementById("dialog-more-info-container").classList.add("hidden"); } dialog.showModal(); } async function showProblem(rsp) { const problem = await rsp.json(); 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))]); } showDialog({ title: problem.title, detail: problem.detail ? problem.detail : null, moreInfoEl: tbl(rows), }); } async function getSysinfo() { const rsp = await fetch(apiBaseUrl + "/sysinfo", { method: "GET", headers: { "Accept-Language": "nl-NL", }, }); if (!rsp.ok) { await showProblem(rsp); return; } const res = await rsp.json(); let rows = [ [el("span", { "class": "help-cursor", "title": "Publicatietijdstip van ingeladen DATEX II-situatiepublicatie (van NDW)" }, txt("Situatiepublicatie van")), txt(new Date(res.using_publication_of).toLocaleString())], [txt("Grootte BLSE-index"), txt(res.blse_index_size.toString())], [txt("Grootte BPE-index"), txt(res.bpe_index_size.toString())], ]; document.getElementById("sysinfo-details").replaceChildren(tbl(rows)); } async function load({ gpxFile, checkPeriod }) { let compressedParts = []; const compressedReader = gpxFile.stream().pipeThrough(new CompressionStream("deflate-raw")).getReader(); while (true) { const { done, value } = await compressedReader.read(); if (done) { break; } else { compressedParts.push(value); } } const rsp = await fetch(apiBaseUrl + `/gpx?cpbegin=${encodeURI(checkPeriod.begin.toString({ smallestUnit: "minute" }))}&cpend=${encodeURI(checkPeriod.end.toString({ smallestUnit: "minute" }))}`, { method: "POST", headers: { "Accept-Language": "nl-NL", "Content-Encoding": "x-deflate-raw", "Content-Type": "application/gpx+xml", }, body: new Blob(compressedParts), }); 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)); // TODO: decide what to do if the situation has no location if (sit.location) { 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();