feat(preview): pause vehicles for traffic signals
This commit is contained in:
@@ -40,8 +40,9 @@
|
||||
const viewer = createViewer();
|
||||
setLoadingMessage("Loading model", config.glbName || "");
|
||||
const assets = await loadSceneAssets(viewer, metadata, placement);
|
||||
const cruise = addVehicleCruises(viewer, routeData, config.vehicleModelNames, config.vehicleModelName);
|
||||
const trafficSignals = addTrafficSignals(viewer, signalData);
|
||||
const trafficStart = Cesium.JulianDate.now();
|
||||
const trafficSignals = addTrafficSignals(viewer, signalData, trafficStart);
|
||||
const cruise = addVehicleCruises(viewer, routeData, signalData, trafficStart, config.vehicleModelNames, config.vehicleModelName);
|
||||
const cameras = createCameraPresets(viewer, metadata, placement, cruise);
|
||||
|
||||
buildAssetToggles(assets);
|
||||
@@ -434,12 +435,12 @@
|
||||
return stopFollow;
|
||||
}
|
||||
|
||||
function addVehicleCruises(viewer, routeData, vehicleModelNames, fallbackVehicleModelName) {
|
||||
function addVehicleCruises(viewer, routeData, signalData, trafficStart, vehicleModelNames, fallbackVehicleModelName) {
|
||||
const segments = ((routeData && (routeData.routes || routeData.segments)) || [])
|
||||
.filter((segment) => segment.coordinates && segment.coordinates.length >= 2)
|
||||
.slice(0, 5);
|
||||
const speed = Number((routeData && routeData.speedMetersPerSecond) || 8);
|
||||
const start = Cesium.JulianDate.now();
|
||||
const start = trafficStart;
|
||||
|
||||
viewer.clock.startTime = start.clone();
|
||||
viewer.clock.currentTime = start.clone();
|
||||
@@ -448,7 +449,7 @@
|
||||
viewer.clock.shouldAnimate = segments.length > 0;
|
||||
|
||||
const vehicles = segments.map((segment, index) => {
|
||||
const vehicle = addCruiseVehicle(viewer, segment, index, start, speed,
|
||||
const vehicle = addCruiseVehicle(viewer, segment, index, start, speed, signalData,
|
||||
selectedVehicleModelName(vehicleModelNames, fallbackVehicleModelName));
|
||||
const option = document.createElement("option");
|
||||
option.value = String(index);
|
||||
@@ -463,10 +464,9 @@
|
||||
};
|
||||
}
|
||||
|
||||
function addTrafficSignals(viewer, signalData) {
|
||||
function addTrafficSignals(viewer, signalData, start) {
|
||||
const anchors = (signalData?.signals || []).filter((signal) => Number.isFinite(signal.longitude) && Number.isFinite(signal.latitude));
|
||||
const entities = [];
|
||||
const start = Cesium.JulianDate.now();
|
||||
for (const signal of anchors) {
|
||||
const mastReach = Number(signal.mastReachMeters) || 4.5;
|
||||
const countdownOffset = -1.15;
|
||||
@@ -639,11 +639,9 @@
|
||||
return usable[Math.floor(Math.random() * usable.length)] || "";
|
||||
}
|
||||
|
||||
function addCruiseVehicle(viewer, segment, index, start, speed, vehicleModelName) {
|
||||
const route = prepareRoute(segment);
|
||||
const positions = new Cesium.CallbackProperty((time, result) => {
|
||||
return routePosition(route, start, time, speed, result);
|
||||
}, false);
|
||||
function addCruiseVehicle(viewer, segment, index, start, speed, signalData, vehicleModelName) {
|
||||
const route = prepareRoute(segment, signalData);
|
||||
const positions = createTrafficAwarePositions(viewer, route, start, speed);
|
||||
const flat = [];
|
||||
for (const coord of segment.coordinates) {
|
||||
flat.push(coord[0], coord[1], 1.05);
|
||||
@@ -668,7 +666,7 @@
|
||||
const vehicle = viewer.entities.add({
|
||||
name: "Cruise vehicle " + (index + 1),
|
||||
position: positions,
|
||||
orientation: routeOrientation(route, start, speed, 0.0),
|
||||
orientation: routeOrientationFromPositions(positions),
|
||||
model: {
|
||||
uri: vehicleModelName,
|
||||
// Keep the library's world scale visible. A minimum screen size made
|
||||
@@ -705,7 +703,7 @@
|
||||
return cruise.vehicles[cruise.state.selectedIndex] || cruise.vehicles[0];
|
||||
}
|
||||
|
||||
function prepareRoute(segment) {
|
||||
function prepareRoute(segment, signalData) {
|
||||
const distances = [0.0];
|
||||
for (let i = 1; i < segment.coordinates.length; i += 1) {
|
||||
distances.push(distances[i - 1] + distanceMeters(segment.coordinates[i - 1], segment.coordinates[i]));
|
||||
@@ -713,13 +711,61 @@
|
||||
return {
|
||||
coordinates: segment.coordinates,
|
||||
distances,
|
||||
length: Math.max(1.0, distances[distances.length - 1])
|
||||
length: Math.max(1.0, distances[distances.length - 1]),
|
||||
stops: routeStops(segment.coordinates, distances, signalData),
|
||||
};
|
||||
}
|
||||
|
||||
function routeStops(coordinates, distances, signalData) {
|
||||
const found = new Map();
|
||||
for (const signal of signalData?.signals || []) {
|
||||
if (!Number.isFinite(signal.stopLongitude) || !Number.isFinite(signal.stopLatitude)) continue;
|
||||
let best = { index: -1, distance: Infinity };
|
||||
for (let index = 0; index < coordinates.length; index += 1) {
|
||||
const distance = distanceMeters(coordinates[index], [signal.stopLongitude, signal.stopLatitude]);
|
||||
if (distance < best.distance) best = { index, distance };
|
||||
}
|
||||
if (best.index >= 0 && best.distance <= 7) {
|
||||
const routeDistance = distances[best.index];
|
||||
const existing = found.get(Math.round(routeDistance));
|
||||
if (!existing || best.distance < existing.matchDistance) found.set(Math.round(routeDistance), { distance: routeDistance, matchDistance: best.distance, signal });
|
||||
}
|
||||
}
|
||||
return [...found.values()].sort((a, b) => a.distance - b.distance);
|
||||
}
|
||||
|
||||
function createTrafficAwarePositions(viewer, route, start, speed) {
|
||||
const state = { distance: 0, lastTime: start.clone() };
|
||||
const scratchTime = new Cesium.JulianDate();
|
||||
viewer.clock.onTick.addEventListener((clock) => {
|
||||
const elapsed = Cesium.JulianDate.secondsDifference(clock.currentTime, state.lastTime);
|
||||
Cesium.JulianDate.clone(clock.currentTime, state.lastTime);
|
||||
if (elapsed <= 0) return;
|
||||
const next = nextRouteStop(route, state.distance);
|
||||
const advance = elapsed * speed;
|
||||
if (next && signalPhase(next.signal.phaseGroup, clock.currentTime, start).active !== "green") {
|
||||
const untilStop = (next.distance - state.distance + route.length) % route.length;
|
||||
if (untilStop <= advance + 1.7) {
|
||||
state.distance = (next.distance - 1.7 + route.length) % route.length;
|
||||
return;
|
||||
}
|
||||
}
|
||||
state.distance = (state.distance + advance) % route.length;
|
||||
});
|
||||
return new Cesium.CallbackProperty((time, result) => routePositionAtDistance(route, state.distance, result), false);
|
||||
}
|
||||
|
||||
function nextRouteStop(route, distance) {
|
||||
return route.stops.find((stop) => stop.distance > distance + 0.05) || route.stops[0] || null;
|
||||
}
|
||||
|
||||
function routePosition(route, start, time, speed, result) {
|
||||
const seconds = Math.max(0, Cesium.JulianDate.secondsDifference(time, start));
|
||||
const distance = (seconds * speed) % route.length;
|
||||
return routePositionAtDistance(route, distance, result);
|
||||
}
|
||||
|
||||
function routePositionAtDistance(route, distance, result) {
|
||||
let index = 1;
|
||||
while (index < route.distances.length - 1 && route.distances[index] < distance) {
|
||||
index += 1;
|
||||
@@ -780,6 +826,10 @@
|
||||
}, false);
|
||||
}
|
||||
|
||||
function routeOrientationFromPositions(positions) {
|
||||
return new Cesium.VelocityOrientationProperty(positions);
|
||||
}
|
||||
|
||||
function createChaseFollow(viewer, positionsProvider) {
|
||||
const scratchPosition = new Cesium.Cartesian3();
|
||||
const scratchPrevious = new Cesium.Cartesian3();
|
||||
|
||||
Reference in New Issue
Block a user