1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    NBEdge.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Sascha Krieg
14 /// @author  Michael Behrisch
15 /// @author  Laura Bieker
16 /// @author  Leonhard Luecken
17 /// @date    Tue, 20 Nov 2001
18 /// @version $Id$
19 ///
20 // Methods for the representation of a single edge
21 /****************************************************************************/
22 
23 
24 // ===========================================================================
25 // included modules
26 // ===========================================================================
27 #include <config.h>
28 
29 #include <vector>
30 #include <string>
31 #include <algorithm>
32 #include <cmath>
33 #include <iomanip>
34 #include <utils/common/MsgHandler.h>
35 #include <utils/common/StringUtils.h>
36 #include <utils/common/ToString.h>
37 #include <utils/common/UtilExceptions.h>
38 #include <utils/common/StdDefs.h>
39 #include <utils/geom/GeomHelper.h>
40 #include <utils/options/OptionsCont.h>
41 #include "NBEdgeCont.h"
42 #include "NBNode.h"
43 #include "NBNodeCont.h"
44 #include "NBContHelper.h"
45 #include "NBHelpers.h"
46 #include "NBTrafficLightDefinition.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 DEBUGCOND (getID() == "71746014#2")
56 //#define DEBUGCOND (getID() == "22762377#1" || getID() == "146511467")
57 #define DEBUGCOND2(obj) ((obj != 0 && (obj)->getID() == "71746014#2"))
58 
59 // ===========================================================================
60 // static members
61 // ===========================================================================
62 const double NBEdge::UNSPECIFIED_WIDTH = -1;
63 const double NBEdge::UNSPECIFIED_OFFSET = 0;
64 const double NBEdge::UNSPECIFIED_SPEED = -1;
65 const double NBEdge::UNSPECIFIED_CONTPOS = -1;
66 const double NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE = -1;
67 
68 const double NBEdge::UNSPECIFIED_SIGNAL_OFFSET = -1;
69 const double NBEdge::UNSPECIFIED_LOADED_LENGTH = -1;
70 const double NBEdge::ANGLE_LOOKAHEAD = 10.0;
71 const int NBEdge::UNSPECIFIED_INTERNAL_LANE_INDEX = -1;
72 const bool NBEdge::UNSPECIFIED_CONNECTION_UNCONTROLLED = false;
73 
74 NBEdge NBEdge::DummyEdge;
75 
76 // ===========================================================================
77 // method definitions
78 // ===========================================================================
79 std::string
getInternalLaneID() const80 NBEdge::Connection::getInternalLaneID() const {
81     return id + "_" + toString(internalLaneIndex);
82 }
83 
84 
85 std::string
getDescription(const NBEdge * parent) const86 NBEdge::Connection::getDescription(const NBEdge* parent) const {
87     return Named::getIDSecure(parent) + "_" + toString(fromLane) + "->" + Named::getIDSecure(toEdge) + "_" + toString(toLane);
88 }
89 
90 
Connection(int fromLane_,NBEdge * toEdge_,int toLane_)91 NBEdge::Connection::Connection(int fromLane_, NBEdge* toEdge_, int toLane_) :
92     fromLane(fromLane_),
93     toEdge(toEdge_),
94     toLane(toLane_),
95     tlLinkIndex(-1),
96     mayDefinitelyPass(false),
97     keepClear(true),
98     contPos(UNSPECIFIED_CONTPOS),
99     visibility(UNSPECIFIED_VISIBILITY_DISTANCE),
100     speed(UNSPECIFIED_SPEED),
101     id(toEdge_ == nullptr ? "" : toEdge->getFromNode()->getID()),
102     haveVia(false),
103     internalLaneIndex(UNSPECIFIED_INTERNAL_LANE_INDEX),
104     uncontrolled(false) {
105 }
106 
107 
Connection(int fromLane_,NBEdge * toEdge_,int toLane_,bool mayDefinitelyPass_,bool keepClear_,double contPos_,double visibility_,double speed_,bool haveVia_,bool uncontrolled_,const PositionVector & customShape_)108 NBEdge::Connection::Connection(int fromLane_, NBEdge* toEdge_, int toLane_, bool mayDefinitelyPass_, bool keepClear_, double contPos_,
109                                double visibility_, double speed_, bool haveVia_, bool uncontrolled_, const PositionVector& customShape_) :
110     fromLane(fromLane_),
111     toEdge(toEdge_),
112     toLane(toLane_),
113     tlLinkIndex(-1),
114     mayDefinitelyPass(mayDefinitelyPass_),
115     keepClear(keepClear_),
116     contPos(contPos_),
117     visibility(visibility_),
118     speed(speed_),
119     customShape(customShape_),
120     id(toEdge_ == nullptr ? "" : toEdge->getFromNode()->getID()),
121     vmax(UNSPECIFIED_SPEED),
122     haveVia(haveVia_),
123     internalLaneIndex(UNSPECIFIED_INTERNAL_LANE_INDEX),
124     uncontrolled(uncontrolled_) {
125 }
126 
127 
Lane(NBEdge * e,const std::string & origID_)128 NBEdge::Lane::Lane(NBEdge* e, const std::string& origID_) :
129     speed(e->getSpeed()),
130     permissions(SVCAll),
131     preferred(0),
132     endOffset(e->getEndOffset()),
133     stopOffsets(e->getStopOffsets()),
134     width(e->getLaneWidth()),
135     accelRamp(false),
136     connectionsDone(false) {
137     if (origID_ != "") {
138         setParameter(SUMO_PARAM_ORIGID, origID_);
139     }
140 }
141 
142 
143 /* -------------------------------------------------------------------------
144  * NBEdge::ToEdgeConnectionsAdder-methods
145  * ----------------------------------------------------------------------- */
146 void
execute(const int lane,const int virtEdge)147 NBEdge::ToEdgeConnectionsAdder::execute(const int lane, const int virtEdge) {
148     // check
149     assert((int)myTransitions.size() > virtEdge);
150     // get the approached edge
151     NBEdge* succEdge = myTransitions[virtEdge];
152     std::vector<int> lanes;
153 
154     // check whether the currently regarded, approached edge has already
155     //  a connection starting at the edge which is currently being build
156     std::map<NBEdge*, std::vector<int> >::iterator i = myConnections.find(succEdge);
157     if (i != myConnections.end()) {
158         // if there were already lanes assigned, get them
159         lanes = (*i).second;
160     }
161 
162     // check whether the current lane was already used to connect the currently
163     //  regarded approached edge
164     std::vector<int>::iterator j = std::find(lanes.begin(), lanes.end(), lane);
165     if (j == lanes.end()) {
166         // if not, add it to the list
167         lanes.push_back(lane);
168     }
169     // set information about connecting lanes
170     myConnections[succEdge] = lanes;
171 }
172 
173 
174 
175 /* -------------------------------------------------------------------------
176  * NBEdge::MainDirections-methods
177  * ----------------------------------------------------------------------- */
MainDirections(const EdgeVector & outgoing,NBEdge * parent,NBNode * to,const std::vector<int> & availableLanes)178 NBEdge::MainDirections::MainDirections(const EdgeVector& outgoing, NBEdge* parent, NBNode* to, const std::vector<int>& availableLanes) : myStraightest(-1) {
179     NBContHelper::edge_similar_direction_sorter sorter(parent);
180     const NBEdge* straight = nullptr;
181     for (const NBEdge* const out : outgoing) {
182         const int outPerms = out->getPermissions();
183         for (const int l : availableLanes) {
184             if ((parent->myLanes[l].permissions & outPerms) != 0) {
185                 if (straight == nullptr || sorter(out, straight)) {
186                     straight = out;
187                 }
188                 break;
189             }
190         }
191     }
192     if (straight == nullptr) {
193         return;
194     }
195     myStraightest = (int)std::distance(outgoing.begin(), std::find(outgoing.begin(), outgoing.end(), straight));
196 
197     // check whether the right turn has a higher priority
198     assert(outgoing.size() > 0);
199     const LinkDirection straightestDir = to->getDirection(parent, straight);
200 #ifdef DEBUG_CONNECTION_GUESSING
201     if (DEBUGCOND2(parent)) {
202         std::cout << " MainDirections edge=" << parent->getID() << " straightest=" << straight->getID() << " dir=" << toString(straightestDir) << "\n";
203     }
204 #endif
205     if (NBNode::isTrafficLight(to->getType()) &&
206             (straightestDir == LINKDIR_STRAIGHT || straightestDir == LINKDIR_PARTLEFT || straightestDir == LINKDIR_PARTRIGHT)) {
207         myDirs.push_back(MainDirections::DIR_FORWARD);
208         return;
209     }
210     if (outgoing[0]->getJunctionPriority(to) == 1) {
211         myDirs.push_back(MainDirections::DIR_RIGHTMOST);
212     }
213     // check whether the left turn has a higher priority
214     if (outgoing.back()->getJunctionPriority(to) == 1) {
215         // ok, the left turn belongs to the higher priorised edges on the junction
216         //  let's check, whether it has also a higher priority (lane number/speed)
217         //  than the current
218         if (outgoing.back()->getPriority() > straight->getPriority()) {
219             myDirs.push_back(MainDirections::DIR_LEFTMOST);
220         } else {
221             if (outgoing.back()->getNumLanes() > straight->getNumLanes()) {
222                 myDirs.push_back(MainDirections::DIR_LEFTMOST);
223             }
224         }
225     }
226     // check whether the forward direction has a higher priority
227     // check whether it has a higher priority and is going straight
228     if (straight->getJunctionPriority(to) == 1 && to->getDirection(parent, straight) == LINKDIR_STRAIGHT) {
229         myDirs.push_back(MainDirections::DIR_FORWARD);
230     }
231 }
232 
233 
~MainDirections()234 NBEdge::MainDirections::~MainDirections() {}
235 
236 
237 bool
empty() const238 NBEdge::MainDirections::empty() const {
239     return myDirs.empty();
240 }
241 
242 
243 bool
includes(Direction d) const244 NBEdge::MainDirections::includes(Direction d) const {
245     return std::find(myDirs.begin(), myDirs.end(), d) != myDirs.end();
246 }
247 
248 
249 /* -------------------------------------------------------------------------
250  * NBEdge::connections_relative_edgelane_sorter-methods
251  * ----------------------------------------------------------------------- */
252 int
operator ()(const Connection & c1,const Connection & c2) const253 NBEdge::connections_relative_edgelane_sorter::operator()(const Connection& c1, const Connection& c2) const {
254     if (c1.toEdge != c2.toEdge) {
255         return NBContHelper::relative_outgoing_edge_sorter(myEdge)(c1.toEdge, c2.toEdge);
256     }
257     return c1.toLane < c2.toLane;
258 }
259 
260 
261 /* -------------------------------------------------------------------------
262  * NBEdge-methods
263  * ----------------------------------------------------------------------- */
NBEdge(const std::string & id,NBNode * from,NBNode * to,std::string type,double speed,int nolanes,int priority,double laneWidth,double endOffset,const std::string & streetName,LaneSpreadFunction spread)264 NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to,
265                std::string type, double speed, int nolanes,
266                int priority, double laneWidth, double endOffset,
267                const std::string& streetName,
268                LaneSpreadFunction spread) :
269     Named(StringUtils::convertUmlaute(id)),
270     myStep(INIT),
271     myType(StringUtils::convertUmlaute(type)),
272     myFrom(from), myTo(to),
273     myStartAngle(0), myEndAngle(0), myTotalAngle(0),
274     myPriority(priority), mySpeed(speed),
275     myTurnDestination(nullptr),
276     myPossibleTurnDestination(nullptr),
277     myFromJunctionPriority(-1), myToJunctionPriority(-1),
278     myLaneSpreadFunction(spread), myEndOffset(endOffset),
279     myStopOffsets(),
280     myLaneWidth(laneWidth),
281     myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
282     myAmInnerEdge(false), myAmMacroscopicConnector(false),
283     myStreetName(streetName),
284     mySignalOffset(UNSPECIFIED_SIGNAL_OFFSET),
285     mySignalNode(nullptr),
286     myIndex(-1) {
287     init(nolanes, false, "");
288 }
289 
290 
NBEdge(const std::string & id,NBNode * from,NBNode * to,std::string type,double speed,int nolanes,int priority,double laneWidth,double endOffset,PositionVector geom,const std::string & streetName,const std::string & origID,LaneSpreadFunction spread,bool tryIgnoreNodePositions)291 NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to,
292                std::string type, double speed, int nolanes,
293                int priority, double laneWidth, double endOffset,
294                PositionVector geom,
295                const std::string& streetName,
296                const std::string& origID,
297                LaneSpreadFunction spread, bool tryIgnoreNodePositions) :
298     Named(StringUtils::convertUmlaute(id)),
299     myStep(INIT),
300     myType(StringUtils::convertUmlaute(type)),
301     myFrom(from), myTo(to),
302     myStartAngle(0), myEndAngle(0), myTotalAngle(0),
303     myPriority(priority), mySpeed(speed),
304     myTurnDestination(nullptr),
305     myPossibleTurnDestination(nullptr),
306     myFromJunctionPriority(-1), myToJunctionPriority(-1),
307     myGeom(geom), myLaneSpreadFunction(spread), myEndOffset(endOffset),
308     myStopOffsets(),
309     myLaneWidth(laneWidth),
310     myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
311     myAmInnerEdge(false), myAmMacroscopicConnector(false),
312     myStreetName(streetName),
313     mySignalOffset(UNSPECIFIED_SIGNAL_OFFSET),
314     mySignalNode(nullptr),
315     myIndex(-1) {
316     init(nolanes, tryIgnoreNodePositions, origID);
317 }
318 
319 
NBEdge(const std::string & id,NBNode * from,NBNode * to,const NBEdge * tpl,const PositionVector & geom,int numLanes)320 NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to, const NBEdge* tpl, const PositionVector& geom, int numLanes) :
321     Named(StringUtils::convertUmlaute(id)),
322     myStep(INIT),
323     myType(tpl->getTypeID()),
324     myFrom(from), myTo(to),
325     myStartAngle(0), myEndAngle(0), myTotalAngle(0),
326     myPriority(tpl->getPriority()), mySpeed(tpl->getSpeed()),
327     myTurnDestination(nullptr),
328     myPossibleTurnDestination(nullptr),
329     myFromJunctionPriority(-1), myToJunctionPriority(-1),
330     myGeom(geom),
331     myLaneSpreadFunction(tpl->getLaneSpreadFunction()),
332     myEndOffset(tpl->getEndOffset()),
333     myStopOffsets(tpl->getStopOffsets()),
334     myLaneWidth(tpl->getLaneWidth()),
335     myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
336     myAmInnerEdge(false),
337     myAmMacroscopicConnector(false),
338     myStreetName(tpl->getStreetName()),
339     mySignalOffset(to == tpl->myTo ? tpl->mySignalOffset : UNSPECIFIED_SIGNAL_OFFSET),
340     mySignalNode(to == tpl->myTo ? tpl->mySignalNode : nullptr) {
341     init(numLanes > 0 ? numLanes : tpl->getNumLanes(), myGeom.size() > 0, "");
342     for (int i = 0; i < getNumLanes(); i++) {
343         const int tplIndex = MIN2(i, tpl->getNumLanes() - 1);
344         setSpeed(i, tpl->getLaneSpeed(tplIndex));
345         setPermissions(tpl->getPermissions(tplIndex), i);
346         setLaneWidth(i, tpl->myLanes[tplIndex].width);
347         myLanes[i].updateParameter(tpl->myLanes[tplIndex].getParametersMap());
348         if (to == tpl->myTo) {
349             setEndOffset(i, tpl->myLanes[tplIndex].endOffset);
350             setStopOffsets(i, tpl->myLanes[tplIndex].stopOffsets);
351         }
352     }
353 }
354 
355 
NBEdge()356 NBEdge::NBEdge() :
357     Named("DUMMY") {
358 }
359 
360 void
reinit(NBNode * from,NBNode * to,const std::string & type,double speed,int nolanes,int priority,PositionVector geom,double laneWidth,double endOffset,const std::string & streetName,LaneSpreadFunction spread,bool tryIgnoreNodePositions)361 NBEdge::reinit(NBNode* from, NBNode* to, const std::string& type,
362                double speed, int nolanes, int priority,
363                PositionVector geom, double laneWidth, double endOffset,
364                const std::string& streetName,
365                LaneSpreadFunction spread,
366                bool tryIgnoreNodePositions) {
367     if (myFrom != from) {
368         myFrom->removeEdge(this, false);
369     }
370     if (myTo != to) {
371         myTo->removeEdge(this, false);
372     }
373     myType = StringUtils::convertUmlaute(type);
374     myFrom = from;
375     myTo = to;
376     myPriority = priority;
377     //?myTurnDestination(0),
378     //?myFromJunctionPriority(-1), myToJunctionPriority(-1),
379     myGeom = geom;
380     myLaneSpreadFunction = spread;
381     myLoadedLength = UNSPECIFIED_LOADED_LENGTH;
382     myStreetName = streetName;
383     //?, myAmTurningWithAngle(0), myAmTurningOf(0),
384     //?myAmInnerEdge(false), myAmMacroscopicConnector(false)
385 
386     // preserve lane-specific settings (geometry must be recomputed)
387     // if new lanes are added they copy the values from the leftmost lane (if specified)
388     const std::vector<Lane> oldLanes = myLanes;
389     init(nolanes, tryIgnoreNodePositions, oldLanes.empty() ? "" : oldLanes[0].getParameter(SUMO_PARAM_ORIGID));
390     for (int i = 0; i < (int)nolanes; ++i) {
391         PositionVector newShape = myLanes[i].shape;
392         myLanes[i] = oldLanes[MIN2(i, (int)oldLanes.size() - 1)];
393         myLanes[i].shape = newShape;
394     }
395     // however, if the new edge defaults are explicityly given, they override the old settings
396     if (endOffset != UNSPECIFIED_OFFSET) {
397         setEndOffset(-1, endOffset);
398     }
399     if (laneWidth != UNSPECIFIED_WIDTH) {
400         setLaneWidth(-1, laneWidth);
401     }
402     if (speed != UNSPECIFIED_SPEED) {
403         setSpeed(-1, speed);
404     }
405 }
406 
407 
408 void
reinitNodes(NBNode * from,NBNode * to)409 NBEdge::reinitNodes(NBNode* from, NBNode* to) {
410     // connections may still be valid
411     if (from == nullptr || to == nullptr) {
412         throw ProcessError("At least one of edge's '" + myID + "' nodes is not known.");
413     }
414     if (myFrom != from) {
415         myFrom->removeEdge(this, false);
416     }
417     if (myTo != to) {
418         myTo->removeEdge(this, false);
419     }
420     // remove first from both nodes and then add to the new nodes
421     // (otherwise reversing does not work)
422     if (myFrom != from) {
423         myFrom = from;
424         myFrom->addOutgoingEdge(this);
425     }
426     if (myTo != to) {
427         myTo = to;
428         myTo->addIncomingEdge(this);
429     }
430     computeAngle();
431 }
432 
433 
434 void
init(int noLanes,bool tryIgnoreNodePositions,const std::string & origID)435 NBEdge::init(int noLanes, bool tryIgnoreNodePositions, const std::string& origID) {
436     if (noLanes == 0) {
437         throw ProcessError("Edge '" + myID + "' needs at least one lane.");
438     }
439     if (myFrom == nullptr || myTo == nullptr) {
440         throw ProcessError("At least one of edge's '" + myID + "' nodes is not known.");
441     }
442     if (!SUMOXMLDefinitions::isValidNetID(myID)) {
443         throw ProcessError("Invalid edge id '" + myID + "'.");
444     }
445     // revisit geometry
446     //  should have at least two points at the end...
447     //  and in dome cases, the node positions must be added
448     myGeom.removeDoublePoints();
449     if (!tryIgnoreNodePositions || myGeom.size() < 2) {
450         if (myGeom.size() == 0) {
451             myGeom.push_back(myFrom->getPosition());
452             myGeom.push_back(myTo->getPosition());
453         } else {
454             myGeom.push_back_noDoublePos(myTo->getPosition());
455             myGeom.push_front_noDoublePos(myFrom->getPosition());
456         }
457     }
458     if (myGeom.size() < 2) {
459         myGeom.clear();
460         myGeom.push_back(myFrom->getPosition());
461         myGeom.push_back(myTo->getPosition());
462     }
463     if (myGeom.size() == 2 && myGeom[0] == myGeom[1]) {
464         WRITE_WARNING("Edge's '" + myID + "' from- and to-node are at the same position.");
465         myGeom[1].add(Position(POSITION_EPS, POSITION_EPS));
466     }
467     //
468     myFrom->addOutgoingEdge(this);
469     myTo->addIncomingEdge(this);
470     // prepare container
471     myLength = myFrom->getPosition().distanceTo(myTo->getPosition());
472     assert(myGeom.size() >= 2);
473     if ((int)myLanes.size() > noLanes) {
474         // remove connections starting at the removed lanes
475         for (int lane = noLanes; lane < (int)myLanes.size(); ++lane) {
476             removeFromConnections(nullptr, lane, -1);
477         }
478         // remove connections targeting the removed lanes
479         const EdgeVector& incoming = myFrom->getIncomingEdges();
480         for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
481             for (int lane = noLanes; lane < (int)myLanes.size(); ++lane) {
482                 (*i)->removeFromConnections(this, -1, lane);
483             }
484         }
485     }
486     myLanes.clear();
487     for (int i = 0; i < noLanes; i++) {
488         myLanes.push_back(Lane(this, origID));
489     }
490     computeLaneShapes();
491     computeAngle();
492 
493 #ifdef DEBUG_CONNECTION_GUESSING
494     if (DEBUGCOND) {
495         std::cout << "init edge=" << getID() << "\n";
496         for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
497             std::cout << "  conn " << getID() << "_" << (*i).fromLane << " to " << Named::getIDSecure((*i).toEdge) << "_" << (*i).toLane << "\n";
498         }
499     }
500 #endif
501 }
502 
503 
~NBEdge()504 NBEdge::~NBEdge() {}
505 
506 
507 // -----------  Applying offset
508 void
reshiftPosition(double xoff,double yoff)509 NBEdge::reshiftPosition(double xoff, double yoff) {
510     myGeom.add(xoff, yoff, 0);
511     for (int i = 0; i < (int)myLanes.size(); i++) {
512         myLanes[i].shape.add(xoff, yoff, 0);
513     }
514     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
515         (*i).customShape.add(xoff, yoff, 0);
516     }
517     computeAngle(); // update angles because they are numerically sensitive (especially where based on centroids)
518 }
519 
520 
521 void
mirrorX()522 NBEdge::mirrorX() {
523     myGeom.mirrorX();
524     for (int i = 0; i < (int)myLanes.size(); i++) {
525         myLanes[i].shape.mirrorX();
526     }
527     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
528         (*i).shape.mirrorX();
529         (*i).viaShape.mirrorX();
530     }
531     computeAngle(); // update angles because they are numerically sensitive (especially where based on centroids)
532 }
533 
534 
535 // ----------- Edge geometry access and computation
536 const PositionVector
getInnerGeometry() const537 NBEdge::getInnerGeometry() const {
538     return myGeom.getSubpartByIndex(1, (int)myGeom.size() - 2);
539 }
540 
541 
542 bool
hasDefaultGeometry() const543 NBEdge::hasDefaultGeometry() const {
544     return myGeom.size() == 2 && hasDefaultGeometryEndpoints();
545 }
546 
547 
548 bool
hasDefaultGeometryEndpoints() const549 NBEdge::hasDefaultGeometryEndpoints() const {
550     return myGeom.front().almostSame(myFrom->getPosition(), 0.01)  &&
551            myGeom.back().almostSame(myTo->getPosition(), 0.01);
552 }
553 
554 
555 bool
hasDefaultGeometryEndpointAtNode(const NBNode * node) const556 NBEdge::hasDefaultGeometryEndpointAtNode(const NBNode* node) const {
557     // do not extend past the node position
558     if (node == myFrom) {
559         return myGeom.front() == node->getPosition();
560     } else {
561         assert(node == myTo);
562         return myGeom.back() == node->getPosition();
563     }
564 }
565 
566 void
setGeometry(const PositionVector & s,bool inner)567 NBEdge::setGeometry(const PositionVector& s, bool inner) {
568     Position begin = myGeom.front(); // may differ from node position
569     Position end = myGeom.back(); // may differ from node position
570     myGeom = s;
571     if (inner) {
572         myGeom.insert(myGeom.begin(), begin);
573         myGeom.push_back(end);
574     }
575     computeLaneShapes();
576     computeAngle();
577 }
578 
579 
580 void
extendGeometryAtNode(const NBNode * node,double maxExtent)581 NBEdge::extendGeometryAtNode(const NBNode* node, double maxExtent) {
582     //std::cout << "extendGeometryAtNode edge=" << getID() << " node=" << node->getID() << " nodePos=" << node->getPosition() << " extent=" << maxExtent << " geom=" << myGeom;
583     if (node == myFrom) {
584         myGeom.extrapolate(maxExtent, true);
585         double offset = myGeom.nearest_offset_to_point2D(node->getPosition());
586         //std::cout << " geom2=" << myGeom << " offset=" << offset;
587         if (offset != GeomHelper::INVALID_OFFSET) {
588             myGeom = myGeom.getSubpart2D(MIN2(offset, myGeom.length2D() - 2 * POSITION_EPS), myGeom.length2D());
589         }
590     } else {
591         assert(node == myTo);
592         myGeom.extrapolate(maxExtent, false, true);
593         double offset = myGeom.nearest_offset_to_point2D(node->getPosition());
594         //std::cout << " geom2=" << myGeom << " offset=" << offset;
595         if (offset != GeomHelper::INVALID_OFFSET) {
596             myGeom = myGeom.getSubpart2D(0, MAX2(offset, 2 * POSITION_EPS));
597         }
598     }
599     //std::cout << " geom3=" << myGeom << "\n";
600 }
601 
602 
603 void
shortenGeometryAtNode(const NBNode * node,double reduction)604 NBEdge::shortenGeometryAtNode(const NBNode* node, double reduction) {
605     //std::cout << "shortenGeometryAtNode edge=" << getID() << " node=" << node->getID() << " nodePos=" << node->getPosition() << " reduction=" << reduction << " geom=" << myGeom;
606     reduction = MIN2(reduction, myGeom.length2D() - 2 * POSITION_EPS);
607     if (node == myFrom) {
608         myGeom = myGeom.getSubpart2D(reduction, myGeom.length2D());
609     } else {
610         myGeom = myGeom.getSubpart2D(0, myGeom.length2D() - reduction);
611     }
612     computeLaneShapes();
613     //std::cout << " geom2=" << myGeom << "\n";
614 }
615 
616 
617 void
setNodeBorder(const NBNode * node,const Position & p,const Position & p2,bool rectangularCut)618 NBEdge::setNodeBorder(const NBNode* node, const Position& p, const Position& p2, bool rectangularCut) {
619     PositionVector border;
620     if (rectangularCut) {
621         const double extend = 100;
622         border = myGeom.getOrthogonal(p, extend, node == myTo);
623     } else {
624         border.push_back(p);
625         border.push_back(p2);
626     }
627     if (border.size() == 2) {
628         double edgeWidth = 0;
629         for (int i = 0; i < (int)myLanes.size(); i++) {
630             edgeWidth += getLaneWidth(i);
631         }
632         border.extrapolate2D(getTotalWidth());
633         if (node == myFrom) {
634             myFromBorder = border;
635         } else {
636             assert(node == myTo);
637             myToBorder = border;
638         }
639     }
640 #ifdef DEBUG_NODE_BORDER
641     gDebugFlag1 = DEBUGCOND;
642     if (DEBUGCOND) std::cout << "setNodeBorder edge=" << getID() << " node=" << node->getID()
643                                  << " rect=" << rectangularCut
644                                  << " p=" << p << " p2=" << p2
645                                  << " border=" << border
646                                  << " myGeom=" << myGeom
647                                  << "\n";
648 
649 #endif
650 }
651 
652 
653 const PositionVector&
getNodeBorder(const NBNode * node)654 NBEdge::getNodeBorder(const NBNode* node) {
655     if (node == myFrom) {
656         return myFromBorder;
657     } else {
658         assert(node == myTo);
659         return myToBorder;
660     }
661 }
662 
663 
664 void
resetNodeBorder(const NBNode * node)665 NBEdge::resetNodeBorder(const NBNode* node) {
666     if (node == myFrom) {
667         myFromBorder.clear();
668     } else {
669         assert(node == myTo);
670         myToBorder.clear();
671     }
672 }
673 
674 
675 bool
isBidiRail(bool ignoreSpread) const676 NBEdge::isBidiRail(bool ignoreSpread) const {
677     return (isRailway(getPermissions())
678             && (ignoreSpread || myLaneSpreadFunction == LANESPREAD_CENTER)
679             && myPossibleTurnDestination != nullptr
680             && (ignoreSpread || myPossibleTurnDestination->getLaneSpreadFunction() == LANESPREAD_CENTER)
681             && isRailway(myPossibleTurnDestination->getPermissions())
682             && myPossibleTurnDestination->getGeometry().reverse() == getGeometry());
683 }
684 
685 
686 bool
isRailDeadEnd() const687 NBEdge::isRailDeadEnd() const {
688     if (!isRailway(getPermissions())) {
689         return false;
690     }
691     for (NBEdge* out : myTo->getOutgoingEdges()) {
692         if (isRailway(out->getPermissions()) &&
693                 out != getTurnDestination(true)) {
694             return true;
695         }
696     }
697     return true;
698 }
699 
700 
701 PositionVector
cutAtIntersection(const PositionVector & old) const702 NBEdge::cutAtIntersection(const PositionVector& old) const {
703     PositionVector shape = old;
704     shape = startShapeAt(shape, myFrom, myFromBorder);
705     if (shape.size() < 2) {
706         // only keep the last snippet
707         const double oldLength = old.length();
708         shape = old.getSubpart(oldLength - 2 * POSITION_EPS, oldLength);
709     }
710     shape = startShapeAt(shape.reverse(), myTo, myToBorder).reverse();
711     // sanity checks
712     if (shape.length() < POSITION_EPS) {
713         if (old.length() < 2 * POSITION_EPS) {
714             shape = old;
715         } else {
716             const double midpoint = old.length() / 2;
717             // EPS*2 because otherwhise shape has only a single point
718             shape = old.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
719             assert(shape.size() >= 2);
720             assert(shape.length() > 0);
721         }
722     } else {
723         // @note If the node shapes are overlapping we may get a shape which goes in the wrong direction
724         // in this case the result shape should shortened
725         if (DEG2RAD(135) < fabs(GeomHelper::angleDiff(shape.beginEndAngle(), old.beginEndAngle()))) {
726             // eliminate intermediate points
727             PositionVector tmp;
728             tmp.push_back(shape[0]);
729             tmp.push_back(shape[-1]);
730             // 3D geometry may be messed up since positions were extrapolated rather than interpolated due to cutting in the wrong direction
731             // make the edge level with one of the intersections (try to pick one that needs to be flat as well)
732             if (myTo->geometryLike()) {
733                 tmp[0].setz(myFrom->getPosition().z());
734                 tmp[1].setz(myFrom->getPosition().z());
735             } else {
736                 tmp[0].setz(myTo->getPosition().z());
737                 tmp[1].setz(myTo->getPosition().z());
738             }
739             shape = tmp;
740             if (tmp.length() < POSITION_EPS) {
741                 // fall back to original shape
742                 if (old.length() < 2 * POSITION_EPS) {
743                     shape = old;
744                 } else {
745                     const double midpoint = old.length() / 2;
746                     // EPS*2 because otherwhise shape has only a single point
747                     shape = old.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
748                     assert(shape.size() >= 2);
749                     assert(shape.length() > 0);
750                 }
751             } else {
752                 const double midpoint = shape.length() / 2;
753                 // cut to size and reverse
754                 shape = shape.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
755                 if (shape.length() < POSITION_EPS) {
756                     assert(false);
757                     // the shape has a sharp turn near the midpoint
758                 }
759                 shape = shape.reverse();
760             }
761         }
762     }
763     return shape;
764 }
765 
766 
767 void
computeEdgeShape(double smoothElevationThreshold)768 NBEdge::computeEdgeShape(double smoothElevationThreshold) {
769     if (smoothElevationThreshold > 0 && myGeom.hasElevation()) {
770         PositionVector cut = cutAtIntersection(myGeom);
771         // cutting and patching z-coordinate may cause steep grades which should be smoothed
772         if (!myFrom->geometryLike()) {
773             cut[0].setz(myFrom->getPosition().z());
774             const double d = cut[0].distanceTo2D(cut[1]);
775             const double dZ = fabs(cut[0].z() - cut[1].z());
776             if (dZ / smoothElevationThreshold > d) {
777                 cut = cut.smoothedZFront(MIN2(cut.length2D() / 2, dZ / smoothElevationThreshold));
778             }
779         }
780         if (!myTo->geometryLike()) {
781             cut[-1].setz(myTo->getPosition().z());
782             const double d = cut[-1].distanceTo2D(cut[-2]);
783             const double dZ = fabs(cut[-1].z() - cut[-2].z());
784             if (dZ / smoothElevationThreshold > d) {
785                 cut = cut.reverse().smoothedZFront(MIN2(cut.length2D() / 2, dZ / smoothElevationThreshold)).reverse();
786             }
787         }
788         cut[0] = myGeom[0];
789         cut[-1] = myGeom[-1];
790         if (cut != myGeom) {
791             myGeom = cut;
792             computeLaneShapes();
793         }
794     }
795     for (int i = 0; i < (int)myLanes.size(); i++) {
796         myLanes[i].shape = cutAtIntersection(myLanes[i].shape);
797     }
798     // recompute edge's length as the average of lane lenghts
799     double avgLength = 0;
800     for (int i = 0; i < (int)myLanes.size(); i++) {
801         avgLength += myLanes[i].shape.length();
802     }
803     myLength = avgLength / (double) myLanes.size();
804     computeAngle(); // update angles using the finalized node and lane shapes
805 }
806 
807 
808 PositionVector
startShapeAt(const PositionVector & laneShape,const NBNode * startNode,PositionVector nodeShape)809 NBEdge::startShapeAt(const PositionVector& laneShape, const NBNode* startNode, PositionVector nodeShape) {
810     if (nodeShape.size() == 0) {
811         nodeShape = startNode->getShape();
812         nodeShape.closePolygon();
813     }
814     PositionVector lb = laneShape;
815     lb.extrapolate2D(100.0);
816     if (nodeShape.intersects(laneShape)) {
817         // shape intersects directly
818         std::vector<double> pbv = laneShape.intersectsAtLengths2D(nodeShape);
819         assert(pbv.size() > 0);
820         // ensure that the subpart has at least two points
821         double pb = MIN2(laneShape.length2D() - POSITION_EPS - NUMERICAL_EPS, VectorHelper<double>::maxValue(pbv));
822         if (pb < 0) {
823             return laneShape;
824         }
825         PositionVector ns = laneShape.getSubpart2D(pb, laneShape.length2D());
826         //PositionVector ns = pb < (laneShape.length() - POSITION_EPS) ? laneShape.getSubpart2D(pb, laneShape.length()) : laneShape;
827         if (!startNode->geometryLike() || pb < 1) {
828             // make "real" intersections and small intersections flat
829             ns[0].setz(startNode->getPosition().z());
830         }
831         assert(ns.size() >= 2);
832         return ns;
833     } else if (nodeShape.intersects(lb)) {
834         // extension of first segment intersects
835         std::vector<double> pbv = lb.intersectsAtLengths2D(nodeShape);
836         assert(pbv.size() > 0);
837         double pb = VectorHelper<double>::maxValue(pbv);
838         assert(pb >= 0);
839         PositionVector result = laneShape.getSubpartByIndex(1, (int)laneShape.size() - 1);
840         Position np = lb.positionAtOffset2D(pb);
841         if (!startNode->geometryLike()) {
842             // make "real" intersections flat
843             np.setz(startNode->getPosition().z());
844         }
845         result.push_front_noDoublePos(np);
846         return result;
847         //if (result.size() >= 2) {
848         //    return result;
849         //} else {
850         //    WRITE_WARNING(error + " (resulting shape is too short)");
851         //    return laneShape;
852         //}
853     } else {
854         // could not find proper intersection. Probably the edge is very short
855         // and lies within nodeShape
856         // @todo enable warning WRITE_WARNING(error + " (laneShape lies within nodeShape)");
857         return laneShape;
858     }
859 }
860 
861 
862 const PositionVector&
getLaneShape(int i) const863 NBEdge::getLaneShape(int i) const {
864     return myLanes[i].shape;
865 }
866 
867 
868 void
setLaneSpreadFunction(LaneSpreadFunction spread)869 NBEdge::setLaneSpreadFunction(LaneSpreadFunction spread) {
870     myLaneSpreadFunction = spread;
871 }
872 
873 
874 void
addGeometryPoint(int index,const Position & p)875 NBEdge::addGeometryPoint(int index, const Position& p) {
876     if (index >= 0) {
877         myGeom.insert_noDoublePos(myGeom.begin() + index, p);
878     } else {
879         myGeom.insert_noDoublePos(myGeom.end() + index, p);
880     }
881 }
882 
883 
884 bool
splitGeometry(NBEdgeCont & ec,NBNodeCont & nc)885 NBEdge::splitGeometry(NBEdgeCont& ec, NBNodeCont& nc) {
886     // check whether there any splits to perform
887     if (myGeom.size() < 3) {
888         return false;
889     }
890     // ok, split
891     NBNode* newFrom = myFrom;
892     NBNode* myLastNode = myTo;
893     NBNode* newTo = nullptr;
894     NBEdge* currentEdge = this;
895     for (int i = 1; i < (int) myGeom.size() - 1; i++) {
896         // build the node first
897         if (i != (int)myGeom.size() - 2) {
898             std::string nodename = myID + "_in_between#" + toString(i);
899             if (!nc.insert(nodename, myGeom[i])) {
900                 throw ProcessError("Error on adding in-between node '" + nodename + "'.");
901             }
902             newTo = nc.retrieve(nodename);
903         } else {
904             newTo = myLastNode;
905         }
906         if (i == 1) {
907             currentEdge->myTo->removeEdge(this);
908             currentEdge->myTo = newTo;
909             newTo->addIncomingEdge(currentEdge);
910         } else {
911             std::string edgename = myID + "[" + toString(i - 1) + "]";
912             // @bug lane-specific width, speed, overall offset and restrictions are ignored
913             currentEdge = new NBEdge(edgename, newFrom, newTo, myType, mySpeed, (int) myLanes.size(),
914                                      myPriority, myLaneWidth, UNSPECIFIED_OFFSET, myStreetName, myLaneSpreadFunction);
915             if (!ec.insert(currentEdge, true)) {
916                 throw ProcessError("Error on adding splitted edge '" + edgename + "'.");
917             }
918         }
919         newFrom = newTo;
920     }
921     myGeom.clear();
922     myGeom.push_back(myFrom->getPosition());
923     myGeom.push_back(myTo->getPosition());
924     myStep = INIT;
925     return true;
926 }
927 
928 
929 void
reduceGeometry(const double minDist)930 NBEdge::reduceGeometry(const double minDist) {
931     myGeom.removeDoublePoints(minDist, true);
932 }
933 
934 
935 void
checkGeometry(const double maxAngle,const double minRadius,bool fix)936 NBEdge::checkGeometry(const double maxAngle, const double minRadius, bool fix) {
937     if (myGeom.size() < 3) {
938         return;
939     }
940     //std::cout << "checking geometry of " << getID() << " geometry = " << toString(myGeom) << "\n";
941     std::vector<double> angles; // absolute segment angles
942     //std::cout << "  absolute angles:";
943     for (int i = 0; i < (int)myGeom.size() - 1; ++i) {
944         angles.push_back(myGeom.angleAt2D(i));
945         //std::cout << " " << angles.back();
946     }
947     //std::cout << "\n  relative angles: ";
948     for (int i = 0; i < (int)angles.size() - 1; ++i) {
949         const double relAngle = fabs(GeomHelper::angleDiff(angles[i], angles[i + 1]));
950         //std::cout << relAngle << " ";
951         if (maxAngle > 0 && relAngle > maxAngle) {
952             WRITE_WARNING("Found angle of " + toString(RAD2DEG(relAngle)) + " degrees at edge '" + getID() + "', segment " + toString(i));
953         }
954         if (relAngle < DEG2RAD(1)) {
955             continue;
956         }
957         if (i == 0 || i == (int)angles.size() - 2) {
958             const bool start = i == 0;
959             const double dist = (start ? myGeom[0].distanceTo2D(myGeom[1]) : myGeom[-2].distanceTo2D(myGeom[-1]));
960             const double r = tan(0.5 * (M_PI - relAngle)) * dist;
961             //std::cout << (start ? "  start" : "  end") << " length=" << dist << " radius=" << r << "  ";
962             if (minRadius > 0 && r < minRadius) {
963                 if (fix) {
964                     WRITE_MESSAGE("Removing sharp turn with radius " + toString(r) + " at the " +
965                                   (start ? "start" : "end") + " of edge '" + getID() + "'.");
966                     myGeom.erase(myGeom.begin() + (start ? 1 : i + 1));
967                     checkGeometry(maxAngle, minRadius, fix);
968                     return;
969                 } else {
970                     WRITE_WARNING("Found sharp turn with radius " + toString(r) + " at the " +
971                                   (start ? "start" : "end") + " of edge '" + getID() + "'.");
972                 }
973             }
974         }
975     }
976     //std::cout << "\n";
977 }
978 
979 
980 // ----------- Setting and getting connections
981 bool
addEdge2EdgeConnection(NBEdge * dest)982 NBEdge::addEdge2EdgeConnection(NBEdge* dest) {
983     if (myStep == INIT_REJECT_CONNECTIONS) {
984         return true;
985     }
986     // check whether the node was merged and now a connection between
987     //  not matching edges is tried to be added
988     //  This happens f.e. within the ptv VISSIM-example "Beijing"
989     if (dest != nullptr && myTo != dest->myFrom) {
990         return false;
991     }
992     if (dest == nullptr) {
993         invalidateConnections();
994         myConnections.push_back(Connection(-1, dest, -1));
995     } else if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(dest)) == myConnections.end()) {
996         myConnections.push_back(Connection(-1, dest, -1));
997     }
998     if (myStep < EDGE2EDGES) {
999         myStep = EDGE2EDGES;
1000     }
1001     return true;
1002 }
1003 
1004 
1005 bool
addLane2LaneConnection(int from,NBEdge * dest,int toLane,Lane2LaneInfoType type,bool mayUseSameDestination,bool mayDefinitelyPass,bool keepClear,double contPos,double visibility,double speed,const PositionVector & customShape,bool uncontrolled)1006 NBEdge::addLane2LaneConnection(int from, NBEdge* dest,
1007                                int toLane, Lane2LaneInfoType type,
1008                                bool mayUseSameDestination,
1009                                bool mayDefinitelyPass,
1010                                bool keepClear,
1011                                double contPos,
1012                                double visibility,
1013                                double speed,
1014                                const PositionVector& customShape,
1015                                bool uncontrolled) {
1016     if (myStep == INIT_REJECT_CONNECTIONS) {
1017         return true;
1018     }
1019     // check whether the node was merged and now a connection between
1020     //  not matching edges is tried to be added
1021     //  This happens f.e. within the ptv VISSIM-example "Beijing"
1022     if (myTo != dest->myFrom) {
1023         return false;
1024     }
1025     if (!addEdge2EdgeConnection(dest)) {
1026         return false;
1027     }
1028     return setConnection(from, dest, toLane, type, mayUseSameDestination, mayDefinitelyPass, keepClear, contPos, visibility, speed, customShape, uncontrolled);
1029 }
1030 
1031 
1032 bool
addLane2LaneConnections(int fromLane,NBEdge * dest,int toLane,int no,Lane2LaneInfoType type,bool invalidatePrevious,bool mayDefinitelyPass)1033 NBEdge::addLane2LaneConnections(int fromLane,
1034                                 NBEdge* dest, int toLane,
1035                                 int no, Lane2LaneInfoType type,
1036                                 bool invalidatePrevious,
1037                                 bool mayDefinitelyPass) {
1038     if (invalidatePrevious) {
1039         invalidateConnections(true);
1040     }
1041     bool ok = true;
1042     for (int i = 0; i < no && ok; i++) {
1043         ok &= addLane2LaneConnection(fromLane + i, dest, toLane + i, type, false, mayDefinitelyPass);
1044     }
1045     return ok;
1046 }
1047 
1048 
1049 bool
setConnection(int lane,NBEdge * destEdge,int destLane,Lane2LaneInfoType type,bool mayUseSameDestination,bool mayDefinitelyPass,bool keepClear,double contPos,double visibility,double speed,const PositionVector & customShape,bool uncontrolled)1050 NBEdge::setConnection(int lane, NBEdge* destEdge,
1051                       int destLane, Lane2LaneInfoType type,
1052                       bool mayUseSameDestination,
1053                       bool mayDefinitelyPass,
1054                       bool keepClear,
1055                       double contPos,
1056                       double visibility,
1057                       double speed,
1058                       const PositionVector& customShape,
1059                       bool uncontrolled) {
1060     if (myStep == INIT_REJECT_CONNECTIONS) {
1061         return false;
1062     }
1063     // some kind of a misbehaviour which may occure when the junction's outgoing
1064     //  edge priorities were not properly computed, what may happen due to
1065     //  an incomplete or not proper input
1066     // what happens is that under some circumstances a single lane may set to
1067     //  be approached more than once by the one of our lanes.
1068     //  This must not be!
1069     // we test whether it is the case and do nothing if so - the connection
1070     //  will be refused
1071     //
1072     if (!mayUseSameDestination && hasConnectionTo(destEdge, destLane)) {
1073         return false;
1074     }
1075     if (find_if(myConnections.begin(), myConnections.end(), connections_finder(lane, destEdge, destLane)) != myConnections.end()) {
1076         return true;
1077     }
1078     if ((int)myLanes.size() <= lane || destEdge->getNumLanes() <= (int)destLane) {
1079         // problem might be corrigible in post-processing
1080         WRITE_WARNING("Could not set connection from '" + getLaneID(lane) + "' to '" + destEdge->getLaneID(destLane) + "'.");
1081         return false;
1082     }
1083     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
1084         if ((*i).toEdge == destEdge && ((*i).fromLane == -1 || (*i).toLane == -1)) {
1085             i = myConnections.erase(i);
1086         } else {
1087             ++i;
1088         }
1089     }
1090     myConnections.push_back(Connection(lane, destEdge, destLane));
1091     if (mayDefinitelyPass) {
1092         myConnections.back().mayDefinitelyPass = true;
1093     }
1094     myConnections.back().keepClear = keepClear;
1095     myConnections.back().contPos = contPos;
1096     myConnections.back().visibility = visibility;
1097     myConnections.back().speed = speed;
1098     myConnections.back().customShape = customShape;
1099     myConnections.back().uncontrolled = uncontrolled;
1100     if (type == L2L_USER) {
1101         myStep = LANES2LANES_USER;
1102     } else {
1103         // check whether we have to take another look at it later
1104         if (type == L2L_COMPUTED) {
1105             // yes, the connection was set using an algorithm which requires a recheck
1106             myStep = LANES2LANES_RECHECK;
1107         } else {
1108             // ok, let's only not recheck it if we did no add something that has to be rechecked
1109             if (myStep != LANES2LANES_RECHECK) {
1110                 myStep = LANES2LANES_DONE;
1111             }
1112         }
1113     }
1114     return true;
1115 }
1116 
1117 
1118 std::vector<NBEdge::Connection>
getConnectionsFromLane(int lane,NBEdge * to,int toLane) const1119 NBEdge::getConnectionsFromLane(int lane, NBEdge* to, int toLane) const {
1120     std::vector<NBEdge::Connection> ret;
1121     for (const Connection& c : myConnections) {
1122         if ((lane < 0 || c.fromLane == lane)
1123                 && (to == nullptr || to == c.toEdge)
1124                 && (toLane < 0 || toLane == c.toLane)) {
1125             ret.push_back(c);
1126         }
1127     }
1128     return ret;
1129 }
1130 
1131 
1132 NBEdge::Connection
getConnection(int fromLane,const NBEdge * to,int toLane) const1133 NBEdge::getConnection(int fromLane, const NBEdge* to, int toLane) const {
1134     for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1135         if (
1136             (*i).fromLane == fromLane
1137             && (*i).toEdge == to
1138             && (*i).toLane == toLane) {
1139             return *i;
1140         }
1141     }
1142     throw ProcessError("Connection from " + getID() + "_" + toString(fromLane)
1143                        + " to " + to->getID() + "_" + toString(toLane) + " not found");
1144 }
1145 
1146 NBEdge::Connection&
getConnectionRef(int fromLane,const NBEdge * to,int toLane)1147 NBEdge::getConnectionRef(int fromLane, const NBEdge* to, int toLane) {
1148     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1149         if (
1150             (*i).fromLane == fromLane
1151             && (*i).toEdge == to
1152             && (*i).toLane == toLane) {
1153             return *i;
1154         }
1155     }
1156     throw ProcessError("Connection from " + getID() + "_" + toString(fromLane)
1157                        + " to " + to->getID() + "_" + toString(toLane) + " not found");
1158 }
1159 
1160 
1161 bool
hasConnectionTo(NBEdge * destEdge,int destLane,int fromLane) const1162 NBEdge::hasConnectionTo(NBEdge* destEdge, int destLane, int fromLane) const {
1163     return destEdge != nullptr && find_if(myConnections.begin(), myConnections.end(), connections_toedgelane_finder(destEdge, destLane, fromLane)) != myConnections.end();
1164 }
1165 
1166 
1167 bool
isConnectedTo(const NBEdge * e) const1168 NBEdge::isConnectedTo(const NBEdge* e) const {
1169     if (e == myTurnDestination) {
1170         return true;
1171     }
1172     return
1173         find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(e))
1174         !=
1175         myConnections.end();
1176 
1177 }
1178 
1179 
1180 const EdgeVector*
getConnectedSorted()1181 NBEdge::getConnectedSorted() {
1182     // check whether connections exist and if not, use edges from the node
1183     EdgeVector outgoing;
1184     if (myConnections.size() == 0) {
1185         outgoing = myTo->getOutgoingEdges();
1186     } else {
1187         for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1188             if (find(outgoing.begin(), outgoing.end(), (*i).toEdge) == outgoing.end()) {
1189                 outgoing.push_back((*i).toEdge);
1190             }
1191         }
1192     }
1193     for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
1194         if (it->fromLane < 0 && it->toLane < 0) {
1195             // found an edge that shall not be connected
1196             EdgeVector::iterator forbidden = std::find(outgoing.begin(), outgoing.end(), it->toEdge);
1197             if (forbidden != outgoing.end()) {
1198                 outgoing.erase(forbidden);
1199             }
1200         }
1201     }
1202     // allocate the sorted container
1203     int size = (int) outgoing.size();
1204     EdgeVector* edges = new EdgeVector();
1205     edges->reserve(size);
1206     for (EdgeVector::const_iterator i = outgoing.begin(); i != outgoing.end(); i++) {
1207         NBEdge* outedge = *i;
1208         if (outedge != nullptr && outedge != myTurnDestination) {
1209             edges->push_back(outedge);
1210         }
1211     }
1212     sort(edges->begin(), edges->end(), NBContHelper::relative_outgoing_edge_sorter(this));
1213     return edges;
1214 }
1215 
1216 
1217 EdgeVector
getConnectedEdges() const1218 NBEdge::getConnectedEdges() const {
1219     EdgeVector ret;
1220     for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1221         if (find(ret.begin(), ret.end(), (*i).toEdge) == ret.end()) {
1222             ret.push_back((*i).toEdge);
1223         }
1224     }
1225     return ret;
1226 }
1227 
1228 
1229 EdgeVector
getIncomingEdges() const1230 NBEdge::getIncomingEdges() const {
1231     EdgeVector ret;
1232     const EdgeVector& candidates = myFrom->getIncomingEdges();
1233     for (EdgeVector::const_iterator i = candidates.begin(); i != candidates.end(); i++) {
1234         if ((*i)->isConnectedTo(this)) {
1235             ret.push_back(*i);
1236         }
1237     }
1238     return ret;
1239 }
1240 
1241 
1242 std::vector<int>
getConnectionLanes(NBEdge * currentOutgoing,bool withBikes) const1243 NBEdge::getConnectionLanes(NBEdge* currentOutgoing, bool withBikes) const {
1244     std::vector<int> ret;
1245     if (currentOutgoing != myTurnDestination) {
1246         for (const Connection& c : myConnections) {
1247             if (c.toEdge == currentOutgoing && (withBikes || getPermissions(c.fromLane) != SVC_BICYCLE)) {
1248                 ret.push_back(c.fromLane);
1249             }
1250         }
1251     }
1252     return ret;
1253 }
1254 
1255 
1256 void
sortOutgoingConnectionsByAngle()1257 NBEdge::sortOutgoingConnectionsByAngle() {
1258     sort(myConnections.begin(), myConnections.end(), connections_relative_edgelane_sorter(this));
1259 }
1260 
1261 
1262 void
sortOutgoingConnectionsByIndex()1263 NBEdge::sortOutgoingConnectionsByIndex() {
1264     sort(myConnections.begin(), myConnections.end(), connections_sorter);
1265 }
1266 
1267 
1268 void
remapConnections(const EdgeVector & incoming)1269 NBEdge::remapConnections(const EdgeVector& incoming) {
1270     EdgeVector connected = getConnectedEdges();
1271     for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
1272         NBEdge* inc = *i;
1273         // We have to do this
1274         inc->myStep = EDGE2EDGES;
1275         // add all connections
1276         for (EdgeVector::iterator j = connected.begin(); j != connected.end(); j++) {
1277             inc->addEdge2EdgeConnection(*j);
1278         }
1279         inc->removeFromConnections(this);
1280     }
1281 }
1282 
1283 
1284 void
removeFromConnections(NBEdge * toEdge,int fromLane,int toLane,bool tryLater,const bool adaptToLaneRemoval,const bool keepPossibleTurns)1285 NBEdge::removeFromConnections(NBEdge* toEdge, int fromLane, int toLane, bool tryLater, const bool adaptToLaneRemoval,
1286                               const bool keepPossibleTurns) {
1287     // remove from "myConnections"
1288     const int fromLaneRemoved = adaptToLaneRemoval && fromLane >= 0 ? fromLane : -1;
1289     const int toLaneRemoved = adaptToLaneRemoval && toLane >= 0 ? toLane : -1;
1290     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
1291         Connection& c = *i;
1292         if ((toEdge == nullptr || c.toEdge == toEdge)
1293                 && (fromLane < 0 || c.fromLane == fromLane)
1294                 && (toLane < 0 || c.toLane == toLane)) {
1295             if (myTo->isTLControlled()) {
1296                 std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
1297                 for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
1298                     (*it)->removeConnection(NBConnection(this, c.fromLane, c.toEdge, c.toLane));
1299                 }
1300             }
1301             i = myConnections.erase(i);
1302             tryLater = false;
1303         } else {
1304             if (fromLaneRemoved >= 0 && c.fromLane > fromLaneRemoved) {
1305                 if (myTo->isTLControlled()) {
1306                     std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
1307                     for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
1308                         for (NBConnectionVector::iterator tlcon = (*it)->getControlledLinks().begin(); tlcon != (*it)->getControlledLinks().end(); ++tlcon) {
1309                             NBConnection& tc = *tlcon;
1310                             if (tc.getTo() == c.toEdge && tc.getFromLane() == c.fromLane && tc.getToLane() == c.toLane) {
1311                                 tc.shiftLaneIndex(this, -1);
1312                             }
1313                         }
1314                     }
1315                 }
1316                 c.fromLane--;
1317             }
1318             if (toLaneRemoved >= 0 && c.toLane > toLaneRemoved) {
1319                 c.toLane--;
1320             }
1321             ++i;
1322         }
1323     }
1324     // check whether it was the turn destination
1325     if (myTurnDestination == toEdge && fromLane < 0) {
1326         myTurnDestination = nullptr;
1327     }
1328     if (myPossibleTurnDestination == toEdge && fromLane < 0 && !keepPossibleTurns) {
1329         myPossibleTurnDestination = nullptr;
1330     }
1331     if (tryLater) {
1332         myConnectionsToDelete.push_back(Connection(fromLane, toEdge, toLane));
1333     }
1334 }
1335 
1336 
1337 bool
removeFromConnections(NBEdge::Connection connectionToRemove)1338 NBEdge::removeFromConnections(NBEdge::Connection connectionToRemove) {
1339     // iterate over connections
1340     for (std::vector<Connection>::iterator i = myConnections.begin(); i !=  myConnections.end(); i++) {
1341         if (((*i).toEdge == connectionToRemove.toEdge) && ((*i).fromLane == connectionToRemove.fromLane) && ((*i).toLane == connectionToRemove.toLane)) {
1342             // remove connection
1343             myConnections.erase(i);
1344             return true;
1345         }
1346     }
1347     assert(false);
1348     return false;
1349 }
1350 
1351 
1352 void
invalidateConnections(bool reallowSetting)1353 NBEdge::invalidateConnections(bool reallowSetting) {
1354     myTurnDestination = nullptr;
1355     myConnections.clear();
1356     if (reallowSetting) {
1357         myStep = INIT;
1358     } else {
1359         myStep = INIT_REJECT_CONNECTIONS;
1360     }
1361 }
1362 
1363 
1364 void
replaceInConnections(NBEdge * which,NBEdge * by,int laneOff)1365 NBEdge::replaceInConnections(NBEdge* which, NBEdge* by, int laneOff) {
1366     // replace in "_connectedEdges"
1367     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1368         if ((*i).toEdge == which) {
1369             (*i).toEdge = by;
1370             (*i).toLane += laneOff;
1371         }
1372     }
1373     // check whether it was the turn destination
1374     if (myTurnDestination == which) {
1375         myTurnDestination = by;
1376     }
1377 }
1378 
1379 void
replaceInConnections(NBEdge * which,const std::vector<NBEdge::Connection> & origConns)1380 NBEdge::replaceInConnections(NBEdge* which, const std::vector<NBEdge::Connection>& origConns) {
1381     std::map<int, int> laneMap;
1382     int minLane = -1;
1383     int maxLane = -1;
1384     // get lanes used to approach the edge to remap
1385     bool wasConnected = false;
1386     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1387         if ((*i).toEdge != which) {
1388             continue;
1389         }
1390         wasConnected = true;
1391         if ((*i).fromLane != -1) {
1392             int fromLane = (*i).fromLane;
1393             laneMap[(*i).toLane] = fromLane;
1394             if (minLane == -1 || minLane > fromLane) {
1395                 minLane = fromLane;
1396             }
1397             if (maxLane == -1 || maxLane < fromLane) {
1398                 maxLane = fromLane;
1399             }
1400         }
1401     }
1402     if (!wasConnected) {
1403         return;
1404     }
1405     // add new connections
1406     std::vector<NBEdge::Connection> conns = origConns;
1407     EdgeVector origTargets = getSuccessors();
1408     for (std::vector<NBEdge::Connection>::iterator i = conns.begin(); i != conns.end(); ++i) {
1409         if ((*i).toEdge == which || (*i).toEdge == this
1410                 // if we already have connections to the target edge, do not add new ones as they are probably from a circular replacement
1411                 || std::find(origTargets.begin(), origTargets.end(), (*i).toEdge) != origTargets.end()) {
1412 #ifdef DEBUG_REPLACECONNECTION
1413             if (DEBUGCOND) {
1414                 std::cout << " replaceInConnections edge=" << getID() << " which=" << which->getID()
1415                           << " origTargets=" << toString(origTargets) << " newTarget=" << i->toEdge->getID() << " skipped\n";
1416             }
1417 #endif
1418             continue;
1419         }
1420         if (which->getStep() == EDGE2EDGES) {
1421             // do not set lane-level connections
1422             replaceInConnections(which, (*i).toEdge, 0);
1423             continue;
1424         }
1425         int fromLane = (*i).fromLane;
1426         int toUse = -1;
1427         if (laneMap.find(fromLane) == laneMap.end()) {
1428             if (fromLane >= 0 && fromLane <= minLane) {
1429                 toUse = minLane;
1430                 // patch laneMap to avoid crossed-over connections
1431                 for (auto& item : laneMap) {
1432                     if (item.first < fromLane) {
1433                         item.second = MIN2(item.second, minLane);
1434                     }
1435                 }
1436             }
1437             if (fromLane >= 0 && fromLane >= maxLane) {
1438                 toUse = maxLane;
1439                 // patch laneMap to avoid crossed-over connections
1440                 for (auto& item : laneMap) {
1441                     if (item.first > fromLane) {
1442                         item.second = MAX2(item.second, maxLane);
1443                     }
1444                 }
1445             }
1446         } else {
1447             toUse = laneMap[fromLane];
1448         }
1449         if (toUse == -1) {
1450             toUse = 0;
1451         }
1452 #ifdef DEBUG_REPLACECONNECTION
1453         if (DEBUGCOND) {
1454             std::cout  << " replaceInConnections edge=" << getID() << " which=" << which->getID() << " origTargets=" << toString(origTargets)
1455                        << " origFrom=" << fromLane << " laneMap=" << joinToString(laneMap, ":", ",") << " minLane=" << minLane << " maxLane=" << maxLane
1456                        << " newTarget=" << i->toEdge->getID() << " fromLane=" << toUse << " toLane=" << i->toLane << "\n";
1457         }
1458 #endif
1459         setConnection(toUse, i->toEdge, i->toLane, L2L_COMPUTED, false, i->mayDefinitelyPass, i->keepClear,
1460                       i->contPos, i->visibility, i->speed, i->customShape, i->uncontrolled);
1461     }
1462     // remove the remapped edge from connections
1463     removeFromConnections(which);
1464 }
1465 
1466 
1467 void
copyConnectionsFrom(NBEdge * src)1468 NBEdge::copyConnectionsFrom(NBEdge* src) {
1469     myStep = src->myStep;
1470     myConnections = src->myConnections;
1471 }
1472 
1473 
1474 bool
canMoveConnection(const Connection & con,int newFromLane) const1475 NBEdge::canMoveConnection(const Connection& con, int newFromLane) const {
1476     // only allow using newFromLane if at least 1 vClass is permitted to use
1477     // this connection. If the connection shall be moved to a sidewalk, only create the connection if there is no walking area
1478     const SVCPermissions common = (getPermissions(newFromLane) & con.toEdge->getPermissions(con.toLane));
1479     return (common > 0 && common != SVC_PEDESTRIAN);
1480 }
1481 
1482 
1483 void
moveConnectionToLeft(int lane)1484 NBEdge::moveConnectionToLeft(int lane) {
1485     int index = 0;
1486     for (int i = 0; i < (int)myConnections.size(); ++i) {
1487         if (myConnections[i].fromLane == (int)(lane) && canMoveConnection(myConnections[i], lane + 1)) {
1488             index = i;
1489         }
1490     }
1491     std::vector<Connection>::iterator i = myConnections.begin() + index;
1492     Connection c = *i;
1493     myConnections.erase(i);
1494     setConnection(lane + 1, c.toEdge, c.toLane, L2L_VALIDATED, false);
1495 }
1496 
1497 
1498 void
moveConnectionToRight(int lane)1499 NBEdge::moveConnectionToRight(int lane) {
1500     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1501         if ((*i).fromLane == (int)lane && canMoveConnection(*i, lane - 1)) {
1502             Connection c = *i;
1503             i = myConnections.erase(i);
1504             setConnection(lane - 1, c.toEdge, c.toLane, L2L_VALIDATED, false);
1505             return;
1506         }
1507     }
1508 }
1509 
1510 
1511 void
buildInnerEdges(const NBNode & n,int noInternalNoSplits,int & linkIndex,int & splitIndex)1512 NBEdge::buildInnerEdges(const NBNode& n, int noInternalNoSplits, int& linkIndex, int& splitIndex) {
1513     const int numPoints = OptionsCont::getOptions().getInt("junctions.internal-link-detail");
1514     const bool joinTurns = OptionsCont::getOptions().getBool("junctions.join-turns");
1515     const double limitTurnSpeed = OptionsCont::getOptions().getFloat("junctions.limit-turn-speed");
1516     const double limitTurnSpeedMinAngle = DEG2RAD(OptionsCont::getOptions().getFloat("junctions.limit-turn-speed.min-angle"));
1517     const double limitTurnSpeedMinAngleRail = DEG2RAD(OptionsCont::getOptions().getFloat("junctions.limit-turn-speed.min-angle.railway"));
1518     const double limitTurnSpeedWarnStraight = OptionsCont::getOptions().getFloat("junctions.limit-turn-speed.warn.straight");
1519     const double limitTurnSpeedWarnTurn = OptionsCont::getOptions().getFloat("junctions.limit-turn-speed.warn.turn");
1520     const bool fromRail = isRailway(getPermissions());
1521     std::string innerID = ":" + n.getID();
1522     NBEdge* toEdge = nullptr;
1523     int edgeIndex = linkIndex;
1524     int internalLaneIndex = 0;
1525     int numLanes = 0; // number of lanes that share the same edge
1526     double lengthSum = 0; // total shape length of all lanes that share the same edge
1527     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1528         Connection& con = *i;
1529         con.haveVia = false; // reset first since this may be called multiple times
1530         if (con.toEdge == nullptr) {
1531             continue;
1532         }
1533         LinkDirection dir = n.getDirection(this, con.toEdge);
1534         const bool isRightTurn = (dir == LINKDIR_RIGHT || dir == LINKDIR_PARTRIGHT);
1535         const bool isTurn = (isRightTurn || dir == LINKDIR_LEFT || dir == LINKDIR_PARTLEFT);
1536 
1537         // put turning internal lanes on separate edges
1538         if (con.toEdge != toEdge || (isTurn && !joinTurns)) {
1539             // skip indices to keep some correspondence between edge ids and link indices:
1540             // internalEdgeIndex + internalLaneIndex = linkIndex
1541             edgeIndex = linkIndex;
1542             toEdge = (*i).toEdge;
1543             internalLaneIndex = 0;
1544             assignInternalLaneLength(i, numLanes, lengthSum);
1545             numLanes = 0;
1546             lengthSum = 0;
1547         }
1548         PositionVector shape = n.computeInternalLaneShape(this, con, numPoints, myTo);
1549         std::vector<int> foeInternalLinks;
1550 
1551         if (dir != LINKDIR_STRAIGHT && shape.length() < POSITION_EPS && !(isBidiRail() && getTurnDestination(true) == con.toEdge)) {
1552             WRITE_WARNING("Connection '" + getID() + "_" + toString(con.fromLane) + "->" + con.toEdge->getID() + "_" + toString(con.toLane) + "' is only " + toString(shape.length()) + " short.");
1553         }
1554 
1555         // crossingPosition, list of foe link indices
1556         std::pair<double, std::vector<int> > crossingPositions(-1, std::vector<int>());
1557         std::set<std::string> tmpFoeIncomingLanes;
1558         switch (dir) {
1559             case LINKDIR_RIGHT:
1560             case LINKDIR_PARTRIGHT:
1561             case LINKDIR_LEFT:
1562             case LINKDIR_PARTLEFT:
1563             case LINKDIR_TURN: {
1564                 int index = 0;
1565                 const std::vector<NBEdge*>& incoming = n.getIncomingEdges();
1566                 for (EdgeVector::const_iterator i2 = incoming.begin(); i2 != incoming.end(); ++i2) {
1567                     const std::vector<Connection>& elv = (*i2)->getConnections();
1568                     for (std::vector<NBEdge::Connection>::const_iterator k2 = elv.begin(); k2 != elv.end(); k2++) {
1569                         if ((*k2).toEdge == nullptr) {
1570                             continue;
1571                         }
1572                         // vehicles are typically less wide than the lane
1573                         // they drive on but but bicycle lanes should be kept clear for their whole width
1574                         double width2 = (*k2).toEdge->getLaneWidth((*k2).toLane);
1575                         if ((*k2).toEdge->getPermissions((*k2).toLane) != SVC_BICYCLE) {
1576                             width2 *= 0.5;
1577                         }
1578                         const bool foes = n.foes(this, con.toEdge, *i2, (*k2).toEdge);
1579                         bool needsCont = n.needsCont(this, *i2, con, *k2);
1580                         bool oppositeLeftIntersect = !foes && bothLeftIntersect(n, shape, dir, *i2, *k2, numPoints, width2);
1581                         int shapeFlag = 0;
1582                         // do not warn if only bicycles pedestrians or delivery vehicles are involved as this is a typical occurence
1583                         SVCPermissions warn = SVCAll & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_DELIVERY);
1584                         if (oppositeLeftIntersect
1585                                 && (((*i2)->getPermissions((*k2).fromLane) & warn) != 0
1586                                     && ((*k2).toEdge->getPermissions((*k2).toLane) & warn) != 0)) {
1587                             // recompute with different curve parameters (unless
1588                             // the other connection is "unimportant"
1589                             shapeFlag = NBNode::AVOID_INTERSECTING_LEFT_TURNS;
1590                             shape = n.computeInternalLaneShape(this, con, numPoints, myTo, shapeFlag);
1591                             oppositeLeftIntersect = bothLeftIntersect(n, shape, dir, *i2, *k2, numPoints, width2, shapeFlag);
1592                         }
1593                         const bool bothPrio = getJunctionPriority(&n) > 0 && (*i2)->getJunctionPriority(&n) > 0;
1594                         //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";
1595                         // compute the crossing point
1596                         if (needsCont || (bothPrio && oppositeLeftIntersect)) {
1597                             crossingPositions.second.push_back(index);
1598                             const PositionVector otherShape = n.computeInternalLaneShape(*i2, *k2, numPoints, 0, shapeFlag);
1599                             const double minDV = firstIntersection(shape, otherShape, width2);
1600                             if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
1601                                 assert(minDV >= 0);
1602                                 if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1603                                     crossingPositions.first = minDV;
1604                                 }
1605                             }
1606                         }
1607                         const bool rightTurnConflict = NBNode::rightTurnConflict(
1608                                                            this, con.toEdge, con.fromLane, (*i2), (*k2).toEdge, (*k2).fromLane);
1609                         // compute foe internal lanes
1610                         if (foes || rightTurnConflict || oppositeLeftIntersect) {
1611                             foeInternalLinks.push_back(index);
1612                         }
1613                         // only warn once per pair of intersecting turns
1614                         if (oppositeLeftIntersect && getID() > (*i2)->getID()
1615                                 && (getPermissions(con.fromLane) & warn) != 0
1616                                 && (con.toEdge->getPermissions(con.toLane) & warn) != 0
1617                                 && ((*i2)->getPermissions((*k2).fromLane) & warn) != 0
1618                                 && ((*k2).toEdge->getPermissions((*k2).toLane) & warn) != 0
1619                                 // do not warn for unregulated nodes
1620                                 && n.getType() != NODETYPE_NOJUNCTION
1621                            ) {
1622                             WRITE_WARNING("Intersecting left turns at junction '" + n.getID() + "' from lane '" + getLaneID(con.fromLane) + "' and lane '" + (*i2)->getLaneID((*k2).fromLane) + "'. (increase junction radius to avoid this)");
1623                         }
1624                         // compute foe incoming lanes
1625                         const bool signalised = hasSignalisedConnectionTo(con.toEdge);
1626                         if ((n.forbids(*i2, (*k2).toEdge, this, con.toEdge, signalised) || rightTurnConflict) && (needsCont || dir == LINKDIR_TURN)) {
1627                             tmpFoeIncomingLanes.insert((*i2)->getID() + "_" + toString((*k2).fromLane));
1628                         }
1629                         if (bothPrio && oppositeLeftIntersect && getID() < (*i2)->getID()) {
1630                             //std::cout << " c1=" << con.getDescription(this) << " c2=" << (*k2).getDescription(*i2) << " bothPrio=" << bothPrio << " oppositeLeftIntersect=" << oppositeLeftIntersect << "\n";
1631                             // break symmetry using edge id
1632                             tmpFoeIncomingLanes.insert(innerID + "_" + toString(index) + "_0");
1633                         }
1634                         index++;
1635                     }
1636                 }
1637                 // foe pedestrian crossings
1638                 std::vector<NBNode::Crossing*> crossings = n.getCrossings();
1639                 for (auto c : crossings) {
1640                     const NBNode::Crossing& crossing = *c;
1641                     for (EdgeVector::const_iterator it_e = crossing.edges.begin(); it_e != crossing.edges.end(); ++it_e) {
1642                         const NBEdge* edge = *it_e;
1643                         // compute foe internal lanes
1644                         if (this == edge || con.toEdge == edge) {
1645                             foeInternalLinks.push_back(index);
1646                             if (con.toEdge == edge &&
1647                                     ((isRightTurn && getJunctionPriority(&n) > 0) || (isTurn && con.tlID != ""))) {
1648                                 // build internal junctions (not for left turns at uncontrolled intersections)
1649                                 PositionVector crossingShape = crossing.shape;
1650                                 crossingShape.extrapolate(5.0); // sometimes shapes miss each other by a small margin
1651                                 const double minDV = firstIntersection(shape, crossingShape, crossing.width / 2);
1652                                 if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) {
1653                                     assert(minDV >= 0);
1654                                     if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1655                                         crossingPositions.first = minDV;
1656                                     }
1657                                 }
1658                             }
1659                         }
1660                     }
1661                     index++;
1662                 }
1663 
1664                 if (dir == LINKDIR_TURN && crossingPositions.first < 0 && crossingPositions.second.size() != 0 && shape.length() > 2. * POSITION_EPS) {
1665                     // let turnarounds wait in the middle if no other crossing point was found and it has a sensible length
1666                     // (if endOffset is used, the crossing point is in the middle of the part within the junction shape)
1667                     crossingPositions.first = (double)(shape.length() + getEndOffset(con.fromLane)) / 2.;
1668                 }
1669             }
1670             break;
1671             default:
1672                 break;
1673         }
1674         if (con.contPos != UNSPECIFIED_CONTPOS) {
1675             // apply custom internal junction position
1676             if (con.contPos <= 0 || con.contPos >= shape.length()) {
1677                 // disable internal junction
1678                 crossingPositions.first = -1;
1679             } else {
1680                 // set custom position
1681                 crossingPositions.first = con.contPos;
1682             }
1683         }
1684 
1685         // @todo compute the maximum speed allowed based on angular velocity
1686         //  see !!! for an explanation (with a_lat_mean ~0.3)
1687         /*
1688         double vmax = (double) 0.3 * (double) 9.80778 *
1689                         getLaneShape(con.fromLane).back().distanceTo(
1690                             con.toEdge->getLaneShape(con.toLane).front())
1691                         / (double) 2.0 / (double) M_PI;
1692         vmax = MIN2(vmax, ((getSpeed() + con.toEdge->getSpeed()) / (double) 2.0));
1693         */
1694         if (con.speed == UNSPECIFIED_SPEED) {
1695             con.vmax = (myLanes[con.fromLane].speed + con.toEdge->getLanes()[con.toLane].speed) / (double) 2.0;
1696             if (limitTurnSpeed > 0) {
1697                 // see [Odhams and Cole, Models of Driver Speed Choice in Curves, 2004]
1698                 const double angleRaw = fabs(GeomHelper::angleDiff(
1699                                                  getLaneShape(con.fromLane).angleAt2D(-2),
1700                                                  con.toEdge->getLaneShape(con.toLane).angleAt2D(0)));
1701                 const double angle = MAX2(0.0, angleRaw - (fromRail ? limitTurnSpeedMinAngleRail : limitTurnSpeedMinAngle));
1702                 const double length = shape.length2D();
1703                 // do not trust the radius of tiny junctions
1704                 // formula adapted from [Odhams, Andre and Cole, David, Models of Driver Speed Choice in Curves, 2004]
1705                 if (angle > 0 && length > 1) {
1706                     // permit higher turning speed on wide lanes
1707                     const double radius = length / angle + getLaneWidth(con.fromLane) / 4;
1708                     const double limit = sqrt(limitTurnSpeed * radius);
1709                     const double reduction = con.vmax - limit;
1710                     // always treat connctions at roundabout as turns when warning
1711                     const bool atRoundabout = getJunctionPriority(myTo) == ROUNDABOUT || con.toEdge->getJunctionPriority(myFrom) == ROUNDABOUT;
1712                     int dir2 = atRoundabout ? LINKDIR_LEFT : dir;
1713                     if ((dir2 == LINKDIR_STRAIGHT && reduction > limitTurnSpeedWarnStraight)
1714                             || (dir2 != LINKDIR_TURN && reduction > limitTurnSpeedWarnTurn)) {
1715                         std::string dirType = std::string(dir == LINKDIR_STRAIGHT ? "straight" : "turning");
1716                         if (atRoundabout) {
1717                             dirType = "roundabout";
1718                         }
1719                         WRITE_WARNING("Speed of " + dirType + " connection '" + con.getDescription(this)
1720                                       + "' reduced by " + toString(reduction) + " due to turning radius of " + toString(radius)
1721                                       + " (length=" + toString(length) + " angle=" + toString(RAD2DEG(angleRaw)) + ")");
1722                     }
1723                     con.vmax = MIN2(con.vmax, limit);
1724                     // value is saved in <net> attribute. Must be set again when importing from .con.xml
1725                     // con.speed = con.vmax;
1726                 }
1727                 assert(con.vmax > 0);
1728                 //if (getID() == "-1017000.0.00") {
1729                 //    std::cout << con.getDescription(this) << " angleRaw=" << angleRaw << " angle=" << RAD2DEG(angle) << " length=" << length << " radius=" << length / angle
1730                 //        << " vmaxTurn=" << sqrt(limitTurnSpeed * length / angle) << " vmax=" << con.vmax << "\n";
1731                 //}
1732             }
1733         } else {
1734             con.vmax = con.speed;
1735         }
1736         //
1737         assert(shape.size() >= 2);
1738         // get internal splits if any
1739         con.id = innerID + "_" + toString(edgeIndex);
1740         if (crossingPositions.first >= 0 && crossingPositions.first < shape.length()) {
1741             std::pair<PositionVector, PositionVector> split = shape.splitAt(crossingPositions.first);
1742             con.shape = split.first;
1743             con.foeIncomingLanes = std::vector<std::string>(tmpFoeIncomingLanes.begin(), tmpFoeIncomingLanes.end());
1744             con.foeInternalLinks = foeInternalLinks; // resolve link indices to lane ids later
1745             con.viaID = innerID + "_" + toString(splitIndex + noInternalNoSplits);
1746             ++splitIndex;
1747             con.viaShape = split.second;
1748             con.haveVia = true;
1749         } else {
1750             con.shape = shape;
1751         }
1752         con.internalLaneIndex = internalLaneIndex;
1753         ++internalLaneIndex;
1754         ++linkIndex;
1755         ++numLanes;
1756         lengthSum += MAX2(POSITION_EPS, con.shape.length());
1757     }
1758     assignInternalLaneLength(myConnections.end(), numLanes, lengthSum);
1759 }
1760 
1761 
1762 void
assignInternalLaneLength(std::vector<Connection>::iterator i,int numLanes,double lengthSum)1763 NBEdge::assignInternalLaneLength(std::vector<Connection>::iterator i, int numLanes, double lengthSum) {
1764     // assign average length to all lanes of the same internal edge
1765     // @note the actual length should be used once sumo supports lanes of
1766     // varying length within the same edge
1767     assert(i - myConnections.begin() >= numLanes);
1768     for (int prevIndex = 1; prevIndex <= numLanes; prevIndex++) {
1769         //std::cout << " con=" << (*(i - prevIndex)).getDescription(this) << " numLanes=" << numLanes << " avgLength=" << lengthSum / numLanes << "\n";
1770         (*(i - prevIndex)).length = lengthSum / numLanes;
1771     }
1772 }
1773 
1774 double
firstIntersection(const PositionVector & v1,const PositionVector & v2,double width2)1775 NBEdge::firstIntersection(const PositionVector& v1, const PositionVector& v2, double width2) {
1776     double intersect = std::numeric_limits<double>::max();
1777     if (v2.length() < POSITION_EPS) {
1778         return intersect;
1779     }
1780     PositionVector v2Right = v2;
1781     v2Right.move2side(width2);
1782 
1783     PositionVector v2Left = v2;
1784     v2Left.move2side(-width2);
1785 
1786     // intersect center line of v1 with left and right border of v2
1787     for (double cand : v1.intersectsAtLengths2D(v2Right)) {
1788         intersect = MIN2(intersect, cand);
1789     }
1790     for (double cand : v1.intersectsAtLengths2D(v2Left)) {
1791         intersect = MIN2(intersect, cand);
1792     }
1793     //std::cout << " v1=" << v1 << " v2Right=" << v2Right << " v2Left=" << v2Left << "\n";
1794     //std::cout << "  intersectsRight=" << toString(v1.intersectsAtLengths2D(v2Right)) << "\n";
1795     //std::cout << "  intersectsLeft=" << toString(v1.intersectsAtLengths2D(v2Left)) << "\n";
1796     return intersect;
1797 }
1798 
1799 
1800 bool
bothLeftIntersect(const NBNode & n,const PositionVector & shape,LinkDirection dir,NBEdge * otherFrom,const NBEdge::Connection & otherCon,int numPoints,double width2,int shapeFlag) const1801 NBEdge::bothLeftIntersect(const NBNode& n, const PositionVector& shape, LinkDirection dir, NBEdge* otherFrom, const NBEdge::Connection& otherCon, int numPoints, double width2, int shapeFlag) const {
1802     if (otherFrom == this) {
1803         // not an opposite pair
1804         return false;
1805     }
1806     LinkDirection dir2 = n.getDirection(otherFrom, otherCon.toEdge);
1807     const bool bothLeft = (dir == LINKDIR_LEFT || dir == LINKDIR_PARTLEFT) && (dir2 == LINKDIR_LEFT || dir2 == LINKDIR_PARTLEFT);
1808     if (bothLeft) {
1809         const PositionVector otherShape = n.computeInternalLaneShape(otherFrom, otherCon, numPoints, 0, shapeFlag);
1810         const double minDV = firstIntersection(shape, otherShape, width2);
1811         if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) {
1812             return true;
1813         } else {
1814             return false;
1815         }
1816     } else {
1817         return false;
1818     }
1819 }
1820 
1821 
1822 // -----------
1823 int
getJunctionPriority(const NBNode * const node) const1824 NBEdge::getJunctionPriority(const NBNode* const node) const {
1825     if (node == myFrom) {
1826         return myFromJunctionPriority;
1827     } else {
1828         return myToJunctionPriority;
1829     }
1830 }
1831 
1832 
1833 void
setJunctionPriority(const NBNode * const node,int prio)1834 NBEdge::setJunctionPriority(const NBNode* const node, int prio) {
1835     if (node == myFrom) {
1836         myFromJunctionPriority = prio;
1837     } else {
1838         myToJunctionPriority = prio;
1839     }
1840 }
1841 
1842 
1843 double
getAngleAtNode(const NBNode * const atNode) const1844 NBEdge::getAngleAtNode(const NBNode* const atNode) const {
1845     // myStartAngle, myEndAngle are in [0,360] and this returns results in [-180,180]
1846     if (atNode == myFrom) {
1847         return GeomHelper::legacyDegree(myGeom.angleAt2D(0));
1848     } else {
1849         assert(atNode == myTo);
1850         return GeomHelper::legacyDegree(myGeom.angleAt2D(-2));
1851     }
1852 }
1853 
1854 
1855 double
getAngleAtNodeToCenter(const NBNode * const atNode) const1856 NBEdge::getAngleAtNodeToCenter(const NBNode* const atNode) const {
1857     if (atNode == myFrom) {
1858         double res = myStartAngle - 180;
1859         if (res < 0) {
1860             res += 360;
1861         }
1862         return res;
1863     } else {
1864         assert(atNode == myTo);
1865         return myEndAngle;
1866     }
1867 }
1868 
1869 
1870 void
setTurningDestination(NBEdge * e,bool onlyPossible)1871 NBEdge::setTurningDestination(NBEdge* e, bool onlyPossible) {
1872     if (!onlyPossible) {
1873         myTurnDestination = e;
1874     }
1875     myPossibleTurnDestination = e;
1876 }
1877 
1878 
1879 double
getLaneSpeed(int lane) const1880 NBEdge::getLaneSpeed(int lane) const {
1881     return myLanes[lane].speed;
1882 }
1883 
1884 
1885 void
computeLaneShapes()1886 NBEdge::computeLaneShapes() {
1887     // vissim needs this
1888     if (myFrom == myTo) {
1889         return;
1890     }
1891     // compute lane offset, first
1892     std::vector<double> offsets(myLanes.size(), 0.);
1893     double offset = 0;
1894     for (int i = (int)myLanes.size() - 2; i >= 0; --i) {
1895         offset += (getLaneWidth(i) + getLaneWidth(i + 1)) / 2. + SUMO_const_laneOffset;
1896         offsets[i] = offset;
1897     }
1898     if (myLaneSpreadFunction == LANESPREAD_RIGHT) {
1899         double laneWidth = myLanes.back().width != UNSPECIFIED_WIDTH ? myLanes.back().width : SUMO_const_laneWidth;
1900         offset = (laneWidth + SUMO_const_laneOffset) / 2.; // @note: offset for half of the center-line marking of the road
1901     } else {
1902         double width = 0;
1903         for (int i = 0; i < (int)myLanes.size(); ++i) {
1904             width += getLaneWidth(i);
1905         }
1906         width += SUMO_const_laneOffset * double(myLanes.size() - 1);
1907         offset = -width / 2. + getLaneWidth((int)myLanes.size() - 1) / 2.;
1908     }
1909     for (int i = 0; i < (int)myLanes.size(); ++i) {
1910         offsets[i] += offset;
1911     }
1912 
1913     // build the shape of each lane
1914     for (int i = 0; i < (int)myLanes.size(); ++i) {
1915         if (myLanes[i].customShape.size() != 0) {
1916             myLanes[i].shape = myLanes[i].customShape;
1917             continue;
1918         }
1919         try {
1920             myLanes[i].shape = computeLaneShape(i, offsets[i]);
1921         } catch (InvalidArgument& e) {
1922             WRITE_WARNING("In edge '" + getID() + "': lane shape could not be determined (" + e.what() + ").");
1923             myLanes[i].shape = myGeom;
1924         }
1925     }
1926 }
1927 
1928 
1929 PositionVector
computeLaneShape(int lane,double offset) const1930 NBEdge::computeLaneShape(int lane, double offset) const {
1931     PositionVector shape = myGeom;
1932     try {
1933         shape.move2side(offset);
1934     } catch (InvalidArgument& e) {
1935         WRITE_WARNING("In lane '" + getLaneID(lane) + "': Could not build shape (" + e.what() + ").");
1936     }
1937     return shape;
1938 }
1939 
1940 
1941 void
computeAngle()1942 NBEdge::computeAngle() {
1943     // taking the angle at the first might be unstable, thus we take the angle
1944     // at a certain distance. (To compare two edges, additional geometry
1945     // segments are considered to resolve ambiguities)
1946     const bool hasFromShape = myFrom->getShape().size() > 0;
1947     const bool hasToShape = myTo->getShape().size() > 0;
1948     Position fromCenter = (hasFromShape ? myFrom->getShape().getCentroid() : myFrom->getPosition());
1949     Position toCenter = (hasToShape ? myTo->getShape().getCentroid() : myTo->getPosition());
1950     PositionVector shape = myGeom;
1951     if ((hasFromShape || hasToShape) && getNumLanes() > 0) {
1952         if (myLaneSpreadFunction == LANESPREAD_RIGHT) {
1953             shape = myLanes[getNumLanes() - 1].shape ;
1954         } else {
1955             shape = myLanes[getNumLanes() / 2].shape;
1956             if (getNumLanes() % 2 == 0) {
1957                 // there is no center lane. shift to get the center
1958                 shape.move2side(getLaneWidth(getNumLanes() / 2) * 0.5);
1959             }
1960         }
1961     }
1962 
1963     // if the junction shape is suspicious we cannot trust the angle to the centroid
1964     if (hasFromShape && (myFrom->getShape().distance2D(shape[0]) > 2 * POSITION_EPS
1965                          || myFrom->getShape().around(shape[-1])
1966                          || !(myFrom->getShape().around(fromCenter)))) {
1967         fromCenter = myFrom->getPosition();
1968     }
1969     if (hasToShape && (myTo->getShape().distance2D(shape[-1]) > 2 * POSITION_EPS
1970                        || myTo->getShape().around(shape[0])
1971                        || !(myTo->getShape().around(toCenter)))) {
1972         toCenter = myTo->getPosition();
1973     }
1974 
1975     const double angleLookahead = MIN2(shape.length2D() / 2, ANGLE_LOOKAHEAD);
1976     const Position referencePosStart = shape.positionAtOffset2D(angleLookahead);
1977     myStartAngle = GeomHelper::legacyDegree(fromCenter.angleTo2D(referencePosStart), true);
1978     const Position referencePosEnd = shape.positionAtOffset2D(shape.length() - angleLookahead);
1979     myEndAngle = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(toCenter), true);
1980     myTotalAngle = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(myTo->getPosition()), true);
1981 #ifdef DEBUG_ANGLES
1982     if (DEBUGCOND) std::cout << "computeAngle edge=" << getID() << " fromCenter=" << fromCenter << " toCenter=" << toCenter
1983                                  << " refStart=" << referencePosStart << " refEnd=" << referencePosEnd << " shape=" << shape
1984                                  << " hasFromShape=" << hasFromShape
1985                                  << " hasToShape=" << hasToShape
1986                                  << " numLanes=" << getNumLanes()
1987                                  << " shapeLane=" << getNumLanes() / 2
1988                                  << " startA=" << myStartAngle << " endA=" << myEndAngle << " totA=" << myTotalAngle << "\n";
1989 #endif
1990 }
1991 
1992 
1993 double
getShapeStartAngle() const1994 NBEdge::getShapeStartAngle() const {
1995     const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
1996     const Position referencePosStart = myGeom.positionAtOffset2D(angleLookahead);
1997     return GeomHelper::legacyDegree(myGeom.front().angleTo2D(referencePosStart), true);
1998 }
1999 
2000 
2001 double
getShapeEndAngle() const2002 NBEdge::getShapeEndAngle() const {
2003     const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
2004     const Position referencePosEnd = myGeom.positionAtOffset2D(myGeom.length() - angleLookahead);
2005     return GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myGeom.back()), true);
2006 }
2007 
2008 
2009 bool
hasPermissions() const2010 NBEdge::hasPermissions() const {
2011     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2012         if ((*i).permissions != SVCAll) {
2013             return true;
2014         }
2015     }
2016     return false;
2017 }
2018 
2019 
2020 bool
hasLaneSpecificPermissions() const2021 NBEdge::hasLaneSpecificPermissions() const {
2022     std::vector<Lane>::const_iterator i = myLanes.begin();
2023     SVCPermissions firstLanePermissions = i->permissions;
2024     i++;
2025     for (; i != myLanes.end(); ++i) {
2026         if (i->permissions != firstLanePermissions) {
2027             return true;
2028         }
2029     }
2030     return false;
2031 }
2032 
2033 
2034 bool
hasLaneSpecificSpeed() const2035 NBEdge::hasLaneSpecificSpeed() const {
2036     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2037         if (i->speed != getSpeed()) {
2038             return true;
2039         }
2040     }
2041     return false;
2042 }
2043 
2044 
2045 bool
hasLaneSpecificWidth() const2046 NBEdge::hasLaneSpecificWidth() const {
2047     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2048         if (i->width != myLanes.begin()->width) {
2049             return true;
2050         }
2051     }
2052     return false;
2053 }
2054 
2055 
2056 bool
hasLaneSpecificEndOffset() const2057 NBEdge::hasLaneSpecificEndOffset() const {
2058     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2059         if (i->endOffset != myLanes.begin()->endOffset) {
2060             return true;
2061         }
2062     }
2063     return false;
2064 }
2065 
2066 
2067 bool
hasLaneSpecificStopOffsets() const2068 NBEdge::hasLaneSpecificStopOffsets() const {
2069     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2070         if (!i->stopOffsets.empty()) {
2071             const std::pair<const int, double>& offsets = *(i->stopOffsets.begin());
2072             if (myStopOffsets.empty() || offsets != *(myStopOffsets.begin())) {
2073                 return true;
2074             }
2075         }
2076     }
2077     return false;
2078 }
2079 
2080 
2081 bool
hasAccelLane() const2082 NBEdge::hasAccelLane() const {
2083     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2084         if (i->accelRamp) {
2085             return true;
2086         }
2087     }
2088     return false;
2089 }
2090 
2091 
2092 bool
hasCustomLaneShape() const2093 NBEdge::hasCustomLaneShape() const {
2094     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2095         if (i->customShape.size() > 0) {
2096             return true;
2097         }
2098     }
2099     return false;
2100 }
2101 
2102 
2103 bool
hasLaneParams() const2104 NBEdge::hasLaneParams() const {
2105     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2106         if (i->getParametersMap().size() > 0) {
2107             return true;
2108         }
2109     }
2110     return false;
2111 }
2112 
2113 bool
needsLaneSpecificOutput() const2114 NBEdge::needsLaneSpecificOutput() const {
2115     return (hasLaneSpecificPermissions()
2116             || hasLaneSpecificSpeed()
2117             || hasLaneSpecificWidth()
2118             || hasLaneSpecificEndOffset()
2119             || hasLaneSpecificStopOffsets()
2120             || hasAccelLane()
2121             || hasCustomLaneShape()
2122             || hasLaneParams()
2123             || (!myLanes.empty() && myLanes.back().oppositeID != ""));
2124 }
2125 
2126 
2127 
2128 bool
computeEdge2Edges(bool noLeftMovers)2129 NBEdge::computeEdge2Edges(bool noLeftMovers) {
2130 #ifdef DEBUG_CONNECTION_GUESSING
2131     if (DEBUGCOND) {
2132         std::cout << "computeEdge2Edges  edge=" << getID() << " step=" << myStep << "\n";
2133         for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
2134             std::cout << "  conn " << getID() << "_" << (*i).fromLane << " to " << Named::getIDSecure((*i).toEdge) << "_" << (*i).toLane << "\n";
2135         }
2136     }
2137 #endif
2138     // return if this relationship has been build in previous steps or
2139     //  during the import
2140     if (myStep >= EDGE2EDGES) {
2141         return true;
2142     }
2143     const EdgeVector& o = myTo->getOutgoingEdges();
2144     const bool fromRail = isRailway(getPermissions());
2145     for (EdgeVector::const_iterator i = o.begin(); i != o.end(); ++i) {
2146         if (noLeftMovers && myTo->isLeftMover(this, *i)) {
2147             continue;
2148         }
2149         // avoid sharp railway turns
2150         if (fromRail && isRailway((*i)->getPermissions()) &&
2151                 fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), (*i)->getAngleAtNode(myTo))) > 90) {
2152             continue;
2153         };
2154         myConnections.push_back(Connection(-1, *i, -1));
2155     }
2156     myStep = EDGE2EDGES;
2157     return true;
2158 }
2159 
2160 
2161 bool
computeLanes2Edges()2162 NBEdge::computeLanes2Edges() {
2163 #ifdef DEBUG_CONNECTION_GUESSING
2164     if (DEBUGCOND) {
2165         std::cout << "computeLanes2Edges edge=" << getID() << " step=" << myStep << "\n";
2166         for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
2167             std::cout << "  conn " << getID() << "_" << (*i).fromLane << " to " << Named::getIDSecure((*i).toEdge) << "_" << (*i).toLane << "\n";
2168         }
2169     }
2170 #endif
2171     // return if this relationship has been build in previous steps or
2172     //  during the import
2173     if (myStep >= LANES2EDGES) {
2174         return true;
2175     }
2176     assert(myStep == EDGE2EDGES);
2177     // get list of possible outgoing edges sorted by direction clockwise
2178     //  the edge in the backward direction (turnaround) is not in the list
2179     const EdgeVector* edges = getConnectedSorted();
2180     if (myConnections.size() != 0 && edges->size() == 0) {
2181         // dead end per definition!?
2182         myConnections.clear();
2183     } else {
2184         // divide the lanes on reachable edges
2185         divideOnEdges(edges);
2186     }
2187     delete edges;
2188     myStep = LANES2EDGES;
2189     return true;
2190 }
2191 
2192 
2193 bool
recheckLanes()2194 NBEdge::recheckLanes() {
2195 #ifdef DEBUG_CONNECTION_GUESSING
2196     if (DEBUGCOND) {
2197         std::cout << "recheckLanes (initial) edge=" << getID() << "\n";
2198         for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
2199             std::cout << "  conn " << getID() << "_" << (*i).fromLane << " to " << Named::getIDSecure((*i).toEdge) << "_" << (*i).toLane << "\n";
2200         }
2201     }
2202 #endif
2203     std::vector<int> connNumbersPerLane(myLanes.size(), 0);
2204     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
2205         if ((*i).toEdge == nullptr || (*i).fromLane < 0 || (*i).toLane < 0) {
2206             i = myConnections.erase(i);
2207         } else {
2208             if ((*i).fromLane >= 0) {
2209                 ++connNumbersPerLane[(*i).fromLane];
2210             }
2211             ++i;
2212         }
2213     }
2214     if (myStep != LANES2LANES_DONE && myStep != LANES2LANES_USER) {
2215         // check #1:
2216         // If there is a lane with no connections and any neighbour lane has
2217         //  more than one connections, try to move one of them.
2218         // This check is only done for edges which connections were assigned
2219         //  using the standard algorithm.
2220         for (int i = 0; i < (int)myLanes.size(); i++) {
2221             if (connNumbersPerLane[i] == 0 && !isForbidden(getPermissions((int)i))) {
2222                 if (i > 0 && connNumbersPerLane[i - 1] > 1 && getPermissions(i) == getPermissions(i - 1)) {
2223                     moveConnectionToLeft(i - 1);
2224                 } else if (i < (int)myLanes.size() - 1 && connNumbersPerLane[i + 1] > 1 && getPermissions(i) == getPermissions(i + 1)) {
2225                     moveConnectionToRight(i + 1);
2226                 }
2227             }
2228         }
2229         // check restrictions
2230         for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
2231             Connection& c = *i;
2232             const SVCPermissions common = getPermissions(c.fromLane) & c.toEdge->getPermissions(c.toLane);
2233             if (common == SVC_PEDESTRIAN || getPermissions(c.fromLane) == SVC_PEDESTRIAN) {
2234                 // these are computed in NBNode::buildWalkingAreas
2235                 i = myConnections.erase(i);
2236             } else if (common == 0) {
2237                 // no common permissions.
2238                 // try to find a suitable target lane to the right
2239                 const int origToLane = c.toLane;
2240                 c.toLane = -1; // ignore this connection when calling hasConnectionTo
2241                 int toLane = origToLane;
2242                 while (toLane > 0
2243                         && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
2244                         && !hasConnectionTo(c.toEdge, toLane)
2245                       ) {
2246                     toLane--;
2247                 }
2248                 if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
2249                         && !hasConnectionTo(c.toEdge, toLane)) {
2250                     c.toLane = toLane;
2251                     ++i;
2252                 } else {
2253                     // try to find a suitable target lane to the left
2254                     int toLane = origToLane;
2255                     while (toLane < (int)c.toEdge->getNumLanes() - 1
2256                             && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
2257                             && !hasConnectionTo(c.toEdge, toLane)
2258                           ) {
2259                         toLane++;
2260                     }
2261                     if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
2262                             && !hasConnectionTo(c.toEdge, toLane)) {
2263                         c.toLane = toLane;
2264                         ++i;
2265                     } else {
2266                         // no alternative target found
2267                         i = myConnections.erase(i);
2268                     }
2269                 }
2270             } else if (isRailway(getPermissions(c.fromLane)) && isRailway(c.toEdge->getPermissions(c.toLane))
2271                        && isTurningDirectionAt(c.toEdge))  {
2272                 // do not allow sharp rail turns
2273                 i = myConnections.erase(i);
2274             } else {
2275                 ++i;
2276             }
2277         }
2278     }
2279     // check delayed removals
2280     for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
2281         removeFromConnections(it->toEdge, it->fromLane, it->toLane, false, false, true);
2282     }
2283     // check involuntary dead end at "real" junctions
2284     if (getPermissions() != SVC_PEDESTRIAN) {
2285         if (myConnections.empty() && myTo->getOutgoingEdges().size() > 1) {
2286             WRITE_WARNING("Edge '" + getID() + "' is not connected to outgoing edges at junction '" + myTo->getID() + "'.");
2287         }
2288         const EdgeVector& incoming = myFrom->getIncomingEdges();
2289         if (incoming.size() > 1) {
2290             for (int i = 0; i < (int)myLanes.size(); i++) {
2291                 if (getPermissions(i) != 0 && getPermissions(i) != SVC_PEDESTRIAN) {
2292                     bool connected = false;
2293                     for (std::vector<NBEdge*>::const_iterator in = incoming.begin(); in != incoming.end(); ++in) {
2294                         if ((*in)->hasConnectionTo(this, i)) {
2295                             connected = true;
2296                             break;
2297                         }
2298                     }
2299                     if (!connected) {
2300                         WRITE_WARNING("Lane '" + getID() + "_" + toString(i) + "' is not connected from any incoming edge at junction '" + myFrom->getID() + "'.");
2301                     }
2302                 }
2303             }
2304         }
2305     }
2306     // check for connections with bad access permissions
2307 #ifdef ADDITIONAL_WARNINGS
2308     for (const Connection& c : myConnections) {
2309         SVCPermissions fromP = getPermissions(c.fromLane);
2310         SVCPermissions toP = c.toEdge->getPermissions(c.toLane);
2311         if ((fromP & SVC_PASSENGER) != 0
2312                 && toP == SVC_BICYCLE) {
2313             bool hasAlternative = false;
2314             for (const Connection& c2 : myConnections) {
2315                 if (c.fromLane == c2.fromLane && c.toEdge == c2.toEdge
2316                         && (c.toEdge->getPermissions(c2.toLane) & SVC_PASSENGER) != 0) {
2317                     hasAlternative = true;
2318                 }
2319             }
2320             if (!hasAlternative) {
2321                 WRITE_WARNING("Road lane ends on bikeLane for connection " + c.getDescription(this));
2322             }
2323         }
2324     }
2325 #endif
2326 #ifdef DEBUG_CONNECTION_GUESSING
2327     if (DEBUGCOND) {
2328         std::cout << "recheckLanes (final) edge=" << getID() << "\n";
2329         for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
2330             std::cout << "  conn " << getID() << "_" << (*i).fromLane << " to " << Named::getIDSecure((*i).toEdge) << "_" << (*i).toLane << "\n";
2331         }
2332     }
2333 #endif
2334     return true;
2335 }
2336 
2337 
2338 void
divideOnEdges(const EdgeVector * outgoing)2339 NBEdge::divideOnEdges(const EdgeVector* outgoing) {
2340     if (outgoing->size() == 0) {
2341         // we have to do this, because the turnaround may have been added before
2342         myConnections.clear();
2343         return;
2344     }
2345 
2346 #ifdef DEBUG_CONNECTION_GUESSING
2347     if (DEBUGCOND) {
2348         std::cout << " divideOnEdges " << getID() << " outgoing=" << toString(*outgoing) << "\n";
2349     }
2350 #endif
2351 
2352     // build connections for miv lanes
2353     std::vector<int> availableLanes;
2354     for (int i = 0; i < (int)myLanes.size(); ++i) {
2355         if ((getPermissions(i) & SVC_PASSENGER) != 0) {
2356             availableLanes.push_back(i);
2357         }
2358     }
2359     if (availableLanes.size() > 0) {
2360         divideSelectedLanesOnEdges(outgoing, availableLanes);
2361     }
2362     // build connections for miscellaneous further modes (more than bike,peds,bus and without passenger)
2363     availableLanes.clear();
2364     for (int i = 0; i < (int)myLanes.size(); ++i) {
2365         const SVCPermissions perms = getPermissions(i);
2366         if ((perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) == 0 || (perms & SVC_PASSENGER) != 0 || isForbidden(perms)) {
2367             continue;
2368         }
2369         availableLanes.push_back(i);
2370     }
2371     if (availableLanes.size() > 0) {
2372         divideSelectedLanesOnEdges(outgoing, availableLanes);
2373     }
2374     // build connections for busses (possibly combined with bicycles)
2375     availableLanes.clear();
2376     for (int i = 0; i < (int)myLanes.size(); ++i) {
2377         const SVCPermissions perms = getPermissions(i);
2378         if (perms != SVC_BUS && perms != (SVC_BUS | SVC_BICYCLE)) {
2379             continue;
2380         }
2381         availableLanes.push_back(i);
2382     }
2383     if (availableLanes.size() > 0) {
2384         divideSelectedLanesOnEdges(outgoing, availableLanes);
2385     }
2386     // build connections for bicycles (possibly combined with pedestrians)
2387     availableLanes.clear();
2388     for (int i = 0; i < (int)myLanes.size(); ++i) {
2389         const SVCPermissions perms = getPermissions(i);
2390         if (perms != SVC_BICYCLE && perms != (SVC_BICYCLE | SVC_PEDESTRIAN)) {
2391             continue;
2392         }
2393         availableLanes.push_back(i);
2394     }
2395     if (availableLanes.size() > 0) {
2396         divideSelectedLanesOnEdges(outgoing, availableLanes);
2397     }
2398     // clean up unassigned fromLanes
2399     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
2400         if ((*i).fromLane == -1) {
2401             i = myConnections.erase(i);
2402         } else {
2403             ++i;
2404         }
2405     }
2406     sortOutgoingConnectionsByIndex();
2407 }
2408 
2409 
2410 void
divideSelectedLanesOnEdges(const EdgeVector * outgoing,const std::vector<int> & availableLanes)2411 NBEdge::divideSelectedLanesOnEdges(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
2412     const std::vector<int>& priorities = prepareEdgePriorities(outgoing, availableLanes);
2413     if (priorities.empty()) {
2414         return;
2415     }
2416 #ifdef DEBUG_CONNECTION_GUESSING
2417     if (DEBUGCOND) {
2418         std::cout << "divideSelectedLanesOnEdges " << getID() << " out=" << toString(*outgoing) << " prios=" << toString(priorities) << " avail=" << toString(availableLanes) << "\n";
2419     }
2420 #endif
2421     // compute the resulting number of lanes that should be used to reach the following edge
2422     const int numOutgoing = (int)outgoing->size();
2423     std::vector<int> resultingLanesFactor;
2424     resultingLanesFactor.reserve(numOutgoing);
2425     int minResulting = std::numeric_limits<int>::max();
2426     for (int i = 0; i < numOutgoing; i++) {
2427         // res / minResulting will be the number of lanes which are meant to reach the current outgoing edge
2428         const int res = priorities[i] * (int)availableLanes.size();
2429         resultingLanesFactor.push_back(res);
2430         if (minResulting > res && res > 0) {
2431             // prevent minResulting from becoming 0
2432             minResulting = res;
2433         }
2434     }
2435     // compute the number of virtual edges
2436     //  a virtual edge is used as a replacement for a real edge from now on
2437     //  it shall allow to divide the existing lanes on this structure without
2438     //  regarding the structure of outgoing edges
2439     int numVirtual = 0;
2440     // compute the transition from virtual to real edges
2441     EdgeVector transition;
2442     transition.reserve(numOutgoing);
2443     for (int i = 0; i < numOutgoing; i++) {
2444         // tmpNum will be the number of connections from this edge to the next edge
2445         assert(i < (int)resultingLanesFactor.size());
2446         const int tmpNum = (resultingLanesFactor[i] + minResulting - 1) / minResulting; // integer division rounding up
2447         numVirtual += tmpNum;
2448         for (int j = 0; j < tmpNum; j++) {
2449             transition.push_back((*outgoing)[i]);
2450         }
2451     }
2452 #ifdef DEBUG_CONNECTION_GUESSING
2453     if (DEBUGCOND) {
2454         std::cout << "   minResulting=" << minResulting << " numVirtual=" << numVirtual << " availLanes=" << toString(availableLanes) << " resLanes=" << toString(resultingLanesFactor) << " transition=" << toString(transition) << "\n";
2455     }
2456 #endif
2457 
2458     // assign lanes to edges
2459     //  (conversion from virtual to real edges is done)
2460     ToEdgeConnectionsAdder adder(transition);
2461     Bresenham::compute(&adder, static_cast<int>(availableLanes.size()), numVirtual);
2462     const std::map<NBEdge*, std::vector<int> >& l2eConns = adder.getBuiltConnections();
2463     for (NBEdge* const target : *outgoing) {
2464         assert(l2eConns.find(target) != l2eConns.end());
2465         for (const int j : l2eConns.find(target)->second) {
2466             const int fromIndex = availableLanes[j];
2467             if ((getPermissions(fromIndex) & target->getPermissions()) == 0) {
2468                 // exclude connection if fromLane and toEdge have no common permissions
2469                 continue;
2470             }
2471             if ((getPermissions(fromIndex) & target->getPermissions()) == SVC_PEDESTRIAN) {
2472                 // exclude connection if the only commonly permitted class are pedestrians
2473                 // these connections are later built in NBNode::buildWalkingAreas
2474                 continue;
2475             }
2476             // avoid building more connections than the edge has viable lanes (earlier
2477             // ones have precedence). This is necessary when running divideSelectedLanesOnEdges more than once.
2478             //    @todo To decide which target lanes are still available we need to do a
2479             // preliminary lane-to-lane assignment in regard to permissions (rather than to ordering)
2480             const int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
2481             int targetLanes = target->getNumLanes();
2482             if (target->getPermissions(0) == SVC_PEDESTRIAN) {
2483                 --targetLanes;
2484             }
2485             if (numConsToTarget >= targetLanes) {
2486                 // let bicycles move onto the road to allow continuation
2487                 // the speed limit is taken from rural roads (which allow cycles)
2488                 // (pending implementation of #1859)
2489                 if (getPermissions(fromIndex) == SVC_BICYCLE && getSpeed() <= (101 / 3.6)) {
2490                     for (NBEdge::Lane& lane : myLanes) {
2491                         if (lane.permissions != SVC_PEDESTRIAN) {
2492                             lane.permissions |= SVC_BICYCLE;
2493                         }
2494                     }
2495                 }
2496                 continue;
2497             }
2498             if (myLanes[fromIndex].connectionsDone) {
2499                 // we already have complete information about connections from
2500                 // this lane. do not add anything else
2501 #ifdef DEBUG_CONNECTION_GUESSING
2502                 if (DEBUGCOND) {
2503                     std::cout << "     connectionsDone from " << getID() << "_" << fromIndex << ": ";
2504                     for (const Connection& c : getConnectionsFromLane(fromIndex)) {
2505                         std::cout << c.getDescription(this) << ", ";
2506                     }
2507                     std::cout << "\n";
2508                 }
2509 #endif
2510                 continue;
2511             }
2512             myConnections.push_back(Connection(fromIndex, target, -1));
2513 #ifdef DEBUG_CONNECTION_GUESSING
2514             if (DEBUGCOND) {
2515                 std::cout << "     request connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
2516             }
2517 #endif
2518         }
2519     }
2520 
2521     addStraightConnections(outgoing, availableLanes, priorities);
2522 }
2523 
2524 
2525 void
addStraightConnections(const EdgeVector * outgoing,const std::vector<int> & availableLanes,const std::vector<int> & priorities)2526 NBEdge::addStraightConnections(const EdgeVector* outgoing, const std::vector<int>& availableLanes, const std::vector<int>& priorities) {
2527     // ensure sufficient straight connections for the (highest-priority) straight target
2528     const int numOutgoing = (int) outgoing->size();
2529     NBEdge* target = nullptr;
2530     NBEdge* rightOfTarget = nullptr;
2531     NBEdge* leftOfTarget = nullptr;
2532     int maxPrio = 0;
2533     for (int i = 0; i < numOutgoing; i++) {
2534         if (maxPrio < priorities[i]) {
2535             const LinkDirection dir = myTo->getDirection(this, (*outgoing)[i]);
2536             if (dir == LINKDIR_STRAIGHT) {
2537                 maxPrio = priorities[i];
2538                 target = (*outgoing)[i];
2539                 rightOfTarget = i == 0 ? outgoing->back() : (*outgoing)[i - 1];
2540                 leftOfTarget = i + 1 == numOutgoing ? outgoing->front() : (*outgoing)[i + 1];
2541             }
2542         }
2543     }
2544     if (target == nullptr) {
2545         return;
2546     }
2547     int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
2548     int targetLanes = (int)target->getNumLanes();
2549     if (target->getPermissions(0) == SVC_PEDESTRIAN) {
2550         --targetLanes;
2551     }
2552     const int numDesiredConsToTarget = MIN2(targetLanes, (int)availableLanes.size());
2553 #ifdef DEBUG_CONNECTION_GUESSING
2554     if (DEBUGCOND) {
2555         std::cout << "  checking extra lanes for target=" << target->getID() << " cons=" << numConsToTarget << " desired=" << numDesiredConsToTarget << "\n";
2556     }
2557 #endif
2558     std::vector<int>::const_iterator it_avail = availableLanes.begin();
2559     while (numConsToTarget < numDesiredConsToTarget && it_avail != availableLanes.end()) {
2560         const int fromIndex = *it_avail;
2561         if (
2562             // not yet connected
2563             (count_if(myConnections.begin(), myConnections.end(), connections_finder(fromIndex, target, -1)) == 0)
2564             // matching permissions
2565             && ((getPermissions(fromIndex) & target->getPermissions()) != 0)
2566             // more than pedestrians
2567             && ((getPermissions(fromIndex) & target->getPermissions()) != SVC_PEDESTRIAN)
2568             // lane not yet fully defined
2569             && !myLanes[fromIndex].connectionsDone
2570         ) {
2571 #ifdef DEBUG_CONNECTION_GUESSING
2572             if (DEBUGCOND) {
2573                 std::cout << "    candidate from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
2574             }
2575 #endif
2576             // prevent same-edge conflicts
2577             if (
2578                 // no outgoing connections to the right from further left
2579                 ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
2580                 // no outgoing connections to the left from further right
2581                 && (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)) {
2582 #ifdef DEBUG_CONNECTION_GUESSING
2583                 if (DEBUGCOND) {
2584                     std::cout << "     request additional connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
2585                 }
2586 #endif
2587                 myConnections.push_back(Connection(fromIndex, target, -1));
2588                 numConsToTarget++;
2589             } else {
2590 #ifdef DEBUG_CONNECTION_GUESSING
2591                 if (DEBUGCOND) std::cout
2592                             << "     fail check1="
2593                             << ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
2594                             << " check2=" << (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)
2595                             << " rightOfTarget=" << rightOfTarget->getID()
2596                             << " leftOfTarget=" << leftOfTarget->getID()
2597                             << "\n";
2598 #endif
2599 
2600             }
2601         }
2602         ++it_avail;
2603     }
2604 }
2605 
2606 
2607 const std::vector<int>
prepareEdgePriorities(const EdgeVector * outgoing,const std::vector<int> & availableLanes)2608 NBEdge::prepareEdgePriorities(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
2609     std::vector<int> priorities;
2610     MainDirections mainDirections(*outgoing, this, myTo, availableLanes);
2611     const int dist = mainDirections.getStraightest();
2612     if (dist == -1) {
2613         return priorities;
2614     }
2615     // copy the priorities first
2616     priorities.reserve(outgoing->size());
2617     for (const NBEdge* const out : *outgoing) {
2618         int prio = NBNode::isTrafficLight(myTo->getType()) ? 0 : out->getJunctionPriority(myTo);
2619         assert((prio + 1) * 2 > 0);
2620         prio = (prio + 1) * 2;
2621         priorities.push_back(prio);
2622     }
2623     // when the right turning direction has not a higher priority, divide
2624     //  the importance by 2 due to the possibility to leave the junction
2625     //  faster from this lane
2626 #ifdef DEBUG_CONNECTION_GUESSING
2627     if (DEBUGCOND) std::cout << "  prepareEdgePriorities " << getID()
2628                                  << " outgoing=" << toString(*outgoing)
2629                                  << " priorities1=" << toString(priorities)
2630                                  << " dist=" << dist
2631                                  << "\n";
2632 #endif
2633     if (dist != 0 && !mainDirections.includes(MainDirections::DIR_RIGHTMOST)) {
2634         assert(priorities.size() > 0);
2635         priorities[0] /= 2;
2636 #ifdef DEBUG_CONNECTION_GUESSING
2637         if (DEBUGCOND) {
2638             std::cout << "   priorities2=" << toString(priorities) << "\n";
2639         }
2640 #endif
2641     }
2642     // HEURISTIC:
2643     // when no higher priority exists, let the forward direction be
2644     //  the main direction
2645     if (mainDirections.empty()) {
2646         assert(dist < (int)priorities.size());
2647         priorities[dist] *= 2;
2648 #ifdef DEBUG_CONNECTION_GUESSING
2649         if (DEBUGCOND) {
2650             std::cout << "   priorities3=" << toString(priorities) << "\n";
2651         }
2652 #endif
2653     }
2654     if (NBNode::isTrafficLight(myTo->getType())) {
2655         priorities[dist] += 1;
2656     } else {
2657         // try to ensure separation of left turns
2658         if (mainDirections.includes(MainDirections::DIR_RIGHTMOST) && mainDirections.includes(MainDirections::DIR_LEFTMOST)) {
2659             priorities[0] /= 4;
2660             priorities[(int)priorities.size() - 1] /= 2;
2661 #ifdef DEBUG_CONNECTION_GUESSING
2662             if (DEBUGCOND) {
2663                 std::cout << "   priorities6=" << toString(priorities) << "\n";
2664             }
2665 #endif
2666         }
2667     }
2668     if (mainDirections.includes(MainDirections::DIR_FORWARD)) {
2669         if (myLanes.size() > 2) {
2670             priorities[dist] *= 2;
2671 #ifdef DEBUG_CONNECTION_GUESSING
2672             if (DEBUGCOND) {
2673                 std::cout << "   priorities4=" << toString(priorities) << "\n";
2674             }
2675 #endif
2676         } else {
2677             priorities[dist] *= 3;
2678 #ifdef DEBUG_CONNECTION_GUESSING
2679             if (DEBUGCOND) {
2680                 std::cout << "   priorities5=" << toString(priorities) << "\n";
2681             }
2682 #endif
2683         }
2684     }
2685     return priorities;
2686 }
2687 
2688 
2689 void
appendTurnaround(bool noTLSControlled,bool onlyDeadends,bool noGeometryLike,bool checkPermissions)2690 NBEdge::appendTurnaround(bool noTLSControlled, bool onlyDeadends, bool noGeometryLike, bool checkPermissions) {
2691     // do nothing if no turnaround is known
2692     if (myTurnDestination == nullptr || myTo->getType() == NODETYPE_RAIL_CROSSING) {
2693         return;
2694     }
2695     // do nothing if the destination node is controlled by a tls and no turnarounds
2696     //  shall be appended for such junctions
2697     if (noTLSControlled && myTo->isTLControlled()) {
2698         return;
2699     }
2700     bool isDeadEnd = true;
2701     for (const Connection& c : myConnections) {
2702         if ((c.toEdge->getPermissions(c.toLane)
2703                 & getPermissions(c.fromLane)
2704                 & SVC_PASSENGER) != 0
2705                 || (c.toEdge->getPermissions() & getPermissions()) == getPermissions()) {
2706             isDeadEnd = false;
2707             break;
2708         }
2709     }
2710     if (onlyDeadends && !isDeadEnd) {
2711         return;
2712     }
2713     const int fromLane = (int)myLanes.size() - 1;
2714     const int toLane = (int)myTurnDestination->getNumLanes() - 1;
2715     if (checkPermissions) {
2716         if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == 0) {
2717             // exclude connection if fromLane and toEdge have no common permissions
2718             return;
2719         }
2720         if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == SVC_PEDESTRIAN) {
2721             // exclude connection if the only commonly permitted class are pedestrians
2722             // these connections are later built in NBNode::buildWalkingAreas
2723             return;
2724         }
2725     }
2726     // avoid railway turn-arounds
2727     if (isRailway(getPermissions()) && isRailway(myTurnDestination->getPermissions())
2728             && fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), myTurnDestination->getAngleAtNode(myTo))) > 90) {
2729         // except at dead-ends on bidi-edges where they model a reversal in train direction
2730         // @todo #4382: once the network fringe is tagged, it also should not receive turn-arounds)
2731         if (isBidiRail() && isRailDeadEnd()) {
2732             // add a slow connection because direction-reversal implies stopping
2733             setConnection(fromLane, myTurnDestination, toLane, L2L_VALIDATED, false, false, true, UNSPECIFIED_CONTPOS, UNSPECIFIED_VISIBILITY_DISTANCE, SUMO_const_haltingSpeed);
2734             return;
2735         } else {
2736             return;
2737         }
2738     };
2739     if (noGeometryLike && myTo->geometryLike() && !isDeadEnd) {
2740         // make sure the turnDestination has other incoming edges
2741         EdgeVector turnIncoming = myTurnDestination->getIncomingEdges();
2742         if (turnIncoming.size() > 1) {
2743             // this edge is always part of incoming
2744             return;
2745         }
2746     }
2747     setConnection(fromLane, myTurnDestination, toLane, L2L_VALIDATED);
2748 }
2749 
2750 
2751 bool
isTurningDirectionAt(const NBEdge * const edge) const2752 NBEdge::isTurningDirectionAt(const NBEdge* const edge) const {
2753     // maybe it was already set as the turning direction
2754     if (edge == myTurnDestination) {
2755         return true;
2756     } else if (myTurnDestination != nullptr) {
2757         // otherwise - it's not if a turning direction exists
2758         return false;
2759     }
2760     return edge == myPossibleTurnDestination;
2761 }
2762 
2763 
2764 NBNode*
tryGetNodeAtPosition(double pos,double tolerance) const2765 NBEdge::tryGetNodeAtPosition(double pos, double tolerance) const {
2766     // return the from-node when the position is at the begin of the edge
2767     if (pos < tolerance) {
2768         return myFrom;
2769     }
2770     // return the to-node when the position is at the end of the edge
2771     if (pos > myLength - tolerance) {
2772         return myTo;
2773     }
2774     return nullptr;
2775 }
2776 
2777 
2778 void
moveOutgoingConnectionsFrom(NBEdge * e,int laneOff)2779 NBEdge::moveOutgoingConnectionsFrom(NBEdge* e, int laneOff) {
2780     int lanes = e->getNumLanes();
2781     for (int i = 0; i < lanes; i++) {
2782         std::vector<NBEdge::Connection> elv = e->getConnectionsFromLane(i);
2783         for (std::vector<NBEdge::Connection>::iterator j = elv.begin(); j != elv.end(); j++) {
2784             NBEdge::Connection el = *j;
2785             assert(el.tlID == "");
2786             addLane2LaneConnection(i + laneOff, el.toEdge, el.toLane, L2L_COMPUTED);
2787         }
2788     }
2789 }
2790 
2791 
2792 bool
lanesWereAssigned() const2793 NBEdge::lanesWereAssigned() const {
2794     return myStep == LANES2LANES_DONE || myStep == LANES2LANES_USER;
2795 }
2796 
2797 
2798 double
getMaxLaneOffset()2799 NBEdge::getMaxLaneOffset() {
2800     return (double) SUMO_const_laneWidthAndOffset * myLanes.size();
2801 }
2802 
2803 
2804 bool
mayBeTLSControlled(int fromLane,NBEdge * toEdge,int toLane) const2805 NBEdge::mayBeTLSControlled(int fromLane, NBEdge* toEdge, int toLane) const {
2806     for (const Connection& c : myConnections) {
2807         if (c.fromLane == fromLane && c.toEdge == toEdge && c.toLane == toLane && c.uncontrolled) {
2808             return false;
2809         }
2810     }
2811     return true;
2812 }
2813 
2814 
2815 bool
setControllingTLInformation(const NBConnection & c,const std::string & tlID)2816 NBEdge::setControllingTLInformation(const NBConnection& c, const std::string& tlID) {
2817     const int fromLane = c.getFromLane();
2818     NBEdge* toEdge = c.getTo();
2819     const int toLane = c.getToLane();
2820     const int tlIndex = c.getTLIndex();
2821     // check whether the connection was not set as not to be controled previously
2822     if (!mayBeTLSControlled(fromLane, toEdge, toLane)) {
2823         return false;
2824     }
2825 
2826     assert(fromLane < 0 || fromLane < (int) myLanes.size());
2827     // try to use information about the connections if given
2828     if (fromLane >= 0 && toLane >= 0) {
2829         // find the specified connection
2830         std::vector<Connection>::iterator i =
2831             find_if(myConnections.begin(), myConnections.end(), connections_finder(fromLane, toEdge, toLane));
2832         // ok, we have to test this as on the removal of self-loop edges some connections
2833         //  will be reassigned
2834         if (i != myConnections.end()) {
2835             // get the connection
2836             Connection& connection = *i;
2837             // set the information about the tl
2838             connection.tlID = tlID;
2839             connection.tlLinkIndex = tlIndex;
2840             return true;
2841         }
2842     }
2843     // if the original connection was not found, set the information for all
2844     //  connections
2845     int no = 0;
2846     bool hadError = false;
2847     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
2848         if ((*i).toEdge != toEdge) {
2849             continue;
2850         }
2851         if (fromLane >= 0 && fromLane != (*i).fromLane) {
2852             continue;
2853         }
2854         if (toLane >= 0 && toLane != (*i).toLane) {
2855             continue;
2856         }
2857         if ((*i).tlID == "") {
2858             (*i).tlID = tlID;
2859             (*i).tlLinkIndex = tlIndex;
2860             no++;
2861         } else {
2862             if ((*i).tlID != tlID && (*i).tlLinkIndex == tlIndex) {
2863                 WRITE_WARNING("The lane '" + toString<int>((*i).fromLane) + "' on edge '" + getID() + "' already had a traffic light signal.");
2864                 hadError = true;
2865             }
2866         }
2867     }
2868     if (hadError && no == 0) {
2869         WRITE_WARNING("Could not set any signal of the tlLogic '" + tlID + "' (unknown group)");
2870     }
2871     return true;
2872 }
2873 
2874 
2875 void
clearControllingTLInformation()2876 NBEdge::clearControllingTLInformation() {
2877     for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); it++) {
2878         it->tlID = "";
2879     }
2880 }
2881 
2882 
2883 PositionVector
getCWBoundaryLine(const NBNode & n) const2884 NBEdge::getCWBoundaryLine(const NBNode& n) const {
2885     PositionVector ret;
2886     double width;
2887     int lane;
2888     if (myFrom == (&n)) {
2889         // outgoing
2890         lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
2891         ret = myLanes[lane].shape;
2892     } else {
2893         // incoming
2894         lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
2895         ret = myLanes[lane].shape.reverse();
2896     }
2897     width = getLaneWidth(lane);
2898     ret.move2side(width * 0.5);
2899     return ret;
2900 }
2901 
2902 
2903 PositionVector
getCCWBoundaryLine(const NBNode & n) const2904 NBEdge::getCCWBoundaryLine(const NBNode& n) const {
2905     PositionVector ret;
2906     double width;
2907     int lane;
2908     if (myFrom == (&n)) {
2909         // outgoing
2910         lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
2911         ret = myLanes[lane].shape;
2912     } else {
2913         // incoming
2914         lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
2915         ret = myLanes[lane].shape.reverse();
2916     }
2917     width = getLaneWidth(lane);
2918     ret.move2side(-width * 0.5);
2919     return ret;
2920 }
2921 
2922 
2923 bool
expandableBy(NBEdge * possContinuation,std::string & reason) const2924 NBEdge::expandableBy(NBEdge* possContinuation, std::string& reason) const {
2925     // ok, the number of lanes must match
2926     if (myLanes.size() != possContinuation->myLanes.size()) {
2927         reason = "laneNumber";
2928         return false;
2929     }
2930     // the priority, too (?)
2931     if (getPriority() != possContinuation->getPriority()) {
2932         reason = "priority";
2933         return false;
2934     }
2935     // the speed allowed
2936     if (mySpeed != possContinuation->mySpeed) {
2937         reason = "speed";
2938         return false;
2939     }
2940     // spreadtype should match or it will look ugly
2941     if (myLaneSpreadFunction != possContinuation->myLaneSpreadFunction) {
2942         reason = "spreadType";
2943         return false;
2944     }
2945     // do not create self loops
2946     if (myFrom == possContinuation->myTo) {
2947         reason = "loop";
2948         return false;
2949     }
2950     // matching lanes must have identical properties
2951     for (int i = 0; i < (int)myLanes.size(); i++) {
2952         if (myLanes[i].speed != possContinuation->myLanes[i].speed) {
2953             reason = "lane " + toString(i) + " speed";
2954             return false;
2955         } else if (myLanes[i].permissions != possContinuation->myLanes[i].permissions) {
2956             reason = "lane " + toString(i) + " permissions";
2957             return false;
2958         } else if (myLanes[i].width != possContinuation->myLanes[i].width) {
2959             reason = "lane " + toString(i) + " width";
2960             return false;
2961         }
2962     }
2963     // conserve bidi-rails
2964     if (isBidiRail() != possContinuation->isBidiRail()) {
2965         reason = "bidi-rail";
2966         return false;
2967     }
2968 
2969     // the vehicle class constraints, too
2970     /*!!!
2971     if (myAllowedOnLanes!=possContinuation->myAllowedOnLanes
2972             ||
2973             myNotAllowedOnLanes!=possContinuation->myNotAllowedOnLanes) {
2974         return false;
2975     }
2976     */
2977     // also, check whether the connections - if any exit do allow to join
2978     //  both edges
2979     // This edge must have a one-to-one connection to the following lanes
2980     switch (myStep) {
2981         case INIT_REJECT_CONNECTIONS:
2982             break;
2983         case INIT:
2984             break;
2985         case EDGE2EDGES: {
2986             // the following edge must be connected
2987             const EdgeVector& conn = getConnectedEdges();
2988             if (find(conn.begin(), conn.end(), possContinuation) == conn.end()) {
2989                 reason = "disconnected";
2990                 return false;
2991             }
2992         }
2993         break;
2994         case LANES2EDGES:
2995         case LANES2LANES_RECHECK:
2996         case LANES2LANES_DONE:
2997         case LANES2LANES_USER: {
2998             // the possible continuation must be connected
2999             if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(possContinuation)) == myConnections.end()) {
3000                 reason = "disconnected";
3001                 return false;
3002             }
3003             // all lanes must go to the possible continuation
3004             std::vector<int> conns = getConnectionLanes(possContinuation);
3005             const int offset = MAX2(0, getFirstNonPedestrianLaneIndex(NBNode::FORWARD, true));
3006             if (conns.size() < myLanes.size() - offset) {
3007                 reason = "some lanes disconnected";
3008                 return false;
3009             }
3010         }
3011         break;
3012         default:
3013             break;
3014     }
3015     return true;
3016 }
3017 
3018 
3019 void
append(NBEdge * e)3020 NBEdge::append(NBEdge* e) {
3021     // append geometry
3022     myGeom.append(e->myGeom);
3023     for (int i = 0; i < (int)myLanes.size(); i++) {
3024         myLanes[i].shape.append(e->myLanes[i].shape);
3025         if (myLanes[i].knowsParameter(SUMO_PARAM_ORIGID) || e->myLanes[i].knowsParameter(SUMO_PARAM_ORIGID)
3026                 || OptionsCont::getOptions().getBool("output.original-names")) {
3027             const std::string origID = myLanes[i].getParameter(SUMO_PARAM_ORIGID, getID());
3028             const std::string origID2 = e->myLanes[i].getParameter(SUMO_PARAM_ORIGID, e->getID());
3029             if (origID != origID2) {
3030                 myLanes[i].setParameter(SUMO_PARAM_ORIGID, origID + " " + origID2);
3031             }
3032         }
3033         myLanes[i].connectionsDone = e->myLanes[i].connectionsDone;
3034     }
3035     // recompute length
3036     myLength += e->myLength;
3037     // copy the connections and the building step if given
3038     myStep = e->myStep;
3039     myConnections = e->myConnections;
3040     myTurnDestination = e->myTurnDestination;
3041     myPossibleTurnDestination = e->myPossibleTurnDestination;
3042     myConnectionsToDelete = e->myConnectionsToDelete;
3043     // set the node
3044     myTo = e->myTo;
3045     myToBorder = e->myToBorder;
3046     if (e->knowsParameter("origTo")) {
3047         setParameter("origTo", e->getParameter("origTo"));
3048     }
3049     if (e->getSignalOffset() != UNSPECIFIED_SIGNAL_OFFSET) {
3050         mySignalOffset = e->getSignalOffset();
3051     } else if (mySignalOffset != UNSPECIFIED_SIGNAL_OFFSET) {
3052         mySignalOffset += e->getLength();
3053     }
3054     computeAngle(); // myEndAngle may be different now
3055 }
3056 
3057 
3058 bool
hasSignalisedConnectionTo(const NBEdge * const e) const3059 NBEdge::hasSignalisedConnectionTo(const NBEdge* const e) const {
3060     for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
3061         if ((*i).toEdge == e && (*i).tlID != "") {
3062             return true;
3063         }
3064     }
3065     return false;
3066 }
3067 
3068 
3069 NBEdge*
getTurnDestination(bool possibleDestination) const3070 NBEdge::getTurnDestination(bool possibleDestination) const {
3071     if (myTurnDestination == nullptr && possibleDestination) {
3072         return myPossibleTurnDestination;
3073     }
3074     return myTurnDestination;
3075 }
3076 
3077 
3078 std::string
getLaneID(int lane) const3079 NBEdge::getLaneID(int lane) const {
3080     return myID + "_" + toString(lane);
3081 }
3082 
3083 
3084 bool
isNearEnough2BeJoined2(NBEdge * e,double threshold) const3085 NBEdge::isNearEnough2BeJoined2(NBEdge* e, double threshold) const {
3086     std::vector<double> distances = myGeom.distances(e->getGeometry());
3087     assert(distances.size() > 0);
3088     return VectorHelper<double>::maxValue(distances) < threshold;
3089 }
3090 
3091 
3092 void
addLane(int index,bool recomputeShape,bool recomputeConnections,bool shiftIndices)3093 NBEdge::addLane(int index, bool recomputeShape, bool recomputeConnections, bool shiftIndices) {
3094     assert(index <= (int)myLanes.size());
3095     myLanes.insert(myLanes.begin() + index, Lane(this, ""));
3096     // copy attributes
3097     if (myLanes.size() > 1) {
3098         int templateIndex = index > 0 ? index - 1 : index + 1;
3099         myLanes[index].speed = myLanes[templateIndex].speed;
3100         myLanes[index].permissions = myLanes[templateIndex].permissions;
3101         myLanes[index].preferred = myLanes[templateIndex].preferred;
3102         myLanes[index].endOffset = myLanes[templateIndex].endOffset;
3103         myLanes[index].width = myLanes[templateIndex].width;
3104         myLanes[index].updateParameter(myLanes[templateIndex].getParametersMap());
3105     }
3106     const EdgeVector& incs = myFrom->getIncomingEdges();
3107     if (recomputeShape) {
3108         computeLaneShapes();
3109     }
3110     if (recomputeConnections) {
3111         for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
3112             (*i)->invalidateConnections(true);
3113         }
3114         invalidateConnections(true);
3115     } else if (shiftIndices) {
3116         // shift outgoing connections above the added lane to the left
3117         for (Connection& c : myConnections) {
3118             if (c.fromLane >= index) {
3119                 c.fromLane += 1;
3120             }
3121         }
3122         // shift incoming connections above the added lane to the left
3123         for (NBEdge* inc : myFrom->getIncomingEdges()) {
3124             for (Connection& c : inc->myConnections) {
3125                 if (c.toEdge == this && c.toLane >= index) {
3126                     c.toLane += 1;
3127                 }
3128             }
3129         }
3130         myFrom->shiftTLConnectionLaneIndex(this, +1, index - 1);
3131         myTo->shiftTLConnectionLaneIndex(this, +1, index - 1);
3132     }
3133 }
3134 
3135 void
incLaneNo(int by)3136 NBEdge::incLaneNo(int by) {
3137     int newLaneNo = (int)myLanes.size() + by;
3138     while ((int)myLanes.size() < newLaneNo) {
3139         // recompute shapes on last addition
3140         const bool recompute = ((int)myLanes.size() == newLaneNo - 1) && myStep < LANES2LANES_USER;
3141         addLane((int)myLanes.size(), recompute, recompute, false);
3142     }
3143 }
3144 
3145 
3146 void
deleteLane(int index,bool recompute,bool shiftIndices)3147 NBEdge::deleteLane(int index, bool recompute, bool shiftIndices) {
3148     assert(index < (int)myLanes.size());
3149     myLanes.erase(myLanes.begin() + index);
3150     if (recompute) {
3151         computeLaneShapes();
3152         const EdgeVector& incs = myFrom->getIncomingEdges();
3153         for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
3154             (*i)->invalidateConnections(true);
3155         }
3156         invalidateConnections(true);
3157     } else if (shiftIndices) {
3158         removeFromConnections(nullptr, index, -1, false, true);
3159         for (NBEdge* inc : myFrom->getIncomingEdges()) {
3160             inc->removeFromConnections(this, -1, index, false, true);
3161         }
3162     }
3163 }
3164 
3165 
3166 void
decLaneNo(int by)3167 NBEdge::decLaneNo(int by) {
3168     int newLaneNo = (int) myLanes.size() - by;
3169     assert(newLaneNo > 0);
3170     while ((int)myLanes.size() > newLaneNo) {
3171         // recompute shapes on last removal
3172         const bool recompute = (int)myLanes.size() == newLaneNo + 1 && myStep < LANES2LANES_USER;
3173         deleteLane((int)myLanes.size() - 1, recompute, false);
3174     }
3175 }
3176 
3177 
3178 void
markAsInLane2LaneState()3179 NBEdge::markAsInLane2LaneState() {
3180     assert(myTo->getOutgoingEdges().size() == 0);
3181     myStep = LANES2LANES_DONE;
3182 }
3183 
3184 
3185 void
allowVehicleClass(int lane,SUMOVehicleClass vclass)3186 NBEdge::allowVehicleClass(int lane, SUMOVehicleClass vclass) {
3187     if (lane < 0) { // all lanes are meant...
3188         for (int i = 0; i < (int)myLanes.size(); i++) {
3189             allowVehicleClass(i, vclass);
3190         }
3191     } else {
3192         assert(lane < (int)myLanes.size());
3193         myLanes[lane].permissions |= vclass;
3194     }
3195 }
3196 
3197 
3198 void
disallowVehicleClass(int lane,SUMOVehicleClass vclass)3199 NBEdge::disallowVehicleClass(int lane, SUMOVehicleClass vclass) {
3200     if (lane < 0) { // all lanes are meant...
3201         for (int i = 0; i < (int)myLanes.size(); i++) {
3202             disallowVehicleClass((int) i, vclass);
3203         }
3204     } else {
3205         assert(lane < (int)myLanes.size());
3206         myLanes[lane].permissions &= ~vclass;
3207     }
3208 }
3209 
3210 
3211 void
preferVehicleClass(int lane,SUMOVehicleClass vclass)3212 NBEdge::preferVehicleClass(int lane, SUMOVehicleClass vclass) {
3213     if (lane < 0) { // all lanes are meant...
3214         for (int i = 0; i < (int)myLanes.size(); i++) {
3215             allowVehicleClass(i, vclass);
3216         }
3217     } else {
3218         assert(lane < (int)myLanes.size());
3219         myLanes[lane].preferred |= vclass;
3220     }
3221 }
3222 
3223 
3224 void
setLaneWidth(int lane,double width)3225 NBEdge::setLaneWidth(int lane, double width) {
3226     if (lane < 0) {
3227         // all lanes are meant...
3228         myLaneWidth = width;
3229         for (int i = 0; i < (int)myLanes.size(); i++) {
3230             // ... do it for each lane
3231             setLaneWidth(i, width);
3232         }
3233         return;
3234     }
3235     assert(lane < (int)myLanes.size());
3236     myLanes[lane].width = width;
3237 }
3238 
3239 
3240 double
getLaneWidth(int lane) const3241 NBEdge::getLaneWidth(int lane) const {
3242     return myLanes[lane].width != UNSPECIFIED_WIDTH
3243            ? myLanes[lane].width
3244            : getLaneWidth() != UNSPECIFIED_WIDTH ? getLaneWidth() : SUMO_const_laneWidth;
3245 }
3246 
3247 
3248 double
getTotalWidth() const3249 NBEdge::getTotalWidth() const {
3250     double result = 0;
3251     for (int i = 0; i < (int)myLanes.size(); i++) {
3252         result += getLaneWidth(i);
3253     }
3254     return result;
3255 }
3256 
3257 double
getEndOffset(int lane) const3258 NBEdge::getEndOffset(int lane) const {
3259     return myLanes[lane].endOffset != UNSPECIFIED_OFFSET ? myLanes[lane].endOffset : getEndOffset();
3260 }
3261 
3262 
3263 const std::map<SVCPermissions, double>&
getStopOffsets(int lane) const3264 NBEdge::getStopOffsets(int lane) const {
3265     if (lane == -1) {
3266         return myStopOffsets;
3267     } else {
3268         return myLanes[lane].stopOffsets;
3269     }
3270 }
3271 
3272 void
setEndOffset(int lane,double offset)3273 NBEdge::setEndOffset(int lane, double offset) {
3274     if (lane < 0) {
3275         // all lanes are meant...
3276         myEndOffset = offset;
3277         for (int i = 0; i < (int)myLanes.size(); i++) {
3278             // ... do it for each lane
3279             setEndOffset(i, offset);
3280         }
3281         return;
3282     }
3283     assert(lane < (int)myLanes.size());
3284     myLanes[lane].endOffset = offset;
3285 }
3286 
3287 
3288 bool
setStopOffsets(int lane,std::map<int,double> offsets,bool overwrite)3289 NBEdge::setStopOffsets(int lane, std::map<int, double> offsets, bool overwrite) {
3290     if (lane < 0) {
3291         if (!overwrite && myStopOffsets.size() != 0) {
3292             return false;
3293         }
3294         // all lanes are meant...
3295         if (offsets.size() != 0 && 0 > offsets.begin()->second) {
3296             //  Edge length unknown at parsing time, thus check here.
3297             std::stringstream ss;
3298             ss << "Ignoring invalid stopOffset for edge " << getID() << " (negative offset).";
3299             WRITE_WARNING(ss.str());
3300             return false;
3301         } else {
3302             myStopOffsets = offsets;
3303         }
3304     } else {
3305         assert(lane < (int)myLanes.size());
3306         if (myLanes[lane].stopOffsets.size() == 0 || overwrite) {
3307             if (offsets.size() != 0 && 0 > offsets.begin()->second) {
3308                 //  Edge length unknown at parsing time, thus check here.
3309                 std::stringstream ss;
3310                 ss << "Ignoring invalid stopOffset for lane " << lane << " on edge " << getID() << " (negative offset).";
3311                 WRITE_WARNING(ss.str());
3312                 return false;
3313             } else {
3314                 myLanes[lane].stopOffsets = offsets;
3315             }
3316         }
3317     }
3318     return true;
3319 }
3320 
3321 
3322 void
setSpeed(int lane,double speed)3323 NBEdge::setSpeed(int lane, double speed) {
3324     if (lane < 0) {
3325         // all lanes are meant...
3326         mySpeed = speed;
3327         for (int i = 0; i < (int)myLanes.size(); i++) {
3328             // ... do it for each lane
3329             setSpeed(i, speed);
3330         }
3331         return;
3332     }
3333     assert(lane < (int)myLanes.size());
3334     myLanes[lane].speed = speed;
3335 }
3336 
3337 void
setAcceleration(int lane,bool accelRamp)3338 NBEdge::setAcceleration(int lane, bool accelRamp) {
3339     assert(lane >= 0);
3340     assert(lane < (int)myLanes.size());
3341     myLanes[lane].accelRamp = accelRamp;
3342 }
3343 
3344 
3345 void
setLaneShape(int lane,const PositionVector & shape)3346 NBEdge::setLaneShape(int lane, const PositionVector& shape) {
3347     assert(lane >= 0);
3348     assert(lane < (int)myLanes.size());
3349     myLanes[lane].customShape = shape;
3350 }
3351 
3352 
3353 void
setPermissions(SVCPermissions permissions,int lane)3354 NBEdge::setPermissions(SVCPermissions permissions, int lane) {
3355     if (lane < 0) {
3356         for (int i = 0; i < (int)myLanes.size(); i++) {
3357             // ... do it for each lane
3358             setPermissions(permissions, i);
3359         }
3360     } else {
3361         assert(lane < (int)myLanes.size());
3362         myLanes[lane].permissions = permissions;
3363     }
3364 }
3365 
3366 
3367 void
setPreferredVehicleClass(SVCPermissions permissions,int lane)3368 NBEdge::setPreferredVehicleClass(SVCPermissions permissions, int lane) {
3369     if (lane < 0) {
3370         for (int i = 0; i < (int)myLanes.size(); i++) {
3371             // ... do it for each lane
3372             setPreferredVehicleClass(permissions, i);
3373         }
3374     } else {
3375         assert(lane < (int)myLanes.size());
3376         myLanes[lane].preferred = permissions;
3377     }
3378 }
3379 
3380 
3381 SVCPermissions
getPermissions(int lane) const3382 NBEdge::getPermissions(int lane) const {
3383     if (lane < 0) {
3384         SVCPermissions result = 0;
3385         for (int i = 0; i < (int)myLanes.size(); i++) {
3386             result |= getPermissions(i);
3387         }
3388         return result;
3389     } else {
3390         assert(lane < (int)myLanes.size());
3391         return myLanes[lane].permissions;
3392     }
3393 }
3394 
3395 
3396 void
setLoadedLength(double val)3397 NBEdge::setLoadedLength(double val) {
3398     myLoadedLength = val;
3399 }
3400 
3401 
3402 void
dismissVehicleClassInformation()3403 NBEdge::dismissVehicleClassInformation() {
3404     for (std::vector<Lane>::iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
3405         (*i).permissions = SVCAll;
3406         (*i).preferred = 0;
3407     }
3408 }
3409 
3410 
3411 bool
connections_sorter(const Connection & c1,const Connection & c2)3412 NBEdge::connections_sorter(const Connection& c1, const Connection& c2) {
3413     if (c1.fromLane != c2.fromLane) {
3414         return c1.fromLane < c2.fromLane;
3415     }
3416     if (c1.toEdge != c2.toEdge) {
3417         return false; // do not change ordering among toEdges as this is determined by angle in an earlier step
3418     }
3419     return c1.toLane < c2.toLane;
3420 }
3421 
3422 
3423 int
getFirstNonPedestrianLaneIndex(int direction,bool exclusive) const3424 NBEdge::getFirstNonPedestrianLaneIndex(int direction, bool exclusive) const {
3425     assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
3426     const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
3427     const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
3428     for (int i = start; i != end; i += direction) {
3429         // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
3430         // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
3431         if ((exclusive && myLanes[i].permissions != SVC_PEDESTRIAN && myLanes[i].permissions != 0)
3432                 || (myLanes[i].permissions == SVCAll || ((myLanes[i].permissions & SVC_PEDESTRIAN) == 0 && myLanes[i].permissions != 0))) {
3433             return i;
3434         }
3435     }
3436     return -1;
3437 }
3438 
3439 int
getSpecialLane(SVCPermissions permissions) const3440 NBEdge::getSpecialLane(SVCPermissions permissions) const {
3441     for (int i = 0; i < (int)myLanes.size(); i++) {
3442         if (myLanes[i].permissions == permissions) {
3443             return i;
3444         }
3445     }
3446     return -1;
3447 }
3448 
3449 int
getFirstAllowedLaneIndex(int direction) const3450 NBEdge::getFirstAllowedLaneIndex(int direction) const {
3451     assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
3452     const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
3453     const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
3454     for (int i = start; i != end; i += direction) {
3455         if (myLanes[i].permissions != 0) {
3456             return i;
3457         }
3458     }
3459     return end - direction;
3460 }
3461 
3462 
3463 std::set<SVCPermissions>
getPermissionVariants(int iStart,int iEnd) const3464 NBEdge::getPermissionVariants(int iStart, int iEnd) const {
3465     std::set<SVCPermissions> result;
3466     if (iStart < 0 || iStart >= getNumLanes() || iEnd > getNumLanes()) {
3467         throw ProcessError("invalid indices iStart " + toString(iStart) + " iEnd " + toString(iEnd) + " for edge with " + toString(getNumLanes()) + " lanes.");
3468     }
3469     for (int i = iStart; i < iEnd; ++i) {
3470         result.insert(getPermissions(i));
3471     }
3472     return result;
3473 }
3474 
3475 
3476 double
getCrossingAngle(NBNode * node)3477 NBEdge::getCrossingAngle(NBNode* node) {
3478     double angle = getAngleAtNode(node) + (getFromNode() == node ? 180.0 : 0.0);
3479     if (angle < 0) {
3480         angle += 360.0;
3481     }
3482     if (angle >= 360) {
3483         angle -= 360.0;
3484     }
3485     if (gDebugFlag1) {
3486         std::cout << getID() << " angle=" << getAngleAtNode(node) << " convAngle=" << angle << "\n";
3487     }
3488     return angle;
3489 }
3490 
3491 
3492 NBEdge::Lane
getFirstNonPedestrianLane(int direction) const3493 NBEdge::getFirstNonPedestrianLane(int direction) const {
3494     int index = getFirstNonPedestrianLaneIndex(direction);
3495     if (index < 0) {
3496         throw ProcessError("Edge " + getID() + " allows pedestrians on all lanes");
3497     }
3498     return myLanes[index];
3499 }
3500 
3501 std::string
getSidewalkID()3502 NBEdge::getSidewalkID() {
3503     // see IntermodalEdge::getSidewalk()
3504     for (int i = 0; i < (int)myLanes.size(); i++) {
3505         if (myLanes[i].permissions == SVC_PEDESTRIAN) {
3506             return getLaneID(i);
3507         }
3508     }
3509     for (int i = 0; i < (int)myLanes.size(); i++) {
3510         if ((myLanes[i].permissions & SVC_PEDESTRIAN) != 0) {
3511             return getLaneID(i);
3512         }
3513     }
3514     return getLaneID(0);
3515 }
3516 
3517 void
addSidewalk(double width)3518 NBEdge::addSidewalk(double width) {
3519     addRestrictedLane(width, SVC_PEDESTRIAN);
3520 }
3521 
3522 
3523 void
restoreSidewalk(std::vector<NBEdge::Lane> oldLanes,PositionVector oldGeometry,std::vector<NBEdge::Connection> oldConnections)3524 NBEdge::restoreSidewalk(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
3525     restoreRestrictedLane(SVC_PEDESTRIAN, oldLanes, oldGeometry, oldConnections);
3526 }
3527 
3528 
3529 void
addBikeLane(double width)3530 NBEdge::addBikeLane(double width) {
3531     addRestrictedLane(width, SVC_BICYCLE);
3532 }
3533 
3534 
3535 void
restoreBikelane(std::vector<NBEdge::Lane> oldLanes,PositionVector oldGeometry,std::vector<NBEdge::Connection> oldConnections)3536 NBEdge::restoreBikelane(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
3537     restoreRestrictedLane(SVC_BICYCLE, oldLanes, oldGeometry, oldConnections);
3538 }
3539 
3540 
3541 void
addRestrictedLane(double width,SUMOVehicleClass vclass)3542 NBEdge::addRestrictedLane(double width, SUMOVehicleClass vclass) {
3543     for (const Lane& lane : myLanes) {
3544         if (lane.permissions == vclass) {
3545             WRITE_WARNING("Edge '" + getID() + "' already has a dedicated lane for " + toString(vclass) + "s. Not adding another one.");
3546             return;
3547         }
3548     }
3549     if (myLaneSpreadFunction == LANESPREAD_CENTER) {
3550         myGeom.move2side(width / 2);
3551     }
3552     // disallow pedestrians on all lanes to ensure that sidewalks are used and
3553     // crossings can be guessed
3554     disallowVehicleClass(-1, vclass);
3555     // add new lane
3556     myLanes.insert(myLanes.begin(), Lane(this, myLanes[0].getParameter(SUMO_PARAM_ORIGID)));
3557     myLanes[0].permissions = vclass;
3558     myLanes[0].width = width;
3559     // shift outgoing connections to the left
3560     for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
3561         Connection& c = *it;
3562         if (c.fromLane >= 0) {
3563             c.fromLane += 1;
3564         }
3565     }
3566     // shift incoming connections to the left
3567     const EdgeVector& incoming = myFrom->getIncomingEdges();
3568     for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
3569         (*it)->shiftToLanesToEdge(this, 1);
3570     }
3571     myFrom->shiftTLConnectionLaneIndex(this, 1);
3572     myTo->shiftTLConnectionLaneIndex(this, 1);
3573     computeLaneShapes();
3574 }
3575 
3576 
3577 void
restoreRestrictedLane(SUMOVehicleClass vclass,std::vector<NBEdge::Lane> oldLanes,PositionVector oldGeometry,std::vector<NBEdge::Connection> oldConnections)3578 NBEdge::restoreRestrictedLane(SUMOVehicleClass vclass, std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
3579     // check that previously lane was transformed
3580     if (myLanes[0].permissions != vclass) {
3581         WRITE_WARNING("Edge '" + getID() + "' don't have a dedicated lane for " + toString(vclass) + "s. Cannot be restored");
3582         return;
3583     }
3584     // restore old values
3585     myGeom = oldGeometry;
3586     myLanes = oldLanes;
3587     myConnections = oldConnections;
3588     // shift incoming connections to the right
3589     const EdgeVector& incoming = myFrom->getIncomingEdges();
3590     for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
3591         (*it)->shiftToLanesToEdge(this, 0);
3592     }
3593     // Shift TL conections
3594     myFrom->shiftTLConnectionLaneIndex(this, 0);
3595     myTo->shiftTLConnectionLaneIndex(this, 0);
3596     computeLaneShapes();
3597 }
3598 
3599 
3600 void
shiftToLanesToEdge(NBEdge * to,int laneOff)3601 NBEdge::shiftToLanesToEdge(NBEdge* to, int laneOff) {
3602     /// XXX could we repurpose the function replaceInConnections ?
3603     for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
3604         if ((*it).toEdge == to && (*it).toLane >= 0) {
3605             (*it).toLane += laneOff;
3606         }
3607     }
3608 }
3609 
3610 
3611 void
shiftPositionAtNode(NBNode * node,NBEdge * other)3612 NBEdge::shiftPositionAtNode(NBNode* node, NBEdge* other) {
3613     if (myLaneSpreadFunction == LANESPREAD_CENTER && !isRailway(getPermissions())) {
3614         const int i = (node == myTo ? -1 : 0);
3615         const int i2 = (node == myTo ? 0 : -1);
3616         const double dist = myGeom[i].distanceTo2D(node->getPosition());
3617         const double neededOffset = (getTotalWidth() + getNumLanes() * SUMO_const_laneOffset) / 2;
3618         const double dist2 = MIN2(myGeom.distance2D(other->getGeometry()[i2]),
3619                                   other->getGeometry().distance2D(myGeom[i]));
3620         const double neededOffset2 = neededOffset + (other->getTotalWidth() + other->getNumLanes() * SUMO_const_laneOffset) / 2;
3621         if (dist < neededOffset && dist2 < neededOffset2) {
3622             PositionVector tmp = myGeom;
3623             // @note this doesn't work well for vissim networks
3624             //tmp.move2side(MIN2(neededOffset - dist, neededOffset2 - dist2));
3625             try {
3626                 tmp.move2side(neededOffset - dist);
3627                 myGeom[i] = tmp[i];
3628             } catch (InvalidArgument&) {
3629                 WRITE_WARNING("Could not avoid overlapping shape at node '" + node->getID() + "' for edge '" + getID() + "'");
3630             }
3631         }
3632     }
3633 }
3634 
3635 
3636 double
getFinalLength() const3637 NBEdge::getFinalLength() const {
3638     double result = getLoadedLength();
3639     if (OptionsCont::getOptions().getBool("no-internal-links") && !hasLoadedLength()) {
3640         // use length to junction center even if a modified geometry was given
3641         PositionVector geom = cutAtIntersection(myGeom);
3642         geom.push_back_noDoublePos(getToNode()->getCenter());
3643         geom.push_front_noDoublePos(getFromNode()->getCenter());
3644         result = geom.length();
3645     }
3646     double avgEndOffset = 0;
3647     for (const Lane& lane : myLanes) {
3648         avgEndOffset += lane.endOffset;
3649     }
3650     if (isBidiRail()) {
3651         avgEndOffset += myPossibleTurnDestination->getEndOffset();
3652     }
3653     avgEndOffset /= myLanes.size();
3654     return MAX2(result - avgEndOffset, POSITION_EPS);
3655 }
3656 
3657 void
setOrigID(const std::string origID)3658 NBEdge::setOrigID(const std::string origID) {
3659     if (origID != "") {
3660         for (int i = 0; i < (int)myLanes.size(); i++) {
3661             myLanes[i].setParameter(SUMO_PARAM_ORIGID, origID);
3662         }
3663     } else {
3664         // do not record empty origID parameter
3665         for (int i = 0; i < (int)myLanes.size(); i++) {
3666             myLanes[i].unsetParameter(SUMO_PARAM_ORIGID);
3667         }
3668     }
3669 }
3670 
3671 
3672 const EdgeVector&
getSuccessors(SUMOVehicleClass vClass) const3673 NBEdge::getSuccessors(SUMOVehicleClass vClass) const {
3674     // @todo cache successors instead of recomputing them every time
3675     mySuccessors.clear();
3676     //std::cout << "getSuccessors edge=" << getID() << " svc=" << toString(vClass) << " cons=" << myConnections.size() << "\n";
3677     for (const Connection& con : myConnections) {
3678         if (con.fromLane >= 0 && con.toLane >= 0 && con.toEdge != nullptr &&
3679                 (vClass == SVC_IGNORING || (getPermissions(con.fromLane)
3680                                             & con.toEdge->getPermissions(con.toLane) & vClass) != 0)
3681                 && std::find(mySuccessors.begin(), mySuccessors.end(), con.toEdge) == mySuccessors.end()) {
3682             mySuccessors.push_back(con.toEdge);
3683             //std::cout << "   succ=" << con.toEdge->getID() << "\n";
3684         }
3685     }
3686     return mySuccessors;
3687 }
3688 
3689 
3690 const NBConstEdgePairVector&
getViaSuccessors(SUMOVehicleClass vClass) const3691 NBEdge::getViaSuccessors(SUMOVehicleClass vClass) const {
3692     // @todo cache successors instead of recomputing them every time
3693     myViaSuccessors.clear();
3694     for (const Connection& con : myConnections) {
3695         std::pair<const NBEdge*, const NBEdge*> pair(con.toEdge, nullptr);
3696         if (con.fromLane >= 0 && con.toLane >= 0 && con.toEdge != nullptr &&
3697                 (getPermissions(con.fromLane)
3698                  & con.toEdge->getPermissions(con.toLane) & vClass) != 0
3699                 && std::find(myViaSuccessors.begin(), myViaSuccessors.end(), pair) == myViaSuccessors.end()) {
3700             myViaSuccessors.push_back(pair);
3701         }
3702     }
3703     return myViaSuccessors;
3704 }
3705 
3706 
3707 void
debugPrintConnections(bool outgoing,bool incoming) const3708 NBEdge::debugPrintConnections(bool outgoing, bool incoming) const {
3709     if (outgoing) {
3710         for (const Connection& c : myConnections) {
3711             std::cout << " " << getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
3712         }
3713     }
3714     if (incoming) {
3715         for (NBEdge* inc : myFrom->getIncomingEdges()) {
3716             for (Connection& c : inc->myConnections) {
3717                 if (c.toEdge == this) {
3718                     std::cout << " " << inc->getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
3719                 }
3720             }
3721         }
3722     }
3723 }
3724 
3725 
3726 int
getLaneIndexFromLaneID(const std::string laneID)3727 NBEdge::getLaneIndexFromLaneID(const std::string laneID) {
3728     return StringUtils::toInt(laneID.substr(laneID.rfind("_") + 1));
3729 }
3730 
3731 /****************************************************************************/
3732