Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/route/tracemapper.py
169674 views
1
#!/usr/bin/env python
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2009-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 tracemapper.py
15
# @author Michael Behrisch
16
# @author Mirko Barthauer
17
# @date 2013-10-23
18
19
20
from __future__ import print_function
21
from __future__ import absolute_import
22
import os
23
import sys
24
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
25
26
import sumolib # noqa
27
import route2poly # noqa
28
29
30
def readPOI(traceFile, net):
31
trace = []
32
for poi in sumolib.xml.parse(traceFile, "poi"):
33
if poi.lon is None:
34
trace.append((poi.x, poi.y))
35
else:
36
trace.append(net.convertLonLat2XY(poi.lon, poi.lat))
37
yield "blub", trace
38
39
40
def readFCD(traceFile, net, geo):
41
"""Reads traces from a file in SUMO's fcd-output format.
42
The file needs to be sorted by vehicle id rather than by time!"""
43
trace = []
44
last = None
45
for v in sumolib.xml.parse_fast(traceFile, "vehicle", ("id", "x", "y")):
46
if last is None:
47
last = v.id
48
if last != v.id:
49
yield last, trace
50
trace = []
51
last = v.id
52
if geo:
53
trace.append(net.convertLonLat2XY(float(v.x), float(v.y)))
54
else:
55
trace.append((float(v.x), float(v.y)))
56
if trace:
57
yield last, trace
58
59
60
def readLines(traceFile, net, geo):
61
with open(traceFile) as traces:
62
for line in traces:
63
tid, traceString = line.split(":")
64
trace = [tuple(map(float, pos.split(","))) for pos in traceString.split()]
65
if geo:
66
trace = [net.convertLonLat2XY(*pos) for pos in trace]
67
yield tid.strip(), trace
68
69
70
if __name__ == "__main__":
71
ap = sumolib.options.ArgumentParser()
72
ap.add_argument("-v", "--verbose", action="store_true",
73
default=False, help="tell me what you are doing")
74
ap.add_argument("-n", "--net", help="SUMO network to use", category="input",
75
type=ap.net_file, metavar="FILE", required=True)
76
ap.add_argument("-t", "--trace", category="input", type=ap.file,
77
help="trace files to use, separated by comma", metavar="FILE", required=True)
78
ap.add_argument("-d", "--delta", default=1., type=float,
79
help="maximum distance between edge and trace points")
80
ap.add_argument("-a", "--air-dist-factor", default=2., type=float,
81
help="maximum factor between airline and route distance between successive trace points")
82
ap.add_argument("-o", "--output", help="route output", metavar="FILE", required=True)
83
ap.add_argument("-p", "--poi-output", category="output", type=ap.file,
84
help="generate POI output for the trace", metavar="FILE")
85
ap.add_argument("-y", "--polygon-output", category="output", type=ap.file,
86
help="generate polygon output for the mapped edges", metavar="FILE")
87
ap.add_argument("--geo", action="store_true",
88
default=False, help="read trace with geo-coordinates")
89
ap.add_argument("--direction", action="store_true",
90
default=False, help="try to use direction of consecutive points when mapping")
91
ap.add_argument("--vehicle-class", default=None,
92
help="filters the edges by the vehicle class the route is meant for")
93
ap.add_argument("--fill-gaps", default=0., type=float,
94
help="repair disconnected routes bridging gaps of up to x meters")
95
ap.add_argument("-g", "--gap-penalty", default=-1, type=float,
96
help="penalty to add for disconnected routes " +
97
"(default of -1 adds the distance between the two endpoints as penalty)")
98
ap.add_argument("--internal", action="store_true",
99
default=False, help="include internal edges in generated shapes")
100
ap.add_argument("--spread", type=float, help="spread polygons laterally to avoid overlap")
101
ap.add_argument("--blur", type=float,
102
default=0, help="maximum random disturbance to route geometry")
103
ap.add_argument("-l", "--layer", default=100, help="layer for generated polygons")
104
ap.add_argument("-b", "--debug", action="store_true",
105
default=False, help="print out the debugging messages")
106
options = ap.parse_args()
107
108
if options.verbose:
109
print("Reading net ...")
110
net = sumolib.net.readNet(options.net, withInternal=True)
111
112
if options.verbose:
113
print("Reading traces ...")
114
115
tracefiles = options.trace.split(',')
116
for t in tracefiles:
117
if len(tracefiles) == 1:
118
outfile = options.output
119
else:
120
outfile = os.path.basename(t).split('.')[0] + '.tc.xml'
121
with open(outfile, "w") as outf:
122
sumolib.xml.writeHeader(outf, root='routes')
123
poiOut = None
124
if options.poi_output is not None:
125
if len(tracefiles) == 1:
126
poi_output = options.poi_output
127
else:
128
poi_output = os.path.basename(t).split('.')[0] + '.poi.xml'
129
poiOut = open(poi_output, "w")
130
sumolib.xml.writeHeader(poiOut, root='additional')
131
polyOut = None
132
if options.polygon_output is not None:
133
if len(tracefiles) == 1:
134
polygon_output = options.polygon_output
135
else:
136
polygon_output = os.path.basename(t).split('.')[0] + '.poly.xml'
137
polyOut = open(polygon_output, "w")
138
sumolib.xml.writeHeader(polyOut, root='additional')
139
colorgen = sumolib.miscutils.Colorgen(('random', 1, 1))
140
# determine file type by reading the first 10000 bytes
141
with open(t) as peek:
142
head = peek.read(10000)
143
if "<poi" in head:
144
traces = readPOI(t, net)
145
elif "<fcd" in head:
146
traces = readFCD(t, net, options.geo)
147
else:
148
traces = readLines(t, net, options.geo)
149
mapOpts = (options.delta, options.verbose, options.air_dist_factor,
150
options.fill_gaps, options.gap_penalty, options.debug, options.direction, options.vehicle_class)
151
for tid, trace in traces:
152
if poiOut is not None:
153
for idx, pos in enumerate(trace):
154
poiOut.write(' <poi id="%s:%s" x="%s" y="%s"/>\n' % (tid, idx, pos[0], pos[1]))
155
edges = [e.getID() for e in sumolib.route.mapTrace(
156
trace, net, *mapOpts) if e.getFunction() != "internal"]
157
edges = [edge for i, edge in enumerate(edges) if i == 0 or edge != edges[i-1]]
158
if polyOut is not None and edges:
159
route2poly.generate_poly(options, net, tid, colorgen(), edges, polyOut)
160
if edges:
161
outf.write(' <route id="%s" edges="%s"/>\n' % (tid, " ".join(edges)))
162
elif options.verbose:
163
print("No edges are found for %s." % (tid))
164
165
outf.write('</routes>\n')
166
if poiOut is not None:
167
poiOut.write('</additional>\n')
168
poiOut.close()
169
if polyOut is not None:
170
polyOut.write('</additional>\n')
171
polyOut.close()
172
173