Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/ptlines2flows.py
193874 views
1
#!/usr/bin/env python
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2010-2026 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 ptlines2flows.py
15
# @author Gregor Laemmel
16
# @author Jakob Erdmann
17
# @author Michael Behrisch
18
# @date 2017-06-23
19
20
from __future__ import print_function
21
import sys
22
import subprocess
23
import collections
24
import random
25
import math
26
from io import StringIO
27
28
import sumolib
29
from sumolib.xml import quoteattr
30
from sumolib.options import ArgumentParser
31
from sumolib import geomhelper
32
33
34
def get_options(args=None):
35
ap = ArgumentParser()
36
ap.add_option("-n", "--net-file", dest="netfile", category="input", required=True,
37
help="network file")
38
ap.add_option("-l", "--ptlines-file", dest="ptlines", category="input", required=True,
39
help="public transit lines file")
40
ap.add_option("-s", "--ptstops-file", dest="ptstops", category="input", required=True,
41
help="public transit stops file")
42
ap.add_option("-o", "--output-file", dest="outfile", category="output", default="flows.rou.xml",
43
help="output flows file")
44
ap.add_option("-i", "--stopinfos-file", dest="stopinfos", category="output", default="stopinfos.xml",
45
help="file from '--stop-output'")
46
ap.add_option("-r", "--routes-file", dest="routes", category="output", default="vehroutes.xml",
47
help="file from '--vehroute-output'")
48
ap.add_option("-t", "--trips-file", dest="trips", default="trips.trips.xml", help="output trips file")
49
ap.add_option("-p", "--period", type=ap.time, default=600,
50
help="the default service period (in seconds) to use if none is specified in the ptlines file")
51
ap.add_option("--period-aerialway", type=ap.time, default=60, dest="periodAerialway",
52
help=("the default service period (in seconds) to use for aerialways "
53
"if none is specified in the ptlines file"))
54
ap.add_option("-b", "--begin", type=ap.time, default=0, help="start time")
55
ap.add_option("-e", "--end", type=ap.time, default=3600, help="end time")
56
ap.add_option("-j", "--jump-duration", type=ap.time, default=180, dest="jumpDuration",
57
help="The time to add for each missing stop (when joining opposite lines)")
58
ap.add_option("-T", "--turnaround-duration", type=ap.time, default=300,
59
help="The extra stopping time at terminal stops")
60
ap.add_option("--join", default=False, action="store_true",
61
help="Join opposite lines at the terminals")
62
ap.add_option("--join-threshold", default=100, type=float, dest='joinThreshold',
63
help="maximum distance for terminal stops when joining lines")
64
ap.add_option("--multistart", default=False, action="store_true",
65
help="Insert multiple vehicles per line at different offsets along the route to avoid warmup")
66
ap.add_option("--min-stops", type=int, default=2, help="only import lines with at least this number of stops")
67
ap.add_option("-f", "--flow-attributes", dest="flowattrs", default="", help="additional flow attributes")
68
ap.add_option("--use-osm-routes", default=False, action="store_true", dest='osmRoutes', help="use osm routes")
69
ap.add_option("--extend-to-fringe", default=False, action="store_true", dest='extendFringe',
70
help="let routes of incomplete lines start/end at the network border if the route edges are known")
71
ap.add_option("--random-begin", default=False, action="store_true", dest='randomBegin',
72
help="randomize begin times within period")
73
ap.add_option("--seed", type=int, help="random seed")
74
ap.add_option("--ignore-errors", default=False, action="store_true", dest='ignoreErrors',
75
help="ignore problems with the input data")
76
ap.add_option("--no-vtypes", default=False, action="store_true", dest='novtypes',
77
help="do not write vtypes for generated flows")
78
ap.add_option("--types", help="only export the given list of types (using OSM nomenclature)")
79
ap.add_option("--bus.parking", default=False, action="store_true", dest='busparking',
80
help="let busses clear the road while stopping")
81
ap.add_option("--vtype-prefix", default="", dest='vtypeprefix', help="prefix for vtype ids")
82
ap.add_option("-d", "--stop-duration", default=20, type=float, dest='stopduration',
83
help="Configure the minimum stopping duration")
84
ap.add_option("--stop-duration-slack", default=10, type=float, dest='stopdurationSlack',
85
help="Stopping time reserve in the schedule")
86
ap.add_option("--speedfactor.bus", default=0.95, type=float, dest='speedFactorBus',
87
help="Assumed bus relative travel speed")
88
ap.add_option("--speedfactor.tram", default=1.0, type=float, dest='speedFactorTram',
89
help="Assumed tram relative travel speed")
90
ap.add_option("-H", "--human-readable-time", dest="hrtime", default=False, action="store_true",
91
help="write times as h:m:s")
92
ap.add_option("--night", action="store_true", default=False, help="Export night service lines")
93
ap.add_option("-v", "--verbose", action="store_true", default=False, help="tell me what you are doing")
94
options = ap.parse_args(args=args)
95
96
if options.netfile is None or options.ptlines is None or options.ptstops is None:
97
sys.stderr.write("Error: net-file, ptlines-file and ptstops-file must be set\n")
98
ap.print_help()
99
sys.exit(1)
100
101
if options.begin >= options.end:
102
sys.stderr.write("Error: end time must be larger than begin time\n")
103
ap.print_help()
104
sys.exit(1)
105
106
if options.types is not None:
107
options.types = options.types.split(',')
108
109
return options
110
111
112
class PTLine:
113
def __init__(self, ref, name, completeness, missingBefore, missingAfter,
114
fromEdge, toEdge, period, color, refOrig,
115
typeID, depart, stop_ids, vias):
116
self.ref = ref
117
self.name = name
118
self.completeness = completeness
119
self.missingBefore = int(missingBefore) if missingBefore is not None else 0
120
self.missingAfter = int(missingAfter) if missingAfter is not None else 0
121
self.fromEdge = fromEdge
122
self.toEdge = toEdge
123
self.period = period
124
self.color = color
125
self.refOrig = refOrig
126
self.typeID = typeID
127
self.depart = depart
128
self.stop_ids = stop_ids
129
self.vias = vias
130
# stop indices that need special handling
131
self.jumps = {} # stopIndex -> duration
132
self.terminalIndices = []
133
134
135
def writeTypes(fout, prefix, options):
136
# note: public transport vehicles have speedDev="0" by default
137
prefixes_and_sf = [prefix, ""] * 11
138
if options:
139
# bus
140
prefixes_and_sf[1] = ' speedFactor="%s"' % options.speedFactorBus
141
# tram
142
prefixes_and_sf[3] = ' speedFactor="%s"' % options.speedFactorTram
143
# trolleybus
144
prefixes_and_sf[13] = ' speedFactor="%s"' % options.speedFactorBus
145
# minibus
146
prefixes_and_sf[15] = ' speedFactor="%s"' % options.speedFactorBus
147
# share_taxi
148
prefixes_and_sf[17] = ' speedFactor="%s"' % options.speedFactorBus
149
150
print(""" <vType id="%sbus" vClass="bus"%s/>
151
<vType id="%stram" vClass="tram"%s/>
152
<vType id="%strain" vClass="rail"%s/>
153
<vType id="%ssubway" vClass="subway"%s/>
154
<vType id="%slight_rail" vClass="rail_urban"%s/>
155
<vType id="%smonorail" vClass="rail_urban"%s/>
156
<vType id="%strolleybus" vClass="bus"%s/>
157
<vType id="%sminibus" vClass="bus"%s/>
158
<vType id="%sshare_taxi" vClass="taxi"%s/>
159
<vType id="%saerialway" vClass="cable_car"%s length="2.5" width="2" personCapacity="4"/>
160
<vType id="%sferry" vClass="ship"%s/>""" % tuple(prefixes_and_sf), file=fout)
161
162
163
def createTrips(options):
164
print("generating trips...")
165
tripList = [] # ids
166
trpMap = {} # ids->PTLine
167
168
departTimes = [options.begin for line in sumolib.output.parse_fast(options.ptlines, 'ptLine', ['id'])]
169
if options.randomBegin:
170
departTimes = sorted([options.begin
171
+ int(random.random() * options.period) for t in departTimes])
172
173
lineCount = collections.defaultdict(int)
174
typeCount = collections.defaultdict(int)
175
numLines = 0
176
numStops = 0
177
numSkipped = 0
178
for trp_nr, line in enumerate(sumolib.output.parse(options.ptlines, 'ptLine', heterogeneous=True)):
179
stop_ids = []
180
if not line.hasAttribute("period"):
181
if line.type == "aerialway":
182
line.setAttribute("period", options.periodAerialway)
183
else:
184
line.setAttribute("period", options.period)
185
if line.busStop is not None:
186
for stop in line.busStop:
187
if stop.id not in options.stopEdges:
188
sys.stderr.write("Warning: skipping unknown stop '%s'\n" % stop.id)
189
continue
190
stop_ids.append(stop.id)
191
192
if options.types is not None and line.type not in options.types:
193
if options.verbose:
194
print("Skipping line '%s' because it has type '%s'" % (line.id, line.type))
195
numSkipped += 1
196
continue
197
198
if line.hasAttribute("nightService"):
199
if line.nightService == "only" and not options.night:
200
if options.verbose:
201
print("Skipping line '%s' because it only drives at night" % (line.id))
202
numSkipped += 1
203
continue
204
if line.nightService == "no" and options.night:
205
if options.verbose:
206
print("Skipping line '%s' because it only drives during the day" % (line.id))
207
numSkipped += 1
208
continue
209
210
lineRefOrig = line.line.replace(" ", "_")
211
lineRefOrig = lineRefOrig.replace(";", "+")
212
lineRefOrig = lineRefOrig.replace(">", "")
213
lineRefOrig = lineRefOrig.replace("<", "")
214
215
if len(stop_ids) < options.min_stops:
216
sys.stderr.write("Warning: skipping line '%s' (%s_%s) because it has too few stops\n" % (
217
line.id, line.type, lineRefOrig))
218
numSkipped += 1
219
continue
220
221
lineRef = "%s:%s" % (lineRefOrig, lineCount[lineRefOrig])
222
lineCount[lineRefOrig] += 1
223
tripID = "%s_%s_%s" % (trp_nr, line.type, lineRef)
224
225
edges = []
226
fromEdge = None
227
toEdge = None
228
vias = []
229
net = options.net
230
if line.route is not None:
231
missing = []
232
for e in line.route[0].edges.split():
233
if net.hasEdge(e):
234
edges.append(e)
235
else:
236
missing.append(e)
237
if missing and options.verbose:
238
print("Removed %s missing edges from OSM route for line '%s'" % (len(missing), line.id))
239
240
if options.osmRoutes and len(edges) == 0 and options.verbose:
241
print("Cannot use OSM route for line '%s' (no edges given)" % line.id)
242
elif options.osmRoutes and len(edges) > 0:
243
fromEdge = edges[0]
244
toEdge = edges[-1]
245
if len(edges) > 2:
246
vias = edges[1:-1]
247
else:
248
if options.extendFringe and len(edges) > len(stop_ids):
249
fromEdge = edges[0]
250
toEdge = edges[-1]
251
# ensure that route actually covers the terminal stops
252
# (otherwise rail network may be invalid beyond stops)
253
if len(stop_ids) > 0:
254
firstStop = options.stopEdges[stop_ids[0]]
255
lastStop = options.stopEdges[stop_ids[-1]]
256
if firstStop not in edges:
257
fromEdge = firstStop
258
if options.verbose:
259
print(("Cannot extend route before first stop for line '%s' " +
260
"(stop edge %s not in route)") % (line.id, firstStop))
261
if lastStop not in edges:
262
toEdge = lastStop
263
if options.verbose:
264
print(("Cannot extend route after last stop for line '%s' " +
265
"(stop edge %s not in route)") % (line.id, lastStop))
266
else:
267
# if route is split, the route is incomplete and we
268
# should not extend beyond the split
269
prev = None
270
lastStopIndex = edges.index(lastStop)
271
for e in edges[lastStopIndex:]:
272
edge = net.getEdge(e)
273
if prev is not None and not prev.getConnections(edge):
274
toEdge = prev.getID()
275
if options.verbose:
276
print("Extending up to route split between %s and %s" % (
277
prev.getID(), edge.getID()))
278
break
279
prev = edge
280
else:
281
if options.extendFringe and options.verbose:
282
print("Cannot extend route to fringe for line '%s' (not enough edges given)" % line.id)
283
if len(stop_ids) == 0:
284
sys.stderr.write("Warning: skipping line '%s' because it has no stops\n" % line.id)
285
numSkipped += 1
286
continue
287
fromEdge = options.stopEdges[stop_ids[0]]
288
toEdge = options.stopEdges[stop_ids[-1]]
289
290
missingBefore = 0 if line.completeness == 1 else line.missingBefore
291
missingAfter = 0 if line.completeness == 1 else line.missingAfter
292
typeID = options.vtypeprefix + line.type
293
trpMap[tripID] = PTLine(lineRef, line.attr_name, line.completeness,
294
missingBefore, missingAfter,
295
fromEdge, toEdge,
296
line.period,
297
line.getAttributeSecure("color"),
298
lineRefOrig,
299
typeID,
300
departTimes[trp_nr],
301
stop_ids, vias)
302
tripList.append(tripID)
303
typeCount[line.type] += 1
304
numLines += 1
305
numStops += len(stop_ids)
306
307
if options.join:
308
joinTrips(options, tripList, trpMap)
309
writeTrips(options, tripList, trpMap)
310
if options.verbose:
311
print("Imported %s lines with %s stops and skipped %s lines" % (numLines, numStops, numSkipped))
312
for lineType, count in sorted(typeCount.items()):
313
print(" %s: %s" % (lineType, count))
314
print("done.")
315
return trpMap
316
317
318
def joinTrips(options, tripList, trpMap):
319
net = options.net
320
# join opposite pairs of trips
321
linePairs = collections.defaultdict(list)
322
for tripID, ptl in trpMap.items():
323
linePairs[ptl.refOrig].append(tripID)
324
325
for refOrig, tripIDs in linePairs.items():
326
if len(tripIDs) > 2:
327
sys.stderr.write("Warning: Cannot join line '%s' with %s trips\n" % (refOrig, len(tripIDs)))
328
elif len(tripIDs) == 2:
329
ptl1 = trpMap[tripIDs[0]]
330
ptl2 = trpMap[tripIDs[1]]
331
join1 = distCheck(options, refOrig, ptl1.toEdge, ptl2.fromEdge)
332
join2 = distCheck(options, refOrig, ptl1.fromEdge, ptl2.toEdge)
333
if not join1 and not join2:
334
continue
335
ptl1.completeness = str(0.5 * (float(ptl1.completeness) + float(ptl2.completeness)))
336
ptl2.completeness = ptl1.completeness
337
ptl1.ref = ptl1.refOrig
338
ptl2.ref = ptl2.refOrig
339
340
from1 = net.getEdge(ptl1.fromEdge)
341
from2 = net.getEdge(ptl2.fromEdge)
342
to1 = net.getEdge(ptl1.toEdge)
343
to2 = net.getEdge(ptl2.toEdge)
344
missingPart1 = ptl1.missingAfter + ptl2.missingBefore
345
missingPart2 = ptl2.missingAfter + ptl1.missingBefore
346
347
if join1:
348
# append ptl1 after ptl2
349
tripList.remove(tripIDs[1])
350
if missingPart1 != 0 or to1.getBidi() != from2.getID():
351
ptl1.jumps[len(ptl1.stop_ids) - 1] = missingPart1 * options.jumpDuration
352
ptl1.terminalIndices.append(len(ptl1.stop_ids) - 1)
353
ptl1.stop_ids += ptl2.stop_ids
354
if ptl1.vias:
355
ptl1.vias += [ptl1.toEdge, ptl2.fromEdge] + ptl2.vias
356
ptl1.toEdge = ptl2.toEdge
357
358
if join2:
359
# line forms a full circle so that vehicles can stay in the simulation continously.
360
# We have to compute the appropriate number of vehicles and then adapt the end time
361
if missingPart2 != 0 or to2.getBidi() != from1.getID():
362
ptl1.jumps[len(ptl1.stop_ids) - 1] = missingPart2 * options.jumpDuration
363
ptl1.terminalIndices.append(len(ptl1.stop_ids) - 1)
364
365
elif join2:
366
# only append ptl1 after ptl2
367
tripList.remove(tripIDs[0])
368
if missingPart2 != 0 or to2.getBidi() != from1.getID():
369
ptl2.jumps[len(ptl2.stop_ids) - 1] = missingPart2 * options.jumpDuration
370
ptl2.terminalIndices.append(len(ptl2.stop_ids) - 1)
371
ptl2.stop_ids += ptl1.stop_ids
372
if ptl2.vias:
373
ptl2.vias += [ptl2.toEdge, ptl1.fromEdge] + ptl1.vias
374
ptl2.toEdge = ptl1.toEdge
375
376
377
def distCheck(options, refOrig, eID1, eID2):
378
net = options.net
379
shape1 = net.getEdge(eID1).getShape(True)
380
shape2 = net.getEdge(eID2).getShape(True)
381
minDist = options.joinThreshold + 1
382
for p in shape1:
383
minDist = min(minDist, geomhelper.distancePointToPolygon(p, shape2))
384
if minDist > options.joinThreshold:
385
sys.stderr.write("Warning: Cannot join line '%s' at edges '%s' and '%s' with distance %s\n" % (
386
refOrig, eID1, eID2, minDist))
387
return False
388
else:
389
return True
390
391
392
def writeTrips(options, tripList, trpMap):
393
with sumolib.openz(options.trips, 'w') as fouttrips:
394
sumolib.writeXMLHeader(
395
fouttrips, "$Id: ptlines2flows.py v1_3_1+0313-ccb31df3eb [email protected] 2019-09-02 13:26:32 +0200 $",
396
"routes", options=options)
397
writeTypes(fouttrips, options.vtypeprefix, options)
398
399
for tripID in tripList:
400
ptl = trpMap[tripID]
401
via = ' via="%s"' % ' '.join(ptl.vias) if ptl.vias else ""
402
fouttrips.write(
403
(' <trip id="%s" type="%s" depart="%s" departLane="best" from="%s" ' +
404
'to="%s"%s>\n') % (
405
tripID, ptl.typeID, ptl.depart, ptl.fromEdge, ptl.toEdge, via))
406
for i, stop in enumerate(ptl.stop_ids):
407
dur = options.stopduration + options.stopdurationSlack
408
comment = " <!-- %s -->" % options.stopNames.get(stop)
409
if i in ptl.jumps:
410
jump = ' jump="%s"' % ptl.jumps[i]
411
else:
412
jump = ""
413
fouttrips.write(' <stop busStop="%s" duration="%s"%s/>%s\n' % (stop, dur, jump, comment))
414
fouttrips.write(' </trip>\n')
415
fouttrips.write("</routes>\n")
416
417
418
def runSimulation(options):
419
print("running SUMO to determine actual departure times...")
420
subprocess.call([sumolib.checkBinary("sumo"),
421
"-n", options.netfile,
422
"-r", options.trips,
423
"--begin", str(options.begin),
424
"--no-step-log",
425
"--ignore-route-errors",
426
"--time-to-teleport.disconnected", "0",
427
"--error-log", options.trips + ".errorlog",
428
"-a", options.ptstops,
429
"--device.rerouting.adaptation-interval", "0", # ignore tls and traffic effects
430
"--vehroute-output", options.routes,
431
"--stop-output", options.stopinfos,
432
"--aggregate-warnings", "5"])
433
print("done.")
434
sys.stdout.flush()
435
436
437
def formatTime(seconds):
438
return "%02i:%02i:%02i" % (seconds / 3600, (seconds % 3600) / 60, (seconds % 60))
439
440
441
def createRoutes(options, trpMap):
442
net = options.net
443
print("creating routes...")
444
stopsUntil = collections.defaultdict(list)
445
for stop in sumolib.output.parse_fast(options.stopinfos, 'stopinfo', ['id', 'ended', 'busStop']):
446
stopsUntil[(stop.id, stop.busStop)].append(float(stop.ended))
447
448
ft = formatTime if options.hrtime else lambda x: x
449
routes = []
450
flows = []
451
actualDepart = {} # departure may be delayed when the edge is not yet empty
452
collections.defaultdict(int)
453
routeDurations = {}
454
routeSize = {}
455
flow_duration = options.end - options.begin
456
for vehicle in sumolib.output.parse(options.routes, 'vehicle'):
457
tmpio = StringIO()
458
id = vehicle.id
459
ptline = trpMap[id]
460
flowID = "%s_%s" % (vehicle.type, ptline.ref)
461
try:
462
if vehicle.route is not None:
463
edges = vehicle.route[0].edges
464
else:
465
edges = vehicle.routeDistribution[0].route[1].edges
466
routeSize[flowID] = len(edges.split())
467
except BaseException:
468
if options.ignoreErrors:
469
sys.stderr.write("Warning: Could not parse edges for vehicle '%s'\n" % id)
470
continue
471
else:
472
sys.exit("Could not parse edges for vehicle '%s'\n" % id)
473
474
flows.append((id, flowID, ptline.ref, vehicle.type, float(vehicle.depart)))
475
actualDepart[id] = float(vehicle.depart)
476
parking = ' parking="true"' if vehicle.type == "bus" and options.busparking else ''
477
color = ' color="%s"' % ptline.color if ptline.color is not None else ""
478
repeat = ""
479
stops = vehicle.stop
480
if len(ptline.terminalIndices) == 2 and stops:
481
lastBusStop = stops[-1].busStop
482
lastUntil = stopsUntil.get((id, lastBusStop))
483
if lastUntil is not None:
484
cycleTime = lastUntil[-1] - actualDepart[id]
485
numRepeats = math.ceil(flow_duration / cycleTime)
486
if numRepeats > 1:
487
repeat = ' repeat="%s" cycleTime="%s"' % (numRepeats, ft(cycleTime))
488
489
# jump over disconnected parts
490
jumps = {} # edge -> duration
491
usedJumps = set()
492
prev = None
493
for edgeID in edges.split():
494
edge = net.getEdge(edgeID)
495
if prev is not None and not prev.getConnections(edge):
496
jumpDist = geomhelper.distance(prev.getToNode().getCoord(), edge.getFromNode().getCoord())
497
jumpSpeed = (prev.getSpeed() + edge.getSpeed()) * 0.5
498
jumpDuration = jumpDist / jumpSpeed
499
jumps[prev.getID()] = jumpDuration
500
prev = edge
501
# record all stops on an edge (only the last stop needs to jump when there is a disconnect
502
edgeStops = collections.defaultdict(list)
503
if stops is not None:
504
for stop in stops:
505
edgeStops[options.stopEdges[stop.busStop]].append(stop.busStop)
506
507
tmpio.write(' <route id="%s"%s edges="%s"%s >\n' % (flowID, color, edges, repeat))
508
if stops is not None:
509
untilOffset = 0
510
for stop in stops:
511
if (id, stop.busStop) in stopsUntil:
512
stopEdge = options.stopEdges[stop.busStop]
513
until = stopsUntil[(id, stop.busStop)]
514
stopname = ''
515
if stop.busStop in options.stopNames:
516
stopname = ' <!-- %s -->' % options.stopNames[stop.busStop]
517
untilZeroBased = until[0] - actualDepart[id] + untilOffset
518
if stop.jump is not None:
519
jump = ' jump="%s"' % stop.jump
520
elif stopEdge in jumps and stop.busStop == edgeStops[stopEdge][-1]:
521
# only the last stop on an edge needs the jump
522
usedJumps.add(stopEdge)
523
untilOffset += jumps[stopEdge]
524
jump = ' jump="%s"' % jumps[stopEdge]
525
else:
526
jump = ""
527
if len(until) > 1:
528
stopsUntil[(id, stop.busStop)] = until[1:]
529
tmpio.write(
530
' <stop busStop="%s" duration="%s" until="%s"%s%s/>%s\n' % (
531
stop.busStop, options.stopduration, ft(untilZeroBased), parking, jump, stopname))
532
routeDurations[flowID] = untilZeroBased
533
else:
534
sys.stderr.write("Warning: Missing stop '%s' for flow '%s'\n" % (stop.busStop, id))
535
else:
536
sys.stderr.write("Warning: No stops for flow '%s'\n" % id)
537
for edgeID, jumpDuration in jumps.items():
538
if edgeID not in usedJumps:
539
tmpio.write(
540
' <stop edge="%s" speed="999" jump="%s" index="fit"/>\n' % (edgeID, jumpDuration))
541
542
tmpio.write(' </route>\n')
543
routes.append((flowID, tmpio.getvalue()))
544
545
with sumolib.openz(options.outfile, 'w') as foutflows:
546
sumolib.writeXMLHeader(foutflows, root="routes", options=options)
547
if not options.novtypes:
548
writeTypes(foutflows, options.vtypeprefix, None)
549
550
for routeID, routeStr in sorted(routes):
551
foutflows.write(routeStr)
552
553
for vehID, flowID, lineRef, type, begin in sorted(flows):
554
ptline = trpMap[vehID]
555
number = None
556
if flowID in routeDurations:
557
number = max(1, int(routeDurations[flowID] / float(ptline.period)))
558
if options.multistart and len(ptline.terminalIndices) == 2:
559
# vehicles stay in a continuous loop. We create a fixed number of vehicles with repeating routes
560
for i in range(number):
561
# this is a hack since edges could have very different lengths
562
departEdge = int(i * routeSize[flowID] / float(number))
563
foutflows.write(' <vehicle id="%s.%s" type="%s" route="%s" depart="%s" departEdge="%s" line="%s" %s>\n' % ( # noqa
564
flowID, i, type, flowID, ft(begin), departEdge, ptline.ref, options.flowattrs))
565
writeParams(foutflows, ptline)
566
foutflows.write(' </vehicle>\n')
567
foutflows.write('\n')
568
569
else:
570
end = ' end="%s"' % ft(begin + flow_duration)
571
if len(ptline.terminalIndices) == 2 and number is not None:
572
end = ' number="%s"' % number
573
foutflows.write(' <flow id="%s" type="%s" route="%s" begin="%s"%s period="%s" line="%s" %s>\n' % (
574
flowID, type, flowID, ft(begin), end,
575
int(float(ptline.period)), ptline.ref, options.flowattrs))
576
writeParams(foutflows, ptline)
577
foutflows.write(' </flow>\n')
578
foutflows.write('</routes>\n')
579
580
print("done.")
581
582
583
def writeParams(foutflows, ptline):
584
if ptline.name is not None:
585
foutflows.write(' <param key="name" value=%s/>\n' % quoteattr(ptline.name))
586
if ptline.completeness is not None:
587
foutflows.write(' <param key="completeness" value=%s/>\n' % quoteattr(ptline.completeness))
588
589
590
def main(options):
591
if options.seed:
592
random.seed(options.seed)
593
sys.stderr.flush()
594
options.net = sumolib.net.readNet(options.netfile)
595
options.stopEdges = {}
596
options.stopNames = {}
597
for stop in sumolib.output.parse(options.ptstops, ['busStop', 'trainStop']):
598
options.stopEdges[stop.id] = sumolib._laneID2edgeID(stop.lane)
599
if stop.name:
600
options.stopNames[stop.id] = stop.attr_name
601
602
trpMap = createTrips(options)
603
sys.stderr.flush()
604
runSimulation(options)
605
createRoutes(options, trpMap)
606
607
608
if __name__ == "__main__":
609
main(get_options())
610
611