Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netbuild/NBNetBuilder.cpp
169665 views
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 NBNetBuilder.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Thimor Bohn
18
/// @author Sascha Krieg
19
/// @author Michael Behrisch
20
/// @author Walter Bamberger
21
/// @date 20 Nov 2001
22
///
23
// Instance responsible for building networks
24
/****************************************************************************/
25
#include <config.h>
26
27
#include <string>
28
#include <fstream>
29
#include <utils/options/OptionsCont.h>
30
#include <utils/common/MsgHandler.h>
31
#include <utils/common/UtilExceptions.h>
32
#include <utils/common/StringTokenizer.h>
33
#include <utils/common/SUMOVehicleClass.h>
34
#include <utils/common/SysUtils.h>
35
#include <utils/common/ToString.h>
36
#include <utils/geom/GeoConvHelper.h>
37
#include "NBAlgorithms.h"
38
#include "NBAlgorithms_Ramps.h"
39
#include "NBAlgorithms_Railway.h"
40
#include "NBHeightMapper.h"
41
#include "NBNodeCont.h"
42
#include "NBEdgeCont.h"
43
#include "NBPTStop.h"
44
#include "NBTrafficLightLogicCont.h"
45
#include "NBDistrictCont.h"
46
#include "NBDistrict.h"
47
#include "NBRequest.h"
48
#include "NBTypeCont.h"
49
#include "NBNetBuilder.h"
50
51
52
// ===========================================================================
53
// method definitions
54
// ===========================================================================
55
NBNetBuilder::NBNetBuilder() :
56
myEdgeCont(myTypeCont),
57
myNetworkHaveCrossings(false) {
58
}
59
60
61
NBNetBuilder::~NBNetBuilder() {}
62
63
64
void
65
NBNetBuilder::applyOptions(OptionsCont& oc) {
66
// apply options to type control
67
myTypeCont.setEdgeTypeDefaults(oc.getInt("default.lanenumber"), oc.getFloat("default.lanewidth"), oc.getFloat("default.speed"), oc.getFloat("default.friction"),
68
oc.getInt("default.priority"), parseVehicleClasses(oc.getString("default.allow"), oc.getString("default.disallow")),
69
SUMOXMLDefinitions::LaneSpreadFunctions.get(oc.getString("default.spreadtype")));
70
// apply options to edge control
71
myEdgeCont.applyOptions(oc);
72
// apply options to traffic light logics control
73
myTLLCont.applyOptions(oc);
74
NBEdge::setDefaultConnectionLength(oc.getFloat("default.connection-length"));
75
}
76
77
78
void
79
NBNetBuilder::compute(OptionsCont& oc, const std::set<std::string>& explicitTurnarounds, bool mayAddOrRemove) {
80
GeoConvHelper& geoConvHelper = GeoConvHelper::getProcessing();
81
82
const bool lefthand = oc.getBool("lefthand");
83
if (lefthand) {
84
mirrorX();
85
}
86
87
// MODIFYING THE SETS OF NODES AND EDGES
88
// Removes edges that are connecting the same node
89
long before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Removing self-loops"));
90
int numRemovedEdges = 0;
91
numRemovedEdges += myNodeCont.removeSelfLoops(myDistrictCont, myEdgeCont, myTLLCont);
92
PROGRESS_TIME_MESSAGE(before);
93
if (mayAddOrRemove && oc.exists("remove-edges.isolated") && oc.getBool("remove-edges.isolated")) {
94
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Finding isolated roads"));
95
numRemovedEdges += myNodeCont.removeIsolatedRoads(myDistrictCont, myEdgeCont);
96
PROGRESS_TIME_MESSAGE(before);
97
}
98
if (mayAddOrRemove && oc.exists("keep-edges.components") && oc.getInt("keep-edges.components") > 0) {
99
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Finding largest components"));
100
const bool hasStops = oc.exists("ptstop-output") && oc.isSet("ptstop-output") && !myPTStopCont.getStops().empty();
101
numRemovedEdges += myNodeCont.removeComponents(myDistrictCont, myEdgeCont, oc.getInt("keep-edges.components"), hasStops);
102
PROGRESS_TIME_MESSAGE(before);
103
}
104
if (mayAddOrRemove && oc.exists("keep-edges.postload") && oc.getBool("keep-edges.postload")) {
105
// pre-process lines to set permissions
106
if (!myPTLineCont.getLines().empty()) {
107
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Revising public transport stops based on pt lines"));
108
myPTLineCont.process(myEdgeCont, myPTStopCont);
109
PROGRESS_TIME_MESSAGE(before);
110
}
111
if (oc.isSet("keep-edges.explicit") || oc.isSet("keep-edges.input-file")) {
112
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Removing unwished edges"));
113
numRemovedEdges += myEdgeCont.removeUnwishedEdges(myDistrictCont);
114
PROGRESS_TIME_MESSAGE(before);
115
}
116
const int removed = myEdgeCont.removeEdgesBySpeed(myDistrictCont);
117
if (removed > 0) {
118
numRemovedEdges += removed;
119
WRITE_MESSAGEF(TL(" Removed % edges because by minimum speed."), removed);
120
}
121
const int removed2 = myEdgeCont.removeEdgesByPermissions(myDistrictCont);
122
if (removed2 > 0) {
123
numRemovedEdges += removed2;
124
WRITE_MESSAGEF(TL(" Removed % edges based on vClass."), removed2);
125
}
126
}
127
if (mayAddOrRemove && oc.getFloat("keep-lanes.min-width") > 0.) {
128
const int removed = myEdgeCont.removeLanesByWidth(myDistrictCont, oc.getFloat("keep-lanes.min-width"));
129
if (removed > 0) {
130
numRemovedEdges += removed;
131
WRITE_MESSAGEF(TL(" Removed % edges because of lane width."), removed);
132
}
133
}
134
if (mayAddOrRemove && oc.exists("junctions.attach-removed") && oc.getFloat("junctions.attach-removed") >= 0) {
135
const int numSplit = myEdgeCont.attachRemoved(myNodeCont, myDistrictCont, oc.getFloat("junctions.attach-removed"));
136
if (numSplit > 0) {
137
WRITE_MESSAGEF(TL(" Split % edges to attach removed nodes"), numSplit);
138
}
139
}
140
// Processing pt stops and lines
141
if (!myPTStopCont.getStops().empty()) {
142
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Processing public transport stops"));
143
if (!(oc.exists("ptline-output") && oc.isSet("ptline-output"))
144
&& !oc.getBool("ptstop-output.no-bidi")) {
145
myPTStopCont.localizePTStops(myEdgeCont);
146
}
147
myPTStopCont.assignEdgeForFloatingStops(myEdgeCont, 20);
148
myPTStopCont.assignLanes(myEdgeCont);
149
PROGRESS_TIME_MESSAGE(before);
150
}
151
if (mayAddOrRemove && oc.exists("keep-edges.components") && oc.getInt("keep-edges.components") > 0) {
152
// post process rail components unless they have stops
153
numRemovedEdges += myNodeCont.removeRailComponents(myDistrictCont, myEdgeCont, myPTStopCont);
154
}
155
// removal is done, clean up roundabouts
156
if (numRemovedEdges > 0) {
157
myEdgeCont.cleanupRoundabouts();
158
}
159
160
if (!myPTLineCont.getLines().empty()) {
161
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Revising public transport stops based on pt lines"));
162
myPTLineCont.process(myEdgeCont, myPTStopCont);
163
PROGRESS_TIME_MESSAGE(before);
164
}
165
166
if (!myPTStopCont.getStops().empty() && !oc.getBool("ptstop-output.no-bidi")) {
167
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Align pt stop id signs with corresponding edge id signs"));
168
myPTStopCont.alignIdSigns();
169
PROGRESS_TIME_MESSAGE(before);
170
}
171
// analyze and fix railway topology
172
int numAddedBidi = 0;
173
if (oc.exists("railway.topology.all-bidi") && oc.getBool("railway.topology.all-bidi")) {
174
NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false);
175
numAddedBidi = NBRailwayTopologyAnalyzer::makeAllBidi(myEdgeCont);
176
} else if (oc.exists("railway.topology.repair") && oc.getBool("railway.topology.repair")) {
177
// correct railway angles for angle-based connectivity heuristic
178
myEdgeCont.checkGeometries(0, false,
179
oc.getFloat("geometry.min-radius"), false,
180
oc.getBool("geometry.min-radius.fix.railways"), true);
181
NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false);
182
numAddedBidi = NBRailwayTopologyAnalyzer::repairTopology(myEdgeCont, myPTStopCont, myPTLineCont);
183
}
184
NBRailwaySignalGuesser::guessRailSignals(myEdgeCont, myPTStopCont);
185
if (numAddedBidi > 0) {
186
// update routes
187
myPTLineCont.process(myEdgeCont, myPTStopCont, true);
188
}
189
if (oc.exists("railway.topology.direction-priority") && oc.getBool("railway.topology.direction-priority")) {
190
NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false); // recompute after new edges were added
191
NBRailwayTopologyAnalyzer::extendDirectionPriority(myEdgeCont, true);
192
} else if (oc.exists("railway.topology.extend-priority") && oc.getBool("railway.topology.extend-priority")) {
193
NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false); // recompute after new edges were added
194
NBRailwayTopologyAnalyzer::extendDirectionPriority(myEdgeCont, false);
195
}
196
if (oc.exists("railway.topology.output") && oc.isSet("railway.topology.output")) {
197
NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false); // recompute after new edges were added
198
NBRailwayTopologyAnalyzer::analyzeTopology(myEdgeCont);
199
}
200
if (oc.exists("railway.geometry.straighten") && oc.getBool("railway.geometry.straighten")) {
201
NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false); // recompute after new edges were added
202
NBRailwayGeometryHelper::straigthenCorrdidor(myEdgeCont, oc.getFloat("geometry.max-angle"));
203
}
204
205
206
if (mayAddOrRemove && oc.exists("edges.join-tram-dist") && oc.getFloat("edges.join-tram-dist") >= 0) {
207
// should come before joining junctions
208
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining tram edges"));
209
int numJoinedTramEdges = myEdgeCont.joinTramEdges(myDistrictCont, myPTStopCont, myPTLineCont, oc.getFloat("edges.join-tram-dist"));
210
PROGRESS_TIME_MESSAGE(before);
211
if (numJoinedTramEdges > 0) {
212
WRITE_MESSAGEF(TL(" Joined % tram edges into roads."), toString(numJoinedTramEdges));
213
}
214
}
215
if (oc.getBool("junctions.join")
216
|| (oc.exists("ramps.guess") && oc.getBool("ramps.guess"))
217
|| oc.getBool("tls.guess.joining")
218
|| (oc.exists("tls.guess-signals") && oc.getBool("tls.guess-signals"))) {
219
// preliminary geometry computations to determine the length of edges
220
// This depends on turning directions and sorting of edge list
221
// in case junctions are joined geometry computations have to be repeated
222
// preliminary roundabout computations to avoid damaging roundabouts via junctions.join or ramps.guess
223
NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false);
224
NBNodesEdgesSorter::sortNodesEdges(myNodeCont);
225
myEdgeCont.computeLaneShapes();
226
myNodeCont.computeNodeShapes();
227
myEdgeCont.computeEdgeShapes();
228
if (oc.getBool("roundabouts.guess")) {
229
myEdgeCont.guessRoundabouts();
230
}
231
const std::set<EdgeSet>& roundabouts = myEdgeCont.getRoundabouts();
232
for (std::set<EdgeSet>::const_iterator it_round = roundabouts.begin();
233
it_round != roundabouts.end(); ++it_round) {
234
std::vector<std::string> nodeIDs;
235
for (EdgeSet::const_iterator it_edge = it_round->begin(); it_edge != it_round->end(); ++it_edge) {
236
nodeIDs.push_back((*it_edge)->getToNode()->getID());
237
}
238
myNodeCont.addJoinExclusion(nodeIDs);
239
}
240
NBNodeTypeComputer::validateRailCrossings(myNodeCont, myTLLCont);
241
} else if ((myEdgeCont.hasGuessedRoundabouts() || oc.getBool("crossings.guess")) && oc.getBool("roundabouts.guess")) {
242
myEdgeCont.guessRoundabouts();
243
myEdgeCont.markRoundabouts();
244
}
245
// join junctions (may create new "geometry"-nodes so it needs to come before removing these
246
if (mayAddOrRemove && oc.exists("junctions.join-exclude") && oc.isSet("junctions.join-exclude")) {
247
myNodeCont.addJoinExclusion(oc.getStringVector("junctions.join-exclude"));
248
}
249
int numJoined = myNodeCont.joinLoadedClusters(myDistrictCont, myEdgeCont, myTLLCont);
250
if (mayAddOrRemove && oc.getBool("junctions.join")) {
251
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining junction clusters"));
252
numJoined += myNodeCont.joinJunctions(oc.getFloat("junctions.join-dist"), myDistrictCont, myEdgeCont, myTLLCont, myPTStopCont);
253
PROGRESS_TIME_MESSAGE(before);
254
}
255
if (numJoined > 0) {
256
WRITE_MESSAGEF(TL(" Joined % junction cluster(s)."), toString(numJoined));
257
}
258
if (mayAddOrRemove && oc.getFloat("junctions.join-same") >= 0) {
259
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining junctions with similar coordinates"));
260
int numJoined2 = myNodeCont.joinSameJunctions(myDistrictCont, myEdgeCont, myTLLCont, oc.getFloat("junctions.join-same"));
261
PROGRESS_TIME_MESSAGE(before);
262
if (numJoined2 > 0) {
263
WRITE_MESSAGEF(TL(" Joined % junctions."), toString(numJoined2));
264
}
265
}
266
//
267
if (mayAddOrRemove && oc.exists("join-lanes") && oc.getBool("join-lanes")) {
268
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining lanes"));
269
const int num = myEdgeCont.joinLanes(SVC_IGNORING) + myEdgeCont.joinLanes(SVC_PEDESTRIAN);
270
PROGRESS_TIME_MESSAGE(before);
271
WRITE_MESSAGEF(TL(" Joined lanes on % edges."), toString(num));
272
}
273
//
274
if (mayAddOrRemove) {
275
const bool removeGeometryNodes = oc.exists("geometry.remove") && oc.getBool("geometry.remove");
276
before = PROGRESS_BEGIN_TIME_MESSAGE("Removing empty nodes" + std::string(removeGeometryNodes ? " and geometry nodes" : ""));
277
// removeUnwishedNodes needs turnDirections. @todo: try to call this less often
278
NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false);
279
const int numRemoved = myNodeCont.removeUnwishedNodes(myDistrictCont, myEdgeCont, myTLLCont, myPTStopCont, myPTLineCont, myParkingCont, removeGeometryNodes);
280
PROGRESS_TIME_MESSAGE(before);
281
WRITE_MESSAGEF(TL(" % nodes removed."), toString(numRemoved));
282
}
283
284
// MOVE TO ORIGIN
285
// compute new boundary after network modifications have taken place
286
Boundary boundary;
287
for (std::map<std::string, NBNode*>::const_iterator it = myNodeCont.begin(); it != myNodeCont.end(); ++it) {
288
boundary.add(it->second->getPosition());
289
}
290
for (std::map<std::string, NBEdge*>::const_iterator it = myEdgeCont.begin(); it != myEdgeCont.end(); ++it) {
291
boundary.add(it->second->getGeometry().getBoxBoundary());
292
}
293
geoConvHelper.setConvBoundary(boundary);
294
295
if (!oc.getBool("offset.disable-normalization") && oc.isDefault("offset.x") && oc.isDefault("offset.y")) {
296
moveToOrigin(geoConvHelper, lefthand);
297
}
298
roundInputs();
299
geoConvHelper.computeFinal(lefthand); // information needed for location element fixed at this point
300
301
// reset shapes and angles for stable re-computation
302
if (myNodeCont.resetNodeShapes()) {
303
myEdgeCont.computeAngles();
304
}
305
306
if (oc.exists("geometry.min-dist") && !oc.isDefault("geometry.min-dist")) {
307
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Reducing geometries"));
308
myEdgeCont.reduceGeometries(oc.getFloat("geometry.min-dist"));
309
PROGRESS_TIME_MESSAGE(before);
310
}
311
// @note: removing geometry can create similar edges so joinSimilarEdges must come afterwards
312
// @note: likewise splitting can destroy similarities so joinSimilarEdges must come before
313
if (mayAddOrRemove && oc.getBool("edges.join")) {
314
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining similar edges"));
315
const bool removeDuplicates = oc.getFloat("junctions.join-same") >= 0;
316
myNodeCont.joinSimilarEdges(myDistrictCont, myEdgeCont, myTLLCont, removeDuplicates);
317
// now we may have new chances to remove geometry if wished
318
if (oc.exists("geometry.remove") && oc.getBool("geometry.remove")) {
319
myNodeCont.removeUnwishedNodes(myDistrictCont, myEdgeCont, myTLLCont, myPTStopCont, myPTLineCont, myParkingCont, true);
320
}
321
PROGRESS_TIME_MESSAGE(before);
322
}
323
if (oc.getBool("opposites.guess")) {
324
PROGRESS_BEGIN_MESSAGE(TL("guessing opposite direction edges"));
325
myEdgeCont.guessOpposites();
326
PROGRESS_DONE_MESSAGE();
327
}
328
//
329
if (mayAddOrRemove && oc.exists("geometry.split") && oc.getBool("geometry.split")) {
330
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Splitting geometry edges"));
331
myEdgeCont.splitGeometry(myDistrictCont, myNodeCont);
332
// newly split junctions might also be joinable
333
PROGRESS_TIME_MESSAGE(before);
334
if (oc.getFloat("junctions.join-same") >= 0) {
335
int numJoined3 = myNodeCont.joinSameJunctions(myDistrictCont, myEdgeCont, myTLLCont, oc.getFloat("junctions.join-same"));
336
if (numJoined3 > 0) {
337
WRITE_MESSAGEF(TL(" Joined % junctions after splitting geometry."), toString(numJoined3));
338
}
339
}
340
}
341
// turning direction
342
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing turning directions"));
343
NBTurningDirectionsComputer::computeTurnDirections(myNodeCont);
344
PROGRESS_TIME_MESSAGE(before);
345
// correct edge geometries to avoid overlap
346
if (oc.exists("geometry.avoid-overlap") && oc.getBool("geometry.avoid-overlap")) {
347
myNodeCont.avoidOverlap();
348
}
349
350
// GUESS TLS POSITIONS
351
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Assigning nodes to traffic lights"));
352
if (oc.isSet("tls.set")) {
353
std::vector<std::string> tlControlledNodes = oc.getStringVector("tls.set");
354
TrafficLightType type = SUMOXMLDefinitions::TrafficLightTypes.get(oc.getString("tls.default-type"));
355
for (std::vector<std::string>::const_iterator i = tlControlledNodes.begin(); i != tlControlledNodes.end(); ++i) {
356
NBNode* node = myNodeCont.retrieve(*i);
357
if (node == nullptr) {
358
WRITE_WARNING("Building a tl-logic for junction '" + *i + "' is not possible." + "\n The junction '" + *i + "' is not known.");
359
} else {
360
myNodeCont.setAsTLControlled(node, myTLLCont, type);
361
}
362
}
363
}
364
myNodeCont.guessTLs(oc, myTLLCont);
365
PROGRESS_TIME_MESSAGE(before);
366
367
// guess ramps (after guessing tls because ramps should not be build at traffic lights)
368
const bool modifyRamps = mayAddOrRemove && (
369
(oc.exists("ramps.guess") && oc.getBool("ramps.guess"))
370
|| (oc.exists("ramps.set") && oc.isSet("ramps.set")));
371
if (modifyRamps || (oc.exists("ramps.guess-acceleration-lanes") && oc.getBool("ramps.guess-acceleration-lanes"))) {
372
before = SysUtils::getCurrentMillis();
373
if (modifyRamps) {
374
PROGRESS_BEGIN_MESSAGE(TL("Guessing and setting on-/off-ramps"));
375
}
376
NBNodesEdgesSorter::sortNodesEdges(myNodeCont);
377
NBRampsComputer rc;
378
rc.computeRamps(*this, oc, mayAddOrRemove);
379
380
if (modifyRamps) {
381
PROGRESS_TIME_MESSAGE(before);
382
}
383
}
384
// guess bike lanes
385
if (mayAddOrRemove && ((oc.getBool("bikelanes.guess") || oc.getBool("bikelanes.guess.from-permissions")))) {
386
const int bikelanes = myEdgeCont.guessSpecialLanes(SVC_BICYCLE, oc.getFloat("default.bikelane-width"),
387
oc.getFloat("bikelanes.guess.min-speed"),
388
oc.getFloat("bikelanes.guess.max-speed"),
389
oc.getBool("bikelanes.guess.from-permissions"),
390
"bikelanes.guess.exclude",
391
myTLLCont);
392
WRITE_MESSAGEF(TL("Guessed % bike lanes."), toString(bikelanes));
393
}
394
395
// guess sidewalks
396
if (mayAddOrRemove && ((oc.getBool("sidewalks.guess") || oc.getBool("sidewalks.guess.from-permissions")))) {
397
const int sidewalks = myEdgeCont.guessSpecialLanes(SVC_PEDESTRIAN, oc.getFloat("default.sidewalk-width"),
398
oc.getFloat("sidewalks.guess.min-speed"),
399
oc.getFloat("sidewalks.guess.max-speed"),
400
oc.getBool("sidewalks.guess.from-permissions"),
401
"sidewalks.guess.exclude",
402
myTLLCont);
403
WRITE_MESSAGEF(TL("Guessed % sidewalks."), toString(sidewalks));
404
}
405
// check whether any not previously setable connections may be set now
406
myEdgeCont.recheckPostProcessConnections();
407
408
// remap ids if wished
409
if (mayAddOrRemove) {
410
const bool numericalIDs = oc.getBool("numerical-ids");
411
const bool reservedIDs = oc.isSet("reserved-ids");
412
const bool keptIDs = oc.isSet("kept-ids");
413
int numChangedEdges = myEdgeCont.remapIDs(numericalIDs, reservedIDs, keptIDs, oc.getString("prefix.edge"), myPTStopCont);
414
int numChangedNodes = myNodeCont.remapIDs(numericalIDs, reservedIDs, keptIDs, oc.getString("prefix.junction"), myTLLCont);
415
if (numChangedEdges + numChangedNodes > 0) {
416
WRITE_MESSAGEF(TL("Remapped % edge IDs and % node IDs."), toString(numChangedEdges), toString(numChangedNodes));
417
}
418
}
419
420
//
421
if (oc.exists("geometry.max-angle")) {
422
myEdgeCont.checkGeometries(
423
DEG2RAD(oc.getFloat("geometry.max-angle")),
424
oc.getBool("geometry.max-angle.fix"),
425
oc.getFloat("geometry.min-radius"),
426
oc.getBool("geometry.min-radius.fix"),
427
oc.getBool("geometry.min-radius.fix.railways"));
428
}
429
430
// GEOMETRY COMPUTATION
431
//
432
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Sorting nodes' edges"));
433
NBNodesEdgesSorter::sortNodesEdges(myNodeCont);
434
PROGRESS_TIME_MESSAGE(before);
435
myEdgeCont.computeLaneShapes();
436
//
437
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing node shapes"));
438
if (oc.exists("geometry.junction-mismatch-threshold")) {
439
myNodeCont.computeNodeShapes(oc.getFloat("geometry.junction-mismatch-threshold"));
440
} else {
441
myNodeCont.computeNodeShapes();
442
}
443
PROGRESS_TIME_MESSAGE(before);
444
//
445
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing edge shapes"));
446
myEdgeCont.computeEdgeShapes(oc.getBool("geometry.max-grade.fix") ? oc.getFloat("geometry.max-grade") / 100 : -1);
447
PROGRESS_TIME_MESSAGE(before);
448
// resort edges based on the node and edge shapes
449
NBNodesEdgesSorter::sortNodesEdges(myNodeCont, true);
450
NBTurningDirectionsComputer::computeTurnDirections(myNodeCont, false);
451
452
// APPLY SPEED MODIFICATIONS
453
if (oc.exists("speed.offset")) {
454
const double speedOffset = oc.getFloat("speed.offset");
455
const double speedFactor = oc.getFloat("speed.factor");
456
const double speedMin = oc.getFloat("speed.minimum");
457
if (speedOffset != 0 || speedFactor != 1 || speedMin > 0) {
458
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Applying speed modifications"));
459
for (const auto& it : myEdgeCont) {
460
NBEdge* const e = it.second;
461
for (int i = 0; i < e->getNumLanes(); i++) {
462
e->setSpeed(i, MAX2(e->getLaneSpeed(i) * speedFactor + speedOffset, speedMin));
463
}
464
}
465
PROGRESS_TIME_MESSAGE(before);
466
}
467
}
468
469
// CONNECTIONS COMPUTATION
470
//
471
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing node types"));
472
NBNode::initRailSignalClasses(myNodeCont);
473
NBNodeTypeComputer::computeNodeTypes(myNodeCont, myTLLCont);
474
PROGRESS_TIME_MESSAGE(before);
475
//
476
myNetworkHaveCrossings = oc.getBool("walkingareas");
477
if (mayAddOrRemove && oc.getBool("crossings.guess")) {
478
myNetworkHaveCrossings = true;
479
int crossings = 0;
480
for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
481
crossings += (*i).second->guessCrossings();
482
}
483
WRITE_MESSAGEF(TL("Guessed % pedestrian crossings."), toString(crossings));
484
}
485
if (!myNetworkHaveCrossings) {
486
bool haveValidCrossings = false;
487
// recheck whether we had crossings in the input
488
for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
489
if (i->second->getCrossings().size() > 0) {
490
myNetworkHaveCrossings = true;
491
haveValidCrossings = true;
492
break;
493
} else if (i->second->getCrossingsIncludingInvalid().size() > 0) {
494
myNetworkHaveCrossings = true;
495
}
496
}
497
if (myNetworkHaveCrossings && !haveValidCrossings) {
498
// initial crossings removed or invalidated, keep walkingareas
499
oc.resetWritable();
500
oc.set("walkingareas", "true");
501
}
502
}
503
504
if (!mayAddOrRemove && myNetworkHaveCrossings) {
505
// crossings added via netedit
506
oc.resetWritable();
507
oc.set("no-internal-links", "false");
508
}
509
510
//
511
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing priorities"));
512
NBEdgePriorityComputer::computeEdgePriorities(myNodeCont);
513
PROGRESS_TIME_MESSAGE(before);
514
//
515
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing approached edges"));
516
myEdgeCont.computeEdge2Edges(oc.getBool("no-left-connections"));
517
PROGRESS_TIME_MESSAGE(before);
518
//
519
if (oc.getBool("roundabouts.guess")) {
520
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Guessing and setting roundabouts"));
521
const int numGuessed = myEdgeCont.guessRoundabouts();
522
if (numGuessed > 0) {
523
WRITE_MESSAGEF(TL(" Guessed % roundabout(s)."), toString(numGuessed));
524
}
525
PROGRESS_TIME_MESSAGE(before);
526
}
527
myEdgeCont.markRoundabouts();
528
//
529
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing approaching lanes"));
530
myEdgeCont.computeLanes2Edges();
531
PROGRESS_TIME_MESSAGE(before);
532
//
533
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Dividing of lanes on approached lanes"));
534
myNodeCont.computeLanes2Lanes();
535
myEdgeCont.sortOutgoingLanesConnections();
536
PROGRESS_TIME_MESSAGE(before);
537
//
538
if (oc.getBool("fringe.guess")) {
539
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Guessing Network fringe"));
540
const int numGuessed = myNodeCont.guessFringe();
541
if (numGuessed > 0) {
542
WRITE_MESSAGEF(TL(" Guessed % fringe nodes."), toString(numGuessed));
543
}
544
PROGRESS_TIME_MESSAGE(before);
545
}
546
//
547
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Processing turnarounds"));
548
if (!oc.getBool("no-turnarounds")) {
549
myEdgeCont.appendTurnarounds(
550
oc.getBool("no-turnarounds.tls"),
551
oc.getBool("no-turnarounds.fringe"),
552
oc.getBool("no-turnarounds.except-deadend"),
553
oc.getBool("no-turnarounds.except-turnlane"),
554
oc.getBool("no-turnarounds.geometry"));
555
} else {
556
myEdgeCont.appendTurnarounds(explicitTurnarounds, oc.getBool("no-turnarounds.tls"));
557
}
558
if (oc.exists("railway.topology.repair.stop-turn") && oc.getBool("railway.topology.repair.stop-turn")
559
&& myPTStopCont.getStops().size() > 0) {
560
// allow direction reversal at all bidi-edges with stops
561
myEdgeCont.appendRailwayTurnarounds(myPTStopCont);
562
}
563
PROGRESS_TIME_MESSAGE(before);
564
//
565
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Rechecking of lane endings"));
566
myEdgeCont.recheckLanes();
567
PROGRESS_TIME_MESSAGE(before);
568
569
if (myNetworkHaveCrossings && !oc.getBool("no-internal-links")) {
570
for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
571
i->second->buildCrossingsAndWalkingAreas();
572
}
573
} else {
574
for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
575
// needed by netedit if the last crossings was deleted from the network
576
// and walkingareas have been invalidated since the last call to compute()
577
i->second->discardWalkingareas();
578
}
579
if (oc.getBool("no-internal-links")) {
580
for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
581
i->second->discardAllCrossings(false);
582
}
583
}
584
}
585
// join traffic lights (after building connections)
586
if (oc.getBool("tls.join")) {
587
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Joining traffic light nodes"));
588
myNodeCont.joinTLS(myTLLCont, oc.getFloat("tls.join-dist"));
589
PROGRESS_TIME_MESSAGE(before);
590
}
591
592
// COMPUTING RIGHT-OF-WAY AND TRAFFIC LIGHT PROGRAMS
593
//
594
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing traffic light control information"));
595
myTLLCont.setTLControllingInformation(myEdgeCont, myNodeCont);
596
if (oc.exists("opendrive-files") && oc.isSet("opendrive-files")) {
597
myTLLCont.setOpenDriveSignalParameters();
598
}
599
PROGRESS_TIME_MESSAGE(before);
600
//
601
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing node logics"));
602
myNodeCont.computeLogics(myEdgeCont);
603
PROGRESS_TIME_MESSAGE(before);
604
605
//
606
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Computing traffic light logics"));
607
std::pair<int, int> numbers = myTLLCont.computeLogics(oc);
608
PROGRESS_TIME_MESSAGE(before);
609
std::string progCount = "";
610
if (numbers.first != numbers.second) {
611
progCount = "(" + toString(numbers.second) + " programs) ";
612
}
613
WRITE_MESSAGEF(TL(" % traffic light(s) %computed."), toString(numbers.first), progCount);
614
if (oc.exists("opendrive-files") && oc.isSet("opendrive-files") && oc.getBool("opendrive.signal-groups")) {
615
myTLLCont.applyOpenDriveControllers(oc);
616
}
617
618
for (std::map<std::string, NBEdge*>::const_iterator i = myEdgeCont.begin(); i != myEdgeCont.end(); ++i) {
619
(*i).second->sortOutgoingConnectionsByIndex();
620
}
621
// FINISHING INNER EDGES
622
std::set<NBTrafficLightDefinition*> largeNodeTLS;
623
if (!oc.getBool("no-internal-links")) {
624
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Building inner edges"));
625
// walking areas shall only be built if crossings are wished as well
626
for (const auto& item : myNodeCont) {
627
if (item.second->buildInnerEdges() > NBTrafficLightDefinition::MIN_YELLOW_SECONDS) {
628
const std::set<NBTrafficLightDefinition*>& tlDefs = item.second->getControllingTLS();
629
largeNodeTLS.insert(tlDefs.begin(), tlDefs.end());
630
}
631
}
632
PROGRESS_TIME_MESSAGE(before);
633
}
634
// PATCH NODE SHAPES
635
if (oc.getFloat("junctions.scurve-stretch") > 0) {
636
// @note: nodes have collected correction hints in buildInnerEdges()
637
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("stretching junctions to smooth geometries"));
638
myEdgeCont.computeLaneShapes();
639
myNodeCont.computeNodeShapes();
640
myEdgeCont.computeEdgeShapes(oc.getBool("geometry.max-grade.fix") ? oc.getFloat("geometry.max-grade") / 100 : -1);
641
for (const auto& item : myNodeCont) {
642
item.second->buildInnerEdges();
643
}
644
PROGRESS_TIME_MESSAGE(before);
645
}
646
if (myEdgeCont.getNumEdgeSplits() > 0 && !oc.getBool("no-internal-links")) {
647
// edges with custom lengths were split, this has to take into account
648
// internal edge lengths (after geometry computation)
649
myEdgeCont.fixSplitCustomLength();
650
}
651
// recheck phases for large junctions
652
for (NBTrafficLightDefinition* def : largeNodeTLS) {
653
myTLLCont.computeSingleLogic(oc, def);
654
}
655
// compute lane-to-lane node logics (require traffic lights and inner edges to be done)
656
myNodeCont.computeLogics2(myEdgeCont, oc);
657
658
// remove guessed traffic lights at junctions without conflicts (requires computeLogics2)
659
myNodeCont.recheckGuessedTLS(myTLLCont);
660
661
// compute keepClear status (requires computeLogics2)
662
myNodeCont.computeKeepClear();
663
664
//
665
if (oc.isSet("street-sign-output")) {
666
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Generating street signs"));
667
myEdgeCont.generateStreetSigns();
668
PROGRESS_TIME_MESSAGE(before);
669
}
670
671
672
if (lefthand != oc.getBool("flip-y-axis")) {
673
mirrorX();
674
}
675
676
if (oc.exists("geometry.check-overlap") && oc.getFloat("geometry.check-overlap") > 0) {
677
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Checking overlapping edges"));
678
myEdgeCont.checkOverlap(oc.getFloat("geometry.check-overlap"), oc.getFloat("geometry.check-overlap.vertical-threshold"));
679
PROGRESS_TIME_MESSAGE(before);
680
}
681
if (geoConvHelper.getConvBoundary().getZRange() > 0 && oc.getFloat("geometry.max-grade") > 0) {
682
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Checking edge grade"));
683
// user input is in %
684
myEdgeCont.checkGrade(oc.getFloat("geometry.max-grade") / 100);
685
PROGRESS_TIME_MESSAGE(before);
686
}
687
688
// find accesses for pt rail stops and add bidi-stops
689
if (!myPTStopCont.getStops().empty()) {
690
// re-adapt stop lanes after adding special lanes and cutting edge shapes at junction
691
myPTStopCont.assignLanes(myEdgeCont);
692
before = SysUtils::getCurrentMillis();
693
int numBidiStops = 0;
694
if (!oc.getBool("ptstop-output.no-bidi")) {
695
numBidiStops = myPTStopCont.generateBidiStops(myEdgeCont);
696
} else {
697
numBidiStops = myPTStopCont.countBidiStops(myEdgeCont);
698
}
699
PROGRESS_BEGIN_MESSAGE(TL("Find accesses for pt rail stops"));
700
double maxRadius = oc.getFloat("railway.access-distance");
701
double accessFactor = oc.getFloat("railway.access-factor");
702
int maxCount = oc.getInt("railway.max-accesses");
703
myPTStopCont.findAccessEdgesForRailStops(myEdgeCont, maxRadius, maxCount, accessFactor);
704
PROGRESS_TIME_MESSAGE(before);
705
if (numBidiStops > 0) {
706
myPTLineCont.fixBidiStops(myEdgeCont);
707
}
708
}
709
myPTLineCont.removeInvalidEdges(myEdgeCont);
710
// ensure that all turning lanes have sufficient permissions
711
myPTLineCont.fixPermissions();
712
if (oc.exists("ptline-clean-up") && oc.getBool("ptline-clean-up")) {
713
before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Cleaning up public transport stops that are not served by any line"));
714
std::set<std::string> usedStops = myPTLineCont.getServedPTStops();
715
myPTStopCont.postprocess(usedStops);
716
PROGRESS_TIME_MESSAGE(before);
717
} else {
718
int numDeletedStops = myPTStopCont.cleanupDeleted(myEdgeCont);
719
if (numDeletedStops > 0) {
720
WRITE_WARNINGF(TL("Removed % pt stops because they could not be assigned to the network"), toString(numDeletedStops));
721
}
722
}
723
724
if (oc.exists("ignore-change-restrictions") && !oc.isDefault("ignore-change-restrictions")) {
725
SVCPermissions ignoring = parseVehicleClasses(oc.getStringVector("ignore-change-restrictions"));
726
myEdgeCont.updateAllChangeRestrictions(ignoring);
727
}
728
729
NBRequest::reportWarnings();
730
// report on very large networks
731
if (MAX2(geoConvHelper.getConvBoundary().xmax(), geoConvHelper.getConvBoundary().ymax()) > 1000000 ||
732
MIN2(geoConvHelper.getConvBoundary().xmin(), geoConvHelper.getConvBoundary().ymin()) < -1000000) {
733
WRITE_WARNING(TL("Network contains very large coordinates and will probably flicker in the GUI. Check for outlying nodes and make sure the network is shifted to the coordinate origin"));
734
}
735
736
// clean up OSM processing params
737
if (oc.exists("osm-files") && oc.isSet("osm-files")) {
738
for (auto item : myEdgeCont) {
739
item.second->unsetParameter(NBTrafficLightDefinition::OSM_DIRECTION);
740
}
741
}
742
}
743
744
745
void
746
NBNetBuilder::moveToOrigin(GeoConvHelper& geoConvHelper, bool lefthand) {
747
long before = PROGRESS_BEGIN_TIME_MESSAGE(TL("Moving network to origin"));
748
Boundary boundary = geoConvHelper.getConvBoundary();
749
const double x = -boundary.xmin();
750
const double y = -(lefthand ? boundary.ymax() : boundary.ymin());
751
//if (lefthand) {
752
// y = boundary.ymax();
753
//}
754
for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
755
(*i).second->reshiftPosition(x, y);
756
}
757
for (std::map<std::string, NBEdge*>::const_iterator i = myEdgeCont.begin(); i != myEdgeCont.end(); ++i) {
758
(*i).second->reshiftPosition(x, y);
759
}
760
for (std::map<std::string, NBDistrict*>::const_iterator i = myDistrictCont.begin(); i != myDistrictCont.end(); ++i) {
761
(*i).second->reshiftPosition(x, y);
762
}
763
for (const auto& stopIt : myPTStopCont.getStops()) {
764
stopIt.second->reshiftPosition(x, y);
765
}
766
geoConvHelper.moveConvertedBy(x, y);
767
PROGRESS_TIME_MESSAGE(before);
768
}
769
770
771
void
772
NBNetBuilder::roundInputs() {
773
// ensure that computed speeds (using during right-of-way computation) are the same as the written speeds
774
for (auto item : myEdgeCont) {
775
item.second->roundSpeed();
776
}
777
// ensure that derived junction and lane geometry computation is done with the same input
778
// edge coordinates as those that will be written to the output.
779
for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
780
(*i).second->roundGeometry();
781
}
782
for (std::map<std::string, NBEdge*>::const_iterator i = myEdgeCont.begin(); i != myEdgeCont.end(); ++i) {
783
(*i).second->roundGeometry();
784
}
785
}
786
787
788
void
789
NBNetBuilder::mirrorX() {
790
// mirror the network along the X-axis
791
for (std::map<std::string, NBNode*>::const_iterator i = myNodeCont.begin(); i != myNodeCont.end(); ++i) {
792
(*i).second->mirrorX();
793
}
794
for (std::map<std::string, NBEdge*>::const_iterator i = myEdgeCont.begin(); i != myEdgeCont.end(); ++i) {
795
(*i).second->mirrorX();
796
}
797
for (std::map<std::string, NBDistrict*>::const_iterator i = myDistrictCont.begin(); i != myDistrictCont.end(); ++i) {
798
(*i).second->mirrorX();
799
}
800
for (const auto& stopIt : myPTStopCont.getStops()) {
801
stopIt.second->mirrorX();
802
}
803
}
804
805
806
bool
807
NBNetBuilder::transformCoordinate(Position& from, bool includeInBoundary, GeoConvHelper* from_srs) {
808
Position orig(from);
809
bool ok = true;
810
if (GeoConvHelper::getNumLoaded() > 1
811
&& GeoConvHelper::getLoaded().usingGeoProjection()
812
&& from_srs != nullptr
813
&& from_srs->usingGeoProjection()
814
&& *from_srs != GeoConvHelper::getLoaded()) {
815
from_srs->cartesian2geo(from);
816
ok &= GeoConvHelper::getLoaded().x2cartesian(from, false);
817
}
818
if (from_srs == nullptr || !GeoConvHelper::getProcessing().usingGeoProjection()) {
819
// if getProcessing is not a geo-projection, assume it a cartesian transformation (i.e. shift)
820
ok &= GeoConvHelper::getProcessing().x2cartesian(from, includeInBoundary);
821
822
if (from_srs == nullptr && GeoConvHelper::getProcessing().usingGeoProjection()
823
&& GeoConvHelper::getNumLoaded() > 0
824
&& GeoConvHelper::getLoaded().usingGeoProjection()) {
825
// apply geo patch to loaded geo-network (offset must match)
826
from = from + GeoConvHelper::getLoaded().getOffset();
827
}
828
}
829
if (ok) {
830
const NBHeightMapper& hm = NBHeightMapper::get();
831
if (hm.ready()) {
832
if (from_srs != nullptr && from_srs->usingGeoProjection()) {
833
from_srs->cartesian2geo(orig);
834
}
835
from.setz(hm.getZ(orig));
836
}
837
}
838
const double eps = 1e-6;
839
from.set(std::round(from.x() / eps) * eps, std::round(from.y() / eps) * eps, std::round(from.z() / eps) * eps);
840
return ok;
841
}
842
843
844
bool
845
NBNetBuilder::transformCoordinates(PositionVector& from, bool includeInBoundary, GeoConvHelper* from_srs) {
846
const double maxLength = OptionsCont::getOptions().getFloat("geometry.max-segment-length");
847
if (maxLength > 0 && from.size() > 1) {
848
// transformation to cartesian coordinates must happen before we can check segment length
849
PositionVector copy = from;
850
for (int i = 0; i < (int) from.size(); i++) {
851
transformCoordinate(copy[i], false);
852
}
853
addGeometrySegments(from, copy, maxLength);
854
}
855
bool ok = true;
856
for (int i = 0; i < (int) from.size(); i++) {
857
ok = ok && transformCoordinate(from[i], includeInBoundary, from_srs);
858
}
859
return ok;
860
}
861
862
863
int
864
NBNetBuilder::addGeometrySegments(PositionVector& from, const PositionVector& cartesian, const double maxLength) {
865
// check lengths and insert new points where needed (in the original
866
// coordinate system)
867
int inserted = 0;
868
for (int i = 0; i < (int)cartesian.size() - 1; i++) {
869
Position start = from[i + inserted];
870
Position end = from[i + inserted + 1];
871
double length = cartesian[i].distanceTo(cartesian[i + 1]);
872
const Position step = (end - start) * (maxLength / length);
873
int steps = 0;
874
while (length > maxLength) {
875
length -= maxLength;
876
steps++;
877
from.insert(from.begin() + i + inserted + 1, start + (step * steps));
878
inserted++;
879
}
880
}
881
return inserted;
882
}
883
884
885
bool
886
NBNetBuilder::runningNetedit() {
887
// see GNELoadThread::fillOptions
888
return OptionsCont::getOptions().exists("new");
889
}
890
891
892
/****************************************************************************/
893
894