diff --git a/README.md b/README.md
index d6db7f0..acbaeda 100644
--- a/README.md
+++ b/README.md
@@ -82,20 +82,6 @@ value also increases the size of the `strecken.pmtiles` file. If not
given explicitly `maxZoom` defaults to 10.
-## Map Editing Mode
-
-As a new experimental feature, Streckenkarte implements a simple
-[brouter][brouter] frontend to allow for easy editing, currently only
-supported on browsers that support
-[showDirectoryPicker](https://developer.mozilla.org/en-US/docs/Web/API/Window/showDirectoryPicker). It
-can be accessed by adding `?edit=1` to the URL and then clicking the
-"edit" button that appears in the top right corner. Then, the
-directory containing the files for the layer that is to be edited can
-be opened in the file selection dialog and lines can be drawn as in
-the normal brouter web frontend. By clicking on "save", the lines are
-saved locally in the chosen directory and will only appear on the
-production map if processed correctly by the tile-generating script.
-
## Troubleshooting
### My pmtiles file is huge (hundreds of megabytes)
diff --git a/frontend/common/map.js b/frontend/common/map.js
deleted file mode 100644
index 771c311..0000000
--- a/frontend/common/map.js
+++ /dev/null
@@ -1,206 +0,0 @@
-function layer_legend(layer) {
- return '' + layer["name"];
-}
-
-const rules = new Array;
-const l = new Array;
-const legend = L.control({position: 'bottomleft'});
-legend.onAdd = function (map) {
- const div = L.DomUtil.create('div', 'legend');
- for (let i = 0; i < l.length; i++) {
- div.innerHTML += layer_legend(l[i]) + "
";
- }
- return div;
-};
-
-
-const map = L.map("map", { center: [52,13], zoom: 3, minZoom: 0 });
-
-function update_hash() {
- const {lat, lng} = this.getCenter();
- const zoom = this.getZoom();
- const digits = 4;
- window.history.replaceState(null, '', `#map=${zoom}/${lat.toFixed(digits)}/${lng.toFixed(digits)}`);
-}
-
-function onHashChange() {
- const hash = document.location.hash;
- const coords = decodeURIComponent(hash.slice(5)).split("/") // strip off the #map= part
- const latLng = L.latLng(parseFloat(coords[1]), parseFloat(coords[2]));
- map.setView(latLng, parseInt(coords[0]));
-}
-
-map.on("moveend", update_hash);
-map.on("zoomend", update_hash);
-
-fetch("layers.json")
- .then((response) => response.json())
- .then((data) => {
- document.title = data["name"]
- const layers = data["layers"]
- for (let key in layers) {
- l.push({ dirname: key , name: layers[key]["humanname"], color: layers[key]["color"] });
- rules.push({
- dataLayer: key,
- symbolizer: new protomapsL.LineSymbolizer(layers[key])
- });
- }
- const tiles = data["tilelayer"]
- const osm = L.tileLayer(
- tiles["url_template"],
- {
- maxZoom: 19,
- attribution: tiles["attribution"]
- }
- );
- const strecken = protomapsL.leafletLayer({
- url: "strecken.pmtiles",
- maxDataZoom: data["maxZoom"] ?? 10,
- maxZoom: 19,
- paintRules: rules,
- });
-
- osm.addTo(map);
- legend.addTo(map);
- strecken.addTo(map);
- })
-
-let dirHandle;
-let editMode = false;
-let markers = [];
-let geojsons = [];
-let geojson;
-let editlayer;
-
-function addGeoJsonToMap(dat) {
- if (editlayer != undefined) {
- const style = {
- "color": editlayer.color
- };
- g = L.geoJSON(dat, {style: style});
- } else {
- g = L.geoJSON(dat);
- }
- g.addTo(map);
- return g;
-}
-
-async function updateBrouter () {
- if (markers.length > 0) {
- for (i=1; i< markers.length-1; i++) {
- markers[i]._icon.classList.remove("red");
- }
- markers[markers.length-1]._icon.classList.add("red");
- markers[0]._icon.classList.add("green");
- }
- if (markers.length < 2) {
- if (geojson != undefined) {
- map.removeLayer(geojson);
- }
- return;
- }
- geojsons = [];
- for (let i = 0; i < markers.length - 1 ; i++) {
- const marker1 = markers[i].getLatLng();
- const marker2 = markers[i+1].getLatLng();
- const url = `https://brouter.de/brouter?lonlats=${marker1.lng},${marker1.lat}|${marker2.lng},${marker2.lat}&profile=rail&alternativeidx=0&format=geojson`;
- fetch(url).then((response) => response.json())
- .then((data) => {
- if (geojson != undefined) {
- map.removeLayer(geojson);
- }
- geojsons.push(data.features[0]);
- const dat = {type: "FeatureCollection", features: geojsons};
- geojson = addGeoJsonToMap(dat);
- })
- }
-}
-
-map.on('click', function(e) {
- if (!editMode) {
- return;
- }
- marker = new L.marker(e.latlng, {draggable: true}) ;
- markers.push(marker);
- marker.on("click", function(e) {
- map.removeLayer(this);
- markers = markers.filter(item => item != this);
- updateBrouter();
- })
- marker.on("dragend", function(e) {
- updateBrouter();
- });
- marker.addTo(map);
- updateBrouter();
-});
-
-
-async function pickDirectory(e){
- e.stopPropagation()
- L.DomEvent.preventDefault(e);
- if (!editMode) {
- dirHandle = await window.showDirectoryPicker({ mode: 'readwrite' });
- for (i = 0; i < l.length ; i++ ) {
- console.log(l[i].dirname);
- if (l[i].dirname === dirHandle.name) {
- editlayer = l[i];
- this.innerHTML = "Editing layer " + layer_legend(l[i]) + "
" + this.innerHTML
- break;
- }
- }
-
- document.getElementById("edit-mode").style.color = "red";
- document.getElementById("edit-mode").innerHTML = "save";
- editMode = true;
- } else {
- const filename = window.prompt("Enter filename:", "test");
- const dat = {type: "FeatureCollection", features: geojsons};
- const file = await dirHandle.getFileHandle(`${filename}.geojson`, {
- create: true
- });
- const blob = new Blob([JSON.stringify(dat)]);
- const writableStream = await file.createWritable();
- await writableStream.write(blob);
- await writableStream.close();
- addGeoJsonToMap(dat);
- for (i=0; i {
- const buttonDiv = L.DomUtil.create('div', 'legend');
- if ("showDirectoryPicker" in window) {
- buttonDiv.innerHTML = ``;
- buttonDiv.addEventListener('click', pickDirectory, this)
- } else {
- buttonDiv.innerHTML = "Your browser does not support editing.
As of 2025, editing is supported on Chromium-based browsers only.";
- }
- return buttonDiv;
- };
- customButton.addTo(map)
-}
-
-
-function resize() {
- document.getElementById("map").style.height = window.innerHeight + 'px';
-}
-resize();
-window.addEventListener('resize', () => {
- resize();
-});
-
-window.addEventListener("hashchange", onHashChange);
-onHashChange();
-
-
-
diff --git a/frontend/map.js b/frontend/map.js
new file mode 100644
index 0000000..413d285
--- /dev/null
+++ b/frontend/map.js
@@ -0,0 +1,75 @@
+
+const rules = new Array;
+const l = new Array;
+var legend = L.control({position: 'bottomleft'});
+legend.onAdd = function (map) {
+ var div = L.DomUtil.create('div', 'legend');
+ for (var i = 0; i < l.length; i++) {
+ div.innerHTML += '' + l[i]["name"] + "
";
+ }
+ return div;
+};
+
+
+const map = L.map("map", { center: [52,13], zoom: 3, minZoom: 0 });
+
+function update_hash() {
+ const {lat, lng} = this.getCenter();
+ const zoom = this.getZoom();
+ const digits = 4;
+ window.history.replaceState(null, '', `#map=${zoom}/${lat.toFixed(digits)}/${lng.toFixed(digits)}`);
+}
+
+function onHashChange() {
+ const hash = document.location.hash;
+ const coords = decodeURIComponent(hash.slice(5)).split("/") // strip off the #map= part
+ const latLng = L.latLng(parseFloat(coords[1]), parseFloat(coords[2]));
+ map.setView(latLng, parseInt(coords[0]));
+}
+
+map.on("moveend", update_hash);
+map.on("zoomend", update_hash);
+
+fetch("layers.json")
+ .then((response) => response.json())
+ .then((data) => {
+ document.title = data["name"]
+ layers = data["layers"]
+ for (let key in layers) {
+ l.push({ name: layers[key]["humanname"], color: layers[key]["color"] });
+ rules.push({
+ dataLayer: key,
+ symbolizer: new protomapsL.LineSymbolizer(layers[key])
+ });
+ }
+ const tiles = data["tilelayer"]
+ var osm = L.tileLayer(
+ tiles["url_template"],
+ {
+ maxZoom: 19,
+ attribution: tiles["attribution"]
+ }
+ );
+ var strecken = protomapsL.leafletLayer({
+ url: "strecken.pmtiles",
+ maxDataZoom: data["maxZoom"] ?? 10,
+ maxZoom: 19,
+ paintRules: rules,
+ });
+
+ osm.addTo(map);
+ legend.addTo(map);
+ strecken.addTo(map);
+ })
+
+function resize() {
+ document.getElementById("map").style.height = window.innerHeight + 'px';
+}
+resize();
+window.addEventListener('resize', () => {
+ resize();
+});
+
+window.addEventListener("hashchange", onHashChange);
+onHashChange();
+
diff --git a/frontend/common/styles.css b/frontend/styles.css
similarity index 77%
rename from frontend/common/styles.css
rename to frontend/styles.css
index 150beda..d756f67 100644
--- a/frontend/common/styles.css
+++ b/frontend/styles.css
@@ -19,5 +19,3 @@ body {
display: inline-block;
margin-right: 0.7em;
}
-img.red { filter: hue-rotate(120deg); }
-img.green { filter: hue-rotate(-120deg); }