diff --git a/tools/brouter-web/README.md b/tools/brouter-web/README.md new file mode 100644 index 0000000..bf8be91 --- /dev/null +++ b/tools/brouter-web/README.md @@ -0,0 +1,21 @@ +# BRouter-Web на зеркале BalDav + +Статический фронтенд nrenner/brouter-web 0.18.1, развёрнут на роутере (Keenetic uhttpd :88): +`/opt/share/www/brouter-web/`. Смотрит на свой RouteServer (ПК, systemd brouter.service, :17777). + +## Файлы кастомизации (кладутся рядом с распакованным brouter-web) +- `config.js` — host RouteServer (LAN 192.168.1.18:17777 / WAN 77.66.192.45:17777), профили с зеркала, + базовые слои (ÖPNV дефолт, CARTO Dark «needs key» и т.д.), центр Новочеркасск, профили trekking/shortest. +- `baldav-custom.css` + `baldav-custom.js` — подключены в index.html после `dist/brouter-web.js`: + тумблер день/ночь (☾/☀) в тулбаре → тёмная карта (invert только слой тайлов) + тёмная правая панель; + авто-простановка нужных POI-оверлеев один раз (флаг localStorage `baldav_bw_overlays_v1`). + +## Развёртывание +1. Скачать `brouter-web..zip` (github nrenner/brouter-web), распаковать. +2. Положить туда `config.js`, `baldav-custom.css`, `baldav-custom.js`. +3. В `index.html` после `` добавить: + `` и ``. +4. Скопировать всё в докрут веб-сервера роутера. Доступ: http://192.168.1.1:88/brouter-web/ (+ WAN :8088). + +Данные (сегменты .rd5 + профили) — в `/opt/share/www/brouter/` на роутере и в +`/mnt/02F6E7F6F6E7E7BD/brouter-mirror/` на ПК (для RouteServer). diff --git a/tools/brouter-web/baldav-custom.css b/tools/brouter-web/baldav-custom.css new file mode 100644 index 0000000..b792576 --- /dev/null +++ b/tools/brouter-web/baldav-custom.css @@ -0,0 +1,18 @@ +/* BalDav dark mode for BRouter-Web. Invert ONLY the tile layer (route/POI overlays + keep colour), dark backdrop, and dark-theme the right sidebar (was white). */ +body.bw-dark .leaflet-tile-pane { filter: invert(1) hue-rotate(180deg) brightness(1.5); } +body.bw-dark .leaflet-container { background: #1b1f24; } +body.bw-dark #sidebar, +body.bw-dark #sidebar .leaflet-sidebar-content, +body.bw-dark #sidebar .leaflet-sidebar-pane, +body.bw-dark .leaflet-control-layers, +body.bw-dark .leaflet-control-layers-expanded { background: #20242a !important; color: #dfe3e8 !important; } +body.bw-dark #sidebar label, +body.bw-dark #sidebar h1, +body.bw-dark #sidebar span, +body.bw-dark #sidebar a, +body.bw-dark #sidebar .leaflet-sidebar-header { color: #dfe3e8 !important; } +body.bw-dark #sidebar input[type="text"], +body.bw-dark #sidebar select { background:#2b3038 !important; color:#dfe3e8 !important; border-color:#3a4048 !important; } +body.bw-dark .leaflet-control-layers-separator { border-top-color: #3a4048 !important; } +#baldav-daynight { cursor: pointer; } diff --git a/tools/brouter-web/baldav-custom.js b/tools/brouter-web/baldav-custom.js new file mode 100644 index 0000000..9fac9fb --- /dev/null +++ b/tools/brouter-web/baldav-custom.js @@ -0,0 +1,50 @@ +(function () { + var LS_DARK = "baldav_bw_dark"; + function apply() { document.body.classList.toggle("bw-dark", localStorage.getItem(LS_DARK) === "1"); } + function toggle() { + var on = localStorage.getItem(LS_DARK) !== "1"; + localStorage.setItem(LS_DARK, on ? "1" : "0"); + apply(); + var b = document.getElementById("baldav-daynight"); + if (b) b.textContent = on ? "☀" : "☾"; // sun / moon + } + function addButton() { + if (document.getElementById("baldav-daynight")) return true; + var exp = document.getElementById("exportButton"); + if (!exp) return false; + var item = exp.closest(".nav-item") || exp.parentNode; + var li = document.createElement("div"); li.className = item.className || "nav-item"; + var a = document.createElement("a"); + a.id = "baldav-daynight"; a.className = exp.className || "nav-link"; a.href = "#"; + a.title = "Тёмная карта"; + a.textContent = (localStorage.getItem(LS_DARK) === "1") ? "☀" : "☾"; + a.onclick = function (e) { e.preventDefault(); toggle(); }; + li.appendChild(a); + item.parentNode.insertBefore(li, item); + return true; + } + var WANT = ["Cycling","Hiking","Bench","Toilets","Bank","ATM","Bar","BBQ","Cafe","Drinking water","Fast food","Food court","Pub","Restaurant","Bicycle parking","Bus station","Bakery","Beverages","Coffee","Supermarket","Picnic site","Viewpoint","Shelter","Route quality"]; + function setDefaultOverlays() { + if (localStorage.getItem("baldav_bw_overlays_v1") === "1") return true; + var labels = document.querySelectorAll("#layers-control-wrapper label, .leaflet-control-layers-overlays label"); + if (!labels.length) return false; + labels.forEach(function (lab) { + var t = (lab.textContent || "").trim(); + if (!WANT.some(function (w) { return t.indexOf(w) !== -1; })) return; + var cb = lab.querySelector("input[type=checkbox]") || lab.control || (lab.htmlFor && document.getElementById(lab.htmlFor)); + if (cb && cb.type === "checkbox" && !cb.checked) cb.click(); + }); + localStorage.setItem("baldav_bw_overlays_v1", "1"); + return true; + } + function init() { + apply(); + var tries = 0; + var iv = setInterval(function () { + tries++; + var b = addButton(), o = setDefaultOverlays(); + if ((b && o) || tries > 40) clearInterval(iv); + }, 500); + } + if (document.readyState !== "loading") init(); else document.addEventListener("DOMContentLoaded", init); +})(); diff --git a/tools/brouter-web/config.js b/tools/brouter-web/config.js new file mode 100644 index 0000000..0927b90 --- /dev/null +++ b/tools/brouter-web/config.js @@ -0,0 +1,133 @@ +(function () { + var hostname = window.location.hostname; + var origin = window.location.protocol + '//' + hostname + (window.location.port ? ':' + window.location.port : ''); + + BR.conf = {}; + + // Switch for intermodal routing demo + BR.conf.transit = false; + // or as query parameter (index.html?transit=true#zoom=...) + // (uses search/query (?) not hash (#) params, as config only executed once at load) + // TODO not included in permalink (better replace permalink with hash plugin) + //var params = new URLSearchParams(window.location.search.slice(1)); + //BR.conf.transit = params.has('transit') && (params.get('transit') === 'true'); + + if (hostname.endsWith('brouter.de')) { + // online service (brouter.de) configuration + + BR.conf.host = origin; + BR.conf.profilesUrl = origin + '/brouter/profiles2/'; + } else { + // desktop configuration + + BR.conf.host = (hostname === '192.168.1.1') ? 'http://192.168.1.18:17777' : 'http://' + hostname + ':17777'; + + // Pre-loading selected profile disabled locally. Needs brouter-web to run on a + // local web server with the profiles in a subdirectory or allowing file access + // in the Browser (security!), see + // https://github.com/mrdoob/three.js/wiki/How-to-run-things-locally + BR.conf.profilesUrl = origin + '/brouter/profiles2/'; + //BR.conf.profilesUrl = 'file://YOUR_PATH_TO/profiles2/'; + } + + BR.conf.privacyPolicyUrl = '/privacypolicy.html'; + + // Set the initial position and zoom level of the map + BR.conf.initialMapLocation = [47.41, 40.11]; + BR.conf.initialMapZoom = 12; + + BR.conf.profiles = [ + 'trekking', + 'shortest', + ]; + + // Map old, renamed legacy profile to new name (from hash of shared or bookmarked URLs) + BR.conf.profilesRename = { + 'hiking-beta': 'hiking-mountain', + }; + + // Removes default base layers when 'true'. Useful for only having custom layers (see below). + BR.conf.clearBaseLayers = true; + + // Add custom tile layers + // URL template see http://leafletjs.com/reference.html#tilelayer + // Multiple entries separated by comma (,) + BR.conf.baseLayers = { + // Clean, no-key layers first (CARTO now watermarks without an API key): + 'Public transport (ÖPNV)': 'https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png', + 'Topo (OpenTopoMap)': 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', + 'Satellite (Esri)': 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', + 'OpenStreetMap': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', + // Dark, but need a free API key (watermarked until keyed): + 'Dark (CARTO — needs key)': 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png', + }; + + // Base layer to show on start, as position number in the layer switcher, starting from 0, default is first + BR.conf.defaultBaseLayerIndex = 0; + + // Initial route line transparency (0-1, overridden by stored slider setting) + BR.conf.defaultOpacity = 0.67; + + // Minimum transparency slider value on load, values between 0 and 1 (0=invisible). + // 0 = no minimum, use stored setting; 1 = always reset to full visibility on load + BR.conf.minOpacity = 0.3; + + BR.conf.routingStyles = { + trailer: { + weight: 5, + dashArray: [10, 10], + opacity: 0.6, + color: 'magenta', + }, + track: { + weight: 5, + color: 'magenta', + opacity: BR.conf.defaultOpacity, + }, + trackCasing: { + weight: 8, + color: 'white', + // assumed to be same as track, see setOpacity + opacity: BR.conf.defaultOpacity, + }, + nodata: { + color: 'darkred', + }, + beeline: { + weight: 5, + dashArray: [1, 10], + color: 'magenta', + opacity: BR.conf.defaultOpacity, + }, + beelineTrailer: { + weight: 5, + dashArray: [1, 10], + opacity: 0.6, + color: 'magenta', + }, + }; + + BR.conf.markerColors = { + // awesome-markers colors (by color picker) + poi: '#436978', + start: '#72b026', + via: '#38aadd', + stop: '#d63e2a', + }; + + // transit (intermodal routing) demo config + if (BR.conf.transit) { + BR.conf.profiles = [ + 'trekking', + 'shortest', + ]; + } + + // regex needs to be in sync with server, see ServerHandler.getTrackName() + BR.conf.tracknameAllowedChars = 'a-zA-Z0-9 \\._\\-'; + + BR.conf.overpassBaseUrl = 'https://overpass.kumi.systems/api/interpreter'; + + // File size limit in kb for loading tracks + BR.conf.trackSizeLimit = 1024 * 10; +})();