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 : #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 1616 : NBRampsComputer::NBRampsComputer() { }
55 :
56 : void
57 1616 : NBRampsComputer::computeRamps(NBNetBuilder& nb, OptionsCont& oc, bool mayAddOrRemove) {
58 1616 : const bool guessAndAdd = oc.getBool("ramps.guess") && mayAddOrRemove;
59 1616 : const double minHighwaySpeed = oc.getFloat("ramps.min-highway-speed");
60 1616 : const double maxRampSpeed = oc.getFloat("ramps.max-ramp-speed");
61 1616 : const double rampLength = oc.getFloat("ramps.ramp-length");
62 1616 : const double minWeaveLength = oc.getFloat("ramps.min-weave-length");
63 3232 : 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 3232 : if (oc.isSet("ramps.unset")) {
71 2 : std::vector<std::string> edges = oc.getStringVector("ramps.unset");
72 : noramps.insert(edges.begin(), edges.end());
73 1 : }
74 : // exclude roundabouts
75 1679 : for (const EdgeSet& round : ec.getRoundabouts()) {
76 401 : for (NBEdge* const edge : round) {
77 338 : noramps.insert(edge->getID());
78 : }
79 : }
80 : // exclude public transport edges
81 1616 : nb.getPTStopCont().addEdges2Keep(oc, noramps);
82 1616 : nb.getParkingCont().addEdges2Keep(oc, noramps);
83 :
84 : // check whether on-off ramps shall be guessed
85 3181 : if (guessAndAdd || oc.getBool("ramps.guess-acceleration-lanes")) {
86 77145 : for (const auto& it : ec) {
87 75529 : 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 46909 : for (const auto& i : nc) {
94 45293 : 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 45293 : if (mayNeedOnRamp(cur, minHighwaySpeed, maxRampSpeed, noramps, minWeaveLength)) {
101 : potOnRamps.insert(cur);
102 : }
103 45293 : if (mayNeedOffRamp(cur, minHighwaySpeed, maxRampSpeed, noramps)) {
104 : potOffRamps.insert(cur);
105 : }
106 : }
107 1698 : for (std::set<NBNode*, ComparatorIdLess>::const_iterator i = potOnRamps.begin(); i != potOnRamps.end(); ++i) {
108 82 : buildOnRamp(*i, nc, ec, dc, rampLength, dontSplit || !guessAndAdd, guessAndAdd);
109 : }
110 1724 : for (std::set<NBNode*, ComparatorIdLess>::const_iterator i = potOffRamps.begin(); i != potOffRamps.end(); ++i) {
111 108 : buildOffRamp(*i, nc, ec, dc, rampLength, dontSplit || !guessAndAdd, guessAndAdd, potOnRamps);
112 : }
113 : }
114 : // check whether on-off ramps are specified
115 3232 : if (oc.isSet("ramps.set") && mayAddOrRemove) {
116 16 : std::vector<std::string> edges = oc.getStringVector("ramps.set");
117 : std::set<NBNode*, ComparatorIdLess> potOnRamps;
118 18 : for (const std::string& i : edges) {
119 10 : NBEdge* e = ec.retrieve(i);
120 0 : if (noramps.count(i) != 0) {
121 0 : WRITE_WARNINGF(TL("Can not build ramp on edge '%' - the edge is unsuitable."), i);
122 0 : continue;
123 : }
124 10 : if (e == nullptr) {
125 0 : WRITE_WARNINGF(TL("Can not build on ramp on edge '%' - the edge is not known."), i);
126 0 : continue;
127 : }
128 10 : NBNode* from = e->getFromNode();
129 10 : if (from->getIncomingEdges().size() == 2 && from->getOutgoingEdges().size() == 1) {
130 6 : buildOnRamp(from, nc, ec, dc, rampLength, dontSplit, true);
131 : potOnRamps.insert(from);
132 : }
133 : // load edge again to check offramps
134 10 : e = ec.retrieve(i);
135 10 : if (e == nullptr) {
136 0 : WRITE_WARNINGF(TL("Can not build off ramp on edge '%' - the edge is not known."), i);
137 0 : continue;
138 : }
139 : NBNode* to = e->getToNode();
140 10 : if (to->getIncomingEdges().size() == 1 && to->getOutgoingEdges().size() == 2) {
141 3 : buildOffRamp(to, nc, ec, dc, rampLength, dontSplit, true, potOnRamps);
142 : }
143 : }
144 8 : }
145 1616 : }
146 :
147 :
148 : bool
149 45293 : NBRampsComputer::mayNeedOnRamp(NBNode* cur, double minHighwaySpeed, double maxRampSpeed, const std::set<std::string>& noramps, double minWeaveLength) {
150 45293 : if (cur->getOutgoingEdges().size() != 1 || cur->getIncomingEdges().size() != 2) {
151 : return false;
152 : }
153 : NBEdge* potHighway, *potRamp, *cont;
154 2919 : 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 2919 : if (fulfillsRampConstraints(potHighway, potRamp, cont, minHighwaySpeed, maxRampSpeed, noramps)) {
162 : // prevent short weaving section
163 91 : double seen = cont->getLength();
164 107 : while (seen < minWeaveLength) {
165 31 : if (cont->getToNode()->getOutgoingEdges().size() > 1) {
166 : return false;
167 22 : } else if (cont->getToNode()->getOutgoingEdges().size() == 0) {
168 : return true;
169 : }
170 16 : cont = cont->getToNode()->getOutgoingEdges().front();
171 16 : seen += cont->getLength();
172 : }
173 : return true;
174 : } else {
175 : return false;
176 : }
177 : }
178 :
179 :
180 : bool
181 45293 : NBRampsComputer::mayNeedOffRamp(NBNode* cur, double minHighwaySpeed, double maxRampSpeed, const std::set<std::string>& noramps) {
182 45293 : 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 3728 : 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 3728 : return fulfillsRampConstraints(potHighway, potRamp, prev, minHighwaySpeed, maxRampSpeed, noramps);
194 : }
195 :
196 :
197 : void
198 88 : NBRampsComputer::buildOnRamp(NBNode* cur, NBNodeCont& nc, NBEdgeCont& ec, NBDistrictCont& dc, double rampLength, bool dontSplit, bool addLanes) {
199 : NBEdge* potHighway, *potRamp, *cont;
200 88 : 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 88 : const int firstLaneNumber = cont->getNumLanes();
208 88 : const int toAdd = (potRamp->getNumLanes() + potHighway->getNumLanes()) - firstLaneNumber;
209 : NBEdge* first = cont;
210 : NBEdge* last = cont;
211 88 : NBEdge* curr = cont;
212 : std::set<NBEdge*> incremented;
213 88 : if (addLanes && toAdd > 0 && std::find(incremented.begin(), incremented.end(), cont) == incremented.end()) {
214 : double currLength = 0;
215 53 : while (curr != nullptr && currLength + curr->getGeometry().length() - POSITION_EPS < rampLength) {
216 20 : if (find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
217 20 : 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 22 : if (curr->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER || !ec.hasPostProcessConnection(curr->getID())) {
221 20 : curr->invalidateConnections(true);
222 : }
223 : incremented.insert(curr);
224 20 : moveRampRight(curr, toAdd);
225 20 : currLength += curr->getGeometry().length(); // !!! loaded length?
226 20 : last = curr;
227 : // mark acceleration lanes
228 46 : for (int i = 0; i < curr->getNumLanes() - potHighway->getNumLanes(); ++i) {
229 26 : curr->setAcceleration(i, true);
230 : }
231 : }
232 20 : NBNode* nextN = curr->getToNode();
233 20 : if (nextN->getOutgoingEdges().size() == 1 && nextN->getIncomingEdges().size() == 1) {
234 11 : curr = nextN->getOutgoingEdges()[0];
235 11 : if (curr->getNumLanes() != firstLaneNumber) {
236 : // the number of lanes changes along the computation; we'll stop...
237 4 : curr = nullptr;
238 7 : } else if (curr->isTurningDirectionAt(last)) {
239 : // turnarounds certainly should not be included in a ramp
240 0 : curr = nullptr;
241 7 : } else if (curr == potHighway || curr == potRamp) {
242 : // circular connectivity. do not split!
243 0 : curr = nullptr;
244 : }
245 : } else {
246 : // ambiguous; and, in fact, what should it be? ...stop
247 9 : curr = nullptr;
248 : }
249 : }
250 : // check whether a further split is necessary
251 53 : 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 20 : std::string newNodeID = getUnusedID(curr->getID() + "-AddedOnRampNode", nc);
255 40 : std::string newEdgeID = getUnusedID(curr->getID() + ADDED_ON_RAMP_EDGE, ec);
256 20 : NBNode* rn = new NBNode(newNodeID, curr->getGeometry().positionAtOffset(rampLength - currLength));
257 20 : nc.insert(rn);
258 20 : std::string name = curr->getID();
259 20 : const double currShift = myShiftedEdges[curr];
260 20 : if (!ec.splitAt(dc, curr, rn, newEdgeID, curr->getID(), curr->getNumLanes() + toAdd, curr->getNumLanes())) {
261 0 : WRITE_WARNING("Could not build on-ramp for edge '" + curr->getID() + "' for unknown reason");
262 : return;
263 : }
264 : //ec.retrieve(name)->invalidateConnections();
265 20 : curr = ec.retrieve(newEdgeID);
266 : // copy shift over
267 20 : myShiftedEdges[curr] = currShift;
268 : incremented.insert(curr);
269 : last = curr;
270 20 : moveRampRight(curr, toAdd);
271 20 : if (wasFirst) {
272 19 : first = curr;
273 : }
274 : // mark acceleration lanes
275 47 : for (int i = 0; i < curr->getNumLanes() - potHighway->getNumLanes(); ++i) {
276 27 : curr->setAcceleration(i, true);
277 : }
278 : }
279 33 : if (curr == cont && dontSplit && addLanes) {
280 0 : WRITE_WARNING("Could not build on-ramp for edge '" + curr->getID() + "' due to option '--ramps.no-split'");
281 0 : return;
282 : }
283 : } else {
284 : // mark acceleration lanes
285 77 : for (int i = 0; i < firstLaneNumber - potHighway->getNumLanes(); ++i) {
286 22 : cont->setAcceleration(i, true);
287 : }
288 : }
289 : // set connections from ramp/highway to added ramp
290 88 : if (addLanes) {
291 37 : if (potHighway->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
292 36 : if (!potHighway->addLane2LaneConnections(0, first, potRamp->getNumLanes(), MIN2(first->getNumLanes() - potRamp->getNumLanes(), potHighway->getNumLanes()), NBEdge::Lane2LaneInfoType::VALIDATED, true)) {
293 0 : throw ProcessError(TL("Could not set connection!"));
294 : }
295 : }
296 37 : if (potRamp->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
297 36 : if (!potRamp->addLane2LaneConnections(0, first, 0, potRamp->getNumLanes(), NBEdge::Lane2LaneInfoType::VALIDATED, true)) {
298 0 : throw ProcessError(TL("Could not set connection!"));
299 : }
300 : }
301 37 : patchRampGeometry(potRamp, first, potHighway, false);
302 : }
303 : }
304 :
305 :
306 : void
307 111 : 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 111 : 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 111 : const int firstLaneNumber = prev->getNumLanes();
318 111 : const int toAdd = (potRamp->getNumLanes() + potHighway->getNumLanes()) - firstLaneNumber;
319 : NBEdge* first = prev;
320 : NBEdge* last = prev;
321 111 : NBEdge* curr = prev;
322 : std::set<NBEdge*> incremented;
323 111 : if (addLanes && toAdd > 0 && std::find(incremented.begin(), incremented.end(), prev) == incremented.end()) {
324 : double currLength = 0;
325 57 : while (curr != nullptr && currLength + curr->getGeometry().length() - POSITION_EPS < rampLength) {
326 21 : if (find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
327 21 : 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 24 : if (curr->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER || !ec.hasPostProcessConnection(curr->getID())) {
331 20 : curr->invalidateConnections(true);
332 : }
333 : incremented.insert(curr);
334 21 : moveRampRight(curr, toAdd);
335 21 : currLength += curr->getGeometry().length(); // !!! loaded length?
336 21 : last = curr;
337 : }
338 21 : NBNode* prevN = curr->getFromNode();
339 21 : if (prevN->getIncomingEdges().size() == 1 && prevN->getOutgoingEdges().size() == 1) {
340 15 : curr = prevN->getIncomingEdges()[0];
341 15 : 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 15 : curr->invalidateConnections();
344 : }
345 15 : if (curr->getNumLanes() != firstLaneNumber) {
346 : // the number of lanes changes along the computation; we'll stop...
347 3 : curr = nullptr;
348 12 : } else if (last->isTurningDirectionAt(curr)) {
349 : // turnarounds certainly should not be included in a ramp
350 0 : curr = nullptr;
351 12 : } else if (curr == potHighway || curr == potRamp) {
352 : // circular connectivity. do not split!
353 0 : curr = nullptr;
354 : }
355 : } else {
356 : // ambiguous; and, in fact, what should it be? ...stop
357 6 : curr = nullptr;
358 : }
359 : }
360 : // check whether a further split is necessary
361 61 : 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 25 : Position pos = curr->getGeometry().positionAtOffset(curr->getGeometry().length() - (rampLength - currLength));
365 25 : std::string newNodeID = getUnusedID(curr->getID() + "-AddedOffRampNode", nc);
366 25 : std::string newEdgeID = getUnusedID(curr->getID() + "-AddedOffRampEdge", ec);
367 25 : NBNode* rn = new NBNode(newNodeID, pos);
368 25 : nc.insert(rn);
369 25 : std::string name = curr->getID();
370 25 : const double currShift = myShiftedEdges[curr];
371 25 : if (!ec.splitAt(dc, curr, rn, curr->getID(), newEdgeID, curr->getNumLanes(), curr->getNumLanes() + toAdd)) {
372 0 : WRITE_WARNING("Could not build off-ramp for edge '" + curr->getID() + "' for unknown reason");
373 : return;
374 : }
375 25 : curr = ec.retrieve(newEdgeID);
376 : // copy shift over
377 25 : myShiftedEdges[curr] = currShift;
378 : incremented.insert(curr);
379 : last = curr;
380 25 : moveRampRight(curr, toAdd);
381 25 : if (wasFirst) {
382 18 : first = curr;
383 : }
384 : }
385 36 : if (curr == prev && dontSplit && addLanes) {
386 2 : WRITE_WARNING("Could not build off-ramp for edge '" + curr->getID() + "' due to option '--ramps.no-split'");
387 1 : return;
388 : }
389 : }
390 110 : NBEdge* toMark = first;
391 : toMark->markOffRamp(true);
392 : double markedLength = toMark->getLoadedLength();
393 187 : while (markedLength < OFFRAMP_LOOKBACK) {
394 136 : if (toMark != first && toMark->getToNode()->getOutgoingEdges().size() != 1) {
395 : break;
396 : }
397 126 : NBNode* from = toMark->getFromNode();
398 126 : if (from->getIncomingEdges().size() == 1) {
399 66 : toMark = from->getIncomingEdges()[0];
400 : } else if (potOnRamps.count(from) == 1) {
401 : NBEdge* potOnRamp, *cont;
402 11 : getOnRampEdges(from, &toMark, &potOnRamp, &cont);
403 : } else {
404 : break;
405 : }
406 77 : toMark->markOffRamp(true);
407 77 : markedLength += toMark->getLoadedLength();
408 : }
409 : // set connections from added ramp to ramp/highway
410 110 : if (addLanes) {
411 43 : if (first->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
412 42 : if (!first->addLane2LaneConnections(potRamp->getNumLanes(), potHighway, 0, MIN2(first->getNumLanes() - 1, potHighway->getNumLanes()), NBEdge::Lane2LaneInfoType::VALIDATED, true)) {
413 0 : throw ProcessError(TL("Could not set connection!"));
414 : }
415 42 : if (!first->addLane2LaneConnections(0, potRamp, 0, potRamp->getNumLanes(), NBEdge::Lane2LaneInfoType::VALIDATED, false)) {
416 0 : throw ProcessError(TL("Could not set connection!"));
417 : }
418 : }
419 43 : patchRampGeometry(potRamp, first, potHighway, true);
420 : }
421 : }
422 :
423 :
424 : void
425 86 : NBRampsComputer::moveRampRight(NBEdge* ramp, int addedLanes) {
426 86 : if (ramp->getLaneSpreadFunction() != LaneSpreadFunction::CENTER) {
427 : return;
428 : }
429 : try {
430 59 : PositionVector g = ramp->getGeometry();
431 59 : double offset = (0.5 * addedLanes *
432 59 : (ramp->getLaneWidth() == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : ramp->getLaneWidth()));
433 : if (myShiftedEdges.count(ramp) != 0) {
434 26 : offset -= myShiftedEdges[ramp];
435 : }
436 59 : g.move2side(offset);
437 59 : ramp->setGeometry(g);
438 59 : myShiftedEdges[ramp] = offset;
439 59 : } catch (InvalidArgument&) {
440 0 : WRITE_WARNINGF(TL("For edge '%': could not compute shape."), ramp->getID());
441 0 : }
442 : }
443 :
444 :
445 : bool
446 0 : NBRampsComputer::determinedBySpeed(NBEdge** potHighway, NBEdge** potRamp) {
447 0 : if (fabs((*potHighway)->getSpeed() - (*potRamp)->getSpeed()) < .1) {
448 : return false;
449 : }
450 0 : if ((*potHighway)->getSpeed() < (*potRamp)->getSpeed()) {
451 : std::swap(*potHighway, *potRamp);
452 : }
453 : return true;
454 : }
455 :
456 :
457 : bool
458 0 : NBRampsComputer::determinedByLaneNumber(NBEdge** potHighway, NBEdge** potRamp) {
459 0 : if ((*potHighway)->getNumLanes() == (*potRamp)->getNumLanes()) {
460 : return false;
461 : }
462 0 : if ((*potHighway)->getNumLanes() < (*potRamp)->getNumLanes()) {
463 : std::swap(*potHighway, *potRamp);
464 : }
465 : return true;
466 : }
467 :
468 :
469 : void
470 3018 : NBRampsComputer::getOnRampEdges(NBNode* n, NBEdge** potHighway, NBEdge** potRamp, NBEdge** other) {
471 3018 : *other = n->getOutgoingEdges()[0];
472 : const std::vector<NBEdge*>& edges = n->getIncomingEdges();
473 : assert(edges.size() == 2);
474 3018 : *potHighway = edges[0];
475 3018 : *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 3018 : if (NBContHelper::relative_incoming_edge_sorter(*other)(*potRamp, *potHighway)) {
488 : std::swap(*potHighway, *potRamp);
489 : }
490 3018 : }
491 :
492 :
493 : void
494 3839 : NBRampsComputer::getOffRampEdges(NBNode* n, NBEdge** potHighway, NBEdge** potRamp, NBEdge** other) {
495 3839 : *other = n->getIncomingEdges()[0];
496 : const std::vector<NBEdge*>& edges = n->getOutgoingEdges();
497 3839 : *potHighway = edges[0];
498 3839 : *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 3839 : std::vector<NBEdge*>::const_iterator i = std::find(edges2.begin(), edges2.end(), *other);
518 3839 : NBContHelper::nextCW(edges2, i);
519 3839 : 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 3839 : }
529 :
530 :
531 : bool
532 6647 : 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 6647 : if (hasWrongMode(potHighway) || hasWrongMode(potRamp) || hasWrongMode(other)) {
537 4789 : return false;
538 : }
539 : // do not build ramps at traffic lights
540 1858 : if (NBNode::isTrafficLight(potRamp->getToNode()->getType())) {
541 : return false;
542 : }
543 : // do not build ramps on connectors
544 1571 : if (potHighway->isMacroscopicConnector() || potRamp->isMacroscopicConnector() || other->isMacroscopicConnector()) {
545 : return false;
546 : }
547 : // check whether a lane is missing
548 1571 : if (potHighway->getNumLanes() + potRamp->getNumLanes() < other->getNumLanes()) {
549 : return false;
550 : }
551 : // is it really a highway?
552 1542 : double maxSpeed = MAX3(potHighway->getSpeed(), other->getSpeed(), potRamp->getSpeed());
553 1542 : if (maxSpeed < minHighwaySpeed) {
554 : return false;
555 : }
556 : // is any of the connections a turnaround?
557 272 : if (other->getToNode() == potHighway->getFromNode()) {
558 : // off ramp
559 298 : if (other->isTurningDirectionAt(potHighway) ||
560 147 : other->isTurningDirectionAt(potRamp)) {
561 4 : return false;
562 : }
563 : } else {
564 : // on ramp
565 242 : if (other->isTurningDirectionAt(potHighway) ||
566 121 : other->isTurningDirectionAt(potRamp)) {
567 2 : return false;
568 : }
569 : }
570 : // are the angles between highway and other / ramp and other more or less straight?
571 122 : const NBNode* node = ((potHighway->getToNode() == potRamp->getToNode() && potHighway->getToNode() == other->getFromNode())
572 266 : ? potHighway->getToNode() : potHighway->getFromNode());
573 266 : double angle = fabs(NBHelpers::relAngle(potHighway->getAngleAtNode(node), other->getAngleAtNode(node)));
574 266 : if (angle >= 60) {
575 : return false;
576 : }
577 231 : angle = fabs(NBHelpers::relAngle(potRamp->getAngleAtNode(node), other->getAngleAtNode(node)));
578 231 : 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 214 : if (maxRampSpeed > 0 && maxRampSpeed < potRamp->getSpeed()) {
588 : return false;
589 : }
590 214 : if (noramps.find(other->getID()) != noramps.end()) {
591 : return false;
592 : }
593 : return true;
594 : }
595 :
596 :
597 : bool
598 10773 : NBRampsComputer::hasWrongMode(NBEdge* edge) {
599 : // must allow passenger vehicles
600 10773 : 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 16431 : for (int i = 0; i < (int)edge->getNumLanes(); ++i) {
605 10447 : if ((edge->getPermissions(i) & ~(SVC_PEDESTRIAN | SVC_BICYCLE)) == 0) {
606 : return true;
607 : }
608 : }
609 : return false;
610 : }
611 :
612 : void
613 80 : NBRampsComputer::patchRampGeometry(NBEdge* potRamp, NBEdge* first, NBEdge* potHighway, bool onRamp) {
614 : // geometry of first and highway should allign on the left side
615 80 : if (first->getLaneSpreadFunction() == LaneSpreadFunction::CENTER && first->hasDefaultGeometryEndpoints()) {
616 5 : const NBNode* n = onRamp ? potHighway->getToNode() : potHighway->getFromNode();
617 5 : if (potHighway->hasDefaultGeometryEndpointAtNode(n)) {
618 : PositionVector p2 = first->getGeometry();
619 : try {
620 5 : p2.move2side((first->getNumLanes() - potHighway->getNumLanes()) * first->getLaneWidth(0) * 0.5);
621 5 : first->setGeometry(p2);
622 0 : } catch (InvalidArgument&) {}
623 5 : }
624 : }
625 :
626 : // ramp should merge smoothly with first
627 : PositionVector p = potRamp->getGeometry();
628 : double offset = 0;
629 80 : int firstIndex = MAX2(0, MIN2(potRamp->getNumLanes(), first->getNumLanes()) - 1);
630 80 : if (potRamp->getLaneSpreadFunction() == LaneSpreadFunction::RIGHT) {
631 28 : offset = -first->getLaneWidth(firstIndex) / 2;
632 : } else {
633 52 : if (firstIndex % 2 == 1) {
634 : // even number of lanes
635 9 : offset = -first->getLaneWidth(firstIndex / 2) / 2;
636 : }
637 52 : firstIndex /= 2; // integer division
638 : }
639 : // reset lane shape (might be affected by earlier junctions.join step. see #947)
640 80 : first->resetLaneShapes();
641 80 : PositionVector l = first->getLaneShape(firstIndex);
642 : try {
643 80 : l.move2side(offset);
644 0 : } catch (InvalidArgument&) {}
645 : //std::cout << " ramp=" << potRamp->getID() << " firstIndex=" << firstIndex << " offset=" << offset << " l=" << l << "\n";
646 :
647 80 : if (onRamp) {
648 43 : p[0] = l[-1];
649 : } else {
650 : p.pop_back();
651 37 : p.push_back(l[0]);
652 : }
653 80 : potRamp->setGeometry(p);
654 :
655 80 : }
656 :
657 :
658 : /****************************************************************************/
|