Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/purgatory/network.py
169674 views
1
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
2
# Copyright (C) 2007-2025 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 network.py
14
# @author Yun-Pang Floetteroed
15
# @author Daniel Krajzewicz
16
# @author Michael Behrisch
17
# @date 2007-12-25
18
19
"""
20
This script is to retrive the network data, the district data and the vehicle data, generated by SUMO,
21
from the respective XML files. Besides, the class 'Net' is also definded here.
22
"""
23
from __future__ import absolute_import
24
from __future__ import print_function
25
26
import os
27
import datetime
28
import operator
29
from xml.sax import handler
30
from elements import Predecessor, Vertex, Edge, Path, TLJunction, Signalphase
31
from dijkstra import dijkstraPlain, dijkstraBoost, dijkstra
32
import sumolib
33
34
# Net class stores the network (vertex and edge collection).
35
# Moreover, the methods for finding k shortest paths and for generating vehicular releasing times
36
# are also in this class.
37
38
39
class Net(sumolib.net.Net):
40
41
def __init__(self):
42
sumolib.net.Net.__init__(self)
43
self._startVertices = []
44
self._endVertices = []
45
self._paths = {}
46
self._detectedLinkCounts = 0.
47
self._detectedEdges = []
48
49
def addNode(self, id, type=None, coord=None, incLanes=None, intLanes=None):
50
if id not in self._id2node:
51
node = Vertex(id, coord, incLanes)
52
self._nodes.append(node)
53
self._id2node[id] = node
54
self.setAdditionalNodeInfo(self._id2node[id], type, coord, incLanes)
55
return self._id2node[id]
56
57
def addEdge(self, id, fromID, toID, prio, function, name, *others):
58
if id not in self._id2edge:
59
fromN = self.addNode(fromID)
60
toN = self.addNode(toID)
61
edge = Edge(id, fromN, toN, prio, function, name)
62
self._edges.append(edge)
63
self._id2edge[id] = edge
64
return self._id2edge[id]
65
66
def getDetectedEdges(self, datadir):
67
for line in open(os.path.join(datadir, "detectedEdges.txt")):
68
line = line.split('\n')[0]
69
if line != '':
70
edgeObj = self.getEdge(line.split(' ')[0])
71
edgeObj.detected = int(line.split(' ')[1])
72
if edgeObj not in self._detectedEdges:
73
self._detectedEdges.append(edgeObj)
74
75
def initialPathSet(self):
76
for startVertex in self._startVertices:
77
self._paths[startVertex] = {}
78
for endVertex in self._endVertices:
79
self._paths[startVertex][endVertex] = []
80
81
def cleanPathSet(self):
82
for startVertex in self._startVertices:
83
for endVertex in self._endVertices:
84
self._paths[startVertex][endVertex] = []
85
86
def addTLJunctions(self, junctionObj):
87
self._junctions[junctionObj.label] = junctionObj
88
89
def getJunction(self, junctionlabel):
90
junctionObj = None
91
if junctionlabel in self._junctions:
92
junctionObj = self._junctions[junctionlabel]
93
return junctionObj
94
95
def getstartCounts(self):
96
return len(self._startVertices)
97
98
def getendCounts(self):
99
return len(self._endVertices)
100
101
def getstartVertices(self):
102
return self._startVertices
103
104
def getendVertices(self):
105
return self._endVertices
106
107
def geteffEdgeCounts(self):
108
return len(self._edges)
109
110
def getfullEdgeCounts(self):
111
return len(self._fullEdges)
112
113
def reduce(self):
114
visited = set()
115
for link in self._edges.values():
116
if link.target in visited:
117
continue
118
sourceNodes = set([link.target])
119
targetNodes = set()
120
pendingSources = [link.target]
121
pendingTargets = []
122
stop = False
123
while not stop and (pendingSources or pendingTargets):
124
if pendingSources:
125
source = pendingSources.pop()
126
for out in source.outEdges:
127
if out.kind == "real":
128
stop = True
129
break
130
if out.target not in targetNodes:
131
targetNodes.add(out.target)
132
pendingTargets.append(out.target)
133
if not stop and pendingTargets:
134
target = pendingTargets.pop()
135
for incoming in target.inEdges:
136
if incoming.kind == "real":
137
stop = True
138
break
139
if incoming.source not in sourceNodes:
140
sourceNodes.add(incoming.source)
141
pendingSources.append(incoming.source)
142
if stop:
143
continue
144
visited.update(sourceNodes)
145
complete = True
146
for source in sourceNodes:
147
if len(source.outEdges) < len(targetNodes):
148
complete = False
149
break
150
if complete:
151
for target in targetNodes:
152
for edge in target.outEdges:
153
link.target.outEdges.add(edge)
154
edge.source = link.target
155
for source in sourceNodes:
156
for edge in source.inEdges:
157
link.target.inEdges.add(edge)
158
edge.target = link.target
159
160
def createBoostGraph(self):
161
from boost.graph import Digraph
162
self._boostGraph = Digraph()
163
for vertex in self._vertices:
164
vertex.boost = self._boostGraph.add_vertex()
165
vertex.boost.partner = vertex
166
self._boostGraph.add_vertex_property('distance')
167
self._boostGraph.add_vertex_property('predecessor')
168
for edge in self._fullEdges.values():
169
edge.boost = self._boostGraph.add_edge(
170
edge.source.boost, edge.target.boost)
171
edge.boost.weight = edge.actualtime
172
173
def checkSmallDiff(self, ODPaths, helpPath, helpPathSet, pathcost):
174
for path in ODPaths:
175
if path.edges == helpPath:
176
return False, False
177
else:
178
sameEdgeCount = 0
179
sameTravelTime = 0.0
180
for edge in path.edges:
181
if edge in helpPathSet:
182
sameEdgeCount += 1
183
sameTravelTime += edge.actualtime
184
if (abs(sameEdgeCount - len(path.edges)) / len(path.edges) <= 0.1 and
185
abs(sameTravelTime / 3600. - pathcost) <= 0.05):
186
return False, True
187
return True, False
188
189
def findNewPath(self, startVertices, endVertices, newRoutes, matrixPshort, gamma, lohse, dk):
190
"""
191
This method finds the new paths for all OD pairs.
192
The Dijkstra algorithm is applied for searching the shortest paths.
193
"""
194
newRoutes = 0
195
for start, startVertex in enumerate(startVertices):
196
endSet = set()
197
for end, endVertex in enumerate(endVertices):
198
if startVertex._id != endVertex._id and matrixPshort[start][end] > 0.:
199
endSet.add(endVertex)
200
if dk == 'boost':
201
D, P = dijkstraBoost(self._boostGraph, startVertex.boost)
202
elif dk == 'plain':
203
D, P = dijkstraPlain(startVertex, endSet)
204
elif dk == 'extend':
205
D, P = dijkstra(startVertex, endSet)
206
for end, endVertex in enumerate(endVertices):
207
if startVertex._id != endVertex._id and matrixPshort[start][end] > 0.:
208
helpPath = []
209
helpPathSet = set()
210
pathcost = D[endVertex] / 3600.
211
ODPaths = self._paths[startVertex][endVertex]
212
for path in ODPaths:
213
path.currentshortest = False
214
215
vertex = endVertex
216
while vertex != startVertex:
217
helpPath.append(P[vertex])
218
helpPathSet.add(P[vertex])
219
vertex = P[vertex]._from
220
helpPath.reverse()
221
222
newPath, smallDiffPath = self.checkSmallDiff(
223
ODPaths, helpPath, helpPathSet, pathcost)
224
225
if newPath:
226
newpath = Path(startVertex, endVertex, helpPath)
227
ODPaths.append(newpath)
228
newpath.getPathLength()
229
for route in ODPaths:
230
route.updateSumOverlap(newpath, gamma)
231
if len(ODPaths) > 1:
232
for route in ODPaths[:-1]:
233
newpath.updateSumOverlap(route, gamma)
234
if lohse:
235
newpath.pathhelpacttime = pathcost
236
else:
237
newpath.actpathtime = pathcost
238
for edge in newpath.edges:
239
newpath.freepathtime += edge.freeflowtime
240
newRoutes += 1
241
elif not smallDiffPath:
242
if lohse:
243
path.pathhelpacttime = pathcost
244
else:
245
path.actpathtime = pathcost
246
path.usedcounts += 1
247
path.currentshortest = True
248
return newRoutes
249
250
# find the k shortest paths for each OD pair. The "k" is defined by users.
251
def calcKPaths(self, verbose, kPaths, newRoutes, startVertices, endVertices, matrixPshort, gamma):
252
if verbose:
253
foutkpath = open('kpaths.xml', 'w')
254
print("""<?xml version="1.0"?>
255
<!-- generated on %s by $Id: network.py v1_3_1+0411-36956f96df [email protected] 2019-01-23 11:12:48 +0000 $ -->
256
<routes>""" % datetime.datetime.now(), file=foutkpath)
257
for start, startVertex in enumerate(startVertices):
258
for vertex in self.getNodes():
259
vertex.preds = []
260
vertex.wasUpdated = False
261
startVertex.preds.append(Predecessor(None, None, 0))
262
updatedVertices = [startVertex]
263
264
while len(updatedVertices) > 0:
265
vertex = updatedVertices.pop(0)
266
vertex.wasUpdated = False
267
for edge in vertex.getOutgoing():
268
if edge._to != startVertex and edge._to.update(kPaths, edge):
269
updatedVertices.append(edge._to)
270
271
for end, endVertex in enumerate(endVertices):
272
ODPaths = self._paths[startVertex][endVertex]
273
if startVertex._id != endVertex._id and matrixPshort[start][end] != 0.:
274
for startPred in endVertex.preds:
275
temppath = []
276
temppathcost = 0.
277
pred = startPred
278
vertex = endVertex
279
while vertex != startVertex:
280
temppath.append(pred.edge)
281
temppathcost += pred.edge.freeflowtime
282
vertex = pred.edge._from
283
pred = pred.pred
284
285
if len(ODPaths) > 0:
286
minpath = min(
287
ODPaths, key=operator.attrgetter('freepathtime'))
288
if minpath.freepathtime * 1.4 < temppathcost / 3600.:
289
break
290
temppath.reverse()
291
newpath = Path(startVertex, endVertex, temppath)
292
newpath.getPathLength()
293
ODPaths.append(newpath)
294
for route in ODPaths:
295
route.updateSumOverlap(newpath, gamma)
296
if len(ODPaths) > 1:
297
for route in ODPaths[:-1]:
298
newpath.updateSumOverlap(route, gamma)
299
newpath.freepathtime = temppathcost / 3600.
300
newpath.actpathtime = newpath.freepathtime
301
newRoutes += 1
302
if verbose:
303
foutkpath.write(' <path id="%s" source="%s" target="%s" pathcost="%s">\n' % (
304
newpath.label, newpath.source, newpath.target, newpath.actpathtime))
305
foutkpath.write(' <route>')
306
for edge in newpath.edges[1:-1]:
307
foutkpath.write('%s ' % edge.label)
308
foutkpath.write('</route>\n')
309
foutkpath.write(' </path>\n')
310
if verbose:
311
foutkpath.write('</routes>\n')
312
foutkpath.close()
313
314
return newRoutes
315
316
def printNet(self, foutnet):
317
foutnet.write(
318
'Name\t Kind\t FrNode\t ToNode\t length\t MaxSpeed\t Lanes\t CR-Curve\t EstCap.\t Free-Flow TT\t' +
319
'ratio\t Connection\n')
320
for edgeName, edgeObj in self._edges.items():
321
foutnet.write('%s\t %s\t %s\t %s\t %s\t %s\t %s\t %s\t %s\t %s\t %s\t %d\n'
322
% (edgeName, edgeObj.kind, edgeObj.source, edgeObj.target, edgeObj.length,
323
edgeObj.maxspeed, edgeObj.numberlane, edgeObj.CRcurve, edgeObj.estcapacity,
324
edgeObj.freeflowtime, edgeObj.ratio, edgeObj.connection))
325
326
327
class DistrictsReader(handler.ContentHandler):
328
329
"""The class is for parsing the XML input file (districts).
330
The data parsed is written into the net.
331
"""
332
333
def __init__(self, net):
334
self._net = net
335
self._node = None
336
self.index = 100
337
338
def startElement(self, name, attrs):
339
if name == 'taz':
340
self._node = self._net.addNode(attrs['id'])
341
self._net._startVertices.append(self._node)
342
self._net._endVertices.append(self._node)
343
elif name == 'tazSink':
344
sinklink = self._net.getEdge(attrs['id'])
345
self.index += 1
346
newEdge = self._net.addEdge(
347
self._node._id + str(self.index), sinklink._to._id, self._node._id, -1, "connector", "")
348
newEdge._length = 0.
349
newEdge.ratio = attrs['weight']
350
newEdge.connection = 1
351
elif name == 'tazSource':
352
sourcelink = self._net.getEdge(attrs['id'])
353
self.index += 1
354
newEdge = self._net.addEdge(
355
self._node._id + str(self.index), self._node._id, sourcelink._from._id, -1, "connector", "")
356
newEdge._length = 0.
357
newEdge.ratio = attrs['weight']
358
newEdge.connection = 2
359
360
# This class is for parsing the additional/updated information about
361
# singal timing plans
362
363
364
class ExtraSignalInformationReader(handler.ContentHandler):
365
366
def __init__(self, net):
367
self._net = net
368
self._junctionObj = None
369
self._junctionlabel = None
370
self._phaseObj = None
371
self._chars = ''
372
self._counter = 0
373
self._phasenoInfo = True
374
375
def startElement(self, name, attrs):
376
self._chars = ''
377
if name == 'tl-logic' or name == 'tlLogic':
378
if 'id' in attrs:
379
self._junctionObj = self._net.getJunction(attrs['id'])
380
if self._junctionObj:
381
self._junctionObj.phases = []
382
else:
383
self._junctionObj = TLJunction()
384
self._junctionObj.label = attrs['id']
385
self._net.addTLJunctions(self._junctionObj)
386
self._phasenoInfo = False
387
elif name == 'phase':
388
if 'state' in attrs:
389
self._newphase = Signalphase(
390
float(attrs['duration']), attrs['state'])
391
else:
392
self._newphase = Signalphase(
393
float(attrs['duration']), None, attrs['phase'], attrs['brake'], attrs['yellow'])
394
if self._junctionObj:
395
self._junctionObj.phases.append(self._newphase)
396
self._counter += 1
397
self._newphase.label = self._counter
398
399
def characters(self, content):
400
self._chars += content
401
402
def endElement(self, name):
403
if name == 'key':
404
self._junctionObj.label = self._chars
405
self._junctionObj = self._net.getJunction(self._junctionObj.label)
406
if self._junctionObj:
407
self._junctionObj.phases = []
408
else:
409
self._junctionObj = TLJunction()
410
self._junctionObj.label = self._junctionObj.label
411
self._net.addTLJunctions(self._junctionObj)
412
self._chars = ''
413
elif name == 'phaseno':
414
self._junctionObj.phaseNum = int(self._chars)
415
self._chars = ''
416
elif name == 'tl-logic' or name == 'tlLogic':
417
if not self._phasenoInfo:
418
self._junctionObj.phaseNum = self._counter
419
self._counter = 0
420
self._phasenoInfo = True
421
self._junctionObj.label = None
422
self._junctionObj = None
423
424
425
class DetectedFlowsReader(handler.ContentHandler):
426
427
def __init__(self, net):
428
self._net = net
429
self._edge = ''
430
self._edgeObj = None
431
self._detectorcounts = 0.
432
self._renew = False
433
self._skip = False
434
435
def startElement(self, name, attrs):
436
if name == 'edge':
437
if self._edge != '' and self._edge == attrs['id']:
438
if self._edgeObj.detectorNum < float(attrs['detectors']):
439
self._edgeObj.detectorNum = float(attrs['detectors'])
440
self.renew = True
441
elif self._edgeObj.detectorNum > float(attrs['detectors']):
442
self._skip = True
443
else:
444
self._edge = attrs['id']
445
self._edgeObj = self._net.getEdge(self._edge)
446
self._edgeObj.detectorNum = float(attrs['detectors'])
447
448
elif name == 'flows':
449
if self._renew:
450
self._newdata.label = attrs['weekday-time']
451
self._newdata.flowPger = float(attrs['passengercars'])
452
self._newdawta.flowTruck = float(attrs['truckflows'])
453
454
else:
455
if not self._skip:
456
# self._newdata = DetectedFlows(
457
# attrs['weekday-time'], float(attrs['passengercars']), float(attrs['truckflows']))
458
self._edgeObj.detecteddata[
459
self._newdata.label] = self._newdata
460
461
def endElement(self, name):
462
if name == 'edge':
463
self._renew = False
464
465