Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file NBEdge.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Sascha Krieg
18 : /// @author Michael Behrisch
19 : /// @author Laura Bieker
20 : /// @author Leonhard Luecken
21 : /// @date Tue, 20 Nov 2001
22 : ///
23 : // Methods for the representation of a single edge
24 : /****************************************************************************/
25 : #include <config.h>
26 :
27 : #include <vector>
28 : #include <string>
29 : #include <algorithm>
30 : #include <cmath>
31 : #include <iomanip>
32 : #include <utils/common/MsgHandler.h>
33 : #include <utils/common/StringTokenizer.h>
34 : #include <utils/common/StringUtils.h>
35 : #include <utils/common/ToString.h>
36 : #include <utils/common/UtilExceptions.h>
37 : #include <utils/common/StdDefs.h>
38 : #include <utils/geom/GeomHelper.h>
39 : #include <utils/options/OptionsCont.h>
40 : #include "NBEdgeCont.h"
41 : #include "NBNode.h"
42 : #include "NBNodeCont.h"
43 : #include "NBContHelper.h"
44 : #include "NBHelpers.h"
45 : #include "NBTrafficLightDefinition.h"
46 : #include "NBOwnTLDef.h"
47 : #include "NBTypeCont.h"
48 : #include "NBEdge.h"
49 :
50 : //#define ADDITIONAL_WARNINGS
51 : //#define DEBUG_CONNECTION_GUESSING
52 : //#define DEBUG_CONNECTION_CHECKING
53 : //#define DEBUG_ANGLES
54 : //#define DEBUG_NODE_BORDER
55 : //#define DEBUG_REPLACECONNECTION
56 : //#define DEBUG_JUNCTIONPRIO
57 : //#define DEBUG_TURNSIGNS
58 : //#define DEBUG_CUT_LANES
59 : #define DEBUGID ""
60 : #define DEBUGCOND (getID() == DEBUGID)
61 : //#define DEBUGCOND (StringUtils::startsWith(getID(), DEBUGID))
62 : //#define DEBUGCOND (getID() == "22762377#1" || getID() == "146511467")
63 : #define DEBUGCOND2(obj) ((obj != 0 && (obj)->getID() == DEBUGID))
64 : //#define DEBUGCOND (true)
65 :
66 : // ===========================================================================
67 : // static members
68 : // ===========================================================================
69 : const double NBEdge::UNSPECIFIED_WIDTH = -1;
70 : const double NBEdge::UNSPECIFIED_OFFSET = 0;
71 : const double NBEdge::UNSPECIFIED_SPEED = -1;
72 : const double NBEdge::UNSPECIFIED_FRICTION = 1.;
73 : const double NBEdge::UNSPECIFIED_CONTPOS = -1;
74 : const double NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE = -1;
75 :
76 : const double NBEdge::UNSPECIFIED_SIGNAL_OFFSET = -1;
77 : const double NBEdge::UNSPECIFIED_LOADED_LENGTH = -1;
78 : const double NBEdge::ANGLE_LOOKAHEAD = 10.0;
79 : const int NBEdge::UNSPECIFIED_INTERNAL_LANE_INDEX = -1;
80 : const bool NBEdge::UNSPECIFIED_CONNECTION_UNCONTROLLED = false;
81 :
82 : double NBEdge::myDefaultConnectionLength = NBEdge::UNSPECIFIED_LOADED_LENGTH;
83 :
84 : NBEdge NBEdge::DummyEdge;
85 :
86 : ConstRouterEdgePairVector NBEdge::Connection::myViaSuccessors = ConstRouterEdgePairVector({ std::pair<NBRouterEdge*, NBRouterEdge*>(nullptr, nullptr) });
87 :
88 : // ===========================================================================
89 : // method definitions
90 : // ===========================================================================
91 : std::string
92 572200 : NBEdge::Connection::getInternalLaneID() const {
93 1144400 : return id + "_" + toString(internalLaneIndex);
94 : }
95 :
96 :
97 : std::string
98 86470 : NBEdge::Connection::getInternalViaLaneID() const {
99 172940 : return viaID + "_" + toString(internalViaLaneIndex);
100 : }
101 :
102 :
103 : std::string
104 351 : NBEdge::Connection::getDescription(const NBEdge* parent) const {
105 2106 : return (Named::getIDSecure(parent) + "_" + toString(fromLane) + "->" + Named::getIDSecure(toEdge) + "_" + toString(toLane)
106 1053 : + (permissions == SVC_UNSPECIFIED ? "" : " (" + getVehicleClassNames(permissions) + ")"));
107 : }
108 :
109 :
110 866032 : NBEdge::Connection::Connection(int fromLane_, NBEdge* toEdge_, int toLane_, const bool mayDefinitelyPass_) :
111 866032 : fromLane(fromLane_),
112 866032 : toEdge(toEdge_),
113 866032 : toLane(toLane_),
114 866032 : mayDefinitelyPass(mayDefinitelyPass_),
115 866032 : customLength(myDefaultConnectionLength),
116 1731959 : id(toEdge_ == nullptr ? "" : toEdge->getFromNode()->getID()) {
117 866032 : }
118 :
119 :
120 196500 : NBEdge::Lane::Lane(NBEdge* e, const std::string& origID_) :
121 196500 : speed(e->getSpeed()),
122 196500 : friction(e->getFriction()),
123 196500 : permissions(SVCAll),
124 196500 : preferred(0),
125 196500 : changeLeft(SVCAll),
126 196500 : changeRight(SVCAll),
127 196500 : endOffset(e->getEndOffset()),
128 196500 : laneStopOffset(e->getEdgeStopOffset()),
129 196500 : width(e->getLaneWidth()),
130 196500 : accelRamp(false),
131 393000 : connectionsDone(false) {
132 196500 : if (origID_ != "") {
133 33993 : setParameter(SUMO_PARAM_ORIGID, origID_);
134 : }
135 196500 : }
136 :
137 :
138 : /* -------------------------------------------------------------------------
139 : * NBEdge::ToEdgeConnectionsAdder-methods
140 : * ----------------------------------------------------------------------- */
141 : void
142 429618 : NBEdge::ToEdgeConnectionsAdder::execute(const int lane, const int virtEdge) {
143 : // check
144 : assert((int)myTransitions.size() > virtEdge);
145 : // get the approached edge
146 429618 : NBEdge* succEdge = myTransitions[virtEdge];
147 : std::vector<int> lanes;
148 :
149 : // check whether the currently regarded, approached edge has already
150 : // a connection starting at the edge which is currently being build
151 : std::map<NBEdge*, std::vector<int> >::iterator i = myConnections.find(succEdge);
152 429618 : if (i != myConnections.end()) {
153 : // if there were already lanes assigned, get them
154 338762 : lanes = (*i).second;
155 : }
156 :
157 : // check whether the current lane was already used to connect the currently
158 : // regarded approached edge
159 429618 : std::vector<int>::iterator j = std::find(lanes.begin(), lanes.end(), lane);
160 429618 : if (j == lanes.end()) {
161 : // if not, add it to the list
162 101610 : lanes.push_back(lane);
163 : }
164 : // set information about connecting lanes
165 429618 : myConnections[succEdge] = lanes;
166 429618 : }
167 :
168 :
169 :
170 : /* -------------------------------------------------------------------------
171 : * NBEdge::MainDirections-methods
172 : * ----------------------------------------------------------------------- */
173 46449 : NBEdge::MainDirections::MainDirections(const EdgeVector& outgoing, NBEdge* parent, NBNode* to, const std::vector<int>& availableLanes) : myStraightest(-1) {
174 : NBContHelper::edge_similar_direction_sorter sorter(parent);
175 46449 : const NBEdge* straight = nullptr;
176 137366 : for (const NBEdge* const out : outgoing) {
177 90917 : const SVCPermissions outPerms = out->getPermissions();
178 91099 : for (const int l : availableLanes) {
179 90984 : if ((parent->myLanes[l].permissions & outPerms) != 0) {
180 90802 : if (straight == nullptr || sorter(out, straight)) {
181 68564 : straight = out;
182 : }
183 : break;
184 : }
185 : }
186 : }
187 46449 : if (straight == nullptr) {
188 4097 : return;
189 : }
190 46400 : myStraightest = (int)std::distance(outgoing.begin(), std::find(outgoing.begin(), outgoing.end(), straight));
191 :
192 : // check whether the right turn has a higher priority
193 : assert(outgoing.size() > 0);
194 46400 : const LinkDirection straightestDir = to->getDirection(parent, straight);
195 : #ifdef DEBUG_CONNECTION_GUESSING
196 : if (DEBUGCOND2(parent)) {
197 : std::cout << " MainDirections edge=" << parent->getID() << " straightest=" << straight->getID() << " dir=" << toString(straightestDir) << "\n";
198 : }
199 : #endif
200 46400 : if (NBNode::isTrafficLight(to->getType()) &&
201 4350 : (straightestDir == LinkDirection::STRAIGHT || straightestDir == LinkDirection::PARTLEFT || straightestDir == LinkDirection::PARTRIGHT)) {
202 4048 : myDirs.push_back(MainDirections::Direction::FORWARD);
203 4048 : return;
204 : }
205 42352 : if (outgoing[0]->getJunctionPriority(to) == 1) {
206 22753 : myDirs.push_back(MainDirections::Direction::RIGHTMOST);
207 : }
208 : // check whether the left turn has a higher priority
209 42352 : if (outgoing.back()->getJunctionPriority(to) == 1) {
210 : // ok, the left turn belongs to the higher priorised edges on the junction
211 : // let's check, whether it has also a higher priority (lane number/speed)
212 : // than the current
213 22980 : if (outgoing.back()->getPriority() > straight->getPriority() ||
214 : outgoing.back()->getNumLanes() > straight->getNumLanes()) {
215 1228 : myDirs.push_back(MainDirections::Direction::LEFTMOST);
216 : }
217 : }
218 : // check whether the forward direction has a higher priority
219 : // check whether it has a higher priority and is going straight
220 42352 : if (straight->getJunctionPriority(to) == 1 && to->getDirection(parent, straight) == LinkDirection::STRAIGHT) {
221 20564 : myDirs.push_back(MainDirections::Direction::FORWARD);
222 : }
223 0 : }
224 :
225 :
226 46449 : NBEdge::MainDirections::~MainDirections() {}
227 :
228 :
229 : bool
230 46400 : NBEdge::MainDirections::empty() const {
231 46400 : return myDirs.empty();
232 : }
233 :
234 :
235 : bool
236 173676 : NBEdge::MainDirections::includes(Direction d) const {
237 173676 : return std::find(myDirs.begin(), myDirs.end(), d) != myDirs.end();
238 : }
239 :
240 :
241 : /* -------------------------------------------------------------------------
242 : * NBEdge::connections_relative_edgelane_sorter-methods
243 : * ----------------------------------------------------------------------- */
244 : int
245 228195 : NBEdge::connections_relative_edgelane_sorter::operator()(const Connection& c1, const Connection& c2) const {
246 228195 : if (c1.toEdge != c2.toEdge) {
247 180964 : return NBContHelper::relative_outgoing_edge_sorter(myEdge)(c1.toEdge, c2.toEdge);
248 : }
249 47231 : return c1.toLane < c2.toLane;
250 : }
251 :
252 :
253 : /* -------------------------------------------------------------------------
254 : * NBEdge-methods
255 : * ----------------------------------------------------------------------- */
256 15692 : NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to,
257 : std::string type, double speed, double friction, int nolanes,
258 : int priority, double laneWidth, double endOffset,
259 15692 : LaneSpreadFunction spread, const std::string& streetName) :
260 15692 : Named(StringUtils::convertUmlaute(id)),
261 15692 : myStep(EdgeBuildingStep::INIT),
262 15692 : myType(StringUtils::convertUmlaute(type)),
263 15692 : myFrom(from), myTo(to),
264 15692 : myStartAngle(0), myEndAngle(0), myTotalAngle(0),
265 15692 : myPriority(priority), mySpeed(speed), myFriction(friction),
266 15692 : myDistance(0),
267 15692 : myTurnDestination(nullptr),
268 15692 : myPossibleTurnDestination(nullptr),
269 15692 : myFromJunctionPriority(-1), myToJunctionPriority(-1),
270 15692 : myLaneSpreadFunction(spread), myEndOffset(endOffset),
271 15692 : myLaneWidth(laneWidth),
272 15692 : myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
273 15692 : myAmInTLS(false), myAmMacroscopicConnector(false),
274 15692 : myStreetName(streetName),
275 15692 : mySignalPosition(Position::INVALID),
276 15692 : mySignalNode(nullptr),
277 15692 : myIsOffRamp(false),
278 15692 : myIsBidi(false),
279 47076 : myIndex(-1) {
280 15692 : init(nolanes, false, "");
281 15692 : }
282 :
283 :
284 133414 : NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to,
285 : std::string type, double speed, double friction, int nolanes,
286 : int priority, double laneWidth, double endOffset,
287 : PositionVector geom,
288 : LaneSpreadFunction spread,
289 : const std::string& streetName,
290 : const std::string& origID,
291 133414 : bool tryIgnoreNodePositions) :
292 133414 : Named(StringUtils::convertUmlaute(id)),
293 133414 : myStep(EdgeBuildingStep::INIT),
294 133415 : myType(StringUtils::convertUmlaute(type)),
295 133414 : myFrom(from), myTo(to),
296 133414 : myStartAngle(0), myEndAngle(0), myTotalAngle(0),
297 133414 : myPriority(priority), mySpeed(speed), myFriction(friction),
298 133414 : myDistance(0),
299 133414 : myTurnDestination(nullptr),
300 133414 : myPossibleTurnDestination(nullptr),
301 133414 : myFromJunctionPriority(-1), myToJunctionPriority(-1),
302 133414 : myGeom(geom), myLaneSpreadFunction(spread), myEndOffset(endOffset),
303 133414 : myLaneWidth(laneWidth),
304 133414 : myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
305 133414 : myAmInTLS(false), myAmMacroscopicConnector(false),
306 133414 : myStreetName(streetName),
307 133414 : mySignalPosition(Position::INVALID),
308 133414 : mySignalNode(nullptr),
309 133414 : myIsOffRamp(false),
310 133414 : myIsBidi(false),
311 400242 : myIndex(-1) {
312 133414 : init(nolanes, tryIgnoreNodePositions, origID);
313 133426 : }
314 :
315 :
316 2716 : NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to, const NBEdge* tpl, const PositionVector& geom, int numLanes) :
317 2716 : Named(StringUtils::convertUmlaute(id)),
318 2716 : myStep(EdgeBuildingStep::INIT),
319 2716 : myType(tpl->getTypeID()),
320 2716 : myFrom(from), myTo(to),
321 2716 : myStartAngle(0), myEndAngle(0), myTotalAngle(0),
322 2716 : myPriority(tpl->getPriority()), mySpeed(tpl->getSpeed()),
323 2716 : myFriction(tpl->getFriction()),
324 2716 : myDistance(0),
325 2716 : myTurnDestination(nullptr),
326 2716 : myPossibleTurnDestination(nullptr),
327 2716 : myFromJunctionPriority(-1), myToJunctionPriority(-1),
328 : myGeom(geom),
329 2716 : myLaneSpreadFunction(tpl->getLaneSpreadFunction()),
330 2716 : myEndOffset(tpl->getEndOffset()),
331 2716 : myEdgeStopOffset(tpl->getEdgeStopOffset()),
332 2716 : myLaneWidth(tpl->getLaneWidth()),
333 2716 : myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
334 2716 : myAmInTLS(false),
335 2716 : myAmMacroscopicConnector(false),
336 2716 : myStreetName(tpl->getStreetName()),
337 2716 : mySignalPosition(to == tpl->myTo ? tpl->mySignalPosition : Position::INVALID),
338 2716 : mySignalNode(to == tpl->myTo ? tpl->mySignalNode : nullptr),
339 2716 : myIsOffRamp(false),
340 2716 : myIsBidi(tpl->myIsBidi),
341 5432 : myIndex(-1) {
342 3854 : init(numLanes > 0 ? numLanes : tpl->getNumLanes(), myGeom.size() > 0, "");
343 6643 : for (int i = 0; i < getNumLanes(); i++) {
344 3927 : const int tplIndex = MIN2(i, tpl->getNumLanes() - 1);
345 3927 : setSpeed(i, tpl->getLaneSpeed(tplIndex));
346 3927 : setFriction(i, tpl->getLaneFriction(tplIndex));
347 3927 : setPermissions(tpl->getPermissions(tplIndex), i);
348 3927 : setLaneWidth(i, tpl->myLanes[tplIndex].width);
349 3927 : setLaneType(i, tpl->myLanes[tplIndex].type);
350 3927 : myLanes[i].updateParameters(tpl->myLanes[tplIndex].getParametersMap());
351 3927 : if (to == tpl->myTo) {
352 1474 : setEndOffset(i, tpl->myLanes[tplIndex].endOffset);
353 1474 : setEdgeStopOffset(i, tpl->myLanes[tplIndex].laneStopOffset);
354 : }
355 : }
356 2716 : if (tpl->myLoadedLength > 0 && to == tpl->getFromNode() && from == tpl->getToNode() && geom == tpl->getGeometry().reverse()) {
357 3 : myLoadedLength = tpl->myLoadedLength;
358 : }
359 2716 : updateParameters(tpl->getParametersMap());
360 2716 : }
361 :
362 :
363 2583 : NBEdge::NBEdge() :
364 : Named("DUMMY"),
365 2583 : myStep(EdgeBuildingStep::INIT),
366 2583 : myFrom(nullptr), myTo(nullptr),
367 2583 : myStartAngle(0), myEndAngle(0), myTotalAngle(0),
368 2583 : myPriority(0), mySpeed(0), myFriction(UNSPECIFIED_FRICTION),
369 2583 : myDistance(0),
370 2583 : myTurnDestination(nullptr),
371 2583 : myPossibleTurnDestination(nullptr),
372 2583 : myFromJunctionPriority(-1), myToJunctionPriority(-1),
373 2583 : myLaneSpreadFunction(LaneSpreadFunction::RIGHT),
374 2583 : myEndOffset(0),
375 2583 : myEdgeStopOffset(StopOffset()),
376 2583 : myLaneWidth(0),
377 2583 : myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
378 2583 : myAmInTLS(false),
379 2583 : myAmMacroscopicConnector(false),
380 2583 : mySignalPosition(Position::INVALID),
381 7749 : mySignalNode(nullptr) {
382 2583 : }
383 :
384 :
385 : void
386 2251 : NBEdge::reinit(NBNode* from, NBNode* to, const std::string& type,
387 : double speed, double friction, int nolanes, int priority,
388 : PositionVector geom, double laneWidth, double endOffset,
389 : const std::string& streetName,
390 : LaneSpreadFunction spread,
391 : bool tryIgnoreNodePositions) {
392 2251 : if (myFrom != from) {
393 10 : myFrom->removeEdge(this, false);
394 : }
395 2251 : if (myTo != to) {
396 11 : myTo->removeEdge(this, false);
397 : }
398 2251 : myType = StringUtils::convertUmlaute(type);
399 2251 : myFrom = from;
400 2251 : myTo = to;
401 2251 : myPriority = priority;
402 : //?myTurnDestination(0),
403 : //?myFromJunctionPriority(-1), myToJunctionPriority(-1),
404 : myGeom = geom;
405 2251 : myLaneSpreadFunction = spread;
406 2251 : myLoadedLength = UNSPECIFIED_LOADED_LENGTH;
407 2251 : myStreetName = streetName;
408 : //?, myAmTurningWithAngle(0), myAmTurningOf(0),
409 : //?myAmInTLS(false), myAmMacroscopicConnector(false)
410 :
411 : // preserve lane-specific settings (geometry must be recomputed)
412 : // if new lanes are added they copy the values from the leftmost lane (if specified)
413 2251 : const std::vector<Lane> oldLanes = myLanes;
414 4502 : init(nolanes, tryIgnoreNodePositions, oldLanes.empty() ? "" : oldLanes[0].getParameter(SUMO_PARAM_ORIGID));
415 4594 : for (int i = 0; i < (int)nolanes; ++i) {
416 2343 : PositionVector newShape = myLanes[i].shape;
417 2343 : myLanes[i] = oldLanes[MIN2(i, (int)oldLanes.size() - 1)];
418 : myLanes[i].shape = newShape;
419 2343 : }
420 : // however, if the new edge defaults are explicityly given, they override the old settings
421 2251 : if (endOffset != UNSPECIFIED_OFFSET) {
422 3 : setEndOffset(-1, endOffset);
423 : }
424 2251 : if (laneWidth != UNSPECIFIED_WIDTH) {
425 2 : setLaneWidth(-1, laneWidth);
426 : }
427 2251 : if (speed != UNSPECIFIED_SPEED) {
428 25 : setSpeed(-1, speed);
429 : }
430 2251 : if (friction != UNSPECIFIED_FRICTION) {
431 0 : setFriction(-1, friction);
432 : }
433 2251 : }
434 :
435 :
436 : void
437 6358 : NBEdge::reinitNodes(NBNode* from, NBNode* to) {
438 : // connections may still be valid
439 6358 : if (from == nullptr || to == nullptr) {
440 0 : throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
441 : }
442 6358 : if (myFrom != from) {
443 3245 : myFrom->removeEdge(this, false);
444 : }
445 6358 : if (myTo != to) {
446 3071 : myTo->removeEdge(this, false);
447 : }
448 : // remove first from both nodes and then add to the new nodes
449 : // (otherwise reversing does not work)
450 6358 : if (myFrom != from) {
451 3245 : myFrom = from;
452 3245 : myFrom->addOutgoingEdge(this);
453 : }
454 6358 : if (myTo != to) {
455 3071 : myTo = to;
456 3071 : myTo->addIncomingEdge(this);
457 : }
458 6358 : computeAngle();
459 6358 : }
460 :
461 :
462 : void
463 154073 : NBEdge::init(int noLanes, bool tryIgnoreNodePositions, const std::string& origID) {
464 154073 : if (noLanes == 0) {
465 0 : throw ProcessError(TLF("Edge '%' needs at least one lane.", myID));
466 : }
467 154073 : if (myFrom == nullptr || myTo == nullptr) {
468 0 : throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
469 : }
470 154073 : if (!SUMOXMLDefinitions::isValidNetID(myID)) {
471 3 : throw ProcessError(TLF("Invalid edge id '%'.", myID));
472 : }
473 : // revisit geometry
474 : // should have at least two points at the end...
475 : // and in dome cases, the node positions must be added
476 : // attempt symmetrical removal for forward and backward direction
477 : // (very important for bidiRail)
478 154072 : if (myFrom->getID() < myTo->getID()) {
479 79407 : PositionVector reverse = myGeom.reverse();
480 79407 : reverse.removeDoublePoints(POSITION_EPS, true);
481 158814 : myGeom = reverse.reverse();
482 79407 : } else {
483 74665 : myGeom.removeDoublePoints(POSITION_EPS, true);
484 : }
485 :
486 154072 : if (!tryIgnoreNodePositions || myGeom.size() < 2) {
487 48385 : if (myGeom.size() == 0) {
488 48078 : myGeom.push_back(myFrom->getPosition());
489 48078 : myGeom.push_back(myTo->getPosition());
490 : } else {
491 307 : myGeom.push_back_noDoublePos(myTo->getPosition());
492 307 : myGeom.push_front_noDoublePos(myFrom->getPosition());
493 : }
494 : }
495 154072 : if (myGeom.size() < 2) {
496 : myGeom.clear();
497 0 : myGeom.push_back(myFrom->getPosition());
498 0 : myGeom.push_back(myTo->getPosition());
499 : }
500 154072 : if (myGeom.size() == 2 && myGeom[0] == myGeom[1]) {
501 12 : WRITE_WARNINGF(TL("Edge's '%' from- and to-node are at the same position."), myID);
502 4 : int patchIndex = myFrom->getID() < myTo->getID() ? 1 : 0;
503 4 : myGeom[patchIndex].add(Position(POSITION_EPS, POSITION_EPS));
504 : }
505 : // avoid degeneration of near-0-length geometrie when shifting later
506 154072 : myGeom.ensureMinLength(gPrecision);
507 : //
508 154072 : myFrom->addOutgoingEdge(this);
509 154072 : myTo->addIncomingEdge(this);
510 : // prepare container
511 : assert(myGeom.size() >= 2);
512 154072 : myLength = myGeom.length();
513 154072 : if ((int)myLanes.size() > noLanes) {
514 : // remove connections starting at the removed lanes
515 6 : for (int lane = noLanes; lane < (int)myLanes.size(); ++lane) {
516 3 : removeFromConnections(nullptr, lane, -1);
517 : }
518 : // remove connections targeting the removed lanes
519 3 : const EdgeVector& incoming = myFrom->getIncomingEdges();
520 6 : for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
521 6 : for (int lane = noLanes; lane < (int)myLanes.size(); ++lane) {
522 3 : (*i)->removeFromConnections(this, -1, lane);
523 : }
524 : }
525 : }
526 : myLanes.clear();
527 346212 : for (int i = 0; i < noLanes; i++) {
528 384280 : myLanes.push_back(Lane(this, origID));
529 : }
530 154072 : computeLaneShapes();
531 154072 : computeAngle();
532 :
533 : #ifdef DEBUG_CONNECTION_GUESSING
534 : if (DEBUGCOND) {
535 : std::cout << "init edge=" << getID() << "\n";
536 : for (Connection& c : myConnections) {
537 : std::cout << " conn " << c.getDescription(this) << "\n";
538 : }
539 : for (Connection& c : myConnectionsToDelete) {
540 : std::cout << " connToDelete " << c.getDescription(this) << "\n";
541 : }
542 : }
543 : #endif
544 154072 : }
545 :
546 :
547 306225 : NBEdge::~NBEdge() {}
548 :
549 :
550 : // ----------- Applying offset
551 : void
552 69203 : NBEdge::reshiftPosition(double xoff, double yoff) {
553 69203 : myGeom.add(xoff, yoff, 0);
554 158708 : for (Lane& lane : myLanes) {
555 89505 : lane.customShape.add(xoff, yoff, 0);
556 : }
557 69203 : computeLaneShapes(); // old shapes are dubious if computed with large coordinates
558 75666 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
559 6463 : (*i).customShape.add(xoff, yoff, 0);
560 : }
561 : if (mySignalPosition != Position::INVALID) {
562 : mySignalPosition.add(xoff, yoff);
563 : }
564 69203 : myFromBorder.add(xoff, yoff, 0);
565 69203 : myToBorder.add(xoff, yoff, 0);
566 69203 : computeEdgeShape();
567 69203 : computeAngle(); // update angles because they are numerically sensitive (especially where based on centroids)
568 69203 : }
569 :
570 :
571 : void
572 127841 : NBEdge::roundGeometry() {
573 127841 : myGeom.round(gPrecision);
574 292446 : for (Lane& lane : myLanes) {
575 164605 : lane.customShape.round(gPrecision);
576 : }
577 247755 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
578 119914 : (*i).customShape.round(gPrecision);
579 : }
580 127841 : }
581 :
582 :
583 : void
584 127841 : NBEdge::roundSpeed() {
585 127841 : mySpeed = roundDecimalToEven(mySpeed, gPrecision);
586 : // lane speeds are not used for computation but are compared to mySpeed in hasLaneSpecificSpeed
587 292446 : for (Lane& l : myLanes) {
588 164605 : l.speed = roundDecimalToEven(l.speed, gPrecision);
589 : }
590 127841 : }
591 :
592 : void
593 1134 : NBEdge::mirrorX() {
594 1134 : myGeom.mirrorX();
595 3001 : for (int i = 0; i < (int)myLanes.size(); i++) {
596 1867 : myLanes[i].shape.mirrorX();
597 1867 : myLanes[i].customShape.mirrorX();
598 : }
599 2482 : for (Connection& c : myConnections) {
600 1348 : c.shape.mirrorX();
601 1348 : c.viaShape.mirrorX();
602 1348 : c.customShape.mirrorX();
603 : }
604 : if (mySignalPosition != Position::INVALID) {
605 24 : mySignalPosition.sety(-mySignalPosition.y());
606 : }
607 1134 : computeAngle(); // update angles because they are numerically sensitive (especially where based on centroids)
608 1134 : }
609 :
610 :
611 : // ----------- Edge geometry access and computation
612 : const PositionVector
613 6426 : NBEdge::getInnerGeometry() const {
614 6426 : return myGeom.getSubpartByIndex(1, (int)myGeom.size() - 2);
615 : }
616 :
617 :
618 : bool
619 129656 : NBEdge::hasDefaultGeometry() const {
620 129656 : return myGeom.size() == 2 && hasDefaultGeometryEndpoints();
621 : }
622 :
623 :
624 : bool
625 71421 : NBEdge::hasDefaultGeometryEndpoints() const {
626 134182 : return myGeom.front().almostSame(myFrom->getPosition(), 0.01) &&
627 62761 : myGeom.back().almostSame(myTo->getPosition(), 0.01);
628 : }
629 :
630 :
631 : bool
632 214507 : NBEdge::hasDefaultGeometryEndpointAtNode(const NBNode* node) const {
633 : // do not extend past the node position
634 214507 : if (node == myFrom) {
635 : return myGeom.front() == node->getPosition();
636 : } else {
637 : assert(node == myTo);
638 : return myGeom.back() == node->getPosition();
639 : }
640 : }
641 :
642 : Position
643 5199 : NBEdge::getEndpointAtNode(const NBNode* node) const {
644 5199 : return node == myFrom ? myGeom.front() : myGeom.back();
645 : }
646 :
647 : void
648 0 : NBEdge::resetEndpointAtNode(const NBNode* node) {
649 : assert(myGeom.size() >= 2);
650 0 : if (node == myFrom) {
651 0 : myGeom[0] = myFrom->getPosition();
652 0 : } else if (node == myTo) {
653 0 : myGeom[-1] = myTo->getPosition();
654 : } else {
655 : assert(false);
656 : }
657 0 : }
658 :
659 : void
660 4481 : NBEdge::setGeometry(const PositionVector& s, bool inner) {
661 4481 : Position begin = myGeom.front(); // may differ from node position
662 4481 : Position end = myGeom.back(); // may differ from node position
663 : myGeom = s;
664 4481 : if (inner) {
665 0 : myGeom.insert(myGeom.begin(), begin);
666 0 : myGeom.push_back(end);
667 : }
668 : // ensure non-zero length (see ::init)
669 4481 : if (myGeom.size() == 2 && myGeom[0] == myGeom[1]) {
670 6 : WRITE_WARNINGF(TL("Edge's '%' from- and to-node are at the same position."), myID);
671 2 : int patchIndex = myFrom->getID() < myTo->getID() ? 1 : 0;
672 2 : myGeom[patchIndex].add(Position(POSITION_EPS, POSITION_EPS));
673 : }
674 4481 : computeLaneShapes();
675 4481 : computeAngle();
676 4481 : myLength = myGeom.length();
677 4481 : }
678 :
679 :
680 : void
681 0 : NBEdge::extendGeometryAtNode(const NBNode* node, double maxExtent) {
682 : //std::cout << "extendGeometryAtNode edge=" << getID() << " node=" << node->getID() << " nodePos=" << node->getPosition() << " extent=" << maxExtent << " geom=" << myGeom;
683 0 : if (node == myFrom) {
684 0 : myGeom.extrapolate(maxExtent, true);
685 0 : double offset = myGeom.nearest_offset_to_point2D(node->getPosition());
686 : //std::cout << " geom2=" << myGeom << " offset=" << offset;
687 0 : if (offset != GeomHelper::INVALID_OFFSET) {
688 0 : myGeom = myGeom.getSubpart2D(MIN2(offset, myGeom.length2D() - 2 * POSITION_EPS), myGeom.length2D());
689 : }
690 : } else {
691 : assert(node == myTo);
692 0 : myGeom.extrapolate(maxExtent, false, true);
693 0 : double offset = myGeom.nearest_offset_to_point2D(node->getPosition());
694 : //std::cout << " geom2=" << myGeom << " offset=" << offset;
695 0 : if (offset != GeomHelper::INVALID_OFFSET) {
696 0 : myGeom = myGeom.getSubpart2D(0, MAX2(offset, 2 * POSITION_EPS));
697 : }
698 : }
699 : //std::cout << " geom3=" << myGeom << "\n";
700 0 : }
701 :
702 :
703 : void
704 2 : NBEdge::shortenGeometryAtNode(const NBNode* node, double reduction) {
705 : //std::cout << "shortenGeometryAtNode edge=" << getID() << " node=" << node->getID() << " nodePos=" << node->getPosition() << " reduction=" << reduction << " geom=" << myGeom;
706 2 : reduction = MIN2(reduction, myGeom.length2D() - 2 * POSITION_EPS);
707 2 : if (node == myFrom) {
708 2 : myGeom = myGeom.getSubpart2D(reduction, myGeom.length2D());
709 : } else {
710 2 : myGeom = myGeom.getSubpart2D(0, myGeom.length2D() - reduction);
711 : }
712 2 : computeLaneShapes();
713 : //std::cout << " geom2=" << myGeom << "\n";
714 2 : }
715 :
716 :
717 : void
718 288710 : NBEdge::setNodeBorder(const NBNode* node, const Position& p, const Position& p2, bool rectangularCut) {
719 288710 : PositionVector border;
720 288710 : if (rectangularCut) {
721 : const double extend = 100;
722 3368 : border = myGeom.getOrthogonal(p, extend, node == myTo);
723 : } else {
724 287026 : border.push_back(p);
725 287026 : border.push_back(p2);
726 : }
727 288710 : if (border.size() == 2) {
728 288710 : border.extrapolate2D(getTotalWidth());
729 288710 : if (node == myFrom) {
730 : myFromBorder = border;
731 : } else {
732 : assert(node == myTo);
733 : myToBorder = border;
734 : }
735 : }
736 : #ifdef DEBUG_NODE_BORDER
737 : gDebugFlag1 = DEBUGCOND;
738 : if (DEBUGCOND) std::cout << "setNodeBorder edge=" << getID() << " node=" << node->getID()
739 : << " rect=" << rectangularCut
740 : << " p=" << p << " p2=" << p2
741 : << " border=" << border
742 : << " myGeom=" << myGeom
743 : << "\n";
744 :
745 : #endif
746 288710 : }
747 :
748 :
749 : const PositionVector&
750 328 : NBEdge::getNodeBorder(const NBNode* node) const {
751 328 : if (node == myFrom) {
752 164 : return myFromBorder;
753 : } else {
754 : assert(node == myTo);
755 164 : return myToBorder;
756 : }
757 : }
758 :
759 :
760 : void
761 38085 : NBEdge::resetNodeBorder(const NBNode* node) {
762 38085 : if (node == myFrom) {
763 : myFromBorder.clear();
764 : } else {
765 : assert(node == myTo);
766 : myToBorder.clear();
767 : }
768 38085 : }
769 :
770 :
771 : bool
772 6137811 : NBEdge::isBidiRail(bool ignoreSpread) const {
773 6137811 : return (isRailway(getPermissions())
774 205071 : && (ignoreSpread || myLaneSpreadFunction == LaneSpreadFunction::CENTER)
775 203670 : && myPossibleTurnDestination != nullptr
776 146440 : && myPossibleTurnDestination->myPossibleTurnDestination == this
777 139059 : && (ignoreSpread || myPossibleTurnDestination->getLaneSpreadFunction() == LaneSpreadFunction::CENTER)
778 138988 : && isRailway(myPossibleTurnDestination->getPermissions())
779 12275622 : && myPossibleTurnDestination->getGeometry().reverse() == getGeometry());
780 : }
781 :
782 :
783 : bool
784 5726962 : NBEdge::isBidiEdge(bool checkPotential) const {
785 5726962 : return myPossibleTurnDestination != nullptr
786 5247370 : && myPossibleTurnDestination->myPossibleTurnDestination == this
787 4992653 : && (myIsBidi || myPossibleTurnDestination->myIsBidi || checkPotential)
788 8938 : && myPossibleTurnDestination->getToNode() == getFromNode()
789 8938 : && myPossibleTurnDestination->getLaneSpreadFunction() == myLaneSpreadFunction
790 : // geometry check a) full overlap geometry
791 5735900 : && ((myLaneSpreadFunction == LaneSpreadFunction::CENTER
792 14582 : && (myPossibleTurnDestination->getGeometry().reverse() == getGeometry()
793 0 : || (checkPotential && getGeometry().size() == 2 && myPossibleTurnDestination->getGeometry().size() == 2)))
794 : // b) TWLT (Two-Way-Left-Turn-lane)
795 5726962 : || (myLanes.back().shape.reverse().almostSame(myPossibleTurnDestination->myLanes.back().shape, POSITION_EPS))
796 5726962 : );
797 :
798 : }
799 :
800 :
801 : bool
802 2848 : NBEdge::isRailDeadEnd() const {
803 2848 : if (!isRailway(getPermissions())) {
804 : return false;
805 : }
806 4443 : for (NBEdge* out : myTo->getOutgoingEdges()) {
807 8454 : if (isRailway(out->getPermissions()) &&
808 4151 : out != getTurnDestination(true)) {
809 : return true;
810 : }
811 : }
812 : return true;
813 : }
814 :
815 :
816 : PositionVector
817 383221 : NBEdge::cutAtIntersection(const PositionVector& old) const {
818 : PositionVector shape = old;
819 766442 : shape = startShapeAt(shape, myFrom, myFromBorder);
820 : #ifdef DEBUG_CUT_LANES
821 : if (DEBUGCOND) {
822 : std::cout << getID() << " cutFrom=" << shape << "\n";
823 : }
824 : #endif
825 383221 : if (shape.size() < 2) {
826 : // only keep the last snippet
827 417 : const double oldLength = old.length();
828 834 : shape = old.getSubpart(oldLength - 2 * POSITION_EPS, oldLength);
829 : #ifdef DEBUG_CUT_LANES
830 : if (DEBUGCOND) {
831 : std::cout << getID() << " cutFromFallback=" << shape << "\n";
832 : }
833 : #endif
834 : }
835 766442 : shape = startShapeAt(shape.reverse(), myTo, myToBorder).reverse();
836 : #ifdef DEBUG_CUT_LANES
837 : if (DEBUGCOND) {
838 : std::cout << getID() << " cutTo=" << shape << "\n";
839 : }
840 : #endif
841 : // sanity checks
842 383221 : if (shape.length() < POSITION_EPS) {
843 426 : if (old.length() < 2 * POSITION_EPS) {
844 : shape = old;
845 : } else {
846 416 : const double midpoint = old.length() / 2;
847 : // EPS*2 because otherwhise shape has only a single point
848 832 : shape = old.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
849 : assert(shape.size() >= 2);
850 : assert(shape.length() > 0);
851 : #ifdef DEBUG_CUT_LANES
852 : if (DEBUGCOND) {
853 : std::cout << getID() << " fallBackShort=" << shape << "\n";
854 : }
855 : #endif
856 : }
857 : } else {
858 : // @note If the node shapes are overlapping we may get a shape which goes in the wrong direction
859 : // in this case the result shape should shortened
860 382795 : if (DEG2RAD(135) < fabs(GeomHelper::angleDiff(shape.beginEndAngle(), old.beginEndAngle()))) {
861 : // eliminate intermediate points
862 33183 : PositionVector tmp;
863 33183 : tmp.push_back(shape[0]);
864 33183 : tmp.push_back(shape[-1]);
865 : shape = tmp;
866 33183 : if (tmp.length() < POSITION_EPS) {
867 : // fall back to original shape
868 98 : if (old.length() < 2 * POSITION_EPS) {
869 : shape = old;
870 : } else {
871 98 : const double midpoint = old.length() / 2;
872 : // EPS*2 because otherwhise shape has only a single point
873 196 : shape = old.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
874 : assert(shape.size() >= 2);
875 : assert(shape.length() > 0);
876 : }
877 : #ifdef DEBUG_CUT_LANES
878 : if (DEBUGCOND) {
879 : std::cout << getID() << " fallBackReversed=" << shape << "\n";
880 : }
881 : #endif
882 : } else {
883 33085 : const double midpoint = shape.length() / 2;
884 : // cut to size and reverse
885 66170 : shape = shape.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
886 33085 : if (shape.length() < POSITION_EPS) {
887 : assert(false);
888 : // the shape has a sharp turn near the midpoint
889 : }
890 66170 : shape = shape.reverse();
891 : #ifdef DEBUG_CUT_LANES
892 : if (DEBUGCOND) {
893 : std::cout << getID() << " fallBackReversed2=" << shape << " mid=" << midpoint << "\n";
894 : }
895 : #endif
896 : }
897 : // make short edge flat (length <= 2 * POSITION_EPS)
898 33183 : const double z = (shape[0].z() + shape[1].z()) / 2;
899 33183 : shape[0].setz(z);
900 33183 : shape[1].setz(z);
901 33183 : }
902 : }
903 383221 : return shape;
904 0 : }
905 :
906 :
907 : void
908 235111 : NBEdge::computeEdgeShape(double smoothElevationThreshold) {
909 235111 : if (smoothElevationThreshold > 0 && myGeom.hasElevation()) {
910 1868 : PositionVector cut = cutAtIntersection(myGeom);
911 : // cutting and patching z-coordinate may cause steep grades which should be smoothed
912 1868 : if (!myFrom->geometryLike()) {
913 1758 : cut[0].setz(myFrom->getPosition().z());
914 1758 : const double d = cut[0].distanceTo2D(cut[1]);
915 1758 : const double dZ = fabs(cut[0].z() - cut[1].z());
916 1758 : if (dZ / smoothElevationThreshold > d) {
917 519 : cut = cut.smoothedZFront(MIN2(cut.length2D() / 2, dZ / smoothElevationThreshold));
918 : }
919 : }
920 1868 : if (!myTo->geometryLike()) {
921 1758 : cut[-1].setz(myTo->getPosition().z());
922 1758 : const double d = cut[-1].distanceTo2D(cut[-2]);
923 1758 : const double dZ = fabs(cut[-1].z() - cut[-2].z());
924 1758 : if (dZ / smoothElevationThreshold > d) {
925 524 : cut = cut.reverse().smoothedZFront(MIN2(cut.length2D() / 2, dZ / smoothElevationThreshold)).reverse();
926 : }
927 : }
928 1868 : cut[0] = myGeom[0];
929 1868 : cut[-1] = myGeom[-1];
930 1868 : if (cut != myGeom) {
931 : myGeom = cut;
932 28 : computeLaneShapes();
933 : }
934 1868 : }
935 540133 : for (int i = 0; i < (int)myLanes.size(); i++) {
936 610044 : myLanes[i].shape = cutAtIntersection(myLanes[i].shape);
937 : }
938 : // recompute edge's length as the average of lane lengths
939 : double avgLength = 0;
940 540133 : for (int i = 0; i < (int)myLanes.size(); i++) {
941 305022 : avgLength += myLanes[i].shape.length();
942 : }
943 235111 : myLength = avgLength / (double) myLanes.size();
944 235111 : computeAngle(); // update angles using the finalized node and lane shapes
945 235111 : }
946 :
947 :
948 : PositionVector
949 766770 : NBEdge::startShapeAt(const PositionVector& laneShape, const NBNode* startNode, PositionVector nodeShape) {
950 766770 : if (nodeShape.size() == 0) {
951 197369 : nodeShape = startNode->getShape();
952 197369 : nodeShape.closePolygon();
953 : }
954 : PositionVector lb = laneShape;
955 766770 : lb.extrapolate2D(100.0);
956 766770 : if (nodeShape.intersects(laneShape)) {
957 : // shape intersects directly
958 567383 : std::vector<double> pbv = laneShape.intersectsAtLengths2D(nodeShape);
959 : assert(pbv.size() > 0);
960 : // ensure that the subpart has at least two points
961 567383 : double pb = MIN2(laneShape.length2D() - POSITION_EPS - NUMERICAL_EPS, VectorHelper<double>::maxValue(pbv));
962 567383 : if (pb < 0) {
963 : return laneShape;
964 : }
965 567361 : PositionVector ns = laneShape.getSubpart2D(pb, laneShape.length2D());
966 : //PositionVector ns = pb < (laneShape.length() - POSITION_EPS) ? laneShape.getSubpart2D(pb, laneShape.length()) : laneShape;
967 567361 : const double delta = ns[0].z() - laneShape[0].z();
968 : //std::cout << "a) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << ns[0].z() << " delta=" << delta << "\n";
969 567361 : if (fabs(delta) > 2 * POSITION_EPS && (!startNode->geometryLike() || pb < 1)) {
970 : // make "real" intersections and small intersections flat
971 : //std::cout << "a) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << ns[0].z() << " delta=" << delta << "\n";
972 3162 : ns[0].setz(startNode->getPosition().z());
973 : }
974 : assert(ns.size() >= 2);
975 : return ns;
976 766770 : } else if (nodeShape.intersects(lb)) {
977 : // extension of first segment intersects
978 76334 : std::vector<double> pbv = lb.intersectsAtLengths2D(nodeShape);
979 : assert(pbv.size() > 0);
980 : double pb = VectorHelper<double>::maxValue(pbv);
981 : assert(pb >= 0);
982 76334 : PositionVector result = laneShape.getSubpartByIndex(1, (int)laneShape.size() - 1);
983 76334 : Position np = lb.positionAtOffset2D(pb);
984 76334 : const double delta = np.z() - laneShape[0].z();
985 : //std::cout << "b) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << np.z() << " delta=" << delta << "\n";
986 76334 : if (fabs(delta) > 2 * POSITION_EPS && !startNode->geometryLike()) {
987 : // avoid z-overshoot when extrapolating
988 : //std::cout << "b) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << np.z() << " delta=" << delta << "\n";
989 : np.setz(startNode->getPosition().z());
990 : }
991 76334 : result.push_front_noDoublePos(np);
992 : return result;
993 : //if (result.size() >= 2) {
994 : // return result;
995 : //} else {
996 : // WRITE_WARNING(error + " (resulting shape is too short)");
997 : // return laneShape;
998 : //}
999 76334 : } else {
1000 : // could not find proper intersection. Probably the edge is very short
1001 : // and lies within nodeShape
1002 : // @todo enable warning WRITE_WARNING(error + " (laneShape lies within nodeShape)");
1003 : return laneShape;
1004 : }
1005 766770 : }
1006 :
1007 :
1008 : const PositionVector&
1009 2529810 : NBEdge::getLaneShape(int i) const {
1010 2529810 : return myLanes[i].shape;
1011 : }
1012 :
1013 :
1014 : void
1015 1390 : NBEdge::setLaneSpreadFunction(LaneSpreadFunction spread) {
1016 1390 : myLaneSpreadFunction = spread;
1017 1390 : }
1018 :
1019 :
1020 : LaneSpreadFunction
1021 355234 : NBEdge::getLaneSpreadFunction() const {
1022 355234 : return myLaneSpreadFunction;
1023 : }
1024 :
1025 :
1026 : void
1027 792 : NBEdge::addGeometryPoint(int index, const Position& p) {
1028 792 : if (index >= 0) {
1029 396 : myGeom.insert(myGeom.begin() + index, p);
1030 : } else {
1031 396 : myGeom.insert(myGeom.end() + index, p);
1032 : }
1033 792 : }
1034 :
1035 :
1036 : void
1037 516 : NBEdge::reduceGeometry(const double minDist) {
1038 : // attempt symmetrical removal for forward and backward direction
1039 : // (very important for bidiRail)
1040 516 : if (myFrom->getID() < myTo->getID()) {
1041 259 : PositionVector reverse = myGeom.reverse();
1042 259 : reverse.removeDoublePoints(minDist, true, 0, 0, true);
1043 518 : myGeom = reverse.reverse();
1044 702 : for (Lane& lane : myLanes) {
1045 886 : reverse = lane.customShape.reverse();
1046 443 : reverse.removeDoublePoints(minDist, true, 0, 0, true);
1047 886 : lane.customShape = reverse.reverse();
1048 : }
1049 259 : } else {
1050 257 : myGeom.removeDoublePoints(minDist, true, 0, 0, true);
1051 653 : for (Lane& lane : myLanes) {
1052 396 : lane.customShape.removeDoublePoints(minDist, true, 0, 0, true);
1053 : }
1054 : }
1055 516 : }
1056 :
1057 :
1058 : void
1059 92076 : NBEdge::checkGeometry(const double maxAngle, bool fixAngle, const double minRadius, bool fix, bool silent) {
1060 92076 : if (myGeom.size() < 3) {
1061 41766 : return;
1062 : }
1063 : //std::cout << "checking geometry of " << getID() << " geometry = " << toString(myGeom) << "\n";
1064 : std::vector<double> angles; // absolute segment angles
1065 : //std::cout << " absolute angles:";
1066 329997 : for (int i = 0; i < (int)myGeom.size() - 1; ++i) {
1067 279642 : angles.push_back(myGeom.angleAt2D(i));
1068 : //std::cout << " " << angles.back();
1069 : }
1070 : //std::cout << "\n relative angles: ";
1071 50355 : NBEdge* bidi = const_cast<NBEdge*>(getBidiEdge());
1072 279464 : for (int i = 0; i < (int)angles.size() - 1; ++i) {
1073 229154 : const double relAngle = fabs(GeomHelper::angleDiff(angles[i], angles[i + 1]));
1074 : //std::cout << relAngle << " ";
1075 229154 : if (maxAngle > 0 && relAngle > maxAngle) {
1076 163 : if (fixAngle) {
1077 3 : WRITE_MESSAGEF(TL("Removing sharp angle of % degrees at edge '%', segment %."),
1078 : toString(relAngle), getID(), i);
1079 1 : myGeom.erase(myGeom.begin() + i + 1);
1080 1 : if (bidi != nullptr) {
1081 0 : bidi->myGeom = myGeom.reverse();
1082 : }
1083 1 : checkGeometry(maxAngle, fixAngle, minRadius, fix, silent);
1084 45 : return;
1085 162 : } else if (!silent) {
1086 486 : WRITE_WARNINGF(TL("Found angle of % degrees at edge '%', segment %."), RAD2DEG(relAngle), getID(), i);
1087 : }
1088 : }
1089 229153 : if (relAngle < DEG2RAD(1)) {
1090 87020 : continue;
1091 : }
1092 142133 : if (i == 0 || i == (int)angles.size() - 2) {
1093 : const bool start = i == 0;
1094 55323 : const double dist = (start ? myGeom[0].distanceTo2D(myGeom[1]) : myGeom[-2].distanceTo2D(myGeom[-1]));
1095 55323 : const double r = tan(0.5 * (M_PI - relAngle)) * dist;
1096 : //std::cout << (start ? " start" : " end") << " length=" << dist << " radius=" << r << " ";
1097 55323 : if (minRadius > 0 && r < minRadius) {
1098 813 : if (fix) {
1099 132 : WRITE_MESSAGEF(TL("Removing sharp turn with radius % at the % of edge '%'."),
1100 : toString(r), start ? TL("start") : TL("end"), getID());
1101 44 : myGeom.erase(myGeom.begin() + (start ? 1 : i + 1));
1102 44 : if (bidi != nullptr) {
1103 0 : bidi->myGeom = myGeom.reverse();
1104 : }
1105 44 : checkGeometry(maxAngle, fixAngle, minRadius, fix, silent);
1106 44 : return;
1107 769 : } else if (!silent) {
1108 2292 : WRITE_WARNINGF(TL("Found sharp turn with radius % at the % of edge '%'."),
1109 : toString(r), start ? TL("start") : TL("end"), getID());
1110 : }
1111 : }
1112 : }
1113 : }
1114 : //std::cout << "\n";
1115 50355 : }
1116 :
1117 :
1118 : // ----------- Setting and getting connections
1119 : bool
1120 140860 : NBEdge::addEdge2EdgeConnection(NBEdge* dest, bool overrideRemoval, SVCPermissions permissions) {
1121 140860 : if (myStep == EdgeBuildingStep::INIT_REJECT_CONNECTIONS) {
1122 : return true;
1123 : }
1124 : // check whether the node was merged and now a connection between
1125 : // not matching edges is tried to be added
1126 : // This happens f.e. within the ptv VISSIM-example "Beijing"
1127 140860 : if (dest != nullptr && myTo != dest->myFrom) {
1128 : return false;
1129 : }
1130 : if (dest == nullptr) {
1131 105 : invalidateConnections();
1132 210 : myConnections.push_back(Connection(-1, dest, -1));
1133 105 : myStep = EdgeBuildingStep::LANES2LANES_USER;
1134 140755 : } else if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(dest)) == myConnections.end()) {
1135 247168 : myConnections.push_back(Connection(-1, dest, -1));
1136 123584 : myConnections.back().permissions = permissions;
1137 : }
1138 140860 : if (overrideRemoval) {
1139 : // override earlier delete decision
1140 901 : for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end();) {
1141 319 : if (it->toEdge == dest) {
1142 2 : it = myConnectionsToDelete.erase(it);
1143 : } else {
1144 : it++;
1145 : }
1146 : }
1147 : }
1148 140860 : if (myStep < EdgeBuildingStep::EDGE2EDGES) {
1149 64424 : myStep = EdgeBuildingStep::EDGE2EDGES;
1150 : }
1151 : return true;
1152 : }
1153 :
1154 :
1155 : bool
1156 140410 : NBEdge::addLane2LaneConnection(int from, NBEdge* dest,
1157 : int toLane, Lane2LaneInfoType type,
1158 : bool mayUseSameDestination,
1159 : bool mayDefinitelyPass,
1160 : KeepClear keepClear,
1161 : double contPos,
1162 : double visibility,
1163 : double speed,
1164 : double friction,
1165 : double length,
1166 : const PositionVector& customShape,
1167 : bool uncontrolled,
1168 : SVCPermissions permissions,
1169 : bool indirectLeft,
1170 : const std::string& edgeType,
1171 : SVCPermissions changeLeft,
1172 : SVCPermissions changeRight,
1173 : bool postProcess) {
1174 140410 : if (myStep == EdgeBuildingStep::INIT_REJECT_CONNECTIONS) {
1175 : return true;
1176 : }
1177 : // check whether the node was merged and now a connection between
1178 : // not matching edges is tried to be added
1179 : // This happens f.e. within the ptv VISSIM-example "Beijing"
1180 140410 : if (myTo != dest->myFrom) {
1181 : return false;
1182 : }
1183 139666 : if (!addEdge2EdgeConnection(dest)) {
1184 : return false;
1185 : }
1186 139666 : return setConnection(from, dest, toLane, type, mayUseSameDestination, mayDefinitelyPass, keepClear, contPos, visibility, speed, friction, length,
1187 139666 : customShape, uncontrolled, permissions, indirectLeft, edgeType, changeLeft, changeRight, postProcess);
1188 : }
1189 :
1190 :
1191 : bool
1192 1472 : NBEdge::addLane2LaneConnections(int fromLane,
1193 : NBEdge* dest, int toLane,
1194 : int no, Lane2LaneInfoType type,
1195 : bool invalidatePrevious,
1196 : bool mayDefinitelyPass) {
1197 1472 : if (invalidatePrevious) {
1198 797 : invalidateConnections(true);
1199 : }
1200 : bool ok = true;
1201 3389 : for (int i = 0; i < no && ok; i++) {
1202 3834 : ok &= addLane2LaneConnection(fromLane + i, dest, toLane + i, type, false, mayDefinitelyPass);
1203 : }
1204 1472 : return ok;
1205 : }
1206 :
1207 :
1208 : bool
1209 286388 : NBEdge::setConnection(int lane, NBEdge* destEdge,
1210 : int destLane, Lane2LaneInfoType type,
1211 : bool mayUseSameDestination,
1212 : bool mayDefinitelyPass,
1213 : KeepClear keepClear,
1214 : double contPos,
1215 : double visibility,
1216 : double speed,
1217 : double friction,
1218 : double length,
1219 : const PositionVector& customShape,
1220 : bool uncontrolled,
1221 : SVCPermissions permissions,
1222 : bool indirectLeft,
1223 : const std::string& edgeType,
1224 : SVCPermissions changeLeft,
1225 : SVCPermissions changeRight,
1226 : bool postProcess) {
1227 286388 : if (myStep == EdgeBuildingStep::INIT_REJECT_CONNECTIONS) {
1228 : return false;
1229 : }
1230 : // some kind of a misbehaviour which may occure when the junction's outgoing
1231 : // edge priorities were not properly computed, what may happen due to
1232 : // an incomplete or not proper input
1233 : // what happens is that under some circumstances a single lane may set to
1234 : // be approached more than once by the one of our lanes.
1235 : // This must not be!
1236 : // we test whether it is the case and do nothing if so - the connection
1237 : // will be refused
1238 : //
1239 286388 : if (!mayUseSameDestination && hasConnectionTo(destEdge, destLane)) {
1240 : return false;
1241 : }
1242 275026 : if (find_if(myConnections.begin(), myConnections.end(), connections_finder(lane, destEdge, destLane)) != myConnections.end()) {
1243 : return true;
1244 : }
1245 274988 : if ((int)myLanes.size() <= lane || destEdge->getNumLanes() <= (int)destLane) {
1246 : // problem might be corrigible in post-processing
1247 22 : WRITE_WARNINGF(TL("Could not set connection from '%' to '%'."), getLaneID(lane), destEdge->getLaneID(destLane));
1248 11 : return false;
1249 : }
1250 861104 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
1251 586127 : if ((*i).toEdge == destEdge && ((*i).fromLane == -1 || (*i).toLane == -1)) {
1252 220246 : if (permissions == SVC_UNSPECIFIED) {
1253 : // @note: in case we were to add multiple connections from the
1254 : // same lane the second one wouldn't get the special permissions!
1255 220164 : permissions = (*i).permissions;
1256 : }
1257 220246 : i = myConnections.erase(i);
1258 : } else {
1259 : ++i;
1260 : }
1261 : }
1262 549954 : myConnections.push_back(Connection(lane, destEdge, destLane));
1263 274977 : if (mayDefinitelyPass) {
1264 27 : myConnections.back().mayDefinitelyPass = true;
1265 : }
1266 274977 : myConnections.back().keepClear = keepClear;
1267 274977 : myConnections.back().contPos = contPos;
1268 274977 : myConnections.back().visibility = visibility;
1269 274977 : myConnections.back().permissions = permissions;
1270 274977 : myConnections.back().indirectLeft = indirectLeft;
1271 274977 : myConnections.back().edgeType = edgeType;
1272 274977 : myConnections.back().changeLeft = changeLeft;
1273 274977 : myConnections.back().changeRight = changeRight;
1274 274977 : myConnections.back().speed = speed;
1275 274977 : myConnections.back().friction = friction;
1276 274977 : myConnections.back().customLength = length;
1277 : myConnections.back().customShape = customShape;
1278 274977 : myConnections.back().uncontrolled = uncontrolled;
1279 274977 : if (type == Lane2LaneInfoType::USER) {
1280 3074 : myStep = EdgeBuildingStep::LANES2LANES_USER;
1281 : } else {
1282 : // check whether we have to take another look at it later
1283 271903 : if (type == Lane2LaneInfoType::COMPUTED) {
1284 : // yes, the connection was set using an algorithm which requires a recheck
1285 104276 : myStep = EdgeBuildingStep::LANES2LANES_RECHECK;
1286 : } else {
1287 : // ok, let's only not recheck it if we did no add something that has to be rechecked
1288 167627 : if (myStep != EdgeBuildingStep::LANES2LANES_RECHECK) {
1289 138124 : myStep = EdgeBuildingStep::LANES2LANES_DONE;
1290 : }
1291 : }
1292 : }
1293 274977 : if (postProcess) {
1294 : // override earlier delete decision
1295 18 : for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end();) {
1296 0 : if ((it->fromLane < 0 || it->fromLane == lane)
1297 1 : && (it->toEdge == nullptr || it->toEdge == destEdge)
1298 2 : && (it->toLane < 0 || it->toLane == destLane)) {
1299 1 : it = myConnectionsToDelete.erase(it);
1300 : } else {
1301 : it++;
1302 : }
1303 : }
1304 : }
1305 : return true;
1306 : }
1307 :
1308 :
1309 : std::vector<NBEdge::Connection>
1310 3904562 : NBEdge::getConnectionsFromLane(int lane, const NBEdge* to, int toLane) const {
1311 : std::vector<NBEdge::Connection> ret;
1312 17673945 : for (const Connection& c : myConnections) {
1313 13769383 : if ((lane < 0 || c.fromLane == lane)
1314 8925513 : && (to == nullptr || to == c.toEdge)
1315 8909238 : && (toLane < 0 || toLane == c.toLane)) {
1316 8908023 : ret.push_back(c);
1317 : }
1318 : }
1319 3904562 : return ret;
1320 0 : }
1321 :
1322 :
1323 : const NBEdge::Connection&
1324 20060 : NBEdge::getConnection(int fromLane, const NBEdge* to, int toLane) const {
1325 65847 : for (const Connection& c : myConnections) {
1326 65847 : if (c.fromLane == fromLane && c.toEdge == to && c.toLane == toLane) {
1327 20060 : return c;
1328 : }
1329 : }
1330 0 : throw ProcessError("Connection from " + getID() + "_" + toString(fromLane)
1331 0 : + " to " + to->getID() + "_" + toString(toLane) + " not found");
1332 : }
1333 :
1334 :
1335 : NBEdge::Connection&
1336 6 : NBEdge::getConnectionRef(int fromLane, const NBEdge* to, int toLane) {
1337 14 : for (Connection& c : myConnections) {
1338 14 : if (c.fromLane == fromLane && c.toEdge == to && c.toLane == toLane) {
1339 6 : return c;
1340 : }
1341 : }
1342 0 : throw ProcessError("Connection from " + getID() + "_" + toString(fromLane)
1343 0 : + " to " + to->getID() + "_" + toString(toLane) + " not found");
1344 : }
1345 :
1346 :
1347 : bool
1348 288563 : NBEdge::hasConnectionTo(const NBEdge* destEdge, int destLane, int fromLane) const {
1349 288563 : return destEdge != nullptr && find_if(myConnections.begin(), myConnections.end(), connections_toedgelane_finder(destEdge, destLane, fromLane)) != myConnections.end();
1350 : }
1351 :
1352 :
1353 : bool
1354 1115298 : NBEdge::isConnectedTo(const NBEdge* e, const bool ignoreTurnaround) const {
1355 1115298 : if (!ignoreTurnaround && (e == myTurnDestination)) {
1356 : return true;
1357 : }
1358 : return
1359 1107276 : find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(e))
1360 : !=
1361 : myConnections.end();
1362 :
1363 : }
1364 :
1365 :
1366 : const EdgeVector*
1367 67845 : NBEdge::getConnectedSorted() {
1368 : // check whether connections exist and if not, use edges from the node
1369 : EdgeVector outgoing;
1370 67845 : if (myConnections.size() == 0) {
1371 22181 : outgoing = myTo->getOutgoingEdges();
1372 : } else {
1373 136242 : for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1374 90578 : if (find(outgoing.begin(), outgoing.end(), (*i).toEdge) == outgoing.end()) {
1375 90267 : outgoing.push_back((*i).toEdge);
1376 : }
1377 : }
1378 : }
1379 74538 : for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
1380 6693 : if (it->fromLane < 0 && it->toLane < 0) {
1381 : // found an edge that shall not be connected
1382 6693 : EdgeVector::iterator forbidden = std::find(outgoing.begin(), outgoing.end(), it->toEdge);
1383 6693 : if (forbidden != outgoing.end()) {
1384 : outgoing.erase(forbidden);
1385 : }
1386 : }
1387 : }
1388 : // allocate the sorted container
1389 67845 : int size = (int) outgoing.size();
1390 67845 : EdgeVector* edges = new EdgeVector();
1391 67845 : edges->reserve(size);
1392 187169 : for (EdgeVector::const_iterator i = outgoing.begin(); i != outgoing.end(); i++) {
1393 119324 : NBEdge* outedge = *i;
1394 119324 : if (outedge != nullptr && outedge != myTurnDestination) {
1395 109504 : edges->push_back(outedge);
1396 : }
1397 : }
1398 67845 : std::sort(edges->begin(), edges->end(), NBContHelper::relative_outgoing_edge_sorter(this));
1399 67845 : return edges;
1400 67845 : }
1401 :
1402 :
1403 : EdgeVector
1404 202911 : NBEdge::getConnectedEdges() const {
1405 : EdgeVector ret;
1406 666873 : for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1407 463962 : if (find(ret.begin(), ret.end(), (*i).toEdge) == ret.end()) {
1408 282343 : ret.push_back((*i).toEdge);
1409 : }
1410 : }
1411 202911 : return ret;
1412 0 : }
1413 :
1414 :
1415 : EdgeVector
1416 6728 : NBEdge::getIncomingEdges() const {
1417 : EdgeVector ret;
1418 6728 : const EdgeVector& candidates = myFrom->getIncomingEdges();
1419 22850 : for (EdgeVector::const_iterator i = candidates.begin(); i != candidates.end(); i++) {
1420 16122 : if ((*i)->isConnectedTo(this)) {
1421 14891 : ret.push_back(*i);
1422 : }
1423 : }
1424 6728 : return ret;
1425 0 : }
1426 :
1427 :
1428 : std::vector<int>
1429 625674 : NBEdge::getConnectionLanes(NBEdge* currentOutgoing, bool withBikes, bool withBusLanes) const {
1430 : std::vector<int> ret;
1431 625674 : if (currentOutgoing != myTurnDestination) {
1432 1903604 : for (const Connection& c : myConnections) {
1433 1328616 : if (c.toEdge == currentOutgoing
1434 501609 : && (withBikes || getPermissions(c.fromLane) != SVC_BICYCLE)
1435 1829712 : && (withBusLanes || getPermissions(c.fromLane) != SVC_BUS)) {
1436 501077 : ret.push_back(c.fromLane);
1437 : }
1438 : }
1439 : }
1440 625674 : return ret;
1441 0 : }
1442 :
1443 :
1444 : void
1445 128707 : NBEdge::sortOutgoingConnectionsByAngle() {
1446 128707 : sort(myConnections.begin(), myConnections.end(), connections_relative_edgelane_sorter(this));
1447 128707 : }
1448 :
1449 :
1450 : void
1451 184838 : NBEdge::sortOutgoingConnectionsByIndex() {
1452 184838 : sort(myConnections.begin(), myConnections.end(), connections_sorter);
1453 184838 : }
1454 :
1455 :
1456 : void
1457 0 : NBEdge::remapConnections(const EdgeVector& incoming) {
1458 0 : EdgeVector connected = getConnectedEdges();
1459 0 : for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
1460 0 : NBEdge* inc = *i;
1461 : // We have to do this
1462 0 : inc->myStep = EdgeBuildingStep::EDGE2EDGES;
1463 : // add all connections
1464 0 : for (EdgeVector::iterator j = connected.begin(); j != connected.end(); j++) {
1465 0 : inc->addEdge2EdgeConnection(*j);
1466 : }
1467 0 : inc->removeFromConnections(this);
1468 : }
1469 0 : }
1470 :
1471 :
1472 : void
1473 80487 : NBEdge::removeFromConnections(NBEdge* toEdge, int fromLane, int toLane, bool tryLater, const bool adaptToLaneRemoval,
1474 : const bool keepPossibleTurns) {
1475 : // remove from "myConnections"
1476 80487 : const int fromLaneRemoved = adaptToLaneRemoval && fromLane >= 0 ? fromLane : -1;
1477 80487 : const int toLaneRemoved = adaptToLaneRemoval && toLane >= 0 ? toLane : -1;
1478 161283 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
1479 : Connection& c = *i;
1480 80796 : if ((toEdge == nullptr || c.toEdge == toEdge)
1481 8909 : && (fromLane < 0 || c.fromLane == fromLane)
1482 8746 : && (toLane < 0 || c.toLane == toLane)) {
1483 8609 : if (myTo->isTLControlled()) {
1484 : std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
1485 470 : for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
1486 235 : (*it)->removeConnection(NBConnection(this, c.fromLane, c.toEdge, c.toLane));
1487 : }
1488 : }
1489 8609 : i = myConnections.erase(i);
1490 : tryLater = false;
1491 8609 : } else {
1492 72187 : if (fromLaneRemoved >= 0 && c.fromLane > fromLaneRemoved) {
1493 128 : if (myTo->isTLControlled()) {
1494 : std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
1495 10 : for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
1496 83 : for (NBConnectionVector::iterator tlcon = (*it)->getControlledLinks().begin(); tlcon != (*it)->getControlledLinks().end(); ++tlcon) {
1497 : NBConnection& tc = *tlcon;
1498 78 : if (tc.getTo() == c.toEdge && tc.getFromLane() == c.fromLane && tc.getToLane() == c.toLane) {
1499 12 : tc.shiftLaneIndex(this, -1);
1500 : }
1501 : }
1502 : }
1503 : }
1504 : //std::cout << getID() << " removeFromConnections fromLane=" << fromLane << " to=" << Named::getIDSecure(toEdge) << " toLane=" << toLane << " reduceFromLane=" << c.fromLane << " (to=" << c.toLane << ")\n";
1505 128 : c.fromLane--;
1506 : }
1507 72187 : if (toLaneRemoved >= 0 && c.toLane > toLaneRemoved && (toEdge == nullptr || c.toEdge == toEdge)) {
1508 : //std::cout << getID() << " removeFromConnections fromLane=" << fromLane << " to=" << Named::getIDSecure(toEdge) << " toLane=" << toLane << " reduceToLane=" << c.toLane << " (from=" << c.fromLane << ")\n";
1509 101 : c.toLane--;
1510 : }
1511 : ++i;
1512 : }
1513 : }
1514 : // check whether it was the turn destination
1515 80487 : if (myTurnDestination == toEdge && fromLane < 0) {
1516 2596 : myTurnDestination = nullptr;
1517 : }
1518 80487 : if (myPossibleTurnDestination == toEdge && fromLane < 0 && !keepPossibleTurns) {
1519 2194 : myPossibleTurnDestination = nullptr;
1520 : }
1521 80487 : if (tryLater) {
1522 13598 : myConnectionsToDelete.push_back(Connection(fromLane, toEdge, toLane));
1523 : #ifdef DEBUG_CONNECTION_GUESSING
1524 : if (DEBUGCOND) {
1525 : std::cout << "removeFromConnections " << getID() << "_" << fromLane << "->" << toEdge->getID() << "_" << toLane << "\n";
1526 : for (Connection& c : myConnections) {
1527 : std::cout << " conn " << c.getDescription(this) << "\n";
1528 : }
1529 : for (Connection& c : myConnectionsToDelete) {
1530 : std::cout << " connToDelete " << c.getDescription(this) << "\n";
1531 : }
1532 : }
1533 : #endif
1534 : }
1535 80487 : }
1536 :
1537 :
1538 : bool
1539 39 : NBEdge::removeFromConnections(const NBEdge::Connection& connectionToRemove) {
1540 : // iterate over connections
1541 44 : for (auto i = myConnections.begin(); i != myConnections.end(); i++) {
1542 44 : if ((i->toEdge == connectionToRemove.toEdge) && (i->fromLane == connectionToRemove.fromLane) && (i->toLane == connectionToRemove.toLane)) {
1543 : // remove connection
1544 39 : myConnections.erase(i);
1545 : return true;
1546 : }
1547 : }
1548 : // assert(false);
1549 : return false;
1550 : }
1551 :
1552 :
1553 : void
1554 3769 : NBEdge::invalidateConnections(bool reallowSetting) {
1555 3769 : myTurnDestination = nullptr;
1556 : myConnections.clear();
1557 3769 : if (reallowSetting) {
1558 3649 : myStep = EdgeBuildingStep::INIT;
1559 : } else {
1560 120 : myStep = EdgeBuildingStep::INIT_REJECT_CONNECTIONS;
1561 : }
1562 3769 : }
1563 :
1564 :
1565 : void
1566 1207 : NBEdge::replaceInConnections(NBEdge* which, NBEdge* by, int laneOff) {
1567 : // replace in "_connectedEdges"
1568 2512 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1569 1305 : if ((*i).toEdge == which) {
1570 761 : (*i).toEdge = by;
1571 761 : (*i).toLane += laneOff;
1572 : }
1573 : }
1574 : // check whether it was the turn destination
1575 1207 : if (myTurnDestination == which) {
1576 40 : myTurnDestination = by;
1577 : }
1578 1207 : }
1579 :
1580 : void
1581 46375 : NBEdge::replaceInConnections(NBEdge* which, const std::vector<NBEdge::Connection>& origConns) {
1582 : std::map<int, int> laneMap;
1583 : int minLane = -1;
1584 : int maxLane = -1;
1585 : // get lanes used to approach the edge to remap
1586 : bool wasConnected = false;
1587 53297 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1588 6922 : if ((*i).toEdge != which) {
1589 6187 : continue;
1590 : }
1591 : wasConnected = true;
1592 735 : if ((*i).fromLane != -1) {
1593 : int fromLane = (*i).fromLane;
1594 705 : laneMap[(*i).toLane] = fromLane;
1595 705 : if (minLane == -1 || minLane > fromLane) {
1596 : minLane = fromLane;
1597 : }
1598 705 : if (maxLane == -1 || maxLane < fromLane) {
1599 : maxLane = fromLane;
1600 : }
1601 : }
1602 : }
1603 46375 : if (!wasConnected) {
1604 : return;
1605 : }
1606 : // add new connections
1607 517 : std::vector<NBEdge::Connection> conns = origConns;
1608 517 : EdgeVector origTargets = getSuccessors();
1609 2633 : for (std::vector<NBEdge::Connection>::iterator i = conns.begin(); i != conns.end(); ++i) {
1610 2116 : if ((*i).toEdge == which || (*i).toEdge == this
1611 : // if we already have connections to the target edge, do not add new ones as they are probably from a circular replacement
1612 4174 : || std::find(origTargets.begin(), origTargets.end(), (*i).toEdge) != origTargets.end()) {
1613 : #ifdef DEBUG_REPLACECONNECTION
1614 : if (DEBUGCOND) {
1615 : std::cout << " replaceInConnections edge=" << getID() << " which=" << which->getID()
1616 : << " origTargets=" << toString(origTargets) << " newTarget=" << i->toEdge->getID() << " skipped\n";
1617 : }
1618 : #endif
1619 881 : continue;
1620 : }
1621 1238 : if (which->getStep() == EdgeBuildingStep::EDGE2EDGES) {
1622 : // do not set lane-level connections
1623 3 : replaceInConnections(which, (*i).toEdge, 0);
1624 3 : continue;
1625 : }
1626 1235 : int fromLane = (*i).fromLane;
1627 : int toUse = -1;
1628 1235 : if (laneMap.find(fromLane) == laneMap.end()) {
1629 288 : if (fromLane >= 0 && fromLane <= minLane) {
1630 : toUse = minLane;
1631 : // patch laneMap to avoid crossed-over connections
1632 455 : for (auto& item : laneMap) {
1633 249 : if (item.first < fromLane) {
1634 35 : item.second = MIN2(item.second, minLane);
1635 : }
1636 : }
1637 : }
1638 288 : if (fromLane >= 0 && fromLane >= maxLane) {
1639 : toUse = maxLane;
1640 : // patch laneMap to avoid crossed-over connections
1641 336 : for (auto& item : laneMap) {
1642 185 : if (item.first > fromLane) {
1643 68 : item.second = MAX2(item.second, maxLane);
1644 : }
1645 : }
1646 : }
1647 : } else {
1648 947 : toUse = laneMap[fromLane];
1649 : }
1650 1235 : if (toUse == -1) {
1651 : toUse = 0;
1652 : }
1653 : #ifdef DEBUG_REPLACECONNECTION
1654 : if (DEBUGCOND) {
1655 : std::cout << " replaceInConnections edge=" << getID() << " which=" << which->getID() << " origTargets=" << toString(origTargets)
1656 : << " origFrom=" << fromLane << " laneMap=" << joinToString(laneMap, ":", ",") << " minLane=" << minLane << " maxLane=" << maxLane
1657 : << " newTarget=" << i->toEdge->getID() << " fromLane=" << toUse << " toLane=" << i->toLane << "\n";
1658 : }
1659 : #endif
1660 2470 : setConnection(toUse, i->toEdge, i->toLane, Lane2LaneInfoType::COMPUTED, false, i->mayDefinitelyPass, i->keepClear,
1661 1235 : i->contPos, i->visibility, i->speed, i->friction, i->customLength, i->customShape, i->uncontrolled);
1662 : }
1663 : // remove the remapped edge from connections
1664 517 : removeFromConnections(which);
1665 517 : }
1666 :
1667 :
1668 : void
1669 789 : NBEdge::copyConnectionsFrom(NBEdge* src) {
1670 789 : myStep = src->myStep;
1671 789 : myConnections = src->myConnections;
1672 789 : }
1673 :
1674 :
1675 : bool
1676 1114 : NBEdge::canMoveConnection(const Connection& con, int newFromLane) const {
1677 : // only allow using newFromLane if at least 1 vClass is permitted to use
1678 : // this connection. If the connection shall be moved to a sidewalk, only create the connection if there is no walking area
1679 1114 : const SVCPermissions common = (getPermissions(newFromLane) & con.toEdge->getPermissions(con.toLane));
1680 1114 : return (common > 0 && common != SVC_PEDESTRIAN);
1681 : }
1682 :
1683 :
1684 : void
1685 492 : NBEdge::moveConnectionToLeft(int lane) {
1686 : #ifdef DEBUG_CONNECTION_CHECKING
1687 : std::cout << " moveConnectionToLeft " << getID() << " lane=" << lane << "\n";
1688 : #endif
1689 : int index = 0;
1690 2987 : for (int i = 0; i < (int)myConnections.size(); ++i) {
1691 2495 : if (myConnections[i].fromLane == (int)(lane) && canMoveConnection(myConnections[i], lane + 1)) {
1692 : index = i;
1693 : }
1694 : }
1695 : std::vector<Connection>::iterator i = myConnections.begin() + index;
1696 492 : Connection c = *i;
1697 492 : myConnections.erase(i);
1698 492 : setConnection(lane + 1, c.toEdge, c.toLane, Lane2LaneInfoType::VALIDATED, false);
1699 492 : }
1700 :
1701 :
1702 : void
1703 101 : NBEdge::moveConnectionToRight(int lane) {
1704 : #ifdef DEBUG_CONNECTION_CHECKING
1705 : std::cout << " moveConnectionToRight " << getID() << " lane=" << lane << "\n";
1706 : #endif
1707 355 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1708 355 : if ((*i).fromLane == (int)lane && canMoveConnection(*i, lane - 1)) {
1709 101 : Connection c = *i;
1710 101 : i = myConnections.erase(i);
1711 101 : setConnection(lane - 1, c.toEdge, c.toLane, Lane2LaneInfoType::VALIDATED, false);
1712 : return;
1713 101 : }
1714 : }
1715 : }
1716 :
1717 :
1718 : double
1719 53999 : NBEdge::buildInnerEdges(const NBNode& n, int noInternalNoSplits, int& linkIndex, int& splitIndex) {
1720 53999 : const OptionsCont& oc = OptionsCont::getOptions();
1721 53999 : const int numPoints = oc.getInt("junctions.internal-link-detail");
1722 53999 : const bool joinTurns = oc.getBool("junctions.join-turns");
1723 53999 : const double limitTurnSpeed = oc.getFloat("junctions.limit-turn-speed");
1724 53999 : const double limitTurnSpeedMinAngle = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle"));
1725 53999 : const double limitTurnSpeedMinAngleRail = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle.railway"));
1726 53999 : const double limitTurnSpeedWarnStraight = oc.getFloat("junctions.limit-turn-speed.warn.straight");
1727 53999 : const double limitTurnSpeedWarnTurn = oc.getFloat("junctions.limit-turn-speed.warn.turn");
1728 53999 : const bool higherSpeed = oc.getBool("junctions.higher-speed");
1729 53999 : const double interalJunctionVehicleWidth = oc.getFloat("internal-junctions.vehicle-width");
1730 53999 : const double defaultContPos = oc.getFloat("default.connection.cont-pos");
1731 53999 : const bool fromRail = isRailway(getPermissions());
1732 53999 : std::string innerID = ":" + n.getID();
1733 : NBEdge* toEdge = nullptr;
1734 53999 : int edgeIndex = linkIndex;
1735 : int internalLaneIndex = 0;
1736 : int numLanes = 0; // number of lanes that share the same edge
1737 : double lengthSum = 0; // total shape length of all lanes that share the same edge
1738 : int avoidedIntersectingLeftOriginLane = std::numeric_limits<int>::max();
1739 : bool averageLength = true;
1740 : double maxCross = 0.;
1741 156534 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1742 : Connection& con = *i;
1743 102535 : con.haveVia = false; // reset first since this may be called multiple times
1744 102535 : if (con.toEdge == nullptr) {
1745 0 : continue;
1746 : }
1747 102535 : LinkDirection dir = n.getDirection(this, con.toEdge);
1748 102535 : const bool isRightTurn = (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT);
1749 102535 : const bool isTurn = (isRightTurn || dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT);
1750 : // put turning internal lanes on separate edges
1751 102535 : if (con.toEdge != toEdge) {
1752 : // skip indices to keep some correspondence between edge ids and link indices:
1753 : // internalEdgeIndex + internalLaneIndex = linkIndex
1754 92652 : edgeIndex = linkIndex;
1755 : toEdge = con.toEdge;
1756 : internalLaneIndex = 0;
1757 92652 : maxCross = MAX2(maxCross, assignInternalLaneLength(i, numLanes, lengthSum, averageLength));
1758 : numLanes = 0;
1759 : lengthSum = 0;
1760 : }
1761 102535 : averageLength = !isTurn || joinTurns; // legacy behavior
1762 102535 : SVCPermissions conPermissions = getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane);
1763 102535 : const int conShapeFlag = (conPermissions & ~SVC_PEDESTRIAN) != 0 ? 0 : NBNode::SCURVE_IGNORE;
1764 102535 : PositionVector shape = n.computeInternalLaneShape(this, con, numPoints, myTo, conShapeFlag);
1765 : std::vector<int> foeInternalLinks;
1766 :
1767 102535 : if (dir != LinkDirection::STRAIGHT && shape.length() < POSITION_EPS && !(isBidiRail() && getTurnDestination(true) == con.toEdge)) {
1768 180 : WRITE_WARNINGF(TL("Connection '%_%->%_%' is only %m short."), getID(), con.fromLane, con.toEdge->getID(), con.toLane, shape.length());
1769 : }
1770 :
1771 : // crossingPosition, list of foe link indices
1772 102535 : std::pair<double, std::vector<int> > crossingPositions(-1, std::vector<int>());
1773 : std::set<std::string> tmpFoeIncomingLanes;
1774 102535 : if (dir != LinkDirection::STRAIGHT || con.contPos != UNSPECIFIED_CONTPOS) {
1775 64806 : int index = 0;
1776 : std::vector<PositionVector> otherShapes;
1777 64806 : const double width1 = MIN2(interalJunctionVehicleWidth / 2, getLaneWidth(con.fromLane) / 2);
1778 : const double width1OppositeLeft = 0; // using width1 changes a lot of curves even though they are rarely responsible for collisions
1779 285091 : for (const NBEdge* i2 : n.getIncomingEdges()) {
1780 1017526 : for (const Connection& k2 : i2->getConnections()) {
1781 797241 : if (k2.toEdge == nullptr) {
1782 0 : continue;
1783 : }
1784 : // vehicles are typically less wide than the lane
1785 : // they drive on but but bicycle lanes should be kept clear for their whole width
1786 797241 : double width2 = k2.toEdge->getLaneWidth(k2.toLane);
1787 797241 : if (k2.toEdge->getPermissions(k2.toLane) != SVC_BICYCLE) {
1788 782871 : width2 *= 0.5;
1789 : }
1790 797241 : const bool foes = n.foes(this, con.toEdge, i2, k2.toEdge);
1791 797241 : LinkDirection dir2 = n.getDirection(i2, k2.toEdge);
1792 797241 : bool needsCont = !isRailway(conPermissions) && (n.needsCont(this, i2, con, k2) || (con.contPos != UNSPECIFIED_CONTPOS && !con.indirectLeft));
1793 797241 : const bool avoidIntersectCandidate = !foes && bothLeftTurns(dir, i2, dir2);
1794 14698 : bool oppositeLeftIntersect = avoidIntersectCandidate && haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2);
1795 : int shapeFlag = 0;
1796 797241 : SVCPermissions warn = SVCAll & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_DELIVERY | SVC_RAIL_CLASSES);
1797 : // do not warn if only bicycles, pedestrians or delivery vehicles are involved as this is a typical occurrence
1798 : if (con.customShape.size() == 0
1799 796522 : && k2.customShape.size() == 0
1800 796120 : && (oppositeLeftIntersect || (avoidedIntersectingLeftOriginLane < con.fromLane && avoidIntersectCandidate))
1801 801031 : && ((i2->getPermissions(k2.fromLane) & warn) != 0
1802 3265 : && (k2.toEdge->getPermissions(k2.toLane) & warn) != 0)) {
1803 : // recompute with different curve parameters (unless
1804 : // the other connection is "unimportant"
1805 3186 : shapeFlag = NBNode::AVOID_INTERSECTING_LEFT_TURNS;
1806 : PositionVector origShape = shape;
1807 6372 : shape = n.computeInternalLaneShape(this, con, numPoints, myTo, shapeFlag);
1808 3186 : oppositeLeftIntersect = haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2, shapeFlag);
1809 3186 : if (oppositeLeftIntersect
1810 2106 : && (conPermissions & (SVCAll & ~(SVC_BICYCLE | SVC_PEDESTRIAN))) == 0) {
1811 : shape = origShape;
1812 : } else {
1813 : // recompute previously computed crossing positions
1814 3138 : if (avoidedIntersectingLeftOriginLane == std::numeric_limits<int>::max()
1815 1757 : || avoidedIntersectingLeftOriginLane < con.fromLane) {
1816 3201 : for (const PositionVector& otherShape : otherShapes) {
1817 1773 : const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
1818 1773 : const double minDV = firstIntersection(shape, otherShape, width1OppositeLeft, width2,
1819 1773 : "Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
1820 1773 : if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
1821 : assert(minDV >= 0);
1822 1647 : if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1823 264 : crossingPositions.first = minDV;
1824 : }
1825 : }
1826 : }
1827 : }
1828 : // make sure connections further to the left do not get a wider angle
1829 3138 : avoidedIntersectingLeftOriginLane = con.fromLane;
1830 : }
1831 3186 : }
1832 797241 : const bool bothPrio = getJunctionPriority(&n) > 0 && i2->getJunctionPriority(&n) > 0;
1833 : //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";
1834 : // the following special case might get obsolete once we have solved #9745
1835 797241 : const bool isBicycleLeftTurn = k2.indirectLeft || (dir2 == LinkDirection::LEFT && (i2->getPermissions(k2.fromLane) & k2.toEdge->getPermissions(k2.toLane)) == SVC_BICYCLE);
1836 : // compute the crossing point
1837 797241 : if ((needsCont || (bothPrio && oppositeLeftIntersect && !isRailway(conPermissions))) && (!con.indirectLeft || dir2 == LinkDirection::STRAIGHT) && !isBicycleLeftTurn) {
1838 33957 : crossingPositions.second.push_back(index);
1839 33957 : const PositionVector otherShape = n.computeInternalLaneShape(i2, k2, numPoints, 0, shapeFlag);
1840 33957 : otherShapes.push_back(otherShape);
1841 33957 : const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
1842 33957 : const double minDV = firstIntersection(shape, otherShape, width1, width2,
1843 33957 : "Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
1844 33957 : if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
1845 : assert(minDV >= 0);
1846 29042 : if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1847 28311 : crossingPositions.first = minDV;
1848 : }
1849 : }
1850 33957 : }
1851 797241 : const bool rightTurnConflict = NBNode::rightTurnConflict(
1852 797241 : this, con.toEdge, con.fromLane, i2, k2.toEdge, k2.fromLane);
1853 797241 : const bool indirectTurnConflit = con.indirectLeft && this == i2 && (dir2 == LinkDirection::STRAIGHT ||
1854 52 : (con.fromLane < k2.fromLane && (dir2 == LinkDirection::RIGHT || dir2 == LinkDirection::PARTRIGHT)));
1855 797241 : const bool mergeConflict = myTo->mergeConflict(this, con, i2, k2, true);
1856 797241 : const bool mergeResponse = myTo->mergeConflict(this, con, i2, k2, false);
1857 797241 : const bool bidiConflict = myTo->bidiConflict(this, con, i2, k2, true);
1858 : // compute foe internal lanes
1859 797241 : if (foes || rightTurnConflict || oppositeLeftIntersect || mergeConflict || indirectTurnConflit || bidiConflict) {
1860 227325 : foeInternalLinks.push_back(index);
1861 : }
1862 : // only warn once per pair of intersecting turns
1863 2737 : if (oppositeLeftIntersect && getID() > i2->getID()
1864 1371 : && (getPermissions(con.fromLane) & warn) != 0
1865 1147 : && (con.toEdge->getPermissions(con.toLane) & warn) != 0
1866 1111 : && (i2->getPermissions(k2.fromLane) & warn) != 0
1867 1039 : && (k2.toEdge->getPermissions(k2.toLane) & warn) != 0
1868 : // do not warn for unregulated nodes
1869 798267 : && n.getType() != SumoXMLNodeType::NOJUNCTION
1870 : ) {
1871 513 : WRITE_WARNINGF(TL("Intersecting left turns at junction '%' from lane '%' and lane '%' (increase junction radius to avoid this)."),
1872 : n.getID(), getLaneID(con.fromLane), i2->getLaneID(k2.fromLane));
1873 : }
1874 : // compute foe incoming lanes
1875 797241 : const bool signalised = hasSignalisedConnectionTo(con.toEdge);
1876 1458057 : if ((n.forbids(i2, k2.toEdge, this, con.toEdge, signalised) || rightTurnConflict || indirectTurnConflit || mergeResponse)
1877 797970 : && (needsCont || dir == LinkDirection::TURN || (!signalised && this != i2 && !con.indirectLeft))) {
1878 243020 : tmpFoeIncomingLanes.insert(i2->getID() + "_" + toString(k2.fromLane));
1879 : }
1880 797241 : if (bothPrio && oppositeLeftIntersect && getID() < i2->getID()) {
1881 : //std::cout << " c1=" << con.getDescription(this) << " c2=" << k2.getDescription(i2) << " bothPrio=" << bothPrio << " oppositeLeftIntersect=" << oppositeLeftIntersect << "\n";
1882 : // break symmetry using edge id
1883 : // only store link index and resolve actual lane id later (might be multi-lane internal edge)
1884 328 : tmpFoeIncomingLanes.insert(":" + toString(index));
1885 : }
1886 797241 : index++;
1887 : }
1888 : }
1889 64806 : if (dir == LinkDirection::TURN && crossingPositions.first < 0 && crossingPositions.second.size() != 0 && shape.length() > 2. * POSITION_EPS) {
1890 : // let turnarounds wait in the middle if no other crossing point was found and it has a sensible length
1891 : // (if endOffset is used, the crossing point is in the middle of the part within the junction shape)
1892 226 : crossingPositions.first = (double)(shape.length() + getEndOffset(con.fromLane)) / 2.;
1893 : }
1894 : // foe pedestrian crossings
1895 64806 : std::vector<NBNode::Crossing*> crossings = n.getCrossings();
1896 82736 : for (auto c : crossings) {
1897 : const NBNode::Crossing& crossing = *c;
1898 50143 : for (EdgeVector::const_iterator it_e = crossing.edges.begin(); it_e != crossing.edges.end(); ++it_e) {
1899 32213 : const NBEdge* edge = *it_e;
1900 : // compute foe internal lanes
1901 32213 : if ((this == edge || con.toEdge == edge) && !isRailway(conPermissions)) {
1902 8410 : foeInternalLinks.push_back(index);
1903 8410 : if (con.toEdge == edge &&
1904 4197 : ((isRightTurn && getJunctionPriority(&n) > 0) || (isTurn && con.tlID != ""))) {
1905 : // build internal junctions (not for left turns at uncontrolled intersections)
1906 : PositionVector crossingShape = crossing.shape;
1907 1723 : crossingShape.extrapolate(5.0); // sometimes shapes miss each other by a small margin
1908 1723 : const double minDV = firstIntersection(shape, crossingShape, 0, crossing.width / 2);
1909 1723 : if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) {
1910 : assert(minDV >= 0);
1911 1594 : if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1912 928 : crossingPositions.first = minDV;
1913 : }
1914 : }
1915 8410 : } else if (this == edge && crossing.priority && !myTo->isTLControlled()) {
1916 20 : crossingPositions.first = 0;
1917 : }
1918 : }
1919 : }
1920 17930 : index++;
1921 : }
1922 :
1923 64806 : }
1924 102535 : if (con.contPos == UNSPECIFIED_CONTPOS) {
1925 102505 : con.contPos = defaultContPos;
1926 : }
1927 102535 : if (con.contPos != UNSPECIFIED_CONTPOS) {
1928 : // apply custom internal junction position
1929 50 : if (con.contPos <= 0 || con.contPos >= shape.length()) {
1930 : // disable internal junction
1931 21 : crossingPositions.first = -1;
1932 : } else {
1933 : // set custom position
1934 29 : crossingPositions.first = con.contPos;
1935 : }
1936 : }
1937 :
1938 : // @todo compute the maximum speed allowed based on angular velocity
1939 : // see !!! for an explanation (with a_lat_mean ~0.3)
1940 : /*
1941 : double vmax = (double) 0.3 * (double) 9.80778 *
1942 : getLaneShape(con.fromLane).back().distanceTo(
1943 : con.toEdge->getLaneShape(con.toLane).front())
1944 : / (double) 2.0 / (double) M_PI;
1945 : vmax = MIN2(vmax, ((getSpeed() + con.toEdge->getSpeed()) / (double) 2.0));
1946 : */
1947 102535 : if (con.speed == UNSPECIFIED_SPEED) {
1948 102367 : if (higherSpeed) {
1949 44 : con.vmax = MAX2(myLanes[con.fromLane].speed, con.toEdge->getLanes()[con.toLane].speed);
1950 : } else {
1951 102343 : con.vmax = (myLanes[con.fromLane].speed + con.toEdge->getLanes()[con.toLane].speed) / (double) 2.0;
1952 : }
1953 102367 : if (limitTurnSpeed > 0) {
1954 : // see [Odhams and Cole, Models of Driver Speed Choice in Curves, 2004]
1955 193926 : const double angleRaw = fabs(GeomHelper::angleDiff(
1956 96963 : getLaneShape(con.fromLane).angleAt2D(-2),
1957 96963 : con.toEdge->getLaneShape(con.toLane).angleAt2D(0)));
1958 190374 : const double angle = MAX2(0.0, angleRaw - (fromRail ? limitTurnSpeedMinAngleRail : limitTurnSpeedMinAngle));
1959 96963 : const double length = shape.length2D();
1960 : // do not trust the radius of tiny junctions
1961 : // formula adapted from [Odhams, Andre and Cole, David, Models of Driver Speed Choice in Curves, 2004]
1962 96963 : if (angle > 0 && length > 1) {
1963 : // permit higher turning speed on wide lanes
1964 64831 : const double radius = length / angle + getLaneWidth(con.fromLane) / 4;
1965 64831 : const double limit = sqrt(limitTurnSpeed * radius);
1966 64831 : const double reduction = con.vmax - limit;
1967 : // always treat connctions at roundabout as turns when warning
1968 64831 : const bool atRoundabout = getJunctionPriority(myTo) == JunctionPriority::ROUNDABOUT || con.toEdge->getJunctionPriority(myFrom) == JunctionPriority::ROUNDABOUT;
1969 : const LinkDirection dir2 = atRoundabout ? LinkDirection::LEFT : dir;
1970 64521 : if ((dir2 == LinkDirection::STRAIGHT && reduction > limitTurnSpeedWarnStraight)
1971 64648 : || (dir2 != LinkDirection::TURN && reduction > limitTurnSpeedWarnTurn)) {
1972 503 : std::string dirType = std::string(dir == LinkDirection::STRAIGHT ? "straight" : "turning");
1973 343 : if (atRoundabout) {
1974 : dirType = "roundabout";
1975 : }
1976 1029 : WRITE_WARNINGF(TL("Speed of % connection '%' reduced by % due to turning radius of % (length=%, angle=%)."),
1977 : dirType, con.getDescription(this), reduction, radius, length, RAD2DEG(angleRaw));
1978 : }
1979 121168 : con.vmax = MIN2(con.vmax, limit);
1980 : // value is saved in <net> attribute. Must be set again when importing from .con.xml
1981 : // con.speed = con.vmax;
1982 : }
1983 : assert(con.vmax > 0);
1984 : //if (getID() == "-1017000.0.00") {
1985 : // std::cout << con.getDescription(this) << " angleRaw=" << angleRaw << " angle=" << RAD2DEG(angle) << " length=" << length << " radius=" << length / angle
1986 : // << " vmaxTurn=" << sqrt(limitTurnSpeed * length / angle) << " vmax=" << con.vmax << "\n";
1987 : //}
1988 5404 : } else if (fromRail && dir == LinkDirection::TURN) {
1989 0 : con.vmax = 0.01;
1990 : }
1991 : } else {
1992 168 : con.vmax = con.speed;
1993 : }
1994 102535 : if (con.friction == UNSPECIFIED_FRICTION) {
1995 102535 : con.friction = (myLanes[con.fromLane].friction + con.toEdge->getLanes()[con.toLane].friction) / 2.;
1996 : }
1997 : //
1998 : assert(shape.size() >= 2);
1999 : // get internal splits if any
2000 205070 : con.id = innerID + "_" + toString(edgeIndex);
2001 102535 : const double shapeLength = shape.length();
2002 : double firstLength = shapeLength;
2003 102535 : con.internalViaLaneIndex = 0; // reset to default for netedit
2004 102535 : if (crossingPositions.first > 0 && crossingPositions.first < shapeLength) {
2005 19337 : std::pair<PositionVector, PositionVector> split = shape.splitAt(crossingPositions.first);
2006 : con.shape = split.first;
2007 19337 : con.foeIncomingLanes = std::vector<std::string>(tmpFoeIncomingLanes.begin(), tmpFoeIncomingLanes.end());
2008 19337 : con.foeInternalLinks = foeInternalLinks; // resolve link indices to lane ids later
2009 19337 : if (i != myConnections.begin() && (i - 1)->toEdge == con.toEdge && (i - 1)->haveVia) {
2010 235 : --splitIndex;
2011 235 : con.internalViaLaneIndex = (i - 1)->internalViaLaneIndex + 1;
2012 : }
2013 38674 : con.viaID = innerID + "_" + toString(splitIndex + noInternalNoSplits);
2014 19337 : ++splitIndex;
2015 : con.viaShape = split.second;
2016 19337 : con.haveVia = true;
2017 19337 : firstLength = con.shape.length();
2018 19337 : } else {
2019 : con.shape = shape;
2020 : }
2021 102535 : con.internalLaneIndex = internalLaneIndex;
2022 102535 : ++internalLaneIndex;
2023 102535 : ++linkIndex;
2024 : ++numLanes;
2025 102535 : if (con.customLength != UNSPECIFIED_LOADED_LENGTH) {
2026 : // split length proportionally
2027 30 : lengthSum += (shapeLength != 0 ? firstLength / shapeLength : 1) * con.customLength;
2028 : } else {
2029 102505 : lengthSum += firstLength;
2030 : }
2031 102535 : }
2032 107998 : return MAX2(maxCross, assignInternalLaneLength(myConnections.end(), numLanes, lengthSum, averageLength));
2033 : }
2034 :
2035 :
2036 : double
2037 146651 : NBEdge::assignInternalLaneLength(std::vector<Connection>::iterator i, int numLanes, double lengthSum, bool averageLength) {
2038 : // assign average length to all lanes of the same internal edge if averageLength is set
2039 : // the lengthSum only covers the part up to the first internal junction
2040 : // TODO This code assumes that either all connections in question have a via or none
2041 : double maxCross = 0.;
2042 : assert(i - myConnections.begin() >= numLanes);
2043 249186 : for (int prevIndex = 1; prevIndex <= numLanes; prevIndex++) {
2044 : //std::cout << " con=" << (*(i - prevIndex)).getDescription(this) << " numLanes=" << numLanes << " avgLength=" << lengthSum / numLanes << "\n";
2045 : Connection& c = (*(i - prevIndex));
2046 102535 : const double minLength = c.customLength != UNSPECIFIED_LOADED_LENGTH ? pow(10, -gPrecision) : POSITION_EPS;
2047 102535 : c.length = MAX2(minLength, averageLength ? lengthSum / numLanes : c.shape.length());
2048 102535 : if (c.haveVia) {
2049 38674 : c.viaLength = MAX2(minLength, c.viaShape.length());
2050 : }
2051 102535 : if (c.customLength != UNSPECIFIED_LOADED_LENGTH) {
2052 30 : if (c.haveVia) {
2053 : // split length proportionally
2054 4 : const double a = c.viaLength / (c.shape.length() + c.viaLength);
2055 6 : c.viaLength = MAX2(minLength, a * c.customLength);
2056 : }
2057 30 : if (!averageLength) {
2058 19 : c.length = MAX2(minLength, c.customLength - c.viaLength);
2059 : }
2060 : }
2061 102535 : if (c.haveVia) {
2062 : // we need to be able to leave from the internal junction by accelerating from 0
2063 19337 : maxCross = MAX2(maxCross, sqrt(2. * c.viaLength)); // t = sqrt(2*s/a) and we assume 'a' is at least 1 (default value for tram in SUMOVTypeParameter)
2064 : }
2065 : // we need to be able to cross the junction in one go but not if we have an indirect left turn
2066 102535 : if (c.indirectLeft) {
2067 15 : maxCross = MAX2(maxCross, MAX2(c.length, c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
2068 : } else {
2069 146677 : maxCross = MAX2(maxCross, (c.length + c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
2070 : }
2071 : }
2072 146651 : return maxCross;
2073 : }
2074 :
2075 :
2076 : double
2077 78065 : NBEdge::firstIntersection(const PositionVector& v1, const PositionVector& v2, double width1, double width2, const std::string& error, bool secondIntersection) {
2078 : double intersect = std::numeric_limits<double>::max();
2079 78065 : if (v2.length() < POSITION_EPS) {
2080 : return intersect;
2081 : }
2082 : try {
2083 : PositionVector v1Right = v1;
2084 78037 : v1Right.move2side(width1);
2085 :
2086 : PositionVector v1Left = v1;
2087 78037 : v1Left.move2side(-width1);
2088 :
2089 : PositionVector v2Right = v2;
2090 78037 : v2Right.move2side(width2);
2091 :
2092 : PositionVector v2Left = v2;
2093 78037 : v2Left.move2side(-width2);
2094 :
2095 : // intersect all border combinations
2096 : bool skip = secondIntersection;
2097 102814 : for (double cand : v1Left.intersectsAtLengths2D(v2Right)) {
2098 24777 : if (skip) {
2099 : skip = false;
2100 8 : continue;
2101 : }
2102 : intersect = MIN2(intersect, cand);
2103 78037 : }
2104 : skip = secondIntersection;
2105 116256 : for (double cand : v1Left.intersectsAtLengths2D(v2Left)) {
2106 38219 : if (skip) {
2107 : skip = false;
2108 11 : continue;
2109 : }
2110 : intersect = MIN2(intersect, cand);
2111 78037 : }
2112 : skip = secondIntersection;
2113 122800 : for (double cand : v1Right.intersectsAtLengths2D(v2Right)) {
2114 44763 : if (skip) {
2115 : skip = false;
2116 9 : continue;
2117 : }
2118 : intersect = MIN2(intersect, cand);
2119 78037 : }
2120 : skip = secondIntersection;
2121 117126 : for (double cand : v1Right.intersectsAtLengths2D(v2Left)) {
2122 39089 : if (skip) {
2123 : skip = false;
2124 11 : continue;
2125 : }
2126 : intersect = MIN2(intersect, cand);
2127 78037 : }
2128 78037 : } catch (InvalidArgument&) {
2129 0 : if (error != "") {
2130 0 : WRITE_WARNING(error);
2131 : }
2132 0 : }
2133 : //std::cout << " v1=" << v1 << " v2Right=" << v2Right << " v2Left=" << v2Left << "\n";
2134 : //std::cout << " intersectsRight=" << toString(v1.intersectsAtLengths2D(v2Right)) << "\n";
2135 : //std::cout << " intersectsLeft=" << toString(v1.intersectsAtLengths2D(v2Left)) << "\n";
2136 : return intersect;
2137 : }
2138 :
2139 :
2140 : bool
2141 573515 : NBEdge::bothLeftTurns(LinkDirection dir, const NBEdge* otherFrom, LinkDirection dir2) const {
2142 573515 : if (otherFrom == this) {
2143 : // not an opposite pair
2144 : return false;
2145 : }
2146 348725 : return (dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT) && (dir2 == LinkDirection::LEFT || dir2 == LinkDirection::PARTLEFT);
2147 : }
2148 :
2149 : bool
2150 17884 : NBEdge::haveIntersection(const NBNode& n, const PositionVector& shape, const NBEdge* otherFrom, const NBEdge::Connection& otherCon, int numPoints,
2151 : double width1, double width2, int shapeFlag) const {
2152 17884 : const PositionVector otherShape = n.computeInternalLaneShape(otherFrom, otherCon, numPoints, 0, shapeFlag);
2153 17884 : const double minDV = firstIntersection(shape, otherShape, width1, width2);
2154 35768 : return minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS;
2155 17884 : }
2156 :
2157 :
2158 : // -----------
2159 : int
2160 12578056 : NBEdge::getJunctionPriority(const NBNode* const node) const {
2161 12578056 : if (node == myFrom) {
2162 288699 : return myFromJunctionPriority;
2163 : } else {
2164 12289357 : return myToJunctionPriority;
2165 : }
2166 : }
2167 :
2168 :
2169 : void
2170 369459 : NBEdge::setJunctionPriority(const NBNode* const node, int prio) {
2171 369459 : if (node == myFrom) {
2172 182317 : myFromJunctionPriority = prio;
2173 : #ifdef DEBUG_JUNCTIONPRIO
2174 : setParameter("fromPrio", toString(prio));
2175 : #endif
2176 : } else {
2177 187142 : myToJunctionPriority = prio;
2178 : #ifdef DEBUG_JUNCTIONPRIO
2179 : setParameter("toPrio", toString(prio));
2180 : #endif
2181 : }
2182 369459 : }
2183 :
2184 :
2185 : double
2186 21332583 : NBEdge::getAngleAtNode(const NBNode* const atNode) const {
2187 21332583 : if (atNode == myFrom) {
2188 10478702 : return GeomHelper::legacyDegree(myGeom.angleAt2D(0));
2189 : }
2190 : assert(atNode == myTo);
2191 10853881 : return GeomHelper::legacyDegree(myGeom.angleAt2D(-2));
2192 : }
2193 :
2194 :
2195 : double
2196 3994034 : NBEdge::getAngleAtNodeNormalized(const NBNode* const atNode) const {
2197 : double res;
2198 3994034 : if (atNode == myFrom) {
2199 1972127 : res = GeomHelper::legacyDegree(myGeom.angleAt2D(0)) - 180;
2200 : } else {
2201 : assert(atNode == myTo);
2202 2021907 : res = GeomHelper::legacyDegree(myGeom.angleAt2D(-2));
2203 : }
2204 3994034 : if (res < 0) {
2205 2884694 : res += 360;
2206 : }
2207 3994034 : return res;
2208 : }
2209 :
2210 :
2211 : double
2212 6325686 : NBEdge::getAngleAtNodeToCenter(const NBNode* const atNode) const {
2213 6325686 : if (atNode == myFrom) {
2214 2939510 : double res = myStartAngle - 180;
2215 2939510 : if (res < 0) {
2216 1275080 : res += 360;
2217 : }
2218 2939510 : return res;
2219 : } else {
2220 : assert(atNode == myTo);
2221 3386176 : return myEndAngle;
2222 : }
2223 : }
2224 :
2225 :
2226 : void
2227 731714 : NBEdge::setTurningDestination(NBEdge* e, bool onlyPossible) {
2228 731714 : if (!onlyPossible) {
2229 659909 : myTurnDestination = e;
2230 : }
2231 731714 : myPossibleTurnDestination = e;
2232 731714 : }
2233 :
2234 :
2235 : double
2236 10706 : NBEdge::getLaneSpeed(int lane) const {
2237 10706 : return myLanes[lane].speed;
2238 : }
2239 :
2240 :
2241 : double
2242 3927 : NBEdge::getLaneFriction(int lane) const {
2243 3927 : return myLanes[lane].friction;
2244 : }
2245 :
2246 :
2247 : void
2248 80 : NBEdge::resetLaneShapes() {
2249 80 : computeLaneShapes();
2250 80 : }
2251 :
2252 :
2253 : void
2254 6 : NBEdge::updateChangeRestrictions(SVCPermissions ignoring) {
2255 24 : for (Lane& lane : myLanes) {
2256 18 : if (lane.changeLeft != SVCAll) {
2257 6 : lane.changeLeft = ignoring;
2258 : }
2259 18 : if (lane.changeRight != SVCAll) {
2260 6 : lane.changeRight = ignoring;
2261 : }
2262 : }
2263 15 : for (Connection& con : myConnections) {
2264 9 : if (con.changeLeft != SVC_UNSPECIFIED && con.changeLeft != SVCAll) {
2265 0 : con.changeLeft = ignoring;
2266 : }
2267 9 : if (con.changeRight != SVC_UNSPECIFIED && con.changeRight != SVCAll) {
2268 0 : con.changeRight = ignoring;
2269 : }
2270 : }
2271 6 : }
2272 :
2273 :
2274 : void
2275 393563 : NBEdge::computeLaneShapes() {
2276 : // vissim needs this
2277 393563 : if (myFrom == myTo) {
2278 0 : return;
2279 : }
2280 : // compute lane offset, first
2281 393563 : std::vector<double> offsets(myLanes.size(), 0.);
2282 : double offset = 0;
2283 507139 : for (int i = (int)myLanes.size() - 2; i >= 0; --i) {
2284 113576 : offset += (getLaneWidth(i) + getLaneWidth(i + 1)) / 2.;
2285 113576 : offsets[i] = offset;
2286 : }
2287 393563 : if (myLaneSpreadFunction == LaneSpreadFunction::CENTER) {
2288 : double width = 0;
2289 387449 : for (int i = 0; i < (int)myLanes.size(); ++i) {
2290 221023 : width += getLaneWidth(i);
2291 : }
2292 166426 : offset = -width / 2. + getLaneWidth((int)myLanes.size() - 1) / 2.;
2293 : } else {
2294 227137 : double laneWidth = myLanes.back().width != UNSPECIFIED_WIDTH ? myLanes.back().width : SUMO_const_laneWidth;
2295 227137 : offset = laneWidth / 2.;
2296 : }
2297 393563 : if (myLaneSpreadFunction == LaneSpreadFunction::ROADCENTER) {
2298 4916 : for (NBEdge* e : myTo->getOutgoingEdges()) {
2299 4279 : if (e->getToNode() == myFrom && getInnerGeometry().reverse() == e->getInnerGeometry()) {
2300 1765 : offset += (e->getTotalWidth() - getTotalWidth()) / 2;
2301 1765 : break;
2302 : }
2303 : }
2304 : }
2305 :
2306 900702 : for (int i = 0; i < (int)myLanes.size(); ++i) {
2307 507139 : offsets[i] += offset;
2308 : }
2309 :
2310 : // build the shape of each lane
2311 900702 : for (int i = 0; i < (int)myLanes.size(); ++i) {
2312 507139 : if (myLanes[i].customShape.size() != 0) {
2313 : myLanes[i].shape = myLanes[i].customShape;
2314 26 : continue;
2315 : }
2316 : try {
2317 1014226 : myLanes[i].shape = computeLaneShape(i, offsets[i]);
2318 0 : } catch (InvalidArgument& e) {
2319 0 : WRITE_WARNINGF(TL("In lane '%': lane shape could not be determined (%)."), getLaneID(i), e.what());
2320 : myLanes[i].shape = myGeom;
2321 0 : }
2322 : }
2323 393563 : }
2324 :
2325 :
2326 : PositionVector
2327 507113 : NBEdge::computeLaneShape(int lane, double offset) const {
2328 : PositionVector shape = myGeom;
2329 : try {
2330 507113 : shape.move2side(offset);
2331 0 : } catch (InvalidArgument& e) {
2332 0 : WRITE_WARNINGF(TL("In lane '%': Could not build shape (%)."), getLaneID(lane), e.what());
2333 0 : }
2334 507113 : return shape;
2335 0 : }
2336 :
2337 :
2338 : void
2339 509891 : NBEdge::computeAngle() {
2340 : // taking the angle at the first might be unstable, thus we take the angle
2341 : // at a certain distance. (To compare two edges, additional geometry
2342 : // segments are considered to resolve ambiguities)
2343 509891 : const bool hasFromShape = myFrom->getShape().size() > 0;
2344 509891 : const bool hasToShape = myTo->getShape().size() > 0;
2345 509891 : Position fromCenter = (hasFromShape ? myFrom->getShape().getCentroid() : myFrom->getPosition());
2346 509891 : Position toCenter = (hasToShape ? myTo->getShape().getCentroid() : myTo->getPosition());
2347 : PositionVector shape = myGeom;
2348 509891 : if ((hasFromShape || hasToShape) && getNumLanes() > 0) {
2349 224275 : if (myLaneSpreadFunction == LaneSpreadFunction::RIGHT) {
2350 117362 : shape = myLanes[getNumLanes() - 1].shape ;
2351 : } else {
2352 106913 : shape = myLanes[getNumLanes() / 2].shape;
2353 106913 : if (getNumLanes() % 2 == 0) {
2354 : // there is no center lane. shift to get the center
2355 16814 : shape.move2side(getLaneWidth(getNumLanes() / 2) * 0.5);
2356 : }
2357 : }
2358 : }
2359 :
2360 : // if the junction shape is suspicious we cannot trust the angle to the centroid
2361 509891 : const bool suspiciousFromShape = hasFromShape && (myFrom->getShape().distance2D(shape[0]) > 2 * POSITION_EPS
2362 191847 : || myFrom->getShape().around(shape[-1])
2363 191091 : || !(myFrom->getShape().around(fromCenter)));
2364 509891 : const bool suspiciousToShape = hasToShape && (myTo->getShape().distance2D(shape[-1]) > 2 * POSITION_EPS
2365 187355 : || myTo->getShape().around(shape[0])
2366 186709 : || !(myTo->getShape().around(toCenter)));
2367 :
2368 509891 : const double angleLookahead = MIN2(shape.length2D() / 2, ANGLE_LOOKAHEAD);
2369 509891 : const Position referencePosStart = shape.positionAtOffset2D(angleLookahead);
2370 509891 : const Position referencePosEnd = shape.positionAtOffset2D(shape.length2D() - angleLookahead);
2371 :
2372 509891 : myStartAngle = GeomHelper::legacyDegree(fromCenter.angleTo2D(referencePosStart), true);
2373 509891 : const double myStartAngle2 = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(referencePosStart), true);
2374 509891 : const double myStartAngle3 = getAngleAtNode(myFrom);
2375 509891 : myEndAngle = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(toCenter), true);
2376 509891 : const double myEndAngle2 = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myTo->getPosition()), true);
2377 509891 : const double myEndAngle3 = getAngleAtNode(myTo);
2378 :
2379 : #ifdef DEBUG_ANGLES
2380 : if (DEBUGCOND) {
2381 : if (suspiciousFromShape) {
2382 : std::cout << "suspiciousFromShape len=" << shape.length() << " startA=" << myStartAngle << " startA2=" << myStartAngle2 << " startA3=" << myStartAngle3
2383 : << " rel=" << NBHelpers::normRelAngle(myStartAngle, myStartAngle2)
2384 : << " fromCenter=" << fromCenter
2385 : << " fromPos=" << myFrom->getPosition()
2386 : << " refStart=" << referencePosStart
2387 : << "\n";
2388 : }
2389 : if (suspiciousToShape) {
2390 : std::cout << "suspiciousToShape len=" << shape.length() << " endA=" << myEndAngle << " endA2=" << myEndAngle2 << " endA3=" << myEndAngle3
2391 : << " rel=" << NBHelpers::normRelAngle(myEndAngle, myEndAngle2)
2392 : << " toCenter=" << toCenter
2393 : << " toPos=" << myTo->getPosition()
2394 : << " refEnd=" << referencePosEnd
2395 : << "\n";
2396 : }
2397 : }
2398 : #endif
2399 :
2400 509891 : if (suspiciousFromShape && shape.length() > 1) {
2401 18489 : myStartAngle = myStartAngle2;
2402 45194 : } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myStartAngle, myStartAngle3)) > 90
2403 : // don't trust footpath angles
2404 495580 : && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
2405 2714 : myStartAngle = myStartAngle3;
2406 2714 : if (myStartAngle < 0) {
2407 1313 : myStartAngle += 360;
2408 : }
2409 : }
2410 :
2411 509891 : if (suspiciousToShape && shape.length() > 1) {
2412 23945 : myEndAngle = myEndAngle2;
2413 24093 : } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myEndAngle, myEndAngle3)) > 90
2414 : // don't trust footpath angles
2415 491216 : && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
2416 3273 : myEndAngle = myEndAngle3;
2417 3273 : if (myEndAngle < 0) {
2418 1604 : myEndAngle += 360;
2419 : }
2420 : }
2421 :
2422 509891 : myTotalAngle = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(myTo->getPosition()), true);
2423 : #ifdef DEBUG_ANGLES
2424 : if (DEBUGCOND) std::cout << "computeAngle edge=" << getID()
2425 : << " fromCenter=" << fromCenter << " toCenter=" << toCenter
2426 : << " refStart=" << referencePosStart << " refEnd=" << referencePosEnd << " shape=" << shape
2427 : << " hasFromShape=" << hasFromShape
2428 : << " hasToShape=" << hasToShape
2429 : << " numLanes=" << getNumLanes()
2430 : << " shapeLane=" << getNumLanes() / 2
2431 : << " startA=" << myStartAngle << " endA=" << myEndAngle << " totA=" << myTotalAngle << "\n";
2432 : #endif
2433 509891 : }
2434 :
2435 :
2436 : double
2437 208472 : NBEdge::getShapeStartAngle() const {
2438 208472 : const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
2439 208472 : const Position referencePosStart = myGeom.positionAtOffset2D(angleLookahead);
2440 208472 : return GeomHelper::legacyDegree(myGeom.front().angleTo2D(referencePosStart), true);
2441 : }
2442 :
2443 :
2444 : double
2445 104140 : NBEdge::getShapeEndAngle() const {
2446 104140 : const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
2447 104140 : const Position referencePosEnd = myGeom.positionAtOffset2D(myGeom.length2D() - angleLookahead);
2448 104140 : return GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myGeom.back()), true);
2449 : }
2450 :
2451 :
2452 : bool
2453 0 : NBEdge::hasPermissions() const {
2454 0 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2455 0 : if ((*i).permissions != SVCAll) {
2456 : return true;
2457 : }
2458 : }
2459 : return false;
2460 : }
2461 :
2462 :
2463 : bool
2464 25358 : NBEdge::hasLaneSpecificPermissions() const {
2465 : std::vector<Lane>::const_iterator i = myLanes.begin();
2466 25358 : SVCPermissions firstLanePermissions = i->permissions;
2467 : i++;
2468 27052 : for (; i != myLanes.end(); ++i) {
2469 11691 : if (i->permissions != firstLanePermissions) {
2470 : return true;
2471 : }
2472 : }
2473 : return false;
2474 : }
2475 :
2476 :
2477 : bool
2478 23075 : NBEdge::hasLaneSpecificSpeed() const {
2479 59532 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2480 36472 : if (i->speed != getSpeed()) {
2481 : return true;
2482 : }
2483 : }
2484 : return false;
2485 : }
2486 :
2487 : bool
2488 3 : NBEdge::hasLaneSpecificFriction() const {
2489 10 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2490 7 : if (i->friction != myLanes.begin()->friction) {
2491 : return true;
2492 : }
2493 : }
2494 : return false;
2495 : }
2496 :
2497 : bool
2498 78092 : NBEdge::hasLaneSpecificWidth() const {
2499 169024 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2500 96848 : if (i->width != myLanes.begin()->width) {
2501 : return true;
2502 : }
2503 : }
2504 : return false;
2505 : }
2506 :
2507 :
2508 : bool
2509 5890 : NBEdge::hasLaneSpecificType() const {
2510 12488 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2511 6600 : if (i->type != myLanes.begin()->type) {
2512 : return true;
2513 : }
2514 : }
2515 : return false;
2516 : }
2517 :
2518 :
2519 : bool
2520 70614 : NBEdge::hasLaneSpecificEndOffset() const {
2521 158475 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2522 87878 : if (i->endOffset != myLanes.begin()->endOffset) {
2523 : return true;
2524 : }
2525 : }
2526 : return false;
2527 : }
2528 :
2529 :
2530 : bool
2531 78770 : NBEdge::hasLaneSpecificStopOffsets() const {
2532 178635 : for (const auto& lane : myLanes) {
2533 99870 : if (lane.laneStopOffset.isDefined()) {
2534 5 : if (myEdgeStopOffset.isDefined() || (myEdgeStopOffset != lane.laneStopOffset)) {
2535 : return true;
2536 : }
2537 : }
2538 : }
2539 : return false;
2540 : }
2541 :
2542 :
2543 : bool
2544 5882 : NBEdge::hasAccelLane() const {
2545 12457 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2546 6576 : if (i->accelRamp) {
2547 : return true;
2548 : }
2549 : }
2550 : return false;
2551 : }
2552 :
2553 :
2554 : bool
2555 5881 : NBEdge::hasCustomLaneShape() const {
2556 12446 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2557 6570 : if (i->customShape.size() > 0) {
2558 : return true;
2559 : }
2560 : }
2561 : return false;
2562 : }
2563 :
2564 :
2565 : bool
2566 5876 : NBEdge::hasLaneParams() const {
2567 9053 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2568 6563 : if (i->getParametersMap().size() > 0) {
2569 : return true;
2570 : }
2571 : }
2572 : return false;
2573 : }
2574 :
2575 : bool
2576 2490 : NBEdge::prohibitsChanging() const {
2577 5649 : for (const Lane& lane : myLanes) {
2578 3166 : if (lane.changeLeft != SVCAll || lane.changeRight != SVCAll) {
2579 : return true;
2580 : }
2581 : }
2582 : return false;
2583 : }
2584 :
2585 : bool
2586 8180 : NBEdge::needsLaneSpecificOutput() const {
2587 8180 : return (hasLaneSpecificPermissions()
2588 5897 : || hasLaneSpecificSpeed()
2589 5894 : || hasLaneSpecificWidth()
2590 5890 : || hasLaneSpecificType()
2591 5888 : || hasLaneSpecificEndOffset()
2592 5882 : || hasLaneSpecificStopOffsets()
2593 5882 : || hasAccelLane()
2594 5881 : || hasCustomLaneShape()
2595 5876 : || hasLaneParams()
2596 2490 : || prohibitsChanging()
2597 10663 : || (!myLanes.empty() && myLanes.back().oppositeID != ""));
2598 : }
2599 :
2600 :
2601 :
2602 : bool
2603 128086 : NBEdge::computeEdge2Edges(bool noLeftMovers) {
2604 : #ifdef DEBUG_CONNECTION_GUESSING
2605 : if (DEBUGCOND) {
2606 : std::cout << "computeEdge2Edges edge=" << getID() << " step=" << (int)myStep << " noLeftMovers=" << noLeftMovers << "\n";
2607 : for (Connection& c : myConnections) {
2608 : std::cout << " conn " << c.getDescription(this) << "\n";
2609 : }
2610 : for (Connection& c : myConnectionsToDelete) {
2611 : std::cout << " connToDelete " << c.getDescription(this) << "\n";
2612 : }
2613 : }
2614 : #endif
2615 : // return if this relationship has been build in previous steps or
2616 : // during the import
2617 128086 : if (myStep >= EdgeBuildingStep::EDGE2EDGES) {
2618 : return true;
2619 : }
2620 67379 : const bool fromRail = isRailway(getPermissions());
2621 233523 : for (NBEdge* out : myTo->getOutgoingEdges()) {
2622 166144 : if (noLeftMovers && myTo->isLeftMover(this, out)) {
2623 1140 : continue;
2624 : }
2625 : // avoid sharp railway turns
2626 165004 : if (fromRail && isRailway(out->getPermissions())) {
2627 9547 : const double angle = fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), out->getAngleAtNode(myTo)));
2628 9547 : if (angle > 150) {
2629 3770 : continue;
2630 5777 : } else if (angle > 90) {
2631 : // possibly the junction is large enough to achieve a plausible radius:
2632 129 : const PositionVector& fromShape = myLanes.front().shape;
2633 129 : const PositionVector& toShape = out->getLanes().front().shape;
2634 129 : PositionVector shape = myTo->computeSmoothShape(fromShape, toShape, 5, getTurnDestination() == out, 5, 5);
2635 129 : const double radius = shape.length2D() / DEG2RAD(angle);
2636 129 : const double minRadius = (getPermissions() & SVC_TRAM) != 0 ? 20 : 80;
2637 : //std::cout << getID() << " to=" << out->getID() << " radius=" << radius << " minRadius=" << minRadius << "\n";
2638 129 : if (radius < minRadius) {
2639 : continue;
2640 : }
2641 129 : }
2642 : }
2643 161106 : if (out == myTurnDestination) {
2644 : // will be added by appendTurnaround
2645 42920 : continue;
2646 : }
2647 118186 : if ((getPermissions() & out->getPermissions() & ~SVC_PEDESTRIAN) == 0) {
2648 : // no common permissions
2649 29102 : continue;
2650 : }
2651 178168 : myConnections.push_back(Connection(-1, out, -1));
2652 : }
2653 67379 : myStep = EdgeBuildingStep::EDGE2EDGES;
2654 67379 : return true;
2655 : }
2656 :
2657 :
2658 : bool
2659 128086 : NBEdge::computeLanes2Edges() {
2660 : #ifdef DEBUG_CONNECTION_GUESSING
2661 : if (DEBUGCOND) {
2662 : std::cout << "computeLanes2Edges edge=" << getID() << " step=" << (int)myStep << "\n";
2663 : for (Connection& c : myConnections) {
2664 : std::cout << " conn " << c.getDescription(this) << "\n";
2665 : }
2666 : for (Connection& c : myConnectionsToDelete) {
2667 : std::cout << " connToDelete " << c.getDescription(this) << "\n";
2668 : }
2669 : }
2670 : #endif
2671 : // return if this relationship has been build in previous steps or
2672 : // during the import
2673 128086 : if (myStep >= EdgeBuildingStep::LANES2EDGES) {
2674 60241 : if (myStep == EdgeBuildingStep::LANES2LANES_USER && myConnections.size() > 1) {
2675 130119 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
2676 95539 : if ((*i).toEdge == nullptr) {
2677 3 : WRITE_WARNINGF("Inconsistent connection definitions at edge '%'.", getID());
2678 1 : i = myConnections.erase(i);
2679 : } else {
2680 : i++;
2681 : }
2682 : }
2683 : }
2684 60241 : return true;
2685 : }
2686 : assert(myStep == EdgeBuildingStep::EDGE2EDGES);
2687 : // get list of possible outgoing edges sorted by direction clockwise
2688 : // the edge in the backward direction (turnaround) is not in the list
2689 67845 : const EdgeVector* edges = getConnectedSorted();
2690 67845 : if (myConnections.size() != 0 && edges->size() == 0) {
2691 : // dead end per definition!?
2692 : myConnections.clear();
2693 : } else {
2694 : // divide the lanes on reachable edges
2695 67823 : divideOnEdges(edges);
2696 : }
2697 67845 : delete edges;
2698 67845 : myStep = EdgeBuildingStep::LANES2EDGES;
2699 67845 : return true;
2700 : }
2701 :
2702 :
2703 : std::vector<LinkDirection>
2704 394 : NBEdge::decodeTurnSigns(int turnSigns, int shift) {
2705 : std::vector<LinkDirection> result;
2706 3546 : for (int i = 0; i < 8; i++) {
2707 : // see LinkDirection in SUMOXMLDefinitions.h
2708 3152 : if ((turnSigns & (1 << (i + shift))) != 0) {
2709 323 : result.push_back((LinkDirection)(1 << i));
2710 : }
2711 : }
2712 394 : return result;
2713 0 : }
2714 :
2715 : void
2716 276 : NBEdge::updateTurnPermissions(SVCPermissions& perm, LinkDirection dir, SVCPermissions spec, std::vector<LinkDirection> dirs) {
2717 276 : if (dirs.size() > 0) {
2718 77 : if (std::find(dirs.begin(), dirs.end(), dir) == dirs.end()) {
2719 2 : perm &= ~spec;
2720 : } else {
2721 75 : perm |= spec;
2722 : }
2723 : }
2724 276 : }
2725 :
2726 : bool
2727 34 : NBEdge::applyTurnSigns() {
2728 : #ifdef DEBUG_TURNSIGNS
2729 : std::cout << "applyTurnSigns edge=" << getID() << "\n";
2730 : #endif
2731 : // build a map of target edges and lanes
2732 : std::vector<const NBEdge*> targets;
2733 : std::map<const NBEdge*, std::vector<int> > toLaneMap;
2734 144 : for (const Connection& c : myConnections) {
2735 110 : if (myLanes[c.fromLane].turnSigns != 0) {
2736 110 : if (std::find(targets.begin(), targets.end(), c.toEdge) == targets.end()) {
2737 90 : targets.push_back(c.toEdge);
2738 : }
2739 110 : toLaneMap[c.toEdge].push_back(c.toLane);
2740 : }
2741 : }
2742 : // might be unsorted due to bike lane connections
2743 124 : for (auto& item : toLaneMap) {
2744 90 : std::sort(item.second.begin(), item.second.end());
2745 : }
2746 :
2747 : // check number of distinct signed directions and count the number of signs for each direction
2748 : std::map<LinkDirection, int> signCons;
2749 : int allDirs = 0;
2750 114 : for (const Lane& lane : myLanes) {
2751 80 : allDirs |= lane.turnSigns;
2752 179 : for (LinkDirection dir : decodeTurnSigns(lane.turnSigns)) {
2753 99 : signCons[dir]++;
2754 80 : }
2755 : }
2756 34 : allDirs |= allDirs >> TURN_SIGN_SHIFT_BUS;
2757 34 : allDirs |= allDirs >> TURN_SIGN_SHIFT_TAXI;
2758 34 : allDirs |= allDirs >> TURN_SIGN_SHIFT_BICYCLE;
2759 :
2760 34 : if ((allDirs & (int)LinkDirection::NODIR) != 0) {
2761 3 : targets.push_back(nullptr); // dead end
2762 : }
2763 :
2764 : SVCPermissions defaultPermissions = SVC_PASSENGER | SVC_DELIVERY;
2765 : // build a mapping from sign directions to targets
2766 34 : std::vector<LinkDirection> signedDirs = decodeTurnSigns(allDirs);
2767 : std::map<LinkDirection, const NBEdge*> dirMap;
2768 : #ifdef DEBUG_TURNSIGNS
2769 : std::cout << " numDirs=" << signedDirs.size() << " numTargets=" << targets.size() << "\n";
2770 : #endif
2771 34 : if (signedDirs.size() > targets.size()) {
2772 18 : WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions but only % targets"), getID(), signedDirs.size(), targets.size());
2773 6 : return false;
2774 28 : } else if (signedDirs.size() < targets.size()) {
2775 : // we need to drop some targets (i.e. turn-around)
2776 : // use sumo-directions as a guide
2777 : std::vector<LinkDirection> sumoDirs;
2778 82 : for (const NBEdge* to : targets) {
2779 65 : sumoDirs.push_back(myTo->getDirection(this, to));
2780 : }
2781 : // remove targets to the left
2782 : bool checkMore = true;
2783 37 : while (signedDirs.size() < targets.size() && checkMore) {
2784 : checkMore = false;
2785 : //std::cout << getID() << " sumoDirs=" << joinToString(sumoDirs, ",") << " signedDirs=" << joinToString(signedDirs, ",") << "\n";
2786 20 : if (sumoDirs.back() != signedDirs.back()) {
2787 : targets.pop_back();
2788 : sumoDirs.pop_back();
2789 : checkMore = true;
2790 : }
2791 : }
2792 : // remove targets to the right
2793 : checkMore = true;
2794 20 : while (signedDirs.size() < targets.size() && checkMore) {
2795 : checkMore = false;
2796 3 : if (sumoDirs.front() != signedDirs.front()) {
2797 : targets.erase(targets.begin());
2798 : sumoDirs.erase(sumoDirs.begin());
2799 : checkMore = true;
2800 : }
2801 : }
2802 : // remove targets by permissions
2803 : int i = 0;
2804 25 : while (signedDirs.size() < targets.size() && i < (int)targets.size()) {
2805 8 : if (targets[i] != nullptr && (targets[i]->getPermissions() & defaultPermissions) == 0) {
2806 : targets.erase(targets.begin() + i);
2807 : sumoDirs.erase(sumoDirs.begin() + i);
2808 : } else {
2809 8 : i++;
2810 : }
2811 : }
2812 17 : if (signedDirs.size() != targets.size()) {
2813 9 : WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions and % targets (after target pruning)"), getID(), signedDirs.size(), targets.size());
2814 : return false;
2815 : }
2816 17 : }
2817 : // directions and connections are both sorted from right to left
2818 87 : for (int i = 0; i < (int)signedDirs.size(); i++) {
2819 62 : dirMap[signedDirs[i]] = targets[i];
2820 : }
2821 : // check whether we have enough target lanes for a each signed direction
2822 83 : for (auto item : signCons) {
2823 60 : const LinkDirection dir = item.first;
2824 60 : if (dir == LinkDirection::NODIR) {
2825 2 : continue;
2826 : }
2827 58 : const NBEdge* to = dirMap[dir];
2828 58 : int candidates = to->getNumLanesThatAllow(defaultPermissions, false);
2829 58 : if (candidates == 0) {
2830 4 : WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because the target edge '%' has no suitable lanes"), getID(), to->getID());
2831 2 : return false;
2832 : }
2833 57 : std::vector<int>& knownTargets = toLaneMap[to];
2834 57 : if ((int)knownTargets.size() < item.second) {
2835 4 : if (candidates < item.second) {
2836 4 : WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed connections with directions '%' but target edge '%' has only % suitable lanes"),
2837 : getID(), item.second, toString(dir), to->getID(), candidates);
2838 1 : return false;
2839 : }
2840 : int i;
2841 : int iInc;
2842 : int iEnd;
2843 3 : if (dir > LinkDirection::STRAIGHT) {
2844 : // set more targets on the left
2845 1 : i = to->getNumLanes() - 1;
2846 : iInc = -1;
2847 : iEnd = -1;
2848 : } else {
2849 : // set more targets on the right
2850 2 : i = 0;
2851 : iInc = 1;
2852 2 : iEnd = to->getNumLanes();
2853 : }
2854 8 : while ((int)knownTargets.size() < item.second && i != iEnd) {
2855 5 : if ((to->getPermissions(i) & defaultPermissions) != 0) {
2856 5 : if (std::find(knownTargets.begin(), knownTargets.end(), i) == knownTargets.end()) {
2857 3 : knownTargets.push_back(i);
2858 : }
2859 : }
2860 5 : i += iInc;
2861 : }
2862 3 : if ((int)knownTargets.size() != item.second) {
2863 0 : WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because not enough target lanes could be determined for direction '%'"), getID(), toString(dir));
2864 0 : return false;
2865 : }
2866 3 : std::sort(knownTargets.begin(), knownTargets.end());
2867 : }
2868 : }
2869 : std::map<const NBEdge*, int> toLaneIndex;
2870 79 : for (int i = 0; i < getNumLanes(); i++) {
2871 56 : const int turnSigns = myLanes[i].turnSigns;
2872 : // no turnSigns are given for bicycle lanes and sidewalks
2873 56 : if (turnSigns != 0) {
2874 : // clear existing connections
2875 253 : for (auto it = myConnections.begin(); it != myConnections.end();) {
2876 197 : if (it->fromLane == i) {
2877 80 : it = myConnections.erase(it);
2878 : } else {
2879 : it++;
2880 : }
2881 : }
2882 : // add new connections
2883 56 : int allSigns = (turnSigns
2884 56 : | turnSigns >> TURN_SIGN_SHIFT_BUS
2885 56 : | turnSigns >> TURN_SIGN_SHIFT_TAXI
2886 56 : | turnSigns >> TURN_SIGN_SHIFT_BICYCLE);
2887 56 : std::vector<LinkDirection> all = decodeTurnSigns(turnSigns);
2888 56 : std::vector<LinkDirection> bus = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BUS);
2889 56 : std::vector<LinkDirection> taxi = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_TAXI);
2890 56 : std::vector<LinkDirection> bike = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BICYCLE);
2891 : //std::cout << " allSigns=" << allSigns << " turnSigns=" << turnSigns << " bus=" << bus.size() << "\n";
2892 56 : SVCPermissions fromP = getPermissions(i);
2893 56 : if ((fromP & SVC_PASSENGER) != 0) {
2894 : // if the source permits passenger traffic, the target should too
2895 : fromP = SVC_PASSENGER;
2896 : }
2897 125 : for (LinkDirection dir : decodeTurnSigns(allSigns)) {
2898 69 : SVCPermissions perm = 0;
2899 69 : updateTurnPermissions(perm, dir, SVCAll, all);
2900 69 : updateTurnPermissions(perm, dir, SVC_BUS, bus);
2901 69 : updateTurnPermissions(perm, dir, SVC_TAXI, taxi);
2902 69 : updateTurnPermissions(perm, dir, SVC_BICYCLE, bike);
2903 69 : if (perm == SVCAll) {
2904 67 : perm = SVC_UNSPECIFIED;
2905 : }
2906 : //std::cout << " lane=" << i << " dir=" << toString(dir) << " perm=" << getVehicleClassNames(perm) << "\n";
2907 69 : NBEdge* to = const_cast<NBEdge*>(dirMap[dir]);
2908 69 : if (to != nullptr) {
2909 : if (toLaneIndex.count(to) == 0) {
2910 : // initialize to rightmost feasible lane
2911 55 : int toLane = toLaneMap[to][0];
2912 57 : while ((to->getPermissions(toLane) & fromP) == 0 && (toLane + 1 < to->getNumLanes())) {
2913 : toLane++;
2914 : /*
2915 : if (toLane == to->getNumLanes()) {
2916 : SOFT_ASSERT(false);
2917 : #ifdef DEBUG_TURNSIGNS
2918 : std::cout << " could not find passenger lane for target=" << to->getID() << "\n";
2919 : #endif
2920 : return false;
2921 : }
2922 : */
2923 : }
2924 : #ifdef DEBUG_TURNSIGNS
2925 : std::cout << " target=" << to->getID() << " initial toLane=" << toLane << "\n";
2926 : #endif
2927 55 : toLaneIndex[to] = toLane;
2928 : }
2929 : #ifdef DEBUG_TURNSIGNS
2930 : //std::cout << " set fromLane=" << i << " to=" << to->getID() << " toLane=" << toLaneIndex[to] << "\n";
2931 : #endif
2932 67 : setConnection(i, to, toLaneIndex[to], Lane2LaneInfoType::VALIDATED, true,
2933 : false, KEEPCLEAR_UNSPECIFIED, UNSPECIFIED_CONTPOS,
2934 : UNSPECIFIED_VISIBILITY_DISTANCE, UNSPECIFIED_SPEED, UNSPECIFIED_FRICTION,
2935 : myDefaultConnectionLength, PositionVector::EMPTY,
2936 : UNSPECIFIED_CONNECTION_UNCONTROLLED,
2937 : perm);
2938 67 : if (toLaneIndex[to] < to->getNumLanes() - 1
2939 67 : && (to->getPermissions(toLaneIndex[to] + 1) & fromP) != 0) {
2940 13 : toLaneIndex[to]++;
2941 54 : } else if (toLaneIndex[to] < to->getNumLanes() - 2
2942 54 : && (to->getPermissions(toLaneIndex[to] + 2) & fromP) != 0) {
2943 : // skip forbidden lane
2944 0 : toLaneIndex[to] += 2;
2945 : }
2946 : }
2947 56 : }
2948 56 : }
2949 : }
2950 23 : sortOutgoingConnectionsByAngle();
2951 23 : sortOutgoingConnectionsByIndex();
2952 : return true;
2953 68 : }
2954 :
2955 :
2956 : bool
2957 128086 : NBEdge::recheckLanes() {
2958 : #ifdef DEBUG_CONNECTION_GUESSING
2959 : if (DEBUGCOND) {
2960 : std::cout << "recheckLanes (initial) edge=" << getID() << "\n";
2961 : for (Connection& c : myConnections) {
2962 : std::cout << " conn " << c.getDescription(this) << "\n";
2963 : }
2964 : for (Connection& c : myConnectionsToDelete) {
2965 : std::cout << " connToDelete " << c.getDescription(this) << "\n";
2966 : }
2967 : }
2968 : #endif
2969 : // check delayed removals
2970 134794 : for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
2971 6708 : removeFromConnections(it->toEdge, it->fromLane, it->toLane, false, false, true);
2972 : }
2973 128086 : std::vector<int> connNumbersPerLane(myLanes.size(), 0);
2974 384056 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
2975 255970 : if ((*i).toEdge == nullptr || (*i).fromLane < 0 || (*i).toLane < 0) {
2976 105 : i = myConnections.erase(i);
2977 : } else {
2978 : if ((*i).fromLane >= 0) {
2979 255865 : ++connNumbersPerLane[(*i).fromLane];
2980 : }
2981 : ++i;
2982 : }
2983 : }
2984 128086 : if (myStep != EdgeBuildingStep::LANES2LANES_DONE && myStep != EdgeBuildingStep::LANES2LANES_USER) {
2985 : #ifdef DEBUG_TURNSIGNS
2986 : if (myLanes.back().turnSigns != 0) {
2987 : std::cout << getID() << " hasTurnSigns\n";
2988 : if (myTurnSignTarget != myTo->getID()) {
2989 : std::cout << " tst=" << myTurnSignTarget << " to=" << myTo->getID() << "\n";
2990 : }
2991 : }
2992 : #endif
2993 57901 : if (myLanes.back().turnSigns == 0 || myTurnSignTarget != myTo->getID() || !applyTurnSigns()) {
2994 : // check #1:
2995 : // If there is a lane with no connections and any neighbour lane has
2996 : // more than one connections, try to move one of them.
2997 : // This check is only done for edges which connections were assigned
2998 : // using the standard algorithm.
2999 133094 : for (int i = 0; i < (int)myLanes.size(); i++) {
3000 75216 : if (connNumbersPerLane[i] == 0 && !isForbidden(getPermissions(i) & ~SVC_PEDESTRIAN)) {
3001 : // dead-end lane found
3002 : bool hasDeadEnd = true;
3003 : // find lane with two connections or more to the right of the current lane
3004 3941 : for (int i2 = i - 1; hasDeadEnd && i2 >= 0; i2--) {
3005 2397 : if (getPermissions(i) != getPermissions(i2)) {
3006 : break;
3007 : }
3008 1612 : if (connNumbersPerLane[i2] > 1) {
3009 356 : connNumbersPerLane[i2]--;
3010 848 : for (int i3 = i2; i3 != i; i3++) {
3011 492 : moveConnectionToLeft(i3);
3012 492 : sortOutgoingConnectionsByAngle();
3013 492 : sortOutgoingConnectionsByIndex();
3014 : }
3015 : hasDeadEnd = false;
3016 : }
3017 : }
3018 2329 : if (hasDeadEnd) {
3019 : // find lane with two connections or more to the left of the current lane
3020 3062 : for (int i2 = i + 1; hasDeadEnd && i2 < getNumLanes(); i2++) {
3021 1184 : if (getPermissions(i) != getPermissions(i2)) {
3022 : break;
3023 : }
3024 1089 : if (connNumbersPerLane[i2] > 1) {
3025 75 : connNumbersPerLane[i2]--;
3026 176 : for (int i3 = i2; i3 != i; i3--) {
3027 101 : moveConnectionToRight(i3);
3028 101 : sortOutgoingConnectionsByAngle();
3029 101 : sortOutgoingConnectionsByIndex();
3030 : }
3031 : hasDeadEnd = false;
3032 : }
3033 : }
3034 : }
3035 1973 : if (hasDeadEnd && myTo->getOutgoingEdges().size() > 1) {
3036 : int passengerLanes = 0;
3037 : int passengerTargetLanes = 0;
3038 906 : for (const Lane& lane : myLanes) {
3039 695 : if ((lane.permissions & SVC_PASSENGER) != 0) {
3040 569 : passengerLanes++;
3041 : }
3042 : }
3043 787 : for (const NBEdge* out : myTo->getOutgoingEdges()) {
3044 576 : if (!isTurningDirectionAt(out)) {
3045 1028 : for (const Lane& lane : out->getLanes()) {
3046 636 : if ((lane.permissions & SVC_PASSENGER) != 0) {
3047 515 : passengerTargetLanes++;
3048 : }
3049 : }
3050 : }
3051 : }
3052 211 : if (passengerLanes > 0 && passengerLanes <= passengerTargetLanes) {
3053 : // no need for dead-ends
3054 39 : if (i > 0) {
3055 : // check if a connection to the right has a usable target to the left of its target
3056 28 : std::vector<Connection> rightCons = getConnectionsFromLane(i - 1);
3057 28 : if (rightCons.size() > 0) {
3058 : const Connection& rc = rightCons.back();
3059 28 : NBEdge* to = rc.toEdge;
3060 28 : int toLane = rc.toLane + 1;
3061 : if (toLane < to->getNumLanes()
3062 0 : && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
3063 28 : && !hasConnectionTo(to, toLane)) {
3064 : #ifdef DEBUG_CONNECTION_CHECKING
3065 : std::cout << " recheck1 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
3066 : #endif
3067 0 : setConnection(i, to, toLane, Lane2LaneInfoType::COMPUTED);
3068 : hasDeadEnd = false;
3069 0 : sortOutgoingConnectionsByAngle();
3070 0 : sortOutgoingConnectionsByIndex();
3071 : }
3072 : if (hasDeadEnd) {
3073 : // check if a connection to the right has a usable target to the right of its target
3074 28 : toLane = rc.toLane - 1;
3075 : if (toLane >= 0
3076 7 : && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(rc.toLane)) != 0
3077 7 : && (getPermissions(rc.fromLane) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
3078 34 : && !hasConnectionTo(to, toLane)) {
3079 : // shift the right lane connection target right and connect the dead lane to the old target
3080 1 : getConnectionRef(rc.fromLane, to, rc.toLane).toLane = toLane;
3081 : #ifdef DEBUG_CONNECTION_CHECKING
3082 : std::cout << " recheck2 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << (toLane + 1) << "\n";
3083 : #endif
3084 1 : setConnection(i, to, toLane + 1, Lane2LaneInfoType::COMPUTED);
3085 : hasDeadEnd = false;
3086 1 : sortOutgoingConnectionsByAngle();
3087 1 : sortOutgoingConnectionsByIndex();
3088 : }
3089 : }
3090 : }
3091 28 : }
3092 39 : if (hasDeadEnd && i < getNumLanes() - 1) {
3093 : // check if a connection to the left has a usable target to the right of its target
3094 30 : std::vector<Connection> leftCons = getConnectionsFromLane(i + 1);
3095 30 : if (leftCons.size() > 0) {
3096 30 : NBEdge* to = leftCons.front().toEdge;
3097 30 : int toLane = leftCons.front().toLane - 1;
3098 : if (toLane >= 0
3099 4 : && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
3100 34 : && !hasConnectionTo(to, toLane)) {
3101 : #ifdef DEBUG_CONNECTION_CHECKING
3102 : std::cout << " recheck3 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
3103 : #endif
3104 4 : setConnection(i, to, toLane, Lane2LaneInfoType::COMPUTED);
3105 : hasDeadEnd = false;
3106 4 : sortOutgoingConnectionsByAngle();
3107 4 : sortOutgoingConnectionsByIndex();
3108 : }
3109 : }
3110 30 : }
3111 : #ifdef ADDITIONAL_WARNINGS
3112 : if (hasDeadEnd) {
3113 : WRITE_WARNING("Found dead-end lane " + getLaneID(i));
3114 : }
3115 : #endif
3116 : }
3117 : }
3118 : }
3119 : }
3120 57878 : removeInvalidConnections();
3121 : }
3122 : }
3123 : // check involuntary dead end at "real" junctions
3124 128086 : if (getPermissions() != SVC_PEDESTRIAN) {
3125 111799 : if (myConnections.empty() && myTo->getOutgoingEdges().size() > 1 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
3126 336 : WRITE_WARNINGF(TL("Edge '%' is not connected to outgoing edges at junction '%'."), getID(), myTo->getID());
3127 : }
3128 111799 : const EdgeVector& incoming = myFrom->getIncomingEdges();
3129 111799 : if (incoming.size() > 1) {
3130 188039 : for (int i = 0; i < (int)myLanes.size(); i++) {
3131 105309 : if (getPermissions(i) != 0 && getPermissions(i) != SVC_PEDESTRIAN) {
3132 : bool connected = false;
3133 133781 : for (std::vector<NBEdge*>::const_iterator in = incoming.begin(); in != incoming.end(); ++in) {
3134 133603 : if ((*in)->hasConnectionTo(this, i)) {
3135 : connected = true;
3136 : break;
3137 : }
3138 : }
3139 99262 : if (!connected) {
3140 534 : WRITE_WARNINGF(TL("Lane '%' is not connected from any incoming edge at junction '%'."), getLaneID(i), myFrom->getID());
3141 : }
3142 : }
3143 : }
3144 : }
3145 : }
3146 : // avoid deadend due to change prohibitions
3147 128086 : if (getNumLanes() > 1 && myConnections.size() > 0) {
3148 82821 : for (int i = 0; i < (int)myLanes.size(); i++) {
3149 58433 : Lane& lane = myLanes[i];
3150 50471 : if ((connNumbersPerLane[i] == 0 || ((lane.accelRamp || (i > 0 && myLanes[i - 1].accelRamp && connNumbersPerLane[i - 1] > 0))
3151 53 : && getSuccessors(SVC_PASSENGER).size() > 1))
3152 58448 : && getPermissions(i) != SVC_PEDESTRIAN && !isForbidden(getPermissions(i))) {
3153 2442 : const bool forbiddenLeft = lane.changeLeft != SVCAll && lane.changeLeft != SVC_IGNORING && lane.changeLeft != SVC_UNSPECIFIED;
3154 2442 : const bool forbiddenRight = lane.changeRight != SVCAll && lane.changeRight != SVC_IGNORING && lane.changeRight != SVC_UNSPECIFIED;
3155 2442 : if (forbiddenLeft && (i == 0 || forbiddenRight)) {
3156 2 : lane.changeLeft = SVC_UNSPECIFIED;
3157 4 : WRITE_WARNINGF(TL("Ignoring changeLeft prohibition for '%' to avoid dead-end"), getLaneID(i));
3158 2440 : } else if (forbiddenRight && (i == getNumLanes() - 1 || (i > 0 && myLanes[i - 1].accelRamp))) {
3159 1 : lane.changeRight = SVC_UNSPECIFIED;
3160 2 : WRITE_WARNINGF(TL("Ignoring changeRight prohibition for '%' to avoid dead-end"), getLaneID(i));
3161 : }
3162 : }
3163 : }
3164 : }
3165 : #ifdef ADDITIONAL_WARNINGS
3166 : // check for connections with bad access permissions
3167 : for (const Connection& c : myConnections) {
3168 : SVCPermissions fromP = getPermissions(c.fromLane);
3169 : SVCPermissions toP = c.toEdge->getPermissions(c.toLane);
3170 : if ((fromP & SVC_PASSENGER) != 0
3171 : && toP == SVC_BICYCLE) {
3172 : bool hasAlternative = false;
3173 : for (const Connection& c2 : myConnections) {
3174 : if (c.fromLane == c2.fromLane && c.toEdge == c2.toEdge
3175 : && (c.toEdge->getPermissions(c2.toLane) & SVC_PASSENGER) != 0) {
3176 : hasAlternative = true;
3177 : }
3178 : }
3179 : if (!hasAlternative) {
3180 : WRITE_WARNING("Road lane ends on bikeLane for connection " + c.getDescription(this));
3181 : }
3182 : }
3183 : }
3184 :
3185 : #endif
3186 : #ifdef DEBUG_CONNECTION_GUESSING
3187 : if (DEBUGCOND) {
3188 : std::cout << "recheckLanes (final) edge=" << getID() << "\n";
3189 : for (Connection& c : myConnections) {
3190 : std::cout << " conn " << c.getDescription(this) << "\n";
3191 : }
3192 : }
3193 : #endif
3194 128086 : if (myStep != EdgeBuildingStep::LANES2LANES_USER) {
3195 71203 : myStep = EdgeBuildingStep::LANES2LANES_DONE;
3196 : }
3197 128086 : return true;
3198 128086 : }
3199 :
3200 :
3201 128133 : void NBEdge::recheckOpposite(const NBEdgeCont& ec, bool fixOppositeLengths) {
3202 128133 : if (getNumLanes() == 0) {
3203 0 : return;
3204 : }
3205 128133 : const int leftmostLane = getNumLanes() - 1;
3206 : // check oppositeID stored in other lanes
3207 165602 : for (int i = 0; i < leftmostLane; i++) {
3208 37469 : const std::string& oppositeID = getLanes()[i].oppositeID;
3209 74938 : NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
3210 37469 : if (oppositeID != "" && oppositeID != "-") {
3211 6 : if (getLanes().back().oppositeID == "" && oppEdge != nullptr) {
3212 1 : getLaneStruct(leftmostLane).oppositeID = oppositeID;
3213 3 : WRITE_WARNINGF(TL("Moving opposite lane '%' from invalid lane '%' to lane index %."), oppositeID, getLaneID(i), leftmostLane);
3214 : } else {
3215 15 : WRITE_WARNINGF(TL("Removing opposite lane '%' for invalid lane '%'."), oppositeID, getLaneID(i));
3216 : }
3217 6 : getLaneStruct(i).oppositeID = "";
3218 : }
3219 : }
3220 128133 : const std::string& oppositeID = getLanes().back().oppositeID;
3221 128133 : if (oppositeID != "" && oppositeID != "-") {
3222 112 : NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
3223 112 : if (oppEdge == nullptr) {
3224 3 : WRITE_WARNINGF(TL("Removing unknown opposite lane '%' for edge '%'."), oppositeID, getID());
3225 1 : getLaneStruct(leftmostLane).oppositeID = "";
3226 : } else {
3227 111 : if (oppEdge->getFromNode() != getToNode() || oppEdge->getToNode() != getFromNode()) {
3228 3 : WRITE_WARNINGF(TL("Opposite lane '%' does not reverse-connect the same nodes as edge '%'!"), oppositeID, getID());
3229 1 : getLaneStruct(getNumLanes() - 1).oppositeID = "";
3230 : } else {
3231 220 : if (oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
3232 1 : const std::string oppEdgeLeftmost = oppEdge->getLaneID(oppEdge->getNumLanes() - 1);
3233 4 : WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppositeID, getID(), oppEdgeLeftmost);
3234 1 : getLaneStruct(leftmostLane).oppositeID = oppEdgeLeftmost;
3235 : }
3236 110 : NBEdge::Lane& oppLane = oppEdge->getLaneStruct(oppEdge->getNumLanes() - 1);
3237 110 : const std::string leftmostID = getLaneID(leftmostLane);
3238 110 : if (oppLane.oppositeID == "") {
3239 15 : WRITE_WARNINGF(TL("Adapting missing opposite lane '%' for edge '%'."), leftmostID, oppEdge->getID());
3240 : oppLane.oppositeID = leftmostID;
3241 105 : } else if (oppLane.oppositeID != leftmostID && oppLane.oppositeID != "-") {
3242 1 : const std::string oppOpp = oppLane.oppositeID.substr(0, oppLane.oppositeID.rfind("_"));
3243 1 : NBEdge* oppOppEdge = ec.retrieve(oppOpp);
3244 1 : if (oppOppEdge == nullptr) {
3245 0 : WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppLane.oppositeID, oppEdge->getID(), leftmostID);
3246 : oppLane.oppositeID = leftmostID;
3247 : } else {
3248 1 : if (oppEdge->getFromNode() != oppOppEdge->getToNode() || oppEdge->getToNode() != oppOppEdge->getFromNode()) {
3249 0 : WRITE_ERRORF(TL("Opposite edge '%' does not reverse-connect the same nodes as edge '%'!"), oppEdge->getID(), oppOppEdge->getID());
3250 : } else {
3251 5 : WRITE_WARNINGF(TL("Adapting inconsistent opposite lanes for edges '%', '%' and '%'."), getID(), oppEdge->getID(), oppOpp);
3252 : }
3253 : oppLane.oppositeID = leftmostID;
3254 1 : NBEdge::Lane& oppOppLane = oppOppEdge->getLaneStruct(oppOppEdge->getNumLanes() - 1);
3255 1 : if (oppOppLane.oppositeID == oppEdge->getLaneID(oppEdge->getNumLanes() - 1)) {
3256 : oppOppLane.oppositeID = "";
3257 : }
3258 : }
3259 : }
3260 110 : if (fabs(oppEdge->getLoadedLength() - getLoadedLength()) > NUMERICAL_EPS) {
3261 3 : if (fixOppositeLengths) {
3262 1 : const double avgLength = 0.5 * (getFinalLength() + oppEdge->getFinalLength());
3263 4 : WRITE_WARNINGF(TL("Averaging edge lengths for lane '%' (length %) and edge '%' (length %)."),
3264 : oppositeID, oppEdge->getLoadedLength(), getID(), getLoadedLength());
3265 1 : setLoadedLength(avgLength);
3266 1 : oppEdge->setLoadedLength(avgLength);
3267 : } else {
3268 10 : WRITE_ERROR("Opposite lane '" + oppositeID + "' (length " + toString(oppEdge->getLoadedLength()) +
3269 : ") differs in length from edge '" + getID() + "' (length " +
3270 : toString(getLoadedLength()) + "). Set --opposites.guess.fix-lengths to fix this.");
3271 2 : getLaneStruct(getNumLanes() - 1).oppositeID = "";
3272 : }
3273 : }
3274 : }
3275 : }
3276 : }
3277 : // check for matching bidi lane shapes (at least for the simple case of 1-lane edges)
3278 128133 : const NBEdge* bidi = getBidiEdge();
3279 128133 : if (bidi != nullptr && getNumLanes() == 1 && bidi->getNumLanes() == 1 && getID() < bidi->getID()) {
3280 6980 : getLaneStruct(0).shape = bidi->getLaneStruct(0).shape.reverse();
3281 : }
3282 : // check for valid offset and speed
3283 135103 : const double startOffset = isBidiRail() ? getTurnDestination(true)->getEndOffset() : 0;
3284 : int i = 0;
3285 293735 : for (const NBEdge::Lane& l : getLanes()) {
3286 165602 : if (startOffset + l.endOffset > getLength()) {
3287 4 : WRITE_WARNINGF(TL("Invalid endOffset % at lane '%' with length % (startOffset %)."),
3288 : toString(l.endOffset), getLaneID(i), toString(l.shape.length()), toString(startOffset));
3289 165600 : } else if (l.speed < 0.) {
3290 0 : WRITE_WARNINGF(TL("Negative allowed speed (%) on lane '%', use --speed.minimum to prevent this."), toString(l.speed), getLaneID(i));
3291 165600 : } else if (l.speed == 0.) {
3292 0 : WRITE_WARNINGF(TL("Lane '%' has a maximum allowed speed of 0."), getLaneID(i));
3293 : }
3294 165602 : i++;
3295 : }
3296 : }
3297 :
3298 57878 : void NBEdge::removeInvalidConnections() {
3299 : // check restrictions
3300 190428 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
3301 : Connection& c = *i;
3302 132550 : const SVCPermissions common = getPermissions(c.fromLane) & c.toEdge->getPermissions(c.toLane);
3303 132550 : if (common == SVC_PEDESTRIAN || getPermissions(c.fromLane) == SVC_PEDESTRIAN) {
3304 : // these are computed in NBNode::buildWalkingAreas
3305 : #ifdef DEBUG_CONNECTION_CHECKING
3306 : std::cout << " remove pedCon " << c.getDescription(this) << "\n";
3307 : #endif
3308 475 : i = myConnections.erase(i);
3309 132075 : } else if (common == 0) {
3310 : // no common permissions.
3311 : // try to find a suitable target lane to the right
3312 83 : const int origToLane = c.toLane;
3313 83 : c.toLane = -1; // ignore this connection when calling hasConnectionTo
3314 : int toLane = origToLane;
3315 83 : while (toLane > 0
3316 45 : && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
3317 143 : && !hasConnectionTo(c.toEdge, toLane)
3318 : ) {
3319 30 : toLane--;
3320 : }
3321 83 : if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
3322 83 : && !hasConnectionTo(c.toEdge, toLane)) {
3323 4 : c.toLane = toLane;
3324 : ++i;
3325 : } else {
3326 : // try to find a suitable target lane to the left
3327 : toLane = origToLane;
3328 148 : while (toLane < (int)c.toEdge->getNumLanes() - 1
3329 95 : && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
3330 217 : && !hasConnectionTo(c.toEdge, toLane)
3331 : ) {
3332 69 : toLane++;
3333 : }
3334 79 : if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
3335 79 : && !hasConnectionTo(c.toEdge, toLane)) {
3336 0 : c.toLane = toLane;
3337 : ++i;
3338 : } else {
3339 : // no alternative target found
3340 : #ifdef DEBUG_CONNECTION_CHECKING
3341 : std::cout << " remove " << c.getDescription(this) << " with no alternative target\n";
3342 : #endif
3343 79 : i = myConnections.erase(i);
3344 : }
3345 : }
3346 140372 : } else if (isRailway(getPermissions(c.fromLane)) && isRailway(c.toEdge->getPermissions(c.toLane))
3347 140290 : && isTurningDirectionAt(c.toEdge)) {
3348 : // do not allow sharp rail turns
3349 : #ifdef DEBUG_CONNECTION_CHECKING
3350 : std::cout << " remove " << c.getDescription(this) << " (rail turnaround)\n";
3351 : #endif
3352 2731 : i = myConnections.erase(i);
3353 : } else {
3354 : ++i;
3355 : }
3356 : }
3357 57878 : }
3358 :
3359 : void
3360 67823 : NBEdge::divideOnEdges(const EdgeVector* outgoing) {
3361 67823 : if (outgoing->size() == 0) {
3362 : // we have to do this, because the turnaround may have been added before
3363 : myConnections.clear();
3364 11686 : return;
3365 : }
3366 :
3367 : #ifdef DEBUG_CONNECTION_GUESSING
3368 : if (DEBUGCOND) {
3369 : std::cout << " divideOnEdges " << getID() << " outgoing=" << toString(*outgoing) << "\n";
3370 : }
3371 : #endif
3372 :
3373 : // build connections for miv lanes
3374 : std::vector<int> availableLanes;
3375 127995 : for (int i = 0; i < (int)myLanes.size(); ++i) {
3376 71858 : if ((getPermissions(i) & SVC_PASSENGER) != 0) {
3377 46150 : availableLanes.push_back(i);
3378 : }
3379 : }
3380 56137 : if (availableLanes.size() > 0) {
3381 34339 : divideSelectedLanesOnEdges(outgoing, availableLanes);
3382 : }
3383 : // build connections for miscellaneous further modes (more than bike,peds,bus and without passenger)
3384 : availableLanes.clear();
3385 127995 : for (int i = 0; i < (int)myLanes.size(); ++i) {
3386 71858 : const SVCPermissions perms = getPermissions(i);
3387 71858 : if ((perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) == 0 || (perms & SVC_PASSENGER) != 0 || isForbidden(perms)) {
3388 61589 : continue;
3389 : }
3390 10269 : availableLanes.push_back(i);
3391 : }
3392 56137 : if (availableLanes.size() > 0) {
3393 10197 : divideSelectedLanesOnEdges(outgoing, availableLanes);
3394 : }
3395 : // build connections for busses from lanes that were excluded in the previous step
3396 : availableLanes.clear();
3397 127995 : for (int i = 0; i < (int)myLanes.size(); ++i) {
3398 71858 : const SVCPermissions perms = getPermissions(i);
3399 71858 : if ((perms & SVC_BUS) == 0 || (perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) != 0 || (perms & SVC_PASSENGER) != 0) {
3400 71594 : continue;
3401 : }
3402 264 : availableLanes.push_back(i);
3403 : }
3404 56137 : if (availableLanes.size() > 0) {
3405 261 : divideSelectedLanesOnEdges(outgoing, availableLanes);
3406 : }
3407 : // build connections for bicycles (possibly combined with pedestrians)
3408 : availableLanes.clear();
3409 127995 : for (int i = 0; i < (int)myLanes.size(); ++i) {
3410 71858 : const SVCPermissions perms = getPermissions(i);
3411 71858 : if (perms != SVC_BICYCLE && perms != (SVC_BICYCLE | SVC_PEDESTRIAN)) {
3412 70181 : continue;
3413 : }
3414 1677 : availableLanes.push_back(i);
3415 : }
3416 56137 : if (availableLanes.size() > 0) {
3417 1652 : divideSelectedLanesOnEdges(outgoing, availableLanes);
3418 : }
3419 : // clean up unassigned fromLanes
3420 : bool explicitTurnaround = false;
3421 56137 : SVCPermissions turnaroundPermissions = SVC_UNSPECIFIED;
3422 245904 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
3423 189767 : if ((*i).fromLane == -1) {
3424 89932 : if ((*i).toEdge == myTurnDestination && myTurnDestination != nullptr) {
3425 : explicitTurnaround = true;
3426 109 : turnaroundPermissions = (*i).permissions;
3427 : }
3428 89932 : if ((*i).permissions != SVC_UNSPECIFIED) {
3429 1472 : for (Connection& c : myConnections) {
3430 1280 : if (c.toLane == -1 && c.toEdge == (*i).toEdge) {
3431 : // carry over loaded edge2edge permissions
3432 345 : c.permissions = (*i).permissions;
3433 : }
3434 : }
3435 : }
3436 89932 : i = myConnections.erase(i);
3437 : } else {
3438 : ++i;
3439 : }
3440 : }
3441 56137 : if (explicitTurnaround) {
3442 218 : myConnections.push_back(Connection((int)myLanes.size() - 1, myTurnDestination, myTurnDestination->getNumLanes() - 1));
3443 109 : myConnections.back().permissions = turnaroundPermissions;
3444 : }
3445 56137 : sortOutgoingConnectionsByIndex();
3446 56137 : }
3447 :
3448 :
3449 : void
3450 46449 : NBEdge::divideSelectedLanesOnEdges(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
3451 46449 : const std::vector<int>& priorities = prepareEdgePriorities(outgoing, availableLanes);
3452 46449 : if (priorities.empty()) {
3453 : return;
3454 : }
3455 : #ifdef DEBUG_CONNECTION_GUESSING
3456 : if (DEBUGCOND) {
3457 : std::cout << "divideSelectedLanesOnEdges " << getID() << " out=" << toString(*outgoing) << " prios=" << toString(priorities) << " avail=" << toString(availableLanes) << "\n";
3458 : }
3459 : #endif
3460 : // compute the resulting number of lanes that should be used to reach the following edge
3461 46400 : const int numOutgoing = (int)outgoing->size();
3462 : std::vector<int> resultingLanesFactor;
3463 46400 : resultingLanesFactor.reserve(numOutgoing);
3464 : int minResulting = std::numeric_limits<int>::max();
3465 137256 : for (int i = 0; i < numOutgoing; i++) {
3466 : // res / minResulting will be the number of lanes which are meant to reach the current outgoing edge
3467 90856 : const int res = priorities[i] * (int)availableLanes.size();
3468 90856 : resultingLanesFactor.push_back(res);
3469 90856 : if (minResulting > res && res > 0) {
3470 : // prevent minResulting from becoming 0
3471 : minResulting = res;
3472 : }
3473 : }
3474 : // compute the number of virtual edges
3475 : // a virtual edge is used as a replacement for a real edge from now on
3476 : // it shall allow to divide the existing lanes on this structure without
3477 : // regarding the structure of outgoing edges
3478 : int numVirtual = 0;
3479 : // compute the transition from virtual to real edges
3480 : EdgeVector transition;
3481 46400 : transition.reserve(numOutgoing);
3482 137256 : for (int i = 0; i < numOutgoing; i++) {
3483 : // tmpNum will be the number of connections from this edge to the next edge
3484 : assert(i < (int)resultingLanesFactor.size());
3485 90856 : const int tmpNum = (resultingLanesFactor[i] + minResulting - 1) / minResulting; // integer division rounding up
3486 90856 : numVirtual += tmpNum;
3487 516158 : for (int j = 0; j < tmpNum; j++) {
3488 425302 : transition.push_back((*outgoing)[i]);
3489 : }
3490 : }
3491 : #ifdef DEBUG_CONNECTION_GUESSING
3492 : if (DEBUGCOND) {
3493 : std::cout << " minResulting=" << minResulting << " numVirtual=" << numVirtual << " availLanes=" << toString(availableLanes) << " resLanes=" << toString(resultingLanesFactor) << " transition=" << toString(transition) << "\n";
3494 : }
3495 : #endif
3496 :
3497 : // assign lanes to edges
3498 : // (conversion from virtual to real edges is done)
3499 : ToEdgeConnectionsAdder adder(transition);
3500 46400 : Bresenham::compute(&adder, static_cast<int>(availableLanes.size()), numVirtual);
3501 : const std::map<NBEdge*, std::vector<int> >& l2eConns = adder.getBuiltConnections();
3502 137256 : for (NBEdge* const target : *outgoing) {
3503 : assert(l2eConns.find(target) != l2eConns.end());
3504 192466 : for (const int j : l2eConns.find(target)->second) {
3505 101610 : const int fromIndex = availableLanes[j];
3506 101610 : if ((getPermissions(fromIndex) & target->getPermissions()) == 0) {
3507 : // exclude connection if fromLane and toEdge have no common permissions
3508 61 : continue;
3509 : }
3510 101549 : if ((getPermissions(fromIndex) & target->getPermissions()) == SVC_PEDESTRIAN) {
3511 : // exclude connection if the only commonly permitted class are pedestrians
3512 : // these connections are later built in NBNode::buildWalkingAreas
3513 249 : continue;
3514 : }
3515 : // avoid building more connections than the edge has viable lanes (earlier
3516 : // ones have precedence). This is necessary when running divideSelectedLanesOnEdges more than once.
3517 : // @todo To decide which target lanes are still available we need to do a
3518 : // preliminary lane-to-lane assignment in regard to permissions (rather than to ordering)
3519 101300 : const int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
3520 : int targetLanes = target->getNumLanes();
3521 101300 : if (target->getPermissions(0) == SVC_PEDESTRIAN) {
3522 7216 : --targetLanes;
3523 : }
3524 101300 : if (numConsToTarget >= targetLanes) {
3525 2623 : continue;
3526 : }
3527 98677 : if (myLanes[fromIndex].connectionsDone) {
3528 : // we already have complete information about connections from
3529 : // this lane. do not add anything else
3530 : #ifdef DEBUG_CONNECTION_GUESSING
3531 : if (DEBUGCOND) {
3532 : std::cout << " connectionsDone from " << getID() << "_" << fromIndex << ": ";
3533 : for (const Connection& c : getConnectionsFromLane(fromIndex)) {
3534 : std::cout << c.getDescription(this) << ", ";
3535 : }
3536 : std::cout << "\n";
3537 : }
3538 : #endif
3539 38 : continue;
3540 : }
3541 197278 : myConnections.push_back(Connection(fromIndex, target, -1));
3542 : #ifdef DEBUG_CONNECTION_GUESSING
3543 : if (DEBUGCOND) {
3544 : std::cout << " request connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3545 : }
3546 : #endif
3547 : }
3548 : }
3549 :
3550 46400 : addStraightConnections(outgoing, availableLanes, priorities);
3551 46449 : }
3552 :
3553 :
3554 : void
3555 46400 : NBEdge::addStraightConnections(const EdgeVector* outgoing, const std::vector<int>& availableLanes, const std::vector<int>& priorities) {
3556 : // ensure sufficient straight connections for the (highest-priority) straight target
3557 46400 : const int numOutgoing = (int) outgoing->size();
3558 : NBEdge* target = nullptr;
3559 : NBEdge* rightOfTarget = nullptr;
3560 : NBEdge* leftOfTarget = nullptr;
3561 : int maxPrio = 0;
3562 137256 : for (int i = 0; i < numOutgoing; i++) {
3563 90856 : if (maxPrio < priorities[i]) {
3564 75941 : const LinkDirection dir = myTo->getDirection(this, (*outgoing)[i]);
3565 75941 : if (dir == LinkDirection::STRAIGHT) {
3566 38196 : maxPrio = priorities[i];
3567 38196 : target = (*outgoing)[i];
3568 38196 : rightOfTarget = i == 0 ? outgoing->back() : (*outgoing)[i - 1];
3569 38196 : leftOfTarget = i + 1 == numOutgoing ? outgoing->front() : (*outgoing)[i + 1];
3570 : }
3571 : }
3572 : }
3573 46400 : if (target == nullptr) {
3574 : return;
3575 : }
3576 38030 : int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
3577 : int targetLanes = (int)target->getNumLanes();
3578 38030 : if (target->getPermissions(0) == SVC_PEDESTRIAN) {
3579 3216 : --targetLanes;
3580 : }
3581 38030 : const int numDesiredConsToTarget = MIN2(targetLanes, (int)availableLanes.size());
3582 : #ifdef DEBUG_CONNECTION_GUESSING
3583 : if (DEBUGCOND) {
3584 : std::cout << " checking extra lanes for target=" << target->getID() << " cons=" << numConsToTarget << " desired=" << numDesiredConsToTarget << "\n";
3585 : }
3586 : #endif
3587 : std::vector<int>::const_iterator it_avail = availableLanes.begin();
3588 39125 : while (numConsToTarget < numDesiredConsToTarget && it_avail != availableLanes.end()) {
3589 1095 : const int fromIndex = *it_avail;
3590 : if (
3591 : // not yet connected
3592 1095 : (count_if(myConnections.begin(), myConnections.end(), connections_finder(fromIndex, target, -1)) == 0)
3593 : // matching permissions
3594 650 : && ((getPermissions(fromIndex) & target->getPermissions()) != 0)
3595 : // more than pedestrians
3596 648 : && ((getPermissions(fromIndex) & target->getPermissions()) != SVC_PEDESTRIAN)
3597 : // lane not yet fully defined
3598 1741 : && !myLanes[fromIndex].connectionsDone
3599 : ) {
3600 : #ifdef DEBUG_CONNECTION_GUESSING
3601 : if (DEBUGCOND) {
3602 : std::cout << " candidate from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3603 : }
3604 : #endif
3605 : // prevent same-edge conflicts
3606 : if (
3607 : // no outgoing connections to the right from further left
3608 317 : ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
3609 : // no outgoing connections to the left from further right
3610 1260 : && (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)) {
3611 : #ifdef DEBUG_CONNECTION_GUESSING
3612 : if (DEBUGCOND) {
3613 : std::cout << " request additional connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3614 : }
3615 : #endif
3616 1144 : myConnections.push_back(Connection(fromIndex, target, -1));
3617 572 : numConsToTarget++;
3618 : } else {
3619 : #ifdef DEBUG_CONNECTION_GUESSING
3620 : if (DEBUGCOND) std::cout
3621 : << " fail check1="
3622 : << ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
3623 : << " check2=" << (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)
3624 : << " rightOfTarget=" << rightOfTarget->getID()
3625 : << " leftOfTarget=" << leftOfTarget->getID()
3626 : << "\n";
3627 : #endif
3628 :
3629 : }
3630 : }
3631 : ++it_avail;
3632 : }
3633 : }
3634 :
3635 :
3636 : const std::vector<int>
3637 46449 : NBEdge::prepareEdgePriorities(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
3638 : std::vector<int> priorities;
3639 46449 : MainDirections mainDirections(*outgoing, this, myTo, availableLanes);
3640 : const int dist = mainDirections.getStraightest();
3641 46449 : if (dist == -1) {
3642 : return priorities;
3643 : }
3644 : // copy the priorities first
3645 46400 : priorities.reserve(outgoing->size());
3646 137256 : for (const NBEdge* const out : *outgoing) {
3647 90856 : int prio = NBNode::isTrafficLight(myTo->getType()) ? 0 : out->getJunctionPriority(myTo);
3648 : assert((prio + 1) * 2 > 0);
3649 90856 : prio = (prio + 1) * 2;
3650 90856 : priorities.push_back(prio);
3651 : }
3652 : // when the right turning direction has not a higher priority, divide
3653 : // the importance by 2 due to the possibility to leave the junction
3654 : // faster from this lane
3655 : #ifdef DEBUG_CONNECTION_GUESSING
3656 : if (DEBUGCOND) std::cout << " prepareEdgePriorities " << getID()
3657 : << " outgoing=" << toString(*outgoing)
3658 : << " priorities1=" << toString(priorities)
3659 : << " dist=" << dist
3660 : << "\n";
3661 : #endif
3662 46400 : if (dist != 0 && !mainDirections.includes(MainDirections::Direction::RIGHTMOST)) {
3663 : assert(priorities.size() > 0);
3664 14653 : priorities[0] /= 2;
3665 : #ifdef DEBUG_CONNECTION_GUESSING
3666 : if (DEBUGCOND) {
3667 : std::cout << " priorities2=" << toString(priorities) << "\n";
3668 : }
3669 : #endif
3670 : }
3671 : // HEURISTIC:
3672 : // when no higher priority exists, let the forward direction be
3673 : // the main direction
3674 46400 : if (mainDirections.empty()) {
3675 : assert(dist < (int)priorities.size());
3676 10374 : priorities[dist] *= 2;
3677 : #ifdef DEBUG_CONNECTION_GUESSING
3678 : if (DEBUGCOND) {
3679 : std::cout << " priorities3=" << toString(priorities) << "\n";
3680 : }
3681 : #endif
3682 : }
3683 46400 : if (NBNode::isTrafficLight(myTo->getType())) {
3684 4350 : priorities[dist] += 1;
3685 : } else {
3686 : // try to ensure separation of left turns
3687 42050 : if (mainDirections.includes(MainDirections::Direction::RIGHTMOST) && mainDirections.includes(MainDirections::Direction::LEFTMOST)) {
3688 818 : priorities[0] /= 4;
3689 818 : priorities[(int)priorities.size() - 1] /= 2;
3690 : #ifdef DEBUG_CONNECTION_GUESSING
3691 : if (DEBUGCOND) {
3692 : std::cout << " priorities6=" << toString(priorities) << "\n";
3693 : }
3694 : #endif
3695 41232 : } else if (mainDirections.includes(MainDirections::Direction::RIGHTMOST)
3696 21705 : && outgoing->size() > 2
3697 4457 : && availableLanes.size() == 2
3698 41412 : && (*outgoing)[dist]->getPriority() == (*outgoing)[0]->getPriority()) {
3699 171 : priorities[0] /= 4;
3700 171 : priorities.back() /= 2;
3701 : #ifdef DEBUG_CONNECTION_GUESSING
3702 : if (DEBUGCOND) {
3703 : std::cout << " priorities7=" << toString(priorities) << "\n";
3704 : }
3705 : #endif
3706 : }
3707 : }
3708 46400 : if (mainDirections.includes(MainDirections::Direction::FORWARD)) {
3709 24612 : if (myLanes.size() > 2) {
3710 2950 : priorities[dist] *= 2;
3711 : #ifdef DEBUG_CONNECTION_GUESSING
3712 : if (DEBUGCOND) {
3713 : std::cout << " priorities4=" << toString(priorities) << "\n";
3714 : }
3715 : #endif
3716 : } else {
3717 21662 : priorities[dist] *= 3;
3718 : #ifdef DEBUG_CONNECTION_GUESSING
3719 : if (DEBUGCOND) {
3720 : std::cout << " priorities5=" << toString(priorities) << "\n";
3721 : }
3722 : #endif
3723 : }
3724 : }
3725 : return priorities;
3726 46449 : }
3727 :
3728 :
3729 : void
3730 66839 : NBEdge::appendTurnaround(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike, bool checkPermissions) {
3731 : // do nothing if no turnaround is known
3732 66839 : if (myTurnDestination == nullptr || myTo->getType() == SumoXMLNodeType::RAIL_CROSSING) {
3733 : return;
3734 : }
3735 : // do nothing if the destination node is controlled by a tls and no turnarounds
3736 : // shall be appended for such junctions
3737 44659 : if (noTLSControlled && myTo->isTLControlled()) {
3738 : return;
3739 : }
3740 44587 : if (noFringe && myTo->getFringeType() == FringeType::OUTER) {
3741 : return;
3742 : }
3743 : bool isDeadEnd = true;
3744 46231 : for (const Connection& c : myConnections) {
3745 37103 : if ((c.toEdge->getPermissions(c.toLane)
3746 37103 : & getPermissions(c.fromLane)
3747 37103 : & SVC_PASSENGER) != 0
3748 37103 : || (c.toEdge->getPermissions() & getPermissions()) == getPermissions()) {
3749 : isDeadEnd = false;
3750 : break;
3751 : }
3752 : }
3753 44573 : if (onlyDeadends && !isDeadEnd) {
3754 : return;
3755 : }
3756 44374 : const int fromLane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
3757 44374 : if (onlyTurnlane) {
3758 90 : for (const Connection& c : getConnectionsFromLane(fromLane)) {
3759 68 : LinkDirection dir = myTo->getDirection(this, c.toEdge);
3760 68 : if (dir != LinkDirection::LEFT && dir != LinkDirection::PARTLEFT) {
3761 : return;
3762 : }
3763 82 : }
3764 : }
3765 44314 : const int toLane = myTurnDestination->getFirstAllowedLaneIndex(NBNode::BACKWARD);
3766 44314 : if (checkPermissions) {
3767 44314 : if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == 0) {
3768 : // exclude connection if fromLane and toEdge have no common permissions
3769 : return;
3770 : }
3771 44041 : if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == SVC_PEDESTRIAN) {
3772 : // exclude connection if the only commonly permitted class are pedestrians
3773 : // these connections are later built in NBNode::buildWalkingAreas
3774 : return;
3775 : }
3776 : }
3777 : // avoid railway turn-arounds
3778 41433 : if (isRailway(getPermissions() & myTurnDestination->getPermissions())
3779 41433 : && fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), myTurnDestination->getAngleAtNode(myTo))) > 90) {
3780 : // except at dead-ends on bidi-edges where they model a reversal in train direction
3781 : // @todo #4382: once the network fringe is tagged, it also should not receive turn-arounds)
3782 3151 : if (isBidiRail() && isRailDeadEnd()) {
3783 : // add a slow connection because direction-reversal implies stopping
3784 2848 : setConnection(fromLane, myTurnDestination, toLane, Lane2LaneInfoType::VALIDATED, false, false, KEEPCLEAR_UNSPECIFIED, UNSPECIFIED_CONTPOS, UNSPECIFIED_VISIBILITY_DISTANCE, SUMO_const_haltingSpeed);
3785 2848 : return;
3786 : } else {
3787 303 : return;
3788 : }
3789 : };
3790 38282 : if (noGeometryLike && !isDeadEnd) {
3791 : // ignore paths and service entrances if this edge is for passenger traffic
3792 32088 : if (myTo->geometryLike() || ((getPermissions() & SVC_PASSENGER) != 0
3793 24248 : && !onlyTurnlane
3794 24240 : && myTo->geometryLike(
3795 56328 : NBEdge::filterByPermissions(myTo->getIncomingEdges(), ~(SVC_BICYCLE | SVC_PEDESTRIAN | SVC_DELIVERY)),
3796 56328 : NBEdge::filterByPermissions(myTo->getOutgoingEdges(), ~(SVC_BICYCLE | SVC_PEDESTRIAN | SVC_DELIVERY))))) {
3797 : // make sure the turnDestination has other incoming edges
3798 6095 : EdgeVector turnIncoming = myTurnDestination->getIncomingEdges();
3799 6095 : if (turnIncoming.size() > 1) {
3800 : // this edge is always part of incoming
3801 : return;
3802 : }
3803 6095 : }
3804 : }
3805 64584 : setConnection(fromLane, myTurnDestination, toLane, Lane2LaneInfoType::VALIDATED);
3806 : }
3807 :
3808 :
3809 : bool
3810 16850355 : NBEdge::isTurningDirectionAt(const NBEdge* const edge) const {
3811 : // maybe it was already set as the turning direction
3812 16850355 : if (edge == myTurnDestination) {
3813 : return true;
3814 13503017 : } else if (myTurnDestination != nullptr) {
3815 : // otherwise - it's not if a turning direction exists
3816 : return false;
3817 : }
3818 5544436 : return edge == myPossibleTurnDestination;
3819 : }
3820 :
3821 :
3822 : NBNode*
3823 0 : NBEdge::tryGetNodeAtPosition(double pos, double tolerance) const {
3824 : // return the from-node when the position is at the begin of the edge
3825 0 : if (pos < tolerance) {
3826 0 : return myFrom;
3827 : }
3828 : // return the to-node when the position is at the end of the edge
3829 0 : if (pos > myLength - tolerance) {
3830 0 : return myTo;
3831 : }
3832 : return nullptr;
3833 : }
3834 :
3835 :
3836 : void
3837 22 : NBEdge::moveOutgoingConnectionsFrom(NBEdge* e, int laneOff) {
3838 : int lanes = e->getNumLanes();
3839 49 : for (int i = 0; i < lanes; i++) {
3840 68 : for (const NBEdge::Connection& el : e->getConnectionsFromLane(i)) {
3841 : assert(el.tlID == "");
3842 82 : addLane2LaneConnection(i + laneOff, el.toEdge, el.toLane, Lane2LaneInfoType::COMPUTED);
3843 27 : }
3844 : }
3845 22 : }
3846 :
3847 :
3848 : bool
3849 93 : NBEdge::lanesWereAssigned() const {
3850 93 : return myStep == EdgeBuildingStep::LANES2LANES_DONE || myStep == EdgeBuildingStep::LANES2LANES_USER;
3851 : }
3852 :
3853 :
3854 : double
3855 0 : NBEdge::getMaxLaneOffset() {
3856 0 : return SUMO_const_laneWidth * (double)myLanes.size();
3857 : }
3858 :
3859 :
3860 : bool
3861 267181 : NBEdge::mayBeTLSControlled(int fromLane, NBEdge* toEdge, int toLane) const {
3862 1293972 : for (const Connection& c : myConnections) {
3863 1027388 : if (c.fromLane == fromLane && c.toEdge == toEdge && c.toLane == toLane && c.uncontrolled) {
3864 : return false;
3865 : }
3866 : }
3867 : return true;
3868 : }
3869 :
3870 :
3871 : bool
3872 43038 : NBEdge::setControllingTLInformation(const NBConnection& c, const std::string& tlID) {
3873 43038 : const int fromLane = c.getFromLane();
3874 43038 : NBEdge* toEdge = c.getTo();
3875 43038 : const int toLane = c.getToLane();
3876 : const int tlIndex = c.getTLIndex();
3877 : const int tlIndex2 = c.getTLIndex2();
3878 : // check whether the connection was not set as not to be controled previously
3879 43038 : if (!mayBeTLSControlled(fromLane, toEdge, toLane)) {
3880 : return false;
3881 : }
3882 :
3883 : assert(fromLane < 0 || fromLane < (int) myLanes.size());
3884 : // try to use information about the connections if given
3885 43038 : if (fromLane >= 0 && toLane >= 0) {
3886 : // find the specified connection
3887 : std::vector<Connection>::iterator i =
3888 43038 : find_if(myConnections.begin(), myConnections.end(), connections_finder(fromLane, toEdge, toLane));
3889 : // ok, we have to test this as on the removal of self-loop edges some connections
3890 : // will be reassigned
3891 43038 : if (i != myConnections.end()) {
3892 : // get the connection
3893 : Connection& connection = *i;
3894 : // set the information about the tl
3895 43038 : connection.tlID = tlID;
3896 43038 : connection.tlLinkIndex = tlIndex;
3897 43038 : connection.tlLinkIndex2 = tlIndex2;
3898 : return true;
3899 : }
3900 : }
3901 : // if the original connection was not found, set the information for all
3902 : // connections
3903 : int no = 0;
3904 : bool hadError = false;
3905 0 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
3906 0 : if ((*i).toEdge != toEdge) {
3907 0 : continue;
3908 : }
3909 0 : if (fromLane >= 0 && fromLane != (*i).fromLane) {
3910 0 : continue;
3911 : }
3912 0 : if (toLane >= 0 && toLane != (*i).toLane) {
3913 0 : continue;
3914 : }
3915 0 : if ((*i).tlID == "") {
3916 : (*i).tlID = tlID;
3917 0 : (*i).tlLinkIndex = tlIndex;
3918 0 : (*i).tlLinkIndex2 = tlIndex2;
3919 0 : no++;
3920 : } else {
3921 0 : if ((*i).tlID != tlID && (*i).tlLinkIndex == tlIndex) {
3922 0 : WRITE_WARNINGF(TL("The lane '%' on edge '%' already had a traffic light signal."), i->fromLane, getID());
3923 : hadError = true;
3924 : }
3925 : }
3926 : }
3927 0 : if (hadError && no == 0) {
3928 0 : WRITE_WARNINGF(TL("Could not set any signal of the tlLogic '%' (unknown group)."), tlID);
3929 : }
3930 : return true;
3931 : }
3932 :
3933 :
3934 : void
3935 128162 : NBEdge::clearControllingTLInformation() {
3936 380969 : for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); it++) {
3937 252807 : it->tlID = "";
3938 : }
3939 128162 : }
3940 :
3941 :
3942 : PositionVector
3943 617146 : NBEdge::getCWBoundaryLine(const NBNode& n) const {
3944 617146 : PositionVector ret;
3945 : int lane;
3946 617146 : if (myFrom == (&n)) {
3947 : // outgoing
3948 330375 : lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
3949 330375 : ret = myLanes[lane].shape;
3950 : } else {
3951 : // incoming
3952 286771 : lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
3953 573542 : ret = myLanes[lane].shape.reverse();
3954 : }
3955 617146 : ret.move2side(getLaneWidth(lane) / 2.);
3956 617146 : return ret;
3957 0 : }
3958 :
3959 :
3960 : PositionVector
3961 614908 : NBEdge::getCCWBoundaryLine(const NBNode& n) const {
3962 614908 : PositionVector ret;
3963 : int lane;
3964 614908 : if (myFrom == (&n)) {
3965 : // outgoing
3966 286767 : lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
3967 286767 : ret = myLanes[lane].shape;
3968 : } else {
3969 : // incoming
3970 328141 : lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
3971 656282 : ret = myLanes[lane].shape.reverse();
3972 : }
3973 614908 : ret.move2side(-getLaneWidth(lane) / 2.);
3974 614908 : return ret;
3975 0 : }
3976 :
3977 :
3978 : bool
3979 8579 : NBEdge::expandableBy(NBEdge* possContinuation, std::string& reason) const {
3980 : // ok, the number of lanes must match
3981 8579 : if (myLanes.size() != possContinuation->myLanes.size()) {
3982 : reason = "laneNumber";
3983 327 : return false;
3984 : }
3985 : // do not create self loops
3986 8252 : if (myFrom == possContinuation->myTo) {
3987 : reason = "loop";
3988 1136 : return false;
3989 : }
3990 : // conserve bidi-rails
3991 7116 : if (isBidiRail() != possContinuation->isBidiRail()) {
3992 : reason = "bidi-rail";
3993 4 : return false;
3994 : }
3995 : // also, check whether the connections - if any exit do allow to join
3996 : // both edges
3997 : // This edge must have a one-to-one connection to the following lanes
3998 7112 : switch (myStep) {
3999 : case EdgeBuildingStep::INIT_REJECT_CONNECTIONS:
4000 : break;
4001 : case EdgeBuildingStep::INIT:
4002 : break;
4003 14 : case EdgeBuildingStep::EDGE2EDGES: {
4004 : // the following edge must be connected
4005 14 : const EdgeVector& conn = getConnectedEdges();
4006 14 : if (find(conn.begin(), conn.end(), possContinuation) == conn.end()) {
4007 : reason = "disconnected";
4008 : return false;
4009 : }
4010 14 : }
4011 : break;
4012 20 : case EdgeBuildingStep::LANES2EDGES:
4013 : case EdgeBuildingStep::LANES2LANES_RECHECK:
4014 : case EdgeBuildingStep::LANES2LANES_DONE:
4015 : case EdgeBuildingStep::LANES2LANES_USER: {
4016 : // the possible continuation must be connected
4017 20 : if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(possContinuation)) == myConnections.end()) {
4018 : reason = "disconnected";
4019 0 : return false;
4020 : }
4021 : // all lanes must go to the possible continuation
4022 20 : std::vector<int> conns = getConnectionLanes(possContinuation);
4023 20 : const int offset = MAX2(0, getFirstNonPedestrianLaneIndex(NBNode::FORWARD, true));
4024 20 : if (conns.size() < myLanes.size() - offset) {
4025 : reason = "some lanes disconnected";
4026 : return false;
4027 : }
4028 20 : }
4029 : break;
4030 : default:
4031 : break;
4032 : }
4033 7112 : const double minLength = OptionsCont::getOptions().getFloat("geometry.remove.min-length");
4034 7113 : if (minLength > 0 && (possContinuation->getLoadedLength() < minLength || getLoadedLength() < minLength)) {
4035 : return true;
4036 : }
4037 7111 : const double maxJunctionSize = OptionsCont::getOptions().getFloat("geometry.remove.max-junction-size");
4038 7111 : if (maxJunctionSize >= 0) {
4039 4 : const double junctionSize = myGeom.back().distanceTo2D(possContinuation->myGeom.front());
4040 4 : if (junctionSize > maxJunctionSize + POSITION_EPS) {
4041 8 : reason = "junction size (" + toString(junctionSize) + ") > max-junction-size (" + toString(maxJunctionSize) + ")";
4042 2 : return false;
4043 : }
4044 : }
4045 : // the priority, too (?)
4046 7109 : if (getPriority() != possContinuation->getPriority()) {
4047 : reason = "priority";
4048 70 : return false;
4049 : }
4050 : // the speed allowed
4051 7039 : if (mySpeed != possContinuation->mySpeed) {
4052 : reason = "speed";
4053 1065 : return false;
4054 : }
4055 : // the routingType
4056 5974 : if (myRoutingType != possContinuation->myRoutingType) {
4057 : reason = "routingType";
4058 2 : return false;
4059 : }
4060 : // spreadtype should match or it will look ugly
4061 5972 : if (myLaneSpreadFunction != possContinuation->myLaneSpreadFunction) {
4062 : reason = "spreadType";
4063 78 : return false;
4064 : }
4065 : // matching lanes must have identical properties
4066 13775 : for (int i = 0; i < (int)myLanes.size(); i++) {
4067 7966 : if (myLanes[i].speed != possContinuation->myLanes[i].speed) {
4068 0 : reason = "lane " + toString(i) + " speed";
4069 85 : return false;
4070 7966 : } else if (myLanes[i].permissions != possContinuation->myLanes[i].permissions) {
4071 116 : reason = "lane " + toString(i) + " permissions";
4072 58 : return false;
4073 7908 : } else if (myLanes[i].changeLeft != possContinuation->myLanes[i].changeLeft || myLanes[i].changeRight != possContinuation->myLanes[i].changeRight) {
4074 14 : reason = "lane " + toString(i) + " change restrictions";
4075 7 : return false;
4076 7980 : } else if (myLanes[i].width != possContinuation->myLanes[i].width &&
4077 8059 : fabs(myLanes[i].width - possContinuation->myLanes[i].width) > OptionsCont::getOptions().getFloat("geometry.remove.width-tolerance")) {
4078 40 : reason = "lane " + toString(i) + " width";
4079 20 : return false;
4080 : }
4081 : }
4082 : // if given identically osm names
4083 15941 : if (!OptionsCont::getOptions().isDefault("output.street-names") && myStreetName != possContinuation->getStreetName()
4084 5918 : && ((myStreetName != "" && possContinuation->getStreetName() != "")
4085 : // only permit merging a short unnamed road with a longer named road
4086 21 : || (myStreetName != "" && myLength <= possContinuation->getLength())
4087 16 : || (myStreetName == "" && myLength >= possContinuation->getLength()))) {
4088 : return false;
4089 : }
4090 :
4091 : return true;
4092 : }
4093 :
4094 :
4095 : void
4096 5512 : NBEdge::append(NBEdge* e) {
4097 : // append geometry
4098 5512 : myGeom.append(e->myGeom);
4099 12957 : for (int i = 0; i < (int)myLanes.size(); i++) {
4100 7445 : myLanes[i].customShape.append(e->myLanes[i].customShape);
4101 9540 : if (myLanes[i].hasParameter(SUMO_PARAM_ORIGID) || e->myLanes[i].hasParameter(SUMO_PARAM_ORIGID)
4102 16985 : || OptionsCont::getOptions().getBool("output.original-names")) {
4103 16062 : const std::string origID = myLanes[i].getParameter(SUMO_PARAM_ORIGID, getID());
4104 16062 : const std::string origID2 = e->myLanes[i].getParameter(SUMO_PARAM_ORIGID, e->getID());
4105 5354 : if (origID != origID2) {
4106 9756 : myLanes[i].setParameter(SUMO_PARAM_ORIGID, origID + " " + origID2);
4107 : }
4108 : }
4109 7445 : myLanes[i].connectionsDone = e->myLanes[i].connectionsDone;
4110 7445 : myLanes[i].turnSigns = e->myLanes[i].turnSigns;
4111 : }
4112 5512 : if (e->getLength() > myLength) {
4113 : // possibly some lane attributes differ (when using option geometry.remove.min-length)
4114 : // make sure to use the attributes from the longer edge
4115 6069 : for (int i = 0; i < (int)myLanes.size(); i++) {
4116 3401 : myLanes[i].width = e->myLanes[i].width;
4117 : }
4118 : // defined name prevails over undefined name of shorter road
4119 2668 : if (myStreetName == "") {
4120 1261 : myStreetName = e->myStreetName;
4121 : }
4122 : }
4123 : // recompute length
4124 5512 : myLength += e->myLength;
4125 5512 : if (myLoadedLength > 0 || e->myLoadedLength > 0) {
4126 1 : myLoadedLength = getFinalLength() + e->getFinalLength();
4127 : }
4128 : // copy the connections and the building step if given
4129 5512 : myStep = e->myStep;
4130 5512 : myConnections = e->myConnections;
4131 5512 : myTurnDestination = e->myTurnDestination;
4132 5512 : myPossibleTurnDestination = e->myPossibleTurnDestination;
4133 5512 : myConnectionsToDelete = e->myConnectionsToDelete;
4134 5512 : updateRemovedNodes(e->getParameter(SUMO_PARAM_REMOVED_NODES));
4135 : // set the node
4136 5512 : myTo = e->myTo;
4137 5512 : myTurnSignTarget = e->myTurnSignTarget;
4138 : myToBorder = e->myToBorder;
4139 11024 : mergeParameters(e->getParametersMap());
4140 : if (e->mySignalPosition != Position::INVALID) {
4141 1169 : mySignalPosition = e->mySignalPosition;
4142 : }
4143 5512 : computeAngle(); // myEndAngle may be different now
4144 5512 : }
4145 :
4146 :
4147 : void
4148 5519 : NBEdge::updateRemovedNodes(const std::string& removed) {
4149 11038 : std::string result = getParameter(SUMO_PARAM_REMOVED_NODES);
4150 5519 : if (!result.empty() && !removed.empty()) {
4151 : result += " ";
4152 : }
4153 : result += removed;
4154 5519 : if (!result.empty()) {
4155 9 : setParameter(SUMO_PARAM_REMOVED_NODES, result);
4156 : }
4157 5519 : }
4158 :
4159 :
4160 : bool
4161 966606 : NBEdge::hasSignalisedConnectionTo(const NBEdge* const e) const {
4162 4019315 : for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
4163 3380879 : if ((*i).toEdge == e && (*i).tlID != "") {
4164 : return true;
4165 : }
4166 : }
4167 : return false;
4168 : }
4169 :
4170 :
4171 : NBEdge*
4172 924829 : NBEdge::getTurnDestination(bool possibleDestination) const {
4173 924829 : if (myTurnDestination == nullptr && possibleDestination) {
4174 74501 : return myPossibleTurnDestination;
4175 : }
4176 : return myTurnDestination;
4177 : }
4178 :
4179 :
4180 : std::string
4181 333463 : NBEdge::getLaneID(int lane) const {
4182 666926 : return myID + "_" + toString(lane);
4183 : }
4184 :
4185 :
4186 : bool
4187 65 : NBEdge::isNearEnough2BeJoined2(NBEdge* e, double threshold) const {
4188 65 : std::vector<double> distances = myGeom.distances(e->getGeometry());
4189 : assert(distances.size() > 0);
4190 130 : return VectorHelper<double>::maxValue(distances) < threshold;
4191 65 : }
4192 :
4193 :
4194 : void
4195 67 : NBEdge::addLane(int index, bool recomputeShape, bool recomputeConnections, bool shiftIndices) {
4196 : assert(index <= (int)myLanes.size());
4197 201 : myLanes.insert(myLanes.begin() + index, Lane(this, ""));
4198 : // copy attributes
4199 67 : if (myLanes.size() > 1) {
4200 67 : int templateIndex = index > 0 ? index - 1 : index + 1;
4201 67 : myLanes[index].speed = myLanes[templateIndex].speed;
4202 67 : myLanes[index].friction = myLanes[templateIndex].friction;
4203 67 : myLanes[index].permissions = myLanes[templateIndex].permissions;
4204 67 : myLanes[index].preferred = myLanes[templateIndex].preferred;
4205 67 : myLanes[index].endOffset = myLanes[templateIndex].endOffset;
4206 67 : myLanes[index].width = myLanes[templateIndex].width;
4207 67 : myLanes[index].updateParameters(myLanes[templateIndex].getParametersMap());
4208 : }
4209 67 : const EdgeVector& incs = myFrom->getIncomingEdges();
4210 67 : if (recomputeShape) {
4211 51 : computeLaneShapes();
4212 : }
4213 67 : if (recomputeConnections) {
4214 136 : for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
4215 85 : (*i)->invalidateConnections(true);
4216 : }
4217 51 : invalidateConnections(true);
4218 16 : } else if (shiftIndices) {
4219 : // shift outgoing connections above the added lane to the left
4220 0 : for (Connection& c : myConnections) {
4221 0 : if (c.fromLane >= index) {
4222 0 : c.fromLane += 1;
4223 : }
4224 : }
4225 : // shift incoming connections above the added lane to the left
4226 0 : for (NBEdge* inc : myFrom->getIncomingEdges()) {
4227 0 : for (Connection& c : inc->myConnections) {
4228 0 : if (c.toEdge == this && c.toLane >= index) {
4229 0 : c.toLane += 1;
4230 : }
4231 : }
4232 : }
4233 0 : myFrom->shiftTLConnectionLaneIndex(this, +1, index - 1);
4234 0 : myTo->shiftTLConnectionLaneIndex(this, +1, index - 1);
4235 : }
4236 67 : }
4237 :
4238 : void
4239 54 : NBEdge::incLaneNo(int by) {
4240 54 : int newLaneNo = (int)myLanes.size() + by;
4241 121 : while ((int)myLanes.size() < newLaneNo) {
4242 : // recompute shapes on last addition
4243 67 : const bool recompute = ((int)myLanes.size() == newLaneNo - 1) && myStep < EdgeBuildingStep::LANES2LANES_USER;
4244 67 : addLane((int)myLanes.size(), recompute, recompute, false);
4245 : }
4246 54 : }
4247 :
4248 :
4249 : void
4250 68 : NBEdge::deleteLane(int index, bool recompute, bool shiftIndices) {
4251 : assert(index < (int)myLanes.size());
4252 68 : myLanes.erase(myLanes.begin() + index);
4253 68 : if (recompute) {
4254 16 : computeLaneShapes();
4255 16 : const EdgeVector& incs = myFrom->getIncomingEdges();
4256 17 : for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
4257 1 : (*i)->invalidateConnections(true);
4258 : }
4259 16 : invalidateConnections(true);
4260 52 : } else if (shiftIndices) {
4261 44 : removeFromConnections(nullptr, index, -1, false, true);
4262 120 : for (NBEdge* inc : myFrom->getIncomingEdges()) {
4263 76 : inc->removeFromConnections(this, -1, index, false, true);
4264 : }
4265 : }
4266 68 : }
4267 :
4268 :
4269 : void
4270 42 : NBEdge::decLaneNo(int by) {
4271 42 : int newLaneNo = (int) myLanes.size() - by;
4272 : assert(newLaneNo > 0);
4273 66 : while ((int)myLanes.size() > newLaneNo) {
4274 : // recompute shapes on last removal
4275 24 : const bool recompute = (int)myLanes.size() == newLaneNo + 1 && myStep < EdgeBuildingStep::LANES2LANES_USER;
4276 24 : deleteLane((int)myLanes.size() - 1, recompute, false);
4277 : }
4278 42 : }
4279 :
4280 :
4281 : void
4282 6279 : NBEdge::markAsInLane2LaneState() {
4283 : assert(myTo->getOutgoingEdges().size() == 0);
4284 6279 : myStep = EdgeBuildingStep::LANES2LANES_DONE;
4285 6279 : }
4286 :
4287 :
4288 : void
4289 7879 : NBEdge::allowVehicleClass(int lane, SUMOVehicleClass vclass) {
4290 7879 : if (lane < 0) { // all lanes are meant...
4291 7818 : for (int i = 0; i < (int)myLanes.size(); i++) {
4292 5510 : allowVehicleClass(i, vclass);
4293 : }
4294 : } else {
4295 : assert(lane < (int)myLanes.size());
4296 5571 : myLanes[lane].permissions |= vclass;
4297 : }
4298 7879 : }
4299 :
4300 :
4301 : void
4302 26242 : NBEdge::disallowVehicleClass(int lane, SUMOVehicleClass vclass) {
4303 26242 : if (lane < 0) { // all lanes are meant...
4304 26242 : for (int i = 0; i < (int)myLanes.size(); i++) {
4305 16180 : disallowVehicleClass((int) i, vclass);
4306 : }
4307 : } else {
4308 : assert(lane < (int)myLanes.size());
4309 16180 : myLanes[lane].permissions &= ~vclass;
4310 : }
4311 26242 : }
4312 :
4313 :
4314 : void
4315 68 : NBEdge::preferVehicleClass(int lane, SVCPermissions vclasses) {
4316 68 : if (lane < 0) { // all lanes are meant...
4317 0 : for (int i = 0; i < (int)myLanes.size(); i++) {
4318 0 : preferVehicleClass(i, vclasses);
4319 : }
4320 : } else {
4321 : assert(lane < (int)myLanes.size());
4322 68 : myLanes[lane].permissions |= vclasses;
4323 68 : myLanes[lane].preferred |= vclasses;
4324 : }
4325 68 : }
4326 :
4327 :
4328 : void
4329 98164 : NBEdge::setLaneWidth(int lane, double width) {
4330 98164 : if (lane < 0) {
4331 : // all lanes are meant...
4332 4917 : myLaneWidth = width;
4333 9855 : for (int i = 0; i < (int)myLanes.size(); i++) {
4334 : // ... do it for each lane
4335 4938 : setLaneWidth(i, width);
4336 : }
4337 : return;
4338 : }
4339 : assert(lane < (int)myLanes.size());
4340 93247 : myLanes[lane].width = width;
4341 : }
4342 :
4343 : void
4344 3972 : NBEdge::setLaneType(int lane, const std::string& type) {
4345 3972 : if (lane < 0) {
4346 0 : for (int i = 0; i < (int)myLanes.size(); i++) {
4347 : // ... do it for each lane
4348 0 : setLaneType(i, type);
4349 : }
4350 : return;
4351 : }
4352 : assert(lane < (int)myLanes.size());
4353 3972 : myLanes[lane].type = type;
4354 : }
4355 :
4356 :
4357 : double
4358 4383297 : NBEdge::getLaneWidth(int lane) const {
4359 4383297 : return myLanes[lane].width != UNSPECIFIED_WIDTH
4360 4383297 : ? myLanes[lane].width
4361 3630646 : : getLaneWidth() != UNSPECIFIED_WIDTH ? getLaneWidth() : SUMO_const_laneWidth;
4362 : }
4363 :
4364 : double
4365 109579 : NBEdge::getInternalLaneWidth(
4366 : const NBNode& node,
4367 : const NBEdge::Connection& connection,
4368 : const NBEdge::Lane& successor,
4369 : bool isVia) const {
4370 :
4371 109579 : if (!isVia && node.isConstantWidthTransition() && getNumLanes() > connection.toEdge->getNumLanes()) {
4372 3 : return getLaneWidth(connection.fromLane);
4373 : }
4374 :
4375 109576 : return (isBikepath(getPermissions(connection.fromLane)) && (
4376 109576 : getLaneWidth(connection.fromLane) < successor.width || successor.width == UNSPECIFIED_WIDTH)) ?
4377 1017 : myLanes[connection.fromLane].width : successor.width; // getLaneWidth(connection.fromLane) never returns -1 (UNSPECIFIED_WIDTH)
4378 : }
4379 :
4380 : double
4381 1055818 : NBEdge::getTotalWidth() const {
4382 : double result = 0;
4383 2434413 : for (int i = 0; i < (int)myLanes.size(); i++) {
4384 1378595 : result += getLaneWidth(i);
4385 : }
4386 1055818 : return result;
4387 : }
4388 :
4389 : double
4390 64931 : NBEdge::getEndOffset(int lane) const {
4391 64931 : return myLanes[lane].endOffset != UNSPECIFIED_OFFSET ? myLanes[lane].endOffset : getEndOffset();
4392 : }
4393 :
4394 :
4395 : const StopOffset&
4396 726043 : NBEdge::getEdgeStopOffset() const {
4397 726043 : return myEdgeStopOffset;
4398 : }
4399 :
4400 :
4401 : const StopOffset&
4402 24 : NBEdge::getLaneStopOffset(int lane) const {
4403 24 : if (lane == -1) {
4404 12 : return myEdgeStopOffset;
4405 : } else {
4406 12 : return myLanes[lane].laneStopOffset;
4407 : }
4408 : }
4409 :
4410 :
4411 : void
4412 82774 : NBEdge::setEndOffset(int lane, double offset) {
4413 82774 : if (lane < 0) {
4414 : // all lanes are meant...
4415 3 : myEndOffset = offset;
4416 8 : for (int i = 0; i < (int)myLanes.size(); i++) {
4417 : // ... do it for each lane
4418 5 : setEndOffset(i, offset);
4419 : }
4420 : return;
4421 : }
4422 : assert(lane < (int)myLanes.size());
4423 82771 : myLanes[lane].endOffset = offset;
4424 : }
4425 :
4426 :
4427 : bool
4428 147474 : NBEdge::setEdgeStopOffset(int lane, const StopOffset& offset, bool overwrite) {
4429 147474 : if (lane < 0) {
4430 64720 : if (!overwrite && myEdgeStopOffset.isDefined()) {
4431 : return false;
4432 : }
4433 : // all lanes are meant...
4434 64716 : if (offset.getOffset() < 0) {
4435 : // Edge length unknown at parsing time, thus check here.
4436 3 : WRITE_WARNINGF(TL("Ignoring invalid stopOffset for edge '%' (negative offset)."), getID());
4437 1 : return false;
4438 : } else {
4439 64715 : myEdgeStopOffset = offset;
4440 : }
4441 82754 : } else if (lane < (int)myLanes.size()) {
4442 82754 : if (!myLanes[lane].laneStopOffset.isDefined() || overwrite) {
4443 82748 : if (offset.getOffset() < 0) {
4444 : // Edge length unknown at parsing time, thus check here.
4445 0 : WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (negative offset)."), getLaneID(lane));
4446 : } else {
4447 82748 : myLanes[lane].laneStopOffset = offset;
4448 : }
4449 : }
4450 : } else {
4451 0 : WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (invalid lane index)."), toString(lane));
4452 : }
4453 : return true;
4454 : }
4455 :
4456 :
4457 : void
4458 87348 : NBEdge::setSpeed(int lane, double speed) {
4459 87348 : if (lane < 0) {
4460 : // all lanes are meant...
4461 347 : mySpeed = speed;
4462 1094 : for (int i = 0; i < (int)myLanes.size(); i++) {
4463 : // ... do it for each lane
4464 747 : setSpeed(i, speed);
4465 : }
4466 : return;
4467 : }
4468 : assert(lane < (int)myLanes.size());
4469 87001 : myLanes[lane].speed = speed;
4470 : }
4471 :
4472 :
4473 : void
4474 87441 : NBEdge::setFriction(int lane, double friction) {
4475 87441 : if (lane < 0) {
4476 : // all lanes are meant...
4477 789 : myFriction = friction;
4478 2263 : for (int i = 0; i < (int)myLanes.size(); i++) {
4479 : // ... do it for each lane
4480 1474 : setFriction(i, friction);
4481 : }
4482 : return;
4483 : }
4484 : assert(lane < (int)myLanes.size());
4485 86652 : myLanes[lane].friction = friction;
4486 : }
4487 :
4488 :
4489 : void
4490 81329 : NBEdge::setAcceleration(int lane, bool accelRamp) {
4491 : assert(lane >= 0);
4492 : assert(lane < (int)myLanes.size());
4493 81329 : myLanes[lane].accelRamp = accelRamp;
4494 81329 : }
4495 :
4496 :
4497 : void
4498 77 : NBEdge::setLaneShape(int lane, const PositionVector& shape) {
4499 : assert(lane >= 0);
4500 : assert(lane < (int)myLanes.size());
4501 77 : myLanes[lane].customShape = shape;
4502 77 : }
4503 :
4504 :
4505 : void
4506 279438 : NBEdge::setPermissions(SVCPermissions permissions, int lane) {
4507 279438 : if (lane < 0) {
4508 186283 : for (int i = 0; i < (int)myLanes.size(); i++) {
4509 : // ... do it for each lane
4510 103100 : setPermissions(permissions, i);
4511 : }
4512 : } else {
4513 : assert(lane < (int)myLanes.size());
4514 196255 : myLanes[lane].permissions = permissions;
4515 : }
4516 279438 : }
4517 :
4518 :
4519 : void
4520 0 : NBEdge::setPreferredVehicleClass(SVCPermissions permissions, int lane) {
4521 0 : if (lane < 0) {
4522 0 : for (int i = 0; i < (int)myLanes.size(); i++) {
4523 : // ... do it for each lane
4524 0 : setPreferredVehicleClass(permissions, i);
4525 : }
4526 : } else {
4527 : assert(lane < (int)myLanes.size());
4528 0 : myLanes[lane].preferred = permissions;
4529 : }
4530 0 : }
4531 :
4532 :
4533 : void
4534 81575 : NBEdge::setPermittedChanging(int lane, SVCPermissions changeLeft, SVCPermissions changeRight) {
4535 : assert(lane >= 0);
4536 : assert(lane < (int)myLanes.size());
4537 81575 : myLanes[lane].changeLeft = changeLeft;
4538 81575 : myLanes[lane].changeRight = changeRight;
4539 81575 : }
4540 :
4541 :
4542 : SVCPermissions
4543 94207316 : NBEdge::getPermissions(int lane) const {
4544 94207316 : if (lane < 0) {
4545 : SVCPermissions result = 0;
4546 85311478 : for (int i = 0; i < (int)myLanes.size(); i++) {
4547 50865273 : result |= getPermissions(i);
4548 : }
4549 34446205 : return result;
4550 : } else {
4551 : assert(lane < (int)myLanes.size());
4552 59761111 : return myLanes[lane].permissions;
4553 : }
4554 : }
4555 :
4556 :
4557 : void
4558 84217 : NBEdge::setLoadedLength(double val) {
4559 84217 : myLoadedLength = val;
4560 84217 : }
4561 :
4562 : void
4563 10 : NBEdge::setAverageLengthWithOpposite(double val) {
4564 10 : myLength = val;
4565 10 : }
4566 :
4567 :
4568 : void
4569 200 : NBEdge::dismissVehicleClassInformation() {
4570 647 : for (std::vector<Lane>::iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
4571 447 : (*i).permissions = SVCAll;
4572 447 : (*i).preferred = 0;
4573 : }
4574 200 : }
4575 :
4576 :
4577 : bool
4578 406909 : NBEdge::connections_sorter(const Connection& c1, const Connection& c2) {
4579 406909 : if (c1.fromLane != c2.fromLane) {
4580 99211 : return c1.fromLane < c2.fromLane;
4581 : }
4582 307698 : if (c1.toEdge != c2.toEdge) {
4583 : return false; // do not change ordering among toEdges as this is determined by angle in an earlier step
4584 : }
4585 7013 : return c1.toLane < c2.toLane;
4586 : }
4587 :
4588 :
4589 : double
4590 10210 : NBEdge::getSignalOffset() const {
4591 : if (mySignalPosition == Position::INVALID) {
4592 : return UNSPECIFIED_SIGNAL_OFFSET;
4593 : } else {
4594 697 : Position laneEnd = myLaneSpreadFunction == LaneSpreadFunction::RIGHT ?
4595 697 : myLanes.back().shape.back() : myLanes[getNumLanes() / 2].shape.back();
4596 : //std::cout << getID() << " signalPos=" << mySignalPosition << " laneEnd=" << laneEnd << " toShape=" << myTo->getShape() << " toBorder=" << myToBorder << "\n";
4597 : return mySignalPosition.distanceTo2D(laneEnd);
4598 : }
4599 : }
4600 :
4601 :
4602 : int
4603 36851 : NBEdge::getFirstNonPedestrianLaneIndex(int direction, bool exclusive) const {
4604 : assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4605 36851 : const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4606 36851 : const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4607 48762 : for (int i = start; i != end; i += direction) {
4608 : // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
4609 : // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
4610 30224 : if ((exclusive && myLanes[i].permissions != SVC_PEDESTRIAN && myLanes[i].permissions != 0)
4611 48752 : || ((myLanes[i].permissions & SVC_PEDESTRIAN) == 0 && myLanes[i].permissions != 0)) {
4612 30238 : return i;
4613 : }
4614 : }
4615 : return -1;
4616 : }
4617 :
4618 : int
4619 62335 : NBEdge::getFirstNonPedestrianNonBicycleLaneIndex(int direction, bool exclusive) const {
4620 : assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4621 62335 : const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4622 62335 : const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4623 81167 : for (int i = start; i != end; i += direction) {
4624 : // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
4625 : // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
4626 64469 : SVCPermissions p = myLanes[i].permissions;
4627 64469 : if ((exclusive && p != SVC_PEDESTRIAN && p != SVC_BICYCLE && p != (SVC_PEDESTRIAN | SVC_BICYCLE) && p != 0)
4628 18832 : || (p == SVCAll || ((p & (SVC_PEDESTRIAN | SVC_BICYCLE)) == 0 && p != 0))) {
4629 45637 : return i;
4630 : }
4631 : }
4632 : return -1;
4633 : }
4634 :
4635 : int
4636 854895 : NBEdge::getSpecialLane(SVCPermissions permissions) const {
4637 1947921 : for (int i = 0; i < (int)myLanes.size(); i++) {
4638 1096768 : if (myLanes[i].permissions == permissions) {
4639 3742 : return i;
4640 : }
4641 : }
4642 : return -1;
4643 : }
4644 :
4645 : int
4646 1320742 : NBEdge::getFirstAllowedLaneIndex(int direction) const {
4647 : assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4648 1320742 : const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4649 1320742 : const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4650 1321166 : for (int i = start; i != end; i += direction) {
4651 1320977 : if (myLanes[i].permissions != 0) {
4652 1320553 : return i;
4653 : }
4654 : }
4655 189 : return end - direction;
4656 : }
4657 :
4658 :
4659 : std::set<SVCPermissions>
4660 5071 : NBEdge::getPermissionVariants(int iStart, int iEnd) const {
4661 : std::set<SVCPermissions> result;
4662 5071 : if (iStart < 0 || iStart >= getNumLanes() || iEnd > getNumLanes()) {
4663 0 : throw ProcessError("invalid indices iStart " + toString(iStart) + " iEnd " + toString(iEnd) + " for edge with " + toString(getNumLanes()) + " lanes.");
4664 : }
4665 11370 : for (int i = iStart; i < iEnd; ++i) {
4666 6299 : result.insert(getPermissions(i));
4667 : }
4668 5071 : return result;
4669 : }
4670 :
4671 : int
4672 7992906 : NBEdge::getNumLanesThatAllow(SVCPermissions permissions, bool allPermissions) const {
4673 : int result = 0;
4674 20215992 : for (const Lane& lane : myLanes) {
4675 12223086 : if ((allPermissions && (lane.permissions & permissions) == permissions)
4676 535918 : || (!allPermissions && (lane.permissions & permissions) != 0)) {
4677 10717980 : result++;
4678 : }
4679 : }
4680 7992906 : return result;
4681 : }
4682 :
4683 : bool
4684 0 : NBEdge::allowsChangingLeft(int lane, SUMOVehicleClass vclass) const {
4685 : assert(lane >= 0 && lane < getNumLanes());
4686 0 : return myLanes[lane].changeLeft == SVC_UNSPECIFIED ? true : (myLanes[lane].changeLeft & vclass) == vclass;
4687 : }
4688 :
4689 : bool
4690 0 : NBEdge::allowsChangingRight(int lane, SUMOVehicleClass vclass) const {
4691 : assert(lane >= 0 && lane < getNumLanes());
4692 0 : return myLanes[lane].changeRight == SVC_UNSPECIFIED ? true : (myLanes[lane].changeRight & vclass) == vclass;
4693 : }
4694 :
4695 : double
4696 5799 : NBEdge::getCrossingAngle(NBNode* node) {
4697 5799 : double angle = getAngleAtNode(node) + (getFromNode() == node ? 180.0 : 0.0);
4698 5799 : if (angle < 0) {
4699 1490 : angle += 360.0;
4700 : }
4701 5799 : if (angle >= 360) {
4702 0 : angle -= 360.0;
4703 : }
4704 5799 : if (gDebugFlag1) {
4705 0 : std::cout << getID() << " angle=" << getAngleAtNode(node) << " convAngle=" << angle << "\n";
4706 : }
4707 5799 : return angle;
4708 : }
4709 :
4710 :
4711 : NBEdge::Lane
4712 0 : NBEdge::getFirstNonPedestrianLane(int direction) const {
4713 0 : int index = getFirstNonPedestrianLaneIndex(direction);
4714 0 : if (index < 0) {
4715 0 : throw ProcessError(TLF("Edge % allows pedestrians on all lanes", getID()));
4716 : }
4717 0 : return myLanes[index];
4718 : }
4719 :
4720 : std::string
4721 18679 : NBEdge::getSidewalkID() {
4722 : // see IntermodalEdge::getSidewalk()
4723 22317 : for (int i = 0; i < (int)myLanes.size(); i++) {
4724 18732 : if (myLanes[i].permissions == SVC_PEDESTRIAN) {
4725 15094 : return getLaneID(i);
4726 : }
4727 : }
4728 3585 : for (int i = 0; i < (int)myLanes.size(); i++) {
4729 3585 : if ((myLanes[i].permissions & SVC_PEDESTRIAN) != 0) {
4730 3585 : return getLaneID(i);
4731 : }
4732 : }
4733 0 : return getLaneID(0);
4734 : }
4735 :
4736 : void
4737 3292 : NBEdge::addSidewalk(double width) {
4738 3292 : addRestrictedLane(width, SVC_PEDESTRIAN);
4739 3292 : }
4740 :
4741 :
4742 : void
4743 0 : NBEdge::restoreSidewalk(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4744 0 : restoreRestrictedLane(SVC_PEDESTRIAN, oldLanes, oldGeometry, oldConnections);
4745 0 : }
4746 :
4747 :
4748 : void
4749 591 : NBEdge::addBikeLane(double width) {
4750 591 : addRestrictedLane(width, SVC_BICYCLE);
4751 591 : }
4752 :
4753 :
4754 : void
4755 0 : NBEdge::restoreBikelane(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4756 0 : restoreRestrictedLane(SVC_BICYCLE, oldLanes, oldGeometry, oldConnections);
4757 0 : }
4758 :
4759 : bool
4760 5066 : NBEdge::hasRestrictedLane(SUMOVehicleClass vclass) const {
4761 12619 : for (const Lane& lane : myLanes) {
4762 7823 : if (lane.permissions == vclass) {
4763 : return true;
4764 : }
4765 : }
4766 : return false;
4767 : }
4768 :
4769 :
4770 : void
4771 4297 : NBEdge::addRestrictedLane(double width, SUMOVehicleClass vclass) {
4772 4297 : if (hasRestrictedLane(vclass)) {
4773 12 : WRITE_WARNINGF(TL("Edge '%' already has a dedicated lane for %s. Not adding another one."), getID(), toString(vclass));
4774 4 : return;
4775 : }
4776 4293 : if (myLaneSpreadFunction == LaneSpreadFunction::CENTER) {
4777 1201 : myGeom.move2side(width / 2);
4778 : }
4779 : // disallow the designated vclass on all "old" lanes
4780 4293 : disallowVehicleClass(-1, vclass);
4781 : // don't create a restricted vehicle lane to the right of a sidewalk
4782 4293 : const int newIndex = (vclass != SVC_PEDESTRIAN && myLanes[0].permissions == SVC_PEDESTRIAN) ? 1 : 0;
4783 : if (newIndex == 0) {
4784 : // disallow pedestrians on all "higher" lanes to ensure that sidewalk remains the rightmost lane
4785 4252 : disallowVehicleClass(-1, SVC_PEDESTRIAN);
4786 : }
4787 : // add new lane
4788 12879 : myLanes.insert(myLanes.begin() + newIndex, Lane(this, myLanes[0].getParameter(SUMO_PARAM_ORIGID)));
4789 4293 : myLanes[newIndex].permissions = vclass;
4790 4293 : myLanes[newIndex].width = fabs(width);
4791 : // shift outgoing connections to the left
4792 4320 : for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
4793 : Connection& c = *it;
4794 27 : if (c.fromLane >= newIndex) {
4795 27 : c.fromLane += 1;
4796 : }
4797 : }
4798 : // shift incoming connections to the left
4799 4293 : const EdgeVector& incoming = myFrom->getIncomingEdges();
4800 9433 : for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4801 5140 : (*it)->shiftToLanesToEdge(this, 1);
4802 : }
4803 4293 : myFrom->shiftTLConnectionLaneIndex(this, 1);
4804 4293 : myTo->shiftTLConnectionLaneIndex(this, 1);
4805 4293 : computeLaneShapes();
4806 : }
4807 :
4808 :
4809 : void
4810 0 : NBEdge::restoreRestrictedLane(SUMOVehicleClass vclass, std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4811 : // check that previously lane was transformed
4812 0 : if (myLanes[0].permissions != vclass) {
4813 0 : WRITE_WARNINGF(TL("Edge '%' doesn't have a dedicated lane for %s. Cannot be restored."), getID(), toString(vclass));
4814 0 : return;
4815 : }
4816 : // restore old values
4817 : myGeom = oldGeometry;
4818 0 : myLanes = oldLanes;
4819 0 : myConnections = oldConnections;
4820 : // shift incoming connections to the right
4821 0 : const EdgeVector& incoming = myFrom->getIncomingEdges();
4822 0 : for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4823 0 : (*it)->shiftToLanesToEdge(this, 0);
4824 : }
4825 : // Shift TL conections
4826 0 : myFrom->shiftTLConnectionLaneIndex(this, 0);
4827 0 : myTo->shiftTLConnectionLaneIndex(this, 0);
4828 0 : computeLaneShapes();
4829 : }
4830 :
4831 :
4832 : void
4833 5140 : NBEdge::shiftToLanesToEdge(NBEdge* to, int laneOff) {
4834 : /// XXX could we repurpose the function replaceInConnections ?
4835 5245 : for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
4836 105 : if ((*it).toEdge == to && (*it).toLane >= 0) {
4837 27 : (*it).toLane += laneOff;
4838 : }
4839 : }
4840 5140 : }
4841 :
4842 :
4843 : bool
4844 67720 : NBEdge::shiftPositionAtNode(NBNode* node, NBEdge* other) {
4845 67720 : if (myLaneSpreadFunction == LaneSpreadFunction::CENTER
4846 17157 : && !isRailway(getPermissions())
4847 10743 : && !isRailway(other->getPermissions())
4848 78433 : && getBidiEdge() == nullptr) {
4849 10697 : const int i = (node == myTo ? -1 : 0);
4850 10697 : const int i2 = (node == myTo ? 0 : -1);
4851 10697 : const double dist = myGeom[i].distanceTo2D(node->getPosition());
4852 10697 : const double neededOffset = getTotalWidth() / 2;
4853 10697 : const double dist2 = MIN2(myGeom.distance2D(other->getGeometry()[i2]),
4854 10697 : other->getGeometry().distance2D(myGeom[i]));
4855 10697 : const double neededOffset2 = neededOffset + (other->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
4856 10697 : ? (other->getTotalWidth()) / 2 : 0);
4857 10697 : const double missing = neededOffset - dist;
4858 10697 : const double missing2 = neededOffset2 - dist2;
4859 : double shift = 0;
4860 10697 : if (missing > 0 && missing2 > 0) {
4861 : shift = MIN2(missing, missing2);
4862 2028 : } else if (missing2) {
4863 : shift = missing2;
4864 : }
4865 10697 : if (shift > 0) {
4866 : PositionVector tmp = myGeom;
4867 : try {
4868 8772 : tmp.move2side(shift);
4869 8772 : tmp[i].round(gPrecision);
4870 8772 : myGeom[i] = tmp[i];
4871 8772 : computeAngle();
4872 : return true;
4873 : //std::cout << getID() << " shiftPositionAtNode needed=" << neededOffset << " dist=" << dist << " needed2=" << neededOffset2 << " dist2=" << dist2 << " by=" << (neededOffset - dist) << " other=" << other->getID() << "\n";
4874 0 : } catch (InvalidArgument&) {
4875 0 : WRITE_WARNINGF(TL("Could not avoid overlapping shape at node '%' for edge '%'."), node->getID(), getID());
4876 0 : }
4877 8772 : }
4878 : }
4879 : return false;
4880 : }
4881 :
4882 :
4883 : Position
4884 93 : NBEdge::geometryPositionAtOffset(double offset) const {
4885 93 : if (myLoadedLength > 0) {
4886 2 : return myGeom.positionAtOffset(offset * myLength / myLoadedLength);
4887 : } else {
4888 91 : return myGeom.positionAtOffset(offset);
4889 : }
4890 : }
4891 :
4892 :
4893 : double
4894 138340 : NBEdge::getFinalLength() const {
4895 : double result = getLoadedLength();
4896 276680 : if (OptionsCont::getOptions().getBool("no-internal-links") && !hasLoadedLength()) {
4897 : // use length to junction center even if a modified geometry was given
4898 76331 : PositionVector geom = cutAtIntersection(myGeom);
4899 76331 : geom.push_back_noDoublePos(getToNode()->getCenter());
4900 76331 : geom.push_front_noDoublePos(getFromNode()->getCenter());
4901 76331 : result = geom.length();
4902 76331 : }
4903 : double avgEndOffset = 0;
4904 313331 : for (const Lane& lane : myLanes) {
4905 174991 : avgEndOffset += lane.endOffset;
4906 : }
4907 138340 : if (isBidiRail()) {
4908 13428 : avgEndOffset += myPossibleTurnDestination->getEndOffset();
4909 : }
4910 138340 : avgEndOffset /= (double)myLanes.size();
4911 138344 : return MAX2(result - avgEndOffset, POSITION_EPS);
4912 : }
4913 :
4914 :
4915 : void
4916 965 : NBEdge::setOrigID(const std::string origID, const bool append, const int laneIdx) {
4917 965 : if (laneIdx == -1) {
4918 908 : for (int i = 0; i < (int)myLanes.size(); i++) {
4919 986 : setOrigID(origID, append, i);
4920 : }
4921 : } else {
4922 550 : if (origID != "") {
4923 550 : if (append) {
4924 114 : std::vector<std::string> oldIDs = StringTokenizer(myLanes[laneIdx].getParameter(SUMO_PARAM_ORIGID)).getVector();
4925 57 : if (std::find(oldIDs.begin(), oldIDs.end(), origID) == oldIDs.end()) {
4926 57 : oldIDs.push_back(origID);
4927 : }
4928 57 : myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, toString(oldIDs));
4929 57 : } else {
4930 493 : myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, origID);
4931 : }
4932 : } else {
4933 : // do not record empty origID parameter
4934 0 : myLanes[laneIdx].unsetParameter(SUMO_PARAM_ORIGID);
4935 : }
4936 : }
4937 965 : }
4938 :
4939 :
4940 : const EdgeVector&
4941 570 : NBEdge::getSuccessors(SUMOVehicleClass vClass) const {
4942 : // @todo cache successors instead of recomputing them every time
4943 : mySuccessors.clear();
4944 : //std::cout << "getSuccessors edge=" << getID() << " svc=" << toString(vClass) << " cons=" << myConnections.size() << "\n";
4945 3022 : for (const Connection& con : myConnections) {
4946 2414 : if (con.fromLane >= 0 && con.toLane >= 0 && con.toEdge != nullptr &&
4947 159 : (vClass == SVC_IGNORING || (getPermissions(con.fromLane)
4948 159 : & con.toEdge->getPermissions(con.toLane) & vClass) != 0)
4949 4866 : && std::find(mySuccessors.begin(), mySuccessors.end(), con.toEdge) == mySuccessors.end()) {
4950 1642 : mySuccessors.push_back(con.toEdge);
4951 : //std::cout << " succ=" << con.toEdge->getID() << "\n";
4952 : }
4953 : }
4954 570 : return mySuccessors;
4955 : }
4956 :
4957 :
4958 : const ConstRouterEdgePairVector&
4959 4283 : NBEdge::getViaSuccessors(SUMOVehicleClass vClass, bool /*ignoreTransientPermissions*/) const {
4960 : // @todo cache successors instead of recomputing them every time
4961 : myViaSuccessors.clear();
4962 9297 : for (const Connection& con : myConnections) {
4963 : std::pair<const NBEdge*, const Connection*> pair(con.toEdge, nullptr);
4964 : // special case for Persons in Netedit
4965 5014 : if (vClass == SVC_PEDESTRIAN) {
4966 0 : myViaSuccessors.push_back(pair); // Pedestrians have complete freedom of movement in all sucessors
4967 5014 : } else if ((con.fromLane >= 0) && (con.toLane >= 0) &&
4968 5014 : (con.toEdge != nullptr) &&
4969 5014 : ((getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane) & vClass) == vClass)) {
4970 : // ignore duplicates
4971 4864 : if (con.getLength() > 0) {
4972 : pair.second = &con;
4973 : }
4974 9728 : myViaSuccessors.push_back(pair);
4975 : }
4976 : }
4977 4283 : return myViaSuccessors;
4978 : }
4979 :
4980 :
4981 : void
4982 0 : NBEdge::debugPrintConnections(bool outgoing, bool incoming) const {
4983 0 : if (outgoing) {
4984 0 : for (const Connection& c : myConnections) {
4985 0 : std::cout << " " << getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
4986 : }
4987 : }
4988 0 : if (incoming) {
4989 0 : for (NBEdge* inc : myFrom->getIncomingEdges()) {
4990 0 : for (Connection& c : inc->myConnections) {
4991 0 : if (c.toEdge == this) {
4992 0 : std::cout << " " << inc->getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
4993 : }
4994 : }
4995 : }
4996 : }
4997 0 : }
4998 :
4999 :
5000 : int
5001 99 : NBEdge::getLaneIndexFromLaneID(const std::string laneID) {
5002 198 : return StringUtils::toInt(laneID.substr(laneID.rfind("_") + 1));
5003 : }
5004 :
5005 : bool
5006 36 : NBEdge::joinLanes(SVCPermissions perms) {
5007 : bool haveJoined = false;
5008 : int i = 0;
5009 181 : while (i < getNumLanes() - 1) {
5010 145 : if ((getPermissions(i) == perms) && (getPermissions(i + 1) == perms)) {
5011 37 : const double newWidth = getLaneWidth(i) + getLaneWidth(i + 1);
5012 37 : const std::string newType = myLanes[i].type + "|" + myLanes[i + 1].type;
5013 37 : deleteLane(i, false, true);
5014 37 : setLaneWidth(i, newWidth);
5015 37 : setLaneType(i, newType);
5016 : haveJoined = true;
5017 : } else {
5018 108 : i++;
5019 : }
5020 : }
5021 36 : return haveJoined;
5022 : }
5023 :
5024 :
5025 : EdgeVector
5026 62274 : NBEdge::filterByPermissions(const EdgeVector& edges, SVCPermissions permissions) {
5027 : EdgeVector result;
5028 268000 : for (NBEdge* edge : edges) {
5029 205726 : if ((edge->getPermissions() & permissions) != 0) {
5030 190545 : result.push_back(edge);
5031 : }
5032 : }
5033 62274 : return result;
5034 0 : }
5035 :
5036 : NBEdge*
5037 2926 : NBEdge::getStraightContinuation(SVCPermissions permissions) const {
5038 2926 : EdgeVector cands = filterByPermissions(myTo->getOutgoingEdges(), permissions);
5039 2926 : if (cands.size() == 0) {
5040 : return nullptr;
5041 : }
5042 2922 : sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this));
5043 2922 : NBEdge* best = cands.front();
5044 2922 : if (isTurningDirectionAt(best)) {
5045 : return nullptr;
5046 : } else {
5047 : return best;
5048 : }
5049 2926 : }
5050 :
5051 : NBEdge*
5052 186 : NBEdge::getStraightPredecessor(SVCPermissions permissions) const {
5053 186 : EdgeVector cands = filterByPermissions(myFrom->getIncomingEdges(), permissions);
5054 186 : if (cands.size() == 0) {
5055 : return nullptr;
5056 : }
5057 174 : sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this, false));
5058 174 : NBEdge* best = cands.front();
5059 174 : if (best->isTurningDirectionAt(this)) {
5060 : return nullptr;
5061 : } else {
5062 : return best;
5063 : }
5064 186 : }
5065 :
5066 :
5067 : NBEdge*
5068 47 : NBEdge::guessOpposite(bool reguess) {
5069 : NBEdge* opposite = nullptr;
5070 47 : if (getNumLanes() > 0) {
5071 : NBEdge::Lane& lastLane = myLanes.back();
5072 47 : const double lastWidth = getLaneWidth(getNumLanes() - 1);
5073 47 : if (lastLane.oppositeID == "" || reguess) {
5074 76 : for (NBEdge* cand : getToNode()->getOutgoingEdges()) {
5075 44 : if (cand->getToNode() == getFromNode() && !cand->getLanes().empty()) {
5076 : const NBEdge::Lane& candLastLane = cand->getLanes().back();
5077 48 : if (candLastLane.oppositeID == "" || candLastLane.oppositeID == getLaneID(getNumLanes() - 1)) {
5078 30 : const double lastWidthCand = cand->getLaneWidth(cand->getNumLanes() - 1);
5079 : // in sharp corners, the difference may be higher
5080 : // factor (sqrt(2) for 90 degree corners
5081 30 : const double threshold = 1.42 * 0.5 * (lastWidth + lastWidthCand) + 0.5;
5082 60 : const double distance = VectorHelper<double>::maxValue(lastLane.shape.distances(cand->getLanes().back().shape));
5083 : //std::cout << " distance=" << distance << " threshold=" << threshold << " distances=" << toString(lastLane.shape.distances(cand->getLanes().back().shape)) << "\n";
5084 30 : if (distance < threshold) {
5085 : opposite = cand;
5086 : }
5087 : }
5088 : }
5089 : }
5090 32 : if (opposite != nullptr) {
5091 56 : lastLane.oppositeID = opposite->getLaneID(opposite->getNumLanes() - 1);
5092 : }
5093 : }
5094 : }
5095 47 : return opposite;
5096 : }
5097 :
5098 : double
5099 0 : NBEdge::getDistancAt(double pos) const {
5100 : // negative values of myDistances indicate descending kilometrage
5101 0 : return fabs(myDistance + pos);
5102 : }
5103 :
5104 : /****************************************************************************/
|