Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/route/addStops2Routes.py
194300 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 addStops2Routes.py
15
# @author Yun-Pang Floetteroed
16
# @author Jakob Erdmann
17
# @date 2019-04-25
18
19
"""
20
add stops to vehicle routes
21
"""
22
from __future__ import absolute_import
23
from __future__ import print_function
24
import os
25
import sys
26
import random
27
from collections import defaultdict
28
29
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
30
from sumolib.output import parse, parse_fast # noqa
31
from sumolib.options import ArgumentParser # noqa
32
import sumolib # noqa
33
34
35
def get_options(args=None):
36
op = ArgumentParser()
37
op.add_option("-n", "--net-file", category='input', dest="netfile", type=op.net_file,
38
help="define the net filename (mandatory)")
39
op.add_option("-r", "--route-files", category='input', dest="routefiles", type=op.route_file,
40
help="define the route file separated by comma (mandatory)")
41
op.add_option("-o", "--output-file", category='output', dest="outfile", type=op.route_file,
42
help="define the output filename")
43
op.add_option("-t", "--typesfile", category='input', dest="typesfile",
44
help="Give a typesfile")
45
op.add_option("-d", "--duration",
46
help="Define duration of vehicle stop (setting 'X-Y' picks randomly from [X,Y[)")
47
op.add_option("-u", "--until",
48
help="Define end time of vehicle stop")
49
op.add_option("--speed",
50
help="Define a waypoint with the given maximum speed")
51
op.add_option("-p", "--parking", dest="parking", action="store_true",
52
default=False, help="Let the vehicle stop beside the road")
53
op.add_option("--relpos",
54
help="relative stopping position along the edge [0,1] or 'random'")
55
op.add_option("--lane", default="0",
56
help="set index of stop lane or 'random' (unusable lanes are not counted)")
57
op.add_option("--reledge", default="1",
58
help="relative stopping position along the route [0,1] or 'random' (1 indicates the last edge)")
59
op.add_option("--via-index", dest="viaIndex",
60
help="index of stop edge along the route (0-based, negative allowed) or 'random'")
61
op.add_option("--probability", type=float, default=1,
62
help="app stop with the given probability ]0, 1]")
63
op.add_option("--parking-areas", dest="parkingareas", default=False, type=op.additional_file,
64
help="load parkingarea definitions and stop at parkingarea on the arrival edge if possible")
65
op.add_option("--start-at-stop", dest="startAtStop", action="store_true",
66
default=False, help="shorten route so it starts at stop")
67
op.add_option("--rel-occupancy", dest="relOccupancy", type=float,
68
help="fill all parkingAreas to relative occupancy")
69
op.add_option("--abs-occupancy", dest="absOccupancy", type=int, default=1,
70
help="fill all parkingAreas to absolute occupancy")
71
op.add_option("--abs-free", dest="absFree", type=int,
72
help="fill all parkingAreas to absolute remaining capacity")
73
op.add_option("-D", "--person-duration", dest="pDuration",
74
help="Define duration of person stop (setting 'X-Y' picks randomly from [X,Y[)")
75
op.add_option("-U", "--person-until", dest="pUntil",
76
help="Define end time of person stop")
77
op.add_option("-s", "--seed", type=int, default=42, help="random seed")
78
op.add_option("-v", "--verbose", dest="verbose", action="store_true",
79
default=False, help="tell me what you are doing")
80
81
options = op.parse_args()
82
83
if options.parkingareas:
84
options.parkingareas = options.parkingareas.split(",")
85
86
if not options.routefiles:
87
if not options.startAtStop:
88
op.print_help()
89
sys.exit("--route-files missing")
90
elif not options.parkingareas:
91
sys.exit("--parking-areas needed to generation stationary traffic without route-files")
92
else:
93
options.routefiles = []
94
if not options.outfile:
95
options.outfile = options.parkingareas[0][:-4] + ".stops.xml"
96
else:
97
options.routefiles = options.routefiles.split(',')
98
if not options.outfile:
99
options.outfile = options.routefiles[0][:-4] + ".stops.xml"
100
101
if not options.netfile:
102
op.print_help()
103
sys.exit("--net-file missing")
104
105
if not options.typesfile:
106
options.typesfile = options.routefiles
107
else:
108
options.typesfile = options.typesfile.split(",")
109
110
if not options.duration and not options.until and not options.speed:
111
op.print_help()
112
sys.exit("stop duration, speed or until missing")
113
114
if options.relpos is not None:
115
try:
116
options.relpos = max(0, min(1, float(options.relpos)))
117
except ValueError:
118
if options.relpos != 'random':
119
sys.exit("option --relpos must be set to 'random' or to a float value from [0,1]")
120
pass
121
122
if options.lane is not None:
123
try:
124
options.lane = int(options.lane)
125
if options.lane < 0:
126
sys.exit("option --lane must be set to 'random' or to a non-negative integer value")
127
except ValueError:
128
if options.lane != 'random':
129
sys.exit("option --lane must be set to 'random' or to an integer value")
130
pass
131
132
if options.reledge is not None:
133
try:
134
options.reledge = max(0, min(1, float(options.reledge)))
135
except ValueError:
136
if options.reledge != 'random':
137
sys.exit("option --reledge must be set to 'random' or to a float value from [0,1]")
138
pass
139
if options.viaIndex is not None:
140
viaIndex = options.viaIndex
141
if isinstance(viaIndex, str):
142
viaIndex = viaIndex.strip()
143
if viaIndex.lower() == 'random':
144
options.viaIndex = 'random'
145
else:
146
try:
147
options.viaIndex = int(viaIndex)
148
except ValueError:
149
sys.exit("option --via-index must be set to 'random' or to an integer value")
150
else:
151
options.viaIndex = int(viaIndex)
152
153
return options
154
155
156
def readTypes(options):
157
vtypes = {None: "passenger"}
158
for file in options.typesfile:
159
for vtype in sumolib.output.parse(file, 'vType'):
160
vtypes[vtype.id] = vtype.getAttributeSecure("vClass", "passenger")
161
# print(vtypes)
162
return vtypes
163
164
165
def getEdgeIDs(obj):
166
result = []
167
if obj.route:
168
return obj.route[0].edges.split()
169
if obj.attr_from:
170
result.append(obj.attr_from)
171
via = getattr(obj, "via", None)
172
if via:
173
result += via.split()
174
if obj.to:
175
result.append(obj.to)
176
return result
177
178
179
def interpretDuration(duration):
180
if '-' in duration:
181
start, stop = duration.split('-')
182
return random.randrange(int(start), int(stop))
183
else:
184
return duration
185
186
187
def loadRouteFiles(options, routefile, edge2parking, outf):
188
net = sumolib.net.readNet(options.netfile)
189
vtypes = readTypes(options)
190
numSkipped = defaultdict(lambda: 0)
191
192
if options.viaIndex is not None and options.reledge not in (None, 1, 1.0):
193
print("Warning: --via-index overrides --reledge", file=sys.stderr)
194
195
for routefile in options.routefiles:
196
for obj in sumolib.xml.parse(routefile, ['vehicle', 'trip', 'flow', 'person', 'vType']):
197
if (obj.name == 'vType' or
198
options.probability < 1 and random.random() > options.probability):
199
outf.write(obj.toXML(' '*4))
200
continue
201
edgeIDs = getEdgeIDs(obj)
202
stopEdgeID = None
203
if edgeIDs:
204
if options.viaIndex is not None:
205
if options.viaIndex == 'random':
206
edgeIndex = random.randrange(len(edgeIDs))
207
else:
208
viaIndex = options.viaIndex
209
if viaIndex < -len(edgeIDs) or viaIndex >= len(edgeIDs):
210
numSkipped[obj.name] += 1
211
print("Warning: via-index %s out of range for %s '%s' (route length %s); no stop added" % (
212
options.viaIndex, obj.name, obj.id, len(edgeIDs)), file=sys.stderr)
213
outf.write(obj.toXML(' '*4))
214
continue
215
edgeIndex = viaIndex if viaIndex >= 0 else len(edgeIDs) + viaIndex
216
stopEdgeID = edgeIDs[edgeIndex]
217
else:
218
reledge = options.reledge
219
if reledge == 'random':
220
reledge = random.random()
221
stopEdgeID = edgeIDs[int(round(reledge * (len(edgeIDs) - 1)))]
222
223
if stopEdgeID is None:
224
if obj.name == 'person' and (
225
options.pDuration is not None
226
or options.pUntil is not None):
227
stopAttrs = {}
228
if options.pDuration:
229
stopAttrs["duration"] = interpretDuration(options.pDuration)
230
if options.pUntil:
231
stopAttrs["until"] = options.pUntil
232
# stop location is derived automatically from previous plan element
233
obj.addChild("stop", attrs=stopAttrs)
234
else:
235
numSkipped[obj.name] += 1
236
outf.write(obj.toXML(' '*4))
237
continue
238
239
try:
240
stopEdge = net.getEdge(stopEdgeID)
241
except Exception:
242
numSkipped[obj.name] += 1
243
print("Warning: edge '%s' not found in net for %s '%s'; no stop added" % (
244
stopEdgeID, obj.name, obj.id), file=sys.stderr)
245
outf.write(obj.toXML(' '*4))
246
continue
247
skip = False
248
stopAttrs = {}
249
if options.parkingareas:
250
if stopEdgeID in edge2parking:
251
stopAttrs["parkingArea"] = edge2parking[stopEdgeID]
252
else:
253
skip = True
254
numSkipped[obj.name] += 1
255
print("Warning: no parkingArea found on edge '%s' for vehicle '%s'" % (
256
stopEdgeID, obj.id), file=sys.stderr)
257
else:
258
# find usable lane
259
skip = True
260
lanes = stopEdge.getLanes()
261
usable = [lane for lane in lanes if lane.allows(vtypes[obj.type])]
262
if usable:
263
lane = None
264
if options.lane == 'random':
265
lane = random.choice(usable)
266
elif options.lane < len(usable):
267
lane = usable[options.lane]
268
269
if lane:
270
skip = False
271
stopAttrs["lane"] = lane.getID()
272
if options.relpos:
273
relpos = options.relpos
274
if options.relpos == 'random':
275
relpos = random.random()
276
stopAttrs["endPos"] = "%.2f" % (lane.getLength() * relpos)
277
if skip:
278
numSkipped[obj.name] += 1
279
print("Warning: no allowed lane found on edge '%s' for vehicle '%s' (%s)" % (
280
stopEdgeID, obj.id, vtypes[obj.type]), file=sys.stderr)
281
282
if options.parking:
283
stopAttrs["parking"] = "true"
284
if options.duration:
285
stopAttrs["duration"] = interpretDuration(options.duration)
286
if options.speed:
287
stopAttrs["speed"] = interpretDuration(options.speed)
288
if options.until:
289
stopAttrs["until"] = interpretDuration(options.until)
290
if not skip:
291
obj.addChild("stop", attrs=stopAttrs)
292
if options.startAtStop:
293
obj.setAttribute("departPos", "stop")
294
if obj.route:
295
obj.route[0].setAttribute("edges", stopEdgeID)
296
elif obj.attr_from:
297
obj.attr_from = obj.to
298
299
outf.write(obj.toXML(' '*4))
300
301
for objType, n in numSkipped.items():
302
print("Warning: No stop added for %s %ss" % (n, objType))
303
304
305
def generateStationary(options, edge2parking, outf):
306
paCapacity = {}
307
if options.parkingareas:
308
for pafile in options.parkingareas:
309
for pa in sumolib.xml.parse(pafile, "parkingArea"):
310
paCapacity[pa.id] = int(pa.getAttributeSecure("roadsideCapacity", 0))
311
312
attrs = ""
313
if options.duration:
314
attrs += ' duration="%s"' % interpretDuration(options.duration)
315
if options.until:
316
attrs += ' until="%s"' % options.until
317
318
for edge, pa in edge2parking.items():
319
n = 0
320
if options.relOccupancy:
321
n = paCapacity[pa] * options.relOccupancy
322
elif options.absFree:
323
n = paCapacity[pa] - options.absFree
324
else:
325
n = options.absOccupancy
326
for i in range(n):
327
id = "%s.%s" % (pa, i)
328
outf.write(' <vehicle id="%s" depart="0" departPos="stop">\n' % id)
329
outf.write(' <route edges="%s"/>\n' % edge)
330
outf.write(' <stop parkingArea="%s"%s/>\n' % (pa, attrs))
331
outf.write(' </vehicle>\n')
332
333
334
def main(options):
335
random.seed(options.seed)
336
337
edge2parking = {}
338
if options.parkingareas:
339
for pafile in options.parkingareas:
340
for pa in sumolib.xml.parse(pafile, "parkingArea"):
341
edge = '_'.join(pa.lane.split('_')[:-1])
342
edge2parking[edge] = pa.id
343
344
# with io.open(options.outfile, 'w', encoding="utf8") as outf:
345
# with open(options.outfile, 'w', encoding="utf8") as outf:
346
with open(options.outfile, 'w') as outf:
347
sumolib.writeXMLHeader(outf, "$Id$", "routes", options=options) # noqa
348
if options.routefiles:
349
loadRouteFiles(options, options.routefiles, edge2parking, outf)
350
else:
351
generateStationary(options, edge2parking, outf)
352
outf.write('</routes>\n')
353
outf.close()
354
355
356
if __name__ == "__main__":
357
options = get_options()
358
main(options)
359
360