Four new route visualization modes extracted from BRouter WayTags: - Smoothness: excellent (green) → impassable (dark red) - Track Type: grade1 (green, best) → grade5 (red, worst) - Cycleway: track/lane/shared_lane/no with direction awareness - Bike Route: icn/ncn/rcn/lcn priority from route_bicycle_* tags Each mode includes map polyline coloring, elevation chart coloring, inline legend, hover label, i18n (EN+DE), and mock fixture data. Dockerfile patched to also expose tracktype in BRouter WayTags (smoothness, cycleway, and route_bicycle already present). Dropdown order: plain, elevation, grade, surface, road type, speed limit, smoothness, track type, cycleway, bike route. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
144 lines
4.3 KiB
TypeScript
144 lines
4.3 KiB
TypeScript
import type { Page } from "@playwright/test";
|
|
|
|
/**
|
|
* Canned enriched route for 3 nearby Berlin waypoints:
|
|
* [52.520, 13.405] → [52.516, 13.377] → [52.510, 13.390]
|
|
*
|
|
* ~3.2km total, 6 track points with elevation.
|
|
*/
|
|
const MOCK_ROUTE_3WP = {
|
|
geojson: {
|
|
type: "FeatureCollection",
|
|
features: [{
|
|
type: "Feature",
|
|
geometry: {
|
|
type: "LineString",
|
|
coordinates: [
|
|
[13.405, 52.520, 34], [13.398, 52.519, 36], [13.391, 52.518, 38],
|
|
[13.384, 52.517, 40], [13.377, 52.516, 42],
|
|
[13.379, 52.514, 40], [13.382, 52.513, 38],
|
|
[13.385, 52.512, 36], [13.388, 52.511, 35], [13.390, 52.510, 34],
|
|
],
|
|
},
|
|
properties: {},
|
|
}],
|
|
},
|
|
coordinates: [
|
|
[13.405, 52.520, 34], [13.398, 52.519, 36], [13.391, 52.518, 38],
|
|
[13.384, 52.517, 40], [13.377, 52.516, 42],
|
|
[13.379, 52.514, 40], [13.382, 52.513, 38],
|
|
[13.385, 52.512, 36], [13.388, 52.511, 35], [13.390, 52.510, 34],
|
|
],
|
|
segmentBoundaries: [0, 4],
|
|
totalLength: 3200,
|
|
totalAscend: 8,
|
|
surfaces: [
|
|
"asphalt", "asphalt", "cobblestone", "cobblestone", "gravel",
|
|
"gravel", "asphalt", "asphalt", "asphalt", "asphalt",
|
|
],
|
|
highways: [
|
|
"residential", "residential", "cycleway", "cycleway", "path",
|
|
"path", "tertiary", "tertiary", "residential", "residential",
|
|
],
|
|
maxspeeds: [
|
|
"30", "30", "unknown", "unknown", "unknown",
|
|
"unknown", "50", "50", "30", "30",
|
|
],
|
|
smoothnesses: [
|
|
"good", "good", "intermediate", "intermediate", "bad",
|
|
"bad", "good", "good", "excellent", "excellent",
|
|
],
|
|
tracktypes: [
|
|
"unknown", "unknown", "unknown", "unknown", "grade3",
|
|
"grade3", "unknown", "unknown", "unknown", "unknown",
|
|
],
|
|
cycleways: [
|
|
"lane", "lane", "track", "track", "unknown",
|
|
"unknown", "shared_lane", "shared_lane", "lane", "lane",
|
|
],
|
|
bikeroutes: [
|
|
"rcn", "rcn", "lcn", "lcn", "none",
|
|
"none", "ncn", "ncn", "rcn", "rcn",
|
|
],
|
|
};
|
|
|
|
/**
|
|
* Canned enriched route for 2 nearby Berlin waypoints:
|
|
* [52.520, 13.405] → [52.515, 13.351]
|
|
*
|
|
* ~3.8km total, 6 track points with elevation.
|
|
*/
|
|
const MOCK_ROUTE_2WP = {
|
|
geojson: {
|
|
type: "FeatureCollection",
|
|
features: [{
|
|
type: "Feature",
|
|
geometry: {
|
|
type: "LineString",
|
|
coordinates: [
|
|
[13.405, 52.520, 34], [13.394, 52.519, 36], [13.383, 52.518, 40],
|
|
[13.372, 52.517, 38], [13.361, 52.516, 36], [13.351, 52.515, 35],
|
|
],
|
|
},
|
|
properties: {},
|
|
}],
|
|
},
|
|
coordinates: [
|
|
[13.405, 52.520, 34], [13.394, 52.519, 36], [13.383, 52.518, 40],
|
|
[13.372, 52.517, 38], [13.361, 52.516, 36], [13.351, 52.515, 35],
|
|
],
|
|
segmentBoundaries: [0],
|
|
totalLength: 3800,
|
|
totalAscend: 6,
|
|
surfaces: [
|
|
"asphalt", "asphalt", "gravel", "gravel", "asphalt", "asphalt",
|
|
],
|
|
highways: [
|
|
"secondary", "secondary", "cycleway", "cycleway", "residential", "residential",
|
|
],
|
|
maxspeeds: [
|
|
"50", "50", "unknown", "unknown", "30", "30",
|
|
],
|
|
smoothnesses: [
|
|
"good", "good", "intermediate", "bad", "good", "good",
|
|
],
|
|
tracktypes: [
|
|
"unknown", "unknown", "grade2", "grade4", "unknown", "unknown",
|
|
],
|
|
cycleways: [
|
|
"lane", "lane", "track", "no", "shared_lane", "shared_lane",
|
|
],
|
|
bikeroutes: [
|
|
"ncn", "ncn", "rcn", "none", "lcn", "lcn",
|
|
],
|
|
};
|
|
|
|
/**
|
|
* Mock the BRouter route API to return instant, deterministic responses.
|
|
* Selects the appropriate canned response based on waypoint count.
|
|
*/
|
|
export async function mockBRouter(page: Page) {
|
|
await page.route("**/api/route", async (route) => {
|
|
const body = route.request().postDataJSON();
|
|
const wpCount = body?.waypoints?.length ?? 0;
|
|
const mock = wpCount >= 3 ? MOCK_ROUTE_3WP : MOCK_ROUTE_2WP;
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify(mock),
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Convert a lat/lon to pixel coordinates on the Leaflet map.
|
|
* Requires MapExposer to have set window.__leafletMap.
|
|
*/
|
|
export async function latLngToPixel(page: Page, lat: number, lon: number): Promise<{ x: number; y: number }> {
|
|
return page.evaluate(([la, ln]) => {
|
|
const map = (window as any).__leafletMap;
|
|
if (!map) throw new Error("__leafletMap not available");
|
|
const point = map.latLngToContainerPoint([la, ln]);
|
|
return { x: point.x, y: point.y };
|
|
}, [lat, lon]);
|
|
}
|