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