Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/net/odConnectionsCheck.py
169673 views
1
#!/usr/bin/env python
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2007-2025 German Aerospace Center (DLR) and others.
4
# This program and the accompanying materials are made available under the
5
# terms of the Eclipse Public License 2.0 which is available at
6
# https://www.eclipse.org/legal/epl-2.0/
7
# This Source Code may also be made available under the following Secondary
8
# Licenses when the conditions for such availability set forth in the Eclipse
9
# Public License 2.0 are satisfied: GNU General Public License, version 2
10
# or later which is available at
11
# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
14
# @file odConnectionsCheck.py
15
# @author Daniel Krajzewicz
16
# @author Yun-Pang Floetteroed
17
# @author Michael Behrisch
18
# @date 2007-03-20
19
20
from __future__ import absolute_import
21
from __future__ import print_function
22
23
import os
24
import sys
25
26
from xml.sax import make_parser, handler
27
from optparse import OptionParser
28
29
pyPath = os.path.dirname(sys.argv[0])
30
sys.path.append(
31
os.path.join("..", "..", "..", "..", "extern", "sumo", "tools", "assign"))
32
from dijkstra import dijkstraPlain # noqa
33
34
# This class is used to build the nodes in the investigated network and
35
# includes the update-function for searching the k shortest paths.
36
37
38
class Vertex:
39
40
"""
41
This class is to store node attributes and the respective incoming/outgoing links.
42
"""
43
44
def __init__(self, num):
45
self.inEdges = []
46
self.outEdges = []
47
self.label = "%s" % num
48
self.sourceEdges = []
49
self.sinkEdges = []
50
51
def __repr__(self):
52
return self.label
53
54
# This class is used to store link information and estimate
55
# as well as flow and capacity for the flow computation and some parameters
56
# read from the net.
57
58
59
class Edge:
60
61
"""
62
This class is to record link attributes
63
"""
64
65
def __init__(self, label, source, target, kind="junction"):
66
self.label = label
67
self.source = source
68
self.target = target
69
self.capacity = sys.maxsize
70
self.kind = kind
71
self.maxspeed = 1.0
72
self.length = 0.0
73
self.freeflowtime = 0.0
74
self.numberlane = 0
75
self.helpacttime = self.freeflowtime
76
77
def init(self, speed, length, laneNumber):
78
self.maxspeed = float(speed)
79
self.length = float(length)
80
self.numberlane = float(laneNumber)
81
if self.source.label == self.target.label:
82
self.freeflowtime = 0.0
83
else:
84
self.freeflowtime = self.length / self.maxspeed
85
self.helpacttime = self.freeflowtime
86
87
def __repr__(self):
88
str(self.capacity)
89
if self.capacity == sys.maxsize or self.connection != 0:
90
pass
91
return "%s_<%s|%s|%s>" % (self.label, self.kind, self.source, self.target)
92
93
94
class Net:
95
96
def __init__(self):
97
self._vertices = []
98
self._edges = {}
99
self._endVertices = []
100
self._startVertices = []
101
self.separateZones = []
102
self.sinkEdges = []
103
self.sourceEdges = []
104
105
def newVertex(self):
106
v = Vertex(len(self._vertices))
107
self._vertices.append(v)
108
return v
109
110
def getEdge(self, edgeLabel):
111
return self._edges[edgeLabel]
112
113
def addEdge(self, edgeObj):
114
edgeObj.source.outEdges.append(edgeObj)
115
edgeObj.target.inEdges.append(edgeObj)
116
if edgeObj.kind == "real":
117
self._edges[edgeObj.label] = edgeObj
118
119
def addIsolatedRealEdge(self, edgeLabel):
120
self.addEdge(Edge(edgeLabel, self.newVertex(), self.newVertex(),
121
"real"))
122
123
def getTargets(self, separateZones):
124
target = set()
125
for end in self._endVertices:
126
if end not in separateZones:
127
target.add(end)
128
return target
129
130
def checkRoute(self, startVertex, endVertex, totalCounts, subCounts, P, odPairSet, matrixEntry, skipList):
131
totalCounts += 1
132
vertex = endVertex
133
if startVertex.label not in skipList and endVertex.label not in skipList:
134
if vertex not in P:
135
subCounts += 1
136
odPairSet.append(
137
(startVertex.label, endVertex.label, matrixEntry))
138
if options.verbose:
139
print("no connection to", endVertex.label)
140
141
return totalCounts, subCounts, odPairSet
142
143
# The class is for parsing the XML input file (network file). The data
144
# parsed is written into the net.
145
146
147
class NetworkReader(handler.ContentHandler):
148
149
def __init__(self, net):
150
self._net = net
151
self._edge = ''
152
self._maxSpeed = 0
153
self._laneNumber = 0
154
self._length = 0
155
self._edgeObj = None
156
self._chars = ''
157
self._counter = 0
158
self._turnlink = None
159
160
def startElement(self, name, attrs):
161
self._chars = ''
162
if name == 'edge' and ('function' not in attrs or attrs['function'] != 'internal'):
163
self._edge = attrs['id']
164
self._net.addIsolatedRealEdge(self._edge)
165
self._edgeObj = self._net.getEdge(self._edge)
166
self._edgeObj.source.label = attrs['from']
167
self._edgeObj.target.label = attrs['to']
168
self._maxSpeed = 0
169
self._laneNumber = 0
170
self._length = 0
171
elif name == 'succ':
172
self._edge = attrs['edge']
173
if self._edge[0] != ':':
174
self._edgeObj = self._net.getEdge(self._edge)
175
elif name == 'succlane' and self._edge != "":
176
lane = attrs['lane']
177
if lane != "SUMO_NO_DESTINATION":
178
toEdge = self._net.getEdge(lane[:lane.rfind('_')])
179
newEdge = Edge(
180
self._edge + "_" + lane[:lane.rfind('_')], self._edgeObj.target, toEdge.source)
181
self._net.addEdge(newEdge)
182
self._edgeObj.finalizer = lane[:lane.rfind('_')]
183
elif name == 'lane' and self._edge != '':
184
self._maxSpeed = max(self._maxSpeed, float(attrs['speed']))
185
self._laneNumber = self._laneNumber + 1
186
self._length = float(attrs['length'])
187
188
def characters(self, content):
189
self._chars += content
190
191
def endElement(self, name):
192
if name == 'edge' and self._edge != '':
193
self._edgeObj.init(self._maxSpeed, self._length, self._laneNumber)
194
self._edge = ''
195
196
# The class is for parsing the XML input file (districts). The data parsed
197
# is written into the net.
198
199
200
class DistrictsReader(handler.ContentHandler):
201
202
def __init__(self, net):
203
self._net = net
204
self._StartDTIn = None
205
self._StartDTOut = None
206
self.index = 100
207
208
def startElement(self, name, attrs):
209
if name == 'taz':
210
self._StartDTIn = self._net.newVertex()
211
self._StartDTIn.label = attrs['id']
212
self._StartDTOut = self._net.newVertex()
213
self._StartDTOut.label = self._StartDTIn.label
214
self._net._startVertices.append(self._StartDTIn)
215
self._net._endVertices.append(self._StartDTOut)
216
elif name == 'tazSink':
217
sinklink = self._net.getEdge(attrs['id'])
218
self.index += 1
219
conlink = self._StartDTOut.label + str(self.index)
220
newEdge = Edge(conlink, sinklink.target, self._StartDTOut, "real")
221
self._net.addEdge(newEdge)
222
newEdge.weight = attrs['weight']
223
newEdge.connection = 1
224
elif name == 'tazSource':
225
sourcelink = self._net.getEdge(attrs['id'])
226
self.index += 1
227
conlink = self._StartDTIn.label + str(self.index)
228
newEdge = Edge(conlink, self._StartDTIn, sourcelink.source, "real")
229
self._net.addEdge(newEdge)
230
newEdge.weight = attrs['weight']
231
newEdge.connection = 2
232
233
234
def getMatrix(net, verbose, matrix, MatrixSum): # , mtxplfile, mtxtfile):
235
"""
236
This method is to read matrix from the given file.
237
"""
238
matrixPshort = []
239
startVertices = []
240
endVertices = []
241
Pshort_EffCells = 0
242
periodList = []
243
244
ODpairs = 0
245
origins = 0
246
CurrentMatrixSum = 0.0
247
skipCount = 0
248
zones = 0
249
smallDemandNum = 0
250
checkCounts = 0
251
for line in open(matrix):
252
checkCounts += 1
253
if line[0] == '$':
254
visumCode = line[1:3]
255
if visumCode != 'VM':
256
skipCount += 1
257
elif line[0] != '*' and line[0] != '$':
258
skipCount += 1
259
if skipCount == 2:
260
for elem in line.split():
261
periodList.append(float(elem))
262
elif skipCount > 3:
263
if zones == 0:
264
for elem in line.split():
265
zones = int(elem)
266
elif len(startVertices) < zones:
267
for elem in line.split():
268
if len(elem) > 0:
269
for startVertex in net._startVertices:
270
if startVertex.label == elem:
271
startVertices.append(startVertex)
272
for endVertex in net._endVertices:
273
if endVertex.label == elem:
274
endVertices.append(endVertex)
275
origins = len(startVertices)
276
len(endVertices)
277
elif len(startVertices) == zones:
278
if ODpairs % origins == 0:
279
matrixPshort.append([])
280
for item in line.split():
281
matrixPshort[-1].append(float(item))
282
ODpairs += 1
283
MatrixSum += float(item)
284
CurrentMatrixSum += float(item)
285
if float(item) > 0.:
286
Pshort_EffCells += 1
287
if float(item) < 1. and float(item) > 0.:
288
smallDemandNum += 1
289
begintime = int(periodList[0])
290
int(periodList[1]) - begintime
291
float(smallDemandNum) / float(Pshort_EffCells)
292
293
return matrixPshort, startVertices, endVertices
294
295
296
def main():
297
parser = make_parser()
298
os.getcwd()
299
dataDir = options.datadir
300
districts = os.path.join(dataDir, options.districtfile)
301
netfile = os.path.join(dataDir, options.netfile)
302
sumDemand = 0.
303
MatrixSum = 0.
304
totalCounts = 0
305
subCounts = 0
306
separateZones = []
307
odPairSet = []
308
skipList = []
309
net = Net()
310
if options.spearatezones:
311
for item in options.spearatezones.split(','):
312
for district in net._startVertices:
313
if district.label == item:
314
separateZones.append(district)
315
316
parser.setContentHandler(NetworkReader(net))
317
parser.parse(netfile)
318
319
parser.setContentHandler(DistrictsReader(net))
320
parser.parse(districts)
321
if options.skipList:
322
for elem in options.skipList.split(','):
323
skipList.append(elem)
324
if options.mtxfile:
325
matrixPshort, startVertices, endVertices = getMatrix(
326
net, options.verbose, os.path.join(dataDir, options.mtxfile), MatrixSum)
327
else:
328
matrixPshort = None
329
startVertices = net._startVertices
330
endVertices = net._endVertices
331
332
if options.verbose:
333
print(len(net._edges), "edges read")
334
print(len(startVertices), "start vertices read")
335
print(len(endVertices), "target vertices read")
336
337
for start, startVertex in enumerate(startVertices):
338
if startVertex not in separateZones:
339
targets = net.getTargets(separateZones)
340
if options.verbose:
341
print("checking start vertex", startVertex.label)
342
D, P = dijkstraPlain(startVertex, targets)
343
344
for end, endVertex in enumerate(endVertices):
345
if startVertex.label != endVertex.label and endVertex not in separateZones:
346
if matrixPshort:
347
entry = matrixPshort[start][end]
348
else:
349
entry = 1
350
if entry > 0.:
351
totalCounts, subCounts, odPairSet = net.checkRoute(
352
startVertex, endVertex, totalCounts, subCounts, P, odPairSet, entry, skipList)
353
else:
354
for endVertex in separateZones:
355
if startVertex.label != endVertex.label:
356
totalCounts, subCounts, odPairSet = net.checkRoute(
357
startVertex, endVertex, totalCounts, subCounts, P, odPairSet, matrixPshort[start][end],
358
skipList)
359
360
print('total OD connections:', totalCounts)
361
if len(odPairSet) > 0:
362
foutzones = open('absentConnections.txt', 'w')
363
for pair in odPairSet:
364
sumDemand += float(pair[2])
365
foutzones.write('from: %s to: %s; demand:%s\n' %
366
(pair[0], pair[1], pair[2]))
367
foutzones.close()
368
print(subCounts, 'connections are absent!')
369
print(sumDemand, 'vehicles are absent.')
370
else:
371
print('all connections exist! ')
372
373
374
optParser = OptionParser()
375
optParser.add_option("-r", "--data-dir", dest="datadir",
376
default=os.getcwd(), help="give the data directory path")
377
optParser.add_option("-n", "--net-file", dest="netfile",
378
help="define the net file")
379
optParser.add_option("-m", "--matrix-file", dest="mtxfile",
380
help="define the matrix file")
381
optParser.add_option("-d", "--districts-file", dest="districtfile",
382
help="define the district file")
383
optParser.add_option("-v", "--verbose", action="store_true", dest="verbose",
384
default=False, help="tell me what you are doing")
385
optParser.add_option("-i", "--separate-zones", dest="spearatezones", type='string',
386
help="define the zones which should be separated") # e.g. dist_00101,dist_00102
387
optParser.add_option("-s", "--skip-list", dest="skipList", type='string',
388
help="define the zones which will not be compared with each other") # e.g. dist_00101,dist_00102
389
optParser.add_option("-b", "--debug", action="store_true", dest="debug",
390
default=False, help="debug the program")
391
392
(options, args) = optParser.parse_args()
393
394
if not options.netfile or not options.districtfile:
395
optParser.print_help()
396
sys.exit()
397
398
main()
399
400