Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions packages/drawtonomy-sdk/__tests__/exporter/odrToShapes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -663,3 +663,39 @@ describe('esmini parking_demo (fixture)', () => {
}
)
})

describe('odrToShapes signal heading', () => {
const SIGNAL_HEADING_MAP = `<?xml version="1.0"?>
<OpenDRIVE>
<header revMajor="1" revMinor="6"/>
<road name="r" length="100" id="1" junction="-1">
<planView><geometry s="0" x="0" y="0" hdg="0" length="100"><line/></geometry></planView>
<lanes>
<laneSection s="0">
<right>
<lane id="-1" type="driving" level="false"><width sOffset="0" a="3.5" b="0" c="0" d="0"/></lane>
</right>
</laneSection>
</lanes>
<signals>
<signal s="10" t="-2" id="s1" name="SgRMArrowLeft.flt" dynamic="no" orientation="+" type="-1" subtype="-1" hOffset="0"/>
<signal s="20" t="2" id="s2" name="SgRMArrowLeft.flt" dynamic="no" orientation="-" type="-1" subtype="-1" hOffset="0"/>
<signal s="30" t="-2" id="s3" name="stop" dynamic="no" orientation="+" type="206" subtype="-1" hOffset="0.5"/>
<signal s="40" t="-2" id="s4" name="untagged" dynamic="no" type="205" subtype="-1"/>
</signals>
</road>
</OpenDRIVE>`

it('derives headingRad from reference heading, orientation and hOffset', () => {
const result = odrToShapes(parseOpenDriveXml(SIGNAL_HEADING_MAP))
const byId = new Map(result.trafficSigns.map(ts => [ts.attributes.odr_signal_id, ts]))
// Road runs along +x (heading 0).
expect(byId.get('s1')?.headingRad).toBeCloseTo(0, 9)
// orientation="-" flips by pi.
expect(byId.get('s2')?.headingRad).toBeCloseTo(Math.PI, 9)
// hOffset adds on top of the oriented direction.
expect(byId.get('s3')?.headingRad).toBeCloseTo(0.5, 9)
// Missing orientation/hOffset default to the reference direction.
expect(byId.get('s4')?.headingRad).toBeCloseTo(0, 9)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ describe('parseOpenDriveXml', () => {
const road = parseOpenDriveXml(SAMPLE).roads[0]
expect(road.hasElevation).toBe(true)
expect(road.signals).toEqual([
{ id: 'sig1', s: 95, t: -6, type: '1000001', subtype: '-1', name: 'tl', dynamic: '', country: '', width: 0, height: 0, validity: [], userData: {} },
{ id: 'sig1', s: 95, t: -6, type: '1000001', subtype: '-1', name: 'tl', dynamic: '', country: '', width: 0, height: 0, orientation: '', hOffset: 0, validity: [], userData: {} },
])
expect(road.objects[0].type).toBe('crosswalk')
})
Expand Down
4 changes: 4 additions & 0 deletions packages/drawtonomy-sdk/src/exporter/odrToShapes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,9 @@ export function odrToShapes(map: OdrMap, options: OdrToShapesOptions = {}): OdrI
// Unit normal toward +t (left of the reference direction in ENU).
const ex = pose.x - Math.sin(pose.hdg) * sig.t
const ey = pose.y + Math.cos(pose.hdg) * sig.t
// World heading the signal applies to (ENU radians): reference-line
// heading, flipped for orientation="-", plus the signal's hOffset.
const headingRad = pose.hdg + (sig.orientation === '-' ? Math.PI : 0) + sig.hOffset

const affected: string[] = []
resolveAffectedLanes(road, sig.s, sig.validity, affected)
Expand Down Expand Up @@ -683,6 +686,7 @@ export function odrToShapes(map: OdrMap, options: OdrToShapesOptions = {}): OdrI
affectedLaneIds: affected,
stopLineId: materializeStopLine(sig.userData['stopLine']),
attributes,
headingRad,
}
result.trafficSigns.push(data)
convertedSignalCount++
Expand Down
9 changes: 9 additions & 0 deletions packages/drawtonomy-sdk/src/exporter/opendriveParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@ export interface OdrSignal {
/** Physical size (m); 0 when absent. */
width: number
height: number
/**
* Facing relative to the reference-line direction: "+" applies to traffic
* running in the positive s direction, "-" against it ("" when absent).
*/
orientation: string
/** Heading offset in radians relative to the (oriented) s direction; 0 when absent. */
hOffset: number
/** Lane ranges restricting which lanes the signal applies to (empty = all). */
validity: OdrSignalValidity[]
/** <userData code value> records attached to the signal (code -> value). */
Expand Down Expand Up @@ -686,6 +693,8 @@ function parseRoad(el: XmlNode): OdrRoad {
country: sig.attrs.country ?? '',
width: numAttr(sig, 'width', 0),
height: numAttr(sig, 'height', 0),
orientation: sig.attrs.orientation ?? '',
hOffset: numAttr(sig, 'hOffset', 0),
validity: parseValidity(sig),
userData: parseUserData(sig),
}))
Expand Down
8 changes: 8 additions & 0 deletions packages/drawtonomy-sdk/src/exporter/osmToShapes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ export interface ImportedTrafficSign {
* keeps a speed limit value (e.g. "50 km/h") when present.
*/
attributes: Record<string, string>
/**
* World heading the signal applies to, in ENU radians (counter-clockwise
* from +x, y-up): reference-line heading at s, plus pi when
* orientation="-", plus hOffset. Only set by the OpenDRIVE import path;
* undefined for OSM/Lanelet2 sources. Renderers drawing surface markings
* (arrows etc.) should align the artwork with this direction.
*/
headingRad?: number
}

export interface ImportedCrosswalk {
Expand Down