Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/district/districtMapper.py
169674 views
1
#!/usr/bin/env python
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2007-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 districtMapper.py
15
# @author Daniel Krajzewicz
16
# @author Michael Behrisch
17
# @author Jakob Erdmann
18
# @author Mirko Barthauer
19
# @date 2007-07-26
20
21
"""
22
Maps the geometry of the districts of two networks by calculating
23
translation and scale parameters from junctions which have been
24
identified by the user as reference points.
25
"""
26
from __future__ import absolute_import
27
from __future__ import print_function
28
import os
29
import sys
30
from xml.sax import make_parser, handler
31
SUMO_HOME = os.environ.get('SUMO_HOME',
32
os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..'))
33
sys.path.append(os.path.join(SUMO_HOME, 'tools'))
34
from sumolib.options import ArgumentParser # noqa
35
36
37
def parseShape(shape):
38
poses = shape.split()
39
ret = []
40
for pos in poses:
41
xy = pos.split(",")
42
ret.append((float(xy[0]), float(xy[1])))
43
return ret
44
45
46
# All members are "private".
47
class JunctionPositionsReader(handler.ContentHandler):
48
49
def __init__(self):
50
self._xPos = {}
51
self._yPos = {}
52
53
def startElement(self, name, attrs):
54
if name == 'junction':
55
self._xPos[attrs['id']] = float(attrs['x'])
56
self._yPos[attrs['id']] = float(attrs['y'])
57
58
def getJunctionXPoses(self, junctions1):
59
ret = []
60
for junction in junctions1:
61
ret.append(self._xPos[junction])
62
return ret
63
64
def getJunctionYPoses(self, junctions1):
65
ret = []
66
for junction in junctions1:
67
ret.append(self._yPos[junction])
68
return ret
69
70
71
# All members are "private".
72
class DistrictMapper(handler.ContentHandler):
73
74
def __init__(self):
75
self._haveDistrict = False
76
self._parsingDistrictShape = False
77
self._districtShapes = {}
78
self._currentID = ""
79
self._shape = ""
80
81
def startElement(self, name, attrs):
82
if name == 'taz':
83
self._haveDistrict = True
84
self._currentID = attrs['id']
85
if 'shape' in attrs:
86
self._shape = attrs['shape']
87
elif name == 'shape' and self._haveDistrict:
88
self._parsingDistrictShape = True
89
90
def characters(self, content):
91
if self._parsingDistrictShape:
92
self._shape += content
93
94
def endElement(self, name):
95
if name == 'taz':
96
self._haveDistrict = False
97
if self._shape != '':
98
self._districtShapes[self._currentID] = parseShape(self._shape)
99
self._shape = ""
100
elif name == 'shape' and self._haveDistrict:
101
self._parsingDistrictShape = False
102
103
def convertShapes(self, xoff1, xoff2, xscale, yoff1, yoff2, yscale):
104
for district in self._districtShapes:
105
shape = self._districtShapes[district]
106
nshape = []
107
for i in range(0, len(shape)):
108
nx = ((shape[i][0] - xoff1) * xscale + xoff2)
109
ny = ((shape[i][1] - yoff1) * yscale + yoff2)
110
nshape.append((nx, ny))
111
self._districtShapes[district] = nshape
112
113
def writeResults(self, output, color, polyoutput):
114
fd = open(output, "w")
115
fd.write("<tazs>\n")
116
for district in self._districtShapes:
117
shape = self._districtShapes[district]
118
shapeStr = " ".join(["%s,%s" % s for s in shape])
119
fd.write(' <taz id="%s" shape="%s"/>\n' % (district, shapeStr))
120
fd.write("</tazs>\n")
121
fd.close()
122
if polyoutput:
123
fd = open(polyoutput, "w")
124
fd.write("<shapes>\n")
125
for district in self._districtShapes:
126
shape = self._districtShapes[district]
127
shapeStr = " ".join(["%s,%s" % s for s in shape])
128
fd.write(' <poly id="%s" color="%s" shape="%s"/>\n' %
129
(district, color, shapeStr))
130
fd.write("</shapes>\n")
131
fd.close()
132
133
134
if __name__ == "__main__":
135
ap = ArgumentParser()
136
ap.add_argument("-v", "--verbose", action="store_true",
137
default=False, help="tell me what you are doing")
138
ap.add_argument("-1", "--net-file1", dest="netfile1", category="input", type=ap.net_file, required=True,
139
help="read first SUMO network from FILE (mandatory)", metavar="FILE")
140
ap.add_argument("-2", "--net-file2", dest="netfile2", category="input", type=ap.net_file, required=True,
141
help="read second SUMO network from FILE (mandatory)", metavar="FILE")
142
ap.add_argument("-o", "--output", default="districts.add.xml", category="output", type=ap.file,
143
help="write resulting districts to FILE (default: %(default)s)", metavar="FILE")
144
ap.add_argument("-p", "--polyoutput", category="output", type=ap.file,
145
help="write districts as polygons to FILE", metavar="FILE")
146
ap.add_argument("-a", "--junctions1", type=str, required=True,
147
help="list of junction ids to use from first network (mandatory)")
148
ap.add_argument("-b", "--junctions2", type=str, required=True,
149
help="list of junction ids to use from second network (mandatory)")
150
ap.add_argument("--color", default="1,0,0", type=str,
151
help="Assign this color to districts (default: %(default)s)")
152
options = ap.parse_args()
153
parser = make_parser()
154
if options.verbose:
155
print("Reading net#1")
156
reader1 = JunctionPositionsReader()
157
parser.setContentHandler(reader1)
158
parser.parse(options.netfile1)
159
if options.verbose:
160
print("Reading net#2")
161
reader2 = JunctionPositionsReader()
162
parser.setContentHandler(reader2)
163
parser.parse(options.netfile2)
164
165
junctions1 = options.junctions1.split(",")
166
junctions2 = options.junctions2.split(",")
167
xposes1 = reader1.getJunctionXPoses(junctions1)
168
yposes1 = reader1.getJunctionYPoses(junctions1)
169
xposes2 = reader2.getJunctionXPoses(junctions2)
170
yposes2 = reader2.getJunctionYPoses(junctions2)
171
172
xmin1 = min(xposes1)
173
xmax1 = max(xposes1)
174
ymin1 = min(yposes1)
175
ymax1 = max(yposes1)
176
xmin2 = min(xposes2)
177
xmax2 = max(xposes2)
178
ymin2 = min(yposes2)
179
ymax2 = max(yposes2)
180
181
width1 = xmax1 - xmin1
182
height1 = ymax1 - ymin1
183
width2 = xmax2 - xmin2
184
height2 = ymax2 - ymin2
185
186
reader = DistrictMapper()
187
parser.setContentHandler(reader)
188
parser.parse(options.netfile1)
189
reader.convertShapes(
190
xmin1, xmin2, width1 / width2, ymin1, ymin2, height1 / height2)
191
reader.writeResults(options.output, options.color, options.polyoutput)
192
193