1#!/usr/local/bin/python3.8
2# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3# Copyright (C) 2009-2019 German Aerospace Center (DLR) and others.
4# This program and the accompanying materials
5# are made available under the terms of the Eclipse Public License v2.0
6# which accompanies this distribution, and is available at
7# http://www.eclipse.org/legal/epl-v20.html
8# SPDX-License-Identifier: EPL-2.0
9
10# @file    generateTLSE3Detectors.py
11# @author  Daniel Krajzewicz
12# @author  Karol Stosiek
13# @author  Michael Behrisch
14# @date    2007-10-25
15# @version $Id$
16
17from __future__ import absolute_import
18from __future__ import print_function
19
20import logging
21import optparse
22import os
23import sys
24
25sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
26import sumolib  # noqa
27
28
29def getOptions():
30    option_parser = optparse.OptionParser()
31    option_parser.add_option("-n", "--net-file",
32                             dest="net_file",
33                             help="Network file to work with. Mandatory.",
34                             type="string")
35    option_parser.add_option("-j", "--junction-ids",
36                             dest="junctionIDs",
37                             help="List of junctions that shall receive detectors (comma separated)",
38                             type="string")
39    option_parser.add_option("-l", "--detector-length",
40                             dest="requested_detector_length",
41                             help="Length of the detector in meters "
42                             "(-1 for maximal length).",
43                             type="int",
44                             default=250)
45    option_parser.add_option("-d", "--distance-to-TLS",
46                             dest="requested_distance_to_tls",
47                             help="Distance of the detector to the traffic "
48                             "light in meters. Defaults to 0.1m.",
49                             type="float",
50                             default=.1)
51    option_parser.add_option("-f", "--frequency",
52                             dest="frequency",
53                             help="Detector's frequency. Defaults to 60.",
54                             type="int",
55                             default=60)
56    option_parser.add_option("-o", "--output",
57                             dest="output",
58                             help="The name of the file to write the detector "
59                             "definitions into. Defaults to e3.add.xml.",
60                             type="string",
61                             default="e3.add.xml")
62    option_parser.add_option("--prefix",
63                             dest="prefix",
64                             help="Prefix for generated detectors",
65                             type="string",
66                             default="e3_")
67    option_parser.add_option("-r", "--results-file",
68                             dest="results",
69                             help="The name of the file the detectors write "
70                             "their output into. Defaults to e3output.xml.",
71                             type="string",
72                             default="e3output.xml")
73    option_parser.add_option("--min-pos",
74                             dest="minPos",
75                             help="minimum position of entry detectors light in meters. Defaults to 0.1m.",
76                             type="float",
77                             default=.1)
78
79    option_parser.add_option(
80        "--interior", action="store_true",
81        default=False, help="Extend measurement area to the junction interior")
82    option_parser.add_option(
83        "--joined", action="store_true",
84        default=False, help="Create one e3Detector per junction")
85    option_parser.add_option(
86        "--follow-turnaround", dest="followTurnaround", action="store_true",
87        default=False, help="Extend entry detectors past turn-around connections")
88    option_parser.set_usage("generateTLSE3Detectors.py -n example.net.xml "
89                            "-l 250 -d .1 -f 60")
90
91    (options, args) = option_parser.parse_args()
92    if not options.net_file:
93        print("Missing arguments")
94        option_parser.print_help()
95        exit()
96    return options
97
98
99def writeEntryExit(options, edge, detector_xml, writeExit=True):
100    stopOnTLS = True
101    stopOnTurnaround = not options.followTurnaround
102    input_edges = network.getDownstreamEdges(
103        edge, options.requested_detector_length, stopOnTLS, stopOnTurnaround)
104    input_edges.sort(key=lambda vals: vals[0].getID())
105    for firstEdge, position, intermediate, aborted in input_edges:
106        if aborted:
107            position = .1
108        position = max(position, min(options.minPos, firstEdge.getLength()))
109        for lane in firstEdge.getLanes():
110            detector_entry_xml = detector_xml.addChild("detEntry")
111            detector_entry_xml.setAttribute("lane", lane.getID())
112            detector_entry_xml.setAttribute("pos", "%.2f" % position)
113
114    if writeExit:
115        if options.interior:
116            # exit just after leaving the intersection
117            for e2 in sorted(edge.getOutgoing(), key=lambda e: e.getID()):
118                for lane in e2.getLanes():
119                    detector_exit_xml = detector_xml.addChild("detExit")
120                    detector_exit_xml.setAttribute("lane", lane.getID())
121                    detector_exit_xml.setAttribute("pos", "0")
122        else:
123            # exit just before entering the intersection
124            for lane in edge.getLanes():
125                detector_exit_xml = detector_xml.addChild("detExit")
126                detector_exit_xml.setAttribute("lane", lane.getID())
127                detector_exit_xml.setAttribute("pos", "-.1")
128
129
130if __name__ == "__main__":
131    # pylint: disable-msg=C0103
132    options = getOptions()
133
134    logging.basicConfig(level="INFO")
135
136    logging.info("Reading net...")
137    network = sumolib.net.readNet(options.net_file)
138
139    logging.info("Generating detectors...")
140    detectors_xml = sumolib.xml.create_document("additional")
141    generated_detectors = 0
142
143    tlsList, getEdges = network._tlss, sumolib.net.TLS.getEdges
144    if options.junctionIDs:
145        tlsList = [network.getNode(n) for n in options.junctionIDs.split(',')]
146        getEdges = sumolib.net.node.Node.getIncoming
147
148    for tls in tlsList:
149        if options.joined:
150            detector_xml = detectors_xml.addChild("e3Detector")
151            detector_xml.setAttribute("id", options.prefix + str(tls.getID()))
152            detector_xml.setAttribute("freq", str(options.frequency))
153            detector_xml.setAttribute("file", options.results)
154            generated_detectors += 1
155            writeExit = True
156            for edge in sorted(getEdges(tls), key=sumolib.net.edge.Edge.getID):
157                writeEntryExit(options, edge, detector_xml, writeExit)
158                writeExit = not options.interior
159
160        else:
161            for edge in sorted(getEdges(tls), key=sumolib.net.edge.Edge.getID):
162                detector_xml = detectors_xml.addChild("e3Detector")
163                detector_xml.setAttribute(
164                    "id", options.prefix + str(tls.getID()) + "_" + str(edge.getID()))
165                detector_xml.setAttribute("freq", str(options.frequency))
166                detector_xml.setAttribute("file", options.results)
167                if options.interior:
168                    detector_xml.setAttribute("openEntry", "true")
169                writeEntryExit(options, edge, detector_xml)
170                generated_detectors += 1
171
172    detector_file = open(options.output, 'w')
173    detector_file.write(detectors_xml.toXML())
174    detector_file.close()
175
176    logging.info("%d e3 detectors generated!" % (generated_detectors))
177