Traffic elements#

Manipulating intersections, particularly connections and traffic signals is one of the most critical portions of this software, so there is direct access to intersections directly from the network’s main class.

from polaris.network.network import Network

net = Network()
net.open('D:/src/argonne/chicago2018-Supply.sqlite')

node_id = 123
intersection = net.get_intersection(node_id)

Fully understanding the Intersection class and its capabilities is critical for the correct generation of connections and traffic signals.

class polaris.network.traffic.intersec.Intersection(data_tables, path_to_file: PathLike, conn: Connection | None = None, osm: OSM | None = None)#

Bases: object

Network intersection class

from polaris.network.network import Network

net = Network()
net.open('D:/Argonne/GTFS/CHICAGO/chicago2018-Supply.sqlite')

curr = net.conn.cursor()
curr.execute('SELECT node FROM Node order by node')


for node in nodes:
    intersection = net.get_intersection(node)

    # To rebuild the intersection checking OSM, one can just do this
    intersection.rebuild_intersection(check_type='osm'):

    # Or one can do each step to be able to manipulate the signal as they go
    intersection.rebuild_connections()
    if intersection.osm_signal():
    # if intersection.determine_geometric_need_for_signal()
        if not intersection.supports_signal():
            print(f'Node {node} does not support a signal. Skipping it')
            continue
        sig = intersection.create_signal()
        # Here you can manipulate the signal
        sig.re_compute()
        # Or after recomputing as well
        sig.save()
__init__(data_tables, path_to_file: PathLike, conn: Connection | None = None, osm: OSM | None = None)#
load(node: int, conn: Connection | None = None) None#

Loads the intersection object for the provided node

Args:

node (int): Node ID to load as an intersection

allow_pockets_for_all() None#

Allows all approximations for this intersection to have pockets created for them

Default permissions for pocket creation are extracted from the Link_Type table. For more information go to the table documentation

block_pockets_for_all() None#

Blocks all approximations for this intersection to have pockets created for them

Default permissions for pocket creation are extracted from the Link_Type table. For more information go to the table documentation

rebuild_intersection(conn: Connection, signal_type='keep_existing', clear_it_first=True)#

Rebuilds an entire intersection

Removes connections and traffic signals before rebuilding connections and (optionally) signals

Args:

signal_type (str, Optional): Type of check to determine if a signal should be added. Defaults to keeping existing intersection control type

check_type
  • ‘geometric’: Determines need for signal based on the intersection geometry

  • ‘osm’: Determines need for signal based on the existence of such on OSM

  • ‘forced’: Forces the placement of a signal

  • ‘stop_sign’: Places stop signs in the intersection only

  • ‘none’: Does not place any type of intersection control on this intersection

  • ‘keep_existing’: Keeps the intersection control currently in place for the intersection

add_stop_sign(conn: Connection)#

Adds a stop sign to this intersection

rebuild_connections(clear_it_first=True, conn: Connection | None = None) None#

Rebuilds all connections for the intersection

Removes any pre-existing connections and traffic signal for this intersection

Args:

clear_it_first (bool, Optional): Whether to clear existing connections, pockets and signals It allows faster processing if no prior clearing is needed, but it will fail if connections and pockets data still exist for this node. Defaults to True

block_movement(from_link: int, to_link: int, conn: Connection) None#

Blocks a movement by inserting it in the Turn_Overrides table with a penalty of -1

It also recomputes the signal in the intersection in case there is a signal to be re-computed

Args:

from_link (int): Origin link for the allowed turn to_link (int): Destination link for the allowed turn

add_movement(from_link: int, to_link: int, conn: Connection, note='') None#

Allows a movement by inserting it in the Turn_Overrides table with a penalty of 0

Args:

from_link (int): Origin link for the allowed turn to_link (int): Destination link for the allowed turn note (str): Any notes that should be added to the Turn_Overrides table

osm_signal(buffer=30, re_check=False, return_when_fail=False) bool#

We check Open-Streets Map for a traffic light in this vicinity

Before making the first call to this function, it is recommended deploying your own OSM Overpass server and set IP and sleep time appropriately. See the OSM module documentation for more details

Args:

buffer (int, Optional): Distance between node and OSM signal to be considered same point. Defaults to 30m

re_check (bool, Optional): To check again for a different buffer, set it to True. Defaults to False

return_when_fail (bool, Optional): Value to return when the OSM query fails.

# Here we default to our own server
osm = net.osm
osm.url = 'http://192.168.0.105:12345/api'
osm.sleep_time = 0
has_signal(conn: Connection) bool#

Checks if there is a signal for this intersection

has_stop_sign(conn: Connection) bool#

Checks if there is a signal for this intersection

delete_stop_sign(conn: Connection)#

Deletes the signal for this intersection

delete_signal(conn: Connection)#

Deletes the signal for this intersection

create_signal(conn: Connection, compute_and_save=True) Signal | None#

Returns a traffic signal object for this intersection

Return:

signal (Signal): Traffic signal object

supports_signal(conn: Connection) bool#

If this intersection geometrically supports a signal (does it make sense)

Return:

support (True): Boolean for it a signal can be configured for this intersection

determine_geometric_need_for_signal(conn: Connection) bool#

Determines if a signal is needed based on intersection geometry, link types and area type

It is somewhat similar to the capability from TranSims

allowed_turns() List[List[int]]#

Returns a list of all link pairs allowed for convergence in this node

property connection_data_records#
save_pockets(conn: Connection)#
connections(conn: Connection)#

Intersection

Network intersection class

U-Turns and Overrides#

Besides the number of lanes in each approximation and the angles between each one of them, there are still two forms of controlling the connections that are created and maintained by the package.

The first one is a global setting in the about_model table named ‘U-TURN allowed’, which has a default value of ‘FALSE’, but that can also be set to ‘TRUE’ by the user.

When set to ‘FALSE’, the default behaviour for intersection configuration is to not allow a connection between a link and itself in the opposed direction, with an exception for dead-end links, as that may cause problems during path-finding.

Overriding the definitions for any particular conversion is possible by inserting a new record in the table Turn_Overrides, which can also be used to hold turn penalties whenever Polaris comes to support such feature.

Whenever a record in the Turn_Overrides has a penalty with a non-negative value, a corresponding connection is inserted in the Connection table (if not already existing), and whenever a record has a penalty value equal to -1, no connection can exist between that corresponding pair of links.

Note

Manual edits to the Connections table are not guaranteed to be permanent, so we recommend that any additions or deletions are made as new records in the Turn_Overrides table.

Even with globally banned and U-Turns and/or specific turn prohibitions in place, some theoretically prohibited turns might still occur in the connections database when there are no other connections between the points possible.

Pockets table#

Pockets are added to a link whenever such link has more approaching lanes than the sum of all lanes of links departing that node (includes the same link in its reverse direction if U-Turns are allowed).

The default length of pockets is 15% of its length, with a minimum of 10m and a maximum of 400m (minimum is also limited by the length of the link).

The logic for creation of pockets is specific for each type of intersection, which are detailed below.

Connections#

Polaris works with only 4 possible turn directions: Thru, Left, U-Turn & Right, which makes it easy to define 90o quadrants centered in each turn. For example, any conversion requiring a turn between 135o and 225o would be categorized as a U-Turn, and anything between 315o and 45o would be considered a Thru movement.

Note

Cases such as freeway ramps that depart the main link at a very acute angle result in more than one THRU movement. This result is different than previous implementations, but it is more physically accurate.

Intersection types#

Because of the functional and geometric differences between intersections, a few special cases have been developed, although the default intersection treatment is expected to cover most of the intersections in any given network.

Note

The handling of special cases is based on the network geometry and on the link types associated to each link. For this reason, proper network coding is paramount for an appropriate creation of connection records.

Single-possibility intersection#

Many intersections are simply the junction of two links, and not what we would call an intersection in real life. In these cases, links are connected “top-to-top”, which is basically connecting all incoming lanes to all outgoing lanes, adding pockets for U-Turns whenever U-Turns are permitted and pockets are allowed.

Freeway intersections#

Freeway intersections cover all cases where only Freeways, expressways and ramps are connected to the node, regardless of how many of they are. For the purpose of this documentation freeway and expressway will both be called freeway from this point on.

The basic idea behind this intersection modelling is to view it as a large lane merge, where all the upstream links need to connect directly with a downstream link, where this downstream links can have merge pockets to accommodate the possible extra number of lanes from upstream, which is to say that the lane balance (number of incoming and outgoing lanes) cannot be taken for granted.

Whenever there are fewer lanes leaving the node than entering it, we go through the list below until we have either reached balance or have added pockets to all links allowed to have pockets.

  1. For each freeway link, ordered in decreasing number of lanes, add right merge pockets.

  2. For each freeway link, ordered in decreasing number of lanes, add left merge pockets.

  3. For each ramp link, ordered in decreasing number of lanes, add right merge pockets.

  4. For each ramp link, ordered in decreasing number of lanes, add left merge pockets.

Since the number of lanes in each pocket (left and right) is limited to one, lane balancing not always feasible, in which case, the lane assignment to each connection will be done by apportioning lanes proportionally.

Whenever there is an excess of lanes leaving the node than entering it, we go through the list below until lane balance has been reached or no more pockets can be added.

  1. For each freeway link, ordered in decreasing number of lanes, add right pocket.

  2. For each freeway link, ordered in decreasing number of lanes, add left pocket.

  3. For each ramp link, ordered in decreasing number of lanes, add right pocket

  4. For each ramp link, ordered in decreasing number of lanes, add left pocket

As it was the case for incoming links, connection will be done by apportioning lanes proportionally whenever incoming and outgoing lanes are not balanced.

Generic intersection#

The basic rules applied to freeway intersections apply, but no hierarchy of links to add pockets to is used.

Intersection control#

There are two types of intersection control in Polaris, stop signs and traffic signals, and both can be manipulated with this package.

Some intersections do to support any time of control, however. These intersections, which are those without conflicting movements, cannot have traffic signals or stop signs added to them.

Stop Signs#

Stop signs may be added to any intersection supporting one (as described above), which causes any existing traffic signal for that intersection to be removed.

Stop signs can be configured for only a portion of the intersections’ approximations, or as a all-way-stop. The built-in stop sign generator distinguishes between these two bases based solely on the link rank (hierarchy) of the incoming links in the following way:

  • If there is a movement conflict between the highest ranking incoming links (smallest rank), then we configure a all-way-stop (i.e. ALL_STOP) * This includes intersections with all links with same link rank

  • If there are no movement conflict between the highest ranking incoming links, we add stop signs to all other links (i.e. STOP)

Finally, stop signs are not allowed in intersections formed solely by freeways, expressways, and ramps. This restrictions is implemented within the Python code, but direct insertions in the database are not controlled.

Traffic signals#

Traffic signals can be generated for any intersection with conflicting movements, and can be manipulated directly, although that is not the standard form of use.

As a default, signal timing cycles will be 75s for intersections with 2 or less major approximations (major, principal, expressway & freeway), and 90s otherwise. These approximations include both incoming and outgoing.

For intersections with more than 5 approximations, an extra 15s are added to the signal cycle.

class polaris.network.traffic.intersection_control.signal.Signal(intersection, conn: Connection)#

Bases: object

default_periods = [[0, 86400]]#
__init__(intersection, conn: Connection)#
delete(conn: Connection)#

Removes signal from database

re_compute(conn: Connection)#
add_period(value_start: int, value_end: int, conn: Connection)#

Adds a period record to this signal

Args:

value_start (int): The second this period goes from (0 to 86400). Must be a whole minute

value_end (int): The second this period goes from (0 to 86400). Must be a whole minute and larger than value_start

save(conn: Connection)#

Saves traffic signal to the network file

property data: dict#