HighwayData XML — Schema 1.1
Normative specification. Status: draft for review (2026-08-28, revision 2).
Reference producer: iolabs_point_cloud_modelling_export.write_xml.create_xml / prettify_xml
(package iolabs-point-cloud-modelling-export; schema 1.0 as of 0.9.0, schema 1.1 targeted at 0.10.0).
Basis: docs/hbw_feature_classes_xml_plan.md (design), owner decisions of 2026-08-28 rounds 1 and 2
(binding; round 2 overrides round 1, and both override the plan — see Appendix A), poster
"2D plans overview" (18 columns).
1. Scope & terminology
1.1 Purpose
- R-1.1.1 This document is the contract between the ioLabs detection pipeline (producer side) and HBW's Grasshopper 2D-plan script (consumer side). Both are implemented independently and concurrently; the XML document is the only agreed interface between them.
- R-1.1.2 The primary subject of this specification is therefore document conformance: which elements exist, in what order, with what cardinality, and what their values mean (§§3–5, §6.1). A conforming document is defined without reference to any particular writer.
- R-1.1.3 Writer-side validation (§6.2, §6.3) is a secondary, deliberately partial projection of §6.1. A writer that emits a non-conforming document because it only warned is a producer bug, not a schema ambiguity. Nothing in §6.2/§6.3 narrows §6.1.
- R-1.1.4 §8 additionally specifies the
datadict accepted bycreate_xml, because that dict is the supported way for ioLabs producers to emit a conforming document. Other producers MAY emit conforming documents by other means. - R-1.1.5 Out of scope: the customer-side Data catalog (
SYMB####block names, rotation modesDRW/OWN/NO/VEC/ORD), CAD layer assignment (Appendix C is informative), hatch generation, tree-group outline detection, DXF export, and any XSD artefact. NoSYMBname, rotation mode, or CAD layer name is carried in the XML. - R-1.1.6 This document specifies behaviour and wire format only. It prescribes no implementation structure.
1.2 Terminology
RFC 2119: MUST, MUST NOT, SHOULD, SHOULD NOT, MAY, REQUIRED, OPTIONAL.
| Term | Meaning |
|---|---|
| Producer | Code that emits a document (cluster_stepper, guardrails, future HBW detectors). |
| Writer | The reference implementation, create_xml + prettify_xml. |
| Consumer | Anything reading the document: HBW's Grasshopper script, guardrails/lane_xml.py. |
| Legacy producer | A producer written against schema 1.0 that supplies no 1.1 key. |
| Feature class | One row of §7 = one Feature/Type + one container + one profile. |
| Container | One of Lines, Points, Polylines, Splines. |
| Geometry element | One Line, Point, Polyline, or Spline element. |
| Input shape | One of the poster's generic layouts: point symbol, polyline, spline, annotation-only. |
<Shape> |
The 1.1 solid-geometry element under <Point> (§4.20). Distinct from "input shape". |
| Export frame | The geoshifted local right-handed frame, metres, as emitted today; Geoshift restores absolutes. |
| 1.0 child | An element a 0.9.0 writer already emits for that parent. |
| NEW | An element introduced by this specification. |
| Frozen profile | A feature class whose serialisation MUST NOT change in 1.1 (§7.21–§7.23). |
| 1.1-touched | Defined in R-2.6.1. |
- R-1.2.1 Rules are numbered
R-<section>.<sub>.<n>and are normative. - R-1.2.2 Notes numbered
N-<section>.<sub>.<n>are informative and impose no requirement. - R-1.2.3 "Emitted" means present in the serialised document. "Byte-identical" is measured on the
UTF-8 bytes produced by
prettify_xml(create_xml(data)).
2. Versioning & compatibility
2.1 Version identification
- R-2.1.1 A 1.1 document MUST contain exactly one
MetaData/SchemaVersionwith text1.1. A 1.1 writer MUST emit it unconditionally, including for documents built from legacy dicts. - R-2.1.2 A consumer treats the absence of
MetaData/SchemaVersionas schema1.0. - R-2.1.3
SchemaVersionMUST match^[0-9]+\.[0-9]+$. A minor increment is additive under §2.3; a major increment signals a breaking change and is out of scope here. - R-2.1.4 A producer MAY set
metadata.schema_version; the writer MUST reject any value other than the version it implements. Absence defaults to1.1.
2.2 What 1.1 does not do
- R-2.2.1 No element defined in 1.0 is renamed, removed, retyped, or moved. In particular the four
containers and their four
NumberOf*siblings are still emitted for everyFeature, empty and0when the corresponding list is absent. - R-2.2.2 1.1 MUST NOT change the value semantics of any 1.0 element.
- R-2.2.3 Three feature groups are frozen and MUST NOT carry any NEW element: lane markings (§7.21),
road axes (§7.22), and cross-section points (§7.23). Their serialisation is byte-identical to 0.9.0 apart
from
MetaData/SchemaVersion. - R-2.2.4 There is no
Feature/Layerelement in 1.1. The CAD layer name is a drawing artefact, is constant per feature class, and is not derivable from point-cloud data; the consumer derives it fromFeature/Typeusing its own mapping. Appendix C gives that mapping informatively.
2.3 Trailing-placement rule
- R-2.3.1 Every NEW element MUST be placed after all 1.0 children of its parent, so that both name-based and index-based readers of 1.0 content are unaffected.
- R-2.3.2 Single documented exception:
Polyline/Typeis placed immediately afterPolyline/LaneID, the slotLine/TypeandSpline/Typehave occupied since 0.8.0. This is safe becausePolyline/Typeis emitted only when a producer supplies it, and no legacy producer does; no legacy document's child indices shift. A consumer MUST NOT assumePolylinechild index 2 isRoadPoint. - R-2.3.3 Within the trailing block, the order of §3 is normative and a consumer MAY rely on it.
- N-2.3.4 Consumers are expected to ignore unknown trailing children rather than fail (confirmed for
lane_xml.py; to be confirmed for Grasshopper by a pre-release sample-file acceptance run — §10 O-18).
2.4 Legacy producer output under 1.1
- R-2.4.1 For a
datadict containing no 1.1 key, a 1.1 writer's output MUST be byte-identical to the 0.9.0 writer's output except for the single inserted line<SchemaVersion>1.1</SchemaVersion>as the last child ofMetaData. - R-2.4.2 Elements that 1.1 makes optional (
Point/RoadPoint,Polyline/RoadPoint,Spline/RoadPoint) MUST still be emitted whenever the corresponding dict key is present, so R-2.4.1 holds. - R-2.4.3 No writer-side validation may prevent R-2.4.1. Conformance rules that 0.9.0 output could violate are scoped per R-2.6.2.
- R-2.4.4 A 1.1 writer MUST NOT introduce a compatibility/downgrade output mode. If HBW's reader proves strict, that is a separate change.
2.5 Class-key resolution
- R-2.5.1
Feature/Typeis the only class key. A consumer MUST filter and dispatch onFeature/Type. - N-2.5.2 This is consistent with the historic DWG→XML sample files, in which
Feature/Typealready carried the German CAD layer name (Bordstein,Schutzeinrichtung,Fahrbahnrand -bituminös-, …). A consumer that keys its catalog byTypetherefore handles both legacy and 1.1 documents with one lookup table. - R-2.5.3 A consumer SHOULD ignore any element it does not recognise, at any depth.
- N-2.5.4 For guard-rail labels a 1.1 consumer reads the
guardrail_typeattribute andAnnotation/Text; for 1.0 files it may fall back to the legacyPoint/FeatureClasssuffix conventionGuard Rails_<type>.
2.6 The "1.1-touched" scope
- R-2.6.1 A geometry element is 1.1-touched if its producer supplied any 1.1 key for it:
direction,shapes,annotation,attributes,closed,sorting_code(or thesort_class/pair_index/pair_roletriple), ortypeon a polyline. AFeatureis 1.1-touched if it suppliesattributesor contains a 1.1-touched geometry element. - R-2.6.2 Conformance rules that 0.9.0 output could violate (R-6.1.6 minimum vertex/control-point counts) apply as writer hard errors only to 1.1-touched elements. For elements that are not 1.1-touched the writer SHOULD warn instead (R-6.3.1). The conformance requirement itself is unchanged for new documents.
3. Document structure
Complete element tree. 1.0 = present in 0.9.0; NEW = introduced by 1.1. Children are listed in the order
in which they MUST be emitted.
Cardinality markers: 1 exactly one · ? 0..1 · * 0..n · + 1..n · 2+ 2..n · 1+/2+ 1..n for legacy
content, 2..n required by every 1.1 profile (§7).
HighwayData 1.0 1
├─ MetaData 1.0 1
│ ├─ CreationDate 1.0 1
│ ├─ Source 1.0 1
│ ├─ SoftwareVersion 1.0 1
│ ├─ PipelineVersions 1.0 ?
│ │ └─ Step @name @date? 1.0 *
│ │ ├─ SegmentRange @segments @date 1.0 * (variant A)
│ │ │ └─ Package @name @version 1.0 *
│ │ └─ Package @name @version 1.0 * (variant B)
│ └─ SchemaVersion NEW 1
├─ Geoshift 1.0 ?
│ ├─ X 1.0 1
│ ├─ Y 1.0 1
│ └─ Z 1.0 1
└─ Features 1.0 1
└─ Feature 1.0 *
├─ Type 1.0 1
├─ ID 1.0 1
├─ LaneID 1.0 1
├─ Lines 1.0 1
│ └─ Line 1.0 *
│ ├─ ID 1.0 1
│ ├─ LaneID 1.0 1
│ ├─ Type 1.0 ?
│ ├─ StartPoint (X, Y, Z) 1.0 1
│ ├─ EndPoint (X, Y, Z) 1.0 1
│ ├─ RoadPoint (From, To) 1.0 1
│ ├─ Width 1.0 ?
│ │ ├─ Value 1.0 ?
│ │ ├─ Sigma 1.0 ?
│ │ ├─ Status 1.0 ?
│ │ └─ Flagged 1.0 ?
│ ├─ Annotation NEW ?
│ └─ Attributes NEW ?
├─ NumberOfLines 1.0 1
├─ Points 1.0 1
│ └─ Point 1.0 *
│ ├─ ID 1.0 1
│ ├─ LaneID 1.0 1
│ ├─ PointerNumber 1.0 1
│ ├─ X 1.0 1
│ ├─ Y 1.0 1
│ ├─ Z 1.0 1
│ ├─ Color 1.0 1
│ ├─ FeatureClass 1.0 1
│ ├─ FeatureID 1.0 1
│ ├─ RoadPoint 1.0 ? (1 in 1.0; ? for 1.1 producers)
│ ├─ Direction (X, Y, Z) NEW ?
│ ├─ Shapes NEW ?
│ │ └─ Shape @role @type NEW +
│ │ ├─ Base (X, Y, Z) NEW 1
│ │ ├─ Top (X, Y, Z) NEW ? (exactly one of Top | Apex,
│ │ ├─ Apex (X, Y, Z) NEW ? selected by @type — R-4.20.3)
│ │ └─ Diameter NEW 1
│ ├─ Annotation NEW ?
│ └─ Attributes NEW ?
├─ NumberOfPoints 1.0 1
├─ Polylines 1.0 1
│ └─ Polyline 1.0 *
│ ├─ ID 1.0 1
│ ├─ LaneID 1.0 1
│ ├─ Type NEW ? (position per R-2.3.2)
│ ├─ RoadPoint (From, To) 1.0 ? (1 in 1.0; ? for 1.1 producers)
│ ├─ Vertices 1.0 1
│ │ └─ Vertex 1.0 1+/2+
│ │ ├─ ID 1.0 1
│ │ ├─ X 1.0 1
│ │ ├─ Y 1.0 1
│ │ └─ Z 1.0 1
│ ├─ SortingCode NEW ?
│ ├─ Closed NEW ?
│ ├─ Annotation NEW ?
│ └─ Attributes NEW ?
├─ NumberOfPolylines 1.0 1
├─ Splines 1.0 1
│ └─ Spline 1.0 *
│ ├─ ID 1.0 1
│ ├─ LaneID 1.0 1
│ ├─ Type 1.0 ?
│ ├─ RoadPoint (From, To) 1.0 ? (1 in 1.0; ? for 1.1 producers)
│ ├─ Width (as Line/Width) 1.0 ?
│ ├─ Fit_Points 1.0 1 (MAY be empty)
│ │ └─ Point (ID, X, Y, Z) 1.0 *
│ ├─ Control_Points 1.0 1
│ │ └─ Point (ID, X, Y, Z) 1.0 1+/2+
│ ├─ Annotation NEW ?
│ └─ Attributes NEW ?
├─ NumberOfSplines 1.0 1
└─ Attributes NEW ?
Shared NEW sub-trees, referenced above:
Annotation NEW
├─ Display NEW 1
├─ Legible NEW ? (enumeration — R-5.10.1)
└─ Text NEW *
Attributes NEW
└─ Attribute @name @type NEW +
- R-3.1 Children MUST be emitted in exactly the order above. A consumer MAY rely on that order.
- R-3.2 No element outside this tree may be emitted.
- R-3.3
Lines,Points,Polylines,Splinesand theirNumberOf*siblings MUST always be emitted, interleaved exactly as shown (Lines,NumberOfLines,Points,NumberOfPoints, …). - R-3.4
NumberOf*MUST equal the count of child geometry elements in the immediately preceding container. - R-3.5 No element carries mixed content; every element is element-only or text-only.
- R-3.6 Attributes appear only where listed:
Step/@name,Step/@date,SegmentRange/@segments,SegmentRange/@date,Package/@name,Package/@version,Attribute/@name,Attribute/@type,Shape/@role,Shape/@type. - R-3.7 The
1+/2+marker means: a document containing legacy content MAY carry a one-vertexPolylineor a one-pointControl_Points(0.9.0 permitted it); every geometry belonging to a §7 1.1 profile MUST carry at least two. Enforcement scope: R-2.6.2.
4. Element reference
"Position" is relative to siblings. Value rules referencing §5 are normative.
4.1 HighwayData — 1.0
Root, cardinality 1. Children in order: MetaData, Geoshift?, Features.
4.2 MetaData — 1.0
Parent HighwayData, 1, first. Children: CreationDate, Source, SoftwareVersion, PipelineVersions?,
SchemaVersion.
4.3 CreationDate, Source, SoftwareVersion — 1.0
Parent MetaData, 1 each, positions 1–3. Text, emitted verbatim from the producer (no str() coercion;
producers supply strings). CreationDate SHOULD be ISO-8601.
4.4 PipelineVersions / Step / SegmentRange / Package — 1.0
Unchanged from 1.0.
- R-4.4.1 Step/@name REQUIRED.
- R-4.4.2 A Step uses either the SegmentRange variant or the flat variant, never both. In the flat
variant Step/@date is always set — to the empty string when the producer supplies no date — and
Package children follow. In the SegmentRange variant Step carries no @date.
- R-4.4.3 SegmentRange/@segments is the segment list joined with ", ", where a range a..b renders
a-b and a singleton (a == b) renders a. SegmentRange/@date REQUIRED.
4.5 SchemaVersion — NEW
Parent MetaData, 1, last child. Text 1.1 (R-2.1.1).
Example: <SchemaVersion>1.1</SchemaVersion>.
4.6 Geoshift (X,Y,Z) — 1.0
Parent HighwayData, 0..1, after MetaData. Emitted iff the producer supplies it. Numeric text per §5.1.
Adding Geoshift to an export-frame coordinate yields the absolute coordinate.
4.7 Features — 1.0
Parent HighwayData, 1, last. Contains 0..n Feature. Always emitted, even when empty.
4.8 Feature — 1.0
Parent Features, 0..n, document order = producer order. Children per §3.
4.9 Feature/Type — 1.0
1, first child. Text, non-empty. The stable semantic class id and the sole class key (R-2.5.1).
Values in use: Cross Section, Center Lines, Axis of the Edge, Central Axis,
Both-Sides Central Axis, Single-Side Central Axis, guardrail, guardrail_support,
guardrail_top_rail, noise_wall, plus the 1.1 classes of §7. Existing lower-case strings are preserved
as-is; NEW classes use English singular Title Case.
- R-4.9.1 The writer MUST NOT validate Type against a closed vocabulary; producers may add classes
without a writer release.
4.10 Feature/ID — 1.0
1, second. Text, non-empty, document-unique (R-6.1.2; producer obligation, writer warns).
4.11 Feature/LaneID — 1.0
1, third. Text. "0" when the feature is not lane-bound. Always emitted.
- R-4.11.1 Every geometry element carries its own LaneID, always emitted. A legacy dict MUST supply
the geometry-level lane_id (the 0.9.0 writer raises without it, and R-2.4.1 depends on it). A
1.1-touched geometry MAY omit it, in which case the writer defaults it to the owning
Feature/LaneID.
4.12 Feature/Attributes — NEW
0..1, last child of Feature (after NumberOfSplines). Feature-level metadata; see §4.28. Rare —
per-geometry Attributes is preferred.
4.13 (reserved — Feature/Layer was removed by R-2.2.4)
No element occupies this slot. Documents produced from pre-release drafts may contain a <Layer> child of
Feature; consumers ignore it (R-2.5.3).
4.14 Line and children — 1.0
ID, LaneID text; Type 0..1 immediately after LaneID; StartPoint/EndPoint with X,Y,Z;
RoadPoint with From,To; Width 0..1. 1.1 appends Annotation? then Attributes? after Width.
- R-4.14.1 Line/Type is an open string; the writer emits whatever the producer supplies and MUST NOT
restrict it.
- N-4.14.2 The vocabulary in use today is solid | dashed (lane markings). This is informative.
4.15 Width (Value,Sigma,Status,Flagged) — 1.0
Parent Line or Spline, 0..1. Each child is emitted only when its source value is not None.
Flagged is str(bool(v)).lower(). Unchanged in 1.1.
4.16 Point — 1.0 (extended)
Parent Points, 0..n. In 1.0 a Point was only a cross-section sample; in 1.1 it is either that (frozen,
§7.23) or a point symbol (§7 point classes).
4.17 Point legacy children — 1.0
ID (text, non-empty), LaneID (text), PointerNumber (integer text), X,Y,Z (numeric text, export
frame), Color, FeatureClass, FeatureID (text; str()-coerced — hence the literal None seen in legacy
cross-section output), RoadPoint (scalar numeric text — not a From/To pair, unlike
Line/Polyline/Spline).
- R-4.17.1 PointerNumber, Color, FeatureClass, FeatureID MUST always be emitted. When a 1.1
producer omits the source key the writer MUST default: PointerNumber = 1-based index of the point within
its Points container; Color = empty text; FeatureClass = the owning Feature/Type; FeatureID = the
owning Feature/ID.
- R-4.17.2 These defaults apply only when the key is absent. A key present with value None is
str()-coerced to the text None, preserving 1.0 behaviour (R-2.4.1).
- R-4.17.3 RoadPoint MUST be emitted iff the producer supplies it. Point symbols with no chainage omit it.
4.18 Point/Direction — NEW
0..1, immediately after RoadPoint (or after FeatureID when RoadPoint is omitted). Children X,Y,Z
in that order, numeric text. Value rules: §5.5. Example:
<Direction><X>0.963767</X><Y>-0.221946</Y><Z>0.147964</Z></Direction>
4.19 Point/Shapes — NEW
0..1, after Direction. Contains 1..n Shape. Emitted iff the producer supplies a non-empty shape list.
Producer order is preserved.
4.20 Point/Shapes/Shape — NEW
1..n. Attributes: role (REQUIRED, free text; known: trunk, crown, post) and type (REQUIRED,
cylinder | cone).
- R-4.20.1 type="cylinder" — children in order: Base (X,Y,Z), Top (X,Y,Z), Diameter.
- R-4.20.2 type="cone" — children in order: Base (X,Y,Z), Apex (X,Y,Z), Diameter.
- R-4.20.3 Exactly one of Top / Apex MUST be present, selected by @type. Emitting both, neither, or
the one that does not match @type is non-conforming.
Geometry semantics: §5.8. Example:
<Shapes>
<Shape role="trunk" type="cylinder">
<Base><X>-109.06</X><Y>-383.29</Y><Z>0.02</Z></Base>
<Top><X>-109.04</X><Y>-383.27</Y><Z>2.52</Z></Top>
<Diameter>0.3</Diameter>
</Shape>
<Shape role="crown" type="cone">
<Base><X>-109.04</X><Y>-383.27</Y><Z>2.52</Z></Base>
<Apex><X>-109.01</X><Y>-383.22</Y><Z>8.52</Z></Apex>
<Diameter>7.0</Diameter>
</Shape>
</Shapes>
4.21 Annotation — NEW
Parent: Point, Polyline, Spline, Line. 0..1. Position: after all 1.0 children and after
Direction/Shapes/SortingCode/Closed where those exist; immediately before Attributes.
Children in order: Display (1), Legible (0..1), Text (0..n).
Emitted iff the producer supplies it — legacy producers are unaffected.
- R-4.21.1 A producer MUST supply Annotation for every geometry whose §7.1 "Annot." column reads true
or false, including when the value is false, so the poster's boolean is explicit in the document.
Classes whose "Annot." column reads — (noise_wall, guardrail_support, guardrail_top_rail, and all
frozen profiles) MUST NOT carry Annotation.
4.22 Annotation/Display — NEW
1, first. Boolean text, lower-case (§5.2). Corresponds to the poster's "has annotation display" / "has a
description".
- R-4.22.1 Display=true means the consumer renders a label at this geometry. Whether the label text
travels in the document is profile-dependent (§4.24, §7).
4.23 Annotation/Legible — NEW
0..1, second child of Annotation. Enumeration, not a boolean: full | partial | none
(R-5.10.1). It reports how much of the text carried by the physical object was recoverable from the
point cloud.
| Value | Meaning | Text |
|---|---|---|
full |
Every character of the physical text was read. | complete lines, no substitution marks |
partial |
Some of the text was read. | the lines that were read, with unreadable characters marked per R-4.24.5; entirely unreadable lines omitted |
none |
Nothing readable. | MUST be omitted |
- R-4.23.1
LegibleMUST be emitted for feature classes whose annotation text is transcribed from a physical object — todayKM Signonly;Traffic Signplates later. - R-4.23.2 For any other class a producer SHOULD NOT emit
Legible; the writer SHOULD warn (R-6.3.6). It carries no meaning for labels computed by us (elevation labels, treeØ=labels, guard-rail type labels). - R-4.23.3 When
Legibleisnone,TextMUST be omitted whileDisplaystaystrue, so the consumer still places the symbol without a label. - R-4.23.4 When
Legibleisfull, noTextvalue may contain a substitution mark (R-4.24.5); the writer SHOULD warn if one does (R-6.3.16). - N-4.23.5 The object was detected in every case;
Legibledescribes only the legibility of its inscription, never the confidence of the detection.
4.24 Annotation/Text — NEW
0..n, after Display/Legible. Text, UTF-8. One element per rendered line, in draw order.
- R-4.24.1 Text absent while Display=true is conforming and profile-dependent. It means either the
consumer derives the label itself (elevation labels — §7.13, §7.23, N-5.7.2) or the label was unrecoverable
(Legible=none — R-4.23.3).
- R-4.24.2 Text MUST NOT be emitted when Display=false (R-6.2.4).
- R-4.24.3 A producer MUST NOT put \n or \r inside a Text value; multi-line labels use multiple
Text elements. The writer does not split such a value; it SHOULD warn (R-6.3.4).
- R-4.24.4 Text is a display string produced upstream. The writer MUST NOT format, localise, round, or
reorder it.
- R-4.24.5 (substitution marks) Under Legible=partial, an unreadable character inside an emitted line
MUST be written as a single ? — one ? per unreadable character. When the number of unreadable
characters is unknown, the run MUST be written as ?… (U+003F followed by U+2026). These two forms are
the only substitution marks; a line that was entirely unreadable is omitted rather than filled with marks.
- N-4.24.6 A consumer SHOULD render partial text verbatim, ? and ?… included: the marks tell the plan
reader that a sign exists and how much of it was recovered. Stripping them would silently fabricate a
complete label.
Example (KM sign, 3 lines):
<Annotation><Display>true</Display><Legible>full</Legible>
<Text>KM 46.5</Text><Text>Abs. 90</Text><Text>Station 0,25</Text></Annotation>
4.25 Polyline/Type — NEW
0..1, immediately after LaneID (R-2.3.2). Open string. Known values: top, bottom (trench/slope outlines)
and guard-rail sub-kinds. Redundant with SortingCode on purpose (human readability); the code is the
contract.
4.26 Polyline/SortingCode — NEW
0..1, immediately after Vertices. Text matching R-5.6.1. Absent = the poster's null.
4.27 Polyline/Closed — NEW
0..1, after SortingCode. Boolean text, lower-case. true = the vertex list describes a closed ring; the
consumer closes it.
- R-4.27.1 When Closed is true the producer MUST NOT repeat the first vertex as the last, and the
polyline MUST have at least 3 vertices. The writer SHOULD warn on either (R-6.3.8, R-6.3.9).
4.28 Attributes / Attribute — NEW
Parent: Feature, Point, Polyline, Spline, Line. 0..1, always the last child of its parent.
Contains 1..n Attribute; Attributes MUST NOT be emitted empty (R-6.2.9).
Attribute/@name REQUIRED, snake_case, unique within its Attributes; units belong in the name (length_m).
Attribute/@type REQUIRED, one of string | integer | float | boolean (§5.9). Text = the serialised
value.
- R-4.28.1 Attribute children MUST be emitted sorted ascending by @name (deterministic output).
- R-4.28.2 An attribute whose value is None MUST be skipped entirely.
Example:
<Attributes>
<Attribute name="guardrail_type" type="string">EDSP</Attribute>
<Attribute name="post_count" type="integer">37</Attribute>
</Attributes>
4.29 Polyline legacy children — 1.0
ID (text), LaneID (text), RoadPoint (From,To), Vertices.
- R-4.29.1 RoadPoint MUST be emitted iff the producer supplies it (optional for 1.1 producers, always
present for legacy ones).
- R-4.29.2 Vertices MUST always be emitted; vertex count per R-3.7.
- R-4.29.3 The writer MUST generate Vertex/ID as f"{polyline_id}-{index+1}" (1-based). Producers MUST
NOT supply vertex ids.
4.30 Spline legacy children — 1.0
ID, LaneID, Type?, RoadPoint?, Width?, Fit_Points, Control_Points.
- R-4.30.1 Fit_Points MUST always be emitted; it MAY be empty (<Fit_Points/>), so the Spline child
layout never varies.
- R-4.30.2 Control_Points MUST always be emitted; point count per R-3.7.
- R-4.30.3 The writer MUST generate ids f"{spline_id}_{n+1}", where n is the 0-based running index
across Fit_Points followed by Control_Points — control-point numbering continues after the fit points.
- R-4.30.4 RoadPoint MUST be emitted iff the producer supplies it.
- R-4.30.5 Spline/Type is an open string (as R-4.14.1).
5. Value types & formatting
5.1 Numbers
- R-5.1.1 Numeric values are serialised as Python
str(value); the writer MUST NOT round, pad, localise, or apply a format spec.0.1→0.1;1.0→1.0;12→12. - R-5.1.2 Precision is the producer's concern: supply an already-rounded
float, or a pre-formattedstr. - R-5.1.3 Producers MUST NOT supply
NaNor±Infin any coordinate or length. The writer hard-rejects this only inside NEW elements (R-6.2.5, R-6.2.7); elsewhere it SHOULD warn (R-6.3.3). - R-5.1.4 The decimal separator in numeric element text MUST be
.. A decimal comma is permitted only insideAnnotation/Text(display strings, e.g.Ø= 0,30/7,0m). - R-5.1.5 All coordinates and lengths are metres in the export frame unless stated otherwise.
5.2 Booleans
- R-5.2.1 Boolean element text MUST be exactly
trueorfalse(lower-case), i.e.str(bool(v)).lower(). Applies toWidth/Flagged(1.0),Annotation/Display,Polyline/Closed, andAttributevalues of@type="boolean". - N-5.2.2
True,1,yesare not valid boolean text. Consumers are not expected to accept them.
5.3 Text & encoding
- R-5.3.1 The document is UTF-8.
prettify_xmlreturns astrbeginning with<?xml version="1.0" encoding="UTF-8"?>; whoever writes it to a file or stream MUST encode it as UTF-8 bytes, so that the declaration is truthful. - R-5.3.2 Non-ASCII characters (
ä ö ü ß Ø ⌀) MUST be emitted as literal UTF-8, not character references. - R-5.3.3
&,<,>MUST be XML-escaped by the serialiser; producers MUST NOT pre-escape. - R-5.3.4 The writer MUST NOT trim or normalise element text. Consumers SHOULD strip leading and trailing whitespace when reading element text, as a defensive measure.
- R-5.3.5 A NEW element's text value MUST NOT be empty or whitespace-only where §4 requires content
(
SchemaVersion,Annotation/Textwhen emitted,Attributetext of@type="string"where the profile requires a value,SortingCode,Polyline/Type,Shapenumeric children). The writer MUST reject such a value (R-6.2.10). This rule does not apply to 1.0 elements, which may legitimately be empty (Color). - N-5.3.6
Ø(U+00D8) and⌀(U+2300) are distinct; the poster shows⌀in its template andØin its example. Producers pick one per deployment; the writer never substitutes (§10 O-15).
5.4 Identifiers
- R-5.4.1
Feature/ID,Point/ID,Line/ID,Polyline/ID,Spline/IDare producer-supplied, non-empty, and MUST be unique within the document. This is a producer obligation; the writer SHOULD warn (R-6.3.2) and MUST NOT reject, so R-2.4.1 holds for legacy input. - R-5.4.2 Ids SHOULD match
^[A-Za-z0-9_.:-]+$so derived vertex ids stay parseable. - R-5.4.3 Vertex and spline-point ids are writer-generated (R-4.29.3, R-4.30.3) and MUST NOT be supplied.
- R-5.4.4
LaneIDis text, not numeric;"0"denotes "no lane".
5.5 Direction vectors
- R-5.5.1
Directionis a full 3D unit vector in the export frame:|v| = 1 ± 1e-6. Any orientation is representable;ZMAY be non-zero (a tilted or inclined sign plate). - R-5.5.2 Components MUST be finite and the vector MUST NOT be zero-length. The writer MUST reject a violation (R-6.2.5).
- R-5.5.3 Producers normalise. The writer MUST NOT normalise and MUST NOT silently repair a non-unit vector; it SHOULD warn (R-6.3.5).
- N-5.5.4 A consumer that needs a plan (2D) rotation projects the vector onto the XY plane and normalises
there. Catalog rotation mode
VECis the main consumer (traffic signs, KM signs, shafts, gullys). - R-5.5.5 Semantics by class: for sign-like classes (
Traffic Sign,KM Sign) the vector is the plate normal pointing towards oncoming traffic; forGully,Shaft, and the remaining point classes it is the road-axis tangent at the point. - R-5.5.6
DirectionMUST be emitted for every 1.1 point class of §7, including classes whose catalog rotation mode ignores it (NO,ORD,DRW). - R-5.5.7 When the true orientation could not be measured, the producer SHOULD emit the substitute vector
and an attribute
direction_sourcewith valuefallbackin the samePoint'sAttributes; valuemeasuredotherwise (§10 O-14).
5.6 Sorting-code grammar
- R-5.6.1 A
SortingCodeMUST match exactly:^[A-Z]+(0|[1-9][0-9]*)\$[AB]$This regex is the single normative definition. Capture semantics:[A-Z]+= family (G= Graben/Grabensohle,B= Böschung); the digits = pair index, an unbounded non-negative integer with no leading zeros;$= separator; trailingA= top outline,B= bottom outline. - R-5.6.2 The pair index is unique per family per XML document.
G10$Ais well-formed. - R-5.6.3 Every
(family, index)pair occurring in a document MUST occur exactly twice — once withAand once withB. Because the two halves may be produced by different detectors or segments, an incomplete pair MUST NOT abort the document; the writer SHOULD warn (R-6.3.7). - R-5.6.4 Two
SortingCodevalues MUST NOT be equal within one document. The writer MUST reject a duplicate (R-6.2.3). - R-5.6.5 The writer accepts either a ready
sorting_codestring, or the triplesort_class(family),pair_index(int),pair_role∈top|bottom, and composesf"{sort_class}{pair_index}${role_letter}"withtop→A,bottom→B. Supplying both forms, or the triple incompletely, is an error (R-6.2.6).
5.7 Annotation text conventions
- R-5.7.1 One
Textper rendered line; no embedded newlines (R-4.24.3). - N-5.7.2 (informative — consumer rendering guidance for elevation labels) For
Terrain PointandCross Section, noTexttravels in the document; the consumer renders the label from the point'sZ. The agreed rendering is the absolute height — export-frameZplusGeoshift/Z— with exactly three decimals, a dot separator, no unit suffix and no thousands separator, e.g.47.333. This is guidance, not a writer rule; the document carries only the numbers. - N-5.7.3 (informative — tree label) The reference rendering is
Ø= 0,30/7,0m: trunk diameter then crown diameter, decimal comma, two decimals for the trunk and one for the crown. It is produced upstream and travels asText; the writer MUST NOT derive it fromShapes(R-6.4.3). See §10 O-15. - R-5.7.4 (KM sign) Up to three
Textlines, in order:KM <km>,Abs. <section>,Station <station>. A line whose source field is unknown is omitted, keeping the remaining lines in order. - R-5.7.5 (guard rail) The
Textis the guard-rail type label as drawn (EDSP,ESP 2,0), matching the machine value of theguardrail_typeattribute.
5.8 Shape geometry semantics
- R-5.8.1 A
Shapelocks its position in 3D explicitly. Its axis is the segmentBase → Top(cylinder) orBase → Apex(cone). Any orientation is representable, including leaning trunks; nothing is implied by the owningPoint's coordinates. - R-5.8.2 All of
Base,Top,Apexare absolute points in the export frame, in metres, with the sameX,Y,Zconventions asPoint/X|Y|Z. - R-5.8.3
Baseis the centre of the bottom circle.Topis the centre of the top circle of a cylinder;Apexis the tip of a cone. - R-5.8.4
Diameteris the diameter of theBasecircle, in metres, and MUST be finite and> 0. For a cylinder the diameter is constant along the axis. - R-5.8.5 The axis MUST have non-zero length:
Base != Top/Base != Apex. - R-5.8.6
Shapeis generic: any point symbol MAY carry one (e.g. a verge post cylinder).@roleis free text scoped by the feature class. - R-5.8.7 For
Tree: a producer MUST emitrole="trunk"with@type="cylinder", androle="crown"with@type="cylinder"or"cone"; exactly one shape of each role. The writer SHOULD warn on a violation (R-6.3.10) rather than reject, so unexpected roles are still exported. - N-5.8.8 For a tree the crown's
Basenormally coincides with the trunk'sTop; nothing requires it.
5.9 Attribute/@type vocabulary
@type |
Source value | Text form |
|---|---|---|
string |
str |
verbatim |
integer |
int (not bool) |
str(v) |
float |
float |
str(v) (§5.1) |
boolean |
bool |
true / false |
- R-5.9.1
boolMUST map toboolean, never tointeger, even thoughboolsubclassesint. - R-5.9.2 A value that is a
Mapping, a sequence, or any other non-scalar MUST be rejected (R-6.2.8); nesting insideAttributesis not representable. - R-5.9.3 Producers SHOULD encode the unit in
@namerather than in the text. - N-5.9.4 The convention in use:
_mmetres,_degdegrees,_sseconds — e.g.length_m,top_height_p90_m. This is a naming habit, not a grammar; nothing validates it.
5.10 Enumerations
- R-5.10.1
Annotation/Legibletext MUST be exactly one offull,partial,none— lower-case, no surrounding whitespace, no other value. The writer MUST reject anything else (R-6.2.16). - R-5.10.2
Shape/@typeMUST be exactlycylinderorcone(R-4.20.1/2). - R-5.10.3
Attribute/@typeMUST be one ofstring,integer,float,boolean(§5.9). - N-5.10.4 Enumerations are closed: a consumer encountering an unknown value SHOULD treat the element as absent rather than guess. Extending an enumeration requires a schema minor version bump.
6. Conformance & validation
§6.1 defines a conforming document — the contract of R-1.1.2, checkable by any consumer or validator. §6.2 and §6.3 state what the reference writer does about each rule; they are a partial projection of §6.1 and never widen or narrow it (R-1.1.3). Every §6.1 rule appears in exactly one of §6.2, §6.3, or §6.4.
6.1 Document conformance
| Rule | A conforming document satisfies | Writer |
|---|---|---|
| R-6.1.1 | Structure, order, and cardinality of §3; NumberOf* matches its container (R-3.4). |
reject (R-6.2.1) |
| R-6.1.2 | Every Feature/ID and geometry ID is non-empty and document-unique (R-5.4.1). |
warn (R-6.3.2) |
| R-6.1.3 | Every SortingCode matches R-5.6.1. |
reject (R-6.2.2) |
| R-6.1.4 | No two SortingCode values are equal (R-5.6.4). |
reject (R-6.2.3) |
| R-6.1.5 | Each (family, index) occurs exactly once as A and once as B (R-5.6.3). |
warn (R-6.3.7) |
| R-6.1.6 | Every Polyline has ≥2 Vertex and every Spline ≥2 Control_Points/Point (R-3.7). |
reject if 1.1-touched, else warn (R-2.6.2, R-6.3.1) |
| R-6.1.7 | Direction components are finite and the vector is non-zero (R-5.5.2). |
reject (R-6.2.5) |
| R-6.1.8 | Direction is unit length within 1e-6 (R-5.5.1). |
warn (R-6.3.5) |
| R-6.1.9 | Annotation has exactly one Display with boolean text; Text is absent when Display=false (R-4.24.2). |
reject (R-6.2.4) |
| R-6.1.10 | Legible appears only on the classes of R-4.23.1. |
warn (R-6.3.6) |
| R-6.1.11 | No Text value contains \n or \r (R-4.24.3). |
warn (R-6.3.4) |
| R-6.1.12 | Annotation is present exactly for the geometries required by R-4.21.1 and absent otherwise. |
warn (R-6.3.11) |
| R-6.1.13 | Each Shape has @role, a @type in {cylinder, cone}, the matching Top/Apex (R-4.20.3), a finite positive Diameter, finite Base/Top/Apex, and a non-degenerate axis (R-5.8.5). |
reject (R-6.2.7) |
| R-6.1.14 | A Tree carries exactly one trunk and one crown shape with the types of R-5.8.7. |
warn (R-6.3.10) |
| R-6.1.15 | Attributes is non-empty; @name is unique within it; @type matches the value per §5.9; values are scalar. |
reject (R-6.2.8, R-6.2.9) |
| R-6.1.16 | Attribute children are sorted ascending by @name (R-4.28.1). |
reject (R-6.2.1) |
| R-6.1.17 | No NEW element carries empty or whitespace-only text where §4 requires content (R-5.3.5). | reject (R-6.2.10) |
| R-6.1.18 | Frozen-profile features (§7.21–§7.23) carry no NEW element (R-2.2.3). | warn (R-6.3.12) |
| R-6.1.19 | Boolean text is lower-case true/false (R-5.2.1); numeric text uses . (R-5.1.4). |
reject for NEW elements (R-6.2.1); guaranteed by construction otherwise |
| R-6.1.20 | Closed=true implies ≥3 vertices and no repeated closing vertex (R-4.27.1). |
warn (R-6.3.8, R-6.3.9) |
| R-6.1.21 | No coordinate or length is NaN/±Inf (R-5.1.3). |
reject inside NEW elements; warn otherwise (R-6.3.3) |
| R-6.1.22 | MetaData/SchemaVersion is present and matches R-2.1.3. |
reject (R-6.2.11) |
| R-6.1.23 | Every KM Sign annotation carries Legible (R-4.23.1, §7.18). |
producer MUST; not detectable without Type — advisory §7.1 lookup only (R-6.3.14) |
| R-6.1.24 | No Text is emitted when Legible=none (R-4.23.3). |
warn; detectable without Type (R-6.3.15) |
| R-6.1.26 | Legible text is one of full | partial | none (R-5.10.1). |
reject (R-6.2.16) |
| R-6.1.27 | No Text under Legible=full contains a substitution mark ? or ?… (R-4.23.4, R-4.24.5). |
warn, advisory only (R-6.3.16) |
| R-6.1.25 | Terrain Point and Cross Section carry no Annotation/Text; the consumer renders the label from Z (§7.13, §7.23, N-5.7.2). |
producer MUST; not detectable without Type — advisory §7.1 lookup only (R-6.3.14) |
6.2 Writer MUST reject (hard errors, nothing is emitted)
| Rule | Condition |
|---|---|
| R-6.2.1 | The writer would emit a tree violating §3 order/cardinality, R-4.28.1 sorting, or §5.2/§5.1.4 formatting of a NEW element. (Structural; a correct writer cannot trip it.) |
| R-6.2.2 | A sorting_code — supplied or composed — not matching R-5.6.1. |
| R-6.2.3 | Two equal SortingCode values within one document. |
| R-6.2.4 | annotation without a display key, display not a bool, or display=false while any Text would be emitted. |
| R-6.2.5 | A Direction component that is non-finite, or a zero-length direction vector. |
| R-6.2.6 | Both sorting_code and any of (sort_class, pair_index, pair_role) on the same polyline; or the triple supplied incompletely; or pair_role outside {top, bottom}, pair_index not a non-negative integer, sort_class not matching ^[A-Z]+$. |
| R-6.2.7 | A Shape missing @role; @type outside {cylinder, cone}; the wrong or both of Top/Apex; a missing, non-positive or non-finite Diameter; a non-finite Base/Top/Apex component; or a zero-length axis. |
| R-6.2.8 | An attribute value that is not str/int/float/bool/None, or a duplicate @name within one Attributes. |
| R-6.2.9 | Attributes would be emitted with zero Attribute children. |
| R-6.2.10 | An empty or whitespace-only text value for a NEW element that requires content (R-5.3.5). |
| R-6.2.11 | metadata.schema_version present and different from the version the writer implements (R-2.1.4). |
| R-6.2.12 | A required 1.0 key missing (metadata.creation_date/source/software_version, feature.type/id/lane_id, geometry id, geometry lane_id on a geometry that is not |
1.1-touched (R-4.11.1), point.position, polyline.vertices, spline.control_points/fit_points, line.start_point/end_point/road_point) — 1.0 behaviour, preserved. |
|
| R-6.2.13 | A 1.1-touched polyline with fewer than 2 vertices, or a 1.1-touched spline with fewer than 2 control points (R-2.6.2). |
| R-6.2.16 | An annotation.legible value outside {full, partial, none} (R-5.10.1), or of a type other than str/None. |
- R-6.2.14 An error message MUST identify the offending
Feature/IDand geometryIDwhere such a context exists; document-level errors (R-6.2.3, R-6.2.11) name the offending values instead. - R-6.2.15 Document-level checks (R-6.2.3) run after the whole tree is built and MUST prevent the document from being returned.
6.3 Writer SHOULD warn (document is still emitted)
| Rule | Condition |
|---|---|
| R-6.3.1 | A polyline with <2 vertices or a spline with <2 control points that is not 1.1-touched (legacy content; R-2.6.2). |
| R-6.3.2 | An empty or duplicated ID anywhere in the document (R-6.1.2). |
| R-6.3.3 | A non-finite coordinate or length outside a NEW element (R-6.1.21). |
| R-6.3.4 | A Text value containing \n or \r. The writer MUST NOT split it (R-4.24.3). |
| R-6.3.5 | Direction length outside 1 ± 1e-6 (R-6.1.8). |
| R-6.3.6 | Legible on a class outside R-4.23.1. |
| R-6.3.7 | A (family, index) sorting-code pair that is not exactly one A plus one B in this document (R-5.6.3). Warn once per incomplete pair. |
| R-6.3.8 | Closed=true while the first and last vertex coincide (redundant closing vertex). |
| R-6.3.9 | Closed=true on a polyline with fewer than 3 vertices. |
| R-6.3.10 | A Tree point whose shapes are not exactly one trunk cylinder plus one crown cylinder/cone, or that repeats a @role (R-5.8.7). |
| R-6.3.11 | A geometry that R-4.21.1 requires to carry Annotation and does not, or that carries one where R-4.21.1 forbids it — determined from the producer-supplied Feature/Type against the §7.1 table, without the writer hard-coding a vocabulary (R-6.4.1). |
| R-6.3.12 | Any 1.1 key supplied for a feature whose Type is a frozen profile (R-2.2.3, R-7.0.2). The writer emits the key as given; the producer is at fault. |
| R-6.3.13 | An @name that is not snake_case. |
| R-6.3.14 | A geometry whose Feature/Type row in §7.1 requires Legible and omits it, or whose §7.1 Text cell reads —/none while Text is emitted (R-6.1.23, R-6.1.25). This is the same advisory §7.1 table lookup as R-6.3.11; the writer hard-codes no vocabulary (R-6.4.1). A writer that implements no such lookup performs no check here — these remain producer obligations. |
| R-6.3.15 | Legible=none while one or more Text elements are emitted (R-6.1.24). Detectable without Type, so every writer SHOULD check it. |
| R-6.3.16 | Legible=full while some Text contains ? or ?… (R-6.1.27). Advisory only — a ? may legitimately be part of the sign's own text; the writer never rewrites the value. |
6.4 Explicit non-validations
- R-6.4.1 The writer MUST NOT validate
Feature/Type,Line/Type,Spline/Type, orPolyline/Typeagainst a closed vocabulary, and MUST NOT branch its emission logic onFeature/Type. All features are treated uniformly. (§7 profile compliance is a producer obligation; warnings that reference §7.1 are advisory table lookups, not emission logic.) - R-6.4.2 The writer MUST ignore unknown dict keys silently.
<Attributes>is built only from an explicitattributesmapping (R-8.6.1); no other key contributes an attribute. - R-6.4.3 The writer MUST NOT derive
Annotation/TextfromShapes,Z, orguardrail_type, nor inferDirectionfrom geometry. - R-6.4.4 The writer MUST NOT validate coordinate ranges, geometry self-intersection, polygon orientation, or annotation spelling.
- R-6.4.5 The writer MUST NOT reorder producer-supplied lists (features, geometries, shapes,
Textlines). OnlyAttributechildren are sorted (R-4.28.1).
7. Feature-class profiles
- R-7.0.1 A profile is a producer obligation. Every rule in §7 constrains what a producer may emit for
that
Feature/Type; none of it changes how the writer serialises a document (R-6.4.1). - R-7.0.2 Producers MUST NOT supply any 1.1 key for a feature belonging to a frozen profile (§7.21–§7.23):
no
direction,shapes,annotation,attributes,closed,sorting_code(or thesort_class/pair_index/pair_roletriple), or polylinetype.layeris not a schema concept at all (R-2.2.4); like any unknown key it is silently ignored (R-6.4.2). Frozen-profile output stays byte-identical to 0.9.0 apart fromMetaData/SchemaVersion. - R-7.0.3 Unless a profile says otherwise, 1.1 profiles set
LaneID = "0"on the feature and on each of its geometry elements (they are not lane-bound) and omitRoadPoint. Frozen profiles keep their existingLaneIDandRoadPointsemantics — lane markings and cross sections carry real lane ids. - N-7.0.4 The CAD layer name for each class is not in the document. Appendix C lists the expected
Type→ layer mapping informatively so the consumer can build its catalog lookup.
7.1 Summary table
"Annot." = the required Annotation/Display value, or — when the class MUST NOT carry Annotation
(R-4.21.1). "Text" = whether label text travels in the document.
| § | Poster col | Feature/Type |
Container | Required geometry | Annot. | Text | Shapes | Attributes |
|---|---|---|---|---|---|---|---|---|
| 7.2 | 1a | Tree Line |
Splines | ≥2 control points | false |
— | — | — |
| 7.3 | 1b | Tree Group |
Polylines | ≥3 vertices, Closed=true |
false |
— | — | — |
| 7.4 | 2 | Bush |
Splines | ≥2 control points | false |
— | — | — |
| 7.5 | 3 | Tree |
Points | position, Direction |
true |
1 line | trunk + crown | opt. species_class |
| 7.6 | 4 | Fence |
Polylines | ≥2 vertices | false |
— | — | — |
| 7.7 | 5 | Paving Edge |
Polylines | ≥2 vertices | false |
— | — | — |
| 7.8 | 6 | noise_wall |
Polylines | ≥2 vertices | — | — | — | height_m, length_m |
| 7.9 | 7 top | Trench |
Polylines | ≥2 vertices, G<i>$A |
false |
— | — | — |
| 7.10 | 7 bottom | Trench Bottom |
Polylines | ≥2 vertices, G<i>$B |
false |
— | — | — |
| 7.11 | 8 | Slope |
Polylines ×2 | B<i>$A + B<i>$B |
false |
— | — | — |
| 7.12 | 9 | Pedestrian Road |
Polylines or Splines | ≥2 vertices / ctrl points | false |
— | — | — |
| 7.13 | 10 | Terrain Point |
Points | position, Direction |
true |
none (consumer renders Z) | — | — |
| 7.14 | 11 | Traffic Sign |
Points | position, Direction |
false |
— | opt. post cylinder | opt. height_m, plate_class |
| 7.15 | 12 | Shaft |
Points | position, Direction |
false |
— | — | — |
| 7.16 | 14 | Emergency Call Box |
Points | position, Direction |
false |
— | — | — |
| 7.17 | 16 | Gully |
Points | position, Direction |
false |
— | — | width_m |
| 7.18 | 15 | KM Sign |
Points | position, Direction |
true + Legible enum |
0..3 lines | — | km, section, station |
| 7.19 | 17 | Wall |
Polylines | ≥2 vertices | false |
— | — | height_m, length_m |
| 7.20 | 18 | guardrail (1.0 geom.) |
Polylines | as today | true |
1 line | — | guardrail_type, … |
| 7.21 | ✔ | Axis of the Edge, Center Lines — FROZEN |
Splines + Lines | as today | — | — | — | — |
| 7.22 | ✔ | Central Axis, Both-Sides Central Axis, Single-Side Central Axis — FROZEN |
Polylines | as today | — | — | — | — |
| 7.23 | 13 | Cross Section — FROZEN |
Points | as today | — | — | — | — |
| 7.24 | — | guardrail_support, guardrail_top_rail |
Polylines | as today | — | — | — | cadence / corridor keys |
| 7.25 | — | Verge Post |
Points | position, Direction |
false |
— | opt. cylinder | height_m, width_m |
| — | 7, 8 | Hatches | — | — | — | — | — | never exported; derived from the A/B pair |
7.2 Tree Line
One Feature per tree line, one Spline with Fit_Points empty and ≥2 Control_Points.
Annotation: <Display>false</Display>, no Text. Spline/Type, Width, RoadPoint omitted.
Crown symbols are placed by the consumer along the spline from its catalog.
7.3 Tree Group
Decision Q10 (ALT 1): the group outline is exported. One Feature per group; one Polyline with ≥3
vertices and <Closed>true</Closed>. Annotation Display=false. SortingCode omitted.
Status: specified, producer TBD — the outline comes from clustering tree detections, a later pipeline
step. Consumers may implement against this profile now; documents will not contain Tree Group features until
that detector ships.
7.4 Bush
As §7.2.
7.5 Tree
One Point. Direction REQUIRED (road-axis tangent, R-5.5.5).
Annotation Display=true, exactly one Text (N-5.7.3). Legible SHOULD NOT be emitted — the label is
computed, not read (R-4.23.2).
Shapes REQUIRED: exactly one role="trunk" type="cylinder" and exactly one
role="crown" type="cylinder"|"cone" (R-5.8.7). Trunk and crown diameters travel as Shape/Diameter; the
numeric attributes trunk_diameter_m / crown_diameter_m are not emitted (decision Q2).
Attributes MAY carry a future species_class.
7.6 Fence, 7.7 Paving Edge
One Polyline, ≥2 vertices, Annotation Display=false, no SortingCode, no Polyline/Type.
7.8 noise_wall
Decision Q7: the existing lower-case Type string noise_wall is kept (not renamed to Noise Wall), and the
geometry stays Polylines — the detector emits polylines; the poster's spline form is not followed.
No Annotation is emitted; the object-name label is deferred to post-processing (§10 O-17).
Attributes MAY carry height_m, length_m.
7.9 Trench / 7.10 Trench Bottom
Two separate Features per trench couple, tied by the pair index <i>: top = Trench with G<i>$A,
bottom = Trench Bottom with G<i>$B. Polyline/Type SHOULD be top / bottom respectively (human
readability; the code is the contract). Annotation Display=false.
The hatch between the outlines is derived by the consumer and MUST NOT be exported.
7.11 Slope
One Feature containing two Polylines: SortingCode B<i>$A with Type top, and B<i>$B with
Type bottom. Annotation Display=false.
Decision Q6: A/B plus the two outlines suffice to derive the hatch direction; no explicit downhill vector.
7.12 Pedestrian Road
One Feature per path, using either Polylines or Splines. The same path MUST NOT be emitted in
both forms. Annotation Display=false.
- R-7.12.1 The consumer selects the CAD layer from the container used (Polylines → pPLC_Gehweg,
Splines → sPLC_Gehweg); see Appendix C.
7.13 Terrain Point
One Point per elevation point. Direction REQUIRED and always emitted although catalog mode NO ignores
it (R-5.5.6). Annotation Display=true with no Text: the consumer renders the elevation label from
Point/Z per N-5.7.2 (decision Q4 revised). Legible SHOULD NOT be emitted.
7.14 Traffic Sign
One Point. Direction REQUIRED = plate normal facing oncoming traffic (R-5.5.5).
Annotation Display=false, no Text — plate text is not transcribed today. If plate transcription ships,
this class moves to the §7.18 pattern (Display=true + Legible) per R-4.23.1.
Shapes OPTIONAL: a producer MAY emit a role="post" type="cylinder" shape for the sign post.
Attributes MAY carry height_m, plate_class, direction_source.
7.15 Shaft, 7.16 Emergency Call Box, 7.17 Gully
One Point each. Direction REQUIRED (road-axis tangent). Annotation Display=false, no Text.
Gully Attributes MAY carry width_m.
- N-7.15.1 The poster's leftover Text km placeholder in column 16 is a copy-paste artefact and is not
reproduced.
7.18 KM Sign
One Point. Direction REQUIRED = plate normal facing oncoming traffic.
Annotation: Display=true; Legible REQUIRED with one of full | partial | none (§4.23) — sign text
is often only partly recoverable from the point cloud; Text 0..3 lines per R-5.7.4, marked per R-4.24.5
under partial. Under none the Text elements are omitted and Display stays true, so the symbol is
still placed.
Attributes SHOULD carry the machine values km (float), section (string/integer), station (float) when
known, so the consumer can re-format without a re-export (§10 O-19).
7.19 Wall
One Polyline, ≥2 vertices, Annotation Display=false, Attributes MAY carry height_m, length_m.
7.20 guardrail — annotation on existing geometry
No new geometry (poster col 18: "part of original XML"). The existing guardrail polyline gains:
Attributes with guardrail_type (machine value, e.g. EDSP, ESP 2.0) plus the migrated legacy keys of
N-8.6.3; and Annotation with Display=true and one Text = the drawn label (EDSP, ESP 2,0).
Decision Q8: the label source is our guardrail_type attribute; the legacy
FeatureClass=Guard Rails_<type> convention is a consumer-side fallback for 1.0 files only (N-2.5.4).
LaneID stays "0"; RoadPoint is kept as today.
7.21 Lane markings — FROZEN
Axis of the Edge and Center Lines: solid segments as Splines, dashed as Lines, exactly as today,
including per-segment Type and Width. Decision Q12: no NEW element of any kind. Output byte-identical to
0.9.0 except MetaData/SchemaVersion (R-2.4.1).
7.22 Road axes — FROZEN
Central Axis, Both-Sides Central Axis, Single-Side Central Axis as Polylines, exactly as today.
Polyline/RoadPoint remains present for these features. No NEW element.
7.23 Cross Section — FROZEN
Decision Q4 (revised): cross-section features are fine as they are. Type stays Cross Section; the 1.0
semantics of PointerNumber (cross-section plane index), RoadPoint (distance along road), Color,
FeatureClass, FeatureID are unchanged. No Direction, no Annotation, no Attributes.
The consumer renders the elevation label from Point/Z per N-5.7.2 and picks the layer from Type via
Appendix C.
7.24 guardrail_support, guardrail_top_rail
Unchanged geometry, no Annotation. Their stand-in attribute keys (parent_guardrail_id, post_spacing_m,
post_count, post_confidence, halfwidth_m, lateral_offset_m) MUST be emitted through the uniform
<Attribute name= type=> form of §4.28 — this is what makes them survive the Nexus exporter.
7.25 Verge Post
Excel-only class (Randpfosten), not on the poster. Standard point-symbol profile: Direction REQUIRED,
Annotation Display=false, Attributes height_m, width_m; MAY carry a role="post" type="cylinder"
Shape. Class name and layer are provisional (§10 O-16).
8. Producer contract (create_xml(data))
8.1 Top level
data = {
"metadata": {"creation_date": str, # REQUIRED
"source": str, # REQUIRED
"software_version": str, # REQUIRED
"pipeline_versions": [...], # OPTIONAL, 1.0 shape unchanged
"schema_version": str}, # OPTIONAL, must be "1.1"
"geoshift": {"x": float, "y": float, "z": float}, # OPTIONAL
"features": [ feature, ... ], # REQUIRED (MAY be empty)
}
pipeline_versions entry: {"step": str, "date": str?, "packages": {name: version}} or
{"step": str, "segment_ranges": [{"segments": [[a, b], ...], "date": str, "packages": {...}}]} (§4.4).
8.2 feature
| Key | Type | Req. | Default / note |
|---|---|---|---|
type |
str | yes | → Feature/Type |
id |
str | yes | document-unique |
lane_id |
str | yes | "0" when not lane-bound |
attributes |
Mapping | no | → Feature/Attributes, last child |
lines / points / polylines / splines |
list | no | absent ⇒ empty container + count 0 |
There is no layer key (R-2.2.4). A layer key, if supplied, is ignored (R-6.4.2).
8.3 point
| Key | Type | Req. | Default / note |
|---|---|---|---|
id |
str | yes | |
position |
[x, y, z] |
yes | export frame, metres → X,Y,Z |
lane_id |
str | legacy yes | absent ⇒ owning Feature/LaneID for 1.1-touched geometry (R-4.11.1); "0" for 1.1 profiles |
pointer_number |
int | no | absent ⇒ 1-based index within the container (R-4.17.1) |
color |
any | no | absent ⇒ empty text; present as None ⇒ text None (R-4.17.2) |
feature_class |
str | no | absent ⇒ owning Feature/Type; present None ⇒ text None |
feature_id |
str | no | absent ⇒ owning Feature/ID; present None ⇒ text None |
road_point |
float | no | element omitted when absent |
direction |
[dx, dy, dz] |
1.1 point classes | unit, finite, non-zero; dz MAY be non-zero |
shapes |
list of shape dicts | no | §8.7 |
annotation |
dict | per R-4.21.1 | §8.8 |
attributes |
Mapping | no |
8.4 polyline
| Key | Type | Req. | Default / note |
|---|---|---|---|
id |
str | yes | |
vertices |
list of [x,y,z] |
yes | ≥2 for 1.1 profiles (R-3.7); vertex ids generated |
lane_id |
str | legacy yes | absent ⇒ owning Feature/LaneID for 1.1-touched geometry (R-4.11.1); "0" for 1.1 profiles |
type |
str | no | → Polyline/Type (open string) |
road_point |
[from, to] |
no | element omitted when absent |
sorting_code |
str | no | XOR with the triple below |
sort_class, pair_index, pair_role |
str, int, top|bottom |
no | composed per R-5.6.5, all-or-none |
closed |
bool | no | → Polyline/Closed |
annotation |
dict | per R-4.21.1 | |
attributes |
Mapping | no |
8.5 spline, line, width
spline: id (req), control_points (req; ≥2 for 1.1 profiles), fit_points (req in 1.0, default [] for
1.1 producers), type, road_point, width, annotation, attributes.
line: 1.0 keys unchanged — id, start_point, end_point, road_point (all required), type
(optional, open string), width (optional) — plus OPTIONAL annotation, attributes.
lane_id on a spline or line: as R-4.11.1 — required for legacy dicts, defaulted from the feature for
1.1-touched geometry.
width dict → <Width>; each child is emitted only when its source value is not None:
| Key | Element | Note |
|---|---|---|
width |
Value |
str(v) |
sigma_width |
Sigma |
str(v) |
status |
Status |
str(v) |
flagged |
Flagged |
str(bool(v)).lower() |
Other keys carried in the same dict (center, blur_left, blur_right, n_points) are ignored (R-8.10.1).
8.6 Attributes are explicit only
- R-8.6.1 The writer MUST build
<Attributes>only from an explicitattributesmapping on thefeature,point,polyline,spline, orlinedict. No other dict key contributes an<Attribute>. - R-8.6.2 There is no legacy-key adapter. Top-level keys that §8 does not list are ignored silently
(R-6.4.2), including the guardrails stand-in's polyline keys. Deriving attributes from them would add an
<Attributes>element to output that 0.9.0 produced without one, breaking R-2.4.1. - N-8.6.3 (informative — guardrails migration) The guardrails producer adopts 1.1 by moving these
polyline keys into
attributes={...}:side,guardrail_type,faces,length_m,feature_type,width_m,top_height_p90_m,parent_guardrail_id,post_spacing_m,post_count,post_confidence,halfwidth_m— plus any further key it wants to carry (e.g.lateral_offset_m). Until it does, those values simply do not reach the document, exactly as under 0.9.0. - N-8.6.4 (informative — stand-in tag names) The stand-in exporter derives its tag name as
key.replace("_", "").title(), which yields<Guardrailtype>,<Topheightp90m>, and<Postspacingm name="post_spacing_m">. Those tags are not lossy — the canonical key rides in@namefor the longer fields — but they are non-uniform, forcing a per-key reader mapping. The uniform<Attribute name= type=>form of §4.28 replaces them; this writer never emits them.
8.7 shape dict
{"role": str, "type": "cylinder"|"cone",
"base": [x, y, z],
"top": [x, y, z], # cylinder only
"apex": [x, y, z], # cone only
"diameter": float}
- R-8.7.1 Exactly one of
top/apexMUST be supplied, matchingtype(R-4.20.3). - R-8.7.2 Order within
shapesis preserved.
8.8 annotation dict
{"display": bool, "legible": "full" | "partial" | "none" | None, "text": str | list[str] | None}.
- R-8.8.1 A str text yields one Text; a list[str] yields one Text per element in order;
None or [] yields none.
- R-8.8.2 legible is a str from the enumeration of R-5.10.1; absent or None ⇒ Legible not
emitted. A bool is not accepted (R-6.2.16).
- R-8.8.3 attributes is Mapping[str, str | int | float | bool | None].
- N-8.8.4 (informative — numpy scalars) Wherever §8 declares an int, a float or a bool, the numpy
scalar equivalents (bool_, integer, floating) are accepted and mapped to boolean/integer/float —
boolean first, so np.bool_ never becomes an integer (R-5.9.1) — and are serialised exactly as the
equivalent Python scalar would be (R-5.1.1, R-5.2.1).
8.9 Producer responsibilities (not the writer's)
- R-8.9.1 Coordinates in the geoshifted local frame, metres.
- R-8.9.2 Unit direction vectors.
- R-8.9.3 All display formatting of
Annotation/Text(decimal comma,Ø=, line split). - R-8.9.4 Globally unique, non-empty ids.
- R-8.9.5 Sorting-code pair completeness across the features it contributes.
- R-8.9.6 §7 profile compliance, including R-7.0.2 (no 1.1 key on a frozen profile).
8.10 What the writer ignores
- R-8.10.1 Any dict key not listed in §8 is ignored silently (e.g. the
widthdict'scenter,blur_left,blur_right,n_points; a straylayer). - R-8.10.2 The writer MUST NOT infer any value from
Feature/Type(R-6.4.1). - R-8.10.3 The writer MUST NOT derive
Annotation/TextfromShapes,Z, orguardrail_type(R-6.4.3).
9. Complete examples
All examples conform to §§3–8. Whitespace is prettify_xml's (two-space indent).
9.1 Point symbols — Tree with Shapes, KM Sign with Legible
<?xml version="1.0" encoding="UTF-8"?>
<HighwayData>
<MetaData>
<CreationDate>2026-08-28T09:12:00</CreationDate>
<Source>hbw_detectors</Source>
<SoftwareVersion>0.10.0</SoftwareVersion>
<SchemaVersion>1.1</SchemaVersion>
</MetaData>
<Geoshift>
<X>2600000.0</X>
<Y>1200000.0</Y>
<Z>400.0</Z>
</Geoshift>
<Features>
<Feature>
<Type>Tree</Type>
<ID>TREE-12</ID>
<LaneID>0</LaneID>
<Lines/>
<NumberOfLines>0</NumberOfLines>
<Points>
<Point>
<ID>TREE-12-1</ID>
<LaneID>0</LaneID>
<PointerNumber>1</PointerNumber>
<X>-109.06</X>
<Y>-383.29</Y>
<Z>0.02</Z>
<Color/>
<FeatureClass>Tree</FeatureClass>
<FeatureID>TREE-12</FeatureID>
<Direction>
<X>0.974467</X>
<Y>-0.224529</Y>
<Z>0.0</Z>
</Direction>
<Shapes>
<Shape role="trunk" type="cylinder">
<Base>
<X>-109.06</X>
<Y>-383.29</Y>
<Z>0.02</Z>
</Base>
<Top>
<X>-109.04</X>
<Y>-383.27</Y>
<Z>2.52</Z>
</Top>
<Diameter>0.3</Diameter>
</Shape>
<Shape role="crown" type="cone">
<Base>
<X>-109.04</X>
<Y>-383.27</Y>
<Z>2.52</Z>
</Base>
<Apex>
<X>-109.01</X>
<Y>-383.22</Y>
<Z>8.52</Z>
</Apex>
<Diameter>7.0</Diameter>
</Shape>
</Shapes>
<Annotation>
<Display>true</Display>
<Text>Ø= 0,30/7,0m</Text>
</Annotation>
</Point>
</Points>
<NumberOfPoints>1</NumberOfPoints>
<Polylines/>
<NumberOfPolylines>0</NumberOfPolylines>
<Splines/>
<NumberOfSplines>0</NumberOfSplines>
</Feature>
<Feature>
<Type>KM Sign</Type>
<ID>KMS-3</ID>
<LaneID>0</LaneID>
<Lines/>
<NumberOfLines>0</NumberOfLines>
<Points>
<Point>
<ID>KMS-3-1</ID>
<LaneID>0</LaneID>
<PointerNumber>1</PointerNumber>
<X>-88.31</X>
<Y>-401.77</Y>
<Z>1.92</Z>
<Color/>
<FeatureClass>KM Sign</FeatureClass>
<FeatureID>KMS-3</FeatureID>
<Direction>
<X>-0.221946</X>
<Y>-0.963767</Y>
<Z>0.147964</Z>
</Direction>
<Annotation>
<Display>true</Display>
<Legible>partial</Legible>
<Text>KM 46.5</Text>
<Text>Abs. ??</Text>
</Annotation>
<Attributes>
<Attribute name="direction_source" type="string">measured</Attribute>
</Attributes>
</Point>
</Points>
<NumberOfPoints>1</NumberOfPoints>
<Polylines/>
<NumberOfPolylines>0</NumberOfPolylines>
<Splines/>
<NumberOfSplines>0</NumberOfSplines>
</Feature>
</Features>
</HighwayData>
Notes: the KM Sign direction is a tilted plate normal (Z != 0, R-5.5.1). Legible=partial — the
kilometre line was read in full, two characters of the section line were not and are marked ??
(R-4.24.5), and the station line was entirely unreadable and is therefore omitted (R-4.23.1, §7.18).
The same sign with nothing readable at all carries no Text, and Display stays true so the symbol is
still placed (R-4.23.3):
<Annotation>
<Display>true</Display>
<Legible>none</Legible>
</Annotation>
9.2 Paired polylines — trench top + bottom (G4$A / G4$B)
<?xml version="1.0" encoding="UTF-8"?>
<HighwayData>
<MetaData>
<CreationDate>2026-08-28T09:12:00</CreationDate>
<Source>hbw_detectors</Source>
<SoftwareVersion>0.10.0</SoftwareVersion>
<SchemaVersion>1.1</SchemaVersion>
</MetaData>
<Features>
<Feature>
<Type>Trench</Type>
<ID>TRN-4-TOP</ID>
<LaneID>0</LaneID>
<Lines/>
<NumberOfLines>0</NumberOfLines>
<Points/>
<NumberOfPoints>0</NumberOfPoints>
<Polylines>
<Polyline>
<ID>TRN-4-TOP-1</ID>
<LaneID>0</LaneID>
<Type>top</Type>
<Vertices>
<Vertex>
<ID>TRN-4-TOP-1-1</ID>
<X>10.0</X>
<Y>0.0</Y>
<Z>1.2</Z>
</Vertex>
<Vertex>
<ID>TRN-4-TOP-1-2</ID>
<X>18.0</X>
<Y>0.4</Y>
<Z>1.25</Z>
</Vertex>
</Vertices>
<SortingCode>G4$A</SortingCode>
<Annotation>
<Display>false</Display>
</Annotation>
</Polyline>
</Polylines>
<NumberOfPolylines>1</NumberOfPolylines>
<Splines/>
<NumberOfSplines>0</NumberOfSplines>
</Feature>
<Feature>
<Type>Trench Bottom</Type>
<ID>TRN-4-BOT</ID>
<LaneID>0</LaneID>
<Lines/>
<NumberOfLines>0</NumberOfLines>
<Points/>
<NumberOfPoints>0</NumberOfPoints>
<Polylines>
<Polyline>
<ID>TRN-4-BOT-1</ID>
<LaneID>0</LaneID>
<Type>bottom</Type>
<Vertices>
<Vertex>
<ID>TRN-4-BOT-1-1</ID>
<X>10.2</X>
<Y>-1.1</Y>
<Z>0.35</Z>
</Vertex>
<Vertex>
<ID>TRN-4-BOT-1-2</ID>
<X>18.1</X>
<Y>-0.7</Y>
<Z>0.4</Z>
</Vertex>
</Vertices>
<SortingCode>G4$B</SortingCode>
<Annotation>
<Display>false</Display>
</Annotation>
</Polyline>
</Polylines>
<NumberOfPolylines>1</NumberOfPolylines>
<Splines/>
<NumberOfSplines>0</NumberOfSplines>
</Feature>
</Features>
</HighwayData>
9.3 Spline — Tree Line
<?xml version="1.0" encoding="UTF-8"?>
<HighwayData>
<MetaData>
<CreationDate>2026-08-28T09:12:00</CreationDate>
<Source>hbw_detectors</Source>
<SoftwareVersion>0.10.0</SoftwareVersion>
<SchemaVersion>1.1</SchemaVersion>
</MetaData>
<Features>
<Feature>
<Type>Tree Line</Type>
<ID>TRL-2</ID>
<LaneID>0</LaneID>
<Lines/>
<NumberOfLines>0</NumberOfLines>
<Points/>
<NumberOfPoints>0</NumberOfPoints>
<Polylines/>
<NumberOfPolylines>0</NumberOfPolylines>
<Splines>
<Spline>
<ID>TRL-2-1</ID>
<LaneID>0</LaneID>
<Fit_Points/>
<Control_Points>
<Point>
<ID>TRL-2-1_1</ID>
<X>-20.0</X>
<Y>7.5</Y>
<Z>0.9</Z>
</Point>
<Point>
<ID>TRL-2-1_2</ID>
<X>-8.0</X>
<Y>8.1</Y>
<Z>0.95</Z>
</Point>
<Point>
<ID>TRL-2-1_3</ID>
<X>4.0</X>
<Y>8.4</Y>
<Z>1.0</Z>
</Point>
</Control_Points>
<Annotation>
<Display>false</Display>
</Annotation>
</Spline>
</Splines>
<NumberOfSplines>1</NumberOfSplines>
</Feature>
</Features>
</HighwayData>
9.4 Closed polyline — Tree Group (profile specified, producer TBD)
<?xml version="1.0" encoding="UTF-8"?>
<HighwayData>
<MetaData>
<CreationDate>2026-08-28T09:12:00</CreationDate>
<Source>hbw_detectors</Source>
<SoftwareVersion>0.10.0</SoftwareVersion>
<SchemaVersion>1.1</SchemaVersion>
</MetaData>
<Features>
<Feature>
<Type>Tree Group</Type>
<ID>TGR-1</ID>
<LaneID>0</LaneID>
<Lines/>
<NumberOfLines>0</NumberOfLines>
<Points/>
<NumberOfPoints>0</NumberOfPoints>
<Polylines>
<Polyline>
<ID>TGR-1-1</ID>
<LaneID>0</LaneID>
<Vertices>
<Vertex>
<ID>TGR-1-1-1</ID>
<X>30.0</X>
<Y>12.0</Y>
<Z>1.1</Z>
</Vertex>
<Vertex>
<ID>TGR-1-1-2</ID>
<X>44.0</X>
<Y>14.5</Y>
<Z>1.2</Z>
</Vertex>
<Vertex>
<ID>TGR-1-1-3</ID>
<X>41.0</X>
<Y>25.0</Y>
<Z>1.15</Z>
</Vertex>
<Vertex>
<ID>TGR-1-1-4</ID>
<X>28.5</X>
<Y>22.0</Y>
<Z>1.05</Z>
</Vertex>
</Vertices>
<Closed>true</Closed>
<Annotation>
<Display>false</Display>
</Annotation>
</Polyline>
</Polylines>
<NumberOfPolylines>1</NumberOfPolylines>
<Splines/>
<NumberOfSplines>0</NumberOfSplines>
</Feature>
</Features>
</HighwayData>
9.5 Guard-rail annotation on an existing guardrail polyline
<?xml version="1.0" encoding="UTF-8"?>
<HighwayData>
<MetaData>
<CreationDate>2026-08-28T09:12:00</CreationDate>
<Source>guardrails</Source>
<SoftwareVersion>0.10.0</SoftwareVersion>
<SchemaVersion>1.1</SchemaVersion>
</MetaData>
<Features>
<Feature>
<Type>guardrail</Type>
<ID>GR-7</ID>
<LaneID>0</LaneID>
<Lines/>
<NumberOfLines>0</NumberOfLines>
<Points/>
<NumberOfPoints>0</NumberOfPoints>
<Polylines>
<Polyline>
<ID>GR-7-1</ID>
<LaneID>0</LaneID>
<RoadPoint>
<From>120.0</From>
<To>184.5</To>
</RoadPoint>
<Vertices>
<Vertex>
<ID>GR-7-1-1</ID>
<X>0.0</X>
<Y>-6.2</Y>
<Z>0.75</Z>
</Vertex>
<Vertex>
<ID>GR-7-1-2</ID>
<X>64.5</X>
<Y>-6.4</Y>
<Z>0.78</Z>
</Vertex>
</Vertices>
<Annotation>
<Display>true</Display>
<Text>ESP 2,0</Text>
</Annotation>
<Attributes>
<Attribute name="faces" type="string">right</Attribute>
<Attribute name="guardrail_type" type="string">ESP 2.0</Attribute>
<Attribute name="length_m" type="float">64.5</Attribute>
<Attribute name="side" type="string">right</Attribute>
<Attribute name="top_height_p90_m" type="float">0.78</Attribute>
</Attributes>
</Polyline>
</Polylines>
<NumberOfPolylines>1</NumberOfPolylines>
<Splines/>
<NumberOfSplines>0</NumberOfSplines>
</Feature>
</Features>
</HighwayData>
Attribute children are sorted by @name (R-4.28.1).
9.6 Legacy cross-section feature under 1.1 — frozen profile §7.23
Produced by an unmodified legacy producer. The tree is exactly the 1.0 shape; the only difference from 0.9.0
output is the SchemaVersion line (R-2.4.1).
<?xml version="1.0" encoding="UTF-8"?>
<HighwayData>
<MetaData>
<CreationDate>2026-08-28T09:12:00</CreationDate>
<Source>cluster_stepper</Source>
<SoftwareVersion>0.10.0</SoftwareVersion>
<SchemaVersion>1.1</SchemaVersion>
</MetaData>
<Features>
<Feature>
<Type>Cross Section</Type>
<ID>CS-left</ID>
<LaneID>1</LaneID>
<Lines/>
<NumberOfLines>0</NumberOfLines>
<Points>
<Point>
<ID>CS-left-1</ID>
<LaneID>1</LaneID>
<PointerNumber>0</PointerNumber>
<X>-3.51</X>
<Y>0.0</Y>
<Z>0.12</Z>
<Color>None</Color>
<FeatureClass>None</FeatureClass>
<FeatureID>None</FeatureID>
<RoadPoint>0.0</RoadPoint>
</Point>
<Point>
<ID>CS-left-2</ID>
<LaneID>1</LaneID>
<PointerNumber>1</PointerNumber>
<X>-3.49</X>
<Y>1.0</Y>
<Z>0.13</Z>
<Color>None</Color>
<FeatureClass>None</FeatureClass>
<FeatureID>None</FeatureID>
<RoadPoint>1.0</RoadPoint>
</Point>
</Points>
<NumberOfPoints>2</NumberOfPoints>
<Polylines/>
<NumberOfPolylines>0</NumberOfPolylines>
<Splines/>
<NumberOfSplines>0</NumberOfSplines>
</Feature>
</Features>
</HighwayData>
The literal None texts are the 1.0 str() coercion of present-but-None keys (R-4.17.2); PointerNumber
is the cross-section plane index, not a 1-based counter (frozen semantics, §7.23). The consumer renders each
point's elevation label from Z (N-5.7.2) and resolves the layer from Type (Appendix C).
10. Open items
10.1 Still open
Each item states the spec's default behaviour (already normative above) and the decider. None blocks implementation.
| # | Item | Source | Default in this spec | Decider |
|---|---|---|---|---|
| O-14 | Emit direction_source="fallback" when plate orientation is unknown? As an Attribute entry or an XML attribute on <Direction>? |
Q5 | Emit, as <Attribute name="direction_source"> (R-5.5.7) |
Miro |
| O-15 | Tree label glyph (Ø vs ⌀), spacing, precision, decimal comma |
Q2 | Ø= 0,30/7,0m reference rendering, producer-formatted (N-5.7.3, N-5.3.6) |
HBW |
| O-16 | Verge Post class name and its informative layer PLC_Randpfosten |
Excel only | Provisional (§7.25, Appendix C) | HBW |
| O-17 | Noise-wall "object name" annotation content | Q7 | No Annotation emitted (§7.8) |
HBW |
| O-18 | Which Grasshopper XML component HBW uses; tolerance of unknown trailing children | Q3 | Assume name-based; one sample-file acceptance run before release (N-2.3.4) | HBW |
| O-19 | KM-sign km / section / station source (our chainage vs HBW reference axis) |
Q1 | Attributes carry whatever the producer knows; unknown lines omitted (R-5.7.4) | HBW |
| O-20 | Confirm HBW renders elevation labels from Z per N-5.7.2 (absolute, 3 decimals, dot) |
Q4/Q9 revised | Consumer-side rendering; no Text in the document (§7.13, §7.23) |
HBW |
| O-21 | Appendix C layer names — confirm the full Type → layer table with HBW |
Q3 revised | Poster/Excel values as listed | HBW |
| O-22 | Spline definition is underspecified for exact 3D reconstruction: Fit_Points/Control_Points are given but degree, knots and weights are not (pre-existing 1.0 gap). |
3D-model review | No rule; consumer and producer assume the same convention (informative) | Miro / HBW |
| O-23 | 3D-model completeness: heights are optional attributes (height_m walls/noise walls), symbol classes (shaft, gully, call box, sign plate) have no extents, no surface concept beyond Shape. Add Shapes / mandatory heights if a 3D model becomes a deliverable. |
3D-model review | Not required by the 2D-plan contract | Miro |
10.2 Closed items (IDs are never reused)
Revision 1 of this spec numbered its open items O-1…O-12. Those IDs are retired: five were resolved in
round 2, and the seven that stayed open were re-issued as O-13…O-21 in §10.1 rather than renumbered in place.
O-13 has since been closed by the Legible enumeration decision, leaving O-14…O-21 open.
| Retired ID | Item | Disposition |
|---|---|---|
| O-1 | Emit Legible on all point annotations, or only object-transcribed ones? |
Resolved — object-transcribed classes only (R-4.23.1). |
| O-2 | <Text/> vs omit when not legible |
Re-issued as O-13, now resolved — Text is omitted under Legible=none (R-4.23.3). |
| O-3 | direction_source="fallback" marker |
Still open → re-issued as O-14. |
| O-4 | Multi-file merge sorting index segment*1000 + i |
Resolved — dropped; unique per family per document (R-5.6.2). |
| O-5 | Cone apex up | Resolved — moot; Apex is an explicit 3D point (R-4.20.2). |
| O-6 | Tree label glyph / precision | Still open → re-issued as O-15. |
| O-7 | Q4's "nothing else changes" for Cross Section vs poster column 13 |
Resolved — Cross Section fully frozen; the consumer renders the label from Z (§7.23). |
| O-8 | Verge Post layer name |
Still open → re-issued as O-16. |
| O-9 | Noise-wall object-name annotation | Still open → re-issued as O-17. |
| O-10 | Which Grasshopper XML component HBW uses | Still open → re-issued as O-18. |
| O-11 | KM-sign km/section/station source |
Still open → re-issued as O-19. |
| O-12 | Generic <Shape> vs tree-specific tags |
Resolved — generic (R-5.8.6). |
| O-13 | <Text/> vs omit when not legible (re-issue of O-2) |
Resolved — omit; superseded by the Legible enumeration, where none forbids Text (R-4.23.3). |
O-20 (confirm consumer-side elevation rendering) and O-21 (confirm the Appendix C layer table) are new in revision 2 and have no revision-1 predecessor.
Appendix A — 1.0 → 1.1 change list
A.1 New elements
| Element | Parent | Position |
|---|---|---|
SchemaVersion |
MetaData |
last |
Attributes / Attribute |
Feature, Point, Polyline, Spline, Line |
last child of parent |
Direction (X,Y,Z) |
Point |
after RoadPoint |
Shapes / Shape (Base, Top|Apex, Diameter) |
Point |
after Direction |
Annotation (Display, Legible enum, Text*) |
Point, Polyline, Spline, Line |
before Attributes |
Type |
Polyline |
after LaneID (exception R-2.3.2) |
SortingCode |
Polyline |
after Vertices |
Closed |
Polyline |
after SortingCode |
No Feature/Layer (R-2.2.4).
A.2 Relaxations (1.1 producers only; legacy output unchanged)
Point/RoadPoint,Polyline/RoadPoint,Spline/RoadPointbecome 0..1.Spline/Fit_PointsMAY be empty; the container is still always emitted.Point/PointerNumber,Color,FeatureClass,FeatureIDgain writer defaults for absent keys (R-4.17.1); a key present with valueNonekeeps its 1.0str()behaviour (R-4.17.2). All four are still always emitted.
A.3 Removed / rejected
- The stand-in exporter's derived tags (
<Guardrailtype>,<Postspacingm name="post_spacing_m">) are not part of 1.1 (N-8.6.4). They are not lossy — the canonical key rides in@namefor the longer fields — but they are non-uniform, requiring a per-key reader mapping;<Attribute name= type=>replaces them. - The guardrails legacy-key adapter proposed in earlier drafts is removed: emitting
<Attributes>from top-level polyline keys would break R-2.4.1 byte-identity. Attributes come only fromattributes(R-8.6.1); the producer migrates per N-8.6.3. ExportNote(stand-in only) is not part of any schema version.Feature/Layer, present in earlier drafts of this spec, is removed (R-2.2.4).
A.4 Where the decisions override the plan
| Plan said | Decision | Effect |
|---|---|---|
Tree carries trunk_diameter_m / crown_diameter_m attributes |
Q2 | 3D solids instead: <Shapes>/<Shape> (§4.19–4.20, §5.8, §7.5). The numeric attributes are not emitted. |
<Annotation> = Display + Text*, "Text required when Display=true" |
Q1 | Adds <Legible>; Text may be absent while Display=true (R-4.24.1). |
Tree Group not exported |
Q10 | Exported as a closed outline polyline; new Polyline/Closed (§7.3, §4.27). |
Rename noise_wall → Noise Wall, maybe splines |
Q7 | Keep noise_wall, keep polylines, no annotation (§7.8). |
Add Layer to lane-marking features |
Q12 | Lane/axis features frozen (§7.21, §7.22, R-7.0.2). |
| Q9 open (local vs absolute Z, comma vs dot) | Q9 | Absolute, 3 decimals, dot — as consumer guidance (N-5.7.2). |
| Q11 open (index scope, >9 pairs) | Q11 | Unique per family per document (R-5.6.2). |
| Q3 possible compatibility export mode | Q3 | No compat flag; acceptance run instead (R-2.4.4). |
| Q5 direction semantics open | Q5 | Unit vector, finite, non-zero, always emitted (§5.5). |
| Q6 possible explicit downhill vector | Q6 | A/B + outlines suffice (§7.11). |
| Q8 label source open | Q8 | guardrail_type attribute + Annotation/Text (§7.20). |
A.5 Where round 2 overrides round 1
| Round 1 | Round 2 | Effect |
|---|---|---|
<Feature/Layer> carries the customer catalog key; consumer falls back Layer → Type |
Q3 revised: the layer is a CAD artefact, constant per class, derivable from Type |
Layer removed entirely (R-2.2.4); Type is the only class key (R-2.5.1); Appendix C is informative |
Cross Section gains Layer, Direction, per-point Annotation |
Q4 revised: fine as it is | Fully frozen (§7.23, R-2.2.3); the revision-1 open item on Cross Section's scope is closed |
Elevation label text emitted by the producer (47.333) |
Q4/Q9 revised: consumer renders it from Z |
Terrain Point and Cross Section carry no Text; format is informative (N-5.7.2, §7.13) |
Direction lies in the XY plane, Z MUST be 0 |
Q5 revised | Full 3D unit vector, Z MAY be non-zero (R-5.5.1) |
Shape = Diameter + Height + BaseZ, vertical axis, cone apex up |
Q2 revised: lock position in 3D explicitly | Base + Top|Apex + Diameter; any orientation; BaseZ/Height and the vertical-axis and apex-up rules removed (§4.20, §5.8) |
Legible scope open (all annotations vs object-transcribed) |
Q1 final | Object-transcribed classes only (R-4.23.1) |
Sorting index may later become segment*1000 + i |
Q11 final | Dropped; per-family-per-document uniqueness only (R-5.6.2) |
Tree Group producer implied near-term |
Q10 final | Profile retained, marked "specified, producer TBD" (§7.3) |
A.6 Later owner decisions (after round 2)
| Was | Decision | Effect |
|---|---|---|
Annotation/Legible is a boolean (true/false) |
Legibility is not binary — sign text is frequently only partly recoverable | Legible becomes the enumeration full | partial | none (§4.23, R-5.10.1); none replaces the old false and forbids Text; partial emits the recovered lines with ? / ?… substitution marks (R-4.24.5). Open item O-13 closes. |
Appendix B — poster → XML field mapping
B.1 Point classes (poster cols 3, 10, 11, 12, 13, 14, 15, 16; + verge post)
| Poster index | Poster item | XML |
|---|---|---|
| 0 | layer name | not in the document — consumer derives it from Feature/Type (Appendix C) |
| 1 | X coordinate | Point/X |
| 2 | Y coordinate | Point/Y |
| 3 | Z coordinate | Point/Z |
| 4 | vector — direction of symbol in drawing | Point/Direction/X, /Y, /Z (3D; project to XY for a plan rotation) |
| 5 | has annotation display | Point/Annotation/Display |
| 6 | annotation text | Point/Annotation/Text (one per line; absent for consumer-rendered labels) |
| — | (1.1 addition, no poster index) | Point/Annotation/Legible — enumeration full | partial | none (§4.23) |
| — | (1.1 addition, no poster index) | Point/Shapes/Shape |
| — | (1.1 addition, no poster index) | Point/Attributes/Attribute |
B.2 Polyline classes (poster cols 4, 5, 7, 8, 9, 17; + tree group)
| Poster index | Poster item | XML |
|---|---|---|
| 0 | layer name | consumer-derived from Feature/Type (+ container for Pedestrian Road) |
| 1 | has a description | Polyline/Annotation/Display |
| 2 | sorting code (or null) | Polyline/SortingCode (omitted = null) |
| 3..n | polyline point coordinates (XYZ) | Polyline/Vertices/Vertex/X, /Y, /Z |
| — | (1.1 addition) | Polyline/Type, Polyline/Closed, Polyline/Attributes |
B.3 Spline classes (poster cols 1a, 2, 6, 9)
| Poster index | Poster item | XML |
|---|---|---|
| 0 | layer name | consumer-derived from Feature/Type (+ container for Pedestrian Road) |
| 1 | has a description | Spline/Annotation/Display |
| 2..n | spline control point coordinates (XYZ) | Spline/Control_Points/Point/X, /Y, /Z |
| — | (always emitted, may be empty) | Spline/Fit_Points |
B.4 Annotation-only / special (poster cols 7, 8 hatches; col 18 guard rails; col 1b group symbol)
| Poster item | XML |
|---|---|
| Trench / slope hatches — "no input data necessary" | nothing exported; derived from the SortingCode A/B pair |
Guard-rail labels EDSP, ESP 2,0 |
Polyline/Annotation/Text + Attribute name="guardrail_type" on the existing guardrail polyline |
| Tree group symbol — "no input geometry" | superseded by decision Q10: closed Tree Group polyline (§7.3) |
| Elevation labels (cols 10, 13) | no Text in the document; consumer renders from Point/Z (N-5.7.2) |
| Data catalog (block name, rotation mode) | never in the XML (R-1.1.5) |
Appendix C — Feature/Type → CAD layer name (INFORMATIVE)
This table is not normative and not part of the document. The layer name is a drawing artefact; it is
constant per feature class and cannot be derived from point-cloud data, so it lives on the consumer side.
The table lets HBW build its Type → layer → Data-catalog lookup independently. Values come from the poster
and the Excel catalog; confirm before release (§10 O-21).
Feature/Type |
Container | Expected layer | Poster col |
|---|---|---|---|
Tree Line |
Splines | sPLC_Laubbaumreihe |
1a |
Tree Group |
Polylines | Nutzungsart |
1b |
Bush |
Splines | sPLC_Hecke |
2 |
Tree |
Points | PLC_Laubbaum |
3 |
Fence |
Polylines | pPLC_Zaun |
4 |
Paving Edge |
Polylines | pPLC_Pflasterkante |
5 |
noise_wall |
Polylines | pPLC_Lärmschutzwand |
6 |
Trench |
Polylines | pPLC_Graben |
7 top |
Trench Bottom |
Polylines | pPLC_Grabensohle |
7 bottom |
Slope |
Polylines | pPLC_Böschung |
8 |
Pedestrian Road |
Polylines | pPLC_Gehweg |
9 |
Pedestrian Road |
Splines | sPLC_Gehweg |
9 |
Terrain Point |
Points | PLC_Geländepunkte |
10 |
Traffic Sign |
Points | PLC_Verkehrszeichen |
11 |
Shaft |
Points | PLC_Schacht |
12 |
Cross Section |
Points | PLC_Punkte_Symbole |
13 |
Emergency Call Box |
Points | PLC_Notrufsäule |
14 |
KM Sign |
Points | PLC_Kilometer-Tafeln |
15 |
Gully |
Points | PLC_Gullys |
16 |
Wall |
Polylines | pPLC_Mauerwerk |
17 |
guardrail, guardrail_support, guardrail_top_rail |
Polylines | Schutzeinrichtung |
18 |
Verge Post |
Points | PLC_Randpfosten (provisional, O-16) |
— |
Axis of the Edge |
Splines + Lines | Fahrbahnmarkierung_durchgezogen |
✔ |
Center Lines |
Splines + Lines | Fahrbahnmarkierung_gestrichelt |
✔ |
Central Axis, Both-Sides Central Axis, Single-Side Central Axis |
Polylines | Fahrbahnrand (to confirm) |
✔ |
- N-C.1
Pedestrian Roadis the only class whose layer depends on the container as well as theType. - N-C.2 Hatches (
Schraffur_Böschungand the trench hatch) have noFeature/Type; the consumer derives them from theSortingCodeA/Bpairs.