Module scenariogeneration.xodr.links

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.

Functions

def are_roads_connected(road1, road2)
Expand source code
def are_roads_connected(road1, road2):
    """checks if road1 and road2 are connected as successor/successor or predecessor/predecessor

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

        road1 (Road): the second road
    Returns
    -------
        bool, str (successor or predecessor)
    """
    if road1.successor is not None and road2.successor is not None:
        if (
            road1.successor.element_type == ElementType.road
            and road2.successor.element_type == ElementType.road
        ):
            if (
                road1.successor.element_id == road2.id
                and road2.successor.element_id == road1.id
            ):
                return True, "successor"
    if road1.predecessor is not None and road2.predecessor is not None:
        if (
            road1.predecessor.element_type == ElementType.road
            and road2.predecessor.element_type == ElementType.road
        ):
            if (
                road1.predecessor.element_id == road2.id
                and road2.predecessor.element_id == road1.id
            ):
                return True, "predecessor"
    return False, ""

checks if road1 and road2 are connected as successor/successor or predecessor/predecessor

Parameters

road1 (Road): the first road

road1 (Road): the second road

Returns

bool, str (successor or predecessor)
def are_roads_consecutive(road1, road2)
Expand source code
def are_roads_consecutive(road1, road2):
    """checks if road2 follows road1

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

        road1 (Road): the second road
    Returns
    -------
        bool
    """

    if road1.successor is not None and road2.predecessor is not None:
        if (
            road1.successor.element_type == ElementType.road
            and road2.predecessor.element_type == ElementType.road
        ):
            if (
                road1.successor.element_id == road2.id
                and road2.predecessor.element_id == road1.id
            ):
                return True

    return False

checks if road2 follows road1

Parameters

road1 (Road): the first road

road1 (Road): the second road

Returns

bool
Expand source code
def create_lane_links(road1, road2):
    """create_lane_links takes two roads and if they are connected, match their lanes
    and creates lane links.

    Parameters
    ----------
        road1 (Road): first road to be lane linked

        road2 (Road): second road to be lane linked
    """
    if road1.road_type == -1 and road2.road_type == -1:
        # both are roads
        if are_roads_consecutive(road1, road2):
            _create_links_roads(road1, road2)
        elif are_roads_consecutive(road2, road1):
            _create_links_roads(road2, road1)
        else:
            connected, connectiontype = are_roads_connected(road1, road2)
            if connected:
                _create_links_roads(road1, road2, connectiontype)

    elif road1.road_type != -1:
        _create_links_connecting_road(road1, road2)
    elif road2.road_type != -1:
        _create_links_connecting_road(road2, road1)

create_lane_links takes two roads and if they are connected, match their lanes and creates lane links.

Parameters

road1 (Road): first road to be lane linked

road2 (Road): second road to be lane linked
Expand source code
def create_lane_links_from_ids(road1, road2, road1_lane_ids, road2_lane_ids):
    """
    Experimental function to connect lanes of two roads given the corresponding lane IDs
    (numbers).

    NOTE: Usually only necessary when there is not the same amount of lanes at the
    connection of two roads or there are new lanes with zero width at the beginning of a
    road.

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

        road2 (Road): the second road

        road1_lane_ids (list of int): list of the ids of road1 (do not use the 0 lane)

        road2_lane_ids (list of int): list of the ids of road2 (do not use the 0 lane)

    """
    if len(road1_lane_ids) != len(road2_lane_ids):
        raise GeneralIssueInputArguments("Length of the lane ID lists is not the same.")

    if (0 in road1_lane_ids) or (0 in road2_lane_ids):
        raise ValueError("The center lane (ID 0) should not be linked.")

    if road1.road_type == -1 and road2.road_type == -1:
        first_linktype, _, first_connecting_lanesec = _get_related_lanesection(
            road1, road2
        )
        second_linktype, _, second_connecting_lanesec = _get_related_lanesection(
            road2, road1
        )

        # The road links need to be reciprocal for the lane linking to succeed
        if first_linktype == None or second_linktype == None:
            raise ValueError(
                "Unable to create lane links for road with ID "
                + str(road1.id)
                + " and road with ID "
                + str(road2.id)
                + " due to non reciprocal road successor/predecessor links."
            )

        for i in range(len(road1_lane_ids)):
            if road1_lane_ids[i] > 0:
                road1.lanes.lanesections[first_connecting_lanesec].leftlanes[
                    road1_lane_ids[i] - 1
                ].add_link(first_linktype, road2_lane_ids[i])
            else:
                road1.lanes.lanesections[first_connecting_lanesec].rightlanes[
                    abs(road1_lane_ids[i]) - 1
                ].add_link(first_linktype, road2_lane_ids[i])
            if road2_lane_ids[i] > 0:
                road2.lanes.lanesections[second_connecting_lanesec].leftlanes[
                    road2_lane_ids[i] - 1
                ].add_link(second_linktype, road1_lane_ids[i])
            else:
                road2.lanes.lanesections[second_connecting_lanesec].rightlanes[
                    abs(road2_lane_ids[i]) - 1
                ].add_link(second_linktype, road1_lane_ids[i])
    else:
        raise NotImplementedError(
            "This API currently does not support linking with junction connecting roads."
        )

Experimental function to connect lanes of two roads given the corresponding lane IDs (numbers).

NOTE: Usually only necessary when there is not the same amount of lanes at the connection of two roads or there are new lanes with zero width at the beginning of a road.

Parameters

road1 (Road): the first road

road2 (Road): the second road

road1_lane_ids (list of int): list of the ids of road1 (do not use the 0 lane)

road2_lane_ids (list of int): list of the ids of road2 (do not use the 0 lane)

Classes

class Connection (incoming_road, connecting_road, contact_point, id=None)
Expand source code
class Connection(XodrBase):
    """Connection creates a connection as a base of junction

    Parameters
    ----------
        incoming_road (int): the id of the incoming road to the junction

        connecting_road (int): id of the connecting road (type junction)

        contact_point (ContactPoint): the contact point of the link

        id (int): id of the junction (automated?)

    Attributes
    ----------
        incoming_road (int): the id of the incoming road to the junction

        connecting_road (int): id of the connecting road (type junction)

        contact_point (ContactPoint): the contact point of the link

        id (int): id of the connection (automated?)

        links (list of tuple(int) ): a list of all lanelinks in the connection

    Methods
    -------
        get_element()
            Returns the full ElementTree of the class

        get_attributes()
            Returns a dictionary of all attributes of the class

        add_lanelink(in_lane,out_lane)
            Adds a lane link to the connection
    """

    def __init__(self, incoming_road, connecting_road, contact_point, id=None):
        """initalize the Connection

        Parameters
        ----------
            incoming_road (int): the id of the incoming road to the junction

            connecting_road (int): id of the connecting road (for junctiontypes virutal and default), or the linkedRoad (for junctiontype direct)

            contact_point (ContactPoint): the contact point of the link

            id (int): id of the junction (automated)
        """
        super().__init__()
        self.incoming_road = incoming_road
        self.connecting_road = connecting_road
        self.contact_point = enumchecker(contact_point, ContactPoint, True)
        self.id = id
        self.links = []

    def __eq__(self, other):
        if isinstance(other, Connection) and super().__eq__(other):
            if (
                self.get_attributes() == other.get_attributes()
                and self.links == other.links
            ):
                return True
        return False

    def _set_id(self, id):
        """id is set

        Parameters
        ----------
            id (int): the id of the connection
        """
        if self.id == None:
            self.id = id

    def add_lanelink(self, in_lane, out_lane):
        """Adds a new link to the connection

        Parameters
        ----------
            in_lane: lane id of the incoming road

            out_lane: lane id of the outgoing road
        """
        self.links.append((in_lane, out_lane))
        return self

    def get_attributes(self, junctiontype=JunctionType.default):
        """returns the attributes as a dict of the Connection

        Parameters
        ----------
            junctiontype (JunctionType): type of junction created (connections will be different)

        """
        retdict = {}
        retdict["incomingRoad"] = str(self.incoming_road)
        retdict["id"] = str(self.id)
        retdict["contactPoint"] = enum2str(self.contact_point)
        if junctiontype == JunctionType.direct:
            retdict["linkedRoad"] = str(self.connecting_road)
        else:
            retdict["connectingRoad"] = str(self.connecting_road)
        return retdict

    def get_element(self, junctiontype=JunctionType.default):
        """returns the elementTree of the Connection

        Parameters
        ----------
            junctiontype (JunctionType): type of junction created (connections will be different)

        """

        element = ET.Element("connection", attrib=self.get_attributes(junctiontype))
        self._add_additional_data_to_element(element)
        for l in sorted(self.links, key=lambda x: x[0], reverse=True):
            ET.SubElement(
                element, "laneLink", attrib={"from": str(l[0]), "to": str(l[1])}
            )
        return element

Connection creates a connection as a base of junction

Parameters

incoming_road (int): the id of the incoming road to the junction

connecting_road (int): id of the connecting road (type junction)

contact_point (ContactPoint): the contact point of the link

id (int): id of the junction (automated?)

Attributes

incoming_road (int): the id of the incoming road to the junction

connecting_road (int): id of the connecting road (type junction)

contact_point (ContactPoint): the contact point of the link

id (int): id of the connection (automated?)

links (list of tuple(int) ): a list of all lanelinks in the connection

Methods

get_element()
    Returns the full ElementTree of the class

get_attributes()
    Returns a dictionary of all attributes of the class

add_lanelink(in_lane,out_lane)
    Adds a lane link to the connection

initalize the Connection

Parameters

incoming_road (int): the id of the incoming road to the junction

connecting_road (int): id of the connecting road (for junctiontypes virutal and default), or the linkedRoad (for junctiontype direct)

contact_point (ContactPoint): the contact point of the link

id (int): id of the junction (automated)

Ancestors

Methods

Expand source code
def add_lanelink(self, in_lane, out_lane):
    """Adds a new link to the connection

    Parameters
    ----------
        in_lane: lane id of the incoming road

        out_lane: lane id of the outgoing road
    """
    self.links.append((in_lane, out_lane))
    return self

Adds a new link to the connection

Parameters

in_lane: lane id of the incoming road

out_lane: lane id of the outgoing road
def get_attributes(self, junctiontype=JunctionType.default)
Expand source code
def get_attributes(self, junctiontype=JunctionType.default):
    """returns the attributes as a dict of the Connection

    Parameters
    ----------
        junctiontype (JunctionType): type of junction created (connections will be different)

    """
    retdict = {}
    retdict["incomingRoad"] = str(self.incoming_road)
    retdict["id"] = str(self.id)
    retdict["contactPoint"] = enum2str(self.contact_point)
    if junctiontype == JunctionType.direct:
        retdict["linkedRoad"] = str(self.connecting_road)
    else:
        retdict["connectingRoad"] = str(self.connecting_road)
    return retdict

returns the attributes as a dict of the Connection

Parameters

junctiontype (JunctionType): type of junction created (connections will be different)
def get_element(self, junctiontype=JunctionType.default)
Expand source code
def get_element(self, junctiontype=JunctionType.default):
    """returns the elementTree of the Connection

    Parameters
    ----------
        junctiontype (JunctionType): type of junction created (connections will be different)

    """

    element = ET.Element("connection", attrib=self.get_attributes(junctiontype))
    self._add_additional_data_to_element(element)
    for l in sorted(self.links, key=lambda x: x[0], reverse=True):
        ET.SubElement(
            element, "laneLink", attrib={"from": str(l[0]), "to": str(l[1])}
        )
    return element

returns the elementTree of the Connection

Parameters

junctiontype (JunctionType): type of junction created (connections will be different)

Inherited members

class Junction (name,
id,
junction_type=JunctionType.default,
orientation=None,
sstart=None,
send=None,
mainroad=None)
Expand source code
class Junction(XodrBase):
    """Junction creates a junction of OpenDRIVE

    Parameters
    ----------
        name (str): name of the junction

        id (int): id of the junction

        junction_type (JunctionType): type of junction
            Default: JunctionType.default

        orientation (Orientation): the orientation of the junction (only used for virtual junction)
            Default: None

        sstart (float): start of the virtual junction (only used for virtual junction)
            Default: None

        send (float): end of the virtual junction (only used for virtual junction)
            Default: None

        mainroad (int): main road for a virtual junction
            Default: None

    Attributes
    ----------
        name (str): name of the junction

        id (int): id of the junction

        connections (list of Connection): all the connections in the junction

        junction_type (JunctionType): type of junction
            Default: JunctionType.default

        orientation (Orientation): the orientation of the junction (only used for virtual junction)

        sstart (float): start of the virtual junction (only used for virtual junction)

        send (float): end of the virtual junction (only used for virtual junction)

        mainroad (int): main road for a virtual junction


    Methods
    -------
        get_element()
            Returns the full ElementTree of the class

        get_attributes()
            Returns a dictionary of all attributes of the class

        add_connection(connection)
            Adds a connection to the junction
    """

    def __init__(
        self,
        name,
        id,
        junction_type=JunctionType.default,
        orientation=None,
        sstart=None,
        send=None,
        mainroad=None,
    ):
        """initalize the Junction

        Parameters
        ----------
            name (str): name of the junction

            id (int): id of the junction

            junction_type (JunctionType): type of junction
                Default: JunctionType.default

            orientation (Orientation): the orientation of the junction (only used for virtual junction)

            sstart (float): start of the virtual junction (only used for virtual junction)

            send (float): end of the virtual junction (only used for virtual junction)

            mainroad (int): main road for a virtual junction

        """
        super().__init__()
        self.name = name
        self.id = id
        self.connections = []
        self._id_counter = 0

        self.junction_type = enumchecker(junction_type, JunctionType)
        if junction_type == JunctionType.virtual:
            if not (
                sstart is not None
                and send is not None
                and mainroad is not None
                and orientation is not None
            ):
                raise NotEnoughInputArguments(
                    "For virtual junctions sstart, send, orientation, and mainroad has to be added"
                )
        self.sstart = sstart
        self.send = send
        self.mainroad = mainroad
        self.orientation = enumchecker(orientation, Orientation, True)

    def __eq__(self, other):
        if isinstance(other, Junction) and super().__eq__(other):
            if (
                self.get_attributes() == other.get_attributes()
                and self.connections == other.connections
            ):
                return True
        return False

    def add_connection(self, connection):
        """Adds a new link to the Junction

        Parameters
        ----------
            connection (Connection): adds a connection to the junction

        """
        if not isinstance(connection, Connection):
            raise TypeError("connection is not of type Connection")
        connection._set_id(self._id_counter)
        self._id_counter += 1
        self.connections.append(connection)
        return self

    def get_attributes(self):
        """returns the attributes as a dict of the Junction"""
        retdict = {}
        retdict["name"] = self.name
        retdict["id"] = str(self.id)
        retdict["type"] = self.junction_type.name

        # these are only used for virtual junctions
        if self.junction_type == JunctionType.virtual:
            if self.orientation == Orientation.positive:
                retdict["orientation"] = "+"
            elif self.orientation == Orientation.negative:
                retdict["orientation"] = "-"
            retdict["sEnd"] = str(self.send)
            retdict["sStart"] = str(self.sstart)
            retdict["mainRoad"] = str(self.mainroad)
        return retdict

    def get_element(self):
        """returns the elementTree of the Junction"""
        element = ET.Element("junction", attrib=self.get_attributes())
        self._add_additional_data_to_element(element)
        for con in self.connections:
            element.append(con.get_element(self.junction_type))
        return element

Junction creates a junction of OpenDRIVE

Parameters

name (str): name of the junction

id (int): id of the junction

junction_type (JunctionType): type of junction
    Default: JunctionType.default

orientation (Orientation): the orientation of the junction (only used for virtual junction)
    Default: None

sstart (float): start of the virtual junction (only used for virtual junction)
    Default: None

send (float): end of the virtual junction (only used for virtual junction)
    Default: None

mainroad (int): main road for a virtual junction
    Default: None

Attributes

name (str): name of the junction

id (int): id of the junction

connections (list of Connection): all the connections in the junction

junction_type (JunctionType): type of junction
    Default: JunctionType.default

orientation (Orientation): the orientation of the junction (only used for virtual junction)

sstart (float): start of the virtual junction (only used for virtual junction)

send (float): end of the virtual junction (only used for virtual junction)

mainroad (int): main road for a virtual junction

Methods

get_element()
    Returns the full ElementTree of the class

get_attributes()
    Returns a dictionary of all attributes of the class

add_connection(connection)
    Adds a connection to the junction

initalize the Junction

Parameters

name (str): name of the junction

id (int): id of the junction

junction_type (JunctionType): type of junction
    Default: JunctionType.default

orientation (Orientation): the orientation of the junction (only used for virtual junction)

sstart (float): start of the virtual junction (only used for virtual junction)

send (float): end of the virtual junction (only used for virtual junction)

mainroad (int): main road for a virtual junction

Ancestors

Methods

def add_connection(self, connection)
Expand source code
def add_connection(self, connection):
    """Adds a new link to the Junction

    Parameters
    ----------
        connection (Connection): adds a connection to the junction

    """
    if not isinstance(connection, Connection):
        raise TypeError("connection is not of type Connection")
    connection._set_id(self._id_counter)
    self._id_counter += 1
    self.connections.append(connection)
    return self

Adds a new link to the Junction

Parameters

connection (Connection): adds a connection to the junction
def get_attributes(self)
Expand source code
def get_attributes(self):
    """returns the attributes as a dict of the Junction"""
    retdict = {}
    retdict["name"] = self.name
    retdict["id"] = str(self.id)
    retdict["type"] = self.junction_type.name

    # these are only used for virtual junctions
    if self.junction_type == JunctionType.virtual:
        if self.orientation == Orientation.positive:
            retdict["orientation"] = "+"
        elif self.orientation == Orientation.negative:
            retdict["orientation"] = "-"
        retdict["sEnd"] = str(self.send)
        retdict["sStart"] = str(self.sstart)
        retdict["mainRoad"] = str(self.mainroad)
    return retdict

returns the attributes as a dict of the Junction

def get_element(self)
Expand source code
def get_element(self):
    """returns the elementTree of the Junction"""
    element = ET.Element("junction", attrib=self.get_attributes())
    self._add_additional_data_to_element(element)
    for con in self.connections:
        element.append(con.get_element(self.junction_type))
    return element

returns the elementTree of the Junction

Inherited members

class JunctionGroup (name, group_id, junction_type=JunctionGroupType.roundabout)
Expand source code
class JunctionGroup(XodrBase):
    """JunctionGroup creates a JunctionGroup of OpenDRIVE

    Parameters
    ----------
        name (str): name of the junctiongroup

        group_id (int): id of the junctiongroup

        junction_type (JunctionGroupType): type of junction
            Default: JunctionGroupType.roundabout

    Attributes
    ----------
        name (str): name of the junctiongroup

        group_id (int): id of the junctiongroup

        junctions (list of int): all the junctions in the junctiongroup

    Methods
    -------
        get_element()
            Returns the full ElementTree of the class

        get_attributes()
            Returns a dictionary of all attributes of the class

        add_junction(junction_id)
            Adds a connection to the junction
    """

    def __init__(self, name, group_id, junction_type=JunctionGroupType.roundabout):
        """initalize the JunctionGroup

        Parameters
        ----------
            name (str): name of the junctiongroup

            group_id (int): id of the junctiongroup

            junction_type (JunctionGroupType): type of junction
                Default: JunctionGroupType.roundabout
        """
        super().__init__()
        self.name = name
        self.group_id = group_id
        self.junctions = []
        self.junction_type = enumchecker(junction_type, JunctionGroupType)

    def __eq__(self, other):
        if isinstance(other, JunctionGroup) and super().__eq__(other):
            if (
                self.get_attributes() == other.get_attributes()
                and self.junctions == other.junctions
            ):
                return True
        return False

    def add_junction(self, junction_id):
        """Adds a new link to the JunctionGroup

        Parameters
        ----------
            junction_id (int): adds a junction to the junctiongroup

        """
        self.junctions.append(junction_id)
        return self

    def get_attributes(self):
        """returns the attributes as a dict of the JunctionGroup"""
        retdict = {}
        retdict["name"] = self.name
        retdict["id"] = str(self.group_id)
        retdict["type"] = enum2str(self.junction_type)
        return retdict

    def get_element(self):
        """returns the elementTree of the Junction"""
        element = ET.Element("junctionGroup", attrib=self.get_attributes())
        self._add_additional_data_to_element(element)
        for j in self.junctions:
            ET.SubElement(element, "junctionReference", attrib={"junction": str(j)})
        return element

JunctionGroup creates a JunctionGroup of OpenDRIVE

Parameters

name (str): name of the junctiongroup

group_id (int): id of the junctiongroup

junction_type (JunctionGroupType): type of junction
    Default: JunctionGroupType.roundabout

Attributes

name (str): name of the junctiongroup

group_id (int): id of the junctiongroup

junctions (list of int): all the junctions in the junctiongroup

Methods

get_element()
    Returns the full ElementTree of the class

get_attributes()
    Returns a dictionary of all attributes of the class

add_junction(junction_id)
    Adds a connection to the junction

initalize the JunctionGroup

Parameters

name (str): name of the junctiongroup

group_id (int): id of the junctiongroup

junction_type (JunctionGroupType): type of junction
    Default: JunctionGroupType.roundabout

Ancestors

Methods

def add_junction(self, junction_id)
Expand source code
def add_junction(self, junction_id):
    """Adds a new link to the JunctionGroup

    Parameters
    ----------
        junction_id (int): adds a junction to the junctiongroup

    """
    self.junctions.append(junction_id)
    return self

Adds a new link to the JunctionGroup

Parameters

junction_id (int): adds a junction to the junctiongroup
def get_attributes(self)
Expand source code
def get_attributes(self):
    """returns the attributes as a dict of the JunctionGroup"""
    retdict = {}
    retdict["name"] = self.name
    retdict["id"] = str(self.group_id)
    retdict["type"] = enum2str(self.junction_type)
    return retdict

returns the attributes as a dict of the JunctionGroup

def get_element(self)
Expand source code
def get_element(self):
    """returns the elementTree of the Junction"""
    element = ET.Element("junctionGroup", attrib=self.get_attributes())
    self._add_additional_data_to_element(element)
    for j in self.junctions:
        ET.SubElement(element, "junctionReference", attrib={"junction": str(j)})
    return element

returns the elementTree of the Junction

Inherited members

class LaneLinker
Expand source code
class LaneLinker:
    """LaneLinker stored information for linking lane sections
    NOTE: Not part of OpenDRIVE, but a helper to link lanes for the user.

    Parameters
    ----------

    Attributes
    ----------
        links: all lane links added (predlane (Lane), succlane (Lane), found=bool)

    Methods
    -------
        add_link(predlane, succlane)
            adds a lane link

    """

    def __init__(self):
        """initalize the _Links"""

        self.links = []

    def add_link(self, predlane, succlane, connecting_road=None):
        """Adds a _Link

        Parameters
        ----------
            predlane (Lane): predecessor lane

            succlane (Lane): successor lane

            connecting_road (id): id of a connecting road (used for junctions)

        """
        self.links.append(_lanelink(predlane, succlane, connecting_road))
        return self

LaneLinker stored information for linking lane sections NOTE: Not part of OpenDRIVE, but a helper to link lanes for the user.

Parameters

Attributes

links: all lane links added (predlane (Lane), succlane (Lane), found=bool)

Methods

add_link(predlane, succlane)
    adds a lane link

initalize the _Links

Methods

Expand source code
def add_link(self, predlane, succlane, connecting_road=None):
    """Adds a _Link

    Parameters
    ----------
        predlane (Lane): predecessor lane

        succlane (Lane): successor lane

        connecting_road (id): id of a connecting road (used for junctions)

    """
    self.links.append(_lanelink(predlane, succlane, connecting_road))
    return self

Adds a _Link

Parameters

predlane (Lane): predecessor lane

succlane (Lane): successor lane

connecting_road (id): id of a connecting road (used for junctions)