BRouter danger: detect untagged fords via BRouter river-class heuristic

OSM doesn't tag many real fords (ford=yes). BRouter still flags them with a high
estimated_river_class on the crossing way. Flag a track/path/footway with
estimated_river_class>=5 and no bridge/tunnel as 'wetcross' (возм. брод, light cyan,
shown as a guess vs the solid-blue confirmed ford). Verified against a real untagged
ford on the Rodionovo route (47.4926,39.8610): river_class=5 on a track, now detected.
This commit is contained in:
4aiman 2026-10-26 22:07:41 +03:00
commit 61cfe7c0ee
2 changed files with 19 additions and 5 deletions

View file

@ -1810,6 +1810,11 @@ GPS.parseRouteGeoJson = function(jsonString) {
let both = wt + " " + nt;
let type = null;
if (/\bford=(yes|stepping_stones)\b/.test(both)) type = "ford";
// likely (untagged) water crossing: a track/path/footway with a high BRouter
// river-class estimate and no bridge/tunnel. Catches fords OSM never tagged ford=yes
// (verified: a real ford showed estimated_river_class=5 on a track, no ford tag).
else if (/\bhighway=(track|path|footway)\b/.test(wt) && /\bestimated_river_class=(5|6|7|8|9|10)\b/.test(wt)
&& !/\bbridge=/.test(wt) && !/\btunnel=/.test(wt)) type = "wetcross";
else if (/\btracktype=grade[45]\b/.test(wt) || /\bsurface=(mud|sand)\b/.test(wt)
|| /\btrail_visibility=(bad|horrible|no)\b/.test(wt)
|| /\bsac_scale=(demanding_mountain_hiking|alpine_hiking|demanding_alpine_hiking|difficult_alpine_hiking)\b/.test(wt)

View file

@ -4300,16 +4300,25 @@ async function showRouteEditor(container, routeId) {
let all = [];
for (let d of legDangers) if (Array.isArray(d)) all = all.concat(d);
let fords = all.filter(x => x.type === "ford").length;
let wet = all.filter(x => x.type === "wetcross").length;
let rough = all.filter(x => x.type === "rough").length;
let dEl = document.querySelector("#re-danger");
if (dEl) dEl.textContent = (fords || rough) ? `⚠ брод: ${fords} · грунт: ${rough}` : "";
if (dEl) {
let bits = [];
if (fords) bits.push(`брод: ${fords}`);
if (wet) bits.push(`возм.брод: ${wet}`);
if (rough) bits.push(`грунт: ${rough}`);
dEl.textContent = bits.length ? "⚠ " + bits.join(" · ") : "";
}
if (!all.length) return;
// ford = confirmed OSM ford (solid blue); wetcross = likely untagged ford from a high
// BRouter river-class on a track (light cyan — a guess); rough = orange; ferry = purple.
let COL = { ford: "#0288d1", wetcross: "#4dd0e1", rough: "#ff9800", ferry: "#9c27b0" };
let LBL = { ford: "брод", wetcross: "возм. брод", rough: "грунт/тропа", ferry: "паром" };
let g = L.layerGroup();
for (let d of all) {
let col = d.type === "ford" ? "#00bcd4" : (d.type === "rough" ? "#ff9800" : "#9c27b0");
let label = d.type === "ford" ? "брод" : (d.type === "rough" ? "грунт/тропа" : "паром");
L.circleMarker([d.lat, d.lon], { radius: 7, color: "#fff", weight: 2, fillColor: col, fillOpacity: 1 })
.bindTooltip(label, { direction: "top" }).addTo(g);
L.circleMarker([d.lat, d.lon], { radius: 7, color: "#fff", weight: 2, fillColor: COL[d.type] || "#9c27b0", fillOpacity: 1 })
.bindTooltip(LBL[d.type] || d.type, { direction: "top" }).addTo(g);
}
g.addTo(map); dangerLayer = g;
}