Handoff — HighwayData XML schema 1.1: downstream decomposer

2026-08-28 17:55 · for the agent implementing the consumer-side parser (HBW / Grasshopper–CAD side) · producer repo: 3dai.iolabs.pointcloud.modellingexport, branch ai3d-376-xml-schema-1-1

Continuation target

Build the decomposer: a consumer-side parser that reads a HighwayData schema-1.1 XML document and decomposes it into typed feature objects and geometry — point symbols (position + Direction + Shapes + Annotation + Attributes), polylines (incl. closed rings and A/B SortingCode pairs), splines, line segments and cross-section point sets — dispatched per the §7 feature-class profiles, ready for the Grasshopper/CAD-side 2D-plan processing at HBW.

Target language is not decided (likely Python, or C#/.NET if it must live inside a Grasshopper component). Everything below is language-agnostic: it is the wire contract, not an API. The writer side is done and frozen enough to code against; this handoff is the contract summary plus the exact numbers to assert in tests.

The XML is the only agreed interface between the ioLabs detection pipeline and HBW's Grasshopper script (spec R-1.1.1). Both sides are implemented independently; do not invent side channels.

Current state (producer side)

Document structure & ordering guarantees

HighwayData
├─ MetaData            CreationDate, Source, SoftwareVersion, PipelineVersions?, SchemaVersion
├─ Geoshift?           X, Y, Z
└─ Features
   └─ Feature*         Type, ID, LaneID,
                       Lines,     NumberOfLines,
                       Points,    NumberOfPoints,
                       Polylines, NumberOfPolylines,
                       Splines,   NumberOfSplines,
                       Attributes?

Per-element parsing rules

Scalars

Point

Order: ID, LaneID, PointerNumber, X, Y, Z, Color, FeatureClass, FeatureID, RoadPoint?, Direction?, Shapes?, Annotation?, Attributes?

Point/Direction NEW

Point/Shapes/Shape NEW

Annotation NEW (on Point, Polyline, Spline, Line)

Annotation/Legible — enumeration, not a boolean

ValueMeaningText
fullEvery character of the physical text was read.complete lines, no substitution marks
partialSome of the text was read.the readable lines, unreadable characters marked; entirely unreadable lines omitted
noneNothing readable.omitted — but Display stays true, so still place the symbol

Attributes / Attribute NEW

Polyline

Order: ID, LaneID, Type?, RoadPoint?, Vertices, SortingCode?, Closed?, Annotation?, Attributes?

Spline and Line

§7.1 feature classes — what the decomposer must produce per Feature/Type

Geometry kind = which container carries the class. "Annot." = the required Annotation/Display value, = the class MUST NOT carry Annotation. "Text" = whether label text travels in the document. Derived from spec §7.1 (cross-checked against the producer's profile table); this table is self-sufficient — no repo access needed.

§Feature/TypeContainerRequired geometry Annot.TextDirectionShapesAttributes
7.2Tree LineSplines≥2 control pointsfalse
7.3Tree GroupPolylines≥3 vertices, Closed=truefalse
7.4BushSplines≥2 control pointsfalse
7.5TreePointspositiontrue1 linereq.trunk + crownopt. species_class
7.6FencePolylines≥2 verticesfalse
7.7Paving EdgePolylines≥2 verticesfalse
7.8noise_wallPolylines≥2 verticesheight_m, length_m
7.9TrenchPolylines≥2 vertices, G<i>$Afalse
7.10Trench BottomPolylines≥2 vertices, G<i>$Bfalse
7.11SlopePolylines ×2B<i>$A + B<i>$B in one Featurefalse
7.12Pedestrian RoadPolylines or Splines≥2 vertices / control pointsfalse
7.13Terrain PointPointspositiontruenone — render from Zreq.
7.14Traffic SignPointspositionfalsereq. (plate normal)opt. post cylinderopt. height_m, plate_class
7.15ShaftPointspositionfalsereq.
7.16Emergency Call BoxPointspositionfalsereq.
7.17GullyPointspositionfalsereq.width_m
7.18KM SignPointspositiontrue + Legible0..3 linesreq. (plate normal)km, section, station
7.19WallPolylines≥2 verticesfalseheight_m, length_m
7.20guardrailPolylinesas today (1.0 geometry)true1 line (type label)guardrail_type, migrated legacy keys
7.24guardrail_support, guardrail_top_railPolylinesas todaycadence / corridor keys
7.25Verge PostPointspositionfalsereq.opt. post cylinderheight_m, width_m
7.21Axis of the Edge, Center Lines — FROZENSplines + Linesas today (solid = Splines, dashed = Lines, per-segment Type/Width)
7.22Central Axis, Both-Sides Central Axis, Single-Side Central Axis — FROZENPolylinesas today, RoadPoint present
7.23Cross Section — FROZENPointsas today; PointerNumber = plane index, RoadPoint = distance along road
Hatches (Böschung / Graben)never exported — derived by the consumer from the A/B SortingCode pair

Frozen vs 1.1-touched. The three frozen groups (§7.21 lane markings, §7.22 road axes, §7.23 cross sections) carry no NEW element whatsoever — no Direction, no Shapes, no Annotation, no Attributes, no SortingCode, no Closed, no Polyline/Type — and keep their real LaneID and RoadPoint semantics. Everything else is "1.1-touched" and follows R-7.0.3: LaneID = "0" and RoadPoint omitted, unless the profile says otherwise (guardrail keeps its RoadPoint). Your decomposer can safely take a frozen-path branch (pure 1.0 parsing) and a 1.1 path, keyed by Feature/Type.

What the decomposer must tolerate

Spec §6.1 defines a conforming document; §6.2 lists what the writer rejects; §6.3 lists what it only warns about and still emits. Everything in §6.3 can and does reach you. Be liberal: log and continue, never abort the document.

Key artifacts

WhatWhere
Normative spec (1698 lines)miro-plans.pages.dev/pages/xml-schema-1-1-spec-20260828 (markdown source in the producer repo: docs/xml_schema_1_1_spec.md)
Spec page sourcemiro-plans.pages.dev/pages/xml-schema-1-1-spec-20260828 (source: docs/pages/xml-schema-1-1-spec-20260828.html)
Example document — 378 KB, 39 features, real + syntheticmiro-plans.pages.dev/pages/run7_branch_001_schema_1_1.xml (repo: docs/examples/run7_branch_001_schema_1_1.xml)
Reference fixture — 34 KB, one feature per profile, all 28 classesmiro-plans.pages.dev/pages/sample_v1_1.xml (repo: tests/fixtures/sample_v1_1.xml)
1.0 golden (schema-1.0 shape, for legacy-path tests)miro-plans.pages.dev/pages/golden_0_9_0.xml (repo: tests/fixtures/golden_0_9_0.xml)
The rows below live only in the producer repo — optional reference, not required for the decomposer.
Writer (producer reference implementation)src/iolabs_point_cloud_modelling_export/write_xml.py
§7.1 profile table + builders + check_profilessrc/iolabs_point_cloud_modelling_export/feature_classes.py
Example builder (docstring + summary tables)scripts/build_example_xml.py
Design predecessor of the specdocs/hbw_feature_classes_xml_plan.md
Producer repo / branch (access optional)iolabs-point-cloud-modelling-export, branch ai3d-376-xml-schema-1-1, HEAD 44428b8
Data source of the real halfrun 260722_run7_line_width, run7 branch_001 (lanefinder_abschnitt_1), first ~290 m, dual three-lane carriageway

Sections worth reading first, in order: spec §3 (element tree), §4 (element reference), §5 (value types, regexes, enums), §7.1 + §7.2–§7.25 (profiles), §6.3 (what you must tolerate), §9 (six complete worked documents), Appendix C (layer mapping). §8 is the producer dict contract — read it only if you also emit documents.

Verification — numbers to assert against the published example

Parse run7_branch_001_schema_1_1.xml and assert the following. These are the exact figures the build script prints; they are stable (the script is deterministic, jitter seeded 20260828). File: 378 632 bytes, 11 806 lines. The real half is byte-identical to the 0.9.0 writer's 331 528-byte output apart from SchemaVersion.

Features per Type — 39 total (11 real + 28 synthetic), 26 of 28 §7.1 classes

TypenoriginTypenorigin
Axis of the Edge4frozenSlope1synthetic
Center Lines4frozenTerrain Point3synthetic
Central Axis1frozenTraffic Sign1synthetic
Cross Section2frozenTree1synthetic
Bush1syntheticTree Group1synthetic
Emergency Call Box1syntheticTree Line1synthetic
Fence1syntheticTrench1synthetic
Gully2syntheticTrench Bottom1synthetic
KM Sign3syntheticVerge Post2synthetic
Paving Edge1syntheticWall1synthetic
Pedestrian Road1syntheticguardrail1synthetic
Shaft1syntheticguardrail_support1synthetic
noise_wall1syntheticguardrail_top_rail1synthetic

Absent from this document (this run has none): Both-Sides Central Axis, Single-Side Central Axis — both are present in sample_v1_1.xml.

Schema-1.1 element counts (example document)

ElementnElementn
MetaData/SchemaVersion1Shape/Base5
Feature/Attributes1Shape/Top4
Point/Attributes11Shape/Apex1
Polyline/Attributes5Shape/Diameter5
Attribute @type=string15Annotation26
Attribute @type=integer2Annotation/Display=true8
Attribute @type=float20Annotation/Display=false18
Attribute @type=boolean1Legible=full / partial / none1 / 1 / 1
Point/Direction14Annotation/Text7
Point/Shapes4Annotations with >1 Text2
Shape @type=cylinder4Polyline/Type4 (top×2, bottom×2)
Shape @type=cone1Polyline/SortingCode4 (G1$A, G1$B, B1$A, B1$B)
Shape rolespost/cylinder ×3, trunk/cylinder ×1, crown/cone ×1Polyline/Closed1
Polyline/RoadPoint (From|To)2Spline with non-empty Fit_Points1

Geometry totals

DocumentFeaturesLinePoint (in Points)PolylineVertexSplinespline Points (fit+control)
run7_branch_001_schema_1_1.xml39602921312611820
sample_v1_1.xml282111638415

Suggested consumer-side test set

Next steps (ordered)

  1. Decide the language and the deployment shape first — a Python library, or a C#/.NET assembly loadable by a Grasshopper component. That decision drives everything else, and it interacts with open item O-18 (which XML component HBW uses).
  2. Read spec §3, §4, §5, §7.1 and mirror §7.1 into a single class-profile table (Type → container, geometry kind, expected optional elements, layer). The §7.1 table on this page already encodes exactly this in a compact form.
  3. Write the structural reader: name-based traversal, unknown elements ignored, NumberOf* cross-check as a warning (not a failure). No index-based child access anywhere.
  4. Write the value layer: number/boolean/text parsing, the SortingCode regex, the Legible and Attribute/@type and Shape/@type enums, the "None"-literal mapping, the two RoadPoint shapes.
  5. Build the typed objects: point symbol (position, direction, shapes, annotation, attributes), polyline (vertices, closed, sorting code, type), spline (fit + control points), line segment (start/end, width), cross-section point set. Keep a raw-attribute bag on every object so unknown keys survive.
  6. Dispatch on Feature/Type into the profile table: frozen path (pure 1.0 parsing) vs 1.1 path. Unknown type ⇒ generic geometry + a diagnostic, never an exception.
  7. Add the consumer-only derivations that the document deliberately does not carry: layer from Appendix C; elevation labels from Z (N-5.7.2); trench/slope hatches from the A/B SortingCode pairs; 2D rotation from the XY projection of Direction; closing of Closed=true rings; symbol placement along Tree Line/Bush splines.
  8. Run the acceptance pass with HBW: feed the published example XML through the real Grasshopper component and confirm it tolerates unknown trailing children (O-18). Do this before the 0.10.0 release — it is the one open item that could still force a schema change.
  9. Confirm the Appendix C layer names and the elevation-label format with HBW (O-21, O-20) and fold the answers back into the spec.

Risks & open questions

IDQuestionCurrent positionImpact on the decomposer
O-18 highest Which Grasshopper XML component HBW uses; does it tolerate unknown trailing children? Assume name-based reading; one sample-file acceptance run before release (N-2.3.4) The whole trailing-placement compatibility strategy rests on this. Verify early with the published example.
O-20 Does HBW render elevation labels from Z per N-5.7.2 (absolute, 3 decimals, dot)? Consumer-side rendering; no Text in the document for Terrain Point / Cross Section The decomposer must implement this itself — it is not in the XML. Confirm the format.
O-21 Appendix C Type → CAD layer table, full confirmation with HBW Poster/Excel values as listed; informative only Keep the mapping in one swappable table; expect renames.
O-15 Tree label glyph Ø (U+00D8) vs (U+2300), spacing, precision, decimal comma Ø= 0,30/7,0m reference rendering, producer-formatted; the writer never substitutes Render Annotation/Text verbatim; do not normalise the glyph. Font must have it.
O-22 Spline definition underspecified: Fit_Points/Control_Points given, degree, knots and weights not (pre-existing 1.0 gap) Consumer and producer assume the same convention (informative) Pin your interpretation explicitly and write it down; exact 3D reconstruction is not guaranteed.
O-23 3D-model completeness: heights are optional attributes, symbol classes have no extents, no surface concept beyond Shape Not required by the 2D-plan contract If a 3D deliverable appears, expect new Shapes / mandatory heights — keep the shape model generic.
O-19 KM-sign km/section/station source: our chainage vs HBW's reference axis Attributes carry whatever the producer knows; unknown lines omitted Do not assume the three attributes and the three text lines always agree or are always present.
O-16 Verge Post class name and layer PLC_Randpfosten Provisional The Feature/Type string may change; keep it a table entry, not a hard-coded literal.
O-14 direction_source="fallback" when plate orientation is unknown Emitted as <Attribute name="direction_source"> Read it; a fallback direction should not drive a confident plan rotation.
O-17 Noise-wall "object name" annotation content No Annotation emitted for noise_wall (§7.8) Do not expect a label there today; it may arrive later as a new trailing element.

Non-spec risks


Assumed inputs for the next session: this page, the spec page and the published XML files above. No access to the producer repo, to local skills or to any tooling on the authoring machine is assumed; repo paths on this page are given only for traceability.