Module scenariogeneration.xodr.generators

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.

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 of ready to use functions, to generate standard road snipets, like:
    - Simple straight road
    - Spiral-Arc-Spiral type of turns
    - Simple roads with different geometries and lanes
    - Simple junction roads
        limited to 3/4-way crossings with 90degree turns (3-way can be 120 deg aswell)
    - Creation of the junction based on the connecting roads and incomming/outgoing roads
"""
import numpy as np
import pyclothoids as pcloth

from .lane import Lane, RoadMark, LaneSection, Lanes, RoadLine
from .enumerations import (
    JunctionType,
    RoadMarkType,
    MarkRule,
    ContactPoint,
    ElementType,
    ObjectType,
)

from .geometry import Line, Arc, Spiral, PlanView, AdjustablePlanview
from .opendrive import Road
from .links import Junction, Connection, _get_related_lanesection, LaneLinker
from .lane_def import (
    LaneDef,
    create_lanes_merge_split,
    std_roadmark_broken,
    std_roadmark_broken_solid,
    std_roadmark_broken_tight,
    std_roadmark_broken_broken,
    std_roadmark_broken_long_line,
    std_roadmark_solid_broken,
    std_roadmark_solid_solid,
    std_roadmark_solid,
)
from .exceptions import (
    GeneralIssueInputArguments,
    NotSameAmountOfLanesError,
    RemovedFunctionality,
)

from warnings import warn


STD_ROADMARK_SOLID = std_roadmark_broken()
STD_ROADMARK_BROKEN = std_roadmark_broken()
STD_ROADMARK_BROKEN_TIGHT = std_roadmark_broken_tight()
STD_ROADMARK_BROKEN_BROKEN = std_roadmark_broken_broken()
STD_ROADMARK_SOLID_SOLID = std_roadmark_solid_solid()
STD_ROADMARK_SOLID_BROKEN = std_roadmark_solid_broken()
STD_ROADMARK_BROKEN_SOLID = std_roadmark_broken_solid()


STD_START_CLOTH = 1 / 1000000000


def standard_lane(offset=3, rm=std_roadmark_broken()):
    """standard_lane creates a simple lane with an offset an a roadmark

    Parameters
    ----------
        offset (int): width of the lane
            default: 3

        rm (RoadMark): road mark used for the standard lane
            default:  RoadMark(STD_ROADMARK_BROKEN)
    Returns
    -------
        lane (Lane): the lane

    """
    lc = Lane(a=offset)
    lc.add_roadmark(rm)
    return lc


def create_road(
    geometry,
    id,
    left_lanes=1,
    right_lanes=1,
    road_type=-1,
    center_road_mark=std_roadmark_solid(),
    lane_width=3,
    lane_width_end=None,
):
    """create_road creates a road with one lanesection with different number of lanes, lane marks will be of type broken,
    except the outer lane, that will be solid.
    The lane_width_end parameter can only be used when inputs for left_lanes and right_lanes are int

    Parameters
    ----------
        geometry (Line, Spiral, ParamPoly3, Arc, a list of(Line, Spiral, ParamPoly3, Arc), or AdjustablePlanview ): geometries to build the road

        id (int): id of the new road

        left_lanes (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.
            Default: 1

        right_lanes (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
            Default: 1

        road_type (int): type of road, -1 normal road, otherwise connecting road

        center_road_mark (RoadMark): roadmark for the center line

        lane_width (float): the width of all lanes

        lane_width_end (float): the end width of all lanes

    Returns
    -------
        road (Road): a straight road
    """

    if isinstance(left_lanes, LaneDef):
        left_lanes = [left_lanes]
    if isinstance(right_lanes, LaneDef):
        right_lanes = [right_lanes]

    pv = PlanView()
    raw_length = 0
    if isinstance(geometry, list):
        for g in geometry:
            pv.add_geometry(g)
            raw_length += g.length
    elif isinstance(geometry, AdjustablePlanview):
        pv = geometry
        pv.left_lane_defs = left_lanes
        pv.right_lane_defs = right_lanes
        pv.center_road_mark = center_road_mark
        pv.lane_width = lane_width
        pv.lane_width_end = lane_width_end
        # create a dummy length
        raw_length = 0

        if isinstance(left_lanes, list) and any(
            isinstance(x, LaneDef) for x in left_lanes
        ):
            raw_length = max(raw_length, max([x.s_end for x in left_lanes]))
        if isinstance(right_lanes, list) and any(
            isinstance(x, LaneDef) for x in right_lanes
        ):
            raw_length = max(raw_length, max([x.s_end for x in right_lanes]))
        if raw_length == 0:
            raw_length = 100  # just a dummy value
    else:
        pv.add_geometry(geometry)
        raw_length += geometry.length

    if (lane_width_end is not None) and (
        (type(left_lanes) != int) or (type(right_lanes) != int)
    ):
        raise RuntimeError(
            "lane_width_end can only be used when left_lanes and right_lanes are int"
        )
    if (
        right_lanes is not None
        and left_lanes is not None
        or center_road_mark is not None
    ):
        lanes = create_lanes_merge_split(
            right_lanes,
            left_lanes,
            raw_length,
            center_road_mark,
            lane_width,
            lane_width_end=lane_width_end,
        )
    else:
        lanes = None

    road = Road(id, pv, lanes, road_type=road_type)

    return road


def create_straight_road(road_id, length=100, junction=-1, n_lanes=1, lane_offset=3):
    """creates a standard straight road with two lanes
    STD_ROADMARK_SOLID
        Parameters
        ----------
            road_id (int): id of the road to create

            length (float): length of the road
                default: 100

            junction (int): if the road belongs to a junction or not
                default: -1

            n_lanes (int): number of lanes
                default: 1

            lane_offset (int): width of the road
                default: 3

        Returns
        -------
            road (Road): a straight road
    """
    warn(
        "create_straight_road should not be used anymore, please use the create_road function instead",
        DeprecationWarning,
        2,
    )
    # create geometry
    line1 = Line(length)

    # create planviews
    planview1 = PlanView()
    planview1.add_geometry(line1)

    # create lanesections
    lanesec1 = LaneSection(0, standard_lane())
    for i in range(1, n_lanes + 1, 1):
        lanesec1.add_right_lane(standard_lane(lane_offset))
        lanesec1.add_left_lane(standard_lane(lane_offset))

    # create lanes
    lanes1 = Lanes()
    lanes1.add_lanesection(lanesec1)

    # finally create the roads
    return Road(road_id, planview1, lanes1, road_type=junction)


def create_cloth_arc_cloth(
    arc_curv,
    arc_angle,
    cloth_angle,
    r_id,
    junction=1,
    cloth_start=STD_START_CLOTH,
    n_lanes=1,
    lane_offset=3,
):
    """creates a curved Road  with a Spiral - Arc - Spiral, and two lanes

    Parameters
    ----------
        arc_curv (float): curvature of the arc (and max clothoid of clothoids)

        arc_angle (float): how much of the curv should be the arc

        cloth_angle (float): how much of the curv should be the clothoid (will be doubled since there are two clothoids)

        r_id (int): the id of the road

        junction (int): if the Road belongs to a junction
            default: 1

        cloth_start (float): staring curvature of clothoids

        n_lanes (int): number of lanes
            default: 1

        lane_offset (int): width of the road
            default: 3

    Returns
    -------
        road (Road): a road built up of a Spiral-Arc-Spiral
    """
    warn(
        "create_cloth_arc_cloth should not be used anymore, please use the create_road (see exampels/xodr/clothoid_generation.py) function instead",
        DeprecationWarning,
        2,
    )
    pv = PlanView()
    # adjust sign if angle is negative
    if cloth_angle < 0 and arc_curv > 0:
        cloth_angle = -cloth_angle
        arc_curv = -arc_curv
        cloth_start = -cloth_start
        arc_angle = -arc_angle

    # create geometries
    spiral1 = Spiral(cloth_start, arc_curv, angle=cloth_angle)
    arc = Arc(arc_curv, angle=arc_angle)
    spiral2 = Spiral(arc_curv, cloth_start, angle=cloth_angle)

    pv.add_geometry(spiral1)
    pv.add_geometry(arc)
    pv.add_geometry(spiral2)

    # create lanes
    lsec = LaneSection(0, standard_lane())
    for i in range(1, n_lanes + 1, 1):
        lsec.add_right_lane(standard_lane(lane_offset))
        lsec.add_left_lane(standard_lane(lane_offset))
    lanes = Lanes()
    lanes.add_lanesection(lsec)

    # create road
    return Road(r_id, pv, lanes, road_type=junction)


def create_3cloths(
    cloth1_start,
    cloth1_end,
    cloth1_length,
    cloth2_start,
    cloth2_end,
    cloth2_length,
    cloth3_start,
    cloth3_end,
    cloth3_length,
    r_id,
    junction=1,
    n_lanes=1,
    lane_offset=3,
    road_marks=std_roadmark_broken(),
):
    """creates a curved Road  with a Spiral - Arc - Spiral, and two lanes

    Parameters
    ----------
        cloth1_start (float): initial curvature of spiral 1

        cloth1_end (float): ending curvature of spiral 1

        cloth1_length (float): total length of spiral 1

        cloth2_start (float): initial curvature of spiral 2

        cloth2_end (float): ending curvature of spiral 2

        cloth2_length (float): total length of spiral 2

        cloth3_start (float): initial curvature of spiral 3

        cloth3_end (float): ending curvature of spiral 3

        cloth3_length (float): total length of spiral 3

        r_id (int): the id of the road

        junction (int): if the Road belongs to a junction
            default: 1

        cloth_start (float): staring curvature of clothoids

        n_lanes (int): number of lanes
            default: 1

        lane_offset (int): width of the road
            default: 3

    Returns
    -------
        road (Road): a road built up of a Spiral-Spiral-Spiral
    """
    warn(
        "create_cloth_arc_cloth should not be used anymore, please use the create_road (see exampels/xodr/clothoid_generation.py) function instead",
        DeprecationWarning,
        2,
    )
    pv = PlanView()

    # create geometries
    spiral1 = Spiral(cloth1_start, cloth1_end, length=cloth1_length)
    spiral2 = Spiral(cloth2_start, cloth2_end, length=cloth2_length)
    spiral3 = Spiral(cloth3_start, cloth3_end, length=cloth3_length)

    pv.add_geometry(spiral1)
    pv.add_geometry(spiral2)
    pv.add_geometry(spiral3)

    # create lanes
    center_lane = Lane()
    if road_marks:
        center_lane.add_roadmark(road_marks)
    lsec = LaneSection(0, center_lane)

    for i in range(1, n_lanes + 1, 1):
        rl = Lane(a=lane_offset)
        ll = Lane(a=lane_offset)
        if road_marks:
            rl.add_roadmark(road_marks)
            ll.add_roadmark(road_marks)
        lsec.add_right_lane(rl)
        lsec.add_left_lane(ll)
    lanes = Lanes()
    lanes.add_lanesection(lsec)

    # create road
    return Road(r_id, pv, lanes, road_type=junction)


def get_lanes_offset(road1, road2, contactpoint):
    """returns number of lanes (hp #left lanes = # right lanes) and their offset (hp offset is constant)


    Parameters
    ----------
        road1 (Road): first road

        road2 (Road): second road

    Returns
    -------
        n_lanes (int):

        lane_offset (int):
    """

    # now we always look at lanesection[0] to take the number of lanes
    # TO DO - understand if the roads are connect through end or start and then take the relative lane section
    if contactpoint == ContactPoint.end:
        n_lanesection = 0
    else:
        n_lanesection = -1
    if len(road1.lanes.lanesections[n_lanesection].leftlanes) == len(
        road2.lanes.lanesections[0].leftlanes
    ) and len(road1.lanes.lanesections[n_lanesection].rightlanes) == len(
        road2.lanes.lanesections[0].rightlanes
    ):
        n_lanes = len(road1.lanes.lanesections[n_lanesection].leftlanes)
        lane_offset = road1.lanes.lanesections[n_lanesection].leftlanes[0].widths[0].a
    else:
        raise NotSameAmountOfLanesError(
            "Incoming road ",
            road1.id,
            " and outcoming road ",
            road2.id,
            "do not have the same number of left lanes.",
        )

    return n_lanes, lane_offset


def create_junction_roads_standalone(
    angles,
    r,
    junction=1,
    spiral_part=1 / 3,
    arc_part=1 / 3,
    startnum=100,
    n_lanes=1,
    lane_width=3,
):
    """creates all needed roads for some simple junctions, the curved parts of the junction are created as a spiral-arc-spiral combo
    - 3way crossings (either a T junction, or 120 deg junction)
    - 4way crossing (all 90 degree turns)
    NOTE: this will not generate any links or add any successor/predecessors to the roads, and has to be added manually,
    if you have the connecting roads please use create_junction_roads

    Parameters
    ----------

        angles (list of float): the angles from where the roads should be coming in (see description for what is supported),
                                to be defined in mathimatically positive order, beginning with the first incoming road

        r (float): the radius of the arcs in the junction (will determine the size of the junction)

        junction (int): the id of the junction
            default: 1

        spiral_part (float): the part of the curve that should be spirals (two of these) spiral_part*2 + arcpart = angle of the turn
            default: (1/3)

        arc_part (float): the part of the curve that should be an arc:  spiral_part*2 + arcpart = angle of the turn
            default: (1/3)

        startnum (int): start number of the roads in the junctions (will increase with 1 for each road)

        n_lanes (int): the number of lanes in the junction

        lane_width (float): the lane width of the lanes in the junction
    Returns
    -------
        junction_roads (list of Road): a list of all roads in a junction without connections added

    """
    warn(
        "create_junction_roads_standalone should not be used anymore, please use the CommonJunctionCreator function instead",
        DeprecationWarning,
        2,
    )
    angle = np.pi / 2
    angle_cloth = angle * spiral_part
    spiral_length = 2 * abs(angle_cloth * r)

    cloth = pcloth.Clothoid.StandardParams(
        0,
        0,
        0,
        STD_START_CLOTH,
        (1 / r - STD_START_CLOTH) / spiral_length,
        spiral_length,
    )

    X0 = cloth.XEnd - r * np.sin(angle_cloth)
    Y0 = cloth.YEnd - r * (1 - np.cos(angle_cloth))
    linelength = 2 * (X0 + r + Y0)

    junction_roads = []

    for i in range(len(angles) - 1):
        for j in range(1 + i, len(angles)):
            # check angle needed for junction
            an = np.sign(angles[j] - angles[i] - np.pi)
            an1 = angles[j] - angles[i] - np.pi
            angle_arc = an1 * arc_part

            angle_cloth = an1 * spiral_part

            # adjust angle if multiple of pi
            if an1 > np.pi:
                an1 = -(2 * np.pi - an1)

            # create road, either straight or curved
            if an == 0:
                tmp_junc = create_straight_road(
                    startnum,
                    length=linelength,
                    junction=junction,
                    n_lanes=n_lanes,
                    lane_offset=lane_width,
                )
            else:
                tmp_junc = create_cloth_arc_cloth(
                    1 / r,
                    angle_arc,
                    angle_cloth,
                    startnum,
                    junction,
                    n_lanes=n_lanes,
                    lane_offset=lane_width,
                )

            # add predecessor and successor
            startnum += 1
            junction_roads.append(tmp_junc)

    return junction_roads


def create_junction_roads_from_arc(
    roads, angles, r=0, junction=1, arc_part=1 / 3, startnum=100
):
    """creates all needed roads for some simple junctions, the curved parts of the junction are created as a spiral-arc-spiral combo
    Supported junctions:
    - 3way crossings (either a T junction, or 120 deg junction)
    - 4way crossing (all 90 degree turns)

    Parameters
    ----------
        roads (list of Road): all roads that should go into the junction

        angles (list of float): the angles from where the roads should be coming in (see description for what is supported),
                                to be defined in mathimatically positive order, beginning with the first incoming road [0, +2pi]

        r (float): the radius of the arcs in the junction (will determine the size of the junction)

        junction (int): the id of the junction
            default: 1

        spiral_part (float): the part of the curve that should be spirals (two of these) spiral_part*2 + arcpart = angle of the turn
            default: (1/3)

        arc_part (float): the part of the curve that should be an arc:  spiral_part*2 + arcpart = angle of the turn
            default: (1/3)

        startnum (int): start number of the roads in the junctions (will increase with 1 for each road)

    Returns
    -------
        junction_roads (list of Road): a list of all roads needed for all traffic connecting the roads
    """
    warn(
        "create_junction_roads_from_arc should not be used anymore, please use the CommonJunctionCreator function instead",
        DeprecationWarning,
        2,
    )
    # arc_part = 1 - 2*spiral_part
    spiral_part = (1 - arc_part) / 2

    angle = np.pi / 2
    angle_cloth = angle * spiral_part
    spiral_length = 2 * abs(angle_cloth * r)

    cloth = pcloth.Clothoid.StandardParams(
        0,
        0,
        0,
        STD_START_CLOTH,
        (1 / r - STD_START_CLOTH) / spiral_length,
        spiral_length,
    )

    X0 = cloth.XEnd - r * np.sin(angle_cloth)
    Y0 = cloth.YEnd - r * (1 - np.cos(angle_cloth))

    linelength = 2 * (X0 + r + Y0)

    junction_roads = []

    # loop over the roads to get all possible combinations of connecting roads
    for i in range(len(roads) - 1):
        # for now the first road is place as base,
        if i == 0:
            cp = ContactPoint.end
            roads[i].add_successor(ElementType.junction, junction)
        else:
            cp = ContactPoint.start
            roads[i].add_predecessor(ElementType.junction, junction)

        for j in range(1 + i, len(roads)):
            # check angle needed for junction [-pi, +pi]
            an1 = angles[j] - angles[i] - np.pi
            # adjust angle if multiple of pi
            if an1 > np.pi:
                an1 = -(2 * np.pi - an1)

            angle_arc = an1 * arc_part
            angle_cloth = an1 * spiral_part

            sig = np.sign(an1)

            # create road, either straight or curved
            n_lanes, lanes_offset = get_lanes_offset(roads[i], roads[j], cp)
            if sig == 0:
                # create straight road
                tmp_junc = create_straight_road(
                    startnum,
                    length=linelength,
                    junction=junction,
                    n_lanes=n_lanes,
                    lane_offset=lanes_offset,
                )
            else:
                # create the cloth-arc-cloth road given the radius fo the arc
                tmp_junc = create_cloth_arc_cloth(
                    1 / r,
                    angle_arc,
                    angle_cloth,
                    startnum,
                    junction,
                    n_lanes=n_lanes,
                    lane_offset=lanes_offset,
                )

            # add predecessor and successor
            tmp_junc.add_predecessor(ElementType.road, roads[i].id, cp)
            tmp_junc.add_successor(ElementType.road, roads[j].id, ContactPoint.start)
            startnum += 1
            junction_roads.append(tmp_junc)

    # add junction to the last road aswell since it's not part of the loop
    roads[-1].add_predecessor(ElementType.junction, junction)

    return junction_roads


def create_junction_roads(
    roads,
    angles,
    R,
    junction=1,
    arc_part=1 / 3,
    startnum=100,
    inner_road_marks=None,
    outer_road_marks=std_roadmark_solid(),
):
    """creates all needed roads for some simple junctions, the curved parts of the junction are created as a spiral-arc-spiral combo
    R is value to the the radius of the whole junction (meaning R = distance between the center of the junction and any external road attached to the junction)
    Supportes all angles and number of roads.

    Parameters
    ----------
        roads (list of Road): all roads that should go into the junction

        angles (list of float): the angles from where the roads should be coming in (see description for what is supported),
                                to be defined in mathimatically positive order, beginning with the first incoming road [0, +2pi]

        R (list of float): the radius of the whole junction, meaning the distance between roads and the center of the junction. If only one value is specified, then all roads will have the same distance.

        junction (int): the id of the junction
            default: 1

        spiral_part (float): the part of the curve that should be spirals (two of these) spiral_part*2 + arcpart = angle of the turn
            default: (1/3)

        startnum (int): start number of the roads in the junctions (will increase with 1 for each road)
            default: 100

        inner_road_marks (RoadMark): the RoadMark that all lanes inside the junction will have (outer will be solid)
            Default: None

        outer_road_marks (RoadMark): the roadmark that will be on the edge of the connecting roads (limit the junction)
            Default: STD_ROADMARK_SOLID
    Returns
    -------
        junction_roads (list of Road): a list of all roads needed for all traffic connecting the roads
    """
    warn(
        "create_junction_roads_from_arc should not be used anymore, please use the CommonJunctionCreator function instead",
        DeprecationWarning,
        2,
    )
    if len(roads) is not len(angles):
        raise GeneralIssueInputArguments("roads and angles do not have the same size.")

    if len(R) == 1:
        R = R * np.ones(len(roads))
    elif len(R) > 1 and len(R) is not len(roads):
        raise GeneralIssueInputArguments("roads and R do not have the same size.")

    # linelength = 2*R
    junction_roads = []

    # loop over the roads to get all possible combinations of connecting roads
    for i in range(len(roads) - 1):
        # for now the first road is place as base,
        if i == 0:
            cp = ContactPoint.end
            roads[i].add_successor(ElementType.junction, junction)
        else:
            cp = ContactPoint.start
            roads[i].add_predecessor(ElementType.junction, junction)

        for j in range(1 + i, len(roads)):
            # check angle needed for junction [-pi, +pi]
            an1 = angles[j] - angles[i] - np.pi
            # adjust angle if multiple of pi
            if an1 > np.pi:
                an1 = -(2 * np.pi - an1)

            sig = np.sign(an1)

            # create road, either straight or curved
            n_lanes, lanes_offset = get_lanes_offset(roads[i], roads[j], cp)
            if sig == 0:
                # create straight road
                linelength = R[i] + R[j]
                tmp_junc = create_straight_road(
                    startnum,
                    length=linelength,
                    junction=junction,
                    n_lanes=n_lanes,
                    lane_offset=lanes_offset,
                )
                if inner_road_marks:
                    for l in tmp_junc.lanes.lanesections[0].leftlanes:
                        l.roadmark[0] = inner_road_marks
                    for r in tmp_junc.lanes.lanesections[0].rightlanes:
                        r.roadmark[0] = inner_road_marks
                    tmp_junc.lanes.lanesections[0].centerlane.roadmark[
                        0
                    ] = inner_road_marks
                if len(roads) == 3:
                    # not sure all will be needed since angles have to be in increasing order, but it "should work"
                    k = [x for x in [0, 1, 2] if x != j and x != i][0]
                    if (angles[i] > angles[j]) and (
                        (angles[k] > angles[j]) or (angles[k] < angles[i])
                    ):
                        tmp_junc.lanes.lanesections[0].rightlanes[-1].roadmark[
                            0
                        ] = outer_road_marks
                    elif (angles[i] < angles[j]) and (
                        (angles[k] > angles[j]) or (angles[k] < angles[i])
                    ):
                        tmp_junc.lanes.lanesections[0].rightlanes[-1].roadmark[
                            0
                        ] = outer_road_marks
                    elif (angles[i] < angles[j]) and (
                        (angles[k] < angles[j]) or (angles[k] > angles[i])
                    ):
                        tmp_junc.lanes.lanesections[0].leftlanes[-1].roadmark[
                            0
                        ] = outer_road_marks
                    else:
                        tmp_junc.lanes.lanesections[0].rightlanes[-1].roadmark[
                            0
                        ] = outer_road_marks
            else:
                clothoids = pcloth.SolveG2(
                    -R[i],
                    0,
                    0,
                    STD_START_CLOTH,
                    R[j] * np.cos(an1),
                    R[j] * np.sin(an1),
                    an1,
                    STD_START_CLOTH,
                )
                tmp_junc = create_3cloths(
                    clothoids[0].KappaStart,
                    clothoids[0].KappaEnd,
                    clothoids[0].length,
                    clothoids[1].KappaStart,
                    clothoids[1].KappaEnd,
                    clothoids[1].length,
                    clothoids[2].KappaStart,
                    clothoids[2].KappaEnd,
                    clothoids[2].length,
                    startnum,
                    junction,
                    n_lanes=n_lanes,
                    lane_offset=lanes_offset,
                    road_marks=inner_road_marks,
                )

                if tmp_junc.planview._raw_geometries[1].curvstart > 0:
                    tmp_junc.lanes.lanesections[0].leftlanes[-1].add_roadmark(
                        outer_road_marks
                    )
                if tmp_junc.planview._raw_geometries[1].curvstart < 0:
                    tmp_junc.lanes.lanesections[0].rightlanes[-1].add_roadmark(
                        outer_road_marks
                    )
            # add predecessor and successor
            tmp_junc.add_predecessor(ElementType.road, roads[i].id, cp)
            tmp_junc.add_successor(ElementType.road, roads[j].id, ContactPoint.start)
            startnum += 1
            junction_roads.append(tmp_junc)

    # add junction to the last road aswell since it's not part of the loop
    roads[-1].add_predecessor(ElementType.junction, junction)

    return junction_roads


def _create_junction_links(
    connection, nlanes, r_or_l, sign, from_offset=0, to_offset=0
):
    """helper function to create junction links

    Parameters
    ----------
        connection (Connection): the connection to fill

        nlanes (int): number of lanes

        r_or_l (1 or -1): if the lane should start from -1 or 1

        sign (1 or -1): if the sign should change

        from_offset (int): if there is an offset in the beginning
            Default: 0

        to_offset (int): if there is an offset in the end of the road
            Default: 0
    """
    for i in range(1, nlanes + 1, 1):
        connection.add_lanelink(r_or_l * i + from_offset, r_or_l * sign * i + to_offset)


def create_junction(junction_roads, id, roads, name="my junction"):
    """create_junction creates the junction struct for a set of roads


    Parameters
    ----------
        junction_roads (list of Road): all connecting roads in the junction

        id (int): the id of the junction

        roads (list of Road): all incomming roads to the junction

        name(str): name of the junction
        default: 'my junction'

    Returns
    -------
        junction (Junction): the junction struct ready to use

    """

    junc = Junction(name, id)

    for jr in junction_roads:
        # handle succesor lanes
        conne1 = Connection(jr.successor.element_id, jr.id, ContactPoint.end)
        _, sign, _ = _get_related_lanesection(
            jr, get_road_by_id(roads, jr.successor.element_id)
        )

        _create_junction_links(
            conne1,
            len(jr.lanes.lanesections[-1].rightlanes),
            -1,
            sign,
            to_offset=jr.lane_offset_suc[str(jr.successor.element_id)],
        )
        _create_junction_links(
            conne1,
            len(jr.lanes.lanesections[-1].leftlanes),
            1,
            sign,
            to_offset=jr.lane_offset_suc[str(jr.successor.element_id)],
        )
        junc.add_connection(conne1)

        # handle predecessor lanes
        conne2 = Connection(jr.predecessor.element_id, jr.id, ContactPoint.start)
        _, sign, _ = _get_related_lanesection(
            jr, get_road_by_id(roads, jr.predecessor.element_id)
        )
        _create_junction_links(
            conne2,
            len(jr.lanes.lanesections[0].rightlanes),
            -1,
            sign,
            from_offset=jr.lane_offset_pred[str(jr.predecessor.element_id)],
        )
        _create_junction_links(
            conne2,
            len(jr.lanes.lanesections[0].leftlanes),
            1,
            sign,
            from_offset=jr.lane_offset_pred[str(jr.predecessor.element_id)],
        )
        junc.add_connection(conne2)
    return junc


def create_direct_junction(roads, id, name="my direct junction"):
    """create_direct_junction creates the junction struct for a set of roads, for a direct junction

    Parameters
    ----------
        roads (list of Road): all roads that are building up the direct junction

        id (int): the id of the junction

        name(str): name of the junction
        default: 'my direct junction'

    Returns
    -------
        junction (Junction): the junction struct ready to use

    """
    raise RemovedFunctionality(
        "The create_direct_junction has been removed, due to its very limited functionality, please try the xodr.DirectJunctionCreator instead."
    )


def get_road_by_id(roads, id):
    """get_road_by_id returns a road based on the road id

    Parameters
    ----------
        roads (list of Roads): a list of roads to seach through

        id (int): the id of the road wanted

    Returns
    -------
        Road
    """
    for r in roads:
        if r.id == id:
            return r

Functions

def create_3cloths(cloth1_start, cloth1_end, cloth1_length, cloth2_start, cloth2_end, cloth2_length, cloth3_start, cloth3_end, cloth3_length, r_id, junction=1, n_lanes=1, lane_offset=3, road_marks=<scenariogeneration.xodr.lane.RoadMark object>)

creates a curved Road with a Spiral - Arc - Spiral, and two lanes

Parameters

cloth1_start (float): initial curvature of spiral 1

cloth1_end (float): ending curvature of spiral 1

cloth1_length (float): total length of spiral 1

cloth2_start (float): initial curvature of spiral 2

cloth2_end (float): ending curvature of spiral 2

cloth2_length (float): total length of spiral 2

cloth3_start (float): initial curvature of spiral 3

cloth3_end (float): ending curvature of spiral 3

cloth3_length (float): total length of spiral 3

r_id (int): the id of the road

junction (int): if the Road belongs to a junction
    default: 1

cloth_start (float): staring curvature of clothoids

n_lanes (int): number of lanes
    default: 1

lane_offset (int): width of the road
    default: 3

Returns

road (Road): a road built up of a Spiral-Spiral-Spiral
Expand source code
def create_3cloths(
    cloth1_start,
    cloth1_end,
    cloth1_length,
    cloth2_start,
    cloth2_end,
    cloth2_length,
    cloth3_start,
    cloth3_end,
    cloth3_length,
    r_id,
    junction=1,
    n_lanes=1,
    lane_offset=3,
    road_marks=std_roadmark_broken(),
):
    """creates a curved Road  with a Spiral - Arc - Spiral, and two lanes

    Parameters
    ----------
        cloth1_start (float): initial curvature of spiral 1

        cloth1_end (float): ending curvature of spiral 1

        cloth1_length (float): total length of spiral 1

        cloth2_start (float): initial curvature of spiral 2

        cloth2_end (float): ending curvature of spiral 2

        cloth2_length (float): total length of spiral 2

        cloth3_start (float): initial curvature of spiral 3

        cloth3_end (float): ending curvature of spiral 3

        cloth3_length (float): total length of spiral 3

        r_id (int): the id of the road

        junction (int): if the Road belongs to a junction
            default: 1

        cloth_start (float): staring curvature of clothoids

        n_lanes (int): number of lanes
            default: 1

        lane_offset (int): width of the road
            default: 3

    Returns
    -------
        road (Road): a road built up of a Spiral-Spiral-Spiral
    """
    warn(
        "create_cloth_arc_cloth should not be used anymore, please use the create_road (see exampels/xodr/clothoid_generation.py) function instead",
        DeprecationWarning,
        2,
    )
    pv = PlanView()

    # create geometries
    spiral1 = Spiral(cloth1_start, cloth1_end, length=cloth1_length)
    spiral2 = Spiral(cloth2_start, cloth2_end, length=cloth2_length)
    spiral3 = Spiral(cloth3_start, cloth3_end, length=cloth3_length)

    pv.add_geometry(spiral1)
    pv.add_geometry(spiral2)
    pv.add_geometry(spiral3)

    # create lanes
    center_lane = Lane()
    if road_marks:
        center_lane.add_roadmark(road_marks)
    lsec = LaneSection(0, center_lane)

    for i in range(1, n_lanes + 1, 1):
        rl = Lane(a=lane_offset)
        ll = Lane(a=lane_offset)
        if road_marks:
            rl.add_roadmark(road_marks)
            ll.add_roadmark(road_marks)
        lsec.add_right_lane(rl)
        lsec.add_left_lane(ll)
    lanes = Lanes()
    lanes.add_lanesection(lsec)

    # create road
    return Road(r_id, pv, lanes, road_type=junction)
def create_cloth_arc_cloth(arc_curv, arc_angle, cloth_angle, r_id, junction=1, cloth_start=1e-09, n_lanes=1, lane_offset=3)

creates a curved Road with a Spiral - Arc - Spiral, and two lanes

Parameters

arc_curv (float): curvature of the arc (and max clothoid of clothoids)

arc_angle (float): how much of the curv should be the arc

cloth_angle (float): how much of the curv should be the clothoid (will be doubled since there are two clothoids)

r_id (int): the id of the road

junction (int): if the Road belongs to a junction
    default: 1

cloth_start (float): staring curvature of clothoids

n_lanes (int): number of lanes
    default: 1

lane_offset (int): width of the road
    default: 3

Returns

road (Road): a road built up of a Spiral-Arc-Spiral
Expand source code
def create_cloth_arc_cloth(
    arc_curv,
    arc_angle,
    cloth_angle,
    r_id,
    junction=1,
    cloth_start=STD_START_CLOTH,
    n_lanes=1,
    lane_offset=3,
):
    """creates a curved Road  with a Spiral - Arc - Spiral, and two lanes

    Parameters
    ----------
        arc_curv (float): curvature of the arc (and max clothoid of clothoids)

        arc_angle (float): how much of the curv should be the arc

        cloth_angle (float): how much of the curv should be the clothoid (will be doubled since there are two clothoids)

        r_id (int): the id of the road

        junction (int): if the Road belongs to a junction
            default: 1

        cloth_start (float): staring curvature of clothoids

        n_lanes (int): number of lanes
            default: 1

        lane_offset (int): width of the road
            default: 3

    Returns
    -------
        road (Road): a road built up of a Spiral-Arc-Spiral
    """
    warn(
        "create_cloth_arc_cloth should not be used anymore, please use the create_road (see exampels/xodr/clothoid_generation.py) function instead",
        DeprecationWarning,
        2,
    )
    pv = PlanView()
    # adjust sign if angle is negative
    if cloth_angle < 0 and arc_curv > 0:
        cloth_angle = -cloth_angle
        arc_curv = -arc_curv
        cloth_start = -cloth_start
        arc_angle = -arc_angle

    # create geometries
    spiral1 = Spiral(cloth_start, arc_curv, angle=cloth_angle)
    arc = Arc(arc_curv, angle=arc_angle)
    spiral2 = Spiral(arc_curv, cloth_start, angle=cloth_angle)

    pv.add_geometry(spiral1)
    pv.add_geometry(arc)
    pv.add_geometry(spiral2)

    # create lanes
    lsec = LaneSection(0, standard_lane())
    for i in range(1, n_lanes + 1, 1):
        lsec.add_right_lane(standard_lane(lane_offset))
        lsec.add_left_lane(standard_lane(lane_offset))
    lanes = Lanes()
    lanes.add_lanesection(lsec)

    # create road
    return Road(r_id, pv, lanes, road_type=junction)
def create_direct_junction(roads, id, name='my direct junction')

create_direct_junction creates the junction struct for a set of roads, for a direct junction

Parameters

roads (list of Road): all roads that are building up the direct junction

id (int): the id of the junction

name(str): name of the junction
default: 'my direct junction'

Returns

junction (Junction): the junction struct ready to use
Expand source code
def create_direct_junction(roads, id, name="my direct junction"):
    """create_direct_junction creates the junction struct for a set of roads, for a direct junction

    Parameters
    ----------
        roads (list of Road): all roads that are building up the direct junction

        id (int): the id of the junction

        name(str): name of the junction
        default: 'my direct junction'

    Returns
    -------
        junction (Junction): the junction struct ready to use

    """
    raise RemovedFunctionality(
        "The create_direct_junction has been removed, due to its very limited functionality, please try the xodr.DirectJunctionCreator instead."
    )
def create_junction(junction_roads, id, roads, name='my junction')

create_junction creates the junction struct for a set of roads

Parameters

junction_roads (list of Road): all connecting roads in the junction

id (int): the id of the junction

roads (list of Road): all incomming roads to the junction

name(str): name of the junction
default: 'my junction'

Returns

junction (Junction): the junction struct ready to use
Expand source code
def create_junction(junction_roads, id, roads, name="my junction"):
    """create_junction creates the junction struct for a set of roads


    Parameters
    ----------
        junction_roads (list of Road): all connecting roads in the junction

        id (int): the id of the junction

        roads (list of Road): all incomming roads to the junction

        name(str): name of the junction
        default: 'my junction'

    Returns
    -------
        junction (Junction): the junction struct ready to use

    """

    junc = Junction(name, id)

    for jr in junction_roads:
        # handle succesor lanes
        conne1 = Connection(jr.successor.element_id, jr.id, ContactPoint.end)
        _, sign, _ = _get_related_lanesection(
            jr, get_road_by_id(roads, jr.successor.element_id)
        )

        _create_junction_links(
            conne1,
            len(jr.lanes.lanesections[-1].rightlanes),
            -1,
            sign,
            to_offset=jr.lane_offset_suc[str(jr.successor.element_id)],
        )
        _create_junction_links(
            conne1,
            len(jr.lanes.lanesections[-1].leftlanes),
            1,
            sign,
            to_offset=jr.lane_offset_suc[str(jr.successor.element_id)],
        )
        junc.add_connection(conne1)

        # handle predecessor lanes
        conne2 = Connection(jr.predecessor.element_id, jr.id, ContactPoint.start)
        _, sign, _ = _get_related_lanesection(
            jr, get_road_by_id(roads, jr.predecessor.element_id)
        )
        _create_junction_links(
            conne2,
            len(jr.lanes.lanesections[0].rightlanes),
            -1,
            sign,
            from_offset=jr.lane_offset_pred[str(jr.predecessor.element_id)],
        )
        _create_junction_links(
            conne2,
            len(jr.lanes.lanesections[0].leftlanes),
            1,
            sign,
            from_offset=jr.lane_offset_pred[str(jr.predecessor.element_id)],
        )
        junc.add_connection(conne2)
    return junc
def create_junction_roads(roads, angles, R, junction=1, arc_part=0.3333333333333333, startnum=100, inner_road_marks=None, outer_road_marks=<scenariogeneration.xodr.lane.RoadMark object>)

creates all needed roads for some simple junctions, the curved parts of the junction are created as a spiral-arc-spiral combo R is value to the the radius of the whole junction (meaning R = distance between the center of the junction and any external road attached to the junction) Supportes all angles and number of roads.

Parameters

roads (list of Road): all roads that should go into the junction

angles (list of float): the angles from where the roads should be coming in (see description for what is supported),
                        to be defined in mathimatically positive order, beginning with the first incoming road [0, +2pi]

R (list of float): the radius of the whole junction, meaning the distance between roads and the center of the junction. If only one value is specified, then all roads will have the same distance.

junction (int): the id of the junction
    default: 1

spiral_part (float): the part of the curve that should be spirals (two of these) spiral_part*2 + arcpart = angle of the turn
    default: (1/3)

startnum (int): start number of the roads in the junctions (will increase with 1 for each road)
    default: 100

inner_road_marks (RoadMark): the RoadMark that all lanes inside the junction will have (outer will be solid)
    Default: None

outer_road_marks (RoadMark): the roadmark that will be on the edge of the connecting roads (limit the junction)
    Default: STD_ROADMARK_SOLID

Returns

junction_roads (list of Road): a list of all roads needed for all traffic connecting the roads
Expand source code
def create_junction_roads(
    roads,
    angles,
    R,
    junction=1,
    arc_part=1 / 3,
    startnum=100,
    inner_road_marks=None,
    outer_road_marks=std_roadmark_solid(),
):
    """creates all needed roads for some simple junctions, the curved parts of the junction are created as a spiral-arc-spiral combo
    R is value to the the radius of the whole junction (meaning R = distance between the center of the junction and any external road attached to the junction)
    Supportes all angles and number of roads.

    Parameters
    ----------
        roads (list of Road): all roads that should go into the junction

        angles (list of float): the angles from where the roads should be coming in (see description for what is supported),
                                to be defined in mathimatically positive order, beginning with the first incoming road [0, +2pi]

        R (list of float): the radius of the whole junction, meaning the distance between roads and the center of the junction. If only one value is specified, then all roads will have the same distance.

        junction (int): the id of the junction
            default: 1

        spiral_part (float): the part of the curve that should be spirals (two of these) spiral_part*2 + arcpart = angle of the turn
            default: (1/3)

        startnum (int): start number of the roads in the junctions (will increase with 1 for each road)
            default: 100

        inner_road_marks (RoadMark): the RoadMark that all lanes inside the junction will have (outer will be solid)
            Default: None

        outer_road_marks (RoadMark): the roadmark that will be on the edge of the connecting roads (limit the junction)
            Default: STD_ROADMARK_SOLID
    Returns
    -------
        junction_roads (list of Road): a list of all roads needed for all traffic connecting the roads
    """
    warn(
        "create_junction_roads_from_arc should not be used anymore, please use the CommonJunctionCreator function instead",
        DeprecationWarning,
        2,
    )
    if len(roads) is not len(angles):
        raise GeneralIssueInputArguments("roads and angles do not have the same size.")

    if len(R) == 1:
        R = R * np.ones(len(roads))
    elif len(R) > 1 and len(R) is not len(roads):
        raise GeneralIssueInputArguments("roads and R do not have the same size.")

    # linelength = 2*R
    junction_roads = []

    # loop over the roads to get all possible combinations of connecting roads
    for i in range(len(roads) - 1):
        # for now the first road is place as base,
        if i == 0:
            cp = ContactPoint.end
            roads[i].add_successor(ElementType.junction, junction)
        else:
            cp = ContactPoint.start
            roads[i].add_predecessor(ElementType.junction, junction)

        for j in range(1 + i, len(roads)):
            # check angle needed for junction [-pi, +pi]
            an1 = angles[j] - angles[i] - np.pi
            # adjust angle if multiple of pi
            if an1 > np.pi:
                an1 = -(2 * np.pi - an1)

            sig = np.sign(an1)

            # create road, either straight or curved
            n_lanes, lanes_offset = get_lanes_offset(roads[i], roads[j], cp)
            if sig == 0:
                # create straight road
                linelength = R[i] + R[j]
                tmp_junc = create_straight_road(
                    startnum,
                    length=linelength,
                    junction=junction,
                    n_lanes=n_lanes,
                    lane_offset=lanes_offset,
                )
                if inner_road_marks:
                    for l in tmp_junc.lanes.lanesections[0].leftlanes:
                        l.roadmark[0] = inner_road_marks
                    for r in tmp_junc.lanes.lanesections[0].rightlanes:
                        r.roadmark[0] = inner_road_marks
                    tmp_junc.lanes.lanesections[0].centerlane.roadmark[
                        0
                    ] = inner_road_marks
                if len(roads) == 3:
                    # not sure all will be needed since angles have to be in increasing order, but it "should work"
                    k = [x for x in [0, 1, 2] if x != j and x != i][0]
                    if (angles[i] > angles[j]) and (
                        (angles[k] > angles[j]) or (angles[k] < angles[i])
                    ):
                        tmp_junc.lanes.lanesections[0].rightlanes[-1].roadmark[
                            0
                        ] = outer_road_marks
                    elif (angles[i] < angles[j]) and (
                        (angles[k] > angles[j]) or (angles[k] < angles[i])
                    ):
                        tmp_junc.lanes.lanesections[0].rightlanes[-1].roadmark[
                            0
                        ] = outer_road_marks
                    elif (angles[i] < angles[j]) and (
                        (angles[k] < angles[j]) or (angles[k] > angles[i])
                    ):
                        tmp_junc.lanes.lanesections[0].leftlanes[-1].roadmark[
                            0
                        ] = outer_road_marks
                    else:
                        tmp_junc.lanes.lanesections[0].rightlanes[-1].roadmark[
                            0
                        ] = outer_road_marks
            else:
                clothoids = pcloth.SolveG2(
                    -R[i],
                    0,
                    0,
                    STD_START_CLOTH,
                    R[j] * np.cos(an1),
                    R[j] * np.sin(an1),
                    an1,
                    STD_START_CLOTH,
                )
                tmp_junc = create_3cloths(
                    clothoids[0].KappaStart,
                    clothoids[0].KappaEnd,
                    clothoids[0].length,
                    clothoids[1].KappaStart,
                    clothoids[1].KappaEnd,
                    clothoids[1].length,
                    clothoids[2].KappaStart,
                    clothoids[2].KappaEnd,
                    clothoids[2].length,
                    startnum,
                    junction,
                    n_lanes=n_lanes,
                    lane_offset=lanes_offset,
                    road_marks=inner_road_marks,
                )

                if tmp_junc.planview._raw_geometries[1].curvstart > 0:
                    tmp_junc.lanes.lanesections[0].leftlanes[-1].add_roadmark(
                        outer_road_marks
                    )
                if tmp_junc.planview._raw_geometries[1].curvstart < 0:
                    tmp_junc.lanes.lanesections[0].rightlanes[-1].add_roadmark(
                        outer_road_marks
                    )
            # add predecessor and successor
            tmp_junc.add_predecessor(ElementType.road, roads[i].id, cp)
            tmp_junc.add_successor(ElementType.road, roads[j].id, ContactPoint.start)
            startnum += 1
            junction_roads.append(tmp_junc)

    # add junction to the last road aswell since it's not part of the loop
    roads[-1].add_predecessor(ElementType.junction, junction)

    return junction_roads
def create_junction_roads_from_arc(roads, angles, r=0, junction=1, arc_part=0.3333333333333333, startnum=100)

creates all needed roads for some simple junctions, the curved parts of the junction are created as a spiral-arc-spiral combo Supported junctions: - 3way crossings (either a T junction, or 120 deg junction) - 4way crossing (all 90 degree turns)

Parameters

roads (list of Road): all roads that should go into the junction

angles (list of float): the angles from where the roads should be coming in (see description for what is supported),
                        to be defined in mathimatically positive order, beginning with the first incoming road [0, +2pi]

r (float): the radius of the arcs in the junction (will determine the size of the junction)

junction (int): the id of the junction
    default: 1

spiral_part (float): the part of the curve that should be spirals (two of these) spiral_part*2 + arcpart = angle of the turn
    default: (1/3)

arc_part (float): the part of the curve that should be an arc:  spiral_part*2 + arcpart = angle of the turn
    default: (1/3)

startnum (int): start number of the roads in the junctions (will increase with 1 for each road)

Returns

junction_roads (list of Road): a list of all roads needed for all traffic connecting the roads
Expand source code
def create_junction_roads_from_arc(
    roads, angles, r=0, junction=1, arc_part=1 / 3, startnum=100
):
    """creates all needed roads for some simple junctions, the curved parts of the junction are created as a spiral-arc-spiral combo
    Supported junctions:
    - 3way crossings (either a T junction, or 120 deg junction)
    - 4way crossing (all 90 degree turns)

    Parameters
    ----------
        roads (list of Road): all roads that should go into the junction

        angles (list of float): the angles from where the roads should be coming in (see description for what is supported),
                                to be defined in mathimatically positive order, beginning with the first incoming road [0, +2pi]

        r (float): the radius of the arcs in the junction (will determine the size of the junction)

        junction (int): the id of the junction
            default: 1

        spiral_part (float): the part of the curve that should be spirals (two of these) spiral_part*2 + arcpart = angle of the turn
            default: (1/3)

        arc_part (float): the part of the curve that should be an arc:  spiral_part*2 + arcpart = angle of the turn
            default: (1/3)

        startnum (int): start number of the roads in the junctions (will increase with 1 for each road)

    Returns
    -------
        junction_roads (list of Road): a list of all roads needed for all traffic connecting the roads
    """
    warn(
        "create_junction_roads_from_arc should not be used anymore, please use the CommonJunctionCreator function instead",
        DeprecationWarning,
        2,
    )
    # arc_part = 1 - 2*spiral_part
    spiral_part = (1 - arc_part) / 2

    angle = np.pi / 2
    angle_cloth = angle * spiral_part
    spiral_length = 2 * abs(angle_cloth * r)

    cloth = pcloth.Clothoid.StandardParams(
        0,
        0,
        0,
        STD_START_CLOTH,
        (1 / r - STD_START_CLOTH) / spiral_length,
        spiral_length,
    )

    X0 = cloth.XEnd - r * np.sin(angle_cloth)
    Y0 = cloth.YEnd - r * (1 - np.cos(angle_cloth))

    linelength = 2 * (X0 + r + Y0)

    junction_roads = []

    # loop over the roads to get all possible combinations of connecting roads
    for i in range(len(roads) - 1):
        # for now the first road is place as base,
        if i == 0:
            cp = ContactPoint.end
            roads[i].add_successor(ElementType.junction, junction)
        else:
            cp = ContactPoint.start
            roads[i].add_predecessor(ElementType.junction, junction)

        for j in range(1 + i, len(roads)):
            # check angle needed for junction [-pi, +pi]
            an1 = angles[j] - angles[i] - np.pi
            # adjust angle if multiple of pi
            if an1 > np.pi:
                an1 = -(2 * np.pi - an1)

            angle_arc = an1 * arc_part
            angle_cloth = an1 * spiral_part

            sig = np.sign(an1)

            # create road, either straight or curved
            n_lanes, lanes_offset = get_lanes_offset(roads[i], roads[j], cp)
            if sig == 0:
                # create straight road
                tmp_junc = create_straight_road(
                    startnum,
                    length=linelength,
                    junction=junction,
                    n_lanes=n_lanes,
                    lane_offset=lanes_offset,
                )
            else:
                # create the cloth-arc-cloth road given the radius fo the arc
                tmp_junc = create_cloth_arc_cloth(
                    1 / r,
                    angle_arc,
                    angle_cloth,
                    startnum,
                    junction,
                    n_lanes=n_lanes,
                    lane_offset=lanes_offset,
                )

            # add predecessor and successor
            tmp_junc.add_predecessor(ElementType.road, roads[i].id, cp)
            tmp_junc.add_successor(ElementType.road, roads[j].id, ContactPoint.start)
            startnum += 1
            junction_roads.append(tmp_junc)

    # add junction to the last road aswell since it's not part of the loop
    roads[-1].add_predecessor(ElementType.junction, junction)

    return junction_roads
def create_junction_roads_standalone(angles, r, junction=1, spiral_part=0.3333333333333333, arc_part=0.3333333333333333, startnum=100, n_lanes=1, lane_width=3)

creates all needed roads for some simple junctions, the curved parts of the junction are created as a spiral-arc-spiral combo - 3way crossings (either a T junction, or 120 deg junction) - 4way crossing (all 90 degree turns) NOTE: this will not generate any links or add any successor/predecessors to the roads, and has to be added manually, if you have the connecting roads please use create_junction_roads

Parameters

angles (list of float): the angles from where the roads should be coming in (see description for what is supported),
                        to be defined in mathimatically positive order, beginning with the first incoming road

r (float): the radius of the arcs in the junction (will determine the size of the junction)

junction (int): the id of the junction
    default: 1

spiral_part (float): the part of the curve that should be spirals (two of these) spiral_part*2 + arcpart = angle of the turn
    default: (1/3)

arc_part (float): the part of the curve that should be an arc:  spiral_part*2 + arcpart = angle of the turn
    default: (1/3)

startnum (int): start number of the roads in the junctions (will increase with 1 for each road)

n_lanes (int): the number of lanes in the junction

lane_width (float): the lane width of the lanes in the junction

Returns

junction_roads (list of Road): a list of all roads in a junction without connections added
Expand source code
def create_junction_roads_standalone(
    angles,
    r,
    junction=1,
    spiral_part=1 / 3,
    arc_part=1 / 3,
    startnum=100,
    n_lanes=1,
    lane_width=3,
):
    """creates all needed roads for some simple junctions, the curved parts of the junction are created as a spiral-arc-spiral combo
    - 3way crossings (either a T junction, or 120 deg junction)
    - 4way crossing (all 90 degree turns)
    NOTE: this will not generate any links or add any successor/predecessors to the roads, and has to be added manually,
    if you have the connecting roads please use create_junction_roads

    Parameters
    ----------

        angles (list of float): the angles from where the roads should be coming in (see description for what is supported),
                                to be defined in mathimatically positive order, beginning with the first incoming road

        r (float): the radius of the arcs in the junction (will determine the size of the junction)

        junction (int): the id of the junction
            default: 1

        spiral_part (float): the part of the curve that should be spirals (two of these) spiral_part*2 + arcpart = angle of the turn
            default: (1/3)

        arc_part (float): the part of the curve that should be an arc:  spiral_part*2 + arcpart = angle of the turn
            default: (1/3)

        startnum (int): start number of the roads in the junctions (will increase with 1 for each road)

        n_lanes (int): the number of lanes in the junction

        lane_width (float): the lane width of the lanes in the junction
    Returns
    -------
        junction_roads (list of Road): a list of all roads in a junction without connections added

    """
    warn(
        "create_junction_roads_standalone should not be used anymore, please use the CommonJunctionCreator function instead",
        DeprecationWarning,
        2,
    )
    angle = np.pi / 2
    angle_cloth = angle * spiral_part
    spiral_length = 2 * abs(angle_cloth * r)

    cloth = pcloth.Clothoid.StandardParams(
        0,
        0,
        0,
        STD_START_CLOTH,
        (1 / r - STD_START_CLOTH) / spiral_length,
        spiral_length,
    )

    X0 = cloth.XEnd - r * np.sin(angle_cloth)
    Y0 = cloth.YEnd - r * (1 - np.cos(angle_cloth))
    linelength = 2 * (X0 + r + Y0)

    junction_roads = []

    for i in range(len(angles) - 1):
        for j in range(1 + i, len(angles)):
            # check angle needed for junction
            an = np.sign(angles[j] - angles[i] - np.pi)
            an1 = angles[j] - angles[i] - np.pi
            angle_arc = an1 * arc_part

            angle_cloth = an1 * spiral_part

            # adjust angle if multiple of pi
            if an1 > np.pi:
                an1 = -(2 * np.pi - an1)

            # create road, either straight or curved
            if an == 0:
                tmp_junc = create_straight_road(
                    startnum,
                    length=linelength,
                    junction=junction,
                    n_lanes=n_lanes,
                    lane_offset=lane_width,
                )
            else:
                tmp_junc = create_cloth_arc_cloth(
                    1 / r,
                    angle_arc,
                    angle_cloth,
                    startnum,
                    junction,
                    n_lanes=n_lanes,
                    lane_offset=lane_width,
                )

            # add predecessor and successor
            startnum += 1
            junction_roads.append(tmp_junc)

    return junction_roads
def create_road(geometry, id, left_lanes=1, right_lanes=1, road_type=-1, center_road_mark=<scenariogeneration.xodr.lane.RoadMark object>, lane_width=3, lane_width_end=None)

create_road creates a road with one lanesection with different number of lanes, lane marks will be of type broken, except the outer lane, that will be solid. The lane_width_end parameter can only be used when inputs for left_lanes and right_lanes are int

Parameters

geometry (Line, Spiral, ParamPoly3, Arc, a list of(Line, Spiral, ParamPoly3, Arc), or AdjustablePlanview ): geometries to build the road

id (int): id of the new road

left_lanes (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.
    Default: 1

right_lanes (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
    Default: 1

road_type (int): type of road, -1 normal road, otherwise connecting road

center_road_mark (RoadMark): roadmark for the center line

lane_width (float): the width of all lanes

lane_width_end (float): the end width of all lanes

Returns

road (Road): a straight road
Expand source code
def create_road(
    geometry,
    id,
    left_lanes=1,
    right_lanes=1,
    road_type=-1,
    center_road_mark=std_roadmark_solid(),
    lane_width=3,
    lane_width_end=None,
):
    """create_road creates a road with one lanesection with different number of lanes, lane marks will be of type broken,
    except the outer lane, that will be solid.
    The lane_width_end parameter can only be used when inputs for left_lanes and right_lanes are int

    Parameters
    ----------
        geometry (Line, Spiral, ParamPoly3, Arc, a list of(Line, Spiral, ParamPoly3, Arc), or AdjustablePlanview ): geometries to build the road

        id (int): id of the new road

        left_lanes (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.
            Default: 1

        right_lanes (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
            Default: 1

        road_type (int): type of road, -1 normal road, otherwise connecting road

        center_road_mark (RoadMark): roadmark for the center line

        lane_width (float): the width of all lanes

        lane_width_end (float): the end width of all lanes

    Returns
    -------
        road (Road): a straight road
    """

    if isinstance(left_lanes, LaneDef):
        left_lanes = [left_lanes]
    if isinstance(right_lanes, LaneDef):
        right_lanes = [right_lanes]

    pv = PlanView()
    raw_length = 0
    if isinstance(geometry, list):
        for g in geometry:
            pv.add_geometry(g)
            raw_length += g.length
    elif isinstance(geometry, AdjustablePlanview):
        pv = geometry
        pv.left_lane_defs = left_lanes
        pv.right_lane_defs = right_lanes
        pv.center_road_mark = center_road_mark
        pv.lane_width = lane_width
        pv.lane_width_end = lane_width_end
        # create a dummy length
        raw_length = 0

        if isinstance(left_lanes, list) and any(
            isinstance(x, LaneDef) for x in left_lanes
        ):
            raw_length = max(raw_length, max([x.s_end for x in left_lanes]))
        if isinstance(right_lanes, list) and any(
            isinstance(x, LaneDef) for x in right_lanes
        ):
            raw_length = max(raw_length, max([x.s_end for x in right_lanes]))
        if raw_length == 0:
            raw_length = 100  # just a dummy value
    else:
        pv.add_geometry(geometry)
        raw_length += geometry.length

    if (lane_width_end is not None) and (
        (type(left_lanes) != int) or (type(right_lanes) != int)
    ):
        raise RuntimeError(
            "lane_width_end can only be used when left_lanes and right_lanes are int"
        )
    if (
        right_lanes is not None
        and left_lanes is not None
        or center_road_mark is not None
    ):
        lanes = create_lanes_merge_split(
            right_lanes,
            left_lanes,
            raw_length,
            center_road_mark,
            lane_width,
            lane_width_end=lane_width_end,
        )
    else:
        lanes = None

    road = Road(id, pv, lanes, road_type=road_type)

    return road
def create_straight_road(road_id, length=100, junction=-1, n_lanes=1, lane_offset=3)

creates a standard straight road with two lanes STD_ROADMARK_SOLID Parameters ---------- road_id (int): id of the road to create

    length (float): length of the road
        default: 100

    junction (int): if the road belongs to a junction or not
        default: -1

    n_lanes (int): number of lanes
        default: 1

    lane_offset (int): width of the road
        default: 3

Returns
-------
    road (Road): a straight road
Expand source code
def create_straight_road(road_id, length=100, junction=-1, n_lanes=1, lane_offset=3):
    """creates a standard straight road with two lanes
    STD_ROADMARK_SOLID
        Parameters
        ----------
            road_id (int): id of the road to create

            length (float): length of the road
                default: 100

            junction (int): if the road belongs to a junction or not
                default: -1

            n_lanes (int): number of lanes
                default: 1

            lane_offset (int): width of the road
                default: 3

        Returns
        -------
            road (Road): a straight road
    """
    warn(
        "create_straight_road should not be used anymore, please use the create_road function instead",
        DeprecationWarning,
        2,
    )
    # create geometry
    line1 = Line(length)

    # create planviews
    planview1 = PlanView()
    planview1.add_geometry(line1)

    # create lanesections
    lanesec1 = LaneSection(0, standard_lane())
    for i in range(1, n_lanes + 1, 1):
        lanesec1.add_right_lane(standard_lane(lane_offset))
        lanesec1.add_left_lane(standard_lane(lane_offset))

    # create lanes
    lanes1 = Lanes()
    lanes1.add_lanesection(lanesec1)

    # finally create the roads
    return Road(road_id, planview1, lanes1, road_type=junction)
def get_lanes_offset(road1, road2, contactpoint)

returns number of lanes (hp #left lanes = # right lanes) and their offset (hp offset is constant)

Parameters

road1 (Road): first road

road2 (Road): second road

Returns

n_lanes (int):

lane_offset (int):
Expand source code
def get_lanes_offset(road1, road2, contactpoint):
    """returns number of lanes (hp #left lanes = # right lanes) and their offset (hp offset is constant)


    Parameters
    ----------
        road1 (Road): first road

        road2 (Road): second road

    Returns
    -------
        n_lanes (int):

        lane_offset (int):
    """

    # now we always look at lanesection[0] to take the number of lanes
    # TO DO - understand if the roads are connect through end or start and then take the relative lane section
    if contactpoint == ContactPoint.end:
        n_lanesection = 0
    else:
        n_lanesection = -1
    if len(road1.lanes.lanesections[n_lanesection].leftlanes) == len(
        road2.lanes.lanesections[0].leftlanes
    ) and len(road1.lanes.lanesections[n_lanesection].rightlanes) == len(
        road2.lanes.lanesections[0].rightlanes
    ):
        n_lanes = len(road1.lanes.lanesections[n_lanesection].leftlanes)
        lane_offset = road1.lanes.lanesections[n_lanesection].leftlanes[0].widths[0].a
    else:
        raise NotSameAmountOfLanesError(
            "Incoming road ",
            road1.id,
            " and outcoming road ",
            road2.id,
            "do not have the same number of left lanes.",
        )

    return n_lanes, lane_offset
def get_road_by_id(roads, id)

get_road_by_id returns a road based on the road id

Parameters

roads (list of Roads): a list of roads to seach through

id (int): the id of the road wanted

Returns

Road
Expand source code
def get_road_by_id(roads, id):
    """get_road_by_id returns a road based on the road id

    Parameters
    ----------
        roads (list of Roads): a list of roads to seach through

        id (int): the id of the road wanted

    Returns
    -------
        Road
    """
    for r in roads:
        if r.id == id:
            return r
def standard_lane(offset=3, rm=<scenariogeneration.xodr.lane.RoadMark object>)

standard_lane creates a simple lane with an offset an a roadmark

Parameters

offset (int): width of the lane
    default: 3

rm (RoadMark): road mark used for the standard lane
    default:  RoadMark(STD_ROADMARK_BROKEN)

Returns

lane (Lane): the lane
Expand source code
def standard_lane(offset=3, rm=std_roadmark_broken()):
    """standard_lane creates a simple lane with an offset an a roadmark

    Parameters
    ----------
        offset (int): width of the lane
            default: 3

        rm (RoadMark): road mark used for the standard lane
            default:  RoadMark(STD_ROADMARK_BROKEN)
    Returns
    -------
        lane (Lane): the lane

    """
    lc = Lane(a=offset)
    lc.add_roadmark(rm)
    return lc