Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/output/stopOrder.py
169674 views
1
#!/usr/bin/env python
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2012-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 stopOrder.py
15
# @author Jakob Erdmann
16
# @date 2020-08-25
17
18
"""
19
Compare ordering of vehicle departure at stops based on a route file with until
20
times (ground truth) and stop-output
21
"""
22
23
from __future__ import absolute_import
24
from __future__ import print_function
25
26
import os
27
import sys
28
from collections import defaultdict
29
30
if 'SUMO_HOME' in os.environ:
31
sys.path.append(os.path.join(os.environ['SUMO_HOME'], 'tools'))
32
import sumolib # noqa
33
from sumolib.miscutils import parseTime, humanReadableTime # noqa
34
from sumolib.xml import parse # noqa
35
36
37
def get_options(args=None):
38
parser = sumolib.options.ArgumentParser(description="Sample routes to match counts")
39
parser.add_argument("-r", "--route-file", dest="routeFile",
40
help="Input route file")
41
parser.add_argument("-s", "--stop-file", dest="stopFile",
42
help="Input stop-output file")
43
parser.add_argument("-H", "--human-readable-time", dest="hrTime", action="store_true", default=False,
44
help="Write time values as hour:minute:second or day:hour:minute:second rathern than seconds")
45
parser.add_argument("-v", "--verbose", action="store_true",
46
default=False, help="tell me what you are doing")
47
48
options = parser.parse_args(args=args)
49
if options.routeFile is None or options.stopFile is None:
50
parser.print_help()
51
sys.exit()
52
53
return options
54
55
56
def main(options):
57
58
def formatVehCode(code):
59
time, veh = code
60
if options.hrTime:
61
time = humanReadableTime(time)
62
return "%s (plan=%s)" % (veh, time)
63
64
# stop (stoppingPlaceID or (lane, pos)) -> [(depart1, veh1), (depart2, veh2), ...]
65
expected_departs = defaultdict(list)
66
actual_departs = defaultdict(dict)
67
ignored_stops = 0
68
parsed_stops = 0
69
70
for vehicle in parse(options.routeFile, ['vehicle', 'trip'], heterogeneous=True):
71
if vehicle.stop is not None:
72
for stop in vehicle.stop:
73
if stop.hasAttribute("until"):
74
if stop.hasAttribute("busStop"):
75
stopCode = stop.busStop
76
else:
77
stopCode = "%s,%s" % (stop.lane, stop.endPos)
78
expected_departs[stopCode].append((parseTime(stop.until), vehicle.id))
79
parsed_stops += 1
80
else:
81
ignored_stops += 1
82
83
print("Parsed %s expected stops at %s locations" % (parsed_stops, len(expected_departs)))
84
if ignored_stops > 0:
85
sys.stderr.write("Ignored %s stops without 'until' attribute\n" % ignored_stops)
86
87
output_stops = 0
88
for stop in parse(options.stopFile, "stopinfo", heterogeneous=True):
89
if stop.hasAttribute("busStop"):
90
stopCode = stop.busStop
91
else:
92
stopCode = (stop.lane, stop.endPos)
93
ended = parseTime(stop.ended)
94
until = ended - parseTime(stop.delay)
95
actual_departs[stopCode][(until, stop.id)] = ended
96
output_stops += 1
97
print("Parsed %s actual stops at %s locations" % (output_stops, len(actual_departs)))
98
99
missing = defaultdict(list)
100
for stopCode in sorted(expected_departs.keys()):
101
vehicles = expected_departs[stopCode]
102
if stopCode in actual_departs:
103
actual_vehicles = actual_departs[stopCode]
104
comparable_expected = []
105
comparable_actual = []
106
for vehCode in vehicles:
107
if vehCode in actual_vehicles:
108
comparable_expected.append(vehCode)
109
comparable_actual.append((actual_vehicles[vehCode], vehCode)) # (ended, (until, vehID))
110
else:
111
missing[stopCode].append(vehCode)
112
comparable_expected.sort()
113
comparable_actual.sort()
114
num_unexpected = len(actual_vehicles) - len(comparable_actual)
115
if num_unexpected > 0:
116
print("Found %s unexpected stops at %s" % (num_unexpected, stopCode))
117
118
# after sorting, discard the 'ended' attribute and only keep vehCode
119
comparable_actual2 = [v[1] for v in comparable_actual]
120
121
# find and remove duplicate
122
tmp = []
123
for vehCode in comparable_expected:
124
if len(tmp) != 0:
125
if vehCode != tmp[-1]:
126
tmp.append(vehCode)
127
else:
128
if options.verbose:
129
print("Found duplicate stop at %s for vehicle %s" % (stopCode, vehCode))
130
comparable_actual2.remove(vehCode)
131
else:
132
tmp.append(vehCode)
133
134
comparable_expected = tmp
135
136
if options.verbose:
137
actual = [(v[0], v[1][1]) for v in comparable_actual]
138
print("stop:", stopCode)
139
print(" expected:", comparable_expected)
140
print(" actual:", actual)
141
142
for i, vehCode in enumerate(comparable_expected):
143
j = comparable_actual2.index(vehCode)
144
indexInStops = ""
145
if options.verbose:
146
indexInStops = " Index in stops: ex=%s act=%s" % (i, j)
147
148
if i < j:
149
print("At %s vehicle %s comes after %s.%s" % (
150
stopCode, formatVehCode(vehCode),
151
','.join(map(formatVehCode, comparable_actual2[i:j])),
152
indexInStops
153
))
154
elif j < i:
155
print("At %s vehicle %s comes before %s.%s" % (
156
stopCode, formatVehCode(vehCode),
157
','.join(map(formatVehCode, comparable_actual2[j:i])),
158
indexInStops
159
))
160
if i != j:
161
# swap to avoid duplicate out-of-order warnings
162
tmp = comparable_actual2[i]
163
comparable_actual2[i] = comparable_actual2[j]
164
comparable_actual2[j] = tmp
165
else:
166
missing[stopCode] = vehicles
167
168
print("Simulation missed %s stops at %s locations" % (sum(map(len, missing.values())), len(missing)))
169
170
171
if __name__ == "__main__":
172
main(get_options())
173
174