# Design: QGIS Traffic Signal Overrides ## Architecture Introduce a separate auxiliary-edit-layer registry rather than adding traffic signals to `SCENE_LAYERS`. The initial registry contains one layer: ```text traffic_signal_assemblies.geojson geometry: Point (pole ground position, EPSG:4326) properties: stable identity, source identity, heading, phase, reach, stop point, enabled, z offset ``` The existing runtime file remains: ```text traffic_signals.json version/layout/signals[] with full pose.* data ``` The editable GeoJSON is the placement source; the runtime JSON is a derived consumer artifact. ## Data Flow ```text OSM controls + topology + stop lines + intersections | intermediates only v traffic_signal_assemblies.geojson | import into GeoPackage | edit in QGIS | reimport v traffic_signal_assemblies.geojson | validate + derive pose v traffic_signals.json / \ Blender preview/Cesium ``` `blender` reruns the final validation/derivation arrow from the editable GeoJSON so derived JSON cannot be stale, but it never reruns the OSM initialization arrow. ## Editable Feature Contract Recommended properties: | Property | Type | Ownership | |---|---|---| | `signal_uid` | string | generated, immutable technical identity | | `display_id` | string | user-editable unique label/number | | `control_id` | string | generated OSM control id | | `approach_id` | string | generated physical approach identity | | `source_way_id` | string | generated matching/diagnostic field | | `heading_deg` | number | user-editable assembly facing direction | | `phase_group` | integer 0/1 | user-editable current two-phase group | | `mast_reach_m` | positive number | user-editable arm reach | | `stop_lon`, `stop_lat` | finite numbers | generated vehicle stop point, preserved when pole moves | | `enabled` | boolean/integer | user-editable suppression flag | | `z_offset_m` | finite number | user-editable vertical adjustment | Point geometry is the pole longitude/latitude. The runtime `id` should be derived from `signal_uid`, not display numbering, so changing `display_id` does not rename GLB nodes or break preview control. ## Stable Identity Extend parsed OSM arm data to retain enough deterministic source identity (control node, way, adjacent arm direction/node). Generate a technical key from those source values. Do not use sorted array index or rounded heading as the primary key. If topology changes on a future `intermediates` run, the rebuilt GeoPackage may produce new identities. This is consistent with current road-edit lifecycle and is explicitly out of scope for MVP migration. Validation still reports duplicate identities and malformed source fields. ## QGIS Integration - Add an auxiliary layer definition separate from the nine render layers. - Import it into the same GeoPackage after render layers. - Include it in the generated project but exclude it from the merged scene and 2D raster preview unless deliberately enabled for editing visibility. - Use a point marker plus rotated direction indicator driven by `heading_deg` and label by `display_id`, falling back to `signal_uid`. - Configure read-only/editor widgets where practical: technical/source ids read-only; phase group constrained to 0/1; numeric fields constrained to valid ranges; enabled as checkbox. ## Reimport and Atomicity Extend the reimport layer manifest to include auxiliary editable layers while keeping render-scene merge derived only from `SCENE_LAYERS`. Export every layer into staging, parse and validate all editable features, then replace output files. Runtime JSON is written only after the staged auxiliary layer passes validation. ## Compatibility - The next `intermediates` run bootstraps existing areas; no old JSON migration is required. - Main `.blend`/GLB geometry changes intentionally when a QGIS edit changes a signal. - Dynamic and countdown GLBs continue using runtime signal ids, now stable across ordinary reimport edits. - Current two-phase simulation remains unchanged. ## Risks and Controls - **OSM way splitting changes source ids:** accepted across a full intermediates rebuild; ordinary reimport is stable. - **QGIS boolean/string coercion:** normalize known GDAL representations before strict validation and test the round-trip output. - **Accidental source-field editing:** mark technical fields read-only in QGIS and validate identity format during reimport. - **Partial overwrite on invalid auxiliary data:** retain the existing staging-before-replace discipline. - **Old spec conflict:** update pipeline specs that currently forbid traffic-signal anchors in GeoPackage, clarifying the distinction between editable assembly points and derived runtime anchors.