Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/edgesInDistricts.py
169660 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 edgesInDistricts.py
15
# @author Daniel Krajzewicz
16
# @author Michael Behrisch
17
# @author Jakob Erdmann
18
# @date 2007-07-26
19
20
"""
21
Parsing a number of networks and taz (district) files with shapes
22
this script writes a taz file with all the edges which are inside
23
the relevant taz.
24
"""
25
from __future__ import print_function
26
from __future__ import absolute_import
27
import sys
28
import argparse
29
import collections
30
import sumolib
31
32
33
# written into the net. All members are "private".
34
class DistrictEdgeComputer:
35
36
def __init__(self, net):
37
self._net = net
38
self._districtEdges = collections.defaultdict(list)
39
self._edgeDistricts = collections.defaultdict(list)
40
self._invalidatedEdges = set()
41
42
def computeWithin(self, polygons, options):
43
districtBoxes = {}
44
for district in polygons:
45
districtBoxes[district.id] = district.getBoundingBox()
46
for idx, edge in enumerate(self._net.getEdges()):
47
shape = edge.getShape()
48
if (edge.getSpeed() < options.maxspeed and edge.getSpeed() > options.minspeed and
49
(options.internal or edge.getFunction() != "internal")):
50
if options.vclass is None or edge.allows(options.vclass):
51
if options.assign_from:
52
xmin, ymin = shape[0]
53
xmax, ymax = shape[0]
54
else:
55
xmin, ymin, xmax, ymax = edge.getBoundingBox()
56
for district in polygons:
57
dxmin, dymin, dxmax, dymax = districtBoxes[district.id]
58
if dxmin <= xmax and dymin <= ymax and dxmax >= xmin and dymax >= ymin:
59
if options.assign_from:
60
if sumolib.geomhelper.isWithin(shape[0], district.shape):
61
self._districtEdges[district].append(edge)
62
self._edgeDistricts[edge].append(district)
63
break
64
else:
65
for pos in shape:
66
if sumolib.geomhelper.isWithin(pos, district.shape):
67
self._districtEdges[
68
district].append(edge)
69
self._edgeDistricts[
70
edge].append(district)
71
break
72
if options.verbose and idx % 100 == 0:
73
sys.stdout.write("%s/%s\r" % (idx, len(self._net.getEdges())))
74
if options.complete:
75
for edge in self._edgeDistricts:
76
if len(self._edgeDistricts[edge]) > 1:
77
self._invalidatedEdges.add(edge)
78
79
def getEdgeDistrictMap(self):
80
result = {}
81
for edge, districts in self._edgeDistricts.items():
82
if len(districts) == 1:
83
result[edge] = districts[0]
84
return result
85
86
def writeResults(self, options):
87
fd = sumolib.openz(options.output, mode="w")
88
sumolib.xml.writeHeader(fd, "$Id$", "tazs", "taz_file.xsd")
89
lastId = None
90
lastEdges = set()
91
key = (lambda i: i[0].attributes[options.merge_param]) if options.merge_param else None
92
for idx, (district, edges) in enumerate(sorted(self._districtEdges.items(), key=key)):
93
filtered = [edge for edge in edges if edge not in self._invalidatedEdges and edge.getLength() >
94
options.min_length]
95
if len(filtered) == 0:
96
print("District '" + district.id + "' has no edges!")
97
else:
98
color = ' color="%s"' % district.color if district.color is not None else ''
99
if options.weighted:
100
if options.shapeinfo:
101
fd.write(' <taz id="%s" shape="%s"%s>\n' %
102
(district.id, district.getShapeString(), color))
103
else:
104
fd.write(' <taz id="%s">\n' % district.id)
105
for edge in filtered:
106
weight = edge.getSpeed() * edge.getLength()
107
fd.write(
108
' <tazSource id="%s" weight="%.2f"/>\n' % (edge.getID(), weight))
109
fd.write(
110
' <tazSink id="%s" weight="%.2f"/>\n' % (edge.getID(), weight))
111
fd.write(u" </taz>\n")
112
else:
113
if options.shapeinfo:
114
fd.write(' <taz id="%s" shape="%s"%s edges="%s"/>\n' %
115
(district.id, district.getShapeString(), color,
116
" ".join([e.getID() for e in filtered])))
117
else:
118
currentId = district.id
119
if options.merge_param is not None:
120
currentId = district.attributes[options.merge_param]
121
if options.merge_separator is not None and options.merge_separator in currentId:
122
currentId = currentId[:currentId.index(options.merge_separator)]
123
if lastId is not None:
124
if lastId == currentId:
125
lastEdges.update(filtered)
126
else:
127
fd.write(' <taz id="%s"%s edges="%s"/>\n' %
128
(lastId, color, " ".join(sorted([e.getID() for e in lastEdges]))))
129
lastId = None
130
if lastId is None:
131
lastId = currentId
132
lastEdges = set(filtered)
133
if options.verbose and idx % 100 == 0:
134
sys.stdout.write("%s/%s\r" % (idx, len(self._districtEdges)))
135
if lastId is not None:
136
fd.write(' <taz id="%s" edges="%s"/>\n' % (lastId, " ".join(sorted([e.getID() for e in lastEdges]))))
137
fd.write(u"</tazs>\n")
138
fd.close()
139
140
def getTotalLength(self, edgeID):
141
edge = self._net.getEdge(edgeID)
142
return edge.getLength() * edge.getLaneNumber()
143
144
145
def fillOptions(argParser):
146
argParser.add_argument("-v", "--verbose", category="processing", action="store_true",
147
default=False, help="tell me what you are doing")
148
argParser.add_argument("--complete", category="processing", action="store_true",
149
default=False, help="assign edges only if they are not in more than one district")
150
argParser.add_argument("-n", "--net-file", category="input",
151
help="read SUMO network from FILE (mandatory)", metavar="FILE")
152
argParser.add_argument("-t", "--taz-files", category="input",
153
help="read districts from FILEs", metavar="FILE")
154
argParser.add_argument("-o", "--output", category="output", default="districts.taz.xml",
155
help="write results to FILE", metavar="FILE")
156
argParser.add_argument("-x", "--max-speed", type=float, dest="maxspeed", category="processing",
157
default=1000.0, help="use lanes where speed is not greater than this (m/s)")
158
argParser.add_argument("-m", "--min-speed", type=float, dest="minspeed", category="processing",
159
default=0., help="use lanes where speed is greater than this (m/s)")
160
argParser.add_argument("-w", "--weighted", action="store_true", category="processing",
161
default=False, help="Weights sources/sinks by lane number and length")
162
argParser.add_argument("-f", "--assign-from", action="store_true", category="processing",
163
default=False, help="Assign the edge always to the district where the 'from' node " +
164
"is located")
165
argParser.add_argument("-i", "--internal", action="store_true", category="processing",
166
default=False, help="Include internal edges in output")
167
argParser.add_argument("-l", "--vclass", category="processing", help="Include only edges allowing VCLASS")
168
argParser.add_argument("-s", "--shapeinfo", category="processing", action="store_true",
169
default=False, help="write also the shape info in the file")
170
argParser.add_argument("--merge-separator", category="processing",
171
help="merge edge lists of taz starting with the same string up to the given separator")
172
argParser.add_argument("--merge-param", category="processing",
173
help="merge edge lists of taz/polygons having the same value for the given parameter")
174
argParser.add_argument("--min-length", type=float, category="processing",
175
default=0., help="use edges where length is greater than this (m)")
176
177
178
def parse_args(args=None):
179
argParser = sumolib.options.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
180
fillOptions(argParser)
181
return argParser.parse_args(args), argParser
182
183
184
if __name__ == "__main__":
185
options, argParser = parse_args()
186
if not options.net_file:
187
argParser.print_help()
188
argParser.exit("Error! Providing a network is mandatory")
189
190
if options.verbose:
191
print("Reading net '" + options.net_file + "'")
192
nets = options.net_file.split(",")
193
if len(nets) > 1:
194
print("Warning! Multiple networks specified. Parsing the first one for edges and tazs, the others for " +
195
"taz only.")
196
dec = DistrictEdgeComputer(sumolib.net.readNet(nets[0]))
197
tazFiles = nets + options.taz_files.split(",")
198
polygons = sumolib.shapes.polygon.read(tazFiles, includeTaz=True)
199
if options.verbose:
200
print("Calculating")
201
dec.computeWithin(polygons, options)
202
if options.verbose:
203
print("Writing results")
204
dec.writeResults(options)
205
206