Module scenariogeneration.xodr.lane_def
scenariogeneration https://github.com/pyoscx/scenariogeneration
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
Copyright (c) 2022 The scenariogeneration Authors.
This is a collection methods and classes not related to OpenDRIVE, but relates to automation of lane creations
Expand source code
"""
scenariogeneration
https://github.com/pyoscx/scenariogeneration
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/.
Copyright (c) 2022 The scenariogeneration Authors.
This is a collection methods and classes not related to OpenDRIVE, but relates to automation of lane creations
"""
import copy
import numpy as np
from .links import LaneLinker
from .utils import get_coeffs_for_poly3
from .lane import RoadMark, RoadMarkType, RoadLine, Lane, LaneSection, Lanes
def std_roadmark_solid():
return RoadMark(RoadMarkType.solid, 0.2)
def std_roadmark_broken():
roadmark = RoadMark(RoadMarkType.broken, 0.2)
roadmark.add_specific_road_line(RoadLine(0.15, 3, 9, 0, 0))
return roadmark
def std_roadmark_broken_long_line():
roadmark = RoadMark(RoadMarkType.broken, 0.2)
roadmark.add_specific_road_line(RoadLine(0.15, 9, 3, 0, 0))
return roadmark
def std_roadmark_broken_tight():
roadmark = RoadMark(RoadMarkType.broken, 0.2)
roadmark.add_specific_road_line(RoadLine(0.15, 3, 3, 0, 0))
return roadmark
def std_roadmark_broken_broken():
roadmark = RoadMark(RoadMarkType.broken_broken)
roadmark.add_specific_road_line(RoadLine(0.2, 3, 3, 0.2, 0))
roadmark.add_specific_road_line(RoadLine(0.2, 3, 3, -0.2, 0))
return roadmark
def std_roadmark_solid_solid():
roadmark = RoadMark(RoadMarkType.solid_solid)
roadmark.add_specific_road_line(RoadLine(0.2, 0, 0, 0.2, 0))
roadmark.add_specific_road_line(RoadLine(0.2, 0, 0, -0.2, 0))
return roadmark
def std_roadmark_solid_broken():
roadmark = RoadMark(RoadMarkType.solid_broken)
roadmark.add_specific_road_line(RoadLine(0.2, 0, 0, 0.2, 0))
roadmark.add_specific_road_line(RoadLine(0.2, 3, 3, -0.2, 0))
return roadmark
def std_roadmark_broken_solid():
roadmark = RoadMark(RoadMarkType.broken_solid)
roadmark.add_specific_road_line(RoadLine(0.2, 0, 0, -0.2, 0))
roadmark.add_specific_road_line(RoadLine(0.2, 3, 3, 0.2, 0))
return roadmark
def create_lanes_merge_split(
right_lane_def,
left_lane_def,
road_length,
center_road_mark,
lane_width,
lane_width_end,
):
"""create_lanes_merge_split is a generator that will create the Lanes of a road road that can contain one or more lane merges/splits
This is a simple implementation and has some constraints:
- left and right merges has to be at the same place (or one per lane), TODO: will be fixed with the singleSide attribute later on.
- the change will be a 3 degree polynomial with the derivative 0 on both start and end.
Please note that the merges/splits are defined in the road direction, NOT the driving direction.
Parameters
----------
right_lane_def (list of LaneDef, or an int): a list of the splits/merges that are wanted on the right side of the road, if int constant number of lanes
left_lane_def (list of LaneDef, or an int): a list of the splits/merges that are wanted on the left side of the road, if int constant number of lanes.
road_length (float): the full length of the road
center_road_mark (RoadMark): roadmark for the center line
lane_width (float): the width of the lanes
lane_width_end (float): the end width of the lanes
Return
------
road (Lanes): the lanes of a road
"""
lanesections = []
# expand the lane list
right_lane, left_lane = _create_lane_lists(
right_lane_def, left_lane_def, road_length, lane_width
)
# create the lanesections needed
for ls in range(len(left_lane)):
lc = Lane(a=0)
lc.add_roadmark(copy.deepcopy(center_road_mark))
lsec = LaneSection(left_lane[ls].s_start, lc)
# do the right lanes
for i in range(max(right_lane[ls].n_lanes_start, right_lane[ls].n_lanes_end)):
# add broken roadmarks for all lanes, except for the outer lane where a solid line is added
if i == max(right_lane[ls].n_lanes_start, right_lane[ls].n_lanes_end) - 1:
rm = std_roadmark_solid()
else:
rm = std_roadmark_broken()
# check if the number of lanes should change or not
if (
right_lane[ls].n_lanes_start > right_lane[ls].n_lanes_end
and i == np.abs(right_lane[ls].sub_lane) - 1
):
# lane merge
coeff = get_coeffs_for_poly3(
right_lane[ls].s_end - right_lane[ls].s_start,
right_lane[ls].lane_start_widths[i],
False,
right_lane[ls].lane_end_widths[i],
)
rightlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
rightlane.add_roadmark(rm)
elif (
right_lane[ls].n_lanes_start < right_lane[ls].n_lanes_end
and i == np.abs(right_lane[ls].sub_lane) - 1
):
# lane split
coeff = get_coeffs_for_poly3(
right_lane[ls].s_end - right_lane[ls].s_start,
right_lane[ls].lane_start_widths[i],
True,
right_lane[ls].lane_end_widths[i],
)
rightlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
rightlane.add_roadmark(rm)
elif (lane_width_end is not None) and (lane_width != lane_width_end):
coeff = get_coeffs_for_poly3(
right_lane[ls].s_end - right_lane[ls].s_start,
lane_width,
False,
lane_width_end=lane_width_end,
)
rightlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
rightlane.add_roadmark(rm)
elif right_lane[ls].lane_start_widths:
coeff = get_coeffs_for_poly3(
right_lane[ls].s_end - right_lane[ls].s_start,
right_lane[ls].lane_start_widths[i],
False,
lane_width_end=right_lane[ls].lane_end_widths[i],
)
rightlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
rightlane.add_roadmark(rm)
else:
rightlane = Lane(lane_width)
rightlane.add_roadmark(rm)
lsec.add_right_lane(rightlane)
# do the left lanes
for i in range(max(left_lane[ls].n_lanes_start, left_lane[ls].n_lanes_end)):
# add broken roadmarks for all lanes, except for the outer lane where a solid line is added
if i == max(left_lane[ls].n_lanes_start, left_lane[ls].n_lanes_end) - 1:
rm = std_roadmark_solid()
else:
rm = std_roadmark_broken()
# check if the number of lanes should change or not
if (
left_lane[ls].n_lanes_start < left_lane[ls].n_lanes_end
and i == left_lane[ls].sub_lane - 1
):
# lane split
coeff = get_coeffs_for_poly3(
left_lane[ls].s_end - left_lane[ls].s_start,
left_lane[ls].lane_start_widths[i],
True,
left_lane[ls].lane_end_widths[i],
)
leftlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
leftlane.add_roadmark(rm)
elif (
left_lane[ls].n_lanes_start > left_lane[ls].n_lanes_end
and i == left_lane[ls].sub_lane - 1
):
# lane merge
coeff = get_coeffs_for_poly3(
left_lane[ls].s_end - left_lane[ls].s_start,
left_lane[ls].lane_start_widths[i],
False,
left_lane[ls].lane_end_widths[i],
)
leftlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
leftlane.add_roadmark(rm)
elif (lane_width_end is not None) and (lane_width != lane_width_end):
coeff = get_coeffs_for_poly3(
left_lane[ls].s_end - left_lane[ls].s_start,
lane_width,
False,
lane_width_end=lane_width_end,
)
leftlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
leftlane.add_roadmark(rm)
elif left_lane[ls].lane_start_widths:
coeff = get_coeffs_for_poly3(
left_lane[ls].s_end - left_lane[ls].s_start,
left_lane[ls].lane_start_widths[i],
False,
lane_width_end=left_lane[ls].lane_end_widths[i],
)
leftlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3])
leftlane.add_roadmark(rm)
else:
leftlane = Lane(lane_width)
leftlane.add_roadmark(rm)
lsec.add_left_lane(leftlane)
lanesections.append(lsec)
# create the lane linker to link the lanes correctly
lanelinker = LaneLinker()
for i in range(1, len(right_lane)):
if right_lane[i].n_lanes_end > right_lane[i].n_lanes_start:
# lane split
for j in range(0, right_lane[i - 1].n_lanes_end + 1):
# adjust for the new lane
if right_lane[i].sub_lane < -(j + 1):
lanelinker.add_link(
lanesections[i - 1].rightlanes[j], lanesections[i].rightlanes[j]
)
elif right_lane[i].sub_lane > -(j + 1):
lanelinker.add_link(
lanesections[i - 1].rightlanes[j - 1],
lanesections[i].rightlanes[j],
)
elif right_lane[i - 1].n_lanes_end < right_lane[i - 1].n_lanes_start:
# lane merge
for j in range(0, right_lane[i - 1].n_lanes_end + 1):
# adjust for the lost lane
if right_lane[i - 1].sub_lane < -(j + 1):
lanelinker.add_link(
lanesections[i - 1].rightlanes[j], lanesections[i].rightlanes[j]
)
elif right_lane[i - 1].sub_lane > -(j + 1):
lanelinker.add_link(
lanesections[i - 1].rightlanes[j],
lanesections[i].rightlanes[j - 1],
)
else:
# same number of lanes, just add the links
for j in range(right_lane[i - 1].n_lanes_end):
lanelinker.add_link(
lanesections[i - 1].rightlanes[j], lanesections[i].rightlanes[j]
)
for i in range(1, len(left_lane)):
if left_lane[i].n_lanes_end > left_lane[i].n_lanes_start:
# lane split
for j in range(0, left_lane[i - 1].n_lanes_end + 1):
# adjust for the new lane
if left_lane[i].sub_lane < (j + 1):
lanelinker.add_link(
lanesections[i - 1].leftlanes[j - 1],
lanesections[i].leftlanes[j],
)
elif left_lane[i].sub_lane > (j + 1):
lanelinker.add_link(
lanesections[i - 1].leftlanes[j], lanesections[i].leftlanes[j]
)
elif left_lane[i - 1].n_lanes_end < left_lane[i - 1].n_lanes_start:
# lane merge
for j in range(0, left_lane[i - 1].n_lanes_end + 1):
# adjust for the lost lane
if left_lane[i - 1].sub_lane < (j + 1):
lanelinker.add_link(
lanesections[i - 1].leftlanes[j],
lanesections[i].leftlanes[j - 1],
)
elif left_lane[i - 1].sub_lane > (j + 1):
lanelinker.add_link(
lanesections[i - 1].leftlanes[j], lanesections[i].leftlanes[j]
)
else:
# same number of lanes, just add the links
for j in range(left_lane[i - 1].n_lanes_end):
lanelinker.add_link(
lanesections[i - 1].leftlanes[j], lanesections[i].leftlanes[j]
)
# Add the lanesections to the lanes struct together the lanelinker
lanes = Lanes()
for ls in lanesections:
lanes.add_lanesection(ls, lanelinker)
return lanes
class LaneDef:
"""LaneDef is used to help create a lane merge or split. Can handle one lane merging or spliting.
NOTE: This is not part of the OpenDRIVE standard, but a helper for the xodr module.
Parameters
----------
s_start (float): s coordinate of the start of the change
s_end (float): s coordinate of the end of the change
n_lanes_start (int): number of lanes at s_start
n_lanes_end (int): number of lanes at s_end
sub_lane (int): the lane that should be created (split) or removed (merge)
lane_start_widths (list of float): widths of lanes at start, must be [] or same length as n_lanes_start
Default: []
lane_end_widths (list of float): widths of lanes at end, must be [] or same length as n_lanes_end
Default: same as lane_start_widths
Attributes
----------
s_start (float): s coordinate of the start of the change
s_end (float): s coordinate of the end of the change
n_lanes_start (int): number of lanes at s_start
n_lanes_end (int): number of lanes at s_end
sub_lane (int): the lane that should be created (split) or removed (merge)
lane_start_widths (list of float): widths of lanes at start, must be [] or same length as n_lanes_start
lane_end_widths (list of float): widths of lanes at end, must be [] or same length as n_lanes_end
"""
def __init__(
self,
s_start,
s_end,
n_lanes_start,
n_lanes_end,
sub_lane=None,
lane_start_widths=[],
lane_end_widths=[],
):
self.s_start = s_start
self.s_end = s_end
self.n_lanes_start = n_lanes_start
self.n_lanes_end = n_lanes_end
self.sub_lane = sub_lane
self.lane_start_widths = lane_start_widths
if lane_end_widths == []:
self.lane_end_widths = self.lane_start_widths.copy()
else:
self.lane_end_widths = lane_end_widths
def _adjust_lane_widths(self):
if self.sub_lane:
if self.lane_end_widths and len(self.lane_end_widths) < self.n_lanes_start:
# mergeo
self.lane_end_widths.insert(abs(self.sub_lane) - 1, 0)
elif (
self.lane_start_widths
and len(self.lane_start_widths) < self.n_lanes_end
):
# split
self.lane_start_widths.insert(abs(self.sub_lane) - 1, 0)
# TODO: add some checks here?
def _create_lane_lists(right, left, tot_length, default_lane_width):
"""_create_lane_lists is a function used by create_lanes_merge_split to expand the list of LaneDefs to be used to create stuffs
Parameters
----------
right (list of LaneDef, or int): the list of LaneDef for the right lane
left (list of LaneDef, or int): the list of LaneDef for the left lane
tot_length (float): the total length of the road
default_lane_width (float): lane_width to be used if not defined in LaneDef
"""
# TODO: implement for left and right lanesection...
def _check_lane_widths(lane):
if lane.lane_start_widths == []:
lane.lane_start_widths = [
default_lane_width for x in range(lane.n_lanes_start)
]
if lane.lane_end_widths == []:
lane.lane_end_widths = [default_lane_width for x in range(lane.n_lanes_end)]
const_right_lanes = None
const_left_lanes = None
retlanes_right = []
retlanes_left = []
present_s = 0
r_it = 0
l_it = 0
# some primariy checks to handle int instead of LaneDef
if not isinstance(right, list):
const_right_lanes = right
right = []
if not isinstance(left, list):
const_left_lanes = left
left = []
while present_s < tot_length:
if r_it < len(right):
# check if there is still a right LaneDef to be used, and is the next one to add
if right[r_it].s_start == present_s:
add_right = True
else:
next_right = right[r_it].s_start
add_right = False
n_r_lanes = right[r_it].n_lanes_start
else:
# no more LaneDefs, just add new right lanes with the const/or last number of lanes
add_right = False
next_right = tot_length
if const_right_lanes or const_right_lanes == 0:
n_r_lanes = const_right_lanes
else:
n_r_lanes = right[-1].n_lanes_end
if l_it < len(left):
# check if there is still a left LaneDef to be used, and is the next one to add
if left[l_it].s_start == present_s:
add_left = True
else:
next_left = left[l_it].s_start
add_left = False
n_l_lanes = left[l_it].n_lanes_start
else:
# no more LaneDefs, just add new left lanes with the const/or last number of lanes
add_left = False
next_left = tot_length
if const_left_lanes or const_left_lanes == 0:
n_l_lanes = const_left_lanes
else:
n_l_lanes = left[-1].n_lanes_end
# create and add the requiered LaneDefs
if not add_left and not add_right:
# no LaneDefs, just add same amout of lanes
s_end = min(next_left, next_right)
if const_right_lanes is not None:
retlanes_right.append(
LaneDef(
present_s,
s_end,
n_r_lanes,
n_r_lanes,
lane_start_widths=[
default_lane_width for x in range(n_r_lanes)
],
lane_end_widths=[default_lane_width for x in range(n_r_lanes)],
)
)
else:
lane_start_widths = [default_lane_width for x in range(n_r_lanes)]
lane_end_widths = [default_lane_width for x in range(n_r_lanes)]
if r_it == len(right):
if right[r_it - 1].lane_end_widths:
lane_start_widths = right[r_it - 1].lane_end_widths.copy()
lane_end_widths = right[r_it - 1].lane_end_widths.copy()
elif right[r_it].lane_start_widths:
lane_start_widths = right[r_it].lane_start_widths.copy()
lane_end_widths = right[r_it].lane_start_widths.copy()
retlanes_right.append(
LaneDef(
present_s,
s_end,
n_r_lanes,
n_r_lanes,
lane_start_widths=lane_start_widths,
lane_end_widths=lane_end_widths,
)
)
if const_left_lanes is not None:
retlanes_left.append(
LaneDef(
present_s,
s_end,
n_l_lanes,
n_l_lanes,
lane_start_widths=[
default_lane_width for x in range(n_l_lanes)
],
lane_end_widths=[default_lane_width for x in range(n_l_lanes)],
)
)
else:
lane_start_widths = [default_lane_width for x in range(n_l_lanes)]
lane_end_widths = [default_lane_width for x in range(n_l_lanes)]
if l_it == len(left):
if left[l_it - 1].lane_end_widths:
lane_start_widths = left[l_it - 1].lane_end_widths.copy()
lane_end_widths = left[l_it - 1].lane_end_widths.copy()
elif left[l_it].lane_start_widths:
lane_start_widths = left[l_it].lane_start_widths.copy()
lane_end_widths = left[l_it].lane_start_widths.copy()
retlanes_left.append(
LaneDef(
present_s,
s_end,
n_l_lanes,
n_l_lanes,
lane_start_widths=lane_start_widths,
lane_end_widths=lane_end_widths,
)
)
present_s = s_end
elif add_left and add_right:
# Both have changes in the amount of lanes,
_check_lane_widths(left[l_it])
_check_lane_widths(right[r_it])
retlanes_left.append(left[l_it])
retlanes_right.append(right[r_it])
present_s = left[l_it].s_end
r_it += 1
l_it += 1
elif add_right:
# only the right lane changes the amount of lanes, and add a LaneDef with the same amount of lanes to the left
_check_lane_widths(right[r_it])
retlanes_right.append(right[r_it])
retlanes_left.append(
LaneDef(
present_s,
right[r_it].s_end,
n_l_lanes,
n_l_lanes,
lane_start_widths=[default_lane_width for x in range(n_l_lanes)],
lane_end_widths=[default_lane_width for x in range(n_l_lanes)],
)
)
present_s = right[r_it].s_end
r_it += 1
elif add_left:
# only the left lane changes the amount of lanes, and add a LaneDef with the same amount of lanes to the right
_check_lane_widths(left[l_it])
retlanes_left.append(left[l_it])
retlanes_right.append(
LaneDef(
present_s,
left[l_it].s_end,
n_r_lanes,
n_r_lanes,
lane_start_widths=[default_lane_width for x in range(n_r_lanes)],
lane_end_widths=[default_lane_width for x in range(n_r_lanes)],
)
)
present_s = left[l_it].s_end
l_it += 1
[x._adjust_lane_widths() for x in retlanes_right]
[x._adjust_lane_widths() for x in retlanes_left]
return retlanes_right, retlanes_left
Functions
def create_lanes_merge_split(right_lane_def, left_lane_def, road_length, center_road_mark, lane_width, lane_width_end)
-
create_lanes_merge_split is a generator that will create the Lanes of a road road that can contain one or more lane merges/splits This is a simple implementation and has some constraints: - left and right merges has to be at the same place (or one per lane), TODO: will be fixed with the singleSide attribute later on. - the change will be a 3 degree polynomial with the derivative 0 on both start and end.
Please note that the merges/splits are defined in the road direction, NOT the driving direction.
Parameters
right_lane_def (list of LaneDef, or an int): a list of the splits/merges that are wanted on the right side of the road, if int constant number of lanes left_lane_def (list of LaneDef, or an int): a list of the splits/merges that are wanted on the left side of the road, if int constant number of lanes. road_length (float): the full length of the road center_road_mark (RoadMark): roadmark for the center line lane_width (float): the width of the lanes lane_width_end (float): the end width of the lanes
Return
road (Lanes): the lanes of a road
Expand source code
def create_lanes_merge_split( right_lane_def, left_lane_def, road_length, center_road_mark, lane_width, lane_width_end, ): """create_lanes_merge_split is a generator that will create the Lanes of a road road that can contain one or more lane merges/splits This is a simple implementation and has some constraints: - left and right merges has to be at the same place (or one per lane), TODO: will be fixed with the singleSide attribute later on. - the change will be a 3 degree polynomial with the derivative 0 on both start and end. Please note that the merges/splits are defined in the road direction, NOT the driving direction. Parameters ---------- right_lane_def (list of LaneDef, or an int): a list of the splits/merges that are wanted on the right side of the road, if int constant number of lanes left_lane_def (list of LaneDef, or an int): a list of the splits/merges that are wanted on the left side of the road, if int constant number of lanes. road_length (float): the full length of the road center_road_mark (RoadMark): roadmark for the center line lane_width (float): the width of the lanes lane_width_end (float): the end width of the lanes Return ------ road (Lanes): the lanes of a road """ lanesections = [] # expand the lane list right_lane, left_lane = _create_lane_lists( right_lane_def, left_lane_def, road_length, lane_width ) # create the lanesections needed for ls in range(len(left_lane)): lc = Lane(a=0) lc.add_roadmark(copy.deepcopy(center_road_mark)) lsec = LaneSection(left_lane[ls].s_start, lc) # do the right lanes for i in range(max(right_lane[ls].n_lanes_start, right_lane[ls].n_lanes_end)): # add broken roadmarks for all lanes, except for the outer lane where a solid line is added if i == max(right_lane[ls].n_lanes_start, right_lane[ls].n_lanes_end) - 1: rm = std_roadmark_solid() else: rm = std_roadmark_broken() # check if the number of lanes should change or not if ( right_lane[ls].n_lanes_start > right_lane[ls].n_lanes_end and i == np.abs(right_lane[ls].sub_lane) - 1 ): # lane merge coeff = get_coeffs_for_poly3( right_lane[ls].s_end - right_lane[ls].s_start, right_lane[ls].lane_start_widths[i], False, right_lane[ls].lane_end_widths[i], ) rightlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3]) rightlane.add_roadmark(rm) elif ( right_lane[ls].n_lanes_start < right_lane[ls].n_lanes_end and i == np.abs(right_lane[ls].sub_lane) - 1 ): # lane split coeff = get_coeffs_for_poly3( right_lane[ls].s_end - right_lane[ls].s_start, right_lane[ls].lane_start_widths[i], True, right_lane[ls].lane_end_widths[i], ) rightlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3]) rightlane.add_roadmark(rm) elif (lane_width_end is not None) and (lane_width != lane_width_end): coeff = get_coeffs_for_poly3( right_lane[ls].s_end - right_lane[ls].s_start, lane_width, False, lane_width_end=lane_width_end, ) rightlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3]) rightlane.add_roadmark(rm) elif right_lane[ls].lane_start_widths: coeff = get_coeffs_for_poly3( right_lane[ls].s_end - right_lane[ls].s_start, right_lane[ls].lane_start_widths[i], False, lane_width_end=right_lane[ls].lane_end_widths[i], ) rightlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3]) rightlane.add_roadmark(rm) else: rightlane = Lane(lane_width) rightlane.add_roadmark(rm) lsec.add_right_lane(rightlane) # do the left lanes for i in range(max(left_lane[ls].n_lanes_start, left_lane[ls].n_lanes_end)): # add broken roadmarks for all lanes, except for the outer lane where a solid line is added if i == max(left_lane[ls].n_lanes_start, left_lane[ls].n_lanes_end) - 1: rm = std_roadmark_solid() else: rm = std_roadmark_broken() # check if the number of lanes should change or not if ( left_lane[ls].n_lanes_start < left_lane[ls].n_lanes_end and i == left_lane[ls].sub_lane - 1 ): # lane split coeff = get_coeffs_for_poly3( left_lane[ls].s_end - left_lane[ls].s_start, left_lane[ls].lane_start_widths[i], True, left_lane[ls].lane_end_widths[i], ) leftlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3]) leftlane.add_roadmark(rm) elif ( left_lane[ls].n_lanes_start > left_lane[ls].n_lanes_end and i == left_lane[ls].sub_lane - 1 ): # lane merge coeff = get_coeffs_for_poly3( left_lane[ls].s_end - left_lane[ls].s_start, left_lane[ls].lane_start_widths[i], False, left_lane[ls].lane_end_widths[i], ) leftlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3]) leftlane.add_roadmark(rm) elif (lane_width_end is not None) and (lane_width != lane_width_end): coeff = get_coeffs_for_poly3( left_lane[ls].s_end - left_lane[ls].s_start, lane_width, False, lane_width_end=lane_width_end, ) leftlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3]) leftlane.add_roadmark(rm) elif left_lane[ls].lane_start_widths: coeff = get_coeffs_for_poly3( left_lane[ls].s_end - left_lane[ls].s_start, left_lane[ls].lane_start_widths[i], False, lane_width_end=left_lane[ls].lane_end_widths[i], ) leftlane = Lane(a=coeff[0], b=coeff[1], c=coeff[2], d=coeff[3]) leftlane.add_roadmark(rm) else: leftlane = Lane(lane_width) leftlane.add_roadmark(rm) lsec.add_left_lane(leftlane) lanesections.append(lsec) # create the lane linker to link the lanes correctly lanelinker = LaneLinker() for i in range(1, len(right_lane)): if right_lane[i].n_lanes_end > right_lane[i].n_lanes_start: # lane split for j in range(0, right_lane[i - 1].n_lanes_end + 1): # adjust for the new lane if right_lane[i].sub_lane < -(j + 1): lanelinker.add_link( lanesections[i - 1].rightlanes[j], lanesections[i].rightlanes[j] ) elif right_lane[i].sub_lane > -(j + 1): lanelinker.add_link( lanesections[i - 1].rightlanes[j - 1], lanesections[i].rightlanes[j], ) elif right_lane[i - 1].n_lanes_end < right_lane[i - 1].n_lanes_start: # lane merge for j in range(0, right_lane[i - 1].n_lanes_end + 1): # adjust for the lost lane if right_lane[i - 1].sub_lane < -(j + 1): lanelinker.add_link( lanesections[i - 1].rightlanes[j], lanesections[i].rightlanes[j] ) elif right_lane[i - 1].sub_lane > -(j + 1): lanelinker.add_link( lanesections[i - 1].rightlanes[j], lanesections[i].rightlanes[j - 1], ) else: # same number of lanes, just add the links for j in range(right_lane[i - 1].n_lanes_end): lanelinker.add_link( lanesections[i - 1].rightlanes[j], lanesections[i].rightlanes[j] ) for i in range(1, len(left_lane)): if left_lane[i].n_lanes_end > left_lane[i].n_lanes_start: # lane split for j in range(0, left_lane[i - 1].n_lanes_end + 1): # adjust for the new lane if left_lane[i].sub_lane < (j + 1): lanelinker.add_link( lanesections[i - 1].leftlanes[j - 1], lanesections[i].leftlanes[j], ) elif left_lane[i].sub_lane > (j + 1): lanelinker.add_link( lanesections[i - 1].leftlanes[j], lanesections[i].leftlanes[j] ) elif left_lane[i - 1].n_lanes_end < left_lane[i - 1].n_lanes_start: # lane merge for j in range(0, left_lane[i - 1].n_lanes_end + 1): # adjust for the lost lane if left_lane[i - 1].sub_lane < (j + 1): lanelinker.add_link( lanesections[i - 1].leftlanes[j], lanesections[i].leftlanes[j - 1], ) elif left_lane[i - 1].sub_lane > (j + 1): lanelinker.add_link( lanesections[i - 1].leftlanes[j], lanesections[i].leftlanes[j] ) else: # same number of lanes, just add the links for j in range(left_lane[i - 1].n_lanes_end): lanelinker.add_link( lanesections[i - 1].leftlanes[j], lanesections[i].leftlanes[j] ) # Add the lanesections to the lanes struct together the lanelinker lanes = Lanes() for ls in lanesections: lanes.add_lanesection(ls, lanelinker) return lanes
def std_roadmark_broken()
-
Expand source code
def std_roadmark_broken(): roadmark = RoadMark(RoadMarkType.broken, 0.2) roadmark.add_specific_road_line(RoadLine(0.15, 3, 9, 0, 0)) return roadmark
def std_roadmark_broken_broken()
-
Expand source code
def std_roadmark_broken_broken(): roadmark = RoadMark(RoadMarkType.broken_broken) roadmark.add_specific_road_line(RoadLine(0.2, 3, 3, 0.2, 0)) roadmark.add_specific_road_line(RoadLine(0.2, 3, 3, -0.2, 0)) return roadmark
def std_roadmark_broken_long_line()
-
Expand source code
def std_roadmark_broken_long_line(): roadmark = RoadMark(RoadMarkType.broken, 0.2) roadmark.add_specific_road_line(RoadLine(0.15, 9, 3, 0, 0)) return roadmark
def std_roadmark_broken_solid()
-
Expand source code
def std_roadmark_broken_solid(): roadmark = RoadMark(RoadMarkType.broken_solid) roadmark.add_specific_road_line(RoadLine(0.2, 0, 0, -0.2, 0)) roadmark.add_specific_road_line(RoadLine(0.2, 3, 3, 0.2, 0)) return roadmark
def std_roadmark_broken_tight()
-
Expand source code
def std_roadmark_broken_tight(): roadmark = RoadMark(RoadMarkType.broken, 0.2) roadmark.add_specific_road_line(RoadLine(0.15, 3, 3, 0, 0)) return roadmark
def std_roadmark_solid()
-
Expand source code
def std_roadmark_solid(): return RoadMark(RoadMarkType.solid, 0.2)
def std_roadmark_solid_broken()
-
Expand source code
def std_roadmark_solid_broken(): roadmark = RoadMark(RoadMarkType.solid_broken) roadmark.add_specific_road_line(RoadLine(0.2, 0, 0, 0.2, 0)) roadmark.add_specific_road_line(RoadLine(0.2, 3, 3, -0.2, 0)) return roadmark
def std_roadmark_solid_solid()
-
Expand source code
def std_roadmark_solid_solid(): roadmark = RoadMark(RoadMarkType.solid_solid) roadmark.add_specific_road_line(RoadLine(0.2, 0, 0, 0.2, 0)) roadmark.add_specific_road_line(RoadLine(0.2, 0, 0, -0.2, 0)) return roadmark
Classes
class LaneDef (s_start, s_end, n_lanes_start, n_lanes_end, sub_lane=None, lane_start_widths=[], lane_end_widths=[])
-
LaneDef is used to help create a lane merge or split. Can handle one lane merging or spliting.
NOTE: This is not part of the OpenDRIVE standard, but a helper for the xodr module.
Parameters
s_start (float): s coordinate of the start of the change s_end (float): s coordinate of the end of the change n_lanes_start (int): number of lanes at s_start n_lanes_end (int): number of lanes at s_end sub_lane (int): the lane that should be created (split) or removed (merge) lane_start_widths (list of float): widths of lanes at start, must be [] or same length as n_lanes_start Default: [] lane_end_widths (list of float): widths of lanes at end, must be [] or same length as n_lanes_end Default: same as lane_start_widths
Attributes
s_start (float): s coordinate of the start of the change s_end (float): s coordinate of the end of the change n_lanes_start (int): number of lanes at s_start n_lanes_end (int): number of lanes at s_end sub_lane (int): the lane that should be created (split) or removed (merge) lane_start_widths (list of float): widths of lanes at start, must be [] or same length as n_lanes_start lane_end_widths (list of float): widths of lanes at end, must be [] or same length as n_lanes_end
Expand source code
class LaneDef: """LaneDef is used to help create a lane merge or split. Can handle one lane merging or spliting. NOTE: This is not part of the OpenDRIVE standard, but a helper for the xodr module. Parameters ---------- s_start (float): s coordinate of the start of the change s_end (float): s coordinate of the end of the change n_lanes_start (int): number of lanes at s_start n_lanes_end (int): number of lanes at s_end sub_lane (int): the lane that should be created (split) or removed (merge) lane_start_widths (list of float): widths of lanes at start, must be [] or same length as n_lanes_start Default: [] lane_end_widths (list of float): widths of lanes at end, must be [] or same length as n_lanes_end Default: same as lane_start_widths Attributes ---------- s_start (float): s coordinate of the start of the change s_end (float): s coordinate of the end of the change n_lanes_start (int): number of lanes at s_start n_lanes_end (int): number of lanes at s_end sub_lane (int): the lane that should be created (split) or removed (merge) lane_start_widths (list of float): widths of lanes at start, must be [] or same length as n_lanes_start lane_end_widths (list of float): widths of lanes at end, must be [] or same length as n_lanes_end """ def __init__( self, s_start, s_end, n_lanes_start, n_lanes_end, sub_lane=None, lane_start_widths=[], lane_end_widths=[], ): self.s_start = s_start self.s_end = s_end self.n_lanes_start = n_lanes_start self.n_lanes_end = n_lanes_end self.sub_lane = sub_lane self.lane_start_widths = lane_start_widths if lane_end_widths == []: self.lane_end_widths = self.lane_start_widths.copy() else: self.lane_end_widths = lane_end_widths def _adjust_lane_widths(self): if self.sub_lane: if self.lane_end_widths and len(self.lane_end_widths) < self.n_lanes_start: # mergeo self.lane_end_widths.insert(abs(self.sub_lane) - 1, 0) elif ( self.lane_start_widths and len(self.lane_start_widths) < self.n_lanes_end ): # split self.lane_start_widths.insert(abs(self.sub_lane) - 1, 0) # TODO: add some checks here?