Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
NBEdge.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-2026 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/****************************************************************************/
23// Methods for the representation of a single edge
24/****************************************************************************/
25#include <config.h>
26
27#include <vector>
28#include <string>
29#include <algorithm>
30#include <cmath>
31#include <iomanip>
40#include "NBEdgeCont.h"
41#include "NBNode.h"
42#include "NBNodeCont.h"
43#include "NBContHelper.h"
44#include "NBHelpers.h"
46#include "NBOwnTLDef.h"
47#include "NBTypeCont.h"
48#include "NBEdge.h"
49
50//#define ADDITIONAL_WARNINGS
51//#define DEBUG_CONNECTION_GUESSING
52//#define DEBUG_CONNECTION_CHECKING
53//#define DEBUG_ANGLES
54//#define DEBUG_NODE_BORDER
55//#define DEBUG_REPLACECONNECTION
56//#define DEBUG_JUNCTIONPRIO
57//#define DEBUG_TURNSIGNS
58//#define DEBUG_CUT_LANES
59#define DEBUGID ""
60#define DEBUGCOND (getID() == DEBUGID)
61//#define DEBUGCOND (StringUtils::startsWith(getID(), DEBUGID))
62//#define DEBUGCOND (getID() == "22762377#1" || getID() == "146511467")
63#define DEBUGCOND2(obj) ((obj != 0 && (obj)->getID() == DEBUGID))
64//#define DEBUGCOND (true)
65
66// ===========================================================================
67// static members
68// ===========================================================================
69const double NBEdge::UNSPECIFIED_WIDTH = -1;
70const double NBEdge::UNSPECIFIED_OFFSET = 0;
71const double NBEdge::UNSPECIFIED_SPEED = -1;
72const double NBEdge::UNSPECIFIED_FRICTION = 1.;
73const double NBEdge::UNSPECIFIED_CONTPOS = -1;
75
76const double NBEdge::UNSPECIFIED_SIGNAL_OFFSET = -1;
77const double NBEdge::UNSPECIFIED_LOADED_LENGTH = -1;
78const double NBEdge::ANGLE_LOOKAHEAD = 10.0;
81
83
85
86ConstRouterEdgePairVector NBEdge::Connection::myViaSuccessors = ConstRouterEdgePairVector({ std::pair<NBRouterEdge*, NBRouterEdge*>(nullptr, nullptr) });
87
88// ===========================================================================
89// method definitions
90// ===========================================================================
91std::string
95
96
97std::string
99 return viaID + "_" + toString(internalViaLaneIndex);
100}
101
102
103std::string
105 return (Named::getIDSecure(parent) + "_" + toString(fromLane) + "->" + Named::getIDSecure(toEdge) + "_" + toString(toLane)
106 + (permissions == SVC_UNSPECIFIED ? "" : " (" + getVehicleClassNames(permissions) + ")"));
107}
108
109
110NBEdge::Connection::Connection(int fromLane_, NBEdge* toEdge_, int toLane_, const bool mayDefinitelyPass_) :
111 fromLane(fromLane_),
112 toEdge(toEdge_),
113 toLane(toLane_),
114 mayDefinitelyPass(mayDefinitelyPass_),
115 customLength(myDefaultConnectionLength),
116 id(toEdge_ == nullptr ? "" : toEdge->getFromNode()->getID()) {
117}
118
119
120NBEdge::Lane::Lane(NBEdge* e, const std::string& origID_) :
121 speed(e->getSpeed()),
122 friction(e->getFriction()),
123 permissions(SVCAll),
124 preferred(0),
125 changeLeft(SVCAll),
126 changeRight(SVCAll),
127 endOffset(e->getEndOffset()),
128 laneStopOffset(e->getEdgeStopOffset()),
129 width(e->getLaneWidth()),
130 accelRamp(false),
131 connectionsDone(false) {
132 if (origID_ != "") {
134 }
135}
136
137
138/* -------------------------------------------------------------------------
139 * NBEdge::ToEdgeConnectionsAdder-methods
140 * ----------------------------------------------------------------------- */
141void
142NBEdge::ToEdgeConnectionsAdder::execute(const int lane, const int virtEdge) {
143 // check
144 assert((int)myTransitions.size() > virtEdge);
145 // get the approached edge
146 NBEdge* succEdge = myTransitions[virtEdge];
147 std::vector<int> lanes;
148
149 // check whether the currently regarded, approached edge has already
150 // a connection starting at the edge which is currently being build
151 std::map<NBEdge*, std::vector<int> >::iterator i = myConnections.find(succEdge);
152 if (i != myConnections.end()) {
153 // if there were already lanes assigned, get them
154 lanes = (*i).second;
155 }
156
157 // check whether the current lane was already used to connect the currently
158 // regarded approached edge
159 std::vector<int>::iterator j = std::find(lanes.begin(), lanes.end(), lane);
160 if (j == lanes.end()) {
161 // if not, add it to the list
162 lanes.push_back(lane);
163 }
164 // set information about connecting lanes
165 myConnections[succEdge] = lanes;
166}
167
168
169
170/* -------------------------------------------------------------------------
171 * NBEdge::MainDirections-methods
172 * ----------------------------------------------------------------------- */
173NBEdge::MainDirections::MainDirections(const EdgeVector& outgoing, NBEdge* parent, NBNode* to, const std::vector<int>& availableLanes) : myStraightest(-1) {
175 const NBEdge* straight = nullptr;
176 for (const NBEdge* const out : outgoing) {
177 const SVCPermissions outPerms = out->getPermissions();
178 for (const int l : availableLanes) {
179 if ((parent->myLanes[l].permissions & outPerms) != 0) {
180 if (straight == nullptr || sorter(out, straight)) {
181 straight = out;
182 }
183 break;
184 }
185 }
186 }
187 if (straight == nullptr) {
188 return;
189 }
190 myStraightest = (int)std::distance(outgoing.begin(), std::find(outgoing.begin(), outgoing.end(), straight));
191
192 // check whether the right turn has a higher priority
193 assert(outgoing.size() > 0);
194 const LinkDirection straightestDir = to->getDirection(parent, straight);
195#ifdef DEBUG_CONNECTION_GUESSING
196 if (DEBUGCOND2(parent)) {
197 std::cout << " MainDirections edge=" << parent->getID() << " straightest=" << straight->getID() << " dir=" << toString(straightestDir) << "\n";
198 }
199#endif
200 if (NBNode::isTrafficLight(to->getType()) &&
201 (straightestDir == LinkDirection::STRAIGHT || straightestDir == LinkDirection::PARTLEFT || straightestDir == LinkDirection::PARTRIGHT)) {
203 return;
204 }
205 if (outgoing[0]->getJunctionPriority(to) == 1) {
207 }
208 // check whether the left turn has a higher priority
209 if (outgoing.back()->getJunctionPriority(to) == 1) {
210 // ok, the left turn belongs to the higher priorised edges on the junction
211 // let's check, whether it has also a higher priority (lane number/speed)
212 // than the current
213 if (outgoing.back()->getPriority() > straight->getPriority() ||
214 outgoing.back()->getNumLanes() > straight->getNumLanes()) {
216 }
217 }
218 // check whether the forward direction has a higher priority
219 // check whether it has a higher priority and is going straight
220 if (straight->getJunctionPriority(to) == 1 && to->getDirection(parent, straight) == LinkDirection::STRAIGHT) {
222 }
223}
224
225
227
228
229bool
231 return myDirs.empty();
232}
233
234
235bool
237 return std::find(myDirs.begin(), myDirs.end(), d) != myDirs.end();
238}
239
240
241/* -------------------------------------------------------------------------
242 * NBEdge::connections_relative_edgelane_sorter-methods
243 * ----------------------------------------------------------------------- */
244int
246 if (c1.toEdge != c2.toEdge) {
248 }
249 return c1.toLane < c2.toLane;
250}
251
252
253/* -------------------------------------------------------------------------
254 * NBEdge-methods
255 * ----------------------------------------------------------------------- */
256NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to,
257 std::string type, double speed, double friction, int nolanes,
258 int priority, double laneWidth, double endOffset,
259 LaneSpreadFunction spread, const std::string& streetName) :
260 Named(StringUtils::convertUmlaute(id)),
262 myType(StringUtils::convertUmlaute(type)),
263 myFrom(from), myTo(to),
265 myPriority(priority), mySpeed(speed), myFriction(friction),
266 myDistance(0),
267 myTurnDestination(nullptr),
270 myLaneSpreadFunction(spread), myEndOffset(endOffset),
271 myLaneWidth(laneWidth),
273 myAmInTLS(false), myAmMacroscopicConnector(false),
274 myStreetName(streetName),
276 mySignalNode(nullptr),
277 myIsOffRamp(false),
278 myIsBidi(false),
279 myIndex(-1) {
280 init(nolanes, false, "");
281}
282
283
284NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to,
285 std::string type, double speed, double friction, int nolanes,
286 int priority, double laneWidth, double endOffset,
287 PositionVector geom,
288 LaneSpreadFunction spread,
289 const std::string& streetName,
290 const std::string& origID,
291 bool tryIgnoreNodePositions) :
292 Named(StringUtils::convertUmlaute(id)),
293 myStep(EdgeBuildingStep::INIT),
294 myType(StringUtils::convertUmlaute(type)),
295 myFrom(from), myTo(to),
296 myStartAngle(0), myEndAngle(0), myTotalAngle(0),
297 myPriority(priority), mySpeed(speed), myFriction(friction),
298 myDistance(0),
299 myTurnDestination(nullptr),
300 myPossibleTurnDestination(nullptr),
301 myFromJunctionPriority(-1), myToJunctionPriority(-1),
302 myGeom(geom), myLaneSpreadFunction(spread), myEndOffset(endOffset),
303 myLaneWidth(laneWidth),
304 myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
305 myAmInTLS(false), myAmMacroscopicConnector(false),
306 myStreetName(streetName),
307 mySignalPosition(Position::INVALID),
308 mySignalNode(nullptr),
309 myIsOffRamp(false),
310 myIsBidi(false),
311 myIndex(-1) {
312 init(nolanes, tryIgnoreNodePositions, origID);
313}
314
315
316NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to, const NBEdge* tpl, const PositionVector& geom, int numLanes) :
317 Named(StringUtils::convertUmlaute(id)),
318 myStep(EdgeBuildingStep::INIT),
319 myType(tpl->getTypeID()),
320 myFrom(from), myTo(to),
321 myStartAngle(0), myEndAngle(0), myTotalAngle(0),
322 myPriority(tpl->getPriority()), mySpeed(tpl->getSpeed()),
323 myFriction(tpl->getFriction()),
324 myDistance(0),
325 myTurnDestination(nullptr),
326 myPossibleTurnDestination(nullptr),
327 myFromJunctionPriority(-1), myToJunctionPriority(-1),
328 myGeom(geom),
329 myLaneSpreadFunction(tpl->getLaneSpreadFunction()),
330 myEndOffset(tpl->getEndOffset()),
331 myEdgeStopOffset(tpl->getEdgeStopOffset()),
332 myLaneWidth(tpl->getLaneWidth()),
333 myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
334 myAmInTLS(false),
335 myAmMacroscopicConnector(false),
336 myStreetName(tpl->getStreetName()),
337 mySignalPosition(to == tpl->myTo ? tpl->mySignalPosition : Position::INVALID),
338 mySignalNode(to == tpl->myTo ? tpl->mySignalNode : nullptr),
339 myIsOffRamp(false),
340 myIsBidi(tpl->myIsBidi),
341 myIndex(-1) {
342 init(numLanes > 0 ? numLanes : tpl->getNumLanes(), myGeom.size() > 0, "");
343 for (int i = 0; i < getNumLanes(); i++) {
344 const int tplIndex = MIN2(i, tpl->getNumLanes() - 1);
345 setSpeed(i, tpl->getLaneSpeed(tplIndex));
346 setFriction(i, tpl->getLaneFriction(tplIndex));
347 setPermissions(tpl->getPermissions(tplIndex), i);
348 setLaneWidth(i, tpl->myLanes[tplIndex].width);
349 setLaneType(i, tpl->myLanes[tplIndex].type);
350 myLanes[i].updateParameters(tpl->myLanes[tplIndex].getParametersMap());
351 if (to == tpl->myTo) {
352 setEndOffset(i, tpl->myLanes[tplIndex].endOffset);
353 setEdgeStopOffset(i, tpl->myLanes[tplIndex].laneStopOffset);
354 }
355 }
356 if (tpl->myLoadedLength > 0 && to == tpl->getFromNode() && from == tpl->getToNode() && geom == tpl->getGeometry().reverse()) {
358 }
360}
361
362
364 Named("DUMMY"),
365 myStep(EdgeBuildingStep::INIT),
366 myFrom(nullptr), myTo(nullptr),
367 myStartAngle(0), myEndAngle(0), myTotalAngle(0),
368 myPriority(0), mySpeed(0), myFriction(UNSPECIFIED_FRICTION),
369 myDistance(0),
370 myTurnDestination(nullptr),
371 myPossibleTurnDestination(nullptr),
372 myFromJunctionPriority(-1), myToJunctionPriority(-1),
373 myLaneSpreadFunction(LaneSpreadFunction::RIGHT),
374 myEndOffset(0),
375 myEdgeStopOffset(StopOffset()),
376 myLaneWidth(0),
377 myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
378 myAmInTLS(false),
379 myAmMacroscopicConnector(false),
380 mySignalPosition(Position::INVALID),
381 mySignalNode(nullptr) {
382}
383
384
385void
386NBEdge::reinit(NBNode* from, NBNode* to, const std::string& type,
387 double speed, double friction, int nolanes, int priority,
388 PositionVector geom, double laneWidth, double endOffset,
389 const std::string& streetName,
390 LaneSpreadFunction spread,
391 bool tryIgnoreNodePositions) {
392 if (myFrom != from) {
393 myFrom->removeEdge(this, false);
394 }
395 if (myTo != to) {
396 myTo->removeEdge(this, false);
397 }
399 myFrom = from;
400 myTo = to;
401 myPriority = priority;
402 //?myTurnDestination(0),
403 //?myFromJunctionPriority(-1), myToJunctionPriority(-1),
404 myGeom = geom;
405 myLaneSpreadFunction = spread;
407 myStreetName = streetName;
408 //?, myAmTurningWithAngle(0), myAmTurningOf(0),
409 //?myAmInTLS(false), myAmMacroscopicConnector(false)
410
411 // preserve lane-specific settings (geometry must be recomputed)
412 // if new lanes are added they copy the values from the leftmost lane (if specified)
413 const std::vector<Lane> oldLanes = myLanes;
414 init(nolanes, tryIgnoreNodePositions, oldLanes.empty() ? "" : oldLanes[0].getParameter(SUMO_PARAM_ORIGID));
415 for (int i = 0; i < (int)nolanes; ++i) {
416 PositionVector newShape = myLanes[i].shape;
417 myLanes[i] = oldLanes[MIN2(i, (int)oldLanes.size() - 1)];
418 myLanes[i].shape = newShape;
419 }
420 // however, if the new edge defaults are explicityly given, they override the old settings
421 if (endOffset != UNSPECIFIED_OFFSET) {
422 setEndOffset(-1, endOffset);
423 }
424 if (laneWidth != UNSPECIFIED_WIDTH) {
425 setLaneWidth(-1, laneWidth);
426 }
427 if (speed != UNSPECIFIED_SPEED) {
428 setSpeed(-1, speed);
429 }
430 if (friction != UNSPECIFIED_FRICTION) {
431 setFriction(-1, friction);
432 }
433}
434
435
436void
438 // connections may still be valid
439 if (from == nullptr || to == nullptr) {
440 throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
441 }
442 if (myFrom != from) {
443 myFrom->removeEdge(this, false);
444 }
445 if (myTo != to) {
446 myTo->removeEdge(this, false);
447 }
448 // remove first from both nodes and then add to the new nodes
449 // (otherwise reversing does not work)
450 if (myFrom != from) {
451 myFrom = from;
452 myFrom->addOutgoingEdge(this);
453 }
454 if (myTo != to) {
455 myTo = to;
456 myTo->addIncomingEdge(this);
457 }
458 computeAngle();
459}
460
461
462void
463NBEdge::init(int noLanes, bool tryIgnoreNodePositions, const std::string& origID) {
464 if (noLanes == 0) {
465 throw ProcessError(TLF("Edge '%' needs at least one lane.", myID));
466 }
467 if (myFrom == nullptr || myTo == nullptr) {
468 throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
469 }
471 throw ProcessError(TLF("Invalid edge id '%'.", myID));
472 }
473 // revisit geometry
474 // should have at least two points at the end...
475 // and in dome cases, the node positions must be added
476 // attempt symmetrical removal for forward and backward direction
477 // (very important for bidiRail)
478 if (myFrom->getID() < myTo->getID()) {
479 PositionVector reverse = myGeom.reverse();
480 reverse.removeDoublePoints(POSITION_EPS, true);
481 myGeom = reverse.reverse();
482 } else {
483 myGeom.removeDoublePoints(POSITION_EPS, true);
484 }
485
486 if (!tryIgnoreNodePositions || myGeom.size() < 2) {
487 if (myGeom.size() == 0) {
488 myGeom.push_back(myFrom->getPosition());
489 myGeom.push_back(myTo->getPosition());
490 } else {
493 }
494 }
495 if (myGeom.size() < 2) {
496 myGeom.clear();
497 myGeom.push_back(myFrom->getPosition());
498 myGeom.push_back(myTo->getPosition());
499 }
500 if (myGeom.size() == 2 && myGeom[0] == myGeom[1]) {
501 WRITE_WARNINGF(TL("Edge's '%' from- and to-node are at the same position."), myID);
502 int patchIndex = myFrom->getID() < myTo->getID() ? 1 : 0;
503 myGeom[patchIndex].add(Position(POSITION_EPS, POSITION_EPS));
504 }
505 // avoid degeneration of near-0-length geometrie when shifting later
507 //
508 myFrom->addOutgoingEdge(this);
509 myTo->addIncomingEdge(this);
510 // prepare container
511 assert(myGeom.size() >= 2);
513 if ((int)myLanes.size() > noLanes) {
514 // remove connections starting at the removed lanes
515 for (int lane = noLanes; lane < (int)myLanes.size(); ++lane) {
516 removeFromConnections(nullptr, lane, -1);
517 }
518 // remove connections targeting the removed lanes
519 const EdgeVector& incoming = myFrom->getIncomingEdges();
520 for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
521 for (int lane = noLanes; lane < (int)myLanes.size(); ++lane) {
522 (*i)->removeFromConnections(this, -1, lane);
523 }
524 }
525 }
526 myLanes.clear();
527 for (int i = 0; i < noLanes; i++) {
528 myLanes.push_back(Lane(this, origID));
529 }
531 computeAngle();
532
533#ifdef DEBUG_CONNECTION_GUESSING
534 if (DEBUGCOND) {
535 std::cout << "init edge=" << getID() << "\n";
536 for (Connection& c : myConnections) {
537 std::cout << " conn " << c.getDescription(this) << "\n";
538 }
540 std::cout << " connToDelete " << c.getDescription(this) << "\n";
541 }
542 }
543#endif
544}
545
546
548
549
550// ----------- Applying offset
551void
552NBEdge::reshiftPosition(double xoff, double yoff) {
553 myGeom.add(xoff, yoff, 0);
554 for (Lane& lane : myLanes) {
555 lane.customShape.add(xoff, yoff, 0);
556 }
557 computeLaneShapes(); // old shapes are dubious if computed with large coordinates
558 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
559 (*i).customShape.add(xoff, yoff, 0);
560 }
562 mySignalPosition.add(xoff, yoff);
563 }
564 myFromBorder.add(xoff, yoff, 0);
565 myToBorder.add(xoff, yoff, 0);
567 computeAngle(); // update angles because they are numerically sensitive (especially where based on centroids)
568}
569
570
571void
574 for (Lane& lane : myLanes) {
575 lane.customShape.round(gPrecision);
576 }
577 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
578 (*i).customShape.round(gPrecision);
579 }
580}
581
582
583void
586 // lane speeds are not used for computation but are compared to mySpeed in hasLaneSpecificSpeed
587 for (Lane& l : myLanes) {
588 l.speed = roundDecimalToEven(l.speed, gPrecision);
589 }
590}
591
592void
594 myGeom.mirrorX();
595 for (int i = 0; i < (int)myLanes.size(); i++) {
596 myLanes[i].shape.mirrorX();
597 myLanes[i].customShape.mirrorX();
598 }
599 for (Connection& c : myConnections) {
600 c.shape.mirrorX();
601 c.viaShape.mirrorX();
602 c.customShape.mirrorX();
603 }
606 }
607 computeAngle(); // update angles because they are numerically sensitive (especially where based on centroids)
608}
609
610
611// ----------- Edge geometry access and computation
612const PositionVector
614 return myGeom.getSubpartByIndex(1, (int)myGeom.size() - 2);
615}
616
617
618bool
620 return myGeom.size() == 2 && hasDefaultGeometryEndpoints();
621}
622
623
624bool
626 return myGeom.front().almostSame(myFrom->getPosition(), 0.01) &&
627 myGeom.back().almostSame(myTo->getPosition(), 0.01);
628}
629
630
631bool
633 // do not extend past the node position
634 if (node == myFrom) {
635 return myGeom.front() == node->getPosition();
636 } else {
637 assert(node == myTo);
638 return myGeom.back() == node->getPosition();
639 }
640}
641
644 return node == myFrom ? myGeom.front() : myGeom.back();
645}
646
647void
649 assert(myGeom.size() >= 2);
650 if (node == myFrom) {
651 myGeom[0] = myFrom->getPosition();
652 } else if (node == myTo) {
653 myGeom[-1] = myTo->getPosition();
654 } else {
655 assert(false);
656 }
657}
658
659void
660NBEdge::setGeometry(const PositionVector& s, bool inner) {
661 Position begin = myGeom.front(); // may differ from node position
662 Position end = myGeom.back(); // may differ from node position
663 myGeom = s;
664 if (inner) {
665 myGeom.insert(myGeom.begin(), begin);
666 myGeom.push_back(end);
667 }
668 // ensure non-zero length (see ::init)
669 if (myGeom.size() == 2 && myGeom[0] == myGeom[1]) {
670 WRITE_WARNINGF(TL("Edge's '%' from- and to-node are at the same position."), myID);
671 int patchIndex = myFrom->getID() < myTo->getID() ? 1 : 0;
672 myGeom[patchIndex].add(Position(POSITION_EPS, POSITION_EPS));
673 }
675 computeAngle();
677}
678
679
680void
681NBEdge::extendGeometryAtNode(const NBNode* node, double maxExtent) {
682 //std::cout << "extendGeometryAtNode edge=" << getID() << " node=" << node->getID() << " nodePos=" << node->getPosition() << " extent=" << maxExtent << " geom=" << myGeom;
683 if (node == myFrom) {
684 myGeom.extrapolate(maxExtent, true);
685 double offset = myGeom.nearest_offset_to_point2D(node->getPosition());
686 //std::cout << " geom2=" << myGeom << " offset=" << offset;
687 if (offset != GeomHelper::INVALID_OFFSET) {
688 myGeom = myGeom.getSubpart2D(MIN2(offset, myGeom.length2D() - 2 * POSITION_EPS), myGeom.length2D());
689 }
690 } else {
691 assert(node == myTo);
692 myGeom.extrapolate(maxExtent, false, true);
693 double offset = myGeom.nearest_offset_to_point2D(node->getPosition());
694 //std::cout << " geom2=" << myGeom << " offset=" << offset;
695 if (offset != GeomHelper::INVALID_OFFSET) {
696 myGeom = myGeom.getSubpart2D(0, MAX2(offset, 2 * POSITION_EPS));
697 }
698 }
699 //std::cout << " geom3=" << myGeom << "\n";
700}
701
702
703void
704NBEdge::shortenGeometryAtNode(const NBNode* node, double reduction) {
705 //std::cout << "shortenGeometryAtNode edge=" << getID() << " node=" << node->getID() << " nodePos=" << node->getPosition() << " reduction=" << reduction << " geom=" << myGeom;
706 reduction = MIN2(reduction, myGeom.length2D() - 2 * POSITION_EPS);
707 if (node == myFrom) {
708 myGeom = myGeom.getSubpart2D(reduction, myGeom.length2D());
709 } else {
710 myGeom = myGeom.getSubpart2D(0, myGeom.length2D() - reduction);
711 }
713 //std::cout << " geom2=" << myGeom << "\n";
714}
715
716
717void
718NBEdge::setNodeBorder(const NBNode* node, const Position& p, const Position& p2, bool rectangularCut) {
719 PositionVector border;
720 if (rectangularCut) {
721 const double extend = 100;
722 border = myGeom.getOrthogonal(p, extend, node == myTo);
723 } else {
724 border.push_back(p);
725 border.push_back(p2);
726 }
727 if (border.size() == 2) {
729 if (node == myFrom) {
730 myFromBorder = border;
731 } else {
732 assert(node == myTo);
733 myToBorder = border;
734 }
735 }
736#ifdef DEBUG_NODE_BORDER
738 if (DEBUGCOND) std::cout << "setNodeBorder edge=" << getID() << " node=" << node->getID()
739 << " rect=" << rectangularCut
740 << " p=" << p << " p2=" << p2
741 << " border=" << border
742 << " myGeom=" << myGeom
743 << "\n";
744
745#endif
746}
747
748
749const PositionVector&
750NBEdge::getNodeBorder(const NBNode* node) const {
751 if (node == myFrom) {
752 return myFromBorder;
753 } else {
754 assert(node == myTo);
755 return myToBorder;
756 }
757}
758
759
760void
762 if (node == myFrom) {
763 myFromBorder.clear();
764 } else {
765 assert(node == myTo);
766 myToBorder.clear();
767 }
768}
769
770
771bool
781
782
783bool
784NBEdge::isBidiEdge(bool checkPotential) const {
785 return myPossibleTurnDestination != nullptr
787 && (myIsBidi || myPossibleTurnDestination->myIsBidi || checkPotential)
790 // geometry check a) full overlap geometry
793 || (checkPotential && getGeometry().size() == 2 && myPossibleTurnDestination->getGeometry().size() == 2)))
794 // b) TWLT (Two-Way-Left-Turn-lane)
795 || (myLanes.back().shape.reverse().almostSame(myPossibleTurnDestination->myLanes.back().shape, POSITION_EPS))
796 );
797
798}
799
800
801bool
803 if (!isRailway(getPermissions())) {
804 return false;
805 }
806 for (NBEdge* out : myTo->getOutgoingEdges()) {
807 if (isRailway(out->getPermissions()) &&
808 out != getTurnDestination(true)) {
809 return true;
810 }
811 }
812 return true;
813}
814
815
818 PositionVector shape = old;
819 shape = startShapeAt(shape, myFrom, myFromBorder);
820#ifdef DEBUG_CUT_LANES
821 if (DEBUGCOND) {
822 std::cout << getID() << " cutFrom=" << shape << "\n";
823 }
824#endif
825 if (shape.size() < 2) {
826 // only keep the last snippet
827 const double oldLength = old.length();
828 shape = old.getSubpart(oldLength - 2 * POSITION_EPS, oldLength);
829#ifdef DEBUG_CUT_LANES
830 if (DEBUGCOND) {
831 std::cout << getID() << " cutFromFallback=" << shape << "\n";
832 }
833#endif
834 }
835 shape = startShapeAt(shape.reverse(), myTo, myToBorder).reverse();
836#ifdef DEBUG_CUT_LANES
837 if (DEBUGCOND) {
838 std::cout << getID() << " cutTo=" << shape << "\n";
839 }
840#endif
841 // sanity checks
842 if (shape.length() < POSITION_EPS) {
843 if (old.length() < 2 * POSITION_EPS) {
844 shape = old;
845 } else {
846 const double midpoint = old.length() / 2;
847 // EPS*2 because otherwhise shape has only a single point
848 shape = old.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
849 assert(shape.size() >= 2);
850 assert(shape.length() > 0);
851#ifdef DEBUG_CUT_LANES
852 if (DEBUGCOND) {
853 std::cout << getID() << " fallBackShort=" << shape << "\n";
854 }
855#endif
856 }
857 } else {
858 // @note If the node shapes are overlapping we may get a shape which goes in the wrong direction
859 // in this case the result shape should shortened
860 if (DEG2RAD(135) < fabs(GeomHelper::angleDiff(shape.beginEndAngle(), old.beginEndAngle()))) {
861 // eliminate intermediate points
862 PositionVector tmp;
863 tmp.push_back(shape[0]);
864 tmp.push_back(shape[-1]);
865 shape = tmp;
866 if (tmp.length() < POSITION_EPS) {
867 // fall back to original shape
868 if (old.length() < 2 * POSITION_EPS) {
869 shape = old;
870 } else {
871 const double midpoint = old.length() / 2;
872 // EPS*2 because otherwhise shape has only a single point
873 shape = old.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
874 assert(shape.size() >= 2);
875 assert(shape.length() > 0);
876 }
877#ifdef DEBUG_CUT_LANES
878 if (DEBUGCOND) {
879 std::cout << getID() << " fallBackReversed=" << shape << "\n";
880 }
881#endif
882 } else {
883 const double midpoint = shape.length() / 2;
884 // cut to size and reverse
885 shape = shape.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
886 if (shape.length() < POSITION_EPS) {
887 assert(false);
888 // the shape has a sharp turn near the midpoint
889 }
890 shape = shape.reverse();
891#ifdef DEBUG_CUT_LANES
892 if (DEBUGCOND) {
893 std::cout << getID() << " fallBackReversed2=" << shape << " mid=" << midpoint << "\n";
894 }
895#endif
896 }
897 // make short edge flat (length <= 2 * POSITION_EPS)
898 const double z = (shape[0].z() + shape[1].z()) / 2;
899 shape[0].setz(z);
900 shape[1].setz(z);
901 }
902 }
903 return shape;
904}
905
906
907void
908NBEdge::computeEdgeShape(double smoothElevationThreshold) {
909 if (smoothElevationThreshold > 0 && myGeom.hasElevation()) {
911 // cutting and patching z-coordinate may cause steep grades which should be smoothed
912 if (!myFrom->geometryLike()) {
913 cut[0].setz(myFrom->getPosition().z());
914 const double d = cut[0].distanceTo2D(cut[1]);
915 const double dZ = fabs(cut[0].z() - cut[1].z());
916 if (dZ / smoothElevationThreshold > d) {
917 cut = cut.smoothedZFront(MIN2(cut.length2D() / 2, dZ / smoothElevationThreshold));
918 }
919 }
920 if (!myTo->geometryLike()) {
921 cut[-1].setz(myTo->getPosition().z());
922 const double d = cut[-1].distanceTo2D(cut[-2]);
923 const double dZ = fabs(cut[-1].z() - cut[-2].z());
924 if (dZ / smoothElevationThreshold > d) {
925 cut = cut.reverse().smoothedZFront(MIN2(cut.length2D() / 2, dZ / smoothElevationThreshold)).reverse();
926 }
927 }
928 cut[0] = myGeom[0];
929 cut[-1] = myGeom[-1];
930 if (cut != myGeom) {
931 myGeom = cut;
933 }
934 }
935 for (int i = 0; i < (int)myLanes.size(); i++) {
936 myLanes[i].shape = cutAtIntersection(myLanes[i].shape);
937 }
938 // recompute edge's length as the average of lane lengths
939 double avgLength = 0;
940 for (int i = 0; i < (int)myLanes.size(); i++) {
941 avgLength += myLanes[i].shape.length();
942 }
943 myLength = avgLength / (double) myLanes.size();
944 computeAngle(); // update angles using the finalized node and lane shapes
945}
946
947
949NBEdge::startShapeAt(const PositionVector& laneShape, const NBNode* startNode, PositionVector nodeShape) {
950 if (nodeShape.size() == 0) {
951 nodeShape = startNode->getShape();
952 nodeShape.closePolygon();
953 }
954 PositionVector lb = laneShape;
955 lb.extrapolate2D(100.0);
956 if (nodeShape.intersects(laneShape)) {
957 // shape intersects directly
958 std::vector<double> pbv = laneShape.intersectsAtLengths2D(nodeShape);
959 assert(pbv.size() > 0);
960 // ensure that the subpart has at least two points
961 double pb = MIN2(laneShape.length2D() - POSITION_EPS - NUMERICAL_EPS, VectorHelper<double>::maxValue(pbv));
962 if (pb < 0) {
963 return laneShape;
964 }
965 PositionVector ns = laneShape.getSubpart2D(pb, laneShape.length2D());
966 //PositionVector ns = pb < (laneShape.length() - POSITION_EPS) ? laneShape.getSubpart2D(pb, laneShape.length()) : laneShape;
967 const double delta = ns[0].z() - laneShape[0].z();
968 //std::cout << "a) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << ns[0].z() << " delta=" << delta << "\n";
969 if (fabs(delta) > 2 * POSITION_EPS && (!startNode->geometryLike() || pb < 1)) {
970 // make "real" intersections and small intersections flat
971 //std::cout << "a) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << ns[0].z() << " delta=" << delta << "\n";
972 ns[0].setz(startNode->getPosition().z());
973 }
974 assert(ns.size() >= 2);
975 return ns;
976 } else if (nodeShape.intersects(lb)) {
977 // extension of first segment intersects
978 std::vector<double> pbv = lb.intersectsAtLengths2D(nodeShape);
979 assert(pbv.size() > 0);
980 double pb = VectorHelper<double>::maxValue(pbv);
981 assert(pb >= 0);
982 PositionVector result = laneShape.getSubpartByIndex(1, (int)laneShape.size() - 1);
983 Position np = lb.positionAtOffset2D(pb);
984 const double delta = np.z() - laneShape[0].z();
985 //std::cout << "b) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << np.z() << " delta=" << delta << "\n";
986 if (fabs(delta) > 2 * POSITION_EPS && !startNode->geometryLike()) {
987 // avoid z-overshoot when extrapolating
988 //std::cout << "b) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << np.z() << " delta=" << delta << "\n";
989 np.setz(startNode->getPosition().z());
990 }
991 result.push_front_noDoublePos(np);
992 return result;
993 //if (result.size() >= 2) {
994 // return result;
995 //} else {
996 // WRITE_WARNING(error + " (resulting shape is too short)");
997 // return laneShape;
998 //}
999 } else {
1000 // could not find proper intersection. Probably the edge is very short
1001 // and lies within nodeShape
1002 // @todo enable warning WRITE_WARNING(error + " (laneShape lies within nodeShape)");
1003 return laneShape;
1004 }
1005}
1006
1007
1008const PositionVector&
1010 return myLanes[i].shape;
1011}
1012
1013
1014void
1018
1019
1024
1025
1026void
1028 if (index >= 0) {
1029 myGeom.insert(myGeom.begin() + index, p);
1030 } else {
1031 myGeom.insert(myGeom.end() + index, p);
1032 }
1033}
1034
1035
1036void
1037NBEdge::reduceGeometry(const double minDist) {
1038 // attempt symmetrical removal for forward and backward direction
1039 // (very important for bidiRail)
1040 if (myFrom->getID() < myTo->getID()) {
1041 PositionVector reverse = myGeom.reverse();
1042 reverse.removeDoublePoints(minDist, true, 0, 0, true);
1043 myGeom = reverse.reverse();
1044 for (Lane& lane : myLanes) {
1045 reverse = lane.customShape.reverse();
1046 reverse.removeDoublePoints(minDist, true, 0, 0, true);
1047 lane.customShape = reverse.reverse();
1048 }
1049 } else {
1050 myGeom.removeDoublePoints(minDist, true, 0, 0, true);
1051 for (Lane& lane : myLanes) {
1052 lane.customShape.removeDoublePoints(minDist, true, 0, 0, true);
1053 }
1054 }
1055}
1056
1057
1058void
1059NBEdge::checkGeometry(const double maxAngle, bool fixAngle, const double minRadius, bool fix, bool silent) {
1060 if (myGeom.size() < 3) {
1061 return;
1062 }
1063 //std::cout << "checking geometry of " << getID() << " geometry = " << toString(myGeom) << "\n";
1064 std::vector<double> angles; // absolute segment angles
1065 //std::cout << " absolute angles:";
1066 for (int i = 0; i < (int)myGeom.size() - 1; ++i) {
1067 angles.push_back(myGeom.angleAt2D(i));
1068 //std::cout << " " << angles.back();
1069 }
1070 //std::cout << "\n relative angles: ";
1071 NBEdge* bidi = const_cast<NBEdge*>(getBidiEdge());
1072 for (int i = 0; i < (int)angles.size() - 1; ++i) {
1073 const double relAngle = fabs(GeomHelper::angleDiff(angles[i], angles[i + 1]));
1074 //std::cout << relAngle << " ";
1075 if (maxAngle > 0 && relAngle > maxAngle) {
1076 if (fixAngle) {
1077 WRITE_MESSAGEF(TL("Removing sharp angle of % degrees at edge '%', segment %."),
1078 toString(relAngle), getID(), i);
1079 myGeom.erase(myGeom.begin() + i + 1);
1080 if (bidi != nullptr) {
1081 bidi->myGeom = myGeom.reverse();
1082 }
1083 checkGeometry(maxAngle, fixAngle, minRadius, fix, silent);
1084 return;
1085 } else if (!silent) {
1086 WRITE_WARNINGF(TL("Found angle of % degrees at edge '%', segment %."), RAD2DEG(relAngle), getID(), i);
1087 }
1088 }
1089 if (relAngle < DEG2RAD(1)) {
1090 continue;
1091 }
1092 if (i == 0 || i == (int)angles.size() - 2) {
1093 const bool start = i == 0;
1094 const double dist = (start ? myGeom[0].distanceTo2D(myGeom[1]) : myGeom[-2].distanceTo2D(myGeom[-1]));
1095 const double r = tan(0.5 * (M_PI - relAngle)) * dist;
1096 //std::cout << (start ? " start" : " end") << " length=" << dist << " radius=" << r << " ";
1097 if (minRadius > 0 && r < minRadius) {
1098 if (fix) {
1099 WRITE_MESSAGEF(TL("Removing sharp turn with radius % at the % of edge '%'."),
1100 toString(r), start ? TL("start") : TL("end"), getID());
1101 myGeom.erase(myGeom.begin() + (start ? 1 : i + 1));
1102 if (bidi != nullptr) {
1103 bidi->myGeom = myGeom.reverse();
1104 }
1105 checkGeometry(maxAngle, fixAngle, minRadius, fix, silent);
1106 return;
1107 } else if (!silent) {
1108 WRITE_WARNINGF(TL("Found sharp turn with radius % at the % of edge '%'."),
1109 toString(r), start ? TL("start") : TL("end"), getID());
1110 }
1111 }
1112 }
1113 }
1114 //std::cout << "\n";
1115}
1116
1117
1118// ----------- Setting and getting connections
1119bool
1120NBEdge::addEdge2EdgeConnection(NBEdge* dest, bool overrideRemoval, SVCPermissions permissions) {
1122 return true;
1123 }
1124 // check whether the node was merged and now a connection between
1125 // not matching edges is tried to be added
1126 // This happens f.e. within the ptv VISSIM-example "Beijing"
1127 if (dest != nullptr && myTo != dest->myFrom) {
1128 return false;
1129 }
1130 if (dest == nullptr) {
1132 myConnections.push_back(Connection(-1, dest, -1));
1134 } else if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(dest)) == myConnections.end()) {
1135 myConnections.push_back(Connection(-1, dest, -1));
1136 myConnections.back().permissions = permissions;
1137 }
1138 if (overrideRemoval) {
1139 // override earlier delete decision
1140 for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end();) {
1141 if (it->toEdge == dest) {
1142 it = myConnectionsToDelete.erase(it);
1143 } else {
1144 it++;
1145 }
1146 }
1147 }
1150 }
1151 return true;
1152}
1153
1154
1155bool
1157 int toLane, Lane2LaneInfoType type,
1158 bool mayUseSameDestination,
1159 bool mayDefinitelyPass,
1160 KeepClear keepClear,
1161 double contPos,
1162 double visibility,
1163 double speed,
1164 double friction,
1165 double length,
1166 const PositionVector& customShape,
1167 bool uncontrolled,
1168 SVCPermissions permissions,
1169 bool indirectLeft,
1170 const std::string& edgeType,
1171 SVCPermissions changeLeft,
1172 SVCPermissions changeRight,
1173 bool postProcess) {
1175 return true;
1176 }
1177 // check whether the node was merged and now a connection between
1178 // not matching edges is tried to be added
1179 // This happens f.e. within the ptv VISSIM-example "Beijing"
1180 if (myTo != dest->myFrom) {
1181 return false;
1182 }
1183 if (!addEdge2EdgeConnection(dest)) {
1184 return false;
1185 }
1186 return setConnection(from, dest, toLane, type, mayUseSameDestination, mayDefinitelyPass, keepClear, contPos, visibility, speed, friction, length,
1187 customShape, uncontrolled, permissions, indirectLeft, edgeType, changeLeft, changeRight, postProcess);
1188}
1189
1190
1191bool
1193 NBEdge* dest, int toLane,
1194 int no, Lane2LaneInfoType type,
1195 bool invalidatePrevious,
1196 bool mayDefinitelyPass) {
1197 if (invalidatePrevious) {
1199 }
1200 bool ok = true;
1201 for (int i = 0; i < no && ok; i++) {
1202 ok &= addLane2LaneConnection(fromLane + i, dest, toLane + i, type, false, mayDefinitelyPass);
1203 }
1204 return ok;
1205}
1206
1207
1208bool
1209NBEdge::setConnection(int lane, NBEdge* destEdge,
1210 int destLane, Lane2LaneInfoType type,
1211 bool mayUseSameDestination,
1212 bool mayDefinitelyPass,
1213 KeepClear keepClear,
1214 double contPos,
1215 double visibility,
1216 double speed,
1217 double friction,
1218 double length,
1219 const PositionVector& customShape,
1220 bool uncontrolled,
1221 SVCPermissions permissions,
1222 bool indirectLeft,
1223 const std::string& edgeType,
1224 SVCPermissions changeLeft,
1225 SVCPermissions changeRight,
1226 bool postProcess) {
1228 return false;
1229 }
1230 // some kind of a misbehaviour which may occure when the junction's outgoing
1231 // edge priorities were not properly computed, what may happen due to
1232 // an incomplete or not proper input
1233 // what happens is that under some circumstances a single lane may set to
1234 // be approached more than once by the one of our lanes.
1235 // This must not be!
1236 // we test whether it is the case and do nothing if so - the connection
1237 // will be refused
1238 //
1239 if (!mayUseSameDestination && hasConnectionTo(destEdge, destLane)) {
1240 return false;
1241 }
1242 if (find_if(myConnections.begin(), myConnections.end(), connections_finder(lane, destEdge, destLane)) != myConnections.end()) {
1243 return true;
1244 }
1245 if ((int)myLanes.size() <= lane || destEdge->getNumLanes() <= (int)destLane) {
1246 // problem might be corrigible in post-processing
1247 WRITE_WARNINGF(TL("Could not set connection from '%' to '%'."), getLaneID(lane), destEdge->getLaneID(destLane));
1248 return false;
1249 }
1250 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
1251 if ((*i).toEdge == destEdge && ((*i).fromLane == -1 || (*i).toLane == -1)) {
1252 if (permissions == SVC_UNSPECIFIED) {
1253 // @note: in case we were to add multiple connections from the
1254 // same lane the second one wouldn't get the special permissions!
1255 permissions = (*i).permissions;
1256 }
1257 i = myConnections.erase(i);
1258 } else {
1259 ++i;
1260 }
1261 }
1262 myConnections.push_back(Connection(lane, destEdge, destLane));
1263 if (mayDefinitelyPass) {
1264 myConnections.back().mayDefinitelyPass = true;
1265 }
1266 myConnections.back().keepClear = keepClear;
1267 myConnections.back().contPos = contPos;
1268 myConnections.back().visibility = visibility;
1269 myConnections.back().permissions = permissions;
1270 myConnections.back().indirectLeft = indirectLeft;
1271 myConnections.back().edgeType = edgeType;
1272 myConnections.back().changeLeft = changeLeft;
1273 myConnections.back().changeRight = changeRight;
1274 myConnections.back().speed = speed;
1275 myConnections.back().friction = friction;
1276 myConnections.back().customLength = length;
1277 myConnections.back().customShape = customShape;
1278 myConnections.back().uncontrolled = uncontrolled;
1279 if (type == Lane2LaneInfoType::USER) {
1281 } else {
1282 // check whether we have to take another look at it later
1283 if (type == Lane2LaneInfoType::COMPUTED) {
1284 // yes, the connection was set using an algorithm which requires a recheck
1286 } else {
1287 // ok, let's only not recheck it if we did no add something that has to be rechecked
1290 }
1291 }
1292 }
1293 if (postProcess) {
1294 // override earlier delete decision
1295 for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end();) {
1296 if ((it->fromLane < 0 || it->fromLane == lane)
1297 && (it->toEdge == nullptr || it->toEdge == destEdge)
1298 && (it->toLane < 0 || it->toLane == destLane)) {
1299 it = myConnectionsToDelete.erase(it);
1300 } else {
1301 it++;
1302 }
1303 }
1304 }
1305 return true;
1306}
1307
1308
1309std::vector<NBEdge::Connection>
1310NBEdge::getConnectionsFromLane(int lane, const NBEdge* to, int toLane) const {
1311 std::vector<NBEdge::Connection> ret;
1312 for (const Connection& c : myConnections) {
1313 if ((lane < 0 || c.fromLane == lane)
1314 && (to == nullptr || to == c.toEdge)
1315 && (toLane < 0 || toLane == c.toLane)) {
1316 ret.push_back(c);
1317 }
1318 }
1319 return ret;
1320}
1321
1322
1323const NBEdge::Connection&
1324NBEdge::getConnection(int fromLane, const NBEdge* to, int toLane) const {
1325 for (const Connection& c : myConnections) {
1326 if (c.fromLane == fromLane && c.toEdge == to && c.toLane == toLane) {
1327 return c;
1328 }
1329 }
1330 throw ProcessError("Connection from " + getID() + "_" + toString(fromLane)
1331 + " to " + to->getID() + "_" + toString(toLane) + " not found");
1332}
1333
1334
1336NBEdge::getConnectionRef(int fromLane, const NBEdge* to, int toLane) {
1337 for (Connection& c : myConnections) {
1338 if (c.fromLane == fromLane && c.toEdge == to && c.toLane == toLane) {
1339 return c;
1340 }
1341 }
1342 throw ProcessError("Connection from " + getID() + "_" + toString(fromLane)
1343 + " to " + to->getID() + "_" + toString(toLane) + " not found");
1344}
1345
1346
1347bool
1348NBEdge::hasConnectionTo(const NBEdge* destEdge, int destLane, int fromLane) const {
1349 return destEdge != nullptr && find_if(myConnections.begin(), myConnections.end(), connections_toedgelane_finder(destEdge, destLane, fromLane)) != myConnections.end();
1350}
1351
1352
1353bool
1354NBEdge::isConnectedTo(const NBEdge* e, const bool ignoreTurnaround) const {
1355 if (!ignoreTurnaround && (e == myTurnDestination)) {
1356 return true;
1357 }
1358 return
1359 find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(e))
1360 !=
1361 myConnections.end();
1362
1363}
1364
1365
1366const EdgeVector*
1368 // check whether connections exist and if not, use edges from the node
1369 EdgeVector outgoing;
1370 if (myConnections.size() == 0) {
1371 outgoing = myTo->getOutgoingEdges();
1372 } else {
1373 for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1374 if (find(outgoing.begin(), outgoing.end(), (*i).toEdge) == outgoing.end()) {
1375 outgoing.push_back((*i).toEdge);
1376 }
1377 }
1378 }
1379 for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
1380 if (it->fromLane < 0 && it->toLane < 0) {
1381 // found an edge that shall not be connected
1382 EdgeVector::iterator forbidden = std::find(outgoing.begin(), outgoing.end(), it->toEdge);
1383 if (forbidden != outgoing.end()) {
1384 outgoing.erase(forbidden);
1385 }
1386 }
1387 }
1388 // allocate the sorted container
1389 int size = (int) outgoing.size();
1390 EdgeVector* edges = new EdgeVector();
1391 edges->reserve(size);
1392 for (EdgeVector::const_iterator i = outgoing.begin(); i != outgoing.end(); i++) {
1393 NBEdge* outedge = *i;
1394 if (outedge != nullptr && outedge != myTurnDestination) {
1395 edges->push_back(outedge);
1396 }
1397 }
1398 std::sort(edges->begin(), edges->end(), NBContHelper::relative_outgoing_edge_sorter(this));
1399 return edges;
1400}
1401
1402
1405 EdgeVector ret;
1406 for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1407 if (find(ret.begin(), ret.end(), (*i).toEdge) == ret.end()) {
1408 ret.push_back((*i).toEdge);
1409 }
1410 }
1411 return ret;
1412}
1413
1414
1417 EdgeVector ret;
1418 const EdgeVector& candidates = myFrom->getIncomingEdges();
1419 for (EdgeVector::const_iterator i = candidates.begin(); i != candidates.end(); i++) {
1420 if ((*i)->isConnectedTo(this)) {
1421 ret.push_back(*i);
1422 }
1423 }
1424 return ret;
1425}
1426
1427
1428std::vector<int>
1429NBEdge::getConnectionLanes(NBEdge* currentOutgoing, bool withBikes, bool withBusLanes) const {
1430 std::vector<int> ret;
1431 if (currentOutgoing != myTurnDestination) {
1432 for (const Connection& c : myConnections) {
1433 if (c.toEdge == currentOutgoing
1434 && (withBikes || getPermissions(c.fromLane) != SVC_BICYCLE)
1435 && (withBusLanes || getPermissions(c.fromLane) != SVC_BUS)) {
1436 ret.push_back(c.fromLane);
1437 }
1438 }
1439 }
1440 return ret;
1441}
1442
1443
1444void
1448
1449
1450void
1454
1455
1456void
1458 EdgeVector connected = getConnectedEdges();
1459 for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
1460 NBEdge* inc = *i;
1461 // We have to do this
1463 // add all connections
1464 for (EdgeVector::iterator j = connected.begin(); j != connected.end(); j++) {
1465 inc->addEdge2EdgeConnection(*j);
1466 }
1467 inc->removeFromConnections(this);
1468 }
1469}
1470
1471
1472void
1473NBEdge::removeFromConnections(NBEdge* toEdge, int fromLane, int toLane, bool tryLater, const bool adaptToLaneRemoval,
1474 const bool keepPossibleTurns) {
1475 // remove from "myConnections"
1476 const int fromLaneRemoved = adaptToLaneRemoval && fromLane >= 0 ? fromLane : -1;
1477 const int toLaneRemoved = adaptToLaneRemoval && toLane >= 0 ? toLane : -1;
1478 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
1479 Connection& c = *i;
1480 if ((toEdge == nullptr || c.toEdge == toEdge)
1481 && (fromLane < 0 || c.fromLane == fromLane)
1482 && (toLane < 0 || c.toLane == toLane)) {
1483 if (myTo->isTLControlled()) {
1484 std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
1485 for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
1486 (*it)->removeConnection(NBConnection(this, c.fromLane, c.toEdge, c.toLane));
1487 }
1488 }
1489 i = myConnections.erase(i);
1490 tryLater = false;
1491 } else {
1492 if (fromLaneRemoved >= 0 && c.fromLane > fromLaneRemoved) {
1493 if (myTo->isTLControlled()) {
1494 std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
1495 for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
1496 for (NBConnectionVector::iterator tlcon = (*it)->getControlledLinks().begin(); tlcon != (*it)->getControlledLinks().end(); ++tlcon) {
1497 NBConnection& tc = *tlcon;
1498 if (tc.getTo() == c.toEdge && tc.getFromLane() == c.fromLane && tc.getToLane() == c.toLane) {
1499 tc.shiftLaneIndex(this, -1);
1500 }
1501 }
1502 }
1503 }
1504 //std::cout << getID() << " removeFromConnections fromLane=" << fromLane << " to=" << Named::getIDSecure(toEdge) << " toLane=" << toLane << " reduceFromLane=" << c.fromLane << " (to=" << c.toLane << ")\n";
1505 c.fromLane--;
1506 }
1507 if (toLaneRemoved >= 0 && c.toLane > toLaneRemoved && (toEdge == nullptr || c.toEdge == toEdge)) {
1508 //std::cout << getID() << " removeFromConnections fromLane=" << fromLane << " to=" << Named::getIDSecure(toEdge) << " toLane=" << toLane << " reduceToLane=" << c.toLane << " (from=" << c.fromLane << ")\n";
1509 c.toLane--;
1510 }
1511 ++i;
1512 }
1513 }
1514 // check whether it was the turn destination
1515 if (myTurnDestination == toEdge && fromLane < 0) {
1516 myTurnDestination = nullptr;
1517 }
1518 if (myPossibleTurnDestination == toEdge && fromLane < 0 && !keepPossibleTurns) {
1519 myPossibleTurnDestination = nullptr;
1520 }
1521 if (tryLater) {
1522 myConnectionsToDelete.push_back(Connection(fromLane, toEdge, toLane));
1523#ifdef DEBUG_CONNECTION_GUESSING
1524 if (DEBUGCOND) {
1525 std::cout << "removeFromConnections " << getID() << "_" << fromLane << "->" << toEdge->getID() << "_" << toLane << "\n";
1526 for (Connection& c : myConnections) {
1527 std::cout << " conn " << c.getDescription(this) << "\n";
1528 }
1530 std::cout << " connToDelete " << c.getDescription(this) << "\n";
1531 }
1532 }
1533#endif
1534 }
1535}
1536
1537
1538bool
1540 // iterate over connections
1541 for (auto i = myConnections.begin(); i != myConnections.end(); i++) {
1542 if ((i->toEdge == connectionToRemove.toEdge) && (i->fromLane == connectionToRemove.fromLane) && (i->toLane == connectionToRemove.toLane)) {
1543 // remove connection
1544 myConnections.erase(i);
1545 return true;
1546 }
1547 }
1548 // assert(false);
1549 return false;
1550}
1551
1552
1553void
1554NBEdge::invalidateConnections(bool reallowSetting) {
1555 myTurnDestination = nullptr;
1556 myConnections.clear();
1557 if (reallowSetting) {
1559 } else {
1561 }
1562}
1563
1564
1565void
1566NBEdge::replaceInConnections(NBEdge* which, NBEdge* by, int laneOff) {
1567 // replace in "_connectedEdges"
1568 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1569 if ((*i).toEdge == which) {
1570 (*i).toEdge = by;
1571 (*i).toLane += laneOff;
1572 }
1573 }
1574 // check whether it was the turn destination
1575 if (myTurnDestination == which) {
1576 myTurnDestination = by;
1577 }
1578}
1579
1580void
1581NBEdge::replaceInConnections(NBEdge* which, const std::vector<NBEdge::Connection>& origConns) {
1582 std::map<int, int> laneMap;
1583 int minLane = -1;
1584 int maxLane = -1;
1585 // get lanes used to approach the edge to remap
1586 bool wasConnected = false;
1587 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1588 if ((*i).toEdge != which) {
1589 continue;
1590 }
1591 wasConnected = true;
1592 if ((*i).fromLane != -1) {
1593 int fromLane = (*i).fromLane;
1594 laneMap[(*i).toLane] = fromLane;
1595 if (minLane == -1 || minLane > fromLane) {
1596 minLane = fromLane;
1597 }
1598 if (maxLane == -1 || maxLane < fromLane) {
1599 maxLane = fromLane;
1600 }
1601 }
1602 }
1603 if (!wasConnected) {
1604 return;
1605 }
1606 // add new connections
1607 std::vector<NBEdge::Connection> conns = origConns;
1608 EdgeVector origTargets = getSuccessors();
1609 for (std::vector<NBEdge::Connection>::iterator i = conns.begin(); i != conns.end(); ++i) {
1610 if ((*i).toEdge == which || (*i).toEdge == this
1611 // if we already have connections to the target edge, do not add new ones as they are probably from a circular replacement
1612 || std::find(origTargets.begin(), origTargets.end(), (*i).toEdge) != origTargets.end()) {
1613#ifdef DEBUG_REPLACECONNECTION
1614 if (DEBUGCOND) {
1615 std::cout << " replaceInConnections edge=" << getID() << " which=" << which->getID()
1616 << " origTargets=" << toString(origTargets) << " newTarget=" << i->toEdge->getID() << " skipped\n";
1617 }
1618#endif
1619 continue;
1620 }
1621 if (which->getStep() == EdgeBuildingStep::EDGE2EDGES) {
1622 // do not set lane-level connections
1623 replaceInConnections(which, (*i).toEdge, 0);
1624 continue;
1625 }
1626 int fromLane = (*i).fromLane;
1627 int toUse = -1;
1628 if (laneMap.find(fromLane) == laneMap.end()) {
1629 if (fromLane >= 0 && fromLane <= minLane) {
1630 toUse = minLane;
1631 // patch laneMap to avoid crossed-over connections
1632 for (auto& item : laneMap) {
1633 if (item.first < fromLane) {
1634 item.second = MIN2(item.second, minLane);
1635 }
1636 }
1637 }
1638 if (fromLane >= 0 && fromLane >= maxLane) {
1639 toUse = maxLane;
1640 // patch laneMap to avoid crossed-over connections
1641 for (auto& item : laneMap) {
1642 if (item.first > fromLane) {
1643 item.second = MAX2(item.second, maxLane);
1644 }
1645 }
1646 }
1647 } else {
1648 toUse = laneMap[fromLane];
1649 }
1650 if (toUse == -1) {
1651 toUse = 0;
1652 }
1653#ifdef DEBUG_REPLACECONNECTION
1654 if (DEBUGCOND) {
1655 std::cout << " replaceInConnections edge=" << getID() << " which=" << which->getID() << " origTargets=" << toString(origTargets)
1656 << " origFrom=" << fromLane << " laneMap=" << joinToString(laneMap, ":", ",") << " minLane=" << minLane << " maxLane=" << maxLane
1657 << " newTarget=" << i->toEdge->getID() << " fromLane=" << toUse << " toLane=" << i->toLane << "\n";
1658 }
1659#endif
1660 setConnection(toUse, i->toEdge, i->toLane, Lane2LaneInfoType::COMPUTED, false, i->mayDefinitelyPass, i->keepClear,
1661 i->contPos, i->visibility, i->speed, i->friction, i->customLength, i->customShape, i->uncontrolled);
1662 }
1663 // remove the remapped edge from connections
1664 removeFromConnections(which);
1665}
1666
1667
1668void
1673
1674
1675bool
1676NBEdge::canMoveConnection(const Connection& con, int newFromLane) const {
1677 // only allow using newFromLane if at least 1 vClass is permitted to use
1678 // this connection. If the connection shall be moved to a sidewalk, only create the connection if there is no walking area
1679 const SVCPermissions common = (getPermissions(newFromLane) & con.toEdge->getPermissions(con.toLane));
1680 return (common > 0 && common != SVC_PEDESTRIAN);
1681}
1682
1683
1684void
1686#ifdef DEBUG_CONNECTION_CHECKING
1687 std::cout << " moveConnectionToLeft " << getID() << " lane=" << lane << "\n";
1688#endif
1689 int index = 0;
1690 for (int i = 0; i < (int)myConnections.size(); ++i) {
1691 if (myConnections[i].fromLane == (int)(lane) && canMoveConnection(myConnections[i], lane + 1)) {
1692 index = i;
1693 }
1694 }
1695 std::vector<Connection>::iterator i = myConnections.begin() + index;
1696 Connection c = *i;
1697 myConnections.erase(i);
1699}
1700
1701
1702void
1704#ifdef DEBUG_CONNECTION_CHECKING
1705 std::cout << " moveConnectionToRight " << getID() << " lane=" << lane << "\n";
1706#endif
1707 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1708 if ((*i).fromLane == (int)lane && canMoveConnection(*i, lane - 1)) {
1709 Connection c = *i;
1710 i = myConnections.erase(i);
1712 return;
1713 }
1714 }
1715}
1716
1717
1718double
1719NBEdge::buildInnerEdges(const NBNode& n, int noInternalNoSplits, int& linkIndex, int& splitIndex) {
1721 const int numPoints = oc.getInt("junctions.internal-link-detail");
1722 const bool joinTurns = oc.getBool("junctions.join-turns");
1723 const double limitTurnSpeed = oc.getFloat("junctions.limit-turn-speed");
1724 const double limitTurnSpeedMinAngle = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle"));
1725 const double limitTurnSpeedMinAngleRail = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle.railway"));
1726 const double limitTurnSpeedWarnStraight = oc.getFloat("junctions.limit-turn-speed.warn.straight");
1727 const double limitTurnSpeedWarnTurn = oc.getFloat("junctions.limit-turn-speed.warn.turn");
1728 const bool higherSpeed = oc.getBool("junctions.higher-speed");
1729 const double interalJunctionVehicleWidth = oc.getFloat("internal-junctions.vehicle-width");
1730 const double defaultContPos = oc.getFloat("default.connection.cont-pos");
1731 const bool fromRail = isRailway(getPermissions());
1732 std::string innerID = ":" + n.getID();
1733 NBEdge* toEdge = nullptr;
1734 int edgeIndex = linkIndex;
1735 int internalLaneIndex = 0;
1736 int numLanes = 0; // number of lanes that share the same edge
1737 double lengthSum = 0; // total shape length of all lanes that share the same edge
1738 int avoidedIntersectingLeftOriginLane = std::numeric_limits<int>::max();
1739 bool averageLength = true;
1740 double maxCross = 0.;
1741 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1742 Connection& con = *i;
1743 con.haveVia = false; // reset first since this may be called multiple times
1744 if (con.toEdge == nullptr) {
1745 continue;
1746 }
1747 LinkDirection dir = n.getDirection(this, con.toEdge);
1748 const bool isRightTurn = (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT);
1749 const bool isTurn = (isRightTurn || dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT);
1750 // put turning internal lanes on separate edges
1751 if (con.toEdge != toEdge) {
1752 // skip indices to keep some correspondence between edge ids and link indices:
1753 // internalEdgeIndex + internalLaneIndex = linkIndex
1754 edgeIndex = linkIndex;
1755 toEdge = con.toEdge;
1756 internalLaneIndex = 0;
1757 maxCross = MAX2(maxCross, assignInternalLaneLength(i, numLanes, lengthSum, averageLength));
1758 numLanes = 0;
1759 lengthSum = 0;
1760 }
1761 averageLength = !isTurn || joinTurns; // legacy behavior
1762 SVCPermissions conPermissions = getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane);
1763 const int conShapeFlag = (conPermissions & ~SVC_PEDESTRIAN) != 0 ? 0 : NBNode::SCURVE_IGNORE;
1764 PositionVector shape = n.computeInternalLaneShape(this, con, numPoints, myTo, conShapeFlag);
1765 std::vector<int> foeInternalLinks;
1766
1767 if (dir != LinkDirection::STRAIGHT && shape.length() < POSITION_EPS && !(isBidiRail() && getTurnDestination(true) == con.toEdge)) {
1768 WRITE_WARNINGF(TL("Connection '%_%->%_%' is only %m short."), getID(), con.fromLane, con.toEdge->getID(), con.toLane, shape.length());
1769 }
1770
1771 // crossingPosition, list of foe link indices
1772 std::pair<double, std::vector<int> > crossingPositions(-1, std::vector<int>());
1773 std::set<std::string> tmpFoeIncomingLanes;
1775 int index = 0;
1776 std::vector<PositionVector> otherShapes;
1777 const double width1 = MIN2(interalJunctionVehicleWidth / 2, getLaneWidth(con.fromLane) / 2);
1778 const double width1OppositeLeft = 0; // using width1 changes a lot of curves even though they are rarely responsible for collisions
1779 for (const NBEdge* i2 : n.getIncomingEdges()) {
1780 for (const Connection& k2 : i2->getConnections()) {
1781 if (k2.toEdge == nullptr) {
1782 continue;
1783 }
1784 // vehicles are typically less wide than the lane
1785 // they drive on but but bicycle lanes should be kept clear for their whole width
1786 double width2 = k2.toEdge->getLaneWidth(k2.toLane);
1787 if (k2.toEdge->getPermissions(k2.toLane) != SVC_BICYCLE) {
1788 width2 *= 0.5;
1789 }
1790 const bool foes = n.foes(this, con.toEdge, i2, k2.toEdge);
1791 LinkDirection dir2 = n.getDirection(i2, k2.toEdge);
1792 bool needsCont = !isRailway(conPermissions) && (n.needsCont(this, i2, con, k2) || (con.contPos != UNSPECIFIED_CONTPOS && !con.indirectLeft));
1793 const bool avoidIntersectCandidate = !foes && bothLeftTurns(dir, i2, dir2);
1794 bool oppositeLeftIntersect = avoidIntersectCandidate && haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2);
1795 int shapeFlag = 0;
1797 // do not warn if only bicycles, pedestrians or delivery vehicles are involved as this is a typical occurrence
1798 if (con.customShape.size() == 0
1799 && k2.customShape.size() == 0
1800 && (oppositeLeftIntersect || (avoidedIntersectingLeftOriginLane < con.fromLane && avoidIntersectCandidate))
1801 && ((i2->getPermissions(k2.fromLane) & warn) != 0
1802 && (k2.toEdge->getPermissions(k2.toLane) & warn) != 0)) {
1803 // recompute with different curve parameters (unless
1804 // the other connection is "unimportant"
1806 PositionVector origShape = shape;
1807 shape = n.computeInternalLaneShape(this, con, numPoints, myTo, shapeFlag);
1808 oppositeLeftIntersect = haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2, shapeFlag);
1809 if (oppositeLeftIntersect
1810 && (conPermissions & (SVCAll & ~(SVC_BICYCLE | SVC_PEDESTRIAN))) == 0) {
1811 shape = origShape;
1812 } else {
1813 // recompute previously computed crossing positions
1814 if (avoidedIntersectingLeftOriginLane == std::numeric_limits<int>::max()
1815 || avoidedIntersectingLeftOriginLane < con.fromLane) {
1816 for (const PositionVector& otherShape : otherShapes) {
1817 const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
1818 const double minDV = firstIntersection(shape, otherShape, width1OppositeLeft, width2,
1819 "Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
1820 if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
1821 assert(minDV >= 0);
1822 if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1823 crossingPositions.first = minDV;
1824 }
1825 }
1826 }
1827 }
1828 // make sure connections further to the left do not get a wider angle
1829 avoidedIntersectingLeftOriginLane = con.fromLane;
1830 }
1831 }
1832 const bool bothPrio = getJunctionPriority(&n) > 0 && i2->getJunctionPriority(&n) > 0;
1833 //std::cout << "n=" << n.getID() << " e1=" << getID() << " prio=" << getJunctionPriority(&n) << " e2=" << i2->getID() << " prio2=" << i2->getJunctionPriority(&n) << " both=" << bothPrio << " bothLeftIntersect=" << bothLeftIntersect(n, shape, dir, i2, k2, numPoints, width2) << " needsCont=" << needsCont << "\n";
1834 // the following special case might get obsolete once we have solved #9745
1835 const bool isBicycleLeftTurn = k2.indirectLeft || (dir2 == LinkDirection::LEFT && (i2->getPermissions(k2.fromLane) & k2.toEdge->getPermissions(k2.toLane)) == SVC_BICYCLE);
1836 // compute the crossing point
1837 if ((needsCont || (bothPrio && oppositeLeftIntersect && !isRailway(conPermissions))) && (!con.indirectLeft || dir2 == LinkDirection::STRAIGHT) && !isBicycleLeftTurn) {
1838 crossingPositions.second.push_back(index);
1839 const PositionVector otherShape = n.computeInternalLaneShape(i2, k2, numPoints, 0, shapeFlag);
1840 otherShapes.push_back(otherShape);
1841 const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
1842 const double minDV = firstIntersection(shape, otherShape, width1, width2,
1843 "Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
1844 if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
1845 assert(minDV >= 0);
1846 if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1847 crossingPositions.first = minDV;
1848 }
1849 }
1850 }
1851 const bool rightTurnConflict = NBNode::rightTurnConflict(
1852 this, con.toEdge, con.fromLane, i2, k2.toEdge, k2.fromLane);
1853 const bool indirectTurnConflit = con.indirectLeft && this == i2 && (dir2 == LinkDirection::STRAIGHT ||
1854 (con.fromLane < k2.fromLane && (dir2 == LinkDirection::RIGHT || dir2 == LinkDirection::PARTRIGHT)));
1855 const bool mergeConflict = myTo->mergeConflict(this, con, i2, k2, true);
1856 const bool mergeResponse = myTo->mergeConflict(this, con, i2, k2, false);
1857 const bool bidiConflict = myTo->bidiConflict(this, con, i2, k2, true);
1858 // compute foe internal lanes
1859 if (foes || rightTurnConflict || oppositeLeftIntersect || mergeConflict || indirectTurnConflit || bidiConflict) {
1860 foeInternalLinks.push_back(index);
1861 }
1862 // only warn once per pair of intersecting turns
1863 if (oppositeLeftIntersect && getID() > i2->getID()
1864 && (getPermissions(con.fromLane) & warn) != 0
1865 && (con.toEdge->getPermissions(con.toLane) & warn) != 0
1866 && (i2->getPermissions(k2.fromLane) & warn) != 0
1867 && (k2.toEdge->getPermissions(k2.toLane) & warn) != 0
1868 // do not warn for unregulated nodes
1870 ) {
1871 WRITE_WARNINGF(TL("Intersecting left turns at junction '%' from lane '%' and lane '%' (increase junction radius to avoid this)."),
1872 n.getID(), getLaneID(con.fromLane), i2->getLaneID(k2.fromLane));
1873 }
1874 // compute foe incoming lanes
1875 const bool signalised = hasSignalisedConnectionTo(con.toEdge);
1876 if ((n.forbids(i2, k2.toEdge, this, con.toEdge, signalised) || rightTurnConflict || indirectTurnConflit || mergeResponse)
1877 && (needsCont || dir == LinkDirection::TURN || (!signalised && this != i2 && !con.indirectLeft))) {
1878 tmpFoeIncomingLanes.insert(i2->getID() + "_" + toString(k2.fromLane));
1879 }
1880 if (bothPrio && oppositeLeftIntersect && getID() < i2->getID()) {
1881 //std::cout << " c1=" << con.getDescription(this) << " c2=" << k2.getDescription(i2) << " bothPrio=" << bothPrio << " oppositeLeftIntersect=" << oppositeLeftIntersect << "\n";
1882 // break symmetry using edge id
1883 // only store link index and resolve actual lane id later (might be multi-lane internal edge)
1884 tmpFoeIncomingLanes.insert(":" + toString(index));
1885 }
1886 index++;
1887 }
1888 }
1889 if (dir == LinkDirection::TURN && crossingPositions.first < 0 && crossingPositions.second.size() != 0 && shape.length() > 2. * POSITION_EPS) {
1890 // let turnarounds wait in the middle if no other crossing point was found and it has a sensible length
1891 // (if endOffset is used, the crossing point is in the middle of the part within the junction shape)
1892 crossingPositions.first = (double)(shape.length() + getEndOffset(con.fromLane)) / 2.;
1893 }
1894 // foe pedestrian crossings
1895 std::vector<NBNode::Crossing*> crossings = n.getCrossings();
1896 for (auto c : crossings) {
1897 const NBNode::Crossing& crossing = *c;
1898 for (EdgeVector::const_iterator it_e = crossing.edges.begin(); it_e != crossing.edges.end(); ++it_e) {
1899 const NBEdge* edge = *it_e;
1900 // compute foe internal lanes
1901 if ((this == edge || con.toEdge == edge) && !isRailway(conPermissions)) {
1902 foeInternalLinks.push_back(index);
1903 if (con.toEdge == edge &&
1904 ((isRightTurn && getJunctionPriority(&n) > 0) || (isTurn && con.tlID != ""))) {
1905 // build internal junctions (not for left turns at uncontrolled intersections)
1906 PositionVector crossingShape = crossing.shape;
1907 crossingShape.extrapolate(5.0); // sometimes shapes miss each other by a small margin
1908 const double minDV = firstIntersection(shape, crossingShape, 0, crossing.width / 2);
1909 if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) {
1910 assert(minDV >= 0);
1911 if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1912 crossingPositions.first = minDV;
1913 }
1914 }
1915 } else if (this == edge && crossing.priority && !myTo->isTLControlled()) {
1916 crossingPositions.first = 0;
1917 }
1918 }
1919 }
1920 index++;
1921 }
1922
1923 }
1924 if (con.contPos == UNSPECIFIED_CONTPOS) {
1925 con.contPos = defaultContPos;
1926 }
1927 if (con.contPos != UNSPECIFIED_CONTPOS) {
1928 // apply custom internal junction position
1929 if (con.contPos <= 0 || con.contPos >= shape.length()) {
1930 // disable internal junction
1931 crossingPositions.first = -1;
1932 } else {
1933 // set custom position
1934 crossingPositions.first = con.contPos;
1935 }
1936 }
1937
1938 // @todo compute the maximum speed allowed based on angular velocity
1939 // see !!! for an explanation (with a_lat_mean ~0.3)
1940 /*
1941 double vmax = (double) 0.3 * (double) 9.80778 *
1942 getLaneShape(con.fromLane).back().distanceTo(
1943 con.toEdge->getLaneShape(con.toLane).front())
1944 / (double) 2.0 / (double) M_PI;
1945 vmax = MIN2(vmax, ((getSpeed() + con.toEdge->getSpeed()) / (double) 2.0));
1946 */
1947 if (con.speed == UNSPECIFIED_SPEED) {
1948 if (higherSpeed) {
1949 con.vmax = MAX2(myLanes[con.fromLane].speed, con.toEdge->getLanes()[con.toLane].speed);
1950 } else {
1951 con.vmax = (myLanes[con.fromLane].speed + con.toEdge->getLanes()[con.toLane].speed) / (double) 2.0;
1952 }
1953 if (limitTurnSpeed > 0) {
1954 // see [Odhams and Cole, Models of Driver Speed Choice in Curves, 2004]
1955 const double angleRaw = fabs(GeomHelper::angleDiff(
1957 con.toEdge->getLaneShape(con.toLane).angleAt2D(0)));
1958 const double angle = MAX2(0.0, angleRaw - (fromRail ? limitTurnSpeedMinAngleRail : limitTurnSpeedMinAngle));
1959 const double length = shape.length2D();
1960 // do not trust the radius of tiny junctions
1961 // formula adapted from [Odhams, Andre and Cole, David, Models of Driver Speed Choice in Curves, 2004]
1962 if (angle > 0 && length > 1) {
1963 // permit higher turning speed on wide lanes
1964 const double radius = length / angle + getLaneWidth(con.fromLane) / 4;
1965 const double limit = sqrt(limitTurnSpeed * radius);
1966 const double reduction = con.vmax - limit;
1967 // always treat connctions at roundabout as turns when warning
1969 const LinkDirection dir2 = atRoundabout ? LinkDirection::LEFT : dir;
1970 if ((dir2 == LinkDirection::STRAIGHT && reduction > limitTurnSpeedWarnStraight)
1971 || (dir2 != LinkDirection::TURN && reduction > limitTurnSpeedWarnTurn)) {
1972 std::string dirType = std::string(dir == LinkDirection::STRAIGHT ? "straight" : "turning");
1973 if (atRoundabout) {
1974 dirType = "roundabout";
1975 }
1976 WRITE_WARNINGF(TL("Speed of % connection '%' reduced by % due to turning radius of % (length=%, angle=%)."),
1977 dirType, con.getDescription(this), reduction, radius, length, RAD2DEG(angleRaw));
1978 }
1979 con.vmax = MIN2(con.vmax, limit);
1980 // value is saved in <net> attribute. Must be set again when importing from .con.xml
1981 // con.speed = con.vmax;
1982 }
1983 assert(con.vmax > 0);
1984 //if (getID() == "-1017000.0.00") {
1985 // std::cout << con.getDescription(this) << " angleRaw=" << angleRaw << " angle=" << RAD2DEG(angle) << " length=" << length << " radius=" << length / angle
1986 // << " vmaxTurn=" << sqrt(limitTurnSpeed * length / angle) << " vmax=" << con.vmax << "\n";
1987 //}
1988 } else if (fromRail && dir == LinkDirection::TURN) {
1989 con.vmax = 0.01;
1990 }
1991 } else {
1992 con.vmax = con.speed;
1993 }
1994 if (con.friction == UNSPECIFIED_FRICTION) {
1995 con.friction = (myLanes[con.fromLane].friction + con.toEdge->getLanes()[con.toLane].friction) / 2.;
1996 }
1997 //
1998 assert(shape.size() >= 2);
1999 // get internal splits if any
2000 con.id = innerID + "_" + toString(edgeIndex);
2001 const double shapeLength = shape.length();
2002 double firstLength = shapeLength;
2003 con.internalViaLaneIndex = 0; // reset to default for netedit
2004 if (crossingPositions.first > 0 && crossingPositions.first < shapeLength) {
2005 std::pair<PositionVector, PositionVector> split = shape.splitAt(crossingPositions.first);
2006 con.shape = split.first;
2007 con.foeIncomingLanes = std::vector<std::string>(tmpFoeIncomingLanes.begin(), tmpFoeIncomingLanes.end());
2008 con.foeInternalLinks = foeInternalLinks; // resolve link indices to lane ids later
2009 if (i != myConnections.begin() && (i - 1)->toEdge == con.toEdge && (i - 1)->haveVia) {
2010 --splitIndex;
2011 con.internalViaLaneIndex = (i - 1)->internalViaLaneIndex + 1;
2012 }
2013 con.viaID = innerID + "_" + toString(splitIndex + noInternalNoSplits);
2014 ++splitIndex;
2015 con.viaShape = split.second;
2016 con.haveVia = true;
2017 firstLength = con.shape.length();
2018 } else {
2019 con.shape = shape;
2020 }
2021 con.internalLaneIndex = internalLaneIndex;
2022 ++internalLaneIndex;
2023 ++linkIndex;
2024 ++numLanes;
2026 // split length proportionally
2027 lengthSum += (shapeLength != 0 ? firstLength / shapeLength : 1) * con.customLength;
2028 } else {
2029 lengthSum += firstLength;
2030 }
2031 }
2032 return MAX2(maxCross, assignInternalLaneLength(myConnections.end(), numLanes, lengthSum, averageLength));
2033}
2034
2035
2036double
2037NBEdge::assignInternalLaneLength(std::vector<Connection>::iterator i, int numLanes, double lengthSum, bool averageLength) {
2038 // assign average length to all lanes of the same internal edge if averageLength is set
2039 // the lengthSum only covers the part up to the first internal junction
2040 // TODO This code assumes that either all connections in question have a via or none
2041 double maxCross = 0.;
2042 assert(i - myConnections.begin() >= numLanes);
2043 for (int prevIndex = 1; prevIndex <= numLanes; prevIndex++) {
2044 //std::cout << " con=" << (*(i - prevIndex)).getDescription(this) << " numLanes=" << numLanes << " avgLength=" << lengthSum / numLanes << "\n";
2045 Connection& c = (*(i - prevIndex));
2046 const double minLength = c.customLength != UNSPECIFIED_LOADED_LENGTH ? pow(10, -gPrecision) : POSITION_EPS;
2047 c.length = MAX2(minLength, averageLength ? lengthSum / numLanes : c.shape.length());
2048 if (c.haveVia) {
2049 c.viaLength = MAX2(minLength, c.viaShape.length());
2050 }
2052 if (c.haveVia) {
2053 // split length proportionally
2054 const double a = c.viaLength / (c.shape.length() + c.viaLength);
2055 c.viaLength = MAX2(minLength, a * c.customLength);
2056 }
2057 if (!averageLength) {
2058 c.length = MAX2(minLength, c.customLength - c.viaLength);
2059 }
2060 }
2061 if (c.haveVia) {
2062 // we need to be able to leave from the internal junction by accelerating from 0
2063 maxCross = MAX2(maxCross, sqrt(2. * c.viaLength)); // t = sqrt(2*s/a) and we assume 'a' is at least 1 (default value for tram in SUMOVTypeParameter)
2064 }
2065 // we need to be able to cross the junction in one go but not if we have an indirect left turn
2066 if (c.indirectLeft) {
2067 maxCross = MAX2(maxCross, MAX2(c.length, c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
2068 } else {
2069 maxCross = MAX2(maxCross, (c.length + c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
2070 }
2071 }
2072 return maxCross;
2073}
2074
2075
2076double
2077NBEdge::firstIntersection(const PositionVector& v1, const PositionVector& v2, double width1, double width2, const std::string& error, bool secondIntersection) {
2078 double intersect = std::numeric_limits<double>::max();
2079 if (v2.length() < POSITION_EPS) {
2080 return intersect;
2081 }
2082 try {
2083 PositionVector v1Right = v1;
2084 v1Right.move2side(width1);
2085
2086 PositionVector v1Left = v1;
2087 v1Left.move2side(-width1);
2088
2089 PositionVector v2Right = v2;
2090 v2Right.move2side(width2);
2091
2092 PositionVector v2Left = v2;
2093 v2Left.move2side(-width2);
2094
2095 // intersect all border combinations
2096 bool skip = secondIntersection;
2097 for (double cand : v1Left.intersectsAtLengths2D(v2Right)) {
2098 if (skip) {
2099 skip = false;
2100 continue;
2101 }
2102 intersect = MIN2(intersect, cand);
2103 }
2104 skip = secondIntersection;
2105 for (double cand : v1Left.intersectsAtLengths2D(v2Left)) {
2106 if (skip) {
2107 skip = false;
2108 continue;
2109 }
2110 intersect = MIN2(intersect, cand);
2111 }
2112 skip = secondIntersection;
2113 for (double cand : v1Right.intersectsAtLengths2D(v2Right)) {
2114 if (skip) {
2115 skip = false;
2116 continue;
2117 }
2118 intersect = MIN2(intersect, cand);
2119 }
2120 skip = secondIntersection;
2121 for (double cand : v1Right.intersectsAtLengths2D(v2Left)) {
2122 if (skip) {
2123 skip = false;
2124 continue;
2125 }
2126 intersect = MIN2(intersect, cand);
2127 }
2128 } catch (InvalidArgument&) {
2129 if (error != "") {
2130 WRITE_WARNING(error);
2131 }
2132 }
2133 //std::cout << " v1=" << v1 << " v2Right=" << v2Right << " v2Left=" << v2Left << "\n";
2134 //std::cout << " intersectsRight=" << toString(v1.intersectsAtLengths2D(v2Right)) << "\n";
2135 //std::cout << " intersectsLeft=" << toString(v1.intersectsAtLengths2D(v2Left)) << "\n";
2136 return intersect;
2137}
2138
2139
2140bool
2141NBEdge::bothLeftTurns(LinkDirection dir, const NBEdge* otherFrom, LinkDirection dir2) const {
2142 if (otherFrom == this) {
2143 // not an opposite pair
2144 return false;
2145 }
2146 return (dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT) && (dir2 == LinkDirection::LEFT || dir2 == LinkDirection::PARTLEFT);
2147}
2148
2149bool
2150NBEdge::haveIntersection(const NBNode& n, const PositionVector& shape, const NBEdge* otherFrom, const NBEdge::Connection& otherCon, int numPoints,
2151 double width1, double width2, int shapeFlag) const {
2152 const PositionVector otherShape = n.computeInternalLaneShape(otherFrom, otherCon, numPoints, 0, shapeFlag);
2153 const double minDV = firstIntersection(shape, otherShape, width1, width2);
2154 return minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS;
2155}
2156
2157
2158// -----------
2159int
2160NBEdge::getJunctionPriority(const NBNode* const node) const {
2161 if (node == myFrom) {
2163 } else {
2164 return myToJunctionPriority;
2165 }
2166}
2167
2168
2169void
2170NBEdge::setJunctionPriority(const NBNode* const node, int prio) {
2171 if (node == myFrom) {
2173#ifdef DEBUG_JUNCTIONPRIO
2174 setParameter("fromPrio", toString(prio));
2175#endif
2176 } else {
2177 myToJunctionPriority = prio;
2178#ifdef DEBUG_JUNCTIONPRIO
2179 setParameter("toPrio", toString(prio));
2180#endif
2181 }
2182}
2183
2184
2185double
2186NBEdge::getAngleAtNode(const NBNode* const atNode) const {
2187 if (atNode == myFrom) {
2189 }
2190 assert(atNode == myTo);
2192}
2193
2194
2195double
2196NBEdge::getAngleAtNodeNormalized(const NBNode* const atNode) const {
2197 double res;
2198 if (atNode == myFrom) {
2200 } else {
2201 assert(atNode == myTo);
2203 }
2204 if (res < 0) {
2205 res += 360;
2206 }
2207 return res;
2208}
2209
2210
2211double
2212NBEdge::getAngleAtNodeToCenter(const NBNode* const atNode) const {
2213 if (atNode == myFrom) {
2214 double res = myStartAngle - 180;
2215 if (res < 0) {
2216 res += 360;
2217 }
2218 return res;
2219 } else {
2220 assert(atNode == myTo);
2221 return myEndAngle;
2222 }
2223}
2224
2225
2226void
2228 if (!onlyPossible) {
2230 }
2232}
2233
2234
2235double
2236NBEdge::getLaneSpeed(int lane) const {
2237 return myLanes[lane].speed;
2238}
2239
2240
2241double
2243 return myLanes[lane].friction;
2244}
2245
2246
2247void
2251
2252
2253void
2255 for (Lane& lane : myLanes) {
2256 if (lane.changeLeft != SVCAll) {
2257 lane.changeLeft = ignoring;
2258 }
2259 if (lane.changeRight != SVCAll) {
2260 lane.changeRight = ignoring;
2261 }
2262 }
2263 for (Connection& con : myConnections) {
2264 if (con.changeLeft != SVC_UNSPECIFIED && con.changeLeft != SVCAll) {
2265 con.changeLeft = ignoring;
2266 }
2267 if (con.changeRight != SVC_UNSPECIFIED && con.changeRight != SVCAll) {
2268 con.changeRight = ignoring;
2269 }
2270 }
2271}
2272
2273
2274void
2276 // vissim needs this
2277 if (myFrom == myTo) {
2278 return;
2279 }
2280 // compute lane offset, first
2281 std::vector<double> offsets(myLanes.size(), 0.);
2282 double offset = 0;
2283 for (int i = (int)myLanes.size() - 2; i >= 0; --i) {
2284 offset += (getLaneWidth(i) + getLaneWidth(i + 1)) / 2.;
2285 offsets[i] = offset;
2286 }
2288 double width = 0;
2289 for (int i = 0; i < (int)myLanes.size(); ++i) {
2290 width += getLaneWidth(i);
2291 }
2292 offset = -width / 2. + getLaneWidth((int)myLanes.size() - 1) / 2.;
2293 } else {
2294 double laneWidth = myLanes.back().width != UNSPECIFIED_WIDTH ? myLanes.back().width : SUMO_const_laneWidth;
2295 offset = laneWidth / 2.;
2296 }
2298 for (NBEdge* e : myTo->getOutgoingEdges()) {
2299 if (e->getToNode() == myFrom && getInnerGeometry().reverse() == e->getInnerGeometry()) {
2300 offset += (e->getTotalWidth() - getTotalWidth()) / 2;
2301 break;
2302 }
2303 }
2304 }
2305
2306 for (int i = 0; i < (int)myLanes.size(); ++i) {
2307 offsets[i] += offset;
2308 }
2309
2310 // build the shape of each lane
2311 for (int i = 0; i < (int)myLanes.size(); ++i) {
2312 if (myLanes[i].customShape.size() != 0) {
2313 myLanes[i].shape = myLanes[i].customShape;
2314 continue;
2315 }
2316 try {
2317 myLanes[i].shape = computeLaneShape(i, offsets[i]);
2318 } catch (InvalidArgument& e) {
2319 WRITE_WARNINGF(TL("In lane '%': lane shape could not be determined (%)."), getLaneID(i), e.what());
2320 myLanes[i].shape = myGeom;
2321 }
2322 }
2323}
2324
2325
2327NBEdge::computeLaneShape(int lane, double offset) const {
2328 PositionVector shape = myGeom;
2329 try {
2330 shape.move2side(offset);
2331 } catch (InvalidArgument& e) {
2332 WRITE_WARNINGF(TL("In lane '%': Could not build shape (%)."), getLaneID(lane), e.what());
2333 }
2334 return shape;
2335}
2336
2337
2338void
2340 // taking the angle at the first might be unstable, thus we take the angle
2341 // at a certain distance. (To compare two edges, additional geometry
2342 // segments are considered to resolve ambiguities)
2343 const bool hasFromShape = myFrom->getShape().size() > 0;
2344 const bool hasToShape = myTo->getShape().size() > 0;
2345 Position fromCenter = (hasFromShape ? myFrom->getShape().getCentroid() : myFrom->getPosition());
2346 Position toCenter = (hasToShape ? myTo->getShape().getCentroid() : myTo->getPosition());
2347 PositionVector shape = myGeom;
2348 if ((hasFromShape || hasToShape) && getNumLanes() > 0) {
2350 shape = myLanes[getNumLanes() - 1].shape ;
2351 } else {
2352 shape = myLanes[getNumLanes() / 2].shape;
2353 if (getNumLanes() % 2 == 0) {
2354 // there is no center lane. shift to get the center
2355 shape.move2side(getLaneWidth(getNumLanes() / 2) * 0.5);
2356 }
2357 }
2358 }
2359
2360 // if the junction shape is suspicious we cannot trust the angle to the centroid
2361 const bool suspiciousFromShape = hasFromShape && (myFrom->getShape().distance2D(shape[0]) > 2 * POSITION_EPS
2362 || myFrom->getShape().around(shape[-1])
2363 || !(myFrom->getShape().around(fromCenter)));
2364 const bool suspiciousToShape = hasToShape && (myTo->getShape().distance2D(shape[-1]) > 2 * POSITION_EPS
2365 || myTo->getShape().around(shape[0])
2366 || !(myTo->getShape().around(toCenter)));
2367
2368 const double angleLookahead = MIN2(shape.length2D() / 2, ANGLE_LOOKAHEAD);
2369 const Position referencePosStart = shape.positionAtOffset2D(angleLookahead);
2370 const Position referencePosEnd = shape.positionAtOffset2D(shape.length2D() - angleLookahead);
2371
2372 myStartAngle = GeomHelper::legacyDegree(fromCenter.angleTo2D(referencePosStart), true);
2373 const double myStartAngle2 = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(referencePosStart), true);
2374 const double myStartAngle3 = getAngleAtNode(myFrom);
2375 myEndAngle = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(toCenter), true);
2376 const double myEndAngle2 = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myTo->getPosition()), true);
2377 const double myEndAngle3 = getAngleAtNode(myTo);
2378
2379#ifdef DEBUG_ANGLES
2380 if (DEBUGCOND) {
2381 if (suspiciousFromShape) {
2382 std::cout << "suspiciousFromShape len=" << shape.length() << " startA=" << myStartAngle << " startA2=" << myStartAngle2 << " startA3=" << myStartAngle3
2383 << " rel=" << NBHelpers::normRelAngle(myStartAngle, myStartAngle2)
2384 << " fromCenter=" << fromCenter
2385 << " fromPos=" << myFrom->getPosition()
2386 << " refStart=" << referencePosStart
2387 << "\n";
2388 }
2389 if (suspiciousToShape) {
2390 std::cout << "suspiciousToShape len=" << shape.length() << " endA=" << myEndAngle << " endA2=" << myEndAngle2 << " endA3=" << myEndAngle3
2391 << " rel=" << NBHelpers::normRelAngle(myEndAngle, myEndAngle2)
2392 << " toCenter=" << toCenter
2393 << " toPos=" << myTo->getPosition()
2394 << " refEnd=" << referencePosEnd
2395 << "\n";
2396 }
2397 }
2398#endif
2399
2400 if (suspiciousFromShape && shape.length() > 1) {
2401 myStartAngle = myStartAngle2;
2402 } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myStartAngle, myStartAngle3)) > 90
2403 // don't trust footpath angles
2404 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
2405 myStartAngle = myStartAngle3;
2406 if (myStartAngle < 0) {
2407 myStartAngle += 360;
2408 }
2409 }
2410
2411 if (suspiciousToShape && shape.length() > 1) {
2412 myEndAngle = myEndAngle2;
2413 } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myEndAngle, myEndAngle3)) > 90
2414 // don't trust footpath angles
2415 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
2416 myEndAngle = myEndAngle3;
2417 if (myEndAngle < 0) {
2418 myEndAngle += 360;
2419 }
2420 }
2421
2423#ifdef DEBUG_ANGLES
2424 if (DEBUGCOND) std::cout << "computeAngle edge=" << getID()
2425 << " fromCenter=" << fromCenter << " toCenter=" << toCenter
2426 << " refStart=" << referencePosStart << " refEnd=" << referencePosEnd << " shape=" << shape
2427 << " hasFromShape=" << hasFromShape
2428 << " hasToShape=" << hasToShape
2429 << " numLanes=" << getNumLanes()
2430 << " shapeLane=" << getNumLanes() / 2
2431 << " startA=" << myStartAngle << " endA=" << myEndAngle << " totA=" << myTotalAngle << "\n";
2432#endif
2433}
2434
2435
2436double
2438 const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
2439 const Position referencePosStart = myGeom.positionAtOffset2D(angleLookahead);
2440 return GeomHelper::legacyDegree(myGeom.front().angleTo2D(referencePosStart), true);
2441}
2442
2443
2444double
2446 const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
2447 const Position referencePosEnd = myGeom.positionAtOffset2D(myGeom.length2D() - angleLookahead);
2448 return GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myGeom.back()), true);
2449}
2450
2451
2452bool
2454 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2455 if ((*i).permissions != SVCAll) {
2456 return true;
2457 }
2458 }
2459 return false;
2460}
2461
2462
2463bool
2465 std::vector<Lane>::const_iterator i = myLanes.begin();
2466 SVCPermissions firstLanePermissions = i->permissions;
2467 i++;
2468 for (; i != myLanes.end(); ++i) {
2469 if (i->permissions != firstLanePermissions) {
2470 return true;
2471 }
2472 }
2473 return false;
2474}
2475
2476
2477bool
2479 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2480 if (i->speed != getSpeed()) {
2481 return true;
2482 }
2483 }
2484 return false;
2485}
2486
2487bool
2489 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2490 if (i->friction != myLanes.begin()->friction) {
2491 return true;
2492 }
2493 }
2494 return false;
2495}
2496
2497bool
2499 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2500 if (i->width != myLanes.begin()->width) {
2501 return true;
2502 }
2503 }
2504 return false;
2505}
2506
2507
2508bool
2510 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2511 if (i->type != myLanes.begin()->type) {
2512 return true;
2513 }
2514 }
2515 return false;
2516}
2517
2518
2519bool
2521 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2522 if (i->endOffset != myLanes.begin()->endOffset) {
2523 return true;
2524 }
2525 }
2526 return false;
2527}
2528
2529
2530bool
2532 for (const auto& lane : myLanes) {
2533 if (lane.laneStopOffset.isDefined()) {
2534 if (myEdgeStopOffset.isDefined() || (myEdgeStopOffset != lane.laneStopOffset)) {
2535 return true;
2536 }
2537 }
2538 }
2539 return false;
2540}
2541
2542
2543bool
2545 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2546 if (i->accelRamp) {
2547 return true;
2548 }
2549 }
2550 return false;
2551}
2552
2553
2554bool
2556 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2557 if (i->customShape.size() > 0) {
2558 return true;
2559 }
2560 }
2561 return false;
2562}
2563
2564
2565bool
2567 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2568 if (i->getParametersMap().size() > 0) {
2569 return true;
2570 }
2571 }
2572 return false;
2573}
2574
2575bool
2577 for (const Lane& lane : myLanes) {
2578 if (lane.changeLeft != SVCAll || lane.changeRight != SVCAll) {
2579 return true;
2580 }
2581 }
2582 return false;
2583}
2584
2585bool
2593 || hasAccelLane()
2595 || hasLaneParams()
2597 || (!myLanes.empty() && myLanes.back().oppositeID != ""));
2598}
2599
2600
2601
2602bool
2603NBEdge::computeEdge2Edges(bool noLeftMovers) {
2604#ifdef DEBUG_CONNECTION_GUESSING
2605 if (DEBUGCOND) {
2606 std::cout << "computeEdge2Edges edge=" << getID() << " step=" << (int)myStep << " noLeftMovers=" << noLeftMovers << "\n";
2607 for (Connection& c : myConnections) {
2608 std::cout << " conn " << c.getDescription(this) << "\n";
2609 }
2611 std::cout << " connToDelete " << c.getDescription(this) << "\n";
2612 }
2613 }
2614#endif
2615 // return if this relationship has been build in previous steps or
2616 // during the import
2618 return true;
2619 }
2620 const bool fromRail = isRailway(getPermissions());
2621 for (NBEdge* out : myTo->getOutgoingEdges()) {
2622 if (noLeftMovers && myTo->isLeftMover(this, out)) {
2623 continue;
2624 }
2625 // avoid sharp railway turns
2626 if (fromRail && isRailway(out->getPermissions())) {
2627 const double angle = fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), out->getAngleAtNode(myTo)));
2628 if (angle > 150) {
2629 continue;
2630 } else if (angle > 90) {
2631 // possibly the junction is large enough to achieve a plausible radius:
2632 const PositionVector& fromShape = myLanes.front().shape;
2633 const PositionVector& toShape = out->getLanes().front().shape;
2634 PositionVector shape = myTo->computeSmoothShape(fromShape, toShape, 5, getTurnDestination() == out, 5, 5);
2635 const double radius = shape.length2D() / DEG2RAD(angle);
2636 const double minRadius = (getPermissions() & SVC_TRAM) != 0 ? 20 : 80;
2637 //std::cout << getID() << " to=" << out->getID() << " radius=" << radius << " minRadius=" << minRadius << "\n";
2638 if (radius < minRadius) {
2639 continue;
2640 }
2641 }
2642 }
2643 if (out == myTurnDestination) {
2644 // will be added by appendTurnaround
2645 continue;
2646 }
2647 if ((getPermissions() & out->getPermissions() & ~SVC_PEDESTRIAN) == 0) {
2648 // no common permissions
2649 continue;
2650 }
2651 myConnections.push_back(Connection(-1, out, -1));
2652 }
2654 return true;
2655}
2656
2657
2658bool
2660#ifdef DEBUG_CONNECTION_GUESSING
2661 if (DEBUGCOND) {
2662 std::cout << "computeLanes2Edges edge=" << getID() << " step=" << (int)myStep << "\n";
2663 for (Connection& c : myConnections) {
2664 std::cout << " conn " << c.getDescription(this) << "\n";
2665 }
2667 std::cout << " connToDelete " << c.getDescription(this) << "\n";
2668 }
2669 }
2670#endif
2671 // return if this relationship has been build in previous steps or
2672 // during the import
2675 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
2676 if ((*i).toEdge == nullptr) {
2677 WRITE_WARNINGF("Inconsistent connection definitions at edge '%'.", getID());
2678 i = myConnections.erase(i);
2679 } else {
2680 i++;
2681 }
2682 }
2683 }
2684 return true;
2685 }
2687 // get list of possible outgoing edges sorted by direction clockwise
2688 // the edge in the backward direction (turnaround) is not in the list
2689 const EdgeVector* edges = getConnectedSorted();
2690 if (myConnections.size() != 0 && edges->size() == 0) {
2691 // dead end per definition!?
2692 myConnections.clear();
2693 } else {
2694 // divide the lanes on reachable edges
2695 divideOnEdges(edges);
2696 }
2697 delete edges;
2699 return true;
2700}
2701
2702
2703std::vector<LinkDirection>
2704NBEdge::decodeTurnSigns(int turnSigns, int shift) {
2705 std::vector<LinkDirection> result;
2706 for (int i = 0; i < 8; i++) {
2707 // see LinkDirection in SUMOXMLDefinitions.h
2708 if ((turnSigns & (1 << (i + shift))) != 0) {
2709 result.push_back((LinkDirection)(1 << i));
2710 }
2711 }
2712 return result;
2713}
2714
2715void
2716NBEdge::updateTurnPermissions(SVCPermissions& perm, LinkDirection dir, SVCPermissions spec, std::vector<LinkDirection> dirs) {
2717 if (dirs.size() > 0) {
2718 if (std::find(dirs.begin(), dirs.end(), dir) == dirs.end()) {
2719 perm &= ~spec;
2720 } else {
2721 perm |= spec;
2722 }
2723 }
2724}
2725
2726bool
2728#ifdef DEBUG_TURNSIGNS
2729 std::cout << "applyTurnSigns edge=" << getID() << "\n";
2730#endif
2731 // build a map of target edges and lanes
2732 std::vector<const NBEdge*> targets;
2733 std::map<const NBEdge*, std::vector<int> > toLaneMap;
2734 for (const Connection& c : myConnections) {
2735 if (myLanes[c.fromLane].turnSigns != 0) {
2736 if (std::find(targets.begin(), targets.end(), c.toEdge) == targets.end()) {
2737 targets.push_back(c.toEdge);
2738 }
2739 toLaneMap[c.toEdge].push_back(c.toLane);
2740 }
2741 }
2742 // might be unsorted due to bike lane connections
2743 for (auto& item : toLaneMap) {
2744 std::sort(item.second.begin(), item.second.end());
2745 }
2746
2747 // check number of distinct signed directions and count the number of signs for each direction
2748 std::map<LinkDirection, int> signCons;
2749 int allDirs = 0;
2750 for (const Lane& lane : myLanes) {
2751 allDirs |= lane.turnSigns;
2752 for (LinkDirection dir : decodeTurnSigns(lane.turnSigns)) {
2753 signCons[dir]++;
2754 }
2755 }
2756 allDirs |= allDirs >> TURN_SIGN_SHIFT_BUS;
2757 allDirs |= allDirs >> TURN_SIGN_SHIFT_TAXI;
2758 allDirs |= allDirs >> TURN_SIGN_SHIFT_BICYCLE;
2759
2760 if ((allDirs & (int)LinkDirection::NODIR) != 0) {
2761 targets.push_back(nullptr); // dead end
2762 }
2763
2764 SVCPermissions defaultPermissions = SVC_PASSENGER | SVC_DELIVERY;
2765 // build a mapping from sign directions to targets
2766 std::vector<LinkDirection> signedDirs = decodeTurnSigns(allDirs);
2767 std::map<LinkDirection, const NBEdge*> dirMap;
2768#ifdef DEBUG_TURNSIGNS
2769 std::cout << " numDirs=" << signedDirs.size() << " numTargets=" << targets.size() << "\n";
2770#endif
2771 if (signedDirs.size() > targets.size()) {
2772 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions but only % targets"), getID(), signedDirs.size(), targets.size());
2773 return false;
2774 } else if (signedDirs.size() < targets.size()) {
2775 // we need to drop some targets (i.e. turn-around)
2776 // use sumo-directions as a guide
2777 std::vector<LinkDirection> sumoDirs;
2778 for (const NBEdge* to : targets) {
2779 sumoDirs.push_back(myTo->getDirection(this, to));
2780 }
2781 // remove targets to the left
2782 bool checkMore = true;
2783 while (signedDirs.size() < targets.size() && checkMore) {
2784 checkMore = false;
2785 //std::cout << getID() << " sumoDirs=" << joinToString(sumoDirs, ",") << " signedDirs=" << joinToString(signedDirs, ",") << "\n";
2786 if (sumoDirs.back() != signedDirs.back()) {
2787 targets.pop_back();
2788 sumoDirs.pop_back();
2789 checkMore = true;
2790 }
2791 }
2792 // remove targets to the right
2793 checkMore = true;
2794 while (signedDirs.size() < targets.size() && checkMore) {
2795 checkMore = false;
2796 if (sumoDirs.front() != signedDirs.front()) {
2797 targets.erase(targets.begin());
2798 sumoDirs.erase(sumoDirs.begin());
2799 checkMore = true;
2800 }
2801 }
2802 // remove targets by permissions
2803 int i = 0;
2804 while (signedDirs.size() < targets.size() && i < (int)targets.size()) {
2805 if (targets[i] != nullptr && (targets[i]->getPermissions() & defaultPermissions) == 0) {
2806 targets.erase(targets.begin() + i);
2807 sumoDirs.erase(sumoDirs.begin() + i);
2808 } else {
2809 i++;
2810 }
2811 }
2812 if (signedDirs.size() != targets.size()) {
2813 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions and % targets (after target pruning)"), getID(), signedDirs.size(), targets.size());
2814 return false;
2815 }
2816 }
2817 // directions and connections are both sorted from right to left
2818 for (int i = 0; i < (int)signedDirs.size(); i++) {
2819 dirMap[signedDirs[i]] = targets[i];
2820 }
2821 // check whether we have enough target lanes for a each signed direction
2822 for (auto item : signCons) {
2823 const LinkDirection dir = item.first;
2824 if (dir == LinkDirection::NODIR) {
2825 continue;
2826 }
2827 const NBEdge* to = dirMap[dir];
2828 int candidates = to->getNumLanesThatAllow(defaultPermissions, false);
2829 if (candidates == 0) {
2830 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because the target edge '%' has no suitable lanes"), getID(), to->getID());
2831 return false;
2832 }
2833 std::vector<int>& knownTargets = toLaneMap[to];
2834 if ((int)knownTargets.size() < item.second) {
2835 if (candidates < item.second) {
2836 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed connections with directions '%' but target edge '%' has only % suitable lanes"),
2837 getID(), item.second, toString(dir), to->getID(), candidates);
2838 return false;
2839 }
2840 int i;
2841 int iInc;
2842 int iEnd;
2843 if (dir > LinkDirection::STRAIGHT) {
2844 // set more targets on the left
2845 i = to->getNumLanes() - 1;
2846 iInc = -1;
2847 iEnd = -1;
2848 } else {
2849 // set more targets on the right
2850 i = 0;
2851 iInc = 1;
2852 iEnd = to->getNumLanes();
2853 }
2854 while ((int)knownTargets.size() < item.second && i != iEnd) {
2855 if ((to->getPermissions(i) & defaultPermissions) != 0) {
2856 if (std::find(knownTargets.begin(), knownTargets.end(), i) == knownTargets.end()) {
2857 knownTargets.push_back(i);
2858 }
2859 }
2860 i += iInc;
2861 }
2862 if ((int)knownTargets.size() != item.second) {
2863 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because not enough target lanes could be determined for direction '%'"), getID(), toString(dir));
2864 return false;
2865 }
2866 std::sort(knownTargets.begin(), knownTargets.end());
2867 }
2868 }
2869 std::map<const NBEdge*, int> toLaneIndex;
2870 for (int i = 0; i < getNumLanes(); i++) {
2871 const int turnSigns = myLanes[i].turnSigns;
2872 // no turnSigns are given for bicycle lanes and sidewalks
2873 if (turnSigns != 0) {
2874 // clear existing connections
2875 for (auto it = myConnections.begin(); it != myConnections.end();) {
2876 if (it->fromLane == i) {
2877 it = myConnections.erase(it);
2878 } else {
2879 it++;
2880 }
2881 }
2882 // add new connections
2883 int allSigns = (turnSigns
2884 | turnSigns >> TURN_SIGN_SHIFT_BUS
2885 | turnSigns >> TURN_SIGN_SHIFT_TAXI
2886 | turnSigns >> TURN_SIGN_SHIFT_BICYCLE);
2887 std::vector<LinkDirection> all = decodeTurnSigns(turnSigns);
2888 std::vector<LinkDirection> bus = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BUS);
2889 std::vector<LinkDirection> taxi = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_TAXI);
2890 std::vector<LinkDirection> bike = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BICYCLE);
2891 //std::cout << " allSigns=" << allSigns << " turnSigns=" << turnSigns << " bus=" << bus.size() << "\n";
2892 SVCPermissions fromP = getPermissions(i);
2893 if ((fromP & SVC_PASSENGER) != 0) {
2894 // if the source permits passenger traffic, the target should too
2895 fromP = SVC_PASSENGER;
2896 }
2897 for (LinkDirection dir : decodeTurnSigns(allSigns)) {
2898 SVCPermissions perm = 0;
2899 updateTurnPermissions(perm, dir, SVCAll, all);
2900 updateTurnPermissions(perm, dir, SVC_BUS, bus);
2901 updateTurnPermissions(perm, dir, SVC_TAXI, taxi);
2902 updateTurnPermissions(perm, dir, SVC_BICYCLE, bike);
2903 if (perm == SVCAll) {
2904 perm = SVC_UNSPECIFIED;
2905 }
2906 //std::cout << " lane=" << i << " dir=" << toString(dir) << " perm=" << getVehicleClassNames(perm) << "\n";
2907 NBEdge* to = const_cast<NBEdge*>(dirMap[dir]);
2908 if (to != nullptr) {
2909 if (toLaneIndex.count(to) == 0) {
2910 // initialize to rightmost feasible lane
2911 int toLane = toLaneMap[to][0];
2912 while ((to->getPermissions(toLane) & fromP) == 0 && (toLane + 1 < to->getNumLanes())) {
2913 toLane++;
2914 /*
2915 if (toLane == to->getNumLanes()) {
2916 SOFT_ASSERT(false);
2917 #ifdef DEBUG_TURNSIGNS
2918 std::cout << " could not find passenger lane for target=" << to->getID() << "\n";
2919 #endif
2920 return false;
2921 }
2922 */
2923 }
2924#ifdef DEBUG_TURNSIGNS
2925 std::cout << " target=" << to->getID() << " initial toLane=" << toLane << "\n";
2926#endif
2927 toLaneIndex[to] = toLane;
2928 }
2929#ifdef DEBUG_TURNSIGNS
2930 //std::cout << " set fromLane=" << i << " to=" << to->getID() << " toLane=" << toLaneIndex[to] << "\n";
2931#endif
2932 setConnection(i, to, toLaneIndex[to], Lane2LaneInfoType::VALIDATED, true,
2937 perm);
2938 if (toLaneIndex[to] < to->getNumLanes() - 1
2939 && (to->getPermissions(toLaneIndex[to] + 1) & fromP) != 0) {
2940 toLaneIndex[to]++;
2941 } else if (toLaneIndex[to] < to->getNumLanes() - 2
2942 && (to->getPermissions(toLaneIndex[to] + 2) & fromP) != 0) {
2943 // skip forbidden lane
2944 toLaneIndex[to] += 2;
2945 }
2946 }
2947 }
2948 }
2949 }
2952 return true;
2953}
2954
2955
2956bool
2958#ifdef DEBUG_CONNECTION_GUESSING
2959 if (DEBUGCOND) {
2960 std::cout << "recheckLanes (initial) edge=" << getID() << "\n";
2961 for (Connection& c : myConnections) {
2962 std::cout << " conn " << c.getDescription(this) << "\n";
2963 }
2965 std::cout << " connToDelete " << c.getDescription(this) << "\n";
2966 }
2967 }
2968#endif
2969 // check delayed removals
2970 for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
2971 removeFromConnections(it->toEdge, it->fromLane, it->toLane, false, false, true);
2972 }
2973 std::vector<int> connNumbersPerLane(myLanes.size(), 0);
2974 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
2975 if ((*i).toEdge == nullptr || (*i).fromLane < 0 || (*i).toLane < 0) {
2976 i = myConnections.erase(i);
2977 } else {
2978 if ((*i).fromLane >= 0) {
2979 ++connNumbersPerLane[(*i).fromLane];
2980 }
2981 ++i;
2982 }
2983 }
2985#ifdef DEBUG_TURNSIGNS
2986 if (myLanes.back().turnSigns != 0) {
2987 std::cout << getID() << " hasTurnSigns\n";
2988 if (myTurnSignTarget != myTo->getID()) {
2989 std::cout << " tst=" << myTurnSignTarget << " to=" << myTo->getID() << "\n";
2990 }
2991 }
2992#endif
2993 if (myLanes.back().turnSigns == 0 || myTurnSignTarget != myTo->getID() || !applyTurnSigns()) {
2994 // check #1:
2995 // If there is a lane with no connections and any neighbour lane has
2996 // more than one connections, try to move one of them.
2997 // This check is only done for edges which connections were assigned
2998 // using the standard algorithm.
2999 for (int i = 0; i < (int)myLanes.size(); i++) {
3000 if (connNumbersPerLane[i] == 0 && !isForbidden(getPermissions(i) & ~SVC_PEDESTRIAN)) {
3001 // dead-end lane found
3002 bool hasDeadEnd = true;
3003 // find lane with two connections or more to the right of the current lane
3004 for (int i2 = i - 1; hasDeadEnd && i2 >= 0; i2--) {
3005 if (getPermissions(i) != getPermissions(i2)) {
3006 break;
3007 }
3008 if (connNumbersPerLane[i2] > 1) {
3009 connNumbersPerLane[i2]--;
3010 for (int i3 = i2; i3 != i; i3++) {
3014 }
3015 hasDeadEnd = false;
3016 }
3017 }
3018 if (hasDeadEnd) {
3019 // find lane with two connections or more to the left of the current lane
3020 for (int i2 = i + 1; hasDeadEnd && i2 < getNumLanes(); i2++) {
3021 if (getPermissions(i) != getPermissions(i2)) {
3022 break;
3023 }
3024 if (connNumbersPerLane[i2] > 1) {
3025 connNumbersPerLane[i2]--;
3026 for (int i3 = i2; i3 != i; i3--) {
3030 }
3031 hasDeadEnd = false;
3032 }
3033 }
3034 }
3035 if (hasDeadEnd && myTo->getOutgoingEdges().size() > 1) {
3036 int passengerLanes = 0;
3037 int passengerTargetLanes = 0;
3038 for (const Lane& lane : myLanes) {
3039 if ((lane.permissions & SVC_PASSENGER) != 0) {
3040 passengerLanes++;
3041 }
3042 }
3043 for (const NBEdge* out : myTo->getOutgoingEdges()) {
3044 if (!isTurningDirectionAt(out)) {
3045 for (const Lane& lane : out->getLanes()) {
3046 if ((lane.permissions & SVC_PASSENGER) != 0) {
3047 passengerTargetLanes++;
3048 }
3049 }
3050 }
3051 }
3052 if (passengerLanes > 0 && passengerLanes <= passengerTargetLanes) {
3053 // no need for dead-ends
3054 if (i > 0) {
3055 // check if a connection to the right has a usable target to the left of its target
3056 std::vector<Connection> rightCons = getConnectionsFromLane(i - 1);
3057 if (rightCons.size() > 0) {
3058 const Connection& rc = rightCons.back();
3059 NBEdge* to = rc.toEdge;
3060 int toLane = rc.toLane + 1;
3061 if (toLane < to->getNumLanes()
3062 && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
3063 && !hasConnectionTo(to, toLane)) {
3064#ifdef DEBUG_CONNECTION_CHECKING
3065 std::cout << " recheck1 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
3066#endif
3068 hasDeadEnd = false;
3071 }
3072 if (hasDeadEnd) {
3073 // check if a connection to the right has a usable target to the right of its target
3074 toLane = rc.toLane - 1;
3075 if (toLane >= 0
3076 && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(rc.toLane)) != 0
3077 && (getPermissions(rc.fromLane) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
3078 && !hasConnectionTo(to, toLane)) {
3079 // shift the right lane connection target right and connect the dead lane to the old target
3080 getConnectionRef(rc.fromLane, to, rc.toLane).toLane = toLane;
3081#ifdef DEBUG_CONNECTION_CHECKING
3082 std::cout << " recheck2 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << (toLane + 1) << "\n";
3083#endif
3084 setConnection(i, to, toLane + 1, Lane2LaneInfoType::COMPUTED);
3085 hasDeadEnd = false;
3088 }
3089 }
3090 }
3091 }
3092 if (hasDeadEnd && i < getNumLanes() - 1) {
3093 // check if a connection to the left has a usable target to the right of its target
3094 std::vector<Connection> leftCons = getConnectionsFromLane(i + 1);
3095 if (leftCons.size() > 0) {
3096 NBEdge* to = leftCons.front().toEdge;
3097 int toLane = leftCons.front().toLane - 1;
3098 if (toLane >= 0
3099 && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
3100 && !hasConnectionTo(to, toLane)) {
3101#ifdef DEBUG_CONNECTION_CHECKING
3102 std::cout << " recheck3 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
3103#endif
3105 hasDeadEnd = false;
3108 }
3109 }
3110 }
3111#ifdef ADDITIONAL_WARNINGS
3112 if (hasDeadEnd) {
3113 WRITE_WARNING("Found dead-end lane " + getLaneID(i));
3114 }
3115#endif
3116 }
3117 }
3118 }
3119 }
3121 }
3122 }
3123 // check involuntary dead end at "real" junctions
3124 if (getPermissions() != SVC_PEDESTRIAN) {
3125 if (myConnections.empty() && myTo->getOutgoingEdges().size() > 1 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
3126 WRITE_WARNINGF(TL("Edge '%' is not connected to outgoing edges at junction '%'."), getID(), myTo->getID());
3127 }
3128 const EdgeVector& incoming = myFrom->getIncomingEdges();
3129 if (incoming.size() > 1) {
3130 for (int i = 0; i < (int)myLanes.size(); i++) {
3131 if (getPermissions(i) != 0 && getPermissions(i) != SVC_PEDESTRIAN) {
3132 bool connected = false;
3133 for (std::vector<NBEdge*>::const_iterator in = incoming.begin(); in != incoming.end(); ++in) {
3134 if ((*in)->hasConnectionTo(this, i)) {
3135 connected = true;
3136 break;
3137 }
3138 }
3139 if (!connected) {
3140 WRITE_WARNINGF(TL("Lane '%' is not connected from any incoming edge at junction '%'."), getLaneID(i), myFrom->getID());
3141 }
3142 }
3143 }
3144 }
3145 }
3146 // avoid deadend due to change prohibitions
3147 if (getNumLanes() > 1 && myConnections.size() > 0) {
3148 for (int i = 0; i < (int)myLanes.size(); i++) {
3149 Lane& lane = myLanes[i];
3150 if ((connNumbersPerLane[i] == 0 || ((lane.accelRamp || (i > 0 && myLanes[i - 1].accelRamp && connNumbersPerLane[i - 1] > 0))
3151 && getSuccessors(SVC_PASSENGER).size() > 1))
3153 const bool forbiddenLeft = lane.changeLeft != SVCAll && lane.changeLeft != SVC_IGNORING && lane.changeLeft != SVC_UNSPECIFIED;
3154 const bool forbiddenRight = lane.changeRight != SVCAll && lane.changeRight != SVC_IGNORING && lane.changeRight != SVC_UNSPECIFIED;
3155 if (forbiddenLeft && (i == 0 || forbiddenRight)) {
3157 WRITE_WARNINGF(TL("Ignoring changeLeft prohibition for '%' to avoid dead-end"), getLaneID(i));
3158 } else if (forbiddenRight && (i == getNumLanes() - 1 || (i > 0 && myLanes[i - 1].accelRamp))) {
3160 WRITE_WARNINGF(TL("Ignoring changeRight prohibition for '%' to avoid dead-end"), getLaneID(i));
3161 }
3162 }
3163 }
3164 }
3165#ifdef ADDITIONAL_WARNINGS
3166 // check for connections with bad access permissions
3167 for (const Connection& c : myConnections) {
3168 SVCPermissions fromP = getPermissions(c.fromLane);
3169 SVCPermissions toP = c.toEdge->getPermissions(c.toLane);
3170 if ((fromP & SVC_PASSENGER) != 0
3171 && toP == SVC_BICYCLE) {
3172 bool hasAlternative = false;
3173 for (const Connection& c2 : myConnections) {
3174 if (c.fromLane == c2.fromLane && c.toEdge == c2.toEdge
3175 && (c.toEdge->getPermissions(c2.toLane) & SVC_PASSENGER) != 0) {
3176 hasAlternative = true;
3177 }
3178 }
3179 if (!hasAlternative) {
3180 WRITE_WARNING("Road lane ends on bikeLane for connection " + c.getDescription(this));
3181 }
3182 }
3183 }
3184
3185#endif
3186#ifdef DEBUG_CONNECTION_GUESSING
3187 if (DEBUGCOND) {
3188 std::cout << "recheckLanes (final) edge=" << getID() << "\n";
3189 for (Connection& c : myConnections) {
3190 std::cout << " conn " << c.getDescription(this) << "\n";
3191 }
3192 }
3193#endif
3196 }
3197 return true;
3198}
3199
3200
3201void NBEdge::recheckOpposite(const NBEdgeCont& ec, bool fixOppositeLengths) {
3202 if (getNumLanes() == 0) {
3203 return;
3204 }
3205 const int leftmostLane = getNumLanes() - 1;
3206 // check oppositeID stored in other lanes
3207 for (int i = 0; i < leftmostLane; i++) {
3208 const std::string& oppositeID = getLanes()[i].oppositeID;
3209 NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
3210 if (oppositeID != "" && oppositeID != "-") {
3211 if (getLanes().back().oppositeID == "" && oppEdge != nullptr) {
3212 getLaneStruct(leftmostLane).oppositeID = oppositeID;
3213 WRITE_WARNINGF(TL("Moving opposite lane '%' from invalid lane '%' to lane index %."), oppositeID, getLaneID(i), leftmostLane);
3214 } else {
3215 WRITE_WARNINGF(TL("Removing opposite lane '%' for invalid lane '%'."), oppositeID, getLaneID(i));
3216 }
3217 getLaneStruct(i).oppositeID = "";
3218 }
3219 }
3220 const std::string& oppositeID = getLanes().back().oppositeID;
3221 if (oppositeID != "" && oppositeID != "-") {
3222 NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
3223 if (oppEdge == nullptr) {
3224 WRITE_WARNINGF(TL("Removing unknown opposite lane '%' for edge '%'."), oppositeID, getID());
3225 getLaneStruct(leftmostLane).oppositeID = "";
3226 } else {
3227 if (oppEdge->getFromNode() != getToNode() || oppEdge->getToNode() != getFromNode()) {
3228 WRITE_WARNINGF(TL("Opposite lane '%' does not reverse-connect the same nodes as edge '%'!"), oppositeID, getID());
3230 } else {
3231 if (oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
3232 const std::string oppEdgeLeftmost = oppEdge->getLaneID(oppEdge->getNumLanes() - 1);
3233 WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppositeID, getID(), oppEdgeLeftmost);
3234 getLaneStruct(leftmostLane).oppositeID = oppEdgeLeftmost;
3235 }
3236 NBEdge::Lane& oppLane = oppEdge->getLaneStruct(oppEdge->getNumLanes() - 1);
3237 const std::string leftmostID = getLaneID(leftmostLane);
3238 if (oppLane.oppositeID == "") {
3239 WRITE_WARNINGF(TL("Adapting missing opposite lane '%' for edge '%'."), leftmostID, oppEdge->getID());
3240 oppLane.oppositeID = leftmostID;
3241 } else if (oppLane.oppositeID != leftmostID && oppLane.oppositeID != "-") {
3242 const std::string oppOpp = oppLane.oppositeID.substr(0, oppLane.oppositeID.rfind("_"));
3243 NBEdge* oppOppEdge = ec.retrieve(oppOpp);
3244 if (oppOppEdge == nullptr) {
3245 WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppLane.oppositeID, oppEdge->getID(), leftmostID);
3246 oppLane.oppositeID = leftmostID;
3247 } else {
3248 if (oppEdge->getFromNode() != oppOppEdge->getToNode() || oppEdge->getToNode() != oppOppEdge->getFromNode()) {
3249 WRITE_ERRORF(TL("Opposite edge '%' does not reverse-connect the same nodes as edge '%'!"), oppEdge->getID(), oppOppEdge->getID());
3250 } else {
3251 WRITE_WARNINGF(TL("Adapting inconsistent opposite lanes for edges '%', '%' and '%'."), getID(), oppEdge->getID(), oppOpp);
3252 }
3253 oppLane.oppositeID = leftmostID;
3254 NBEdge::Lane& oppOppLane = oppOppEdge->getLaneStruct(oppOppEdge->getNumLanes() - 1);
3255 if (oppOppLane.oppositeID == oppEdge->getLaneID(oppEdge->getNumLanes() - 1)) {
3256 oppOppLane.oppositeID = "";
3257 }
3258 }
3259 }
3260 if (fabs(oppEdge->getLoadedLength() - getLoadedLength()) > NUMERICAL_EPS) {
3261 if (fixOppositeLengths) {
3262 const double avgLength = 0.5 * (getFinalLength() + oppEdge->getFinalLength());
3263 WRITE_WARNINGF(TL("Averaging edge lengths for lane '%' (length %) and edge '%' (length %)."),
3264 oppositeID, oppEdge->getLoadedLength(), getID(), getLoadedLength());
3265 setLoadedLength(avgLength);
3266 oppEdge->setLoadedLength(avgLength);
3267 } else {
3268 WRITE_ERROR("Opposite lane '" + oppositeID + "' (length " + toString(oppEdge->getLoadedLength()) +
3269 ") differs in length from edge '" + getID() + "' (length " +
3270 toString(getLoadedLength()) + "). Set --opposites.guess.fix-lengths to fix this.");
3272 }
3273 }
3274 }
3275 }
3276 }
3277 // check for matching bidi lane shapes (at least for the simple case of 1-lane edges)
3278 const NBEdge* bidi = getBidiEdge();
3279 if (bidi != nullptr && getNumLanes() == 1 && bidi->getNumLanes() == 1 && getID() < bidi->getID()) {
3281 }
3282 // check for valid offset and speed
3283 const double startOffset = isBidiRail() ? getTurnDestination(true)->getEndOffset() : 0;
3284 int i = 0;
3285 for (const NBEdge::Lane& l : getLanes()) {
3286 if (startOffset + l.endOffset > getLength()) {
3287 WRITE_WARNINGF(TL("Invalid endOffset % at lane '%' with length % (startOffset %)."),
3288 toString(l.endOffset), getLaneID(i), toString(l.shape.length()), toString(startOffset));
3289 } else if (l.speed < 0.) {
3290 WRITE_WARNINGF(TL("Negative allowed speed (%) on lane '%', use --speed.minimum to prevent this."), toString(l.speed), getLaneID(i));
3291 } else if (l.speed == 0.) {
3292 WRITE_WARNINGF(TL("Lane '%' has a maximum allowed speed of 0."), getLaneID(i));
3293 }
3294 i++;
3295 }
3296}
3297
3299 // check restrictions
3300 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
3301 Connection& c = *i;
3303 if (common == SVC_PEDESTRIAN || getPermissions(c.fromLane) == SVC_PEDESTRIAN) {
3304 // these are computed in NBNode::buildWalkingAreas
3305#ifdef DEBUG_CONNECTION_CHECKING
3306 std::cout << " remove pedCon " << c.getDescription(this) << "\n";
3307#endif
3308 i = myConnections.erase(i);
3309 } else if (common == 0) {
3310 // no common permissions.
3311 // try to find a suitable target lane to the right
3312 const int origToLane = c.toLane;
3313 c.toLane = -1; // ignore this connection when calling hasConnectionTo
3314 int toLane = origToLane;
3315 while (toLane > 0
3316 && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
3317 && !hasConnectionTo(c.toEdge, toLane)
3318 ) {
3319 toLane--;
3320 }
3321 if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
3322 && !hasConnectionTo(c.toEdge, toLane)) {
3323 c.toLane = toLane;
3324 ++i;
3325 } else {
3326 // try to find a suitable target lane to the left
3327 toLane = origToLane;
3328 while (toLane < (int)c.toEdge->getNumLanes() - 1
3329 && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
3330 && !hasConnectionTo(c.toEdge, toLane)
3331 ) {
3332 toLane++;
3333 }
3334 if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
3335 && !hasConnectionTo(c.toEdge, toLane)) {
3336 c.toLane = toLane;
3337 ++i;
3338 } else {
3339 // no alternative target found
3340#ifdef DEBUG_CONNECTION_CHECKING
3341 std::cout << " remove " << c.getDescription(this) << " with no alternative target\n";
3342#endif
3343 i = myConnections.erase(i);
3344 }
3345 }
3348 // do not allow sharp rail turns
3349#ifdef DEBUG_CONNECTION_CHECKING
3350 std::cout << " remove " << c.getDescription(this) << " (rail turnaround)\n";
3351#endif
3352 i = myConnections.erase(i);
3353 } else {
3354 ++i;
3355 }
3356 }
3357}
3358
3359void
3361 if (outgoing->size() == 0) {
3362 // we have to do this, because the turnaround may have been added before
3363 myConnections.clear();
3364 return;
3365 }
3366
3367#ifdef DEBUG_CONNECTION_GUESSING
3368 if (DEBUGCOND) {
3369 std::cout << " divideOnEdges " << getID() << " outgoing=" << toString(*outgoing) << "\n";
3370 }
3371#endif
3372
3373 // build connections for miv lanes
3374 std::vector<int> availableLanes;
3375 for (int i = 0; i < (int)myLanes.size(); ++i) {
3376 if ((getPermissions(i) & SVC_PASSENGER) != 0) {
3377 availableLanes.push_back(i);
3378 }
3379 }
3380 if (availableLanes.size() > 0) {
3381 divideSelectedLanesOnEdges(outgoing, availableLanes);
3382 }
3383 // build connections for miscellaneous further modes (more than bike,peds,bus and without passenger)
3384 availableLanes.clear();
3385 for (int i = 0; i < (int)myLanes.size(); ++i) {
3386 const SVCPermissions perms = getPermissions(i);
3387 if ((perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) == 0 || (perms & SVC_PASSENGER) != 0 || isForbidden(perms)) {
3388 continue;
3389 }
3390 availableLanes.push_back(i);
3391 }
3392 if (availableLanes.size() > 0) {
3393 divideSelectedLanesOnEdges(outgoing, availableLanes);
3394 }
3395 // build connections for busses from lanes that were excluded in the previous step
3396 availableLanes.clear();
3397 for (int i = 0; i < (int)myLanes.size(); ++i) {
3398 const SVCPermissions perms = getPermissions(i);
3399 if ((perms & SVC_BUS) == 0 || (perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) != 0 || (perms & SVC_PASSENGER) != 0) {
3400 continue;
3401 }
3402 availableLanes.push_back(i);
3403 }
3404 if (availableLanes.size() > 0) {
3405 divideSelectedLanesOnEdges(outgoing, availableLanes);
3406 }
3407 // build connections for bicycles (possibly combined with pedestrians)
3408 availableLanes.clear();
3409 for (int i = 0; i < (int)myLanes.size(); ++i) {
3410 const SVCPermissions perms = getPermissions(i);
3411 if (perms != SVC_BICYCLE && perms != (SVC_BICYCLE | SVC_PEDESTRIAN)) {
3412 continue;
3413 }
3414 availableLanes.push_back(i);
3415 }
3416 if (availableLanes.size() > 0) {
3417 divideSelectedLanesOnEdges(outgoing, availableLanes);
3418 }
3419 // clean up unassigned fromLanes
3420 bool explicitTurnaround = false;
3421 SVCPermissions turnaroundPermissions = SVC_UNSPECIFIED;
3422 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
3423 if ((*i).fromLane == -1) {
3424 if ((*i).toEdge == myTurnDestination && myTurnDestination != nullptr) {
3425 explicitTurnaround = true;
3426 turnaroundPermissions = (*i).permissions;
3427 }
3428 if ((*i).permissions != SVC_UNSPECIFIED) {
3429 for (Connection& c : myConnections) {
3430 if (c.toLane == -1 && c.toEdge == (*i).toEdge) {
3431 // carry over loaded edge2edge permissions
3432 c.permissions = (*i).permissions;
3433 }
3434 }
3435 }
3436 i = myConnections.erase(i);
3437 } else {
3438 ++i;
3439 }
3440 }
3441 if (explicitTurnaround) {
3443 myConnections.back().permissions = turnaroundPermissions;
3444 }
3446}
3447
3448
3449void
3450NBEdge::divideSelectedLanesOnEdges(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
3451 const std::vector<int>& priorities = prepareEdgePriorities(outgoing, availableLanes);
3452 if (priorities.empty()) {
3453 return;
3454 }
3455#ifdef DEBUG_CONNECTION_GUESSING
3456 if (DEBUGCOND) {
3457 std::cout << "divideSelectedLanesOnEdges " << getID() << " out=" << toString(*outgoing) << " prios=" << toString(priorities) << " avail=" << toString(availableLanes) << "\n";
3458 }
3459#endif
3460 // compute the resulting number of lanes that should be used to reach the following edge
3461 const int numOutgoing = (int)outgoing->size();
3462 std::vector<int> resultingLanesFactor;
3463 resultingLanesFactor.reserve(numOutgoing);
3464 int minResulting = std::numeric_limits<int>::max();
3465 for (int i = 0; i < numOutgoing; i++) {
3466 // res / minResulting will be the number of lanes which are meant to reach the current outgoing edge
3467 const int res = priorities[i] * (int)availableLanes.size();
3468 resultingLanesFactor.push_back(res);
3469 if (minResulting > res && res > 0) {
3470 // prevent minResulting from becoming 0
3471 minResulting = res;
3472 }
3473 }
3474 // compute the number of virtual edges
3475 // a virtual edge is used as a replacement for a real edge from now on
3476 // it shall allow to divide the existing lanes on this structure without
3477 // regarding the structure of outgoing edges
3478 int numVirtual = 0;
3479 // compute the transition from virtual to real edges
3480 EdgeVector transition;
3481 transition.reserve(numOutgoing);
3482 for (int i = 0; i < numOutgoing; i++) {
3483 // tmpNum will be the number of connections from this edge to the next edge
3484 assert(i < (int)resultingLanesFactor.size());
3485 const int tmpNum = (resultingLanesFactor[i] + minResulting - 1) / minResulting; // integer division rounding up
3486 numVirtual += tmpNum;
3487 for (int j = 0; j < tmpNum; j++) {
3488 transition.push_back((*outgoing)[i]);
3489 }
3490 }
3491#ifdef DEBUG_CONNECTION_GUESSING
3492 if (DEBUGCOND) {
3493 std::cout << " minResulting=" << minResulting << " numVirtual=" << numVirtual << " availLanes=" << toString(availableLanes) << " resLanes=" << toString(resultingLanesFactor) << " transition=" << toString(transition) << "\n";
3494 }
3495#endif
3496
3497 // assign lanes to edges
3498 // (conversion from virtual to real edges is done)
3499 ToEdgeConnectionsAdder adder(transition);
3500 Bresenham::compute(&adder, static_cast<int>(availableLanes.size()), numVirtual);
3501 const std::map<NBEdge*, std::vector<int> >& l2eConns = adder.getBuiltConnections();
3502 for (NBEdge* const target : *outgoing) {
3503 assert(l2eConns.find(target) != l2eConns.end());
3504 for (const int j : l2eConns.find(target)->second) {
3505 const int fromIndex = availableLanes[j];
3506 if ((getPermissions(fromIndex) & target->getPermissions()) == 0) {
3507 // exclude connection if fromLane and toEdge have no common permissions
3508 continue;
3509 }
3510 if ((getPermissions(fromIndex) & target->getPermissions()) == SVC_PEDESTRIAN) {
3511 // exclude connection if the only commonly permitted class are pedestrians
3512 // these connections are later built in NBNode::buildWalkingAreas
3513 continue;
3514 }
3515 // avoid building more connections than the edge has viable lanes (earlier
3516 // ones have precedence). This is necessary when running divideSelectedLanesOnEdges more than once.
3517 // @todo To decide which target lanes are still available we need to do a
3518 // preliminary lane-to-lane assignment in regard to permissions (rather than to ordering)
3519 const int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
3520 int targetLanes = target->getNumLanes();
3521 if (target->getPermissions(0) == SVC_PEDESTRIAN) {
3522 --targetLanes;
3523 }
3524 if (numConsToTarget >= targetLanes) {
3525 continue;
3526 }
3527 if (myLanes[fromIndex].connectionsDone) {
3528 // we already have complete information about connections from
3529 // this lane. do not add anything else
3530#ifdef DEBUG_CONNECTION_GUESSING
3531 if (DEBUGCOND) {
3532 std::cout << " connectionsDone from " << getID() << "_" << fromIndex << ": ";
3533 for (const Connection& c : getConnectionsFromLane(fromIndex)) {
3534 std::cout << c.getDescription(this) << ", ";
3535 }
3536 std::cout << "\n";
3537 }
3538#endif
3539 continue;
3540 }
3541 myConnections.push_back(Connection(fromIndex, target, -1));
3542#ifdef DEBUG_CONNECTION_GUESSING
3543 if (DEBUGCOND) {
3544 std::cout << " request connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3545 }
3546#endif
3547 }
3548 }
3549
3550 addStraightConnections(outgoing, availableLanes, priorities);
3551}
3552
3553
3554void
3555NBEdge::addStraightConnections(const EdgeVector* outgoing, const std::vector<int>& availableLanes, const std::vector<int>& priorities) {
3556 // ensure sufficient straight connections for the (highest-priority) straight target
3557 const int numOutgoing = (int) outgoing->size();
3558 NBEdge* target = nullptr;
3559 NBEdge* rightOfTarget = nullptr;
3560 NBEdge* leftOfTarget = nullptr;
3561 int maxPrio = 0;
3562 for (int i = 0; i < numOutgoing; i++) {
3563 if (maxPrio < priorities[i]) {
3564 const LinkDirection dir = myTo->getDirection(this, (*outgoing)[i]);
3565 if (dir == LinkDirection::STRAIGHT) {
3566 maxPrio = priorities[i];
3567 target = (*outgoing)[i];
3568 rightOfTarget = i == 0 ? outgoing->back() : (*outgoing)[i - 1];
3569 leftOfTarget = i + 1 == numOutgoing ? outgoing->front() : (*outgoing)[i + 1];
3570 }
3571 }
3572 }
3573 if (target == nullptr) {
3574 return;
3575 }
3576 int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
3577 int targetLanes = (int)target->getNumLanes();
3578 if (target->getPermissions(0) == SVC_PEDESTRIAN) {
3579 --targetLanes;
3580 }
3581 const int numDesiredConsToTarget = MIN2(targetLanes, (int)availableLanes.size());
3582#ifdef DEBUG_CONNECTION_GUESSING
3583 if (DEBUGCOND) {
3584 std::cout << " checking extra lanes for target=" << target->getID() << " cons=" << numConsToTarget << " desired=" << numDesiredConsToTarget << "\n";
3585 }
3586#endif
3587 std::vector<int>::const_iterator it_avail = availableLanes.begin();
3588 while (numConsToTarget < numDesiredConsToTarget && it_avail != availableLanes.end()) {
3589 const int fromIndex = *it_avail;
3590 if (
3591 // not yet connected
3592 (count_if(myConnections.begin(), myConnections.end(), connections_finder(fromIndex, target, -1)) == 0)
3593 // matching permissions
3594 && ((getPermissions(fromIndex) & target->getPermissions()) != 0)
3595 // more than pedestrians
3596 && ((getPermissions(fromIndex) & target->getPermissions()) != SVC_PEDESTRIAN)
3597 // lane not yet fully defined
3598 && !myLanes[fromIndex].connectionsDone
3599 ) {
3600#ifdef DEBUG_CONNECTION_GUESSING
3601 if (DEBUGCOND) {
3602 std::cout << " candidate from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3603 }
3604#endif
3605 // prevent same-edge conflicts
3606 if (
3607 // no outgoing connections to the right from further left
3608 ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
3609 // no outgoing connections to the left from further right
3610 && (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)) {
3611#ifdef DEBUG_CONNECTION_GUESSING
3612 if (DEBUGCOND) {
3613 std::cout << " request additional connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3614 }
3615#endif
3616 myConnections.push_back(Connection(fromIndex, target, -1));
3617 numConsToTarget++;
3618 } else {
3619#ifdef DEBUG_CONNECTION_GUESSING
3620 if (DEBUGCOND) std::cout
3621 << " fail check1="
3622 << ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
3623 << " check2=" << (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)
3624 << " rightOfTarget=" << rightOfTarget->getID()
3625 << " leftOfTarget=" << leftOfTarget->getID()
3626 << "\n";
3627#endif
3628
3629 }
3630 }
3631 ++it_avail;
3632 }
3633}
3634
3635
3636const std::vector<int>
3637NBEdge::prepareEdgePriorities(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
3638 std::vector<int> priorities;
3639 MainDirections mainDirections(*outgoing, this, myTo, availableLanes);
3640 const int dist = mainDirections.getStraightest();
3641 if (dist == -1) {
3642 return priorities;
3643 }
3644 // copy the priorities first
3645 priorities.reserve(outgoing->size());
3646 for (const NBEdge* const out : *outgoing) {
3647 int prio = NBNode::isTrafficLight(myTo->getType()) ? 0 : out->getJunctionPriority(myTo);
3648 assert((prio + 1) * 2 > 0);
3649 prio = (prio + 1) * 2;
3650 priorities.push_back(prio);
3651 }
3652 // when the right turning direction has not a higher priority, divide
3653 // the importance by 2 due to the possibility to leave the junction
3654 // faster from this lane
3655#ifdef DEBUG_CONNECTION_GUESSING
3656 if (DEBUGCOND) std::cout << " prepareEdgePriorities " << getID()
3657 << " outgoing=" << toString(*outgoing)
3658 << " priorities1=" << toString(priorities)
3659 << " dist=" << dist
3660 << "\n";
3661#endif
3662 if (dist != 0 && !mainDirections.includes(MainDirections::Direction::RIGHTMOST)) {
3663 assert(priorities.size() > 0);
3664 priorities[0] /= 2;
3665#ifdef DEBUG_CONNECTION_GUESSING
3666 if (DEBUGCOND) {
3667 std::cout << " priorities2=" << toString(priorities) << "\n";
3668 }
3669#endif
3670 }
3671 // HEURISTIC:
3672 // when no higher priority exists, let the forward direction be
3673 // the main direction
3674 if (mainDirections.empty()) {
3675 assert(dist < (int)priorities.size());
3676 priorities[dist] *= 2;
3677#ifdef DEBUG_CONNECTION_GUESSING
3678 if (DEBUGCOND) {
3679 std::cout << " priorities3=" << toString(priorities) << "\n";
3680 }
3681#endif
3682 }
3684 priorities[dist] += 1;
3685 } else {
3686 // try to ensure separation of left turns
3688 priorities[0] /= 4;
3689 priorities[(int)priorities.size() - 1] /= 2;
3690#ifdef DEBUG_CONNECTION_GUESSING
3691 if (DEBUGCOND) {
3692 std::cout << " priorities6=" << toString(priorities) << "\n";
3693 }
3694#endif
3695 } else if (mainDirections.includes(MainDirections::Direction::RIGHTMOST)
3696 && outgoing->size() > 2
3697 && availableLanes.size() == 2
3698 && (*outgoing)[dist]->getPriority() == (*outgoing)[0]->getPriority()) {
3699 priorities[0] /= 4;
3700 priorities.back() /= 2;
3701#ifdef DEBUG_CONNECTION_GUESSING
3702 if (DEBUGCOND) {
3703 std::cout << " priorities7=" << toString(priorities) << "\n";
3704 }
3705#endif
3706 }
3707 }
3708 if (mainDirections.includes(MainDirections::Direction::FORWARD)) {
3709 if (myLanes.size() > 2) {
3710 priorities[dist] *= 2;
3711#ifdef DEBUG_CONNECTION_GUESSING
3712 if (DEBUGCOND) {
3713 std::cout << " priorities4=" << toString(priorities) << "\n";
3714 }
3715#endif
3716 } else {
3717 priorities[dist] *= 3;
3718#ifdef DEBUG_CONNECTION_GUESSING
3719 if (DEBUGCOND) {
3720 std::cout << " priorities5=" << toString(priorities) << "\n";
3721 }
3722#endif
3723 }
3724 }
3725 return priorities;
3726}
3727
3728
3729void
3730NBEdge::appendTurnaround(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike, bool checkPermissions) {
3731 // do nothing if no turnaround is known
3733 return;
3734 }
3735 // do nothing if the destination node is controlled by a tls and no turnarounds
3736 // shall be appended for such junctions
3737 if (noTLSControlled && myTo->isTLControlled()) {
3738 return;
3739 }
3740 if (noFringe && myTo->getFringeType() == FringeType::OUTER) {
3741 return;
3742 }
3743 bool isDeadEnd = true;
3744 for (const Connection& c : myConnections) {
3745 if ((c.toEdge->getPermissions(c.toLane)
3746 & getPermissions(c.fromLane)
3747 & SVC_PASSENGER) != 0
3748 || (c.toEdge->getPermissions() & getPermissions()) == getPermissions()) {
3749 isDeadEnd = false;
3750 break;
3751 }
3752 }
3753 if (onlyDeadends && !isDeadEnd) {
3754 return;
3755 }
3756 const int fromLane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
3757 if (onlyTurnlane) {
3758 for (const Connection& c : getConnectionsFromLane(fromLane)) {
3759 LinkDirection dir = myTo->getDirection(this, c.toEdge);
3760 if (dir != LinkDirection::LEFT && dir != LinkDirection::PARTLEFT) {
3761 return;
3762 }
3763 }
3764 }
3766 if (checkPermissions) {
3767 if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == 0) {
3768 // exclude connection if fromLane and toEdge have no common permissions
3769 return;
3770 }
3771 if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == SVC_PEDESTRIAN) {
3772 // exclude connection if the only commonly permitted class are pedestrians
3773 // these connections are later built in NBNode::buildWalkingAreas
3774 return;
3775 }
3776 }
3777 // avoid railway turn-arounds
3780 // except at dead-ends on bidi-edges where they model a reversal in train direction
3781 // @todo #4382: once the network fringe is tagged, it also should not receive turn-arounds)
3782 if (isBidiRail() && isRailDeadEnd()) {
3783 // add a slow connection because direction-reversal implies stopping
3785 return;
3786 } else {
3787 return;
3788 }
3789 };
3790 if (noGeometryLike && !isDeadEnd) {
3791 // ignore paths and service entrances if this edge is for passenger traffic
3792 if (myTo->geometryLike() || ((getPermissions() & SVC_PASSENGER) != 0
3793 && !onlyTurnlane
3794 && myTo->geometryLike(
3797 // make sure the turnDestination has other incoming edges
3799 if (turnIncoming.size() > 1) {
3800 // this edge is always part of incoming
3801 return;
3802 }
3803 }
3804 }
3806}
3807
3808
3809bool
3810NBEdge::isTurningDirectionAt(const NBEdge* const edge) const {
3811 // maybe it was already set as the turning direction
3812 if (edge == myTurnDestination) {
3813 return true;
3814 } else if (myTurnDestination != nullptr) {
3815 // otherwise - it's not if a turning direction exists
3816 return false;
3817 }
3818 return edge == myPossibleTurnDestination;
3819}
3820
3821
3822NBNode*
3823NBEdge::tryGetNodeAtPosition(double pos, double tolerance) const {
3824 // return the from-node when the position is at the begin of the edge
3825 if (pos < tolerance) {
3826 return myFrom;
3827 }
3828 // return the to-node when the position is at the end of the edge
3829 if (pos > myLength - tolerance) {
3830 return myTo;
3831 }
3832 return nullptr;
3833}
3834
3835
3836void
3838 int lanes = e->getNumLanes();
3839 for (int i = 0; i < lanes; i++) {
3840 for (const NBEdge::Connection& el : e->getConnectionsFromLane(i)) {
3841 assert(el.tlID == "");
3842 addLane2LaneConnection(i + laneOff, el.toEdge, el.toLane, Lane2LaneInfoType::COMPUTED);
3843 }
3844 }
3845}
3846
3847
3848bool
3852
3853
3854double
3856 return SUMO_const_laneWidth * (double)myLanes.size();
3857}
3858
3859
3860bool
3861NBEdge::mayBeTLSControlled(int fromLane, NBEdge* toEdge, int toLane) const {
3862 for (const Connection& c : myConnections) {
3863 if (c.fromLane == fromLane && c.toEdge == toEdge && c.toLane == toLane && c.uncontrolled) {
3864 return false;
3865 }
3866 }
3867 return true;
3868}
3869
3870
3871bool
3872NBEdge::setControllingTLInformation(const NBConnection& c, const std::string& tlID) {
3873 const int fromLane = c.getFromLane();
3874 NBEdge* toEdge = c.getTo();
3875 const int toLane = c.getToLane();
3876 const int tlIndex = c.getTLIndex();
3877 const int tlIndex2 = c.getTLIndex2();
3878 // check whether the connection was not set as not to be controled previously
3879 if (!mayBeTLSControlled(fromLane, toEdge, toLane)) {
3880 return false;
3881 }
3882
3883 assert(fromLane < 0 || fromLane < (int) myLanes.size());
3884 // try to use information about the connections if given
3885 if (fromLane >= 0 && toLane >= 0) {
3886 // find the specified connection
3887 std::vector<Connection>::iterator i =
3888 find_if(myConnections.begin(), myConnections.end(), connections_finder(fromLane, toEdge, toLane));
3889 // ok, we have to test this as on the removal of self-loop edges some connections
3890 // will be reassigned
3891 if (i != myConnections.end()) {
3892 // get the connection
3893 Connection& connection = *i;
3894 // set the information about the tl
3895 connection.tlID = tlID;
3896 connection.tlLinkIndex = tlIndex;
3897 connection.tlLinkIndex2 = tlIndex2;
3898 return true;
3899 }
3900 }
3901 // if the original connection was not found, set the information for all
3902 // connections
3903 int no = 0;
3904 bool hadError = false;
3905 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
3906 if ((*i).toEdge != toEdge) {
3907 continue;
3908 }
3909 if (fromLane >= 0 && fromLane != (*i).fromLane) {
3910 continue;
3911 }
3912 if (toLane >= 0 && toLane != (*i).toLane) {
3913 continue;
3914 }
3915 if ((*i).tlID == "") {
3916 (*i).tlID = tlID;
3917 (*i).tlLinkIndex = tlIndex;
3918 (*i).tlLinkIndex2 = tlIndex2;
3919 no++;
3920 } else {
3921 if ((*i).tlID != tlID && (*i).tlLinkIndex == tlIndex) {
3922 WRITE_WARNINGF(TL("The lane '%' on edge '%' already had a traffic light signal."), i->fromLane, getID());
3923 hadError = true;
3924 }
3925 }
3926 }
3927 if (hadError && no == 0) {
3928 WRITE_WARNINGF(TL("Could not set any signal of the tlLogic '%' (unknown group)."), tlID);
3929 }
3930 return true;
3931}
3932
3933
3934void
3936 for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); it++) {
3937 it->tlID = "";
3938 }
3939}
3940
3941
3944 PositionVector ret;
3945 int lane;
3946 if (myFrom == (&n)) {
3947 // outgoing
3949 ret = myLanes[lane].shape;
3950 } else {
3951 // incoming
3953 ret = myLanes[lane].shape.reverse();
3954 }
3955 ret.move2side(getLaneWidth(lane) / 2.);
3956 return ret;
3957}
3958
3959
3962 PositionVector ret;
3963 int lane;
3964 if (myFrom == (&n)) {
3965 // outgoing
3967 ret = myLanes[lane].shape;
3968 } else {
3969 // incoming
3971 ret = myLanes[lane].shape.reverse();
3972 }
3973 ret.move2side(-getLaneWidth(lane) / 2.);
3974 return ret;
3975}
3976
3977
3978bool
3979NBEdge::expandableBy(NBEdge* possContinuation, std::string& reason) const {
3980 // ok, the number of lanes must match
3981 if (myLanes.size() != possContinuation->myLanes.size()) {
3982 reason = "laneNumber";
3983 return false;
3984 }
3985 // do not create self loops
3986 if (myFrom == possContinuation->myTo) {
3987 reason = "loop";
3988 return false;
3989 }
3990 // conserve bidi-rails
3991 if (isBidiRail() != possContinuation->isBidiRail()) {
3992 reason = "bidi-rail";
3993 return false;
3994 }
3995 // also, check whether the connections - if any exit do allow to join
3996 // both edges
3997 // This edge must have a one-to-one connection to the following lanes
3998 switch (myStep) {
4000 break;
4002 break;
4004 // the following edge must be connected
4005 const EdgeVector& conn = getConnectedEdges();
4006 if (find(conn.begin(), conn.end(), possContinuation) == conn.end()) {
4007 reason = "disconnected";
4008 return false;
4009 }
4010 }
4011 break;
4016 // the possible continuation must be connected
4017 if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(possContinuation)) == myConnections.end()) {
4018 reason = "disconnected";
4019 return false;
4020 }
4021 // all lanes must go to the possible continuation
4022 std::vector<int> conns = getConnectionLanes(possContinuation);
4023 const int offset = MAX2(0, getFirstNonPedestrianLaneIndex(NBNode::FORWARD, true));
4024 if (conns.size() < myLanes.size() - offset) {
4025 reason = "some lanes disconnected";
4026 return false;
4027 }
4028 }
4029 break;
4030 default:
4031 break;
4032 }
4033 const double minLength = OptionsCont::getOptions().getFloat("geometry.remove.min-length");
4034 if (minLength > 0 && (possContinuation->getLoadedLength() < minLength || getLoadedLength() < minLength)) {
4035 return true;
4036 }
4037 const double maxJunctionSize = OptionsCont::getOptions().getFloat("geometry.remove.max-junction-size");
4038 if (maxJunctionSize >= 0) {
4039 const double junctionSize = myGeom.back().distanceTo2D(possContinuation->myGeom.front());
4040 if (junctionSize > maxJunctionSize + POSITION_EPS) {
4041 reason = "junction size (" + toString(junctionSize) + ") > max-junction-size (" + toString(maxJunctionSize) + ")";
4042 return false;
4043 }
4044 }
4045 // the priority, too (?)
4046 if (getPriority() != possContinuation->getPriority()) {
4047 reason = "priority";
4048 return false;
4049 }
4050 // the speed allowed
4051 if (mySpeed != possContinuation->mySpeed) {
4052 reason = "speed";
4053 return false;
4054 }
4055 // the routingType
4056 if (myRoutingType != possContinuation->myRoutingType) {
4057 reason = "routingType";
4058 return false;
4059 }
4060 // spreadtype should match or it will look ugly
4061 if (myLaneSpreadFunction != possContinuation->myLaneSpreadFunction) {
4062 reason = "spreadType";
4063 return false;
4064 }
4065 // matching lanes must have identical properties
4066 for (int i = 0; i < (int)myLanes.size(); i++) {
4067 if (myLanes[i].speed != possContinuation->myLanes[i].speed) {
4068 reason = "lane " + toString(i) + " speed";
4069 return false;
4070 } else if (myLanes[i].permissions != possContinuation->myLanes[i].permissions) {
4071 reason = "lane " + toString(i) + " permissions";
4072 return false;
4073 } else if (myLanes[i].changeLeft != possContinuation->myLanes[i].changeLeft || myLanes[i].changeRight != possContinuation->myLanes[i].changeRight) {
4074 reason = "lane " + toString(i) + " change restrictions";
4075 return false;
4076 } else if (myLanes[i].width != possContinuation->myLanes[i].width &&
4077 fabs(myLanes[i].width - possContinuation->myLanes[i].width) > OptionsCont::getOptions().getFloat("geometry.remove.width-tolerance")) {
4078 reason = "lane " + toString(i) + " width";
4079 return false;
4080 }
4081 }
4082 // if given identically osm names
4083 if (!OptionsCont::getOptions().isDefault("output.street-names") && myStreetName != possContinuation->getStreetName()
4084 && ((myStreetName != "" && possContinuation->getStreetName() != "")
4085 // only permit merging a short unnamed road with a longer named road
4086 || (myStreetName != "" && myLength <= possContinuation->getLength())
4087 || (myStreetName == "" && myLength >= possContinuation->getLength()))) {
4088 return false;
4089 }
4090
4091 return true;
4092}
4093
4094
4095void
4097 // append geometry
4098 myGeom.append(e->myGeom);
4099 for (int i = 0; i < (int)myLanes.size(); i++) {
4100 myLanes[i].customShape.append(e->myLanes[i].customShape);
4101 if (myLanes[i].hasParameter(SUMO_PARAM_ORIGID) || e->myLanes[i].hasParameter(SUMO_PARAM_ORIGID)
4102 || OptionsCont::getOptions().getBool("output.original-names")) {
4103 const std::string origID = myLanes[i].getParameter(SUMO_PARAM_ORIGID, getID());
4104 const std::string origID2 = e->myLanes[i].getParameter(SUMO_PARAM_ORIGID, e->getID());
4105 if (origID != origID2) {
4106 myLanes[i].setParameter(SUMO_PARAM_ORIGID, origID + " " + origID2);
4107 }
4108 }
4109 myLanes[i].connectionsDone = e->myLanes[i].connectionsDone;
4110 myLanes[i].turnSigns = e->myLanes[i].turnSigns;
4111 }
4112 if (e->getLength() > myLength) {
4113 // possibly some lane attributes differ (when using option geometry.remove.min-length)
4114 // make sure to use the attributes from the longer edge
4115 for (int i = 0; i < (int)myLanes.size(); i++) {
4116 myLanes[i].width = e->myLanes[i].width;
4117 }
4118 // defined name prevails over undefined name of shorter road
4119 if (myStreetName == "") {
4121 }
4122 }
4123 // recompute length
4124 myLength += e->myLength;
4125 if (myLoadedLength > 0 || e->myLoadedLength > 0) {
4127 }
4128 // copy the connections and the building step if given
4129 myStep = e->myStep;
4135 // set the node
4136 myTo = e->myTo;
4142 }
4143 computeAngle(); // myEndAngle may be different now
4144}
4145
4146
4147void
4148NBEdge::updateRemovedNodes(const std::string& removed) {
4149 std::string result = getParameter(SUMO_PARAM_REMOVED_NODES);
4150 if (!result.empty() && !removed.empty()) {
4151 result += " ";
4152 }
4153 result += removed;
4154 if (!result.empty()) {
4156 }
4157}
4158
4159
4160bool
4162 for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
4163 if ((*i).toEdge == e && (*i).tlID != "") {
4164 return true;
4165 }
4166 }
4167 return false;
4168}
4169
4170
4171NBEdge*
4172NBEdge::getTurnDestination(bool possibleDestination) const {
4173 if (myTurnDestination == nullptr && possibleDestination) {
4175 }
4176 return myTurnDestination;
4177}
4178
4179
4180std::string
4181NBEdge::getLaneID(int lane) const {
4182 return myID + "_" + toString(lane);
4183}
4184
4185
4186bool
4187NBEdge::isNearEnough2BeJoined2(NBEdge* e, double threshold) const {
4188 std::vector<double> distances = myGeom.distances(e->getGeometry());
4189 assert(distances.size() > 0);
4190 return VectorHelper<double>::maxValue(distances) < threshold;
4191}
4192
4193
4194void
4195NBEdge::addLane(int index, bool recomputeShape, bool recomputeConnections, bool shiftIndices) {
4196 assert(index <= (int)myLanes.size());
4197 myLanes.insert(myLanes.begin() + index, Lane(this, ""));
4198 // copy attributes
4199 if (myLanes.size() > 1) {
4200 int templateIndex = index > 0 ? index - 1 : index + 1;
4201 myLanes[index].speed = myLanes[templateIndex].speed;
4202 myLanes[index].friction = myLanes[templateIndex].friction;
4203 myLanes[index].permissions = myLanes[templateIndex].permissions;
4204 myLanes[index].preferred = myLanes[templateIndex].preferred;
4205 myLanes[index].endOffset = myLanes[templateIndex].endOffset;
4206 myLanes[index].width = myLanes[templateIndex].width;
4207 myLanes[index].updateParameters(myLanes[templateIndex].getParametersMap());
4208 }
4209 const EdgeVector& incs = myFrom->getIncomingEdges();
4210 if (recomputeShape) {
4212 }
4213 if (recomputeConnections) {
4214 for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
4215 (*i)->invalidateConnections(true);
4216 }
4218 } else if (shiftIndices) {
4219 // shift outgoing connections above the added lane to the left
4220 for (Connection& c : myConnections) {
4221 if (c.fromLane >= index) {
4222 c.fromLane += 1;
4223 }
4224 }
4225 // shift incoming connections above the added lane to the left
4226 for (NBEdge* inc : myFrom->getIncomingEdges()) {
4227 for (Connection& c : inc->myConnections) {
4228 if (c.toEdge == this && c.toLane >= index) {
4229 c.toLane += 1;
4230 }
4231 }
4232 }
4233 myFrom->shiftTLConnectionLaneIndex(this, +1, index - 1);
4234 myTo->shiftTLConnectionLaneIndex(this, +1, index - 1);
4235 }
4236}
4237
4238void
4240 int newLaneNo = (int)myLanes.size() + by;
4241 while ((int)myLanes.size() < newLaneNo) {
4242 // recompute shapes on last addition
4243 const bool recompute = ((int)myLanes.size() == newLaneNo - 1) && myStep < EdgeBuildingStep::LANES2LANES_USER;
4244 addLane((int)myLanes.size(), recompute, recompute, false);
4245 }
4246}
4247
4248
4249void
4250NBEdge::deleteLane(int index, bool recompute, bool shiftIndices) {
4251 assert(index < (int)myLanes.size());
4252 myLanes.erase(myLanes.begin() + index);
4253 if (recompute) {
4255 const EdgeVector& incs = myFrom->getIncomingEdges();
4256 for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
4257 (*i)->invalidateConnections(true);
4258 }
4260 } else if (shiftIndices) {
4261 removeFromConnections(nullptr, index, -1, false, true);
4262 for (NBEdge* inc : myFrom->getIncomingEdges()) {
4263 inc->removeFromConnections(this, -1, index, false, true);
4264 }
4265 }
4266}
4267
4268
4269void
4271 int newLaneNo = (int) myLanes.size() - by;
4272 assert(newLaneNo > 0);
4273 while ((int)myLanes.size() > newLaneNo) {
4274 // recompute shapes on last removal
4275 const bool recompute = (int)myLanes.size() == newLaneNo + 1 && myStep < EdgeBuildingStep::LANES2LANES_USER;
4276 deleteLane((int)myLanes.size() - 1, recompute, false);
4277 }
4278}
4279
4280
4281void
4286
4287
4288void
4290 if (lane < 0) { // all lanes are meant...
4291 for (int i = 0; i < (int)myLanes.size(); i++) {
4292 allowVehicleClass(i, vclass);
4293 }
4294 } else {
4295 assert(lane < (int)myLanes.size());
4296 myLanes[lane].permissions |= vclass;
4297 }
4298}
4299
4300
4301void
4303 if (lane < 0) { // all lanes are meant...
4304 for (int i = 0; i < (int)myLanes.size(); i++) {
4305 disallowVehicleClass((int) i, vclass);
4306 }
4307 } else {
4308 assert(lane < (int)myLanes.size());
4309 myLanes[lane].permissions &= ~vclass;
4310 }
4311}
4312
4313
4314void
4316 if (lane < 0) { // all lanes are meant...
4317 for (int i = 0; i < (int)myLanes.size(); i++) {
4318 preferVehicleClass(i, vclasses);
4319 }
4320 } else {
4321 assert(lane < (int)myLanes.size());
4322 myLanes[lane].permissions |= vclasses;
4323 myLanes[lane].preferred |= vclasses;
4324 }
4325}
4326
4327
4328void
4329NBEdge::setLaneWidth(int lane, double width) {
4330 if (lane < 0) {
4331 // all lanes are meant...
4332 myLaneWidth = width;
4333 for (int i = 0; i < (int)myLanes.size(); i++) {
4334 // ... do it for each lane
4335 setLaneWidth(i, width);
4336 }
4337 return;
4338 }
4339 assert(lane < (int)myLanes.size());
4340 myLanes[lane].width = width;
4341}
4342
4343void
4344NBEdge::setLaneType(int lane, const std::string& type) {
4345 if (lane < 0) {
4346 for (int i = 0; i < (int)myLanes.size(); i++) {
4347 // ... do it for each lane
4348 setLaneType(i, type);
4349 }
4350 return;
4351 }
4352 assert(lane < (int)myLanes.size());
4353 myLanes[lane].type = type;
4354}
4355
4356
4357double
4358NBEdge::getLaneWidth(int lane) const {
4359 return myLanes[lane].width != UNSPECIFIED_WIDTH
4360 ? myLanes[lane].width
4362}
4363
4364double
4366 const NBNode& node,
4367 const NBEdge::Connection& connection,
4368 const NBEdge::Lane& successor,
4369 bool isVia) const {
4370
4371 if (!isVia && node.isConstantWidthTransition() && getNumLanes() > connection.toEdge->getNumLanes()) {
4372 return getLaneWidth(connection.fromLane);
4373 }
4374
4375 return (isBikepath(getPermissions(connection.fromLane)) && (
4376 getLaneWidth(connection.fromLane) < successor.width || successor.width == UNSPECIFIED_WIDTH)) ?
4377 myLanes[connection.fromLane].width : successor.width; // getLaneWidth(connection.fromLane) never returns -1 (UNSPECIFIED_WIDTH)
4378}
4379
4380double
4382 double result = 0;
4383 for (int i = 0; i < (int)myLanes.size(); i++) {
4384 result += getLaneWidth(i);
4385 }
4386 return result;
4387}
4388
4389double
4390NBEdge::getEndOffset(int lane) const {
4391 return myLanes[lane].endOffset != UNSPECIFIED_OFFSET ? myLanes[lane].endOffset : getEndOffset();
4392}
4393
4394
4395const StopOffset&
4397 return myEdgeStopOffset;
4398}
4399
4400
4401const StopOffset&
4403 if (lane == -1) {
4404 return myEdgeStopOffset;
4405 } else {
4406 return myLanes[lane].laneStopOffset;
4407 }
4408}
4409
4410
4411void
4412NBEdge::setEndOffset(int lane, double offset) {
4413 if (lane < 0) {
4414 // all lanes are meant...
4415 myEndOffset = offset;
4416 for (int i = 0; i < (int)myLanes.size(); i++) {
4417 // ... do it for each lane
4418 setEndOffset(i, offset);
4419 }
4420 return;
4421 }
4422 assert(lane < (int)myLanes.size());
4423 myLanes[lane].endOffset = offset;
4424}
4425
4426
4427bool
4428NBEdge::setEdgeStopOffset(int lane, const StopOffset& offset, bool overwrite) {
4429 if (lane < 0) {
4430 if (!overwrite && myEdgeStopOffset.isDefined()) {
4431 return false;
4432 }
4433 // all lanes are meant...
4434 if (offset.getOffset() < 0) {
4435 // Edge length unknown at parsing time, thus check here.
4436 WRITE_WARNINGF(TL("Ignoring invalid stopOffset for edge '%' (negative offset)."), getID());
4437 return false;
4438 } else {
4439 myEdgeStopOffset = offset;
4440 }
4441 } else if (lane < (int)myLanes.size()) {
4442 if (!myLanes[lane].laneStopOffset.isDefined() || overwrite) {
4443 if (offset.getOffset() < 0) {
4444 // Edge length unknown at parsing time, thus check here.
4445 WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (negative offset)."), getLaneID(lane));
4446 } else {
4447 myLanes[lane].laneStopOffset = offset;
4448 }
4449 }
4450 } else {
4451 WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (invalid lane index)."), toString(lane));
4452 }
4453 return true;
4454}
4455
4456
4457void
4458NBEdge::setSpeed(int lane, double speed) {
4459 if (lane < 0) {
4460 // all lanes are meant...
4461 mySpeed = speed;
4462 for (int i = 0; i < (int)myLanes.size(); i++) {
4463 // ... do it for each lane
4464 setSpeed(i, speed);
4465 }
4466 return;
4467 }
4468 assert(lane < (int)myLanes.size());
4469 myLanes[lane].speed = speed;
4470}
4471
4472
4473void
4474NBEdge::setFriction(int lane, double friction) {
4475 if (lane < 0) {
4476 // all lanes are meant...
4477 myFriction = friction;
4478 for (int i = 0; i < (int)myLanes.size(); i++) {
4479 // ... do it for each lane
4480 setFriction(i, friction);
4481 }
4482 return;
4483 }
4484 assert(lane < (int)myLanes.size());
4485 myLanes[lane].friction = friction;
4486}
4487
4488
4489void
4490NBEdge::setAcceleration(int lane, bool accelRamp) {
4491 assert(lane >= 0);
4492 assert(lane < (int)myLanes.size());
4493 myLanes[lane].accelRamp = accelRamp;
4494}
4495
4496
4497void
4498NBEdge::setLaneShape(int lane, const PositionVector& shape) {
4499 assert(lane >= 0);
4500 assert(lane < (int)myLanes.size());
4501 myLanes[lane].customShape = shape;
4502}
4503
4504
4505void
4507 if (lane < 0) {
4508 for (int i = 0; i < (int)myLanes.size(); i++) {
4509 // ... do it for each lane
4510 setPermissions(permissions, i);
4511 }
4512 } else {
4513 assert(lane < (int)myLanes.size());
4514 myLanes[lane].permissions = permissions;
4515 }
4516}
4517
4518
4519void
4521 if (lane < 0) {
4522 for (int i = 0; i < (int)myLanes.size(); i++) {
4523 // ... do it for each lane
4524 setPreferredVehicleClass(permissions, i);
4525 }
4526 } else {
4527 assert(lane < (int)myLanes.size());
4528 myLanes[lane].preferred = permissions;
4529 }
4530}
4531
4532
4533void
4535 assert(lane >= 0);
4536 assert(lane < (int)myLanes.size());
4537 myLanes[lane].changeLeft = changeLeft;
4538 myLanes[lane].changeRight = changeRight;
4539}
4540
4541
4543NBEdge::getPermissions(int lane) const {
4544 if (lane < 0) {
4545 SVCPermissions result = 0;
4546 for (int i = 0; i < (int)myLanes.size(); i++) {
4547 result |= getPermissions(i);
4548 }
4549 return result;
4550 } else {
4551 assert(lane < (int)myLanes.size());
4552 return myLanes[lane].permissions;
4553 }
4554}
4555
4556
4557void
4559 myLoadedLength = val;
4560}
4561
4562void
4564 myLength = val;
4565}
4566
4567
4568void
4570 for (std::vector<Lane>::iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
4571 (*i).permissions = SVCAll;
4572 (*i).preferred = 0;
4573 }
4574}
4575
4576
4577bool
4579 if (c1.fromLane != c2.fromLane) {
4580 return c1.fromLane < c2.fromLane;
4581 }
4582 if (c1.toEdge != c2.toEdge) {
4583 return false; // do not change ordering among toEdges as this is determined by angle in an earlier step
4584 }
4585 return c1.toLane < c2.toLane;
4586}
4587
4588
4589double
4593 } else {
4595 myLanes.back().shape.back() : myLanes[getNumLanes() / 2].shape.back();
4596 //std::cout << getID() << " signalPos=" << mySignalPosition << " laneEnd=" << laneEnd << " toShape=" << myTo->getShape() << " toBorder=" << myToBorder << "\n";
4597 return mySignalPosition.distanceTo2D(laneEnd);
4598 }
4599}
4600
4601
4602int
4603NBEdge::getFirstNonPedestrianLaneIndex(int direction, bool exclusive) const {
4604 assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4605 const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4606 const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4607 for (int i = start; i != end; i += direction) {
4608 // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
4609 // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
4610 if ((exclusive && myLanes[i].permissions != SVC_PEDESTRIAN && myLanes[i].permissions != 0)
4611 || ((myLanes[i].permissions & SVC_PEDESTRIAN) == 0 && myLanes[i].permissions != 0)) {
4612 return i;
4613 }
4614 }
4615 return -1;
4616}
4617
4618int
4619NBEdge::getFirstNonPedestrianNonBicycleLaneIndex(int direction, bool exclusive) const {
4620 assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4621 const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4622 const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4623 for (int i = start; i != end; i += direction) {
4624 // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
4625 // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
4626 SVCPermissions p = myLanes[i].permissions;
4627 if ((exclusive && p != SVC_PEDESTRIAN && p != SVC_BICYCLE && p != (SVC_PEDESTRIAN | SVC_BICYCLE) && p != 0)
4628 || (p == SVCAll || ((p & (SVC_PEDESTRIAN | SVC_BICYCLE)) == 0 && p != 0))) {
4629 return i;
4630 }
4631 }
4632 return -1;
4633}
4634
4635int
4637 for (int i = 0; i < (int)myLanes.size(); i++) {
4638 if (myLanes[i].permissions == permissions) {
4639 return i;
4640 }
4641 }
4642 return -1;
4643}
4644
4645int
4647 assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4648 const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4649 const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4650 for (int i = start; i != end; i += direction) {
4651 if (myLanes[i].permissions != 0) {
4652 return i;
4653 }
4654 }
4655 return end - direction;
4656}
4657
4658
4659std::set<SVCPermissions>
4660NBEdge::getPermissionVariants(int iStart, int iEnd) const {
4661 std::set<SVCPermissions> result;
4662 if (iStart < 0 || iStart >= getNumLanes() || iEnd > getNumLanes()) {
4663 throw ProcessError("invalid indices iStart " + toString(iStart) + " iEnd " + toString(iEnd) + " for edge with " + toString(getNumLanes()) + " lanes.");
4664 }
4665 for (int i = iStart; i < iEnd; ++i) {
4666 result.insert(getPermissions(i));
4667 }
4668 return result;
4669}
4670
4671int
4672NBEdge::getNumLanesThatAllow(SVCPermissions permissions, bool allPermissions) const {
4673 int result = 0;
4674 for (const Lane& lane : myLanes) {
4675 if ((allPermissions && (lane.permissions & permissions) == permissions)
4676 || (!allPermissions && (lane.permissions & permissions) != 0)) {
4677 result++;
4678 }
4679 }
4680 return result;
4681}
4682
4683bool
4685 assert(lane >= 0 && lane < getNumLanes());
4686 return myLanes[lane].changeLeft == SVC_UNSPECIFIED ? true : (myLanes[lane].changeLeft & vclass) == vclass;
4687}
4688
4689bool
4691 assert(lane >= 0 && lane < getNumLanes());
4692 return myLanes[lane].changeRight == SVC_UNSPECIFIED ? true : (myLanes[lane].changeRight & vclass) == vclass;
4693}
4694
4695double
4697 double angle = getAngleAtNode(node) + (getFromNode() == node ? 180.0 : 0.0);
4698 if (angle < 0) {
4699 angle += 360.0;
4700 }
4701 if (angle >= 360) {
4702 angle -= 360.0;
4703 }
4704 if (gDebugFlag1) {
4705 std::cout << getID() << " angle=" << getAngleAtNode(node) << " convAngle=" << angle << "\n";
4706 }
4707 return angle;
4708}
4709
4710
4713 int index = getFirstNonPedestrianLaneIndex(direction);
4714 if (index < 0) {
4715 throw ProcessError(TLF("Edge % allows pedestrians on all lanes", getID()));
4716 }
4717 return myLanes[index];
4718}
4719
4720std::string
4722 // see IntermodalEdge::getSidewalk()
4723 for (int i = 0; i < (int)myLanes.size(); i++) {
4724 if (myLanes[i].permissions == SVC_PEDESTRIAN) {
4725 return getLaneID(i);
4726 }
4727 }
4728 for (int i = 0; i < (int)myLanes.size(); i++) {
4729 if ((myLanes[i].permissions & SVC_PEDESTRIAN) != 0) {
4730 return getLaneID(i);
4731 }
4732 }
4733 return getLaneID(0);
4734}
4735
4736void
4737NBEdge::addSidewalk(double width) {
4739}
4740
4741
4742void
4743NBEdge::restoreSidewalk(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4744 restoreRestrictedLane(SVC_PEDESTRIAN, oldLanes, oldGeometry, oldConnections);
4745}
4746
4747
4748void
4749NBEdge::addBikeLane(double width) {
4751}
4752
4753
4754void
4755NBEdge::restoreBikelane(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4756 restoreRestrictedLane(SVC_BICYCLE, oldLanes, oldGeometry, oldConnections);
4757}
4758
4759bool
4761 for (const Lane& lane : myLanes) {
4762 if (lane.permissions == vclass) {
4763 return true;
4764 }
4765 }
4766 return false;
4767}
4768
4769
4770void
4772 if (hasRestrictedLane(vclass)) {
4773 WRITE_WARNINGF(TL("Edge '%' already has a dedicated lane for %s. Not adding another one."), getID(), toString(vclass));
4774 return;
4775 }
4777 myGeom.move2side(width / 2);
4778 }
4779 // disallow the designated vclass on all "old" lanes
4780 disallowVehicleClass(-1, vclass);
4781 // don't create a restricted vehicle lane to the right of a sidewalk
4782 const int newIndex = (vclass != SVC_PEDESTRIAN && myLanes[0].permissions == SVC_PEDESTRIAN) ? 1 : 0;
4783 if (newIndex == 0) {
4784 // disallow pedestrians on all "higher" lanes to ensure that sidewalk remains the rightmost lane
4786 }
4787 // add new lane
4788 myLanes.insert(myLanes.begin() + newIndex, Lane(this, myLanes[0].getParameter(SUMO_PARAM_ORIGID)));
4789 myLanes[newIndex].permissions = vclass;
4790 myLanes[newIndex].width = fabs(width);
4791 // shift outgoing connections to the left
4792 for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
4793 Connection& c = *it;
4794 if (c.fromLane >= newIndex) {
4795 c.fromLane += 1;
4796 }
4797 }
4798 // shift incoming connections to the left
4799 const EdgeVector& incoming = myFrom->getIncomingEdges();
4800 for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4801 (*it)->shiftToLanesToEdge(this, 1);
4802 }
4806}
4807
4808
4809void
4810NBEdge::restoreRestrictedLane(SUMOVehicleClass vclass, std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4811 // check that previously lane was transformed
4812 if (myLanes[0].permissions != vclass) {
4813 WRITE_WARNINGF(TL("Edge '%' doesn't have a dedicated lane for %s. Cannot be restored."), getID(), toString(vclass));
4814 return;
4815 }
4816 // restore old values
4817 myGeom = oldGeometry;
4818 myLanes = oldLanes;
4819 myConnections = oldConnections;
4820 // shift incoming connections to the right
4821 const EdgeVector& incoming = myFrom->getIncomingEdges();
4822 for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4823 (*it)->shiftToLanesToEdge(this, 0);
4824 }
4825 // Shift TL conections
4829}
4830
4831
4832void
4835 for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
4836 if ((*it).toEdge == to && (*it).toLane >= 0) {
4837 (*it).toLane += laneOff;
4838 }
4839 }
4840}
4841
4842
4843bool
4847 && !isRailway(other->getPermissions())
4848 && getBidiEdge() == nullptr) {
4849 const int i = (node == myTo ? -1 : 0);
4850 const int i2 = (node == myTo ? 0 : -1);
4851 const double dist = myGeom[i].distanceTo2D(node->getPosition());
4852 const double neededOffset = getTotalWidth() / 2;
4853 const double dist2 = MIN2(myGeom.distance2D(other->getGeometry()[i2]),
4854 other->getGeometry().distance2D(myGeom[i]));
4855 const double neededOffset2 = neededOffset + (other->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
4856 ? (other->getTotalWidth()) / 2 : 0);
4857 const double missing = neededOffset - dist;
4858 const double missing2 = neededOffset2 - dist2;
4859 double shift = 0;
4860 if (missing > 0 && missing2 > 0) {
4861 shift = MIN2(missing, missing2);
4862 } else if (missing2) {
4863 shift = missing2;
4864 }
4865 if (shift > 0) {
4866 PositionVector tmp = myGeom;
4867 try {
4868 tmp.move2side(shift);
4869 tmp[i].round(gPrecision);
4870 myGeom[i] = tmp[i];
4871 computeAngle();
4872 return true;
4873 //std::cout << getID() << " shiftPositionAtNode needed=" << neededOffset << " dist=" << dist << " needed2=" << neededOffset2 << " dist2=" << dist2 << " by=" << (neededOffset - dist) << " other=" << other->getID() << "\n";
4874 } catch (InvalidArgument&) {
4875 WRITE_WARNINGF(TL("Could not avoid overlapping shape at node '%' for edge '%'."), node->getID(), getID());
4876 }
4877 }
4878 }
4879 return false;
4880}
4881
4882
4885 if (myLoadedLength > 0) {
4887 } else {
4888 return myGeom.positionAtOffset(offset);
4889 }
4890}
4891
4892
4893double
4895 double result = getLoadedLength();
4896 if (OptionsCont::getOptions().getBool("no-internal-links") && !hasLoadedLength()) {
4897 // use length to junction center even if a modified geometry was given
4899 geom.push_back_noDoublePos(getToNode()->getCenter());
4900 geom.push_front_noDoublePos(getFromNode()->getCenter());
4901 result = geom.length();
4902 }
4903 double avgEndOffset = 0;
4904 for (const Lane& lane : myLanes) {
4905 avgEndOffset += lane.endOffset;
4906 }
4907 if (isBidiRail()) {
4908 avgEndOffset += myPossibleTurnDestination->getEndOffset();
4909 }
4910 avgEndOffset /= (double)myLanes.size();
4911 return MAX2(result - avgEndOffset, POSITION_EPS);
4912}
4913
4914
4915void
4916NBEdge::setOrigID(const std::string origID, const bool append, const int laneIdx) {
4917 if (laneIdx == -1) {
4918 for (int i = 0; i < (int)myLanes.size(); i++) {
4919 setOrigID(origID, append, i);
4920 }
4921 } else {
4922 if (origID != "") {
4923 if (append) {
4924 std::vector<std::string> oldIDs = StringTokenizer(myLanes[laneIdx].getParameter(SUMO_PARAM_ORIGID)).getVector();
4925 if (std::find(oldIDs.begin(), oldIDs.end(), origID) == oldIDs.end()) {
4926 oldIDs.push_back(origID);
4927 }
4928 myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, toString(oldIDs));
4929 } else {
4930 myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, origID);
4931 }
4932 } else {
4933 // do not record empty origID parameter
4934 myLanes[laneIdx].unsetParameter(SUMO_PARAM_ORIGID);
4935 }
4936 }
4937}
4938
4939
4940const EdgeVector&
4942 // @todo cache successors instead of recomputing them every time
4943 mySuccessors.clear();
4944 //std::cout << "getSuccessors edge=" << getID() << " svc=" << toString(vClass) << " cons=" << myConnections.size() << "\n";
4945 for (const Connection& con : myConnections) {
4946 if (con.fromLane >= 0 && con.toLane >= 0 && con.toEdge != nullptr &&
4947 (vClass == SVC_IGNORING || (getPermissions(con.fromLane)
4948 & con.toEdge->getPermissions(con.toLane) & vClass) != 0)
4949 && std::find(mySuccessors.begin(), mySuccessors.end(), con.toEdge) == mySuccessors.end()) {
4950 mySuccessors.push_back(con.toEdge);
4951 //std::cout << " succ=" << con.toEdge->getID() << "\n";
4952 }
4953 }
4954 return mySuccessors;
4955}
4956
4957
4959NBEdge::getViaSuccessors(SUMOVehicleClass vClass, bool /*ignoreTransientPermissions*/) const {
4960 // @todo cache successors instead of recomputing them every time
4961 myViaSuccessors.clear();
4962 for (const Connection& con : myConnections) {
4963 std::pair<const NBEdge*, const Connection*> pair(con.toEdge, nullptr);
4964 // special case for Persons in Netedit
4965 if (vClass == SVC_PEDESTRIAN) {
4966 myViaSuccessors.push_back(pair); // Pedestrians have complete freedom of movement in all sucessors
4967 } else if ((con.fromLane >= 0) && (con.toLane >= 0) &&
4968 (con.toEdge != nullptr) &&
4969 ((getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane) & vClass) == vClass)) {
4970 // ignore duplicates
4971 if (con.getLength() > 0) {
4972 pair.second = &con;
4973 }
4974 myViaSuccessors.push_back(pair);
4975 }
4976 }
4977 return myViaSuccessors;
4978}
4979
4980
4981void
4982NBEdge::debugPrintConnections(bool outgoing, bool incoming) const {
4983 if (outgoing) {
4984 for (const Connection& c : myConnections) {
4985 std::cout << " " << getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
4986 }
4987 }
4988 if (incoming) {
4989 for (NBEdge* inc : myFrom->getIncomingEdges()) {
4990 for (Connection& c : inc->myConnections) {
4991 if (c.toEdge == this) {
4992 std::cout << " " << inc->getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
4993 }
4994 }
4995 }
4996 }
4997}
4998
4999
5000int
5001NBEdge::getLaneIndexFromLaneID(const std::string laneID) {
5002 return StringUtils::toInt(laneID.substr(laneID.rfind("_") + 1));
5003}
5004
5005bool
5007 bool haveJoined = false;
5008 int i = 0;
5009 while (i < getNumLanes() - 1) {
5010 if ((getPermissions(i) == perms) && (getPermissions(i + 1) == perms)) {
5011 const double newWidth = getLaneWidth(i) + getLaneWidth(i + 1);
5012 const std::string newType = myLanes[i].type + "|" + myLanes[i + 1].type;
5013 deleteLane(i, false, true);
5014 setLaneWidth(i, newWidth);
5015 setLaneType(i, newType);
5016 haveJoined = true;
5017 } else {
5018 i++;
5019 }
5020 }
5021 return haveJoined;
5022}
5023
5024
5027 EdgeVector result;
5028 for (NBEdge* edge : edges) {
5029 if ((edge->getPermissions() & permissions) != 0) {
5030 result.push_back(edge);
5031 }
5032 }
5033 return result;
5034}
5035
5036NBEdge*
5038 EdgeVector cands = filterByPermissions(myTo->getOutgoingEdges(), permissions);
5039 if (cands.size() == 0) {
5040 return nullptr;
5041 }
5042 sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this));
5043 NBEdge* best = cands.front();
5044 if (isTurningDirectionAt(best)) {
5045 return nullptr;
5046 } else {
5047 return best;
5048 }
5049}
5050
5051NBEdge*
5053 EdgeVector cands = filterByPermissions(myFrom->getIncomingEdges(), permissions);
5054 if (cands.size() == 0) {
5055 return nullptr;
5056 }
5057 sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this, false));
5058 NBEdge* best = cands.front();
5059 if (best->isTurningDirectionAt(this)) {
5060 return nullptr;
5061 } else {
5062 return best;
5063 }
5064}
5065
5066
5067NBEdge*
5069 NBEdge* opposite = nullptr;
5070 if (getNumLanes() > 0) {
5071 NBEdge::Lane& lastLane = myLanes.back();
5072 const double lastWidth = getLaneWidth(getNumLanes() - 1);
5073 if (lastLane.oppositeID == "" || reguess) {
5074 for (NBEdge* cand : getToNode()->getOutgoingEdges()) {
5075 if (cand->getToNode() == getFromNode() && !cand->getLanes().empty()) {
5076 const NBEdge::Lane& candLastLane = cand->getLanes().back();
5077 if (candLastLane.oppositeID == "" || candLastLane.oppositeID == getLaneID(getNumLanes() - 1)) {
5078 const double lastWidthCand = cand->getLaneWidth(cand->getNumLanes() - 1);
5079 // in sharp corners, the difference may be higher
5080 // factor (sqrt(2) for 90 degree corners
5081 const double threshold = 1.42 * 0.5 * (lastWidth + lastWidthCand) + 0.5;
5082 const double distance = VectorHelper<double>::maxValue(lastLane.shape.distances(cand->getLanes().back().shape));
5083 //std::cout << " distance=" << distance << " threshold=" << threshold << " distances=" << toString(lastLane.shape.distances(cand->getLanes().back().shape)) << "\n";
5084 if (distance < threshold) {
5085 opposite = cand;
5086 }
5087 }
5088 }
5089 }
5090 if (opposite != nullptr) {
5091 lastLane.oppositeID = opposite->getLaneID(opposite->getNumLanes() - 1);
5092 }
5093 }
5094 }
5095 return opposite;
5096}
5097
5098double
5099NBEdge::getDistancAt(double pos) const {
5100 // negative values of myDistances indicate descending kilometrage
5101 return fabs(myDistance + pos);
5102}
5103
5104/****************************************************************************/
#define DEG2RAD(x)
Definition GeomHelper.h:35
#define RAD2DEG(x)
Definition GeomHelper.h:36
#define DEBUGCOND(PED)
#define DEBUGCOND2(LANE)
std::vector< std::string > & split(const std::string &s, char delim, std::vector< std::string > &elems)
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:287
#define WRITE_MESSAGEF(...)
Definition MsgHandler.h:289
#define WRITE_ERRORF(...)
Definition MsgHandler.h:296
#define WRITE_ERROR(msg)
Definition MsgHandler.h:295
#define WRITE_WARNING(msg)
Definition MsgHandler.h:286
#define TL(string)
Definition MsgHandler.h:304
#define TLF(string,...)
Definition MsgHandler.h:306
std::vector< std::pair< const NBRouterEdge *, const NBRouterEdge * > > ConstRouterEdgePairVector
Definition NBCont.h:46
std::vector< NBEdge * > EdgeVector
container for (sorted) edges
Definition NBCont.h:42
KeepClear
keepClear status of connections
Definition NBCont.h:58
@ KEEPCLEAR_UNSPECIFIED
Definition NBCont.h:61
const SVCPermissions SVCAll
all VClasses are allowed
bool isRailway(SVCPermissions permissions)
Returns whether an edge with the given permissions is a (exclusive) railway edge.
const SVCPermissions SVC_UNSPECIFIED
permissions not specified
const std::string & getVehicleClassNames(SVCPermissions permissions, bool expand)
Returns the ids of the given classes, divided using a ' '.
bool isForbidden(SVCPermissions permissions)
Returns whether an edge with the given permissions is a forbidden edge.
bool isBikepath(SVCPermissions permissions)
Returns whether an edge with the given permissions is a bicycle edge.
long long int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
SUMOVehicleClass
Definition of vehicle classes to differ between different lane usage and authority types.
@ SVC_IGNORING
vehicles ignoring classes
@ SVC_RAIL_CLASSES
classes which drive on tracks
@ SVC_PASSENGER
vehicle is a passenger car (a "normal" car)
@ SVC_BICYCLE
vehicle is a bicycle
@ SVC_DELIVERY
vehicle is a small delivery vehicle
@ SVC_TRAM
vehicle is a light rail
@ SVC_TAXI
vehicle is a taxi
@ SVC_BUS
vehicle is a bus
@ SVC_PEDESTRIAN
pedestrian
@ RIGHT
At the rightmost side of the lane.
const std::string SUMO_PARAM_ORIGID
LaneSpreadFunction
Numbers representing special SUMO-XML-attribute values Information how the edge's lateral offset shal...
const std::string SUMO_PARAM_REMOVED_NODES
LinkDirection
The different directions a link between two lanes may take (or a stream between two edges)....
@ PARTLEFT
The link is a partial left direction.
@ RIGHT
The link is a (hard) right direction.
@ TURN
The link is a 180 degree turn.
@ LEFT
The link is a (hard) left direction.
@ STRAIGHT
The link is a straight direction.
@ PARTRIGHT
The link is a partial right direction.
@ NODIR
The link has no direction (is a dead end link)
double roundDecimalToEven(double x, int precision)
round to the given number of decimal digits (bankers rounding)
Definition StdDefs.cpp:67
int gPrecision
the precision for floating point outputs
Definition StdDefs.cpp:27
bool gDebugFlag1
global utility flags for debugging
Definition StdDefs.cpp:44
const double SUMO_const_laneWidth
Definition StdDefs.h:52
T MIN2(T a, T b)
Definition StdDefs.h:80
const double SUMO_const_haltingSpeed
the speed threshold at which vehicles are considered as halting
Definition StdDefs.h:62
T MAX2(T a, T b)
Definition StdDefs.h:86
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition ToString.h:314
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:49
static void compute(BresenhamCallBack *callBack, const int val1, const int val2)
Definition Bresenham.cpp:32
static const double INVALID_OFFSET
a value to signify offsets outside the range of [0, Line.length()]
Definition GeomHelper.h:50
static double legacyDegree(const double angle, const bool positive=false)
static double angleDiff(const double angle1, const double angle2)
Returns the difference of the second angle to the first angle in radiants.
int getFromLane() const
returns the from-lane
int getTLIndex2() const
int getTLIndex() const
returns the index within the controlling tls or InvalidTLIndex if this link is unontrolled
void shiftLaneIndex(NBEdge *edge, int offset, int threshold=-1)
patches lane indices refering to the given edge and above the threshold by the given offset
int getToLane() const
returns the to-lane
NBEdge * getTo() const
returns the to-edge (end of the connection)
Holds (- relative to the edge it is build from -!!!) the list of main directions a vehicle that drive...
Definition NBEdge.h:1631
bool empty() const
returns the information whether no following street has a higher priority
Definition NBEdge.cpp:230
bool includes(Direction d) const
returns the information whether the street in the given direction has a higher priority
Definition NBEdge.cpp:236
int getStraightest() const
returns the index of the straightmost among the given outgoing edges
Definition NBEdge.h:1648
MainDirections(const EdgeVector &outgoing, NBEdge *parent, NBNode *to, const std::vector< int > &availableLanes)
constructor
Definition NBEdge.cpp:173
std::vector< Direction > myDirs
list of the main direction within the following junction relative to the edge
Definition NBEdge.h:1663
~MainDirections()
destructor
Definition NBEdge.cpp:226
int myStraightest
the index of the straightmost among the given outgoing edges
Definition NBEdge.h:1660
Direction
enum of possible directions
Definition NBEdge.h:1634
A class that being a bresenham-callback assigns the incoming lanes to the edges.
Definition NBEdge.h:1591
const std::map< NBEdge *, std::vector< int > > & getBuiltConnections() const
get built connections
Definition NBEdge.h:1611
void execute(const int lane, const int virtEdge)
executes a bresenham - step
Definition NBEdge.cpp:142
Class to sort edges by their angle.
Definition NBEdge.h:2007
int operator()(const Connection &c1, const Connection &c2) const
comparing operation
Definition NBEdge.cpp:245
Storage for edges, including some functionality operating on multiple edges.
Definition NBEdgeCont.h:59
NBEdge * retrieve(const std::string &id, bool retrieveExtracted=false) const
Returns the edge that has the given id.
The representation of a single edge during network building.
Definition NBEdge.h:92
void reinit(NBNode *from, NBNode *to, const std::string &type, double speed, double friction, int nolanes, int priority, PositionVector geom, double width, double endOffset, const std::string &streetName, LaneSpreadFunction spread, bool tryIgnoreNodePositions=false)
Resets initial values.
Definition NBEdge.cpp:386
void updateRemovedNodes(const std::string &removed)
update parameter with removed nodes
Definition NBEdge.cpp:4148
void addGeometryPoint(int index, const Position &p)
Adds a further geometry point.
Definition NBEdge.cpp:1027
static std::vector< LinkDirection > decodeTurnSigns(int turnSigns, int shift=0)
decode bitset
Definition NBEdge.cpp:2704
void mirrorX()
mirror coordinates along the x-axis
Definition NBEdge.cpp:593
void setPreferredVehicleClass(SVCPermissions permissions, int lane=-1)
set preferred Vehicle Class
Definition NBEdge.cpp:4520
static const int TURN_SIGN_SHIFT_BUS
shift values for decoding turn signs
Definition NBEdge.h:379
double getLaneSpeed(int lane) const
get lane speed
Definition NBEdge.cpp:2236
static const int TURN_SIGN_SHIFT_BICYCLE
Definition NBEdge.h:381
NBEdge * guessOpposite(bool reguess=false)
set oppositeID and return opposite edge if found
Definition NBEdge.cpp:5068
void setPermittedChanging(int lane, SVCPermissions changeLeft, SVCPermissions changeRight)
set allowed classes for changing to the left and right from the given lane
Definition NBEdge.cpp:4534
double getLength() const
Returns the computed length of the edge.
Definition NBEdge.h:599
double myLaneWidth
This width of this edge's lanes.
Definition NBEdge.h:1826
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition NBEdge.cpp:4543
std::vector< Connection > myConnectionsToDelete
List of connections marked for delayed removal.
Definition NBEdge.h:1796
const EdgeVector * getConnectedSorted()
Returns the list of outgoing edges without the turnaround sorted in clockwise direction.
Definition NBEdge.cpp:1367
double getDistancAt(double pos) const
get distance at the given offset
Definition NBEdge.cpp:5099
double myEndOffset
This edges's offset to the intersection begin (will be applied to all lanes)
Definition NBEdge.h:1817
int myToJunctionPriority
The priority normalised for the node the edge is incoming in.
Definition NBEdge.h:1808
void setPermissions(SVCPermissions permissions, int lane=-1)
set allowed/disallowed classes for the given lane or for all lanes if -1 is given
Definition NBEdge.cpp:4506
StopOffset myEdgeStopOffset
A vClass specific stop offset - assumed of length 0 (unspecified) or 1. For the latter case the int i...
Definition NBEdge.h:1823
@ ROUNDABOUT
Definition NBEdge.h:387
double getLoadedLength() const
Returns the length was set explicitly or the computed length if it wasn't set.
Definition NBEdge.h:608
double getCrossingAngle(NBNode *node)
return the angle for computing pedestrian crossings at the given node
Definition NBEdge.cpp:4696
void addBikeLane(double width)
add a bicycle lane of the given width and shift existing connctions
Definition NBEdge.cpp:4749
bool expandableBy(NBEdge *possContinuation, std::string &reason) const
Check if Node is expandable.
Definition NBEdge.cpp:3979
double getLaneFriction(int lane) const
get lane friction of specified lane
Definition NBEdge.cpp:2242
void recheckOpposite(const NBEdgeCont &ec, bool fixOppositeLengths)
recheck whether all opposite and bidi settings are consistent
Definition NBEdge.cpp:3201
const ConstRouterEdgePairVector & getViaSuccessors(SUMOVehicleClass vClass=SVC_IGNORING, bool ignoreTransientPermissions=false) const
Returns the following edges for the given vClass.
Definition NBEdge.cpp:4959
void init(int noLanes, bool tryIgnoreNodePositions, const std::string &origID)
Initialization routines common to all constructors.
Definition NBEdge.cpp:463
void setSpeed(int lane, double speed)
set lane specific speed (negative lane implies set for all lanes)
Definition NBEdge.cpp:4458
void reinitNodes(NBNode *from, NBNode *to)
Resets nodes but keeps all other values the same (used when joining)
Definition NBEdge.cpp:437
double mySpeed
The maximal speed.
Definition NBEdge.h:1782
bool hasLaneSpecificFriction() const
whether lanes differ in friction
Definition NBEdge.cpp:2488
double getLaneWidth() const
Returns the default width of lanes of this edge.
Definition NBEdge.h:648
PositionVector getCWBoundaryLine(const NBNode &n) const
get the outer boundary of this edge when going clock-wise around the given node
Definition NBEdge.cpp:3943
NBNode * getToNode() const
Returns the destination node of the edge.
Definition NBEdge.h:552
void checkGeometry(const double maxAngle, bool fixAngle, const double minRadius, bool fix, bool silent)
Check the angles of successive geometry segments.
Definition NBEdge.cpp:1059
std::vector< Connection > myConnections
List of connections to following edges.
Definition NBEdge.h:1793
Connection & getConnectionRef(int fromLane, const NBEdge *to, int toLane)
Returns reference to the specified connection This method goes through "myConnections" and returns th...
Definition NBEdge.cpp:1336
NBEdge()
constructor for dummy edge
Definition NBEdge.cpp:363
void divideOnEdges(const EdgeVector *outgoing)
divides the lanes on the outgoing edges
Definition NBEdge.cpp:3360
ConstRouterEdgePairVector myViaSuccessors
Definition NBEdge.h:1874
PositionVector getCCWBoundaryLine(const NBNode &n) const
get the outer boundary of this edge when going counter-clock-wise around the given node
Definition NBEdge.cpp:3961
double buildInnerEdges(const NBNode &n, int noInternalNoSplits, int &linkIndex, int &splitIndex)
Definition NBEdge.cpp:1719
static const double UNSPECIFIED_FRICTION
unspecified lane friction
Definition NBEdge.h:355
void incLaneNo(int by)
increment lane
Definition NBEdge.cpp:4239
static EdgeVector filterByPermissions(const EdgeVector &edges, SVCPermissions permissions)
return only those edges that permit at least one of the give permissions
Definition NBEdge.cpp:5026
Lane & getLaneStruct(int lane)
Definition NBEdge.h:1451
const Connection & getConnection(int fromLane, const NBEdge *to, int toLane) const
Returns the specified connection (unmodifiable) This method goes through "myConnections" and returns ...
Definition NBEdge.cpp:1324
void addLane(int index, bool recomputeShape, bool recomputeConnections, bool shiftIndices)
add lane
Definition NBEdge.cpp:4195
bool hasLaneSpecificSpeed() const
whether lanes differ in speed
Definition NBEdge.cpp:2478
void setAverageLengthWithOpposite(double val)
patch average lane length in regard to the opposite edge
Definition NBEdge.cpp:4563
void disallowVehicleClass(int lane, SUMOVehicleClass vclass)
set disallowed class for the given lane or for all lanes if -1 is given
Definition NBEdge.cpp:4302
void removeInvalidConnections()
Definition NBEdge.cpp:3298
double getShapeStartAngle() const
Returns the angle at the start of the edge.
Definition NBEdge.cpp:2437
static const int UNSPECIFIED_INTERNAL_LANE_INDEX
internal lane computation not yet done
Definition NBEdge.h:373
void appendTurnaround(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike, bool checkPermissions)
Add a connection to the previously computed turnaround, if wished and a turning direction exists (myT...
Definition NBEdge.cpp:3730
static bool connections_sorter(const Connection &c1, const Connection &c2)
connections_sorter sort by fromLane, toEdge and toLane
Definition NBEdge.cpp:4578
std::string myType
The type of the edge.
Definition NBEdge.h:1760
const PositionVector & getGeometry() const
Returns the geometry of the edge.
Definition NBEdge.h:789
bool hasPermissions() const
whether at least one lane has restrictions
Definition NBEdge.cpp:2453
double myTotalAngle
Definition NBEdge.h:1775
LaneSpreadFunction getLaneSpreadFunction() const
Returns how this edge's lanes' lateral offset is computed.
Definition NBEdge.cpp:1021
bool hasDefaultGeometryEndpoints() const
Returns whether the geometry is terminated by the node positions This default may be violated by init...
Definition NBEdge.cpp:625
std::string myTurnSignTarget
node for which turnSign information applies
Definition NBEdge.h:1766
bool isBidiRail(bool ignoreSpread=false) const
whether this edge is part of a bidirectional railway
Definition NBEdge.cpp:772
static const bool UNSPECIFIED_CONNECTION_UNCONTROLLED
TLS-controlled despite its node controlled not specified.
Definition NBEdge.h:376
const EdgeVector & getSuccessors(SUMOVehicleClass vClass=SVC_IGNORING) const
Returns the following edges for the given vClass.
Definition NBEdge.cpp:4941
void dismissVehicleClassInformation()
dimiss vehicle class information
Definition NBEdge.cpp:4569
bool computeEdge2Edges(bool noLeftMovers)
computes the edge (step1: computation of approached edges)
Definition NBEdge.cpp:2603
EdgeBuildingStep getStep() const
The building step of this edge.
Definition NBEdge.h:641
LaneSpreadFunction myLaneSpreadFunction
The information about how to spread the lanes.
Definition NBEdge.h:1814
void moveConnectionToLeft(int lane)
moves a connection one place to the left;
Definition NBEdge.cpp:1685
void updateChangeRestrictions(SVCPermissions ignoring)
modify all existing restrictions on lane changing
Definition NBEdge.cpp:2254
void restoreBikelane(std::vector< NBEdge::Lane > oldLanes, PositionVector oldGeometry, std::vector< NBEdge::Connection > oldConnections)
restore an previously added BikeLane
Definition NBEdge.cpp:4755
Position getEndpointAtNode(const NBNode *node) const
Definition NBEdge.cpp:643
NBEdge * getStraightContinuation(SVCPermissions permissions) const
return the straightest follower edge for the given permissions or nullptr (never returns turn-arounds...
Definition NBEdge.cpp:5037
bool hasLoadedLength() const
Returns whether a length was set explicitly.
Definition NBEdge.h:618
void resetEndpointAtNode(const NBNode *node)
Definition NBEdge.cpp:648
void restoreSidewalk(std::vector< NBEdge::Lane > oldLanes, PositionVector oldGeometry, std::vector< NBEdge::Connection > oldConnections)
restore an previously added sidewalk
Definition NBEdge.cpp:4743
bool addEdge2EdgeConnection(NBEdge *dest, bool overrideRemoval=false, SVCPermissions permission=SVC_UNSPECIFIED)
Adds a connection to another edge.
Definition NBEdge.cpp:1120
bool addLane2LaneConnection(int fromLane, NBEdge *dest, int toLane, Lane2LaneInfoType type, bool mayUseSameDestination=false, bool mayDefinitelyPass=false, KeepClear keepClear=KEEPCLEAR_UNSPECIFIED, double contPos=UNSPECIFIED_CONTPOS, double visibility=UNSPECIFIED_VISIBILITY_DISTANCE, double speed=UNSPECIFIED_SPEED, double friction=UNSPECIFIED_FRICTION, double length=myDefaultConnectionLength, const PositionVector &customShape=PositionVector::EMPTY, const bool uncontrolled=UNSPECIFIED_CONNECTION_UNCONTROLLED, SVCPermissions permissions=SVC_UNSPECIFIED, const bool indirectLeft=false, const std::string &edgeType="", SVCPermissions changeLeft=SVC_UNSPECIFIED, SVCPermissions changeRight=SVC_UNSPECIFIED, bool postProcess=false)
Adds a connection between the specified this edge's lane and an approached one.
Definition NBEdge.cpp:1156
void divideSelectedLanesOnEdges(const EdgeVector *outgoing, const std::vector< int > &availableLanes)
divide selected lanes on edges
Definition NBEdge.cpp:3450
bool setEdgeStopOffset(int lane, const StopOffset &offset, bool overwrite=false)
set lane and vehicle class specific stopOffset (negative lane implies set for all lanes)
Definition NBEdge.cpp:4428
const std::vector< NBEdge::Lane > & getLanes() const
Returns the lane definitions.
Definition NBEdge.h:736
bool hasLaneSpecificStopOffsets() const
whether lanes differ in stopOffsets
Definition NBEdge.cpp:2531
void setNodeBorder(const NBNode *node, const Position &p, const Position &p2, bool rectangularCut)
Set Node border.
Definition NBEdge.cpp:718
int getFirstNonPedestrianLaneIndex(int direction, bool exclusive=false) const
return the first lane with permissions other than SVC_PEDESTRIAN and 0
Definition NBEdge.cpp:4603
EdgeVector mySuccessors
Definition NBEdge.h:1871
void shiftToLanesToEdge(NBEdge *to, int laneOff)
modifify the toLane for all connections to the given edge
Definition NBEdge.cpp:4833
static double myDefaultConnectionLength
Definition NBEdge.h:1877
bool isNearEnough2BeJoined2(NBEdge *e, double threshold) const
Check if edge is near enought to be joined to another edge.
Definition NBEdge.cpp:4187
EdgeBuildingStep myStep
The building step.
Definition NBEdge.h:1757
void setLaneType(int lane, const std::string &type)
set lane specific type (negative lane implies set for all lanes)
Definition NBEdge.cpp:4344
bool computeLanes2Edges()
computes the edge, step2: computation of which lanes approach the edges)
Definition NBEdge.cpp:2659
EdgeBuildingStep
Current state of the edge within the building process.
Definition NBEdge.h:109
@ INIT_REJECT_CONNECTIONS
The edge has been loaded and connections shall not be added.
@ EDGE2EDGES
The relationships between edges are computed/loaded.
@ LANES2LANES_RECHECK
Lanes to lanes - relationships are computed; should be rechecked.
@ LANES2LANES_DONE
Lanes to lanes - relationships are computed; no recheck is necessary/wished.
@ LANES2EDGES
Lanes to edges - relationships are computed/loaded.
@ LANES2LANES_USER
Lanes to lanes - relationships are loaded; no recheck is necessary/wished.
@ INIT
The edge has been loaded, nothing is computed yet.
NBEdge * getStraightPredecessor(SVCPermissions permissions) const
return the straightest predecessor edge for the given permissions or nullptr (never returns turn-arou...
Definition NBEdge.cpp:5052
void remapConnections(const EdgeVector &incoming)
Remaps the connection in a way that allows the removal of it.
Definition NBEdge.cpp:1457
double getSpeed() const
Returns the speed allowed on this edge.
Definition NBEdge.h:625
~NBEdge()
Destructor.
Definition NBEdge.cpp:547
NBNode * myTo
Definition NBEdge.h:1763
double myEndAngle
Definition NBEdge.h:1774
const std::string & getID() const
Definition NBEdge.h:1551
int getFirstAllowedLaneIndex(int direction) const
return the first lane that permits at least 1 vClass or the last lane if search direction of there is...
Definition NBEdge.cpp:4646
bool allowsChangingRight(int lane, SUMOVehicleClass vclass) const
Returns whether the given vehicle class may change left from this lane.
Definition NBEdge.cpp:4690
static const double UNSPECIFIED_LOADED_LENGTH
no length override given
Definition NBEdge.h:364
void setLaneWidth(int lane, double width)
set lane specific width (negative lane implies set for all lanes)
Definition NBEdge.cpp:4329
void resetLaneShapes()
reset lane shapes to what they would be before cutting with the junction shapes
Definition NBEdge.cpp:2248
bool setControllingTLInformation(const NBConnection &c, const std::string &tlID)
Returns if the link could be set as to be controlled.
Definition NBEdge.cpp:3872
bool bothLeftTurns(LinkDirection dir, const NBEdge *otherFrom, LinkDirection dir2) const
determine conflict between opposite left turns
Definition NBEdge.cpp:2141
bool shiftPositionAtNode(NBNode *node, NBEdge *opposite)
shift geometry at the given node to avoid overlap and return whether geometry was changed
Definition NBEdge.cpp:4844
void setAcceleration(int lane, bool accelRamp)
marks one lane as acceleration lane
Definition NBEdge.cpp:4490
const StopOffset & getEdgeStopOffset() const
Returns the stopOffset to the end of the edge.
Definition NBEdge.cpp:4396
NBNode * tryGetNodeAtPosition(double pos, double tolerance=5.0) const
Returns the node at the given edges length (using an epsilon)
Definition NBEdge.cpp:3823
void setLaneSpreadFunction(LaneSpreadFunction spread)
(Re)sets how the lanes lateral offset shall be computed
Definition NBEdge.cpp:1015
void clearControllingTLInformation()
clears tlID for all connections
Definition NBEdge.cpp:3935
bool isTurningDirectionAt(const NBEdge *const edge) const
Returns whether the given edge is the opposite direction to this edge.
Definition NBEdge.cpp:3810
void addStraightConnections(const EdgeVector *outgoing, const std::vector< int > &availableLanes, const std::vector< int > &priorities)
add some straight connections
Definition NBEdge.cpp:3555
bool hasLaneSpecificPermissions() const
whether lanes differ in allowed vehicle classes
Definition NBEdge.cpp:2464
bool needsLaneSpecificOutput() const
whether at least one lane has values differing from the edges values
Definition NBEdge.cpp:2586
void computeAngle()
computes the angle of this edge and stores it in myAngle
Definition NBEdge.cpp:2339
void roundSpeed()
ensure consistency between input and output speed
Definition NBEdge.cpp:584
bool isBidiEdge(bool checkPotential=false) const
whether this edge is part of a bidirectional edge pair
Definition NBEdge.cpp:784
static const double UNSPECIFIED_SIGNAL_OFFSET
unspecified signal offset
Definition NBEdge.h:367
void addSidewalk(double width)
add a pedestrian sidewalk of the given width and shift existing connctions
Definition NBEdge.cpp:4737
bool hasSignalisedConnectionTo(const NBEdge *const e) const
Check if edge has signalised connections.
Definition NBEdge.cpp:4161
std::vector< Lane > myLanes
Lane information.
Definition NBEdge.h:1831
int getNumLanes() const
Returns the number of lanes.
Definition NBEdge.h:526
std::vector< Connection > getConnectionsFromLane(int lane, const NBEdge *to=nullptr, int toLane=-1) const
Returns connections from a given lane.
Definition NBEdge.cpp:1310
bool hasAccelLane() const
whether one of the lanes is an acceleration lane
Definition NBEdge.cpp:2544
bool myIsBidi
whether this edge is part of a non-rail bidi edge pair
Definition NBEdge.h:1862
static double firstIntersection(const PositionVector &v1, const PositionVector &v2, double width1, double width2, const std::string &error="", bool secondIntersection=false)
compute the first intersection point between the given lane geometries considering their rspective wi...
Definition NBEdge.cpp:2077
PositionVector myToBorder
Definition NBEdge.h:1855
void extendGeometryAtNode(const NBNode *node, double maxExtent)
linearly extend the geometry at the given node
Definition NBEdge.cpp:681
void setFriction(int lane, double friction)
set lane specific friction (negative lane implies set for all lanes)
Definition NBEdge.cpp:4474
static const double UNSPECIFIED_CONTPOS
unspecified internal junction position
Definition NBEdge.h:358
static const double ANGLE_LOOKAHEAD
the distance at which to take the default angle
Definition NBEdge.h:370
int getNumLanesThatAllow(SVCPermissions permissions, bool allPermissions=true) const
Definition NBEdge.cpp:4672
void reduceGeometry(const double minDist)
Removes points with a distance lesser than the given.
Definition NBEdge.cpp:1037
static NBEdge DummyEdge
Dummy edge to use when a reference must be supplied in the no-arguments constructor (FOX technicality...
Definition NBEdge.h:343
bool joinLanes(SVCPermissions perms)
join adjacent lanes with the given permissions
Definition NBEdge.cpp:5006
void resetNodeBorder(const NBNode *node)
Definition NBEdge.cpp:761
void markAsInLane2LaneState()
mark edge as in lane to state lane
Definition NBEdge.cpp:4282
bool mayBeTLSControlled(int fromLane, NBEdge *toEdge, int toLane) const
return true if certain connection must be controlled by TLS
Definition NBEdge.cpp:3861
void addRestrictedLane(double width, SUMOVehicleClass vclass)
add a lane of the given width, restricted to the given class and shift existing connections
Definition NBEdge.cpp:4771
void removeFromConnections(NBEdge *toEdge, int fromLane=-1, int toLane=-1, bool tryLater=false, const bool adaptToLaneRemoval=false, const bool keepPossibleTurns=false)
Removes the specified connection(s)
Definition NBEdge.cpp:1473
double myLength
The length of the edge.
Definition NBEdge.h:1769
NBEdge::Lane getFirstNonPedestrianLane(int direction) const
get first non-pedestrian lane
Definition NBEdge.cpp:4712
void invalidateConnections(bool reallowSetting=false)
invalidate current connections of edge
Definition NBEdge.cpp:1554
const std::vector< int > prepareEdgePriorities(const EdgeVector *outgoing, const std::vector< int > &availableLanes)
recomputes the edge priorities and manipulates them for a distribution of lanes on edges which is mor...
Definition NBEdge.cpp:3637
int myIndex
the index of the edge in the list of all edges. Set by NBEdgeCont and requires re-set whenever the li...
Definition NBEdge.h:1868
double getTotalWidth() const
Returns the combined width of all lanes of this edge.
Definition NBEdge.cpp:4381
PositionVector cutAtIntersection(const PositionVector &old) const
cut shape at the intersection shapes
Definition NBEdge.cpp:817
Position geometryPositionAtOffset(double offset) const
return position taking into account loaded length
Definition NBEdge.cpp:4884
static const double UNSPECIFIED_VISIBILITY_DISTANCE
unspecified foe visibility for connections
Definition NBEdge.h:361
bool canMoveConnection(const Connection &con, int newFromLane) const
whether the connection can originate on newFromLane
Definition NBEdge.cpp:1676
double getInternalLaneWidth(const NBNode &node, const NBEdge::Connection &connection, const NBEdge::Lane &successor, bool isVia) const
Returns the width of the internal lane associated with the connection.
Definition NBEdge.cpp:4365
void allowVehicleClass(int lane, SUMOVehicleClass vclass)
set allowed class for the given lane or for all lanes if -1 is given
Definition NBEdge.cpp:4289
bool isConnectedTo(const NBEdge *e, const bool ignoreTurnaround=false) const
Returns the information whethe a connection to the given edge has been added (or computed)
Definition NBEdge.cpp:1354
double getMaxLaneOffset()
get max lane offset
Definition NBEdge.cpp:3855
void deleteLane(int index, bool recompute, bool shiftIndices)
delete lane
Definition NBEdge.cpp:4250
NBEdge * myPossibleTurnDestination
The edge that would be the turn destination if there was one.
Definition NBEdge.h:1802
const PositionVector & getNodeBorder(const NBNode *node) const
Definition NBEdge.cpp:750
const NBNode * mySignalNode
Definition NBEdge.h:1850
bool hasLaneSpecificWidth() const
whether lanes differ in width
Definition NBEdge.cpp:2498
void moveConnectionToRight(int lane)
moves a connection one place to the right; @noteAttention! no checking for field validity
Definition NBEdge.cpp:1703
std::set< SVCPermissions > getPermissionVariants(int iStart, int iEnd) const
return all permission variants within the specified lane range [iStart, iEnd[
Definition NBEdge.cpp:4660
void reshiftPosition(double xoff, double yoff)
Applies an offset to the edge.
Definition NBEdge.cpp:552
static const int TURN_SIGN_SHIFT_TAXI
Definition NBEdge.h:380
void moveOutgoingConnectionsFrom(NBEdge *e, int laneOff)
move outgoing connection
Definition NBEdge.cpp:3837
std::string getLaneID(int lane) const
get lane ID
Definition NBEdge.cpp:4181
bool myIsOffRamp
whether this edge is an Off-Ramp or leads to one
Definition NBEdge.h:1859
static const double UNSPECIFIED_SPEED
unspecified lane speed
Definition NBEdge.h:352
Lane2LaneInfoType
Modes of setting connections between lanes.
Definition NBEdge.h:130
@ USER
The connection was given by the user.
@ VALIDATED
The connection was computed and validated.
@ COMPUTED
The connection was computed.
double getFriction() const
Returns the friction on this edge.
Definition NBEdge.h:632
static PositionVector startShapeAt(const PositionVector &laneShape, const NBNode *startNode, PositionVector nodeShape)
Definition NBEdge.cpp:949
std::string getSidewalkID()
get the lane id for the canonical sidewalk lane
Definition NBEdge.cpp:4721
void computeLaneShapes()
compute lane shapes
Definition NBEdge.cpp:2275
double getAngleAtNodeToCenter(const NBNode *const node) const
Returns the angle of from the node shape center to where the edge meets the node shape.
Definition NBEdge.cpp:2212
int getSpecialLane(SVCPermissions permissions) const
return index of the first lane that allows the given permissions
Definition NBEdge.cpp:4636
bool setConnection(int lane, NBEdge *destEdge, int destLane, Lane2LaneInfoType type, bool mayUseSameDestination=false, bool mayDefinitelyPass=false, KeepClear keepClear=KEEPCLEAR_UNSPECIFIED, double contPos=UNSPECIFIED_CONTPOS, double visibility=UNSPECIFIED_VISIBILITY_DISTANCE, double speed=UNSPECIFIED_SPEED, double friction=UNSPECIFIED_FRICTION, double length=myDefaultConnectionLength, const PositionVector &customShape=PositionVector::EMPTY, const bool uncontrolled=UNSPECIFIED_CONNECTION_UNCONTROLLED, SVCPermissions permissions=SVC_UNSPECIFIED, bool indirectLeft=false, const std::string &edgeType="", SVCPermissions changeLeft=SVC_UNSPECIFIED, SVCPermissions changeRight=SVC_UNSPECIFIED, bool postProcess=false)
Adds a connection to a certain lane of a certain edge.
Definition NBEdge.cpp:1209
bool hasLaneSpecificEndOffset() const
whether lanes differ in offset
Definition NBEdge.cpp:2520
int getJunctionPriority(const NBNode *const node) const
Returns the junction priority (normalised for the node currently build)
Definition NBEdge.cpp:2160
double myDistance
The mileage/kilometrage at the start of this edge in a linear coordination system.
Definition NBEdge.h:1788
bool myAmMacroscopicConnector
Information whether this edge is a (macroscopic) connector.
Definition NBEdge.h:1840
EdgeVector getConnectedEdges() const
Returns the list of outgoing edges unsorted.
Definition NBEdge.cpp:1404
const std::string & getStreetName() const
Returns the street name of this edge.
Definition NBEdge.h:675
void setLaneShape(int lane, const PositionVector &shape)
sets a custom lane shape
Definition NBEdge.cpp:4498
double myLoadedLength
An optional length to use (-1 if not valid)
Definition NBEdge.h:1834
static void updateTurnPermissions(SVCPermissions &perm, LinkDirection dir, SVCPermissions spec, std::vector< LinkDirection > dirs)
Definition NBEdge.cpp:2716
void sortOutgoingConnectionsByAngle()
sorts the outgoing connections by their angle relative to their junction
Definition NBEdge.cpp:1445
bool applyTurnSigns()
apply loaded turn sign information
Definition NBEdge.cpp:2727
bool haveIntersection(const NBNode &n, const PositionVector &shape, const NBEdge *otherFrom, const NBEdge::Connection &otherCon, int numPoints, double width1, double width2, int shapeFlag=0) const
Definition NBEdge.cpp:2150
void preferVehicleClass(int lane, SVCPermissions vclasses)
prefer certain vehicle classes for the given lane or for all lanes if -1 is given (ensures also permi...
Definition NBEdge.cpp:4315
const NBEdge * getBidiEdge() const
Definition NBEdge.h:1537
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition NBEdge.h:545
double myStartAngle
The angles of the edge.
Definition NBEdge.h:1773
double getAngleAtNodeNormalized(const NBNode *const node) const
Returns the angle of the edge's geometry at the given node and disregards edge direction.
Definition NBEdge.cpp:2196
NBEdge * getTurnDestination(bool possibleDestination=false) const
Definition NBEdge.cpp:4172
double getAngleAtNode(const NBNode *const node) const
Returns the angle of the edge's geometry at the given node.
Definition NBEdge.cpp:2186
bool hasLaneSpecificType() const
whether lanes differ in type
Definition NBEdge.cpp:2509
PositionVector myFromBorder
intersection borders (because the node shape might be invalid)
Definition NBEdge.h:1854
double getSignalOffset() const
Returns the offset of a traffic signal from the end of this edge.
Definition NBEdge.cpp:4590
bool hasDefaultGeometry() const
Returns whether the geometry consists only of the node positions.
Definition NBEdge.cpp:619
void roundGeometry()
ensure consistency between input and output geometries
Definition NBEdge.cpp:572
std::string myRoutingType
The routing type of the edge.
Definition NBEdge.h:1865
std::vector< int > getConnectionLanes(NBEdge *currentOutgoing, bool withBikes=true, bool withBusLanes=true) const
Returns the list of lanes that may be used to reach the given edge.
Definition NBEdge.cpp:1429
bool myAmInTLS
Information whether this is lies within a joined tls.
Definition NBEdge.h:1837
void setTurningDestination(NBEdge *e, bool onlyPossible=false)
Sets the turing destination at the given edge.
Definition NBEdge.cpp:2227
bool hasDefaultGeometryEndpointAtNode(const NBNode *node) const
Returns whether the geometry is terminated by the node positions This default may be violated by init...
Definition NBEdge.cpp:632
NBEdge * myTurnDestination
The turn destination edge (if a connection exists)
Definition NBEdge.h:1799
int getPriority() const
Returns the priority of the edge.
Definition NBEdge.h:533
void computeEdgeShape(double smoothElevationThreshold=-1)
Recomputeds the lane shapes to terminate at the node shape For every lane the intersection with the f...
Definition NBEdge.cpp:908
double assignInternalLaneLength(std::vector< Connection >::iterator i, int numLanes, double lengthSum, bool averageLength)
assign length to all lanes of an internal edge
Definition NBEdge.cpp:2037
static const double UNSPECIFIED_WIDTH
unspecified lane width
Definition NBEdge.h:346
bool hasRestrictedLane(SUMOVehicleClass vclass) const
returns whether any lane already allows the given vclass exclusively
Definition NBEdge.cpp:4760
void copyConnectionsFrom(NBEdge *src)
copy connections from antoher edge
Definition NBEdge.cpp:1669
const StopOffset & getLaneStopOffset(int lane) const
Returns the stop offset to the specified lane's end.
Definition NBEdge.cpp:4402
void debugPrintConnections(bool outgoing=true, bool incoming=false) const
debugging helper to print all connections
Definition NBEdge.cpp:4982
Position mySignalPosition
the position of a traffic light signal on this edge
Definition NBEdge.h:1849
void replaceInConnections(NBEdge *which, NBEdge *by, int laneOff)
replace in current connections of edge
Definition NBEdge.cpp:1566
bool lanesWereAssigned() const
Check if lanes were assigned.
Definition NBEdge.cpp:3849
void restoreRestrictedLane(SUMOVehicleClass vclass, std::vector< NBEdge::Lane > oldLanes, PositionVector oldGeometry, std::vector< NBEdge::Connection > oldConnections)
restore a restricted lane
Definition NBEdge.cpp:4810
double getEndOffset() const
Returns the offset to the destination node.
Definition NBEdge.h:695
bool isRailDeadEnd() const
whether this edge is a railway edge that does not continue
Definition NBEdge.cpp:802
double myFriction
The current friction.
Definition NBEdge.h:1785
void setEndOffset(int lane, double offset)
set lane specific end-offset (negative lane implies set for all lanes)
Definition NBEdge.cpp:4412
static const double UNSPECIFIED_OFFSET
unspecified lane offset
Definition NBEdge.h:349
void sortOutgoingConnectionsByIndex()
sorts the outgoing connections by their from-lane-index and their to-lane-index
Definition NBEdge.cpp:1451
bool recheckLanes()
recheck whether all lanes within the edge are all right and optimises the connections once again
Definition NBEdge.cpp:2957
int myFromJunctionPriority
The priority normalised for the node the edge is outgoing of.
Definition NBEdge.h:1805
bool addLane2LaneConnections(int fromLane, NBEdge *dest, int toLane, int no, Lane2LaneInfoType type, bool invalidatePrevious=false, bool mayDefinitelyPass=false)
Builds no connections starting at the given lanes.
Definition NBEdge.cpp:1192
void setOrigID(const std::string origID, const bool append, const int laneIdx=-1)
set origID for all lanes or for a specific lane
Definition NBEdge.cpp:4916
PositionVector computeLaneShape(int lane, double offset) const
Computes the shape for the given lane.
Definition NBEdge.cpp:2327
bool allowsChangingLeft(int lane, SUMOVehicleClass vclass) const
Returns whether the given vehicle class may change left from this lane.
Definition NBEdge.cpp:4684
static int getLaneIndexFromLaneID(const std::string laneID)
Definition NBEdge.cpp:5001
bool hasConnectionTo(const NBEdge *destEdge, int destLane, int fromLane=-1) const
Retrieves info about a connection to a certain lane of a certain edge.
Definition NBEdge.cpp:1348
bool hasCustomLaneShape() const
whether one of the lanes has a custom shape
Definition NBEdge.cpp:2555
bool hasLaneParams() const
whether one of the lanes has parameters set
Definition NBEdge.cpp:2566
const PositionVector & getLaneShape(int i) const
Returns the shape of the nth lane.
Definition NBEdge.cpp:1009
double getShapeEndAngle() const
Returns the angle at the end of the edge.
Definition NBEdge.cpp:2445
bool prohibitsChanging() const
whether one of the lanes prohibits lane changing
Definition NBEdge.cpp:2576
void setLoadedLength(double val)
set loaded length
Definition NBEdge.cpp:4558
PositionVector myGeom
The geometry for the edge.
Definition NBEdge.h:1811
const PositionVector getInnerGeometry() const
Returns the geometry of the edge without the endpoints.
Definition NBEdge.cpp:613
void decLaneNo(int by)
decrement lane
Definition NBEdge.cpp:4270
NBNode * myFrom
The source and the destination node.
Definition NBEdge.h:1763
void append(NBEdge *continuation)
append another edge
Definition NBEdge.cpp:4096
void setJunctionPriority(const NBNode *const node, int prio)
Sets the junction priority of the edge.
Definition NBEdge.cpp:2170
double getFinalLength() const
get length that will be assigned to the lanes in the final network
Definition NBEdge.cpp:4894
void shortenGeometryAtNode(const NBNode *node, double reduction)
linearly extend the geometry at the given node
Definition NBEdge.cpp:704
void setGeometry(const PositionVector &g, bool inner=false)
(Re)sets the edge's geometry
Definition NBEdge.cpp:660
int myPriority
The priority of the edge.
Definition NBEdge.h:1779
std::string myStreetName
The street name (or whatever arbitrary string you wish to attach)
Definition NBEdge.h:1843
EdgeVector getIncomingEdges() const
Returns the list of incoming edges unsorted.
Definition NBEdge.cpp:1416
int getFirstNonPedestrianNonBicycleLaneIndex(int direction, bool exclusive=false) const
return the first lane with permissions other than SVC_PEDESTRIAN, SVC_BICYCLE and 0
Definition NBEdge.cpp:4619
static double normRelAngle(double angle1, double angle2)
ensure that reverse relAngles (>=179.999) always count as turnarounds (-180)
Definition NBHelpers.cpp:58
A definition of a pedestrian crossing.
Definition NBNode.h:137
PositionVector shape
The crossing's shape.
Definition NBNode.h:146
bool priority
whether the pedestrians have priority
Definition NBNode.h:160
EdgeVector edges
The edges being crossed.
Definition NBNode.h:144
double width
This crossing's width.
Definition NBNode.h:152
Represents a single node (junction) during network building.
Definition NBNode.h:66
void addIncomingEdge(NBEdge *edge)
adds an incoming edge
Definition NBNode.cpp:541
LinkDirection getDirection(const NBEdge *const incoming, const NBEdge *const outgoing, bool leftHand=false) const
Returns the representation of the described stream's direction.
Definition NBNode.cpp:2507
static const int AVOID_INTERSECTING_LEFT_TURNS
Definition NBNode.h:228
void removeEdge(NBEdge *edge, bool removeFromConnections=true)
Removes edge from this node and optionally removes connections as well.
Definition NBNode.cpp:2076
const std::set< NBTrafficLightDefinition * > & getControllingTLS() const
Returns the traffic lights that were assigned to this node (The set of tls that control this node)
Definition NBNode.h:347
bool needsCont(const NBEdge *fromE, const NBEdge *otherFromE, const NBEdge::Connection &c, const NBEdge::Connection &otherC, bool checkOnlyTLS=false) const
whether an internal junction should be built at from and respect other
Definition NBNode.cpp:986
FringeType getFringeType() const
Returns fringe type.
Definition NBNode.h:307
static const int BACKWARD
Definition NBNode.h:219
SumoXMLNodeType getType() const
Returns the type of this node.
Definition NBNode.h:287
bool isTrafficLight() const
Definition NBNode.h:841
const EdgeVector & getIncomingEdges() const
Returns this node's incoming edges (The edges which yield in this node)
Definition NBNode.h:270
static bool rightTurnConflict(const NBEdge *from, const NBEdge *to, int fromLane, const NBEdge *prohibitorFrom, const NBEdge *prohibitorTo, int prohibitorFromLane)
return whether the given laneToLane connection is a right turn which must yield to a bicycle crossing...
Definition NBNode.cpp:2192
const EdgeVector & getOutgoingEdges() const
Returns this node's outgoing edges (The edges which start at this node)
Definition NBNode.h:275
bool forbids(const NBEdge *const possProhibitorFrom, const NBEdge *const possProhibitorTo, const NBEdge *const possProhibitedFrom, const NBEdge *const possProhibitedTo, bool regardNonSignalisedLowerPriority) const
Returns the information whether "prohibited" flow must let "prohibitor" flow pass.
Definition NBNode.cpp:2337
bool bidiConflict(const NBEdge *from, const NBEdge::Connection &con, const NBEdge *prohibitorFrom, const NBEdge::Connection &prohibitorCon, bool foes) const
whether the foe connections is oncoming on the same lane
Definition NBNode.cpp:2263
PositionVector computeSmoothShape(const PositionVector &begShape, const PositionVector &endShape, int numPoints, bool isTurnaround, double extrapolateBeg, double extrapolateEnd, NBNode *recordError=0, int shapeFlag=0) const
Compute a smooth curve between the given geometries.
Definition NBNode.cpp:596
bool isLeftMover(const NBEdge *const from, const NBEdge *const to) const
Computes whether the given connection is a left mover across the junction.
Definition NBNode.cpp:2318
bool mergeConflict(const NBEdge *from, const NBEdge::Connection &con, const NBEdge *prohibitorFrom, const NBEdge::Connection &prohibitorCon, bool foes) const
whether multiple connections from the same edge target the same lane
Definition NBNode.cpp:2254
std::vector< Crossing * > getCrossings() const
return this junctions pedestrian crossings
Definition NBNode.cpp:3107
void addOutgoingEdge(NBEdge *edge)
adds an outgoing edge
Definition NBNode.cpp:551
bool isConstantWidthTransition() const
detects whether a given junction splits or merges lanes while keeping constant road width
Definition NBNode.cpp:929
const Position & getPosition() const
Definition NBNode.h:262
const PositionVector & getShape() const
retrieve the junction shape
Definition NBNode.cpp:2792
static const int FORWARD
edge directions (for pedestrian related stuff)
Definition NBNode.h:218
bool foes(const NBEdge *const from1, const NBEdge *const to1, const NBEdge *const from2, const NBEdge *const to2) const
Returns the information whether the given flows cross.
Definition NBNode.cpp:2347
PositionVector computeInternalLaneShape(const NBEdge *fromE, const NBEdge::Connection &con, int numPoints, NBNode *recordError=0, int shapeFlag=0) const
Compute the shape for an internal lane.
Definition NBNode.cpp:844
void shiftTLConnectionLaneIndex(NBEdge *edge, int offset, int threshold=-1)
patches loaded signal plans by modifying lane indices above threshold by the given offset
Definition NBNode.cpp:500
bool geometryLike() const
whether this is structurally similar to a geometry node
Definition NBNode.cpp:4033
bool isTLControlled() const
Returns whether this node is controlled by any tls.
Definition NBNode.h:338
static const int SCURVE_IGNORE
Definition NBNode.h:229
static const double MIN_SPEED_CROSSING_TIME
minimum speed for computing time to cross intersection
Definition NBOwnTLDef.h:152
Base class for objects which have an id.
Definition Named.h:53
std::string myID
The name of the object.
Definition Named.h:124
static std::string getIDSecure(const T *obj, const std::string &fallBack="NULL")
get an identifier for Named-like object which may be Null
Definition Named.h:66
const std::string & getID() const
Returns the id.
Definition Named.h:73
A storage for options typed value containers)
Definition OptionsCont.h:89
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
int getInt(const std::string &name) const
Returns the int-value of the named option (only for Option_Integer)
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
static OptionsCont & getOptions()
Retrieves the options.
bool hasParameter(const std::string &key) const
Returns whether the parameter is set.
void mergeParameters(const Parameterised::Map &mapArg, const std::string separator=" ", bool uniqueValues=true)
Adds or appends all given parameters from the map.
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const
Returns the value for a given key.
const Parameterised::Map & getParametersMap() const
Returns the inner key/value map.
virtual void setParameter(const std::string &key, const std::string &value)
Sets a parameter.
void updateParameters(const Parameterised::Map &mapArg)
Adds or updates all given parameters from the map.
A point in 2D or 3D with translation and scaling methods.
Definition Position.h:37
static const Position INVALID
used to indicate that a position is valid
Definition Position.h:323
double distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition Position.h:273
void add(const Position &pos)
Adds the given position to this one.
Definition Position.h:129
void setz(double z)
set position z
Definition Position.h:77
double z() const
Returns the z-position.
Definition Position.h:62
double angleTo2D(const Position &other) const
returns the angle in the plane of the vector pointing from here to the other position (in radians bet...
Definition Position.h:283
void sety(double y)
set position y
Definition Position.h:72
double y() const
Returns the y-position.
Definition Position.h:57
A list of positions.
double length2D() const
Returns the length.
void append(const PositionVector &v, double sameThreshold=2.0)
double beginEndAngle() const
returns the angle in radians of the line connecting the first and the last position
double length() const
Returns the length.
void round(int precision, bool avoidDegeneration=true)
round all coordinates to the given precision
void ensureMinLength(int precision)
ensure minimum length so that the geometry will not degenerate to 0-length on writing with the given ...
void push_front_noDoublePos(const Position &p)
insert in front a non double position
Position positionAtOffset(double pos, double lateralOffset=0) const
Returns the position at the given length.
void add(double xoff, double yoff, double zoff)
void closePolygon()
ensures that the last position equals the first
std::vector< double > intersectsAtLengths2D(const PositionVector &other) const
For all intersections between this vector and other, return the 2D-length of the subvector from this ...
double distance2D(const Position &p, bool perpendicular=false) const
closest 2D-distance to point p (or -1 if perpendicular is true and the point is beyond this vector)
double nearest_offset_to_point2D(const Position &p, bool perpendicular=true) const
return the nearest offest to point 2D
std::vector< double > distances(const PositionVector &s, bool perpendicular=false) const
distances of all my points to s and all of s points to myself
PositionVector getOrthogonal(const Position &p, double extend, bool before, double length=1.0, double deg=90) const
return orthogonal through p (extending this vector if necessary)
std::pair< PositionVector, PositionVector > splitAt(double where, bool use2D=false) const
Returns the two lists made when this list vector is splitted at the given point.
void move2side(double amount, double maxExtension=100)
move position vector to side using certain amount
bool almostSame(const PositionVector &v2, double maxDiv=POSITION_EPS) const
check if the two vectors have the same length and pairwise similar positions
PositionVector getSubpart2D(double beginOffset, double endOffset) const
get subpart of a position vector in two dimensions (Z is ignored)
PositionVector smoothedZFront(double dist=std::numeric_limits< double >::max()) const
returned vector that is smoothed at the front (within dist)
double angleAt2D(int pos) const
get angle in certain position of position vector (in radians between -M_PI and M_PI)
bool hasElevation() const
return whether two positions differ in z-coordinate
static const PositionVector EMPTY
empty Vector
void extrapolate(const double val, const bool onlyFirst=false, const bool onlyLast=false)
extrapolate position vector
Position getCentroid() const
Returns the centroid (closes the polygon if unclosed)
void extrapolate2D(const double val, const bool onlyFirst=false)
extrapolate position vector in two dimensions (Z is ignored)
Position positionAtOffset2D(double pos, double lateralOffset=0, bool extrapolateBeyond=false) const
Returns the position at the given length.
void push_back_noDoublePos(const Position &p)
insert in back a non double position
void removeDoublePoints(double minDist=POSITION_EPS, bool assertLength=false, int beginOffset=0, int endOffset=0, bool resample=false)
Removes positions if too near.
bool intersects(const Position &p1, const Position &p2) const
Returns the information whether this list of points interesects the given line.
PositionVector reverse() const
reverse position vector
PositionVector getSubpartByIndex(int beginIndex, int count) const
get subpart of a position vector using index and a cout
PositionVector getSubpart(double beginOffset, double endOffset) const
get subpart of a position vector
bool around(const Position &p, double offset=0) const
Returns the information whether the position vector describes a polygon lying around the given point.
static bool isValidNetID(const std::string &value)
whether the given string is a valid id for a network element
stop offset
bool isDefined() const
check if stopOffset was defined
double getOffset() const
get offset
std::vector< std::string > getVector()
return vector of strings
Some static methods for string processing.
Definition StringUtils.h:39
static std::string convertUmlaute(std::string str)
Converts german "Umlaute" to their latin-version.
static int toInt(const std::string &sData)
converts a string into the integer value described by it by calling the char-type converter,...
static T maxValue(const std::vector< T > &v)
#define M_PI
Definition odrSpiral.cpp:45
A structure which describes a connection between edges or lanes.
Definition NBEdge.h:201
bool indirectLeft
Whether this connection is an indirect left turn.
Definition NBEdge.h:261
int fromLane
The lane the connections starts at.
Definition NBEdge.h:210
std::string viaID
if Connection have a via, ID of it
Definition NBEdge.h:279
int toLane
The lane the connections yields in.
Definition NBEdge.h:216
std::vector< int > foeInternalLinks
FOE Internal links.
Definition NBEdge.h:288
Connection(int fromLane_, NBEdge *toEdge_, int toLane_, const bool mayDefinitelyPass_=false)
Constructor.
Definition NBEdge.cpp:110
std::string getInternalViaLaneID() const
get ID of internal lane (second part)
Definition NBEdge.cpp:98
double speed
custom speed for connection
Definition NBEdge.h:240
NBEdge * toEdge
The edge the connections yields in.
Definition NBEdge.h:213
double customLength
custom length for connection
Definition NBEdge.h:246
double vmax
maximum velocity
Definition NBEdge.h:273
PositionVector customShape
custom shape for connection
Definition NBEdge.h:249
PositionVector viaShape
shape of via
Definition NBEdge.h:282
std::string getDescription(const NBEdge *parent) const
get string describing this connection
Definition NBEdge.cpp:104
double contPos
custom position for internal junction on this connection
Definition NBEdge.h:234
std::string getInternalLaneID() const
get ID of internal lane
Definition NBEdge.cpp:92
int internalLaneIndex
The lane index of this internal lane within the internal edge.
Definition NBEdge.h:294
std::string tlID
The id of the traffic light that controls this connection.
Definition NBEdge.h:219
int internalViaLaneIndex
Definition NBEdge.h:295
int tlLinkIndex2
The index of the internal junction within the controlling traffic light (optional)
Definition NBEdge.h:225
double length
computed length (average of all internal lane shape lengths that share an internal edge)
Definition NBEdge.h:310
PositionVector shape
shape of Connection
Definition NBEdge.h:270
std::string id
id of Connection
Definition NBEdge.h:267
std::vector< std::string > foeIncomingLanes
FOE Incomings lanes.
Definition NBEdge.h:291
bool haveVia
check if Connection have a Via
Definition NBEdge.h:276
int tlLinkIndex
The index of this connection within the controlling traffic light.
Definition NBEdge.h:222
double viaLength
the length of the via shape (maybe customized)
Definition NBEdge.h:285
static ConstRouterEdgePairVector myViaSuccessors
Definition NBEdge.h:314
An (internal) definition of a single lane of an edge.
Definition NBEdge.h:143
double width
This lane's width.
Definition NBEdge.h:176
std::string oppositeID
An opposite lane ID, if given.
Definition NBEdge.h:179
SVCPermissions changeRight
List of vehicle types that are allowed to change right from this lane.
Definition NBEdge.h:166
SVCPermissions changeLeft
List of vehicle types that are allowed to change Left from this lane.
Definition NBEdge.h:163
Lane(NBEdge *e, const std::string &_origID)
constructor
Definition NBEdge.cpp:120
bool accelRamp
Whether this lane is an acceleration lane.
Definition NBEdge.h:182
PositionVector shape
The lane's shape.
Definition NBEdge.h:148