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