Files
osmWorkflow/.trellis/tasks/archive/2026-08/08-07-qgis-traffic-signal-overrides/prd.md

5.7 KiB

QGIS Traffic Signal Overrides

Goal

Make every automatically generated vehicle traffic signal independently identifiable and editable in QGIS. A user must be able to assign a display number, move a pole, rotate its assembly, adjust supported placement attributes, run reimport, and have Blender and Cesium consume that edited result without OSM regeneration overwriting it.

Background

  • Current traffic signals are derived from OSM highway=traffic_signals controls, road topology, vehicle_stop_lines.geojson, and intersection_surface.geojson by scripts/lib/traffic-signals.js.
  • Current sequential ids such as signal-1 depend on generation order and are not suitable as persistent edit identities.
  • Current <geojsonDir>/traffic_signals.json contains fully derived pose.* data but is deliberately excluded from the GeoPackage and QGIS project.
  • Existing road editing establishes the desired lifecycle: intermediates initializes a GeoPackage, the user edits it in QGIS, and reimport exports the edited data back to GeoJSON. Running intermediates again may discard manual edits; that behavior remains explicit and unchanged.

Requirements

R1. Editable auxiliary layer

  • intermediates must create a point FeatureCollection containing one feature per physical signal assembly and import it into the area GeoPackage.
  • The generated QGIS project must expose the layer with a visible directional symbol and a label suitable for identifying individual signals.
  • The auxiliary layer must not join SCENE_LAYERS or the merged road scene because it is an editing/control artifact, not a road render layer.

R2. Stable identity and numbering

  • Every generated feature must contain an immutable technical signal_uid derived deterministically from its OSM control and physical approach identity, rather than array order.
  • Every feature must contain an editable display_id intended for user-facing numbering.
  • Build/reimport validation must reject duplicate or missing signal_uid values and duplicate non-empty display_id values with an actionable error.

R3. Editable placement contract

  • Point geometry represents the pole ground position.
  • Editable attributes must include at least display_id, heading_deg, phase_group, mast_reach_m, enabled, and z_offset_m.
  • Source/control attributes required for matching and diagnostics must be preserved, including control_id and approach identity.
  • Vehicle stop coordinates remain independent attributes; moving the pole must not silently move the vehicle stop point.
  • After reimport, the pipeline must deterministically rebuild pose.pole, pose.arm, pose.head, pose.lenses, and pose.countdown from the edited point and attributes.

R4. Stage ownership

  • intermediates initializes the editable signal layer from current OSM/topology and derives the runtime traffic_signals.json from it.
  • reimport must stage, validate, and export the editable signal layer along with the existing road layers, then rebuild the runtime JSON.
  • blender must rebuild runtime traffic_signals.json from the current editable signal GeoJSON. It must not recompute signal placement directly from OSM and erase QGIS edits.
  • cesium and preview continue consuming artifacts derived from the same runtime JSON and retain matching signal node ids.

R5. Diagnostics and compatibility

  • Invalid geometry, invalid numeric fields, duplicate identities, unsupported phase groups, and unmatched source references must fail before replacing valid output artifacts.
  • enabled=false suppresses a signal without requiring feature deletion, so automatic regeneration cannot accidentally resurrect an intentionally disabled assembly within the same edit lifecycle.
  • Existing areas without an editable signal layer must receive one on their next intermediates run. No migration of previously hand-edited traffic signal JSON is required.

Acceptance Criteria

  • A clean intermediates run creates the editable traffic-signal GeoJSON, a GeoPackage layer with the same feature count, and a QGIS project layer with labels and directional symbols.
  • T junctions produce three editable features and cross junctions produce four, each with a unique deterministic signal_uid.
  • Moving one point in QGIS and changing its display_id and heading_deg, followed by reimport,blender,cesium,preview, changes only that signal assembly's placement/identity-facing metadata while preserving its vehicle stop point.
  • Re-running blender after reimport does not overwrite the QGIS-edited pole position or heading from OSM.
  • Setting one feature to disabled removes its static and dynamic signal assets while leaving the other signals intact.
  • Duplicate signal_uid or non-empty display_id, invalid geometry, and invalid placement fields abort reimport without partially replacing GeoJSON outputs.
  • Blender/Cesium node counts and ids match the enabled features in the final runtime JSON; lights and countdowns continue switching correctly.
  • Existing road GeoPackage import/reimport behavior and merged scene layer order remain unchanged.
  • Unit/integration tests cover stable ids, editable-feature validation, override-to-pose reconstruction, auxiliary GeoPackage round-trip, and stage ownership.

Out Of Scope

  • Preserving QGIS edits across a subsequent full intermediates rebuild; as with road edits, users must preserve or reapply edits before regenerating the GeoPackage.
  • A complete traffic-controller timing editor or arbitrary multi-phase signal program.
  • Independent editing of each lens or countdown glyph position; those remain derived from the assembly point, heading, and shared layout.
  • Automatically assigning a stable identity to a brand-new signal feature drawn manually in QGIS.