Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netbuild/NBAlgorithms_Ramps.cpp
169665 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2012-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 NBAlgorithms_Ramps.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Michael Behrisch
18
/// @date 29. March 2012
19
///
20
// Algorithms for highway on-/off-ramps computation
21
/****************************************************************************/
22
#include <config.h>
23
24
#include <cassert>
25
#include <utils/options/OptionsCont.h>
26
#include <utils/common/MsgHandler.h>
27
#include <utils/common/ToString.h>
28
#include "NBNetBuilder.h"
29
#include "NBNodeCont.h"
30
#include "NBNode.h"
31
#include "NBEdge.h"
32
#include "NBAlgorithms_Ramps.h"
33
34
#define OFFRAMP_LOOKBACK 500
35
#define MIN_SPLIT_LENGTH POSITION_EPS
36
37
//#define DEBUG_RAMPS
38
#define DEBUGNODEID ""
39
#define DEBUGCOND(obj) ((obj != 0 && (obj)->getID() == DEBUGNODEID))
40
//#define DEBUGCOND(obj) true
41
42
// ===========================================================================
43
// static members
44
// ===========================================================================
45
const std::string NBRampsComputer::ADDED_ON_RAMP_EDGE("-AddedOnRampEdge");
46
47
// ===========================================================================
48
// method definitions
49
// ===========================================================================
50
// ---------------------------------------------------------------------------
51
// NBRampsComputer
52
// ---------------------------------------------------------------------------
53
54
NBRampsComputer::NBRampsComputer() { }
55
56
void
57
NBRampsComputer::computeRamps(NBNetBuilder& nb, OptionsCont& oc, bool mayAddOrRemove) {
58
const bool guessAndAdd = oc.getBool("ramps.guess") && mayAddOrRemove;
59
const double minHighwaySpeed = oc.getFloat("ramps.min-highway-speed");
60
const double maxRampSpeed = oc.getFloat("ramps.max-ramp-speed");
61
const double rampLength = oc.getFloat("ramps.ramp-length");
62
const double minWeaveLength = oc.getFloat("ramps.min-weave-length");
63
const bool dontSplit = oc.getBool("ramps.no-split");
64
NBNodeCont& nc = nb.getNodeCont();
65
NBEdgeCont& ec = nb.getEdgeCont();
66
NBDistrictCont& dc = nb.getDistrictCont();
67
std::set<NBEdge*> incremented;
68
// collect join exclusions
69
std::set<std::string> noramps;
70
if (oc.isSet("ramps.unset")) {
71
std::vector<std::string> edges = oc.getStringVector("ramps.unset");
72
noramps.insert(edges.begin(), edges.end());
73
}
74
// exclude roundabouts
75
for (const EdgeSet& round : ec.getRoundabouts()) {
76
for (NBEdge* const edge : round) {
77
noramps.insert(edge->getID());
78
}
79
}
80
// exclude public transport edges
81
nb.getPTStopCont().addEdges2Keep(oc, noramps);
82
nb.getParkingCont().addEdges2Keep(oc, noramps);
83
84
// check whether on-off ramps shall be guessed
85
if (guessAndAdd || oc.getBool("ramps.guess-acceleration-lanes")) {
86
for (const auto& it : ec) {
87
it.second->markOffRamp(false);
88
}
89
90
// if an edge is part of two ramps, ordering is important
91
std::set<NBNode*, ComparatorIdLess> potOnRamps;
92
std::set<NBNode*, ComparatorIdLess> potOffRamps;
93
for (const auto& i : nc) {
94
NBNode* cur = i.second;
95
#ifdef DEBUG_RAMPS
96
if (DEBUGCOND(cur)) {
97
std::cout << "check ramps cur=" << cur->getID() << "\n";
98
}
99
#endif
100
if (mayNeedOnRamp(cur, minHighwaySpeed, maxRampSpeed, noramps, minWeaveLength)) {
101
potOnRamps.insert(cur);
102
}
103
if (mayNeedOffRamp(cur, minHighwaySpeed, maxRampSpeed, noramps)) {
104
potOffRamps.insert(cur);
105
}
106
}
107
for (std::set<NBNode*, ComparatorIdLess>::const_iterator i = potOnRamps.begin(); i != potOnRamps.end(); ++i) {
108
buildOnRamp(*i, nc, ec, dc, rampLength, dontSplit || !guessAndAdd, guessAndAdd);
109
}
110
for (std::set<NBNode*, ComparatorIdLess>::const_iterator i = potOffRamps.begin(); i != potOffRamps.end(); ++i) {
111
buildOffRamp(*i, nc, ec, dc, rampLength, dontSplit || !guessAndAdd, guessAndAdd, potOnRamps);
112
}
113
}
114
// check whether on-off ramps are specified
115
if (oc.isSet("ramps.set") && mayAddOrRemove) {
116
std::vector<std::string> edges = oc.getStringVector("ramps.set");
117
std::set<NBNode*, ComparatorIdLess> potOnRamps;
118
for (const std::string& i : edges) {
119
NBEdge* e = ec.retrieve(i);
120
if (noramps.count(i) != 0) {
121
WRITE_WARNINGF(TL("Can not build ramp on edge '%' - the edge is unsuitable."), i);
122
continue;
123
}
124
if (e == nullptr) {
125
WRITE_WARNINGF(TL("Can not build on ramp on edge '%' - the edge is not known."), i);
126
continue;
127
}
128
NBNode* from = e->getFromNode();
129
if (from->getIncomingEdges().size() == 2 && from->getOutgoingEdges().size() == 1) {
130
buildOnRamp(from, nc, ec, dc, rampLength, dontSplit, true);
131
potOnRamps.insert(from);
132
}
133
// load edge again to check offramps
134
e = ec.retrieve(i);
135
if (e == nullptr) {
136
WRITE_WARNINGF(TL("Can not build off ramp on edge '%' - the edge is not known."), i);
137
continue;
138
}
139
NBNode* to = e->getToNode();
140
if (to->getIncomingEdges().size() == 1 && to->getOutgoingEdges().size() == 2) {
141
buildOffRamp(to, nc, ec, dc, rampLength, dontSplit, true, potOnRamps);
142
}
143
}
144
}
145
}
146
147
148
bool
149
NBRampsComputer::mayNeedOnRamp(NBNode* cur, double minHighwaySpeed, double maxRampSpeed, const std::set<std::string>& noramps, double minWeaveLength) {
150
if (cur->getOutgoingEdges().size() != 1 || cur->getIncomingEdges().size() != 2) {
151
return false;
152
}
153
NBEdge* potHighway, *potRamp, *cont;
154
getOnRampEdges(cur, &potHighway, &potRamp, &cont);
155
// may be an on-ramp
156
#ifdef DEBUG_RAMPS
157
if (DEBUGCOND(cur)) {
158
std::cout << "check on ramp hw=" << potHighway->getID() << " ramp=" << potRamp->getID() << " cont=" << cont->getID() << std::endl;
159
}
160
#endif
161
if (fulfillsRampConstraints(potHighway, potRamp, cont, minHighwaySpeed, maxRampSpeed, noramps)) {
162
// prevent short weaving section
163
double seen = cont->getLength();
164
while (seen < minWeaveLength) {
165
if (cont->getToNode()->getOutgoingEdges().size() > 1) {
166
return false;
167
} else if (cont->getToNode()->getOutgoingEdges().size() == 0) {
168
return true;
169
}
170
cont = cont->getToNode()->getOutgoingEdges().front();
171
seen += cont->getLength();
172
}
173
return true;
174
} else {
175
return false;
176
}
177
}
178
179
180
bool
181
NBRampsComputer::mayNeedOffRamp(NBNode* cur, double minHighwaySpeed, double maxRampSpeed, const std::set<std::string>& noramps) {
182
if (cur->getIncomingEdges().size() != 1 || cur->getOutgoingEdges().size() != 2) {
183
return false;
184
}
185
// may be an off-ramp
186
NBEdge* potHighway, *potRamp, *prev;
187
getOffRampEdges(cur, &potHighway, &potRamp, &prev);
188
#ifdef DEBUG_RAMPS
189
if (DEBUGCOND(cur)) {
190
std::cout << "check off ramp hw=" << potHighway->getID() << " ramp=" << potRamp->getID() << " prev=" << prev->getID() << std::endl;
191
}
192
#endif
193
return fulfillsRampConstraints(potHighway, potRamp, prev, minHighwaySpeed, maxRampSpeed, noramps);
194
}
195
196
197
void
198
NBRampsComputer::buildOnRamp(NBNode* cur, NBNodeCont& nc, NBEdgeCont& ec, NBDistrictCont& dc, double rampLength, bool dontSplit, bool addLanes) {
199
NBEdge* potHighway, *potRamp, *cont;
200
getOnRampEdges(cur, &potHighway, &potRamp, &cont);
201
#ifdef DEBUG_RAMPS
202
if (DEBUGCOND(cur)) {
203
std::cout << "buildOnRamp cur=" << cur->getID() << " hw=" << potHighway->getID() << " ramp=" << potRamp->getID() << " cont=" << cont->getID() << "\n";
204
}
205
#endif
206
// compute the number of lanes to append
207
const int firstLaneNumber = cont->getNumLanes();
208
const int toAdd = (potRamp->getNumLanes() + potHighway->getNumLanes()) - firstLaneNumber;
209
NBEdge* first = cont;
210
NBEdge* last = cont;
211
NBEdge* curr = cont;
212
std::set<NBEdge*> incremented;
213
if (addLanes && toAdd > 0 && std::find(incremented.begin(), incremented.end(), cont) == incremented.end()) {
214
double currLength = 0;
215
while (curr != nullptr && currLength + curr->getGeometry().length() - POSITION_EPS < rampLength) {
216
if (find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
217
curr->incLaneNo(toAdd);
218
// we need to distinguish between loading a .net.xml (connections are defined and applicable)
219
// and manual connection patches (should be post-prcess because the lane wasn't added yet)
220
if (curr->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER || !ec.hasPostProcessConnection(curr->getID())) {
221
curr->invalidateConnections(true);
222
}
223
incremented.insert(curr);
224
moveRampRight(curr, toAdd);
225
currLength += curr->getGeometry().length(); // !!! loaded length?
226
last = curr;
227
// mark acceleration lanes
228
for (int i = 0; i < curr->getNumLanes() - potHighway->getNumLanes(); ++i) {
229
curr->setAcceleration(i, true);
230
}
231
}
232
NBNode* nextN = curr->getToNode();
233
if (nextN->getOutgoingEdges().size() == 1 && nextN->getIncomingEdges().size() == 1) {
234
curr = nextN->getOutgoingEdges()[0];
235
if (curr->getNumLanes() != firstLaneNumber) {
236
// the number of lanes changes along the computation; we'll stop...
237
curr = nullptr;
238
} else if (curr->isTurningDirectionAt(last)) {
239
// turnarounds certainly should not be included in a ramp
240
curr = nullptr;
241
} else if (curr == potHighway || curr == potRamp) {
242
// circular connectivity. do not split!
243
curr = nullptr;
244
}
245
} else {
246
// ambiguous; and, in fact, what should it be? ...stop
247
curr = nullptr;
248
}
249
}
250
// check whether a further split is necessary
251
if (curr != nullptr && !dontSplit && currLength + MIN_SPLIT_LENGTH < rampLength && curr->getNumLanes() == firstLaneNumber && std::find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
252
// there is enough place to build a ramp; do it
253
bool wasFirst = first == curr;
254
std::string newNodeID = getUnusedID(curr->getID() + "-AddedOnRampNode", nc);
255
std::string newEdgeID = getUnusedID(curr->getID() + ADDED_ON_RAMP_EDGE, ec);
256
NBNode* rn = new NBNode(newNodeID, curr->getGeometry().positionAtOffset(rampLength - currLength));
257
nc.insert(rn);
258
std::string name = curr->getID();
259
const double currShift = myShiftedEdges[curr];
260
if (!ec.splitAt(dc, curr, rn, newEdgeID, curr->getID(), curr->getNumLanes() + toAdd, curr->getNumLanes())) {
261
WRITE_WARNING("Could not build on-ramp for edge '" + curr->getID() + "' for unknown reason");
262
return;
263
}
264
//ec.retrieve(name)->invalidateConnections();
265
curr = ec.retrieve(newEdgeID);
266
// copy shift over
267
myShiftedEdges[curr] = currShift;
268
incremented.insert(curr);
269
last = curr;
270
moveRampRight(curr, toAdd);
271
if (wasFirst) {
272
first = curr;
273
}
274
// mark acceleration lanes
275
for (int i = 0; i < curr->getNumLanes() - potHighway->getNumLanes(); ++i) {
276
curr->setAcceleration(i, true);
277
}
278
}
279
if (curr == cont && dontSplit && addLanes) {
280
WRITE_WARNING("Could not build on-ramp for edge '" + curr->getID() + "' due to option '--ramps.no-split'");
281
return;
282
}
283
} else {
284
// mark acceleration lanes
285
for (int i = 0; i < firstLaneNumber - potHighway->getNumLanes(); ++i) {
286
cont->setAcceleration(i, true);
287
}
288
}
289
// set connections from ramp/highway to added ramp
290
if (addLanes) {
291
if (potHighway->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
292
if (!potHighway->addLane2LaneConnections(0, first, potRamp->getNumLanes(), MIN2(first->getNumLanes() - potRamp->getNumLanes(), potHighway->getNumLanes()), NBEdge::Lane2LaneInfoType::VALIDATED, true)) {
293
throw ProcessError(TL("Could not set connection!"));
294
}
295
}
296
if (potRamp->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
297
if (!potRamp->addLane2LaneConnections(0, first, 0, potRamp->getNumLanes(), NBEdge::Lane2LaneInfoType::VALIDATED, true)) {
298
throw ProcessError(TL("Could not set connection!"));
299
}
300
}
301
patchRampGeometry(potRamp, first, potHighway, false);
302
}
303
}
304
305
306
void
307
NBRampsComputer::buildOffRamp(NBNode* cur, NBNodeCont& nc, NBEdgeCont& ec, NBDistrictCont& dc, double rampLength, bool dontSplit, bool addLanes,
308
const std::set<NBNode*, ComparatorIdLess>& potOnRamps) {
309
NBEdge* potHighway, *potRamp, *prev;
310
getOffRampEdges(cur, &potHighway, &potRamp, &prev);
311
#ifdef DEBUG_RAMPS
312
if (DEBUGCOND(cur)) {
313
std::cout << "buildOffRamp cur=" << cur->getID() << " hw=" << potHighway->getID() << " ramp=" << potRamp->getID() << " prev=" << prev->getID() << "\n";
314
}
315
#endif
316
// compute the number of lanes to append
317
const int firstLaneNumber = prev->getNumLanes();
318
const int toAdd = (potRamp->getNumLanes() + potHighway->getNumLanes()) - firstLaneNumber;
319
NBEdge* first = prev;
320
NBEdge* last = prev;
321
NBEdge* curr = prev;
322
std::set<NBEdge*> incremented;
323
if (addLanes && toAdd > 0 && std::find(incremented.begin(), incremented.end(), prev) == incremented.end()) {
324
double currLength = 0;
325
while (curr != nullptr && currLength + curr->getGeometry().length() - POSITION_EPS < rampLength) {
326
if (find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
327
curr->incLaneNo(toAdd);
328
// we need to distinguish between loading a .net.xml (connections are defined and applicable)
329
// and manual connection patches (should be post-prcess because the lane wasn't added yet)
330
if (curr->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER || !ec.hasPostProcessConnection(curr->getID())) {
331
curr->invalidateConnections(true);
332
}
333
incremented.insert(curr);
334
moveRampRight(curr, toAdd);
335
currLength += curr->getGeometry().length(); // !!! loaded length?
336
last = curr;
337
}
338
NBNode* prevN = curr->getFromNode();
339
if (prevN->getIncomingEdges().size() == 1 && prevN->getOutgoingEdges().size() == 1) {
340
curr = prevN->getIncomingEdges()[0];
341
if (curr->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER || !ec.hasPostProcessConnection(curr->getID())) {
342
// curr might be an onRamp. In this case connections need to be rebuilt
343
curr->invalidateConnections();
344
}
345
if (curr->getNumLanes() != firstLaneNumber) {
346
// the number of lanes changes along the computation; we'll stop...
347
curr = nullptr;
348
} else if (last->isTurningDirectionAt(curr)) {
349
// turnarounds certainly should not be included in a ramp
350
curr = nullptr;
351
} else if (curr == potHighway || curr == potRamp) {
352
// circular connectivity. do not split!
353
curr = nullptr;
354
}
355
} else {
356
// ambiguous; and, in fact, what should it be? ...stop
357
curr = nullptr;
358
}
359
}
360
// check whether a further split is necessary
361
if (curr != nullptr && !dontSplit && currLength + MIN_SPLIT_LENGTH < rampLength && curr->getNumLanes() == firstLaneNumber && std::find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
362
// there is enough place to build a ramp; do it
363
bool wasFirst = first == curr;
364
Position pos = curr->getGeometry().positionAtOffset(curr->getGeometry().length() - (rampLength - currLength));
365
std::string newNodeID = getUnusedID(curr->getID() + "-AddedOffRampNode", nc);
366
std::string newEdgeID = getUnusedID(curr->getID() + "-AddedOffRampEdge", ec);
367
NBNode* rn = new NBNode(newNodeID, pos);
368
nc.insert(rn);
369
std::string name = curr->getID();
370
const double currShift = myShiftedEdges[curr];
371
if (!ec.splitAt(dc, curr, rn, curr->getID(), newEdgeID, curr->getNumLanes(), curr->getNumLanes() + toAdd)) {
372
WRITE_WARNING("Could not build off-ramp for edge '" + curr->getID() + "' for unknown reason");
373
return;
374
}
375
curr = ec.retrieve(newEdgeID);
376
// copy shift over
377
myShiftedEdges[curr] = currShift;
378
incremented.insert(curr);
379
last = curr;
380
moveRampRight(curr, toAdd);
381
if (wasFirst) {
382
first = curr;
383
}
384
}
385
if (curr == prev && dontSplit && addLanes) {
386
WRITE_WARNING("Could not build off-ramp for edge '" + curr->getID() + "' due to option '--ramps.no-split'");
387
return;
388
}
389
}
390
NBEdge* toMark = first;
391
toMark->markOffRamp(true);
392
double markedLength = toMark->getLoadedLength();
393
while (markedLength < OFFRAMP_LOOKBACK) {
394
if (toMark != first && toMark->getToNode()->getOutgoingEdges().size() != 1) {
395
break;
396
}
397
NBNode* from = toMark->getFromNode();
398
if (from->getIncomingEdges().size() == 1) {
399
toMark = from->getIncomingEdges()[0];
400
} else if (potOnRamps.count(from) == 1) {
401
NBEdge* potOnRamp, *cont;
402
getOnRampEdges(from, &toMark, &potOnRamp, &cont);
403
} else {
404
break;
405
}
406
toMark->markOffRamp(true);
407
markedLength += toMark->getLoadedLength();
408
}
409
// set connections from added ramp to ramp/highway
410
if (addLanes) {
411
if (first->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
412
if (!first->addLane2LaneConnections(potRamp->getNumLanes(), potHighway, 0, MIN2(first->getNumLanes() - 1, potHighway->getNumLanes()), NBEdge::Lane2LaneInfoType::VALIDATED, true)) {
413
throw ProcessError(TL("Could not set connection!"));
414
}
415
if (!first->addLane2LaneConnections(0, potRamp, 0, potRamp->getNumLanes(), NBEdge::Lane2LaneInfoType::VALIDATED, false)) {
416
throw ProcessError(TL("Could not set connection!"));
417
}
418
}
419
patchRampGeometry(potRamp, first, potHighway, true);
420
}
421
}
422
423
424
void
425
NBRampsComputer::moveRampRight(NBEdge* ramp, int addedLanes) {
426
if (ramp->getLaneSpreadFunction() != LaneSpreadFunction::CENTER) {
427
return;
428
}
429
try {
430
PositionVector g = ramp->getGeometry();
431
double offset = (0.5 * addedLanes *
432
(ramp->getLaneWidth() == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : ramp->getLaneWidth()));
433
if (myShiftedEdges.count(ramp) != 0) {
434
offset -= myShiftedEdges[ramp];
435
}
436
g.move2side(offset);
437
ramp->setGeometry(g);
438
myShiftedEdges[ramp] = offset;
439
} catch (InvalidArgument&) {
440
WRITE_WARNINGF(TL("For edge '%': could not compute shape."), ramp->getID());
441
}
442
}
443
444
445
bool
446
NBRampsComputer::determinedBySpeed(NBEdge** potHighway, NBEdge** potRamp) {
447
if (fabs((*potHighway)->getSpeed() - (*potRamp)->getSpeed()) < .1) {
448
return false;
449
}
450
if ((*potHighway)->getSpeed() < (*potRamp)->getSpeed()) {
451
std::swap(*potHighway, *potRamp);
452
}
453
return true;
454
}
455
456
457
bool
458
NBRampsComputer::determinedByLaneNumber(NBEdge** potHighway, NBEdge** potRamp) {
459
if ((*potHighway)->getNumLanes() == (*potRamp)->getNumLanes()) {
460
return false;
461
}
462
if ((*potHighway)->getNumLanes() < (*potRamp)->getNumLanes()) {
463
std::swap(*potHighway, *potRamp);
464
}
465
return true;
466
}
467
468
469
void
470
NBRampsComputer::getOnRampEdges(NBNode* n, NBEdge** potHighway, NBEdge** potRamp, NBEdge** other) {
471
*other = n->getOutgoingEdges()[0];
472
const std::vector<NBEdge*>& edges = n->getIncomingEdges();
473
assert(edges.size() == 2);
474
*potHighway = edges[0];
475
*potRamp = edges[1];
476
/*
477
// heuristic: highway is faster than ramp
478
if(determinedBySpeed(potHighway, potRamp)) {
479
return;
480
}
481
// heuristic: highway has more lanes than ramp
482
if(determinedByLaneNumber(potHighway, potRamp)) {
483
return;
484
}
485
*/
486
// heuristic: ramp comes from right
487
if (NBContHelper::relative_incoming_edge_sorter(*other)(*potRamp, *potHighway)) {
488
std::swap(*potHighway, *potRamp);
489
}
490
}
491
492
493
void
494
NBRampsComputer::getOffRampEdges(NBNode* n, NBEdge** potHighway, NBEdge** potRamp, NBEdge** other) {
495
*other = n->getIncomingEdges()[0];
496
const std::vector<NBEdge*>& edges = n->getOutgoingEdges();
497
*potHighway = edges[0];
498
*potRamp = edges[1];
499
assert(edges.size() == 2);
500
/*
501
// heuristic: highway is faster than ramp
502
if(determinedBySpeed(potHighway, potRamp)) {
503
return;
504
}
505
// heuristic: highway has more lanes than ramp
506
if(determinedByLaneNumber(potHighway, potRamp)) {
507
return;
508
}
509
*/
510
// heuristic: ramp goes to right
511
const std::vector<NBEdge*>& edges2 = n->getEdges();
512
#ifdef DEBUG_RAMPS
513
if (DEBUGCOND(n)) {
514
std::cout << " edges=" << toString(edges) << " edges2=" << toString(edges2) << "\n";
515
}
516
#endif
517
std::vector<NBEdge*>::const_iterator i = std::find(edges2.begin(), edges2.end(), *other);
518
NBContHelper::nextCW(edges2, i);
519
if ((*i) == *potRamp) {
520
std::swap(*potHighway, *potRamp);
521
}
522
// the following would be better but runs afoul of misleading angles when both edges
523
// have the same geometry start point but different references lanes are
524
// chosen for NBEdge::computeAngle()
525
//if (NBContHelper::relative_outgoing_edge_sorter(*other)(*potHighway, *potRamp)) {
526
// std::swap(*potHighway, *potRamp);
527
//}
528
}
529
530
531
bool
532
NBRampsComputer::fulfillsRampConstraints(
533
NBEdge* potHighway, NBEdge* potRamp, NBEdge* other, double minHighwaySpeed, double maxRampSpeed,
534
const std::set<std::string>& noramps) {
535
// check modes that are not appropriate for rampsdo not build ramps on rail edges
536
if (hasWrongMode(potHighway) || hasWrongMode(potRamp) || hasWrongMode(other)) {
537
return false;
538
}
539
// do not build ramps at traffic lights
540
if (NBNode::isTrafficLight(potRamp->getToNode()->getType())) {
541
return false;
542
}
543
// do not build ramps on connectors
544
if (potHighway->isMacroscopicConnector() || potRamp->isMacroscopicConnector() || other->isMacroscopicConnector()) {
545
return false;
546
}
547
// check whether a lane is missing
548
if (potHighway->getNumLanes() + potRamp->getNumLanes() < other->getNumLanes()) {
549
return false;
550
}
551
// is it really a highway?
552
double maxSpeed = MAX3(potHighway->getSpeed(), other->getSpeed(), potRamp->getSpeed());
553
if (maxSpeed < minHighwaySpeed) {
554
return false;
555
}
556
// is any of the connections a turnaround?
557
if (other->getToNode() == potHighway->getFromNode()) {
558
// off ramp
559
if (other->isTurningDirectionAt(potHighway) ||
560
other->isTurningDirectionAt(potRamp)) {
561
return false;
562
}
563
} else {
564
// on ramp
565
if (other->isTurningDirectionAt(potHighway) ||
566
other->isTurningDirectionAt(potRamp)) {
567
return false;
568
}
569
}
570
// are the angles between highway and other / ramp and other more or less straight?
571
const NBNode* node = ((potHighway->getToNode() == potRamp->getToNode() && potHighway->getToNode() == other->getFromNode())
572
? potHighway->getToNode() : potHighway->getFromNode());
573
double angle = fabs(NBHelpers::relAngle(potHighway->getAngleAtNode(node), other->getAngleAtNode(node)));
574
if (angle >= 60) {
575
return false;
576
}
577
angle = fabs(NBHelpers::relAngle(potRamp->getAngleAtNode(node), other->getAngleAtNode(node)));
578
if (angle >= 60) {
579
return false;
580
}
581
/*
582
if (potHighway->getSpeed() < minHighwaySpeed || other->getSpeed() < minHighwaySpeed) {
583
return false;
584
}
585
*/
586
// is it really a ramp?
587
if (maxRampSpeed > 0 && maxRampSpeed < potRamp->getSpeed()) {
588
return false;
589
}
590
if (noramps.find(other->getID()) != noramps.end()) {
591
return false;
592
}
593
return true;
594
}
595
596
597
bool
598
NBRampsComputer::hasWrongMode(NBEdge* edge) {
599
// must allow passenger vehicles
600
if ((edge->getPermissions() & SVC_PASSENGER) == 0) {
601
return true;
602
}
603
// must not have a green verge or a lane that is only for soft modes
604
for (int i = 0; i < (int)edge->getNumLanes(); ++i) {
605
if ((edge->getPermissions(i) & ~(SVC_PEDESTRIAN | SVC_BICYCLE)) == 0) {
606
return true;
607
}
608
}
609
return false;
610
}
611
612
void
613
NBRampsComputer::patchRampGeometry(NBEdge* potRamp, NBEdge* first, NBEdge* potHighway, bool onRamp) {
614
// geometry of first and highway should allign on the left side
615
if (first->getLaneSpreadFunction() == LaneSpreadFunction::CENTER && first->hasDefaultGeometryEndpoints()) {
616
const NBNode* n = onRamp ? potHighway->getToNode() : potHighway->getFromNode();
617
if (potHighway->hasDefaultGeometryEndpointAtNode(n)) {
618
PositionVector p2 = first->getGeometry();
619
try {
620
p2.move2side((first->getNumLanes() - potHighway->getNumLanes()) * first->getLaneWidth(0) * 0.5);
621
first->setGeometry(p2);
622
} catch (InvalidArgument&) {}
623
}
624
}
625
626
// ramp should merge smoothly with first
627
PositionVector p = potRamp->getGeometry();
628
double offset = 0;
629
int firstIndex = MAX2(0, MIN2(potRamp->getNumLanes(), first->getNumLanes()) - 1);
630
if (potRamp->getLaneSpreadFunction() == LaneSpreadFunction::RIGHT) {
631
offset = -first->getLaneWidth(firstIndex) / 2;
632
} else {
633
if (firstIndex % 2 == 1) {
634
// even number of lanes
635
offset = -first->getLaneWidth(firstIndex / 2) / 2;
636
}
637
firstIndex /= 2; // integer division
638
}
639
// reset lane shape (might be affected by earlier junctions.join step. see #947)
640
first->resetLaneShapes();
641
PositionVector l = first->getLaneShape(firstIndex);
642
try {
643
l.move2side(offset);
644
} catch (InvalidArgument&) {}
645
//std::cout << " ramp=" << potRamp->getID() << " firstIndex=" << firstIndex << " offset=" << offset << " l=" << l << "\n";
646
647
if (onRamp) {
648
p[0] = l[-1];
649
} else {
650
p.pop_back();
651
p.push_back(l[0]);
652
}
653
potRamp->setGeometry(p);
654
655
}
656
657
658
/****************************************************************************/
659
660