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