Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/route/routeStats.py
169674 views
1
#!/usr/bin/env python
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2014-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 routeStats.py
15
# @author Jakob Erdmann
16
# @date 2014-12-18
17
18
"""
19
Compute statistics on route lengths for a single route or
20
for the length-difference between two sets of routes.
21
Routes must be children of <vehicle> elements and when comparing two sets of
22
routes, the same vehicle ids must appear.
23
24
If option --edges is given, all edge ids from that file are retrieved and a
25
statistic is conducted on how many of these edges appear per route
26
"""
27
from __future__ import absolute_import
28
from __future__ import print_function
29
30
import os
31
import sys
32
33
if 'SUMO_HOME' in os.environ:
34
sys.path.append(os.path.join(os.environ['SUMO_HOME'], 'tools'))
35
else:
36
sys.exit("please declare environment variable 'SUMO_HOME'")
37
import sumolib # noqa
38
from sumolib.miscutils import Statistics, parseTime # noqa
39
40
41
def get_options():
42
USAGE = """Usage %(prog)s [options] <rou.xml> [<rou2.xml>]"""
43
optParser = sumolib.options.ArgumentParser(usage=USAGE)
44
optParser.add_option("-v", "--verbose", action="store_true",
45
default=False, help="Give more output")
46
optParser.add_option("-f", "--fast", action="store_true",
47
default=False, help="Use fast xml parser (does not work with length and numEdges)")
48
optParser.add_option("-n", "--network",
49
help="The network file to use with attribute 'length'")
50
optParser.add_option("-e", "--edges-file", dest="edgesFile",
51
help="The edges file (.edg.xml or edgedata) for retrieving relevant edge ids")
52
optParser.add_option("-a", "--attribute", default="length",
53
help="attribute to analyze [length,depart,numEdges,duration,routeLength,speed,speedKmh]")
54
optParser.add_option("-b", "--binwidth", type=float,
55
default=500, help="binning width of result histogram")
56
optParser.add_option("-l", "--minlength", type=float,
57
default=0, help="only use routes with a minimum route length")
58
optParser.add_option("--hist-output", help="output file for histogram (gnuplot compatible)")
59
optParser.add_option("--full-output", help="output file for full data dump")
60
options, args = optParser.parse_known_args()
61
62
if len(args) not in (1, 2):
63
sys.exit(USAGE)
64
65
options.routeFile2 = None
66
if len(args) >= 1:
67
options.routeFile = args[0]
68
if len(args) == 2:
69
options.routeFile2 = args[1]
70
if options.edgesFile:
71
options.attribute = "edge visit"
72
if options.attribute == "length" and not options.network:
73
print("Option --network must be set when computing length statistics", file=sys.stderr)
74
sys.exit(1)
75
76
return options
77
78
79
def main():
80
options = get_options()
81
net = None
82
if options.edgesFile:
83
edgeSet = set([e.id for e in sumolib.xml.parse_fast(options.edgesFile, 'edge', ['id'])])
84
85
def attribute_retriever(vehicle): # noqa
86
return len([e for e in vehicle.route[0].edges.split() if e in edgeSet])
87
elif options.attribute == "length":
88
net = sumolib.net.readNet(options.network)
89
90
def attribute_retriever(vehicle): # noqa
91
return sum([net.getEdge(e).getLength() for e in vehicle.route[0].edges.split()])
92
elif options.attribute == "depart":
93
def attribute_retriever(vehicle):
94
return parseTime(vehicle.depart)
95
elif options.attribute == "numEdges":
96
def attribute_retriever(vehicle):
97
return len(vehicle.route[0].edges.split())
98
elif options.attribute == "duration":
99
def attribute_retriever(vehicle):
100
return parseTime(vehicle.arrival) - parseTime(vehicle.depart)
101
elif options.attribute == "routeLength":
102
def attribute_retriever(vehicle):
103
return float(vehicle.routeLength)
104
elif options.attribute == "speed":
105
def attribute_retriever(vehicle):
106
return float(vehicle.routeLength) / (parseTime(vehicle.arrival) - parseTime(vehicle.depart))
107
elif options.attribute == "speedKmh":
108
def attribute_retriever(vehicle):
109
return 3.6 * float(vehicle.routeLength) / (parseTime(vehicle.arrival) - parseTime(vehicle.depart))
110
else:
111
raise ValueError("Invalid value '%s' for option --attribute" % options.attribute)
112
113
lengths = {}
114
lengths2 = {}
115
116
if options.routeFile2 is None:
117
# write statistics on a single route file
118
stats = Statistics("route %ss" % options.attribute, histogram=True, scale=options.binwidth)
119
120
if options.fast:
121
def parse(routes):
122
return sumolib.xml.parse_fast(routes, 'vehicle', ['id', 'depart', 'arrival', 'routeLength'])
123
else:
124
def parse(routes):
125
return sumolib.xml.parse(routes, 'vehicle')
126
127
for vehicle in parse(options.routeFile):
128
if vehicle.routeLength is None or float(vehicle.routeLength) >= options.minlength:
129
length = attribute_retriever(vehicle)
130
if options.routeFile2 is None:
131
stats.add(length, vehicle.id)
132
lengths[vehicle.id] = length
133
134
if options.routeFile2 is not None:
135
# compare route lengths between two files
136
stats = Statistics("route %s difference" % options.attribute, histogram=True, scale=options.binwidth)
137
for vehicle in parse(options.routeFile2):
138
if vehicle.routeLength is None or float(vehicle.routeLength) >= options.minlength:
139
lengths2[vehicle.id] = attribute_retriever(vehicle)
140
stats.add(lengths2[vehicle.id] - lengths[vehicle.id], vehicle.id)
141
print(stats)
142
143
if options.hist_output is not None:
144
with open(options.hist_output, 'w') as f:
145
for bin, count in stats.histogram():
146
f.write("%s %s\n" % (bin, count))
147
148
if options.full_output is not None:
149
with open(options.full_output, 'w') as f:
150
if options.routeFile2 is None:
151
data = [(v, k) for k, v in lengths.items()]
152
else:
153
data = [(lengths2[id] - lengths[id], id)
154
for id in lengths.keys()]
155
for val, id in sorted(data):
156
f.write("%s %s\n" % (val, id))
157
158
159
if __name__ == "__main__":
160
main()
161
162