Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/generateContinuousRerouters.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 generateContinuousRerouters.py
15
# @author Jakob Erdmann
16
# @date 2019-11-23
17
18
"""
19
This script generates rerouters ahead of every intersection with routes to each of
20
the downstream intersections.
21
"""
22
from __future__ import print_function
23
from __future__ import absolute_import
24
import os
25
import sys
26
from collections import defaultdict
27
28
if 'SUMO_HOME' in os.environ:
29
sys.path.append(os.path.join(os.environ['SUMO_HOME'], 'tools'))
30
import sumolib # noqa
31
from sumolib.miscutils import euclidean # noqa
32
from sumolib.geomhelper import naviDegree, minAngleDegreeDiff # noqa
33
34
35
def get_options(args=None):
36
ap = sumolib.options.ArgumentParser()
37
ap.add_option("-n", "--net-file", dest="netfile", required=True, category="input", type=ap.net_file,
38
help="define the net file (mandatory)")
39
ap.add_option("-o", "--output-file", dest="outfile", default="rerouters.xml", category="output", type=ap.file,
40
help="define the output rerouter filename")
41
ap.add_option("-T", "--turn-defaults", dest="turnDefaults", default="30,50,20",
42
help="Use STR[] as default turn probabilities [right,straight,left[,turn]]")
43
ap.add_option("-l", "--long-routes", action="store_true", dest="longRoutes", default=False,
44
help="place rerouters further upstream (after the previous decision point) to increase " +
45
"overlap of routes when rerouting and thereby improve anticipation of intersections")
46
ap.add_option("--vclass", help="only create routes which permit the given vehicle class")
47
ap.add_option("-s", "--stop-file", dest="stopsFile", type=ap.file,
48
help="Stop at any stopping places loaded from file")
49
ap.add_option("--stop.duration", dest="stopDuration", default=60, type=int,
50
help="Set duration for added stops")
51
ap.add_option("-b", "--begin", default=0, help="begin time")
52
ap.add_option("-e", "--end", default=3600, help="end time (default 3600)")
53
options = ap.parse_args(args=args)
54
55
options.turnDefaults = list(map(float, options.turnDefaults.split(',')))
56
if len(options.turnDefaults) not in [3, 4]:
57
sys.exit("turn-defaults must be defined as 3 or 4 numbers")
58
if len(options.turnDefaults) == 3:
59
options.turnDefaults.append(0) # turn with 0 probability
60
61
return options
62
63
64
def getEdgesToIntersection(edge, vclass):
65
result = [edge]
66
seen = set()
67
seen.add(edge)
68
while len(edge.getOutgoing().keys()) == 1:
69
edge = list(edge.getOutgoing().keys())[0]
70
if edge in seen:
71
break
72
elif vclass is not None and not edge.allows(vclass):
73
break
74
else:
75
seen.add(edge)
76
result.append(edge)
77
78
return result
79
80
81
def getTurnIndex(fromEdge, toEdge):
82
cons = fromEdge.getOutgoing()[toEdge]
83
con = cons[0]
84
dir = con.getDirection()
85
if dir == con.LINKDIR_RIGHT or dir == con.LINKDIR_PARTRIGHT:
86
return 0
87
elif dir == con.LINKDIR_STRAIGHT:
88
return 1
89
elif dir == con.LINKDIR_LEFT or dir == con.LINKDIR_PARTLEFT:
90
return 2
91
else:
92
return 3
93
94
95
def getNumAlternatives(edge, routes):
96
numAlternatives = 0
97
for edges in routes:
98
if edges[0] in edge.getOutgoing().keys():
99
numAlternatives += 1
100
return numAlternatives
101
102
103
def getNumSiblings(edge):
104
"""return number of outgoing edges at the fromNode of this edge that can be
105
reached from a common predecessor of the given edge"""
106
siblings = set()
107
for cons in edge.getIncoming().values():
108
for con in cons:
109
for outCons in con.getFrom().getOutgoing().values():
110
for outCon in outCons:
111
siblings.add(outCon.getTo())
112
return len(siblings)
113
114
115
def writeRoute(options, outf, routeID, edgeIDs, edgeStops):
116
close = '/>\n'
117
outf.write(' <route id="%s" edges="%s"' % (routeID, ' '.join(edgeIDs)))
118
if edgeStops:
119
stops = [edgeStops[e] for e in edgeIDs if e in edgeStops]
120
if stops:
121
outf.write('>\n')
122
for stop in stops:
123
outf.write(' <stop busStop="%s" duration="%s"/>\n' % (stop, options.stopDuration))
124
close = ' </route>\n'
125
outf.write(close)
126
127
128
def main(options):
129
net = sumolib.net.readNet(options.netfile)
130
incomingRoutes = defaultdict(set) # edge : set(route0, route1, ...)
131
132
edgeStops = {}
133
if options.stopsFile:
134
for stop in sumolib.output.parse(options.stopsFile, ['busStop', 'trainStop']):
135
edgeStops[sumolib._laneID2edgeID(stop.lane)] = stop.id
136
137
if options.longRoutes:
138
# build dictionary of routes leading from an intersection to each edge
139
for junction in net.getNodes():
140
isEntry = len(junction.getIncoming()) == 0
141
if len(junction.getOutgoing()) > 1 or isEntry:
142
for edge in junction.getOutgoing():
143
if isEntry or getNumSiblings(edge) > 1:
144
edges = getEdgesToIntersection(edge, options.vclass)
145
edgeIDs = tuple([e.getID() for e in edges])
146
incomingRoutes[edges[-1]].add(edgeIDs)
147
148
with open(options.outfile, 'w') as outf:
149
sumolib.xml.writeHeader(outf, root="additional", options=options)
150
for junction in net.getNodes():
151
if len(junction.getOutgoing()) > 1:
152
routes = []
153
for edge in junction.getOutgoing():
154
if options.vclass is None or edge.allows(options.vclass):
155
routes.append(getEdgesToIntersection(edge, options.vclass))
156
157
for edge in junction.getIncoming():
158
if options.longRoutes:
159
# overlapping routes: start behind an intersection and
160
# route across the next intersection to the entry of the
161
# 2nd intersection (more rerouters and overlapping routes)
162
if getNumAlternatives(edge, routes) > 1:
163
for incomingRoute in sorted(incomingRoutes[edge]):
164
assert incomingRoute[-1] == edge.getID()
165
firstEdgeID = incomingRoute[0]
166
routeIDs = []
167
for edges in routes:
168
if edges[0] in edge.getOutgoing().keys():
169
routeID = "%s_%s_%s" % (firstEdgeID, edge.getID(), edges[0].getID())
170
prob = options.turnDefaults[getTurnIndex(edge, edges[0])]
171
edgeIDs = list(incomingRoute) + [e.getID() for e in edges]
172
writeRoute(options, outf, routeID, edgeIDs, edgeStops)
173
routeIDs.append((routeID, prob))
174
175
outf.write(' <rerouter id="rr_%s_%s" edges="%s">\n' %
176
(firstEdgeID, edge.getID(), firstEdgeID))
177
outf.write(' <interval begin="%s" end="%s">\n' % (options.begin, options.end))
178
for routeID, prob in routeIDs:
179
outf.write(' <routeProbReroute id="%s" probability="%s"/>\n' %
180
(routeID, prob))
181
outf.write(' </interval>\n')
182
outf.write(' </rerouter>\n')
183
184
else:
185
# minimal routes: start ahead of an intersection and
186
# continue up to the entry of the next intersection
187
routeIDs = []
188
for edges in routes:
189
if edges[0] in edge.getOutgoing().keys():
190
routeID = "%s_%s" % (edge.getID(), edges[0].getID())
191
prob = options.turnDefaults[getTurnIndex(edge, edges[0])]
192
edgeIDs = [e.getID() for e in [edge] + edges]
193
writeRoute(options, outf, routeID, edgeIDs, edgeStops)
194
routeIDs.append((routeID, prob))
195
if len(routeIDs) > 1:
196
outf.write(' <rerouter id="rr_%s" edges="%s">\n' % (edge.getID(), edge.getID()))
197
outf.write(' <interval begin="%s" end="%s">\n' % (options.begin, options.end))
198
for routeID, prob in routeIDs:
199
outf.write(' <routeProbReroute id="%s" probability="%s"/>\n' %
200
(routeID, prob))
201
outf.write(' </interval>\n')
202
outf.write(' </rerouter>\n')
203
204
outf.write('</additional>\n')
205
206
207
if __name__ == "__main__":
208
main(get_options())
209
210