Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/net/remap_attributes.py
428331 views
1
#!/usr/bin/env python
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2009-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 remap_attributes.py
15
# @author Jakob Erdmann
16
# @date 2026-02-26
17
18
from __future__ import print_function
19
from __future__ import absolute_import
20
import os
21
import sys
22
import subprocess
23
24
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
25
import sumolib # noqa
26
from sumolib.xml import parse # noqa
27
from sumolib.net import lane2edge, lane2index # noqa
28
29
30
def get_options(args=None):
31
ap = sumolib.options.ArgumentParser(description="Remap edge attributes from one network to another")
32
ap.add_argument("--orig-net", dest="origNet", required=True, category="input", type=ap.net_file,
33
help="SUMO network for reading attributes", metavar="FILE")
34
ap.add_argument("--target-net", dest="targetNet", required=True, category="input", type=ap.net_file,
35
help="SUMO network for receiving attributes", metavar="FILE")
36
ap.add_argument("-o", "--output-file", dest="output", required=True, category="output", type=ap.net_file,
37
help="File for writing the patched network", metavar="FILE")
38
ap.add_argument("-p", "--patch-file-prefix", dest="patchPrefix", category="output", default="patch",
39
help="prefix for patch files")
40
ap.add_argument("-a", "--attributes", required=True,
41
help="the list of edge attributes that shall be transferred")
42
gp = ap.add_mutually_exclusive_group(required=False)
43
gp.add_argument("--osm.origid", action="store_true", dest="osmOrigId", default=False,
44
help="match objects based on OSM ids (stored in params from --output.original-names)")
45
gp.add_argument("--origid", action="store_true", dest="origId", default=False,
46
help="match objects based on origIDs after renaming")
47
ap.add_argument("-v", "--verbose", action="store_true", dest="verbose",
48
default=False, help="tell me what you are doing")
49
50
options = ap.parse_args()
51
52
options.attributes = options.attributes.split(',')
53
options.edgfile = options.patchPrefix + ".edg.xml"
54
return options
55
56
57
class Ambiguous:
58
pass
59
60
61
# override canonical method name if defaults if needed
62
RETRIEVERS = {}
63
64
65
def getAttr(edge, a):
66
method = RETRIEVERS.get(a, 'get' + a[0].upper() + a[1:])
67
return getattr(edge, method)()
68
69
70
def getAttrs(options, edge):
71
return [getAttr(edge, a) for a in options.attributes]
72
73
74
def main(options):
75
if options.verbose:
76
print("Reading orig-net '%s'" % options.origNet)
77
net = sumolib.net.readNet(options.origNet)
78
if options.verbose:
79
print("Reading target-net '%s'" % options.targetNet)
80
net2 = sumolib.net.readNet(options.targetNet)
81
82
lookup = {} # origId -> values
83
if options.osmOrigId:
84
# lookup is based on edge-origIds in orig-net
85
for edge in net.getEdges():
86
lane = edge.getLanes()[0]
87
origIds = lane.getParam("origId").split()
88
attrs = getAttrs(options, edge)
89
for origId in origIds:
90
attrs2 = lookup.get(origId)
91
if attrs2 is None:
92
lookup[origId] = attrs
93
else:
94
attrs3 = []
95
for a, v, v2 in zip(options.attributes, attrs, attrs2):
96
if v == v2 or v2 is None:
97
attrs3.append(v)
98
elif v is None:
99
attrs3.append(v2)
100
else:
101
print("Ambiguous attribute '%s' for origId '%s' (%s != %s)" % (
102
a, origId, v, v2), file=sys.stderr)
103
attrs3.append(Ambiguous)
104
lookup[origId] = attrs3
105
elif options.origId:
106
# loopup is based on edge ids in orig-net
107
for edge in net.getEdges():
108
attrs = getAttrs(options, edge)
109
lookup[edge.getID()] = attrs
110
else:
111
print("Geometrical matching not yet implemented", file=sys.stderr)
112
sys.exit(1)
113
114
patchedValues = {} # edgeID -> new attributes
115
if options.osmOrigId or options.origId:
116
for edge in net2.getEdges():
117
lane = edge.getLanes()[0]
118
origIds = lane.getParam("origId")
119
if origIds is None:
120
continue
121
origIds = origIds.split()
122
attrs = getAttrs(options, edge)
123
attrs2 = []
124
for i, a in enumerate(options.attributes):
125
v = None
126
for origId in origIds:
127
if origId in lookup:
128
v2 = lookup[origId][i]
129
if v2 == Ambiguous:
130
v = None
131
break
132
if v is None:
133
v = v2
134
elif v2 is not None and v != v2:
135
print("Ambiguous attribute '%s' for edge '%s' with origIds '%s' (%s != %s)" % (
136
a, edge.getID(), origIds, v, v2), file=sys.stderr)
137
v = None
138
break
139
if v == attrs[i]:
140
# no need to patch if the attribute is already correct
141
v = None
142
attrs2.append(v)
143
if any([v is not None for v in attrs2]):
144
patchedValues[edge.getID()] = attrs2
145
146
with sumolib.openz(options.edgfile, 'w') as fout:
147
sumolib.writeXMLHeader(fout, "$Id$", "edges", schemaPath="edgediff_file.xsd", options=options)
148
for edgeID in sorted(patchedValues.keys()):
149
validAttrs = [(a, v) for a, v in zip(options.attributes, patchedValues[edgeID]) if v is not None]
150
fout.write(' <edge id="%s" %s/>\n' % (
151
edgeID, ' '.join(['%s="%s"' % av for av in validAttrs])))
152
fout.write('</edges>\n')
153
154
NETCONVERT = sumolib.checkBinary('netconvert')
155
subprocess.call([NETCONVERT,
156
'-s', options.targetNet,
157
'-e', options.edgfile,
158
'-o', options.output])
159
160
161
if __name__ == "__main__":
162
main(get_options())
163
164