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