@@ -212,6 +212,52 @@ def point_at(self, s):
212212 return self .rel_to_abs ((s , 0 , s ))
213213
214214
215+ def makeCurve (x0 , y0 , hdg , length , curve_elem ):
216+ """Create a reference-line curve from an OpenDRIVE geometry element.
217+
218+ The given length is the effective length Scenic should use, which may
219+ differ from the length declared in the map if the planView is inconsistent.
220+ """
221+ if curve_elem .tag == "line" :
222+ return Line (x0 , y0 , hdg , length )
223+ elif curve_elem .tag == "arc" :
224+ # Arc is clothoid of constant curvature.
225+ curv = float (curve_elem .get ("curvature" ))
226+ return Clothoid (x0 , y0 , hdg , length , curv , curv )
227+ elif curve_elem .tag == "spiral" :
228+ curv0 = float (curve_elem .get ("curvStart" ))
229+ curv1 = float (curve_elem .get ("curvEnd" ))
230+ return Clothoid (x0 , y0 , hdg , length , curv0 , curv1 )
231+ elif curve_elem .tag == "poly3" :
232+ a , b , c , d = (
233+ float (curve_elem .get ("a" )),
234+ float (curve_elem .get ("b" )),
235+ float (curve_elem .get ("c" )),
236+ float (curve_elem .get ("d" )),
237+ )
238+ return Cubic (x0 , y0 , hdg , length , a , b , c , d )
239+ elif curve_elem .tag == "paramPoly3" :
240+ au , bu , cu , du , av , bv , cv , dv = (
241+ float (curve_elem .get ("aU" )),
242+ float (curve_elem .get ("bU" )),
243+ float (curve_elem .get ("cU" )),
244+ float (curve_elem .get ("dU" )),
245+ float (curve_elem .get ("aV" )),
246+ float (curve_elem .get ("bV" )),
247+ float (curve_elem .get ("cV" )),
248+ float (curve_elem .get ("dV" )),
249+ )
250+ p_range = curve_elem .get ("pRange" )
251+ if p_range and p_range != "normalized" :
252+ # TODO support arcLength
253+ raise NotImplementedError ("unsupported pRange for paramPoly3" )
254+ else :
255+ p_range = 1
256+ return ParamCubic (x0 , y0 , hdg , length , au , bu , cu , du , av , bv , cv , dv , p_range )
257+ else :
258+ raise NotImplementedError (f'unhandled OpenDRIVE geometry type "{ curve_elem .tag } "' )
259+
260+
215261class Lane :
216262 def __init__ (self , id_ , type_ , pred = None , succ = None ):
217263 self .id_ = id_
@@ -1434,82 +1480,58 @@ def parse(self, path):
14341480 hdg = float (geom .get ("hdg" ))
14351481 length = float (geom .get ("length" ))
14361482 curve_elem = geom [0 ]
1437- curve = None
1438- if curve_elem .tag == "line" :
1439- curve = Line (x0 , y0 , hdg , length )
1440- elif curve_elem .tag == "arc" :
1441- # Arc is clothoid of constant curvature.
1442- curv = float (curve_elem .get ("curvature" ))
1443- curve = Clothoid (x0 , y0 , hdg , length , curv , curv )
1444- elif curve_elem .tag == "spiral" :
1445- curv0 = float (curve_elem .get ("curvStart" ))
1446- curv1 = float (curve_elem .get ("curvEnd" ))
1447- curve = Clothoid (x0 , y0 , hdg , length , curv0 , curv1 )
1448- elif curve_elem .tag == "poly3" :
1449- a , b , c , d = (
1450- cubic_elem .get ("a" ),
1451- float (curve_elem .get ("b" )),
1452- float (curve_elem .get ("c" )),
1453- float (curve_elem .get ("d" )),
1454- )
1455- curve = Cubic (x0 , y0 , hdg , length , a , b , c , d )
1456- elif curve_elem .tag == "paramPoly3" :
1457- au , bu , cu , du , av , bv , cv , dv = (
1458- float (curve_elem .get ("aU" )),
1459- float (curve_elem .get ("bU" )),
1460- float (curve_elem .get ("cU" )),
1461- float (curve_elem .get ("dU" )),
1462- float (curve_elem .get ("aV" )),
1463- float (curve_elem .get ("bV" )),
1464- float (curve_elem .get ("cV" )),
1465- float (curve_elem .get ("dV" )),
1466- )
1467- p_range = curve_elem .get ("pRange" )
1468- if p_range and p_range != "normalized" :
1469- # TODO support arcLength
1470- raise NotImplementedError ("unsupported pRange for paramPoly3" )
1471- else :
1472- p_range = 1
1473- curve = ParamCubic (
1474- x0 , y0 , hdg , length , au , bu , cu , du , av , bv , cv , dv , p_range
1475- )
1476- curves .append ((s0 , curve ))
1483+ curves .append ((s0 , x0 , y0 , hdg , length , curve_elem ))
1484+
14771485 if not curves :
14781486 raise ValueError (f"road { road .id_ } has an empty planView" )
14791487 if not curves [0 ][0 ] == 0 :
14801488 raise ValueError (
14811489 f"reference line of road { road .id_ } does not start at s=0"
14821490 )
1483- lastS = 0
1484- lastCurve = curves [0 ][1 ]
1491+
1492+ lastS = curves [0 ][0 ]
1493+ lastCurve = curves [0 ]
14851494 refLine = []
1486- for s0 , curve in curves [1 :]:
1495+ for curve in curves [1 :]:
1496+ s0 = curve [0 ]
14871497 l = s0 - lastS
1488- if abs (lastCurve .length - l ) > 1e-4 :
1489- raise ValueError (
1490- f"planView of road { road .id_ } has inconsistent length"
1491- )
1498+
14921499 if l < 0 :
14931500 raise ValueError (f"planView of road { road .id_ } is not in order" )
1494- elif l < 1e-6 :
1501+
1502+ lastS0 , x0 , y0 , hdg , length , curve_elem = lastCurve
1503+ if abs (length - l ) > 1e-3 :
1504+ warn (
1505+ f"planView of road { road .id_ } has inconsistent length: "
1506+ f"geometry at s={ lastS0 } has declared length { length } , "
1507+ f"but the next geometry starts at s={ s0 } ; "
1508+ f"using length { l } "
1509+ )
1510+ length = l
1511+
1512+ if l < 1e-6 :
14951513 warn (
14961514 f"road { road .id_ } reference line has a geometry of "
14971515 f"length { l } ; skipping it"
14981516 )
14991517 else :
1500- refLine .append (lastCurve )
1518+ refLine .append (makeCurve (x0 , y0 , hdg , length , curve_elem ))
1519+
15011520 lastS = s0
15021521 lastCurve = curve
1503- if refLine and lastCurve .length < 1e-6 :
1522+
1523+ lastS0 , x0 , y0 , hdg , length , curve_elem = lastCurve
1524+ if refLine and length < 1e-6 :
15041525 warn (
15051526 f"road { road .id_ } reference line has a geometry of "
1506- f"length { lastCurve . length } ; skipping it"
1527+ f"length { length } ; skipping it"
15071528 )
15081529 else :
15091530 # even if the last curve is shorter than the threshold, we'll keep it if
15101531 # it is the only curve; getting rid of the road entirely is handled by
15111532 # road elision above
1512- refLine .append (lastCurve )
1533+ refLine .append (makeCurve (x0 , y0 , hdg , length , curve_elem ))
1534+
15131535 assert refLine
15141536 road .ref_line = refLine
15151537
0 commit comments