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