Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-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 netgen_main.cpp
15 : /// @author Markus Hartinger
16 : /// @author Daniel Krajzewicz
17 : /// @author Jakob Erdmann
18 : /// @author Michael Behrisch
19 : /// @date Mar, 2003
20 : ///
21 : // Main for NETGENERATE
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #ifdef HAVE_VERSION_H
26 : #include <version.h>
27 : #endif
28 :
29 : #include <iostream>
30 : #include <fstream>
31 : #include <string>
32 : #include <ctime>
33 : #include <netgen/NGNet.h>
34 : #include <netgen/NGRandomNetBuilder.h>
35 : #include <netgen/NGFrame.h>
36 : #include <netbuild/NBNetBuilder.h>
37 : #include <netbuild/NBFrame.h>
38 : #include <netwrite/NWFrame.h>
39 : #include <netimport/NITypeLoader.h>
40 : #include <netimport/NIXMLTypesHandler.h>
41 : #include <utils/options/OptionsCont.h>
42 : #include <utils/options/OptionsIO.h>
43 : #include <utils/options/Option.h>
44 : #include <utils/common/MsgHandler.h>
45 : #include <utils/common/SystemFrame.h>
46 : #include <utils/common/UtilExceptions.h>
47 : #include <utils/common/RandHelper.h>
48 : #include <utils/common/ToString.h>
49 : #include <utils/distribution/RandomDistributor.h>
50 : #include <utils/geom/GeoConvHelper.h>
51 : #include <utils/xml/XMLSubSys.h>
52 : #include <utils/iodevices/OutputDevice.h>
53 :
54 :
55 : // ===========================================================================
56 : // method definitions
57 : // ===========================================================================
58 : void
59 120 : fillOptions() {
60 120 : OptionsCont& oc = OptionsCont::getOptions();
61 240 : oc.addCallExample("-c <CONFIGURATION>", TL("create net from given configuration"));
62 240 : oc.addCallExample("--grid [grid-network options] -o <OUTPUTFILE>", TL("create grid net"));
63 240 : oc.addCallExample("--spider [spider-network options] -o <OUTPUTFILE>", TL("create spider net"));
64 240 : oc.addCallExample("--rand [random-network options] -o <OUTPUTFILE>", TL("create random net"));
65 :
66 120 : oc.setAdditionalHelpMessage(" Either \"--grid\", \"--spider\" or \"--rand\" must be supplied.\n In dependence to these switches other options are used.");
67 :
68 : // insert options sub-topics
69 120 : SystemFrame::addConfigurationOptions(oc); // this subtopic is filled here, too
70 120 : oc.addOptionSubTopic("Grid Network");
71 120 : oc.addOptionSubTopic("Spider Network");
72 120 : oc.addOptionSubTopic("Random Network");
73 120 : oc.addOptionSubTopic("Input");
74 120 : oc.addOptionSubTopic("Output");
75 120 : oc.addOptionSubTopic("Processing");
76 120 : oc.addOptionSubTopic("Building Defaults");
77 120 : oc.addOptionSubTopic("TLS Building");
78 120 : oc.addOptionSubTopic("Edge Removal");
79 120 : oc.addOptionSubTopic("Unregulated Nodes");
80 120 : oc.addOptionSubTopic("Junctions");
81 120 : oc.addOptionSubTopic("Pedestrian");
82 120 : oc.addOptionSubTopic("Bicycle");
83 120 : SystemFrame::addReportOptions(oc); // this subtopic is filled here, too
84 :
85 120 : NGFrame::fillOptions();
86 120 : NBFrame::fillOptions(oc, true);
87 120 : NWFrame::fillOptions(oc, true);
88 120 : oc.doRegister("default-junction-type", 'j', new Option_String());
89 240 : oc.addSynonyme("default-junction-type", "junctions");
90 240 : oc.addDescription("default-junction-type", "Building Defaults", TL("[traffic_light|priority|right_before_left|left_before_right|traffic_light_right_on_red|priority_stop|allway_stop|...] Determines junction type (see wiki/Networks/PlainXML#Node_types)"));
91 120 : RandHelper::insertRandOptions(oc);
92 :
93 120 : oc.doRegister("tls.discard-simple", new Option_Bool(false));
94 240 : oc.addDescription("tls.discard-simple", "TLS Building", "Does not instantiate traffic lights at geometry-like nodes");
95 :
96 240 : oc.doRegister("railway.signal.permit-unsignalized", new Option_StringVector({"tram", "cable_car"}));
97 240 : oc.addDescription("railway.signal.permit-unsignalized", "TLS Building", TL("List rail classes that may run without rail signals"));
98 120 : }
99 :
100 :
101 : bool
102 112 : checkOptions() {
103 112 : OptionsCont& oc = OptionsCont::getOptions();
104 112 : bool ok = NGFrame::checkOptions();
105 112 : ok &= NBFrame::checkOptions(oc);
106 112 : ok &= NWFrame::checkOptions(oc);
107 112 : ok &= SystemFrame::checkOptions(oc);
108 112 : return ok;
109 : }
110 :
111 :
112 : NGNet*
113 100 : buildNetwork(NBNetBuilder& nb) {
114 100 : OptionsCont& oc = OptionsCont::getOptions();
115 :
116 101 : const double laneWidth = oc.isDefault("default.lanewidth") ? SUMO_const_laneWidth : oc.getFloat("default.lanewidth");
117 100 : double minLength = (oc.getInt("default.lanenumber") + oc.getInt("turn-lanes")) * 2 * laneWidth + oc.getFloat("default.junctions.radius") * 2 + POSITION_EPS;
118 : // spider-net
119 200 : if (oc.getBool("spider")) {
120 : // check values
121 : bool hadError = false;
122 46 : if (oc.getInt("spider.arm-number") < 3) {
123 2 : WRITE_ERROR(TL("Spider networks need at least 3 arms."));
124 : hadError = true;
125 : }
126 40 : if (oc.getInt("spider.arm-number") > 4 && !oc.getBool("spider.omit-center")) {
127 22 : WRITE_WARNING(TL("Spider networks with many arms should use option spider.omit-center"));
128 : }
129 46 : if (oc.getInt("spider.circle-number") < 1) {
130 4 : WRITE_ERROR(TL("Spider networks need at least one circle."));
131 : hadError = true;
132 : }
133 23 : minLength = MAX2(minLength, laneWidth * 2 * MAX2(oc.getInt("spider.arm-number"), 3) / (2 * M_PI));
134 46 : if (oc.getFloat("spider.space-radius") < POSITION_EPS) {
135 0 : WRITE_ERROR("The radius of spider networks must be at least " + toString(POSITION_EPS));
136 : hadError = true;
137 46 : } else if (oc.getFloat("spider.space-radius") < minLength) {
138 2 : WRITE_WARNINGF(TL("The radius of spider networks should be at least % for the given lanenumber, lanewidth and junction radius"), toString(minLength));
139 : }
140 23 : if (hadError) {
141 3 : throw ProcessError();
142 : }
143 : // build if everything's ok
144 20 : NGNet* net = new NGNet(nb);
145 60 : net->createSpiderWeb(oc.getInt("spider.arm-number"), oc.getInt("spider.circle-number"),
146 40 : oc.getFloat("spider.space-radius"), !oc.getBool("spider.omit-center"),
147 : oc.getFloat("spider.attach-length"));
148 20 : return net;
149 : }
150 : // grid-net
151 154 : if (oc.getBool("grid")) {
152 : // get options
153 53 : int xNo = oc.getInt("grid.x-number");
154 53 : int yNo = oc.getInt("grid.y-number");
155 53 : double xLength = oc.getFloat("grid.x-length");
156 53 : double yLength = oc.getFloat("grid.y-length");
157 53 : double xAttachLength = oc.getFloat("grid.x-attach-length");
158 53 : double yAttachLength = oc.getFloat("grid.y-attach-length");
159 98 : if (oc.isDefault("grid.x-number") && !oc.isDefault("grid.number")) {
160 62 : xNo = oc.getInt("grid.number");
161 : }
162 98 : if (oc.isDefault("grid.y-number") && !oc.isDefault("grid.number")) {
163 62 : yNo = oc.getInt("grid.number");
164 : }
165 112 : if (oc.isDefault("grid.x-length") && !oc.isDefault("grid.length")) {
166 62 : xLength = oc.getFloat("grid.length");
167 : }
168 112 : if (oc.isDefault("grid.y-length") && !oc.isDefault("grid.length")) {
169 62 : yLength = oc.getFloat("grid.length");
170 : }
171 153 : if (oc.isDefault("grid.x-attach-length") && !oc.isDefault("grid.attach-length")) {
172 8 : xAttachLength = oc.getFloat("grid.attach-length");
173 : }
174 153 : if (oc.isDefault("grid.y-attach-length") && !oc.isDefault("grid.attach-length")) {
175 8 : yAttachLength = oc.getFloat("grid.attach-length");
176 : }
177 : // check values
178 : bool hadError = false;
179 53 : if (xNo < 1 || yNo < 1 || (xAttachLength == 0 && yAttachLength == 0 && (xNo < 2 && yNo < 2))) {
180 12 : WRITE_ERROR(TL("The number of nodes must be positive and at least 2 in one direction if there are no attachments."));
181 : hadError = true;
182 : }
183 53 : const double minAttachLength = minLength / 2 + POSITION_EPS / 2;
184 53 : if (xLength < POSITION_EPS || yLength < POSITION_EPS) {
185 6 : WRITE_ERROR("The distance between nodes must be at least " + toString(POSITION_EPS));
186 3 : hadError = true;
187 50 : } else if (xLength < minLength || yLength < minLength) {
188 8 : WRITE_WARNINGF(TL("The distance between nodes should be at least % for the given lanenumber, lanewidth and junction radius"), toString(minLength));
189 : }
190 53 : if (xAttachLength != 0.0 && xAttachLength < POSITION_EPS) {
191 0 : WRITE_ERROR("The length of attached streets must be at least " + toString(POSITION_EPS));
192 : hadError = true;
193 5 : } else if (xAttachLength != 0.0 && xAttachLength < minAttachLength) {
194 0 : WRITE_WARNINGF(TL("The length of attached streets should be at least % for the given lanenumber, lanewidth and junction radius"), toString(minAttachLength));
195 53 : } else if (yAttachLength != 0.0 && yAttachLength < POSITION_EPS) {
196 0 : WRITE_ERROR("The length of attached streets must be at least " + toString(POSITION_EPS));
197 : hadError = true;
198 5 : } else if (yAttachLength != 0.0 && yAttachLength < minAttachLength) {
199 0 : WRITE_WARNINGF(TL("The length of attached streets should be at least % for the given lanenumber, lanewidth and junction radius"), toString(minAttachLength));
200 : }
201 53 : if (hadError) {
202 9 : throw ProcessError();
203 : }
204 : // build if everything's ok
205 44 : NGNet* net = new NGNet(nb);
206 44 : net->createChequerBoard(xNo, yNo, xLength, yLength, xAttachLength, yAttachLength);
207 : return net;
208 : }
209 : // random net
210 : RandomDistributor<int> neighborDist;
211 24 : neighborDist.add(1, oc.getFloat("rand.neighbor-dist1"));
212 24 : neighborDist.add(2, oc.getFloat("rand.neighbor-dist2"));
213 24 : neighborDist.add(3, oc.getFloat("rand.neighbor-dist3"));
214 24 : neighborDist.add(4, oc.getFloat("rand.neighbor-dist4"));
215 24 : neighborDist.add(5, oc.getFloat("rand.neighbor-dist5"));
216 24 : neighborDist.add(6, oc.getFloat("rand.neighbor-dist6"));
217 24 : NGNet* net = new NGNet(nb);
218 : NGRandomNetBuilder randomNet(*net,
219 48 : DEG2RAD(oc.getFloat("rand.min-angle")),
220 : oc.getFloat("rand.min-distance"),
221 : oc.getFloat("rand.max-distance"),
222 : oc.getFloat("rand.connectivity"),
223 : oc.getInt("rand.num-tries"),
224 72 : neighborDist);
225 48 : randomNet.createNet(oc.getInt("rand.iterations"), oc.getBool("rand.grid"));
226 : return net;
227 24 : }
228 :
229 :
230 :
231 : int
232 120 : main(int argc, char** argv) {
233 120 : OptionsCont& oc = OptionsCont::getOptions();
234 120 : oc.setApplicationDescription(TL("Synthetic network generator for the microscopic, multi-modal traffic simulation SUMO."));
235 240 : oc.setApplicationName("netgenerate", "Eclipse SUMO netgenerate " VERSION_STRING);
236 : int ret = 0;
237 : try {
238 : // initialise the application system (messaging, xml, options)
239 120 : XMLSubSys::init();
240 120 : fillOptions();
241 120 : OptionsIO::setArgs(argc, argv);
242 120 : OptionsIO::getOptions();
243 119 : if (oc.processMetaOptions(argc < 2)) {
244 7 : SystemFrame::close();
245 7 : return 0;
246 : }
247 224 : XMLSubSys::setValidation(oc.getString("xml-validation"), "never", "never");
248 112 : MsgHandler::initOutputOptions();
249 112 : if (!checkOptions()) {
250 12 : throw ProcessError();
251 : }
252 200 : GeoConvHelper::init("!",
253 200 : Position(oc.getFloat("offset.x"), oc.getFloat("offset.y")),
254 100 : Boundary(), Boundary());
255 100 : RandHelper::initRandGlobal();
256 100 : NBNetBuilder nb;
257 100 : nb.applyOptions(oc);
258 200 : if (oc.isSet("type-files")) {
259 3 : NIXMLTypesHandler handler(nb.getTypeCont());
260 6 : NITypeLoader::load(handler, oc.getStringVector("type-files"), toString(SUMO_TAG_TYPES));
261 3 : }
262 : // build the netgen-network description
263 100 : NGNet* net = buildNetwork(nb);
264 : // ... and we have to do this...
265 88 : oc.resetWritable();
266 : // transfer to the netbuilding structures
267 88 : net->toNB();
268 88 : delete net;
269 : // report generated structures
270 88 : WRITE_MESSAGE(TL(" Generation done;"));
271 176 : WRITE_MESSAGEF(TL(" % nodes generated."), toString<int>(nb.getNodeCont().size()));
272 176 : WRITE_MESSAGEF(TL(" % edges generated."), toString<int>(nb.getEdgeCont().size()));
273 176 : if (oc.getBool("tls.discard-simple")) {
274 1 : nb.getNodeCont().discardTrafficLights(nb.getTLLogicCont(), true);
275 1 : int removed = nb.getTLLogicCont().getNumExtracted();
276 1 : if (removed > 0) {
277 2 : WRITE_MESSAGEF(TL(" Removed % traffic lights at geometry-like nodes"), toString(removed));
278 : }
279 : }
280 88 : nb.compute(oc);
281 88 : nb.getNodeCont().printBuiltNodesStatistics();
282 88 : NWFrame::writeNetwork(oc, nb);
283 128 : } catch (const ProcessError& e) {
284 36 : if (std::string(e.what()) != std::string("Process Error") && std::string(e.what()) != std::string("")) {
285 8 : WRITE_ERROR(e.what());
286 : }
287 28 : MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);
288 : ret = 1;
289 : #ifndef _DEBUG
290 28 : } catch (const std::exception& e) {
291 0 : if (std::string(e.what()) != std::string("")) {
292 0 : WRITE_ERROR(e.what());
293 : }
294 0 : MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);
295 : ret = 1;
296 0 : } catch (...) {
297 0 : MsgHandler::getErrorInstance()->inform("Quitting (on unknown error).", false);
298 : ret = 1;
299 : #endif
300 0 : }
301 113 : SystemFrame::close();
302 : if (ret == 0) {
303 : std::cout << "Success." << std::endl;
304 : }
305 : return ret;
306 : }
307 :
308 :
309 : /****************************************************************************/
|