Eclipse SUMO - Simulation of Urban MObility
NBEdge.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3// Copyright (C) 2001-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
736}
737
738
739bool
740NBEdge::isBidiEdge(bool checkPotential) const {
741 return myPossibleTurnDestination != nullptr
742 && (myIsBidi || myPossibleTurnDestination->myIsBidi || checkPotential)
745 // geometry check a) full overlap geometry
748 || (checkPotential && getGeometry().size() == 2 && myPossibleTurnDestination->getGeometry().size() == 2)))
749 // b) TWLT (Two-Way-Left-Turn-lane)
750 || (myLanes.back().shape.reverse().almostSame(myPossibleTurnDestination->myLanes.back().shape, POSITION_EPS))
751 );
752
753}
754
755
756bool
758 if (!isRailway(getPermissions())) {
759 return false;
760 }
761 for (NBEdge* out : myTo->getOutgoingEdges()) {
762 if (isRailway(out->getPermissions()) &&
763 out != getTurnDestination(true)) {
764 return true;
765 }
766 }
767 return true;
768}
769
770
773 PositionVector shape = old;
774 shape = startShapeAt(shape, myFrom, myFromBorder);
775 if (shape.size() < 2) {
776 // only keep the last snippet
777 const double oldLength = old.length();
778 shape = old.getSubpart(oldLength - 2 * POSITION_EPS, oldLength);
779 }
780 shape = startShapeAt(shape.reverse(), myTo, myToBorder).reverse();
781 // sanity checks
782 if (shape.length() < POSITION_EPS) {
783 if (old.length() < 2 * POSITION_EPS) {
784 shape = old;
785 } else {
786 const double midpoint = old.length() / 2;
787 // EPS*2 because otherwhise shape has only a single point
788 shape = old.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
789 assert(shape.size() >= 2);
790 assert(shape.length() > 0);
791 }
792 } else {
793 // @note If the node shapes are overlapping we may get a shape which goes in the wrong direction
794 // in this case the result shape should shortened
795 if (DEG2RAD(135) < fabs(GeomHelper::angleDiff(shape.beginEndAngle(), old.beginEndAngle()))) {
796 // eliminate intermediate points
797 PositionVector tmp;
798 tmp.push_back(shape[0]);
799 tmp.push_back(shape[-1]);
800 shape = tmp;
801 if (tmp.length() < POSITION_EPS) {
802 // fall back to original shape
803 if (old.length() < 2 * POSITION_EPS) {
804 shape = old;
805 } else {
806 const double midpoint = old.length() / 2;
807 // EPS*2 because otherwhise shape has only a single point
808 shape = old.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
809 assert(shape.size() >= 2);
810 assert(shape.length() > 0);
811 }
812 } else {
813 const double midpoint = shape.length() / 2;
814 // cut to size and reverse
815 shape = shape.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
816 if (shape.length() < POSITION_EPS) {
817 assert(false);
818 // the shape has a sharp turn near the midpoint
819 }
820 shape = shape.reverse();
821 }
822 // make short edge flat (length <= 2 * POSITION_EPS)
823 const double z = (shape[0].z() + shape[1].z()) / 2;
824 shape[0].setz(z);
825 shape[1].setz(z);
826 }
827 }
828 return shape;
829}
830
831
832void
833NBEdge::computeEdgeShape(double smoothElevationThreshold) {
834 if (smoothElevationThreshold > 0 && myGeom.hasElevation()) {
836 // cutting and patching z-coordinate may cause steep grades which should be smoothed
837 if (!myFrom->geometryLike()) {
838 cut[0].setz(myFrom->getPosition().z());
839 const double d = cut[0].distanceTo2D(cut[1]);
840 const double dZ = fabs(cut[0].z() - cut[1].z());
841 if (dZ / smoothElevationThreshold > d) {
842 cut = cut.smoothedZFront(MIN2(cut.length2D() / 2, dZ / smoothElevationThreshold));
843 }
844 }
845 if (!myTo->geometryLike()) {
846 cut[-1].setz(myTo->getPosition().z());
847 const double d = cut[-1].distanceTo2D(cut[-2]);
848 const double dZ = fabs(cut[-1].z() - cut[-2].z());
849 if (dZ / smoothElevationThreshold > d) {
850 cut = cut.reverse().smoothedZFront(MIN2(cut.length2D() / 2, dZ / smoothElevationThreshold)).reverse();
851 }
852 }
853 cut[0] = myGeom[0];
854 cut[-1] = myGeom[-1];
855 if (cut != myGeom) {
856 myGeom = cut;
858 }
859 }
860 for (int i = 0; i < (int)myLanes.size(); i++) {
861 myLanes[i].shape = cutAtIntersection(myLanes[i].shape);
862 }
863 // recompute edge's length as the average of lane lengths
864 double avgLength = 0;
865 for (int i = 0; i < (int)myLanes.size(); i++) {
866 avgLength += myLanes[i].shape.length();
867 }
868 myLength = avgLength / (double) myLanes.size();
869 computeAngle(); // update angles using the finalized node and lane shapes
870}
871
872
874NBEdge::startShapeAt(const PositionVector& laneShape, const NBNode* startNode, PositionVector nodeShape) {
875 if (nodeShape.size() == 0) {
876 nodeShape = startNode->getShape();
877 nodeShape.closePolygon();
878 }
879 PositionVector lb = laneShape;
880 lb.extrapolate2D(100.0);
881 if (nodeShape.intersects(laneShape)) {
882 // shape intersects directly
883 std::vector<double> pbv = laneShape.intersectsAtLengths2D(nodeShape);
884 assert(pbv.size() > 0);
885 // ensure that the subpart has at least two points
886 double pb = MIN2(laneShape.length2D() - POSITION_EPS - NUMERICAL_EPS, VectorHelper<double>::maxValue(pbv));
887 if (pb < 0) {
888 return laneShape;
889 }
890 PositionVector ns = laneShape.getSubpart2D(pb, laneShape.length2D());
891 //PositionVector ns = pb < (laneShape.length() - POSITION_EPS) ? laneShape.getSubpart2D(pb, laneShape.length()) : laneShape;
892 const double delta = ns[0].z() - laneShape[0].z();
893 //std::cout << "a) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << ns[0].z() << " delta=" << delta << "\n";
894 if (fabs(delta) > 2 * POSITION_EPS && (!startNode->geometryLike() || pb < 1)) {
895 // make "real" intersections and small intersections flat
896 //std::cout << "a) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << ns[0].z() << " delta=" << delta << "\n";
897 ns[0].setz(startNode->getPosition().z());
898 }
899 assert(ns.size() >= 2);
900 return ns;
901 } else if (nodeShape.intersects(lb)) {
902 // extension of first segment intersects
903 std::vector<double> pbv = lb.intersectsAtLengths2D(nodeShape);
904 assert(pbv.size() > 0);
905 double pb = VectorHelper<double>::maxValue(pbv);
906 assert(pb >= 0);
907 PositionVector result = laneShape.getSubpartByIndex(1, (int)laneShape.size() - 1);
908 Position np = lb.positionAtOffset2D(pb);
909 const double delta = np.z() - laneShape[0].z();
910 //std::cout << "b) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << np.z() << " delta=" << delta << "\n";
911 if (fabs(delta) > 2 * POSITION_EPS && !startNode->geometryLike()) {
912 // avoid z-overshoot when extrapolating
913 //std::cout << "b) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << np.z() << " delta=" << delta << "\n";
914 np.setz(startNode->getPosition().z());
915 }
916 result.push_front_noDoublePos(np);
917 return result;
918 //if (result.size() >= 2) {
919 // return result;
920 //} else {
921 // WRITE_WARNING(error + " (resulting shape is too short)");
922 // return laneShape;
923 //}
924 } else {
925 // could not find proper intersection. Probably the edge is very short
926 // and lies within nodeShape
927 // @todo enable warning WRITE_WARNING(error + " (laneShape lies within nodeShape)");
928 return laneShape;
929 }
930}
931
932
933const PositionVector&
935 return myLanes[i].shape;
936}
937
938
939void
941 myLaneSpreadFunction = spread;
942}
943
944
948}
949
950
951void
952NBEdge::addGeometryPoint(int index, const Position& p) {
953 if (index >= 0) {
954 myGeom.insert(myGeom.begin() + index, p);
955 } else {
956 myGeom.insert(myGeom.end() + index, p);
957 }
958}
959
960
961void
962NBEdge::reduceGeometry(const double minDist) {
963 // attempt symmetrical removal for forward and backward direction
964 // (very important for bidiRail)
965 if (myFrom->getID() < myTo->getID()) {
966 PositionVector reverse = myGeom.reverse();
967 reverse.removeDoublePoints(minDist, true, 0, 0, true);
968 myGeom = reverse.reverse();
969 for (Lane& lane : myLanes) {
970 reverse = lane.customShape.reverse();
971 reverse.removeDoublePoints(minDist, true, 0, 0, true);
972 lane.customShape = reverse.reverse();
973 }
974 } else {
975 myGeom.removeDoublePoints(minDist, true, 0, 0, true);
976 for (Lane& lane : myLanes) {
977 lane.customShape.removeDoublePoints(minDist, true, 0, 0, true);
978 }
979 }
980}
981
982
983void
984NBEdge::checkGeometry(const double maxAngle, const double minRadius, bool fix, bool silent) {
985 if (myGeom.size() < 3) {
986 return;
987 }
988 //std::cout << "checking geometry of " << getID() << " geometry = " << toString(myGeom) << "\n";
989 std::vector<double> angles; // absolute segment angles
990 //std::cout << " absolute angles:";
991 for (int i = 0; i < (int)myGeom.size() - 1; ++i) {
992 angles.push_back(myGeom.angleAt2D(i));
993 //std::cout << " " << angles.back();
994 }
995 //std::cout << "\n relative angles: ";
996 for (int i = 0; i < (int)angles.size() - 1; ++i) {
997 const double relAngle = fabs(GeomHelper::angleDiff(angles[i], angles[i + 1]));
998 //std::cout << relAngle << " ";
999 if (maxAngle > 0 && relAngle > maxAngle && !silent) {
1000 WRITE_WARNINGF(TL("Found angle of % degrees at edge '%', segment %."), RAD2DEG(relAngle), getID(), i);
1001 }
1002 if (relAngle < DEG2RAD(1)) {
1003 continue;
1004 }
1005 if (i == 0 || i == (int)angles.size() - 2) {
1006 const bool start = i == 0;
1007 const double dist = (start ? myGeom[0].distanceTo2D(myGeom[1]) : myGeom[-2].distanceTo2D(myGeom[-1]));
1008 const double r = tan(0.5 * (M_PI - relAngle)) * dist;
1009 //std::cout << (start ? " start" : " end") << " length=" << dist << " radius=" << r << " ";
1010 if (minRadius > 0 && r < minRadius) {
1011 if (fix) {
1012 WRITE_MESSAGE("Removing sharp turn with radius " + toString(r) + " at the " +
1013 (start ? "start" : "end") + " of edge '" + getID() + "'.");
1014 myGeom.erase(myGeom.begin() + (start ? 1 : i + 1));
1015 checkGeometry(maxAngle, minRadius, fix, silent);
1016 return;
1017 } else if (!silent) {
1018 WRITE_WARNINGF("Found sharp turn with radius % at the " +
1019 toString(start ? "start" : "end") + " of edge '%'.", r, getID());
1020 }
1021 }
1022 }
1023 }
1024 //std::cout << "\n";
1025}
1026
1027
1028// ----------- Setting and getting connections
1029bool
1030NBEdge::addEdge2EdgeConnection(NBEdge* dest, bool overrideRemoval, SVCPermissions permissions) {
1032 return true;
1033 }
1034 // check whether the node was merged and now a connection between
1035 // not matching edges is tried to be added
1036 // This happens f.e. within the ptv VISSIM-example "Beijing"
1037 if (dest != nullptr && myTo != dest->myFrom) {
1038 return false;
1039 }
1040 if (dest == nullptr) {
1042 myConnections.push_back(Connection(-1, dest, -1));
1043 } else if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(dest)) == myConnections.end()) {
1044 myConnections.push_back(Connection(-1, dest, -1));
1045 myConnections.back().permissions = permissions;
1046 }
1047 if (overrideRemoval) {
1048 // override earlier delete decision
1049 for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end();) {
1050 if (it->toEdge == dest) {
1051 it = myConnectionsToDelete.erase(it);
1052 } else {
1053 it++;
1054 }
1055 }
1056 }
1059 }
1060 return true;
1061}
1062
1063
1064bool
1066 int toLane, Lane2LaneInfoType type,
1067 bool mayUseSameDestination,
1068 bool mayDefinitelyPass,
1069 KeepClear keepClear,
1070 double contPos,
1071 double visibility,
1072 double speed,
1073 double friction,
1074 double length,
1075 const PositionVector& customShape,
1076 bool uncontrolled,
1077 SVCPermissions permissions,
1078 bool indirectLeft,
1079 const std::string& edgeType,
1080 SVCPermissions changeLeft,
1081 SVCPermissions changeRight,
1082 bool postProcess) {
1084 return true;
1085 }
1086 // check whether the node was merged and now a connection between
1087 // not matching edges is tried to be added
1088 // This happens f.e. within the ptv VISSIM-example "Beijing"
1089 if (myTo != dest->myFrom) {
1090 return false;
1091 }
1092 if (!addEdge2EdgeConnection(dest)) {
1093 return false;
1094 }
1095 return setConnection(from, dest, toLane, type, mayUseSameDestination, mayDefinitelyPass, keepClear, contPos, visibility, speed, friction, length,
1096 customShape, uncontrolled, permissions, indirectLeft, edgeType, changeLeft, changeRight, postProcess);
1097}
1098
1099
1100bool
1102 NBEdge* dest, int toLane,
1103 int no, Lane2LaneInfoType type,
1104 bool invalidatePrevious,
1105 bool mayDefinitelyPass) {
1106 if (invalidatePrevious) {
1108 }
1109 bool ok = true;
1110 for (int i = 0; i < no && ok; i++) {
1111 ok &= addLane2LaneConnection(fromLane + i, dest, toLane + i, type, false, mayDefinitelyPass);
1112 }
1113 return ok;
1114}
1115
1116
1117bool
1118NBEdge::setConnection(int lane, NBEdge* destEdge,
1119 int destLane, Lane2LaneInfoType type,
1120 bool mayUseSameDestination,
1121 bool mayDefinitelyPass,
1122 KeepClear keepClear,
1123 double contPos,
1124 double visibility,
1125 double speed,
1126 double friction,
1127 double length,
1128 const PositionVector& customShape,
1129 bool uncontrolled,
1130 SVCPermissions permissions,
1131 bool indirectLeft,
1132 const std::string& edgeType,
1133 SVCPermissions changeLeft,
1134 SVCPermissions changeRight,
1135 bool postProcess) {
1137 return false;
1138 }
1139 // some kind of a misbehaviour which may occure when the junction's outgoing
1140 // edge priorities were not properly computed, what may happen due to
1141 // an incomplete or not proper input
1142 // what happens is that under some circumstances a single lane may set to
1143 // be approached more than once by the one of our lanes.
1144 // This must not be!
1145 // we test whether it is the case and do nothing if so - the connection
1146 // will be refused
1147 //
1148 if (!mayUseSameDestination && hasConnectionTo(destEdge, destLane)) {
1149 return false;
1150 }
1151 if (find_if(myConnections.begin(), myConnections.end(), connections_finder(lane, destEdge, destLane)) != myConnections.end()) {
1152 return true;
1153 }
1154 if ((int)myLanes.size() <= lane || destEdge->getNumLanes() <= (int)destLane) {
1155 // problem might be corrigible in post-processing
1156 WRITE_WARNINGF(TL("Could not set connection from '%' to '%'."), getLaneID(lane), destEdge->getLaneID(destLane));
1157 return false;
1158 }
1159 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
1160 if ((*i).toEdge == destEdge && ((*i).fromLane == -1 || (*i).toLane == -1)) {
1161 if (permissions == SVC_UNSPECIFIED) {
1162 // @note: in case we were to add multiple connections from the
1163 // same lane the second one wouldn't get the special permissions!
1164 permissions = (*i).permissions;
1165 }
1166 i = myConnections.erase(i);
1167 } else {
1168 ++i;
1169 }
1170 }
1171 myConnections.push_back(Connection(lane, destEdge, destLane));
1172 if (mayDefinitelyPass) {
1173 myConnections.back().mayDefinitelyPass = true;
1174 }
1175 myConnections.back().keepClear = keepClear;
1176 myConnections.back().contPos = contPos;
1177 myConnections.back().visibility = visibility;
1178 myConnections.back().permissions = permissions;
1179 myConnections.back().indirectLeft = indirectLeft;
1180 myConnections.back().edgeType = edgeType;
1181 myConnections.back().changeLeft = changeLeft;
1182 myConnections.back().changeRight = changeRight;
1183 myConnections.back().speed = speed;
1184 myConnections.back().friction = friction;
1185 myConnections.back().customLength = length;
1186 myConnections.back().customShape = customShape;
1187 myConnections.back().uncontrolled = uncontrolled;
1188 if (type == Lane2LaneInfoType::USER) {
1190 } else {
1191 // check whether we have to take another look at it later
1192 if (type == Lane2LaneInfoType::COMPUTED) {
1193 // yes, the connection was set using an algorithm which requires a recheck
1195 } else {
1196 // ok, let's only not recheck it if we did no add something that has to be rechecked
1199 }
1200 }
1201 }
1202 if (postProcess) {
1203 // override earlier delete decision
1204 for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end();) {
1205 if ((it->fromLane < 0 || it->fromLane == lane)
1206 && (it->toEdge == nullptr || it->toEdge == destEdge)
1207 && (it->toLane < 0 || it->toLane == destLane)) {
1208 it = myConnectionsToDelete.erase(it);
1209 } else {
1210 it++;
1211 }
1212 }
1213 }
1214 return true;
1215}
1216
1217
1218std::vector<NBEdge::Connection>
1219NBEdge::getConnectionsFromLane(int lane, NBEdge* to, int toLane) const {
1220 std::vector<NBEdge::Connection> ret;
1221 for (const Connection& c : myConnections) {
1222 if ((lane < 0 || c.fromLane == lane)
1223 && (to == nullptr || to == c.toEdge)
1224 && (toLane < 0 || toLane == c.toLane)) {
1225 ret.push_back(c);
1226 }
1227 }
1228 return ret;
1229}
1230
1231
1232const NBEdge::Connection&
1233NBEdge::getConnection(int fromLane, const NBEdge* to, int toLane) const {
1234 for (const Connection& c : myConnections) {
1235 if (c.fromLane == fromLane && c.toEdge == to && c.toLane == toLane) {
1236 return c;
1237 }
1238 }
1239 throw ProcessError("Connection from " + getID() + "_" + toString(fromLane)
1240 + " to " + to->getID() + "_" + toString(toLane) + " not found");
1241}
1242
1243
1245NBEdge::getConnectionRef(int fromLane, const NBEdge* to, int toLane) {
1246 for (Connection& c : myConnections) {
1247 if (c.fromLane == fromLane && c.toEdge == to && c.toLane == toLane) {
1248 return c;
1249 }
1250 }
1251 throw ProcessError("Connection from " + getID() + "_" + toString(fromLane)
1252 + " to " + to->getID() + "_" + toString(toLane) + " not found");
1253}
1254
1255
1256bool
1257NBEdge::hasConnectionTo(const NBEdge* destEdge, int destLane, int fromLane) const {
1258 return destEdge != nullptr && find_if(myConnections.begin(), myConnections.end(), connections_toedgelane_finder(destEdge, destLane, fromLane)) != myConnections.end();
1259}
1260
1261
1262bool
1263NBEdge::isConnectedTo(const NBEdge* e, const bool ignoreTurnaround) const {
1264 if (!ignoreTurnaround && (e == myTurnDestination)) {
1265 return true;
1266 }
1267 return
1268 find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(e))
1269 !=
1270 myConnections.end();
1271
1272}
1273
1274
1275const EdgeVector*
1277 // check whether connections exist and if not, use edges from the node
1278 EdgeVector outgoing;
1279 if (myConnections.size() == 0) {
1280 outgoing = myTo->getOutgoingEdges();
1281 } else {
1282 for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1283 if (find(outgoing.begin(), outgoing.end(), (*i).toEdge) == outgoing.end()) {
1284 outgoing.push_back((*i).toEdge);
1285 }
1286 }
1287 }
1288 for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
1289 if (it->fromLane < 0 && it->toLane < 0) {
1290 // found an edge that shall not be connected
1291 EdgeVector::iterator forbidden = std::find(outgoing.begin(), outgoing.end(), it->toEdge);
1292 if (forbidden != outgoing.end()) {
1293 outgoing.erase(forbidden);
1294 }
1295 }
1296 }
1297 // allocate the sorted container
1298 int size = (int) outgoing.size();
1299 EdgeVector* edges = new EdgeVector();
1300 edges->reserve(size);
1301 for (EdgeVector::const_iterator i = outgoing.begin(); i != outgoing.end(); i++) {
1302 NBEdge* outedge = *i;
1303 if (outedge != nullptr && outedge != myTurnDestination) {
1304 edges->push_back(outedge);
1305 }
1306 }
1307 std::sort(edges->begin(), edges->end(), NBContHelper::relative_outgoing_edge_sorter(this));
1308 return edges;
1309}
1310
1311
1314 EdgeVector ret;
1315 for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1316 if (find(ret.begin(), ret.end(), (*i).toEdge) == ret.end()) {
1317 ret.push_back((*i).toEdge);
1318 }
1319 }
1320 return ret;
1321}
1322
1323
1326 EdgeVector ret;
1327 const EdgeVector& candidates = myFrom->getIncomingEdges();
1328 for (EdgeVector::const_iterator i = candidates.begin(); i != candidates.end(); i++) {
1329 if ((*i)->isConnectedTo(this)) {
1330 ret.push_back(*i);
1331 }
1332 }
1333 return ret;
1334}
1335
1336
1337std::vector<int>
1338NBEdge::getConnectionLanes(NBEdge* currentOutgoing, bool withBikes) const {
1339 std::vector<int> ret;
1340 if (currentOutgoing != myTurnDestination) {
1341 for (const Connection& c : myConnections) {
1342 if (c.toEdge == currentOutgoing && (withBikes || getPermissions(c.fromLane) != SVC_BICYCLE)) {
1343 ret.push_back(c.fromLane);
1344 }
1345 }
1346 }
1347 return ret;
1348}
1349
1350
1351void
1354}
1355
1356
1357void
1359 sort(myConnections.begin(), myConnections.end(), connections_sorter);
1360}
1361
1362
1363void
1365 EdgeVector connected = getConnectedEdges();
1366 for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
1367 NBEdge* inc = *i;
1368 // We have to do this
1370 // add all connections
1371 for (EdgeVector::iterator j = connected.begin(); j != connected.end(); j++) {
1372 inc->addEdge2EdgeConnection(*j);
1373 }
1374 inc->removeFromConnections(this);
1375 }
1376}
1377
1378
1379void
1380NBEdge::removeFromConnections(NBEdge* toEdge, int fromLane, int toLane, bool tryLater, const bool adaptToLaneRemoval,
1381 const bool keepPossibleTurns) {
1382 // remove from "myConnections"
1383 const int fromLaneRemoved = adaptToLaneRemoval && fromLane >= 0 ? fromLane : -1;
1384 const int toLaneRemoved = adaptToLaneRemoval && toLane >= 0 ? toLane : -1;
1385 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
1386 Connection& c = *i;
1387 if ((toEdge == nullptr || c.toEdge == toEdge)
1388 && (fromLane < 0 || c.fromLane == fromLane)
1389 && (toLane < 0 || c.toLane == toLane)) {
1390 if (myTo->isTLControlled()) {
1391 std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
1392 for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
1393 (*it)->removeConnection(NBConnection(this, c.fromLane, c.toEdge, c.toLane));
1394 }
1395 }
1396 i = myConnections.erase(i);
1397 tryLater = false;
1398 } else {
1399 if (fromLaneRemoved >= 0 && c.fromLane > fromLaneRemoved) {
1400 if (myTo->isTLControlled()) {
1401 std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
1402 for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
1403 for (NBConnectionVector::iterator tlcon = (*it)->getControlledLinks().begin(); tlcon != (*it)->getControlledLinks().end(); ++tlcon) {
1404 NBConnection& tc = *tlcon;
1405 if (tc.getTo() == c.toEdge && tc.getFromLane() == c.fromLane && tc.getToLane() == c.toLane) {
1406 tc.shiftLaneIndex(this, -1);
1407 }
1408 }
1409 }
1410 }
1411 //std::cout << getID() << " removeFromConnections fromLane=" << fromLane << " to=" << Named::getIDSecure(toEdge) << " toLane=" << toLane << " reduceFromLane=" << c.fromLane << " (to=" << c.toLane << ")\n";
1412 c.fromLane--;
1413 }
1414 if (toLaneRemoved >= 0 && c.toLane > toLaneRemoved && (toEdge == nullptr || c.toEdge == toEdge)) {
1415 //std::cout << getID() << " removeFromConnections fromLane=" << fromLane << " to=" << Named::getIDSecure(toEdge) << " toLane=" << toLane << " reduceToLane=" << c.toLane << " (from=" << c.fromLane << ")\n";
1416 c.toLane--;
1417 }
1418 ++i;
1419 }
1420 }
1421 // check whether it was the turn destination
1422 if (myTurnDestination == toEdge && fromLane < 0) {
1423 myTurnDestination = nullptr;
1424 }
1425 if (myPossibleTurnDestination == toEdge && fromLane < 0 && !keepPossibleTurns) {
1426 myPossibleTurnDestination = nullptr;
1427 }
1428 if (tryLater) {
1429 myConnectionsToDelete.push_back(Connection(fromLane, toEdge, toLane));
1430#ifdef DEBUG_CONNECTION_GUESSING
1431 if (DEBUGCOND) {
1432 std::cout << "removeFromConnections " << getID() << "_" << fromLane << "->" << toEdge->getID() << "_" << toLane << "\n";
1433 for (Connection& c : myConnections) {
1434 std::cout << " conn " << c.getDescription(this) << "\n";
1435 }
1437 std::cout << " connToDelete " << c.getDescription(this) << "\n";
1438 }
1439 }
1440#endif
1441 }
1442}
1443
1444
1445bool
1447 // iterate over connections
1448 for (auto i = myConnections.begin(); i != myConnections.end(); i++) {
1449 if ((i->toEdge == connectionToRemove.toEdge) && (i->fromLane == connectionToRemove.fromLane) && (i->toLane == connectionToRemove.toLane)) {
1450 // remove connection
1451 myConnections.erase(i);
1452 return true;
1453 }
1454 }
1455 // assert(false);
1456 return false;
1457}
1458
1459
1460void
1461NBEdge::invalidateConnections(bool reallowSetting) {
1462 myTurnDestination = nullptr;
1463 myConnections.clear();
1464 if (reallowSetting) {
1466 } else {
1468 }
1469}
1470
1471
1472void
1473NBEdge::replaceInConnections(NBEdge* which, NBEdge* by, int laneOff) {
1474 // replace in "_connectedEdges"
1475 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1476 if ((*i).toEdge == which) {
1477 (*i).toEdge = by;
1478 (*i).toLane += laneOff;
1479 }
1480 }
1481 // check whether it was the turn destination
1482 if (myTurnDestination == which) {
1483 myTurnDestination = by;
1484 }
1485}
1486
1487void
1488NBEdge::replaceInConnections(NBEdge* which, const std::vector<NBEdge::Connection>& origConns) {
1489 std::map<int, int> laneMap;
1490 int minLane = -1;
1491 int maxLane = -1;
1492 // get lanes used to approach the edge to remap
1493 bool wasConnected = false;
1494 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1495 if ((*i).toEdge != which) {
1496 continue;
1497 }
1498 wasConnected = true;
1499 if ((*i).fromLane != -1) {
1500 int fromLane = (*i).fromLane;
1501 laneMap[(*i).toLane] = fromLane;
1502 if (minLane == -1 || minLane > fromLane) {
1503 minLane = fromLane;
1504 }
1505 if (maxLane == -1 || maxLane < fromLane) {
1506 maxLane = fromLane;
1507 }
1508 }
1509 }
1510 if (!wasConnected) {
1511 return;
1512 }
1513 // add new connections
1514 std::vector<NBEdge::Connection> conns = origConns;
1515 EdgeVector origTargets = getSuccessors();
1516 for (std::vector<NBEdge::Connection>::iterator i = conns.begin(); i != conns.end(); ++i) {
1517 if ((*i).toEdge == which || (*i).toEdge == this
1518 // if we already have connections to the target edge, do not add new ones as they are probably from a circular replacement
1519 || std::find(origTargets.begin(), origTargets.end(), (*i).toEdge) != origTargets.end()) {
1520#ifdef DEBUG_REPLACECONNECTION
1521 if (DEBUGCOND) {
1522 std::cout << " replaceInConnections edge=" << getID() << " which=" << which->getID()
1523 << " origTargets=" << toString(origTargets) << " newTarget=" << i->toEdge->getID() << " skipped\n";
1524 }
1525#endif
1526 continue;
1527 }
1528 if (which->getStep() == EdgeBuildingStep::EDGE2EDGES) {
1529 // do not set lane-level connections
1530 replaceInConnections(which, (*i).toEdge, 0);
1531 continue;
1532 }
1533 int fromLane = (*i).fromLane;
1534 int toUse = -1;
1535 if (laneMap.find(fromLane) == laneMap.end()) {
1536 if (fromLane >= 0 && fromLane <= minLane) {
1537 toUse = minLane;
1538 // patch laneMap to avoid crossed-over connections
1539 for (auto& item : laneMap) {
1540 if (item.first < fromLane) {
1541 item.second = MIN2(item.second, minLane);
1542 }
1543 }
1544 }
1545 if (fromLane >= 0 && fromLane >= maxLane) {
1546 toUse = maxLane;
1547 // patch laneMap to avoid crossed-over connections
1548 for (auto& item : laneMap) {
1549 if (item.first > fromLane) {
1550 item.second = MAX2(item.second, maxLane);
1551 }
1552 }
1553 }
1554 } else {
1555 toUse = laneMap[fromLane];
1556 }
1557 if (toUse == -1) {
1558 toUse = 0;
1559 }
1560#ifdef DEBUG_REPLACECONNECTION
1561 if (DEBUGCOND) {
1562 std::cout << " replaceInConnections edge=" << getID() << " which=" << which->getID() << " origTargets=" << toString(origTargets)
1563 << " origFrom=" << fromLane << " laneMap=" << joinToString(laneMap, ":", ",") << " minLane=" << minLane << " maxLane=" << maxLane
1564 << " newTarget=" << i->toEdge->getID() << " fromLane=" << toUse << " toLane=" << i->toLane << "\n";
1565 }
1566#endif
1567 setConnection(toUse, i->toEdge, i->toLane, Lane2LaneInfoType::COMPUTED, false, i->mayDefinitelyPass, i->keepClear,
1568 i->contPos, i->visibility, i->speed, i->friction, i->customLength, i->customShape, i->uncontrolled);
1569 }
1570 // remove the remapped edge from connections
1571 removeFromConnections(which);
1572}
1573
1574
1575void
1577 myStep = src->myStep;
1579}
1580
1581
1582bool
1583NBEdge::canMoveConnection(const Connection& con, int newFromLane) const {
1584 // only allow using newFromLane if at least 1 vClass is permitted to use
1585 // this connection. If the connection shall be moved to a sidewalk, only create the connection if there is no walking area
1586 const SVCPermissions common = (getPermissions(newFromLane) & con.toEdge->getPermissions(con.toLane));
1587 return (common > 0 && common != SVC_PEDESTRIAN);
1588}
1589
1590
1591void
1593 int index = 0;
1594 for (int i = 0; i < (int)myConnections.size(); ++i) {
1595 if (myConnections[i].fromLane == (int)(lane) && canMoveConnection(myConnections[i], lane + 1)) {
1596 index = i;
1597 }
1598 }
1599 std::vector<Connection>::iterator i = myConnections.begin() + index;
1600 Connection c = *i;
1601 myConnections.erase(i);
1603}
1604
1605
1606void
1608 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1609 if ((*i).fromLane == (int)lane && canMoveConnection(*i, lane - 1)) {
1610 Connection c = *i;
1611 i = myConnections.erase(i);
1613 return;
1614 }
1615 }
1616}
1617
1618
1619double
1620NBEdge::buildInnerEdges(const NBNode& n, int noInternalNoSplits, int& linkIndex, int& splitIndex) {
1622 const int numPoints = oc.getInt("junctions.internal-link-detail");
1623 const bool joinTurns = oc.getBool("junctions.join-turns");
1624 const double limitTurnSpeed = oc.getFloat("junctions.limit-turn-speed");
1625 const double limitTurnSpeedMinAngle = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle"));
1626 const double limitTurnSpeedMinAngleRail = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle.railway"));
1627 const double limitTurnSpeedWarnStraight = oc.getFloat("junctions.limit-turn-speed.warn.straight");
1628 const double limitTurnSpeedWarnTurn = oc.getFloat("junctions.limit-turn-speed.warn.turn");
1629 const bool higherSpeed = oc.getBool("junctions.higher-speed");
1630 const double interalJunctionVehicleWidth = oc.getFloat("internal-junctions.vehicle-width");
1631 const bool fromRail = isRailway(getPermissions());
1632 std::string innerID = ":" + n.getID();
1633 NBEdge* toEdge = nullptr;
1634 int edgeIndex = linkIndex;
1635 int internalLaneIndex = 0;
1636 int numLanes = 0; // number of lanes that share the same edge
1637 double lengthSum = 0; // total shape length of all lanes that share the same edge
1638 int avoidedIntersectingLeftOriginLane = std::numeric_limits<int>::max();
1639 bool averageLength = true;
1640 double maxCross = 0.;
1641 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1642 Connection& con = *i;
1643 con.haveVia = false; // reset first since this may be called multiple times
1644 if (con.toEdge == nullptr) {
1645 continue;
1646 }
1647 LinkDirection dir = n.getDirection(this, con.toEdge);
1648 const bool isRightTurn = (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT);
1649 const bool isTurn = (isRightTurn || dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT);
1650 // put turning internal lanes on separate edges
1651 if (con.toEdge != toEdge) {
1652 // skip indices to keep some correspondence between edge ids and link indices:
1653 // internalEdgeIndex + internalLaneIndex = linkIndex
1654 edgeIndex = linkIndex;
1655 toEdge = con.toEdge;
1656 internalLaneIndex = 0;
1657 maxCross = MAX2(maxCross, assignInternalLaneLength(i, numLanes, lengthSum, averageLength));
1658 numLanes = 0;
1659 lengthSum = 0;
1660 }
1661 averageLength = !isTurn || joinTurns; // legacy behavior
1662 SVCPermissions conPermissions = getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane);
1663 const int conShapeFlag = (conPermissions & ~SVC_PEDESTRIAN) != 0 ? 0 : NBNode::SCURVE_IGNORE;
1664 PositionVector shape = n.computeInternalLaneShape(this, con, numPoints, myTo, conShapeFlag);
1665 std::vector<int> foeInternalLinks;
1666
1667 if (dir != LinkDirection::STRAIGHT && shape.length() < POSITION_EPS && !(isBidiRail() && getTurnDestination(true) == con.toEdge)) {
1668 WRITE_WARNINGF(TL("Connection '%_%->%_%' is only %m short."), getID(), con.fromLane, con.toEdge->getID(), con.toLane, shape.length());
1669 }
1670
1671 // crossingPosition, list of foe link indices
1672 std::pair<double, std::vector<int> > crossingPositions(-1, std::vector<int>());
1673 std::set<std::string> tmpFoeIncomingLanes;
1675 int index = 0;
1676 std::vector<PositionVector> otherShapes;
1677 const double width1 = MIN2(interalJunctionVehicleWidth / 2, getLaneWidth(con.fromLane) / 2);
1678 const double width1OppositeLeft = 0; // using width1 changes a lot of curves even though they are rarely responsible for collisions
1679 for (const NBEdge* i2 : n.getIncomingEdges()) {
1680 for (const Connection& k2 : i2->getConnections()) {
1681 if (k2.toEdge == nullptr) {
1682 continue;
1683 }
1684 // vehicles are typically less wide than the lane
1685 // they drive on but but bicycle lanes should be kept clear for their whole width
1686 double width2 = k2.toEdge->getLaneWidth(k2.toLane);
1687 if (k2.toEdge->getPermissions(k2.toLane) != SVC_BICYCLE) {
1688 width2 *= 0.5;
1689 }
1690 const bool foes = n.foes(this, con.toEdge, i2, k2.toEdge);
1691 LinkDirection dir2 = n.getDirection(i2, k2.toEdge);
1692 bool needsCont = !isRailway(conPermissions) && (n.needsCont(this, i2, con, k2) || (con.contPos != UNSPECIFIED_CONTPOS && !con.indirectLeft));
1693 const bool avoidIntersectCandidate = !foes && bothLeftTurns(dir, i2, dir2);
1694 bool oppositeLeftIntersect = avoidIntersectCandidate && haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2);
1695 int shapeFlag = 0;
1697 // do not warn if only bicycles, pedestrians or delivery vehicles are involved as this is a typical occurrence
1698 if (con.customShape.size() == 0
1699 && k2.customShape.size() == 0
1700 && (oppositeLeftIntersect || (avoidedIntersectingLeftOriginLane < con.fromLane && avoidIntersectCandidate))
1701 && ((i2->getPermissions(k2.fromLane) & warn) != 0
1702 && (k2.toEdge->getPermissions(k2.toLane) & warn) != 0)) {
1703 // recompute with different curve parameters (unless
1704 // the other connection is "unimportant"
1706 PositionVector origShape = shape;
1707 shape = n.computeInternalLaneShape(this, con, numPoints, myTo, shapeFlag);
1708 oppositeLeftIntersect = haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2, shapeFlag);
1709 if (oppositeLeftIntersect
1710 && (conPermissions & (SVCAll & ~(SVC_BICYCLE | SVC_PEDESTRIAN))) == 0) {
1711 shape = origShape;
1712 } else {
1713 // recompute previously computed crossing positions
1714 if (avoidedIntersectingLeftOriginLane == std::numeric_limits<int>::max()
1715 || avoidedIntersectingLeftOriginLane < con.fromLane) {
1716 for (const PositionVector& otherShape : otherShapes) {
1717 const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
1718 const double minDV = firstIntersection(shape, otherShape, width1OppositeLeft, width2,
1719 "Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
1720 if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
1721 assert(minDV >= 0);
1722 if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1723 crossingPositions.first = minDV;
1724 }
1725 }
1726 }
1727 }
1728 // make sure connections further to the left do not get a wider angle
1729 avoidedIntersectingLeftOriginLane = con.fromLane;
1730 }
1731 }
1732 const bool bothPrio = getJunctionPriority(&n) > 0 && i2->getJunctionPriority(&n) > 0;
1733 //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";
1734 // the following special case might get obsolete once we have solved #9745
1735 const bool isBicycleLeftTurn = k2.indirectLeft || (dir2 == LinkDirection::LEFT && (i2->getPermissions(k2.fromLane) & k2.toEdge->getPermissions(k2.toLane)) == SVC_BICYCLE);
1736 // compute the crossing point
1737 if ((needsCont || (bothPrio && oppositeLeftIntersect && !isRailway(conPermissions))) && (!con.indirectLeft || dir2 == LinkDirection::STRAIGHT) && !isBicycleLeftTurn) {
1738 crossingPositions.second.push_back(index);
1739 const PositionVector otherShape = n.computeInternalLaneShape(i2, k2, numPoints, 0, shapeFlag);
1740 otherShapes.push_back(otherShape);
1741 const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
1742 const double minDV = firstIntersection(shape, otherShape, width1, width2,
1743 "Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
1744 if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
1745 assert(minDV >= 0);
1746 if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1747 crossingPositions.first = minDV;
1748 }
1749 }
1750 }
1751 const bool rightTurnConflict = NBNode::rightTurnConflict(
1752 this, con.toEdge, con.fromLane, i2, k2.toEdge, k2.fromLane);
1753 const bool indirectTurnConflit = con.indirectLeft && this == i2 && dir2 == LinkDirection::STRAIGHT;
1754 const bool mergeConflict = myTo->mergeConflict(this, con, i2, k2, true);
1755 const bool mergeResponse = myTo->mergeConflict(this, con, i2, k2, false);
1756 // compute foe internal lanes
1757 if (foes || rightTurnConflict || oppositeLeftIntersect || mergeConflict || indirectTurnConflit) {
1758 foeInternalLinks.push_back(index);
1759 }
1760 // only warn once per pair of intersecting turns
1761 if (oppositeLeftIntersect && getID() > i2->getID()
1762 && (getPermissions(con.fromLane) & warn) != 0
1763 && (con.toEdge->getPermissions(con.toLane) & warn) != 0
1764 && (i2->getPermissions(k2.fromLane) & warn) != 0
1765 && (k2.toEdge->getPermissions(k2.toLane) & warn) != 0
1766 // do not warn for unregulated nodes
1768 ) {
1769 WRITE_WARNINGF(TL("Intersecting left turns at junction '%' from lane '%' and lane '%' (increase junction radius to avoid this)."),
1770 n.getID(), getLaneID(con.fromLane), i2->getLaneID(k2.fromLane));
1771 }
1772 // compute foe incoming lanes
1773 const bool signalised = hasSignalisedConnectionTo(con.toEdge);
1774 if ((n.forbids(i2, k2.toEdge, this, con.toEdge, signalised) || rightTurnConflict || indirectTurnConflit || mergeResponse)
1775 && (needsCont || dir == LinkDirection::TURN || (!signalised && this != i2 && !con.indirectLeft))) {
1776 tmpFoeIncomingLanes.insert(i2->getID() + "_" + toString(k2.fromLane));
1777 }
1778 if (bothPrio && oppositeLeftIntersect && getID() < i2->getID()) {
1779 //std::cout << " c1=" << con.getDescription(this) << " c2=" << k2.getDescription(i2) << " bothPrio=" << bothPrio << " oppositeLeftIntersect=" << oppositeLeftIntersect << "\n";
1780 // break symmetry using edge id
1781 // only store link index and resolve actual lane id later (might be multi-lane internal edge)
1782 tmpFoeIncomingLanes.insert(":" + toString(index));
1783 }
1784 index++;
1785 }
1786 }
1787 // foe pedestrian crossings
1788 std::vector<NBNode::Crossing*> crossings = n.getCrossings();
1789 for (auto c : crossings) {
1790 const NBNode::Crossing& crossing = *c;
1791 for (EdgeVector::const_iterator it_e = crossing.edges.begin(); it_e != crossing.edges.end(); ++it_e) {
1792 const NBEdge* edge = *it_e;
1793 // compute foe internal lanes
1794 if (this == edge || con.toEdge == edge) {
1795 foeInternalLinks.push_back(index);
1796 if (con.toEdge == edge &&
1797 ((isRightTurn && getJunctionPriority(&n) > 0) || (isTurn && con.tlID != ""))) {
1798 // build internal junctions (not for left turns at uncontrolled intersections)
1799 PositionVector crossingShape = crossing.shape;
1800 crossingShape.extrapolate(5.0); // sometimes shapes miss each other by a small margin
1801 const double minDV = firstIntersection(shape, crossingShape, 0, crossing.width / 2);
1802 if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) {
1803 assert(minDV >= 0);
1804 if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1805 crossingPositions.first = minDV;
1806 }
1807 }
1808 }
1809 }
1810 }
1811 index++;
1812 }
1813
1814 if (dir == LinkDirection::TURN && crossingPositions.first < 0 && crossingPositions.second.size() != 0 && shape.length() > 2. * POSITION_EPS) {
1815 // let turnarounds wait in the middle if no other crossing point was found and it has a sensible length
1816 // (if endOffset is used, the crossing point is in the middle of the part within the junction shape)
1817 crossingPositions.first = (double)(shape.length() + getEndOffset(con.fromLane)) / 2.;
1818 }
1819 }
1820 if (con.contPos != UNSPECIFIED_CONTPOS) {
1821 // apply custom internal junction position
1822 if (con.contPos <= 0 || con.contPos >= shape.length()) {
1823 // disable internal junction
1824 crossingPositions.first = -1;
1825 } else {
1826 // set custom position
1827 crossingPositions.first = con.contPos;
1828 }
1829 }
1830
1831 // @todo compute the maximum speed allowed based on angular velocity
1832 // see !!! for an explanation (with a_lat_mean ~0.3)
1833 /*
1834 double vmax = (double) 0.3 * (double) 9.80778 *
1835 getLaneShape(con.fromLane).back().distanceTo(
1836 con.toEdge->getLaneShape(con.toLane).front())
1837 / (double) 2.0 / (double) M_PI;
1838 vmax = MIN2(vmax, ((getSpeed() + con.toEdge->getSpeed()) / (double) 2.0));
1839 */
1840 if (con.speed == UNSPECIFIED_SPEED) {
1841 if (higherSpeed) {
1842 con.vmax = MAX2(myLanes[con.fromLane].speed, con.toEdge->getLanes()[con.toLane].speed);
1843 } else {
1844 con.vmax = (myLanes[con.fromLane].speed + con.toEdge->getLanes()[con.toLane].speed) / (double) 2.0;
1845 }
1846 if (limitTurnSpeed > 0) {
1847 // see [Odhams and Cole, Models of Driver Speed Choice in Curves, 2004]
1848 const double angleRaw = fabs(GeomHelper::angleDiff(
1850 con.toEdge->getLaneShape(con.toLane).angleAt2D(0)));
1851 const double angle = MAX2(0.0, angleRaw - (fromRail ? limitTurnSpeedMinAngleRail : limitTurnSpeedMinAngle));
1852 const double length = shape.length2D();
1853 // do not trust the radius of tiny junctions
1854 // formula adapted from [Odhams, Andre and Cole, David, Models of Driver Speed Choice in Curves, 2004]
1855 if (angle > 0 && length > 1) {
1856 // permit higher turning speed on wide lanes
1857 const double radius = length / angle + getLaneWidth(con.fromLane) / 4;
1858 const double limit = sqrt(limitTurnSpeed * radius);
1859 const double reduction = con.vmax - limit;
1860 // always treat connctions at roundabout as turns when warning
1861 const bool atRoundabout = getJunctionPriority(myTo) == JunctionPriority::ROUNDABOUT || con.toEdge->getJunctionPriority(myFrom) == JunctionPriority::ROUNDABOUT;
1862 const LinkDirection dir2 = atRoundabout ? LinkDirection::LEFT : dir;
1863 if ((dir2 == LinkDirection::STRAIGHT && reduction > limitTurnSpeedWarnStraight)
1864 || (dir2 != LinkDirection::TURN && reduction > limitTurnSpeedWarnTurn)) {
1865 std::string dirType = std::string(dir == LinkDirection::STRAIGHT ? "straight" : "turning");
1866 if (atRoundabout) {
1867 dirType = "roundabout";
1868 }
1869 WRITE_WARNINGF(TL("Speed of % connection '%' reduced by % due to turning radius of % (length=%, angle=%)."),
1870 dirType, con.getDescription(this), reduction, radius, length, RAD2DEG(angleRaw));
1871 }
1872 con.vmax = MIN2(con.vmax, limit);
1873 // value is saved in <net> attribute. Must be set again when importing from .con.xml
1874 // con.speed = con.vmax;
1875 }
1876 assert(con.vmax > 0);
1877 //if (getID() == "-1017000.0.00") {
1878 // std::cout << con.getDescription(this) << " angleRaw=" << angleRaw << " angle=" << RAD2DEG(angle) << " length=" << length << " radius=" << length / angle
1879 // << " vmaxTurn=" << sqrt(limitTurnSpeed * length / angle) << " vmax=" << con.vmax << "\n";
1880 //}
1881 } else if (fromRail && dir == LinkDirection::TURN) {
1882 con.vmax = 0.01;
1883 }
1884 } else {
1885 con.vmax = con.speed;
1886 }
1887 if (con.friction == UNSPECIFIED_FRICTION) {
1888 con.friction = (myLanes[con.fromLane].friction + con.toEdge->getLanes()[con.toLane].friction) / 2.;
1889 }
1890 //
1891 assert(shape.size() >= 2);
1892 // get internal splits if any
1893 con.id = innerID + "_" + toString(edgeIndex);
1894 const double shapeLength = shape.length();
1895 double firstLength = shapeLength;
1896 if (crossingPositions.first >= 0 && crossingPositions.first < shapeLength) {
1897 std::pair<PositionVector, PositionVector> split = shape.splitAt(crossingPositions.first);
1898 con.shape = split.first;
1899 con.foeIncomingLanes = std::vector<std::string>(tmpFoeIncomingLanes.begin(), tmpFoeIncomingLanes.end());
1900 con.foeInternalLinks = foeInternalLinks; // resolve link indices to lane ids later
1901 con.viaID = innerID + "_" + toString(splitIndex + noInternalNoSplits);
1902 ++splitIndex;
1903 con.viaShape = split.second;
1904 con.haveVia = true;
1905 firstLength = con.shape.length();
1906 } else {
1907 con.shape = shape;
1908 }
1909 con.internalLaneIndex = internalLaneIndex;
1910 ++internalLaneIndex;
1911 ++linkIndex;
1912 ++numLanes;
1914 // split length proportionally
1915 lengthSum += firstLength / shapeLength * con.customLength;
1916 } else {
1917 lengthSum += firstLength;
1918 }
1919 }
1920 return MAX2(maxCross, assignInternalLaneLength(myConnections.end(), numLanes, lengthSum, averageLength));
1921}
1922
1923
1924double
1925NBEdge::assignInternalLaneLength(std::vector<Connection>::iterator i, int numLanes, double lengthSum, bool averageLength) {
1926 // assign average length to all lanes of the same internal edge if averageLength is set
1927 // the lengthSum only covers the part up to the first internal junction
1928 // TODO This code assumes that either all connections in question have a via or none
1929 double maxCross = 0.;
1930 assert(i - myConnections.begin() >= numLanes);
1931 for (int prevIndex = 1; prevIndex <= numLanes; prevIndex++) {
1932 //std::cout << " con=" << (*(i - prevIndex)).getDescription(this) << " numLanes=" << numLanes << " avgLength=" << lengthSum / numLanes << "\n";
1933 Connection& c = (*(i - prevIndex));
1934 const double minLength = c.customLength != UNSPECIFIED_LOADED_LENGTH ? pow(10, -gPrecision) : POSITION_EPS;
1935 c.length = MAX2(minLength, averageLength ? lengthSum / numLanes : c.shape.length());
1936 if (c.haveVia) {
1937 c.viaLength = MAX2(minLength, c.viaShape.length());
1938 }
1940 if (c.haveVia) {
1941 // split length proportionally
1942 const double a = c.viaLength / (c.shape.length() + c.viaLength);
1943 c.viaLength = MAX2(minLength, a * c.customLength);
1944 }
1945 if (!averageLength) {
1946 c.length = MAX2(minLength, c.customLength - c.viaLength);
1947 }
1948 }
1949 if (c.haveVia) {
1950 // we need to be able to leave from the internal junction by accelerating from 0
1951 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)
1952 }
1953 // we need to be able to cross the junction in one go but not if we have an indirect left turn
1954 if (c.indirectLeft) {
1955 maxCross = MAX2(maxCross, MAX2(c.length, c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
1956 } else {
1957 maxCross = MAX2(maxCross, (c.length + c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
1958 }
1959 }
1960 return maxCross;
1961}
1962
1963
1964double
1965NBEdge::firstIntersection(const PositionVector& v1, const PositionVector& v2, double width1, double width2, const std::string& error, bool secondIntersection) {
1966 double intersect = std::numeric_limits<double>::max();
1967 if (v2.length() < POSITION_EPS) {
1968 return intersect;
1969 }
1970 try {
1971 PositionVector v1Right = v1;
1972 v1Right.move2side(width1);
1973
1974 PositionVector v1Left = v1;
1975 v1Left.move2side(-width1);
1976
1977 PositionVector v2Right = v2;
1978 v2Right.move2side(width2);
1979
1980 PositionVector v2Left = v2;
1981 v2Left.move2side(-width2);
1982
1983 // intersect all border combinations
1984 bool skip = secondIntersection;
1985 for (double cand : v1Left.intersectsAtLengths2D(v2Right)) {
1986 if (skip) {
1987 skip = false;
1988 continue;
1989 }
1990 intersect = MIN2(intersect, cand);
1991 }
1992 skip = secondIntersection;
1993 for (double cand : v1Left.intersectsAtLengths2D(v2Left)) {
1994 if (skip) {
1995 skip = false;
1996 continue;
1997 }
1998 intersect = MIN2(intersect, cand);
1999 }
2000 skip = secondIntersection;
2001 for (double cand : v1Right.intersectsAtLengths2D(v2Right)) {
2002 if (skip) {
2003 skip = false;
2004 continue;
2005 }
2006 intersect = MIN2(intersect, cand);
2007 }
2008 skip = secondIntersection;
2009 for (double cand : v1Right.intersectsAtLengths2D(v2Left)) {
2010 if (skip) {
2011 skip = false;
2012 continue;
2013 }
2014 intersect = MIN2(intersect, cand);
2015 }
2016 } catch (InvalidArgument&) {
2017 if (error != "") {
2018 WRITE_WARNING(error);
2019 }
2020 }
2021 //std::cout << " v1=" << v1 << " v2Right=" << v2Right << " v2Left=" << v2Left << "\n";
2022 //std::cout << " intersectsRight=" << toString(v1.intersectsAtLengths2D(v2Right)) << "\n";
2023 //std::cout << " intersectsLeft=" << toString(v1.intersectsAtLengths2D(v2Left)) << "\n";
2024 return intersect;
2025}
2026
2027
2028bool
2029NBEdge::bothLeftTurns(LinkDirection dir, const NBEdge* otherFrom, LinkDirection dir2) const {
2030 if (otherFrom == this) {
2031 // not an opposite pair
2032 return false;
2033 }
2034 return (dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT) && (dir2 == LinkDirection::LEFT || dir2 == LinkDirection::PARTLEFT);
2035}
2036
2037bool
2038NBEdge::haveIntersection(const NBNode& n, const PositionVector& shape, const NBEdge* otherFrom, const NBEdge::Connection& otherCon, int numPoints,
2039 double width1, double width2, int shapeFlag) const {
2040 const PositionVector otherShape = n.computeInternalLaneShape(otherFrom, otherCon, numPoints, 0, shapeFlag);
2041 const double minDV = firstIntersection(shape, otherShape, width1, width2);
2042 return minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS;
2043}
2044
2045
2046// -----------
2047int
2048NBEdge::getJunctionPriority(const NBNode* const node) const {
2049 if (node == myFrom) {
2051 } else {
2052 return myToJunctionPriority;
2053 }
2054}
2055
2056
2057void
2058NBEdge::setJunctionPriority(const NBNode* const node, int prio) {
2059 if (node == myFrom) {
2061#ifdef DEBUG_JUNCTIONPRIO
2062 setParameter("fromPrio", toString(prio));
2063#endif
2064 } else {
2065 myToJunctionPriority = prio;
2066#ifdef DEBUG_JUNCTIONPRIO
2067 setParameter("toPrio", toString(prio));
2068#endif
2069 }
2070}
2071
2072
2073double
2074NBEdge::getAngleAtNode(const NBNode* const atNode) const {
2075 // myStartAngle, myEndAngle are in [0,360] and this returns results in [-180,180]
2076 if (atNode == myFrom) {
2078 } else {
2079 assert(atNode == myTo);
2081 }
2082}
2083
2084double
2085NBEdge::getAngleAtNodeNormalized(const NBNode* const atNode) const {
2086 // myStartAngle, myEndAngle are in [0,360] and this returns results in [-180,180]
2087 double res;
2088 if (atNode == myFrom) {
2090 } else {
2091 assert(atNode == myTo);
2093 }
2094 if (res < 0) {
2095 res += 360;
2096 }
2097 return res;
2098}
2099
2100
2101double
2102NBEdge::getAngleAtNodeToCenter(const NBNode* const atNode) const {
2103 if (atNode == myFrom) {
2104 double res = myStartAngle - 180;
2105 if (res < 0) {
2106 res += 360;
2107 }
2108 return res;
2109 } else {
2110 assert(atNode == myTo);
2111 return myEndAngle;
2112 }
2113}
2114
2115
2116void
2118 if (!onlyPossible) {
2120 }
2122}
2123
2124
2125double
2126NBEdge::getLaneSpeed(int lane) const {
2127 return myLanes[lane].speed;
2128}
2129
2130
2131double
2133 return myLanes[lane].friction;
2134}
2135
2136
2137void
2140}
2141
2142
2143void
2145 for (Lane& lane : myLanes) {
2146 if (lane.changeLeft != SVCAll) {
2147 lane.changeLeft = ignoring;
2148 }
2149 if (lane.changeRight != SVCAll) {
2150 lane.changeRight = ignoring;
2151 }
2152 }
2153 for (Connection& con : myConnections) {
2154 if (con.changeLeft != SVC_UNSPECIFIED && con.changeLeft != SVCAll) {
2155 con.changeLeft = ignoring;
2156 }
2157 if (con.changeRight != SVC_UNSPECIFIED && con.changeRight != SVCAll) {
2158 con.changeRight = ignoring;
2159 }
2160 }
2161}
2162
2163
2164void
2166 // vissim needs this
2167 if (myFrom == myTo) {
2168 return;
2169 }
2170 // compute lane offset, first
2171 std::vector<double> offsets(myLanes.size(), 0.);
2172 double offset = 0;
2173 for (int i = (int)myLanes.size() - 2; i >= 0; --i) {
2174 offset += (getLaneWidth(i) + getLaneWidth(i + 1)) / 2.;
2175 offsets[i] = offset;
2176 }
2178 double width = 0;
2179 for (int i = 0; i < (int)myLanes.size(); ++i) {
2180 width += getLaneWidth(i);
2181 }
2182 offset = -width / 2. + getLaneWidth((int)myLanes.size() - 1) / 2.;
2183 } else {
2184 double laneWidth = myLanes.back().width != UNSPECIFIED_WIDTH ? myLanes.back().width : SUMO_const_laneWidth;
2185 offset = laneWidth / 2.;
2186 }
2188 for (NBEdge* e : myTo->getOutgoingEdges()) {
2189 if (e->getToNode() == myFrom && getInnerGeometry().reverse() == e->getInnerGeometry()) {
2190 offset += (e->getTotalWidth() - getTotalWidth()) / 2;
2191 break;
2192 }
2193 }
2194 }
2195
2196 for (int i = 0; i < (int)myLanes.size(); ++i) {
2197 offsets[i] += offset;
2198 }
2199
2200 // build the shape of each lane
2201 for (int i = 0; i < (int)myLanes.size(); ++i) {
2202 if (myLanes[i].customShape.size() != 0) {
2203 myLanes[i].shape = myLanes[i].customShape;
2204 continue;
2205 }
2206 try {
2207 myLanes[i].shape = computeLaneShape(i, offsets[i]);
2208 } catch (InvalidArgument& e) {
2209 WRITE_WARNINGF(TL("In lane '%': lane shape could not be determined (%)."), getLaneID(i), e.what());
2210 myLanes[i].shape = myGeom;
2211 }
2212 }
2213}
2214
2215
2217NBEdge::computeLaneShape(int lane, double offset) const {
2218 PositionVector shape = myGeom;
2219 try {
2220 shape.move2side(offset);
2221 } catch (InvalidArgument& e) {
2222 WRITE_WARNINGF(TL("In lane '%': Could not build shape (%)."), getLaneID(lane), e.what());
2223 }
2224 return shape;
2225}
2226
2227
2228void
2230 // taking the angle at the first might be unstable, thus we take the angle
2231 // at a certain distance. (To compare two edges, additional geometry
2232 // segments are considered to resolve ambiguities)
2233 const bool hasFromShape = myFrom->getShape().size() > 0;
2234 const bool hasToShape = myTo->getShape().size() > 0;
2235 Position fromCenter = (hasFromShape ? myFrom->getShape().getCentroid() : myFrom->getPosition());
2236 Position toCenter = (hasToShape ? myTo->getShape().getCentroid() : myTo->getPosition());
2237 PositionVector shape = myGeom;
2238 if ((hasFromShape || hasToShape) && getNumLanes() > 0) {
2240 shape = myLanes[getNumLanes() - 1].shape ;
2241 } else {
2242 shape = myLanes[getNumLanes() / 2].shape;
2243 if (getNumLanes() % 2 == 0) {
2244 // there is no center lane. shift to get the center
2245 shape.move2side(getLaneWidth(getNumLanes() / 2) * 0.5);
2246 }
2247 }
2248 }
2249
2250 // if the junction shape is suspicious we cannot trust the angle to the centroid
2251 const bool suspiciousFromShape = hasFromShape && (myFrom->getShape().distance2D(shape[0]) > 2 * POSITION_EPS
2252 || myFrom->getShape().around(shape[-1])
2253 || !(myFrom->getShape().around(fromCenter)));
2254 const bool suspiciousToShape = hasToShape && (myTo->getShape().distance2D(shape[-1]) > 2 * POSITION_EPS
2255 || myTo->getShape().around(shape[0])
2256 || !(myTo->getShape().around(toCenter)));
2257
2258 const double angleLookahead = MIN2(shape.length2D() / 2, ANGLE_LOOKAHEAD);
2259 const Position referencePosStart = shape.positionAtOffset2D(angleLookahead);
2260 const Position referencePosEnd = shape.positionAtOffset2D(shape.length() - angleLookahead);
2261
2262 myStartAngle = GeomHelper::legacyDegree(fromCenter.angleTo2D(referencePosStart), true);
2263 const double myStartAngle2 = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(referencePosStart), true);
2264 const double myStartAngle3 = getAngleAtNode(myFrom);
2265 myEndAngle = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(toCenter), true);
2266 const double myEndAngle2 = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myTo->getPosition()), true);
2267 const double myEndAngle3 = getAngleAtNode(myTo);
2268
2269#ifdef DEBUG_ANGLES
2270 if (DEBUGCOND) {
2271 if (suspiciousFromShape) {
2272 std::cout << "suspiciousFromShape len=" << shape.length() << " startA=" << myStartAngle << " startA2=" << myStartAngle2 << " startA3=" << myStartAngle3
2273 << " rel=" << NBHelpers::normRelAngle(myStartAngle, myStartAngle2)
2274 << " fromCenter=" << fromCenter
2275 << " fromPos=" << myFrom->getPosition()
2276 << " refStart=" << referencePosStart
2277 << "\n";
2278 }
2279 if (suspiciousToShape) {
2280 std::cout << "suspiciousToShape len=" << shape.length() << " endA=" << myEndAngle << " endA2=" << myEndAngle2 << " endA3=" << myEndAngle3
2281 << " rel=" << NBHelpers::normRelAngle(myEndAngle, myEndAngle2)
2282 << " toCenter=" << toCenter
2283 << " toPos=" << myTo->getPosition()
2284 << " refEnd=" << referencePosEnd
2285 << "\n";
2286 }
2287 }
2288#endif
2289
2290 if (suspiciousFromShape && shape.length() > 1) {
2291 myStartAngle = myStartAngle2;
2292 } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myStartAngle, myStartAngle3)) > 90
2293 // don't trust footpath angles
2294 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
2295 myStartAngle = myStartAngle3;
2296 if (myStartAngle < 0) {
2297 myStartAngle += 360;
2298 }
2299 }
2300
2301 if (suspiciousToShape && shape.length() > 1) {
2302 myEndAngle = myEndAngle2;
2303 } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myEndAngle, myEndAngle3)) > 90
2304 // don't trust footpath angles
2305 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
2306 myEndAngle = myEndAngle3;
2307 if (myEndAngle < 0) {
2308 myEndAngle += 360;
2309 }
2310 }
2311
2313#ifdef DEBUG_ANGLES
2314 if (DEBUGCOND) std::cout << "computeAngle edge=" << getID()
2315 << " fromCenter=" << fromCenter << " toCenter=" << toCenter
2316 << " refStart=" << referencePosStart << " refEnd=" << referencePosEnd << " shape=" << shape
2317 << " hasFromShape=" << hasFromShape
2318 << " hasToShape=" << hasToShape
2319 << " numLanes=" << getNumLanes()
2320 << " shapeLane=" << getNumLanes() / 2
2321 << " startA=" << myStartAngle << " endA=" << myEndAngle << " totA=" << myTotalAngle << "\n";
2322#endif
2323}
2324
2325
2326double
2328 const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
2329 const Position referencePosStart = myGeom.positionAtOffset2D(angleLookahead);
2330 return GeomHelper::legacyDegree(myGeom.front().angleTo2D(referencePosStart), true);
2331}
2332
2333
2334double
2336 const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
2337 const Position referencePosEnd = myGeom.positionAtOffset2D(myGeom.length() - angleLookahead);
2338 return GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myGeom.back()), true);
2339}
2340
2341
2342bool
2344 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2345 if ((*i).permissions != SVCAll) {
2346 return true;
2347 }
2348 }
2349 return false;
2350}
2351
2352
2353bool
2355 std::vector<Lane>::const_iterator i = myLanes.begin();
2356 SVCPermissions firstLanePermissions = i->permissions;
2357 i++;
2358 for (; i != myLanes.end(); ++i) {
2359 if (i->permissions != firstLanePermissions) {
2360 return true;
2361 }
2362 }
2363 return false;
2364}
2365
2366
2367bool
2369 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2370 if (i->speed != getSpeed()) {
2371 return true;
2372 }
2373 }
2374 return false;
2375}
2376
2377bool
2379 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2380 if (i->friction != myLanes.begin()->friction) {
2381 return true;
2382 }
2383 }
2384 return false;
2385}
2386
2387bool
2389 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2390 if (i->width != myLanes.begin()->width) {
2391 return true;
2392 }
2393 }
2394 return false;
2395}
2396
2397
2398bool
2400 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2401 if (i->type != myLanes.begin()->type) {
2402 return true;
2403 }
2404 }
2405 return false;
2406}
2407
2408
2409bool
2411 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2412 if (i->endOffset != myLanes.begin()->endOffset) {
2413 return true;
2414 }
2415 }
2416 return false;
2417}
2418
2419
2420bool
2422 for (const auto& lane : myLanes) {
2423 if (lane.laneStopOffset.isDefined()) {
2424 if (myEdgeStopOffset.isDefined() || (myEdgeStopOffset != lane.laneStopOffset)) {
2425 return true;
2426 }
2427 }
2428 }
2429 return false;
2430}
2431
2432
2433bool
2435 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2436 if (i->accelRamp) {
2437 return true;
2438 }
2439 }
2440 return false;
2441}
2442
2443
2444bool
2446 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2447 if (i->customShape.size() > 0) {
2448 return true;
2449 }
2450 }
2451 return false;
2452}
2453
2454
2455bool
2457 for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2458 if (i->getParametersMap().size() > 0) {
2459 return true;
2460 }
2461 }
2462 return false;
2463}
2464
2465bool
2467 for (const Lane& lane : myLanes) {
2468 if (lane.changeLeft != SVCAll || lane.changeRight != SVCAll) {
2469 return true;
2470 }
2471 }
2472 return false;
2473}
2474
2475bool
2483 || hasAccelLane()
2485 || hasLaneParams()
2487 || (!myLanes.empty() && myLanes.back().oppositeID != ""));
2488}
2489
2490
2491
2492bool
2493NBEdge::computeEdge2Edges(bool noLeftMovers) {
2494#ifdef DEBUG_CONNECTION_GUESSING
2495 if (DEBUGCOND) {
2496 std::cout << "computeEdge2Edges edge=" << getID() << " step=" << (int)myStep << " noLeftMovers=" << noLeftMovers << "\n";
2497 for (Connection& c : myConnections) {
2498 std::cout << " conn " << c.getDescription(this) << "\n";
2499 }
2501 std::cout << " connToDelete " << c.getDescription(this) << "\n";
2502 }
2503 }
2504#endif
2505 // return if this relationship has been build in previous steps or
2506 // during the import
2508 return true;
2509 }
2510 const bool fromRail = isRailway(getPermissions());
2511 for (NBEdge* out : myTo->getOutgoingEdges()) {
2512 if (noLeftMovers && myTo->isLeftMover(this, out)) {
2513 continue;
2514 }
2515 // avoid sharp railway turns
2516 if (fromRail && isRailway(out->getPermissions())) {
2517 const double angle = fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), out->getAngleAtNode(myTo)));
2518 if (angle > 150) {
2519 continue;
2520 } else if (angle > 90) {
2521 // possibly the junction is large enough to achieve a plausible radius:
2522 const PositionVector& fromShape = myLanes.front().shape;
2523 const PositionVector& toShape = out->getLanes().front().shape;
2524 PositionVector shape = myTo->computeSmoothShape(fromShape, toShape, 5, getTurnDestination() == out, 5, 5);
2525 const double radius = shape.length2D() / DEG2RAD(angle);
2526 const double minRadius = (getPermissions() & SVC_TRAM) != 0 ? 20 : 80;
2527 //std::cout << getID() << " to=" << out->getID() << " radius=" << radius << " minRadius=" << minRadius << "\n";
2528 if (radius < minRadius) {
2529 continue;
2530 }
2531 }
2532 }
2533 if (out == myTurnDestination) {
2534 // will be added by appendTurnaround
2535 continue;
2536 }
2537 if ((getPermissions() & out->getPermissions() & ~SVC_PEDESTRIAN) == 0) {
2538 // no common permissions
2539 continue;
2540 }
2541 myConnections.push_back(Connection(-1, out, -1));
2542 }
2544 return true;
2545}
2546
2547
2548bool
2550#ifdef DEBUG_CONNECTION_GUESSING
2551 if (DEBUGCOND) {
2552 std::cout << "computeLanes2Edges edge=" << getID() << " step=" << (int)myStep << "\n";
2553 for (Connection& c : myConnections) {
2554 std::cout << " conn " << c.getDescription(this) << "\n";
2555 }
2557 std::cout << " connToDelete " << c.getDescription(this) << "\n";
2558 }
2559 }
2560#endif
2561 // return if this relationship has been build in previous steps or
2562 // during the import
2564 return true;
2565 }
2567 // get list of possible outgoing edges sorted by direction clockwise
2568 // the edge in the backward direction (turnaround) is not in the list
2569 const EdgeVector* edges = getConnectedSorted();
2570 if (myConnections.size() != 0 && edges->size() == 0) {
2571 // dead end per definition!?
2572 myConnections.clear();
2573 } else {
2574 // divide the lanes on reachable edges
2575 divideOnEdges(edges);
2576 }
2577 delete edges;
2579 return true;
2580}
2581
2582
2583std::vector<LinkDirection>
2585 std::vector<LinkDirection> result;
2586 for (int i = 0; i < 8; i++) {
2587 // see LinkDirection in SUMOXMLDefinitions.h
2588 if ((turnSigns & (1 << i)) != 0) {
2589 result.push_back((LinkDirection)(1 << i));
2590 }
2591 }
2592 return result;
2593}
2594
2595bool
2597#ifdef DEBUG_TURNSIGNS
2598 std::cout << "applyTurnSigns edge=" << getID() << "\n";
2599#endif
2600 // build a map of target edges and lanes
2601 std::vector<const NBEdge*> targets;
2602 std::map<const NBEdge*, std::vector<int> > toLaneMap;
2603 for (const Connection& c : myConnections) {
2604 if (myLanes[c.fromLane].turnSigns != 0) {
2605 if (std::find(targets.begin(), targets.end(), c.toEdge) == targets.end()) {
2606 targets.push_back(c.toEdge);
2607 }
2608 toLaneMap[c.toEdge].push_back(c.toLane);
2609 }
2610 }
2611 // might be unsorted due to bike lane connections
2612 for (auto& item : toLaneMap) {
2613 std::sort(item.second.begin(), item.second.end());
2614 }
2615
2616 // check number of distinct signed directions and count the number of signs for each direction
2617 std::map<LinkDirection, int> signCons;
2618 int allDirs = 0;
2619 for (const Lane& lane : myLanes) {
2620 allDirs |= lane.turnSigns;
2621 for (LinkDirection dir : decodeTurnSigns(lane.turnSigns)) {
2622 signCons[dir]++;
2623 }
2624 }
2625 if ((allDirs & (int)LinkDirection::NODIR) != 0) {
2626 targets.push_back(nullptr); // dead end
2627 }
2628
2629 // build a mapping from sign directions to targets
2630 std::vector<LinkDirection> signedDirs = decodeTurnSigns(allDirs);
2631 std::map<LinkDirection, const NBEdge*> dirMap;
2632#ifdef DEBUG_TURNSIGNS
2633 std::cout << " numDirs=" << signedDirs.size() << " numTargets=" << targets.size() << "\n";
2634#endif
2635 if (signedDirs.size() > targets.size()) {
2636 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions but only % targets"), getID(), signedDirs.size(), targets.size());
2637 return false;
2638 } else if (signedDirs.size() < targets.size()) {
2639 // we need to drop some targets (i.e. turn-around)
2640 // use sumo-directions as a guide
2641 std::vector<LinkDirection> sumoDirs;
2642 for (const NBEdge* to : targets) {
2643 sumoDirs.push_back(myTo->getDirection(this, to));
2644 }
2645 // remove targets to the left
2646 bool checkMore = true;
2647 while (signedDirs.size() < targets.size() && checkMore) {
2648 checkMore = false;
2649 //std::cout << getID() << " sumoDirs=" << joinToString(sumoDirs, ",") << " signedDirs=" << joinToString(signedDirs, ",") << "\n";
2650 if (sumoDirs.back() != signedDirs.back()) {
2651 targets.pop_back();
2652 sumoDirs.pop_back();
2653 checkMore = true;
2654 }
2655 }
2656 // remove targets to the right
2657 checkMore = true;
2658 while (signedDirs.size() < targets.size() && checkMore) {
2659 checkMore = false;
2660 if (sumoDirs.front() != signedDirs.front()) {
2661 targets.erase(targets.begin());
2662 sumoDirs.erase(sumoDirs.begin());
2663 checkMore = true;
2664 }
2665 }
2666 // remove targets by permissions
2667 int i = 0;
2668 while (signedDirs.size() < targets.size() && i < (int)targets.size()) {
2669 if (targets[i] != nullptr && (targets[i]->getPermissions() & SVC_PASSENGER) == 0) {
2670 targets.erase(targets.begin() + i);
2671 sumoDirs.erase(sumoDirs.begin() + i);
2672 } else {
2673 i++;
2674 }
2675 }
2676 if (signedDirs.size() != targets.size()) {
2677 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions and % targets (after target pruning)"), getID(), signedDirs.size(), targets.size());
2678 return false;
2679 }
2680 }
2681 // directions and connections are both sorted from right to left
2682 for (int i = 0; i < (int)signedDirs.size(); i++) {
2683 dirMap[signedDirs[i]] = targets[i];
2684 }
2685 // check whether we have enough target lanes for a each signed direction
2686 for (auto item : signCons) {
2687 const LinkDirection dir = item.first;
2688 if (dir == LinkDirection::NODIR) {
2689 continue;
2690 }
2691 const NBEdge* to = dirMap[dir];
2692 std::vector<int>& knownTargets = toLaneMap[to];
2693 if ((int)knownTargets.size() < item.second) {
2694 int candidates = to->getNumLanesThatAllow(SVC_PASSENGER);
2695 if (candidates < item.second) {
2696 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed connections with directions '%' but target edge '%' has only % suitable lanes"),
2697 getID(), item.second, toString(dir), to->getID(), candidates);
2698 return false;
2699 }
2700 int i;
2701 int iInc;
2702 int iEnd;
2703 if (dir > LinkDirection::STRAIGHT) {
2704 // set more targets on the left
2705 i = to->getNumLanes() - 1;
2706 iInc = -1;
2707 iEnd = -1;
2708 } else {
2709 // set more targets on the right
2710 i = 0;
2711 iInc = 1;
2712 iEnd = to->getNumLanes();
2713 }
2714 while ((int)knownTargets.size() < item.second && i != iEnd) {
2715 if ((to->getPermissions(i) & SVC_PASSENGER) != 0) {
2716 if (std::find(knownTargets.begin(), knownTargets.end(), i) == knownTargets.end()) {
2717 knownTargets.push_back(i);
2718 }
2719 }
2720 i += iInc;
2721 }
2722 if ((int)knownTargets.size() != item.second) {
2723 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because not enough target lanes could be determined for direction '%'"), getID(), toString(dir));
2724 return false;
2725 }
2726 std::sort(knownTargets.begin(), knownTargets.end());
2727 }
2728 }
2729 std::map<const NBEdge*, int> toLaneIndex;
2730 for (int i = 0; i < getNumLanes(); i++) {
2731 const int turnSigns = myLanes[i].turnSigns;
2732 // no turnSigns are given for bicycle lanes and sidewalks
2733 if (turnSigns != 0) {
2734 // clear existing connections
2735 for (auto it = myConnections.begin(); it != myConnections.end();) {
2736 if (it->fromLane == i) {
2737 it = myConnections.erase(it);
2738 } else {
2739 it++;
2740 }
2741 }
2742 // add new connections
2743 for (LinkDirection dir : decodeTurnSigns(turnSigns)) {
2744 NBEdge* to = const_cast<NBEdge*>(dirMap[dir]);
2745 if (to != nullptr) {
2746 if (toLaneIndex.count(to) == 0) {
2747 // initialize to rightmost feasible lane
2748 SVCPermissions fromP = getPermissions(i);
2749 if ((fromP & SVC_PASSENGER) != 0) {
2750 // if the source permits passenger traffic, the target should too
2751 fromP = SVC_PASSENGER;
2752 }
2753 int toLane = 0;
2754 while ((to->getPermissions(toLane) & fromP) == 0) {
2755 toLane++;
2756 if (toLane == to->getNumLanes()) {
2757 SOFT_ASSERT(false);
2758#ifdef DEBUG_TURNSIGNS
2759 std::cout << " could not find passenger lane for target=" << to->getID() << "\n";
2760#endif
2761 return false;
2762 }
2763 }
2764#ifdef DEBUG_TURNSIGNS
2765 std::cout << " target=" << to->getID() << " initial toLane=" << toLane << "\n";
2766#endif
2767 toLaneIndex[to] = toLane;
2768 }
2769 setConnection(i, to, toLaneMap[to][toLaneIndex[to]++], Lane2LaneInfoType::VALIDATED, true);
2770 }
2771 }
2772 }
2773 }
2776 return true;
2777}
2778
2779
2780bool
2782#ifdef DEBUG_CONNECTION_GUESSING
2783 if (DEBUGCOND) {
2784 std::cout << "recheckLanes (initial) edge=" << getID() << "\n";
2785 for (Connection& c : myConnections) {
2786 std::cout << " conn " << c.getDescription(this) << "\n";
2787 }
2789 std::cout << " connToDelete " << c.getDescription(this) << "\n";
2790 }
2791 }
2792#endif
2793 // check delayed removals
2794 for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
2795 removeFromConnections(it->toEdge, it->fromLane, it->toLane, false, false, true);
2796 }
2797 std::vector<int> connNumbersPerLane(myLanes.size(), 0);
2798 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
2799 if ((*i).toEdge == nullptr || (*i).fromLane < 0 || (*i).toLane < 0) {
2800 i = myConnections.erase(i);
2801 } else {
2802 if ((*i).fromLane >= 0) {
2803 ++connNumbersPerLane[(*i).fromLane];
2804 }
2805 ++i;
2806 }
2807 }
2809 //if (myLanes.back().turnSigns != 0 && myTurnSignTarget != myTo->getID()) {
2810 // std::cout << getID() << " tst=" << myTurnSignTarget << " to=" << myTo->getID() << "\n";
2811 //}
2812 if (myLanes.back().turnSigns == 0 || myTurnSignTarget != myTo->getID() || !applyTurnSigns()) {
2813 // check #1:
2814 // If there is a lane with no connections and any neighbour lane has
2815 // more than one connections, try to move one of them.
2816 // This check is only done for edges which connections were assigned
2817 // using the standard algorithm.
2818 for (int i = 0; i < (int)myLanes.size(); i++) {
2819 if (connNumbersPerLane[i] == 0 && !isForbidden(getPermissions(i) & ~SVC_PEDESTRIAN)) {
2820 // dead-end lane found
2821 bool hasDeadEnd = true;
2822 // find lane with two connections or more to the right of the current lane
2823 for (int i2 = i - 1; hasDeadEnd && i2 >= 0; i2--) {
2824 if (getPermissions(i) != getPermissions(i2)) {
2825 break;
2826 }
2827 if (connNumbersPerLane[i2] > 1) {
2828 connNumbersPerLane[i2]--;
2829 for (int i3 = i2; i3 != i; i3++) {
2833 }
2834 hasDeadEnd = false;
2835 }
2836 }
2837 if (hasDeadEnd) {
2838 // find lane with two connections or more to the left of the current lane
2839 for (int i2 = i + 1; hasDeadEnd && i2 < getNumLanes(); i2++) {
2840 if (getPermissions(i) != getPermissions(i2)) {
2841 break;
2842 }
2843 if (connNumbersPerLane[i2] > 1) {
2844 connNumbersPerLane[i2]--;
2845 for (int i3 = i2; i3 != i; i3--) {
2849 }
2850 hasDeadEnd = false;
2851 }
2852 }
2853 }
2854 if (hasDeadEnd && myTo->getOutgoingEdges().size() > 1) {
2855 int passengerLanes = 0;
2856 int passengerTargetLanes = 0;
2857 for (const Lane& lane : myLanes) {
2858 if ((lane.permissions & SVC_PASSENGER) != 0) {
2859 passengerLanes++;
2860 }
2861 }
2862 for (const NBEdge* out : myTo->getOutgoingEdges()) {
2863 if (!isTurningDirectionAt(out)) {
2864 for (const Lane& lane : out->getLanes()) {
2865 if ((lane.permissions & SVC_PASSENGER) != 0) {
2866 passengerTargetLanes++;
2867 }
2868 }
2869 }
2870 }
2871 if (passengerLanes > 0 && passengerLanes <= passengerTargetLanes) {
2872 // no need for dead-ends
2873 if (i > 0) {
2874 // check if a connection to the right has a usable target to the left of its target
2875 std::vector<Connection> rightCons = getConnectionsFromLane(i - 1);
2876 if (rightCons.size() > 0) {
2877 const Connection& rc = rightCons.back();
2878 NBEdge* to = rc.toEdge;
2879 int toLane = rc.toLane + 1;
2880 if (toLane < to->getNumLanes()
2881 && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
2882 && !hasConnectionTo(to, toLane)) {
2884 hasDeadEnd = false;
2887 }
2888 if (hasDeadEnd) {
2889 // check if a connection to the right has a usable target to the right of its target
2890 toLane = rc.toLane - 1;
2891 if (toLane >= 0
2892 && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(rc.toLane)) != 0
2893 && (getPermissions(rc.fromLane) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
2894 && !hasConnectionTo(to, toLane)) {
2895 // shift the right lane connection target right and connect the dead lane to the old target
2896 getConnectionRef(rc.fromLane, to, rc.toLane).toLane = toLane;
2897 setConnection(i, to, toLane + 1, Lane2LaneInfoType::COMPUTED);
2898 hasDeadEnd = false;
2901 }
2902 }
2903 }
2904 }
2905 if (hasDeadEnd && i < getNumLanes() - 1) {
2906 // check if a connection to the left has a usable target to the right of its target
2907 std::vector<Connection> leftCons = getConnectionsFromLane(i + 1);
2908 if (leftCons.size() > 0) {
2909 NBEdge* to = leftCons.front().toEdge;
2910 int toLane = leftCons.front().toLane - 1;
2911 if (toLane >= 0
2912 && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
2913 && !hasConnectionTo(to, toLane)) {
2915 hasDeadEnd = false;
2918 }
2919 }
2920 }
2921#ifdef ADDITIONAL_WARNINGS
2922 if (hasDeadEnd) {
2923 WRITE_WARNING("Found dead-end lane " + getLaneID(i));
2924 }
2925#endif
2926 }
2927 }
2928 }
2929 }
2930 // check restrictions
2931 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
2932 Connection& c = *i;
2934 if (common == SVC_PEDESTRIAN || getPermissions(c.fromLane) == SVC_PEDESTRIAN) {
2935 // these are computed in NBNode::buildWalkingAreas
2936 i = myConnections.erase(i);
2937 } else if (common == 0) {
2938 // no common permissions.
2939 // try to find a suitable target lane to the right
2940 const int origToLane = c.toLane;
2941 c.toLane = -1; // ignore this connection when calling hasConnectionTo
2942 int toLane = origToLane;
2943 while (toLane > 0
2944 && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
2945 && !hasConnectionTo(c.toEdge, toLane)
2946 ) {
2947 toLane--;
2948 }
2949 if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
2950 && !hasConnectionTo(c.toEdge, toLane)) {
2951 c.toLane = toLane;
2952 ++i;
2953 } else {
2954 // try to find a suitable target lane to the left
2955 toLane = origToLane;
2956 while (toLane < (int)c.toEdge->getNumLanes() - 1
2957 && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
2958 && !hasConnectionTo(c.toEdge, toLane)
2959 ) {
2960 toLane++;
2961 }
2962 if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
2963 && !hasConnectionTo(c.toEdge, toLane)) {
2964 c.toLane = toLane;
2965 ++i;
2966 } else {
2967 // no alternative target found
2968 i = myConnections.erase(i);
2969 }
2970 }
2973 // do not allow sharp rail turns
2974 i = myConnections.erase(i);
2975 } else {
2976 ++i;
2977 }
2978 }
2979 }
2980 }
2981 // check involuntary dead end at "real" junctions
2982 if (getPermissions() != SVC_PEDESTRIAN) {
2983 if (myConnections.empty() && myTo->getOutgoingEdges().size() > 1 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
2984 WRITE_WARNINGF(TL("Edge '%' is not connected to outgoing edges at junction '%'."), getID(), myTo->getID());
2985 }
2986 const EdgeVector& incoming = myFrom->getIncomingEdges();
2987 if (incoming.size() > 1) {
2988 for (int i = 0; i < (int)myLanes.size(); i++) {
2989 if (getPermissions(i) != 0 && getPermissions(i) != SVC_PEDESTRIAN) {
2990 bool connected = false;
2991 for (std::vector<NBEdge*>::const_iterator in = incoming.begin(); in != incoming.end(); ++in) {
2992 if ((*in)->hasConnectionTo(this, i)) {
2993 connected = true;
2994 break;
2995 }
2996 }
2997 if (!connected) {
2998 WRITE_WARNINGF(TL("Lane '%' is not connected from any incoming edge at junction '%'."), getLaneID(i), myFrom->getID());
2999 }
3000 }
3001 }
3002 }
3003 }
3004 // avoid deadend due to change prohibitions
3005 if (getNumLanes() > 1 && myConnections.size() > 0) {
3006 for (int i = 0; i < (int)myLanes.size(); i++) {
3007 Lane& lane = myLanes[i];
3008 if ((connNumbersPerLane[i] == 0 || ((lane.accelRamp || (i > 0 && myLanes[i - 1].accelRamp && connNumbersPerLane[i - 1] > 0))
3009 && getSuccessors(SVC_PASSENGER).size() > 1))
3011 const bool forbiddenLeft = lane.changeLeft != SVCAll && lane.changeLeft != SVC_IGNORING && lane.changeLeft != SVC_UNSPECIFIED;
3012 const bool forbiddenRight = lane.changeRight != SVCAll && lane.changeRight != SVC_IGNORING && lane.changeRight != SVC_UNSPECIFIED;
3013 if (forbiddenLeft && (i == 0 || forbiddenRight)) {
3015 WRITE_WARNINGF(TL("Ignoring changeLeft prohibition for '%' to avoid dead-end"), getLaneID(i));
3016 } else if (forbiddenRight && (i == getNumLanes() - 1 || (i > 0 && myLanes[i - 1].accelRamp))) {
3018 WRITE_WARNINGF(TL("Ignoring changeRight prohibition for '%' to avoid dead-end"), getLaneID(i));
3019 }
3020 }
3021 }
3022 }
3023#ifdef ADDITIONAL_WARNINGS
3024 // check for connections with bad access permissions
3025 for (const Connection& c : myConnections) {
3026 SVCPermissions fromP = getPermissions(c.fromLane);
3027 SVCPermissions toP = c.toEdge->getPermissions(c.toLane);
3028 if ((fromP & SVC_PASSENGER) != 0
3029 && toP == SVC_BICYCLE) {
3030 bool hasAlternative = false;
3031 for (const Connection& c2 : myConnections) {
3032 if (c.fromLane == c2.fromLane && c.toEdge == c2.toEdge
3033 && (c.toEdge->getPermissions(c2.toLane) & SVC_PASSENGER) != 0) {
3034 hasAlternative = true;
3035 }
3036 }
3037 if (!hasAlternative) {
3038 WRITE_WARNING("Road lane ends on bikeLane for connection " + c.getDescription(this));
3039 }
3040 }
3041 }
3042
3043#endif
3044#ifdef DEBUG_CONNECTION_GUESSING
3045 if (DEBUGCOND) {
3046 std::cout << "recheckLanes (final) edge=" << getID() << "\n";
3047 for (Connection& c : myConnections) {
3048 std::cout << " conn " << c.getDescription(this) << "\n";
3049 }
3050 }
3051#endif
3052 return true;
3053}
3054
3055
3056void
3058 if (outgoing->size() == 0) {
3059 // we have to do this, because the turnaround may have been added before
3060 myConnections.clear();
3061 return;
3062 }
3063
3064#ifdef DEBUG_CONNECTION_GUESSING
3065 if (DEBUGCOND) {
3066 std::cout << " divideOnEdges " << getID() << " outgoing=" << toString(*outgoing) << "\n";
3067 }
3068#endif
3069
3070 // build connections for miv lanes
3071 std::vector<int> availableLanes;
3072 for (int i = 0; i < (int)myLanes.size(); ++i) {
3073 if ((getPermissions(i) & SVC_PASSENGER) != 0) {
3074 availableLanes.push_back(i);
3075 }
3076 }
3077 if (availableLanes.size() > 0) {
3078 divideSelectedLanesOnEdges(outgoing, availableLanes);
3079 }
3080 // build connections for miscellaneous further modes (more than bike,peds,bus and without passenger)
3081 availableLanes.clear();
3082 for (int i = 0; i < (int)myLanes.size(); ++i) {
3083 const SVCPermissions perms = getPermissions(i);
3084 if ((perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) == 0 || (perms & SVC_PASSENGER) != 0 || isForbidden(perms)) {
3085 continue;
3086 }
3087 availableLanes.push_back(i);
3088 }
3089 if (availableLanes.size() > 0) {
3090 divideSelectedLanesOnEdges(outgoing, availableLanes);
3091 }
3092 // build connections for busses from lanes that were excluded in the previous step
3093 availableLanes.clear();
3094 for (int i = 0; i < (int)myLanes.size(); ++i) {
3095 const SVCPermissions perms = getPermissions(i);
3096 if ((perms & SVC_BUS) == 0 || (perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) != 0 || (perms & SVC_PASSENGER) != 0) {
3097 continue;
3098 }
3099 availableLanes.push_back(i);
3100 }
3101 if (availableLanes.size() > 0) {
3102 divideSelectedLanesOnEdges(outgoing, availableLanes);
3103 }
3104 // build connections for bicycles (possibly combined with pedestrians)
3105 availableLanes.clear();
3106 for (int i = 0; i < (int)myLanes.size(); ++i) {
3107 const SVCPermissions perms = getPermissions(i);
3108 if (perms != SVC_BICYCLE && perms != (SVC_BICYCLE | SVC_PEDESTRIAN)) {
3109 continue;
3110 }
3111 availableLanes.push_back(i);
3112 }
3113 if (availableLanes.size() > 0) {
3114 divideSelectedLanesOnEdges(outgoing, availableLanes);
3115 }
3116 // clean up unassigned fromLanes
3117 bool explicitTurnaround = false;
3118 SVCPermissions turnaroundPermissions = SVC_UNSPECIFIED;
3119 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
3120 if ((*i).fromLane == -1) {
3121 if ((*i).toEdge == myTurnDestination && myTurnDestination != nullptr) {
3122 explicitTurnaround = true;
3123 turnaroundPermissions = (*i).permissions;
3124 }
3125 if ((*i).permissions != SVC_UNSPECIFIED) {
3126 for (Connection& c : myConnections) {
3127 if (c.toLane == -1 && c.toEdge == (*i).toEdge) {
3128 // carry over loaded edge2edge permissions
3129 c.permissions = (*i).permissions;
3130 }
3131 }
3132 }
3133 i = myConnections.erase(i);
3134 } else {
3135 ++i;
3136 }
3137 }
3138 if (explicitTurnaround) {
3140 myConnections.back().permissions = turnaroundPermissions;
3141 }
3143}
3144
3145
3146void
3147NBEdge::divideSelectedLanesOnEdges(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
3148 const std::vector<int>& priorities = prepareEdgePriorities(outgoing, availableLanes);
3149 if (priorities.empty()) {
3150 return;
3151 }
3152#ifdef DEBUG_CONNECTION_GUESSING
3153 if (DEBUGCOND) {
3154 std::cout << "divideSelectedLanesOnEdges " << getID() << " out=" << toString(*outgoing) << " prios=" << toString(priorities) << " avail=" << toString(availableLanes) << "\n";
3155 }
3156#endif
3157 // compute the resulting number of lanes that should be used to reach the following edge
3158 const int numOutgoing = (int)outgoing->size();
3159 std::vector<int> resultingLanesFactor;
3160 resultingLanesFactor.reserve(numOutgoing);
3161 int minResulting = std::numeric_limits<int>::max();
3162 for (int i = 0; i < numOutgoing; i++) {
3163 // res / minResulting will be the number of lanes which are meant to reach the current outgoing edge
3164 const int res = priorities[i] * (int)availableLanes.size();
3165 resultingLanesFactor.push_back(res);
3166 if (minResulting > res && res > 0) {
3167 // prevent minResulting from becoming 0
3168 minResulting = res;
3169 }
3170 }
3171 // compute the number of virtual edges
3172 // a virtual edge is used as a replacement for a real edge from now on
3173 // it shall allow to divide the existing lanes on this structure without
3174 // regarding the structure of outgoing edges
3175 int numVirtual = 0;
3176 // compute the transition from virtual to real edges
3177 EdgeVector transition;
3178 transition.reserve(numOutgoing);
3179 for (int i = 0; i < numOutgoing; i++) {
3180 // tmpNum will be the number of connections from this edge to the next edge
3181 assert(i < (int)resultingLanesFactor.size());
3182 const int tmpNum = (resultingLanesFactor[i] + minResulting - 1) / minResulting; // integer division rounding up
3183 numVirtual += tmpNum;
3184 for (int j = 0; j < tmpNum; j++) {
3185 transition.push_back((*outgoing)[i]);
3186 }
3187 }
3188#ifdef DEBUG_CONNECTION_GUESSING
3189 if (DEBUGCOND) {
3190 std::cout << " minResulting=" << minResulting << " numVirtual=" << numVirtual << " availLanes=" << toString(availableLanes) << " resLanes=" << toString(resultingLanesFactor) << " transition=" << toString(transition) << "\n";
3191 }
3192#endif
3193
3194 // assign lanes to edges
3195 // (conversion from virtual to real edges is done)
3196 ToEdgeConnectionsAdder adder(transition);
3197 Bresenham::compute(&adder, static_cast<int>(availableLanes.size()), numVirtual);
3198 const std::map<NBEdge*, std::vector<int> >& l2eConns = adder.getBuiltConnections();
3199 for (NBEdge* const target : *outgoing) {
3200 assert(l2eConns.find(target) != l2eConns.end());
3201 for (const int j : l2eConns.find(target)->second) {
3202 const int fromIndex = availableLanes[j];
3203 if ((getPermissions(fromIndex) & target->getPermissions()) == 0) {
3204 // exclude connection if fromLane and toEdge have no common permissions
3205 continue;
3206 }
3207 if ((getPermissions(fromIndex) & target->getPermissions()) == SVC_PEDESTRIAN) {
3208 // exclude connection if the only commonly permitted class are pedestrians
3209 // these connections are later built in NBNode::buildWalkingAreas
3210 continue;
3211 }
3212 // avoid building more connections than the edge has viable lanes (earlier
3213 // ones have precedence). This is necessary when running divideSelectedLanesOnEdges more than once.
3214 // @todo To decide which target lanes are still available we need to do a
3215 // preliminary lane-to-lane assignment in regard to permissions (rather than to ordering)
3216 const int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
3217 int targetLanes = target->getNumLanes();
3218 if (target->getPermissions(0) == SVC_PEDESTRIAN) {
3219 --targetLanes;
3220 }
3221 if (numConsToTarget >= targetLanes) {
3222 continue;
3223 }
3224 if (myLanes[fromIndex].connectionsDone) {
3225 // we already have complete information about connections from
3226 // this lane. do not add anything else
3227#ifdef DEBUG_CONNECTION_GUESSING
3228 if (DEBUGCOND) {
3229 std::cout << " connectionsDone from " << getID() << "_" << fromIndex << ": ";
3230 for (const Connection& c : getConnectionsFromLane(fromIndex)) {
3231 std::cout << c.getDescription(this) << ", ";
3232 }
3233 std::cout << "\n";
3234 }
3235#endif
3236 continue;
3237 }
3238 myConnections.push_back(Connection(fromIndex, target, -1));
3239#ifdef DEBUG_CONNECTION_GUESSING
3240 if (DEBUGCOND) {
3241 std::cout << " request connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3242 }
3243#endif
3244 }
3245 }
3246
3247 addStraightConnections(outgoing, availableLanes, priorities);
3248}
3249
3250
3251void
3252NBEdge::addStraightConnections(const EdgeVector* outgoing, const std::vector<int>& availableLanes, const std::vector<int>& priorities) {
3253 // ensure sufficient straight connections for the (highest-priority) straight target
3254 const int numOutgoing = (int) outgoing->size();
3255 NBEdge* target = nullptr;
3256 NBEdge* rightOfTarget = nullptr;
3257 NBEdge* leftOfTarget = nullptr;
3258 int maxPrio = 0;
3259 for (int i = 0; i < numOutgoing; i++) {
3260 if (maxPrio < priorities[i]) {
3261 const LinkDirection dir = myTo->getDirection(this, (*outgoing)[i]);
3262 if (dir == LinkDirection::STRAIGHT) {
3263 maxPrio = priorities[i];
3264 target = (*outgoing)[i];
3265 rightOfTarget = i == 0 ? outgoing->back() : (*outgoing)[i - 1];
3266 leftOfTarget = i + 1 == numOutgoing ? outgoing->front() : (*outgoing)[i + 1];
3267 }
3268 }
3269 }
3270 if (target == nullptr) {
3271 return;
3272 }
3273 int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
3274 int targetLanes = (int)target->getNumLanes();
3275 if (target->getPermissions(0) == SVC_PEDESTRIAN) {
3276 --targetLanes;
3277 }
3278 const int numDesiredConsToTarget = MIN2(targetLanes, (int)availableLanes.size());
3279#ifdef DEBUG_CONNECTION_GUESSING
3280 if (DEBUGCOND) {
3281 std::cout << " checking extra lanes for target=" << target->getID() << " cons=" << numConsToTarget << " desired=" << numDesiredConsToTarget << "\n";
3282 }
3283#endif
3284 std::vector<int>::const_iterator it_avail = availableLanes.begin();
3285 while (numConsToTarget < numDesiredConsToTarget && it_avail != availableLanes.end()) {
3286 const int fromIndex = *it_avail;
3287 if (
3288 // not yet connected
3289 (count_if(myConnections.begin(), myConnections.end(), connections_finder(fromIndex, target, -1)) == 0)
3290 // matching permissions
3291 && ((getPermissions(fromIndex) & target->getPermissions()) != 0)
3292 // more than pedestrians
3293 && ((getPermissions(fromIndex) & target->getPermissions()) != SVC_PEDESTRIAN)
3294 // lane not yet fully defined
3295 && !myLanes[fromIndex].connectionsDone
3296 ) {
3297#ifdef DEBUG_CONNECTION_GUESSING
3298 if (DEBUGCOND) {
3299 std::cout << " candidate from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3300 }
3301#endif
3302 // prevent same-edge conflicts
3303 if (
3304 // no outgoing connections to the right from further left
3305 ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
3306 // no outgoing connections to the left from further right
3307 && (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)) {
3308#ifdef DEBUG_CONNECTION_GUESSING
3309 if (DEBUGCOND) {
3310 std::cout << " request additional connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3311 }
3312#endif
3313 myConnections.push_back(Connection(fromIndex, target, -1));
3314 numConsToTarget++;
3315 } else {
3316#ifdef DEBUG_CONNECTION_GUESSING
3317 if (DEBUGCOND) std::cout
3318 << " fail check1="
3319 << ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
3320 << " check2=" << (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)
3321 << " rightOfTarget=" << rightOfTarget->getID()
3322 << " leftOfTarget=" << leftOfTarget->getID()
3323 << "\n";
3324#endif
3325
3326 }
3327 }
3328 ++it_avail;
3329 }
3330}
3331
3332
3333const std::vector<int>
3334NBEdge::prepareEdgePriorities(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
3335 std::vector<int> priorities;
3336 MainDirections mainDirections(*outgoing, this, myTo, availableLanes);
3337 const int dist = mainDirections.getStraightest();
3338 if (dist == -1) {
3339 return priorities;
3340 }
3341 // copy the priorities first
3342 priorities.reserve(outgoing->size());
3343 for (const NBEdge* const out : *outgoing) {
3344 int prio = NBNode::isTrafficLight(myTo->getType()) ? 0 : out->getJunctionPriority(myTo);
3345 assert((prio + 1) * 2 > 0);
3346 prio = (prio + 1) * 2;
3347 priorities.push_back(prio);
3348 }
3349 // when the right turning direction has not a higher priority, divide
3350 // the importance by 2 due to the possibility to leave the junction
3351 // faster from this lane
3352#ifdef DEBUG_CONNECTION_GUESSING
3353 if (DEBUGCOND) std::cout << " prepareEdgePriorities " << getID()
3354 << " outgoing=" << toString(*outgoing)
3355 << " priorities1=" << toString(priorities)
3356 << " dist=" << dist
3357 << "\n";
3358#endif
3359 if (dist != 0 && !mainDirections.includes(MainDirections::Direction::RIGHTMOST)) {
3360 assert(priorities.size() > 0);
3361 priorities[0] /= 2;
3362#ifdef DEBUG_CONNECTION_GUESSING
3363 if (DEBUGCOND) {
3364 std::cout << " priorities2=" << toString(priorities) << "\n";
3365 }
3366#endif
3367 }
3368 // HEURISTIC:
3369 // when no higher priority exists, let the forward direction be
3370 // the main direction
3371 if (mainDirections.empty()) {
3372 assert(dist < (int)priorities.size());
3373 priorities[dist] *= 2;
3374#ifdef DEBUG_CONNECTION_GUESSING
3375 if (DEBUGCOND) {
3376 std::cout << " priorities3=" << toString(priorities) << "\n";
3377 }
3378#endif
3379 }
3381 priorities[dist] += 1;
3382 } else {
3383 // try to ensure separation of left turns
3385 priorities[0] /= 4;
3386 priorities[(int)priorities.size() - 1] /= 2;
3387#ifdef DEBUG_CONNECTION_GUESSING
3388 if (DEBUGCOND) {
3389 std::cout << " priorities6=" << toString(priorities) << "\n";
3390 }
3391#endif
3392 } else if (mainDirections.includes(MainDirections::Direction::RIGHTMOST)
3393 && outgoing->size() > 2
3394 && availableLanes.size() == 2
3395 && (*outgoing)[dist]->getPriority() == (*outgoing)[0]->getPriority()) {
3396 priorities[0] /= 4;
3397 priorities.back() /= 2;
3398#ifdef DEBUG_CONNECTION_GUESSING
3399 if (DEBUGCOND) {
3400 std::cout << " priorities7=" << toString(priorities) << "\n";
3401 }
3402#endif
3403 }
3404 }
3405 if (mainDirections.includes(MainDirections::Direction::FORWARD)) {
3406 if (myLanes.size() > 2) {
3407 priorities[dist] *= 2;
3408#ifdef DEBUG_CONNECTION_GUESSING
3409 if (DEBUGCOND) {
3410 std::cout << " priorities4=" << toString(priorities) << "\n";
3411 }
3412#endif
3413 } else {
3414 priorities[dist] *= 3;
3415#ifdef DEBUG_CONNECTION_GUESSING
3416 if (DEBUGCOND) {
3417 std::cout << " priorities5=" << toString(priorities) << "\n";
3418 }
3419#endif
3420 }
3421 }
3422 return priorities;
3423}
3424
3425
3426void
3427NBEdge::appendTurnaround(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike, bool checkPermissions) {
3428 // do nothing if no turnaround is known
3430 return;
3431 }
3432 // do nothing if the destination node is controlled by a tls and no turnarounds
3433 // shall be appended for such junctions
3434 if (noTLSControlled && myTo->isTLControlled()) {
3435 return;
3436 }
3437 if (noFringe && myTo->getFringeType() == FringeType::OUTER) {
3438 return;
3439 }
3440 bool isDeadEnd = true;
3441 for (const Connection& c : myConnections) {
3442 if ((c.toEdge->getPermissions(c.toLane)
3443 & getPermissions(c.fromLane)
3444 & SVC_PASSENGER) != 0
3445 || (c.toEdge->getPermissions() & getPermissions()) == getPermissions()) {
3446 isDeadEnd = false;
3447 break;
3448 }
3449 }
3450 if (onlyDeadends && !isDeadEnd) {
3451 return;
3452 }
3453 const int fromLane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
3454 if (onlyTurnlane) {
3455 for (const Connection& c : getConnectionsFromLane(fromLane)) {
3456 LinkDirection dir = myTo->getDirection(this, c.toEdge);
3457 if (dir != LinkDirection::LEFT && dir != LinkDirection::PARTLEFT) {
3458 return;
3459 }
3460 }
3461 }
3463 if (checkPermissions) {
3464 if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == 0) {
3465 // exclude connection if fromLane and toEdge have no common permissions
3466 return;
3467 }
3468 if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == SVC_PEDESTRIAN) {
3469 // exclude connection if the only commonly permitted class are pedestrians
3470 // these connections are later built in NBNode::buildWalkingAreas
3471 return;
3472 }
3473 }
3474 // avoid railway turn-arounds
3477 // except at dead-ends on bidi-edges where they model a reversal in train direction
3478 // @todo #4382: once the network fringe is tagged, it also should not receive turn-arounds)
3479 if (isBidiRail() && isRailDeadEnd()) {
3480 // add a slow connection because direction-reversal implies stopping
3482 return;
3483 } else {
3484 return;
3485 }
3486 };
3487 if (noGeometryLike && !isDeadEnd) {
3488 // ignore paths and service entrances if this edge is for passenger traffic
3489 if (myTo->geometryLike() || ((getPermissions() & SVC_PASSENGER) != 0
3490 && !onlyTurnlane
3491 && myTo->geometryLike(
3494 // make sure the turnDestination has other incoming edges
3496 if (turnIncoming.size() > 1) {
3497 // this edge is always part of incoming
3498 return;
3499 }
3500 }
3501 }
3503}
3504
3505
3506bool
3507NBEdge::isTurningDirectionAt(const NBEdge* const edge) const {
3508 // maybe it was already set as the turning direction
3509 if (edge == myTurnDestination) {
3510 return true;
3511 } else if (myTurnDestination != nullptr) {
3512 // otherwise - it's not if a turning direction exists
3513 return false;
3514 }
3515 return edge == myPossibleTurnDestination;
3516}
3517
3518
3519NBNode*
3520NBEdge::tryGetNodeAtPosition(double pos, double tolerance) const {
3521 // return the from-node when the position is at the begin of the edge
3522 if (pos < tolerance) {
3523 return myFrom;
3524 }
3525 // return the to-node when the position is at the end of the edge
3526 if (pos > myLength - tolerance) {
3527 return myTo;
3528 }
3529 return nullptr;
3530}
3531
3532
3533void
3535 int lanes = e->getNumLanes();
3536 for (int i = 0; i < lanes; i++) {
3537 for (const NBEdge::Connection& el : e->getConnectionsFromLane(i)) {
3538 assert(el.tlID == "");
3539 addLane2LaneConnection(i + laneOff, el.toEdge, el.toLane, Lane2LaneInfoType::COMPUTED);
3540 }
3541 }
3542}
3543
3544
3545bool
3548}
3549
3550
3551double
3553 return SUMO_const_laneWidth * (double)myLanes.size();
3554}
3555
3556
3557bool
3558NBEdge::mayBeTLSControlled(int fromLane, NBEdge* toEdge, int toLane) const {
3559 for (const Connection& c : myConnections) {
3560 if (c.fromLane == fromLane && c.toEdge == toEdge && c.toLane == toLane && c.uncontrolled) {
3561 return false;
3562 }
3563 }
3564 return true;
3565}
3566
3567
3568bool
3569NBEdge::setControllingTLInformation(const NBConnection& c, const std::string& tlID) {
3570 const int fromLane = c.getFromLane();
3571 NBEdge* toEdge = c.getTo();
3572 const int toLane = c.getToLane();
3573 const int tlIndex = c.getTLIndex();
3574 const int tlIndex2 = c.getTLIndex2();
3575 // check whether the connection was not set as not to be controled previously
3576 if (!mayBeTLSControlled(fromLane, toEdge, toLane)) {
3577 return false;
3578 }
3579
3580 assert(fromLane < 0 || fromLane < (int) myLanes.size());
3581 // try to use information about the connections if given
3582 if (fromLane >= 0 && toLane >= 0) {
3583 // find the specified connection
3584 std::vector<Connection>::iterator i =
3585 find_if(myConnections.begin(), myConnections.end(), connections_finder(fromLane, toEdge, toLane));
3586 // ok, we have to test this as on the removal of self-loop edges some connections
3587 // will be reassigned
3588 if (i != myConnections.end()) {
3589 // get the connection
3590 Connection& connection = *i;
3591 // set the information about the tl
3592 connection.tlID = tlID;
3593 connection.tlLinkIndex = tlIndex;
3594 connection.tlLinkIndex2 = tlIndex2;
3595 return true;
3596 }
3597 }
3598 // if the original connection was not found, set the information for all
3599 // connections
3600 int no = 0;
3601 bool hadError = false;
3602 for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
3603 if ((*i).toEdge != toEdge) {
3604 continue;
3605 }
3606 if (fromLane >= 0 && fromLane != (*i).fromLane) {
3607 continue;
3608 }
3609 if (toLane >= 0 && toLane != (*i).toLane) {
3610 continue;
3611 }
3612 if ((*i).tlID == "") {
3613 (*i).tlID = tlID;
3614 (*i).tlLinkIndex = tlIndex;
3615 (*i).tlLinkIndex2 = tlIndex2;
3616 no++;
3617 } else {
3618 if ((*i).tlID != tlID && (*i).tlLinkIndex == tlIndex) {
3619 WRITE_WARNINGF(TL("The lane '%' on edge '%' already had a traffic light signal."), i->fromLane, getID());
3620 hadError = true;
3621 }
3622 }
3623 }
3624 if (hadError && no == 0) {
3625 WRITE_WARNINGF(TL("Could not set any signal of the tlLogic '%' (unknown group)."), tlID);
3626 }
3627 return true;
3628}
3629
3630
3631void
3633 for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); it++) {
3634 it->tlID = "";
3635 }
3636}
3637
3638
3641 PositionVector ret;
3642 int lane;
3643 if (myFrom == (&n)) {
3644 // outgoing
3646 ret = myLanes[lane].shape;
3647 } else {
3648 // incoming
3650 ret = myLanes[lane].shape.reverse();
3651 }
3652 ret.move2side(getLaneWidth(lane) / 2.);
3653 return ret;
3654}
3655
3656
3659 PositionVector ret;
3660 int lane;
3661 if (myFrom == (&n)) {
3662 // outgoing
3664 ret = myLanes[lane].shape;
3665 } else {
3666 // incoming
3668 ret = myLanes[lane].shape.reverse();
3669 }
3670 ret.move2side(-getLaneWidth(lane) / 2.);
3671 return ret;
3672}
3673
3674
3675bool
3676NBEdge::expandableBy(NBEdge* possContinuation, std::string& reason) const {
3677 // ok, the number of lanes must match
3678 if (myLanes.size() != possContinuation->myLanes.size()) {
3679 reason = "laneNumber";
3680 return false;
3681 }
3682 // do not create self loops
3683 if (myFrom == possContinuation->myTo) {
3684 reason = "loop";
3685 return false;
3686 }
3687 // conserve bidi-rails
3688 if (isBidiRail() != possContinuation->isBidiRail()) {
3689 reason = "bidi-rail";
3690 return false;
3691 }
3692 // also, check whether the connections - if any exit do allow to join
3693 // both edges
3694 // This edge must have a one-to-one connection to the following lanes
3695 switch (myStep) {
3697 break;
3699 break;
3701 // the following edge must be connected
3702 const EdgeVector& conn = getConnectedEdges();
3703 if (find(conn.begin(), conn.end(), possContinuation) == conn.end()) {
3704 reason = "disconnected";
3705 return false;
3706 }
3707 }
3708 break;
3713 // the possible continuation must be connected
3714 if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(possContinuation)) == myConnections.end()) {
3715 reason = "disconnected";
3716 return false;
3717 }
3718 // all lanes must go to the possible continuation
3719 std::vector<int> conns = getConnectionLanes(possContinuation);
3720 const int offset = MAX2(0, getFirstNonPedestrianLaneIndex(NBNode::FORWARD, true));
3721 if (conns.size() < myLanes.size() - offset) {
3722 reason = "some lanes disconnected";
3723 return false;
3724 }
3725 }
3726 break;
3727 default:
3728 break;
3729 }
3730 const double minLength = OptionsCont::getOptions().getFloat("geometry.remove.min-length");
3731 if (minLength > 0 && (possContinuation->getLoadedLength() < minLength || getLoadedLength() < minLength)) {
3732 return true;
3733 }
3734 // the priority, too (?)
3735 if (getPriority() != possContinuation->getPriority()) {
3736 reason = "priority";
3737 return false;
3738 }
3739 // the speed allowed
3740 if (mySpeed != possContinuation->mySpeed) {
3741 reason = "speed";
3742 return false;
3743 }
3744 // spreadtype should match or it will look ugly
3745 if (myLaneSpreadFunction != possContinuation->myLaneSpreadFunction) {
3746 reason = "spreadType";
3747 return false;
3748 }
3749 // matching lanes must have identical properties
3750 for (int i = 0; i < (int)myLanes.size(); i++) {
3751 if (myLanes[i].speed != possContinuation->myLanes[i].speed) {
3752 reason = "lane " + toString(i) + " speed";
3753 return false;
3754 } else if (myLanes[i].permissions != possContinuation->myLanes[i].permissions) {
3755 reason = "lane " + toString(i) + " permissions";
3756 return false;
3757 } else if (myLanes[i].changeLeft != possContinuation->myLanes[i].changeLeft || myLanes[i].changeRight != possContinuation->myLanes[i].changeRight) {
3758 reason = "lane " + toString(i) + " change restrictions";
3759 return false;
3760 } else if (myLanes[i].width != possContinuation->myLanes[i].width &&
3761 fabs(myLanes[i].width - possContinuation->myLanes[i].width) > OptionsCont::getOptions().getFloat("geometry.remove.width-tolerance")) {
3762 reason = "lane " + toString(i) + " width";
3763 return false;
3764 }
3765 }
3766 // if given identically osm names
3767 if (!OptionsCont::getOptions().isDefault("output.street-names") && myStreetName != possContinuation->getStreetName()
3768 && ((myStreetName != "" && possContinuation->getStreetName() != "")
3769 // only permit merging a short unnamed road with a longer named road
3770 || (myStreetName != "" && myLength <= possContinuation->getLength())
3771 || (myStreetName == "" && myLength >= possContinuation->getLength()))) {
3772 return false;
3773 }
3774
3775 return true;
3776}
3777
3778
3779void
3781 // append geometry
3782 myGeom.append(e->myGeom);
3783 for (int i = 0; i < (int)myLanes.size(); i++) {
3784 myLanes[i].customShape.append(e->myLanes[i].customShape);
3785 if (myLanes[i].knowsParameter(SUMO_PARAM_ORIGID) || e->myLanes[i].knowsParameter(SUMO_PARAM_ORIGID)
3786 || OptionsCont::getOptions().getBool("output.original-names")) {
3787 const std::string origID = myLanes[i].getParameter(SUMO_PARAM_ORIGID, getID());
3788 const std::string origID2 = e->myLanes[i].getParameter(SUMO_PARAM_ORIGID, e->getID());
3789 if (origID != origID2) {
3790 myLanes[i].setParameter(SUMO_PARAM_ORIGID, origID + " " + origID2);
3791 }
3792 }
3793 myLanes[i].connectionsDone = e->myLanes[i].connectionsDone;
3794 myLanes[i].turnSigns = e->myLanes[i].turnSigns;
3795 }
3796 if (e->getLength() > myLength) {
3797 // possibly some lane attributes differ (when using option geometry.remove.min-length)
3798 // make sure to use the attributes from the longer edge
3799 for (int i = 0; i < (int)myLanes.size(); i++) {
3800 myLanes[i].width = e->myLanes[i].width;
3801 }
3802 // defined name prevails over undefined name of shorter road
3803 if (myStreetName == "") {
3805 }
3806 }
3807 // recompute length
3808 myLength += e->myLength;
3809 if (myLoadedLength > 0 || e->myLoadedLength > 0) {
3811 }
3812 // copy the connections and the building step if given
3813 myStep = e->myStep;
3818 // set the node
3819 myTo = e->myTo;
3822 if (e->knowsParameter("origTo")) {
3823 setParameter("origTo", e->getParameter("origTo"));
3824 }
3827 }
3828 computeAngle(); // myEndAngle may be different now
3829}
3830
3831
3832bool
3834 for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
3835 if ((*i).toEdge == e && (*i).tlID != "") {
3836 return true;
3837 }
3838 }
3839 return false;
3840}
3841
3842
3843NBEdge*
3844NBEdge::getTurnDestination(bool possibleDestination) const {
3845 if (myTurnDestination == nullptr && possibleDestination) {
3847 }
3848 return myTurnDestination;
3849}
3850
3851
3852std::string
3853NBEdge::getLaneID(int lane) const {
3854 return myID + "_" + toString(lane);
3855}
3856
3857
3858bool
3859NBEdge::isNearEnough2BeJoined2(NBEdge* e, double threshold) const {
3860 std::vector<double> distances = myGeom.distances(e->getGeometry());
3861 assert(distances.size() > 0);
3862 return VectorHelper<double>::maxValue(distances) < threshold;
3863}
3864
3865
3866void
3867NBEdge::addLane(int index, bool recomputeShape, bool recomputeConnections, bool shiftIndices) {
3868 assert(index <= (int)myLanes.size());
3869 myLanes.insert(myLanes.begin() + index, Lane(this, ""));
3870 // copy attributes
3871 if (myLanes.size() > 1) {
3872 int templateIndex = index > 0 ? index - 1 : index + 1;
3873 myLanes[index].speed = myLanes[templateIndex].speed;
3874 myLanes[index].friction = myLanes[templateIndex].friction;
3875 myLanes[index].permissions = myLanes[templateIndex].permissions;
3876 myLanes[index].preferred = myLanes[templateIndex].preferred;
3877 myLanes[index].endOffset = myLanes[templateIndex].endOffset;
3878 myLanes[index].width = myLanes[templateIndex].width;
3879 myLanes[index].updateParameters(myLanes[templateIndex].getParametersMap());
3880 }
3881 const EdgeVector& incs = myFrom->getIncomingEdges();
3882 if (recomputeShape) {
3884 }
3885 if (recomputeConnections) {
3886 for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
3887 (*i)->invalidateConnections(true);
3888 }
3890 } else if (shiftIndices) {
3891 // shift outgoing connections above the added lane to the left
3892 for (Connection& c : myConnections) {
3893 if (c.fromLane >= index) {
3894 c.fromLane += 1;
3895 }
3896 }
3897 // shift incoming connections above the added lane to the left
3898 for (NBEdge* inc : myFrom->getIncomingEdges()) {
3899 for (Connection& c : inc->myConnections) {
3900 if (c.toEdge == this && c.toLane >= index) {
3901 c.toLane += 1;
3902 }
3903 }
3904 }
3905 myFrom->shiftTLConnectionLaneIndex(this, +1, index - 1);
3906 myTo->shiftTLConnectionLaneIndex(this, +1, index - 1);
3907 }
3908}
3909
3910void
3912 int newLaneNo = (int)myLanes.size() + by;
3913 while ((int)myLanes.size() < newLaneNo) {
3914 // recompute shapes on last addition
3915 const bool recompute = ((int)myLanes.size() == newLaneNo - 1) && myStep < EdgeBuildingStep::LANES2LANES_USER;
3916 addLane((int)myLanes.size(), recompute, recompute, false);
3917 }
3918}
3919
3920
3921void
3922NBEdge::deleteLane(int index, bool recompute, bool shiftIndices) {
3923 assert(index < (int)myLanes.size());
3924 myLanes.erase(myLanes.begin() + index);
3925 if (recompute) {
3927 const EdgeVector& incs = myFrom->getIncomingEdges();
3928 for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
3929 (*i)->invalidateConnections(true);
3930 }
3932 } else if (shiftIndices) {
3933 removeFromConnections(nullptr, index, -1, false, true);
3934 for (NBEdge* inc : myFrom->getIncomingEdges()) {
3935 inc->removeFromConnections(this, -1, index, false, true);
3936 }
3937 }
3938}
3939
3940
3941void
3943 int newLaneNo = (int) myLanes.size() - by;
3944 assert(newLaneNo > 0);
3945 while ((int)myLanes.size() > newLaneNo) {
3946 // recompute shapes on last removal
3947 const bool recompute = (int)myLanes.size() == newLaneNo + 1 && myStep < EdgeBuildingStep::LANES2LANES_USER;
3948 deleteLane((int)myLanes.size() - 1, recompute, false);
3949 }
3950}
3951
3952
3953void
3955 assert(myTo->getOutgoingEdges().size() == 0);
3957}
3958
3959
3960void
3962 if (lane < 0) { // all lanes are meant...
3963 for (int i = 0; i < (int)myLanes.size(); i++) {
3964 allowVehicleClass(i, vclass);
3965 }
3966 } else {
3967 assert(lane < (int)myLanes.size());
3968 myLanes[lane].permissions |= vclass;
3969 }
3970}
3971
3972
3973void
3975 if (lane < 0) { // all lanes are meant...
3976 for (int i = 0; i < (int)myLanes.size(); i++) {
3977 disallowVehicleClass((int) i, vclass);
3978 }
3979 } else {
3980 assert(lane < (int)myLanes.size());
3981 myLanes[lane].permissions &= ~vclass;
3982 }
3983}
3984
3985
3986void
3988 if (lane < 0) { // all lanes are meant...
3989 for (int i = 0; i < (int)myLanes.size(); i++) {
3990 preferVehicleClass(i, vclasses);
3991 }
3992 } else {
3993 assert(lane < (int)myLanes.size());
3994 myLanes[lane].permissions |= vclasses;
3995 myLanes[lane].preferred |= vclasses;
3996 }
3997}
3998
3999
4000void
4001NBEdge::setLaneWidth(int lane, double width) {
4002 if (lane < 0) {
4003 // all lanes are meant...
4004 myLaneWidth = width;
4005 for (int i = 0; i < (int)myLanes.size(); i++) {
4006 // ... do it for each lane
4007 setLaneWidth(i, width);
4008 }
4009 return;
4010 }
4011 assert(lane < (int)myLanes.size());
4012 myLanes[lane].width = width;
4013}
4014
4015void
4016NBEdge::setLaneType(int lane, const std::string& type) {
4017 if (lane < 0) {
4018 for (int i = 0; i < (int)myLanes.size(); i++) {
4019 // ... do it for each lane
4020 setLaneType(i, type);
4021 }
4022 return;
4023 }
4024 assert(lane < (int)myLanes.size());
4025 myLanes[lane].type = type;
4026}
4027
4028
4029double
4030NBEdge::getLaneWidth(int lane) const {
4031 return myLanes[lane].width != UNSPECIFIED_WIDTH
4032 ? myLanes[lane].width
4034}
4035
4036double
4038 const NBNode& node,
4039 const NBEdge::Connection& connection,
4040 const NBEdge::Lane& successor,
4041 bool isVia) const {
4042
4043 if (!isVia && node.isConstantWidthTransition() && getNumLanes() > connection.toEdge->getNumLanes()) {
4044 return getLaneWidth(connection.fromLane);
4045 }
4046
4047 return (isBikepath(getPermissions(connection.fromLane)) && (
4048 getLaneWidth(connection.fromLane) < successor.width || successor.width == UNSPECIFIED_WIDTH)) ?
4049 myLanes[connection.fromLane].width : successor.width; // getLaneWidth(connection.fromLane) never returns -1 (UNSPECIFIED_WIDTH)
4050}
4051
4052double
4054 double result = 0;
4055 for (int i = 0; i < (int)myLanes.size(); i++) {
4056 result += getLaneWidth(i);
4057 }
4058 return result;
4059}
4060
4061double
4062NBEdge::getEndOffset(int lane) const {
4063 return myLanes[lane].endOffset != UNSPECIFIED_OFFSET ? myLanes[lane].endOffset : getEndOffset();
4064}
4065
4066
4067const StopOffset&
4069 return myEdgeStopOffset;
4070}
4071
4072
4073const StopOffset&
4075 if (lane == -1) {
4076 return myEdgeStopOffset;
4077 } else {
4078 return myLanes[lane].laneStopOffset;
4079 }
4080}
4081
4082
4083void
4084NBEdge::setEndOffset(int lane, double offset) {
4085 if (lane < 0) {
4086 // all lanes are meant...
4087 myEndOffset = offset;
4088 for (int i = 0; i < (int)myLanes.size(); i++) {
4089 // ... do it for each lane
4090 setEndOffset(i, offset);
4091 }
4092 return;
4093 }
4094 assert(lane < (int)myLanes.size());
4095 myLanes[lane].endOffset = offset;
4096}
4097
4098
4099bool
4100NBEdge::setEdgeStopOffset(int lane, const StopOffset& offset, bool overwrite) {
4101 if (lane < 0) {
4102 if (!overwrite && myEdgeStopOffset.isDefined()) {
4103 return false;
4104 }
4105 // all lanes are meant...
4106 if (offset.getOffset() < 0) {
4107 // Edge length unknown at parsing time, thus check here.
4108 WRITE_WARNINGF(TL("Ignoring invalid stopOffset for edge '%' (negative offset)."), getID());
4109 return false;
4110 } else {
4111 myEdgeStopOffset = offset;
4112 }
4113 } else if (lane < (int)myLanes.size()) {
4114 if (!myLanes[lane].laneStopOffset.isDefined() || overwrite) {
4115 if (offset.getOffset() < 0) {
4116 // Edge length unknown at parsing time, thus check here.
4117 WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (negative offset)."), getLaneID(lane));
4118 } else {
4119 myLanes[lane].laneStopOffset = offset;
4120 }
4121 }
4122 } else {
4123 WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (invalid lane index)."), toString(lane));
4124 }
4125 return true;
4126}
4127
4128
4129void
4130NBEdge::setSpeed(int lane, double speed) {
4131 if (lane < 0) {
4132 // all lanes are meant...
4133 mySpeed = speed;
4134 for (int i = 0; i < (int)myLanes.size(); i++) {
4135 // ... do it for each lane
4136 setSpeed(i, speed);
4137 }
4138 return;
4139 }
4140 assert(lane < (int)myLanes.size());
4141 myLanes[lane].speed = speed;
4142}
4143
4144
4145void
4146NBEdge::setFriction(int lane, double friction) {
4147 if (lane < 0) {
4148 // all lanes are meant...
4149 myFriction = friction;
4150 for (int i = 0; i < (int)myLanes.size(); i++) {
4151 // ... do it for each lane
4152 setFriction(i, friction);
4153 }
4154 return;
4155 }
4156 assert(lane < (int)myLanes.size());
4157 myLanes[lane].friction = friction;
4158}
4159
4160
4161void
4162NBEdge::setAcceleration(int lane, bool accelRamp) {
4163 assert(lane >= 0);
4164 assert(lane < (int)myLanes.size());
4165 myLanes[lane].accelRamp = accelRamp;
4166}
4167
4168
4169void
4170NBEdge::setLaneShape(int lane, const PositionVector& shape) {
4171 assert(lane >= 0);
4172 assert(lane < (int)myLanes.size());
4173 myLanes[lane].customShape = shape;
4174}
4175
4176
4177void
4179 if (lane < 0) {
4180 for (int i = 0; i < (int)myLanes.size(); i++) {
4181 // ... do it for each lane
4182 setPermissions(permissions, i);
4183 }
4184 } else {
4185 assert(lane < (int)myLanes.size());
4186 myLanes[lane].permissions = permissions;
4187 }
4188}
4189
4190
4191void
4193 if (lane < 0) {
4194 for (int i = 0; i < (int)myLanes.size(); i++) {
4195 // ... do it for each lane
4196 setPreferredVehicleClass(permissions, i);
4197 }
4198 } else {
4199 assert(lane < (int)myLanes.size());
4200 myLanes[lane].preferred = permissions;
4201 }
4202}
4203
4204
4205void
4207 assert(lane >= 0);
4208 assert(lane < (int)myLanes.size());
4209 myLanes[lane].changeLeft = changeLeft;
4210 myLanes[lane].changeRight = changeRight;
4211}
4212
4213
4215NBEdge::getPermissions(int lane) const {
4216 if (lane < 0) {
4217 SVCPermissions result = 0;
4218 for (int i = 0; i < (int)myLanes.size(); i++) {
4219 result |= getPermissions(i);
4220 }
4221 return result;
4222 } else {
4223 assert(lane < (int)myLanes.size());
4224 return myLanes[lane].permissions;
4225 }
4226}
4227
4228
4229void
4231 myLoadedLength = val;
4232}
4233
4234void
4236 myLength = val;
4237}
4238
4239
4240void
4242 for (std::vector<Lane>::iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
4243 (*i).permissions = SVCAll;
4244 (*i).preferred = 0;
4245 }
4246}
4247
4248
4249bool
4251 if (c1.fromLane != c2.fromLane) {
4252 return c1.fromLane < c2.fromLane;
4253 }
4254 if (c1.toEdge != c2.toEdge) {
4255 return false; // do not change ordering among toEdges as this is determined by angle in an earlier step
4256 }
4257 return c1.toLane < c2.toLane;
4258}
4259
4260
4261double
4265 } else {
4267 myLanes.back().shape.back() : myLanes[getNumLanes() / 2].shape.back();
4268 //std::cout << getID() << " signalPos=" << mySignalPosition << " laneEnd=" << laneEnd << " toShape=" << myTo->getShape() << " toBorder=" << myToBorder << "\n";
4269 return mySignalPosition.distanceTo2D(laneEnd);
4270 }
4271}
4272
4273
4274int
4275NBEdge::getFirstNonPedestrianLaneIndex(int direction, bool exclusive) const {
4276 assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4277 const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4278 const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4279 for (int i = start; i != end; i += direction) {
4280 // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
4281 // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
4282 if ((exclusive && myLanes[i].permissions != SVC_PEDESTRIAN && myLanes[i].permissions != 0)
4283 || ((myLanes[i].permissions & SVC_PEDESTRIAN) == 0 && myLanes[i].permissions != 0)) {
4284 return i;
4285 }
4286 }
4287 return -1;
4288}
4289
4290int
4291NBEdge::getFirstNonPedestrianNonBicycleLaneIndex(int direction, bool exclusive) const {
4292 assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4293 const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4294 const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4295 for (int i = start; i != end; i += direction) {
4296 // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
4297 // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
4298 SVCPermissions p = myLanes[i].permissions;
4299 if ((exclusive && p != SVC_PEDESTRIAN && p != SVC_BICYCLE && p != (SVC_PEDESTRIAN | SVC_BICYCLE) && p != 0)
4300 || (p == SVCAll || ((p & (SVC_PEDESTRIAN | SVC_BICYCLE)) == 0 && p != 0))) {
4301 return i;
4302 }
4303 }
4304 return -1;
4305}
4306
4307int
4309 for (int i = 0; i < (int)myLanes.size(); i++) {
4310 if (myLanes[i].permissions == permissions) {
4311 return i;
4312 }
4313 }
4314 return -1;
4315}
4316
4317int
4319 assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4320 const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4321 const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4322 for (int i = start; i != end; i += direction) {
4323 if (myLanes[i].permissions != 0) {
4324 return i;
4325 }
4326 }
4327 return end - direction;
4328}
4329
4330
4331std::set<SVCPermissions>
4332NBEdge::getPermissionVariants(int iStart, int iEnd) const {
4333 std::set<SVCPermissions> result;
4334 if (iStart < 0 || iStart >= getNumLanes() || iEnd > getNumLanes()) {
4335 throw ProcessError("invalid indices iStart " + toString(iStart) + " iEnd " + toString(iEnd) + " for edge with " + toString(getNumLanes()) + " lanes.");
4336 }
4337 for (int i = iStart; i < iEnd; ++i) {
4338 result.insert(getPermissions(i));
4339 }
4340 return result;
4341}
4342
4343int
4345 int result = 0;
4346 for (const Lane& lane : myLanes) {
4347 if ((lane.permissions & permissions) == permissions) {
4348 result++;
4349 }
4350 }
4351 return result;
4352}
4353
4354bool
4356 assert(lane >= 0 && lane < getNumLanes());
4357 return myLanes[lane].changeLeft == SVC_UNSPECIFIED ? true : (myLanes[lane].changeLeft & vclass) == vclass;
4358}
4359
4360bool
4362 assert(lane >= 0 && lane < getNumLanes());
4363 return myLanes[lane].changeRight == SVC_UNSPECIFIED ? true : (myLanes[lane].changeRight & vclass) == vclass;
4364}
4365
4366double
4368 double angle = getAngleAtNode(node) + (getFromNode() == node ? 180.0 : 0.0);
4369 if (angle < 0) {
4370 angle += 360.0;
4371 }
4372 if (angle >= 360) {
4373 angle -= 360.0;
4374 }
4375 if (gDebugFlag1) {
4376 std::cout << getID() << " angle=" << getAngleAtNode(node) << " convAngle=" << angle << "\n";
4377 }
4378 return angle;
4379}
4380
4381
4384 int index = getFirstNonPedestrianLaneIndex(direction);
4385 if (index < 0) {
4386 throw ProcessError(TLF("Edge % allows pedestrians on all lanes", getID()));
4387 }
4388 return myLanes[index];
4389}
4390
4391std::string
4393 // see IntermodalEdge::getSidewalk()
4394 for (int i = 0; i < (int)myLanes.size(); i++) {
4395 if (myLanes[i].permissions == SVC_PEDESTRIAN) {
4396 return getLaneID(i);
4397 }
4398 }
4399 for (int i = 0; i < (int)myLanes.size(); i++) {
4400 if ((myLanes[i].permissions & SVC_PEDESTRIAN) != 0) {
4401 return getLaneID(i);
4402 }
4403 }
4404 return getLaneID(0);
4405}
4406
4407void
4408NBEdge::addSidewalk(double width) {
4410}
4411
4412
4413void
4414NBEdge::restoreSidewalk(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4415 restoreRestrictedLane(SVC_PEDESTRIAN, oldLanes, oldGeometry, oldConnections);
4416}
4417
4418
4419void
4420NBEdge::addBikeLane(double width) {
4422}
4423
4424
4425void
4426NBEdge::restoreBikelane(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4427 restoreRestrictedLane(SVC_BICYCLE, oldLanes, oldGeometry, oldConnections);
4428}
4429
4430bool
4432 for (const Lane& lane : myLanes) {
4433 if (lane.permissions == vclass) {
4434 return true;
4435 }
4436 }
4437 return false;
4438}
4439
4440
4441void
4443 if (hasRestrictedLane(vclass)) {
4444 WRITE_WARNINGF(TL("Edge '%' already has a dedicated lane for %s. Not adding another one."), getID(), toString(vclass));
4445 return;
4446 }
4448 myGeom.move2side(width / 2);
4449 }
4450 // disallow pedestrians on all lanes to ensure that sidewalks are used and
4451 // crossings can be guessed
4452 disallowVehicleClass(-1, vclass);
4453 // don't create a restricted vehicle lane to the right of a sidewalk
4454 const int newIndex = (vclass != SVC_PEDESTRIAN && myLanes[0].permissions == SVC_PEDESTRIAN) ? 1 : 0;
4455 // add new lane
4456 myLanes.insert(myLanes.begin() + newIndex, Lane(this, myLanes[0].getParameter(SUMO_PARAM_ORIGID)));
4457 myLanes[newIndex].permissions = vclass;
4458 myLanes[newIndex].width = fabs(width);
4459 // shift outgoing connections to the left
4460 for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
4461 Connection& c = *it;
4462 if (c.fromLane >= newIndex) {
4463 c.fromLane += 1;
4464 }
4465 }
4466 // shift incoming connections to the left
4467 const EdgeVector& incoming = myFrom->getIncomingEdges();
4468 for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4469 (*it)->shiftToLanesToEdge(this, 1);
4470 }
4474}
4475
4476
4477void
4478NBEdge::restoreRestrictedLane(SUMOVehicleClass vclass, std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4479 // check that previously lane was transformed
4480 if (myLanes[0].permissions != vclass) {
4481 WRITE_WARNINGF(TL("Edge '%' doesn't have a dedicated lane for %s. Cannot be restored."), getID(), toString(vclass));
4482 return;
4483 }
4484 // restore old values
4485 myGeom = oldGeometry;
4486 myLanes = oldLanes;
4487 myConnections = oldConnections;
4488 // shift incoming connections to the right
4489 const EdgeVector& incoming = myFrom->getIncomingEdges();
4490 for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4491 (*it)->shiftToLanesToEdge(this, 0);
4492 }
4493 // Shift TL conections
4497}
4498
4499
4500void
4503 for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
4504 if ((*it).toEdge == to && (*it).toLane >= 0) {
4505 (*it).toLane += laneOff;
4506 }
4507 }
4508}
4509
4510
4511void
4514 const int i = (node == myTo ? -1 : 0);
4515 const int i2 = (node == myTo ? 0 : -1);
4516 const double dist = myGeom[i].distanceTo2D(node->getPosition());
4517 const double neededOffset = getTotalWidth() / 2;
4518 const double dist2 = MIN2(myGeom.distance2D(other->getGeometry()[i2]),
4519 other->getGeometry().distance2D(myGeom[i]));
4520 const double neededOffset2 = neededOffset + (other->getTotalWidth()) / 2;
4521 if (dist < neededOffset && dist2 < neededOffset2) {
4522 PositionVector tmp = myGeom;
4523 // @note this doesn't work well for vissim networks
4524 //tmp.move2side(MIN2(neededOffset - dist, neededOffset2 - dist2));
4525 try {
4526 tmp.move2side(neededOffset - dist);
4527 myGeom[i] = tmp[i];
4528 } catch (InvalidArgument&) {
4529 WRITE_WARNINGF(TL("Could not avoid overlapping shape at node '%' for edge '%'."), node->getID(), getID());
4530 }
4531 }
4532 }
4533}
4534
4535
4538 if (myLoadedLength > 0) {
4540 } else {
4541 return myGeom.positionAtOffset(offset);
4542 }
4543}
4544
4545
4546double
4548 double result = getLoadedLength();
4549 if (OptionsCont::getOptions().getBool("no-internal-links") && !hasLoadedLength()) {
4550 // use length to junction center even if a modified geometry was given
4552 geom.push_back_noDoublePos(getToNode()->getCenter());
4553 geom.push_front_noDoublePos(getFromNode()->getCenter());
4554 result = geom.length();
4555 }
4556 double avgEndOffset = 0;
4557 for (const Lane& lane : myLanes) {
4558 avgEndOffset += lane.endOffset;
4559 }
4560 if (isBidiRail()) {
4561 avgEndOffset += myPossibleTurnDestination->getEndOffset();
4562 }
4563 avgEndOffset /= (double)myLanes.size();
4564 return MAX2(result - avgEndOffset, POSITION_EPS);
4565}
4566
4567
4568void
4569NBEdge::setOrigID(const std::string origID, const bool append, const int laneIdx) {
4570 if (laneIdx == -1) {
4571 for (int i = 0; i < (int)myLanes.size(); i++) {
4572 setOrigID(origID, append, i);
4573 }
4574 } else {
4575 if (origID != "") {
4576 if (append) {
4577 std::vector<std::string> oldIDs = StringTokenizer(myLanes[laneIdx].getParameter(SUMO_PARAM_ORIGID)).getVector();
4578 if (std::find(oldIDs.begin(), oldIDs.end(), origID) == oldIDs.end()) {
4579 oldIDs.push_back(origID);
4580 }
4581 myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, toString(oldIDs));
4582 } else {
4583 myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, origID);
4584 }
4585 } else {
4586 // do not record empty origID parameter
4587 myLanes[laneIdx].unsetParameter(SUMO_PARAM_ORIGID);
4588 }
4589 }
4590}
4591
4592
4593const EdgeVector&
4595 // @todo cache successors instead of recomputing them every time
4596 mySuccessors.clear();
4597 //std::cout << "getSuccessors edge=" << getID() << " svc=" << toString(vClass) << " cons=" << myConnections.size() << "\n";
4598 for (const Connection& con : myConnections) {
4599 if (con.fromLane >= 0 && con.toLane >= 0 && con.toEdge != nullptr &&
4600 (vClass == SVC_IGNORING || (getPermissions(con.fromLane)
4601 & con.toEdge->getPermissions(con.toLane) & vClass) != 0)
4602 && std::find(mySuccessors.begin(), mySuccessors.end(), con.toEdge) == mySuccessors.end()) {
4603 mySuccessors.push_back(con.toEdge);
4604 //std::cout << " succ=" << con.toEdge->getID() << "\n";
4605 }
4606 }
4607 return mySuccessors;
4608}
4609
4610
4613 // @todo cache successors instead of recomputing them every time
4614 myViaSuccessors.clear();
4615 for (const Connection& con : myConnections) {
4616 std::pair<const NBEdge*, const Connection*> pair(con.toEdge, nullptr);
4617 // special case for Persons in Netedit
4618 if (vClass == SVC_PEDESTRIAN) {
4619 myViaSuccessors.push_back(pair); // Pedestrians have complete freedom of movement in all sucessors
4620 } else if ((con.fromLane >= 0) && (con.toLane >= 0) &&
4621 (con.toEdge != nullptr) &&
4622 ((getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane) & vClass) == vClass)) {
4623 // ignore duplicates
4624 if (con.getLength() > 0) {
4625 pair.second = &con;
4626 }
4627 myViaSuccessors.push_back(pair);
4628 }
4629 }
4630 return myViaSuccessors;
4631}
4632
4633
4634void
4635NBEdge::debugPrintConnections(bool outgoing, bool incoming) const {
4636 if (outgoing) {
4637 for (const Connection& c : myConnections) {
4638 std::cout << " " << getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
4639 }
4640 }
4641 if (incoming) {
4642 for (NBEdge* inc : myFrom->getIncomingEdges()) {
4643 for (Connection& c : inc->myConnections) {
4644 if (c.toEdge == this) {
4645 std::cout << " " << inc->getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
4646 }
4647 }
4648 }
4649 }
4650}
4651
4652
4653int
4654NBEdge::getLaneIndexFromLaneID(const std::string laneID) {
4655 return StringUtils::toInt(laneID.substr(laneID.rfind("_") + 1));
4656}
4657
4658bool
4660 bool haveJoined = false;
4661 int i = 0;
4662 while (i < getNumLanes() - 1) {
4663 if ((getPermissions(i) == perms) && (getPermissions(i + 1) == perms)) {
4664 const double newWidth = getLaneWidth(i) + getLaneWidth(i + 1);
4665 const std::string newType = myLanes[i].type + "|" + myLanes[i + 1].type;
4666 deleteLane(i, false, true);
4667 setLaneWidth(i, newWidth);
4668 setLaneType(i, newType);
4669 haveJoined = true;
4670 } else {
4671 i++;
4672 }
4673 }
4674 return haveJoined;
4675}
4676
4677
4680 EdgeVector result;
4681 for (NBEdge* edge : edges) {
4682 if ((edge->getPermissions() & permissions) != 0) {
4683 result.push_back(edge);
4684 }
4685 }
4686 return result;
4687}
4688
4689NBEdge*
4691 EdgeVector cands = filterByPermissions(myTo->getOutgoingEdges(), permissions);
4692 if (cands.size() == 0) {
4693 return nullptr;
4694 }
4695 sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this));
4696 NBEdge* best = cands.front();
4697 if (isTurningDirectionAt(best)) {
4698 return nullptr;
4699 } else {
4700 return best;
4701 }
4702}
4703
4704NBEdge*
4706 EdgeVector cands = filterByPermissions(myFrom->getIncomingEdges(), permissions);
4707 if (cands.size() == 0) {
4708 return nullptr;
4709 }
4710 sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this, false));
4711 NBEdge* best = cands.front();
4712 if (best->isTurningDirectionAt(this)) {
4713 return nullptr;
4714 } else {
4715 return best;
4716 }
4717}
4718
4719
4720NBEdge*
4722 NBEdge* opposite = nullptr;
4723 if (getNumLanes() > 0) {
4724 NBEdge::Lane& lastLane = myLanes.back();
4725 const double lastWidth = getLaneWidth(getNumLanes() - 1);
4726 if (lastLane.oppositeID == "" || reguess) {
4727 for (NBEdge* cand : getToNode()->getOutgoingEdges()) {
4728 if (cand->getToNode() == getFromNode() && !cand->getLanes().empty()) {
4729 const double lastWidthCand = cand->getLaneWidth(cand->getNumLanes() - 1);
4730 // in sharp corners, the difference may be higher
4731 // factor (sqrt(2) for 90 degree corners
4732 const double threshold = 1.42 * 0.5 * (lastWidth + lastWidthCand) + 0.5;
4733 const double distance = VectorHelper<double>::maxValue(lastLane.shape.distances(cand->getLanes().back().shape));
4734 //std::cout << " distance=" << distance << " threshold=" << threshold << " distances=" << toString(lastLane.shape.distances(cand->getLanes().back().shape)) << "\n";
4735 if (distance < threshold) {
4736 opposite = cand;
4737 }
4738 }
4739 }
4740 if (opposite != nullptr) {
4741 lastLane.oppositeID = opposite->getLaneID(opposite->getNumLanes() - 1);
4742 }
4743 }
4744 }
4745 return opposite;
4746}
4747
4748double
4749NBEdge::getDistancAt(double pos) const {
4750 // negative values of myDistances indicate descending kilometrage
4751 return fabs(myDistance + pos);
4752}
4753
4754/****************************************************************************/
#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:268
#define WRITE_MESSAGE(msg)
Definition: MsgHandler.h:269
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:267
#define TL(string)
Definition: MsgHandler.h:284
#define TLF(string,...)
Definition: MsgHandler.h:285
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
#define SOFT_ASSERT(expr)
define SOFT_ASSERT raise an assertion in debug mode everywhere except on the windows test server
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:952
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:4192
double getLaneSpeed(int lane) const
get lane speed
Definition: NBEdge.cpp:2126
NBEdge * guessOpposite(bool reguess=false)
set oppositeID and return opposite edge if found
Definition: NBEdge.cpp:4721
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:4206
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:4215
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:1276
double getDistancAt(double pos) const
get distance at the given offset
Definition: NBEdge.cpp:4749
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:4178
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:4367
void addBikeLane(double width)
add a bicycle lane of the given width and shift existing connctions
Definition: NBEdge.cpp:4420
bool expandableBy(NBEdge *possContinuation, std::string &reason) const
Check if Node is expandable.
Definition: NBEdge.cpp:3676
double getLaneFriction(int lane) const
get lane friction of specified lane
Definition: NBEdge.cpp:2132
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:4130
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:2378
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:3640
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:1245
NBEdge()
constructor for dummy edge
Definition: NBEdge.cpp:355
void divideOnEdges(const EdgeVector *outgoing)
divides the lanes on the outgoing edges
Definition: NBEdge.cpp:3057
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:3658
double buildInnerEdges(const NBNode &n, int noInternalNoSplits, int &linkIndex, int &splitIndex)
Definition: NBEdge.cpp:1620
static const double UNSPECIFIED_FRICTION
unspecified lane friction
Definition: NBEdge.h:350
void incLaneNo(int by)
increment lane
Definition: NBEdge.cpp:3911
static EdgeVector filterByPermissions(const EdgeVector &edges, SVCPermissions permissions)
return only those edges that permit at least one of the give permissions
Definition: NBEdge.cpp:4679
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:1233
void addLane(int index, bool recomputeShape, bool recomputeConnections, bool shiftIndices)
add lane
Definition: NBEdge.cpp:3867
bool hasLaneSpecificSpeed() const
whether lanes differ in speed
Definition: NBEdge.cpp:2368
void setAverageLengthWithOpposite(double val)
patch average lane length in regard to the opposite edge
Definition: NBEdge.cpp:4235
void disallowVehicleClass(int lane, SUMOVehicleClass vclass)
set disallowed class for the given lane or for all lanes if -1 is given
Definition: NBEdge.cpp:3974
double getShapeStartAngle() const
Returns the angle at the start of the edge.
Definition: NBEdge.cpp:2327
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:3427
static bool connections_sorter(const Connection &c1, const Connection &c2)
connections_sorter sort by fromLane, toEdge and toLane
Definition: NBEdge.cpp:4250
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:2343
double myTotalAngle
Definition: NBEdge.h:1734
LaneSpreadFunction getLaneSpreadFunction() const
Returns how this edge's lanes' lateral offset is computed.
Definition: NBEdge.cpp:946
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:4594
std::vector< LinkDirection > decodeTurnSigns(int turnSigns)
decode bitset
Definition: NBEdge.cpp:2584
void dismissVehicleClassInformation()
dimiss vehicle class information
Definition: NBEdge.cpp:4241
bool computeEdge2Edges(bool noLeftMovers)
computes the edge (step1: computation of approached edges)
Definition: NBEdge.cpp:2493
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:1592
void updateChangeRestrictions(SVCPermissions ignoring)
modify all existing restrictions on lane changing
Definition: NBEdge.cpp:2144
void restoreBikelane(std::vector< NBEdge::Lane > oldLanes, PositionVector oldGeometry, std::vector< NBEdge::Connection > oldConnections)
restore an previously added BikeLane
Definition: NBEdge.cpp:4426
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:4690
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:4414
bool addEdge2EdgeConnection(NBEdge *dest, bool overrideRemoval=false, SVCPermissions permission=SVC_UNSPECIFIED)
Adds a connection to another edge.
Definition: NBEdge.cpp:1030
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:1065
void divideSelectedLanesOnEdges(const EdgeVector *outgoing, const std::vector< int > &availableLanes)
divide selected lanes on edges
Definition: NBEdge.cpp:3147
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:4100
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:2421
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:4275
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:4501
void checkGeometry(const double maxAngle, const double minRadius, bool fix, bool silent)
Check the angles of successive geometry segments.
Definition: NBEdge.cpp:984
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:3859
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:4016
bool computeLanes2Edges()
computes the edge, step2: computation of which lanes approach the edges)
Definition: NBEdge.cpp:2549
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:4705
void remapConnections(const EdgeVector &incoming)
Remaps the connection in a way that allows the removal of it.
Definition: NBEdge.cpp:1364
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:4318
bool allowsChangingRight(int lane, SUMOVehicleClass vclass) const
Returns whether the given vehicle class may change left from this lane.
Definition: NBEdge.cpp:4361
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:4001
void resetLaneShapes()
reset lane shapes to what they would be before cutting with the junction shapes
Definition: NBEdge.cpp:2138
bool setControllingTLInformation(const NBConnection &c, const std::string &tlID)
Returns if the link could be set as to be controlled.
Definition: NBEdge.cpp:3569
bool bothLeftTurns(LinkDirection dir, const NBEdge *otherFrom, LinkDirection dir2) const
determine conflict between opposite left turns
Definition: NBEdge.cpp:2029
void setAcceleration(int lane, bool accelRamp)
marks one lane as acceleration lane
Definition: NBEdge.cpp:4162
const StopOffset & getEdgeStopOffset() const
Returns the stopOffset to the end of the edge.
Definition: NBEdge.cpp:4068
NBNode * tryGetNodeAtPosition(double pos, double tolerance=5.0) const
Returns the node at the given edges length (using an epsilon)
Definition: NBEdge.cpp:3520
void setLaneSpreadFunction(LaneSpreadFunction spread)
(Re)sets how the lanes lateral offset shall be computed
Definition: NBEdge.cpp:940
void clearControllingTLInformation()
clears tlID for all connections
Definition: NBEdge.cpp:3632
bool isTurningDirectionAt(const NBEdge *const edge) const
Returns whether the given edge is the opposite direction to this edge.
Definition: NBEdge.cpp:3507
void addStraightConnections(const EdgeVector *outgoing, const std::vector< int > &availableLanes, const std::vector< int > &priorities)
add some straight connections
Definition: NBEdge.cpp:3252
bool hasLaneSpecificPermissions() const
whether lanes differ in allowed vehicle classes
Definition: NBEdge.cpp:2354
bool needsLaneSpecificOutput() const
whether at least one lane has values differing from the edges values
Definition: NBEdge.cpp:2476
void computeAngle()
computes the angle of this edge and stores it in myAngle
Definition: NBEdge.cpp:2229
std::vector< Connection > getConnectionsFromLane(int lane, NBEdge *to=nullptr, int toLane=-1) const
Returns connections from a given lane.
Definition: NBEdge.cpp:1219
bool isBidiEdge(bool checkPotential=false) const
whether this edge is part of a bidirectional edge pair
Definition: NBEdge.cpp:740
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:4408
bool hasSignalisedConnectionTo(const NBEdge *const e) const
Check if edge has signalised connections.
Definition: NBEdge.cpp:3833
std::vector< Lane > myLanes
Lane information.
Definition: NBEdge.h:1790
int getNumLanes() const
Returns the number of lanes.
Definition: NBEdge.h:510
bool hasAccelLane() const
whether one of the lanes is an acceleration lane
Definition: NBEdge.cpp:2434
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:1965
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:4146
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:962
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:4659
void resetNodeBorder(const NBNode *node)
Definition: NBEdge.cpp:718
void markAsInLane2LaneState()
mark edge as in lane to state lane
Definition: NBEdge.cpp:3954
bool mayBeTLSControlled(int fromLane, NBEdge *toEdge, int toLane) const
return true if certain connection must be controlled by TLS
Definition: NBEdge.cpp:3558
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:4442
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:1380
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:4383
void invalidateConnections(bool reallowSetting=false)
invalidate current connections of edge
Definition: NBEdge.cpp:1461
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:3334
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:4053
PositionVector cutAtIntersection(const PositionVector &old) const
cut shape at the intersection shapes
Definition: NBEdge.cpp:772
Position geometryPositionAtOffset(double offset) const
return position taking into account loaded length
Definition: NBEdge.cpp:4537
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:1583
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:4037
void allowVehicleClass(int lane, SUMOVehicleClass vclass)
set allowed class for the given lane or for all lanes if -1 is given
Definition: NBEdge.cpp:3961
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:1263
double getMaxLaneOffset()
get max lane offset
Definition: NBEdge.cpp:3552
void deleteLane(int index, bool recompute, bool shiftIndices)
delete lane
Definition: NBEdge.cpp:3922
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:4344
const NBNode * mySignalNode
Definition: NBEdge.h:1809
bool hasLaneSpecificWidth() const
whether lanes differ in width
Definition: NBEdge.cpp:2388
void moveConnectionToRight(int lane)
Definition: NBEdge.cpp:1607
std::set< SVCPermissions > getPermissionVariants(int iStart, int iEnd) const
return all permission variants within the specified lane range [iStart, iEnd[
Definition: NBEdge.cpp:4332
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:3534
std::string getLaneID(int lane) const
get lane ID
Definition: NBEdge.cpp:3853
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:874
std::string getSidewalkID()
get the lane id for the canonical sidewalk lane
Definition: NBEdge.cpp:4392
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:1338
void computeLaneShapes()
compute lane shapes
Definition: NBEdge.cpp:2165
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:2102
int getSpecialLane(SVCPermissions permissions) const
return index of the first lane that allows the given permissions
Definition: NBEdge.cpp:4308
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:1118
bool hasLaneSpecificEndOffset() const
whether lanes differ in offset
Definition: NBEdge.cpp:2410
int getJunctionPriority(const NBNode *const node) const
Returns the junction priority (normalised for the node currently build)
Definition: NBEdge.cpp:2048
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:1313
const ConstRouterEdgePairVector & getViaSuccessors(SUMOVehicleClass vClass=SVC_IGNORING) const
Returns the following edges for the given vClass.
Definition: NBEdge.cpp:4612
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:4170
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:1352
bool applyTurnSigns()
apply loaded turn sign information
Definition: NBEdge.cpp:2596
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:2038
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:3987
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:2085
NBEdge * getTurnDestination(bool possibleDestination=false) const
Definition: NBEdge.cpp:3844
void shiftPositionAtNode(NBNode *node, NBEdge *opposite)
shift geometry at the given node to avoid overlap
Definition: NBEdge.cpp:4512
double getAngleAtNode(const NBNode *const node) const
Returns the angle of the edge's geometry at the given node.
Definition: NBEdge.cpp:2074
bool hasLaneSpecificType() const
whether lanes differ in type
Definition: NBEdge.cpp:2399
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:4262
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:2117
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:833
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:1925
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:4431
void copyConnectionsFrom(NBEdge *src)
copy connections from antoher edge
Definition: NBEdge.cpp:1576
const StopOffset & getLaneStopOffset(int lane) const
Returns the stop offset to the specified lane's end.
Definition: NBEdge.cpp:4074
void debugPrintConnections(bool outgoing=true, bool incoming=false) const
debugging helper to print all connections
Definition: NBEdge.cpp:4635
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:1473
bool lanesWereAssigned() const
Check if lanes were assigned.
Definition: NBEdge.cpp:3546
void restoreRestrictedLane(SUMOVehicleClass vclass, std::vector< NBEdge::Lane > oldLanes, PositionVector oldGeometry, std::vector< NBEdge::Connection > oldConnections)
restore a restricted lane
Definition: NBEdge.cpp:4478
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:757
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:4084
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:1358
bool recheckLanes()
recheck whether all lanes within the edge are all right and optimises the connections once again
Definition: NBEdge.cpp:2781
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:1101
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:4569
PositionVector computeLaneShape(int lane, double offset) const
Computes the shape for the given lane.
Definition: NBEdge.cpp:2217
bool allowsChangingLeft(int lane, SUMOVehicleClass vclass) const
Returns whether the given vehicle class may change left from this lane.
Definition: NBEdge.cpp:4355
static int getLaneIndexFromLaneID(const std::string laneID)
Definition: NBEdge.cpp:4654
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:1257
bool hasCustomLaneShape() const
whether one of the lanes has a custom shape
Definition: NBEdge.cpp:2445
bool hasLaneParams() const
whether one of the lanes has parameters set
Definition: NBEdge.cpp:2456
const PositionVector & getLaneShape(int i) const
Returns the shape of the nth lane.
Definition: NBEdge.cpp:934
double getShapeEndAngle() const
Returns the angle at the end of the edge.
Definition: NBEdge.cpp:2335
bool prohibitsChanging() const
whether one of the lanes prohibits lane changing
Definition: NBEdge.cpp:2466
void setLoadedLength(double val)
set loaded length
Definition: NBEdge.cpp:4230
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:3942
NBNode * myFrom
The source and the destination node.
Definition: NBEdge.h:1722
void append(NBEdge *continuation)
append another edge
Definition: NBEdge.cpp:3780
void setJunctionPriority(const NBNode *const node, int prio)
Sets the junction priority of the edge.
Definition: NBEdge.cpp:2058
double getFinalLength() const
get length that will be assigned to the lanes in the final network
Definition: NBEdge.cpp:4547
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:1325
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:4291
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:2306
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:3816
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:2136
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:2117
bool mergeConflict(const NBEdge *from, const NBEdge::Connection &con, const NBEdge *prohibitorFrom, const NBEdge::Connection &prohibitorCon, bool foes) const
whether multple connections from the same edge target the same lane
Definition: NBNode.cpp:2062
std::vector< Crossing * > getCrossings() const
return this junctions pedestrian crossings
Definition: NBNode.cpp:2896
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:2562
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:2146
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:3595
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:143
Base class for objects which have an id.
Definition: Named.h:54
std::string myID
The name of the object.
Definition: Named.h:125
static std::string getIDSecure(const T *obj, const std::string &fallBack="NULL")
get an identifier for Named-like object which may be Null
Definition: Named.h:67
const std::string & getID() const
Returns the id.
Definition: Named.h:74
A storage for options typed value containers)
Definition: OptionsCont.h:89
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
int getInt(const std::string &name) const
Returns the int-value of the named option (only for Option_Integer)
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:59
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const
Returns the value for a given key.
const Parameterised::Map & getParametersMap() const
Returns the inner key/value map.
virtual void setParameter(const std::string &key, const std::string &value)
Sets a parameter.
void updateParameters(const Parameterised::Map &mapArg)
Adds or updates all given parameters from the map.
bool knowsParameter(const std::string &key) const
Returns whether the parameter is known.
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
static const Position INVALID
used to indicate that a position is valid
Definition: Position.h: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