feat: add cesium traffic signal countdowns
This commit is contained in:
@@ -5,6 +5,32 @@ const fs = require("fs");
|
||||
const EARTH_RADIUS = 6371008.8;
|
||||
const CURB_OFFSET_METERS = 5.2;
|
||||
const MAST_REACH_METERS = 4.5;
|
||||
// This layout is serialized with the anchors so Blender's static structure and
|
||||
// Cesium's dynamic overlay cannot independently drift in size or handedness.
|
||||
// Lateral offsets use the approach travel direction: positive is the driver's
|
||||
// right. The countdown board therefore sits at +1.15m from the signal head.
|
||||
const SIGNAL_LAYOUT = Object.freeze({
|
||||
poleHeightMeters: 6.7,
|
||||
poleRadiusMeters: 0.13,
|
||||
armWidthMeters: 0.21,
|
||||
// The mast arm and the signal head share this centre elevation.
|
||||
mastHeightMeters: 6.25,
|
||||
headCenterHeightMeters: 6.25,
|
||||
headWidthMeters: 0.68,
|
||||
headDepthMeters: 0.30,
|
||||
headBodyHeightMeters: 1.62,
|
||||
lensRadiusMeters: 0.22,
|
||||
lensDepthMeters: 0.07,
|
||||
lensFaceOffsetMeters: 0.18,
|
||||
lensVerticalOffsetsMeters: [0.49, -0.01, -0.51],
|
||||
countdownLateralMeters: 1.15,
|
||||
countdownFaceOffsetMeters: 0.05,
|
||||
countdownWidthMeters: 0.82,
|
||||
countdownDepthMeters: 0.14,
|
||||
countdownHeightMeters: 0.56,
|
||||
// The countdown board is fixed on the mast arm, not hung below it.
|
||||
countdownVerticalOffsetMeters: 0.0,
|
||||
});
|
||||
|
||||
function buildTrafficSignals(stopLines, intersections) {
|
||||
const centers = (intersections.features || []).map((feature, index) => {
|
||||
@@ -37,9 +63,36 @@ function buildTrafficSignals(stopLines, intersections) {
|
||||
stopLatitude: center[1],
|
||||
headingDegrees: Math.atan2(axis[0], axis[1]) * 180 / Math.PI,
|
||||
mastReachMeters: MAST_REACH_METERS,
|
||||
pose: buildSignalPose(point, axis, MAST_REACH_METERS),
|
||||
});
|
||||
}
|
||||
return { version: 1, signals };
|
||||
return { version: 3, layout: SIGNAL_LAYOUT, signals };
|
||||
}
|
||||
|
||||
function buildSignalPose(pole, axis, mastReach) {
|
||||
const lateral = [axis[1], -axis[0]];
|
||||
const face = [-axis[0], -axis[1]];
|
||||
const head = moveMeters(pole, lateral, -mastReach);
|
||||
const faceHeadingDegrees = Math.atan2(face[0], face[1]) * 180 / Math.PI;
|
||||
const position = (point, height) => ({ longitude: point[0], latitude: point[1], height });
|
||||
const lensPoint = moveMeters(head, face, SIGNAL_LAYOUT.lensFaceOffsetMeters);
|
||||
const board = moveMeters(
|
||||
moveMeters(head, lateral, SIGNAL_LAYOUT.countdownLateralMeters),
|
||||
face, SIGNAL_LAYOUT.countdownFaceOffsetMeters,
|
||||
);
|
||||
return {
|
||||
pole: position(pole, 0),
|
||||
arm: {
|
||||
from: position(pole, SIGNAL_LAYOUT.mastHeightMeters),
|
||||
to: position(head, SIGNAL_LAYOUT.mastHeightMeters),
|
||||
},
|
||||
head: { ...position(head, SIGNAL_LAYOUT.headCenterHeightMeters), faceHeadingDegrees },
|
||||
lenses: ["red", "yellow", "green"].map((state, index) => ({
|
||||
state,
|
||||
...position(lensPoint, SIGNAL_LAYOUT.headCenterHeightMeters + SIGNAL_LAYOUT.lensVerticalOffsetsMeters[index]),
|
||||
})),
|
||||
countdown: { ...position(board, SIGNAL_LAYOUT.mastHeightMeters), faceHeadingDegrees },
|
||||
};
|
||||
}
|
||||
|
||||
function readTrafficSignals(stopLinePath, intersectionPath) {
|
||||
@@ -90,4 +143,4 @@ function moveMeters(point, vector, meters) {
|
||||
return [point[0] + vector[0] * meters * scale / Math.cos(point[1] * Math.PI / 180), point[1] + vector[1] * meters * scale];
|
||||
}
|
||||
|
||||
module.exports = { buildTrafficSignals, readTrafficSignals };
|
||||
module.exports = { SIGNAL_LAYOUT, buildTrafficSignals, readTrafficSignals };
|
||||
|
||||
Reference in New Issue
Block a user