# Design: OSM turn-lane arrows ## Boundaries - `scripts/build-osm2streets-qgis.js` remains the intermediates owner. It will append generated custom features to the existing in-memory `laneArrows` collection before it is written as `lane_arrows_webscale.geojson`. - No render layer, QGIS symbol, Blender material, or Cesium export contract changes. Existing `lane_arrows_webscale` styling and catalog ownership apply to generated features automatically. - Vendored upstream artwork lives under a dedicated repository-local asset directory with its CC0 license and a machine-readable manifest. The manifest is the source of truth for upstream path, source revision, local path, support status, and test status. ## Asset Contract - Import every arrow SVG from `openstreetmap/lane-icons/docs/` that represents an arrow. Preserve the source bytes and retain `LICENSE.md` with the assets. - Mark only `through`, `left`, `right`, `through;left`, and `through;right` as `supported` and `tested` in the manifest. All remaining imported arrows are `unsupported` and `untested`; no production parser may select them. - The upstream repository has left-side examples only. Derive `right` and `through;right` through a deterministic horizontal mirror of the matching left-side geometry, recording `derived_from` in the manifest. ## Data Flow 1. Parse OSM ways once using the existing OSM parser. 2. Read `turn:lanes:forward` / `turn:lanes:backward`, split lane declarations by `|`, and map only the five supported normalized maneuvers to manifest entries. 3. For each valid lane near a determinable intersection endpoint, calculate a stable lane center and road-local axis; transform the selected local arrow polygon into WGS84 coordinates. 4. Add a GeoJSON feature with existing render-layer behavior and explicit provenance: custom source, OSM way id, direction, lane index, maneuver, source asset, and deterministic feature id/order. 5. Record a structured skipped item when tags, lane geometry, or endpoint classification are insufficient. Do not invent a fallback location. 6. Existing normalization, scene merge, GeoPackage import, Blender assembly, and Cesium export consume the expanded collection unchanged. ## Geometry and Compatibility - Parse only the vendored SVG subset used by supported assets at build time: `line` elements and `path` commands are converted into local meter-scale polygons, with cubic curves sampled and SVG strokes expanded into outlines. The source SVG is the sole geometry authority; no hand-drawn replacement template is permitted. This stays dependency-free because the supported upstream syntax is intentionally small and covered by Node tests. - Use the upstream Standard Arrow shape as the geometry basis, but let the existing QGIS fill and outline preserve the project's visual language. - Preserve the existing osm2streets-generated arrow features. Custom features are additive and have provenance that distinguishes them from upstream osm2streets output. - Deterministic ordering is by way id, travel direction, and lane index. - Each asset manifest entry defines the source SVG's shaft-axis `anchor_x`. Placement aligns that axis, rather than the SVG viewBox center, with the lane centerline. Mirrored right-side variants mirror around the same axis. - Upstream SVG units are converted at `0.10 m` per unit. This calibrates their local extent against the existing approximately 1.4 m osm2streets arrows; do not use SVG display-pixel scale as road-marking scale. ## Risks and Rollback - OSM lane counts, one-way semantics, or endpoint topology can be incomplete. These cases remain skipped with diagnostics rather than creating misleading markings. - Asset import is self-contained and CC0. Rollback removes the custom feature generation and local asset directory; the existing arrow layer remains unchanged. - User approval of the two initial visual samples is required before the feature is enabled in production output.