177 lines
4.2 KiB
JavaScript
177 lines
4.2 KiB
JavaScript
"use strict";
|
|
|
|
// Single source of truth for the osm2streets render layers.
|
|
//
|
|
// The same nine layers, in the same order, previously appeared four times:
|
|
// the merged-scene z_index table, the scene style JSON, the generated QGIS
|
|
// project (layer dict + draw_order), and the README's manual rebuild snippet.
|
|
// Adding a layer or changing a z-index meant editing all of them in lockstep,
|
|
// and a missed copy produces a silently mis-stacked scene downstream in
|
|
// Blender/Cesium. Everything now derives from SCENE_LAYERS.
|
|
//
|
|
// zIndex doubles as draw order: lowest paints first (bottom of the stack).
|
|
// outline: null means "no stroke" (QGIS gets a fully transparent outline).
|
|
|
|
const SCENE_LAYERS = [
|
|
{
|
|
id: "road_surface",
|
|
splitKey: "roadSurface",
|
|
zIndex: 10,
|
|
title: "road surface",
|
|
fill: "#2b2b28",
|
|
outline: "#1e1e1c",
|
|
outlineWidth: 0.04,
|
|
},
|
|
{
|
|
id: "intersection_surface",
|
|
splitKey: "intersectionSurface",
|
|
zIndex: 20,
|
|
title: "intersection surface",
|
|
fill: "#2b2b28",
|
|
outline: "#1e1e1c",
|
|
outlineWidth: 0.04,
|
|
},
|
|
{
|
|
id: "sidewalks",
|
|
splitKey: "sidewalks",
|
|
zIndex: 30,
|
|
title: "sidewalks",
|
|
fill: "#bebeb6",
|
|
outline: "#9c9c94",
|
|
outlineWidth: 0.025,
|
|
},
|
|
{
|
|
id: "sidewalk_corners",
|
|
splitKey: "sidewalkCorners",
|
|
zIndex: 40,
|
|
title: "sidewalk corners",
|
|
fill: "#bebeb6",
|
|
outline: "#9c9c94",
|
|
outlineWidth: 0.025,
|
|
},
|
|
{
|
|
id: "lane_separators",
|
|
splitKey: "laneSeparators",
|
|
zIndex: 50,
|
|
title: "lane separators",
|
|
fill: "#eeeee6",
|
|
outline: null,
|
|
outlineWidth: 0,
|
|
},
|
|
{
|
|
id: "center_lines",
|
|
splitKey: "centerLines",
|
|
zIndex: 60,
|
|
title: "center lines",
|
|
fill: "#f5be2a",
|
|
outline: null,
|
|
outlineWidth: 0,
|
|
},
|
|
{
|
|
id: "crosswalks",
|
|
splitKey: "crosswalks",
|
|
zIndex: 70,
|
|
title: "crosswalks",
|
|
fill: "#fffff6",
|
|
outline: null,
|
|
outlineWidth: 0,
|
|
},
|
|
{
|
|
id: "vehicle_stop_lines",
|
|
splitKey: "vehicleStopLines",
|
|
zIndex: 80,
|
|
title: "vehicle stop lines",
|
|
fill: "#fffff6",
|
|
outline: null,
|
|
outlineWidth: 0,
|
|
},
|
|
{
|
|
id: "lane_arrows_webscale",
|
|
splitKey: "laneArrows",
|
|
zIndex: 90,
|
|
title: "lane arrows",
|
|
fill: "#fffff6",
|
|
outline: "#2b2b28",
|
|
outlineAlpha: 200,
|
|
outlineWidth: 0.015,
|
|
},
|
|
];
|
|
|
|
// Editable control layers share the GeoPackage/QGIS lifecycle but never enter
|
|
// the merged render scene or its draw order.
|
|
const AUXILIARY_EDIT_LAYERS = [
|
|
{
|
|
id: "traffic_signal_assemblies",
|
|
file: "traffic_signal_assemblies.geojson",
|
|
title: "traffic signal assemblies",
|
|
geometry: "Point",
|
|
},
|
|
];
|
|
|
|
const SCENE_FILE = "osm2streets_scene.geojson";
|
|
const SCENE_STYLE_FILE = "osm2streets_scene_style.json";
|
|
|
|
function layerFile(layer) {
|
|
return `${layer.id}.geojson`;
|
|
}
|
|
|
|
// getCollection(layer) -> FeatureCollection, so callers can source layers from
|
|
// the in-memory split (build) or from disk (reimport) with the same merge.
|
|
function mergeScene(getCollection) {
|
|
return {
|
|
type: "FeatureCollection",
|
|
features: SCENE_LAYERS.flatMap((layer) => {
|
|
const collection = getCollection(layer) || {};
|
|
return (collection.features || []).map((feature) => ({
|
|
...feature,
|
|
properties: {
|
|
...(feature.properties || {}),
|
|
render_layer: layer.id,
|
|
z_index: layer.zIndex,
|
|
},
|
|
}));
|
|
}),
|
|
};
|
|
}
|
|
|
|
function sceneStyle() {
|
|
return {
|
|
version: 1,
|
|
geometry: "polygon",
|
|
sortProperty: "z_index",
|
|
layerProperty: "render_layer",
|
|
layers: SCENE_LAYERS.map((layer) => ({
|
|
id: layer.id,
|
|
zIndex: layer.zIndex,
|
|
fill: layer.fill,
|
|
outline: layer.outline,
|
|
outlineWidth: layer.outlineWidth,
|
|
})),
|
|
};
|
|
}
|
|
|
|
// QGIS symbol properties want "r,g,b,a" strings rather than hex.
|
|
function qgisRgba(hex, alpha = 255) {
|
|
if (!hex) return "0,0,0,0";
|
|
const match = /^#?([0-9a-f]{6})$/i.exec(hex.trim());
|
|
if (!match) {
|
|
throw new Error(`Expected #rrggbb color, got: ${hex}`);
|
|
}
|
|
const value = parseInt(match[1], 16);
|
|
const r = (value >> 16) & 0xff;
|
|
const g = (value >> 8) & 0xff;
|
|
const b = value & 0xff;
|
|
return `${r},${g},${b},${alpha}`;
|
|
}
|
|
|
|
module.exports = {
|
|
SCENE_LAYERS,
|
|
AUXILIARY_EDIT_LAYERS,
|
|
SCENE_FILE,
|
|
SCENE_STYLE_FILE,
|
|
layerFile,
|
|
mergeScene,
|
|
sceneStyle,
|
|
qgisRgba,
|
|
};
|