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