diff --git a/.trellis/tasks/08-07-qgis-traffic-signal-overrides/check.jsonl b/.trellis/tasks/08-07-qgis-traffic-signal-overrides/check.jsonl deleted file mode 100644 index 113b732..0000000 --- a/.trellis/tasks/08-07-qgis-traffic-signal-overrides/check.jsonl +++ /dev/null @@ -1,5 +0,0 @@ -{"file":".trellis/spec/pipeline/cli-and-stages.md","reason":"Check stage ownership, diagnostics, and traffic signal contract compliance"} -{"file":".trellis/spec/pipeline/external-tools.md","reason":"Check atomic reimport behavior and external-tool handling"} -{"file":".trellis/spec/guides/cross-layer-thinking-guide.md","reason":"Check full editable-layer to runtime JSON to Blender/Cesium data flow"} -{"file":".trellis/spec/guides/artifact-parity-guide.md","reason":"Check intended and unintended scene/GLB structural differences"} -{"file":".trellis/spec/blender/testing.md","reason":"Check appropriate pure and Blender validation coverage"} diff --git a/.trellis/tasks/08-07-qgis-traffic-signal-overrides/design.md b/.trellis/tasks/08-07-qgis-traffic-signal-overrides/design.md deleted file mode 100644 index d7114ab..0000000 --- a/.trellis/tasks/08-07-qgis-traffic-signal-overrides/design.md +++ /dev/null @@ -1,100 +0,0 @@ -# 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. - diff --git a/.trellis/tasks/08-07-qgis-traffic-signal-overrides/implement.jsonl b/.trellis/tasks/08-07-qgis-traffic-signal-overrides/implement.jsonl deleted file mode 100644 index f965b3b..0000000 --- a/.trellis/tasks/08-07-qgis-traffic-signal-overrides/implement.jsonl +++ /dev/null @@ -1,6 +0,0 @@ -{"file":".trellis/spec/pipeline/cli-and-stages.md","reason":"Traffic signal stage ownership, reimport lifecycle, manifests, and current anchor contract"} -{"file":".trellis/spec/pipeline/layer-registry.md","reason":"Keep the auxiliary editable layer separate from the nine render layers and preserve their order"} -{"file":".trellis/spec/pipeline/external-tools.md","reason":"GeoPackage import/export and staging-before-replace requirements"} -{"file":".trellis/spec/guides/cross-layer-thinking-guide.md","reason":"OSM to GeoJSON/GPKG to Blender/Cesium contract review"} -{"file":".trellis/spec/blender/asset-generation.md","reason":"Signal pose and dynamic asset generation constraints"} -{"file":".trellis/spec/preview/vehicle-routes.md","reason":"Vehicle stop coordinates and runtime signal data coupling"} diff --git a/.trellis/tasks/08-07-qgis-traffic-signal-overrides/implement.md b/.trellis/tasks/08-07-qgis-traffic-signal-overrides/implement.md deleted file mode 100644 index 98768ad..0000000 --- a/.trellis/tasks/08-07-qgis-traffic-signal-overrides/implement.md +++ /dev/null @@ -1,53 +0,0 @@ -# Implementation Plan: QGIS Traffic Signal Overrides - -## 1. Contracts and Pure Logic - -- [x] Add an auxiliary editable-layer definition without modifying `SCENE_LAYERS` ordering. -- [x] Extend OSM arm parsing with deterministic approach identity inputs. -- [x] Split traffic-signal logic into automatic editable-feature generation, feature validation/normalization, and runtime pose derivation. -- [x] Use stable technical ids for runtime signal ids; keep `display_id` as editable metadata. -- [x] Add pure Node tests for T/cross counts, stable ids, movement/heading reconstruction, disabled features, duplicate ids, and invalid values. - -## 2. Intermediates and QGIS - -- [x] Write `traffic_signal_assemblies.geojson` after stop-line/intersection outputs are stable. -- [x] Import the auxiliary point layer into the GeoPackage after the nine render layers. -- [x] Extend generated QGIS project code with point/direction styling, labels, and field widgets/constraints. -- [x] Confirm the auxiliary layer is excluded from merged road scene ordering and raster preview behavior. - -## 3. Reimport and Stage Ownership - -- [x] Extend `reimport-gpkg.js` to discover/export render and auxiliary layers through staging. -- [x] Validate the staged editable layer before replacing any output artifact. -- [x] Rebuild runtime `traffic_signals.json` from editable GeoJSON after `intermediates`, `reimport`, and at Blender entry. -- [x] Remove Blender-entry OSM placement regeneration so QGIS edits remain authoritative. -- [x] Extend stage manifests and diagnostics with auxiliary input/output records and feature counts. - -## 4. Cross-Layer Consumers - -- [x] Preserve `display_id` and stable runtime ids through Blender and Cesium metadata where useful. -- [x] Verify static signal objects, dynamic lenses, countdown nodes, and vehicle stop behavior all consume the same enabled runtime records. -- [x] Update pipeline specifications to replace the old prohibition with the editable-layer/derived-runtime distinction. - -## 5. Validation - -- [x] Run Node syntax checks and focused unit tests. -- [x] Run existing preview-assets, preflight, budget, and relevant pipeline tests. -- [x] Run `intermediates` and inspect the GeoPackage/QGIS project feature schema and styling. -- [ ] Make a controlled QGIS edit to one signal (display id, point, heading), run `reimport,blender,cesium,preview`, and verify only the intended assembly changes. (`reimport` and Blender passed; Cesium/preview refresh was not repeated.) -- [x] Confirm an invalid/duplicate edit fails before overwriting valid outputs. -- [ ] Inspect Blender/Cesium structural digests and Safari preview for T and cross junctions. (Blocked this run by Blender 4.5.12 Metal startup SIGSEGV before project Python.) - -## Risky Files / Rollback Points - -- `scripts/lib/traffic-signals.js`: identity and pose contract; land pure tests before pipeline integration. -- `scripts/build-osm2streets-qgis.js`: GeoPackage recreation and generated QGIS Python; verify auxiliary import independently before styling. -- `scripts/reimport-gpkg.js`: atomic overwrite boundary; preserve staging semantics. -- `scripts/build-area.js`: stage ownership; ensure Blender derives from editable GeoJSON rather than overwriting it. -- `blender/osmassets/traffic_signals.py` and preview runtime should require minimal or no geometry changes; unexpected edits here indicate contract leakage. - -## Review Gate Before Start - -- [ ] User approves the final planning summary. -- [ ] `prd.md`, `design.md`, and `implement.md` agree on full editable layer ownership and out-of-scope intermediates persistence. -- [ ] No unresolved product decision remains. diff --git a/.trellis/tasks/08-07-qgis-traffic-signal-overrides/notes.md b/.trellis/tasks/08-07-qgis-traffic-signal-overrides/notes.md deleted file mode 100644 index dc97d04..0000000 --- a/.trellis/tasks/08-07-qgis-traffic-signal-overrides/notes.md +++ /dev/null @@ -1,14 +0,0 @@ -# Debug Notes - -## 2026-08-07 countdown node-name regression - -Stable `signal_uid` values are intentionally descriptive and can exceed Blender's -63-byte object-name limit. Using them directly in dynamic lens/countdown node -names caused Blender to truncate names while Cesium looked up the untruncated -strings. The countdown GLBs then exposed all digits without the runtime being -able to hide the inactive values, appearing as overlapping/blurred numbers. - -Runtime signal records now carry a deterministic short `nodeKey` (`ts_` plus -the first 16 hex characters of SHA-256 of `signal_uid`). Blender uses it for -dynamic object names and Cesium uses the same key for lookups. Preview keeps a -fallback to `signal.id` for older metadata files. diff --git a/.trellis/tasks/08-07-qgis-traffic-signal-overrides/prd.md b/.trellis/tasks/08-07-qgis-traffic-signal-overrides/prd.md deleted file mode 100644 index 6488b26..0000000 --- a/.trellis/tasks/08-07-qgis-traffic-signal-overrides/prd.md +++ /dev/null @@ -1,67 +0,0 @@ -# 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 `/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. - diff --git a/.trellis/tasks/08-07-qgis-traffic-signal-overrides/task.json b/.trellis/tasks/08-07-qgis-traffic-signal-overrides/task.json deleted file mode 100644 index afe91b0..0000000 --- a/.trellis/tasks/08-07-qgis-traffic-signal-overrides/task.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id": "qgis-traffic-signal-overrides", - "name": "qgis-traffic-signal-overrides", - "title": "QGIS traffic signal overrides", - "description": "", - "status": "in_progress", - "dev_type": null, - "scope": null, - "package": null, - "priority": "P2", - "creator": "dingkang", - "assignee": "dingkang", - "createdAt": "2026-08-07", - "completedAt": null, - "branch": null, - "base_branch": "main", - "worktree_path": null, - "commit": null, - "pr_url": null, - "subtasks": [], - "children": [], - "parent": null, - "relatedFiles": [], - "notes": "", - "meta": {} -} \ No newline at end of file diff --git a/.trellis/workspace/dingkang/index.md b/.trellis/workspace/dingkang/index.md index 10e2073..13547b0 100644 --- a/.trellis/workspace/dingkang/index.md +++ b/.trellis/workspace/dingkang/index.md @@ -8,7 +8,7 @@ - **Active File**: `journal-1.md` -- **Total Sessions**: 26 +- **Total Sessions**: 27 - **Last Active**: 2026-08-07 @@ -19,7 +19,7 @@ | File | Lines | Status | |------|-------|--------| -| `journal-1.md` | ~554 | Active | +| `journal-1.md` | ~575 | Active | --- @@ -29,6 +29,7 @@ | # | Date | Title | Commits | Branch | |---|------|-------|---------|--------| +| 27 | 2026-08-07 | QGIS traffic signal editing and countdown stability | `e153a1c` | `main` | | 26 | 2026-08-07 | 交通信号拓扑与部分构建同步修复 | `1c077a3` | `main` | | 25 | 2026-08-06 | Cesium traffic signal countdowns | `0e1574f` | `main` | | 24 | 2026-08-05 | 拆分 LowPoly Cars 车辆资产 | `3108336`, `2489b8a` | `main` | diff --git a/.trellis/workspace/dingkang/journal-1.md b/.trellis/workspace/dingkang/journal-1.md index cec96bb..b499bcf 100644 --- a/.trellis/workspace/dingkang/journal-1.md +++ b/.trellis/workspace/dingkang/journal-1.md @@ -552,3 +552,24 @@ Added shared 7LED countdown geometry, split dynamic Cesium assets by phase group ### Status [OK] **Completed** + + +## Session 27: QGIS traffic signal editing and countdown stability + +**Date**: 2026-08-07 +**Task**: QGIS traffic signal editing and countdown stability +**Branch**: `main` + +### Summary + +Implemented editable traffic signal assemblies in QGIS with stable IDs, position/heading overrides, reimport ownership, source validation, and Blender/Cesium runtime derivation. Fixed long signal IDs overflowing Blender node names with short nodeKey values, and changed QGIS SVG symbols to direct heading_deg field rotation so QGIS rotation edits write back to the field. Verified real GeoPackage round-trip, focused Node tests, preview tests, and 59 Blender tests. Safari cache caused stale GLB symptoms and was resolved with a hard refresh. + +### Git Commits + +| Hash | Message | +|------|---------| +| `e153a1c` | (see git log) | + +### Status + +[OK] **Completed**