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