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