Files
osmWorkflow/scripts/lib/area-preview.js

143 lines
5.7 KiB
JavaScript

"use strict";
const fs = require("fs");
const path = require("path");
function writeCesiumPreviewSupportFiles(outDir) {
const files = [
[path.join(__dirname, "cesium-preview.css"), "cesium-preview.css"],
[path.join(__dirname, "cesium-preview.js"), "cesium-preview.js"],
[path.join(__dirname, "..", "..", "assets", "preview", "vehicle-breakdown.png"), "vehicle-breakdown.png"],
[path.join(__dirname, "..", "..", "assets", "preview", "vehicle-accident.png"), "vehicle-accident.png"],
];
for (const [source, file] of files) {
if (!fs.existsSync(source)) {
throw new Error(`Cesium preview support file '${file}' not found: ${source}`);
}
fs.copyFileSync(source, path.join(outDir, file));
}
}
function cesiumPreviewHtml(glbName, metadataName, routeName, vehicleModelName, areaId, vehicleModelNames = [], trafficSignalsName = null, previewDescriptorName = null) {
const previewConfig = {
areaId,
glbName,
metadataName,
routeName,
vehicleModelName,
vehicleModelNames,
trafficSignalsName,
previewDescriptorName,
};
return `<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>${escapeHtml(areaId)} Cesium Preview</title>
<script src="https://cdn.jsdelivr.net/npm/cesium@1.121.1/Build/Cesium/Cesium.js"></script>
<link href="https://cdn.jsdelivr.net/npm/cesium@1.121.1/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<link href="cesium-preview.css" rel="stylesheet">
</head>
<body>
<div id="cesiumContainer"></div>
<div id="controls">
<div class="control-group">
<button id="toggleCruise">Pause</button>
<button id="toggleFollow">Follow</button>
<label>Vehicle <select id="vehicleSelect"></select></label>
<label>Speed <input id="speedControl" type="range" min="2" max="22" step="1" value="8"></label>
<span id="speedLabel">8 m/s</span>
</div>
<div class="control-group">
<span class="control-label">View</span>
<span id="viewMode" class="segmented-control" role="group" aria-label="Scene view mode">
<button type="button" data-view-mode="scene" aria-pressed="true">Scene</button>
<button type="button" data-view-mode="inspect" aria-pressed="false">Inspect</button>
</span>
<label><input id="toggleScene" type="checkbox" checked> Scene</label>
<label><input id="toggleBuildingGhost" type="checkbox"> Building ghost</label>
<span id="assetToggles" class="control-subgroup"></span>
<span id="semanticToggles" class="control-subgroup hidden"></span>
<label><input id="toggleRoutes" type="checkbox" checked> Routes</label>
<label><input id="toggleVehicles" type="checkbox" checked> Vehicles</label>
<label id="signalsControl"><input id="toggleSignals" type="checkbox" checked> Signals</label>
<label><input id="toggleFps" type="checkbox"> FPS</label>
<label><input id="toggleDiagnostics" type="checkbox" checked> Info</label>
</div>
<div class="control-group">
<button data-camera="overview">Overview</button>
<button data-camera="oblique">Oblique</button>
<button data-camera="detail">Detail</button>
<button data-camera="route">Route</button>
</div>
</div>
<div id="diagnostics">Loading diagnostics...</div>
<div id="status">Loading ${escapeHtml(glbName)}...</div>
<aside id="vehicleInfoCard" class="hidden" aria-live="polite">
<div class="vehicle-card-heading">
<strong id="vehicleInfoTitle">Vehicle</strong>
<button id="closeVehicleInfo" type="button" aria-label="Close vehicle information">Close</button>
</div>
<dl id="vehicleInfoDetails"></dl>
<label class="vehicle-card-field">Event note
<textarea id="vehicleIncidentNote" rows="2" maxlength="160" placeholder="Optional note"></textarea>
</label>
<div class="vehicle-card-actions" role="group" aria-label="Vehicle status">
<button type="button" data-vehicle-status="normal">Normal</button>
<button type="button" data-vehicle-status="breakdown">Breakdown</button>
<button type="button" data-vehicle-status="accident">Accident</button>
</div>
</aside>
<div id="loadingOverlay">
<div class="loading-card">
<div class="loading-spinner" aria-hidden="true"></div>
<div class="loading-copy">
<strong>Loading scene</strong>
<span>${escapeHtml(areaId)}</span>
</div>
<div class="loading-bar" aria-hidden="true"><span></span></div>
</div>
</div>
<script>window.OSM_ASSET_PREVIEW_CONFIG = ${escapeScriptJson(JSON.stringify(previewConfig))};</script>
<script src="cesium-preview.js"></script>
</body>
</html>
`;
}
function escapeHtml(value) {
return String(value)
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;");
}
function escapeScriptJson(value) {
return String(value)
.replaceAll("<", "\\u003c")
.replaceAll(">", "\\u003e")
.replaceAll("&", "\\u0026")
.replaceAll("\u2028", "\\u2028")
.replaceAll("\u2029", "\\u2029");
}
function previewSummary(area) {
const route = JSON.parse(fs.readFileSync(area.outputs.vehicleRoute, "utf8"));
return {
glbName: "package/manifest.json",
metadataName: "package/manifest.json",
routeName: path.relative(area.outputs.areaDir, area.outputs.vehicleRoute).split(path.sep).join("/"),
vehicleModelName: path.relative(area.outputs.areaDir, area.outputs.vehicleModel).split(path.sep).join("/"),
trafficSignalsName: "package/runtime/traffic-signals.json",
routeSegments: Array.isArray(route.segments) ? route.segments.length : null,
};
}
module.exports = {
cesiumPreviewHtml,
previewSummary,
writeCesiumPreviewSupportFiles,
};