feat: add native lane separator style overrides
This commit is contained in:
@@ -16,6 +16,7 @@ const CENTER_LINE_DASH_LENGTH_METERS = 2;
|
||||
const CENTER_LINE_DASH_GAP_METERS = 2;
|
||||
const CENTER_LINE_WIDTH_METERS = .25;
|
||||
const CENTER_LINE_SOLID_OVERLAP_METERS = .04;
|
||||
const CENTER_LINE_CONTROL_CLEARANCE_METERS = 1;
|
||||
const CENTER_LINE_COLORS = new Set(["yellow", "white"]);
|
||||
const CENTER_LINE_PATTERNS = new Set(["dashed", "solid"]);
|
||||
|
||||
@@ -166,6 +167,8 @@ function validateOverrides(value, model) {
|
||||
if (typeof item.fromLaneId !== "string" || typeof item.toLaneId !== "string" || typeof item.enabled !== "boolean" || (laneIds && (!laneIds.has(item.fromLaneId) || !laneIds.has(item.toLaneId)))) throw new Error("Invalid lane connection override.");
|
||||
} else if (item.kind === "center-line-style") {
|
||||
if (typeof item.segmentId !== "string" || !CENTER_LINE_COLORS.has(item.color) || !CENTER_LINE_PATTERNS.has(item.pattern) || (segmentIds && !segmentIds.has(item.segmentId))) throw new Error("Invalid center line style override.");
|
||||
} else if (item.kind === "lane-separator-style") {
|
||||
if (typeof item.roadId !== "string" || (roadIds && !roadIds.has(item.roadId)) || !Number.isInteger(item.leftLaneIndex) || item.rightLaneIndex !== item.leftLaneIndex + 1 || !CENTER_LINE_COLORS.has(item.color) || !CENTER_LINE_PATTERNS.has(item.pattern)) throw new Error("Invalid lane separator style override.");
|
||||
} else throw new Error(`Unsupported override kind: ${item.kind}`);
|
||||
}
|
||||
return { schema: OVERRIDE_SCHEMA, overrides: value.overrides };
|
||||
@@ -254,7 +257,7 @@ function compileGeometry(model, overrides = { overrides: [] }) {
|
||||
const lanes = compileLaneCenterlines(model, diagnostics, junctionPlans);
|
||||
const controls = compileControlMarkings(model, lanes, diagnostics);
|
||||
const centerLines = compileCenterLines(model, overrides, junctionPlans, controls, diagnostics);
|
||||
const markings = compileLaneMarkings(model, lanes, diagnostics, junctionPlans, controls);
|
||||
const markings = compileLaneMarkings(model, overrides, lanes, diagnostics, junctionPlans, controls);
|
||||
const sidewalks = compileSidewalkSurfaces(model, diagnostics, junctionPlans);
|
||||
const connectorResult = compileConnectors(model, lanes, diagnostics, overrides);
|
||||
const junctionFeatures = compileJunctionSurfaces(model, junctionPlans, connectorResult.features, connectorResult.movements, diagnostics);
|
||||
@@ -284,7 +287,8 @@ function compileCenterLines(model, overrides, junctionPlans, controls, diagnosti
|
||||
const placement = pointAndAxisAlongLine(line, start + markLength / 2);
|
||||
if (!placement) continue;
|
||||
const ring = rectangleAt(placement.point, placement.axis, [-placement.axis[1], placement.axis[0]], markLength, CENTER_LINE_WIDTH_METERS, 0);
|
||||
if (ringsOverlapControl([ring], controlFeatures)) continue;
|
||||
const clearanceRing = rectangleAt(placement.point, placement.axis, [-placement.axis[1], placement.axis[0]], markLength + CENTER_LINE_CONTROL_CLEARANCE_METERS * 2, CENTER_LINE_WIDTH_METERS + CENTER_LINE_CONTROL_CLEARANCE_METERS * 2, 0);
|
||||
if (ringsOverlapControl([clearanceRing], controlFeatures)) continue;
|
||||
features.push({ type: "Feature", properties: { native_id: `center-line:${segmentId}:${dashIndex}`, segment_id: segmentId, road_id: forward.id, directional_road_ids: roads.map((road) => road.id).join(","), osm_way_ids: forward.osmWayIds.join(","), dash_index: dashIndex, dash_length_m: markLength, dash_gap_m: gap, color: style.color, pattern: style.pattern, effective_style: `${style.color}-${style.pattern}`, placement_rule: "native-bidirectional-centerline/v1", provenance: "native-road-center-line/v1" }, geometry: { type: "Polygon", coordinates: [ring] } });
|
||||
}
|
||||
}
|
||||
@@ -323,7 +327,7 @@ function offsetByMeters(point, axis, meters) { return unproject([axis[0] * meter
|
||||
function rectangleAt(center, axis, across, length, width, offset) { const shifted = offsetByMeters(center, across, offset); const corners = [[-length / 2, -width / 2], [length / 2, -width / 2], [length / 2, width / 2], [-length / 2, width / 2]].map(([forward, side]) => unproject([axis[0] * forward + across[0] * side, axis[1] * forward + across[1] * side], shifted)); return [...corners, corners[0]]; }
|
||||
function controlFeature(kind, crossing, candidate, part, ring) { const stop = kind === "stop-line"; return { type: "Feature", properties: { native_id: `${kind}:node/${crossing.id}:${part}`, crossing_node_id: crossing.id, road_id: candidate.road.id, lane_id: candidate.lane.id, osm_way_ids: candidate.road.osmWayIds.join(","), direction: candidate.road.direction, placement_method: "native-lane-nearest-point/v1", provenance: stop ? "native-road-stop-line/v1" : "native-road-crosswalk/v1" }, geometry: { type: "Polygon", coordinates: [ring] } }; }
|
||||
|
||||
function compileLaneMarkings(model, lanes, diagnostics, junctionPlans, controls) {
|
||||
function compileLaneMarkings(model, overrides, lanes, diagnostics, junctionPlans, controls) {
|
||||
const separators = []; const directionArrows = []; const turnArrows = [];
|
||||
const controlFeatures = [...controls.crosswalks, ...controls.stopLines];
|
||||
for (const road of model.roads) {
|
||||
@@ -332,8 +336,10 @@ function compileLaneMarkings(model, lanes, diagnostics, junctionPlans, controls)
|
||||
const left = roadLanes[index - 1].coordinates; const right = roadLanes[index].coordinates;
|
||||
if (left.length !== right.length) continue;
|
||||
const centerline = left.map((point, pointIndex) => [(point[0] + right[pointIndex][0]) / 2, (point[1] + right[pointIndex][1]) / 2]);
|
||||
const ring = roadRing(centerline, 0.12);
|
||||
if (ring) separators.push({ type: "Feature", properties: { native_id: `lane-separator:${road.id}:${index}-${index + 1}`, road_id: road.id, left_lane_index: index, right_lane_index: index + 1, osm_way_ids: road.osmWayIds.join(","), provenance: "native-road-lane-separator/v1" }, geometry: { type: "Polygon", coordinates: [ring] } });
|
||||
const style = laneSeparatorStyle(overrides, road.id, index, index + 1);
|
||||
const properties = { road_id: road.id, left_lane_index: index, right_lane_index: index + 1, osm_way_ids: road.osmWayIds.join(","), color: style.color, pattern: style.pattern, effective_style: `${style.color}-${style.pattern}`, provenance: "native-road-lane-separator/v1" };
|
||||
if (style.pattern === "solid") { const ring = roadRing(centerline, 0.12); if (ring) separators.push({ type: "Feature", properties: { native_id: `lane-separator:${road.id}:${index}-${index + 1}`, ...properties }, geometry: { type: "Polygon", coordinates: [ring] } }); }
|
||||
else for (let distance = 1, part = 1; distance + 1 <= lineLengthMeters(centerline); distance += 4, part += 1) { const placement = pointAndAxisAlongLine(centerline, distance); if (!placement) continue; const ring = rectangleAt(placement.point, placement.axis, [-placement.axis[1], placement.axis[0]], 2, .12, 0); separators.push({ type: "Feature", properties: { native_id: `lane-separator:${road.id}:${index}-${index + 1}:${part}`, ...properties }, geometry: { type: "Polygon", coordinates: [ring] } }); }
|
||||
}
|
||||
for (const lane of roadLanes) directionArrows.push(...directionArrowFeatures(road, lane, controlFeatures, diagnostics));
|
||||
const turns = road.tags[`turn:lanes:${road.direction}`] ?? road.tags["turn:lanes"];
|
||||
@@ -359,6 +365,8 @@ function compileLaneMarkings(model, lanes, diagnostics, junctionPlans, controls)
|
||||
return { separators, directionArrows, turnArrows };
|
||||
}
|
||||
|
||||
function laneSeparatorStyle(overrides, roadId, leftLaneIndex, rightLaneIndex) { const value = overrides.overrides.find((item) => item.kind === "lane-separator-style" && item.roadId === roadId && item.leftLaneIndex === leftLaneIndex && item.rightLaneIndex === rightLaneIndex); return value ? { color: value.color, pattern: value.pattern } : { color: "white", pattern: "dashed" }; }
|
||||
|
||||
function directionArrowFeatures(road, lane, controlFeatures, diagnostics) {
|
||||
const length = lineLengthMeters(lane.coordinates);
|
||||
const features = [];
|
||||
|
||||
Reference in New Issue
Block a user