Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
NBEdge.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
23// Methods for the representation of a single edge
24/****************************************************************************/
25#include <config.h>
26
27#include <vector>
28#include <string>
29#include <algorithm>
30#include <cmath>
31#include <iomanip>
40#include "NBEdgeCont.h"
41#include "NBNode.h"
42#include "NBNodeCont.h"
43#include "NBContHelper.h"
44#include "NBHelpers.h"
46#include "NBOwnTLDef.h"
47#include "NBTypeCont.h"
48#include "NBEdge.h"
49
50//#define ADDITIONAL_WARNINGS
51//#define DEBUG_CONNECTION_GUESSING
52//#define DEBUG_CONNECTION_CHECKING
53//#define DEBUG_ANGLES
54//#define DEBUG_NODE_BORDER
55//#define DEBUG_REPLACECONNECTION
56//#define DEBUG_JUNCTIONPRIO
57//#define DEBUG_TURNSIGNS
58//#define DEBUG_CUT_LANES
59#define DEBUGID ""
60#define DEBUGCOND (getID() == DEBUGID)
61//#define DEBUGCOND (StringUtils::startsWith(getID(), DEBUGID))
62//#define DEBUGCOND (getID() == "22762377#1" || getID() == "146511467")
63#define DEBUGCOND2(obj) ((obj != 0 && (obj)->getID() == DEBUGID))
64//#define DEBUGCOND (true)
65
66// ===========================================================================
67// static members
68// ===========================================================================
69const double NBEdge::UNSPECIFIED_WIDTH = -1;
70const double NBEdge::UNSPECIFIED_OFFSET = 0;
71const double NBEdge::UNSPECIFIED_SPEED = -1;
72const double NBEdge::UNSPECIFIED_FRICTION = 1.;
73const double NBEdge::UNSPECIFIED_CONTPOS = -1;
75
76const double NBEdge::UNSPECIFIED_SIGNAL_OFFSET = -1;
77const double NBEdge::UNSPECIFIED_LOADED_LENGTH = -1;
78const double NBEdge::ANGLE_LOOKAHEAD = 10.0;
81
83
85
86ConstRouterEdgePairVector NBEdge::Connection::myViaSuccessors = ConstRouterEdgePairVector({ std::pair<NBRouterEdge*, NBRouterEdge*>(nullptr, nullptr) });
87
88// ===========================================================================
89// method definitions
90// ===========================================================================
91std::string
95
96
97std::string
99 return viaID + "_" + toString(internalViaLaneIndex);
100}
101
102
103std::string
105 return (Named::getIDSecure(parent) + "_" + toString(fromLane) + "->" + Named::getIDSecure(toEdge) + "_" + toString(toLane)
106 + (permissions == SVC_UNSPECIFIED ? "" : " (" + getVehicleClassNames(permissions) + ")"));
107}
108
109
110NBEdge::Connection::Connection(int fromLane_, NBEdge* toEdge_, int toLane_, const bool mayDefinitelyPass_) :
111 fromLane(fromLane_),
112 toEdge(toEdge_),
113 toLane(toLane_),
114 mayDefinitelyPass(mayDefinitelyPass_),
115 customLength(myDefaultConnectionLength),
116 id(toEdge_ == nullptr ? "" : toEdge->getFromNode()->getID()) {
117}
118
119
120NBEdge::Lane::Lane(NBEdge* e, const std::string& origID_) :
121 speed(e->getSpeed()),
122 friction(e->getFriction()),
123 permissions(SVCAll),
124 preferred(0),
125 changeLeft(SVCAll),
126 changeRight(SVCAll),
127 endOffset(e->getEndOffset()),
128 laneStopOffset(e->getEdgeStopOffset()),
129 width(e->getLaneWidth()),
130 accelRamp(false),
131 connectionsDone(false) {
132 if (origID_ != "") {
134 }
135}
136
137
138/* -------------------------------------------------------------------------
139 * NBEdge::ToEdgeConnectionsAdder-methods
140 * ----------------------------------------------------------------------- */
141void
142NBEdge::ToEdgeConnectionsAdder::execute(const int lane, const int virtEdge) {
143 // check
144 assert((int)myTransitions.size() > virtEdge);
145 // get the approached edge
146 NBEdge* succEdge = myTransitions[virtEdge];
147 std::vector<int> lanes;
148
149 // check whether the currently regarded, approached edge has already
150 // a connection starting at the edge which is currently being build
151 std::map<NBEdge*, std::vector<int> >::iterator i = myConnections.find(succEdge);
152 if (i != myConnections.end()) {
153 // if there were already lanes assigned, get them
154 lanes = (*i).second;
155 }
156
157 // check whether the current lane was already used to connect the currently
158 // regarded approached edge
159 std::vector<int>::iterator j = std::find(lanes.begin(), lanes.end(), lane);
160 if (j == lanes.end()) {
161 // if not, add it to the list
162 lanes.push_back(lane);
163 }
164 // set information about connecting lanes
165 myConnections[succEdge] = lanes;
166}
167
168
169
170/* -------------------------------------------------------------------------
171 * NBEdge::MainDirections-methods
172 * ----------------------------------------------------------------------- */
173NBEdge::MainDirections::MainDirections(const EdgeVector& outgoing, NBEdge* parent, NBNode* to, const std::vector<int>& availableLanes) : myStraightest(-1) {
175 const NBEdge* straight = nullptr;
176 for (const NBEdge* const out : outgoing) {
177 const SVCPermissions outPerms = out->getPermissions();
178 for (const int l : availableLanes) {
179 if ((parent->myLanes[l].permissions & outPerms) != 0) {
180 if (straight == nullptr || sorter(out, straight)) {
181 straight = out;
182 }
183 break;
184 }
185 }
186 }
187 if (straight == nullptr) {
188 return;
189 }
190 myStraightest = (int)std::distance(outgoing.begin(), std::find(outgoing.begin(), outgoing.end(), straight));
191
192 // check whether the right turn has a higher priority
193 assert(outgoing.size() > 0);
194 const LinkDirection straightestDir = to->getDirection(parent, straight);
195#ifdef DEBUG_CONNECTION_GUESSING
196 if (DEBUGCOND2(parent)) {
197 std::cout << " MainDirections edge=" << parent->getID() << " straightest=" << straight->getID() << " dir=" << toString(straightestDir) << "\n";
198 }
199#endif
200 if (NBNode::isTrafficLight(to->getType()) &&
201 (straightestDir == LinkDirection::STRAIGHT || straightestDir == LinkDirection::PARTLEFT || straightestDir == LinkDirection::PARTRIGHT)) {
203 return;
204 }
205 if (outgoing[0]->getJunctionPriority(to) == 1) {
207 }
208 // check whether the left turn has a higher priority
209 if (outgoing.back()->getJunctionPriority(to) == 1) {
210 // ok, the left turn belongs to the higher priorised edges on the junction
211 // let's check, whether it has also a higher priority (lane number/speed)
212 // than the current
213 if (outgoing.back()->getPriority() > straight->getPriority() ||
214 outgoing.back()->getNumLanes() > straight->getNumLanes()) {
216 }
217 }
218 // check whether the forward direction has a higher priority
219 // check whether it has a higher priority and is going straight
220 if (straight->getJunctionPriority(to) == 1 && to->getDirection(parent, straight) == LinkDirection::STRAIGHT) {
222 }
223}
224
225
227
228
229bool
231 return myDirs.empty();
232}
233
234
235bool
237 return std::find(myDirs.begin(), myDirs.end(), d) != myDirs.end();
238}
239
240
241/* -------------------------------------------------------------------------
242 * NBEdge::connections_relative_edgelane_sorter-methods
243 * ----------------------------------------------------------------------- */
244int
246 if (c1.toEdge != c2.toEdge) {
248 }
249 return c1.toLane < c2.toLane;
250}
251
252
253/* -------------------------------------------------------------------------
254 * NBEdge-methods
255 * ----------------------------------------------------------------------- */
256NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to,
257 std::string type, double speed, double friction, int nolanes,
258 int priority, double laneWidth, double endOffset,
259 LaneSpreadFunction spread, const std::string& streetName) :
260 Named(StringUtils::convertUmlaute(id)),
262 myType(StringUtils::convertUmlaute(type)),
263 myFrom(from), myTo(to),
265 myPriority(priority), mySpeed(speed), myFriction(friction),
266 myDistance(0),
267 myTurnDestination(nullptr),
270 myLaneSpreadFunction(spread), myEndOffset(endOffset),
271 myLaneWidth(laneWidth),
273 myAmInTLS(false), myAmMacroscopicConnector(false),
274 myStreetName(streetName),
276 mySignalNode(nullptr),
277 myIsOffRamp(false),
278 myIsBidi(false),
279 myIndex(-1) {
280 init(nolanes, false, "");
281}
282
283
284NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to,
285 std::string type, double speed, double friction, int nolanes,
286 int priority, double laneWidth, double endOffset,
287 PositionVector geom,
288 LaneSpreadFunction spread,
289 const std::string& streetName,
290 const std::string& origID,
291 bool tryIgnoreNodePositions) :
292 Named(StringUtils::convertUmlaute(id)),
293 myStep(EdgeBuildingStep::INIT),
294 myType(StringUtils::convertUmlaute(type)),
295 myFrom(from), myTo(to),
296 myStartAngle(0), myEndAngle(0), myTotalAngle(0),
297 myPriority(priority), mySpeed(speed), myFriction(friction),
298 myDistance(0),
299 myTurnDestination(nullptr),
300 myPossibleTurnDestination(nullptr),
301 myFromJunctionPriority(-1), myToJunctionPriority(-1),
302 myGeom(geom), myLaneSpreadFunction(spread), myEndOffset(endOffset),
303 myLaneWidth(laneWidth),
304 myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
305 myAmInTLS(false), myAmMacroscopicConnector(false),
306 myStreetName(streetName),
307 mySignalPosition(Position::INVALID),
308 mySignalNode(nullptr),
309 myIsOffRamp(false),
310 myIsBidi(false),
311 myIndex(-1) {
312 init(nolanes, tryIgnoreNodePositions, origID);
313}
314
315
316NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to, const NBEdge* tpl, const PositionVector& geom, int numLanes) :
317 Named(StringUtils::convertUmlaute(id)),
318 myStep(EdgeBuildingStep::INIT),
319 myType(tpl->getTypeID()),
320 myFrom(from), myTo(to),
321 myStartAngle(0), myEndAngle(0), myTotalAngle(0),
322 myPriority(tpl->getPriority()), mySpeed(tpl->getSpeed()),
323 myFriction(tpl->getFriction()),
324 myDistance(0),
325 myTurnDestination(nullptr),
326 myPossibleTurnDestination(nullptr),
327 myFromJunctionPriority(-1), myToJunctionPriority(-1),
328 myGeom(geom),
329 myLaneSpreadFunction(tpl->getLaneSpreadFunction()),
330 myEndOffset(tpl->getEndOffset()),
331 myEdgeStopOffset(tpl->getEdgeStopOffset()),
332 myLaneWidth(tpl->getLaneWidth()),
333 myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
334 myAmInTLS(false),
335 myAmMacroscopicConnector(false),
336 myStreetName(tpl->getStreetName()),
337 mySignalPosition(to == tpl->myTo ? tpl->mySignalPosition : Position::INVALID),
338 mySignalNode(to == tpl->myTo ? tpl->mySignalNode : nullptr),
339 myIsOffRamp(false),
340 myIsBidi(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->getFromNode() != getToNode() || oppEdge->getToNode() != getFromNode()) {
3192 WRITE_ERRORF(TL("Opposite lane '%' does not reverse-connect the same nodes as edge '%'!"), oppositeID, getID());
3194 } else {
3195 if (oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
3196 const std::string oppEdgeLeftmost = oppEdge->getLaneID(oppEdge->getNumLanes() - 1);
3197 WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppositeID, getID(), oppEdgeLeftmost);
3198 getLaneStruct(leftmostLane).oppositeID = oppEdgeLeftmost;
3199 }
3200 NBEdge::Lane& oppLane = oppEdge->getLaneStruct(oppEdge->getNumLanes() - 1);
3201 const std::string leftmostID = getLaneID(leftmostLane);
3202 if (oppLane.oppositeID == "") {
3203 WRITE_WARNINGF(TL("Adapting missing opposite lane '%' for edge '%'."), leftmostID, oppEdge->getID());
3204 oppLane.oppositeID = leftmostID;
3205 } else if (oppLane.oppositeID != leftmostID && oppLane.oppositeID != "-") {
3206 const std::string oppOpp = oppLane.oppositeID.substr(0, oppLane.oppositeID.rfind("_"));
3207 NBEdge* oppOppEdge = ec.retrieve(oppOpp);
3208 if (oppOppEdge == nullptr) {
3209 WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppLane.oppositeID, oppEdge->getID(), leftmostID);
3210 oppLane.oppositeID = leftmostID;
3211 } else {
3212 if (oppEdge->getFromNode() != oppOppEdge->getToNode() || oppEdge->getToNode() != oppOppEdge->getFromNode()) {
3213 WRITE_ERRORF(TL("Opposite edge '%' does not reverse-connect the same nodes as edge '%'!"), oppEdge->getID(), oppOppEdge->getID());
3214 } else {
3215 WRITE_WARNINGF(TL("Adapting inconsistent opposite lanes for edges '%', '%' and '%'."), getID(), oppEdge->getID(), oppOpp);
3216 }
3217 oppLane.oppositeID = leftmostID;
3218 NBEdge::Lane& oppOppLane = oppOppEdge->getLaneStruct(oppOppEdge->getNumLanes() - 1);
3219 if (oppOppLane.oppositeID == oppEdge->getLaneID(oppEdge->getNumLanes() - 1)) {
3220 oppOppLane.oppositeID = "";
3221 }
3222 }
3223 }
3224 if (fabs(oppEdge->getLoadedLength() - getLoadedLength()) > NUMERICAL_EPS) {
3225 if (fixOppositeLengths) {
3226 const double avgLength = 0.5 * (getFinalLength() + oppEdge->getFinalLength());
3227 WRITE_WARNINGF(TL("Averaging edge lengths for lane '%' (length %) and edge '%' (length %)."),
3228 oppositeID, oppEdge->getLoadedLength(), getID(), getLoadedLength());
3229 setLoadedLength(avgLength);
3230 oppEdge->setLoadedLength(avgLength);
3231 } else {
3232 WRITE_ERROR("Opposite lane '" + oppositeID + "' (length " + toString(oppEdge->getLoadedLength()) +
3233 ") differs in length from edge '" + getID() + "' (length " +
3234 toString(getLoadedLength()) + "). Set --opposites.guess.fix-lengths to fix this.");
3236 }
3237 }
3238 }
3239 }
3240 }
3241 // check for matching bidi lane shapes (at least for the simple case of 1-lane edges)
3242 const NBEdge* bidi = getBidiEdge();
3243 if (bidi != nullptr && getNumLanes() == 1 && bidi->getNumLanes() == 1 && getID() < bidi->getID()) {
3245 }
3246 // check for valid offset and speed
3247 const double startOffset = isBidiRail() ? getTurnDestination(true)->getEndOffset() : 0;
3248 int i = 0;
3249 for (const NBEdge::Lane& l : getLanes()) {
3250 if (startOffset + l.endOffset > getLength()) {
3251 WRITE_WARNINGF(TL("Invalid endOffset % at lane '%' with length % (startOffset %)."),
3252 toString(l.endOffset), getLaneID(i), toString(l.shape.length()), toString(startOffset));
3253 } else if (l.speed < 0.) {
3254 WRITE_WARNINGF(TL("Negative allowed speed (%) on lane '%', use --speed.minimum to prevent this."), toString(l.speed), getLaneID(i));
3255 } else if (l.speed == 0.) {
3256 WRITE_WARNINGF(TL("Lane '%' has a maximum allowed speed of 0."), getLaneID(i));
3257 }
3258 i++;
3259 }
3260}
3261
3263 // check restrictions
3264 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
3265 Connection& c = *i;
3267 if (common == SVC_PEDESTRIAN || getPermissions(c.fromLane) == SVC_PEDESTRIAN) {
3268 // these are computed in NBNode::buildWalkingAreas
3269#ifdef DEBUG_CONNECTION_CHECKING
3270 std::cout << " remove pedCon " << c.getDescription(this) << "\n";
3271#endif
3272 i = myConnections.erase(i);
3273 } else if (common == 0) {
3274 // no common permissions.
3275 // try to find a suitable target lane to the right
3276 const int origToLane = c.toLane;
3277 c.toLane = -1; // ignore this connection when calling hasConnectionTo
3278 int toLane = origToLane;
3279 while (toLane > 0
3280 && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
3281 && !hasConnectionTo(c.toEdge, toLane)
3282 ) {
3283 toLane--;
3284 }
3285 if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
3286 && !hasConnectionTo(c.toEdge, toLane)) {
3287 c.toLane = toLane;
3288 ++i;
3289 } else {
3290 // try to find a suitable target lane to the left
3291 toLane = origToLane;
3292 while (toLane < (int)c.toEdge->getNumLanes() - 1
3293 && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
3294 && !hasConnectionTo(c.toEdge, toLane)
3295 ) {
3296 toLane++;
3297 }
3298 if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
3299 && !hasConnectionTo(c.toEdge, toLane)) {
3300 c.toLane = toLane;
3301 ++i;
3302 } else {
3303 // no alternative target found
3304#ifdef DEBUG_CONNECTION_CHECKING
3305 std::cout << " remove " << c.getDescription(this) << " with no alternative target\n";
3306#endif
3307 i = myConnections.erase(i);
3308 }
3309 }
3312 // do not allow sharp rail turns
3313#ifdef DEBUG_CONNECTION_CHECKING
3314 std::cout << " remove " << c.getDescription(this) << " (rail turnaround)\n";
3315#endif
3316 i = myConnections.erase(i);
3317 } else {
3318 ++i;
3319 }
3320 }
3321}
3322
3323void
3325 if (outgoing->size() == 0) {
3326 // we have to do this, because the turnaround may have been added before
3327 myConnections.clear();
3328 return;
3329 }
3330
3331#ifdef DEBUG_CONNECTION_GUESSING
3332 if (DEBUGCOND) {
3333 std::cout << " divideOnEdges " << getID() << " outgoing=" << toString(*outgoing) << "\n";
3334 }
3335#endif
3336
3337 // build connections for miv lanes
3338 std::vector<int> availableLanes;
3339 for (int i = 0; i < (int)myLanes.size(); ++i) {
3340 if ((getPermissions(i) & SVC_PASSENGER) != 0) {
3341 availableLanes.push_back(i);
3342 }
3343 }
3344 if (availableLanes.size() > 0) {
3345 divideSelectedLanesOnEdges(outgoing, availableLanes);
3346 }
3347 // build connections for miscellaneous further modes (more than bike,peds,bus and without passenger)
3348 availableLanes.clear();
3349 for (int i = 0; i < (int)myLanes.size(); ++i) {
3350 const SVCPermissions perms = getPermissions(i);
3351 if ((perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) == 0 || (perms & SVC_PASSENGER) != 0 || isForbidden(perms)) {
3352 continue;
3353 }
3354 availableLanes.push_back(i);
3355 }
3356 if (availableLanes.size() > 0) {
3357 divideSelectedLanesOnEdges(outgoing, availableLanes);
3358 }
3359 // build connections for busses from lanes that were excluded in the previous step
3360 availableLanes.clear();
3361 for (int i = 0; i < (int)myLanes.size(); ++i) {
3362 const SVCPermissions perms = getPermissions(i);
3363 if ((perms & SVC_BUS) == 0 || (perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) != 0 || (perms & SVC_PASSENGER) != 0) {
3364 continue;
3365 }
3366 availableLanes.push_back(i);
3367 }
3368 if (availableLanes.size() > 0) {
3369 divideSelectedLanesOnEdges(outgoing, availableLanes);
3370 }
3371 // build connections for bicycles (possibly combined with pedestrians)
3372 availableLanes.clear();
3373 for (int i = 0; i < (int)myLanes.size(); ++i) {
3374 const SVCPermissions perms = getPermissions(i);
3375 if (perms != SVC_BICYCLE && perms != (SVC_BICYCLE | SVC_PEDESTRIAN)) {
3376 continue;
3377 }
3378 availableLanes.push_back(i);
3379 }
3380 if (availableLanes.size() > 0) {
3381 divideSelectedLanesOnEdges(outgoing, availableLanes);
3382 }
3383 // clean up unassigned fromLanes
3384 bool explicitTurnaround = false;
3385 SVCPermissions turnaroundPermissions = SVC_UNSPECIFIED;
3386 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
3387 if ((*i).fromLane == -1) {
3388 if ((*i).toEdge == myTurnDestination && myTurnDestination != nullptr) {
3389 explicitTurnaround = true;
3390 turnaroundPermissions = (*i).permissions;
3391 }
3392 if ((*i).permissions != SVC_UNSPECIFIED) {
3393 for (Connection& c : myConnections) {
3394 if (c.toLane == -1 && c.toEdge == (*i).toEdge) {
3395 // carry over loaded edge2edge permissions
3396 c.permissions = (*i).permissions;
3397 }
3398 }
3399 }
3400 i = myConnections.erase(i);
3401 } else {
3402 ++i;
3403 }
3404 }
3405 if (explicitTurnaround) {
3407 myConnections.back().permissions = turnaroundPermissions;
3408 }
3410}
3411
3412
3413void
3414NBEdge::divideSelectedLanesOnEdges(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
3415 const std::vector<int>& priorities = prepareEdgePriorities(outgoing, availableLanes);
3416 if (priorities.empty()) {
3417 return;
3418 }
3419#ifdef DEBUG_CONNECTION_GUESSING
3420 if (DEBUGCOND) {
3421 std::cout << "divideSelectedLanesOnEdges " << getID() << " out=" << toString(*outgoing) << " prios=" << toString(priorities) << " avail=" << toString(availableLanes) << "\n";
3422 }
3423#endif
3424 // compute the resulting number of lanes that should be used to reach the following edge
3425 const int numOutgoing = (int)outgoing->size();
3426 std::vector<int> resultingLanesFactor;
3427 resultingLanesFactor.reserve(numOutgoing);
3428 int minResulting = std::numeric_limits<int>::max();
3429 for (int i = 0; i < numOutgoing; i++) {
3430 // res / minResulting will be the number of lanes which are meant to reach the current outgoing edge
3431 const int res = priorities[i] * (int)availableLanes.size();
3432 resultingLanesFactor.push_back(res);
3433 if (minResulting > res && res > 0) {
3434 // prevent minResulting from becoming 0
3435 minResulting = res;
3436 }
3437 }
3438 // compute the number of virtual edges
3439 // a virtual edge is used as a replacement for a real edge from now on
3440 // it shall allow to divide the existing lanes on this structure without
3441 // regarding the structure of outgoing edges
3442 int numVirtual = 0;
3443 // compute the transition from virtual to real edges
3444 EdgeVector transition;
3445 transition.reserve(numOutgoing);
3446 for (int i = 0; i < numOutgoing; i++) {
3447 // tmpNum will be the number of connections from this edge to the next edge
3448 assert(i < (int)resultingLanesFactor.size());
3449 const int tmpNum = (resultingLanesFactor[i] + minResulting - 1) / minResulting; // integer division rounding up
3450 numVirtual += tmpNum;
3451 for (int j = 0; j < tmpNum; j++) {
3452 transition.push_back((*outgoing)[i]);
3453 }
3454 }
3455#ifdef DEBUG_CONNECTION_GUESSING
3456 if (DEBUGCOND) {
3457 std::cout << " minResulting=" << minResulting << " numVirtual=" << numVirtual << " availLanes=" << toString(availableLanes) << " resLanes=" << toString(resultingLanesFactor) << " transition=" << toString(transition) << "\n";
3458 }
3459#endif
3460
3461 // assign lanes to edges
3462 // (conversion from virtual to real edges is done)
3463 ToEdgeConnectionsAdder adder(transition);
3464 Bresenham::compute(&adder, static_cast<int>(availableLanes.size()), numVirtual);
3465 const std::map<NBEdge*, std::vector<int> >& l2eConns = adder.getBuiltConnections();
3466 for (NBEdge* const target : *outgoing) {
3467 assert(l2eConns.find(target) != l2eConns.end());
3468 for (const int j : l2eConns.find(target)->second) {
3469 const int fromIndex = availableLanes[j];
3470 if ((getPermissions(fromIndex) & target->getPermissions()) == 0) {
3471 // exclude connection if fromLane and toEdge have no common permissions
3472 continue;
3473 }
3474 if ((getPermissions(fromIndex) & target->getPermissions()) == SVC_PEDESTRIAN) {
3475 // exclude connection if the only commonly permitted class are pedestrians
3476 // these connections are later built in NBNode::buildWalkingAreas
3477 continue;
3478 }
3479 // avoid building more connections than the edge has viable lanes (earlier
3480 // ones have precedence). This is necessary when running divideSelectedLanesOnEdges more than once.
3481 // @todo To decide which target lanes are still available we need to do a
3482 // preliminary lane-to-lane assignment in regard to permissions (rather than to ordering)
3483 const int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
3484 int targetLanes = target->getNumLanes();
3485 if (target->getPermissions(0) == SVC_PEDESTRIAN) {
3486 --targetLanes;
3487 }
3488 if (numConsToTarget >= targetLanes) {
3489 continue;
3490 }
3491 if (myLanes[fromIndex].connectionsDone) {
3492 // we already have complete information about connections from
3493 // this lane. do not add anything else
3494#ifdef DEBUG_CONNECTION_GUESSING
3495 if (DEBUGCOND) {
3496 std::cout << " connectionsDone from " << getID() << "_" << fromIndex << ": ";
3497 for (const Connection& c : getConnectionsFromLane(fromIndex)) {
3498 std::cout << c.getDescription(this) << ", ";
3499 }
3500 std::cout << "\n";
3501 }
3502#endif
3503 continue;
3504 }
3505 myConnections.push_back(Connection(fromIndex, target, -1));
3506#ifdef DEBUG_CONNECTION_GUESSING
3507 if (DEBUGCOND) {
3508 std::cout << " request connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3509 }
3510#endif
3511 }
3512 }
3513
3514 addStraightConnections(outgoing, availableLanes, priorities);
3515}
3516
3517
3518void
3519NBEdge::addStraightConnections(const EdgeVector* outgoing, const std::vector<int>& availableLanes, const std::vector<int>& priorities) {
3520 // ensure sufficient straight connections for the (highest-priority) straight target
3521 const int numOutgoing = (int) outgoing->size();
3522 NBEdge* target = nullptr;
3523 NBEdge* rightOfTarget = nullptr;
3524 NBEdge* leftOfTarget = nullptr;
3525 int maxPrio = 0;
3526 for (int i = 0; i < numOutgoing; i++) {
3527 if (maxPrio < priorities[i]) {
3528 const LinkDirection dir = myTo->getDirection(this, (*outgoing)[i]);
3529 if (dir == LinkDirection::STRAIGHT) {
3530 maxPrio = priorities[i];
3531 target = (*outgoing)[i];
3532 rightOfTarget = i == 0 ? outgoing->back() : (*outgoing)[i - 1];
3533 leftOfTarget = i + 1 == numOutgoing ? outgoing->front() : (*outgoing)[i + 1];
3534 }
3535 }
3536 }
3537 if (target == nullptr) {
3538 return;
3539 }
3540 int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
3541 int targetLanes = (int)target->getNumLanes();
3542 if (target->getPermissions(0) == SVC_PEDESTRIAN) {
3543 --targetLanes;
3544 }
3545 const int numDesiredConsToTarget = MIN2(targetLanes, (int)availableLanes.size());
3546#ifdef DEBUG_CONNECTION_GUESSING
3547 if (DEBUGCOND) {
3548 std::cout << " checking extra lanes for target=" << target->getID() << " cons=" << numConsToTarget << " desired=" << numDesiredConsToTarget << "\n";
3549 }
3550#endif
3551 std::vector<int>::const_iterator it_avail = availableLanes.begin();
3552 while (numConsToTarget < numDesiredConsToTarget && it_avail != availableLanes.end()) {
3553 const int fromIndex = *it_avail;
3554 if (
3555 // not yet connected
3556 (count_if(myConnections.begin(), myConnections.end(), connections_finder(fromIndex, target, -1)) == 0)
3557 // matching permissions
3558 && ((getPermissions(fromIndex) & target->getPermissions()) != 0)
3559 // more than pedestrians
3560 && ((getPermissions(fromIndex) & target->getPermissions()) != SVC_PEDESTRIAN)
3561 // lane not yet fully defined
3562 && !myLanes[fromIndex].connectionsDone
3563 ) {
3564#ifdef DEBUG_CONNECTION_GUESSING
3565 if (DEBUGCOND) {
3566 std::cout << " candidate from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3567 }
3568#endif
3569 // prevent same-edge conflicts
3570 if (
3571 // no outgoing connections to the right from further left
3572 ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
3573 // no outgoing connections to the left from further right
3574 && (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)) {
3575#ifdef DEBUG_CONNECTION_GUESSING
3576 if (DEBUGCOND) {
3577 std::cout << " request additional connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3578 }
3579#endif
3580 myConnections.push_back(Connection(fromIndex, target, -1));
3581 numConsToTarget++;
3582 } else {
3583#ifdef DEBUG_CONNECTION_GUESSING
3584 if (DEBUGCOND) std::cout
3585 << " fail check1="
3586 << ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
3587 << " check2=" << (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)
3588 << " rightOfTarget=" << rightOfTarget->getID()
3589 << " leftOfTarget=" << leftOfTarget->getID()
3590 << "\n";
3591#endif
3592
3593 }
3594 }
3595 ++it_avail;
3596 }
3597}
3598
3599
3600const std::vector<int>
3601NBEdge::prepareEdgePriorities(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
3602 std::vector<int> priorities;
3603 MainDirections mainDirections(*outgoing, this, myTo, availableLanes);
3604 const int dist = mainDirections.getStraightest();
3605 if (dist == -1) {
3606 return priorities;
3607 }
3608 // copy the priorities first
3609 priorities.reserve(outgoing->size());
3610 for (const NBEdge* const out : *outgoing) {
3611 int prio = NBNode::isTrafficLight(myTo->getType()) ? 0 : out->getJunctionPriority(myTo);
3612 assert((prio + 1) * 2 > 0);
3613 prio = (prio + 1) * 2;
3614 priorities.push_back(prio);
3615 }
3616 // when the right turning direction has not a higher priority, divide
3617 // the importance by 2 due to the possibility to leave the junction
3618 // faster from this lane
3619#ifdef DEBUG_CONNECTION_GUESSING
3620 if (DEBUGCOND) std::cout << " prepareEdgePriorities " << getID()
3621 << " outgoing=" << toString(*outgoing)
3622 << " priorities1=" << toString(priorities)
3623 << " dist=" << dist
3624 << "\n";
3625#endif
3626 if (dist != 0 && !mainDirections.includes(MainDirections::Direction::RIGHTMOST)) {
3627 assert(priorities.size() > 0);
3628 priorities[0] /= 2;
3629#ifdef DEBUG_CONNECTION_GUESSING
3630 if (DEBUGCOND) {
3631 std::cout << " priorities2=" << toString(priorities) << "\n";
3632 }
3633#endif
3634 }
3635 // HEURISTIC:
3636 // when no higher priority exists, let the forward direction be
3637 // the main direction
3638 if (mainDirections.empty()) {
3639 assert(dist < (int)priorities.size());
3640 priorities[dist] *= 2;
3641#ifdef DEBUG_CONNECTION_GUESSING
3642 if (DEBUGCOND) {
3643 std::cout << " priorities3=" << toString(priorities) << "\n";
3644 }
3645#endif
3646 }
3648 priorities[dist] += 1;
3649 } else {
3650 // try to ensure separation of left turns
3652 priorities[0] /= 4;
3653 priorities[(int)priorities.size() - 1] /= 2;
3654#ifdef DEBUG_CONNECTION_GUESSING
3655 if (DEBUGCOND) {
3656 std::cout << " priorities6=" << toString(priorities) << "\n";
3657 }
3658#endif
3659 } else if (mainDirections.includes(MainDirections::Direction::RIGHTMOST)
3660 && outgoing->size() > 2
3661 && availableLanes.size() == 2
3662 && (*outgoing)[dist]->getPriority() == (*outgoing)[0]->getPriority()) {
3663 priorities[0] /= 4;
3664 priorities.back() /= 2;
3665#ifdef DEBUG_CONNECTION_GUESSING
3666 if (DEBUGCOND) {
3667 std::cout << " priorities7=" << toString(priorities) << "\n";
3668 }
3669#endif
3670 }
3671 }
3672 if (mainDirections.includes(MainDirections::Direction::FORWARD)) {
3673 if (myLanes.size() > 2) {
3674 priorities[dist] *= 2;
3675#ifdef DEBUG_CONNECTION_GUESSING
3676 if (DEBUGCOND) {
3677 std::cout << " priorities4=" << toString(priorities) << "\n";
3678 }
3679#endif
3680 } else {
3681 priorities[dist] *= 3;
3682#ifdef DEBUG_CONNECTION_GUESSING
3683 if (DEBUGCOND) {
3684 std::cout << " priorities5=" << toString(priorities) << "\n";
3685 }
3686#endif
3687 }
3688 }
3689 return priorities;
3690}
3691
3692
3693void
3694NBEdge::appendTurnaround(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike, bool checkPermissions) {
3695 // do nothing if no turnaround is known
3697 return;
3698 }
3699 // do nothing if the destination node is controlled by a tls and no turnarounds
3700 // shall be appended for such junctions
3701 if (noTLSControlled && myTo->isTLControlled()) {
3702 return;
3703 }
3704 if (noFringe && myTo->getFringeType() == FringeType::OUTER) {
3705 return;
3706 }
3707 bool isDeadEnd = true;
3708 for (const Connection& c : myConnections) {
3709 if ((c.toEdge->getPermissions(c.toLane)
3710 & getPermissions(c.fromLane)
3711 & SVC_PASSENGER) != 0
3712 || (c.toEdge->getPermissions() & getPermissions()) == getPermissions()) {
3713 isDeadEnd = false;
3714 break;
3715 }
3716 }
3717 if (onlyDeadends && !isDeadEnd) {
3718 return;
3719 }
3720 const int fromLane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
3721 if (onlyTurnlane) {
3722 for (const Connection& c : getConnectionsFromLane(fromLane)) {
3723 LinkDirection dir = myTo->getDirection(this, c.toEdge);
3724 if (dir != LinkDirection::LEFT && dir != LinkDirection::PARTLEFT) {
3725 return;
3726 }
3727 }
3728 }
3730 if (checkPermissions) {
3731 if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == 0) {
3732 // exclude connection if fromLane and toEdge have no common permissions
3733 return;
3734 }
3735 if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == SVC_PEDESTRIAN) {
3736 // exclude connection if the only commonly permitted class are pedestrians
3737 // these connections are later built in NBNode::buildWalkingAreas
3738 return;
3739 }
3740 }
3741 // avoid railway turn-arounds
3744 // except at dead-ends on bidi-edges where they model a reversal in train direction
3745 // @todo #4382: once the network fringe is tagged, it also should not receive turn-arounds)
3746 if (isBidiRail() && isRailDeadEnd()) {
3747 // add a slow connection because direction-reversal implies stopping
3749 return;
3750 } else {
3751 return;
3752 }
3753 };
3754 if (noGeometryLike && !isDeadEnd) {
3755 // ignore paths and service entrances if this edge is for passenger traffic
3756 if (myTo->geometryLike() || ((getPermissions() & SVC_PASSENGER) != 0
3757 && !onlyTurnlane
3758 && myTo->geometryLike(
3761 // make sure the turnDestination has other incoming edges
3763 if (turnIncoming.size() > 1) {
3764 // this edge is always part of incoming
3765 return;
3766 }
3767 }
3768 }
3770}
3771
3772
3773bool
3774NBEdge::isTurningDirectionAt(const NBEdge* const edge) const {
3775 // maybe it was already set as the turning direction
3776 if (edge == myTurnDestination) {
3777 return true;
3778 } else if (myTurnDestination != nullptr) {
3779 // otherwise - it's not if a turning direction exists
3780 return false;
3781 }
3782 return edge == myPossibleTurnDestination;
3783}
3784
3785
3786NBNode*
3787NBEdge::tryGetNodeAtPosition(double pos, double tolerance) const {
3788 // return the from-node when the position is at the begin of the edge
3789 if (pos < tolerance) {
3790 return myFrom;
3791 }
3792 // return the to-node when the position is at the end of the edge
3793 if (pos > myLength - tolerance) {
3794 return myTo;
3795 }
3796 return nullptr;
3797}
3798
3799
3800void
3802 int lanes = e->getNumLanes();
3803 for (int i = 0; i < lanes; i++) {
3804 for (const NBEdge::Connection& el : e->getConnectionsFromLane(i)) {
3805 assert(el.tlID == "");
3806 addLane2LaneConnection(i + laneOff, el.toEdge, el.toLane, Lane2LaneInfoType::COMPUTED);
3807 }
3808 }
3809}
3810
3811
3812bool
3816
3817
3818double
3820 return SUMO_const_laneWidth * (double)myLanes.size();
3821}
3822
3823
3824bool
3825NBEdge::mayBeTLSControlled(int fromLane, NBEdge* toEdge, int toLane) const {
3826 for (const Connection& c : myConnections) {
3827 if (c.fromLane == fromLane && c.toEdge == toEdge && c.toLane == toLane && c.uncontrolled) {
3828 return false;
3829 }
3830 }
3831 return true;
3832}
3833
3834
3835bool
3836NBEdge::setControllingTLInformation(const NBConnection& c, const std::string& tlID) {
3837 const int fromLane = c.getFromLane();
3838 NBEdge* toEdge = c.getTo();
3839 const int toLane = c.getToLane();
3840 const int tlIndex = c.getTLIndex();
3841 const int tlIndex2 = c.getTLIndex2();
3842 // check whether the connection was not set as not to be controled previously
3843 if (!mayBeTLSControlled(fromLane, toEdge, toLane)) {
3844 return false;
3845 }
3846
3847 assert(fromLane < 0 || fromLane < (int) myLanes.size());
3848 // try to use information about the connections if given
3849 if (fromLane >= 0 && toLane >= 0) {
3850 // find the specified connection
3851 std::vector<Connection>::iterator i =
3852 find_if(myConnections.begin(), myConnections.end(), connections_finder(fromLane, toEdge, toLane));
3853 // ok, we have to test this as on the removal of self-loop edges some connections
3854 // will be reassigned
3855 if (i != myConnections.end()) {
3856 // get the connection
3857 Connection& connection = *i;
3858 // set the information about the tl
3859 connection.tlID = tlID;
3860 connection.tlLinkIndex = tlIndex;
3861 connection.tlLinkIndex2 = tlIndex2;
3862 return true;
3863 }
3864 }
3865 // if the original connection was not found, set the information for all
3866 // connections
3867 int no = 0;
3868 bool hadError = false;
3869 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
3870 if ((*i).toEdge != toEdge) {
3871 continue;
3872 }
3873 if (fromLane >= 0 && fromLane != (*i).fromLane) {
3874 continue;
3875 }
3876 if (toLane >= 0 && toLane != (*i).toLane) {
3877 continue;
3878 }
3879 if ((*i).tlID == "") {
3880 (*i).tlID = tlID;
3881 (*i).tlLinkIndex = tlIndex;
3882 (*i).tlLinkIndex2 = tlIndex2;
3883 no++;
3884 } else {
3885 if ((*i).tlID != tlID && (*i).tlLinkIndex == tlIndex) {
3886 WRITE_WARNINGF(TL("The lane '%' on edge '%' already had a traffic light signal."), i->fromLane, getID());
3887 hadError = true;
3888 }
3889 }
3890 }
3891 if (hadError && no == 0) {
3892 WRITE_WARNINGF(TL("Could not set any signal of the tlLogic '%' (unknown group)."), tlID);
3893 }
3894 return true;
3895}
3896
3897
3898void
3900 for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); it++) {
3901 it->tlID = "";
3902 }
3903}
3904
3905
3908 PositionVector ret;
3909 int lane;
3910 if (myFrom == (&n)) {
3911 // outgoing
3913 ret = myLanes[lane].shape;
3914 } else {
3915 // incoming
3917 ret = myLanes[lane].shape.reverse();
3918 }
3919 ret.move2side(getLaneWidth(lane) / 2.);
3920 return ret;
3921}
3922
3923
3926 PositionVector ret;
3927 int lane;
3928 if (myFrom == (&n)) {
3929 // outgoing
3931 ret = myLanes[lane].shape;
3932 } else {
3933 // incoming
3935 ret = myLanes[lane].shape.reverse();
3936 }
3937 ret.move2side(-getLaneWidth(lane) / 2.);
3938 return ret;
3939}
3940
3941
3942bool
3943NBEdge::expandableBy(NBEdge* possContinuation, std::string& reason) const {
3944 // ok, the number of lanes must match
3945 if (myLanes.size() != possContinuation->myLanes.size()) {
3946 reason = "laneNumber";
3947 return false;
3948 }
3949 // do not create self loops
3950 if (myFrom == possContinuation->myTo) {
3951 reason = "loop";
3952 return false;
3953 }
3954 // conserve bidi-rails
3955 if (isBidiRail() != possContinuation->isBidiRail()) {
3956 reason = "bidi-rail";
3957 return false;
3958 }
3959 // also, check whether the connections - if any exit do allow to join
3960 // both edges
3961 // This edge must have a one-to-one connection to the following lanes
3962 switch (myStep) {
3964 break;
3966 break;
3968 // the following edge must be connected
3969 const EdgeVector& conn = getConnectedEdges();
3970 if (find(conn.begin(), conn.end(), possContinuation) == conn.end()) {
3971 reason = "disconnected";
3972 return false;
3973 }
3974 }
3975 break;
3980 // the possible continuation must be connected
3981 if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(possContinuation)) == myConnections.end()) {
3982 reason = "disconnected";
3983 return false;
3984 }
3985 // all lanes must go to the possible continuation
3986 std::vector<int> conns = getConnectionLanes(possContinuation);
3987 const int offset = MAX2(0, getFirstNonPedestrianLaneIndex(NBNode::FORWARD, true));
3988 if (conns.size() < myLanes.size() - offset) {
3989 reason = "some lanes disconnected";
3990 return false;
3991 }
3992 }
3993 break;
3994 default:
3995 break;
3996 }
3997 const double minLength = OptionsCont::getOptions().getFloat("geometry.remove.min-length");
3998 if (minLength > 0 && (possContinuation->getLoadedLength() < minLength || getLoadedLength() < minLength)) {
3999 return true;
4000 }
4001 const double maxJunctionSize = OptionsCont::getOptions().getFloat("geometry.remove.max-junction-size");
4002 if (maxJunctionSize >= 0) {
4003 const double junctionSize = myGeom.back().distanceTo2D(possContinuation->myGeom.front());
4004 if (junctionSize > maxJunctionSize + POSITION_EPS) {
4005 reason = "junction size (" + toString(junctionSize) + ") > max-junction-size (" + toString(maxJunctionSize) + ")";
4006 return false;
4007 }
4008 }
4009 // the priority, too (?)
4010 if (getPriority() != possContinuation->getPriority()) {
4011 reason = "priority";
4012 return false;
4013 }
4014 // the speed allowed
4015 if (mySpeed != possContinuation->mySpeed) {
4016 reason = "speed";
4017 return false;
4018 }
4019 // spreadtype should match or it will look ugly
4020 if (myLaneSpreadFunction != possContinuation->myLaneSpreadFunction) {
4021 reason = "spreadType";
4022 return false;
4023 }
4024 // matching lanes must have identical properties
4025 for (int i = 0; i < (int)myLanes.size(); i++) {
4026 if (myLanes[i].speed != possContinuation->myLanes[i].speed) {
4027 reason = "lane " + toString(i) + " speed";
4028 return false;
4029 } else if (myLanes[i].permissions != possContinuation->myLanes[i].permissions) {
4030 reason = "lane " + toString(i) + " permissions";
4031 return false;
4032 } else if (myLanes[i].changeLeft != possContinuation->myLanes[i].changeLeft || myLanes[i].changeRight != possContinuation->myLanes[i].changeRight) {
4033 reason = "lane " + toString(i) + " change restrictions";
4034 return false;
4035 } else if (myLanes[i].width != possContinuation->myLanes[i].width &&
4036 fabs(myLanes[i].width - possContinuation->myLanes[i].width) > OptionsCont::getOptions().getFloat("geometry.remove.width-tolerance")) {
4037 reason = "lane " + toString(i) + " width";
4038 return false;
4039 }
4040 }
4041 // if given identically osm names
4042 if (!OptionsCont::getOptions().isDefault("output.street-names") && myStreetName != possContinuation->getStreetName()
4043 && ((myStreetName != "" && possContinuation->getStreetName() != "")
4044 // only permit merging a short unnamed road with a longer named road
4045 || (myStreetName != "" && myLength <= possContinuation->getLength())
4046 || (myStreetName == "" && myLength >= possContinuation->getLength()))) {
4047 return false;
4048 }
4049
4050 return true;
4051}
4052
4053
4054void
4056 // append geometry
4057 myGeom.append(e->myGeom);
4058 for (int i = 0; i < (int)myLanes.size(); i++) {
4059 myLanes[i].customShape.append(e->myLanes[i].customShape);
4060 if (myLanes[i].hasParameter(SUMO_PARAM_ORIGID) || e->myLanes[i].hasParameter(SUMO_PARAM_ORIGID)
4061 || OptionsCont::getOptions().getBool("output.original-names")) {
4062 const std::string origID = myLanes[i].getParameter(SUMO_PARAM_ORIGID, getID());
4063 const std::string origID2 = e->myLanes[i].getParameter(SUMO_PARAM_ORIGID, e->getID());
4064 if (origID != origID2) {
4065 myLanes[i].setParameter(SUMO_PARAM_ORIGID, origID + " " + origID2);
4066 }
4067 }
4068 myLanes[i].connectionsDone = e->myLanes[i].connectionsDone;
4069 myLanes[i].turnSigns = e->myLanes[i].turnSigns;
4070 }
4071 if (e->getLength() > myLength) {
4072 // possibly some lane attributes differ (when using option geometry.remove.min-length)
4073 // make sure to use the attributes from the longer edge
4074 for (int i = 0; i < (int)myLanes.size(); i++) {
4075 myLanes[i].width = e->myLanes[i].width;
4076 }
4077 // defined name prevails over undefined name of shorter road
4078 if (myStreetName == "") {
4080 }
4081 }
4082 // recompute length
4083 myLength += e->myLength;
4084 if (myLoadedLength > 0 || e->myLoadedLength > 0) {
4086 }
4087 // copy the connections and the building step if given
4088 myStep = e->myStep;
4093 // set the node
4094 myTo = e->myTo;
4100 }
4101 computeAngle(); // myEndAngle may be different now
4102}
4103
4104
4105bool
4107 for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
4108 if ((*i).toEdge == e && (*i).tlID != "") {
4109 return true;
4110 }
4111 }
4112 return false;
4113}
4114
4115
4116NBEdge*
4117NBEdge::getTurnDestination(bool possibleDestination) const {
4118 if (myTurnDestination == nullptr && possibleDestination) {
4120 }
4121 return myTurnDestination;
4122}
4123
4124
4125std::string
4126NBEdge::getLaneID(int lane) const {
4127 return myID + "_" + toString(lane);
4128}
4129
4130
4131bool
4132NBEdge::isNearEnough2BeJoined2(NBEdge* e, double threshold) const {
4133 std::vector<double> distances = myGeom.distances(e->getGeometry());
4134 assert(distances.size() > 0);
4135 return VectorHelper<double>::maxValue(distances) < threshold;
4136}
4137
4138
4139void
4140NBEdge::addLane(int index, bool recomputeShape, bool recomputeConnections, bool shiftIndices) {
4141 assert(index <= (int)myLanes.size());
4142 myLanes.insert(myLanes.begin() + index, Lane(this, ""));
4143 // copy attributes
4144 if (myLanes.size() > 1) {
4145 int templateIndex = index > 0 ? index - 1 : index + 1;
4146 myLanes[index].speed = myLanes[templateIndex].speed;
4147 myLanes[index].friction = myLanes[templateIndex].friction;
4148 myLanes[index].permissions = myLanes[templateIndex].permissions;
4149 myLanes[index].preferred = myLanes[templateIndex].preferred;
4150 myLanes[index].endOffset = myLanes[templateIndex].endOffset;
4151 myLanes[index].width = myLanes[templateIndex].width;
4152 myLanes[index].updateParameters(myLanes[templateIndex].getParametersMap());
4153 }
4154 const EdgeVector& incs = myFrom->getIncomingEdges();
4155 if (recomputeShape) {
4157 }
4158 if (recomputeConnections) {
4159 for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
4160 (*i)->invalidateConnections(true);
4161 }
4163 } else if (shiftIndices) {
4164 // shift outgoing connections above the added lane to the left
4165 for (Connection& c : myConnections) {
4166 if (c.fromLane >= index) {
4167 c.fromLane += 1;
4168 }
4169 }
4170 // shift incoming connections above the added lane to the left
4171 for (NBEdge* inc : myFrom->getIncomingEdges()) {
4172 for (Connection& c : inc->myConnections) {
4173 if (c.toEdge == this && c.toLane >= index) {
4174 c.toLane += 1;
4175 }
4176 }
4177 }
4178 myFrom->shiftTLConnectionLaneIndex(this, +1, index - 1);
4179 myTo->shiftTLConnectionLaneIndex(this, +1, index - 1);
4180 }
4181}
4182
4183void
4185 int newLaneNo = (int)myLanes.size() + by;
4186 while ((int)myLanes.size() < newLaneNo) {
4187 // recompute shapes on last addition
4188 const bool recompute = ((int)myLanes.size() == newLaneNo - 1) && myStep < EdgeBuildingStep::LANES2LANES_USER;
4189 addLane((int)myLanes.size(), recompute, recompute, false);
4190 }
4191}
4192
4193
4194void
4195NBEdge::deleteLane(int index, bool recompute, bool shiftIndices) {
4196 assert(index < (int)myLanes.size());
4197 myLanes.erase(myLanes.begin() + index);
4198 if (recompute) {
4200 const EdgeVector& incs = myFrom->getIncomingEdges();
4201 for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
4202 (*i)->invalidateConnections(true);
4203 }
4205 } else if (shiftIndices) {
4206 removeFromConnections(nullptr, index, -1, false, true);
4207 for (NBEdge* inc : myFrom->getIncomingEdges()) {
4208 inc->removeFromConnections(this, -1, index, false, true);
4209 }
4210 }
4211}
4212
4213
4214void
4216 int newLaneNo = (int) myLanes.size() - by;
4217 assert(newLaneNo > 0);
4218 while ((int)myLanes.size() > newLaneNo) {
4219 // recompute shapes on last removal
4220 const bool recompute = (int)myLanes.size() == newLaneNo + 1 && myStep < EdgeBuildingStep::LANES2LANES_USER;
4221 deleteLane((int)myLanes.size() - 1, recompute, false);
4222 }
4223}
4224
4225
4226void
4231
4232
4233void
4235 if (lane < 0) { // all lanes are meant...
4236 for (int i = 0; i < (int)myLanes.size(); i++) {
4237 allowVehicleClass(i, vclass);
4238 }
4239 } else {
4240 assert(lane < (int)myLanes.size());
4241 myLanes[lane].permissions |= vclass;
4242 }
4243}
4244
4245
4246void
4248 if (lane < 0) { // all lanes are meant...
4249 for (int i = 0; i < (int)myLanes.size(); i++) {
4250 disallowVehicleClass((int) i, vclass);
4251 }
4252 } else {
4253 assert(lane < (int)myLanes.size());
4254 myLanes[lane].permissions &= ~vclass;
4255 }
4256}
4257
4258
4259void
4261 if (lane < 0) { // all lanes are meant...
4262 for (int i = 0; i < (int)myLanes.size(); i++) {
4263 preferVehicleClass(i, vclasses);
4264 }
4265 } else {
4266 assert(lane < (int)myLanes.size());
4267 myLanes[lane].permissions |= vclasses;
4268 myLanes[lane].preferred |= vclasses;
4269 }
4270}
4271
4272
4273void
4274NBEdge::setLaneWidth(int lane, double width) {
4275 if (lane < 0) {
4276 // all lanes are meant...
4277 myLaneWidth = width;
4278 for (int i = 0; i < (int)myLanes.size(); i++) {
4279 // ... do it for each lane
4280 setLaneWidth(i, width);
4281 }
4282 return;
4283 }
4284 assert(lane < (int)myLanes.size());
4285 myLanes[lane].width = width;
4286}
4287
4288void
4289NBEdge::setLaneType(int lane, const std::string& type) {
4290 if (lane < 0) {
4291 for (int i = 0; i < (int)myLanes.size(); i++) {
4292 // ... do it for each lane
4293 setLaneType(i, type);
4294 }
4295 return;
4296 }
4297 assert(lane < (int)myLanes.size());
4298 myLanes[lane].type = type;
4299}
4300
4301
4302double
4303NBEdge::getLaneWidth(int lane) const {
4304 return myLanes[lane].width != UNSPECIFIED_WIDTH
4305 ? myLanes[lane].width
4307}
4308
4309double
4311 const NBNode& node,
4312 const NBEdge::Connection& connection,
4313 const NBEdge::Lane& successor,
4314 bool isVia) const {
4315
4316 if (!isVia && node.isConstantWidthTransition() && getNumLanes() > connection.toEdge->getNumLanes()) {
4317 return getLaneWidth(connection.fromLane);
4318 }
4319
4320 return (isBikepath(getPermissions(connection.fromLane)) && (
4321 getLaneWidth(connection.fromLane) < successor.width || successor.width == UNSPECIFIED_WIDTH)) ?
4322 myLanes[connection.fromLane].width : successor.width; // getLaneWidth(connection.fromLane) never returns -1 (UNSPECIFIED_WIDTH)
4323}
4324
4325double
4327 double result = 0;
4328 for (int i = 0; i < (int)myLanes.size(); i++) {
4329 result += getLaneWidth(i);
4330 }
4331 return result;
4332}
4333
4334double
4335NBEdge::getEndOffset(int lane) const {
4336 return myLanes[lane].endOffset != UNSPECIFIED_OFFSET ? myLanes[lane].endOffset : getEndOffset();
4337}
4338
4339
4340const StopOffset&
4342 return myEdgeStopOffset;
4343}
4344
4345
4346const StopOffset&
4348 if (lane == -1) {
4349 return myEdgeStopOffset;
4350 } else {
4351 return myLanes[lane].laneStopOffset;
4352 }
4353}
4354
4355
4356void
4357NBEdge::setEndOffset(int lane, double offset) {
4358 if (lane < 0) {
4359 // all lanes are meant...
4360 myEndOffset = offset;
4361 for (int i = 0; i < (int)myLanes.size(); i++) {
4362 // ... do it for each lane
4363 setEndOffset(i, offset);
4364 }
4365 return;
4366 }
4367 assert(lane < (int)myLanes.size());
4368 myLanes[lane].endOffset = offset;
4369}
4370
4371
4372bool
4373NBEdge::setEdgeStopOffset(int lane, const StopOffset& offset, bool overwrite) {
4374 if (lane < 0) {
4375 if (!overwrite && myEdgeStopOffset.isDefined()) {
4376 return false;
4377 }
4378 // all lanes are meant...
4379 if (offset.getOffset() < 0) {
4380 // Edge length unknown at parsing time, thus check here.
4381 WRITE_WARNINGF(TL("Ignoring invalid stopOffset for edge '%' (negative offset)."), getID());
4382 return false;
4383 } else {
4384 myEdgeStopOffset = offset;
4385 }
4386 } else if (lane < (int)myLanes.size()) {
4387 if (!myLanes[lane].laneStopOffset.isDefined() || overwrite) {
4388 if (offset.getOffset() < 0) {
4389 // Edge length unknown at parsing time, thus check here.
4390 WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (negative offset)."), getLaneID(lane));
4391 } else {
4392 myLanes[lane].laneStopOffset = offset;
4393 }
4394 }
4395 } else {
4396 WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (invalid lane index)."), toString(lane));
4397 }
4398 return true;
4399}
4400
4401
4402void
4403NBEdge::setSpeed(int lane, double speed) {
4404 if (lane < 0) {
4405 // all lanes are meant...
4406 mySpeed = speed;
4407 for (int i = 0; i < (int)myLanes.size(); i++) {
4408 // ... do it for each lane
4409 setSpeed(i, speed);
4410 }
4411 return;
4412 }
4413 assert(lane < (int)myLanes.size());
4414 myLanes[lane].speed = speed;
4415}
4416
4417
4418void
4419NBEdge::setFriction(int lane, double friction) {
4420 if (lane < 0) {
4421 // all lanes are meant...
4422 myFriction = friction;
4423 for (int i = 0; i < (int)myLanes.size(); i++) {
4424 // ... do it for each lane
4425 setFriction(i, friction);
4426 }
4427 return;
4428 }
4429 assert(lane < (int)myLanes.size());
4430 myLanes[lane].friction = friction;
4431}
4432
4433
4434void
4435NBEdge::setAcceleration(int lane, bool accelRamp) {
4436 assert(lane >= 0);
4437 assert(lane < (int)myLanes.size());
4438 myLanes[lane].accelRamp = accelRamp;
4439}
4440
4441
4442void
4443NBEdge::setLaneShape(int lane, const PositionVector& shape) {
4444 assert(lane >= 0);
4445 assert(lane < (int)myLanes.size());
4446 myLanes[lane].customShape = shape;
4447}
4448
4449
4450void
4452 if (lane < 0) {
4453 for (int i = 0; i < (int)myLanes.size(); i++) {
4454 // ... do it for each lane
4455 setPermissions(permissions, i);
4456 }
4457 } else {
4458 assert(lane < (int)myLanes.size());
4459 myLanes[lane].permissions = permissions;
4460 }
4461}
4462
4463
4464void
4466 if (lane < 0) {
4467 for (int i = 0; i < (int)myLanes.size(); i++) {
4468 // ... do it for each lane
4469 setPreferredVehicleClass(permissions, i);
4470 }
4471 } else {
4472 assert(lane < (int)myLanes.size());
4473 myLanes[lane].preferred = permissions;
4474 }
4475}
4476
4477
4478void
4480 assert(lane >= 0);
4481 assert(lane < (int)myLanes.size());
4482 myLanes[lane].changeLeft = changeLeft;
4483 myLanes[lane].changeRight = changeRight;
4484}
4485
4486
4488NBEdge::getPermissions(int lane) const {
4489 if (lane < 0) {
4490 SVCPermissions result = 0;
4491 for (int i = 0; i < (int)myLanes.size(); i++) {
4492 result |= getPermissions(i);
4493 }
4494 return result;
4495 } else {
4496 assert(lane < (int)myLanes.size());
4497 return myLanes[lane].permissions;
4498 }
4499}
4500
4501
4502void
4504 myLoadedLength = val;
4505}
4506
4507void
4509 myLength = val;
4510}
4511
4512
4513void
4515 for (std::vector<Lane>::iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
4516 (*i).permissions = SVCAll;
4517 (*i).preferred = 0;
4518 }
4519}
4520
4521
4522bool
4524 if (c1.fromLane != c2.fromLane) {
4525 return c1.fromLane < c2.fromLane;
4526 }
4527 if (c1.toEdge != c2.toEdge) {
4528 return false; // do not change ordering among toEdges as this is determined by angle in an earlier step
4529 }
4530 return c1.toLane < c2.toLane;
4531}
4532
4533
4534double
4538 } else {
4540 myLanes.back().shape.back() : myLanes[getNumLanes() / 2].shape.back();
4541 //std::cout << getID() << " signalPos=" << mySignalPosition << " laneEnd=" << laneEnd << " toShape=" << myTo->getShape() << " toBorder=" << myToBorder << "\n";
4542 return mySignalPosition.distanceTo2D(laneEnd);
4543 }
4544}
4545
4546
4547int
4548NBEdge::getFirstNonPedestrianLaneIndex(int direction, bool exclusive) const {
4549 assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4550 const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4551 const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4552 for (int i = start; i != end; i += direction) {
4553 // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
4554 // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
4555 if ((exclusive && myLanes[i].permissions != SVC_PEDESTRIAN && myLanes[i].permissions != 0)
4556 || ((myLanes[i].permissions & SVC_PEDESTRIAN) == 0 && myLanes[i].permissions != 0)) {
4557 return i;
4558 }
4559 }
4560 return -1;
4561}
4562
4563int
4564NBEdge::getFirstNonPedestrianNonBicycleLaneIndex(int direction, bool exclusive) const {
4565 assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4566 const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4567 const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4568 for (int i = start; i != end; i += direction) {
4569 // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
4570 // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
4571 SVCPermissions p = myLanes[i].permissions;
4572 if ((exclusive && p != SVC_PEDESTRIAN && p != SVC_BICYCLE && p != (SVC_PEDESTRIAN | SVC_BICYCLE) && p != 0)
4573 || (p == SVCAll || ((p & (SVC_PEDESTRIAN | SVC_BICYCLE)) == 0 && p != 0))) {
4574 return i;
4575 }
4576 }
4577 return -1;
4578}
4579
4580int
4582 for (int i = 0; i < (int)myLanes.size(); i++) {
4583 if (myLanes[i].permissions == permissions) {
4584 return i;
4585 }
4586 }
4587 return -1;
4588}
4589
4590int
4592 assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4593 const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4594 const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4595 for (int i = start; i != end; i += direction) {
4596 if (myLanes[i].permissions != 0) {
4597 return i;
4598 }
4599 }
4600 return end - direction;
4601}
4602
4603
4604std::set<SVCPermissions>
4605NBEdge::getPermissionVariants(int iStart, int iEnd) const {
4606 std::set<SVCPermissions> result;
4607 if (iStart < 0 || iStart >= getNumLanes() || iEnd > getNumLanes()) {
4608 throw ProcessError("invalid indices iStart " + toString(iStart) + " iEnd " + toString(iEnd) + " for edge with " + toString(getNumLanes()) + " lanes.");
4609 }
4610 for (int i = iStart; i < iEnd; ++i) {
4611 result.insert(getPermissions(i));
4612 }
4613 return result;
4614}
4615
4616int
4617NBEdge::getNumLanesThatAllow(SVCPermissions permissions, bool allPermissions) const {
4618 int result = 0;
4619 for (const Lane& lane : myLanes) {
4620 if ((allPermissions && (lane.permissions & permissions) == permissions)
4621 || (!allPermissions && (lane.permissions & permissions) != 0)) {
4622 result++;
4623 }
4624 }
4625 return result;
4626}
4627
4628bool
4630 assert(lane >= 0 && lane < getNumLanes());
4631 return myLanes[lane].changeLeft == SVC_UNSPECIFIED ? true : (myLanes[lane].changeLeft & vclass) == vclass;
4632}
4633
4634bool
4636 assert(lane >= 0 && lane < getNumLanes());
4637 return myLanes[lane].changeRight == SVC_UNSPECIFIED ? true : (myLanes[lane].changeRight & vclass) == vclass;
4638}
4639
4640double
4642 double angle = getAngleAtNode(node) + (getFromNode() == node ? 180.0 : 0.0);
4643 if (angle < 0) {
4644 angle += 360.0;
4645 }
4646 if (angle >= 360) {
4647 angle -= 360.0;
4648 }
4649 if (gDebugFlag1) {
4650 std::cout << getID() << " angle=" << getAngleAtNode(node) << " convAngle=" << angle << "\n";
4651 }
4652 return angle;
4653}
4654
4655
4658 int index = getFirstNonPedestrianLaneIndex(direction);
4659 if (index < 0) {
4660 throw ProcessError(TLF("Edge % allows pedestrians on all lanes", getID()));
4661 }
4662 return myLanes[index];
4663}
4664
4665std::string
4667 // see IntermodalEdge::getSidewalk()
4668 for (int i = 0; i < (int)myLanes.size(); i++) {
4669 if (myLanes[i].permissions == SVC_PEDESTRIAN) {
4670 return getLaneID(i);
4671 }
4672 }
4673 for (int i = 0; i < (int)myLanes.size(); i++) {
4674 if ((myLanes[i].permissions & SVC_PEDESTRIAN) != 0) {
4675 return getLaneID(i);
4676 }
4677 }
4678 return getLaneID(0);
4679}
4680
4681void
4682NBEdge::addSidewalk(double width) {
4684}
4685
4686
4687void
4688NBEdge::restoreSidewalk(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4689 restoreRestrictedLane(SVC_PEDESTRIAN, oldLanes, oldGeometry, oldConnections);
4690}
4691
4692
4693void
4694NBEdge::addBikeLane(double width) {
4696}
4697
4698
4699void
4700NBEdge::restoreBikelane(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4701 restoreRestrictedLane(SVC_BICYCLE, oldLanes, oldGeometry, oldConnections);
4702}
4703
4704bool
4706 for (const Lane& lane : myLanes) {
4707 if (lane.permissions == vclass) {
4708 return true;
4709 }
4710 }
4711 return false;
4712}
4713
4714
4715void
4717 if (hasRestrictedLane(vclass)) {
4718 WRITE_WARNINGF(TL("Edge '%' already has a dedicated lane for %s. Not adding another one."), getID(), toString(vclass));
4719 return;
4720 }
4722 myGeom.move2side(width / 2);
4723 }
4724 // disallow the designated vclass on all "old" lanes
4725 disallowVehicleClass(-1, vclass);
4726 // don't create a restricted vehicle lane to the right of a sidewalk
4727 const int newIndex = (vclass != SVC_PEDESTRIAN && myLanes[0].permissions == SVC_PEDESTRIAN) ? 1 : 0;
4728 if (newIndex == 0) {
4729 // disallow pedestrians on all "higher" lanes to ensure that sidewalk remains the rightmost lane
4731 }
4732 // add new lane
4733 myLanes.insert(myLanes.begin() + newIndex, Lane(this, myLanes[0].getParameter(SUMO_PARAM_ORIGID)));
4734 myLanes[newIndex].permissions = vclass;
4735 myLanes[newIndex].width = fabs(width);
4736 // shift outgoing connections to the left
4737 for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
4738 Connection& c = *it;
4739 if (c.fromLane >= newIndex) {
4740 c.fromLane += 1;
4741 }
4742 }
4743 // shift incoming connections to the left
4744 const EdgeVector& incoming = myFrom->getIncomingEdges();
4745 for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4746 (*it)->shiftToLanesToEdge(this, 1);
4747 }
4751}
4752
4753
4754void
4755NBEdge::restoreRestrictedLane(SUMOVehicleClass vclass, std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4756 // check that previously lane was transformed
4757 if (myLanes[0].permissions != vclass) {
4758 WRITE_WARNINGF(TL("Edge '%' doesn't have a dedicated lane for %s. Cannot be restored."), getID(), toString(vclass));
4759 return;
4760 }
4761 // restore old values
4762 myGeom = oldGeometry;
4763 myLanes = oldLanes;
4764 myConnections = oldConnections;
4765 // shift incoming connections to the right
4766 const EdgeVector& incoming = myFrom->getIncomingEdges();
4767 for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4768 (*it)->shiftToLanesToEdge(this, 0);
4769 }
4770 // Shift TL conections
4774}
4775
4776
4777void
4780 for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
4781 if ((*it).toEdge == to && (*it).toLane >= 0) {
4782 (*it).toLane += laneOff;
4783 }
4784 }
4785}
4786
4787
4788void
4792 && !isRailway(other->getPermissions())
4793 && getBidiEdge() == nullptr) {
4794 const int i = (node == myTo ? -1 : 0);
4795 const int i2 = (node == myTo ? 0 : -1);
4796 const double dist = myGeom[i].distanceTo2D(node->getPosition());
4797 const double neededOffset = getTotalWidth() / 2;
4798 const double dist2 = MIN2(myGeom.distance2D(other->getGeometry()[i2]),
4799 other->getGeometry().distance2D(myGeom[i]));
4800 const double neededOffset2 = neededOffset + (other->getTotalWidth()) / 2;
4801 if (dist < neededOffset && dist2 < neededOffset2) {
4802 PositionVector tmp = myGeom;
4803 // @note this doesn't work well for vissim networks
4804 //tmp.move2side(MIN2(neededOffset - dist, neededOffset2 - dist2));
4805 try {
4806 tmp.move2side(neededOffset - dist);
4807 myGeom[i] = tmp[i];
4808 //std::cout << getID() << " shiftPositionAtNode needed=" << neededOffset << " dist=" << dist << " needed2=" << neededOffset2 << " dist2=" << dist2 << " by=" << (neededOffset - dist) << " other=" << other->getID() << "\n";
4809 } catch (InvalidArgument&) {
4810 WRITE_WARNINGF(TL("Could not avoid overlapping shape at node '%' for edge '%'."), node->getID(), getID());
4811 }
4812 }
4813 }
4814}
4815
4816
4819 if (myLoadedLength > 0) {
4821 } else {
4822 return myGeom.positionAtOffset(offset);
4823 }
4824}
4825
4826
4827double
4829 double result = getLoadedLength();
4830 if (OptionsCont::getOptions().getBool("no-internal-links") && !hasLoadedLength()) {
4831 // use length to junction center even if a modified geometry was given
4833 geom.push_back_noDoublePos(getToNode()->getCenter());
4834 geom.push_front_noDoublePos(getFromNode()->getCenter());
4835 result = geom.length();
4836 }
4837 double avgEndOffset = 0;
4838 for (const Lane& lane : myLanes) {
4839 avgEndOffset += lane.endOffset;
4840 }
4841 if (isBidiRail()) {
4842 avgEndOffset += myPossibleTurnDestination->getEndOffset();
4843 }
4844 avgEndOffset /= (double)myLanes.size();
4845 return MAX2(result - avgEndOffset, POSITION_EPS);
4846}
4847
4848
4849void
4850NBEdge::setOrigID(const std::string origID, const bool append, const int laneIdx) {
4851 if (laneIdx == -1) {
4852 for (int i = 0; i < (int)myLanes.size(); i++) {
4853 setOrigID(origID, append, i);
4854 }
4855 } else {
4856 if (origID != "") {
4857 if (append) {
4858 std::vector<std::string> oldIDs = StringTokenizer(myLanes[laneIdx].getParameter(SUMO_PARAM_ORIGID)).getVector();
4859 if (std::find(oldIDs.begin(), oldIDs.end(), origID) == oldIDs.end()) {
4860 oldIDs.push_back(origID);
4861 }
4862 myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, toString(oldIDs));
4863 } else {
4864 myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, origID);
4865 }
4866 } else {
4867 // do not record empty origID parameter
4868 myLanes[laneIdx].unsetParameter(SUMO_PARAM_ORIGID);
4869 }
4870 }
4871}
4872
4873
4874const EdgeVector&
4876 // @todo cache successors instead of recomputing them every time
4877 mySuccessors.clear();
4878 //std::cout << "getSuccessors edge=" << getID() << " svc=" << toString(vClass) << " cons=" << myConnections.size() << "\n";
4879 for (const Connection& con : myConnections) {
4880 if (con.fromLane >= 0 && con.toLane >= 0 && con.toEdge != nullptr &&
4881 (vClass == SVC_IGNORING || (getPermissions(con.fromLane)
4882 & con.toEdge->getPermissions(con.toLane) & vClass) != 0)
4883 && std::find(mySuccessors.begin(), mySuccessors.end(), con.toEdge) == mySuccessors.end()) {
4884 mySuccessors.push_back(con.toEdge);
4885 //std::cout << " succ=" << con.toEdge->getID() << "\n";
4886 }
4887 }
4888 return mySuccessors;
4889}
4890
4891
4893NBEdge::getViaSuccessors(SUMOVehicleClass vClass, bool /*ignoreTransientPermissions*/) const {
4894 // @todo cache successors instead of recomputing them every time
4895 myViaSuccessors.clear();
4896 for (const Connection& con : myConnections) {
4897 std::pair<const NBEdge*, const Connection*> pair(con.toEdge, nullptr);
4898 // special case for Persons in Netedit
4899 if (vClass == SVC_PEDESTRIAN) {
4900 myViaSuccessors.push_back(pair); // Pedestrians have complete freedom of movement in all sucessors
4901 } else if ((con.fromLane >= 0) && (con.toLane >= 0) &&
4902 (con.toEdge != nullptr) &&
4903 ((getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane) & vClass) == vClass)) {
4904 // ignore duplicates
4905 if (con.getLength() > 0) {
4906 pair.second = &con;
4907 }
4908 myViaSuccessors.push_back(pair);
4909 }
4910 }
4911 return myViaSuccessors;
4912}
4913
4914
4915void
4916NBEdge::debugPrintConnections(bool outgoing, bool incoming) const {
4917 if (outgoing) {
4918 for (const Connection& c : myConnections) {
4919 std::cout << " " << getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
4920 }
4921 }
4922 if (incoming) {
4923 for (NBEdge* inc : myFrom->getIncomingEdges()) {
4924 for (Connection& c : inc->myConnections) {
4925 if (c.toEdge == this) {
4926 std::cout << " " << inc->getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
4927 }
4928 }
4929 }
4930 }
4931}
4932
4933
4934int
4935NBEdge::getLaneIndexFromLaneID(const std::string laneID) {
4936 return StringUtils::toInt(laneID.substr(laneID.rfind("_") + 1));
4937}
4938
4939bool
4941 bool haveJoined = false;
4942 int i = 0;
4943 while (i < getNumLanes() - 1) {
4944 if ((getPermissions(i) == perms) && (getPermissions(i + 1) == perms)) {
4945 const double newWidth = getLaneWidth(i) + getLaneWidth(i + 1);
4946 const std::string newType = myLanes[i].type + "|" + myLanes[i + 1].type;
4947 deleteLane(i, false, true);
4948 setLaneWidth(i, newWidth);
4949 setLaneType(i, newType);
4950 haveJoined = true;
4951 } else {
4952 i++;
4953 }
4954 }
4955 return haveJoined;
4956}
4957
4958
4961 EdgeVector result;
4962 for (NBEdge* edge : edges) {
4963 if ((edge->getPermissions() & permissions) != 0) {
4964 result.push_back(edge);
4965 }
4966 }
4967 return result;
4968}
4969
4970NBEdge*
4972 EdgeVector cands = filterByPermissions(myTo->getOutgoingEdges(), permissions);
4973 if (cands.size() == 0) {
4974 return nullptr;
4975 }
4976 sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this));
4977 NBEdge* best = cands.front();
4978 if (isTurningDirectionAt(best)) {
4979 return nullptr;
4980 } else {
4981 return best;
4982 }
4983}
4984
4985NBEdge*
4987 EdgeVector cands = filterByPermissions(myFrom->getIncomingEdges(), permissions);
4988 if (cands.size() == 0) {
4989 return nullptr;
4990 }
4991 sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this, false));
4992 NBEdge* best = cands.front();
4993 if (best->isTurningDirectionAt(this)) {
4994 return nullptr;
4995 } else {
4996 return best;
4997 }
4998}
4999
5000
5001NBEdge*
5003 NBEdge* opposite = nullptr;
5004 if (getNumLanes() > 0) {
5005 NBEdge::Lane& lastLane = myLanes.back();
5006 const double lastWidth = getLaneWidth(getNumLanes() - 1);
5007 if (lastLane.oppositeID == "" || reguess) {
5008 for (NBEdge* cand : getToNode()->getOutgoingEdges()) {
5009 if (cand->getToNode() == getFromNode() && !cand->getLanes().empty()) {
5010 const NBEdge::Lane& candLastLane = cand->getLanes().back();
5011 if (candLastLane.oppositeID == "" || candLastLane.oppositeID == getLaneID(getNumLanes() - 1)) {
5012 const double lastWidthCand = cand->getLaneWidth(cand->getNumLanes() - 1);
5013 // in sharp corners, the difference may be higher
5014 // factor (sqrt(2) for 90 degree corners
5015 const double threshold = 1.42 * 0.5 * (lastWidth + lastWidthCand) + 0.5;
5016 const double distance = VectorHelper<double>::maxValue(lastLane.shape.distances(cand->getLanes().back().shape));
5017 //std::cout << " distance=" << distance << " threshold=" << threshold << " distances=" << toString(lastLane.shape.distances(cand->getLanes().back().shape)) << "\n";
5018 if (distance < threshold) {
5019 opposite = cand;
5020 }
5021 }
5022 }
5023 }
5024 if (opposite != nullptr) {
5025 lastLane.oppositeID = opposite->getLaneID(opposite->getNumLanes() - 1);
5026 }
5027 }
5028 }
5029 return opposite;
5030}
5031
5032double
5033NBEdge::getDistancAt(double pos) const {
5034 // negative values of myDistances indicate descending kilometrage
5035 return fabs(myDistance + pos);
5036}
5037
5038/****************************************************************************/
#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:288
#define WRITE_MESSAGEF(...)
Definition MsgHandler.h:290
#define WRITE_ERRORF(...)
Definition MsgHandler.h:297
#define WRITE_ERROR(msg)
Definition MsgHandler.h:296
#define WRITE_WARNING(msg)
Definition MsgHandler.h:287
#define TL(string)
Definition MsgHandler.h:305
#define TLF(string,...)
Definition MsgHandler.h:307
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:38
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:4465
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:5002
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:4479
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:4488
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:5033
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:4451
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:4641
void addBikeLane(double width)
add a bicycle lane of the given width and shift existing connctions
Definition NBEdge.cpp:4694
bool expandableBy(NBEdge *possContinuation, std::string &reason) const
Check if Node is expandable.
Definition NBEdge.cpp:3943
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:4893
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:4403
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:3907
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:3324
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:3925
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:4184
static EdgeVector filterByPermissions(const EdgeVector &edges, SVCPermissions permissions)
return only those edges that permit at least one of the give permissions
Definition NBEdge.cpp:4960
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:4140
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:4508
void disallowVehicleClass(int lane, SUMOVehicleClass vclass)
set disallowed class for the given lane or for all lanes if -1 is given
Definition NBEdge.cpp:4247
void removeInvalidConnections()
Definition NBEdge.cpp:3262
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:3694
static bool connections_sorter(const Connection &c1, const Connection &c2)
connections_sorter sort by fromLane, toEdge and toLane
Definition NBEdge.cpp:4523
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:4875
void dismissVehicleClassInformation()
dimiss vehicle class information
Definition NBEdge.cpp:4514
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:4700
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:4971
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:4688
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:3414
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:4373
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:4548
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:4778
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:4132
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:4289
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:4986
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:4591
bool allowsChangingRight(int lane, SUMOVehicleClass vclass) const
Returns whether the given vehicle class may change left from this lane.
Definition NBEdge.cpp:4635
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:4274
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:3836
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:4435
const StopOffset & getEdgeStopOffset() const
Returns the stopOffset to the end of the edge.
Definition NBEdge.cpp:4341
NBNode * tryGetNodeAtPosition(double pos, double tolerance=5.0) const
Returns the node at the given edges length (using an epsilon)
Definition NBEdge.cpp:3787
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:3899
bool isTurningDirectionAt(const NBEdge *const edge) const
Returns whether the given edge is the opposite direction to this edge.
Definition NBEdge.cpp:3774
void addStraightConnections(const EdgeVector *outgoing, const std::vector< int > &availableLanes, const std::vector< int > &priorities)
add some straight connections
Definition NBEdge.cpp:3519
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:4682
bool hasSignalisedConnectionTo(const NBEdge *const e) const
Check if edge has signalised connections.
Definition NBEdge.cpp:4106
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:4419
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:4617
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:4940
void resetNodeBorder(const NBNode *node)
Definition NBEdge.cpp:738
void markAsInLane2LaneState()
mark edge as in lane to state lane
Definition NBEdge.cpp:4227
bool mayBeTLSControlled(int fromLane, NBEdge *toEdge, int toLane) const
return true if certain connection must be controlled by TLS
Definition NBEdge.cpp:3825
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:4716
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:4657
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:3601
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:4326
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:4818
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:4310
void allowVehicleClass(int lane, SUMOVehicleClass vclass)
set allowed class for the given lane or for all lanes if -1 is given
Definition NBEdge.cpp:4234
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:3819
void deleteLane(int index, bool recompute, bool shiftIndices)
delete lane
Definition NBEdge.cpp:4195
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:4605
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:3801
std::string getLaneID(int lane) const
get lane ID
Definition NBEdge.cpp:4126
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:4666
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:4581
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:4443
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:4260
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:4117
void shiftPositionAtNode(NBNode *node, NBEdge *opposite)
shift geometry at the given node to avoid overlap
Definition NBEdge.cpp:4789
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:4535
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:4705
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:4347
void debugPrintConnections(bool outgoing=true, bool incoming=false) const
debugging helper to print all connections
Definition NBEdge.cpp:4916
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:3813
void restoreRestrictedLane(SUMOVehicleClass vclass, std::vector< NBEdge::Lane > oldLanes, PositionVector oldGeometry, std::vector< NBEdge::Connection > oldConnections)
restore a restricted lane
Definition NBEdge.cpp:4755
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:4357
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:4850
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:4629
static int getLaneIndexFromLaneID(const std::string laneID)
Definition NBEdge.cpp:4935
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:4503
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:4215
NBNode * myFrom
The source and the destination node.
Definition NBEdge.h:1743
void append(NBEdge *continuation)
append another edge
Definition NBEdge.cpp:4055
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:4828
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:4564
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:507
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:2472
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:2041
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:952
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:2157
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:2302
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:2228
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:562
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:2283
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:2219
std::vector< Crossing * > getCrossings() const
return this junctions pedestrian crossings
Definition NBNode.cpp:3072
void addOutgoingEdge(NBEdge *edge)
adds an outgoing edge
Definition NBNode.cpp:517
bool isConstantWidthTransition() const
detects whether a given junction splits or merges lanes while keeping constant road width
Definition NBNode.cpp:895
const Position & getPosition() const
Definition NBNode.h:260
const PositionVector & getShape() const
retrieve the junction shape
Definition NBNode.cpp:2757
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:2312
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:810
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:466
bool geometryLike() const
whether this is structurally similar to a geometry node
Definition NBNode.cpp:4011
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