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

Traffic Light Signal for a sumo network

TLS(id)
70    def __init__(self, id):
71        self._id = id
72        self._connections = []
73        self._maxConnectionNo = -1
74        self._programs = {}
def addConnection(self, inLane, outLane, linkNo):
76    def addConnection(self, inLane, outLane, linkNo):
77        self._connections.append([inLane, outLane, linkNo])
78        if linkNo > self._maxConnectionNo:
79            self._maxConnectionNo = linkNo
def getConnections(self):
81    def getConnections(self):
82        return self._connections
def getID(self):
84    def getID(self):
85        return self._id
def getEdges(self):
95    def getEdges(self):
96        edges = set()
97        for c in self._connections:
98            edges.add(c[0].getEdge())
99        return edges
def addProgram(self, program):
101    def addProgram(self, program):
102        self._programs[program._id] = program
def removePrograms(self):
104    def removePrograms(self):
105        self._programs.clear()
def toXML(self):
107    def toXML(self):
108        ret = ""
109        for p in self._programs:
110            ret = ret + self._programs[p].toXML(self._id)
111        return ret
def getPrograms(self):
113    def getPrograms(self):
114        return self._programs
class Phase:
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))
Phase( duration, state, minDur=None, maxDur=None, next=(), name='', earlyTarget='')
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

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

The whole sumo network.

hasInternal
hasWalkingArea
def initRoutingCache(self, maxsize=1000):
244    def initRoutingCache(self, maxsize=1000):
245        self._routingCache = lru_cache(maxsize=maxsize)(lambda fromEdge, fromPos, fastest, vClass, ignoreDirection, reversalPenalty, preferences: {})  # noqa
def getVersion(self):
247    def getVersion(self):
248        return self._version
def getEdgeType(self, typeID):
250    def getEdgeType(self, typeID):
251        return self._edgeTypes[typeID]
def setLocation(self, netOffset, convBoundary, origBoundary, projParameter):
253    def setLocation(self, netOffset, convBoundary, origBoundary, projParameter):
254        self._location["netOffset"] = netOffset
255        self._location["convBoundary"] = convBoundary
256        self._location["origBoundary"] = origBoundary
257        self._location["projParameter"] = projParameter
def loadSelection(self, selectionFile):
259    def loadSelection(self, selectionFile):
260        self.resetSelection()
261        with io.open(selectionFile, "r", encoding="utf-8") as f:
262            for line in f:
263                line = line.strip()
264                if line.startswith("edge:"):
265                    edgeID = line[5:]
266                    if edgeID in self._id2edge:
267                        self.getEdge(edgeID).select()
268                elif line.startswith("junction:"):
269                    nodeID = line[9:]
270                    if nodeID in self._id2node:
271                        self.getNode(nodeID).select()
def resetSelection(self):
273    def resetSelection(self):
274        for n in self._nodes:
275            n.select(False)
276        for e in self._edges:
277            e.select(False)
def addNode(self, id, type=None, coord=None, incLanes=None, intLanes=None):
279    def addNode(self, id, type=None, coord=None, incLanes=None, intLanes=None):
280        if id is None:
281            return None
282        if id not in self._id2node:
283            n = node.Node(id, type, coord, incLanes, intLanes)
284            self._nodes.append(n)
285            self._id2node[id] = n
286        self.setAdditionalNodeInfo(
287            self._id2node[id], type, coord, incLanes, intLanes)
288        return self._id2node[id]
def setAdditionalNodeInfo(self, node, type, coord, incLanes, intLanes=None):
290    def setAdditionalNodeInfo(self, node, type, coord, incLanes, intLanes=None):
291        if coord is not None and node._coord is None:
292            node._coord = coord
293            self._ranges[0][0] = min(self._ranges[0][0], coord[0])
294            self._ranges[0][1] = max(self._ranges[0][1], coord[0])
295            self._ranges[1][0] = min(self._ranges[1][0], coord[1])
296            self._ranges[1][1] = max(self._ranges[1][1], coord[1])
297        if incLanes is not None and node._incLanes is None:
298            node._incLanes = incLanes
299        if intLanes is not None and node._intLanes is None:
300            node._intLanes = intLanes
301        if type is not None and node._type is None:
302            node._type = type
def addEdge( self, id, fromID, toID, prio, function, name, edgeType='', routingType=''):
304    def addEdge(self, id, fromID, toID, prio, function, name, edgeType='', routingType=''):
305        if id not in self._id2edge:
306            fromN = self.addNode(fromID)
307            toN = self.addNode(toID)
308            e = edge.Edge(id, fromN, toN, prio, function, name, edgeType, routingType)
309            self._edges.append(e)
310            self._id2edge[id] = e
311            if function:
312                self.hasInternal = True
313                if function == "walkingarea":
314                    self.hasWalkingArea = True
315        return self._id2edge[id]
def addLane( self, edge, speed, length, width, allow=None, disallow=None, acceleration=False):
317    def addLane(self, edge, speed, length, width, allow=None, disallow=None, acceleration=False):
318        return lane.Lane(edge, speed, length, width, allow, disallow, acceleration)
def addRoundabout(self, nodes, edges=None):
320    def addRoundabout(self, nodes, edges=None):
321        r = roundabout.Roundabout(nodes, edges)
322        self._roundabouts.append(r)
323        return r
def addConnection( self, fromEdge, toEdge, fromlane, tolane, direction, tls, tllink, tllink2, allow, disallow, state, viaLaneID=None):
325    def addConnection(self, fromEdge, toEdge, fromlane, tolane, direction,
326                      tls, tllink, tllink2, allow, disallow, state, viaLaneID=None):
327        conn = connection.Connection(
328            fromEdge, toEdge, fromlane, tolane, direction,
329            tls, tllink, tllink2, allow, disallow, state, viaLaneID)
330        fromEdge.addOutgoing(conn)
331        fromlane.addOutgoing(conn)
332        toEdge._addIncoming(conn)
333        if viaLaneID:
334            try:
335                # internal lanes are only available when building with option withInternal=True
336                viaLane = self.getLane(viaLaneID)
337                viaEdge = viaLane.getEdge()
338                viaEdge._addIncoming(connection.Connection(
339                    fromEdge, viaEdge, fromlane, viaLane, direction, tls,
340                    tllink, tllink2, allow, disallow, state, ''))
341            except Exception:
342                pass
343        return conn
def getEdges(self, withInternal=True):
345    def getEdges(self, withInternal=True):
346        if not withInternal:
347            return [e for e in self._edges if e.getFunction() == '']
348        else:
349            return self._edges
def getRoundabouts(self):
351    def getRoundabouts(self):
352        return self._roundabouts
def hasEdge(self, id):
354    def hasEdge(self, id):
355        return id in self._id2edge
def getEdge(self, id):
357    def getEdge(self, id):
358        return self._id2edge[id]
def getLane(self, laneID):
360    def getLane(self, laneID):
361        edge_id, lane_index = laneID.rsplit("_", 1)
362        return self.getEdge(edge_id).getLane(int(lane_index))
def getNeighboringEdges(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
373    def getNeighboringEdges(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
374        edges = []
375        try:
376            if self._rtreeEdges is None:
377                self._rtreeEdges = self._initRTree(self._edges, includeJunctions)
378            for i in self._rtreeEdges.intersection((x - r, y - r, x + r, y + r)):
379                e = self._edges[i]
380                d = sumolib.geomhelper.distancePointToPolygon(
381                    (x, y), e.getShape(includeJunctions))
382                if d < r:
383                    edges.append((e, d))
384        except ImportError:
385            if not allowFallback:
386                raise
387            warnings.warn("Module 'rtree' not available. Using brute-force fallback.")
388            for the_edge in self._edges:
389                d = sumolib.geomhelper.distancePointToPolygon((x, y), the_edge.getShape(includeJunctions))
390                if d < r:
391                    edges.append((the_edge, d))
392        return edges
def getNeighboringLanes(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
394    def getNeighboringLanes(self, x, y, r=0.1, includeJunctions=True, allowFallback=True):
395        lanes = []
396        try:
397            if self._rtreeLanes is None:
398                for the_edge in self._edges:
399                    self._allLanes += the_edge.getLanes()
400                self._rtreeLanes = self._initRTree(self._allLanes, includeJunctions)
401            for i in self._rtreeLanes.intersection((x - r, y - r, x + r, y + r)):
402                the_lane = self._allLanes[i]
403                d = sumolib.geomhelper.distancePointToPolygon((x, y), the_lane.getShape(includeJunctions))
404                if d < r:
405                    lanes.append((the_lane, d))
406        except ImportError:
407            if not allowFallback:
408                raise
409            warnings.warn("Module 'rtree' not available. Using brute-force fallback.")
410            for the_edge in self._edges:
411                for the_lane in the_edge.getLanes():
412                    d = sumolib.geomhelper.distancePointToPolygon((x, y), the_lane.getShape(includeJunctions))
413                    if d < r:
414                        lanes.append((the_lane, d))
415        return lanes
def hasNode(self, id):
417    def hasNode(self, id):
418        return id in self._id2node
def getNode(self, id):
420    def getNode(self, id):
421        return self._id2node[id]
def getNodes(self):
423    def getNodes(self):
424        return self._nodes
def getTLS(self, tlid):
426    def getTLS(self, tlid):
427        return self._id2tls[tlid]
def getTLSSecure(self, tlid):
429    def getTLSSecure(self, tlid):
430        if tlid in self._id2tls:
431            tls = self._id2tls[tlid]
432        else:
433            tls = TLS(tlid)
434            self._id2tls[tlid] = tls
435            self._tlss.append(tls)
436        return tls
def getTrafficLights(self):
438    def getTrafficLights(self):
439        return self._tlss
def addTLS(self, tlid, inLane, outLane, linkNo):
441    def addTLS(self, tlid, inLane, outLane, linkNo):
442        tls = self.getTLSSecure(tlid)
443        tls.addConnection(inLane, outLane, linkNo)
444        return tls
def addTLSProgram(self, tlid, programID, offset, type, removeOthers):
446    def addTLSProgram(self, tlid, programID, offset, type, removeOthers):
447        tls = self.getTLSSecure(tlid)
448        program = TLSProgram(programID, offset, type)
449        if removeOthers:
450            tls.removePrograms()
451        tls.addProgram(program)
452        return program
def setFoes(self, junctionID, index, foes, prohibits):
454    def setFoes(self, junctionID, index, foes, prohibits):
455        self._id2node[junctionID].setFoes(index, foes, prohibits)
def forbids(self, possProhibitor, possProhibited):
457    def forbids(self, possProhibitor, possProhibited):
458        return possProhibitor.getFrom().getToNode().forbids(possProhibitor, possProhibited)
def getUpstreamEdges(self, edge, distance, stopOnTLS, stopOnTurnaround):
460    def getUpstreamEdges(self, edge, distance, stopOnTLS, stopOnTurnaround):
461        """return a list of lists of the form
462           [[firstEdge, pos, [edge_0, edge_1, ..., edge_k], aborted], ...]
463           where
464             firstEdge: is the upstream edge furthest away from the intersection,
465             [edge_0, ..., edge_k]: is the list of edges from the intersection upstream to firstEdge
466             pos: is the position on firstEdge with distance to the end of the input edge
467             aborted: a flag indicating whether the downstream
468                 search stopped at a TLS or a node without incoming edges before reaching the distance threshold
469        """
470        ret = []
471        seen = set()
472        toProc = []
473        toProc.append([edge, 0, []])
474        while not len(toProc) == 0:
475            ie = toProc.pop()
476            if ie[0] in seen:
477                continue
478            seen.add(ie[0])
479            if ie[1] + ie[0].getLength() >= distance:
480                ret.append(
481                    [ie[0], ie[0].getLength() + ie[1] - distance, ie[2], False])
482                continue
483            if len(ie[0]._incoming) == 0:
484                ret.append([ie[0], ie[0].getLength() + ie[1], ie[2], True])
485                continue
486            mn = []
487            stop = False
488            for ci in ie[0]._incoming:
489                if ci not in seen:
490                    prev = copy(ie[2])
491                    if stopOnTLS and ci._tls and ci != edge and not stop:
492                        ret.append([ie[0], ie[1], prev, True])
493                        stop = True
494                    elif (stopOnTurnaround and ie[0]._incoming[ci][0].getDirection() == Connection.LINKDIR_TURN and
495                          not stop):
496                        ret.append([ie[0], ie[1], prev, True])
497                        stop = True
498                    else:
499                        prev.append(ie[0])
500                        mn.append([ci, ie[0].getLength() + ie[1], prev])
501            if not stop:
502                toProc.extend(mn)
503        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):
505    def getEdgesByOrigID(self, origID):
506        if self._origIdx is None:
507            self._origIdx = defaultdict(set)
508            for the_edge in self._edges:
509                for the_lane in the_edge.getLanes():
510                    for oID in the_lane.getParam("origId", "").split():
511                        self._origIdx[oID].add(the_edge)
512        return self._origIdx[origID]
def getGeometries(self, useLanes, includeJunctions=False):
514    def getGeometries(self, useLanes, includeJunctions=False):
515        for e in self._edges:
516            if useLanes:
517                for the_lane in e.getLanes():
518                    yield the_lane.getID(), the_lane.getShape(), the_lane.getWidth()
519            else:
520                yield e.getID(), e.getShape(includeJunctions), sum([the_lane.getWidth() for the_lane in e.getLanes()])
def getBBoxXY(self):
522    def getBBoxXY(self):
523        """
524        Get the bounding box (bottom left and top right coordinates) for a net;
525        Coordinates are in X and Y (not Lat and Lon)
526
527        :return [(bottom_left_X, bottom_left_Y), (top_right_X, top_right_Y)]
528        """
529        return [(self._ranges[0][0], self._ranges[1][0]),
530                (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):
533    def getBBoxDiameter(self):
534        return math.sqrt(
535            (self._ranges[0][0] - self._ranges[0][1]) ** 2 +
536            (self._ranges[1][0] - self._ranges[1][1]) ** 2)
def hasGeoProj(self):
538    def hasGeoProj(self):
539        projString = self._location["projParameter"]
540        return projString != "!"
def getGeoProj(self):
542    def getGeoProj(self):
543        if not self.hasGeoProj() or not HAVE_PYPROJ:
544            raise RuntimeError("Network does not provide geo-projection or pyproj not installed.")
545        if self._proj is None:
546            import pyproj
547            try:
548                self._proj = pyproj.Proj(projparams=self._location["projParameter"])
549            except RuntimeError:
550                if hasattr(pyproj.datadir, 'set_data_dir'):
551                    pyproj.datadir.set_data_dir('/usr/share/proj')
552                    self._proj = pyproj.Proj(projparams=self._location["projParameter"])
553                raise
554        return self._proj
def getLocationOffset(self):
556    def getLocationOffset(self):
557        """ offset to be added after converting from geo-coordinates to UTM"""
558        return list(map(float, self._location["netOffset"].split(",")))

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

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

return xmin,ymin,xmax,ymax network coordinates

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

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

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

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

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