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