brouter-web: dual-sided route coloring (slope offset-left, surface offset-right)
Per-segment offset polylines in pixels, rebuilt on zoom; surface from per-segment WayTags (messages[i][9]), slope from point altitudes. Centre keeps the neutral black/white casing. v1 — offset width/colours to tune visually.
This commit is contained in:
parent
06ffab66a1
commit
1df97d3e73
1 changed files with 66 additions and 3 deletions
|
|
@ -131,6 +131,68 @@
|
|||
var d = prev.distanceTo(cur); if (!d) return 0;
|
||||
return 100 * (((cur.alt) || 0) - ((prev.alt) || 0)) / d; // SIGNED grade %
|
||||
}
|
||||
|
||||
// --- Dual-sided route coloring: SLOPE offset to the LEFT, SURFACE offset to the RIGHT ---
|
||||
// (task: see gradient AND surface at once). Surface comes from the per-segment WayTags in
|
||||
// feature.properties.messages[i][9]; offset is in pixels, recomputed on zoom.
|
||||
var SURF_COL = {
|
||||
asphalt: "#455a64", concrete: "#455a64", paved: "#455a64", paving_stones: "#455a64",
|
||||
compacted: "#6d4c41", ground: "#8d6e63", dirt: "#8d6e63", earth: "#8d6e63", soil: "#8d6e63",
|
||||
cobblestone: "#78909c", sett: "#78909c", gravel: "#b0bec5", fine_gravel: "#b0bec5", pebblestone: "#b0bec5",
|
||||
clay: "#a1887f", grass: "#7cb342", grass_paver: "#7cb342", wood: "#a1887f",
|
||||
sand: "#ffca28", mud: "#ff7043"
|
||||
};
|
||||
function surfColor(wayTags) {
|
||||
var m = /(?:^|\s)surface=([a-z_]+)/.exec(wayTags || "");
|
||||
return (m && SURF_COL[m[1]]) || "#9e9e9e"; // grey = unknown surface
|
||||
}
|
||||
function slopeBandColor(gpct) { var i = Math.min(10, Math.floor(Math.abs(gpct || 0))); return gpct < 0 ? DOWN[i] : UP[i]; }
|
||||
function offsetLL(map, lls, px) {
|
||||
var pts = [], i;
|
||||
for (i = 0; i < lls.length; i++) pts.push(map.latLngToLayerPoint(lls[i]));
|
||||
var out = [];
|
||||
for (i = 0; i < pts.length; i++) {
|
||||
var a = pts[Math.max(0, i - 1)], b = pts[Math.min(pts.length - 1, i + 1)];
|
||||
var dx = b.x - a.x, dy = b.y - a.y, len = Math.sqrt(dx * dx + dy * dy) || 1;
|
||||
out.push(map.layerPointToLatLng(L.point(pts[i].x + (-dy / len) * px, pts[i].y + (dx / len) * px)));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
var dualData = null, dualGroup = null, dualHooked = false;
|
||||
function buildDual() {
|
||||
try {
|
||||
var map = window.BR && BR.__map;
|
||||
if (!map || !dualData || !dualData.length) return;
|
||||
if (dualGroup) { map.removeLayer(dualGroup); dualGroup = null; }
|
||||
var g = L.layerGroup(), OFF = 5;
|
||||
dualData.forEach(function (seg) {
|
||||
var lls = seg.lls, msgs = seg.msgs;
|
||||
if (!lls || lls.length < 2) return;
|
||||
var L1 = offsetLL(map, lls, -OFF), R1 = offsetLL(map, lls, OFF);
|
||||
for (var i = 0; i < lls.length - 1; i++) {
|
||||
var d = lls[i].distanceTo(lls[i + 1]) || 1;
|
||||
var a0 = lls[i].alt, a1 = lls[i + 1].alt;
|
||||
var gpct = (a0 == null || a1 == null) ? 0 : 100 * (a1 - a0) / d;
|
||||
L.polyline([L1[i], L1[i + 1]], { color: slopeBandColor(gpct), weight: 3, opacity: 0.95, interactive: false, lineCap: "butt" }).addTo(g);
|
||||
var wt = (msgs && msgs[i]) ? msgs[i][9] : "";
|
||||
L.polyline([R1[i], R1[i + 1]], { color: surfColor(wt), weight: 3, opacity: 0.95, interactive: false, lineCap: "butt" }).addTo(g);
|
||||
}
|
||||
});
|
||||
g.addTo(map); dualGroup = g;
|
||||
} catch (e) {}
|
||||
}
|
||||
function captureDual(segments) {
|
||||
dualData = [];
|
||||
for (var i = 0; segments && i < segments.length; i++) {
|
||||
var seg = segments[i], r = seg && seg._routing;
|
||||
if (r && r.beeline) continue;
|
||||
if (!seg || !seg.getLatLngs) continue;
|
||||
var msgs = (seg.feature && seg.feature.properties && seg.feature.properties.messages) || null;
|
||||
dualData.push({ lls: seg.getLatLngs(), msgs: msgs ? msgs.slice(1) : null }); // drop header row
|
||||
}
|
||||
if (!dualHooked && window.BR && BR.__map) { dualHooked = true; BR.__map.on("zoomend", buildDual); }
|
||||
buildDual();
|
||||
}
|
||||
// White+black casing under the colored line so it reads on any background. leaflet-hotline
|
||||
// has only ONE outlineColor, so we render the colored core thin (no built-in outline) and
|
||||
// add two plain polylines beneath it in a dedicated pane below the hotline pane: a wide
|
||||
|
|
@ -175,10 +237,11 @@
|
|||
// Prepend black+white casings to whatever hotlines the provider computes (added first
|
||||
// = under the colored line). Wrap once.
|
||||
if (!pr.__baldavCased && typeof pr.computeLayers === "function") {
|
||||
var orig = pr.computeLayers.bind(pr);
|
||||
pr.computeLayers = function (segments) {
|
||||
var hotlines = orig(segments);
|
||||
try { return makeCasings(segments).concat(hotlines); } catch (e) { return hotlines; }
|
||||
// dual-sided: slope offset-left + surface offset-right (managed on the map itself,
|
||||
// rebuilt on zoom); the overlay keeps only the neutral black/white centre casing.
|
||||
try { captureDual(segments); } catch (e) {}
|
||||
try { return makeCasings(segments); } catch (e) { return []; }
|
||||
};
|
||||
pr.__baldavCased = true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue