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