Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/generateLandmarks.py
169659 views
1
#!/usr/bin/env python
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2010-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 generateLandmarks.py
15
# @author Jakob Erdmann
16
# @author Mirko Barthauer
17
# @date 2021-11-25
18
19
"""
20
Create a landmark table input file for generating a full table duarouter options
21
--astar.landmark-distances --astar.save-landmark-distances
22
for a given netfile and with a given number of landmarks
23
The landmarks are somewhat evenly spaced around the rim of the network
24
"""
25
26
from __future__ import print_function
27
from __future__ import absolute_import
28
import os
29
import sys
30
import random
31
from math import pi, degrees
32
33
if 'SUMO_HOME' in os.environ:
34
sys.path.append(os.path.join(os.environ['SUMO_HOME'], 'tools'))
35
import sumolib # noqa
36
37
38
def get_options(args=None):
39
optParser = sumolib.options.ArgumentParser(description="Generate random boundary landmarks for a given network")
40
optParser.add_argument("-n", "--net-file", category="input", dest="netfile", required=True,
41
help="define the net file (mandatory)")
42
optParser.add_argument("-o", "--output-file", category="output", dest="outfile",
43
default="landmarks.txt", help="define the output filename")
44
optParser.add_argument("--selection-output", category="output", dest="selout",
45
help="Write a selection file for visualization")
46
optParser.add_argument("-N", "--number", category="processing", type=int, default=10,
47
help="Define number of landmarks to generate")
48
optParser.add_argument("--geo", category="processing", action="store_true", default=False,
49
help="Store landmarks as geo-coordinates instead of edge IDs")
50
optParser.add_argument("-d", "--no-deadends", category="processing", action="store_true", default=False,
51
dest="noDeadends",
52
help="Only use edges that have at least one follower and one successor")
53
optParser.add_argument("-p", "--min-priority", category="processing", type=int, default=-10, dest="minPrio",
54
help="Only use edges with priority of at least INT")
55
optParser.add_argument("-s", "--seed", category="processing", type=int, default=42, help="random seed")
56
optParser.add_argument("--random", category="processing", action="store_true", default=False,
57
help="use a random seed to initialize the random number generator")
58
optParser.add_argument("--vclass", category="processing", default="passenger",
59
help="only use edges which permit the given vehicle class")
60
optParser.add_argument("-v", "--verbose", category="processing", action="store_true",
61
default=False, help="tell me what you are doing")
62
return optParser.parse_args(args=args)
63
64
65
def filterEdges(options, edges):
66
return [e for e in edges if (
67
e.getPriority() >= options.minPrio
68
and e.allows(options.vclass)
69
and (not options.noDeadends or (e.getAllowedOutgoing(options.vclass) and e.getIncoming())))]
70
71
72
def getAngles(net, edges):
73
"""compute angle towards network center and sort edges by this angle"""
74
xmin, ymin, xmax, ymax = net.getBoundary()
75
center = ((xmin + xmax) / 2, (ymin + ymax) / 2)
76
result = []
77
for e in edges:
78
pos = e.getToNode().getCoord()
79
angle = sumolib.geomhelper.angleTo2D(center, pos)
80
dist = sumolib.geomhelper.distance(center, pos)
81
result.append((angle, dist, e))
82
result.sort(key=lambda x: x[0:2])
83
return result
84
85
86
def main(options):
87
if not options.random:
88
random.seed(options.seed)
89
net = sumolib.net.readNet(options.netfile)
90
if options.geo and not net.hasGeoProj():
91
print("Network does not provide geo-projection. Option --geo cannot be used", file=sys.stderr)
92
sys.exit(1)
93
94
edges = filterEdges(options, net.getEdges())
95
edgeAngles = getAngles(net, edges)
96
97
angleSteps = [-pi + (i + 1) * 2 * pi / options.number for i in range(options.number)]
98
i = 0
99
landmarks = []
100
aPrev = -pi
101
for a in angleSteps:
102
edgeDistances = []
103
while i < len(edgeAngles) and edgeAngles[i][0] < a:
104
edgeDistances.append(edgeAngles[i][1:])
105
i += 1
106
if edgeDistances:
107
edgeDistances.sort(key=lambda x: x[0])
108
landmarks.append(edgeDistances[-1][1])
109
else:
110
print("Found no edges between angles %.0f and %0.f" % (degrees(aPrev), degrees(a)), file=sys.stderr)
111
aPrev = a
112
113
with open(options.outfile, 'w') as outf:
114
for e in landmarks:
115
if options.geo:
116
x, y = sumolib.geomhelper.positionAtShapeOffset(e.getShape(), e.getLength() / 2)
117
outf.write("%f %f\n" % net.convertXY2LonLat(x, y))
118
else:
119
outf.write("%s\n" % e.getID())
120
121
if options.selout:
122
with open(options.selout, 'w') as outf:
123
for e in landmarks:
124
outf.write("edge:%s\n" % e.getID())
125
126
127
if __name__ == "__main__":
128
if not main(get_options()):
129
sys.exit(1)
130
131