Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/output/attributeCompare.py
193673 views
1
#!/usr/bin/env python
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2014-2026 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 attributeCompare.py
15
# @author Jakob Erdmann
16
# @date 2023-04-13
17
18
"""
19
compute statistics on attributes over multiple files
20
"""
21
from __future__ import absolute_import
22
from __future__ import print_function
23
24
import os
25
import sys
26
from collections import defaultdict
27
from lxml import etree as ET
28
29
if 'SUMO_HOME' in os.environ:
30
tools = os.path.join(os.environ['SUMO_HOME'], 'tools')
31
sys.path.append(os.path.join(tools))
32
import sumolib # noqa
33
from sumolib.statistics import Statistics # noqa
34
from sumolib.miscutils import short_names # noqa
35
else:
36
sys.exit("please declare environment variable 'SUMO_HOME'")
37
38
FILE_ATTR = "@FILE"
39
40
41
def get_options():
42
op = sumolib.options.ArgumentParser()
43
op.add_option("datafiles", nargs="+", category="input", type=op.data_file,
44
help="XML files to analyze")
45
op.add_option("-v", "--verbose", action="store_true", default=False,
46
help="Give more output")
47
op.add_option("-e", "--element", help="element to analyze")
48
op.add_option("-a", "--attribute", help="attribute to analyze")
49
op.add_option("-i", "--id-attribute", dest="idAttribute",
50
help="extra attribute to distinguish values")
51
op.add_option("-l", "--label-attribute", dest="labelAttribute",
52
help="extra attribute to label values")
53
op.add_option("-o", "--xml-output", help="write differences to xml file")
54
op.add_option("-p", "--precision", type=int, default=2, help="Set output precision")
55
options = op.parse_args()
56
57
if options.attribute:
58
options.attribute = options.attribute.split(',')
59
if options.idAttribute:
60
options.idAttribute = options.idAttribute.split(',')
61
if options.labelAttribute:
62
options.labelAttribute = options.labelAttribute.split(',')
63
if options.element:
64
options.element = options.element.split(',')
65
66
options.shortName = dict(zip(options.datafiles, short_names(options.datafiles, True)))
67
68
return options
69
70
71
def main():
72
options = get_options()
73
74
def elements(fname):
75
stack = []
76
idStack = []
77
labelStack = []
78
with sumolib.openz(fname, 'rb') as f:
79
for event, node in ET.iterparse(f, events=('start', 'end')):
80
if options.element is not None and node.tag not in options.element:
81
continue
82
if event == 'start':
83
stack.append(node.tag)
84
if options.idAttribute:
85
idStack.append([])
86
for attr in options.idAttribute:
87
if node.get(attr) is not None:
88
idStack[-1].append(node.get(attr))
89
elif len(idStack) == 1 and attr == FILE_ATTR:
90
idStack[-1].append(options.shortName[fname])
91
if options.labelAttribute:
92
labelStack.append([])
93
for attr in options.labelAttribute:
94
if node.get(attr) is not None:
95
labelStack[-1].append(node.get(attr))
96
elif len(labelStack) == 1 and attr == FILE_ATTR:
97
labelStack[-1].append(options.shortName[fname])
98
99
else:
100
stack.pop()
101
if options.idAttribute:
102
idStack.pop()
103
if options.labelAttribute:
104
labelStack.pop()
105
continue
106
107
tags = tuple(stack[1:]) if options.element is None else tuple(stack) # exclude root
108
elementDescription = '.'.join(tags)
109
jointID = None
110
if options.idAttribute:
111
for ids in idStack:
112
if ids:
113
jointID = '|'.join(ids)
114
elementDescription += '|' + jointID
115
if options.labelAttribute:
116
for ids in labelStack:
117
if ids:
118
jointID = '|'.join(ids)
119
120
if options.attribute is None:
121
for k, v in node.items():
122
yield elementDescription, k, v, jointID
123
else:
124
for attr in options.attribute:
125
if node.get(attr) is not None or options.element is not None:
126
yield elementDescription, attr, node.get(attr), jointID
127
128
values = defaultdict(lambda: defaultdict(lambda: Statistics(printDev=True))) # elem->attr->values
129
invalidType = defaultdict(set)
130
131
for fname in options.datafiles:
132
for tag, attr, stringVal, jointID in elements(fname):
133
if stringVal is not None:
134
try:
135
if '_' in stringVal:
136
# float() accepts '_' but this doesn't play nice with lane ids
137
raise Exception
138
val = sumolib.miscutils.parseTime(stringVal)
139
values[tag][attr].add(val, jointID)
140
except Exception:
141
invalidType[attr].add(stringVal)
142
143
if options.verbose or options.xml_output is None:
144
for tag in sorted(values.keys()):
145
for attr, stats in sorted(values[tag].items()):
146
stats.label = tag + " " + attr
147
print(stats)
148
149
if invalidType and options.attribute is not None:
150
for attr in sorted(invalidType.keys()):
151
sys.stderr.write(("%s distinct values of attribute '%s' could not be interpreted " +
152
"as numerical value or time. Example values: '%s'\n") %
153
(len(invalidType[attr]), attr, "', '".join(sorted(invalidType[attr])[:10])))
154
155
if options.xml_output is not None:
156
with open(options.xml_output, 'w') as f:
157
root = "attributeCompare"
158
sumolib.writeXMLHeader(f, root=root)
159
for elem_id in sorted(values.keys()):
160
parts = elem_id.split('|')
161
elem = parts[0]
162
f.write(' <%s' % elem)
163
if len(parts) > 1:
164
f.write(' id="%s"' % '|'.join(parts[1:]))
165
f.write('>\n')
166
for attr, stats in sorted(values[elem_id].items()):
167
f.write(stats.toXML(tag=attr, indent=8, label=''))
168
f.write(' </%s>\n' % elem)
169
f.write('</%s>\n' % root)
170
171
172
if __name__ == "__main__":
173
main()
174
175