"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, }, ]; 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, SCENE_FILE, SCENE_STYLE_FILE, layerFile, mergeScene, sceneStyle, qgisRgba, };