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