sumolib.net

This file contains a content handler for parsing sumo network xml files. It uses other classes from this module to represent the road network.

   1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
   2# Copyright (C) 2008-2026 German Aerospace Center (DLR) and others.
   3# This program and the accompanying materials are made available under the
   4# terms of the Eclipse Public License 2.0 which is available at
   5# https://www.eclipse.org/legal/epl-2.0/
   6# This Source Code may also be made available under the following Secondary
   7# Licenses when the conditions for such availability set forth in the Eclipse
   8# Public License 2.0 are satisfied: GNU General Public License, version 2
   9# or later which is available at
  10# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
  11# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
  12
  13# @file    __init__.py
  14# @author  Daniel Krajzewicz
  15# @author  Laura Bieker
  16# @author  Karol Stosiek
  17# @author  Michael Behrisch
  18# @author  Jakob Erdmann
  19# @author  Robert Hilbrich
  20# @author  Mirko Barthauer
  21# @date    2008-03-27
  22
  23"""
  24This file contains a content handler for parsing sumo network xml files.
  25It uses other classes from this module to represent the road network.
  26"""
  27
  28from __future__ import print_function
  29from __future__ import absolute_import
  30import sys
  31import math
  32import heapq
  33import gzip
  34import warnings
  35import io
  36from xml.sax import handler, parse
  37from copy import copy
  38from collections import defaultdict
  39from itertools import chain
  40try:
  41    from functools import lru_cache
  42    HAVE_LRU_CACHE = True
  43except ImportError:
  44    HAVE_LRU_CACHE = False
  45
  46try:
  47    import lxml.etree
  48    import pathlib
  49    HAVE_LXML = True
  50except ImportError:
  51    HAVE_LXML = False
  52
  53import sumolib
  54from . import lane, edge, netshiftadaptor, node, connection, roundabout  # noqa
  55from .connection import Connection
  56from sumolib.miscutils import intIfPossible
  57
  58
  59class TLS:
  60
  61    """Traffic Light Signal for a sumo network"""
  62
  63    def __init__(self, id):
  64        self._id = id
  65        self._connections = []
  66        self._maxConnectionNo = -1
  67        self._programs = {}
  68
  69    def addConnection(self, inLane, outLane, linkNo):
  70        self._connections.append([inLane, outLane, linkNo])
  71        if linkNo > self._maxConnectionNo:
  72            self._maxConnectionNo = linkNo
  73
  74    def getConnections(self):
  75        return self._connections
  76
  77    def getID(self):
  78        return self._id
  79
  80    def getLinks(self):
  81        links = {}
  82        for the_connection in self._connections:
  83            if the_connection[2] not in links:
  84                links[the_connection[2]] = []
  85            links[the_connection[2]].append(the_connection)
  86        return links
  87
  88    def getEdges(self):
  89        edges = set()
  90        for c in self._connections:
  91            edges.add(c[0].getEdge())
  92        return edges
  93
  94    def addProgram(self, program):
  95        self._programs[program._id] = program
  96
  97    def removePrograms(self):
  98        self._programs.clear()
  99
 100    def toXML(self):
 101        ret = ""
 102        for p in self._programs:
 103            ret = ret + self._programs[p].toXML(self._id)
 104        return ret
 105
 106    def getPrograms(self):
 107        return self._programs
 108
 109
 110class Phase:
 111
 112    def __init__(self, duration, state, minDur=None, maxDur=None, next=tuple(), name="", earlyTarget=""):
 113        """
 114        Constructs a traffic light phase
 115        duration (float): the duration of the phase in seconds
 116        state (string): the state codes for each controlled link
 117        minDur (float): the minimum duration (ignored by static tls)
 118        maxDur (float): the maximum duration (ignored by static tls)
 119        next (intList): possible succesor phase (optional)
 120        name (string): the name of the phase
 121        earlyTarget (string): early switching to phase with the given index(es)
 122        """
 123        self.duration = duration
 124        self.state = state
 125        # minimum and maximum duration (only for actuated tls)
 126        self.minDur = minDur if minDur is not None else duration
 127        self.maxDur = maxDur if maxDur is not None else duration
 128        self.next = next
 129        self.name = name
 130        self.earlyTarget = earlyTarget
 131
 132    def __repr__(self):
 133        name = (", name='%s'" % self.name) if self.name else ""
 134        next = (", next='%s'" % str(self.next)) if self.next else ""
 135        earlyTarget = (", earlyTarget='%s'" % self.earlyTarget) if self.earlyTarget else ""
 136        return ("Phase(duration=%s, state='%s', minDur=%s, maxDur=%s%s%s%s)"
 137                % (self.duration, self.state, self.minDur, self.maxDur, name, next, earlyTarget))
 138
 139
 140class TLSProgram:
 141
 142    def __init__(self, id, offset, type):
 143        self._id = id
 144        self._type = type
 145        self._offset = offset
 146        self._phases = []
 147        self._params = {}
 148        self._conditions = {}
 149
 150    def addPhase(self, state, duration, minDur=-1, maxDur=-1, next=None, name="", earlyTarget=""):
 151        self._phases.append(Phase(duration, state, minDur, maxDur, next, name, earlyTarget))
 152
 153    def addCondition(self, id, value):
 154        self._conditions[id] = value
 155
 156    def toXML(self, tlsID):
 157        ret = '  <tlLogic id="%s" type="%s" programID="%s" offset="%s">\n' % (
 158            tlsID, self._type, self._id, self._offset)
 159        for p in self._phases:
 160            minDur = '' if p.minDur < 0 else ' minDur="%s"' % p.minDur
 161            maxDur = '' if p.maxDur < 0 else ' maxDur="%s"' % p.maxDur
 162            name = '' if p.name == '' else ' name="%s"' % p.name
 163            next = '' if len(p.next) == 0 else ' next="%s"' % ' '.join(map(str, p.next))
 164            earlyTarget = '' if p.earlyTarget == '' else ' earlyTarget="%s"' % p.earlyTarget
 165            ret += '    <phase duration="%s" state="%s"%s%s%s%s%s/>\n' % (
 166                p.duration, p.state, minDur, maxDur, name, next, earlyTarget)
 167        for k, v in self._params.items():
 168            ret += '    <param key="%s" value="%s"/>\n' % (k, v)
 169        for i, v in self._conditions.items():
 170            ret += '    <condition id="%s" value="%s"/>\n' % (i, v)
 171        ret += '  </tlLogic>\n'
 172        return ret
 173
 174    def getPhases(self):
 175        return self._phases
 176
 177    def getType(self):
 178        return self._type
 179
 180    def setParam(self, key, value):
 181        self._params[key] = value
 182
 183    def getParam(self, key, default=None):
 184        return self._params.get(key, default)
 185
 186    def getParams(self):
 187        return self._params
 188
 189    def getStages(self):
 190        stages = dict()
 191        for idx, phase in enumerate(self.getPhases()):
 192            if phase not in stages.values():
 193                if 'G' in phase.state and 'y' not in phase.state and phase.name:
 194                    stages[idx] = phase
 195        return stages
 196
 197    def getOffset(self):
 198        return self._offset
 199
 200
 201class EdgeType:
 202    def __init__(self, id, allow, disallow):
 203        self.id = id
 204        self.allow = allow
 205        self.disallow = disallow
 206
 207
 208class Net:
 209
 210    """The whole sumo network."""
 211
 212    def __init__(self):
 213        self._location = {}
 214        self._id2node = {}
 215        self._id2edge = {}
 216        self._crossings_and_walkingAreas = set()
 217        self._macroConnectors = set()
 218        self._id2tls = {}
 219        self._nodes = []
 220        self._edges = []
 221        self._tlss = []
 222        self._ranges = [[sys.float_info.max, -sys.float_info.max], [sys.float_info.max, -sys.float_info.max]]
 223        self._roundabouts = []
 224        self._rtreeEdges = None
 225        self._rtreeLanes = None
 226        self._allLanes = []
 227        self._origIdx = None
 228        self._proj = None
 229        self.hasInternal = False
 230        self.hasWalkingArea = False
 231        # store dijsktra heap for reuse if the same origin is used repeatedly
 232        self._shortestPathCache = None
 233        self._version = None
 234        self._edgeTypes = defaultdict(lambda: EdgeType("DEFAULT_EDGETYPE", "", ""))
 235        self._routingCache = None
 236
 237    def initRoutingCache(self, maxsize=1000):
 238        self._routingCache = lru_cache(maxsize=maxsize)(lambda fromEdge, fromPos, fastest, vClass, ignoreDirection, reversalPenalty, preferences: {})  # noqa
 239
 240    def getVersion(self):
 241        return self._version
 242
 243    def getEdgeType(self, typeID):
 244        return self._edgeTypes[typeID]
 245
 246    def setLocation(self, netOffset, convBoundary, origBoundary, projParameter):
 247        self._location["netOffset"] = netOffset
 248        self._location["convBoundary"] = convBoundary
 249        self._location["origBoundary"] = origBoundary
 250        self._location["projParameter"] = projParameter
 251
 252    def loadSelection(self, selectionFile):
 253        self.resetSelection()
 254        with io.open(selectionFile, "r", encoding="utf-8") as f:
 255            for line in f:
 256                line = line.strip()
 257                if line.startswith("edge:"):
 258                    edgeID = line[5:]
 259                    if edgeID in self._id2edge:
 260                        self.getEdge(edgeID).select()
 261                elif line.startswith("junction:"):
 262                    nodeID = line[9:]
 263                    if nodeID in self._id2node:
 264                        self.getNode(nodeID).select()
 265
 266    def resetSelection(self):
 267        for n in self._nodes:
 268            n.select(False)
 269        for e in self._edges:
 270            e.select(False)
 271
 272    def addNode(self, id, type=None, coord=None, incLanes=None, intLanes=None):
 273        if id is None:
 274            return None
 275        if id not in self._id2node:
 276            n = node.Node(id, type, coord, incLanes, intLanes)
 277            self._nodes.append(n)
 278            self._id2node[id] = n
 279        self.setAdditionalNodeInfo(
 280            self._id2node[id], type, coord, incLanes, intLanes)
 281        return self._id2node[id]
 282
 283    def setAdditionalNodeInfo(self, node, type, coord, incLanes, intLanes=None):
 284        if coord is not None and node._coord is None:
 285            node._coord = coord
 286            self._ranges[0][0] = min(self._ranges[0][0], coord[0])
 287            self._ranges[0][1] = max(self._ranges[0][1], coord[0])
 288            self._ranges[1][0] = min(self._ranges[1][0], coord[1])
 289            self._ranges[1][1] = max(self._ranges[1][1], coord[1])
 290        if incLanes is not None and node._incLanes is None:
 291            node._incLanes = incLanes
 292        if intLanes is not None and node._intLanes is None:
 293            node._intLanes = intLanes
 294        if type is not None and node._type is None:
 295            node._type = type
 296
 297    def addEdge(self, id, fromID, toID, prio, function, name, edgeType='', routingType=''):
 298        if id not in self._id2edge:
 299            fromN = self.addNode(fromID)
 300            toN = self.addNode(toID)
 301            e = edge.Edge(id, fromN, toN, prio, function, name, edgeType, routingType)
 302            self._edges.append(e)
 303            self._id2edge[id] = e
 304            if function:
 305                self.hasInternal = True
 306                if function == "walkingarea":
 307                    self.hasWalkingArea = True
 308        return self._id2edge[id]
 309
 310    def addLane(self, edge, speed, length, width, allow=None, disallow=None, acceleration=False):
 311        return lane.Lane(edge, speed, length, width, allow, disallow, acceleration)
 312
 313    def addRoundabout(self, nodes, edges=None):
 314        r = roundabout.Roundabout(nodes, edges)
 315        self._roundabouts.append(r)
 316        return r
 317
 318    def addConnection(self, fromEdge, toEdge, fromlane, tolane, direction,
 319                      tls, tllink, tllink2, allow, disallow, state, viaLaneID=None):
 320        conn = connection.Connection(
 321            fromEdge, toEdge, fromlane, tolane, direction,
 322            tls, tllink, tllink2, allow, disallow, state, viaLaneID)
 323        fromEdge.addOutgoing(conn)
 324        fromlane.addOutgoing(conn)
 325        toEdge._addIncoming(conn)
 326        if viaLaneID:
 327            try:
 328                # internal lanes are only available when building with option withInternal=True
 329                viaLane = self.getLane(viaLaneID)
 330                viaEdge = viaLane.getEdge()
 331                viaEdge._addIncoming(connection.Connection(
 332                    fromEdge, viaEdge, fromlane, viaLane, direction, tls,
 333                    tllink, tllink2, allow, disallow, state, ''))
 334            except Exception:
 335                pass
 336        return conn
 337
 338    def getEdges(self, withInternal=True):
 339        if not withInternal:
 340            return [e for e in self._edges if e.getFunction() == '']
 341        else:
 342            return self._edges
 343
 344    def getRoundabouts(self):
 345        return self._roundabouts
 346
 347    def hasEdge(self, id):
 348        return id in self._id2edge
 349
 350    def getEdge(self, id):
 351        return self._id2edge[id]
 352
 353    def getLane(self, laneID):
 354        edge_id, lane_index = laneID.rsplit("_", 1)
 355        return self.getEdge(edge_id).getLane(int(lane_index))
 356
 357    def _initRTree(self, shapeList, includeJunctions=True):
 358        import rtree  # noqa
 359        result = rtree.index.Index()
 360        result.interleaved = True
 361        for ri, shape in enumerate(shapeList):
 362            result.add(ri, shape.getBoundingBox(includeJunctions))
 363        return result
 364
 365    # Please be aware that the resulting list of edges is NOT sorted
 366    def getNeighboringEdges(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
 367        edges = []
 368        try:
 369            if self._rtreeEdges is None:
 370                self._rtreeEdges = self._initRTree(self._edges, includeJunctions)
 371            for i in self._rtreeEdges.intersection((x - r, y - r, x + r, y + r)):
 372                e = self._edges[i]
 373                d = sumolib.geomhelper.distancePointToPolygon(
 374                    (x, y), e.getShape(includeJunctions))
 375                if d < r:
 376                    edges.append((e, d))
 377        except ImportError:
 378            if not allowFallback:
 379                raise
 380            warnings.warn("Module 'rtree' not available. Using brute-force fallback.")
 381            for the_edge in self._edges:
 382                d = sumolib.geomhelper.distancePointToPolygon((x, y), the_edge.getShape(includeJunctions))
 383                if d < r:
 384                    edges.append((the_edge, d))
 385        return edges
 386
 387    def getNeighboringLanes(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
 388        lanes = []
 389        try:
 390            if self._rtreeLanes is None:
 391                for the_edge in self._edges:
 392                    self._allLanes += the_edge.getLanes()
 393                self._rtreeLanes = self._initRTree(self._allLanes, includeJunctions)
 394            for i in self._rtreeLanes.intersection((x - r, y - r, x + r, y + r)):
 395                the_lane = self._allLanes[i]
 396                d = sumolib.geomhelper.distancePointToPolygon((x, y), the_lane.getShape(includeJunctions))
 397                if d < r:
 398                    lanes.append((the_lane, d))
 399        except ImportError:
 400            if not allowFallback:
 401                raise
 402            warnings.warn("Module 'rtree' not available. Using brute-force fallback.")
 403            for the_edge in self._edges:
 404                for the_lane in the_edge.getLanes():
 405                    d = sumolib.geomhelper.distancePointToPolygon((x, y), the_lane.getShape(includeJunctions))
 406                    if d < r:
 407                        lanes.append((the_lane, d))
 408        return lanes
 409
 410    def hasNode(self, id):
 411        return id in self._id2node
 412
 413    def getNode(self, id):
 414        return self._id2node[id]
 415
 416    def getNodes(self):
 417        return self._nodes
 418
 419    def getTLS(self, tlid):
 420        return self._id2tls[tlid]
 421
 422    def getTLSSecure(self, tlid):
 423        if tlid in self._id2tls:
 424            tls = self._id2tls[tlid]
 425        else:
 426            tls = TLS(tlid)
 427            self._id2tls[tlid] = tls
 428            self._tlss.append(tls)
 429        return tls
 430
 431    def getTrafficLights(self):
 432        return self._tlss
 433
 434    def addTLS(self, tlid, inLane, outLane, linkNo):
 435        tls = self.getTLSSecure(tlid)
 436        tls.addConnection(inLane, outLane, linkNo)
 437        return tls
 438
 439    def addTLSProgram(self, tlid, programID, offset, type, removeOthers):
 440        tls = self.getTLSSecure(tlid)
 441        program = TLSProgram(programID, offset, type)
 442        if removeOthers:
 443            tls.removePrograms()
 444        tls.addProgram(program)
 445        return program
 446
 447    def setFoes(self, junctionID, index, foes, prohibits):
 448        self._id2node[junctionID].setFoes(index, foes, prohibits)
 449
 450    def forbids(self, possProhibitor, possProhibited):
 451        return possProhibitor.getFrom().getToNode().forbids(possProhibitor, possProhibited)
 452
 453    def getUpstreamEdges(self, edge, distance, stopOnTLS, stopOnTurnaround):
 454        """return a list of lists of the form
 455           [[firstEdge, pos, [edge_0, edge_1, ..., edge_k], aborted], ...]
 456           where
 457             firstEdge: is the upstream edge furthest away from the intersection,
 458             [edge_0, ..., edge_k]: is the list of edges from the intersection upstream to firstEdge
 459             pos: is the position on firstEdge with distance to the end of the input edge
 460             aborted: a flag indicating whether the downstream
 461                 search stopped at a TLS or a node without incoming edges before reaching the distance threshold
 462        """
 463        ret = []
 464        seen = set()
 465        toProc = []
 466        toProc.append([edge, 0, []])
 467        while not len(toProc) == 0:
 468            ie = toProc.pop()
 469            if ie[0] in seen:
 470                continue
 471            seen.add(ie[0])
 472            if ie[1] + ie[0].getLength() >= distance:
 473                ret.append(
 474                    [ie[0], ie[0].getLength() + ie[1] - distance, ie[2], False])
 475                continue
 476            if len(ie[0]._incoming) == 0:
 477                ret.append([ie[0], ie[0].getLength() + ie[1], ie[2], True])
 478                continue
 479            mn = []
 480            stop = False
 481            for ci in ie[0]._incoming:
 482                if ci not in seen:
 483                    prev = copy(ie[2])
 484                    if stopOnTLS and ci._tls and ci != edge and not stop:
 485                        ret.append([ie[0], ie[1], prev, True])
 486                        stop = True
 487                    elif (stopOnTurnaround and ie[0]._incoming[ci][0].getDirection() == Connection.LINKDIR_TURN and
 488                          not stop):
 489                        ret.append([ie[0], ie[1], prev, True])
 490                        stop = True
 491                    else:
 492                        prev.append(ie[0])
 493                        mn.append([ci, ie[0].getLength() + ie[1], prev])
 494            if not stop:
 495                toProc.extend(mn)
 496        return ret
 497
 498    def getEdgesByOrigID(self, origID):
 499        if self._origIdx is None:
 500            self._origIdx = defaultdict(set)
 501            for the_edge in self._edges:
 502                for the_lane in the_edge.getLanes():
 503                    for oID in the_lane.getParam("origId", "").split():
 504                        self._origIdx[oID].add(the_edge)
 505        return self._origIdx[origID]
 506
 507    def getGeometries(self, useLanes, includeJunctions=False):
 508        for e in self._edges:
 509            if useLanes:
 510                for the_lane in e.getLanes():
 511                    yield the_lane.getID(), the_lane.getShape(), the_lane.getWidth()
 512            else:
 513                yield e.getID(), e.getShape(includeJunctions), sum([the_lane.getWidth() for the_lane in e.getLanes()])
 514
 515    def getBBoxXY(self):
 516        """
 517        Get the bounding box (bottom left and top right coordinates) for a net;
 518        Coordinates are in X and Y (not Lat and Lon)
 519
 520        :return [(bottom_left_X, bottom_left_Y), (top_right_X, top_right_Y)]
 521        """
 522        return [(self._ranges[0][0], self._ranges[1][0]),
 523                (self._ranges[0][1], self._ranges[1][1])]
 524
 525    # the diagonal of the bounding box of all nodes
 526    def getBBoxDiameter(self):
 527        return math.sqrt(
 528            (self._ranges[0][0] - self._ranges[0][1]) ** 2 +
 529            (self._ranges[1][0] - self._ranges[1][1]) ** 2)
 530
 531    def hasGeoProj(self):
 532        projString = self._location["projParameter"]
 533        return projString != "!"
 534
 535    def getGeoProj(self):
 536        if not self.hasGeoProj():
 537            raise RuntimeError("Network does not provide geo-projection")
 538        if self._proj is None:
 539            import pyproj
 540            try:
 541                self._proj = pyproj.Proj(projparams=self._location["projParameter"])
 542            except RuntimeError:
 543                if hasattr(pyproj.datadir, 'set_data_dir'):
 544                    pyproj.datadir.set_data_dir('/usr/share/proj')
 545                    self._proj = pyproj.Proj(projparams=self._location["projParameter"])
 546                raise
 547        return self._proj
 548
 549    def getLocationOffset(self):
 550        """ offset to be added after converting from geo-coordinates to UTM"""
 551        return list(map(float, self._location["netOffset"].split(",")))
 552
 553    def getBoundary(self):
 554        """ return xmin,ymin,xmax,ymax network coordinates"""
 555        return list(map(float, self._location["convBoundary"].split(",")))
 556
 557    def convertLonLat2XY(self, lon, lat, rawUTM=False):
 558        x, y = self.getGeoProj()(lon, lat)
 559        if rawUTM:
 560            return x, y
 561        else:
 562            x_off, y_off = self.getLocationOffset()
 563            return x + x_off, y + y_off
 564
 565    def convertXY2LonLat(self, x, y, rawUTM=False):
 566        if not rawUTM:
 567            x_off, y_off = self.getLocationOffset()
 568            x -= x_off
 569            y -= y_off
 570        return self.getGeoProj()(x, y, inverse=True)
 571
 572    def move(self, dx, dy, dz=0):
 573        for n in self._nodes:
 574            n._coord = (n._coord[0] + dx, n._coord[1] + dy, n._coord[2] + dz)
 575        for e in self._edges:
 576            for _lane in e.getLanes():
 577                _lane.setShape([(p[0] + dx, p[1] + dy, p[2] + dz) for p in _lane.getShape3D()])
 578            e.rebuildShape()
 579
 580    def getInternalPath(self, conn, fastest=False):
 581        minInternalCost = 1e400
 582        minPath = None
 583        for c in conn:
 584            if c.getViaLaneID() != "":
 585                viaCost = 0
 586                viaID = c.getViaLaneID()
 587                viaPath = []
 588                while viaID != "":
 589                    viaLane = self.getLane(viaID)
 590                    viaCost += viaLane.getLength() if not fastest else viaLane.getLength() / viaLane.getSpeed()
 591                    viaID = viaLane.getOutgoing()[0].getViaLaneID()
 592                    viaPath.append(viaLane.getEdge())
 593                if viaCost < minInternalCost:
 594                    minInternalCost = viaCost
 595                    minPath = viaPath
 596        return minPath, minInternalCost
 597
 598    def getOptimalPath(self, fromEdge, toEdge, fastest=False, maxCost=1e400, vClass=None, reversalPenalty=0,
 599                       includeFromToCost=True, withInternal=False, ignoreDirection=False,
 600                       fromPos=None, toPos=None, preferences={}):
 601        """
 602        Finds the optimal (shortest or fastest) path for vClass from fromEdge to toEdge
 603        by using using Dijkstra's algorithm.
 604        It returns a pair of a tuple of edges and the cost.
 605        If no path is found the first element is None.
 606        The cost for the returned path is equal to the sum of all edge costs in the path,
 607        including the internal connectors, if they are present in the network.
 608        The path itself does not include internal edges except for the case
 609        when the start or end edge are internal edges.
 610        The search may be limited using the given threshold.
 611        The preferences declare a mapping from the 'routingType' of each edge to divisor
 612        that is applied to the computed cost of that edge (i.e. a preference of 2 reduces cost by a factor of 0.5).
 613        """
 614
 615        if preferences:
 616            if fastest:
 617                def speedFunc(edge):
 618                    return preferences.get(edge.getRoutingType(), 1.0) * edge.getSpeed()
 619            else:
 620                def speedFunc(edge):
 621                    return preferences.get(edge.getRoutingType(), 1.0)
 622        elif fastest:
 623            def speedFunc(edge):
 624                return edge.getSpeed()
 625        else:
 626            def speedFunc(edge):
 627                return 1.0
 628
 629        def remainder(edge, pos):
 630            if pos < 0:
 631                return min(-pos, edge.getLength())
 632            return max(0., edge.getLength() - pos)
 633
 634        def getToNormalIncoming(edge):
 635            if edge.getFunction() == '':
 636                return [(e, None) for e in edge.getToNode().getIncoming() if e.getFunction() == '']
 637            else:
 638                return []
 639
 640        if self.hasInternal:
 641            appendix = []
 642            appendixCost = 0.
 643            while toEdge.getFunction() == "internal":
 644                appendix = [toEdge] + appendix
 645                appendixCost += toEdge.getLength() / speedFunc(toEdge)
 646                toEdge = list(toEdge.getIncoming().keys())[0]
 647
 648        def finalizeCost(cost, path):
 649            if includeFromToCost:
 650                # add costs for (part of) the first edge, still needs to be fixed for wrong direction travel
 651                remainFrom = fromEdge.getLength() if fromPos is None else remainder(fromEdge, fromPos)
 652                cost += remainFrom / speedFunc(fromEdge)
 653                # remove costs for (part of) the last edge, still needs to be fixed for wrong direction travel
 654                removeTo = 0. if toPos is None else remainder(toEdge, toPos)
 655            else:
 656                removeTo = toEdge.getLength() if len(path) > 1 else 0.
 657            cost -= removeTo / speedFunc(toEdge)
 658            return cost
 659
 660        def constructPath(dist):
 661            # destination was already reached in a previous query
 662            cost, pred = dist[toEdge]
 663            path = [toEdge]
 664            while pred is not None:
 665                if self.hasInternal and withInternal:
 666                    viaPath, minInternalCost = self.getInternalPath(pred.getAllowedOutgoing(vClass).get(path[-1], []),
 667                                                                    fastest=fastest)
 668                    if viaPath is not None:
 669                        path += reversed(viaPath)
 670                path.append(pred)
 671                _, pred = dist[pred]
 672
 673            path.reverse()
 674            cost = finalizeCost(cost, path)
 675            assert cost >= 0
 676            if self.hasInternal:
 677                if appendix:
 678                    return tuple(path + appendix), cost + appendixCost
 679                elif ignoreDirection and self.hasWalkingArea and not withInternal:
 680                    return [e for e in path if e.getFunction() == ''], cost
 681            return tuple(path), cost
 682
 683        needLoop = (fromEdge == toEdge
 684                    and fromPos is not None
 685                    and toPos is not None
 686                    and fromPos > toPos
 687                    and not ignoreDirection)
 688
 689        seen = set()
 690        dist = {}
 691        q = []
 692        if self._routingCache is not None:
 693            if needLoop:
 694                # use cached results from all follower edges:
 695                bestCost = maxCost
 696                bestPath = None
 697                for e2, conn in fromEdge.getAllowedOutgoing(vClass).items():
 698                    path, cost = self.getOptimalPath(e2, toEdge, fastest=fastest, maxCost=maxCost, vClass=vClass,
 699                                                     reversalPenalty=reversalPenalty,
 700                                                     includeFromToCost=includeFromToCost,
 701                                                     withInternal=withInternal, fromPos=0, toPos=toPos,
 702                                                     preferences=preferences)
 703                    if path is not None and cost < bestCost:
 704                        bestPath = path
 705                        bestCost = cost
 706                if bestPath is not None:
 707                    path = [fromEdge]
 708                    if self.hasInternal and withInternal:
 709                        viaPath, minInternalCost = self.getInternalPath(
 710                            fromEdge.getAllowedOutgoing(vClass).get(path[0], []), fastest=fastest)
 711                        if viaPath is not None:
 712                            path += viaPath
 713                            bestCost += minInternalCost
 714                    path += list(bestPath)
 715                    if includeFromToCost:
 716                        bestCost += remainder(fromEdge, fromPos) / speedFunc(fromEdge)
 717                    return tuple(path), bestCost
 718                else:
 719                    return None, 1e400
 720
 721            dist = self._routingCache(fromEdge, fromPos, fastest, vClass, ignoreDirection, reversalPenalty,
 722                                      tuple(preferences.items()))
 723            if toEdge in dist:
 724                return constructPath(dist)
 725            else:
 726                # initialize heap from previous query
 727                q = []
 728                frontier = set(dist.keys())
 729                for cost, prev in dist.values():
 730                    frontier.discard(prev)
 731                for e in frontier:
 732                    cost, prev = dist[e]
 733                    heapq.heappush(q, (cost, e, prev))
 734        elif needLoop:
 735            # start search on successors of fromEdge
 736            for e2, conn in fromEdge.getAllowedOutgoing(vClass).items():
 737                q.append((e2.getLength() / speedFunc(e2), e2, fromEdge))
 738
 739        if len(dist) == 0:
 740            dist[fromEdge] = (0., None)
 741            if not needLoop:
 742                q.append((0., fromEdge, None))
 743
 744        while q:
 745            cost, e1, prev = heapq.heappop(q)
 746            if e1 in seen:
 747                continue
 748            seen.add(e1)
 749            if e1 == toEdge:
 750                return constructPath(dist)
 751            if cost > maxCost:
 752                return None, cost
 753
 754            for e2, conn in chain(e1.getAllowedOutgoing(vClass).items(),
 755                                  e1.getIncoming().items() if ignoreDirection else [],
 756                                  getToNormalIncoming(e1) if ignoreDirection and not self.hasWalkingArea else []):
 757                if e2 not in seen:
 758                    newCost = cost + e2.getLength() / speedFunc(e2)
 759                    #  print(cost, newCost, e2.getID(), speedFunc(e2))
 760                    if e2 == e1.getBidi():
 761                        newCost += reversalPenalty
 762                    if self.hasInternal and conn is not None:
 763                        viaPath, minInternalCost = self.getInternalPath(conn, fastest=fastest)
 764                        if viaPath is not None:
 765                            newCost += minInternalCost
 766                    if e2 not in dist or newCost < dist[e2][0]:
 767                        dist[e2] = (newCost, e1)
 768                        heapq.heappush(q, (newCost, e2, e1))
 769        return None, 1e400
 770
 771    def getShortestPath(self, fromEdge, toEdge, maxCost=1e400, vClass=None, reversalPenalty=0,
 772                        includeFromToCost=True, withInternal=False, ignoreDirection=False,
 773                        fromPos=None, toPos=None, preferences={}):
 774        """
 775        Finds the shortest path from fromEdge to toEdge respecting vClass, using Dijkstra's algorithm.
 776        It returns a pair of a tuple of edges and the cost. If no path is found the first element is None.
 777        The cost for the returned path is equal to the sum of all edge lengths in the path,
 778        including the internal connectors, if they are present in the network.
 779        The path itself does not include internal edges except for the case
 780        when the start or end edge are internal edges.
 781        The search may be limited using the given threshold.
 782        The preferences declare a mapping from the 'routingType' of each edge to divisor
 783        that is applied to the lenght of that edge
 784        (i.e. a preference of 2 reduces reduces effective length by a factor of 0.5).
 785        """
 786
 787        return self.getOptimalPath(fromEdge, toEdge, False, maxCost, vClass, reversalPenalty,
 788                                   includeFromToCost, withInternal, ignoreDirection, fromPos, toPos,
 789                                   preferences)
 790
 791    def getFastestPath(self, fromEdge, toEdge, maxCost=1e400, vClass=None, reversalPenalty=0,
 792                       includeFromToCost=True, withInternal=False, ignoreDirection=False,
 793                       fromPos=None, toPos=None, preferences={}):
 794        """
 795        Finds the fastest path from fromEdge to toEdge respecting vClass, using Dijkstra's algorithm.
 796        It returns a pair of a tuple of edges and the cost. If no path is found the first element is None.
 797        The cost for the returned path is equal to the sum of all edge costs in the path,
 798        including the internal connectors, if they are present in the network.
 799        The path itself does not include internal edges except for the case
 800        when the start or end edge are internal edges.
 801        The search may be limited using the given threshold.
 802        The preferences declare a mapping from the 'routingType' of each edge to divisor
 803        that is applied to the computed traveltime of that edge
 804        (i.e. a preference of 2 reduces effective traveltime by a factor of 0.5).
 805        """
 806
 807        return self.getOptimalPath(fromEdge, toEdge, True, maxCost, vClass, reversalPenalty,
 808                                   includeFromToCost, withInternal, ignoreDirection, fromPos, toPos,
 809                                   preferences)
 810
 811    def getReachable(self, source, vclass=None, useIncoming=False, cache=None):
 812        if vclass is not None and not source.allows(vclass):
 813            raise RuntimeError("'{}' does not allow {}".format(source.getID(), vclass))
 814        fringe = [source]
 815        found = set()
 816        found.add(source)
 817        while len(fringe) > 0:
 818            new_fringe = []
 819            for e in fringe:
 820                if vclass == "pedestrian":
 821                    cands = chain(chain(*e.getIncoming().values()), chain(*e.getOutgoing().values()))
 822                else:
 823                    cands = chain(*(e.getIncoming().values() if useIncoming else e.getOutgoing().values()))
 824                # print("\n".join(map(str, list(cands))))
 825                for conn in cands:
 826                    if vclass is None or (
 827                            conn.getFromLane().allows(vclass)
 828                            and conn.getToLane().allows(vclass)):
 829                        for reachable in [conn.getTo(), conn.getFrom()]:
 830                            if reachable not in found:
 831                                # print("added %s via %s" % (reachable, conn))
 832                                if cache and reachable in cache:
 833                                    found.update(cache[reachable])
 834                                else:
 835                                    found.add(reachable)
 836                                    new_fringe.append(reachable)
 837            fringe = new_fringe
 838        if cache is not None:
 839            cache[source] = tuple(found)
 840        return found
 841
 842
 843class NetReader(handler.ContentHandler):
 844
 845    """Reads a network, storing the edge geometries, lane numbers and max. speeds"""
 846
 847    def __init__(self, **others):
 848        self._net = others.get('net', Net())
 849        self._currentEdge = None
 850        self._currentNode = None
 851        self._currentConnection = None
 852        self._currentLane = None
 853        self._crossingID2edgeIDs = {}
 854        self._withPhases = others.get('withPrograms', False)
 855        self._latestProgram = others.get('withLatestPrograms', False)
 856        if self._latestProgram:
 857            self._withPhases = True
 858        self._withConnections = others.get('withConnections', True)
 859        self._withFoes = others.get('withFoes', True)
 860        self._withPedestrianConnections = others.get('withPedestrianConnections', False)
 861        self._withMacroConnectors = others.get('withMacroConnectors', False)
 862        self._withInternal = others.get('withInternal', self._withPedestrianConnections)
 863        if self._withPedestrianConnections and not self._withInternal:
 864            sys.stderr.write("Warning: Option withPedestrianConnections requires withInternal\n")
 865            self._withInternal = True
 866        self._bidiEdgeIDs = {}
 867
 868    def startElement(self, name, attrs):
 869        if name == 'net':
 870            parts = attrs["version"].split('.', 1)
 871            self._net._version = (int(parts[0]), float(parts[1]))
 872        elif name == 'location':
 873            self._net.setLocation(attrs["netOffset"], attrs["convBoundary"], attrs[
 874                                  "origBoundary"], attrs["projParameter"])
 875        elif name == 'type':
 876            self._net._edgeTypes[attrs['id']] = EdgeType(attrs['id'], attrs.get('allow'), attrs.get('disallow'))
 877        elif name == 'edge':
 878            function = attrs.get('function', '')
 879            if (function == ''
 880                    or (self._withInternal and function in ['internal', 'crossing', 'walkingarea'])
 881                    or (self._withMacroConnectors and function == 'connector')):
 882                prio = -1
 883                if 'priority' in attrs:
 884                    prio = int(attrs['priority'])
 885
 886                # get the  ids
 887                edgeID = attrs['id']
 888                fromNodeID = attrs.get('from', None)
 889                toNodeID = attrs.get('to', None)
 890
 891                # for internal junctions use the junction's id for from and to node
 892                if function == 'internal' or function == 'crossing' or function == 'walkingarea':
 893                    fromNodeID = toNodeID = edgeID[1:edgeID.rfind('_')]
 894
 895                # remember edges crossed by pedestrians to link them later to the crossing objects
 896                if function == 'crossing':
 897                    self._crossingID2edgeIDs[edgeID] = attrs.get('crossingEdges').split(' ')
 898
 899                self._currentEdge = self._net.addEdge(edgeID, fromNodeID, toNodeID, prio, function,
 900                                                      attrs.get('name', ''), attrs.get('type', ''),
 901                                                      attrs.get('routingType', ''))
 902
 903                self._currentEdge.setRawShape(convertShape(attrs.get('shape', '')))
 904
 905                bidi = attrs.get('bidi', '')
 906                if bidi:
 907                    self._bidiEdgeIDs[edgeID] = bidi
 908            else:
 909                if function in ['crossing', 'walkingarea']:
 910                    self._net._crossings_and_walkingAreas.add(attrs['id'])
 911                elif function == 'connector':
 912                    self._net._macroConnectors.add(attrs['id'])
 913                self._currentEdge = None
 914        elif name == 'lane' and self._currentEdge is not None:
 915            self._currentLane = self._net.addLane(
 916                self._currentEdge,
 917                float(attrs['speed']),
 918                float(attrs['length']),
 919                float(attrs.get('width', 3.2)),
 920                attrs.get('allow'),
 921                attrs.get('disallow'),
 922                attrs.get('acceleration') == "1")
 923            self._currentLane.setShape(convertShape(attrs.get('shape', '')))
 924        elif name == 'neigh' and self._currentLane is not None:
 925            self._currentLane.setNeigh(attrs['lane'])
 926        elif name == 'junction':
 927            if attrs['id'][0] != ':':
 928                intLanes = None
 929                if self._withInternal:
 930                    intLanes = attrs["intLanes"].split(" ")
 931                self._currentNode = self._net.addNode(attrs['id'], attrs['type'],
 932                                                      tuple(
 933                                                          map(float, [attrs['x'], attrs['y'],
 934                                                                      attrs['z'] if 'z' in attrs else '0'])),
 935                                                      attrs['incLanes'].split(" "), intLanes)
 936                self._currentNode.setShape(
 937                    convertShape(attrs.get('shape', '')))
 938                if 'fringe' in attrs:
 939                    self._currentNode._fringe = attrs['fringe']
 940
 941        elif name == 'succ' and self._withConnections:  # deprecated
 942            if attrs['edge'][0] != ':':
 943                self._currentEdge = self._net.getEdge(attrs['edge'])
 944                self._currentLane = attrs['lane']
 945                self._currentLane = int(
 946                    self._currentLane[self._currentLane.rfind('_') + 1:])
 947            else:
 948                self._currentEdge = None
 949        elif name == 'succlane' and self._withConnections:  # deprecated
 950            lid = attrs['lane']
 951            if lid[0] != ':' and lid != "SUMO_NO_DESTINATION" and self._currentEdge:
 952                connected = self._net.getEdge(lid[:lid.rfind('_')])
 953                tolane = int(lid[lid.rfind('_') + 1:])
 954                if 'tl' in attrs and attrs['tl'] != "":
 955                    tl = attrs['tl']
 956                    tllink = int(attrs['linkIdx'])
 957                    tlid = attrs['tl']
 958                    toEdge = self._net.getEdge(lid[:lid.rfind('_')])
 959                    tolane2 = toEdge._lanes[tolane]
 960                    tls = self._net.addTLS(
 961                        tlid, self._currentEdge._lanes[self._currentLane], tolane2, tllink)
 962                    self._currentEdge.setTLS(tls)
 963                else:
 964                    tl = ""
 965                    tllink = -1
 966                toEdge = self._net.getEdge(lid[:lid.rfind('_')])
 967                tolane = toEdge._lanes[tolane]
 968                viaLaneID = attrs['via']
 969                self._net.addConnection(self._currentEdge, connected, self._currentEdge._lanes[
 970                                        self._currentLane], tolane,
 971                                        attrs['dir'], tl, tllink, -1,
 972                                        attrs.get('allow'), attrs.get('disallow'), attrs['state'], viaLaneID)
 973        elif name == 'connection' and self._withConnections and (attrs['from'][0] != ":" or self._withInternal):
 974            fromEdgeID = attrs['from']
 975            toEdgeID = attrs['to']
 976            if ((self._withPedestrianConnections or not (fromEdgeID in self._net._crossings_and_walkingAreas or
 977                                                         toEdgeID in self._net._crossings_and_walkingAreas))
 978                and (self._withMacroConnectors or not (fromEdgeID in self._net._macroConnectors or toEdgeID in
 979                                                       self._net._macroConnectors))):
 980                fromEdge = self._net.getEdge(fromEdgeID)
 981                toEdge = self._net.getEdge(toEdgeID)
 982                fromLane = fromEdge.getLane(int(attrs['fromLane']))
 983                toLane = toEdge.getLane(int(attrs['toLane']))
 984                if 'tl' in attrs and attrs['tl'] != "":
 985                    tl = attrs['tl']
 986                    tllink = int(attrs['linkIndex'])
 987                    tllink2 = int(attrs.get('linkIndex2', -1))
 988                    tls = self._net.addTLS(tl, fromLane, toLane, tllink)
 989                    fromEdge.setTLS(tls)
 990                else:
 991                    tl = ""
 992                    tllink = -1
 993                    tllink2 = -1
 994                try:
 995                    viaLaneID = attrs['via']
 996                except KeyError:
 997                    viaLaneID = ''
 998
 999                self._currentConnection = self._net.addConnection(
1000                    fromEdge, toEdge, fromLane, toLane, attrs['dir'], tl,
1001                    tllink, tllink2, attrs.get('allow'), attrs.get('disallow'), attrs['state'], viaLaneID)
1002
1003        # 'row-logic' is deprecated!!!
1004        elif self._withFoes and name == 'ROWLogic':
1005            self._currentNode = attrs['id']
1006        elif name == 'logicitem' and self._withFoes:  # deprecated
1007            self._net.setFoes(
1008                self._currentNode, int(attrs['request']), attrs["foes"], attrs["response"])
1009        elif name == 'request' and self._withFoes:
1010            self._currentNode.setFoes(
1011                int(attrs['index']), attrs["foes"], attrs["response"])
1012        # tl-logic is deprecated!!! NOTE: nevertheless, this is still used by
1013        # netconvert... (Leo)
1014        elif self._withPhases and name == 'tlLogic':
1015            self._currentProgram = self._net.addTLSProgram(
1016                attrs['id'], attrs['programID'],
1017                intIfPossible(float(attrs['offset'])), attrs['type'], self._latestProgram)
1018        elif self._withPhases and name == 'phase':
1019            self._currentProgram.addPhase(
1020                attrs['state'],
1021                intIfPossible(float(attrs['duration'])),
1022                intIfPossible(float(attrs['minDur'])) if 'minDur' in attrs else -1,
1023                intIfPossible(float(attrs['maxDur'])) if 'maxDur' in attrs else -1,
1024                list(map(int, attrs['next'].split())) if 'next' in attrs else [],
1025                attrs['name'] if 'name' in attrs else ""
1026            )
1027        elif name == 'roundabout':
1028            self._net.addRoundabout(
1029                attrs['nodes'].split(), attrs['edges'].split())
1030        elif name == 'param':
1031            if self._currentLane is not None:
1032                self._currentLane.setParam(attrs['key'], attrs['value'])
1033            elif self._currentEdge is not None:
1034                self._currentEdge.setParam(attrs['key'], attrs['value'])
1035            elif self._currentNode is not None:
1036                self._currentNode.setParam(attrs['key'], attrs['value'])
1037            elif self._currentConnection is not None:
1038                self._currentConnection.setParam(attrs['key'], attrs['value'])
1039            elif self._withPhases and self._currentProgram is not None:
1040                self._currentProgram.setParam(attrs['key'], attrs['value'])
1041
1042    def endElement(self, name):
1043        if name == 'lane':
1044            self._currentLane = None
1045        elif name == 'edge':
1046            self._currentEdge = None
1047        elif name == 'junction':
1048            self._currentNode = None
1049        elif name == 'connection':
1050            self._currentConnection = None
1051        # 'row-logic' is deprecated!!!
1052        elif name == 'ROWLogic' or name == 'row-logic':
1053            self._haveROWLogic = False
1054        # tl-logic is deprecated!!!
1055        elif self._withPhases and (name == 'tlLogic' or name == 'tl-logic'):
1056            self._currentProgram = None
1057        elif name == 'net':
1058            for edgeID, bidiID in self._bidiEdgeIDs.items():
1059                self._net.getEdge(edgeID)._bidi = self._net.getEdge(bidiID)
1060
1061    def endDocument(self):
1062        # set crossed edges of pedestrian crossings
1063        for crossingID, crossedEdgeIDs in self._crossingID2edgeIDs.items():
1064            pedCrossing = self._net.getEdge(crossingID)
1065            for crossedEdgeID in crossedEdgeIDs:
1066                pedCrossing._addCrossingEdge(self._net.getEdge(crossedEdgeID))
1067
1068    def getNet(self):
1069        return self._net
1070
1071
1072def convertShape(shapeString):
1073    """ Convert xml shape string into float tuples.
1074
1075    This method converts the 2d or 3d shape string from SUMO's xml file
1076    into a list containing 3d float-tuples. Non existent z coordinates default
1077    to zero. If shapeString is empty, an empty list will be returned.
1078    """
1079
1080    cshape = []
1081    for pointString in shapeString.split():
1082        p = [float(e) for e in pointString.split(",")]
1083        if len(p) == 2:
1084            cshape.append((p[0], p[1], 0.))
1085        elif len(p) == 3:
1086            cshape.append(tuple(p))
1087        else:
1088            raise ValueError(
1089                'Invalid shape point "%s", should be either 2d or 3d' % pointString)
1090    return cshape
1091
1092
1093def lane2edge(laneID):
1094    return laneID[:laneID.rfind("_")]
1095
1096
1097def lane2index(laneID):
1098    return int(laneID[laneID.rfind("_") + 1:])
1099
1100
1101def readNet(filename, **others):
1102    """ load a .net.xml file
1103    The following named options are supported:
1104
1105        'net' : initialize data structures with an existing net object (default Net())
1106        'withPrograms' : import all traffic light programs (default False)
1107        'withLatestPrograms' : import only the last program for each traffic light.
1108                               This is the program that would be active in sumo by default.
1109                               (default False)
1110        'withConnections' : import all connections (default True)
1111        'withFoes' : import right-of-way information (default True)
1112        'withInternal' : import internal edges and lanes (default False)
1113        'withPedestrianConnections' : import connections between sidewalks, crossings (default False)
1114        'lxml' : set to False to use the xml.sax parser instead of the lxml parser
1115        'maxcache' : set maximum cache size (default 1000) or 0 to disable optimal route caching
1116    """
1117    netreader = NetReader(**others)
1118    try:
1119        source = gzip.open(filename)
1120        source.read(10)
1121        source.seek(0)
1122    except IOError:
1123        source = filename
1124    if HAVE_LXML and others.get("lxml", True):
1125        if isinstance(source, pathlib.Path):
1126            source = str(source)
1127        for event, v in lxml.etree.iterparse(source, events=("start", "end")):
1128            if event == "start":
1129                netreader.startElement(v.tag, v.attrib)
1130            elif event == "end":
1131                netreader.endElement(v.tag)
1132            v.clear()  # reduce memory footprint
1133    else:
1134        parse(source, netreader)
1135    net = netreader.getNet()
1136    maxcache = others.get('maxcache', 1000)
1137    if HAVE_LRU_CACHE and maxcache is not None and maxcache > 0:
1138        net.initRoutingCache(maxcache)
1139    return net
class TLS:
 60class TLS:
 61
 62    """Traffic Light Signal for a sumo network"""
 63
 64    def __init__(self, id):
 65        self._id = id
 66        self._connections = []
 67        self._maxConnectionNo = -1
 68        self._programs = {}
 69
 70    def addConnection(self, inLane, outLane, linkNo):
 71        self._connections.append([inLane, outLane, linkNo])
 72        if linkNo > self._maxConnectionNo:
 73            self._maxConnectionNo = linkNo
 74
 75    def getConnections(self):
 76        return self._connections
 77
 78    def getID(self):
 79        return self._id
 80
 81    def getLinks(self):
 82        links = {}
 83        for the_connection in self._connections:
 84            if the_connection[2] not in links:
 85                links[the_connection[2]] = []
 86            links[the_connection[2]].append(the_connection)
 87        return links
 88
 89    def getEdges(self):
 90        edges = set()
 91        for c in self._connections:
 92            edges.add(c[0].getEdge())
 93        return edges
 94
 95    def addProgram(self, program):
 96        self._programs[program._id] = program
 97
 98    def removePrograms(self):
 99        self._programs.clear()
100
101    def toXML(self):
102        ret = ""
103        for p in self._programs:
104            ret = ret + self._programs[p].toXML(self._id)
105        return ret
106
107    def getPrograms(self):
108        return self._programs

Traffic Light Signal for a sumo network

TLS(id)
64    def __init__(self, id):
65        self._id = id
66        self._connections = []
67        self._maxConnectionNo = -1
68        self._programs = {}
def addConnection(self, inLane, outLane, linkNo):
70    def addConnection(self, inLane, outLane, linkNo):
71        self._connections.append([inLane, outLane, linkNo])
72        if linkNo > self._maxConnectionNo:
73            self._maxConnectionNo = linkNo
def getConnections(self):
75    def getConnections(self):
76        return self._connections
def getID(self):
78    def getID(self):
79        return self._id
def getEdges(self):
89    def getEdges(self):
90        edges = set()
91        for c in self._connections:
92            edges.add(c[0].getEdge())
93        return edges
def addProgram(self, program):
95    def addProgram(self, program):
96        self._programs[program._id] = program
def removePrograms(self):
98    def removePrograms(self):
99        self._programs.clear()
def toXML(self):
101    def toXML(self):
102        ret = ""
103        for p in self._programs:
104            ret = ret + self._programs[p].toXML(self._id)
105        return ret
def getPrograms(self):
107    def getPrograms(self):
108        return self._programs
class Phase:
111class Phase:
112
113    def __init__(self, duration, state, minDur=None, maxDur=None, next=tuple(), name="", earlyTarget=""):
114        """
115        Constructs a traffic light phase
116        duration (float): the duration of the phase in seconds
117        state (string): the state codes for each controlled link
118        minDur (float): the minimum duration (ignored by static tls)
119        maxDur (float): the maximum duration (ignored by static tls)
120        next (intList): possible succesor phase (optional)
121        name (string): the name of the phase
122        earlyTarget (string): early switching to phase with the given index(es)
123        """
124        self.duration = duration
125        self.state = state
126        # minimum and maximum duration (only for actuated tls)
127        self.minDur = minDur if minDur is not None else duration
128        self.maxDur = maxDur if maxDur is not None else duration
129        self.next = next
130        self.name = name
131        self.earlyTarget = earlyTarget
132
133    def __repr__(self):
134        name = (", name='%s'" % self.name) if self.name else ""
135        next = (", next='%s'" % str(self.next)) if self.next else ""
136        earlyTarget = (", earlyTarget='%s'" % self.earlyTarget) if self.earlyTarget else ""
137        return ("Phase(duration=%s, state='%s', minDur=%s, maxDur=%s%s%s%s)"
138                % (self.duration, self.state, self.minDur, self.maxDur, name, next, earlyTarget))
Phase( duration, state, minDur=None, maxDur=None, next=(), name='', earlyTarget='')
113    def __init__(self, duration, state, minDur=None, maxDur=None, next=tuple(), name="", earlyTarget=""):
114        """
115        Constructs a traffic light phase
116        duration (float): the duration of the phase in seconds
117        state (string): the state codes for each controlled link
118        minDur (float): the minimum duration (ignored by static tls)
119        maxDur (float): the maximum duration (ignored by static tls)
120        next (intList): possible succesor phase (optional)
121        name (string): the name of the phase
122        earlyTarget (string): early switching to phase with the given index(es)
123        """
124        self.duration = duration
125        self.state = state
126        # minimum and maximum duration (only for actuated tls)
127        self.minDur = minDur if minDur is not None else duration
128        self.maxDur = maxDur if maxDur is not None else duration
129        self.next = next
130        self.name = name
131        self.earlyTarget = earlyTarget

Constructs a traffic light phase duration (float): the duration of the phase in seconds state (string): the state codes for each controlled link minDur (float): the minimum duration (ignored by static tls) maxDur (float): the maximum duration (ignored by static tls) next (intList): possible succesor phase (optional) name (string): the name of the phase earlyTarget (string): early switching to phase with the given index(es)

duration
state
minDur
maxDur
next
name
earlyTarget
class TLSProgram:
141class TLSProgram:
142
143    def __init__(self, id, offset, type):
144        self._id = id
145        self._type = type
146        self._offset = offset
147        self._phases = []
148        self._params = {}
149        self._conditions = {}
150
151    def addPhase(self, state, duration, minDur=-1, maxDur=-1, next=None, name="", earlyTarget=""):
152        self._phases.append(Phase(duration, state, minDur, maxDur, next, name, earlyTarget))
153
154    def addCondition(self, id, value):
155        self._conditions[id] = value
156
157    def toXML(self, tlsID):
158        ret = '  <tlLogic id="%s" type="%s" programID="%s" offset="%s">\n' % (
159            tlsID, self._type, self._id, self._offset)
160        for p in self._phases:
161            minDur = '' if p.minDur < 0 else ' minDur="%s"' % p.minDur
162            maxDur = '' if p.maxDur < 0 else ' maxDur="%s"' % p.maxDur
163            name = '' if p.name == '' else ' name="%s"' % p.name
164            next = '' if len(p.next) == 0 else ' next="%s"' % ' '.join(map(str, p.next))
165            earlyTarget = '' if p.earlyTarget == '' else ' earlyTarget="%s"' % p.earlyTarget
166            ret += '    <phase duration="%s" state="%s"%s%s%s%s%s/>\n' % (
167                p.duration, p.state, minDur, maxDur, name, next, earlyTarget)
168        for k, v in self._params.items():
169            ret += '    <param key="%s" value="%s"/>\n' % (k, v)
170        for i, v in self._conditions.items():
171            ret += '    <condition id="%s" value="%s"/>\n' % (i, v)
172        ret += '  </tlLogic>\n'
173        return ret
174
175    def getPhases(self):
176        return self._phases
177
178    def getType(self):
179        return self._type
180
181    def setParam(self, key, value):
182        self._params[key] = value
183
184    def getParam(self, key, default=None):
185        return self._params.get(key, default)
186
187    def getParams(self):
188        return self._params
189
190    def getStages(self):
191        stages = dict()
192        for idx, phase in enumerate(self.getPhases()):
193            if phase not in stages.values():
194                if 'G' in phase.state and 'y' not in phase.state and phase.name:
195                    stages[idx] = phase
196        return stages
197
198    def getOffset(self):
199        return self._offset
TLSProgram(id, offset, type)
143    def __init__(self, id, offset, type):
144        self._id = id
145        self._type = type
146        self._offset = offset
147        self._phases = []
148        self._params = {}
149        self._conditions = {}
def addPhase( self, state, duration, minDur=-1, maxDur=-1, next=None, name='', earlyTarget=''):
151    def addPhase(self, state, duration, minDur=-1, maxDur=-1, next=None, name="", earlyTarget=""):
152        self._phases.append(Phase(duration, state, minDur, maxDur, next, name, earlyTarget))
def addCondition(self, id, value):
154    def addCondition(self, id, value):
155        self._conditions[id] = value
def toXML(self, tlsID):
157    def toXML(self, tlsID):
158        ret = '  <tlLogic id="%s" type="%s" programID="%s" offset="%s">\n' % (
159            tlsID, self._type, self._id, self._offset)
160        for p in self._phases:
161            minDur = '' if p.minDur < 0 else ' minDur="%s"' % p.minDur
162            maxDur = '' if p.maxDur < 0 else ' maxDur="%s"' % p.maxDur
163            name = '' if p.name == '' else ' name="%s"' % p.name
164            next = '' if len(p.next) == 0 else ' next="%s"' % ' '.join(map(str, p.next))
165            earlyTarget = '' if p.earlyTarget == '' else ' earlyTarget="%s"' % p.earlyTarget
166            ret += '    <phase duration="%s" state="%s"%s%s%s%s%s/>\n' % (
167                p.duration, p.state, minDur, maxDur, name, next, earlyTarget)
168        for k, v in self._params.items():
169            ret += '    <param key="%s" value="%s"/>\n' % (k, v)
170        for i, v in self._conditions.items():
171            ret += '    <condition id="%s" value="%s"/>\n' % (i, v)
172        ret += '  </tlLogic>\n'
173        return ret
def getPhases(self):
175    def getPhases(self):
176        return self._phases
def getType(self):
178    def getType(self):
179        return self._type
def setParam(self, key, value):
181    def setParam(self, key, value):
182        self._params[key] = value
def getParam(self, key, default=None):
184    def getParam(self, key, default=None):
185        return self._params.get(key, default)
def getParams(self):
187    def getParams(self):
188        return self._params
def getStages(self):
190    def getStages(self):
191        stages = dict()
192        for idx, phase in enumerate(self.getPhases()):
193            if phase not in stages.values():
194                if 'G' in phase.state and 'y' not in phase.state and phase.name:
195                    stages[idx] = phase
196        return stages
def getOffset(self):
198    def getOffset(self):
199        return self._offset
class EdgeType:
202class EdgeType:
203    def __init__(self, id, allow, disallow):
204        self.id = id
205        self.allow = allow
206        self.disallow = disallow
EdgeType(id, allow, disallow)
203    def __init__(self, id, allow, disallow):
204        self.id = id
205        self.allow = allow
206        self.disallow = disallow
id
allow
disallow
class Net:
209class Net:
210
211    """The whole sumo network."""
212
213    def __init__(self):
214        self._location = {}
215        self._id2node = {}
216        self._id2edge = {}
217        self._crossings_and_walkingAreas = set()
218        self._macroConnectors = set()
219        self._id2tls = {}
220        self._nodes = []
221        self._edges = []
222        self._tlss = []
223        self._ranges = [[sys.float_info.max, -sys.float_info.max], [sys.float_info.max, -sys.float_info.max]]
224        self._roundabouts = []
225        self._rtreeEdges = None
226        self._rtreeLanes = None
227        self._allLanes = []
228        self._origIdx = None
229        self._proj = None
230        self.hasInternal = False
231        self.hasWalkingArea = False
232        # store dijsktra heap for reuse if the same origin is used repeatedly
233        self._shortestPathCache = None
234        self._version = None
235        self._edgeTypes = defaultdict(lambda: EdgeType("DEFAULT_EDGETYPE", "", ""))
236        self._routingCache = None
237
238    def initRoutingCache(self, maxsize=1000):
239        self._routingCache = lru_cache(maxsize=maxsize)(lambda fromEdge, fromPos, fastest, vClass, ignoreDirection, reversalPenalty, preferences: {})  # noqa
240
241    def getVersion(self):
242        return self._version
243
244    def getEdgeType(self, typeID):
245        return self._edgeTypes[typeID]
246
247    def setLocation(self, netOffset, convBoundary, origBoundary, projParameter):
248        self._location["netOffset"] = netOffset
249        self._location["convBoundary"] = convBoundary
250        self._location["origBoundary"] = origBoundary
251        self._location["projParameter"] = projParameter
252
253    def loadSelection(self, selectionFile):
254        self.resetSelection()
255        with io.open(selectionFile, "r", encoding="utf-8") as f:
256            for line in f:
257                line = line.strip()
258                if line.startswith("edge:"):
259                    edgeID = line[5:]
260                    if edgeID in self._id2edge:
261                        self.getEdge(edgeID).select()
262                elif line.startswith("junction:"):
263                    nodeID = line[9:]
264                    if nodeID in self._id2node:
265                        self.getNode(nodeID).select()
266
267    def resetSelection(self):
268        for n in self._nodes:
269            n.select(False)
270        for e in self._edges:
271            e.select(False)
272
273    def addNode(self, id, type=None, coord=None, incLanes=None, intLanes=None):
274        if id is None:
275            return None
276        if id not in self._id2node:
277            n = node.Node(id, type, coord, incLanes, intLanes)
278            self._nodes.append(n)
279            self._id2node[id] = n
280        self.setAdditionalNodeInfo(
281            self._id2node[id], type, coord, incLanes, intLanes)
282        return self._id2node[id]
283
284    def setAdditionalNodeInfo(self, node, type, coord, incLanes, intLanes=None):
285        if coord is not None and node._coord is None:
286            node._coord = coord
287            self._ranges[0][0] = min(self._ranges[0][0], coord[0])
288            self._ranges[0][1] = max(self._ranges[0][1], coord[0])
289            self._ranges[1][0] = min(self._ranges[1][0], coord[1])
290            self._ranges[1][1] = max(self._ranges[1][1], coord[1])
291        if incLanes is not None and node._incLanes is None:
292            node._incLanes = incLanes
293        if intLanes is not None and node._intLanes is None:
294            node._intLanes = intLanes
295        if type is not None and node._type is None:
296            node._type = type
297
298    def addEdge(self, id, fromID, toID, prio, function, name, edgeType='', routingType=''):
299        if id not in self._id2edge:
300            fromN = self.addNode(fromID)
301            toN = self.addNode(toID)
302            e = edge.Edge(id, fromN, toN, prio, function, name, edgeType, routingType)
303            self._edges.append(e)
304            self._id2edge[id] = e
305            if function:
306                self.hasInternal = True
307                if function == "walkingarea":
308                    self.hasWalkingArea = True
309        return self._id2edge[id]
310
311    def addLane(self, edge, speed, length, width, allow=None, disallow=None, acceleration=False):
312        return lane.Lane(edge, speed, length, width, allow, disallow, acceleration)
313
314    def addRoundabout(self, nodes, edges=None):
315        r = roundabout.Roundabout(nodes, edges)
316        self._roundabouts.append(r)
317        return r
318
319    def addConnection(self, fromEdge, toEdge, fromlane, tolane, direction,
320                      tls, tllink, tllink2, allow, disallow, state, viaLaneID=None):
321        conn = connection.Connection(
322            fromEdge, toEdge, fromlane, tolane, direction,
323            tls, tllink, tllink2, allow, disallow, state, viaLaneID)
324        fromEdge.addOutgoing(conn)
325        fromlane.addOutgoing(conn)
326        toEdge._addIncoming(conn)
327        if viaLaneID:
328            try:
329                # internal lanes are only available when building with option withInternal=True
330                viaLane = self.getLane(viaLaneID)
331                viaEdge = viaLane.getEdge()
332                viaEdge._addIncoming(connection.Connection(
333                    fromEdge, viaEdge, fromlane, viaLane, direction, tls,
334                    tllink, tllink2, allow, disallow, state, ''))
335            except Exception:
336                pass
337        return conn
338
339    def getEdges(self, withInternal=True):
340        if not withInternal:
341            return [e for e in self._edges if e.getFunction() == '']
342        else:
343            return self._edges
344
345    def getRoundabouts(self):
346        return self._roundabouts
347
348    def hasEdge(self, id):
349        return id in self._id2edge
350
351    def getEdge(self, id):
352        return self._id2edge[id]
353
354    def getLane(self, laneID):
355        edge_id, lane_index = laneID.rsplit("_", 1)
356        return self.getEdge(edge_id).getLane(int(lane_index))
357
358    def _initRTree(self, shapeList, includeJunctions=True):
359        import rtree  # noqa
360        result = rtree.index.Index()
361        result.interleaved = True
362        for ri, shape in enumerate(shapeList):
363            result.add(ri, shape.getBoundingBox(includeJunctions))
364        return result
365
366    # Please be aware that the resulting list of edges is NOT sorted
367    def getNeighboringEdges(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
368        edges = []
369        try:
370            if self._rtreeEdges is None:
371                self._rtreeEdges = self._initRTree(self._edges, includeJunctions)
372            for i in self._rtreeEdges.intersection((x - r, y - r, x + r, y + r)):
373                e = self._edges[i]
374                d = sumolib.geomhelper.distancePointToPolygon(
375                    (x, y), e.getShape(includeJunctions))
376                if d < r:
377                    edges.append((e, d))
378        except ImportError:
379            if not allowFallback:
380                raise
381            warnings.warn("Module 'rtree' not available. Using brute-force fallback.")
382            for the_edge in self._edges:
383                d = sumolib.geomhelper.distancePointToPolygon((x, y), the_edge.getShape(includeJunctions))
384                if d < r:
385                    edges.append((the_edge, d))
386        return edges
387
388    def getNeighboringLanes(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
389        lanes = []
390        try:
391            if self._rtreeLanes is None:
392                for the_edge in self._edges:
393                    self._allLanes += the_edge.getLanes()
394                self._rtreeLanes = self._initRTree(self._allLanes, includeJunctions)
395            for i in self._rtreeLanes.intersection((x - r, y - r, x + r, y + r)):
396                the_lane = self._allLanes[i]
397                d = sumolib.geomhelper.distancePointToPolygon((x, y), the_lane.getShape(includeJunctions))
398                if d < r:
399                    lanes.append((the_lane, d))
400        except ImportError:
401            if not allowFallback:
402                raise
403            warnings.warn("Module 'rtree' not available. Using brute-force fallback.")
404            for the_edge in self._edges:
405                for the_lane in the_edge.getLanes():
406                    d = sumolib.geomhelper.distancePointToPolygon((x, y), the_lane.getShape(includeJunctions))
407                    if d < r:
408                        lanes.append((the_lane, d))
409        return lanes
410
411    def hasNode(self, id):
412        return id in self._id2node
413
414    def getNode(self, id):
415        return self._id2node[id]
416
417    def getNodes(self):
418        return self._nodes
419
420    def getTLS(self, tlid):
421        return self._id2tls[tlid]
422
423    def getTLSSecure(self, tlid):
424        if tlid in self._id2tls:
425            tls = self._id2tls[tlid]
426        else:
427            tls = TLS(tlid)
428            self._id2tls[tlid] = tls
429            self._tlss.append(tls)
430        return tls
431
432    def getTrafficLights(self):
433        return self._tlss
434
435    def addTLS(self, tlid, inLane, outLane, linkNo):
436        tls = self.getTLSSecure(tlid)
437        tls.addConnection(inLane, outLane, linkNo)
438        return tls
439
440    def addTLSProgram(self, tlid, programID, offset, type, removeOthers):
441        tls = self.getTLSSecure(tlid)
442        program = TLSProgram(programID, offset, type)
443        if removeOthers:
444            tls.removePrograms()
445        tls.addProgram(program)
446        return program
447
448    def setFoes(self, junctionID, index, foes, prohibits):
449        self._id2node[junctionID].setFoes(index, foes, prohibits)
450
451    def forbids(self, possProhibitor, possProhibited):
452        return possProhibitor.getFrom().getToNode().forbids(possProhibitor, possProhibited)
453
454    def getUpstreamEdges(self, edge, distance, stopOnTLS, stopOnTurnaround):
455        """return a list of lists of the form
456           [[firstEdge, pos, [edge_0, edge_1, ..., edge_k], aborted], ...]
457           where
458             firstEdge: is the upstream edge furthest away from the intersection,
459             [edge_0, ..., edge_k]: is the list of edges from the intersection upstream to firstEdge
460             pos: is the position on firstEdge with distance to the end of the input edge
461             aborted: a flag indicating whether the downstream
462                 search stopped at a TLS or a node without incoming edges before reaching the distance threshold
463        """
464        ret = []
465        seen = set()
466        toProc = []
467        toProc.append([edge, 0, []])
468        while not len(toProc) == 0:
469            ie = toProc.pop()
470            if ie[0] in seen:
471                continue
472            seen.add(ie[0])
473            if ie[1] + ie[0].getLength() >= distance:
474                ret.append(
475                    [ie[0], ie[0].getLength() + ie[1] - distance, ie[2], False])
476                continue
477            if len(ie[0]._incoming) == 0:
478                ret.append([ie[0], ie[0].getLength() + ie[1], ie[2], True])
479                continue
480            mn = []
481            stop = False
482            for ci in ie[0]._incoming:
483                if ci not in seen:
484                    prev = copy(ie[2])
485                    if stopOnTLS and ci._tls and ci != edge and not stop:
486                        ret.append([ie[0], ie[1], prev, True])
487                        stop = True
488                    elif (stopOnTurnaround and ie[0]._incoming[ci][0].getDirection() == Connection.LINKDIR_TURN and
489                          not stop):
490                        ret.append([ie[0], ie[1], prev, True])
491                        stop = True
492                    else:
493                        prev.append(ie[0])
494                        mn.append([ci, ie[0].getLength() + ie[1], prev])
495            if not stop:
496                toProc.extend(mn)
497        return ret
498
499    def getEdgesByOrigID(self, origID):
500        if self._origIdx is None:
501            self._origIdx = defaultdict(set)
502            for the_edge in self._edges:
503                for the_lane in the_edge.getLanes():
504                    for oID in the_lane.getParam("origId", "").split():
505                        self._origIdx[oID].add(the_edge)
506        return self._origIdx[origID]
507
508    def getGeometries(self, useLanes, includeJunctions=False):
509        for e in self._edges:
510            if useLanes:
511                for the_lane in e.getLanes():
512                    yield the_lane.getID(), the_lane.getShape(), the_lane.getWidth()
513            else:
514                yield e.getID(), e.getShape(includeJunctions), sum([the_lane.getWidth() for the_lane in e.getLanes()])
515
516    def getBBoxXY(self):
517        """
518        Get the bounding box (bottom left and top right coordinates) for a net;
519        Coordinates are in X and Y (not Lat and Lon)
520
521        :return [(bottom_left_X, bottom_left_Y), (top_right_X, top_right_Y)]
522        """
523        return [(self._ranges[0][0], self._ranges[1][0]),
524                (self._ranges[0][1], self._ranges[1][1])]
525
526    # the diagonal of the bounding box of all nodes
527    def getBBoxDiameter(self):
528        return math.sqrt(
529            (self._ranges[0][0] - self._ranges[0][1]) ** 2 +
530            (self._ranges[1][0] - self._ranges[1][1]) ** 2)
531
532    def hasGeoProj(self):
533        projString = self._location["projParameter"]
534        return projString != "!"
535
536    def getGeoProj(self):
537        if not self.hasGeoProj():
538            raise RuntimeError("Network does not provide geo-projection")
539        if self._proj is None:
540            import pyproj
541            try:
542                self._proj = pyproj.Proj(projparams=self._location["projParameter"])
543            except RuntimeError:
544                if hasattr(pyproj.datadir, 'set_data_dir'):
545                    pyproj.datadir.set_data_dir('/usr/share/proj')
546                    self._proj = pyproj.Proj(projparams=self._location["projParameter"])
547                raise
548        return self._proj
549
550    def getLocationOffset(self):
551        """ offset to be added after converting from geo-coordinates to UTM"""
552        return list(map(float, self._location["netOffset"].split(",")))
553
554    def getBoundary(self):
555        """ return xmin,ymin,xmax,ymax network coordinates"""
556        return list(map(float, self._location["convBoundary"].split(",")))
557
558    def convertLonLat2XY(self, lon, lat, rawUTM=False):
559        x, y = self.getGeoProj()(lon, lat)
560        if rawUTM:
561            return x, y
562        else:
563            x_off, y_off = self.getLocationOffset()
564            return x + x_off, y + y_off
565
566    def convertXY2LonLat(self, x, y, rawUTM=False):
567        if not rawUTM:
568            x_off, y_off = self.getLocationOffset()
569            x -= x_off
570            y -= y_off
571        return self.getGeoProj()(x, y, inverse=True)
572
573    def move(self, dx, dy, dz=0):
574        for n in self._nodes:
575            n._coord = (n._coord[0] + dx, n._coord[1] + dy, n._coord[2] + dz)
576        for e in self._edges:
577            for _lane in e.getLanes():
578                _lane.setShape([(p[0] + dx, p[1] + dy, p[2] + dz) for p in _lane.getShape3D()])
579            e.rebuildShape()
580
581    def getInternalPath(self, conn, fastest=False):
582        minInternalCost = 1e400
583        minPath = None
584        for c in conn:
585            if c.getViaLaneID() != "":
586                viaCost = 0
587                viaID = c.getViaLaneID()
588                viaPath = []
589                while viaID != "":
590                    viaLane = self.getLane(viaID)
591                    viaCost += viaLane.getLength() if not fastest else viaLane.getLength() / viaLane.getSpeed()
592                    viaID = viaLane.getOutgoing()[0].getViaLaneID()
593                    viaPath.append(viaLane.getEdge())
594                if viaCost < minInternalCost:
595                    minInternalCost = viaCost
596                    minPath = viaPath
597        return minPath, minInternalCost
598
599    def getOptimalPath(self, fromEdge, toEdge, fastest=False, maxCost=1e400, vClass=None, reversalPenalty=0,
600                       includeFromToCost=True, withInternal=False, ignoreDirection=False,
601                       fromPos=None, toPos=None, preferences={}):
602        """
603        Finds the optimal (shortest or fastest) path for vClass from fromEdge to toEdge
604        by using using Dijkstra's algorithm.
605        It returns a pair of a tuple of edges and the cost.
606        If no path is found the first element is None.
607        The cost for the returned path is equal to the sum of all edge costs in the path,
608        including the internal connectors, if they are present in the network.
609        The path itself does not include internal edges except for the case
610        when the start or end edge are internal edges.
611        The search may be limited using the given threshold.
612        The preferences declare a mapping from the 'routingType' of each edge to divisor
613        that is applied to the computed cost of that edge (i.e. a preference of 2 reduces cost by a factor of 0.5).
614        """
615
616        if preferences:
617            if fastest:
618                def speedFunc(edge):
619                    return preferences.get(edge.getRoutingType(), 1.0) * edge.getSpeed()
620            else:
621                def speedFunc(edge):
622                    return preferences.get(edge.getRoutingType(), 1.0)
623        elif fastest:
624            def speedFunc(edge):
625                return edge.getSpeed()
626        else:
627            def speedFunc(edge):
628                return 1.0
629
630        def remainder(edge, pos):
631            if pos < 0:
632                return min(-pos, edge.getLength())
633            return max(0., edge.getLength() - pos)
634
635        def getToNormalIncoming(edge):
636            if edge.getFunction() == '':
637                return [(e, None) for e in edge.getToNode().getIncoming() if e.getFunction() == '']
638            else:
639                return []
640
641        if self.hasInternal:
642            appendix = []
643            appendixCost = 0.
644            while toEdge.getFunction() == "internal":
645                appendix = [toEdge] + appendix
646                appendixCost += toEdge.getLength() / speedFunc(toEdge)
647                toEdge = list(toEdge.getIncoming().keys())[0]
648
649        def finalizeCost(cost, path):
650            if includeFromToCost:
651                # add costs for (part of) the first edge, still needs to be fixed for wrong direction travel
652                remainFrom = fromEdge.getLength() if fromPos is None else remainder(fromEdge, fromPos)
653                cost += remainFrom / speedFunc(fromEdge)
654                # remove costs for (part of) the last edge, still needs to be fixed for wrong direction travel
655                removeTo = 0. if toPos is None else remainder(toEdge, toPos)
656            else:
657                removeTo = toEdge.getLength() if len(path) > 1 else 0.
658            cost -= removeTo / speedFunc(toEdge)
659            return cost
660
661        def constructPath(dist):
662            # destination was already reached in a previous query
663            cost, pred = dist[toEdge]
664            path = [toEdge]
665            while pred is not None:
666                if self.hasInternal and withInternal:
667                    viaPath, minInternalCost = self.getInternalPath(pred.getAllowedOutgoing(vClass).get(path[-1], []),
668                                                                    fastest=fastest)
669                    if viaPath is not None:
670                        path += reversed(viaPath)
671                path.append(pred)
672                _, pred = dist[pred]
673
674            path.reverse()
675            cost = finalizeCost(cost, path)
676            assert cost >= 0
677            if self.hasInternal:
678                if appendix:
679                    return tuple(path + appendix), cost + appendixCost
680                elif ignoreDirection and self.hasWalkingArea and not withInternal:
681                    return [e for e in path if e.getFunction() == ''], cost
682            return tuple(path), cost
683
684        needLoop = (fromEdge == toEdge
685                    and fromPos is not None
686                    and toPos is not None
687                    and fromPos > toPos
688                    and not ignoreDirection)
689
690        seen = set()
691        dist = {}
692        q = []
693        if self._routingCache is not None:
694            if needLoop:
695                # use cached results from all follower edges:
696                bestCost = maxCost
697                bestPath = None
698                for e2, conn in fromEdge.getAllowedOutgoing(vClass).items():
699                    path, cost = self.getOptimalPath(e2, toEdge, fastest=fastest, maxCost=maxCost, vClass=vClass,
700                                                     reversalPenalty=reversalPenalty,
701                                                     includeFromToCost=includeFromToCost,
702                                                     withInternal=withInternal, fromPos=0, toPos=toPos,
703                                                     preferences=preferences)
704                    if path is not None and cost < bestCost:
705                        bestPath = path
706                        bestCost = cost
707                if bestPath is not None:
708                    path = [fromEdge]
709                    if self.hasInternal and withInternal:
710                        viaPath, minInternalCost = self.getInternalPath(
711                            fromEdge.getAllowedOutgoing(vClass).get(path[0], []), fastest=fastest)
712                        if viaPath is not None:
713                            path += viaPath
714                            bestCost += minInternalCost
715                    path += list(bestPath)
716                    if includeFromToCost:
717                        bestCost += remainder(fromEdge, fromPos) / speedFunc(fromEdge)
718                    return tuple(path), bestCost
719                else:
720                    return None, 1e400
721
722            dist = self._routingCache(fromEdge, fromPos, fastest, vClass, ignoreDirection, reversalPenalty,
723                                      tuple(preferences.items()))
724            if toEdge in dist:
725                return constructPath(dist)
726            else:
727                # initialize heap from previous query
728                q = []
729                frontier = set(dist.keys())
730                for cost, prev in dist.values():
731                    frontier.discard(prev)
732                for e in frontier:
733                    cost, prev = dist[e]
734                    heapq.heappush(q, (cost, e, prev))
735        elif needLoop:
736            # start search on successors of fromEdge
737            for e2, conn in fromEdge.getAllowedOutgoing(vClass).items():
738                q.append((e2.getLength() / speedFunc(e2), e2, fromEdge))
739
740        if len(dist) == 0:
741            dist[fromEdge] = (0., None)
742            if not needLoop:
743                q.append((0., fromEdge, None))
744
745        while q:
746            cost, e1, prev = heapq.heappop(q)
747            if e1 in seen:
748                continue
749            seen.add(e1)
750            if e1 == toEdge:
751                return constructPath(dist)
752            if cost > maxCost:
753                return None, cost
754
755            for e2, conn in chain(e1.getAllowedOutgoing(vClass).items(),
756                                  e1.getIncoming().items() if ignoreDirection else [],
757                                  getToNormalIncoming(e1) if ignoreDirection and not self.hasWalkingArea else []):
758                if e2 not in seen:
759                    newCost = cost + e2.getLength() / speedFunc(e2)
760                    #  print(cost, newCost, e2.getID(), speedFunc(e2))
761                    if e2 == e1.getBidi():
762                        newCost += reversalPenalty
763                    if self.hasInternal and conn is not None:
764                        viaPath, minInternalCost = self.getInternalPath(conn, fastest=fastest)
765                        if viaPath is not None:
766                            newCost += minInternalCost
767                    if e2 not in dist or newCost < dist[e2][0]:
768                        dist[e2] = (newCost, e1)
769                        heapq.heappush(q, (newCost, e2, e1))
770        return None, 1e400
771
772    def getShortestPath(self, fromEdge, toEdge, maxCost=1e400, vClass=None, reversalPenalty=0,
773                        includeFromToCost=True, withInternal=False, ignoreDirection=False,
774                        fromPos=None, toPos=None, preferences={}):
775        """
776        Finds the shortest path from fromEdge to toEdge respecting vClass, using Dijkstra's algorithm.
777        It returns a pair of a tuple of edges and the cost. If no path is found the first element is None.
778        The cost for the returned path is equal to the sum of all edge lengths in the path,
779        including the internal connectors, if they are present in the network.
780        The path itself does not include internal edges except for the case
781        when the start or end edge are internal edges.
782        The search may be limited using the given threshold.
783        The preferences declare a mapping from the 'routingType' of each edge to divisor
784        that is applied to the lenght of that edge
785        (i.e. a preference of 2 reduces reduces effective length by a factor of 0.5).
786        """
787
788        return self.getOptimalPath(fromEdge, toEdge, False, maxCost, vClass, reversalPenalty,
789                                   includeFromToCost, withInternal, ignoreDirection, fromPos, toPos,
790                                   preferences)
791
792    def getFastestPath(self, fromEdge, toEdge, maxCost=1e400, vClass=None, reversalPenalty=0,
793                       includeFromToCost=True, withInternal=False, ignoreDirection=False,
794                       fromPos=None, toPos=None, preferences={}):
795        """
796        Finds the fastest path from fromEdge to toEdge respecting vClass, using Dijkstra's algorithm.
797        It returns a pair of a tuple of edges and the cost. If no path is found the first element is None.
798        The cost for the returned path is equal to the sum of all edge costs in the path,
799        including the internal connectors, if they are present in the network.
800        The path itself does not include internal edges except for the case
801        when the start or end edge are internal edges.
802        The search may be limited using the given threshold.
803        The preferences declare a mapping from the 'routingType' of each edge to divisor
804        that is applied to the computed traveltime of that edge
805        (i.e. a preference of 2 reduces effective traveltime by a factor of 0.5).
806        """
807
808        return self.getOptimalPath(fromEdge, toEdge, True, maxCost, vClass, reversalPenalty,
809                                   includeFromToCost, withInternal, ignoreDirection, fromPos, toPos,
810                                   preferences)
811
812    def getReachable(self, source, vclass=None, useIncoming=False, cache=None):
813        if vclass is not None and not source.allows(vclass):
814            raise RuntimeError("'{}' does not allow {}".format(source.getID(), vclass))
815        fringe = [source]
816        found = set()
817        found.add(source)
818        while len(fringe) > 0:
819            new_fringe = []
820            for e in fringe:
821                if vclass == "pedestrian":
822                    cands = chain(chain(*e.getIncoming().values()), chain(*e.getOutgoing().values()))
823                else:
824                    cands = chain(*(e.getIncoming().values() if useIncoming else e.getOutgoing().values()))
825                # print("\n".join(map(str, list(cands))))
826                for conn in cands:
827                    if vclass is None or (
828                            conn.getFromLane().allows(vclass)
829                            and conn.getToLane().allows(vclass)):
830                        for reachable in [conn.getTo(), conn.getFrom()]:
831                            if reachable not in found:
832                                # print("added %s via %s" % (reachable, conn))
833                                if cache and reachable in cache:
834                                    found.update(cache[reachable])
835                                else:
836                                    found.add(reachable)
837                                    new_fringe.append(reachable)
838            fringe = new_fringe
839        if cache is not None:
840            cache[source] = tuple(found)
841        return found

The whole sumo network.

hasInternal
hasWalkingArea
def initRoutingCache(self, maxsize=1000):
238    def initRoutingCache(self, maxsize=1000):
239        self._routingCache = lru_cache(maxsize=maxsize)(lambda fromEdge, fromPos, fastest, vClass, ignoreDirection, reversalPenalty, preferences: {})  # noqa
def getVersion(self):
241    def getVersion(self):
242        return self._version
def getEdgeType(self, typeID):
244    def getEdgeType(self, typeID):
245        return self._edgeTypes[typeID]
def setLocation(self, netOffset, convBoundary, origBoundary, projParameter):
247    def setLocation(self, netOffset, convBoundary, origBoundary, projParameter):
248        self._location["netOffset"] = netOffset
249        self._location["convBoundary"] = convBoundary
250        self._location["origBoundary"] = origBoundary
251        self._location["projParameter"] = projParameter
def loadSelection(self, selectionFile):
253    def loadSelection(self, selectionFile):
254        self.resetSelection()
255        with io.open(selectionFile, "r", encoding="utf-8") as f:
256            for line in f:
257                line = line.strip()
258                if line.startswith("edge:"):
259                    edgeID = line[5:]
260                    if edgeID in self._id2edge:
261                        self.getEdge(edgeID).select()
262                elif line.startswith("junction:"):
263                    nodeID = line[9:]
264                    if nodeID in self._id2node:
265                        self.getNode(nodeID).select()
def resetSelection(self):
267    def resetSelection(self):
268        for n in self._nodes:
269            n.select(False)
270        for e in self._edges:
271            e.select(False)
def addNode(self, id, type=None, coord=None, incLanes=None, intLanes=None):
273    def addNode(self, id, type=None, coord=None, incLanes=None, intLanes=None):
274        if id is None:
275            return None
276        if id not in self._id2node:
277            n = node.Node(id, type, coord, incLanes, intLanes)
278            self._nodes.append(n)
279            self._id2node[id] = n
280        self.setAdditionalNodeInfo(
281            self._id2node[id], type, coord, incLanes, intLanes)
282        return self._id2node[id]
def setAdditionalNodeInfo(self, node, type, coord, incLanes, intLanes=None):
284    def setAdditionalNodeInfo(self, node, type, coord, incLanes, intLanes=None):
285        if coord is not None and node._coord is None:
286            node._coord = coord
287            self._ranges[0][0] = min(self._ranges[0][0], coord[0])
288            self._ranges[0][1] = max(self._ranges[0][1], coord[0])
289            self._ranges[1][0] = min(self._ranges[1][0], coord[1])
290            self._ranges[1][1] = max(self._ranges[1][1], coord[1])
291        if incLanes is not None and node._incLanes is None:
292            node._incLanes = incLanes
293        if intLanes is not None and node._intLanes is None:
294            node._intLanes = intLanes
295        if type is not None and node._type is None:
296            node._type = type
def addEdge( self, id, fromID, toID, prio, function, name, edgeType='', routingType=''):
298    def addEdge(self, id, fromID, toID, prio, function, name, edgeType='', routingType=''):
299        if id not in self._id2edge:
300            fromN = self.addNode(fromID)
301            toN = self.addNode(toID)
302            e = edge.Edge(id, fromN, toN, prio, function, name, edgeType, routingType)
303            self._edges.append(e)
304            self._id2edge[id] = e
305            if function:
306                self.hasInternal = True
307                if function == "walkingarea":
308                    self.hasWalkingArea = True
309        return self._id2edge[id]
def addLane( self, edge, speed, length, width, allow=None, disallow=None, acceleration=False):
311    def addLane(self, edge, speed, length, width, allow=None, disallow=None, acceleration=False):
312        return lane.Lane(edge, speed, length, width, allow, disallow, acceleration)
def addRoundabout(self, nodes, edges=None):
314    def addRoundabout(self, nodes, edges=None):
315        r = roundabout.Roundabout(nodes, edges)
316        self._roundabouts.append(r)
317        return r
def addConnection( self, fromEdge, toEdge, fromlane, tolane, direction, tls, tllink, tllink2, allow, disallow, state, viaLaneID=None):
319    def addConnection(self, fromEdge, toEdge, fromlane, tolane, direction,
320                      tls, tllink, tllink2, allow, disallow, state, viaLaneID=None):
321        conn = connection.Connection(
322            fromEdge, toEdge, fromlane, tolane, direction,
323            tls, tllink, tllink2, allow, disallow, state, viaLaneID)
324        fromEdge.addOutgoing(conn)
325        fromlane.addOutgoing(conn)
326        toEdge._addIncoming(conn)
327        if viaLaneID:
328            try:
329                # internal lanes are only available when building with option withInternal=True
330                viaLane = self.getLane(viaLaneID)
331                viaEdge = viaLane.getEdge()
332                viaEdge._addIncoming(connection.Connection(
333                    fromEdge, viaEdge, fromlane, viaLane, direction, tls,
334                    tllink, tllink2, allow, disallow, state, ''))
335            except Exception:
336                pass
337        return conn
def getEdges(self, withInternal=True):
339    def getEdges(self, withInternal=True):
340        if not withInternal:
341            return [e for e in self._edges if e.getFunction() == '']
342        else:
343            return self._edges
def getRoundabouts(self):
345    def getRoundabouts(self):
346        return self._roundabouts
def hasEdge(self, id):
348    def hasEdge(self, id):
349        return id in self._id2edge
def getEdge(self, id):
351    def getEdge(self, id):
352        return self._id2edge[id]
def getLane(self, laneID):
354    def getLane(self, laneID):
355        edge_id, lane_index = laneID.rsplit("_", 1)
356        return self.getEdge(edge_id).getLane(int(lane_index))
def getNeighboringEdges(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
367    def getNeighboringEdges(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
368        edges = []
369        try:
370            if self._rtreeEdges is None:
371                self._rtreeEdges = self._initRTree(self._edges, includeJunctions)
372            for i in self._rtreeEdges.intersection((x - r, y - r, x + r, y + r)):
373                e = self._edges[i]
374                d = sumolib.geomhelper.distancePointToPolygon(
375                    (x, y), e.getShape(includeJunctions))
376                if d < r:
377                    edges.append((e, d))
378        except ImportError:
379            if not allowFallback:
380                raise
381            warnings.warn("Module 'rtree' not available. Using brute-force fallback.")
382            for the_edge in self._edges:
383                d = sumolib.geomhelper.distancePointToPolygon((x, y), the_edge.getShape(includeJunctions))
384                if d < r:
385                    edges.append((the_edge, d))
386        return edges
def getNeighboringLanes(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
388    def getNeighboringLanes(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
389        lanes = []
390        try:
391            if self._rtreeLanes is None:
392                for the_edge in self._edges:
393                    self._allLanes += the_edge.getLanes()
394                self._rtreeLanes = self._initRTree(self._allLanes, includeJunctions)
395            for i in self._rtreeLanes.intersection((x - r, y - r, x + r, y + r)):
396                the_lane = self._allLanes[i]
397                d = sumolib.geomhelper.distancePointToPolygon((x, y), the_lane.getShape(includeJunctions))
398                if d < r:
399                    lanes.append((the_lane, d))
400        except ImportError:
401            if not allowFallback:
402                raise
403            warnings.warn("Module 'rtree' not available. Using brute-force fallback.")
404            for the_edge in self._edges:
405                for the_lane in the_edge.getLanes():
406                    d = sumolib.geomhelper.distancePointToPolygon((x, y), the_lane.getShape(includeJunctions))
407                    if d < r:
408                        lanes.append((the_lane, d))
409        return lanes
def hasNode(self, id):
411    def hasNode(self, id):
412        return id in self._id2node
def getNode(self, id):
414    def getNode(self, id):
415        return self._id2node[id]
def getNodes(self):
417    def getNodes(self):
418        return self._nodes
def getTLS(self, tlid):
420    def getTLS(self, tlid):
421        return self._id2tls[tlid]
def getTLSSecure(self, tlid):
423    def getTLSSecure(self, tlid):
424        if tlid in self._id2tls:
425            tls = self._id2tls[tlid]
426        else:
427            tls = TLS(tlid)
428            self._id2tls[tlid] = tls
429            self._tlss.append(tls)
430        return tls
def getTrafficLights(self):
432    def getTrafficLights(self):
433        return self._tlss
def addTLS(self, tlid, inLane, outLane, linkNo):
435    def addTLS(self, tlid, inLane, outLane, linkNo):
436        tls = self.getTLSSecure(tlid)
437        tls.addConnection(inLane, outLane, linkNo)
438        return tls
def addTLSProgram(self, tlid, programID, offset, type, removeOthers):
440    def addTLSProgram(self, tlid, programID, offset, type, removeOthers):
441        tls = self.getTLSSecure(tlid)
442        program = TLSProgram(programID, offset, type)
443        if removeOthers:
444            tls.removePrograms()
445        tls.addProgram(program)
446        return program
def setFoes(self, junctionID, index, foes, prohibits):
448    def setFoes(self, junctionID, index, foes, prohibits):
449        self._id2node[junctionID].setFoes(index, foes, prohibits)
def forbids(self, possProhibitor, possProhibited):
451    def forbids(self, possProhibitor, possProhibited):
452        return possProhibitor.getFrom().getToNode().forbids(possProhibitor, possProhibited)
def getUpstreamEdges(self, edge, distance, stopOnTLS, stopOnTurnaround):
454    def getUpstreamEdges(self, edge, distance, stopOnTLS, stopOnTurnaround):
455        """return a list of lists of the form
456           [[firstEdge, pos, [edge_0, edge_1, ..., edge_k], aborted], ...]
457           where
458             firstEdge: is the upstream edge furthest away from the intersection,
459             [edge_0, ..., edge_k]: is the list of edges from the intersection upstream to firstEdge
460             pos: is the position on firstEdge with distance to the end of the input edge
461             aborted: a flag indicating whether the downstream
462                 search stopped at a TLS or a node without incoming edges before reaching the distance threshold
463        """
464        ret = []
465        seen = set()
466        toProc = []
467        toProc.append([edge, 0, []])
468        while not len(toProc) == 0:
469            ie = toProc.pop()
470            if ie[0] in seen:
471                continue
472            seen.add(ie[0])
473            if ie[1] + ie[0].getLength() >= distance:
474                ret.append(
475                    [ie[0], ie[0].getLength() + ie[1] - distance, ie[2], False])
476                continue
477            if len(ie[0]._incoming) == 0:
478                ret.append([ie[0], ie[0].getLength() + ie[1], ie[2], True])
479                continue
480            mn = []
481            stop = False
482            for ci in ie[0]._incoming:
483                if ci not in seen:
484                    prev = copy(ie[2])
485                    if stopOnTLS and ci._tls and ci != edge and not stop:
486                        ret.append([ie[0], ie[1], prev, True])
487                        stop = True
488                    elif (stopOnTurnaround and ie[0]._incoming[ci][0].getDirection() == Connection.LINKDIR_TURN and
489                          not stop):
490                        ret.append([ie[0], ie[1], prev, True])
491                        stop = True
492                    else:
493                        prev.append(ie[0])
494                        mn.append([ci, ie[0].getLength() + ie[1], prev])
495            if not stop:
496                toProc.extend(mn)
497        return ret

return a list of lists of the form [[firstEdge, pos, [edge_0, edge_1, ..., edge_k], aborted], ...] where firstEdge: is the upstream edge furthest away from the intersection, pos: is the position on firstEdge with distance to the end of the input edge aborted: a flag indicating whether the downstream search stopped at a TLS or a node without incoming edges before reaching the distance threshold

def getEdgesByOrigID(self, origID):
499    def getEdgesByOrigID(self, origID):
500        if self._origIdx is None:
501            self._origIdx = defaultdict(set)
502            for the_edge in self._edges:
503                for the_lane in the_edge.getLanes():
504                    for oID in the_lane.getParam("origId", "").split():
505                        self._origIdx[oID].add(the_edge)
506        return self._origIdx[origID]
def getGeometries(self, useLanes, includeJunctions=False):
508    def getGeometries(self, useLanes, includeJunctions=False):
509        for e in self._edges:
510            if useLanes:
511                for the_lane in e.getLanes():
512                    yield the_lane.getID(), the_lane.getShape(), the_lane.getWidth()
513            else:
514                yield e.getID(), e.getShape(includeJunctions), sum([the_lane.getWidth() for the_lane in e.getLanes()])
def getBBoxXY(self):
516    def getBBoxXY(self):
517        """
518        Get the bounding box (bottom left and top right coordinates) for a net;
519        Coordinates are in X and Y (not Lat and Lon)
520
521        :return [(bottom_left_X, bottom_left_Y), (top_right_X, top_right_Y)]
522        """
523        return [(self._ranges[0][0], self._ranges[1][0]),
524                (self._ranges[0][1], self._ranges[1][1])]

Get the bounding box (bottom left and top right coordinates) for a net; Coordinates are in X and Y (not Lat and Lon)

:return [(bottom_left_X, bottom_left_Y), (top_right_X, top_right_Y)]

def getBBoxDiameter(self):
527    def getBBoxDiameter(self):
528        return math.sqrt(
529            (self._ranges[0][0] - self._ranges[0][1]) ** 2 +
530            (self._ranges[1][0] - self._ranges[1][1]) ** 2)
def hasGeoProj(self):
532    def hasGeoProj(self):
533        projString = self._location["projParameter"]
534        return projString != "!"
def getGeoProj(self):
536    def getGeoProj(self):
537        if not self.hasGeoProj():
538            raise RuntimeError("Network does not provide geo-projection")
539        if self._proj is None:
540            import pyproj
541            try:
542                self._proj = pyproj.Proj(projparams=self._location["projParameter"])
543            except RuntimeError:
544                if hasattr(pyproj.datadir, 'set_data_dir'):
545                    pyproj.datadir.set_data_dir('/usr/share/proj')
546                    self._proj = pyproj.Proj(projparams=self._location["projParameter"])
547                raise
548        return self._proj
def getLocationOffset(self):
550    def getLocationOffset(self):
551        """ offset to be added after converting from geo-coordinates to UTM"""
552        return list(map(float, self._location["netOffset"].split(",")))

offset to be added after converting from geo-coordinates to UTM

def getBoundary(self):
554    def getBoundary(self):
555        """ return xmin,ymin,xmax,ymax network coordinates"""
556        return list(map(float, self._location["convBoundary"].split(",")))

return xmin,ymin,xmax,ymax network coordinates

def convertLonLat2XY(self, lon, lat, rawUTM=False):
558    def convertLonLat2XY(self, lon, lat, rawUTM=False):
559        x, y = self.getGeoProj()(lon, lat)
560        if rawUTM:
561            return x, y
562        else:
563            x_off, y_off = self.getLocationOffset()
564            return x + x_off, y + y_off
def convertXY2LonLat(self, x, y, rawUTM=False):
566    def convertXY2LonLat(self, x, y, rawUTM=False):
567        if not rawUTM:
568            x_off, y_off = self.getLocationOffset()
569            x -= x_off
570            y -= y_off
571        return self.getGeoProj()(x, y, inverse=True)
def move(self, dx, dy, dz=0):
573    def move(self, dx, dy, dz=0):
574        for n in self._nodes:
575            n._coord = (n._coord[0] + dx, n._coord[1] + dy, n._coord[2] + dz)
576        for e in self._edges:
577            for _lane in e.getLanes():
578                _lane.setShape([(p[0] + dx, p[1] + dy, p[2] + dz) for p in _lane.getShape3D()])
579            e.rebuildShape()
def getInternalPath(self, conn, fastest=False):
581    def getInternalPath(self, conn, fastest=False):
582        minInternalCost = 1e400
583        minPath = None
584        for c in conn:
585            if c.getViaLaneID() != "":
586                viaCost = 0
587                viaID = c.getViaLaneID()
588                viaPath = []
589                while viaID != "":
590                    viaLane = self.getLane(viaID)
591                    viaCost += viaLane.getLength() if not fastest else viaLane.getLength() / viaLane.getSpeed()
592                    viaID = viaLane.getOutgoing()[0].getViaLaneID()
593                    viaPath.append(viaLane.getEdge())
594                if viaCost < minInternalCost:
595                    minInternalCost = viaCost
596                    minPath = viaPath
597        return minPath, minInternalCost
def getOptimalPath( self, fromEdge, toEdge, fastest=False, maxCost=inf, vClass=None, reversalPenalty=0, includeFromToCost=True, withInternal=False, ignoreDirection=False, fromPos=None, toPos=None, preferences={}):
599    def getOptimalPath(self, fromEdge, toEdge, fastest=False, maxCost=1e400, vClass=None, reversalPenalty=0,
600                       includeFromToCost=True, withInternal=False, ignoreDirection=False,
601                       fromPos=None, toPos=None, preferences={}):
602        """
603        Finds the optimal (shortest or fastest) path for vClass from fromEdge to toEdge
604        by using using Dijkstra's algorithm.
605        It returns a pair of a tuple of edges and the cost.
606        If no path is found the first element is None.
607        The cost for the returned path is equal to the sum of all edge costs in the path,
608        including the internal connectors, if they are present in the network.
609        The path itself does not include internal edges except for the case
610        when the start or end edge are internal edges.
611        The search may be limited using the given threshold.
612        The preferences declare a mapping from the 'routingType' of each edge to divisor
613        that is applied to the computed cost of that edge (i.e. a preference of 2 reduces cost by a factor of 0.5).
614        """
615
616        if preferences:
617            if fastest:
618                def speedFunc(edge):
619                    return preferences.get(edge.getRoutingType(), 1.0) * edge.getSpeed()
620            else:
621                def speedFunc(edge):
622                    return preferences.get(edge.getRoutingType(), 1.0)
623        elif fastest:
624            def speedFunc(edge):
625                return edge.getSpeed()
626        else:
627            def speedFunc(edge):
628                return 1.0
629
630        def remainder(edge, pos):
631            if pos < 0:
632                return min(-pos, edge.getLength())
633            return max(0., edge.getLength() - pos)
634
635        def getToNormalIncoming(edge):
636            if edge.getFunction() == '':
637                return [(e, None) for e in edge.getToNode().getIncoming() if e.getFunction() == '']
638            else:
639                return []
640
641        if self.hasInternal:
642            appendix = []
643            appendixCost = 0.
644            while toEdge.getFunction() == "internal":
645                appendix = [toEdge] + appendix
646                appendixCost += toEdge.getLength() / speedFunc(toEdge)
647                toEdge = list(toEdge.getIncoming().keys())[0]
648
649        def finalizeCost(cost, path):
650            if includeFromToCost:
651                # add costs for (part of) the first edge, still needs to be fixed for wrong direction travel
652                remainFrom = fromEdge.getLength() if fromPos is None else remainder(fromEdge, fromPos)
653                cost += remainFrom / speedFunc(fromEdge)
654                # remove costs for (part of) the last edge, still needs to be fixed for wrong direction travel
655                removeTo = 0. if toPos is None else remainder(toEdge, toPos)
656            else:
657                removeTo = toEdge.getLength() if len(path) > 1 else 0.
658            cost -= removeTo / speedFunc(toEdge)
659            return cost
660
661        def constructPath(dist):
662            # destination was already reached in a previous query
663            cost, pred = dist[toEdge]
664            path = [toEdge]
665            while pred is not None:
666                if self.hasInternal and withInternal:
667                    viaPath, minInternalCost = self.getInternalPath(pred.getAllowedOutgoing(vClass).get(path[-1], []),
668                                                                    fastest=fastest)
669                    if viaPath is not None:
670                        path += reversed(viaPath)
671                path.append(pred)
672                _, pred = dist[pred]
673
674            path.reverse()
675            cost = finalizeCost(cost, path)
676            assert cost >= 0
677            if self.hasInternal:
678                if appendix:
679                    return tuple(path + appendix), cost + appendixCost
680                elif ignoreDirection and self.hasWalkingArea and not withInternal:
681                    return [e for e in path if e.getFunction() == ''], cost
682            return tuple(path), cost
683
684        needLoop = (fromEdge == toEdge
685                    and fromPos is not None
686                    and toPos is not None
687                    and fromPos > toPos
688                    and not ignoreDirection)
689
690        seen = set()
691        dist = {}
692        q = []
693        if self._routingCache is not None:
694            if needLoop:
695                # use cached results from all follower edges:
696                bestCost = maxCost
697                bestPath = None
698                for e2, conn in fromEdge.getAllowedOutgoing(vClass).items():
699                    path, cost = self.getOptimalPath(e2, toEdge, fastest=fastest, maxCost=maxCost, vClass=vClass,
700                                                     reversalPenalty=reversalPenalty,
701                                                     includeFromToCost=includeFromToCost,
702                                                     withInternal=withInternal, fromPos=0, toPos=toPos,
703                                                     preferences=preferences)
704                    if path is not None and cost < bestCost:
705                        bestPath = path
706                        bestCost = cost
707                if bestPath is not None:
708                    path = [fromEdge]
709                    if self.hasInternal and withInternal:
710                        viaPath, minInternalCost = self.getInternalPath(
711                            fromEdge.getAllowedOutgoing(vClass).get(path[0], []), fastest=fastest)
712                        if viaPath is not None:
713                            path += viaPath
714                            bestCost += minInternalCost
715                    path += list(bestPath)
716                    if includeFromToCost:
717                        bestCost += remainder(fromEdge, fromPos) / speedFunc(fromEdge)
718                    return tuple(path), bestCost
719                else:
720                    return None, 1e400
721
722            dist = self._routingCache(fromEdge, fromPos, fastest, vClass, ignoreDirection, reversalPenalty,
723                                      tuple(preferences.items()))
724            if toEdge in dist:
725                return constructPath(dist)
726            else:
727                # initialize heap from previous query
728                q = []
729                frontier = set(dist.keys())
730                for cost, prev in dist.values():
731                    frontier.discard(prev)
732                for e in frontier:
733                    cost, prev = dist[e]
734                    heapq.heappush(q, (cost, e, prev))
735        elif needLoop:
736            # start search on successors of fromEdge
737            for e2, conn in fromEdge.getAllowedOutgoing(vClass).items():
738                q.append((e2.getLength() / speedFunc(e2), e2, fromEdge))
739
740        if len(dist) == 0:
741            dist[fromEdge] = (0., None)
742            if not needLoop:
743                q.append((0., fromEdge, None))
744
745        while q:
746            cost, e1, prev = heapq.heappop(q)
747            if e1 in seen:
748                continue
749            seen.add(e1)
750            if e1 == toEdge:
751                return constructPath(dist)
752            if cost > maxCost:
753                return None, cost
754
755            for e2, conn in chain(e1.getAllowedOutgoing(vClass).items(),
756                                  e1.getIncoming().items() if ignoreDirection else [],
757                                  getToNormalIncoming(e1) if ignoreDirection and not self.hasWalkingArea else []):
758                if e2 not in seen:
759                    newCost = cost + e2.getLength() / speedFunc(e2)
760                    #  print(cost, newCost, e2.getID(), speedFunc(e2))
761                    if e2 == e1.getBidi():
762                        newCost += reversalPenalty
763                    if self.hasInternal and conn is not None:
764                        viaPath, minInternalCost = self.getInternalPath(conn, fastest=fastest)
765                        if viaPath is not None:
766                            newCost += minInternalCost
767                    if e2 not in dist or newCost < dist[e2][0]:
768                        dist[e2] = (newCost, e1)
769                        heapq.heappush(q, (newCost, e2, e1))
770        return None, 1e400

Finds the optimal (shortest or fastest) path for vClass from fromEdge to toEdge by using using Dijkstra's algorithm. It returns a pair of a tuple of edges and the cost. If no path is found the first element is None. The cost for the returned path is equal to the sum of all edge costs in the path, including the internal connectors, if they are present in the network. The path itself does not include internal edges except for the case when the start or end edge are internal edges. The search may be limited using the given threshold. The preferences declare a mapping from the 'routingType' of each edge to divisor that is applied to the computed cost of that edge (i.e. a preference of 2 reduces cost by a factor of 0.5).

def getShortestPath( self, fromEdge, toEdge, maxCost=inf, vClass=None, reversalPenalty=0, includeFromToCost=True, withInternal=False, ignoreDirection=False, fromPos=None, toPos=None, preferences={}):
772    def getShortestPath(self, fromEdge, toEdge, maxCost=1e400, vClass=None, reversalPenalty=0,
773                        includeFromToCost=True, withInternal=False, ignoreDirection=False,
774                        fromPos=None, toPos=None, preferences={}):
775        """
776        Finds the shortest path from fromEdge to toEdge respecting vClass, using Dijkstra's algorithm.
777        It returns a pair of a tuple of edges and the cost. If no path is found the first element is None.
778        The cost for the returned path is equal to the sum of all edge lengths in the path,
779        including the internal connectors, if they are present in the network.
780        The path itself does not include internal edges except for the case
781        when the start or end edge are internal edges.
782        The search may be limited using the given threshold.
783        The preferences declare a mapping from the 'routingType' of each edge to divisor
784        that is applied to the lenght of that edge
785        (i.e. a preference of 2 reduces reduces effective length by a factor of 0.5).
786        """
787
788        return self.getOptimalPath(fromEdge, toEdge, False, maxCost, vClass, reversalPenalty,
789                                   includeFromToCost, withInternal, ignoreDirection, fromPos, toPos,
790                                   preferences)

Finds the shortest path from fromEdge to toEdge respecting vClass, using Dijkstra's algorithm. It returns a pair of a tuple of edges and the cost. If no path is found the first element is None. The cost for the returned path is equal to the sum of all edge lengths in the path, including the internal connectors, if they are present in the network. The path itself does not include internal edges except for the case when the start or end edge are internal edges. The search may be limited using the given threshold. The preferences declare a mapping from the 'routingType' of each edge to divisor that is applied to the lenght of that edge (i.e. a preference of 2 reduces reduces effective length by a factor of 0.5).

def getFastestPath( self, fromEdge, toEdge, maxCost=inf, vClass=None, reversalPenalty=0, includeFromToCost=True, withInternal=False, ignoreDirection=False, fromPos=None, toPos=None, preferences={}):
792    def getFastestPath(self, fromEdge, toEdge, maxCost=1e400, vClass=None, reversalPenalty=0,
793                       includeFromToCost=True, withInternal=False, ignoreDirection=False,
794                       fromPos=None, toPos=None, preferences={}):
795        """
796        Finds the fastest path from fromEdge to toEdge respecting vClass, using Dijkstra's algorithm.
797        It returns a pair of a tuple of edges and the cost. If no path is found the first element is None.
798        The cost for the returned path is equal to the sum of all edge costs in the path,
799        including the internal connectors, if they are present in the network.
800        The path itself does not include internal edges except for the case
801        when the start or end edge are internal edges.
802        The search may be limited using the given threshold.
803        The preferences declare a mapping from the 'routingType' of each edge to divisor
804        that is applied to the computed traveltime of that edge
805        (i.e. a preference of 2 reduces effective traveltime by a factor of 0.5).
806        """
807
808        return self.getOptimalPath(fromEdge, toEdge, True, maxCost, vClass, reversalPenalty,
809                                   includeFromToCost, withInternal, ignoreDirection, fromPos, toPos,
810                                   preferences)

Finds the fastest path from fromEdge to toEdge respecting vClass, using Dijkstra's algorithm. It returns a pair of a tuple of edges and the cost. If no path is found the first element is None. The cost for the returned path is equal to the sum of all edge costs in the path, including the internal connectors, if they are present in the network. The path itself does not include internal edges except for the case when the start or end edge are internal edges. The search may be limited using the given threshold. The preferences declare a mapping from the 'routingType' of each edge to divisor that is applied to the computed traveltime of that edge (i.e. a preference of 2 reduces effective traveltime by a factor of 0.5).

def getReachable(self, source, vclass=None, useIncoming=False, cache=None):
812    def getReachable(self, source, vclass=None, useIncoming=False, cache=None):
813        if vclass is not None and not source.allows(vclass):
814            raise RuntimeError("'{}' does not allow {}".format(source.getID(), vclass))
815        fringe = [source]
816        found = set()
817        found.add(source)
818        while len(fringe) > 0:
819            new_fringe = []
820            for e in fringe:
821                if vclass == "pedestrian":
822                    cands = chain(chain(*e.getIncoming().values()), chain(*e.getOutgoing().values()))
823                else:
824                    cands = chain(*(e.getIncoming().values() if useIncoming else e.getOutgoing().values()))
825                # print("\n".join(map(str, list(cands))))
826                for conn in cands:
827                    if vclass is None or (
828                            conn.getFromLane().allows(vclass)
829                            and conn.getToLane().allows(vclass)):
830                        for reachable in [conn.getTo(), conn.getFrom()]:
831                            if reachable not in found:
832                                # print("added %s via %s" % (reachable, conn))
833                                if cache and reachable in cache:
834                                    found.update(cache[reachable])
835                                else:
836                                    found.add(reachable)
837                                    new_fringe.append(reachable)
838            fringe = new_fringe
839        if cache is not None:
840            cache[source] = tuple(found)
841        return found
class NetReader(xml.sax.handler.ContentHandler):
 844class NetReader(handler.ContentHandler):
 845
 846    """Reads a network, storing the edge geometries, lane numbers and max. speeds"""
 847
 848    def __init__(self, **others):
 849        self._net = others.get('net', Net())
 850        self._currentEdge = None
 851        self._currentNode = None
 852        self._currentConnection = None
 853        self._currentLane = None
 854        self._crossingID2edgeIDs = {}
 855        self._withPhases = others.get('withPrograms', False)
 856        self._latestProgram = others.get('withLatestPrograms', False)
 857        if self._latestProgram:
 858            self._withPhases = True
 859        self._withConnections = others.get('withConnections', True)
 860        self._withFoes = others.get('withFoes', True)
 861        self._withPedestrianConnections = others.get('withPedestrianConnections', False)
 862        self._withMacroConnectors = others.get('withMacroConnectors', False)
 863        self._withInternal = others.get('withInternal', self._withPedestrianConnections)
 864        if self._withPedestrianConnections and not self._withInternal:
 865            sys.stderr.write("Warning: Option withPedestrianConnections requires withInternal\n")
 866            self._withInternal = True
 867        self._bidiEdgeIDs = {}
 868
 869    def startElement(self, name, attrs):
 870        if name == 'net':
 871            parts = attrs["version"].split('.', 1)
 872            self._net._version = (int(parts[0]), float(parts[1]))
 873        elif name == 'location':
 874            self._net.setLocation(attrs["netOffset"], attrs["convBoundary"], attrs[
 875                                  "origBoundary"], attrs["projParameter"])
 876        elif name == 'type':
 877            self._net._edgeTypes[attrs['id']] = EdgeType(attrs['id'], attrs.get('allow'), attrs.get('disallow'))
 878        elif name == 'edge':
 879            function = attrs.get('function', '')
 880            if (function == ''
 881                    or (self._withInternal and function in ['internal', 'crossing', 'walkingarea'])
 882                    or (self._withMacroConnectors and function == 'connector')):
 883                prio = -1
 884                if 'priority' in attrs:
 885                    prio = int(attrs['priority'])
 886
 887                # get the  ids
 888                edgeID = attrs['id']
 889                fromNodeID = attrs.get('from', None)
 890                toNodeID = attrs.get('to', None)
 891
 892                # for internal junctions use the junction's id for from and to node
 893                if function == 'internal' or function == 'crossing' or function == 'walkingarea':
 894                    fromNodeID = toNodeID = edgeID[1:edgeID.rfind('_')]
 895
 896                # remember edges crossed by pedestrians to link them later to the crossing objects
 897                if function == 'crossing':
 898                    self._crossingID2edgeIDs[edgeID] = attrs.get('crossingEdges').split(' ')
 899
 900                self._currentEdge = self._net.addEdge(edgeID, fromNodeID, toNodeID, prio, function,
 901                                                      attrs.get('name', ''), attrs.get('type', ''),
 902                                                      attrs.get('routingType', ''))
 903
 904                self._currentEdge.setRawShape(convertShape(attrs.get('shape', '')))
 905
 906                bidi = attrs.get('bidi', '')
 907                if bidi:
 908                    self._bidiEdgeIDs[edgeID] = bidi
 909            else:
 910                if function in ['crossing', 'walkingarea']:
 911                    self._net._crossings_and_walkingAreas.add(attrs['id'])
 912                elif function == 'connector':
 913                    self._net._macroConnectors.add(attrs['id'])
 914                self._currentEdge = None
 915        elif name == 'lane' and self._currentEdge is not None:
 916            self._currentLane = self._net.addLane(
 917                self._currentEdge,
 918                float(attrs['speed']),
 919                float(attrs['length']),
 920                float(attrs.get('width', 3.2)),
 921                attrs.get('allow'),
 922                attrs.get('disallow'),
 923                attrs.get('acceleration') == "1")
 924            self._currentLane.setShape(convertShape(attrs.get('shape', '')))
 925        elif name == 'neigh' and self._currentLane is not None:
 926            self._currentLane.setNeigh(attrs['lane'])
 927        elif name == 'junction':
 928            if attrs['id'][0] != ':':
 929                intLanes = None
 930                if self._withInternal:
 931                    intLanes = attrs["intLanes"].split(" ")
 932                self._currentNode = self._net.addNode(attrs['id'], attrs['type'],
 933                                                      tuple(
 934                                                          map(float, [attrs['x'], attrs['y'],
 935                                                                      attrs['z'] if 'z' in attrs else '0'])),
 936                                                      attrs['incLanes'].split(" "), intLanes)
 937                self._currentNode.setShape(
 938                    convertShape(attrs.get('shape', '')))
 939                if 'fringe' in attrs:
 940                    self._currentNode._fringe = attrs['fringe']
 941
 942        elif name == 'succ' and self._withConnections:  # deprecated
 943            if attrs['edge'][0] != ':':
 944                self._currentEdge = self._net.getEdge(attrs['edge'])
 945                self._currentLane = attrs['lane']
 946                self._currentLane = int(
 947                    self._currentLane[self._currentLane.rfind('_') + 1:])
 948            else:
 949                self._currentEdge = None
 950        elif name == 'succlane' and self._withConnections:  # deprecated
 951            lid = attrs['lane']
 952            if lid[0] != ':' and lid != "SUMO_NO_DESTINATION" and self._currentEdge:
 953                connected = self._net.getEdge(lid[:lid.rfind('_')])
 954                tolane = int(lid[lid.rfind('_') + 1:])
 955                if 'tl' in attrs and attrs['tl'] != "":
 956                    tl = attrs['tl']
 957                    tllink = int(attrs['linkIdx'])
 958                    tlid = attrs['tl']
 959                    toEdge = self._net.getEdge(lid[:lid.rfind('_')])
 960                    tolane2 = toEdge._lanes[tolane]
 961                    tls = self._net.addTLS(
 962                        tlid, self._currentEdge._lanes[self._currentLane], tolane2, tllink)
 963                    self._currentEdge.setTLS(tls)
 964                else:
 965                    tl = ""
 966                    tllink = -1
 967                toEdge = self._net.getEdge(lid[:lid.rfind('_')])
 968                tolane = toEdge._lanes[tolane]
 969                viaLaneID = attrs['via']
 970                self._net.addConnection(self._currentEdge, connected, self._currentEdge._lanes[
 971                                        self._currentLane], tolane,
 972                                        attrs['dir'], tl, tllink, -1,
 973                                        attrs.get('allow'), attrs.get('disallow'), attrs['state'], viaLaneID)
 974        elif name == 'connection' and self._withConnections and (attrs['from'][0] != ":" or self._withInternal):
 975            fromEdgeID = attrs['from']
 976            toEdgeID = attrs['to']
 977            if ((self._withPedestrianConnections or not (fromEdgeID in self._net._crossings_and_walkingAreas or
 978                                                         toEdgeID in self._net._crossings_and_walkingAreas))
 979                and (self._withMacroConnectors or not (fromEdgeID in self._net._macroConnectors or toEdgeID in
 980                                                       self._net._macroConnectors))):
 981                fromEdge = self._net.getEdge(fromEdgeID)
 982                toEdge = self._net.getEdge(toEdgeID)
 983                fromLane = fromEdge.getLane(int(attrs['fromLane']))
 984                toLane = toEdge.getLane(int(attrs['toLane']))
 985                if 'tl' in attrs and attrs['tl'] != "":
 986                    tl = attrs['tl']
 987                    tllink = int(attrs['linkIndex'])
 988                    tllink2 = int(attrs.get('linkIndex2', -1))
 989                    tls = self._net.addTLS(tl, fromLane, toLane, tllink)
 990                    fromEdge.setTLS(tls)
 991                else:
 992                    tl = ""
 993                    tllink = -1
 994                    tllink2 = -1
 995                try:
 996                    viaLaneID = attrs['via']
 997                except KeyError:
 998                    viaLaneID = ''
 999
1000                self._currentConnection = self._net.addConnection(
1001                    fromEdge, toEdge, fromLane, toLane, attrs['dir'], tl,
1002                    tllink, tllink2, attrs.get('allow'), attrs.get('disallow'), attrs['state'], viaLaneID)
1003
1004        # 'row-logic' is deprecated!!!
1005        elif self._withFoes and name == 'ROWLogic':
1006            self._currentNode = attrs['id']
1007        elif name == 'logicitem' and self._withFoes:  # deprecated
1008            self._net.setFoes(
1009                self._currentNode, int(attrs['request']), attrs["foes"], attrs["response"])
1010        elif name == 'request' and self._withFoes:
1011            self._currentNode.setFoes(
1012                int(attrs['index']), attrs["foes"], attrs["response"])
1013        # tl-logic is deprecated!!! NOTE: nevertheless, this is still used by
1014        # netconvert... (Leo)
1015        elif self._withPhases and name == 'tlLogic':
1016            self._currentProgram = self._net.addTLSProgram(
1017                attrs['id'], attrs['programID'],
1018                intIfPossible(float(attrs['offset'])), attrs['type'], self._latestProgram)
1019        elif self._withPhases and name == 'phase':
1020            self._currentProgram.addPhase(
1021                attrs['state'],
1022                intIfPossible(float(attrs['duration'])),
1023                intIfPossible(float(attrs['minDur'])) if 'minDur' in attrs else -1,
1024                intIfPossible(float(attrs['maxDur'])) if 'maxDur' in attrs else -1,
1025                list(map(int, attrs['next'].split())) if 'next' in attrs else [],
1026                attrs['name'] if 'name' in attrs else ""
1027            )
1028        elif name == 'roundabout':
1029            self._net.addRoundabout(
1030                attrs['nodes'].split(), attrs['edges'].split())
1031        elif name == 'param':
1032            if self._currentLane is not None:
1033                self._currentLane.setParam(attrs['key'], attrs['value'])
1034            elif self._currentEdge is not None:
1035                self._currentEdge.setParam(attrs['key'], attrs['value'])
1036            elif self._currentNode is not None:
1037                self._currentNode.setParam(attrs['key'], attrs['value'])
1038            elif self._currentConnection is not None:
1039                self._currentConnection.setParam(attrs['key'], attrs['value'])
1040            elif self._withPhases and self._currentProgram is not None:
1041                self._currentProgram.setParam(attrs['key'], attrs['value'])
1042
1043    def endElement(self, name):
1044        if name == 'lane':
1045            self._currentLane = None
1046        elif name == 'edge':
1047            self._currentEdge = None
1048        elif name == 'junction':
1049            self._currentNode = None
1050        elif name == 'connection':
1051            self._currentConnection = None
1052        # 'row-logic' is deprecated!!!
1053        elif name == 'ROWLogic' or name == 'row-logic':
1054            self._haveROWLogic = False
1055        # tl-logic is deprecated!!!
1056        elif self._withPhases and (name == 'tlLogic' or name == 'tl-logic'):
1057            self._currentProgram = None
1058        elif name == 'net':
1059            for edgeID, bidiID in self._bidiEdgeIDs.items():
1060                self._net.getEdge(edgeID)._bidi = self._net.getEdge(bidiID)
1061
1062    def endDocument(self):
1063        # set crossed edges of pedestrian crossings
1064        for crossingID, crossedEdgeIDs in self._crossingID2edgeIDs.items():
1065            pedCrossing = self._net.getEdge(crossingID)
1066            for crossedEdgeID in crossedEdgeIDs:
1067                pedCrossing._addCrossingEdge(self._net.getEdge(crossedEdgeID))
1068
1069    def getNet(self):
1070        return self._net

Reads a network, storing the edge geometries, lane numbers and max. speeds

NetReader(**others)
848    def __init__(self, **others):
849        self._net = others.get('net', Net())
850        self._currentEdge = None
851        self._currentNode = None
852        self._currentConnection = None
853        self._currentLane = None
854        self._crossingID2edgeIDs = {}
855        self._withPhases = others.get('withPrograms', False)
856        self._latestProgram = others.get('withLatestPrograms', False)
857        if self._latestProgram:
858            self._withPhases = True
859        self._withConnections = others.get('withConnections', True)
860        self._withFoes = others.get('withFoes', True)
861        self._withPedestrianConnections = others.get('withPedestrianConnections', False)
862        self._withMacroConnectors = others.get('withMacroConnectors', False)
863        self._withInternal = others.get('withInternal', self._withPedestrianConnections)
864        if self._withPedestrianConnections and not self._withInternal:
865            sys.stderr.write("Warning: Option withPedestrianConnections requires withInternal\n")
866            self._withInternal = True
867        self._bidiEdgeIDs = {}
def startElement(self, name, attrs):
 869    def startElement(self, name, attrs):
 870        if name == 'net':
 871            parts = attrs["version"].split('.', 1)
 872            self._net._version = (int(parts[0]), float(parts[1]))
 873        elif name == 'location':
 874            self._net.setLocation(attrs["netOffset"], attrs["convBoundary"], attrs[
 875                                  "origBoundary"], attrs["projParameter"])
 876        elif name == 'type':
 877            self._net._edgeTypes[attrs['id']] = EdgeType(attrs['id'], attrs.get('allow'), attrs.get('disallow'))
 878        elif name == 'edge':
 879            function = attrs.get('function', '')
 880            if (function == ''
 881                    or (self._withInternal and function in ['internal', 'crossing', 'walkingarea'])
 882                    or (self._withMacroConnectors and function == 'connector')):
 883                prio = -1
 884                if 'priority' in attrs:
 885                    prio = int(attrs['priority'])
 886
 887                # get the  ids
 888                edgeID = attrs['id']
 889                fromNodeID = attrs.get('from', None)
 890                toNodeID = attrs.get('to', None)
 891
 892                # for internal junctions use the junction's id for from and to node
 893                if function == 'internal' or function == 'crossing' or function == 'walkingarea':
 894                    fromNodeID = toNodeID = edgeID[1:edgeID.rfind('_')]
 895
 896                # remember edges crossed by pedestrians to link them later to the crossing objects
 897                if function == 'crossing':
 898                    self._crossingID2edgeIDs[edgeID] = attrs.get('crossingEdges').split(' ')
 899
 900                self._currentEdge = self._net.addEdge(edgeID, fromNodeID, toNodeID, prio, function,
 901                                                      attrs.get('name', ''), attrs.get('type', ''),
 902                                                      attrs.get('routingType', ''))
 903
 904                self._currentEdge.setRawShape(convertShape(attrs.get('shape', '')))
 905
 906                bidi = attrs.get('bidi', '')
 907                if bidi:
 908                    self._bidiEdgeIDs[edgeID] = bidi
 909            else:
 910                if function in ['crossing', 'walkingarea']:
 911                    self._net._crossings_and_walkingAreas.add(attrs['id'])
 912                elif function == 'connector':
 913                    self._net._macroConnectors.add(attrs['id'])
 914                self._currentEdge = None
 915        elif name == 'lane' and self._currentEdge is not None:
 916            self._currentLane = self._net.addLane(
 917                self._currentEdge,
 918                float(attrs['speed']),
 919                float(attrs['length']),
 920                float(attrs.get('width', 3.2)),
 921                attrs.get('allow'),
 922                attrs.get('disallow'),
 923                attrs.get('acceleration') == "1")
 924            self._currentLane.setShape(convertShape(attrs.get('shape', '')))
 925        elif name == 'neigh' and self._currentLane is not None:
 926            self._currentLane.setNeigh(attrs['lane'])
 927        elif name == 'junction':
 928            if attrs['id'][0] != ':':
 929                intLanes = None
 930                if self._withInternal:
 931                    intLanes = attrs["intLanes"].split(" ")
 932                self._currentNode = self._net.addNode(attrs['id'], attrs['type'],
 933                                                      tuple(
 934                                                          map(float, [attrs['x'], attrs['y'],
 935                                                                      attrs['z'] if 'z' in attrs else '0'])),
 936                                                      attrs['incLanes'].split(" "), intLanes)
 937                self._currentNode.setShape(
 938                    convertShape(attrs.get('shape', '')))
 939                if 'fringe' in attrs:
 940                    self._currentNode._fringe = attrs['fringe']
 941
 942        elif name == 'succ' and self._withConnections:  # deprecated
 943            if attrs['edge'][0] != ':':
 944                self._currentEdge = self._net.getEdge(attrs['edge'])
 945                self._currentLane = attrs['lane']
 946                self._currentLane = int(
 947                    self._currentLane[self._currentLane.rfind('_') + 1:])
 948            else:
 949                self._currentEdge = None
 950        elif name == 'succlane' and self._withConnections:  # deprecated
 951            lid = attrs['lane']
 952            if lid[0] != ':' and lid != "SUMO_NO_DESTINATION" and self._currentEdge:
 953                connected = self._net.getEdge(lid[:lid.rfind('_')])
 954                tolane = int(lid[lid.rfind('_') + 1:])
 955                if 'tl' in attrs and attrs['tl'] != "":
 956                    tl = attrs['tl']
 957                    tllink = int(attrs['linkIdx'])
 958                    tlid = attrs['tl']
 959                    toEdge = self._net.getEdge(lid[:lid.rfind('_')])
 960                    tolane2 = toEdge._lanes[tolane]
 961                    tls = self._net.addTLS(
 962                        tlid, self._currentEdge._lanes[self._currentLane], tolane2, tllink)
 963                    self._currentEdge.setTLS(tls)
 964                else:
 965                    tl = ""
 966                    tllink = -1
 967                toEdge = self._net.getEdge(lid[:lid.rfind('_')])
 968                tolane = toEdge._lanes[tolane]
 969                viaLaneID = attrs['via']
 970                self._net.addConnection(self._currentEdge, connected, self._currentEdge._lanes[
 971                                        self._currentLane], tolane,
 972                                        attrs['dir'], tl, tllink, -1,
 973                                        attrs.get('allow'), attrs.get('disallow'), attrs['state'], viaLaneID)
 974        elif name == 'connection' and self._withConnections and (attrs['from'][0] != ":" or self._withInternal):
 975            fromEdgeID = attrs['from']
 976            toEdgeID = attrs['to']
 977            if ((self._withPedestrianConnections or not (fromEdgeID in self._net._crossings_and_walkingAreas or
 978                                                         toEdgeID in self._net._crossings_and_walkingAreas))
 979                and (self._withMacroConnectors or not (fromEdgeID in self._net._macroConnectors or toEdgeID in
 980                                                       self._net._macroConnectors))):
 981                fromEdge = self._net.getEdge(fromEdgeID)
 982                toEdge = self._net.getEdge(toEdgeID)
 983                fromLane = fromEdge.getLane(int(attrs['fromLane']))
 984                toLane = toEdge.getLane(int(attrs['toLane']))
 985                if 'tl' in attrs and attrs['tl'] != "":
 986                    tl = attrs['tl']
 987                    tllink = int(attrs['linkIndex'])
 988                    tllink2 = int(attrs.get('linkIndex2', -1))
 989                    tls = self._net.addTLS(tl, fromLane, toLane, tllink)
 990                    fromEdge.setTLS(tls)
 991                else:
 992                    tl = ""
 993                    tllink = -1
 994                    tllink2 = -1
 995                try:
 996                    viaLaneID = attrs['via']
 997                except KeyError:
 998                    viaLaneID = ''
 999
1000                self._currentConnection = self._net.addConnection(
1001                    fromEdge, toEdge, fromLane, toLane, attrs['dir'], tl,
1002                    tllink, tllink2, attrs.get('allow'), attrs.get('disallow'), attrs['state'], viaLaneID)
1003
1004        # 'row-logic' is deprecated!!!
1005        elif self._withFoes and name == 'ROWLogic':
1006            self._currentNode = attrs['id']
1007        elif name == 'logicitem' and self._withFoes:  # deprecated
1008            self._net.setFoes(
1009                self._currentNode, int(attrs['request']), attrs["foes"], attrs["response"])
1010        elif name == 'request' and self._withFoes:
1011            self._currentNode.setFoes(
1012                int(attrs['index']), attrs["foes"], attrs["response"])
1013        # tl-logic is deprecated!!! NOTE: nevertheless, this is still used by
1014        # netconvert... (Leo)
1015        elif self._withPhases and name == 'tlLogic':
1016            self._currentProgram = self._net.addTLSProgram(
1017                attrs['id'], attrs['programID'],
1018                intIfPossible(float(attrs['offset'])), attrs['type'], self._latestProgram)
1019        elif self._withPhases and name == 'phase':
1020            self._currentProgram.addPhase(
1021                attrs['state'],
1022                intIfPossible(float(attrs['duration'])),
1023                intIfPossible(float(attrs['minDur'])) if 'minDur' in attrs else -1,
1024                intIfPossible(float(attrs['maxDur'])) if 'maxDur' in attrs else -1,
1025                list(map(int, attrs['next'].split())) if 'next' in attrs else [],
1026                attrs['name'] if 'name' in attrs else ""
1027            )
1028        elif name == 'roundabout':
1029            self._net.addRoundabout(
1030                attrs['nodes'].split(), attrs['edges'].split())
1031        elif name == 'param':
1032            if self._currentLane is not None:
1033                self._currentLane.setParam(attrs['key'], attrs['value'])
1034            elif self._currentEdge is not None:
1035                self._currentEdge.setParam(attrs['key'], attrs['value'])
1036            elif self._currentNode is not None:
1037                self._currentNode.setParam(attrs['key'], attrs['value'])
1038            elif self._currentConnection is not None:
1039                self._currentConnection.setParam(attrs['key'], attrs['value'])
1040            elif self._withPhases and self._currentProgram is not None:
1041                self._currentProgram.setParam(attrs['key'], attrs['value'])

Signals the start of an element in non-namespace mode.

The name parameter contains the raw XML 1.0 name of the element type as a string and the attrs parameter holds an instance of the Attributes class containing the attributes of the element.

def endElement(self, name):
1043    def endElement(self, name):
1044        if name == 'lane':
1045            self._currentLane = None
1046        elif name == 'edge':
1047            self._currentEdge = None
1048        elif name == 'junction':
1049            self._currentNode = None
1050        elif name == 'connection':
1051            self._currentConnection = None
1052        # 'row-logic' is deprecated!!!
1053        elif name == 'ROWLogic' or name == 'row-logic':
1054            self._haveROWLogic = False
1055        # tl-logic is deprecated!!!
1056        elif self._withPhases and (name == 'tlLogic' or name == 'tl-logic'):
1057            self._currentProgram = None
1058        elif name == 'net':
1059            for edgeID, bidiID in self._bidiEdgeIDs.items():
1060                self._net.getEdge(edgeID)._bidi = self._net.getEdge(bidiID)

Signals the end of an element in non-namespace mode.

The name parameter contains the name of the element type, just as with the startElement event.

def endDocument(self):
1062    def endDocument(self):
1063        # set crossed edges of pedestrian crossings
1064        for crossingID, crossedEdgeIDs in self._crossingID2edgeIDs.items():
1065            pedCrossing = self._net.getEdge(crossingID)
1066            for crossedEdgeID in crossedEdgeIDs:
1067                pedCrossing._addCrossingEdge(self._net.getEdge(crossedEdgeID))

Receive notification of the end of a document.

The SAX parser will invoke this method only once, and it will be the last method invoked during the parse. The parser shall not invoke this method until it has either abandoned parsing (because of an unrecoverable error) or reached the end of input.

def getNet(self):
1069    def getNet(self):
1070        return self._net
def convertShape(shapeString):
1073def convertShape(shapeString):
1074    """ Convert xml shape string into float tuples.
1075
1076    This method converts the 2d or 3d shape string from SUMO's xml file
1077    into a list containing 3d float-tuples. Non existent z coordinates default
1078    to zero. If shapeString is empty, an empty list will be returned.
1079    """
1080
1081    cshape = []
1082    for pointString in shapeString.split():
1083        p = [float(e) for e in pointString.split(",")]
1084        if len(p) == 2:
1085            cshape.append((p[0], p[1], 0.))
1086        elif len(p) == 3:
1087            cshape.append(tuple(p))
1088        else:
1089            raise ValueError(
1090                'Invalid shape point "%s", should be either 2d or 3d' % pointString)
1091    return cshape

Convert xml shape string into float tuples.

This method converts the 2d or 3d shape string from SUMO's xml file into a list containing 3d float-tuples. Non existent z coordinates default to zero. If shapeString is empty, an empty list will be returned.

def lane2edge(laneID):
1094def lane2edge(laneID):
1095    return laneID[:laneID.rfind("_")]
def lane2index(laneID):
1098def lane2index(laneID):
1099    return int(laneID[laneID.rfind("_") + 1:])
def readNet(filename, **others):
1102def readNet(filename, **others):
1103    """ load a .net.xml file
1104    The following named options are supported:
1105
1106        'net' : initialize data structures with an existing net object (default Net())
1107        'withPrograms' : import all traffic light programs (default False)
1108        'withLatestPrograms' : import only the last program for each traffic light.
1109                               This is the program that would be active in sumo by default.
1110                               (default False)
1111        'withConnections' : import all connections (default True)
1112        'withFoes' : import right-of-way information (default True)
1113        'withInternal' : import internal edges and lanes (default False)
1114        'withPedestrianConnections' : import connections between sidewalks, crossings (default False)
1115        'lxml' : set to False to use the xml.sax parser instead of the lxml parser
1116        'maxcache' : set maximum cache size (default 1000) or 0 to disable optimal route caching
1117    """
1118    netreader = NetReader(**others)
1119    try:
1120        source = gzip.open(filename)
1121        source.read(10)
1122        source.seek(0)
1123    except IOError:
1124        source = filename
1125    if HAVE_LXML and others.get("lxml", True):
1126        if isinstance(source, pathlib.Path):
1127            source = str(source)
1128        for event, v in lxml.etree.iterparse(source, events=("start", "end")):
1129            if event == "start":
1130                netreader.startElement(v.tag, v.attrib)
1131            elif event == "end":
1132                netreader.endElement(v.tag)
1133            v.clear()  # reduce memory footprint
1134    else:
1135        parse(source, netreader)
1136    net = netreader.getNet()
1137    maxcache = others.get('maxcache', 1000)
1138    if HAVE_LRU_CACHE and maxcache is not None and maxcache > 0:
1139        net.initRoutingCache(maxcache)
1140    return net

load a .net.xml file The following named options are supported:

'net' : initialize data structures with an existing net object (default Net())
'withPrograms' : import all traffic light programs (default False)
'withLatestPrograms' : import only the last program for each traffic light.
                       This is the program that would be active in sumo by default.
                       (default False)
'withConnections' : import all connections (default True)
'withFoes' : import right-of-way information (default True)
'withInternal' : import internal edges and lanes (default False)
'withPedestrianConnections' : import connections between sidewalks, crossings (default False)
'lxml' : set to False to use the xml.sax parser instead of the lxml parser
'maxcache' : set maximum cache size (default 1000) or 0 to disable optimal route caching