Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/route/addStops2Routes.py
169674 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 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("--probability", type=float, default=1,
60
help="app stop with the given probability ]0, 1]")
61
op.add_option("--parking-areas", dest="parkingareas", default=False, type=op.additional_file,
62
help="load parkingarea definitions and stop at parkingarea on the arrival edge if possible")
63
op.add_option("--start-at-stop", dest="startAtStop", action="store_true",
64
default=False, help="shorten route so it starts at stop")
65
op.add_option("--rel-occupancy", dest="relOccupancy", type=float,
66
help="fill all parkingAreas to relative occupancy")
67
op.add_option("--abs-occupancy", dest="absOccupancy", type=int, default=1,
68
help="fill all parkingAreas to absolute occupancy")
69
op.add_option("--abs-free", dest="absFree", type=int,
70
help="fill all parkingAreas to absolute remaining capacity")
71
op.add_option("-D", "--person-duration", dest="pDuration",
72
help="Define duration of person stop (setting 'X-Y' picks randomly from [X,Y[)")
73
op.add_option("-U", "--person-until", dest="pUntil",
74
help="Define end time of person stop")
75
op.add_option("-s", "--seed", type=int, default=42, help="random seed")
76
op.add_option("-v", "--verbose", dest="verbose", action="store_true",
77
default=False, help="tell me what you are doing")
78
79
options = op.parse_args()
80
81
if options.parkingareas:
82
options.parkingareas = options.parkingareas.split(",")
83
84
if not options.routefiles:
85
if not options.startAtStop:
86
op.print_help()
87
sys.exit("--route-files missing")
88
elif not options.parkingareas:
89
sys.exit("--parking-areas needed to generation stationary traffic without route-files")
90
else:
91
options.routefiles = []
92
if not options.outfile:
93
options.outfile = options.parkingareas[0][:-4] + ".stops.xml"
94
else:
95
options.routefiles = options.routefiles.split(',')
96
if not options.outfile:
97
options.outfile = options.routefiles[0][:-4] + ".stops.xml"
98
99
if not options.netfile:
100
op.print_help()
101
sys.exit("--net-file missing")
102
103
if not options.typesfile:
104
options.typesfile = options.routefiles
105
else:
106
options.typesfile = options.typesfile.split(",")
107
108
if not options.duration and not options.until and not options.speed:
109
op.print_help()
110
sys.exit("stop duration, speed or until missing")
111
112
if options.relpos is not None:
113
try:
114
options.relpos = max(0, min(1, float(options.relpos)))
115
except ValueError:
116
if options.relpos != 'random':
117
sys.exit("option --relpos must be set to 'random' or to a float value from [0,1]")
118
pass
119
120
if options.lane is not None:
121
try:
122
options.lane = int(options.lane)
123
if options.lane < 0:
124
sys.exit("option --lane must be set to 'random' or to a non-negative integer value")
125
except ValueError:
126
if options.lane != 'random':
127
sys.exit("option --lane must be set to 'random' or to an integer value")
128
pass
129
130
if options.reledge is not None:
131
try:
132
options.reledge = max(0, min(1, float(options.reledge)))
133
except ValueError:
134
if options.reledge != 'random':
135
sys.exit("option --reledge must be set to 'random' or to a float value from [0,1]")
136
pass
137
138
return options
139
140
141
def readTypes(options):
142
vtypes = {None: "passenger"}
143
for file in options.typesfile:
144
for vtype in sumolib.output.parse(file, 'vType'):
145
vtypes[vtype.id] = vtype.getAttributeSecure("vClass", "passenger")
146
# print(vtypes)
147
return vtypes
148
149
150
def getEdgeIDs(obj):
151
result = []
152
if obj.route:
153
return obj.route[0].edges.split()
154
if obj.attr_from:
155
result.append(obj.attr_from)
156
if obj.to:
157
result.append(obj.to)
158
return result
159
160
161
def interpretDuration(duration):
162
if '-' in duration:
163
start, stop = duration.split('-')
164
return random.randrange(int(start), int(stop))
165
else:
166
return duration
167
168
169
def loadRouteFiles(options, routefile, edge2parking, outf):
170
net = sumolib.net.readNet(options.netfile)
171
vtypes = readTypes(options)
172
numSkipped = defaultdict(lambda: 0)
173
174
for routefile in options.routefiles:
175
for obj in sumolib.xml.parse(routefile, ['vehicle', 'trip', 'flow', 'person', 'vType']):
176
if (obj.name == 'vType' or
177
options.probability < 1 and random.random() > options.probability):
178
outf.write(obj.toXML(' '*4))
179
continue
180
edgeIDs = getEdgeIDs(obj)
181
reledge = options.reledge
182
if reledge == 'random':
183
reledge = random.random()
184
lastEdgeID = None
185
if edgeIDs:
186
lastEdgeID = edgeIDs[int(round(reledge * (len(edgeIDs) - 1)))]
187
188
if lastEdgeID is None:
189
if obj.name == 'person' and (
190
options.pDuration is not None
191
or options.pUntil is not None):
192
stopAttrs = {}
193
if options.pDuration:
194
stopAttrs["duration"] = interpretDuration(options.pDuration)
195
if options.pUntil:
196
stopAttrs["until"] = options.pUntil
197
# stop location is derived automatically from previous plan element
198
obj.addChild("stop", attrs=stopAttrs)
199
else:
200
numSkipped[obj.name] += 1
201
outf.write(obj.toXML(' '*4))
202
continue
203
204
lastEdge = net.getEdge(lastEdgeID)
205
skip = False
206
stopAttrs = {}
207
if options.parkingareas:
208
if lastEdgeID in edge2parking:
209
stopAttrs["parkingArea"] = edge2parking[lastEdgeID]
210
else:
211
skip = True
212
numSkipped[obj.name] += 1
213
print("Warning: no parkingArea found on edge '%s' for vehicle '%s'" % (
214
lastEdgeID, obj.id), file=sys.stderr)
215
else:
216
# find usable lane
217
skip = True
218
lanes = lastEdge.getLanes()
219
usable = [lane for lane in lanes if lane.allows(vtypes[obj.type])]
220
if usable:
221
lane = None
222
if options.lane == 'random':
223
lane = random.choice(usable)
224
elif options.lane < len(usable):
225
lane = usable[options.lane]
226
227
if lane:
228
skip = False
229
stopAttrs["lane"] = lane.getID()
230
if options.relpos:
231
relpos = options.relpos
232
if options.relpos == 'random':
233
relpos = random.random()
234
stopAttrs["endPos"] = "%.2f" % (lane.getLength() * relpos)
235
if skip:
236
numSkipped[obj.name] += 1
237
print("Warning: no allowed lane found on edge '%s' for vehicle '%s' (%s)" % (
238
lastEdgeID, obj.id, vtypes[obj.type]), file=sys.stderr)
239
240
if options.parking:
241
stopAttrs["parking"] = "true"
242
if options.duration:
243
stopAttrs["duration"] = interpretDuration(options.duration)
244
if options.speed:
245
stopAttrs["speed"] = interpretDuration(options.speed)
246
if options.until:
247
stopAttrs["until"] = interpretDuration(options.until)
248
if not skip:
249
obj.addChild("stop", attrs=stopAttrs)
250
if options.startAtStop:
251
obj.setAttribute("departPos", "stop")
252
if obj.route:
253
obj.route[0].setAttribute("edges", lastEdgeID)
254
elif obj.attr_from:
255
obj.attr_from = obj.to
256
257
outf.write(obj.toXML(' '*4))
258
259
for objType, n in numSkipped.items():
260
print("Warning: No stop added for %s %ss" % (n, objType))
261
262
263
def generateStationary(options, edge2parking, outf):
264
paCapacity = {}
265
if options.parkingareas:
266
for pafile in options.parkingareas:
267
for pa in sumolib.xml.parse(pafile, "parkingArea"):
268
paCapacity[pa.id] = int(pa.getAttributeSecure("roadsideCapacity", 0))
269
270
attrs = ""
271
if options.duration:
272
attrs += ' duration="%s"' % interpretDuration(options.duration)
273
if options.until:
274
attrs += ' until="%s"' % options.until
275
276
for edge, pa in edge2parking.items():
277
n = 0
278
if options.relOccupancy:
279
n = paCapacity[pa] * options.relOccupancy
280
elif options.absFree:
281
n = paCapacity[pa] - options.absFree
282
else:
283
n = options.absOccupancy
284
for i in range(n):
285
id = "%s.%s" % (pa, i)
286
outf.write(' <vehicle id="%s" depart="0" departPos="stop">\n' % id)
287
outf.write(' <route edges="%s"/>\n' % edge)
288
outf.write(' <stop parkingArea="%s"%s/>\n' % (pa, attrs))
289
outf.write(' </vehicle>\n')
290
291
292
def main(options):
293
random.seed(options.seed)
294
295
edge2parking = {}
296
if options.parkingareas:
297
for pafile in options.parkingareas:
298
for pa in sumolib.xml.parse(pafile, "parkingArea"):
299
edge = '_'.join(pa.lane.split('_')[:-1])
300
edge2parking[edge] = pa.id
301
302
# with io.open(options.outfile, 'w', encoding="utf8") as outf:
303
# with open(options.outfile, 'w', encoding="utf8") as outf:
304
with open(options.outfile, 'w') as outf:
305
sumolib.writeXMLHeader(outf, "$Id$", "routes", options=options) # noqa
306
if options.routefiles:
307
loadRouteFiles(options, options.routefiles, edge2parking, outf)
308
else:
309
generateStationary(options, edge2parking, outf)
310
outf.write('</routes>\n')
311
outf.close()
312
313
314
if __name__ == "__main__":
315
options = get_options()
316
main(options)
317
318