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

Traffic Light Signal for a sumo network

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

The whole sumo network.

hasInternal
hasWalkingArea
def initRoutingCache(self, maxsize=1000):
248    def initRoutingCache(self, maxsize=1000):
249        self._routingCache = lru_cache(maxsize=maxsize)(lambda fromEdge, fastest, vClass, ignoreDirection, reversalPenalty, preferences: {})  # noqa
def cacheRoute(self, fromEdge, toEdge, fastest, vClass, cost, path):
251    def cacheRoute(self, fromEdge, toEdge, fastest, vClass, cost, path):
252        if self._routingCacheOD is None:
253            self._routingCacheOD = dict()
254        self._routingCacheOD[(fromEdge, toEdge, fastest, vClass)] = (cost, path)
def getVersion(self):
256    def getVersion(self):
257        return self._version
def getEdgeType(self, typeID):
259    def getEdgeType(self, typeID):
260        return self._edgeTypes[typeID]
def setLocation(self, netOffset, convBoundary, origBoundary, projParameter):
262    def setLocation(self, netOffset, convBoundary, origBoundary, projParameter):
263        self._location["netOffset"] = netOffset
264        self._location["convBoundary"] = convBoundary
265        self._location["origBoundary"] = origBoundary
266        self._location["projParameter"] = projParameter
def loadSelection(self, selectionFile):
268    def loadSelection(self, selectionFile):
269        self.resetSelection()
270        with io.open(selectionFile, "r", encoding="utf-8") as f:
271            for line in f:
272                line = line.strip()
273                if line.startswith("edge:"):
274                    edgeID = line[5:]
275                    if edgeID in self._id2edge:
276                        self.getEdge(edgeID).select()
277                elif line.startswith("junction:"):
278                    nodeID = line[9:]
279                    if nodeID in self._id2node:
280                        self.getNode(nodeID).select()
def resetSelection(self):
282    def resetSelection(self):
283        for n in self._nodes:
284            n.select(False)
285        for e in self._edges:
286            e.select(False)
def addNode(self, id, type=None, coord=None, incLanes=None, intLanes=None):
288    def addNode(self, id, type=None, coord=None, incLanes=None, intLanes=None):
289        if id is None:
290            return None
291        if id not in self._id2node:
292            n = node.Node(id, type, coord, incLanes, intLanes)
293            self._nodes.append(n)
294            self._id2node[id] = n
295        self.setAdditionalNodeInfo(
296            self._id2node[id], type, coord, incLanes, intLanes)
297        return self._id2node[id]
def setAdditionalNodeInfo(self, node, type, coord, incLanes, intLanes=None):
299    def setAdditionalNodeInfo(self, node, type, coord, incLanes, intLanes=None):
300        if coord is not None and node._coord is None:
301            node._coord = coord
302            self._ranges[0][0] = min(self._ranges[0][0], coord[0])
303            self._ranges[0][1] = max(self._ranges[0][1], coord[0])
304            self._ranges[1][0] = min(self._ranges[1][0], coord[1])
305            self._ranges[1][1] = max(self._ranges[1][1], coord[1])
306        if incLanes is not None and node._incLanes is None:
307            node._incLanes = incLanes
308        if intLanes is not None and node._intLanes is None:
309            node._intLanes = intLanes
310        if type is not None and node._type is None:
311            node._type = type
def addEdge( self, id, fromID, toID, prio, function, name, edgeType='', routingType=''):
313    def addEdge(self, id, fromID, toID, prio, function, name, edgeType='', routingType=''):
314        if id not in self._id2edge:
315            fromN = self.addNode(fromID)
316            toN = self.addNode(toID)
317            e = edge.Edge(id, fromN, toN, prio, function, name, edgeType, routingType)
318            self._edges.append(e)
319            self._id2edge[id] = e
320            if function:
321                self.hasInternal = True
322                if function == "walkingarea":
323                    self.hasWalkingArea = True
324        return self._id2edge[id]
def addLane( self, edge, speed, length, width, allow=None, disallow=None, acceleration=False):
326    def addLane(self, edge, speed, length, width, allow=None, disallow=None, acceleration=False):
327        return lane.Lane(edge, speed, length, width, allow, disallow, acceleration)
def addRoundabout(self, nodes, edges=None):
329    def addRoundabout(self, nodes, edges=None):
330        r = roundabout.Roundabout(nodes, edges)
331        self._roundabouts.append(r)
332        return r
def addConnection( self, fromEdge, toEdge, fromlane, tolane, direction, tls, tllink, tllink2, allow, disallow, state, viaLaneID=None):
334    def addConnection(self, fromEdge, toEdge, fromlane, tolane, direction,
335                      tls, tllink, tllink2, allow, disallow, state, viaLaneID=None):
336        conn = connection.Connection(
337            fromEdge, toEdge, fromlane, tolane, direction,
338            tls, tllink, tllink2, allow, disallow, state, viaLaneID)
339        fromEdge.addOutgoing(conn)
340        fromlane.addOutgoing(conn)
341        toEdge._addIncoming(conn)
342        if viaLaneID:
343            try:
344                # internal lanes are only available when building with option withInternal=True
345                viaLane = self.getLane(viaLaneID)
346                viaEdge = viaLane.getEdge()
347                viaEdge._addIncoming(connection.Connection(
348                    fromEdge, viaEdge, fromlane, viaLane, direction, tls,
349                    tllink, tllink2, allow, disallow, state, ''))
350            except Exception:
351                pass
352        return conn
def getEdges(self, withInternal=True):
354    def getEdges(self, withInternal=True):
355        if not withInternal:
356            return [e for e in self._edges if e.getFunction() == '']
357        else:
358            return self._edges
def getRoundabouts(self):
360    def getRoundabouts(self):
361        return self._roundabouts
def hasEdge(self, id):
363    def hasEdge(self, id):
364        return id in self._id2edge
def getEdge(self, id):
366    def getEdge(self, id):
367        return self._id2edge[id]
def getLane(self, laneID):
369    def getLane(self, laneID):
370        edge_id, lane_index = laneID.rsplit("_", 1)
371        return self.getEdge(edge_id).getLane(int(lane_index))
def getNeighboringEdges(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
382    def getNeighboringEdges(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
383        edges = []
384        rtree = self._rtreeEdges[includeJunctions]
385        try:
386            if rtree is None:
387                rtree = self._initRTree(self._edges, includeJunctions)
388                self._rtreeEdges[includeJunctions] = rtree
389            for i in rtree.intersection((x - r, y - r, x + r, y + r)):
390                e = self._edges[i]
391                d = sumolib.geomhelper.distancePointToPolygon(
392                    (x, y), e.getShape(includeJunctions))
393                if d < r:
394                    edges.append((e, d))
395        except ImportError:
396            if not allowFallback:
397                raise
398            warnings.warn("Module 'rtree' not available. Using brute-force fallback.")
399            for the_edge in self._edges:
400                d = sumolib.geomhelper.distancePointToPolygon((x, y), the_edge.getShape(includeJunctions))
401                if d < r:
402                    edges.append((the_edge, d))
403        return edges
def getNeighboringLanes(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
405    def getNeighboringLanes(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
406        lanes = []
407        rtree = self._rtreeLanes[includeJunctions]
408        try:
409            if rtree is None:
410                for the_edge in self._edges:
411                    self._allLanes += the_edge.getLanes()
412                rtree = self._initRTree(self._allLanes, includeJunctions)
413                self._rtreeLanes[includeJunctions] = rtree
414            for i in rtree.intersection((x - r, y - r, x + r, y + r)):
415                the_lane = self._allLanes[i]
416                d = sumolib.geomhelper.distancePointToPolygon((x, y), the_lane.getShape(includeJunctions))
417                if d < r:
418                    lanes.append((the_lane, d))
419        except ImportError:
420            if not allowFallback:
421                raise
422            warnings.warn("Module 'rtree' not available. Using brute-force fallback.")
423            for the_edge in self._edges:
424                for the_lane in the_edge.getLanes():
425                    d = sumolib.geomhelper.distancePointToPolygon((x, y), the_lane.getShape(includeJunctions))
426                    if d < r:
427                        lanes.append((the_lane, d))
428        return lanes
def getNearestLane(self, x, y, r=0.1, vClass=None, isGeo=False, allowFallback=True):
430    def getNearestLane(self, x, y, r=0.1, vClass=None, isGeo=False, allowFallback=True):
431        """Return (lane, lanePos, dist) for the nearest lane at a distance less than r,
432        or None if no lane matches. Both lanePos and dist are in meters.
433
434        If vClass is given, only lanes allowing that vehicle class are considered.
435        If isGeo is True, x and y are longitude and latitude, respectively, and
436        the network's geo-projection is used (requires pyproj).
437        Distances are measured to the lane shape without junction extensions.
438        Internal lanes are considered if the network was read withInternal=True.
439        """
440        if isGeo:
441            x, y = self.convertLonLat2XY(x, y)
442        candidates = [(lane, dist) for lane, dist in
443                      self.getNeighboringLanes(x, y, r, includeJunctions=False, allowFallback=allowFallback)
444                      if lane.allows(vClass)]
445        if not candidates:
446            return None
447        lane, dist = min(candidates, key=lambda candidate: candidate[1])
448        lanePos, dist = lane.getClosestLanePosAndDist((x, y))
449        return lane, lanePos, dist

Return (lane, lanePos, dist) for the nearest lane at a distance less than r, or None if no lane matches. Both lanePos and dist are in meters.

If vClass is given, only lanes allowing that vehicle class are considered. If isGeo is True, x and y are longitude and latitude, respectively, and the network's geo-projection is used (requires pyproj). Distances are measured to the lane shape without junction extensions. Internal lanes are considered if the network was read withInternal=True.

def hasNode(self, id):
451    def hasNode(self, id):
452        return id in self._id2node
def getNode(self, id):
454    def getNode(self, id):
455        return self._id2node[id]
def getNodes(self):
457    def getNodes(self):
458        return self._nodes
def getTLS(self, tlid):
460    def getTLS(self, tlid):
461        return self._id2tls[tlid]
def getTLSSecure(self, tlid):
463    def getTLSSecure(self, tlid):
464        if tlid in self._id2tls:
465            tls = self._id2tls[tlid]
466        else:
467            tls = TLS(tlid)
468            self._id2tls[tlid] = tls
469            self._tlss.append(tls)
470        return tls
def getTrafficLights(self):
472    def getTrafficLights(self):
473        return self._tlss
def addTLS(self, tlid, inLane, outLane, linkNo):
475    def addTLS(self, tlid, inLane, outLane, linkNo):
476        tls = self.getTLSSecure(tlid)
477        tls.addConnection(inLane, outLane, linkNo)
478        return tls
def addTLSProgram(self, tlid, programID, offset, type, removeOthers):
480    def addTLSProgram(self, tlid, programID, offset, type, removeOthers):
481        tls = self.getTLSSecure(tlid)
482        program = TLSProgram(programID, offset, type)
483        if removeOthers:
484            tls.removePrograms()
485        tls.addProgram(program)
486        return program
def setFoes(self, junctionID, index, foes, prohibits):
488    def setFoes(self, junctionID, index, foes, prohibits):
489        self._id2node[junctionID].setFoes(index, foes, prohibits)
def forbids(self, possProhibitor, possProhibited):
491    def forbids(self, possProhibitor, possProhibited):
492        return possProhibitor.getFrom().getToNode().forbids(possProhibitor, possProhibited)
def getUpstreamEdges(self, edge, distance, stopOnTLS, stopOnTurnaround):
494    def getUpstreamEdges(self, edge, distance, stopOnTLS, stopOnTurnaround):
495        """return a list of lists of the form
496           [[firstEdge, pos, [edge_0, edge_1, ..., edge_k], aborted], ...]
497           where
498             firstEdge: is the upstream edge furthest away from the intersection,
499             [edge_0, ..., edge_k]: is the list of edges from the intersection upstream to firstEdge
500             pos: is the position on firstEdge with distance to the end of the input edge
501             aborted: a flag indicating whether the downstream
502                 search stopped at a TLS or a node without incoming edges before reaching the distance threshold
503        """
504        ret = []
505        seen = set()
506        toProc = []
507        toProc.append([edge, 0, []])
508        while not len(toProc) == 0:
509            ie = toProc.pop()
510            if ie[0] in seen:
511                continue
512            seen.add(ie[0])
513            if ie[1] + ie[0].getLength() >= distance:
514                ret.append(
515                    [ie[0], ie[0].getLength() + ie[1] - distance, ie[2], False])
516                continue
517            if len(ie[0]._incoming) == 0:
518                ret.append([ie[0], ie[0].getLength() + ie[1], ie[2], True])
519                continue
520            mn = []
521            stop = False
522            for ci in ie[0]._incoming:
523                if ci not in seen:
524                    prev = copy(ie[2])
525                    if stopOnTLS and ci._tls and ci != edge and not stop:
526                        ret.append([ie[0], ie[1], prev, True])
527                        stop = True
528                    elif (stopOnTurnaround and ie[0]._incoming[ci][0].getDirection() == Connection.LINKDIR_TURN and
529                          not stop):
530                        ret.append([ie[0], ie[1], prev, True])
531                        stop = True
532                    else:
533                        prev.append(ie[0])
534                        mn.append([ci, ie[0].getLength() + ie[1], prev])
535            if not stop:
536                toProc.extend(mn)
537        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):
539    def getEdgesByOrigID(self, origID):
540        if self._origIdx is None:
541            self._origIdx = defaultdict(set)
542            for the_edge in self._edges:
543                for the_lane in the_edge.getLanes():
544                    for oID in the_lane.getParam("origId", "").split():
545                        self._origIdx[oID].add(the_edge)
546        return self._origIdx[origID]
def getGeometries(self, useLanes, includeJunctions=False):
548    def getGeometries(self, useLanes, includeJunctions=False):
549        for e in self._edges:
550            if useLanes:
551                for the_lane in e.getLanes():
552                    yield the_lane.getID(), the_lane.getShape(), the_lane.getWidth()
553            else:
554                yield e.getID(), e.getShape(includeJunctions), sum([the_lane.getWidth() for the_lane in e.getLanes()])
def getBBoxXY(self):
556    def getBBoxXY(self):
557        """
558        Get the bounding box (bottom left and top right coordinates) for a net;
559        Coordinates are in X and Y (not Lat and Lon)
560
561        :return [(bottom_left_X, bottom_left_Y), (top_right_X, top_right_Y)]
562        """
563        return [(self._ranges[0][0], self._ranges[1][0]),
564                (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):
567    def getBBoxDiameter(self):
568        return math.sqrt(
569            (self._ranges[0][0] - self._ranges[0][1]) ** 2 +
570            (self._ranges[1][0] - self._ranges[1][1]) ** 2)
def hasGeoProj(self):
572    def hasGeoProj(self):
573        projString = self._location["projParameter"]
574        return projString != "!"
def getGeoProj(self):
576    def getGeoProj(self):
577        if not self.hasGeoProj() or not HAVE_PYPROJ:
578            raise RuntimeError("Network does not provide geo-projection or pyproj not installed.")
579        if self._proj is None:
580            try:
581                self._proj = pyproj.Proj(projparams=self._location["projParameter"])
582            except RuntimeError:
583                if hasattr(pyproj.datadir, 'set_data_dir'):
584                    pyproj.datadir.set_data_dir('/usr/share/proj')
585                    self._proj = pyproj.Proj(projparams=self._location["projParameter"])
586                raise
587        return self._proj
def getLocationOffset(self):
589    def getLocationOffset(self):
590        """ offset to be added after converting from geo-coordinates to UTM"""
591        return list(map(float, self._location["netOffset"].split(",")))

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

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

return xmin,ymin,xmax,ymax network coordinates

def convertLonLat2XY(self, lon, lat, rawUTM=False):
597    def convertLonLat2XY(self, lon, lat, rawUTM=False):
598        x, y = self.getGeoProj()(lon, lat)
599        if rawUTM:
600            return x, y
601        else:
602            x_off, y_off = self.getLocationOffset()
603            return x + x_off, y + y_off
def convertXY2LonLat(self, x, y, rawUTM=False):
605    def convertXY2LonLat(self, x, y, rawUTM=False):
606        if not rawUTM:
607            x_off, y_off = self.getLocationOffset()
608            x -= x_off
609            y -= y_off
610        return self.getGeoProj()(x, y, inverse=True)
def move(self, dx, dy, dz=0):
612    def move(self, dx, dy, dz=0):
613        for n in self._nodes:
614            n._coord = (n._coord[0] + dx, n._coord[1] + dy, n._coord[2] + dz)
615        for e in self._edges:
616            for _lane in e.getLanes():
617                _lane.setShape([(p[0] + dx, p[1] + dy, p[2] + dz) for p in _lane.getShape3D()])
618            e.rebuildShape()
def getInternalPath(self, conn, fastest=False):
620    def getInternalPath(self, conn, fastest=False):
621        minInternalCost = PRACTICAL_INFINITY
622        minPath = None
623        for c in conn:
624            if c.getViaLaneID() != "":
625                viaCost = 0
626                viaID = c.getViaLaneID()
627                viaPath = []
628                while viaID != "":
629                    viaLane = self.getLane(viaID)
630                    viaCost += viaLane.getLength() if not fastest else viaLane.getLength() / viaLane.getSpeed()
631                    viaID = viaLane.getOutgoing()[0].getViaLaneID()
632                    viaPath.append(viaLane.getEdge())
633                if viaCost < minInternalCost:
634                    minInternalCost = viaCost
635                    minPath = viaPath
636        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={}, length=None):
640    def getOptimalPath(self, fromEdge, toEdge, fastest=False, maxCost=PRACTICAL_INFINITY, vClass=None,
641                       reversalPenalty=0, includeFromToCost=True, withInternal=False, ignoreDirection=False,
642                       fromPos=None, toPos=None, preferences={}, length=None):
643        """
644        Finds the optimal (shortest or fastest) path for vClass from fromEdge to toEdge
645        by using using Dijkstra's algorithm.
646        It returns a pair of a tuple of edges and the cost.
647        If no path is found the first element is None.
648        The cost for the returned path is equal to the sum of all edge costs in the path,
649        including the internal connectors, if they are present in the network.
650        The path itself does not include internal edges except for the case
651        when the start or end edge are internal edges.
652        The search may be limited using the given threshold.
653        The preferences declare a mapping from the 'routingType' of each edge to divisor
654        that is applied to the computed cost of that edge (i.e. a preference of 2 reduces cost by a factor of 0.5).
655        """
656
657        if preferences:
658            if fastest:
659                def speedFunc(edge):
660                    return preferences.get(edge.getRoutingType(), 1.0) * edge.getSpeed()
661            else:
662                def speedFunc(edge):
663                    return preferences.get(edge.getRoutingType(), 1.0)
664        elif fastest:
665            def speedFunc(edge):
666                return edge.getSpeed()
667        else:
668            def speedFunc(edge):
669                return 1.0
670
671        def remainder(edge, pos):
672            if pos < 0:
673                return min(-pos, edge.getLength())
674            return max(0., edge.getLength() - pos)
675
676        def getToNormalIncoming(edge):
677            if edge.getFunction() == '':
678                return [(e, None) for e in edge.getToNode().getIncoming() if e.getFunction() == '']
679            else:
680                return []
681
682        if self._edgeSuccessorCache != (vClass, ignoreDirection):
683            self._edgeSuccessorCache = (vClass, ignoreDirection)
684            for e1 in self.getEdges():
685                e1._succCacche = tuple(chain(e1.getAllowedOutgoing(vClass).items(),
686                                       e1.getIncoming().items() if ignoreDirection else [],
687                                       getToNormalIncoming(e1) if ignoreDirection and not self.hasWalkingArea else []))
688
689        if self._edgeCostCache != (fastest, preferences):
690            self._edgeCostCache = (fastest, preferences)
691            for e1 in self.getEdges():
692                e1._costCache = e1.getLength() / speedFunc(e1)
693
694        if self.hasInternal:
695            appendix = []
696            appendixCost = 0.
697            while toEdge.getFunction() == "internal":
698                appendix = [toEdge] + appendix
699                appendixCost += toEdge._costCache
700                toEdge = list(toEdge.getIncoming().keys())[0]
701
702        def finalizeCost(cost, path):
703            if includeFromToCost:
704                # add costs for (part of) the first edge, still needs to be fixed for wrong direction travel
705                remainFrom = fromEdge.getLength() if fromPos is None else remainder(fromEdge, fromPos)
706                cost += remainFrom / speedFunc(fromEdge)
707                # remove costs for (part of) the last edge, still needs to be fixed for wrong direction travel
708                removeTo = 0. if toPos is None else remainder(toEdge, toPos)
709            else:
710                removeTo = toEdge.getLength() if len(path) > 1 else 0.
711            cost -= removeTo / speedFunc(toEdge)
712            return cost
713
714        def constructPath(dist):
715            # destination was already reached in a previous query
716            cost, pred = dist[toEdge]
717            if ((cost == PRACTICAL_INFINITY
718                 # the final node may exceed max cost but the prior node may not
719                 or (cost > maxCost and cost - toEdge._costCache > maxCost))):
720                return None, cost
721            path = [toEdge]
722            while pred is not None:
723                if self.hasInternal and withInternal:
724                    viaPath, minInternalCost = self.getInternalPath(pred.getAllowedOutgoing(vClass).get(path[-1], []),
725                                                                    fastest=fastest)
726                    if viaPath is not None:
727                        path += reversed(viaPath)
728                path.append(pred)
729                _, pred = dist[pred]
730
731            path.reverse()
732            cost = finalizeCost(cost, path)
733            assert cost >= 0
734            if self.hasInternal:
735                if appendix:
736                    return tuple(path + appendix), cost + appendixCost
737                elif ignoreDirection and self.hasWalkingArea and not withInternal:
738                    return [e for e in path if e.getFunction() == ''], cost
739            return tuple(path), cost
740
741        needLoop = (fromEdge == toEdge
742                    and fromPos is not None
743                    and toPos is not None
744                    and fromPos > toPos
745                    and not ignoreDirection)
746
747        seen = set()
748        dist = {}
749        q = []
750        if self._routingCache is not None:
751            if needLoop:
752                # use cached results from all follower edges:
753                bestCost = maxCost
754                bestPath = None
755                for e2, conn in fromEdge.getAllowedOutgoing(vClass).items():
756                    path, cost = self.getOptimalPath(e2, toEdge, fastest=fastest, maxCost=maxCost, vClass=vClass,
757                                                     reversalPenalty=reversalPenalty,
758                                                     includeFromToCost=includeFromToCost,
759                                                     withInternal=withInternal, fromPos=0, toPos=toPos,
760                                                     preferences=preferences)
761                    if path is not None and cost < bestCost:
762                        bestPath = path
763                        bestCost = cost
764                if bestPath is not None:
765                    path = [fromEdge]
766                    if self.hasInternal and withInternal:
767                        viaPath, minInternalCost = self.getInternalPath(
768                            fromEdge.getAllowedOutgoing(vClass).get(path[0], []), fastest=fastest)
769                        if viaPath is not None:
770                            path += viaPath
771                            bestCost += minInternalCost
772                    path += list(bestPath)
773                    if includeFromToCost:
774                        bestCost += remainder(fromEdge, fromPos) / speedFunc(fromEdge)
775                    return tuple(path), bestCost
776                else:
777                    return None, PRACTICAL_INFINITY
778
779            if self._routingCacheOD is not None and (fromEdge, toEdge, fastest, vClass) in self._routingCacheOD:
780                cost, path = self._routingCacheOD[(fromEdge, toEdge, fastest, vClass)]
781                if cost == PRACTICAL_INFINITY:
782                    return None, cost
783                cost = finalizeCost(cost, path)
784                if cost > maxCost:
785                    return None, cost
786                else:
787                    return tuple(path), cost
788
789            dist = self._routingCache(fromEdge, fastest, vClass, ignoreDirection, reversalPenalty,
790                                      tuple(preferences.items()))
791            if toEdge in dist:
792                return constructPath(dist)
793            else:
794                # initialize heap from previous query
795                q = []
796                frontier = set(dist.keys())
797                for cost, prev in dist.values():
798                    frontier.discard(prev)
799                for e in frontier:
800                    cost, prev = dist[e]
801                    heapq.heappush(q, (cost, e, prev))
802        elif needLoop:
803            # start search on successors of fromEdge
804            for e2, conn in fromEdge.getAllowedOutgoing(vClass).items():
805                q.append((e2._costCache, e2, fromEdge))
806
807        if len(dist) == 0:
808            dist[fromEdge] = (0., None)
809            if not needLoop:
810                q.append((0., fromEdge, None))
811
812        while q:
813            cost, e1, prev = heapq.heappop(q)
814            if e1 == toEdge:
815                return constructPath(dist)
816            if cost > maxCost:
817                return None, cost
818            seen.add(e1)
819            bidi = e1._bidi  # attribute access is faster than method access
820            for e2, conn in e1._succCacche:
821                if e2 not in seen:
822                    newCost = cost + e2._costCache
823                    #  print(cost, newCost, e2.getID(), speedFunc(e2))
824                    if e2 == bidi:
825                        newCost += reversalPenalty
826                    if self.hasInternal and conn is not None:
827                        viaPath, minInternalCost = self.getInternalPath(conn, fastest=fastest)
828                        if viaPath is not None:
829                            newCost += minInternalCost
830                    if e2 not in dist or newCost < dist[e2][0]:
831                        dist[e2] = (newCost, e1)
832                        heapq.heappush(q, (newCost, e2, e1))
833        return None, PRACTICAL_INFINITY

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={}):
835    def getShortestPath(self, fromEdge, toEdge, maxCost=PRACTICAL_INFINITY, vClass=None, reversalPenalty=0,
836                        includeFromToCost=True, withInternal=False, ignoreDirection=False,
837                        fromPos=None, toPos=None, preferences={}):
838        """
839        Finds the shortest path from fromEdge to toEdge respecting vClass, using Dijkstra's algorithm.
840        It returns a pair of a tuple of edges and the cost. If no path is found the first element is None.
841        The cost for the returned path is equal to the sum of all edge lengths in the path,
842        including the internal connectors, if they are present in the network.
843        The path itself does not include internal edges except for the case
844        when the start or end edge are internal edges.
845        The search may be limited using the given threshold.
846        The preferences declare a mapping from the 'routingType' of each edge to divisor
847        that is applied to the lenght of that edge
848        (i.e. a preference of 2 reduces reduces effective length by a factor of 0.5).
849        """
850
851        return self.getOptimalPath(fromEdge, toEdge, False, maxCost, vClass, reversalPenalty,
852                                   includeFromToCost, withInternal, ignoreDirection, fromPos, toPos,
853                                   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={}):
855    def getFastestPath(self, fromEdge, toEdge, maxCost=PRACTICAL_INFINITY, vClass=None, reversalPenalty=0,
856                       includeFromToCost=True, withInternal=False, ignoreDirection=False,
857                       fromPos=None, toPos=None, preferences={}):
858        """
859        Finds the fastest path from fromEdge to toEdge respecting vClass, using Dijkstra's algorithm.
860        It returns a pair of a tuple of edges and the cost. If no path is found the first element is None.
861        The cost for the returned path is equal to the sum of all edge costs in the path,
862        including the internal connectors, if they are present in the network.
863        The path itself does not include internal edges except for the case
864        when the start or end edge are internal edges.
865        The search may be limited using the given threshold.
866        The preferences declare a mapping from the 'routingType' of each edge to divisor
867        that is applied to the computed traveltime of that edge
868        (i.e. a preference of 2 reduces effective traveltime by a factor of 0.5).
869        """
870
871        return self.getOptimalPath(fromEdge, toEdge, True, maxCost, vClass, reversalPenalty,
872                                   includeFromToCost, withInternal, ignoreDirection, fromPos, toPos,
873                                   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):
875    def getReachable(self, source, vclass=None, useIncoming=False, cache=None):
876        if vclass is not None and not source.allows(vclass):
877            raise RuntimeError("'{}' does not allow {}".format(source.getID(), vclass))
878        fringe = [source]
879        found = set()
880        found.add(source)
881        while len(fringe) > 0:
882            new_fringe = []
883            for e in fringe:
884                if vclass == "pedestrian":
885                    cands = chain(chain(*e.getIncoming().values()), chain(*e.getOutgoing().values()))
886                else:
887                    cands = chain(*(e.getIncoming().values() if useIncoming else e.getOutgoing().values()))
888                # print("\n".join(map(str, list(cands))))
889                for conn in cands:
890                    if vclass is None or (
891                            conn.getFromLane().allows(vclass)
892                            and conn.getToLane().allows(vclass)):
893                        for reachable in [conn.getTo(), conn.getFrom()]:
894                            if reachable not in found:
895                                # print("added %s via %s" % (reachable, conn))
896                                if cache and reachable in cache:
897                                    found.update(cache[reachable])
898                                else:
899                                    found.add(reachable)
900                                    new_fringe.append(reachable)
901            fringe = new_fringe
902        if cache is not None:
903            cache[source] = tuple(found)
904        return found
class NetReader(xml.sax.handler.ContentHandler):
 907class NetReader(handler.ContentHandler):
 908
 909    """Reads a network, storing the edge geometries, lane numbers and max. speeds"""
 910
 911    def __init__(self, **others):
 912        self._net = others.get('net', Net())
 913        self._currentEdge = None
 914        self._currentNode = None
 915        self._currentConnection = None
 916        self._currentLane = None
 917        self._crossingID2edgeIDs = {}
 918        self._withPhases = others.get('withPrograms', False)
 919        self._latestProgram = others.get('withLatestPrograms', False)
 920        if self._latestProgram:
 921            self._withPhases = True
 922        self._withConnections = others.get('withConnections', True)
 923        self._withFoes = others.get('withFoes', True)
 924        self._withPedestrianConnections = others.get('withPedestrianConnections', False)
 925        self._withMacroConnectors = others.get('withMacroConnectors', False)
 926        self._withInternal = others.get('withInternal', self._withPedestrianConnections)
 927        if self._withPedestrianConnections and not self._withInternal:
 928            sys.stderr.write("Warning: Option withPedestrianConnections requires withInternal\n")
 929            self._withInternal = True
 930        self._bidiEdgeIDs = {}
 931
 932    def startElement(self, name, attrs):
 933        if name == 'net':
 934            parts = attrs["version"].split('.', 1)
 935            self._net._version = (int(parts[0]), float(parts[1]))
 936        elif name == 'location':
 937            self._net.setLocation(attrs["netOffset"], attrs["convBoundary"], attrs[
 938                                  "origBoundary"], attrs["projParameter"])
 939        elif name == 'type':
 940            self._net._edgeTypes[attrs['id']] = EdgeType(attrs['id'], attrs.get('allow'), attrs.get('disallow'))
 941        elif name == 'edge':
 942            function = attrs.get('function', '')
 943            if (function == ''
 944                    or (self._withInternal and function in ['internal', 'crossing', 'walkingarea'])
 945                    or (self._withMacroConnectors and function == 'connector')):
 946                prio = -1
 947                if 'priority' in attrs:
 948                    prio = int(attrs['priority'])
 949
 950                # get the  ids
 951                edgeID = attrs['id']
 952                fromNodeID = attrs.get('from', None)
 953                toNodeID = attrs.get('to', None)
 954
 955                # for internal junctions use the junction's id for from and to node
 956                if function == 'internal' or function == 'crossing' or function == 'walkingarea':
 957                    fromNodeID = toNodeID = edgeID[1:edgeID.rfind('_')]
 958
 959                # remember edges crossed by pedestrians to link them later to the crossing objects
 960                if function == 'crossing':
 961                    self._crossingID2edgeIDs[edgeID] = attrs.get('crossingEdges').split(' ')
 962
 963                self._currentEdge = self._net.addEdge(edgeID, fromNodeID, toNodeID, prio, function,
 964                                                      attrs.get('name', ''), attrs.get('type', ''),
 965                                                      attrs.get('routingType', ''))
 966
 967                self._currentEdge.setRawShape(convertShape(attrs.get('shape', '')))
 968
 969                bidi = attrs.get('bidi', '')
 970                if bidi:
 971                    self._bidiEdgeIDs[edgeID] = bidi
 972            else:
 973                if function in ['crossing', 'walkingarea']:
 974                    self._net._crossings_and_walkingAreas.add(attrs['id'])
 975                elif function == 'connector':
 976                    self._net._macroConnectors.add(attrs['id'])
 977                self._currentEdge = None
 978        elif name == 'lane' and self._currentEdge is not None:
 979            self._currentLane = self._net.addLane(
 980                self._currentEdge,
 981                float(attrs['speed']),
 982                float(attrs['length']),
 983                float(attrs.get('width', 3.2)),
 984                attrs.get('allow'),
 985                attrs.get('disallow'),
 986                attrs.get('acceleration') == "1")
 987            self._currentLane.setShape(convertShape(attrs.get('shape', '')))
 988        elif name == 'neigh' and self._currentLane is not None:
 989            self._currentLane.setNeigh(attrs['lane'])
 990        elif name == 'junction':
 991            if attrs['id'][0] != ':':
 992                intLanes = None
 993                if self._withInternal:
 994                    intLanes = attrs["intLanes"].split(" ")
 995                self._currentNode = self._net.addNode(attrs['id'], attrs['type'],
 996                                                      tuple(
 997                                                          map(float, [attrs['x'], attrs['y'],
 998                                                                      attrs['z'] if 'z' in attrs else '0'])),
 999                                                      attrs['incLanes'].split(" "), intLanes)
1000                self._currentNode.setShape(
1001                    convertShape(attrs.get('shape', '')))
1002                if 'fringe' in attrs:
1003                    self._currentNode._fringe = attrs['fringe']
1004
1005        elif name == 'succ' and self._withConnections:  # deprecated
1006            if attrs['edge'][0] != ':':
1007                self._currentEdge = self._net.getEdge(attrs['edge'])
1008                self._currentLane = attrs['lane']
1009                self._currentLane = int(
1010                    self._currentLane[self._currentLane.rfind('_') + 1:])
1011            else:
1012                self._currentEdge = None
1013        elif name == 'succlane' and self._withConnections:  # deprecated
1014            lid = attrs['lane']
1015            if lid[0] != ':' and lid != "SUMO_NO_DESTINATION" and self._currentEdge:
1016                connected = self._net.getEdge(lid[:lid.rfind('_')])
1017                tolane = int(lid[lid.rfind('_') + 1:])
1018                if 'tl' in attrs and attrs['tl'] != "":
1019                    tl = attrs['tl']
1020                    tllink = int(attrs['linkIdx'])
1021                    tlid = attrs['tl']
1022                    toEdge = self._net.getEdge(lid[:lid.rfind('_')])
1023                    tolane2 = toEdge._lanes[tolane]
1024                    tls = self._net.addTLS(
1025                        tlid, self._currentEdge._lanes[self._currentLane], tolane2, tllink)
1026                    self._currentEdge.setTLS(tls)
1027                else:
1028                    tl = ""
1029                    tllink = -1
1030                toEdge = self._net.getEdge(lid[:lid.rfind('_')])
1031                tolane = toEdge._lanes[tolane]
1032                viaLaneID = attrs['via']
1033                self._net.addConnection(self._currentEdge, connected, self._currentEdge._lanes[
1034                                        self._currentLane], tolane,
1035                                        attrs['dir'], tl, tllink, -1,
1036                                        attrs.get('allow'), attrs.get('disallow'), attrs['state'], viaLaneID)
1037        elif name == 'connection' and self._withConnections and (attrs['from'][0] != ":" or self._withInternal):
1038            fromEdgeID = attrs['from']
1039            toEdgeID = attrs['to']
1040            if ((self._withPedestrianConnections or not (fromEdgeID in self._net._crossings_and_walkingAreas or
1041                                                         toEdgeID in self._net._crossings_and_walkingAreas))
1042                and (self._withMacroConnectors or not (fromEdgeID in self._net._macroConnectors or toEdgeID in
1043                                                       self._net._macroConnectors))):
1044                fromEdge = self._net.getEdge(fromEdgeID)
1045                toEdge = self._net.getEdge(toEdgeID)
1046                fromLane = fromEdge.getLane(int(attrs['fromLane']))
1047                toLane = toEdge.getLane(int(attrs['toLane']))
1048                if 'tl' in attrs and attrs['tl'] != "":
1049                    tl = attrs['tl']
1050                    tllink = int(attrs['linkIndex'])
1051                    tllink2 = int(attrs.get('linkIndex2', -1))
1052                    tls = self._net.addTLS(tl, fromLane, toLane, tllink)
1053                    fromEdge.setTLS(tls)
1054                else:
1055                    tl = ""
1056                    tllink = -1
1057                    tllink2 = -1
1058                try:
1059                    viaLaneID = attrs['via']
1060                except KeyError:
1061                    viaLaneID = ''
1062
1063                self._currentConnection = self._net.addConnection(
1064                    fromEdge, toEdge, fromLane, toLane, attrs['dir'], tl,
1065                    tllink, tllink2, attrs.get('allow'), attrs.get('disallow'), attrs['state'], viaLaneID)
1066
1067        # 'row-logic' is deprecated!!!
1068        elif self._withFoes and name == 'ROWLogic':
1069            self._currentNode = attrs['id']
1070        elif name == 'logicitem' and self._withFoes:  # deprecated
1071            self._net.setFoes(
1072                self._currentNode, int(attrs['request']), attrs["foes"], attrs["response"])
1073        elif name == 'request' and self._withFoes:
1074            self._currentNode.setFoes(
1075                int(attrs['index']), attrs["foes"], attrs["response"])
1076        # tl-logic is deprecated!!! NOTE: nevertheless, this is still used by
1077        # netconvert... (Leo)
1078        elif self._withPhases and name == 'tlLogic':
1079            self._currentProgram = self._net.addTLSProgram(
1080                attrs['id'], attrs['programID'],
1081                intIfPossible(float(attrs['offset'])), attrs['type'], self._latestProgram)
1082        elif self._withPhases and name == 'phase':
1083            self._currentProgram.addPhase(
1084                attrs['state'],
1085                intIfPossible(float(attrs['duration'])),
1086                intIfPossible(float(attrs['minDur'])) if 'minDur' in attrs else -1,
1087                intIfPossible(float(attrs['maxDur'])) if 'maxDur' in attrs else -1,
1088                list(map(int, attrs['next'].split())) if 'next' in attrs else [],
1089                attrs['name'] if 'name' in attrs else ""
1090            )
1091        elif name == 'roundabout':
1092            self._net.addRoundabout(
1093                attrs['nodes'].split(), attrs['edges'].split())
1094        elif name == 'param':
1095            if self._currentLane is not None:
1096                self._currentLane.setParam(attrs['key'], attrs['value'])
1097            elif self._currentEdge is not None:
1098                self._currentEdge.setParam(attrs['key'], attrs['value'])
1099            elif self._currentNode is not None:
1100                self._currentNode.setParam(attrs['key'], attrs['value'])
1101            elif self._currentConnection is not None:
1102                self._currentConnection.setParam(attrs['key'], attrs['value'])
1103            elif self._withPhases and self._currentProgram is not None:
1104                self._currentProgram.setParam(attrs['key'], attrs['value'])
1105
1106    def endElement(self, name):
1107        if name == 'lane':
1108            self._currentLane = None
1109        elif name == 'edge':
1110            self._currentEdge = None
1111        elif name == 'junction':
1112            self._currentNode = None
1113        elif name == 'connection':
1114            self._currentConnection = None
1115        # 'row-logic' is deprecated!!!
1116        elif name == 'ROWLogic' or name == 'row-logic':
1117            self._haveROWLogic = False
1118        # tl-logic is deprecated!!!
1119        elif self._withPhases and (name == 'tlLogic' or name == 'tl-logic'):
1120            self._currentProgram = None
1121        elif name == 'net':
1122            for edgeID, bidiID in self._bidiEdgeIDs.items():
1123                self._net.getEdge(edgeID)._bidi = self._net.getEdge(bidiID)
1124
1125    def endDocument(self):
1126        # set crossed edges of pedestrian crossings
1127        for crossingID, crossedEdgeIDs in self._crossingID2edgeIDs.items():
1128            pedCrossing = self._net.getEdge(crossingID)
1129            for crossedEdgeID in crossedEdgeIDs:
1130                pedCrossing._addCrossingEdge(self._net.getEdge(crossedEdgeID))
1131
1132    def getNet(self):
1133        return self._net

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

NetReader(**others)
911    def __init__(self, **others):
912        self._net = others.get('net', Net())
913        self._currentEdge = None
914        self._currentNode = None
915        self._currentConnection = None
916        self._currentLane = None
917        self._crossingID2edgeIDs = {}
918        self._withPhases = others.get('withPrograms', False)
919        self._latestProgram = others.get('withLatestPrograms', False)
920        if self._latestProgram:
921            self._withPhases = True
922        self._withConnections = others.get('withConnections', True)
923        self._withFoes = others.get('withFoes', True)
924        self._withPedestrianConnections = others.get('withPedestrianConnections', False)
925        self._withMacroConnectors = others.get('withMacroConnectors', False)
926        self._withInternal = others.get('withInternal', self._withPedestrianConnections)
927        if self._withPedestrianConnections and not self._withInternal:
928            sys.stderr.write("Warning: Option withPedestrianConnections requires withInternal\n")
929            self._withInternal = True
930        self._bidiEdgeIDs = {}
def startElement(self, name, attrs):
 932    def startElement(self, name, attrs):
 933        if name == 'net':
 934            parts = attrs["version"].split('.', 1)
 935            self._net._version = (int(parts[0]), float(parts[1]))
 936        elif name == 'location':
 937            self._net.setLocation(attrs["netOffset"], attrs["convBoundary"], attrs[
 938                                  "origBoundary"], attrs["projParameter"])
 939        elif name == 'type':
 940            self._net._edgeTypes[attrs['id']] = EdgeType(attrs['id'], attrs.get('allow'), attrs.get('disallow'))
 941        elif name == 'edge':
 942            function = attrs.get('function', '')
 943            if (function == ''
 944                    or (self._withInternal and function in ['internal', 'crossing', 'walkingarea'])
 945                    or (self._withMacroConnectors and function == 'connector')):
 946                prio = -1
 947                if 'priority' in attrs:
 948                    prio = int(attrs['priority'])
 949
 950                # get the  ids
 951                edgeID = attrs['id']
 952                fromNodeID = attrs.get('from', None)
 953                toNodeID = attrs.get('to', None)
 954
 955                # for internal junctions use the junction's id for from and to node
 956                if function == 'internal' or function == 'crossing' or function == 'walkingarea':
 957                    fromNodeID = toNodeID = edgeID[1:edgeID.rfind('_')]
 958
 959                # remember edges crossed by pedestrians to link them later to the crossing objects
 960                if function == 'crossing':
 961                    self._crossingID2edgeIDs[edgeID] = attrs.get('crossingEdges').split(' ')
 962
 963                self._currentEdge = self._net.addEdge(edgeID, fromNodeID, toNodeID, prio, function,
 964                                                      attrs.get('name', ''), attrs.get('type', ''),
 965                                                      attrs.get('routingType', ''))
 966
 967                self._currentEdge.setRawShape(convertShape(attrs.get('shape', '')))
 968
 969                bidi = attrs.get('bidi', '')
 970                if bidi:
 971                    self._bidiEdgeIDs[edgeID] = bidi
 972            else:
 973                if function in ['crossing', 'walkingarea']:
 974                    self._net._crossings_and_walkingAreas.add(attrs['id'])
 975                elif function == 'connector':
 976                    self._net._macroConnectors.add(attrs['id'])
 977                self._currentEdge = None
 978        elif name == 'lane' and self._currentEdge is not None:
 979            self._currentLane = self._net.addLane(
 980                self._currentEdge,
 981                float(attrs['speed']),
 982                float(attrs['length']),
 983                float(attrs.get('width', 3.2)),
 984                attrs.get('allow'),
 985                attrs.get('disallow'),
 986                attrs.get('acceleration') == "1")
 987            self._currentLane.setShape(convertShape(attrs.get('shape', '')))
 988        elif name == 'neigh' and self._currentLane is not None:
 989            self._currentLane.setNeigh(attrs['lane'])
 990        elif name == 'junction':
 991            if attrs['id'][0] != ':':
 992                intLanes = None
 993                if self._withInternal:
 994                    intLanes = attrs["intLanes"].split(" ")
 995                self._currentNode = self._net.addNode(attrs['id'], attrs['type'],
 996                                                      tuple(
 997                                                          map(float, [attrs['x'], attrs['y'],
 998                                                                      attrs['z'] if 'z' in attrs else '0'])),
 999                                                      attrs['incLanes'].split(" "), intLanes)
1000                self._currentNode.setShape(
1001                    convertShape(attrs.get('shape', '')))
1002                if 'fringe' in attrs:
1003                    self._currentNode._fringe = attrs['fringe']
1004
1005        elif name == 'succ' and self._withConnections:  # deprecated
1006            if attrs['edge'][0] != ':':
1007                self._currentEdge = self._net.getEdge(attrs['edge'])
1008                self._currentLane = attrs['lane']
1009                self._currentLane = int(
1010                    self._currentLane[self._currentLane.rfind('_') + 1:])
1011            else:
1012                self._currentEdge = None
1013        elif name == 'succlane' and self._withConnections:  # deprecated
1014            lid = attrs['lane']
1015            if lid[0] != ':' and lid != "SUMO_NO_DESTINATION" and self._currentEdge:
1016                connected = self._net.getEdge(lid[:lid.rfind('_')])
1017                tolane = int(lid[lid.rfind('_') + 1:])
1018                if 'tl' in attrs and attrs['tl'] != "":
1019                    tl = attrs['tl']
1020                    tllink = int(attrs['linkIdx'])
1021                    tlid = attrs['tl']
1022                    toEdge = self._net.getEdge(lid[:lid.rfind('_')])
1023                    tolane2 = toEdge._lanes[tolane]
1024                    tls = self._net.addTLS(
1025                        tlid, self._currentEdge._lanes[self._currentLane], tolane2, tllink)
1026                    self._currentEdge.setTLS(tls)
1027                else:
1028                    tl = ""
1029                    tllink = -1
1030                toEdge = self._net.getEdge(lid[:lid.rfind('_')])
1031                tolane = toEdge._lanes[tolane]
1032                viaLaneID = attrs['via']
1033                self._net.addConnection(self._currentEdge, connected, self._currentEdge._lanes[
1034                                        self._currentLane], tolane,
1035                                        attrs['dir'], tl, tllink, -1,
1036                                        attrs.get('allow'), attrs.get('disallow'), attrs['state'], viaLaneID)
1037        elif name == 'connection' and self._withConnections and (attrs['from'][0] != ":" or self._withInternal):
1038            fromEdgeID = attrs['from']
1039            toEdgeID = attrs['to']
1040            if ((self._withPedestrianConnections or not (fromEdgeID in self._net._crossings_and_walkingAreas or
1041                                                         toEdgeID in self._net._crossings_and_walkingAreas))
1042                and (self._withMacroConnectors or not (fromEdgeID in self._net._macroConnectors or toEdgeID in
1043                                                       self._net._macroConnectors))):
1044                fromEdge = self._net.getEdge(fromEdgeID)
1045                toEdge = self._net.getEdge(toEdgeID)
1046                fromLane = fromEdge.getLane(int(attrs['fromLane']))
1047                toLane = toEdge.getLane(int(attrs['toLane']))
1048                if 'tl' in attrs and attrs['tl'] != "":
1049                    tl = attrs['tl']
1050                    tllink = int(attrs['linkIndex'])
1051                    tllink2 = int(attrs.get('linkIndex2', -1))
1052                    tls = self._net.addTLS(tl, fromLane, toLane, tllink)
1053                    fromEdge.setTLS(tls)
1054                else:
1055                    tl = ""
1056                    tllink = -1
1057                    tllink2 = -1
1058                try:
1059                    viaLaneID = attrs['via']
1060                except KeyError:
1061                    viaLaneID = ''
1062
1063                self._currentConnection = self._net.addConnection(
1064                    fromEdge, toEdge, fromLane, toLane, attrs['dir'], tl,
1065                    tllink, tllink2, attrs.get('allow'), attrs.get('disallow'), attrs['state'], viaLaneID)
1066
1067        # 'row-logic' is deprecated!!!
1068        elif self._withFoes and name == 'ROWLogic':
1069            self._currentNode = attrs['id']
1070        elif name == 'logicitem' and self._withFoes:  # deprecated
1071            self._net.setFoes(
1072                self._currentNode, int(attrs['request']), attrs["foes"], attrs["response"])
1073        elif name == 'request' and self._withFoes:
1074            self._currentNode.setFoes(
1075                int(attrs['index']), attrs["foes"], attrs["response"])
1076        # tl-logic is deprecated!!! NOTE: nevertheless, this is still used by
1077        # netconvert... (Leo)
1078        elif self._withPhases and name == 'tlLogic':
1079            self._currentProgram = self._net.addTLSProgram(
1080                attrs['id'], attrs['programID'],
1081                intIfPossible(float(attrs['offset'])), attrs['type'], self._latestProgram)
1082        elif self._withPhases and name == 'phase':
1083            self._currentProgram.addPhase(
1084                attrs['state'],
1085                intIfPossible(float(attrs['duration'])),
1086                intIfPossible(float(attrs['minDur'])) if 'minDur' in attrs else -1,
1087                intIfPossible(float(attrs['maxDur'])) if 'maxDur' in attrs else -1,
1088                list(map(int, attrs['next'].split())) if 'next' in attrs else [],
1089                attrs['name'] if 'name' in attrs else ""
1090            )
1091        elif name == 'roundabout':
1092            self._net.addRoundabout(
1093                attrs['nodes'].split(), attrs['edges'].split())
1094        elif name == 'param':
1095            if self._currentLane is not None:
1096                self._currentLane.setParam(attrs['key'], attrs['value'])
1097            elif self._currentEdge is not None:
1098                self._currentEdge.setParam(attrs['key'], attrs['value'])
1099            elif self._currentNode is not None:
1100                self._currentNode.setParam(attrs['key'], attrs['value'])
1101            elif self._currentConnection is not None:
1102                self._currentConnection.setParam(attrs['key'], attrs['value'])
1103            elif self._withPhases and self._currentProgram is not None:
1104                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):
1106    def endElement(self, name):
1107        if name == 'lane':
1108            self._currentLane = None
1109        elif name == 'edge':
1110            self._currentEdge = None
1111        elif name == 'junction':
1112            self._currentNode = None
1113        elif name == 'connection':
1114            self._currentConnection = None
1115        # 'row-logic' is deprecated!!!
1116        elif name == 'ROWLogic' or name == 'row-logic':
1117            self._haveROWLogic = False
1118        # tl-logic is deprecated!!!
1119        elif self._withPhases and (name == 'tlLogic' or name == 'tl-logic'):
1120            self._currentProgram = None
1121        elif name == 'net':
1122            for edgeID, bidiID in self._bidiEdgeIDs.items():
1123                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):
1125    def endDocument(self):
1126        # set crossed edges of pedestrian crossings
1127        for crossingID, crossedEdgeIDs in self._crossingID2edgeIDs.items():
1128            pedCrossing = self._net.getEdge(crossingID)
1129            for crossedEdgeID in crossedEdgeIDs:
1130                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):
1132    def getNet(self):
1133        return self._net
def convertShape(shapeString):
1136def convertShape(shapeString):
1137    """ Convert xml shape string into float tuples.
1138
1139    This method converts the 2d or 3d shape string from SUMO's xml file
1140    into a list containing 3d float-tuples. Non existent z coordinates default
1141    to zero. If shapeString is empty, an empty list will be returned.
1142    """
1143
1144    cshape = []
1145    for pointString in shapeString.split():
1146        p = [float(e) for e in pointString.split(",")]
1147        if len(p) == 2:
1148            cshape.append((p[0], p[1], 0.))
1149        elif len(p) == 3:
1150            cshape.append(tuple(p))
1151        else:
1152            raise ValueError(
1153                'Invalid shape point "%s", should be either 2d or 3d' % pointString)
1154    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):
1157def lane2edge(laneID):
1158    return laneID[:laneID.rfind("_")]
def lane2index(laneID):
1161def lane2index(laneID):
1162    return int(laneID[laneID.rfind("_") + 1:])
def hasInternal(filename):
1165def hasInternal(filename):
1166    with openz(filename) as f:
1167        for line in f:
1168            if '<edge id="' in line:
1169                if '<edge id=":' in line:
1170                    return True
1171                else:
1172                    return False
1173    return False
def readNet(filename, **others):
1176def readNet(filename, **others):
1177    """ load a .net.xml file
1178    The following named options are supported:
1179
1180        'net' : initialize data structures with an existing net object (default Net())
1181        'withPrograms' : import all traffic light programs (default False)
1182        'withLatestPrograms' : import only the last program for each traffic light.
1183                               This is the program that would be active in sumo by default.
1184                               (default False)
1185        'withConnections' : import all connections (default True)
1186        'withFoes' : import right-of-way information (default True)
1187        'withInternal' : import internal edges and lanes (default False)
1188        'withPedestrianConnections' : import connections between sidewalks, crossings (default False)
1189        'lxml' : set to False to use the xml.sax parser instead of the lxml parser
1190        'maxcache' : set maximum cache size (default 1000) or 0 to disable optimal route caching
1191    """
1192    netreader = NetReader(**others)
1193    try:
1194        source = gzip.open(filename)
1195        source.read(10)
1196        source.seek(0)
1197    except IOError:
1198        source = filename
1199    if HAVE_LXML and others.get("lxml", True):
1200        if isinstance(source, pathlib.Path):
1201            source = str(source)
1202        for event, v in lxml.etree.iterparse(source, events=("start", "end")):
1203            if event == "start":
1204                netreader.startElement(v.tag, v.attrib)
1205            elif event == "end":
1206                netreader.endElement(v.tag)
1207            v.clear()  # reduce memory footprint
1208    else:
1209        parse(source, netreader)
1210    net = netreader.getNet()
1211    maxcache = others.get('maxcache', 1000)
1212    if HAVE_LRU_CACHE and maxcache is not None and maxcache > 0:
1213        net.initRoutingCache(maxcache)
1214    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