Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/net/split_at_stops.py
169674 views
1
#!/usr/bin/env python
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2011-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 split_at_stops.py
15
# @author Michael Behrisch
16
# @date 2023-02-07
17
18
"""
19
This script uses an input with stop location to emit an edgediff file with splits
20
at the begin and end of each stop. Optionally it can write the modified network directly
21
and write an adapted stop and route file.
22
"""
23
24
from __future__ import print_function
25
import os
26
import sys
27
import collections
28
import subprocess
29
sys.path.append(os.path.join(os.environ['SUMO_HOME'], 'tools'))
30
import sumolib # noqa
31
32
33
def get_options(args=None):
34
ap = sumolib.options.ArgumentParser()
35
ap.add_argument("stopfile", nargs="+", help="stop files to process")
36
ap.add_argument("-n", "--network", category="input", type=ap.net_file,
37
help="validate positions against this network")
38
ap.add_argument("-r", "--routes", category="input", type=ap.route_file, help="route file to adapt")
39
ap.add_argument("--split-output", category="output", type=ap.edgedata_file,
40
default="splits.edg.xml", help="split file to generate")
41
ap.add_argument("-o", "--output", category="output", type=ap.net_file,
42
default="net.net.xml", help="net file to generate")
43
ap.add_argument("--stop-output", category="output", type=ap.additional_file,
44
default="stops.add.xml", help="stop file to generate")
45
ap.add_argument("--route-output", category="output", type=ap.route_file,
46
default="routes.rou.xml", help="route file to generate")
47
ap.add_argument("--stop-type", default="trainStop", help="which stop types to use")
48
return ap.parse_args(args)
49
50
51
def check_replace(replace_edges, e, offset):
52
replace_edges.setdefault(e, e)
53
new_edge = "%s.%s" % (e, int(offset))
54
if " " + new_edge not in replace_edges[e]:
55
replace_edges[e] += " " + new_edge
56
return new_edge
57
return None
58
59
60
def main(options):
61
locs = collections.defaultdict(list)
62
stops = {}
63
replace_edges = {}
64
types = set(options.stop_type.split(","))
65
for f in options.stopfile:
66
for stop in sumolib.xml.parse(f, ("busStop", "trainStop")):
67
if stop.name in types:
68
locs[stop.lane[:stop.lane.rfind("_")]].append(stop)
69
stops[stop.id] = stop
70
if not locs:
71
print("No stops of type '%s' found." % options.stop_type)
72
return
73
net = sumolib.net.readNet(options.network) if options.network else None
74
with sumolib.openz(options.split_output, "w") as out:
75
sumolib.xml.writeHeader(out, root="edges", schemaPath="edgediff_file.xsd", options=options)
76
for e, sl in locs.items():
77
if len(sl) > 1:
78
skip = set()
79
sorted_stops = list(sorted(sl, key=lambda x: float(x.startPos)))
80
seen = set()
81
prev_end = 0.
82
for s in sorted_stops:
83
if float(s.startPos) == 0.:
84
skip.add(s.id)
85
if float(s.startPos) > prev_end:
86
if len(seen) > 1:
87
print("Skipping overlapping stops %s." % ", ".join(sorted(seen)))
88
skip.update(seen)
89
seen = set()
90
seen.add(s.id)
91
prev_end = max(prev_end, float(s.endPos))
92
if len(seen) > 1:
93
print("Skipping overlapping stops %s." % ", ".join(sorted(seen)))
94
skip.update(seen)
95
print(' <edge id="%s">' % e, file=out)
96
curr_edge = None
97
for s in sorted_stops:
98
start = float(s.startPos)
99
end = float(s.endPos)
100
split = False
101
if s.id not in skip and (not net or net.getEdge(e).getLength() > start):
102
new_edge = check_replace(replace_edges, e, start)
103
if new_edge:
104
print(' <split pos="%s"/>' % start, file=out)
105
prev_split = start
106
stops[s.id].lane = new_edge + s.lane[s.lane.rfind("_"):]
107
stops[s.id].startPos = "0"
108
stops[s.id].endPos = "%.2f" % (end - start)
109
split = True
110
curr_edge = new_edge
111
if curr_edge and not split:
112
stops[s.id].lane = curr_edge + s.lane[s.lane.rfind("_"):]
113
stops[s.id].startPos = "%.2f" % (start - prev_split)
114
stops[s.id].endPos = "%.2f" % (end - prev_split)
115
if s.id not in skip and (not net or net.getEdge(e).getLength() > end + .1):
116
new_edge = check_replace(replace_edges, e, end)
117
if new_edge:
118
print(' <split pos="%s"/>' % end, file=out)
119
prev_split = end
120
curr_edge = new_edge
121
print(' </edge>', file=out)
122
print('</edges>', file=out)
123
if net:
124
subprocess.call([sumolib.checkBinary("netconvert"), "-s", options.network,
125
"-e", out.name, "-o", options.output])
126
with sumolib.openz(options.stop_output, "w") as stop_out:
127
sumolib.xml.writeHeader(stop_out, root="additional", options=options)
128
for s in stops.values():
129
stop_out.write(s.toXML(" "))
130
print('</additional>', file=stop_out)
131
if options.routes:
132
with sumolib.openz(options.routes) as route_in, sumolib.openz(options.route_output, "w") as route_out:
133
for line in route_in:
134
if "<route" in line:
135
eb = line.find('edges="') + 7
136
ee = line.find('"', eb)
137
ll = [replace_edges.get(e, e) for e in line[eb:ee].split()]
138
line = line[:eb] + " ".join(ll) + line[ee:]
139
route_out.write(line)
140
141
142
if __name__ == "__main__":
143
main(get_options())
144
145