Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/output/attributeCompare.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 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
33
from sumolib.statistics import Statistics
34
else:
35
sys.exit("please declare environment variable 'SUMO_HOME'")
36
37
38
def get_options():
39
op = sumolib.options.ArgumentParser()
40
op.add_option("datafiles", nargs="+", category="input", type=op.data_file,
41
help="XML files to analyze")
42
op.add_option("-v", "--verbose", action="store_true", default=False,
43
help="Give more output")
44
op.add_option("-e", "--element", help="element to analyze")
45
op.add_option("-a", "--attribute", help="attribute to analyze")
46
op.add_option("-i", "--id-attribute", dest="idAttribute",
47
help="extra attribute to distinguish values")
48
op.add_option("-o", "--xml-output", help="write differences to xml file")
49
op.add_option("-p", "--precision", type=int, default=2, help="Set output precision")
50
options = op.parse_args()
51
52
if options.attribute:
53
options.attribute = options.attribute.split(',')
54
if options.idAttribute:
55
options.idAttribute = options.idAttribute.split(',')
56
if options.element:
57
options.element = options.element.split(',')
58
59
return options
60
61
62
def main():
63
options = get_options()
64
65
def elements(fname):
66
stack = []
67
idStack = []
68
with sumolib.openz(fname, 'rb') as f:
69
for event, node in ET.iterparse(f, events=('start', 'end')):
70
if options.element is not None and node.tag not in options.element:
71
continue
72
if event == 'start':
73
stack.append(node.tag)
74
if options.idAttribute:
75
idStack.append([])
76
for attr in options.idAttribute:
77
if node.get(attr) is not None:
78
idStack[-1].append(node.get(attr))
79
80
else:
81
stack.pop()
82
if options.idAttribute:
83
idStack.pop()
84
continue
85
86
tags = tuple(stack[1:]) if options.element is None else tuple(stack) # exclude root
87
elementDescription = '.'.join(tags)
88
jointID = None
89
if options.idAttribute:
90
for ids in idStack:
91
if ids:
92
jointID = '|'.join(ids)
93
elementDescription += '|' + jointID
94
95
if options.attribute is None:
96
for k, v in node.items():
97
yield elementDescription, k, v, jointID
98
else:
99
for attr in options.attribute:
100
if node.get(attr) is not None or options.element is not None:
101
yield elementDescription, attr, node.get(attr), jointID
102
103
values = defaultdict(lambda: defaultdict(lambda: Statistics(printDev=True))) # elem->attr->values
104
invalidType = defaultdict(set)
105
106
for fname in options.datafiles:
107
for tag, attr, stringVal, jointID in elements(fname):
108
if stringVal is not None:
109
try:
110
if '_' in stringVal:
111
# float() accepts '_' but this doesn't play nice with lane ids
112
raise Exception
113
val = sumolib.miscutils.parseTime(stringVal)
114
values[tag][attr].add(val, jointID)
115
except Exception:
116
invalidType[attr].add(stringVal)
117
118
if options.verbose or options.xml_output is None:
119
for tag in sorted(values.keys()):
120
for attr, stats in sorted(values[tag].items()):
121
stats.label = tag + " " + attr
122
print(stats)
123
124
if invalidType and options.attribute is not None:
125
for attr in sorted(invalidType.keys()):
126
sys.stderr.write(("%s distinct values of attribute '%s' could not be interpreted " +
127
"as numerical value or time. Example values: '%s'\n") %
128
(len(invalidType[attr]), attr, "', '".join(sorted(invalidType[attr])[:10])))
129
130
if options.xml_output is not None:
131
with open(options.xml_output, 'w') as f:
132
root = "attributeCompare"
133
sumolib.writeXMLHeader(f, root=root)
134
for elem_id in sorted(values.keys()):
135
parts = elem_id.split('|')
136
elem = parts[0]
137
f.write(' <%s' % elem)
138
if len(parts) > 1:
139
f.write(' id="%s"' % '|'.join(parts[1:]))
140
f.write('>\n')
141
for attr, stats in sorted(values[elem_id].items()):
142
f.write(stats.toXML(tag=attr, indent=8, label=''))
143
f.write(' </%s>\n' % elem)
144
f.write('</%s>\n' % root)
145
146
147
if __name__ == "__main__":
148
main()
149
150