tools/brouter-web: preserve mirror customization (config + dark toggle + default overlays)

Static brouter-web on the Keenetic mirror pointed at the PC RouteServer.
config.js (host/layers/centre), baldav-custom.{css,js} (day/night toggle that
inverts only the tile layer + dark sidebar, one-time default POI overlays),
and a README with the deploy steps. Kept in-repo so the mirror is reproducible.
This commit is contained in:
4aiman 2026-09-09 13:50:42 +03:00
commit 74770c1e60
4 changed files with 222 additions and 0 deletions

View file

@ -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.<ver>.zip` (github nrenner/brouter-web), распаковать.
2. Положить туда `config.js`, `baldav-custom.css`, `baldav-custom.js`.
3. В `index.html` после `<script src="dist/brouter-web.js"></script>` добавить:
`<link rel="stylesheet" href="baldav-custom.css" />` и `<script src="baldav-custom.js"></script>`.
4. Скопировать всё в докрут веб-сервера роутера. Доступ: http://192.168.1.1:88/brouter-web/ (+ WAN :8088).
Данные (сегменты .rd5 + профили) — в `/opt/share/www/brouter/` на роутере и в
`/mnt/02F6E7F6F6E7E7BD/brouter-mirror/` на ПК (для RouteServer).

View file

@ -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; }

View file

@ -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);
})();

133
tools/brouter-web/config.js Normal file
View file

@ -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;
})();