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-2025 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 if (crossingPositions.first > 0 && crossingPositions.first < shapeLength) {
2002 std::pair<PositionVector, PositionVector> split = shape.splitAt(crossingPositions.first);
2003 con.shape = split.first;
2004 con.foeIncomingLanes = std::vector<std::string>(tmpFoeIncomingLanes.begin(), tmpFoeIncomingLanes.end());
2005 con.foeInternalLinks = foeInternalLinks; // resolve link indices to lane ids later
2006 if (i != myConnections.begin() && (i - 1)->toEdge == con.toEdge && (i - 1)->haveVia) {
2007 --splitIndex;
2008 con.internalViaLaneIndex = (i - 1)->internalViaLaneIndex + 1;
2009 }
2010 con.viaID = innerID + "_" + toString(splitIndex + noInternalNoSplits);
2011 ++splitIndex;
2012 con.viaShape = split.second;
2013 con.haveVia = true;
2014 firstLength = con.shape.length();
2015 } else {
2016 con.shape = shape;
2017 }
2018 con.internalLaneIndex = internalLaneIndex;
2019 ++internalLaneIndex;
2020 ++linkIndex;
2021 ++numLanes;
2023 // split length proportionally
2024 lengthSum += (shapeLength != 0 ? firstLength / shapeLength : 1) * con.customLength;
2025 } else {
2026 lengthSum += firstLength;
2027 }
2028 }
2029 return MAX2(maxCross, assignInternalLaneLength(myConnections.end(), numLanes, lengthSum, averageLength));
2030}
2031
2032
2033double
2034NBEdge::assignInternalLaneLength(std::vector<Connection>::iterator i, int numLanes, double lengthSum, bool averageLength) {
2035 // assign average length to all lanes of the same internal edge if averageLength is set
2036 // the lengthSum only covers the part up to the first internal junction
2037 // TODO This code assumes that either all connections in question have a via or none
2038 double maxCross = 0.;
2039 assert(i - myConnections.begin() >= numLanes);
2040 for (int prevIndex = 1; prevIndex <= numLanes; prevIndex++) {
2041 //std::cout << " con=" << (*(i - prevIndex)).getDescription(this) << " numLanes=" << numLanes << " avgLength=" << lengthSum / numLanes << "\n";
2042 Connection& c = (*(i - prevIndex));
2043 const double minLength = c.customLength != UNSPECIFIED_LOADED_LENGTH ? pow(10, -gPrecision) : POSITION_EPS;
2044 c.length = MAX2(minLength, averageLength ? lengthSum / numLanes : c.shape.length());
2045 if (c.haveVia) {
2046 c.viaLength = MAX2(minLength, c.viaShape.length());
2047 }
2049 if (c.haveVia) {
2050 // split length proportionally
2051 const double a = c.viaLength / (c.shape.length() + c.viaLength);
2052 c.viaLength = MAX2(minLength, a * c.customLength);
2053 }
2054 if (!averageLength) {
2055 c.length = MAX2(minLength, c.customLength - c.viaLength);
2056 }
2057 }
2058 if (c.haveVia) {
2059 // we need to be able to leave from the internal junction by accelerating from 0
2060 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)
2061 }
2062 // we need to be able to cross the junction in one go but not if we have an indirect left turn
2063 if (c.indirectLeft) {
2064 maxCross = MAX2(maxCross, MAX2(c.length, c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
2065 } else {
2066 maxCross = MAX2(maxCross, (c.length + c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
2067 }
2068 }
2069 return maxCross;
2070}
2071
2072
2073double
2074NBEdge::firstIntersection(const PositionVector& v1, const PositionVector& v2, double width1, double width2, const std::string& error, bool secondIntersection) {
2075 double intersect = std::numeric_limits<double>::max();
2076 if (v2.length() < POSITION_EPS) {
2077 return intersect;
2078 }
2079 try {
2080 PositionVector v1Right = v1;
2081 v1Right.move2side(width1);
2082
2083 PositionVector v1Left = v1;
2084 v1Left.move2side(-width1);
2085
2086 PositionVector v2Right = v2;
2087 v2Right.move2side(width2);
2088
2089 PositionVector v2Left = v2;
2090 v2Left.move2side(-width2);
2091
2092 // intersect all border combinations
2093 bool skip = secondIntersection;
2094 for (double cand : v1Left.intersectsAtLengths2D(v2Right)) {
2095 if (skip) {
2096 skip = false;
2097 continue;
2098 }
2099 intersect = MIN2(intersect, cand);
2100 }
2101 skip = secondIntersection;
2102 for (double cand : v1Left.intersectsAtLengths2D(v2Left)) {
2103 if (skip) {
2104 skip = false;
2105 continue;
2106 }
2107 intersect = MIN2(intersect, cand);
2108 }
2109 skip = secondIntersection;
2110 for (double cand : v1Right.intersectsAtLengths2D(v2Right)) {
2111 if (skip) {
2112 skip = false;
2113 continue;
2114 }
2115 intersect = MIN2(intersect, cand);
2116 }
2117 skip = secondIntersection;
2118 for (double cand : v1Right.intersectsAtLengths2D(v2Left)) {
2119 if (skip) {
2120 skip = false;
2121 continue;
2122 }
2123 intersect = MIN2(intersect, cand);
2124 }
2125 } catch (InvalidArgument&) {
2126 if (error != "") {
2127 WRITE_WARNING(error);
2128 }
2129 }
2130 //std::cout << " v1=" << v1 << " v2Right=" << v2Right << " v2Left=" << v2Left << "\n";
2131 //std::cout << " intersectsRight=" << toString(v1.intersectsAtLengths2D(v2Right)) << "\n";
2132 //std::cout << " intersectsLeft=" << toString(v1.intersectsAtLengths2D(v2Left)) << "\n";
2133 return intersect;
2134}
2135
2136
2137bool
2138NBEdge::bothLeftTurns(LinkDirection dir, const NBEdge* otherFrom, LinkDirection dir2) const {
2139 if (otherFrom == this) {
2140 // not an opposite pair
2141 return false;
2142 }
2143 return (dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT) && (dir2 == LinkDirection::LEFT || dir2 == LinkDirection::PARTLEFT);
2144}
2145
2146bool
2147NBEdge::haveIntersection(const NBNode& n, const PositionVector& shape, const NBEdge* otherFrom, const NBEdge::Connection& otherCon, int numPoints,
2148 double width1, double width2, int shapeFlag) const {
2149 const PositionVector otherShape = n.computeInternalLaneShape(otherFrom, otherCon, numPoints, 0, shapeFlag);
2150 const double minDV = firstIntersection(shape, otherShape, width1, width2);
2151 return minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS;
2152}
2153
2154
2155// -----------
2156int
2157NBEdge::getJunctionPriority(const NBNode* const node) const {
2158 if (node == myFrom) {
2160 } else {
2161 return myToJunctionPriority;
2162 }
2163}
2164
2165
2166void
2167NBEdge::setJunctionPriority(const NBNode* const node, int prio) {
2168 if (node == myFrom) {
2170#ifdef DEBUG_JUNCTIONPRIO
2171 setParameter("fromPrio", toString(prio));
2172#endif
2173 } else {
2174 myToJunctionPriority = prio;
2175#ifdef DEBUG_JUNCTIONPRIO
2176 setParameter("toPrio", toString(prio));
2177#endif
2178 }
2179}
2180
2181
2182double
2183NBEdge::getAngleAtNode(const NBNode* const atNode) const {
2184 if (atNode == myFrom) {
2186 }
2187 assert(atNode == myTo);
2189}
2190
2191
2192double
2193NBEdge::getAngleAtNodeNormalized(const NBNode* const atNode) const {
2194 double res;
2195 if (atNode == myFrom) {
2197 } else {
2198 assert(atNode == myTo);
2200 }
2201 if (res < 0) {
2202 res += 360;
2203 }
2204 return res;
2205}
2206
2207
2208double
2209NBEdge::getAngleAtNodeToCenter(const NBNode* const atNode) const {
2210 if (atNode == myFrom) {
2211 double res = myStartAngle - 180;
2212 if (res < 0) {
2213 res += 360;
2214 }
2215 return res;
2216 } else {
2217 assert(atNode == myTo);
2218 return myEndAngle;
2219 }
2220}
2221
2222
2223void
2225 if (!onlyPossible) {
2227 }
2229}
2230
2231
2232double
2233NBEdge::getLaneSpeed(int lane) const {
2234 return myLanes[lane].speed;
2235}
2236
2237
2238double
2240 return myLanes[lane].friction;
2241}
2242
2243
2244void
2248
2249
2250void
2252 for (Lane& lane : myLanes) {
2253 if (lane.changeLeft != SVCAll) {
2254 lane.changeLeft = ignoring;
2255 }
2256 if (lane.changeRight != SVCAll) {
2257 lane.changeRight = ignoring;
2258 }
2259 }
2260 for (Connection& con : myConnections) {
2261 if (con.changeLeft != SVC_UNSPECIFIED && con.changeLeft != SVCAll) {
2262 con.changeLeft = ignoring;
2263 }
2264 if (con.changeRight != SVC_UNSPECIFIED && con.changeRight != SVCAll) {
2265 con.changeRight = ignoring;
2266 }
2267 }
2268}
2269
2270
2271void
2273 // vissim needs this
2274 if (myFrom == myTo) {
2275 return;
2276 }
2277 // compute lane offset, first
2278 std::vector<double> offsets(myLanes.size(), 0.);
2279 double offset = 0;
2280 for (int i = (int)myLanes.size() - 2; i >= 0; --i) {
2281 offset += (getLaneWidth(i) + getLaneWidth(i + 1)) / 2.;
2282 offsets[i] = offset;
2283 }
2285 double width = 0;
2286 for (int i = 0; i < (int)myLanes.size(); ++i) {
2287 width += getLaneWidth(i);
2288 }
2289 offset = -width / 2. + getLaneWidth((int)myLanes.size() - 1) / 2.;
2290 } else {
2291 double laneWidth = myLanes.back().width != UNSPECIFIED_WIDTH ? myLanes.back().width : SUMO_const_laneWidth;
2292 offset = laneWidth / 2.;
2293 }
2295 for (NBEdge* e : myTo->getOutgoingEdges()) {
2296 if (e->getToNode() == myFrom && getInnerGeometry().reverse() == e->getInnerGeometry()) {
2297 offset += (e->getTotalWidth() - getTotalWidth()) / 2;
2298 break;
2299 }
2300 }
2301 }
2302
2303 for (int i = 0; i < (int)myLanes.size(); ++i) {
2304 offsets[i] += offset;
2305 }
2306
2307 // build the shape of each lane
2308 for (int i = 0; i < (int)myLanes.size(); ++i) {
2309 if (myLanes[i].customShape.size() != 0) {
2310 myLanes[i].shape = myLanes[i].customShape;
2311 continue;
2312 }
2313 try {
2314 myLanes[i].shape = computeLaneShape(i, offsets[i]);
2315 } catch (InvalidArgument& e) {
2316 WRITE_WARNINGF(TL("In lane '%': lane shape could not be determined (%)."), getLaneID(i), e.what());
2317 myLanes[i].shape = myGeom;
2318 }
2319 }
2320}
2321
2322
2324NBEdge::computeLaneShape(int lane, double offset) const {
2325 PositionVector shape = myGeom;
2326 try {
2327 shape.move2side(offset);
2328 } catch (InvalidArgument& e) {
2329 WRITE_WARNINGF(TL("In lane '%': Could not build shape (%)."), getLaneID(lane), e.what());
2330 }
2331 return shape;
2332}
2333
2334
2335void
2337 // taking the angle at the first might be unstable, thus we take the angle
2338 // at a certain distance. (To compare two edges, additional geometry
2339 // segments are considered to resolve ambiguities)
2340 const bool hasFromShape = myFrom->getShape().size() > 0;
2341 const bool hasToShape = myTo->getShape().size() > 0;
2342 Position fromCenter = (hasFromShape ? myFrom->getShape().getCentroid() : myFrom->getPosition());
2343 Position toCenter = (hasToShape ? myTo->getShape().getCentroid() : myTo->getPosition());
2344 PositionVector shape = myGeom;
2345 if ((hasFromShape || hasToShape) && getNumLanes() > 0) {
2347 shape = myLanes[getNumLanes() - 1].shape ;
2348 } else {
2349 shape = myLanes[getNumLanes() / 2].shape;
2350 if (getNumLanes() % 2 == 0) {
2351 // there is no center lane. shift to get the center
2352 shape.move2side(getLaneWidth(getNumLanes() / 2) * 0.5);
2353 }
2354 }
2355 }
2356
2357 // if the junction shape is suspicious we cannot trust the angle to the centroid
2358 const bool suspiciousFromShape = hasFromShape && (myFrom->getShape().distance2D(shape[0]) > 2 * POSITION_EPS
2359 || myFrom->getShape().around(shape[-1])
2360 || !(myFrom->getShape().around(fromCenter)));
2361 const bool suspiciousToShape = hasToShape && (myTo->getShape().distance2D(shape[-1]) > 2 * POSITION_EPS
2362 || myTo->getShape().around(shape[0])
2363 || !(myTo->getShape().around(toCenter)));
2364
2365 const double angleLookahead = MIN2(shape.length2D() / 2, ANGLE_LOOKAHEAD);
2366 const Position referencePosStart = shape.positionAtOffset2D(angleLookahead);
2367 const Position referencePosEnd = shape.positionAtOffset2D(shape.length2D() - angleLookahead);
2368
2369 myStartAngle = GeomHelper::legacyDegree(fromCenter.angleTo2D(referencePosStart), true);
2370 const double myStartAngle2 = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(referencePosStart), true);
2371 const double myStartAngle3 = getAngleAtNode(myFrom);
2372 myEndAngle = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(toCenter), true);
2373 const double myEndAngle2 = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myTo->getPosition()), true);
2374 const double myEndAngle3 = getAngleAtNode(myTo);
2375
2376#ifdef DEBUG_ANGLES
2377 if (DEBUGCOND) {
2378 if (suspiciousFromShape) {
2379 std::cout << "suspiciousFromShape len=" << shape.length() << " startA=" << myStartAngle << " startA2=" << myStartAngle2 << " startA3=" << myStartAngle3
2380 << " rel=" << NBHelpers::normRelAngle(myStartAngle, myStartAngle2)
2381 << " fromCenter=" << fromCenter
2382 << " fromPos=" << myFrom->getPosition()
2383 << " refStart=" << referencePosStart
2384 << "\n";
2385 }
2386 if (suspiciousToShape) {
2387 std::cout << "suspiciousToShape len=" << shape.length() << " endA=" << myEndAngle << " endA2=" << myEndAngle2 << " endA3=" << myEndAngle3
2388 << " rel=" << NBHelpers::normRelAngle(myEndAngle, myEndAngle2)
2389 << " toCenter=" << toCenter
2390 << " toPos=" << myTo->getPosition()
2391 << " refEnd=" << referencePosEnd
2392 << "\n";
2393 }
2394 }
2395#endif
2396
2397 if (suspiciousFromShape && shape.length() > 1) {
2398 myStartAngle = myStartAngle2;
2399 } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myStartAngle, myStartAngle3)) > 90
2400 // don't trust footpath angles
2401 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
2402 myStartAngle = myStartAngle3;
2403 if (myStartAngle < 0) {
2404 myStartAngle += 360;
2405 }
2406 }
2407
2408 if (suspiciousToShape && shape.length() > 1) {
2409 myEndAngle = myEndAngle2;
2410 } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myEndAngle, myEndAngle3)) > 90
2411 // don't trust footpath angles
2412 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
2413 myEndAngle = myEndAngle3;
2414 if (myEndAngle < 0) {
2415 myEndAngle += 360;
2416 }
2417 }
2418
2420#ifdef DEBUG_ANGLES
2421 if (DEBUGCOND) std::cout << "computeAngle edge=" << getID()
2422 << " fromCenter=" << fromCenter << " toCenter=" << toCenter
2423 << " refStart=" << referencePosStart << " refEnd=" << referencePosEnd << " shape=" << shape
2424 << " hasFromShape=" << hasFromShape
2425 << " hasToShape=" << hasToShape
2426 << " numLanes=" << getNumLanes()
2427 << " shapeLane=" << getNumLanes() / 2
2428 << " startA=" << myStartAngle << " endA=" << myEndAngle << " totA=" << myTotalAngle << "\n";
2429#endif
2430}
2431
2432
2433double
2435 const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
2436 const Position referencePosStart = myGeom.positionAtOffset2D(angleLookahead);
2437 return GeomHelper::legacyDegree(myGeom.front().angleTo2D(referencePosStart), true);
2438}
2439
2440
2441double
2443 const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
2444 const Position referencePosEnd = myGeom.positionAtOffset2D(myGeom.length2D() - angleLookahead);
2445 return GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myGeom.back()), true);
2446}
2447
2448
2449bool
2451 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2452 if ((*i).permissions != SVCAll) {
2453 return true;
2454 }
2455 }
2456 return false;
2457}
2458
2459
2460bool
2462 std::vector<Lane>::const_iterator i = myLanes.begin();
2463 SVCPermissions firstLanePermissions = i->permissions;
2464 i++;
2465 for (; i != myLanes.end(); ++i) {
2466 if (i->permissions != firstLanePermissions) {
2467 return true;
2468 }
2469 }
2470 return false;
2471}
2472
2473
2474bool
2476 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2477 if (i->speed != getSpeed()) {
2478 return true;
2479 }
2480 }
2481 return false;
2482}
2483
2484bool
2486 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2487 if (i->friction != myLanes.begin()->friction) {
2488 return true;
2489 }
2490 }
2491 return false;
2492}
2493
2494bool
2496 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2497 if (i->width != myLanes.begin()->width) {
2498 return true;
2499 }
2500 }
2501 return false;
2502}
2503
2504
2505bool
2507 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2508 if (i->type != myLanes.begin()->type) {
2509 return true;
2510 }
2511 }
2512 return false;
2513}
2514
2515
2516bool
2518 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2519 if (i->endOffset != myLanes.begin()->endOffset) {
2520 return true;
2521 }
2522 }
2523 return false;
2524}
2525
2526
2527bool
2529 for (const auto& lane : myLanes) {
2530 if (lane.laneStopOffset.isDefined()) {
2531 if (myEdgeStopOffset.isDefined() || (myEdgeStopOffset != lane.laneStopOffset)) {
2532 return true;
2533 }
2534 }
2535 }
2536 return false;
2537}
2538
2539
2540bool
2542 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2543 if (i->accelRamp) {
2544 return true;
2545 }
2546 }
2547 return false;
2548}
2549
2550
2551bool
2553 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2554 if (i->customShape.size() > 0) {
2555 return true;
2556 }
2557 }
2558 return false;
2559}
2560
2561
2562bool
2564 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2565 if (i->getParametersMap().size() > 0) {
2566 return true;
2567 }
2568 }
2569 return false;
2570}
2571
2572bool
2574 for (const Lane& lane : myLanes) {
2575 if (lane.changeLeft != SVCAll || lane.changeRight != SVCAll) {
2576 return true;
2577 }
2578 }
2579 return false;
2580}
2581
2582bool
2590 || hasAccelLane()
2592 || hasLaneParams()
2594 || (!myLanes.empty() && myLanes.back().oppositeID != ""));
2595}
2596
2597
2598
2599bool
2600NBEdge::computeEdge2Edges(bool noLeftMovers) {
2601#ifdef DEBUG_CONNECTION_GUESSING
2602 if (DEBUGCOND) {
2603 std::cout << "computeEdge2Edges edge=" << getID() << " step=" << (int)myStep << " noLeftMovers=" << noLeftMovers << "\n";
2604 for (Connection& c : myConnections) {
2605 std::cout << " conn " << c.getDescription(this) << "\n";
2606 }
2608 std::cout << " connToDelete " << c.getDescription(this) << "\n";
2609 }
2610 }
2611#endif
2612 // return if this relationship has been build in previous steps or
2613 // during the import
2615 return true;
2616 }
2617 const bool fromRail = isRailway(getPermissions());
2618 for (NBEdge* out : myTo->getOutgoingEdges()) {
2619 if (noLeftMovers && myTo->isLeftMover(this, out)) {
2620 continue;
2621 }
2622 // avoid sharp railway turns
2623 if (fromRail && isRailway(out->getPermissions())) {
2624 const double angle = fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), out->getAngleAtNode(myTo)));
2625 if (angle > 150) {
2626 continue;
2627 } else if (angle > 90) {
2628 // possibly the junction is large enough to achieve a plausible radius:
2629 const PositionVector& fromShape = myLanes.front().shape;
2630 const PositionVector& toShape = out->getLanes().front().shape;
2631 PositionVector shape = myTo->computeSmoothShape(fromShape, toShape, 5, getTurnDestination() == out, 5, 5);
2632 const double radius = shape.length2D() / DEG2RAD(angle);
2633 const double minRadius = (getPermissions() & SVC_TRAM) != 0 ? 20 : 80;
2634 //std::cout << getID() << " to=" << out->getID() << " radius=" << radius << " minRadius=" << minRadius << "\n";
2635 if (radius < minRadius) {
2636 continue;
2637 }
2638 }
2639 }
2640 if (out == myTurnDestination) {
2641 // will be added by appendTurnaround
2642 continue;
2643 }
2644 if ((getPermissions() & out->getPermissions() & ~SVC_PEDESTRIAN) == 0) {
2645 // no common permissions
2646 continue;
2647 }
2648 myConnections.push_back(Connection(-1, out, -1));
2649 }
2651 return true;
2652}
2653
2654
2655bool
2657#ifdef DEBUG_CONNECTION_GUESSING
2658 if (DEBUGCOND) {
2659 std::cout << "computeLanes2Edges edge=" << getID() << " step=" << (int)myStep << "\n";
2660 for (Connection& c : myConnections) {
2661 std::cout << " conn " << c.getDescription(this) << "\n";
2662 }
2664 std::cout << " connToDelete " << c.getDescription(this) << "\n";
2665 }
2666 }
2667#endif
2668 // return if this relationship has been build in previous steps or
2669 // during the import
2672 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
2673 if ((*i).toEdge == nullptr) {
2674 WRITE_WARNINGF("Inconsistent connection definitions at edge '%'.", getID());
2675 i = myConnections.erase(i);
2676 } else {
2677 i++;
2678 }
2679 }
2680 }
2681 return true;
2682 }
2684 // get list of possible outgoing edges sorted by direction clockwise
2685 // the edge in the backward direction (turnaround) is not in the list
2686 const EdgeVector* edges = getConnectedSorted();
2687 if (myConnections.size() != 0 && edges->size() == 0) {
2688 // dead end per definition!?
2689 myConnections.clear();
2690 } else {
2691 // divide the lanes on reachable edges
2692 divideOnEdges(edges);
2693 }
2694 delete edges;
2696 return true;
2697}
2698
2699
2700std::vector<LinkDirection>
2701NBEdge::decodeTurnSigns(int turnSigns, int shift) {
2702 std::vector<LinkDirection> result;
2703 for (int i = 0; i < 8; i++) {
2704 // see LinkDirection in SUMOXMLDefinitions.h
2705 if ((turnSigns & (1 << (i + shift))) != 0) {
2706 result.push_back((LinkDirection)(1 << i));
2707 }
2708 }
2709 return result;
2710}
2711
2712void
2713NBEdge::updateTurnPermissions(SVCPermissions& perm, LinkDirection dir, SVCPermissions spec, std::vector<LinkDirection> dirs) {
2714 if (dirs.size() > 0) {
2715 if (std::find(dirs.begin(), dirs.end(), dir) == dirs.end()) {
2716 perm &= ~spec;
2717 } else {
2718 perm |= spec;
2719 }
2720 }
2721}
2722
2723bool
2725#ifdef DEBUG_TURNSIGNS
2726 std::cout << "applyTurnSigns edge=" << getID() << "\n";
2727#endif
2728 // build a map of target edges and lanes
2729 std::vector<const NBEdge*> targets;
2730 std::map<const NBEdge*, std::vector<int> > toLaneMap;
2731 for (const Connection& c : myConnections) {
2732 if (myLanes[c.fromLane].turnSigns != 0) {
2733 if (std::find(targets.begin(), targets.end(), c.toEdge) == targets.end()) {
2734 targets.push_back(c.toEdge);
2735 }
2736 toLaneMap[c.toEdge].push_back(c.toLane);
2737 }
2738 }
2739 // might be unsorted due to bike lane connections
2740 for (auto& item : toLaneMap) {
2741 std::sort(item.second.begin(), item.second.end());
2742 }
2743
2744 // check number of distinct signed directions and count the number of signs for each direction
2745 std::map<LinkDirection, int> signCons;
2746 int allDirs = 0;
2747 for (const Lane& lane : myLanes) {
2748 allDirs |= lane.turnSigns;
2749 for (LinkDirection dir : decodeTurnSigns(lane.turnSigns)) {
2750 signCons[dir]++;
2751 }
2752 }
2753 allDirs |= allDirs >> TURN_SIGN_SHIFT_BUS;
2754 allDirs |= allDirs >> TURN_SIGN_SHIFT_TAXI;
2755 allDirs |= allDirs >> TURN_SIGN_SHIFT_BICYCLE;
2756
2757 if ((allDirs & (int)LinkDirection::NODIR) != 0) {
2758 targets.push_back(nullptr); // dead end
2759 }
2760
2761 SVCPermissions defaultPermissions = SVC_PASSENGER | SVC_DELIVERY;
2762 // build a mapping from sign directions to targets
2763 std::vector<LinkDirection> signedDirs = decodeTurnSigns(allDirs);
2764 std::map<LinkDirection, const NBEdge*> dirMap;
2765#ifdef DEBUG_TURNSIGNS
2766 std::cout << " numDirs=" << signedDirs.size() << " numTargets=" << targets.size() << "\n";
2767#endif
2768 if (signedDirs.size() > targets.size()) {
2769 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions but only % targets"), getID(), signedDirs.size(), targets.size());
2770 return false;
2771 } else if (signedDirs.size() < targets.size()) {
2772 // we need to drop some targets (i.e. turn-around)
2773 // use sumo-directions as a guide
2774 std::vector<LinkDirection> sumoDirs;
2775 for (const NBEdge* to : targets) {
2776 sumoDirs.push_back(myTo->getDirection(this, to));
2777 }
2778 // remove targets to the left
2779 bool checkMore = true;
2780 while (signedDirs.size() < targets.size() && checkMore) {
2781 checkMore = false;
2782 //std::cout << getID() << " sumoDirs=" << joinToString(sumoDirs, ",") << " signedDirs=" << joinToString(signedDirs, ",") << "\n";
2783 if (sumoDirs.back() != signedDirs.back()) {
2784 targets.pop_back();
2785 sumoDirs.pop_back();
2786 checkMore = true;
2787 }
2788 }
2789 // remove targets to the right
2790 checkMore = true;
2791 while (signedDirs.size() < targets.size() && checkMore) {
2792 checkMore = false;
2793 if (sumoDirs.front() != signedDirs.front()) {
2794 targets.erase(targets.begin());
2795 sumoDirs.erase(sumoDirs.begin());
2796 checkMore = true;
2797 }
2798 }
2799 // remove targets by permissions
2800 int i = 0;
2801 while (signedDirs.size() < targets.size() && i < (int)targets.size()) {
2802 if (targets[i] != nullptr && (targets[i]->getPermissions() & defaultPermissions) == 0) {
2803 targets.erase(targets.begin() + i);
2804 sumoDirs.erase(sumoDirs.begin() + i);
2805 } else {
2806 i++;
2807 }
2808 }
2809 if (signedDirs.size() != targets.size()) {
2810 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());
2811 return false;
2812 }
2813 }
2814 // directions and connections are both sorted from right to left
2815 for (int i = 0; i < (int)signedDirs.size(); i++) {
2816 dirMap[signedDirs[i]] = targets[i];
2817 }
2818 // check whether we have enough target lanes for a each signed direction
2819 for (auto item : signCons) {
2820 const LinkDirection dir = item.first;
2821 if (dir == LinkDirection::NODIR) {
2822 continue;
2823 }
2824 const NBEdge* to = dirMap[dir];
2825 int candidates = to->getNumLanesThatAllow(defaultPermissions, false);
2826 if (candidates == 0) {
2827 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because the target edge '%' has no suitable lanes"), getID(), to->getID());
2828 return false;
2829 }
2830 std::vector<int>& knownTargets = toLaneMap[to];
2831 if ((int)knownTargets.size() < item.second) {
2832 if (candidates < item.second) {
2833 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed connections with directions '%' but target edge '%' has only % suitable lanes"),
2834 getID(), item.second, toString(dir), to->getID(), candidates);
2835 return false;
2836 }
2837 int i;
2838 int iInc;
2839 int iEnd;
2840 if (dir > LinkDirection::STRAIGHT) {
2841 // set more targets on the left
2842 i = to->getNumLanes() - 1;
2843 iInc = -1;
2844 iEnd = -1;
2845 } else {
2846 // set more targets on the right
2847 i = 0;
2848 iInc = 1;
2849 iEnd = to->getNumLanes();
2850 }
2851 while ((int)knownTargets.size() < item.second && i != iEnd) {
2852 if ((to->getPermissions(i) & defaultPermissions) != 0) {
2853 if (std::find(knownTargets.begin(), knownTargets.end(), i) == knownTargets.end()) {
2854 knownTargets.push_back(i);
2855 }
2856 }
2857 i += iInc;
2858 }
2859 if ((int)knownTargets.size() != item.second) {
2860 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because not enough target lanes could be determined for direction '%'"), getID(), toString(dir));
2861 return false;
2862 }
2863 std::sort(knownTargets.begin(), knownTargets.end());
2864 }
2865 }
2866 std::map<const NBEdge*, int> toLaneIndex;
2867 for (int i = 0; i < getNumLanes(); i++) {
2868 const int turnSigns = myLanes[i].turnSigns;
2869 // no turnSigns are given for bicycle lanes and sidewalks
2870 if (turnSigns != 0) {
2871 // clear existing connections
2872 for (auto it = myConnections.begin(); it != myConnections.end();) {
2873 if (it->fromLane == i) {
2874 it = myConnections.erase(it);
2875 } else {
2876 it++;
2877 }
2878 }
2879 // add new connections
2880 int allSigns = (turnSigns
2881 | turnSigns >> TURN_SIGN_SHIFT_BUS
2882 | turnSigns >> TURN_SIGN_SHIFT_TAXI
2883 | turnSigns >> TURN_SIGN_SHIFT_BICYCLE);
2884 std::vector<LinkDirection> all = decodeTurnSigns(turnSigns);
2885 std::vector<LinkDirection> bus = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BUS);
2886 std::vector<LinkDirection> taxi = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_TAXI);
2887 std::vector<LinkDirection> bike = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BICYCLE);
2888 //std::cout << " allSigns=" << allSigns << " turnSigns=" << turnSigns << " bus=" << bus.size() << "\n";
2889 SVCPermissions fromP = getPermissions(i);
2890 if ((fromP & SVC_PASSENGER) != 0) {
2891 // if the source permits passenger traffic, the target should too
2892 fromP = SVC_PASSENGER;
2893 }
2894 for (LinkDirection dir : decodeTurnSigns(allSigns)) {
2895 SVCPermissions perm = 0;
2896 updateTurnPermissions(perm, dir, SVCAll, all);
2897 updateTurnPermissions(perm, dir, SVC_BUS, bus);
2898 updateTurnPermissions(perm, dir, SVC_TAXI, taxi);
2899 updateTurnPermissions(perm, dir, SVC_BICYCLE, bike);
2900 if (perm == SVCAll) {
2901 perm = SVC_UNSPECIFIED;
2902 }
2903 //std::cout << " lane=" << i << " dir=" << toString(dir) << " perm=" << getVehicleClassNames(perm) << "\n";
2904 NBEdge* to = const_cast<NBEdge*>(dirMap[dir]);
2905 if (to != nullptr) {
2906 if (toLaneIndex.count(to) == 0) {
2907 // initialize to rightmost feasible lane
2908 int toLane = toLaneMap[to][0];
2909 while ((to->getPermissions(toLane) & fromP) == 0 && (toLane + 1 < to->getNumLanes())) {
2910 toLane++;
2911 /*
2912 if (toLane == to->getNumLanes()) {
2913 SOFT_ASSERT(false);
2914 #ifdef DEBUG_TURNSIGNS
2915 std::cout << " could not find passenger lane for target=" << to->getID() << "\n";
2916 #endif
2917 return false;
2918 }
2919 */
2920 }
2921#ifdef DEBUG_TURNSIGNS
2922 std::cout << " target=" << to->getID() << " initial toLane=" << toLane << "\n";
2923#endif
2924 toLaneIndex[to] = toLane;
2925 }
2926#ifdef DEBUG_TURNSIGNS
2927 //std::cout << " set fromLane=" << i << " to=" << to->getID() << " toLane=" << toLaneIndex[to] << "\n";
2928#endif
2929 setConnection(i, to, toLaneIndex[to], Lane2LaneInfoType::VALIDATED, true,
2934 perm);
2935 if (toLaneIndex[to] < to->getNumLanes() - 1
2936 && (to->getPermissions(toLaneIndex[to] + 1) & fromP) != 0) {
2937 toLaneIndex[to]++;
2938 } else if (toLaneIndex[to] < to->getNumLanes() - 2
2939 && (to->getPermissions(toLaneIndex[to] + 2) & fromP) != 0) {
2940 // skip forbidden lane
2941 toLaneIndex[to] += 2;
2942 }
2943 }
2944 }
2945 }
2946 }
2949 return true;
2950}
2951
2952
2953bool
2955#ifdef DEBUG_CONNECTION_GUESSING
2956 if (DEBUGCOND) {
2957 std::cout << "recheckLanes (initial) edge=" << getID() << "\n";
2958 for (Connection& c : myConnections) {
2959 std::cout << " conn " << c.getDescription(this) << "\n";
2960 }
2962 std::cout << " connToDelete " << c.getDescription(this) << "\n";
2963 }
2964 }
2965#endif
2966 // check delayed removals
2967 for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
2968 removeFromConnections(it->toEdge, it->fromLane, it->toLane, false, false, true);
2969 }
2970 std::vector<int> connNumbersPerLane(myLanes.size(), 0);
2971 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
2972 if ((*i).toEdge == nullptr || (*i).fromLane < 0 || (*i).toLane < 0) {
2973 i = myConnections.erase(i);
2974 } else {
2975 if ((*i).fromLane >= 0) {
2976 ++connNumbersPerLane[(*i).fromLane];
2977 }
2978 ++i;
2979 }
2980 }
2982#ifdef DEBUG_TURNSIGNS
2983 if (myLanes.back().turnSigns != 0) {
2984 std::cout << getID() << " hasTurnSigns\n";
2985 if (myTurnSignTarget != myTo->getID()) {
2986 std::cout << " tst=" << myTurnSignTarget << " to=" << myTo->getID() << "\n";
2987 }
2988 }
2989#endif
2990 if (myLanes.back().turnSigns == 0 || myTurnSignTarget != myTo->getID() || !applyTurnSigns()) {
2991 // check #1:
2992 // If there is a lane with no connections and any neighbour lane has
2993 // more than one connections, try to move one of them.
2994 // This check is only done for edges which connections were assigned
2995 // using the standard algorithm.
2996 for (int i = 0; i < (int)myLanes.size(); i++) {
2997 if (connNumbersPerLane[i] == 0 && !isForbidden(getPermissions(i) & ~SVC_PEDESTRIAN)) {
2998 // dead-end lane found
2999 bool hasDeadEnd = true;
3000 // find lane with two connections or more to the right of the current lane
3001 for (int i2 = i - 1; hasDeadEnd && i2 >= 0; i2--) {
3002 if (getPermissions(i) != getPermissions(i2)) {
3003 break;
3004 }
3005 if (connNumbersPerLane[i2] > 1) {
3006 connNumbersPerLane[i2]--;
3007 for (int i3 = i2; i3 != i; i3++) {
3011 }
3012 hasDeadEnd = false;
3013 }
3014 }
3015 if (hasDeadEnd) {
3016 // find lane with two connections or more to the left of the current lane
3017 for (int i2 = i + 1; hasDeadEnd && i2 < getNumLanes(); i2++) {
3018 if (getPermissions(i) != getPermissions(i2)) {
3019 break;
3020 }
3021 if (connNumbersPerLane[i2] > 1) {
3022 connNumbersPerLane[i2]--;
3023 for (int i3 = i2; i3 != i; i3--) {
3027 }
3028 hasDeadEnd = false;
3029 }
3030 }
3031 }
3032 if (hasDeadEnd && myTo->getOutgoingEdges().size() > 1) {
3033 int passengerLanes = 0;
3034 int passengerTargetLanes = 0;
3035 for (const Lane& lane : myLanes) {
3036 if ((lane.permissions & SVC_PASSENGER) != 0) {
3037 passengerLanes++;
3038 }
3039 }
3040 for (const NBEdge* out : myTo->getOutgoingEdges()) {
3041 if (!isTurningDirectionAt(out)) {
3042 for (const Lane& lane : out->getLanes()) {
3043 if ((lane.permissions & SVC_PASSENGER) != 0) {
3044 passengerTargetLanes++;
3045 }
3046 }
3047 }
3048 }
3049 if (passengerLanes > 0 && passengerLanes <= passengerTargetLanes) {
3050 // no need for dead-ends
3051 if (i > 0) {
3052 // check if a connection to the right has a usable target to the left of its target
3053 std::vector<Connection> rightCons = getConnectionsFromLane(i - 1);
3054 if (rightCons.size() > 0) {
3055 const Connection& rc = rightCons.back();
3056 NBEdge* to = rc.toEdge;
3057 int toLane = rc.toLane + 1;
3058 if (toLane < to->getNumLanes()
3059 && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
3060 && !hasConnectionTo(to, toLane)) {
3061#ifdef DEBUG_CONNECTION_CHECKING
3062 std::cout << " recheck1 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
3063#endif
3065 hasDeadEnd = false;
3068 }
3069 if (hasDeadEnd) {
3070 // check if a connection to the right has a usable target to the right of its target
3071 toLane = rc.toLane - 1;
3072 if (toLane >= 0
3073 && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(rc.toLane)) != 0
3074 && (getPermissions(rc.fromLane) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
3075 && !hasConnectionTo(to, toLane)) {
3076 // shift the right lane connection target right and connect the dead lane to the old target
3077 getConnectionRef(rc.fromLane, to, rc.toLane).toLane = toLane;
3078#ifdef DEBUG_CONNECTION_CHECKING
3079 std::cout << " recheck2 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << (toLane + 1) << "\n";
3080#endif
3081 setConnection(i, to, toLane + 1, Lane2LaneInfoType::COMPUTED);
3082 hasDeadEnd = false;
3085 }
3086 }
3087 }
3088 }
3089 if (hasDeadEnd && i < getNumLanes() - 1) {
3090 // check if a connection to the left has a usable target to the right of its target
3091 std::vector<Connection> leftCons = getConnectionsFromLane(i + 1);
3092 if (leftCons.size() > 0) {
3093 NBEdge* to = leftCons.front().toEdge;
3094 int toLane = leftCons.front().toLane - 1;
3095 if (toLane >= 0
3096 && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
3097 && !hasConnectionTo(to, toLane)) {
3098#ifdef DEBUG_CONNECTION_CHECKING
3099 std::cout << " recheck3 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
3100#endif
3102 hasDeadEnd = false;
3105 }
3106 }
3107 }
3108#ifdef ADDITIONAL_WARNINGS
3109 if (hasDeadEnd) {
3110 WRITE_WARNING("Found dead-end lane " + getLaneID(i));
3111 }
3112#endif
3113 }
3114 }
3115 }
3116 }
3118 }
3119 }
3120 // check involuntary dead end at "real" junctions
3121 if (getPermissions() != SVC_PEDESTRIAN) {
3122 if (myConnections.empty() && myTo->getOutgoingEdges().size() > 1 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
3123 WRITE_WARNINGF(TL("Edge '%' is not connected to outgoing edges at junction '%'."), getID(), myTo->getID());
3124 }
3125 const EdgeVector& incoming = myFrom->getIncomingEdges();
3126 if (incoming.size() > 1) {
3127 for (int i = 0; i < (int)myLanes.size(); i++) {
3128 if (getPermissions(i) != 0 && getPermissions(i) != SVC_PEDESTRIAN) {
3129 bool connected = false;
3130 for (std::vector<NBEdge*>::const_iterator in = incoming.begin(); in != incoming.end(); ++in) {
3131 if ((*in)->hasConnectionTo(this, i)) {
3132 connected = true;
3133 break;
3134 }
3135 }
3136 if (!connected) {
3137 WRITE_WARNINGF(TL("Lane '%' is not connected from any incoming edge at junction '%'."), getLaneID(i), myFrom->getID());
3138 }
3139 }
3140 }
3141 }
3142 }
3143 // avoid deadend due to change prohibitions
3144 if (getNumLanes() > 1 && myConnections.size() > 0) {
3145 for (int i = 0; i < (int)myLanes.size(); i++) {
3146 Lane& lane = myLanes[i];
3147 if ((connNumbersPerLane[i] == 0 || ((lane.accelRamp || (i > 0 && myLanes[i - 1].accelRamp && connNumbersPerLane[i - 1] > 0))
3148 && getSuccessors(SVC_PASSENGER).size() > 1))
3150 const bool forbiddenLeft = lane.changeLeft != SVCAll && lane.changeLeft != SVC_IGNORING && lane.changeLeft != SVC_UNSPECIFIED;
3151 const bool forbiddenRight = lane.changeRight != SVCAll && lane.changeRight != SVC_IGNORING && lane.changeRight != SVC_UNSPECIFIED;
3152 if (forbiddenLeft && (i == 0 || forbiddenRight)) {
3154 WRITE_WARNINGF(TL("Ignoring changeLeft prohibition for '%' to avoid dead-end"), getLaneID(i));
3155 } else if (forbiddenRight && (i == getNumLanes() - 1 || (i > 0 && myLanes[i - 1].accelRamp))) {
3157 WRITE_WARNINGF(TL("Ignoring changeRight prohibition for '%' to avoid dead-end"), getLaneID(i));
3158 }
3159 }
3160 }
3161 }
3162#ifdef ADDITIONAL_WARNINGS
3163 // check for connections with bad access permissions
3164 for (const Connection& c : myConnections) {
3165 SVCPermissions fromP = getPermissions(c.fromLane);
3166 SVCPermissions toP = c.toEdge->getPermissions(c.toLane);
3167 if ((fromP & SVC_PASSENGER) != 0
3168 && toP == SVC_BICYCLE) {
3169 bool hasAlternative = false;
3170 for (const Connection& c2 : myConnections) {
3171 if (c.fromLane == c2.fromLane && c.toEdge == c2.toEdge
3172 && (c.toEdge->getPermissions(c2.toLane) & SVC_PASSENGER) != 0) {
3173 hasAlternative = true;
3174 }
3175 }
3176 if (!hasAlternative) {
3177 WRITE_WARNING("Road lane ends on bikeLane for connection " + c.getDescription(this));
3178 }
3179 }
3180 }
3181
3182#endif
3183#ifdef DEBUG_CONNECTION_GUESSING
3184 if (DEBUGCOND) {
3185 std::cout << "recheckLanes (final) edge=" << getID() << "\n";
3186 for (Connection& c : myConnections) {
3187 std::cout << " conn " << c.getDescription(this) << "\n";
3188 }
3189 }
3190#endif
3193 }
3194 return true;
3195}
3196
3197
3198void NBEdge::recheckOpposite(const NBEdgeCont& ec, bool fixOppositeLengths) {
3199 if (getNumLanes() == 0) {
3200 return;
3201 }
3202 const int leftmostLane = getNumLanes() - 1;
3203 // check oppositeID stored in other lanes
3204 for (int i = 0; i < leftmostLane; i++) {
3205 const std::string& oppositeID = getLanes()[i].oppositeID;
3206 NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
3207 if (oppositeID != "" && oppositeID != "-") {
3208 if (getLanes().back().oppositeID == "" && oppEdge != nullptr) {
3209 getLaneStruct(leftmostLane).oppositeID = oppositeID;
3210 WRITE_WARNINGF(TL("Moving opposite lane '%' from invalid lane '%' to lane index %."), oppositeID, getLaneID(i), leftmostLane);
3211 } else {
3212 WRITE_WARNINGF(TL("Removing opposite lane '%' for invalid lane '%'."), oppositeID, getLaneID(i));
3213 }
3214 getLaneStruct(i).oppositeID = "";
3215 }
3216 }
3217 const std::string& oppositeID = getLanes().back().oppositeID;
3218 if (oppositeID != "" && oppositeID != "-") {
3219 NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
3220 if (oppEdge == nullptr) {
3221 WRITE_WARNINGF(TL("Removing unknown opposite lane '%' for edge '%'."), oppositeID, getID());
3222 getLaneStruct(leftmostLane).oppositeID = "";
3223 } else {
3224 if (oppEdge->getFromNode() != getToNode() || oppEdge->getToNode() != getFromNode()) {
3225 WRITE_ERRORF(TL("Opposite lane '%' does not reverse-connect the same nodes as edge '%'!"), oppositeID, getID());
3227 } else {
3228 if (oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
3229 const std::string oppEdgeLeftmost = oppEdge->getLaneID(oppEdge->getNumLanes() - 1);
3230 WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppositeID, getID(), oppEdgeLeftmost);
3231 getLaneStruct(leftmostLane).oppositeID = oppEdgeLeftmost;
3232 }
3233 NBEdge::Lane& oppLane = oppEdge->getLaneStruct(oppEdge->getNumLanes() - 1);
3234 const std::string leftmostID = getLaneID(leftmostLane);
3235 if (oppLane.oppositeID == "") {
3236 WRITE_WARNINGF(TL("Adapting missing opposite lane '%' for edge '%'."), leftmostID, oppEdge->getID());
3237 oppLane.oppositeID = leftmostID;
3238 } else if (oppLane.oppositeID != leftmostID && oppLane.oppositeID != "-") {
3239 const std::string oppOpp = oppLane.oppositeID.substr(0, oppLane.oppositeID.rfind("_"));
3240 NBEdge* oppOppEdge = ec.retrieve(oppOpp);
3241 if (oppOppEdge == nullptr) {
3242 WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppLane.oppositeID, oppEdge->getID(), leftmostID);
3243 oppLane.oppositeID = leftmostID;
3244 } else {
3245 if (oppEdge->getFromNode() != oppOppEdge->getToNode() || oppEdge->getToNode() != oppOppEdge->getFromNode()) {
3246 WRITE_ERRORF(TL("Opposite edge '%' does not reverse-connect the same nodes as edge '%'!"), oppEdge->getID(), oppOppEdge->getID());
3247 } else {
3248 WRITE_WARNINGF(TL("Adapting inconsistent opposite lanes for edges '%', '%' and '%'."), getID(), oppEdge->getID(), oppOpp);
3249 }
3250 oppLane.oppositeID = leftmostID;
3251 NBEdge::Lane& oppOppLane = oppOppEdge->getLaneStruct(oppOppEdge->getNumLanes() - 1);
3252 if (oppOppLane.oppositeID == oppEdge->getLaneID(oppEdge->getNumLanes() - 1)) {
3253 oppOppLane.oppositeID = "";
3254 }
3255 }
3256 }
3257 if (fabs(oppEdge->getLoadedLength() - getLoadedLength()) > NUMERICAL_EPS) {
3258 if (fixOppositeLengths) {
3259 const double avgLength = 0.5 * (getFinalLength() + oppEdge->getFinalLength());
3260 WRITE_WARNINGF(TL("Averaging edge lengths for lane '%' (length %) and edge '%' (length %)."),
3261 oppositeID, oppEdge->getLoadedLength(), getID(), getLoadedLength());
3262 setLoadedLength(avgLength);
3263 oppEdge->setLoadedLength(avgLength);
3264 } else {
3265 WRITE_ERROR("Opposite lane '" + oppositeID + "' (length " + toString(oppEdge->getLoadedLength()) +
3266 ") differs in length from edge '" + getID() + "' (length " +
3267 toString(getLoadedLength()) + "). Set --opposites.guess.fix-lengths to fix this.");
3269 }
3270 }
3271 }
3272 }
3273 }
3274 // check for matching bidi lane shapes (at least for the simple case of 1-lane edges)
3275 const NBEdge* bidi = getBidiEdge();
3276 if (bidi != nullptr && getNumLanes() == 1 && bidi->getNumLanes() == 1 && getID() < bidi->getID()) {
3278 }
3279 // check for valid offset and speed
3280 const double startOffset = isBidiRail() ? getTurnDestination(true)->getEndOffset() : 0;
3281 int i = 0;
3282 for (const NBEdge::Lane& l : getLanes()) {
3283 if (startOffset + l.endOffset > getLength()) {
3284 WRITE_WARNINGF(TL("Invalid endOffset % at lane '%' with length % (startOffset %)."),
3285 toString(l.endOffset), getLaneID(i), toString(l.shape.length()), toString(startOffset));
3286 } else if (l.speed < 0.) {
3287 WRITE_WARNINGF(TL("Negative allowed speed (%) on lane '%', use --speed.minimum to prevent this."), toString(l.speed), getLaneID(i));
3288 } else if (l.speed == 0.) {
3289 WRITE_WARNINGF(TL("Lane '%' has a maximum allowed speed of 0."), getLaneID(i));
3290 }
3291 i++;
3292 }
3293}
3294
3296 // check restrictions
3297 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
3298 Connection& c = *i;
3300 if (common == SVC_PEDESTRIAN || getPermissions(c.fromLane) == SVC_PEDESTRIAN) {
3301 // these are computed in NBNode::buildWalkingAreas
3302#ifdef DEBUG_CONNECTION_CHECKING
3303 std::cout << " remove pedCon " << c.getDescription(this) << "\n";
3304#endif
3305 i = myConnections.erase(i);
3306 } else if (common == 0) {
3307 // no common permissions.
3308 // try to find a suitable target lane to the right
3309 const int origToLane = c.toLane;
3310 c.toLane = -1; // ignore this connection when calling hasConnectionTo
3311 int toLane = origToLane;
3312 while (toLane > 0
3313 && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
3314 && !hasConnectionTo(c.toEdge, toLane)
3315 ) {
3316 toLane--;
3317 }
3318 if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
3319 && !hasConnectionTo(c.toEdge, toLane)) {
3320 c.toLane = toLane;
3321 ++i;
3322 } else {
3323 // try to find a suitable target lane to the left
3324 toLane = origToLane;
3325 while (toLane < (int)c.toEdge->getNumLanes() - 1
3326 && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
3327 && !hasConnectionTo(c.toEdge, toLane)
3328 ) {
3329 toLane++;
3330 }
3331 if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
3332 && !hasConnectionTo(c.toEdge, toLane)) {
3333 c.toLane = toLane;
3334 ++i;
3335 } else {
3336 // no alternative target found
3337#ifdef DEBUG_CONNECTION_CHECKING
3338 std::cout << " remove " << c.getDescription(this) << " with no alternative target\n";
3339#endif
3340 i = myConnections.erase(i);
3341 }
3342 }
3345 // do not allow sharp rail turns
3346#ifdef DEBUG_CONNECTION_CHECKING
3347 std::cout << " remove " << c.getDescription(this) << " (rail turnaround)\n";
3348#endif
3349 i = myConnections.erase(i);
3350 } else {
3351 ++i;
3352 }
3353 }
3354}
3355
3356void
3358 if (outgoing->size() == 0) {
3359 // we have to do this, because the turnaround may have been added before
3360 myConnections.clear();
3361 return;
3362 }
3363
3364#ifdef DEBUG_CONNECTION_GUESSING
3365 if (DEBUGCOND) {
3366 std::cout << " divideOnEdges " << getID() << " outgoing=" << toString(*outgoing) << "\n";
3367 }
3368#endif
3369
3370 // build connections for miv lanes
3371 std::vector<int> availableLanes;
3372 for (int i = 0; i < (int)myLanes.size(); ++i) {
3373 if ((getPermissions(i) & SVC_PASSENGER) != 0) {
3374 availableLanes.push_back(i);
3375 }
3376 }
3377 if (availableLanes.size() > 0) {
3378 divideSelectedLanesOnEdges(outgoing, availableLanes);
3379 }
3380 // build connections for miscellaneous further modes (more than bike,peds,bus and without passenger)
3381 availableLanes.clear();
3382 for (int i = 0; i < (int)myLanes.size(); ++i) {
3383 const SVCPermissions perms = getPermissions(i);
3384 if ((perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) == 0 || (perms & SVC_PASSENGER) != 0 || isForbidden(perms)) {
3385 continue;
3386 }
3387 availableLanes.push_back(i);
3388 }
3389 if (availableLanes.size() > 0) {
3390 divideSelectedLanesOnEdges(outgoing, availableLanes);
3391 }
3392 // build connections for busses from lanes that were excluded in the previous step
3393 availableLanes.clear();
3394 for (int i = 0; i < (int)myLanes.size(); ++i) {
3395 const SVCPermissions perms = getPermissions(i);
3396 if ((perms & SVC_BUS) == 0 || (perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) != 0 || (perms & SVC_PASSENGER) != 0) {
3397 continue;
3398 }
3399 availableLanes.push_back(i);
3400 }
3401 if (availableLanes.size() > 0) {
3402 divideSelectedLanesOnEdges(outgoing, availableLanes);
3403 }
3404 // build connections for bicycles (possibly combined with pedestrians)
3405 availableLanes.clear();
3406 for (int i = 0; i < (int)myLanes.size(); ++i) {
3407 const SVCPermissions perms = getPermissions(i);
3408 if (perms != SVC_BICYCLE && perms != (SVC_BICYCLE | SVC_PEDESTRIAN)) {
3409 continue;
3410 }
3411 availableLanes.push_back(i);
3412 }
3413 if (availableLanes.size() > 0) {
3414 divideSelectedLanesOnEdges(outgoing, availableLanes);
3415 }
3416 // clean up unassigned fromLanes
3417 bool explicitTurnaround = false;
3418 SVCPermissions turnaroundPermissions = SVC_UNSPECIFIED;
3419 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
3420 if ((*i).fromLane == -1) {
3421 if ((*i).toEdge == myTurnDestination && myTurnDestination != nullptr) {
3422 explicitTurnaround = true;
3423 turnaroundPermissions = (*i).permissions;
3424 }
3425 if ((*i).permissions != SVC_UNSPECIFIED) {
3426 for (Connection& c : myConnections) {
3427 if (c.toLane == -1 && c.toEdge == (*i).toEdge) {
3428 // carry over loaded edge2edge permissions
3429 c.permissions = (*i).permissions;
3430 }
3431 }
3432 }
3433 i = myConnections.erase(i);
3434 } else {
3435 ++i;
3436 }
3437 }
3438 if (explicitTurnaround) {
3440 myConnections.back().permissions = turnaroundPermissions;
3441 }
3443}
3444
3445
3446void
3447NBEdge::divideSelectedLanesOnEdges(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
3448 const std::vector<int>& priorities = prepareEdgePriorities(outgoing, availableLanes);
3449 if (priorities.empty()) {
3450 return;
3451 }
3452#ifdef DEBUG_CONNECTION_GUESSING
3453 if (DEBUGCOND) {
3454 std::cout << "divideSelectedLanesOnEdges " << getID() << " out=" << toString(*outgoing) << " prios=" << toString(priorities) << " avail=" << toString(availableLanes) << "\n";
3455 }
3456#endif
3457 // compute the resulting number of lanes that should be used to reach the following edge
3458 const int numOutgoing = (int)outgoing->size();
3459 std::vector<int> resultingLanesFactor;
3460 resultingLanesFactor.reserve(numOutgoing);
3461 int minResulting = std::numeric_limits<int>::max();
3462 for (int i = 0; i < numOutgoing; i++) {
3463 // res / minResulting will be the number of lanes which are meant to reach the current outgoing edge
3464 const int res = priorities[i] * (int)availableLanes.size();
3465 resultingLanesFactor.push_back(res);
3466 if (minResulting > res && res > 0) {
3467 // prevent minResulting from becoming 0
3468 minResulting = res;
3469 }
3470 }
3471 // compute the number of virtual edges
3472 // a virtual edge is used as a replacement for a real edge from now on
3473 // it shall allow to divide the existing lanes on this structure without
3474 // regarding the structure of outgoing edges
3475 int numVirtual = 0;
3476 // compute the transition from virtual to real edges
3477 EdgeVector transition;
3478 transition.reserve(numOutgoing);
3479 for (int i = 0; i < numOutgoing; i++) {
3480 // tmpNum will be the number of connections from this edge to the next edge
3481 assert(i < (int)resultingLanesFactor.size());
3482 const int tmpNum = (resultingLanesFactor[i] + minResulting - 1) / minResulting; // integer division rounding up
3483 numVirtual += tmpNum;
3484 for (int j = 0; j < tmpNum; j++) {
3485 transition.push_back((*outgoing)[i]);
3486 }
3487 }
3488#ifdef DEBUG_CONNECTION_GUESSING
3489 if (DEBUGCOND) {
3490 std::cout << " minResulting=" << minResulting << " numVirtual=" << numVirtual << " availLanes=" << toString(availableLanes) << " resLanes=" << toString(resultingLanesFactor) << " transition=" << toString(transition) << "\n";
3491 }
3492#endif
3493
3494 // assign lanes to edges
3495 // (conversion from virtual to real edges is done)
3496 ToEdgeConnectionsAdder adder(transition);
3497 Bresenham::compute(&adder, static_cast<int>(availableLanes.size()), numVirtual);
3498 const std::map<NBEdge*, std::vector<int> >& l2eConns = adder.getBuiltConnections();
3499 for (NBEdge* const target : *outgoing) {
3500 assert(l2eConns.find(target) != l2eConns.end());
3501 for (const int j : l2eConns.find(target)->second) {
3502 const int fromIndex = availableLanes[j];
3503 if ((getPermissions(fromIndex) & target->getPermissions()) == 0) {
3504 // exclude connection if fromLane and toEdge have no common permissions
3505 continue;
3506 }
3507 if ((getPermissions(fromIndex) & target->getPermissions()) == SVC_PEDESTRIAN) {
3508 // exclude connection if the only commonly permitted class are pedestrians
3509 // these connections are later built in NBNode::buildWalkingAreas
3510 continue;
3511 }
3512 // avoid building more connections than the edge has viable lanes (earlier
3513 // ones have precedence). This is necessary when running divideSelectedLanesOnEdges more than once.
3514 // @todo To decide which target lanes are still available we need to do a
3515 // preliminary lane-to-lane assignment in regard to permissions (rather than to ordering)
3516 const int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
3517 int targetLanes = target->getNumLanes();
3518 if (target->getPermissions(0) == SVC_PEDESTRIAN) {
3519 --targetLanes;
3520 }
3521 if (numConsToTarget >= targetLanes) {
3522 continue;
3523 }
3524 if (myLanes[fromIndex].connectionsDone) {
3525 // we already have complete information about connections from
3526 // this lane. do not add anything else
3527#ifdef DEBUG_CONNECTION_GUESSING
3528 if (DEBUGCOND) {
3529 std::cout << " connectionsDone from " << getID() << "_" << fromIndex << ": ";
3530 for (const Connection& c : getConnectionsFromLane(fromIndex)) {
3531 std::cout << c.getDescription(this) << ", ";
3532 }
3533 std::cout << "\n";
3534 }
3535#endif
3536 continue;
3537 }
3538 myConnections.push_back(Connection(fromIndex, target, -1));
3539#ifdef DEBUG_CONNECTION_GUESSING
3540 if (DEBUGCOND) {
3541 std::cout << " request connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3542 }
3543#endif
3544 }
3545 }
3546
3547 addStraightConnections(outgoing, availableLanes, priorities);
3548}
3549
3550
3551void
3552NBEdge::addStraightConnections(const EdgeVector* outgoing, const std::vector<int>& availableLanes, const std::vector<int>& priorities) {
3553 // ensure sufficient straight connections for the (highest-priority) straight target
3554 const int numOutgoing = (int) outgoing->size();
3555 NBEdge* target = nullptr;
3556 NBEdge* rightOfTarget = nullptr;
3557 NBEdge* leftOfTarget = nullptr;
3558 int maxPrio = 0;
3559 for (int i = 0; i < numOutgoing; i++) {
3560 if (maxPrio < priorities[i]) {
3561 const LinkDirection dir = myTo->getDirection(this, (*outgoing)[i]);
3562 if (dir == LinkDirection::STRAIGHT) {
3563 maxPrio = priorities[i];
3564 target = (*outgoing)[i];
3565 rightOfTarget = i == 0 ? outgoing->back() : (*outgoing)[i - 1];
3566 leftOfTarget = i + 1 == numOutgoing ? outgoing->front() : (*outgoing)[i + 1];
3567 }
3568 }
3569 }
3570 if (target == nullptr) {
3571 return;
3572 }
3573 int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
3574 int targetLanes = (int)target->getNumLanes();
3575 if (target->getPermissions(0) == SVC_PEDESTRIAN) {
3576 --targetLanes;
3577 }
3578 const int numDesiredConsToTarget = MIN2(targetLanes, (int)availableLanes.size());
3579#ifdef DEBUG_CONNECTION_GUESSING
3580 if (DEBUGCOND) {
3581 std::cout << " checking extra lanes for target=" << target->getID() << " cons=" << numConsToTarget << " desired=" << numDesiredConsToTarget << "\n";
3582 }
3583#endif
3584 std::vector<int>::const_iterator it_avail = availableLanes.begin();
3585 while (numConsToTarget < numDesiredConsToTarget && it_avail != availableLanes.end()) {
3586 const int fromIndex = *it_avail;
3587 if (
3588 // not yet connected
3589 (count_if(myConnections.begin(), myConnections.end(), connections_finder(fromIndex, target, -1)) == 0)
3590 // matching permissions
3591 && ((getPermissions(fromIndex) & target->getPermissions()) != 0)
3592 // more than pedestrians
3593 && ((getPermissions(fromIndex) & target->getPermissions()) != SVC_PEDESTRIAN)
3594 // lane not yet fully defined
3595 && !myLanes[fromIndex].connectionsDone
3596 ) {
3597#ifdef DEBUG_CONNECTION_GUESSING
3598 if (DEBUGCOND) {
3599 std::cout << " candidate from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3600 }
3601#endif
3602 // prevent same-edge conflicts
3603 if (
3604 // no outgoing connections to the right from further left
3605 ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
3606 // no outgoing connections to the left from further right
3607 && (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)) {
3608#ifdef DEBUG_CONNECTION_GUESSING
3609 if (DEBUGCOND) {
3610 std::cout << " request additional connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3611 }
3612#endif
3613 myConnections.push_back(Connection(fromIndex, target, -1));
3614 numConsToTarget++;
3615 } else {
3616#ifdef DEBUG_CONNECTION_GUESSING
3617 if (DEBUGCOND) std::cout
3618 << " fail check1="
3619 << ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
3620 << " check2=" << (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)
3621 << " rightOfTarget=" << rightOfTarget->getID()
3622 << " leftOfTarget=" << leftOfTarget->getID()
3623 << "\n";
3624#endif
3625
3626 }
3627 }
3628 ++it_avail;
3629 }
3630}
3631
3632
3633const std::vector<int>
3634NBEdge::prepareEdgePriorities(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
3635 std::vector<int> priorities;
3636 MainDirections mainDirections(*outgoing, this, myTo, availableLanes);
3637 const int dist = mainDirections.getStraightest();
3638 if (dist == -1) {
3639 return priorities;
3640 }
3641 // copy the priorities first
3642 priorities.reserve(outgoing->size());
3643 for (const NBEdge* const out : *outgoing) {
3644 int prio = NBNode::isTrafficLight(myTo->getType()) ? 0 : out->getJunctionPriority(myTo);
3645 assert((prio + 1) * 2 > 0);
3646 prio = (prio + 1) * 2;
3647 priorities.push_back(prio);
3648 }
3649 // when the right turning direction has not a higher priority, divide
3650 // the importance by 2 due to the possibility to leave the junction
3651 // faster from this lane
3652#ifdef DEBUG_CONNECTION_GUESSING
3653 if (DEBUGCOND) std::cout << " prepareEdgePriorities " << getID()
3654 << " outgoing=" << toString(*outgoing)
3655 << " priorities1=" << toString(priorities)
3656 << " dist=" << dist
3657 << "\n";
3658#endif
3659 if (dist != 0 && !mainDirections.includes(MainDirections::Direction::RIGHTMOST)) {
3660 assert(priorities.size() > 0);
3661 priorities[0] /= 2;
3662#ifdef DEBUG_CONNECTION_GUESSING
3663 if (DEBUGCOND) {
3664 std::cout << " priorities2=" << toString(priorities) << "\n";
3665 }
3666#endif
3667 }
3668 // HEURISTIC:
3669 // when no higher priority exists, let the forward direction be
3670 // the main direction
3671 if (mainDirections.empty()) {
3672 assert(dist < (int)priorities.size());
3673 priorities[dist] *= 2;
3674#ifdef DEBUG_CONNECTION_GUESSING
3675 if (DEBUGCOND) {
3676 std::cout << " priorities3=" << toString(priorities) << "\n";
3677 }
3678#endif
3679 }
3681 priorities[dist] += 1;
3682 } else {
3683 // try to ensure separation of left turns
3685 priorities[0] /= 4;
3686 priorities[(int)priorities.size() - 1] /= 2;
3687#ifdef DEBUG_CONNECTION_GUESSING
3688 if (DEBUGCOND) {
3689 std::cout << " priorities6=" << toString(priorities) << "\n";
3690 }
3691#endif
3692 } else if (mainDirections.includes(MainDirections::Direction::RIGHTMOST)
3693 && outgoing->size() > 2
3694 && availableLanes.size() == 2
3695 && (*outgoing)[dist]->getPriority() == (*outgoing)[0]->getPriority()) {
3696 priorities[0] /= 4;
3697 priorities.back() /= 2;
3698#ifdef DEBUG_CONNECTION_GUESSING
3699 if (DEBUGCOND) {
3700 std::cout << " priorities7=" << toString(priorities) << "\n";
3701 }
3702#endif
3703 }
3704 }
3705 if (mainDirections.includes(MainDirections::Direction::FORWARD)) {
3706 if (myLanes.size() > 2) {
3707 priorities[dist] *= 2;
3708#ifdef DEBUG_CONNECTION_GUESSING
3709 if (DEBUGCOND) {
3710 std::cout << " priorities4=" << toString(priorities) << "\n";
3711 }
3712#endif
3713 } else {
3714 priorities[dist] *= 3;
3715#ifdef DEBUG_CONNECTION_GUESSING
3716 if (DEBUGCOND) {
3717 std::cout << " priorities5=" << toString(priorities) << "\n";
3718 }
3719#endif
3720 }
3721 }
3722 return priorities;
3723}
3724
3725
3726void
3727NBEdge::appendTurnaround(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike, bool checkPermissions) {
3728 // do nothing if no turnaround is known
3730 return;
3731 }
3732 // do nothing if the destination node is controlled by a tls and no turnarounds
3733 // shall be appended for such junctions
3734 if (noTLSControlled && myTo->isTLControlled()) {
3735 return;
3736 }
3737 if (noFringe && myTo->getFringeType() == FringeType::OUTER) {
3738 return;
3739 }
3740 bool isDeadEnd = true;
3741 for (const Connection& c : myConnections) {
3742 if ((c.toEdge->getPermissions(c.toLane)
3743 & getPermissions(c.fromLane)
3744 & SVC_PASSENGER) != 0
3745 || (c.toEdge->getPermissions() & getPermissions()) == getPermissions()) {
3746 isDeadEnd = false;
3747 break;
3748 }
3749 }
3750 if (onlyDeadends && !isDeadEnd) {
3751 return;
3752 }
3753 const int fromLane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
3754 if (onlyTurnlane) {
3755 for (const Connection& c : getConnectionsFromLane(fromLane)) {
3756 LinkDirection dir = myTo->getDirection(this, c.toEdge);
3757 if (dir != LinkDirection::LEFT && dir != LinkDirection::PARTLEFT) {
3758 return;
3759 }
3760 }
3761 }
3763 if (checkPermissions) {
3764 if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == 0) {
3765 // exclude connection if fromLane and toEdge have no common permissions
3766 return;
3767 }
3768 if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == SVC_PEDESTRIAN) {
3769 // exclude connection if the only commonly permitted class are pedestrians
3770 // these connections are later built in NBNode::buildWalkingAreas
3771 return;
3772 }
3773 }
3774 // avoid railway turn-arounds
3777 // except at dead-ends on bidi-edges where they model a reversal in train direction
3778 // @todo #4382: once the network fringe is tagged, it also should not receive turn-arounds)
3779 if (isBidiRail() && isRailDeadEnd()) {
3780 // add a slow connection because direction-reversal implies stopping
3782 return;
3783 } else {
3784 return;
3785 }
3786 };
3787 if (noGeometryLike && !isDeadEnd) {
3788 // ignore paths and service entrances if this edge is for passenger traffic
3789 if (myTo->geometryLike() || ((getPermissions() & SVC_PASSENGER) != 0
3790 && !onlyTurnlane
3791 && myTo->geometryLike(
3794 // make sure the turnDestination has other incoming edges
3796 if (turnIncoming.size() > 1) {
3797 // this edge is always part of incoming
3798 return;
3799 }
3800 }
3801 }
3803}
3804
3805
3806bool
3807NBEdge::isTurningDirectionAt(const NBEdge* const edge) const {
3808 // maybe it was already set as the turning direction
3809 if (edge == myTurnDestination) {
3810 return true;
3811 } else if (myTurnDestination != nullptr) {
3812 // otherwise - it's not if a turning direction exists
3813 return false;
3814 }
3815 return edge == myPossibleTurnDestination;
3816}
3817
3818
3819NBNode*
3820NBEdge::tryGetNodeAtPosition(double pos, double tolerance) const {
3821 // return the from-node when the position is at the begin of the edge
3822 if (pos < tolerance) {
3823 return myFrom;
3824 }
3825 // return the to-node when the position is at the end of the edge
3826 if (pos > myLength - tolerance) {
3827 return myTo;
3828 }
3829 return nullptr;
3830}
3831
3832
3833void
3835 int lanes = e->getNumLanes();
3836 for (int i = 0; i < lanes; i++) {
3837 for (const NBEdge::Connection& el : e->getConnectionsFromLane(i)) {
3838 assert(el.tlID == "");
3839 addLane2LaneConnection(i + laneOff, el.toEdge, el.toLane, Lane2LaneInfoType::COMPUTED);
3840 }
3841 }
3842}
3843
3844
3845bool
3849
3850
3851double
3853 return SUMO_const_laneWidth * (double)myLanes.size();
3854}
3855
3856
3857bool
3858NBEdge::mayBeTLSControlled(int fromLane, NBEdge* toEdge, int toLane) const {
3859 for (const Connection& c : myConnections) {
3860 if (c.fromLane == fromLane && c.toEdge == toEdge && c.toLane == toLane && c.uncontrolled) {
3861 return false;
3862 }
3863 }
3864 return true;
3865}
3866
3867
3868bool
3869NBEdge::setControllingTLInformation(const NBConnection& c, const std::string& tlID) {
3870 const int fromLane = c.getFromLane();
3871 NBEdge* toEdge = c.getTo();
3872 const int toLane = c.getToLane();
3873 const int tlIndex = c.getTLIndex();
3874 const int tlIndex2 = c.getTLIndex2();
3875 // check whether the connection was not set as not to be controled previously
3876 if (!mayBeTLSControlled(fromLane, toEdge, toLane)) {
3877 return false;
3878 }
3879
3880 assert(fromLane < 0 || fromLane < (int) myLanes.size());
3881 // try to use information about the connections if given
3882 if (fromLane >= 0 && toLane >= 0) {
3883 // find the specified connection
3884 std::vector<Connection>::iterator i =
3885 find_if(myConnections.begin(), myConnections.end(), connections_finder(fromLane, toEdge, toLane));
3886 // ok, we have to test this as on the removal of self-loop edges some connections
3887 // will be reassigned
3888 if (i != myConnections.end()) {
3889 // get the connection
3890 Connection& connection = *i;
3891 // set the information about the tl
3892 connection.tlID = tlID;
3893 connection.tlLinkIndex = tlIndex;
3894 connection.tlLinkIndex2 = tlIndex2;
3895 return true;
3896 }
3897 }
3898 // if the original connection was not found, set the information for all
3899 // connections
3900 int no = 0;
3901 bool hadError = false;
3902 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
3903 if ((*i).toEdge != toEdge) {
3904 continue;
3905 }
3906 if (fromLane >= 0 && fromLane != (*i).fromLane) {
3907 continue;
3908 }
3909 if (toLane >= 0 && toLane != (*i).toLane) {
3910 continue;
3911 }
3912 if ((*i).tlID == "") {
3913 (*i).tlID = tlID;
3914 (*i).tlLinkIndex = tlIndex;
3915 (*i).tlLinkIndex2 = tlIndex2;
3916 no++;
3917 } else {
3918 if ((*i).tlID != tlID && (*i).tlLinkIndex == tlIndex) {
3919 WRITE_WARNINGF(TL("The lane '%' on edge '%' already had a traffic light signal."), i->fromLane, getID());
3920 hadError = true;
3921 }
3922 }
3923 }
3924 if (hadError && no == 0) {
3925 WRITE_WARNINGF(TL("Could not set any signal of the tlLogic '%' (unknown group)."), tlID);
3926 }
3927 return true;
3928}
3929
3930
3931void
3933 for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); it++) {
3934 it->tlID = "";
3935 }
3936}
3937
3938
3941 PositionVector ret;
3942 int lane;
3943 if (myFrom == (&n)) {
3944 // outgoing
3946 ret = myLanes[lane].shape;
3947 } else {
3948 // incoming
3950 ret = myLanes[lane].shape.reverse();
3951 }
3952 ret.move2side(getLaneWidth(lane) / 2.);
3953 return ret;
3954}
3955
3956
3959 PositionVector ret;
3960 int lane;
3961 if (myFrom == (&n)) {
3962 // outgoing
3964 ret = myLanes[lane].shape;
3965 } else {
3966 // incoming
3968 ret = myLanes[lane].shape.reverse();
3969 }
3970 ret.move2side(-getLaneWidth(lane) / 2.);
3971 return ret;
3972}
3973
3974
3975bool
3976NBEdge::expandableBy(NBEdge* possContinuation, std::string& reason) const {
3977 // ok, the number of lanes must match
3978 if (myLanes.size() != possContinuation->myLanes.size()) {
3979 reason = "laneNumber";
3980 return false;
3981 }
3982 // do not create self loops
3983 if (myFrom == possContinuation->myTo) {
3984 reason = "loop";
3985 return false;
3986 }
3987 // conserve bidi-rails
3988 if (isBidiRail() != possContinuation->isBidiRail()) {
3989 reason = "bidi-rail";
3990 return false;
3991 }
3992 // also, check whether the connections - if any exit do allow to join
3993 // both edges
3994 // This edge must have a one-to-one connection to the following lanes
3995 switch (myStep) {
3997 break;
3999 break;
4001 // the following edge must be connected
4002 const EdgeVector& conn = getConnectedEdges();
4003 if (find(conn.begin(), conn.end(), possContinuation) == conn.end()) {
4004 reason = "disconnected";
4005 return false;
4006 }
4007 }
4008 break;
4013 // the possible continuation must be connected
4014 if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(possContinuation)) == myConnections.end()) {
4015 reason = "disconnected";
4016 return false;
4017 }
4018 // all lanes must go to the possible continuation
4019 std::vector<int> conns = getConnectionLanes(possContinuation);
4020 const int offset = MAX2(0, getFirstNonPedestrianLaneIndex(NBNode::FORWARD, true));
4021 if (conns.size() < myLanes.size() - offset) {
4022 reason = "some lanes disconnected";
4023 return false;
4024 }
4025 }
4026 break;
4027 default:
4028 break;
4029 }
4030 const double minLength = OptionsCont::getOptions().getFloat("geometry.remove.min-length");
4031 if (minLength > 0 && (possContinuation->getLoadedLength() < minLength || getLoadedLength() < minLength)) {
4032 return true;
4033 }
4034 const double maxJunctionSize = OptionsCont::getOptions().getFloat("geometry.remove.max-junction-size");
4035 if (maxJunctionSize >= 0) {
4036 const double junctionSize = myGeom.back().distanceTo2D(possContinuation->myGeom.front());
4037 if (junctionSize > maxJunctionSize + POSITION_EPS) {
4038 reason = "junction size (" + toString(junctionSize) + ") > max-junction-size (" + toString(maxJunctionSize) + ")";
4039 return false;
4040 }
4041 }
4042 // the priority, too (?)
4043 if (getPriority() != possContinuation->getPriority()) {
4044 reason = "priority";
4045 return false;
4046 }
4047 // the speed allowed
4048 if (mySpeed != possContinuation->mySpeed) {
4049 reason = "speed";
4050 return false;
4051 }
4052 // the routingType
4053 if (myRoutingType != possContinuation->myRoutingType) {
4054 reason = "routingType";
4055 return false;
4056 }
4057 // spreadtype should match or it will look ugly
4058 if (myLaneSpreadFunction != possContinuation->myLaneSpreadFunction) {
4059 reason = "spreadType";
4060 return false;
4061 }
4062 // matching lanes must have identical properties
4063 for (int i = 0; i < (int)myLanes.size(); i++) {
4064 if (myLanes[i].speed != possContinuation->myLanes[i].speed) {
4065 reason = "lane " + toString(i) + " speed";
4066 return false;
4067 } else if (myLanes[i].permissions != possContinuation->myLanes[i].permissions) {
4068 reason = "lane " + toString(i) + " permissions";
4069 return false;
4070 } else if (myLanes[i].changeLeft != possContinuation->myLanes[i].changeLeft || myLanes[i].changeRight != possContinuation->myLanes[i].changeRight) {
4071 reason = "lane " + toString(i) + " change restrictions";
4072 return false;
4073 } else if (myLanes[i].width != possContinuation->myLanes[i].width &&
4074 fabs(myLanes[i].width - possContinuation->myLanes[i].width) > OptionsCont::getOptions().getFloat("geometry.remove.width-tolerance")) {
4075 reason = "lane " + toString(i) + " width";
4076 return false;
4077 }
4078 }
4079 // if given identically osm names
4080 if (!OptionsCont::getOptions().isDefault("output.street-names") && myStreetName != possContinuation->getStreetName()
4081 && ((myStreetName != "" && possContinuation->getStreetName() != "")
4082 // only permit merging a short unnamed road with a longer named road
4083 || (myStreetName != "" && myLength <= possContinuation->getLength())
4084 || (myStreetName == "" && myLength >= possContinuation->getLength()))) {
4085 return false;
4086 }
4087
4088 return true;
4089}
4090
4091
4092void
4094 // append geometry
4095 myGeom.append(e->myGeom);
4096 for (int i = 0; i < (int)myLanes.size(); i++) {
4097 myLanes[i].customShape.append(e->myLanes[i].customShape);
4098 if (myLanes[i].hasParameter(SUMO_PARAM_ORIGID) || e->myLanes[i].hasParameter(SUMO_PARAM_ORIGID)
4099 || OptionsCont::getOptions().getBool("output.original-names")) {
4100 const std::string origID = myLanes[i].getParameter(SUMO_PARAM_ORIGID, getID());
4101 const std::string origID2 = e->myLanes[i].getParameter(SUMO_PARAM_ORIGID, e->getID());
4102 if (origID != origID2) {
4103 myLanes[i].setParameter(SUMO_PARAM_ORIGID, origID + " " + origID2);
4104 }
4105 }
4106 myLanes[i].connectionsDone = e->myLanes[i].connectionsDone;
4107 myLanes[i].turnSigns = e->myLanes[i].turnSigns;
4108 }
4109 if (e->getLength() > myLength) {
4110 // possibly some lane attributes differ (when using option geometry.remove.min-length)
4111 // make sure to use the attributes from the longer edge
4112 for (int i = 0; i < (int)myLanes.size(); i++) {
4113 myLanes[i].width = e->myLanes[i].width;
4114 }
4115 // defined name prevails over undefined name of shorter road
4116 if (myStreetName == "") {
4118 }
4119 }
4120 // recompute length
4121 myLength += e->myLength;
4122 if (myLoadedLength > 0 || e->myLoadedLength > 0) {
4124 }
4125 // copy the connections and the building step if given
4126 myStep = e->myStep;
4132 // set the node
4133 myTo = e->myTo;
4139 }
4140 computeAngle(); // myEndAngle may be different now
4141}
4142
4143
4144void
4145NBEdge::updateRemovedNodes(const std::string& removed) {
4146 std::string result = getParameter(SUMO_PARAM_REMOVED_NODES);
4147 if (!result.empty() && !removed.empty()) {
4148 result += " ";
4149 }
4150 result += removed;
4151 if (!result.empty()) {
4153 }
4154}
4155
4156
4157bool
4159 for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
4160 if ((*i).toEdge == e && (*i).tlID != "") {
4161 return true;
4162 }
4163 }
4164 return false;
4165}
4166
4167
4168NBEdge*
4169NBEdge::getTurnDestination(bool possibleDestination) const {
4170 if (myTurnDestination == nullptr && possibleDestination) {
4172 }
4173 return myTurnDestination;
4174}
4175
4176
4177std::string
4178NBEdge::getLaneID(int lane) const {
4179 return myID + "_" + toString(lane);
4180}
4181
4182
4183bool
4184NBEdge::isNearEnough2BeJoined2(NBEdge* e, double threshold) const {
4185 std::vector<double> distances = myGeom.distances(e->getGeometry());
4186 assert(distances.size() > 0);
4187 return VectorHelper<double>::maxValue(distances) < threshold;
4188}
4189
4190
4191void
4192NBEdge::addLane(int index, bool recomputeShape, bool recomputeConnections, bool shiftIndices) {
4193 assert(index <= (int)myLanes.size());
4194 myLanes.insert(myLanes.begin() + index, Lane(this, ""));
4195 // copy attributes
4196 if (myLanes.size() > 1) {
4197 int templateIndex = index > 0 ? index - 1 : index + 1;
4198 myLanes[index].speed = myLanes[templateIndex].speed;
4199 myLanes[index].friction = myLanes[templateIndex].friction;
4200 myLanes[index].permissions = myLanes[templateIndex].permissions;
4201 myLanes[index].preferred = myLanes[templateIndex].preferred;
4202 myLanes[index].endOffset = myLanes[templateIndex].endOffset;
4203 myLanes[index].width = myLanes[templateIndex].width;
4204 myLanes[index].updateParameters(myLanes[templateIndex].getParametersMap());
4205 }
4206 const EdgeVector& incs = myFrom->getIncomingEdges();
4207 if (recomputeShape) {
4209 }
4210 if (recomputeConnections) {
4211 for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
4212 (*i)->invalidateConnections(true);
4213 }
4215 } else if (shiftIndices) {
4216 // shift outgoing connections above the added lane to the left
4217 for (Connection& c : myConnections) {
4218 if (c.fromLane >= index) {
4219 c.fromLane += 1;
4220 }
4221 }
4222 // shift incoming connections above the added lane to the left
4223 for (NBEdge* inc : myFrom->getIncomingEdges()) {
4224 for (Connection& c : inc->myConnections) {
4225 if (c.toEdge == this && c.toLane >= index) {
4226 c.toLane += 1;
4227 }
4228 }
4229 }
4230 myFrom->shiftTLConnectionLaneIndex(this, +1, index - 1);
4231 myTo->shiftTLConnectionLaneIndex(this, +1, index - 1);
4232 }
4233}
4234
4235void
4237 int newLaneNo = (int)myLanes.size() + by;
4238 while ((int)myLanes.size() < newLaneNo) {
4239 // recompute shapes on last addition
4240 const bool recompute = ((int)myLanes.size() == newLaneNo - 1) && myStep < EdgeBuildingStep::LANES2LANES_USER;
4241 addLane((int)myLanes.size(), recompute, recompute, false);
4242 }
4243}
4244
4245
4246void
4247NBEdge::deleteLane(int index, bool recompute, bool shiftIndices) {
4248 assert(index < (int)myLanes.size());
4249 myLanes.erase(myLanes.begin() + index);
4250 if (recompute) {
4252 const EdgeVector& incs = myFrom->getIncomingEdges();
4253 for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
4254 (*i)->invalidateConnections(true);
4255 }
4257 } else if (shiftIndices) {
4258 removeFromConnections(nullptr, index, -1, false, true);
4259 for (NBEdge* inc : myFrom->getIncomingEdges()) {
4260 inc->removeFromConnections(this, -1, index, false, true);
4261 }
4262 }
4263}
4264
4265
4266void
4268 int newLaneNo = (int) myLanes.size() - by;
4269 assert(newLaneNo > 0);
4270 while ((int)myLanes.size() > newLaneNo) {
4271 // recompute shapes on last removal
4272 const bool recompute = (int)myLanes.size() == newLaneNo + 1 && myStep < EdgeBuildingStep::LANES2LANES_USER;
4273 deleteLane((int)myLanes.size() - 1, recompute, false);
4274 }
4275}
4276
4277
4278void
4283
4284
4285void
4287 if (lane < 0) { // all lanes are meant...
4288 for (int i = 0; i < (int)myLanes.size(); i++) {
4289 allowVehicleClass(i, vclass);
4290 }
4291 } else {
4292 assert(lane < (int)myLanes.size());
4293 myLanes[lane].permissions |= vclass;
4294 }
4295}
4296
4297
4298void
4300 if (lane < 0) { // all lanes are meant...
4301 for (int i = 0; i < (int)myLanes.size(); i++) {
4302 disallowVehicleClass((int) i, vclass);
4303 }
4304 } else {
4305 assert(lane < (int)myLanes.size());
4306 myLanes[lane].permissions &= ~vclass;
4307 }
4308}
4309
4310
4311void
4313 if (lane < 0) { // all lanes are meant...
4314 for (int i = 0; i < (int)myLanes.size(); i++) {
4315 preferVehicleClass(i, vclasses);
4316 }
4317 } else {
4318 assert(lane < (int)myLanes.size());
4319 myLanes[lane].permissions |= vclasses;
4320 myLanes[lane].preferred |= vclasses;
4321 }
4322}
4323
4324
4325void
4326NBEdge::setLaneWidth(int lane, double width) {
4327 if (lane < 0) {
4328 // all lanes are meant...
4329 myLaneWidth = width;
4330 for (int i = 0; i < (int)myLanes.size(); i++) {
4331 // ... do it for each lane
4332 setLaneWidth(i, width);
4333 }
4334 return;
4335 }
4336 assert(lane < (int)myLanes.size());
4337 myLanes[lane].width = width;
4338}
4339
4340void
4341NBEdge::setLaneType(int lane, const std::string& type) {
4342 if (lane < 0) {
4343 for (int i = 0; i < (int)myLanes.size(); i++) {
4344 // ... do it for each lane
4345 setLaneType(i, type);
4346 }
4347 return;
4348 }
4349 assert(lane < (int)myLanes.size());
4350 myLanes[lane].type = type;
4351}
4352
4353
4354double
4355NBEdge::getLaneWidth(int lane) const {
4356 return myLanes[lane].width != UNSPECIFIED_WIDTH
4357 ? myLanes[lane].width
4359}
4360
4361double
4363 const NBNode& node,
4364 const NBEdge::Connection& connection,
4365 const NBEdge::Lane& successor,
4366 bool isVia) const {
4367
4368 if (!isVia && node.isConstantWidthTransition() && getNumLanes() > connection.toEdge->getNumLanes()) {
4369 return getLaneWidth(connection.fromLane);
4370 }
4371
4372 return (isBikepath(getPermissions(connection.fromLane)) && (
4373 getLaneWidth(connection.fromLane) < successor.width || successor.width == UNSPECIFIED_WIDTH)) ?
4374 myLanes[connection.fromLane].width : successor.width; // getLaneWidth(connection.fromLane) never returns -1 (UNSPECIFIED_WIDTH)
4375}
4376
4377double
4379 double result = 0;
4380 for (int i = 0; i < (int)myLanes.size(); i++) {
4381 result += getLaneWidth(i);
4382 }
4383 return result;
4384}
4385
4386double
4387NBEdge::getEndOffset(int lane) const {
4388 return myLanes[lane].endOffset != UNSPECIFIED_OFFSET ? myLanes[lane].endOffset : getEndOffset();
4389}
4390
4391
4392const StopOffset&
4394 return myEdgeStopOffset;
4395}
4396
4397
4398const StopOffset&
4400 if (lane == -1) {
4401 return myEdgeStopOffset;
4402 } else {
4403 return myLanes[lane].laneStopOffset;
4404 }
4405}
4406
4407
4408void
4409NBEdge::setEndOffset(int lane, double offset) {
4410 if (lane < 0) {
4411 // all lanes are meant...
4412 myEndOffset = offset;
4413 for (int i = 0; i < (int)myLanes.size(); i++) {
4414 // ... do it for each lane
4415 setEndOffset(i, offset);
4416 }
4417 return;
4418 }
4419 assert(lane < (int)myLanes.size());
4420 myLanes[lane].endOffset = offset;
4421}
4422
4423
4424bool
4425NBEdge::setEdgeStopOffset(int lane, const StopOffset& offset, bool overwrite) {
4426 if (lane < 0) {
4427 if (!overwrite && myEdgeStopOffset.isDefined()) {
4428 return false;
4429 }
4430 // all lanes are meant...
4431 if (offset.getOffset() < 0) {
4432 // Edge length unknown at parsing time, thus check here.
4433 WRITE_WARNINGF(TL("Ignoring invalid stopOffset for edge '%' (negative offset)."), getID());
4434 return false;
4435 } else {
4436 myEdgeStopOffset = offset;
4437 }
4438 } else if (lane < (int)myLanes.size()) {
4439 if (!myLanes[lane].laneStopOffset.isDefined() || overwrite) {
4440 if (offset.getOffset() < 0) {
4441 // Edge length unknown at parsing time, thus check here.
4442 WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (negative offset)."), getLaneID(lane));
4443 } else {
4444 myLanes[lane].laneStopOffset = offset;
4445 }
4446 }
4447 } else {
4448 WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (invalid lane index)."), toString(lane));
4449 }
4450 return true;
4451}
4452
4453
4454void
4455NBEdge::setSpeed(int lane, double speed) {
4456 if (lane < 0) {
4457 // all lanes are meant...
4458 mySpeed = speed;
4459 for (int i = 0; i < (int)myLanes.size(); i++) {
4460 // ... do it for each lane
4461 setSpeed(i, speed);
4462 }
4463 return;
4464 }
4465 assert(lane < (int)myLanes.size());
4466 myLanes[lane].speed = speed;
4467}
4468
4469
4470void
4471NBEdge::setFriction(int lane, double friction) {
4472 if (lane < 0) {
4473 // all lanes are meant...
4474 myFriction = friction;
4475 for (int i = 0; i < (int)myLanes.size(); i++) {
4476 // ... do it for each lane
4477 setFriction(i, friction);
4478 }
4479 return;
4480 }
4481 assert(lane < (int)myLanes.size());
4482 myLanes[lane].friction = friction;
4483}
4484
4485
4486void
4487NBEdge::setAcceleration(int lane, bool accelRamp) {
4488 assert(lane >= 0);
4489 assert(lane < (int)myLanes.size());
4490 myLanes[lane].accelRamp = accelRamp;
4491}
4492
4493
4494void
4495NBEdge::setLaneShape(int lane, const PositionVector& shape) {
4496 assert(lane >= 0);
4497 assert(lane < (int)myLanes.size());
4498 myLanes[lane].customShape = shape;
4499}
4500
4501
4502void
4504 if (lane < 0) {
4505 for (int i = 0; i < (int)myLanes.size(); i++) {
4506 // ... do it for each lane
4507 setPermissions(permissions, i);
4508 }
4509 } else {
4510 assert(lane < (int)myLanes.size());
4511 myLanes[lane].permissions = permissions;
4512 }
4513}
4514
4515
4516void
4518 if (lane < 0) {
4519 for (int i = 0; i < (int)myLanes.size(); i++) {
4520 // ... do it for each lane
4521 setPreferredVehicleClass(permissions, i);
4522 }
4523 } else {
4524 assert(lane < (int)myLanes.size());
4525 myLanes[lane].preferred = permissions;
4526 }
4527}
4528
4529
4530void
4532 assert(lane >= 0);
4533 assert(lane < (int)myLanes.size());
4534 myLanes[lane].changeLeft = changeLeft;
4535 myLanes[lane].changeRight = changeRight;
4536}
4537
4538
4540NBEdge::getPermissions(int lane) const {
4541 if (lane < 0) {
4542 SVCPermissions result = 0;
4543 for (int i = 0; i < (int)myLanes.size(); i++) {
4544 result |= getPermissions(i);
4545 }
4546 return result;
4547 } else {
4548 assert(lane < (int)myLanes.size());
4549 return myLanes[lane].permissions;
4550 }
4551}
4552
4553
4554void
4556 myLoadedLength = val;
4557}
4558
4559void
4561 myLength = val;
4562}
4563
4564
4565void
4567 for (std::vector<Lane>::iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
4568 (*i).permissions = SVCAll;
4569 (*i).preferred = 0;
4570 }
4571}
4572
4573
4574bool
4576 if (c1.fromLane != c2.fromLane) {
4577 return c1.fromLane < c2.fromLane;
4578 }
4579 if (c1.toEdge != c2.toEdge) {
4580 return false; // do not change ordering among toEdges as this is determined by angle in an earlier step
4581 }
4582 return c1.toLane < c2.toLane;
4583}
4584
4585
4586double
4590 } else {
4592 myLanes.back().shape.back() : myLanes[getNumLanes() / 2].shape.back();
4593 //std::cout << getID() << " signalPos=" << mySignalPosition << " laneEnd=" << laneEnd << " toShape=" << myTo->getShape() << " toBorder=" << myToBorder << "\n";
4594 return mySignalPosition.distanceTo2D(laneEnd);
4595 }
4596}
4597
4598
4599int
4600NBEdge::getFirstNonPedestrianLaneIndex(int direction, bool exclusive) const {
4601 assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4602 const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4603 const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4604 for (int i = start; i != end; i += direction) {
4605 // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
4606 // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
4607 if ((exclusive && myLanes[i].permissions != SVC_PEDESTRIAN && myLanes[i].permissions != 0)
4608 || ((myLanes[i].permissions & SVC_PEDESTRIAN) == 0 && myLanes[i].permissions != 0)) {
4609 return i;
4610 }
4611 }
4612 return -1;
4613}
4614
4615int
4616NBEdge::getFirstNonPedestrianNonBicycleLaneIndex(int direction, bool exclusive) const {
4617 assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4618 const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4619 const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4620 for (int i = start; i != end; i += direction) {
4621 // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
4622 // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
4623 SVCPermissions p = myLanes[i].permissions;
4624 if ((exclusive && p != SVC_PEDESTRIAN && p != SVC_BICYCLE && p != (SVC_PEDESTRIAN | SVC_BICYCLE) && p != 0)
4625 || (p == SVCAll || ((p & (SVC_PEDESTRIAN | SVC_BICYCLE)) == 0 && p != 0))) {
4626 return i;
4627 }
4628 }
4629 return -1;
4630}
4631
4632int
4634 for (int i = 0; i < (int)myLanes.size(); i++) {
4635 if (myLanes[i].permissions == permissions) {
4636 return i;
4637 }
4638 }
4639 return -1;
4640}
4641
4642int
4644 assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4645 const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4646 const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4647 for (int i = start; i != end; i += direction) {
4648 if (myLanes[i].permissions != 0) {
4649 return i;
4650 }
4651 }
4652 return end - direction;
4653}
4654
4655
4656std::set<SVCPermissions>
4657NBEdge::getPermissionVariants(int iStart, int iEnd) const {
4658 std::set<SVCPermissions> result;
4659 if (iStart < 0 || iStart >= getNumLanes() || iEnd > getNumLanes()) {
4660 throw ProcessError("invalid indices iStart " + toString(iStart) + " iEnd " + toString(iEnd) + " for edge with " + toString(getNumLanes()) + " lanes.");
4661 }
4662 for (int i = iStart; i < iEnd; ++i) {
4663 result.insert(getPermissions(i));
4664 }
4665 return result;
4666}
4667
4668int
4669NBEdge::getNumLanesThatAllow(SVCPermissions permissions, bool allPermissions) const {
4670 int result = 0;
4671 for (const Lane& lane : myLanes) {
4672 if ((allPermissions && (lane.permissions & permissions) == permissions)
4673 || (!allPermissions && (lane.permissions & permissions) != 0)) {
4674 result++;
4675 }
4676 }
4677 return result;
4678}
4679
4680bool
4682 assert(lane >= 0 && lane < getNumLanes());
4683 return myLanes[lane].changeLeft == SVC_UNSPECIFIED ? true : (myLanes[lane].changeLeft & vclass) == vclass;
4684}
4685
4686bool
4688 assert(lane >= 0 && lane < getNumLanes());
4689 return myLanes[lane].changeRight == SVC_UNSPECIFIED ? true : (myLanes[lane].changeRight & vclass) == vclass;
4690}
4691
4692double
4694 double angle = getAngleAtNode(node) + (getFromNode() == node ? 180.0 : 0.0);
4695 if (angle < 0) {
4696 angle += 360.0;
4697 }
4698 if (angle >= 360) {
4699 angle -= 360.0;
4700 }
4701 if (gDebugFlag1) {
4702 std::cout << getID() << " angle=" << getAngleAtNode(node) << " convAngle=" << angle << "\n";
4703 }
4704 return angle;
4705}
4706
4707
4710 int index = getFirstNonPedestrianLaneIndex(direction);
4711 if (index < 0) {
4712 throw ProcessError(TLF("Edge % allows pedestrians on all lanes", getID()));
4713 }
4714 return myLanes[index];
4715}
4716
4717std::string
4719 // see IntermodalEdge::getSidewalk()
4720 for (int i = 0; i < (int)myLanes.size(); i++) {
4721 if (myLanes[i].permissions == SVC_PEDESTRIAN) {
4722 return getLaneID(i);
4723 }
4724 }
4725 for (int i = 0; i < (int)myLanes.size(); i++) {
4726 if ((myLanes[i].permissions & SVC_PEDESTRIAN) != 0) {
4727 return getLaneID(i);
4728 }
4729 }
4730 return getLaneID(0);
4731}
4732
4733void
4734NBEdge::addSidewalk(double width) {
4736}
4737
4738
4739void
4740NBEdge::restoreSidewalk(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4741 restoreRestrictedLane(SVC_PEDESTRIAN, oldLanes, oldGeometry, oldConnections);
4742}
4743
4744
4745void
4746NBEdge::addBikeLane(double width) {
4748}
4749
4750
4751void
4752NBEdge::restoreBikelane(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4753 restoreRestrictedLane(SVC_BICYCLE, oldLanes, oldGeometry, oldConnections);
4754}
4755
4756bool
4758 for (const Lane& lane : myLanes) {
4759 if (lane.permissions == vclass) {
4760 return true;
4761 }
4762 }
4763 return false;
4764}
4765
4766
4767void
4769 if (hasRestrictedLane(vclass)) {
4770 WRITE_WARNINGF(TL("Edge '%' already has a dedicated lane for %s. Not adding another one."), getID(), toString(vclass));
4771 return;
4772 }
4774 myGeom.move2side(width / 2);
4775 }
4776 // disallow the designated vclass on all "old" lanes
4777 disallowVehicleClass(-1, vclass);
4778 // don't create a restricted vehicle lane to the right of a sidewalk
4779 const int newIndex = (vclass != SVC_PEDESTRIAN && myLanes[0].permissions == SVC_PEDESTRIAN) ? 1 : 0;
4780 if (newIndex == 0) {
4781 // disallow pedestrians on all "higher" lanes to ensure that sidewalk remains the rightmost lane
4783 }
4784 // add new lane
4785 myLanes.insert(myLanes.begin() + newIndex, Lane(this, myLanes[0].getParameter(SUMO_PARAM_ORIGID)));
4786 myLanes[newIndex].permissions = vclass;
4787 myLanes[newIndex].width = fabs(width);
4788 // shift outgoing connections to the left
4789 for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
4790 Connection& c = *it;
4791 if (c.fromLane >= newIndex) {
4792 c.fromLane += 1;
4793 }
4794 }
4795 // shift incoming connections to the left
4796 const EdgeVector& incoming = myFrom->getIncomingEdges();
4797 for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4798 (*it)->shiftToLanesToEdge(this, 1);
4799 }
4803}
4804
4805
4806void
4807NBEdge::restoreRestrictedLane(SUMOVehicleClass vclass, std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4808 // check that previously lane was transformed
4809 if (myLanes[0].permissions != vclass) {
4810 WRITE_WARNINGF(TL("Edge '%' doesn't have a dedicated lane for %s. Cannot be restored."), getID(), toString(vclass));
4811 return;
4812 }
4813 // restore old values
4814 myGeom = oldGeometry;
4815 myLanes = oldLanes;
4816 myConnections = oldConnections;
4817 // shift incoming connections to the right
4818 const EdgeVector& incoming = myFrom->getIncomingEdges();
4819 for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4820 (*it)->shiftToLanesToEdge(this, 0);
4821 }
4822 // Shift TL conections
4826}
4827
4828
4829void
4832 for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
4833 if ((*it).toEdge == to && (*it).toLane >= 0) {
4834 (*it).toLane += laneOff;
4835 }
4836 }
4837}
4838
4839
4840bool
4844 && !isRailway(other->getPermissions())
4845 && getBidiEdge() == nullptr) {
4846 const int i = (node == myTo ? -1 : 0);
4847 const int i2 = (node == myTo ? 0 : -1);
4848 const double dist = myGeom[i].distanceTo2D(node->getPosition());
4849 const double neededOffset = getTotalWidth() / 2;
4850 const double dist2 = MIN2(myGeom.distance2D(other->getGeometry()[i2]),
4851 other->getGeometry().distance2D(myGeom[i]));
4852 const double neededOffset2 = neededOffset + (other->getTotalWidth()) / 2;
4853 if (dist < neededOffset && dist2 < neededOffset2) {
4854 PositionVector tmp = myGeom;
4855 // @note this doesn't work well for vissim networks
4856 //tmp.move2side(MIN2(neededOffset - dist, neededOffset2 - dist2));
4857 try {
4858 tmp.move2side(neededOffset - dist);
4859 tmp[i].round(gPrecision);
4860 myGeom[i] = tmp[i];
4861 computeAngle();
4862 return true;
4863 //std::cout << getID() << " shiftPositionAtNode needed=" << neededOffset << " dist=" << dist << " needed2=" << neededOffset2 << " dist2=" << dist2 << " by=" << (neededOffset - dist) << " other=" << other->getID() << "\n";
4864 } catch (InvalidArgument&) {
4865 WRITE_WARNINGF(TL("Could not avoid overlapping shape at node '%' for edge '%'."), node->getID(), getID());
4866 }
4867 }
4868 }
4869 return false;
4870}
4871
4872
4875 if (myLoadedLength > 0) {
4877 } else {
4878 return myGeom.positionAtOffset(offset);
4879 }
4880}
4881
4882
4883double
4885 double result = getLoadedLength();
4886 if (OptionsCont::getOptions().getBool("no-internal-links") && !hasLoadedLength()) {
4887 // use length to junction center even if a modified geometry was given
4889 geom.push_back_noDoublePos(getToNode()->getCenter());
4890 geom.push_front_noDoublePos(getFromNode()->getCenter());
4891 result = geom.length();
4892 }
4893 double avgEndOffset = 0;
4894 for (const Lane& lane : myLanes) {
4895 avgEndOffset += lane.endOffset;
4896 }
4897 if (isBidiRail()) {
4898 avgEndOffset += myPossibleTurnDestination->getEndOffset();
4899 }
4900 avgEndOffset /= (double)myLanes.size();
4901 return MAX2(result - avgEndOffset, POSITION_EPS);
4902}
4903
4904
4905void
4906NBEdge::setOrigID(const std::string origID, const bool append, const int laneIdx) {
4907 if (laneIdx == -1) {
4908 for (int i = 0; i < (int)myLanes.size(); i++) {
4909 setOrigID(origID, append, i);
4910 }
4911 } else {
4912 if (origID != "") {
4913 if (append) {
4914 std::vector<std::string> oldIDs = StringTokenizer(myLanes[laneIdx].getParameter(SUMO_PARAM_ORIGID)).getVector();
4915 if (std::find(oldIDs.begin(), oldIDs.end(), origID) == oldIDs.end()) {
4916 oldIDs.push_back(origID);
4917 }
4918 myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, toString(oldIDs));
4919 } else {
4920 myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, origID);
4921 }
4922 } else {
4923 // do not record empty origID parameter
4924 myLanes[laneIdx].unsetParameter(SUMO_PARAM_ORIGID);
4925 }
4926 }
4927}
4928
4929
4930const EdgeVector&
4932 // @todo cache successors instead of recomputing them every time
4933 mySuccessors.clear();
4934 //std::cout << "getSuccessors edge=" << getID() << " svc=" << toString(vClass) << " cons=" << myConnections.size() << "\n";
4935 for (const Connection& con : myConnections) {
4936 if (con.fromLane >= 0 && con.toLane >= 0 && con.toEdge != nullptr &&
4937 (vClass == SVC_IGNORING || (getPermissions(con.fromLane)
4938 & con.toEdge->getPermissions(con.toLane) & vClass) != 0)
4939 && std::find(mySuccessors.begin(), mySuccessors.end(), con.toEdge) == mySuccessors.end()) {
4940 mySuccessors.push_back(con.toEdge);
4941 //std::cout << " succ=" << con.toEdge->getID() << "\n";
4942 }
4943 }
4944 return mySuccessors;
4945}
4946
4947
4949NBEdge::getViaSuccessors(SUMOVehicleClass vClass, bool /*ignoreTransientPermissions*/) const {
4950 // @todo cache successors instead of recomputing them every time
4951 myViaSuccessors.clear();
4952 for (const Connection& con : myConnections) {
4953 std::pair<const NBEdge*, const Connection*> pair(con.toEdge, nullptr);
4954 // special case for Persons in Netedit
4955 if (vClass == SVC_PEDESTRIAN) {
4956 myViaSuccessors.push_back(pair); // Pedestrians have complete freedom of movement in all sucessors
4957 } else if ((con.fromLane >= 0) && (con.toLane >= 0) &&
4958 (con.toEdge != nullptr) &&
4959 ((getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane) & vClass) == vClass)) {
4960 // ignore duplicates
4961 if (con.getLength() > 0) {
4962 pair.second = &con;
4963 }
4964 myViaSuccessors.push_back(pair);
4965 }
4966 }
4967 return myViaSuccessors;
4968}
4969
4970
4971void
4972NBEdge::debugPrintConnections(bool outgoing, bool incoming) const {
4973 if (outgoing) {
4974 for (const Connection& c : myConnections) {
4975 std::cout << " " << getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
4976 }
4977 }
4978 if (incoming) {
4979 for (NBEdge* inc : myFrom->getIncomingEdges()) {
4980 for (Connection& c : inc->myConnections) {
4981 if (c.toEdge == this) {
4982 std::cout << " " << inc->getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
4983 }
4984 }
4985 }
4986 }
4987}
4988
4989
4990int
4991NBEdge::getLaneIndexFromLaneID(const std::string laneID) {
4992 return StringUtils::toInt(laneID.substr(laneID.rfind("_") + 1));
4993}
4994
4995bool
4997 bool haveJoined = false;
4998 int i = 0;
4999 while (i < getNumLanes() - 1) {
5000 if ((getPermissions(i) == perms) && (getPermissions(i + 1) == perms)) {
5001 const double newWidth = getLaneWidth(i) + getLaneWidth(i + 1);
5002 const std::string newType = myLanes[i].type + "|" + myLanes[i + 1].type;
5003 deleteLane(i, false, true);
5004 setLaneWidth(i, newWidth);
5005 setLaneType(i, newType);
5006 haveJoined = true;
5007 } else {
5008 i++;
5009 }
5010 }
5011 return haveJoined;
5012}
5013
5014
5017 EdgeVector result;
5018 for (NBEdge* edge : edges) {
5019 if ((edge->getPermissions() & permissions) != 0) {
5020 result.push_back(edge);
5021 }
5022 }
5023 return result;
5024}
5025
5026NBEdge*
5028 EdgeVector cands = filterByPermissions(myTo->getOutgoingEdges(), permissions);
5029 if (cands.size() == 0) {
5030 return nullptr;
5031 }
5032 sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this));
5033 NBEdge* best = cands.front();
5034 if (isTurningDirectionAt(best)) {
5035 return nullptr;
5036 } else {
5037 return best;
5038 }
5039}
5040
5041NBEdge*
5043 EdgeVector cands = filterByPermissions(myFrom->getIncomingEdges(), permissions);
5044 if (cands.size() == 0) {
5045 return nullptr;
5046 }
5047 sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this, false));
5048 NBEdge* best = cands.front();
5049 if (best->isTurningDirectionAt(this)) {
5050 return nullptr;
5051 } else {
5052 return best;
5053 }
5054}
5055
5056
5057NBEdge*
5059 NBEdge* opposite = nullptr;
5060 if (getNumLanes() > 0) {
5061 NBEdge::Lane& lastLane = myLanes.back();
5062 const double lastWidth = getLaneWidth(getNumLanes() - 1);
5063 if (lastLane.oppositeID == "" || reguess) {
5064 for (NBEdge* cand : getToNode()->getOutgoingEdges()) {
5065 if (cand->getToNode() == getFromNode() && !cand->getLanes().empty()) {
5066 const NBEdge::Lane& candLastLane = cand->getLanes().back();
5067 if (candLastLane.oppositeID == "" || candLastLane.oppositeID == getLaneID(getNumLanes() - 1)) {
5068 const double lastWidthCand = cand->getLaneWidth(cand->getNumLanes() - 1);
5069 // in sharp corners, the difference may be higher
5070 // factor (sqrt(2) for 90 degree corners
5071 const double threshold = 1.42 * 0.5 * (lastWidth + lastWidthCand) + 0.5;
5072 const double distance = VectorHelper<double>::maxValue(lastLane.shape.distances(cand->getLanes().back().shape));
5073 //std::cout << " distance=" << distance << " threshold=" << threshold << " distances=" << toString(lastLane.shape.distances(cand->getLanes().back().shape)) << "\n";
5074 if (distance < threshold) {
5075 opposite = cand;
5076 }
5077 }
5078 }
5079 }
5080 if (opposite != nullptr) {
5081 lastLane.oppositeID = opposite->getLaneID(opposite->getNumLanes() - 1);
5082 }
5083 }
5084 }
5085 return opposite;
5086}
5087
5088double
5089NBEdge::getDistancAt(double pos) const {
5090 // negative values of myDistances indicate descending kilometrage
5091 return fabs(myDistance + pos);
5092}
5093
5094/****************************************************************************/
#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:66
int gPrecision
the precision for floating point outputs
Definition StdDefs.cpp:27
bool gDebugFlag1
global utility flags for debugging
Definition StdDefs.cpp:43
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:283
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:46
static void compute(BresenhamCallBack *callBack, const int val1, const int val2)
Definition Bresenham.cpp:32
static const double INVALID_OFFSET
a value to signify offsets outside the range of [0, Line.length()]
Definition GeomHelper.h:50
static double legacyDegree(const double angle, const bool positive=false)
static double angleDiff(const double angle1, const double angle2)
Returns the difference of the second angle to the first angle in radiants.
int getFromLane() const
returns the from-lane
int getTLIndex2() const
int getTLIndex() const
returns the index within the controlling tls or InvalidTLIndex if this link is unontrolled
void shiftLaneIndex(NBEdge *edge, int offset, int threshold=-1)
patches lane indices refering to the given edge and above the threshold by the given offset
int getToLane() const
returns the to-lane
NBEdge * getTo() const
returns the to-edge (end of the connection)
Holds (- relative to the edge it is build from -!!!) the list of main directions a vehicle that drive...
Definition NBEdge.h: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:4145
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:2701
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:4517
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:2233
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:5058
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:4531
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:4540
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:5089
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:4503
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:4693
void addBikeLane(double width)
add a bicycle lane of the given width and shift existing connctions
Definition NBEdge.cpp:4746
bool expandableBy(NBEdge *possContinuation, std::string &reason) const
Check if Node is expandable.
Definition NBEdge.cpp:3976
double getLaneFriction(int lane) const
get lane friction of specified lane
Definition NBEdge.cpp:2239
void recheckOpposite(const NBEdgeCont &ec, bool fixOppositeLengths)
recheck whether all opposite and bidi settings are consistent
Definition NBEdge.cpp:3198
const ConstRouterEdgePairVector & getViaSuccessors(SUMOVehicleClass vClass=SVC_IGNORING, bool ignoreTransientPermissions=false) const
Returns the following edges for the given vClass.
Definition NBEdge.cpp:4949
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:4455
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:2485
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:3940
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:3357
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:3958
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:4236
static EdgeVector filterByPermissions(const EdgeVector &edges, SVCPermissions permissions)
return only those edges that permit at least one of the give permissions
Definition NBEdge.cpp:5016
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:4192
bool hasLaneSpecificSpeed() const
whether lanes differ in speed
Definition NBEdge.cpp:2475
void setAverageLengthWithOpposite(double val)
patch average lane length in regard to the opposite edge
Definition NBEdge.cpp:4560
void disallowVehicleClass(int lane, SUMOVehicleClass vclass)
set disallowed class for the given lane or for all lanes if -1 is given
Definition NBEdge.cpp:4299
void removeInvalidConnections()
Definition NBEdge.cpp:3295
double getShapeStartAngle() const
Returns the angle at the start of the edge.
Definition NBEdge.cpp:2434
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:3727
static bool connections_sorter(const Connection &c1, const Connection &c2)
connections_sorter sort by fromLane, toEdge and toLane
Definition NBEdge.cpp:4575
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:2450
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:4931
void dismissVehicleClassInformation()
dimiss vehicle class information
Definition NBEdge.cpp:4566
bool computeEdge2Edges(bool noLeftMovers)
computes the edge (step1: computation of approached edges)
Definition NBEdge.cpp:2600
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:2251
void restoreBikelane(std::vector< NBEdge::Lane > oldLanes, PositionVector oldGeometry, std::vector< NBEdge::Connection > oldConnections)
restore an previously added BikeLane
Definition NBEdge.cpp:4752
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:5027
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:4740
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:3447
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:4425
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:2528
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:4600
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:4830
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:4184
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:4341
bool computeLanes2Edges()
computes the edge, step2: computation of which lanes approach the edges)
Definition NBEdge.cpp:2656
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:5042
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:4643
bool allowsChangingRight(int lane, SUMOVehicleClass vclass) const
Returns whether the given vehicle class may change left from this lane.
Definition NBEdge.cpp:4687
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:4326
void resetLaneShapes()
reset lane shapes to what they would be before cutting with the junction shapes
Definition NBEdge.cpp:2245
bool setControllingTLInformation(const NBConnection &c, const std::string &tlID)
Returns if the link could be set as to be controlled.
Definition NBEdge.cpp:3869
bool bothLeftTurns(LinkDirection dir, const NBEdge *otherFrom, LinkDirection dir2) const
determine conflict between opposite left turns
Definition NBEdge.cpp:2138
bool shiftPositionAtNode(NBNode *node, NBEdge *opposite)
shift geometry at the given node to avoid overlap and return whether geometry was changed
Definition NBEdge.cpp:4841
void setAcceleration(int lane, bool accelRamp)
marks one lane as acceleration lane
Definition NBEdge.cpp:4487
const StopOffset & getEdgeStopOffset() const
Returns the stopOffset to the end of the edge.
Definition NBEdge.cpp:4393
NBNode * tryGetNodeAtPosition(double pos, double tolerance=5.0) const
Returns the node at the given edges length (using an epsilon)
Definition NBEdge.cpp:3820
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:3932
bool isTurningDirectionAt(const NBEdge *const edge) const
Returns whether the given edge is the opposite direction to this edge.
Definition NBEdge.cpp:3807
void addStraightConnections(const EdgeVector *outgoing, const std::vector< int > &availableLanes, const std::vector< int > &priorities)
add some straight connections
Definition NBEdge.cpp:3552
bool hasLaneSpecificPermissions() const
whether lanes differ in allowed vehicle classes
Definition NBEdge.cpp:2461
bool needsLaneSpecificOutput() const
whether at least one lane has values differing from the edges values
Definition NBEdge.cpp:2583
void computeAngle()
computes the angle of this edge and stores it in myAngle
Definition NBEdge.cpp:2336
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:4734
bool hasSignalisedConnectionTo(const NBEdge *const e) const
Check if edge has signalised connections.
Definition NBEdge.cpp:4158
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:2541
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:2074
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:4471
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:4669
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:4996
void resetNodeBorder(const NBNode *node)
Definition NBEdge.cpp:761
void markAsInLane2LaneState()
mark edge as in lane to state lane
Definition NBEdge.cpp:4279
bool mayBeTLSControlled(int fromLane, NBEdge *toEdge, int toLane) const
return true if certain connection must be controlled by TLS
Definition NBEdge.cpp:3858
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:4768
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:4709
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:3634
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:4378
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:4874
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:4362
void allowVehicleClass(int lane, SUMOVehicleClass vclass)
set allowed class for the given lane or for all lanes if -1 is given
Definition NBEdge.cpp:4286
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:3852
void deleteLane(int index, bool recompute, bool shiftIndices)
delete lane
Definition NBEdge.cpp:4247
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:2495
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:4657
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:3834
std::string getLaneID(int lane) const
get lane ID
Definition NBEdge.cpp:4178
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:4718
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:2272
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:2209
int getSpecialLane(SVCPermissions permissions) const
return index of the first lane that allows the given permissions
Definition NBEdge.cpp:4633
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:2517
int getJunctionPriority(const NBNode *const node) const
Returns the junction priority (normalised for the node currently build)
Definition NBEdge.cpp:2157
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:4495
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:2713
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:2724
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:2147
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:4312
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:2193
NBEdge * getTurnDestination(bool possibleDestination=false) const
Definition NBEdge.cpp:4169
double getAngleAtNode(const NBNode *const node) const
Returns the angle of the edge's geometry at the given node.
Definition NBEdge.cpp:2183
bool hasLaneSpecificType() const
whether lanes differ in type
Definition NBEdge.cpp:2506
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:4587
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:2224
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:2034
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:4757
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:4399
void debugPrintConnections(bool outgoing=true, bool incoming=false) const
debugging helper to print all connections
Definition NBEdge.cpp:4972
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:3846
void restoreRestrictedLane(SUMOVehicleClass vclass, std::vector< NBEdge::Lane > oldLanes, PositionVector oldGeometry, std::vector< NBEdge::Connection > oldConnections)
restore a restricted lane
Definition NBEdge.cpp:4807
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:4409
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:2954
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:4906
PositionVector computeLaneShape(int lane, double offset) const
Computes the shape for the given lane.
Definition NBEdge.cpp:2324
bool allowsChangingLeft(int lane, SUMOVehicleClass vclass) const
Returns whether the given vehicle class may change left from this lane.
Definition NBEdge.cpp:4681
static int getLaneIndexFromLaneID(const std::string laneID)
Definition NBEdge.cpp:4991
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:2552
bool hasLaneParams() const
whether one of the lanes has parameters set
Definition NBEdge.cpp:2563
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:2442
bool prohibitsChanging() const
whether one of the lanes prohibits lane changing
Definition NBEdge.cpp:2573
void setLoadedLength(double val)
set loaded length
Definition NBEdge.cpp:4555
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:4267
NBNode * myFrom
The source and the destination node.
Definition NBEdge.h:1763
void append(NBEdge *continuation)
append another edge
Definition NBEdge.cpp:4093
void setJunctionPriority(const NBNode *const node, int prio)
Sets the junction priority of the edge.
Definition NBEdge.cpp:2167
double getFinalLength() const
get length that will be assigned to the lanes in the final network
Definition NBEdge.cpp:4884
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:4616
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:531
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:2496
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:2065
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:340
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:976
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:829
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:2181
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:2326
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:2252
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:586
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:2307
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:2243
std::vector< Crossing * > getCrossings() const
return this junctions pedestrian crossings
Definition NBNode.cpp:3096
void addOutgoingEdge(NBEdge *edge)
adds an outgoing edge
Definition NBNode.cpp:541
bool isConstantWidthTransition() const
detects whether a given junction splits or merges lanes while keeping constant road width
Definition NBNode.cpp:919
const Position & getPosition() const
Definition NBNode.h:260
const PositionVector & getShape() const
retrieve the junction shape
Definition NBNode.cpp:2781
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:2336
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:834
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:490
bool geometryLike() const
whether this is structurally similar to a geometry node
Definition NBNode.cpp:4035
bool isTLControlled() const
Returns whether this node is controlled by any tls.
Definition NBNode.h:331
static const int SCURVE_IGNORE
Definition NBNode.h:227
static const double MIN_SPEED_CROSSING_TIME
minimum speed for computing time to cross intersection
Definition NBOwnTLDef.h:152
Base class for objects which have an id.
Definition Named.h:54
std::string myID
The name of the object.
Definition Named.h:125
static std::string getIDSecure(const T *obj, const std::string &fallBack="NULL")
get an identifier for Named-like object which may be Null
Definition Named.h:67
const std::string & getID() const
Returns the id.
Definition Named.h:74
A storage for options typed value containers)
Definition OptionsCont.h:89
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
int getInt(const std::string &name) const
Returns the int-value of the named option (only for Option_Integer)
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
static OptionsCont & getOptions()
Retrieves the options.
bool hasParameter(const std::string &key) const
Returns whether the parameter is set.
void mergeParameters(const Parameterised::Map &mapArg, const std::string separator=" ", bool uniqueValues=true)
Adds or appends all given parameters from the map.
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const
Returns the value for a given key.
const Parameterised::Map & getParametersMap() const
Returns the inner key/value map.
virtual void setParameter(const std::string &key, const std::string &value)
Sets a parameter.
void updateParameters(const Parameterised::Map &mapArg)
Adds or updates all given parameters from the map.
A point in 2D or 3D with translation and scaling methods.
Definition Position.h:37
static const Position INVALID
used to indicate that a position is valid
Definition Position.h: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