Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2025 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 568460 : NBEdge::Connection::getInternalLaneID() const {
93 1136920 : return id + "_" + toString(internalLaneIndex);
94 : }
95 :
96 :
97 : std::string
98 86220 : NBEdge::Connection::getInternalViaLaneID() const {
99 172440 : return viaID + "_" + toString(internalViaLaneIndex);
100 : }
101 :
102 :
103 : std::string
104 348 : NBEdge::Connection::getDescription(const NBEdge* parent) const {
105 2088 : return (Named::getIDSecure(parent) + "_" + toString(fromLane) + "->" + Named::getIDSecure(toEdge) + "_" + toString(toLane)
106 1044 : + (permissions == SVC_UNSPECIFIED ? "" : " (" + getVehicleClassNames(permissions) + ")"));
107 : }
108 :
109 :
110 699471 : NBEdge::Connection::Connection(int fromLane_, NBEdge* toEdge_, int toLane_, const bool mayDefinitelyPass_) :
111 699471 : fromLane(fromLane_),
112 699471 : toEdge(toEdge_),
113 699471 : toLane(toLane_),
114 699471 : mayDefinitelyPass(mayDefinitelyPass_),
115 699471 : customLength(myDefaultConnectionLength),
116 1398837 : id(toEdge_ == nullptr ? "" : toEdge->getFromNode()->getID()) {
117 699471 : }
118 :
119 :
120 158098 : NBEdge::Lane::Lane(NBEdge* e, const std::string& origID_) :
121 158098 : speed(e->getSpeed()),
122 158098 : friction(e->getFriction()),
123 158098 : permissions(SVCAll),
124 158098 : preferred(0),
125 158098 : changeLeft(SVCAll),
126 158098 : changeRight(SVCAll),
127 158098 : endOffset(e->getEndOffset()),
128 158098 : laneStopOffset(e->getEdgeStopOffset()),
129 158098 : width(e->getLaneWidth()),
130 158098 : accelRamp(false),
131 316196 : connectionsDone(false) {
132 158098 : if (origID_ != "") {
133 34112 : setParameter(SUMO_PARAM_ORIGID, origID_);
134 : }
135 158098 : }
136 :
137 :
138 : /* -------------------------------------------------------------------------
139 : * NBEdge::ToEdgeConnectionsAdder-methods
140 : * ----------------------------------------------------------------------- */
141 : void
142 428995 : NBEdge::ToEdgeConnectionsAdder::execute(const int lane, const int virtEdge) {
143 : // check
144 : assert((int)myTransitions.size() > virtEdge);
145 : // get the approached edge
146 428995 : 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 428995 : if (i != myConnections.end()) {
153 : // if there were already lanes assigned, get them
154 338415 : lanes = (*i).second;
155 : }
156 :
157 : // check whether the current lane was already used to connect the currently
158 : // regarded approached edge
159 428995 : std::vector<int>::iterator j = std::find(lanes.begin(), lanes.end(), lane);
160 428995 : if (j == lanes.end()) {
161 : // if not, add it to the list
162 101257 : lanes.push_back(lane);
163 : }
164 : // set information about connecting lanes
165 428995 : myConnections[succEdge] = lanes;
166 428995 : }
167 :
168 :
169 :
170 : /* -------------------------------------------------------------------------
171 : * NBEdge::MainDirections-methods
172 : * ----------------------------------------------------------------------- */
173 46243 : 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 46243 : const NBEdge* straight = nullptr;
176 136883 : for (const NBEdge* const out : outgoing) {
177 90640 : const SVCPermissions outPerms = out->getPermissions();
178 90821 : for (const int l : availableLanes) {
179 90707 : if ((parent->myLanes[l].permissions & outPerms) != 0) {
180 90526 : if (straight == nullptr || sorter(out, straight)) {
181 68327 : straight = out;
182 : }
183 : break;
184 : }
185 : }
186 : }
187 46243 : if (straight == nullptr) {
188 4109 : return;
189 : }
190 46195 : 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 46195 : 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 46195 : if (NBNode::isTrafficLight(to->getType()) &&
201 4364 : (straightestDir == LinkDirection::STRAIGHT || straightestDir == LinkDirection::PARTLEFT || straightestDir == LinkDirection::PARTRIGHT)) {
202 4061 : myDirs.push_back(MainDirections::Direction::FORWARD);
203 4061 : return;
204 : }
205 42134 : if (outgoing[0]->getJunctionPriority(to) == 1) {
206 22589 : myDirs.push_back(MainDirections::Direction::RIGHTMOST);
207 : }
208 : // check whether the left turn has a higher priority
209 42134 : 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 22811 : if (outgoing.back()->getPriority() > straight->getPriority() ||
214 : outgoing.back()->getNumLanes() > straight->getNumLanes()) {
215 1237 : 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 42134 : if (straight->getJunctionPriority(to) == 1 && to->getDirection(parent, straight) == LinkDirection::STRAIGHT) {
221 20398 : myDirs.push_back(MainDirections::Direction::FORWARD);
222 : }
223 0 : }
224 :
225 :
226 46243 : NBEdge::MainDirections::~MainDirections() {}
227 :
228 :
229 : bool
230 46195 : NBEdge::MainDirections::empty() const {
231 46195 : return myDirs.empty();
232 : }
233 :
234 :
235 : bool
236 172824 : NBEdge::MainDirections::includes(Direction d) const {
237 172824 : 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 175671 : NBEdge::connections_relative_edgelane_sorter::operator()(const Connection& c1, const Connection& c2) const {
246 175671 : if (c1.toEdge != c2.toEdge) {
247 135976 : return NBContHelper::relative_outgoing_edge_sorter(myEdge)(c1.toEdge, c2.toEdge);
248 : }
249 39695 : return c1.toLane < c2.toLane;
250 : }
251 :
252 :
253 : /* -------------------------------------------------------------------------
254 : * NBEdge-methods
255 : * ----------------------------------------------------------------------- */
256 15568 : 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 15568 : LaneSpreadFunction spread, const std::string& streetName) :
260 15568 : Named(StringUtils::convertUmlaute(id)),
261 15568 : myStep(EdgeBuildingStep::INIT),
262 15568 : myType(StringUtils::convertUmlaute(type)),
263 15568 : myFrom(from), myTo(to),
264 15568 : myStartAngle(0), myEndAngle(0), myTotalAngle(0),
265 15568 : myPriority(priority), mySpeed(speed), myFriction(friction),
266 15568 : myDistance(0),
267 15568 : myTurnDestination(nullptr),
268 15568 : myPossibleTurnDestination(nullptr),
269 15568 : myFromJunctionPriority(-1), myToJunctionPriority(-1),
270 15568 : myLaneSpreadFunction(spread), myEndOffset(endOffset),
271 15568 : myLaneWidth(laneWidth),
272 15568 : myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
273 15568 : myAmInTLS(false), myAmMacroscopicConnector(false),
274 15568 : myStreetName(streetName),
275 15568 : mySignalPosition(Position::INVALID),
276 15568 : mySignalNode(nullptr),
277 15568 : myIsOffRamp(false),
278 15568 : myIsBidi(false),
279 46704 : myIndex(-1) {
280 15568 : init(nolanes, false, "");
281 15568 : }
282 :
283 :
284 102139 : 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 102139 : bool tryIgnoreNodePositions) :
292 102139 : Named(StringUtils::convertUmlaute(id)),
293 102139 : myStep(EdgeBuildingStep::INIT),
294 102140 : myType(StringUtils::convertUmlaute(type)),
295 102139 : myFrom(from), myTo(to),
296 102139 : myStartAngle(0), myEndAngle(0), myTotalAngle(0),
297 102139 : myPriority(priority), mySpeed(speed), myFriction(friction),
298 102139 : myDistance(0),
299 102139 : myTurnDestination(nullptr),
300 102139 : myPossibleTurnDestination(nullptr),
301 102139 : myFromJunctionPriority(-1), myToJunctionPriority(-1),
302 102139 : myGeom(geom), myLaneSpreadFunction(spread), myEndOffset(endOffset),
303 102139 : myLaneWidth(laneWidth),
304 102139 : myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
305 102139 : myAmInTLS(false), myAmMacroscopicConnector(false),
306 102139 : myStreetName(streetName),
307 102139 : mySignalPosition(Position::INVALID),
308 102139 : mySignalNode(nullptr),
309 102139 : myIsOffRamp(false),
310 102139 : myIsBidi(false),
311 306417 : myIndex(-1) {
312 102139 : init(nolanes, tryIgnoreNodePositions, origID);
313 102151 : }
314 :
315 :
316 2613 : NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to, const NBEdge* tpl, const PositionVector& geom, int numLanes) :
317 2613 : Named(StringUtils::convertUmlaute(id)),
318 2613 : myStep(EdgeBuildingStep::INIT),
319 2613 : myType(tpl->getTypeID()),
320 2613 : myFrom(from), myTo(to),
321 2613 : myStartAngle(0), myEndAngle(0), myTotalAngle(0),
322 2613 : myPriority(tpl->getPriority()), mySpeed(tpl->getSpeed()),
323 2613 : myFriction(tpl->getFriction()),
324 2613 : myDistance(0),
325 2613 : myTurnDestination(nullptr),
326 2613 : myPossibleTurnDestination(nullptr),
327 2613 : myFromJunctionPriority(-1), myToJunctionPriority(-1),
328 : myGeom(geom),
329 2613 : myLaneSpreadFunction(tpl->getLaneSpreadFunction()),
330 2613 : myEndOffset(tpl->getEndOffset()),
331 2613 : myEdgeStopOffset(tpl->getEdgeStopOffset()),
332 2613 : myLaneWidth(tpl->getLaneWidth()),
333 2613 : myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
334 2613 : myAmInTLS(false),
335 2613 : myAmMacroscopicConnector(false),
336 2613 : myStreetName(tpl->getStreetName()),
337 2613 : mySignalPosition(to == tpl->myTo ? tpl->mySignalPosition : Position::INVALID),
338 2613 : mySignalNode(to == tpl->myTo ? tpl->mySignalNode : nullptr),
339 2613 : myIsOffRamp(false),
340 2613 : myIsBidi(tpl->myIsBidi),
341 5226 : myIndex(-1) {
342 3674 : init(numLanes > 0 ? numLanes : tpl->getNumLanes(), myGeom.size() > 0, "");
343 6425 : for (int i = 0; i < getNumLanes(); i++) {
344 3812 : const int tplIndex = MIN2(i, tpl->getNumLanes() - 1);
345 3812 : setSpeed(i, tpl->getLaneSpeed(tplIndex));
346 3812 : setFriction(i, tpl->getLaneFriction(tplIndex));
347 3812 : setPermissions(tpl->getPermissions(tplIndex), i);
348 3812 : setLaneWidth(i, tpl->myLanes[tplIndex].width);
349 3812 : setLaneType(i, tpl->myLanes[tplIndex].type);
350 3812 : myLanes[i].updateParameters(tpl->myLanes[tplIndex].getParametersMap());
351 3812 : if (to == tpl->myTo) {
352 1455 : setEndOffset(i, tpl->myLanes[tplIndex].endOffset);
353 1455 : setEdgeStopOffset(i, tpl->myLanes[tplIndex].laneStopOffset);
354 : }
355 : }
356 2613 : if (tpl->myLoadedLength > 0 && to == tpl->getFromNode() && from == tpl->getToNode() && geom == tpl->getGeometry().reverse()) {
357 3 : myLoadedLength = tpl->myLoadedLength;
358 : }
359 2613 : updateParameters(tpl->getParametersMap());
360 2613 : }
361 :
362 :
363 2154 : NBEdge::NBEdge() :
364 : Named("DUMMY"),
365 2154 : myStep(EdgeBuildingStep::INIT),
366 2154 : myFrom(nullptr), myTo(nullptr),
367 2154 : myStartAngle(0), myEndAngle(0), myTotalAngle(0),
368 2154 : myPriority(0), mySpeed(0), myFriction(UNSPECIFIED_FRICTION),
369 2154 : myDistance(0),
370 2154 : myTurnDestination(nullptr),
371 2154 : myPossibleTurnDestination(nullptr),
372 2154 : myFromJunctionPriority(-1), myToJunctionPriority(-1),
373 2154 : myLaneSpreadFunction(LaneSpreadFunction::RIGHT),
374 2154 : myEndOffset(0),
375 2154 : myEdgeStopOffset(StopOffset()),
376 2154 : myLaneWidth(0),
377 2154 : myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
378 2154 : myAmInTLS(false),
379 2154 : myAmMacroscopicConnector(false),
380 2154 : mySignalPosition(Position::INVALID),
381 6462 : mySignalNode(nullptr) {
382 2154 : }
383 :
384 :
385 : void
386 2123 : 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 2123 : if (myFrom != from) {
393 10 : myFrom->removeEdge(this, false);
394 : }
395 2123 : if (myTo != to) {
396 11 : myTo->removeEdge(this, false);
397 : }
398 2123 : myType = StringUtils::convertUmlaute(type);
399 2123 : myFrom = from;
400 2123 : myTo = to;
401 2123 : myPriority = priority;
402 : //?myTurnDestination(0),
403 : //?myFromJunctionPriority(-1), myToJunctionPriority(-1),
404 : myGeom = geom;
405 2123 : myLaneSpreadFunction = spread;
406 2123 : myLoadedLength = UNSPECIFIED_LOADED_LENGTH;
407 2123 : 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 2123 : const std::vector<Lane> oldLanes = myLanes;
414 4246 : init(nolanes, tryIgnoreNodePositions, oldLanes.empty() ? "" : oldLanes[0].getParameter(SUMO_PARAM_ORIGID));
415 4338 : for (int i = 0; i < (int)nolanes; ++i) {
416 2215 : PositionVector newShape = myLanes[i].shape;
417 2215 : myLanes[i] = oldLanes[MIN2(i, (int)oldLanes.size() - 1)];
418 : myLanes[i].shape = newShape;
419 2215 : }
420 : // however, if the new edge defaults are explicityly given, they override the old settings
421 2123 : if (endOffset != UNSPECIFIED_OFFSET) {
422 1 : setEndOffset(-1, endOffset);
423 : }
424 2123 : if (laneWidth != UNSPECIFIED_WIDTH) {
425 2 : setLaneWidth(-1, laneWidth);
426 : }
427 2123 : if (speed != UNSPECIFIED_SPEED) {
428 28 : setSpeed(-1, speed);
429 : }
430 2123 : if (friction != UNSPECIFIED_FRICTION) {
431 0 : setFriction(-1, friction);
432 : }
433 2123 : }
434 :
435 :
436 : void
437 6730 : NBEdge::reinitNodes(NBNode* from, NBNode* to) {
438 : // connections may still be valid
439 6730 : if (from == nullptr || to == nullptr) {
440 0 : throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
441 : }
442 6730 : if (myFrom != from) {
443 3439 : myFrom->removeEdge(this, false);
444 : }
445 6730 : if (myTo != to) {
446 3269 : 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 6730 : if (myFrom != from) {
451 3439 : myFrom = from;
452 3439 : myFrom->addOutgoingEdge(this);
453 : }
454 6730 : if (myTo != to) {
455 3269 : myTo = to;
456 3269 : myTo->addIncomingEdge(this);
457 : }
458 6730 : computeAngle();
459 6730 : }
460 :
461 :
462 : void
463 122443 : NBEdge::init(int noLanes, bool tryIgnoreNodePositions, const std::string& origID) {
464 122443 : if (noLanes == 0) {
465 0 : throw ProcessError(TLF("Edge '%' needs at least one lane.", myID));
466 : }
467 122443 : if (myFrom == nullptr || myTo == nullptr) {
468 0 : throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
469 : }
470 122443 : 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 122442 : if (myFrom->getID() < myTo->getID()) {
479 63204 : PositionVector reverse = myGeom.reverse();
480 63204 : reverse.removeDoublePoints(POSITION_EPS, true);
481 126408 : myGeom = reverse.reverse();
482 63204 : } else {
483 59238 : myGeom.removeDoublePoints(POSITION_EPS, true);
484 : }
485 :
486 122442 : if (!tryIgnoreNodePositions || myGeom.size() < 2) {
487 41547 : if (myGeom.size() == 0) {
488 41240 : myGeom.push_back(myFrom->getPosition());
489 41240 : 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 122442 : if (myGeom.size() < 2) {
496 : myGeom.clear();
497 0 : myGeom.push_back(myFrom->getPosition());
498 0 : myGeom.push_back(myTo->getPosition());
499 : }
500 122442 : 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 122442 : myGeom.ensureMinLength(gPrecision);
507 : //
508 122442 : myFrom->addOutgoingEdge(this);
509 122442 : myTo->addIncomingEdge(this);
510 : // prepare container
511 : assert(myGeom.size() >= 2);
512 122442 : myLength = myGeom.length();
513 122442 : 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 276494 : for (int i = 0; i < noLanes; i++) {
528 308104 : myLanes.push_back(Lane(this, origID));
529 : }
530 122442 : computeLaneShapes();
531 122442 : 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 122442 : }
545 :
546 :
547 242742 : NBEdge::~NBEdge() {}
548 :
549 :
550 : // ----------- Applying offset
551 : void
552 68280 : NBEdge::reshiftPosition(double xoff, double yoff) {
553 68280 : myGeom.add(xoff, yoff, 0);
554 156425 : for (Lane& lane : myLanes) {
555 88145 : lane.customShape.add(xoff, yoff, 0);
556 : }
557 68280 : computeLaneShapes(); // old shapes are dubious if computed with large coordinates
558 74706 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
559 6426 : (*i).customShape.add(xoff, yoff, 0);
560 : }
561 : if (mySignalPosition != Position::INVALID) {
562 : mySignalPosition.add(xoff, yoff);
563 : }
564 68280 : myFromBorder.add(xoff, yoff, 0);
565 68280 : myToBorder.add(xoff, yoff, 0);
566 68280 : computeEdgeShape();
567 68280 : computeAngle(); // update angles because they are numerically sensitive (especially where based on centroids)
568 68280 : }
569 :
570 :
571 : void
572 102259 : NBEdge::roundGeometry() {
573 102259 : myGeom.round(gPrecision);
574 234733 : for (Lane& lane : myLanes) {
575 132474 : lane.customShape.round(gPrecision);
576 : }
577 174069 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
578 71810 : (*i).customShape.round(gPrecision);
579 : }
580 102259 : }
581 :
582 :
583 : void
584 102259 : NBEdge::roundSpeed() {
585 102259 : mySpeed = roundDecimalToEven(mySpeed, gPrecision);
586 : // lane speeds are not used for computation but are compared to mySpeed in hasLaneSpecificSpeed
587 234733 : for (Lane& l : myLanes) {
588 132474 : l.speed = roundDecimalToEven(l.speed, gPrecision);
589 : }
590 102259 : }
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 2352 : NBEdge::getInnerGeometry() const {
614 2352 : return myGeom.getSubpartByIndex(1, (int)myGeom.size() - 2);
615 : }
616 :
617 :
618 : bool
619 103947 : NBEdge::hasDefaultGeometry() const {
620 103947 : return myGeom.size() == 2 && hasDefaultGeometryEndpoints();
621 : }
622 :
623 :
624 : bool
625 63123 : NBEdge::hasDefaultGeometryEndpoints() const {
626 118159 : return myGeom.front().almostSame(myFrom->getPosition(), 0.01) &&
627 55036 : myGeom.back().almostSame(myTo->getPosition(), 0.01);
628 : }
629 :
630 :
631 : bool
632 179646 : NBEdge::hasDefaultGeometryEndpointAtNode(const NBNode* node) const {
633 : // do not extend past the node position
634 179646 : 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 3547 : NBEdge::getEndpointAtNode(const NBNode* node) const {
644 3547 : 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 4493 : NBEdge::setGeometry(const PositionVector& s, bool inner) {
661 4493 : Position begin = myGeom.front(); // may differ from node position
662 4493 : Position end = myGeom.back(); // may differ from node position
663 : myGeom = s;
664 4493 : if (inner) {
665 0 : myGeom.insert(myGeom.begin(), begin);
666 0 : myGeom.push_back(end);
667 : }
668 : // ensure non-zero length (see ::init)
669 4493 : 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 4493 : computeLaneShapes();
675 4493 : computeAngle();
676 4493 : myLength = myGeom.length();
677 4493 : }
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 242012 : NBEdge::setNodeBorder(const NBNode* node, const Position& p, const Position& p2, bool rectangularCut) {
719 242012 : PositionVector border;
720 242012 : if (rectangularCut) {
721 : const double extend = 100;
722 3368 : border = myGeom.getOrthogonal(p, extend, node == myTo);
723 : } else {
724 240328 : border.push_back(p);
725 240328 : border.push_back(p2);
726 : }
727 242012 : if (border.size() == 2) {
728 242012 : border.extrapolate2D(getTotalWidth());
729 242012 : 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 242012 : }
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 32139 : NBEdge::resetNodeBorder(const NBNode* node) {
762 32139 : if (node == myFrom) {
763 : myFromBorder.clear();
764 : } else {
765 : assert(node == myTo);
766 : myToBorder.clear();
767 : }
768 32139 : }
769 :
770 :
771 : bool
772 5185546 : NBEdge::isBidiRail(bool ignoreSpread) const {
773 5185546 : return (isRailway(getPermissions())
774 164696 : && (ignoreSpread || myLaneSpreadFunction == LaneSpreadFunction::CENTER)
775 163092 : && myPossibleTurnDestination != nullptr
776 120496 : && myPossibleTurnDestination->myPossibleTurnDestination == this
777 113794 : && (ignoreSpread || myPossibleTurnDestination->getLaneSpreadFunction() == LaneSpreadFunction::CENTER)
778 113723 : && isRailway(myPossibleTurnDestination->getPermissions())
779 10371092 : && myPossibleTurnDestination->getGeometry().reverse() == getGeometry());
780 : }
781 :
782 :
783 : bool
784 4825209 : NBEdge::isBidiEdge(bool checkPotential) const {
785 4825209 : return myPossibleTurnDestination != nullptr
786 4500563 : && myPossibleTurnDestination->myPossibleTurnDestination == this
787 4264848 : && (myIsBidi || myPossibleTurnDestination->myIsBidi || checkPotential)
788 5626 : && myPossibleTurnDestination->getToNode() == getFromNode()
789 5626 : && myPossibleTurnDestination->getLaneSpreadFunction() == myLaneSpreadFunction
790 : // geometry check a) full overlap geometry
791 4830835 : && ((myLaneSpreadFunction == LaneSpreadFunction::CENTER
792 8006 : && (myPossibleTurnDestination->getGeometry().reverse() == getGeometry()
793 0 : || (checkPotential && getGeometry().size() == 2 && myPossibleTurnDestination->getGeometry().size() == 2)))
794 : // b) TWLT (Two-Way-Left-Turn-lane)
795 4825209 : || (myLanes.back().shape.reverse().almostSame(myPossibleTurnDestination->myLanes.back().shape, POSITION_EPS))
796 4825209 : );
797 :
798 : }
799 :
800 :
801 : bool
802 2690 : NBEdge::isRailDeadEnd() const {
803 2690 : if (!isRailway(getPermissions())) {
804 : return false;
805 : }
806 4184 : for (NBEdge* out : myTo->getOutgoingEdges()) {
807 7995 : if (isRailway(out->getPermissions()) &&
808 3933 : out != getTurnDestination(true)) {
809 : return true;
810 : }
811 : }
812 : return true;
813 : }
814 :
815 :
816 : PositionVector
817 327622 : NBEdge::cutAtIntersection(const PositionVector& old) const {
818 : PositionVector shape = old;
819 655244 : shape = startShapeAt(shape, myFrom, myFromBorder);
820 : #ifdef DEBUG_CUT_LANES
821 : if (DEBUGCOND) {
822 : std::cout << getID() << " cutFrom=" << shape << "\n";
823 : }
824 : #endif
825 327622 : if (shape.size() < 2) {
826 : // only keep the last snippet
827 365 : const double oldLength = old.length();
828 730 : 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 655244 : 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 327622 : if (shape.length() < POSITION_EPS) {
843 399 : if (old.length() < 2 * POSITION_EPS) {
844 : shape = old;
845 : } else {
846 394 : const double midpoint = old.length() / 2;
847 : // EPS*2 because otherwhise shape has only a single point
848 788 : 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 327223 : if (DEG2RAD(135) < fabs(GeomHelper::angleDiff(shape.beginEndAngle(), old.beginEndAngle()))) {
861 : // eliminate intermediate points
862 29207 : PositionVector tmp;
863 29207 : tmp.push_back(shape[0]);
864 29207 : tmp.push_back(shape[-1]);
865 : shape = tmp;
866 29207 : if (tmp.length() < POSITION_EPS) {
867 : // fall back to original shape
868 65 : if (old.length() < 2 * POSITION_EPS) {
869 : shape = old;
870 : } else {
871 65 : const double midpoint = old.length() / 2;
872 : // EPS*2 because otherwhise shape has only a single point
873 130 : 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 29142 : const double midpoint = shape.length() / 2;
884 : // cut to size and reverse
885 58284 : shape = shape.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
886 29142 : if (shape.length() < POSITION_EPS) {
887 : assert(false);
888 : // the shape has a sharp turn near the midpoint
889 : }
890 58284 : 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 29207 : const double z = (shape[0].z() + shape[1].z()) / 2;
899 29207 : shape[0].setz(z);
900 29207 : shape[1].setz(z);
901 29207 : }
902 : }
903 327622 : return shape;
904 0 : }
905 :
906 :
907 : void
908 207175 : NBEdge::computeEdgeShape(double smoothElevationThreshold) {
909 207175 : 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 476605 : for (int i = 0; i < (int)myLanes.size(); i++) {
936 538860 : myLanes[i].shape = cutAtIntersection(myLanes[i].shape);
937 : }
938 : // recompute edge's length as the average of lane lengths
939 : double avgLength = 0;
940 476605 : for (int i = 0; i < (int)myLanes.size(); i++) {
941 269430 : avgLength += myLanes[i].shape.length();
942 : }
943 207175 : myLength = avgLength / (double) myLanes.size();
944 207175 : computeAngle(); // update angles using the finalized node and lane shapes
945 207175 : }
946 :
947 :
948 : PositionVector
949 655572 : NBEdge::startShapeAt(const PositionVector& laneShape, const NBNode* startNode, PositionVector nodeShape) {
950 655572 : if (nodeShape.size() == 0) {
951 183372 : nodeShape = startNode->getShape();
952 183372 : nodeShape.closePolygon();
953 : }
954 : PositionVector lb = laneShape;
955 655572 : lb.extrapolate2D(100.0);
956 655572 : if (nodeShape.intersects(laneShape)) {
957 : // shape intersects directly
958 469423 : std::vector<double> pbv = laneShape.intersectsAtLengths2D(nodeShape);
959 : assert(pbv.size() > 0);
960 : // ensure that the subpart has at least two points
961 469423 : double pb = MIN2(laneShape.length2D() - POSITION_EPS - NUMERICAL_EPS, VectorHelper<double>::maxValue(pbv));
962 469423 : if (pb < 0) {
963 : return laneShape;
964 : }
965 469406 : PositionVector ns = laneShape.getSubpart2D(pb, laneShape.length2D());
966 : //PositionVector ns = pb < (laneShape.length() - POSITION_EPS) ? laneShape.getSubpart2D(pb, laneShape.length()) : laneShape;
967 469406 : 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 469406 : 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 655572 : } else if (nodeShape.intersects(lb)) {
977 : // extension of first segment intersects
978 66196 : std::vector<double> pbv = lb.intersectsAtLengths2D(nodeShape);
979 : assert(pbv.size() > 0);
980 : double pb = VectorHelper<double>::maxValue(pbv);
981 : assert(pb >= 0);
982 66196 : PositionVector result = laneShape.getSubpartByIndex(1, (int)laneShape.size() - 1);
983 66196 : Position np = lb.positionAtOffset2D(pb);
984 66196 : 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 66196 : 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 66196 : 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 66196 : } 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 655572 : }
1006 :
1007 :
1008 : const PositionVector&
1009 2472065 : NBEdge::getLaneShape(int i) const {
1010 2472065 : return myLanes[i].shape;
1011 : }
1012 :
1013 :
1014 : void
1015 1217 : NBEdge::setLaneSpreadFunction(LaneSpreadFunction spread) {
1016 1217 : myLaneSpreadFunction = spread;
1017 1217 : }
1018 :
1019 :
1020 : LaneSpreadFunction
1021 271281 : NBEdge::getLaneSpreadFunction() const {
1022 271281 : return myLaneSpreadFunction;
1023 : }
1024 :
1025 :
1026 : void
1027 702 : NBEdge::addGeometryPoint(int index, const Position& p) {
1028 702 : if (index >= 0) {
1029 351 : myGeom.insert(myGeom.begin() + index, p);
1030 : } else {
1031 351 : myGeom.insert(myGeom.end() + index, p);
1032 : }
1033 702 : }
1034 :
1035 :
1036 : void
1037 462 : NBEdge::reduceGeometry(const double minDist) {
1038 : // attempt symmetrical removal for forward and backward direction
1039 : // (very important for bidiRail)
1040 462 : if (myFrom->getID() < myTo->getID()) {
1041 232 : PositionVector reverse = myGeom.reverse();
1042 232 : reverse.removeDoublePoints(minDist, true, 0, 0, true);
1043 464 : myGeom = reverse.reverse();
1044 628 : for (Lane& lane : myLanes) {
1045 792 : reverse = lane.customShape.reverse();
1046 396 : reverse.removeDoublePoints(minDist, true, 0, 0, true);
1047 792 : lane.customShape = reverse.reverse();
1048 : }
1049 232 : } else {
1050 230 : myGeom.removeDoublePoints(minDist, true, 0, 0, true);
1051 578 : for (Lane& lane : myLanes) {
1052 348 : lane.customShape.removeDoublePoints(minDist, true, 0, 0, true);
1053 : }
1054 : }
1055 462 : }
1056 :
1057 :
1058 : void
1059 70173 : NBEdge::checkGeometry(const double maxAngle, bool fixAngle, const double minRadius, bool fix, bool silent) {
1060 70173 : if (myGeom.size() < 3) {
1061 35963 : 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 202255 : for (int i = 0; i < (int)myGeom.size() - 1; ++i) {
1067 168001 : angles.push_back(myGeom.angleAt2D(i));
1068 : //std::cout << " " << angles.back();
1069 : }
1070 : //std::cout << "\n relative angles: ";
1071 34254 : NBEdge* bidi = const_cast<NBEdge*>(getBidiEdge());
1072 167824 : for (int i = 0; i < (int)angles.size() - 1; ++i) {
1073 133614 : const double relAngle = fabs(GeomHelper::angleDiff(angles[i], angles[i + 1]));
1074 : //std::cout << relAngle << " ";
1075 133614 : if (maxAngle > 0 && relAngle > maxAngle) {
1076 120 : 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 44 : return;
1085 119 : } else if (!silent) {
1086 357 : WRITE_WARNINGF(TL("Found angle of % degrees at edge '%', segment %."), RAD2DEG(relAngle), getID(), i);
1087 : }
1088 : }
1089 133613 : if (relAngle < DEG2RAD(1)) {
1090 39107 : continue;
1091 : }
1092 94506 : if (i == 0 || i == (int)angles.size() - 2) {
1093 : const bool start = i == 0;
1094 38694 : const double dist = (start ? myGeom[0].distanceTo2D(myGeom[1]) : myGeom[-2].distanceTo2D(myGeom[-1]));
1095 38694 : const double r = tan(0.5 * (M_PI - relAngle)) * dist;
1096 : //std::cout << (start ? " start" : " end") << " length=" << dist << " radius=" << r << " ";
1097 38694 : if (minRadius > 0 && r < minRadius) {
1098 564 : if (fix) {
1099 129 : WRITE_MESSAGEF(TL("Removing sharp turn with radius % at the % of edge '%'."),
1100 : toString(r), start ? TL("start") : TL("end"), getID());
1101 43 : myGeom.erase(myGeom.begin() + (start ? 1 : i + 1));
1102 43 : if (bidi != nullptr) {
1103 0 : bidi->myGeom = myGeom.reverse();
1104 : }
1105 43 : checkGeometry(maxAngle, fixAngle, minRadius, fix, silent);
1106 43 : return;
1107 521 : } else if (!silent) {
1108 1548 : 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 34254 : }
1116 :
1117 :
1118 : // ----------- Setting and getting connections
1119 : bool
1120 77133 : NBEdge::addEdge2EdgeConnection(NBEdge* dest, bool overrideRemoval, SVCPermissions permissions) {
1121 77133 : 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 77133 : 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 77028 : } else if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(dest)) == myConnections.end()) {
1135 129058 : myConnections.push_back(Connection(-1, dest, -1));
1136 64529 : myConnections.back().permissions = permissions;
1137 : }
1138 77133 : if (overrideRemoval) {
1139 : // override earlier delete decision
1140 906 : for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end();) {
1141 320 : if (it->toEdge == dest) {
1142 2 : it = myConnectionsToDelete.erase(it);
1143 : } else {
1144 : it++;
1145 : }
1146 : }
1147 : }
1148 77133 : if (myStep < EdgeBuildingStep::EDGE2EDGES) {
1149 36318 : myStep = EdgeBuildingStep::EDGE2EDGES;
1150 : }
1151 : return true;
1152 : }
1153 :
1154 :
1155 : bool
1156 76663 : 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 76663 : 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 76663 : if (myTo != dest->myFrom) {
1181 : return false;
1182 : }
1183 75935 : if (!addEdge2EdgeConnection(dest)) {
1184 : return false;
1185 : }
1186 75935 : return setConnection(from, dest, toLane, type, mayUseSameDestination, mayDefinitelyPass, keepClear, contPos, visibility, speed, friction, length,
1187 75935 : customShape, uncontrolled, permissions, indirectLeft, edgeType, changeLeft, changeRight, postProcess);
1188 : }
1189 :
1190 :
1191 : bool
1192 1456 : NBEdge::addLane2LaneConnections(int fromLane,
1193 : NBEdge* dest, int toLane,
1194 : int no, Lane2LaneInfoType type,
1195 : bool invalidatePrevious,
1196 : bool mayDefinitelyPass) {
1197 1456 : if (invalidatePrevious) {
1198 788 : invalidateConnections(true);
1199 : }
1200 : bool ok = true;
1201 3352 : for (int i = 0; i < no && ok; i++) {
1202 3792 : ok &= addLane2LaneConnection(fromLane + i, dest, toLane + i, type, false, mayDefinitelyPass);
1203 : }
1204 1456 : return ok;
1205 : }
1206 :
1207 :
1208 : bool
1209 221662 : 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 221662 : 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 221662 : if (!mayUseSameDestination && hasConnectionTo(destEdge, destLane)) {
1240 : return false;
1241 : }
1242 210428 : if (find_if(myConnections.begin(), myConnections.end(), connections_finder(lane, destEdge, destLane)) != myConnections.end()) {
1243 : return true;
1244 : }
1245 210390 : 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 676058 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
1251 465679 : if ((*i).toEdge == destEdge && ((*i).fromLane == -1 || (*i).toLane == -1)) {
1252 160867 : 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 160833 : permissions = (*i).permissions;
1256 : }
1257 160867 : i = myConnections.erase(i);
1258 : } else {
1259 : ++i;
1260 : }
1261 : }
1262 420758 : myConnections.push_back(Connection(lane, destEdge, destLane));
1263 210379 : if (mayDefinitelyPass) {
1264 27 : myConnections.back().mayDefinitelyPass = true;
1265 : }
1266 210379 : myConnections.back().keepClear = keepClear;
1267 210379 : myConnections.back().contPos = contPos;
1268 210379 : myConnections.back().visibility = visibility;
1269 210379 : myConnections.back().permissions = permissions;
1270 210379 : myConnections.back().indirectLeft = indirectLeft;
1271 210379 : myConnections.back().edgeType = edgeType;
1272 210379 : myConnections.back().changeLeft = changeLeft;
1273 210379 : myConnections.back().changeRight = changeRight;
1274 210379 : myConnections.back().speed = speed;
1275 210379 : myConnections.back().friction = friction;
1276 210379 : myConnections.back().customLength = length;
1277 : myConnections.back().customShape = customShape;
1278 210379 : myConnections.back().uncontrolled = uncontrolled;
1279 210379 : if (type == Lane2LaneInfoType::USER) {
1280 3032 : myStep = EdgeBuildingStep::LANES2LANES_USER;
1281 : } else {
1282 : // check whether we have to take another look at it later
1283 207347 : if (type == Lane2LaneInfoType::COMPUTED) {
1284 : // yes, the connection was set using an algorithm which requires a recheck
1285 103860 : 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 103487 : if (myStep != EdgeBuildingStep::LANES2LANES_RECHECK) {
1289 74124 : myStep = EdgeBuildingStep::LANES2LANES_DONE;
1290 : }
1291 : }
1292 : }
1293 210379 : 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 3216829 : NBEdge::getConnectionsFromLane(int lane, const NBEdge* to, int toLane) const {
1311 : std::vector<NBEdge::Connection> ret;
1312 14818944 : for (const Connection& c : myConnections) {
1313 11602115 : if ((lane < 0 || c.fromLane == lane)
1314 7371506 : && (to == nullptr || to == c.toEdge)
1315 7355355 : && (toLane < 0 || toLane == c.toLane)) {
1316 7354160 : ret.push_back(c);
1317 : }
1318 : }
1319 3216829 : return ret;
1320 0 : }
1321 :
1322 :
1323 : const NBEdge::Connection&
1324 10791 : NBEdge::getConnection(int fromLane, const NBEdge* to, int toLane) const {
1325 36744 : for (const Connection& c : myConnections) {
1326 36744 : if (c.fromLane == fromLane && c.toEdge == to && c.toLane == toLane) {
1327 10791 : 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 258418 : NBEdge::hasConnectionTo(const NBEdge* destEdge, int destLane, int fromLane) const {
1349 258418 : return destEdge != nullptr && find_if(myConnections.begin(), myConnections.end(), connections_toedgelane_finder(destEdge, destLane, fromLane)) != myConnections.end();
1350 : }
1351 :
1352 :
1353 : bool
1354 884284 : NBEdge::isConnectedTo(const NBEdge* e, const bool ignoreTurnaround) const {
1355 884284 : if (!ignoreTurnaround && (e == myTurnDestination)) {
1356 : return true;
1357 : }
1358 : return
1359 876298 : find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(e))
1360 : !=
1361 : myConnections.end();
1362 :
1363 : }
1364 :
1365 :
1366 : const EdgeVector*
1367 66926 : NBEdge::getConnectedSorted() {
1368 : // check whether connections exist and if not, use edges from the node
1369 : EdgeVector outgoing;
1370 66926 : if (myConnections.size() == 0) {
1371 21462 : outgoing = myTo->getOutgoingEdges();
1372 : } else {
1373 135814 : for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1374 90350 : if (find(outgoing.begin(), outgoing.end(), (*i).toEdge) == outgoing.end()) {
1375 90039 : outgoing.push_back((*i).toEdge);
1376 : }
1377 : }
1378 : }
1379 74014 : for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
1380 7088 : if (it->fromLane < 0 && it->toLane < 0) {
1381 : // found an edge that shall not be connected
1382 7088 : EdgeVector::iterator forbidden = std::find(outgoing.begin(), outgoing.end(), it->toEdge);
1383 7088 : if (forbidden != outgoing.end()) {
1384 : outgoing.erase(forbidden);
1385 : }
1386 : }
1387 : }
1388 : // allocate the sorted container
1389 66926 : int size = (int) outgoing.size();
1390 66926 : EdgeVector* edges = new EdgeVector();
1391 66926 : edges->reserve(size);
1392 185199 : for (EdgeVector::const_iterator i = outgoing.begin(); i != outgoing.end(); i++) {
1393 118273 : NBEdge* outedge = *i;
1394 118273 : if (outedge != nullptr && outedge != myTurnDestination) {
1395 108948 : edges->push_back(outedge);
1396 : }
1397 : }
1398 66926 : std::sort(edges->begin(), edges->end(), NBContHelper::relative_outgoing_edge_sorter(this));
1399 66926 : return edges;
1400 66926 : }
1401 :
1402 :
1403 : EdgeVector
1404 189064 : NBEdge::getConnectedEdges() const {
1405 : EdgeVector ret;
1406 621476 : for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1407 432412 : if (find(ret.begin(), ret.end(), (*i).toEdge) == ret.end()) {
1408 261866 : ret.push_back((*i).toEdge);
1409 : }
1410 : }
1411 189064 : return ret;
1412 0 : }
1413 :
1414 :
1415 : EdgeVector
1416 6688 : NBEdge::getIncomingEdges() const {
1417 : EdgeVector ret;
1418 6688 : const EdgeVector& candidates = myFrom->getIncomingEdges();
1419 22740 : for (EdgeVector::const_iterator i = candidates.begin(); i != candidates.end(); i++) {
1420 16052 : if ((*i)->isConnectedTo(this)) {
1421 14827 : ret.push_back(*i);
1422 : }
1423 : }
1424 6688 : return ret;
1425 0 : }
1426 :
1427 :
1428 : std::vector<int>
1429 513842 : NBEdge::getConnectionLanes(NBEdge* currentOutgoing, bool withBikes) const {
1430 : std::vector<int> ret;
1431 513842 : if (currentOutgoing != myTurnDestination) {
1432 1549735 : for (const Connection& c : myConnections) {
1433 1080598 : if (c.toEdge == currentOutgoing && (withBikes || getPermissions(c.fromLane) != SVC_BICYCLE)) {
1434 418437 : ret.push_back(c.fromLane);
1435 : }
1436 : }
1437 : }
1438 513842 : return ret;
1439 0 : }
1440 :
1441 :
1442 : void
1443 103125 : NBEdge::sortOutgoingConnectionsByAngle() {
1444 103125 : sort(myConnections.begin(), myConnections.end(), connections_relative_edgelane_sorter(this));
1445 103125 : }
1446 :
1447 :
1448 : void
1449 158911 : NBEdge::sortOutgoingConnectionsByIndex() {
1450 158911 : sort(myConnections.begin(), myConnections.end(), connections_sorter);
1451 158911 : }
1452 :
1453 :
1454 : void
1455 0 : NBEdge::remapConnections(const EdgeVector& incoming) {
1456 0 : EdgeVector connected = getConnectedEdges();
1457 0 : for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
1458 0 : NBEdge* inc = *i;
1459 : // We have to do this
1460 0 : inc->myStep = EdgeBuildingStep::EDGE2EDGES;
1461 : // add all connections
1462 0 : for (EdgeVector::iterator j = connected.begin(); j != connected.end(); j++) {
1463 0 : inc->addEdge2EdgeConnection(*j);
1464 : }
1465 0 : inc->removeFromConnections(this);
1466 : }
1467 0 : }
1468 :
1469 :
1470 : void
1471 49941 : NBEdge::removeFromConnections(NBEdge* toEdge, int fromLane, int toLane, bool tryLater, const bool adaptToLaneRemoval,
1472 : const bool keepPossibleTurns) {
1473 : // remove from "myConnections"
1474 49941 : const int fromLaneRemoved = adaptToLaneRemoval && fromLane >= 0 ? fromLane : -1;
1475 49941 : const int toLaneRemoved = adaptToLaneRemoval && toLane >= 0 ? toLane : -1;
1476 70504 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
1477 : Connection& c = *i;
1478 20563 : if ((toEdge == nullptr || c.toEdge == toEdge)
1479 1206 : && (fromLane < 0 || c.fromLane == fromLane)
1480 1043 : && (toLane < 0 || c.toLane == toLane)) {
1481 906 : if (myTo->isTLControlled()) {
1482 : std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
1483 364 : for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
1484 182 : (*it)->removeConnection(NBConnection(this, c.fromLane, c.toEdge, c.toLane));
1485 : }
1486 : }
1487 906 : i = myConnections.erase(i);
1488 : tryLater = false;
1489 906 : } else {
1490 19657 : if (fromLaneRemoved >= 0 && c.fromLane > fromLaneRemoved) {
1491 128 : if (myTo->isTLControlled()) {
1492 : std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
1493 10 : for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
1494 83 : for (NBConnectionVector::iterator tlcon = (*it)->getControlledLinks().begin(); tlcon != (*it)->getControlledLinks().end(); ++tlcon) {
1495 : NBConnection& tc = *tlcon;
1496 78 : if (tc.getTo() == c.toEdge && tc.getFromLane() == c.fromLane && tc.getToLane() == c.toLane) {
1497 12 : tc.shiftLaneIndex(this, -1);
1498 : }
1499 : }
1500 : }
1501 : }
1502 : //std::cout << getID() << " removeFromConnections fromLane=" << fromLane << " to=" << Named::getIDSecure(toEdge) << " toLane=" << toLane << " reduceFromLane=" << c.fromLane << " (to=" << c.toLane << ")\n";
1503 128 : c.fromLane--;
1504 : }
1505 19657 : if (toLaneRemoved >= 0 && c.toLane > toLaneRemoved && (toEdge == nullptr || c.toEdge == toEdge)) {
1506 : //std::cout << getID() << " removeFromConnections fromLane=" << fromLane << " to=" << Named::getIDSecure(toEdge) << " toLane=" << toLane << " reduceToLane=" << c.toLane << " (from=" << c.fromLane << ")\n";
1507 101 : c.toLane--;
1508 : }
1509 : ++i;
1510 : }
1511 : }
1512 : // check whether it was the turn destination
1513 49941 : if (myTurnDestination == toEdge && fromLane < 0) {
1514 2681 : myTurnDestination = nullptr;
1515 : }
1516 49941 : if (myPossibleTurnDestination == toEdge && fromLane < 0 && !keepPossibleTurns) {
1517 2187 : myPossibleTurnDestination = nullptr;
1518 : }
1519 49941 : if (tryLater) {
1520 14388 : myConnectionsToDelete.push_back(Connection(fromLane, toEdge, toLane));
1521 : #ifdef DEBUG_CONNECTION_GUESSING
1522 : if (DEBUGCOND) {
1523 : std::cout << "removeFromConnections " << getID() << "_" << fromLane << "->" << toEdge->getID() << "_" << toLane << "\n";
1524 : for (Connection& c : myConnections) {
1525 : std::cout << " conn " << c.getDescription(this) << "\n";
1526 : }
1527 : for (Connection& c : myConnectionsToDelete) {
1528 : std::cout << " connToDelete " << c.getDescription(this) << "\n";
1529 : }
1530 : }
1531 : #endif
1532 : }
1533 49941 : }
1534 :
1535 :
1536 : bool
1537 11 : NBEdge::removeFromConnections(const NBEdge::Connection& connectionToRemove) {
1538 : // iterate over connections
1539 13 : for (auto i = myConnections.begin(); i != myConnections.end(); i++) {
1540 13 : if ((i->toEdge == connectionToRemove.toEdge) && (i->fromLane == connectionToRemove.fromLane) && (i->toLane == connectionToRemove.toLane)) {
1541 : // remove connection
1542 11 : myConnections.erase(i);
1543 : return true;
1544 : }
1545 : }
1546 : // assert(false);
1547 : return false;
1548 : }
1549 :
1550 :
1551 : void
1552 3641 : NBEdge::invalidateConnections(bool reallowSetting) {
1553 3641 : myTurnDestination = nullptr;
1554 : myConnections.clear();
1555 3641 : if (reallowSetting) {
1556 3521 : myStep = EdgeBuildingStep::INIT;
1557 : } else {
1558 120 : myStep = EdgeBuildingStep::INIT_REJECT_CONNECTIONS;
1559 : }
1560 3641 : }
1561 :
1562 :
1563 : void
1564 1190 : NBEdge::replaceInConnections(NBEdge* which, NBEdge* by, int laneOff) {
1565 : // replace in "_connectedEdges"
1566 2493 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1567 1303 : if ((*i).toEdge == which) {
1568 760 : (*i).toEdge = by;
1569 760 : (*i).toLane += laneOff;
1570 : }
1571 : }
1572 : // check whether it was the turn destination
1573 1190 : if (myTurnDestination == which) {
1574 37 : myTurnDestination = by;
1575 : }
1576 1190 : }
1577 :
1578 : void
1579 46572 : NBEdge::replaceInConnections(NBEdge* which, const std::vector<NBEdge::Connection>& origConns) {
1580 : std::map<int, int> laneMap;
1581 : int minLane = -1;
1582 : int maxLane = -1;
1583 : // get lanes used to approach the edge to remap
1584 : bool wasConnected = false;
1585 53388 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1586 6816 : if ((*i).toEdge != which) {
1587 6113 : continue;
1588 : }
1589 : wasConnected = true;
1590 703 : if ((*i).fromLane != -1) {
1591 : int fromLane = (*i).fromLane;
1592 673 : laneMap[(*i).toLane] = fromLane;
1593 673 : if (minLane == -1 || minLane > fromLane) {
1594 : minLane = fromLane;
1595 : }
1596 673 : if (maxLane == -1 || maxLane < fromLane) {
1597 : maxLane = fromLane;
1598 : }
1599 : }
1600 : }
1601 46572 : if (!wasConnected) {
1602 : return;
1603 : }
1604 : // add new connections
1605 497 : std::vector<NBEdge::Connection> conns = origConns;
1606 497 : EdgeVector origTargets = getSuccessors();
1607 2551 : for (std::vector<NBEdge::Connection>::iterator i = conns.begin(); i != conns.end(); ++i) {
1608 2054 : if ((*i).toEdge == which || (*i).toEdge == this
1609 : // if we already have connections to the target edge, do not add new ones as they are probably from a circular replacement
1610 4054 : || std::find(origTargets.begin(), origTargets.end(), (*i).toEdge) != origTargets.end()) {
1611 : #ifdef DEBUG_REPLACECONNECTION
1612 : if (DEBUGCOND) {
1613 : std::cout << " replaceInConnections edge=" << getID() << " which=" << which->getID()
1614 : << " origTargets=" << toString(origTargets) << " newTarget=" << i->toEdge->getID() << " skipped\n";
1615 : }
1616 : #endif
1617 863 : continue;
1618 : }
1619 1194 : if (which->getStep() == EdgeBuildingStep::EDGE2EDGES) {
1620 : // do not set lane-level connections
1621 3 : replaceInConnections(which, (*i).toEdge, 0);
1622 3 : continue;
1623 : }
1624 1191 : int fromLane = (*i).fromLane;
1625 : int toUse = -1;
1626 1191 : if (laneMap.find(fromLane) == laneMap.end()) {
1627 278 : if (fromLane >= 0 && fromLane <= minLane) {
1628 : toUse = minLane;
1629 : // patch laneMap to avoid crossed-over connections
1630 441 : for (auto& item : laneMap) {
1631 241 : if (item.first < fromLane) {
1632 35 : item.second = MIN2(item.second, minLane);
1633 : }
1634 : }
1635 : }
1636 278 : if (fromLane >= 0 && fromLane >= maxLane) {
1637 : toUse = maxLane;
1638 : // patch laneMap to avoid crossed-over connections
1639 320 : for (auto& item : laneMap) {
1640 175 : if (item.first > fromLane) {
1641 62 : item.second = MAX2(item.second, maxLane);
1642 : }
1643 : }
1644 : }
1645 : } else {
1646 913 : toUse = laneMap[fromLane];
1647 : }
1648 1191 : if (toUse == -1) {
1649 : toUse = 0;
1650 : }
1651 : #ifdef DEBUG_REPLACECONNECTION
1652 : if (DEBUGCOND) {
1653 : std::cout << " replaceInConnections edge=" << getID() << " which=" << which->getID() << " origTargets=" << toString(origTargets)
1654 : << " origFrom=" << fromLane << " laneMap=" << joinToString(laneMap, ":", ",") << " minLane=" << minLane << " maxLane=" << maxLane
1655 : << " newTarget=" << i->toEdge->getID() << " fromLane=" << toUse << " toLane=" << i->toLane << "\n";
1656 : }
1657 : #endif
1658 2382 : setConnection(toUse, i->toEdge, i->toLane, Lane2LaneInfoType::COMPUTED, false, i->mayDefinitelyPass, i->keepClear,
1659 1191 : i->contPos, i->visibility, i->speed, i->friction, i->customLength, i->customShape, i->uncontrolled);
1660 : }
1661 : // remove the remapped edge from connections
1662 497 : removeFromConnections(which);
1663 497 : }
1664 :
1665 :
1666 : void
1667 776 : NBEdge::copyConnectionsFrom(NBEdge* src) {
1668 776 : myStep = src->myStep;
1669 776 : myConnections = src->myConnections;
1670 776 : }
1671 :
1672 :
1673 : bool
1674 1114 : NBEdge::canMoveConnection(const Connection& con, int newFromLane) const {
1675 : // only allow using newFromLane if at least 1 vClass is permitted to use
1676 : // this connection. If the connection shall be moved to a sidewalk, only create the connection if there is no walking area
1677 1114 : const SVCPermissions common = (getPermissions(newFromLane) & con.toEdge->getPermissions(con.toLane));
1678 1114 : return (common > 0 && common != SVC_PEDESTRIAN);
1679 : }
1680 :
1681 :
1682 : void
1683 492 : NBEdge::moveConnectionToLeft(int lane) {
1684 : #ifdef DEBUG_CONNECTION_CHECKING
1685 : std::cout << " moveConnectionToLeft " << getID() << " lane=" << lane << "\n";
1686 : #endif
1687 : int index = 0;
1688 2987 : for (int i = 0; i < (int)myConnections.size(); ++i) {
1689 2495 : if (myConnections[i].fromLane == (int)(lane) && canMoveConnection(myConnections[i], lane + 1)) {
1690 : index = i;
1691 : }
1692 : }
1693 : std::vector<Connection>::iterator i = myConnections.begin() + index;
1694 492 : Connection c = *i;
1695 492 : myConnections.erase(i);
1696 492 : setConnection(lane + 1, c.toEdge, c.toLane, Lane2LaneInfoType::VALIDATED, false);
1697 492 : }
1698 :
1699 :
1700 : void
1701 101 : NBEdge::moveConnectionToRight(int lane) {
1702 : #ifdef DEBUG_CONNECTION_CHECKING
1703 : std::cout << " moveConnectionToRight " << getID() << " lane=" << lane << "\n";
1704 : #endif
1705 355 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1706 355 : if ((*i).fromLane == (int)lane && canMoveConnection(*i, lane - 1)) {
1707 101 : Connection c = *i;
1708 101 : i = myConnections.erase(i);
1709 101 : setConnection(lane - 1, c.toEdge, c.toLane, Lane2LaneInfoType::VALIDATED, false);
1710 : return;
1711 101 : }
1712 : }
1713 : }
1714 :
1715 :
1716 : double
1717 48388 : NBEdge::buildInnerEdges(const NBNode& n, int noInternalNoSplits, int& linkIndex, int& splitIndex) {
1718 48388 : const OptionsCont& oc = OptionsCont::getOptions();
1719 48388 : const int numPoints = oc.getInt("junctions.internal-link-detail");
1720 48388 : const bool joinTurns = oc.getBool("junctions.join-turns");
1721 48388 : const double limitTurnSpeed = oc.getFloat("junctions.limit-turn-speed");
1722 48388 : const double limitTurnSpeedMinAngle = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle"));
1723 48388 : const double limitTurnSpeedMinAngleRail = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle.railway"));
1724 48388 : const double limitTurnSpeedWarnStraight = oc.getFloat("junctions.limit-turn-speed.warn.straight");
1725 48388 : const double limitTurnSpeedWarnTurn = oc.getFloat("junctions.limit-turn-speed.warn.turn");
1726 48388 : const bool higherSpeed = oc.getBool("junctions.higher-speed");
1727 48388 : const double interalJunctionVehicleWidth = oc.getFloat("internal-junctions.vehicle-width");
1728 48388 : const double defaultContPos = oc.getFloat("default.connection.cont-pos");
1729 48388 : const bool fromRail = isRailway(getPermissions());
1730 48388 : std::string innerID = ":" + n.getID();
1731 : NBEdge* toEdge = nullptr;
1732 48388 : int edgeIndex = linkIndex;
1733 : int internalLaneIndex = 0;
1734 : int numLanes = 0; // number of lanes that share the same edge
1735 : double lengthSum = 0; // total shape length of all lanes that share the same edge
1736 : int avoidedIntersectingLeftOriginLane = std::numeric_limits<int>::max();
1737 : bool averageLength = true;
1738 : double maxCross = 0.;
1739 144668 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1740 : Connection& con = *i;
1741 96280 : con.haveVia = false; // reset first since this may be called multiple times
1742 96280 : if (con.toEdge == nullptr) {
1743 0 : continue;
1744 : }
1745 96280 : LinkDirection dir = n.getDirection(this, con.toEdge);
1746 96280 : const bool isRightTurn = (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT);
1747 96280 : const bool isTurn = (isRightTurn || dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT);
1748 : // put turning internal lanes on separate edges
1749 96280 : if (con.toEdge != toEdge) {
1750 : // skip indices to keep some correspondence between edge ids and link indices:
1751 : // internalEdgeIndex + internalLaneIndex = linkIndex
1752 86711 : edgeIndex = linkIndex;
1753 : toEdge = con.toEdge;
1754 : internalLaneIndex = 0;
1755 86711 : maxCross = MAX2(maxCross, assignInternalLaneLength(i, numLanes, lengthSum, averageLength));
1756 : numLanes = 0;
1757 : lengthSum = 0;
1758 : }
1759 96280 : averageLength = !isTurn || joinTurns; // legacy behavior
1760 96280 : SVCPermissions conPermissions = getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane);
1761 96280 : const int conShapeFlag = (conPermissions & ~SVC_PEDESTRIAN) != 0 ? 0 : NBNode::SCURVE_IGNORE;
1762 96280 : PositionVector shape = n.computeInternalLaneShape(this, con, numPoints, myTo, conShapeFlag);
1763 : std::vector<int> foeInternalLinks;
1764 :
1765 96280 : if (dir != LinkDirection::STRAIGHT && shape.length() < POSITION_EPS && !(isBidiRail() && getTurnDestination(true) == con.toEdge)) {
1766 164 : WRITE_WARNINGF(TL("Connection '%_%->%_%' is only %m short."), getID(), con.fromLane, con.toEdge->getID(), con.toLane, shape.length());
1767 : }
1768 :
1769 : // crossingPosition, list of foe link indices
1770 96280 : std::pair<double, std::vector<int> > crossingPositions(-1, std::vector<int>());
1771 : std::set<std::string> tmpFoeIncomingLanes;
1772 96280 : if (dir != LinkDirection::STRAIGHT || con.contPos != UNSPECIFIED_CONTPOS) {
1773 61118 : int index = 0;
1774 : std::vector<PositionVector> otherShapes;
1775 61118 : const double width1 = MIN2(interalJunctionVehicleWidth / 2, getLaneWidth(con.fromLane) / 2);
1776 : const double width1OppositeLeft = 0; // using width1 changes a lot of curves even though they are rarely responsible for collisions
1777 269079 : for (const NBEdge* i2 : n.getIncomingEdges()) {
1778 969806 : for (const Connection& k2 : i2->getConnections()) {
1779 761845 : if (k2.toEdge == nullptr) {
1780 0 : continue;
1781 : }
1782 : // vehicles are typically less wide than the lane
1783 : // they drive on but but bicycle lanes should be kept clear for their whole width
1784 761845 : double width2 = k2.toEdge->getLaneWidth(k2.toLane);
1785 761845 : if (k2.toEdge->getPermissions(k2.toLane) != SVC_BICYCLE) {
1786 749107 : width2 *= 0.5;
1787 : }
1788 761845 : const bool foes = n.foes(this, con.toEdge, i2, k2.toEdge);
1789 761845 : LinkDirection dir2 = n.getDirection(i2, k2.toEdge);
1790 761845 : bool needsCont = !isRailway(conPermissions) && (n.needsCont(this, i2, con, k2) || (con.contPos != UNSPECIFIED_CONTPOS && !con.indirectLeft));
1791 761845 : const bool avoidIntersectCandidate = !foes && bothLeftTurns(dir, i2, dir2);
1792 14288 : bool oppositeLeftIntersect = avoidIntersectCandidate && haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2);
1793 : int shapeFlag = 0;
1794 761845 : SVCPermissions warn = SVCAll & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_DELIVERY | SVC_RAIL_CLASSES);
1795 : // do not warn if only bicycles, pedestrians or delivery vehicles are involved as this is a typical occurrence
1796 : if (con.customShape.size() == 0
1797 761126 : && k2.customShape.size() == 0
1798 760724 : && (oppositeLeftIntersect || (avoidedIntersectingLeftOriginLane < con.fromLane && avoidIntersectCandidate))
1799 765589 : && ((i2->getPermissions(k2.fromLane) & warn) != 0
1800 3243 : && (k2.toEdge->getPermissions(k2.toLane) & warn) != 0)) {
1801 : // recompute with different curve parameters (unless
1802 : // the other connection is "unimportant"
1803 3167 : shapeFlag = NBNode::AVOID_INTERSECTING_LEFT_TURNS;
1804 : PositionVector origShape = shape;
1805 6334 : shape = n.computeInternalLaneShape(this, con, numPoints, myTo, shapeFlag);
1806 3167 : oppositeLeftIntersect = haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2, shapeFlag);
1807 3167 : if (oppositeLeftIntersect
1808 2109 : && (conPermissions & (SVCAll & ~(SVC_BICYCLE | SVC_PEDESTRIAN))) == 0) {
1809 : shape = origShape;
1810 : } else {
1811 : // recompute previously computed crossing positions
1812 3118 : if (avoidedIntersectingLeftOriginLane == std::numeric_limits<int>::max()
1813 1750 : || avoidedIntersectingLeftOriginLane < con.fromLane) {
1814 3123 : for (const PositionVector& otherShape : otherShapes) {
1815 1716 : const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
1816 1716 : const double minDV = firstIntersection(shape, otherShape, width1OppositeLeft, width2,
1817 1716 : "Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
1818 1716 : if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
1819 : assert(minDV >= 0);
1820 1602 : if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1821 257 : crossingPositions.first = minDV;
1822 : }
1823 : }
1824 : }
1825 : }
1826 : // make sure connections further to the left do not get a wider angle
1827 3118 : avoidedIntersectingLeftOriginLane = con.fromLane;
1828 : }
1829 3167 : }
1830 761845 : const bool bothPrio = getJunctionPriority(&n) > 0 && i2->getJunctionPriority(&n) > 0;
1831 : //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";
1832 : // the following special case might get obsolete once we have solved #9745
1833 761845 : const bool isBicycleLeftTurn = k2.indirectLeft || (dir2 == LinkDirection::LEFT && (i2->getPermissions(k2.fromLane) & k2.toEdge->getPermissions(k2.toLane)) == SVC_BICYCLE);
1834 : // compute the crossing point
1835 761845 : if ((needsCont || (bothPrio && oppositeLeftIntersect && !isRailway(conPermissions))) && (!con.indirectLeft || dir2 == LinkDirection::STRAIGHT) && !isBicycleLeftTurn) {
1836 32503 : crossingPositions.second.push_back(index);
1837 32503 : const PositionVector otherShape = n.computeInternalLaneShape(i2, k2, numPoints, 0, shapeFlag);
1838 32503 : otherShapes.push_back(otherShape);
1839 32503 : const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
1840 32503 : const double minDV = firstIntersection(shape, otherShape, width1, width2,
1841 32503 : "Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
1842 32503 : if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
1843 : assert(minDV >= 0);
1844 27751 : if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1845 27085 : crossingPositions.first = minDV;
1846 : }
1847 : }
1848 32503 : }
1849 761845 : const bool rightTurnConflict = NBNode::rightTurnConflict(
1850 761845 : this, con.toEdge, con.fromLane, i2, k2.toEdge, k2.fromLane);
1851 761845 : const bool indirectTurnConflit = con.indirectLeft && this == i2 && (dir2 == LinkDirection::STRAIGHT ||
1852 52 : (con.fromLane < k2.fromLane && (dir2 == LinkDirection::RIGHT || dir2 == LinkDirection::PARTRIGHT)));
1853 761845 : const bool mergeConflict = myTo->mergeConflict(this, con, i2, k2, true);
1854 761845 : const bool mergeResponse = myTo->mergeConflict(this, con, i2, k2, false);
1855 761845 : const bool bidiConflict = myTo->bidiConflict(this, con, i2, k2, true);
1856 : // compute foe internal lanes
1857 761845 : if (foes || rightTurnConflict || oppositeLeftIntersect || mergeConflict || indirectTurnConflit || bidiConflict) {
1858 217070 : foeInternalLinks.push_back(index);
1859 : }
1860 : // only warn once per pair of intersecting turns
1861 2713 : if (oppositeLeftIntersect && getID() > i2->getID()
1862 1358 : && (getPermissions(con.fromLane) & warn) != 0
1863 1143 : && (con.toEdge->getPermissions(con.toLane) & warn) != 0
1864 1109 : && (i2->getPermissions(k2.fromLane) & warn) != 0
1865 1040 : && (k2.toEdge->getPermissions(k2.toLane) & warn) != 0
1866 : // do not warn for unregulated nodes
1867 762872 : && n.getType() != SumoXMLNodeType::NOJUNCTION
1868 : ) {
1869 516 : WRITE_WARNINGF(TL("Intersecting left turns at junction '%' from lane '%' and lane '%' (increase junction radius to avoid this)."),
1870 : n.getID(), getLaneID(con.fromLane), i2->getLaneID(k2.fromLane));
1871 : }
1872 : // compute foe incoming lanes
1873 761845 : const bool signalised = hasSignalisedConnectionTo(con.toEdge);
1874 1393549 : if ((n.forbids(i2, k2.toEdge, this, con.toEdge, signalised) || rightTurnConflict || indirectTurnConflit || mergeResponse)
1875 762482 : && (needsCont || dir == LinkDirection::TURN || (!signalised && this != i2 && !con.indirectLeft))) {
1876 232138 : tmpFoeIncomingLanes.insert(i2->getID() + "_" + toString(k2.fromLane));
1877 : }
1878 761845 : if (bothPrio && oppositeLeftIntersect && getID() < i2->getID()) {
1879 : //std::cout << " c1=" << con.getDescription(this) << " c2=" << k2.getDescription(i2) << " bothPrio=" << bothPrio << " oppositeLeftIntersect=" << oppositeLeftIntersect << "\n";
1880 : // break symmetry using edge id
1881 : // only store link index and resolve actual lane id later (might be multi-lane internal edge)
1882 326 : tmpFoeIncomingLanes.insert(":" + toString(index));
1883 : }
1884 761845 : index++;
1885 : }
1886 : }
1887 61118 : if (dir == LinkDirection::TURN && crossingPositions.first < 0 && crossingPositions.second.size() != 0 && shape.length() > 2. * POSITION_EPS) {
1888 : // let turnarounds wait in the middle if no other crossing point was found and it has a sensible length
1889 : // (if endOffset is used, the crossing point is in the middle of the part within the junction shape)
1890 224 : crossingPositions.first = (double)(shape.length() + getEndOffset(con.fromLane)) / 2.;
1891 : }
1892 : // foe pedestrian crossings
1893 61118 : std::vector<NBNode::Crossing*> crossings = n.getCrossings();
1894 71862 : for (auto c : crossings) {
1895 : const NBNode::Crossing& crossing = *c;
1896 29551 : for (EdgeVector::const_iterator it_e = crossing.edges.begin(); it_e != crossing.edges.end(); ++it_e) {
1897 18807 : const NBEdge* edge = *it_e;
1898 : // compute foe internal lanes
1899 18807 : if ((this == edge || con.toEdge == edge) && !isRailway(conPermissions)) {
1900 4843 : foeInternalLinks.push_back(index);
1901 4843 : if (con.toEdge == edge &&
1902 2420 : ((isRightTurn && getJunctionPriority(&n) > 0) || (isTurn && con.tlID != ""))) {
1903 : // build internal junctions (not for left turns at uncontrolled intersections)
1904 : PositionVector crossingShape = crossing.shape;
1905 1191 : crossingShape.extrapolate(5.0); // sometimes shapes miss each other by a small margin
1906 1191 : const double minDV = firstIntersection(shape, crossingShape, 0, crossing.width / 2);
1907 1191 : if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) {
1908 : assert(minDV >= 0);
1909 1108 : if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1910 618 : crossingPositions.first = minDV;
1911 : }
1912 : }
1913 4843 : } else if (this == edge && crossing.priority && !myTo->isTLControlled()) {
1914 20 : crossingPositions.first = 0;
1915 : }
1916 : }
1917 : }
1918 10744 : index++;
1919 : }
1920 :
1921 61118 : }
1922 96280 : if (con.contPos == UNSPECIFIED_CONTPOS) {
1923 96250 : con.contPos = defaultContPos;
1924 : }
1925 96280 : if (con.contPos != UNSPECIFIED_CONTPOS) {
1926 : // apply custom internal junction position
1927 50 : if (con.contPos <= 0 || con.contPos >= shape.length()) {
1928 : // disable internal junction
1929 21 : crossingPositions.first = -1;
1930 : } else {
1931 : // set custom position
1932 29 : crossingPositions.first = con.contPos;
1933 : }
1934 : }
1935 :
1936 : // @todo compute the maximum speed allowed based on angular velocity
1937 : // see !!! for an explanation (with a_lat_mean ~0.3)
1938 : /*
1939 : double vmax = (double) 0.3 * (double) 9.80778 *
1940 : getLaneShape(con.fromLane).back().distanceTo(
1941 : con.toEdge->getLaneShape(con.toLane).front())
1942 : / (double) 2.0 / (double) M_PI;
1943 : vmax = MIN2(vmax, ((getSpeed() + con.toEdge->getSpeed()) / (double) 2.0));
1944 : */
1945 96280 : if (con.speed == UNSPECIFIED_SPEED) {
1946 96124 : if (higherSpeed) {
1947 44 : con.vmax = MAX2(myLanes[con.fromLane].speed, con.toEdge->getLanes()[con.toLane].speed);
1948 : } else {
1949 96100 : con.vmax = (myLanes[con.fromLane].speed + con.toEdge->getLanes()[con.toLane].speed) / (double) 2.0;
1950 : }
1951 96124 : if (limitTurnSpeed > 0) {
1952 : // see [Odhams and Cole, Models of Driver Speed Choice in Curves, 2004]
1953 181470 : const double angleRaw = fabs(GeomHelper::angleDiff(
1954 90735 : getLaneShape(con.fromLane).angleAt2D(-2),
1955 90735 : con.toEdge->getLaneShape(con.toLane).angleAt2D(0)));
1956 178662 : const double angle = MAX2(0.0, angleRaw - (fromRail ? limitTurnSpeedMinAngleRail : limitTurnSpeedMinAngle));
1957 90735 : const double length = shape.length2D();
1958 : // do not trust the radius of tiny junctions
1959 : // formula adapted from [Odhams, Andre and Cole, David, Models of Driver Speed Choice in Curves, 2004]
1960 90735 : if (angle > 0 && length > 1) {
1961 : // permit higher turning speed on wide lanes
1962 61069 : const double radius = length / angle + getLaneWidth(con.fromLane) / 4;
1963 61069 : const double limit = sqrt(limitTurnSpeed * radius);
1964 61069 : const double reduction = con.vmax - limit;
1965 : // always treat connctions at roundabout as turns when warning
1966 61069 : const bool atRoundabout = getJunctionPriority(myTo) == JunctionPriority::ROUNDABOUT || con.toEdge->getJunctionPriority(myFrom) == JunctionPriority::ROUNDABOUT;
1967 : const LinkDirection dir2 = atRoundabout ? LinkDirection::LEFT : dir;
1968 60759 : if ((dir2 == LinkDirection::STRAIGHT && reduction > limitTurnSpeedWarnStraight)
1969 60889 : || (dir2 != LinkDirection::TURN && reduction > limitTurnSpeedWarnTurn)) {
1970 500 : std::string dirType = std::string(dir == LinkDirection::STRAIGHT ? "straight" : "turning");
1971 340 : if (atRoundabout) {
1972 : dirType = "roundabout";
1973 : }
1974 1020 : WRITE_WARNINGF(TL("Speed of % connection '%' reduced by % due to turning radius of % (length=%, angle=%)."),
1975 : dirType, con.getDescription(this), reduction, radius, length, RAD2DEG(angleRaw));
1976 : }
1977 114467 : con.vmax = MIN2(con.vmax, limit);
1978 : // value is saved in <net> attribute. Must be set again when importing from .con.xml
1979 : // con.speed = con.vmax;
1980 : }
1981 : assert(con.vmax > 0);
1982 : //if (getID() == "-1017000.0.00") {
1983 : // std::cout << con.getDescription(this) << " angleRaw=" << angleRaw << " angle=" << RAD2DEG(angle) << " length=" << length << " radius=" << length / angle
1984 : // << " vmaxTurn=" << sqrt(limitTurnSpeed * length / angle) << " vmax=" << con.vmax << "\n";
1985 : //}
1986 5389 : } else if (fromRail && dir == LinkDirection::TURN) {
1987 0 : con.vmax = 0.01;
1988 : }
1989 : } else {
1990 156 : con.vmax = con.speed;
1991 : }
1992 96280 : if (con.friction == UNSPECIFIED_FRICTION) {
1993 96280 : con.friction = (myLanes[con.fromLane].friction + con.toEdge->getLanes()[con.toLane].friction) / 2.;
1994 : }
1995 : //
1996 : assert(shape.size() >= 2);
1997 : // get internal splits if any
1998 192560 : con.id = innerID + "_" + toString(edgeIndex);
1999 96280 : const double shapeLength = shape.length();
2000 : double firstLength = shapeLength;
2001 96280 : if (crossingPositions.first > 0 && crossingPositions.first < shapeLength) {
2002 18255 : std::pair<PositionVector, PositionVector> split = shape.splitAt(crossingPositions.first);
2003 : con.shape = split.first;
2004 18255 : con.foeIncomingLanes = std::vector<std::string>(tmpFoeIncomingLanes.begin(), tmpFoeIncomingLanes.end());
2005 18255 : con.foeInternalLinks = foeInternalLinks; // resolve link indices to lane ids later
2006 18255 : if (i != myConnections.begin() && (i - 1)->toEdge == con.toEdge && (i - 1)->haveVia) {
2007 214 : --splitIndex;
2008 214 : con.internalViaLaneIndex = (i - 1)->internalViaLaneIndex + 1;
2009 : }
2010 36510 : con.viaID = innerID + "_" + toString(splitIndex + noInternalNoSplits);
2011 18255 : ++splitIndex;
2012 : con.viaShape = split.second;
2013 18255 : con.haveVia = true;
2014 18255 : firstLength = con.shape.length();
2015 18255 : } else {
2016 : con.shape = shape;
2017 : }
2018 96280 : con.internalLaneIndex = internalLaneIndex;
2019 96280 : ++internalLaneIndex;
2020 96280 : ++linkIndex;
2021 : ++numLanes;
2022 96280 : if (con.customLength != UNSPECIFIED_LOADED_LENGTH) {
2023 : // split length proportionally
2024 30 : lengthSum += (shapeLength != 0 ? firstLength / shapeLength : 1) * con.customLength;
2025 : } else {
2026 96250 : lengthSum += firstLength;
2027 : }
2028 96280 : }
2029 96776 : return MAX2(maxCross, assignInternalLaneLength(myConnections.end(), numLanes, lengthSum, averageLength));
2030 : }
2031 :
2032 :
2033 : double
2034 135099 : NBEdge::assignInternalLaneLength(std::vector<Connection>::iterator i, int numLanes, double lengthSum, bool averageLength) {
2035 : // assign average length to all lanes of the same internal edge if averageLength is set
2036 : // the lengthSum only covers the part up to the first internal junction
2037 : // TODO This code assumes that either all connections in question have a via or none
2038 : double maxCross = 0.;
2039 : assert(i - myConnections.begin() >= numLanes);
2040 231379 : for (int prevIndex = 1; prevIndex <= numLanes; prevIndex++) {
2041 : //std::cout << " con=" << (*(i - prevIndex)).getDescription(this) << " numLanes=" << numLanes << " avgLength=" << lengthSum / numLanes << "\n";
2042 : Connection& c = (*(i - prevIndex));
2043 96280 : const double minLength = c.customLength != UNSPECIFIED_LOADED_LENGTH ? pow(10, -gPrecision) : POSITION_EPS;
2044 96280 : c.length = MAX2(minLength, averageLength ? lengthSum / numLanes : c.shape.length());
2045 96280 : if (c.haveVia) {
2046 36510 : c.viaLength = MAX2(minLength, c.viaShape.length());
2047 : }
2048 96280 : if (c.customLength != UNSPECIFIED_LOADED_LENGTH) {
2049 30 : if (c.haveVia) {
2050 : // split length proportionally
2051 4 : const double a = c.viaLength / (c.shape.length() + c.viaLength);
2052 6 : c.viaLength = MAX2(minLength, a * c.customLength);
2053 : }
2054 30 : if (!averageLength) {
2055 19 : c.length = MAX2(minLength, c.customLength - c.viaLength);
2056 : }
2057 : }
2058 96280 : if (c.haveVia) {
2059 : // we need to be able to leave from the internal junction by accelerating from 0
2060 18255 : 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)
2061 : }
2062 : // we need to be able to cross the junction in one go but not if we have an indirect left turn
2063 96280 : if (c.indirectLeft) {
2064 15 : maxCross = MAX2(maxCross, MAX2(c.length, c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
2065 : } else {
2066 137471 : maxCross = MAX2(maxCross, (c.length + c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
2067 : }
2068 : }
2069 135099 : return maxCross;
2070 : }
2071 :
2072 :
2073 : double
2074 74773 : NBEdge::firstIntersection(const PositionVector& v1, const PositionVector& v2, double width1, double width2, const std::string& error, bool secondIntersection) {
2075 : double intersect = std::numeric_limits<double>::max();
2076 74773 : if (v2.length() < POSITION_EPS) {
2077 : return intersect;
2078 : }
2079 : try {
2080 : PositionVector v1Right = v1;
2081 74745 : v1Right.move2side(width1);
2082 :
2083 : PositionVector v1Left = v1;
2084 74745 : v1Left.move2side(-width1);
2085 :
2086 : PositionVector v2Right = v2;
2087 74745 : v2Right.move2side(width2);
2088 :
2089 : PositionVector v2Left = v2;
2090 74745 : v2Left.move2side(-width2);
2091 :
2092 : // intersect all border combinations
2093 : bool skip = secondIntersection;
2094 98499 : for (double cand : v1Left.intersectsAtLengths2D(v2Right)) {
2095 23754 : if (skip) {
2096 : skip = false;
2097 8 : continue;
2098 : }
2099 : intersect = MIN2(intersect, cand);
2100 74745 : }
2101 : skip = secondIntersection;
2102 111341 : for (double cand : v1Left.intersectsAtLengths2D(v2Left)) {
2103 36596 : if (skip) {
2104 : skip = false;
2105 11 : continue;
2106 : }
2107 : intersect = MIN2(intersect, cand);
2108 74745 : }
2109 : skip = secondIntersection;
2110 117859 : for (double cand : v1Right.intersectsAtLengths2D(v2Right)) {
2111 43114 : if (skip) {
2112 : skip = false;
2113 9 : continue;
2114 : }
2115 : intersect = MIN2(intersect, cand);
2116 74745 : }
2117 : skip = secondIntersection;
2118 112052 : for (double cand : v1Right.intersectsAtLengths2D(v2Left)) {
2119 37307 : if (skip) {
2120 : skip = false;
2121 11 : continue;
2122 : }
2123 : intersect = MIN2(intersect, cand);
2124 74745 : }
2125 74745 : } catch (InvalidArgument&) {
2126 0 : if (error != "") {
2127 0 : WRITE_WARNING(error);
2128 : }
2129 0 : }
2130 : //std::cout << " v1=" << v1 << " v2Right=" << v2Right << " v2Left=" << v2Left << "\n";
2131 : //std::cout << " intersectsRight=" << toString(v1.intersectsAtLengths2D(v2Right)) << "\n";
2132 : //std::cout << " intersectsLeft=" << toString(v1.intersectsAtLengths2D(v2Left)) << "\n";
2133 : return intersect;
2134 : }
2135 :
2136 :
2137 : bool
2138 548222 : NBEdge::bothLeftTurns(LinkDirection dir, const NBEdge* otherFrom, LinkDirection dir2) const {
2139 548222 : if (otherFrom == this) {
2140 : // not an opposite pair
2141 : return false;
2142 : }
2143 334401 : return (dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT) && (dir2 == LinkDirection::LEFT || dir2 == LinkDirection::PARTLEFT);
2144 : }
2145 :
2146 : bool
2147 17455 : NBEdge::haveIntersection(const NBNode& n, const PositionVector& shape, const NBEdge* otherFrom, const NBEdge::Connection& otherCon, int numPoints,
2148 : double width1, double width2, int shapeFlag) const {
2149 17455 : const PositionVector otherShape = n.computeInternalLaneShape(otherFrom, otherCon, numPoints, 0, shapeFlag);
2150 17455 : const double minDV = firstIntersection(shape, otherShape, width1, width2);
2151 34910 : return minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS;
2152 17455 : }
2153 :
2154 :
2155 : // -----------
2156 : int
2157 10911886 : NBEdge::getJunctionPriority(const NBNode* const node) const {
2158 10911886 : if (node == myFrom) {
2159 278410 : return myFromJunctionPriority;
2160 : } else {
2161 10633476 : return myToJunctionPriority;
2162 : }
2163 : }
2164 :
2165 :
2166 : void
2167 296316 : NBEdge::setJunctionPriority(const NBNode* const node, int prio) {
2168 296316 : if (node == myFrom) {
2169 146297 : myFromJunctionPriority = prio;
2170 : #ifdef DEBUG_JUNCTIONPRIO
2171 : setParameter("fromPrio", toString(prio));
2172 : #endif
2173 : } else {
2174 150019 : myToJunctionPriority = prio;
2175 : #ifdef DEBUG_JUNCTIONPRIO
2176 : setParameter("toPrio", toString(prio));
2177 : #endif
2178 : }
2179 296316 : }
2180 :
2181 :
2182 : double
2183 18137726 : NBEdge::getAngleAtNode(const NBNode* const atNode) const {
2184 18137726 : if (atNode == myFrom) {
2185 8913801 : return GeomHelper::legacyDegree(myGeom.angleAt2D(0));
2186 : }
2187 : assert(atNode == myTo);
2188 9223925 : return GeomHelper::legacyDegree(myGeom.angleAt2D(-2));
2189 : }
2190 :
2191 :
2192 : double
2193 3214198 : NBEdge::getAngleAtNodeNormalized(const NBNode* const atNode) const {
2194 : double res;
2195 3214198 : if (atNode == myFrom) {
2196 1586991 : res = GeomHelper::legacyDegree(myGeom.angleAt2D(0)) - 180;
2197 : } else {
2198 : assert(atNode == myTo);
2199 1627207 : res = GeomHelper::legacyDegree(myGeom.angleAt2D(-2));
2200 : }
2201 3214198 : if (res < 0) {
2202 2320871 : res += 360;
2203 : }
2204 3214198 : return res;
2205 : }
2206 :
2207 :
2208 : double
2209 5093978 : NBEdge::getAngleAtNodeToCenter(const NBNode* const atNode) const {
2210 5093978 : if (atNode == myFrom) {
2211 2371119 : double res = myStartAngle - 180;
2212 2371119 : if (res < 0) {
2213 1025791 : res += 360;
2214 : }
2215 2371119 : return res;
2216 : } else {
2217 : assert(atNode == myTo);
2218 2722859 : return myEndAngle;
2219 : }
2220 : }
2221 :
2222 :
2223 : void
2224 602254 : NBEdge::setTurningDestination(NBEdge* e, bool onlyPossible) {
2225 602254 : if (!onlyPossible) {
2226 557946 : myTurnDestination = e;
2227 : }
2228 602254 : myPossibleTurnDestination = e;
2229 602254 : }
2230 :
2231 :
2232 : double
2233 10587 : NBEdge::getLaneSpeed(int lane) const {
2234 10587 : return myLanes[lane].speed;
2235 : }
2236 :
2237 :
2238 : double
2239 3812 : NBEdge::getLaneFriction(int lane) const {
2240 3812 : return myLanes[lane].friction;
2241 : }
2242 :
2243 :
2244 : void
2245 80 : NBEdge::resetLaneShapes() {
2246 80 : computeLaneShapes();
2247 80 : }
2248 :
2249 :
2250 : void
2251 6 : NBEdge::updateChangeRestrictions(SVCPermissions ignoring) {
2252 24 : for (Lane& lane : myLanes) {
2253 18 : if (lane.changeLeft != SVCAll) {
2254 6 : lane.changeLeft = ignoring;
2255 : }
2256 18 : if (lane.changeRight != SVCAll) {
2257 6 : lane.changeRight = ignoring;
2258 : }
2259 : }
2260 15 : for (Connection& con : myConnections) {
2261 9 : if (con.changeLeft != SVC_UNSPECIFIED && con.changeLeft != SVCAll) {
2262 0 : con.changeLeft = ignoring;
2263 : }
2264 9 : if (con.changeRight != SVC_UNSPECIFIED && con.changeRight != SVCAll) {
2265 0 : con.changeRight = ignoring;
2266 : }
2267 : }
2268 6 : }
2269 :
2270 :
2271 : void
2272 335077 : NBEdge::computeLaneShapes() {
2273 : // vissim needs this
2274 335077 : if (myFrom == myTo) {
2275 0 : return;
2276 : }
2277 : // compute lane offset, first
2278 335077 : std::vector<double> offsets(myLanes.size(), 0.);
2279 : double offset = 0;
2280 434595 : for (int i = (int)myLanes.size() - 2; i >= 0; --i) {
2281 99518 : offset += (getLaneWidth(i) + getLaneWidth(i + 1)) / 2.;
2282 99518 : offsets[i] = offset;
2283 : }
2284 335077 : if (myLaneSpreadFunction == LaneSpreadFunction::CENTER) {
2285 : double width = 0;
2286 333263 : for (int i = 0; i < (int)myLanes.size(); ++i) {
2287 189706 : width += getLaneWidth(i);
2288 : }
2289 143557 : offset = -width / 2. + getLaneWidth((int)myLanes.size() - 1) / 2.;
2290 : } else {
2291 191520 : double laneWidth = myLanes.back().width != UNSPECIFIED_WIDTH ? myLanes.back().width : SUMO_const_laneWidth;
2292 191520 : offset = laneWidth / 2.;
2293 : }
2294 335077 : if (myLaneSpreadFunction == LaneSpreadFunction::ROADCENTER) {
2295 543 : for (NBEdge* e : myTo->getOutgoingEdges()) {
2296 462 : if (e->getToNode() == myFrom && getInnerGeometry().reverse() == e->getInnerGeometry()) {
2297 248 : offset += (e->getTotalWidth() - getTotalWidth()) / 2;
2298 248 : break;
2299 : }
2300 : }
2301 : }
2302 :
2303 769672 : for (int i = 0; i < (int)myLanes.size(); ++i) {
2304 434595 : offsets[i] += offset;
2305 : }
2306 :
2307 : // build the shape of each lane
2308 769672 : for (int i = 0; i < (int)myLanes.size(); ++i) {
2309 434595 : if (myLanes[i].customShape.size() != 0) {
2310 : myLanes[i].shape = myLanes[i].customShape;
2311 26 : continue;
2312 : }
2313 : try {
2314 869138 : myLanes[i].shape = computeLaneShape(i, offsets[i]);
2315 0 : } catch (InvalidArgument& e) {
2316 0 : WRITE_WARNINGF(TL("In lane '%': lane shape could not be determined (%)."), getLaneID(i), e.what());
2317 : myLanes[i].shape = myGeom;
2318 0 : }
2319 : }
2320 335077 : }
2321 :
2322 :
2323 : PositionVector
2324 434569 : NBEdge::computeLaneShape(int lane, double offset) const {
2325 : PositionVector shape = myGeom;
2326 : try {
2327 434569 : shape.move2side(offset);
2328 0 : } catch (InvalidArgument& e) {
2329 0 : WRITE_WARNINGF(TL("In lane '%': Could not build shape (%)."), getLaneID(lane), e.what());
2330 0 : }
2331 434569 : return shape;
2332 0 : }
2333 :
2334 :
2335 : void
2336 449870 : NBEdge::computeAngle() {
2337 : // taking the angle at the first might be unstable, thus we take the angle
2338 : // at a certain distance. (To compare two edges, additional geometry
2339 : // segments are considered to resolve ambiguities)
2340 449870 : const bool hasFromShape = myFrom->getShape().size() > 0;
2341 449870 : const bool hasToShape = myTo->getShape().size() > 0;
2342 449870 : Position fromCenter = (hasFromShape ? myFrom->getShape().getCentroid() : myFrom->getPosition());
2343 449870 : Position toCenter = (hasToShape ? myTo->getShape().getCentroid() : myTo->getPosition());
2344 : PositionVector shape = myGeom;
2345 449870 : if ((hasFromShape || hasToShape) && getNumLanes() > 0) {
2346 197029 : if (myLaneSpreadFunction == LaneSpreadFunction::RIGHT) {
2347 103575 : shape = myLanes[getNumLanes() - 1].shape ;
2348 : } else {
2349 93454 : shape = myLanes[getNumLanes() / 2].shape;
2350 93454 : if (getNumLanes() % 2 == 0) {
2351 : // there is no center lane. shift to get the center
2352 13646 : shape.move2side(getLaneWidth(getNumLanes() / 2) * 0.5);
2353 : }
2354 : }
2355 : }
2356 :
2357 : // if the junction shape is suspicious we cannot trust the angle to the centroid
2358 449870 : const bool suspiciousFromShape = hasFromShape && (myFrom->getShape().distance2D(shape[0]) > 2 * POSITION_EPS
2359 166855 : || myFrom->getShape().around(shape[-1])
2360 166181 : || !(myFrom->getShape().around(fromCenter)));
2361 449870 : const bool suspiciousToShape = hasToShape && (myTo->getShape().distance2D(shape[-1]) > 2 * POSITION_EPS
2362 162541 : || myTo->getShape().around(shape[0])
2363 161986 : || !(myTo->getShape().around(toCenter)));
2364 :
2365 449870 : const double angleLookahead = MIN2(shape.length2D() / 2, ANGLE_LOOKAHEAD);
2366 449870 : const Position referencePosStart = shape.positionAtOffset2D(angleLookahead);
2367 449870 : const Position referencePosEnd = shape.positionAtOffset2D(shape.length2D() - angleLookahead);
2368 :
2369 449870 : myStartAngle = GeomHelper::legacyDegree(fromCenter.angleTo2D(referencePosStart), true);
2370 449870 : const double myStartAngle2 = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(referencePosStart), true);
2371 449870 : const double myStartAngle3 = getAngleAtNode(myFrom);
2372 449870 : myEndAngle = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(toCenter), true);
2373 449870 : const double myEndAngle2 = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myTo->getPosition()), true);
2374 449870 : const double myEndAngle3 = getAngleAtNode(myTo);
2375 :
2376 : #ifdef DEBUG_ANGLES
2377 : if (DEBUGCOND) {
2378 : if (suspiciousFromShape) {
2379 : std::cout << "suspiciousFromShape len=" << shape.length() << " startA=" << myStartAngle << " startA2=" << myStartAngle2 << " startA3=" << myStartAngle3
2380 : << " rel=" << NBHelpers::normRelAngle(myStartAngle, myStartAngle2)
2381 : << " fromCenter=" << fromCenter
2382 : << " fromPos=" << myFrom->getPosition()
2383 : << " refStart=" << referencePosStart
2384 : << "\n";
2385 : }
2386 : if (suspiciousToShape) {
2387 : std::cout << "suspiciousToShape len=" << shape.length() << " endA=" << myEndAngle << " endA2=" << myEndAngle2 << " endA3=" << myEndAngle3
2388 : << " rel=" << NBHelpers::normRelAngle(myEndAngle, myEndAngle2)
2389 : << " toCenter=" << toCenter
2390 : << " toPos=" << myTo->getPosition()
2391 : << " refEnd=" << referencePosEnd
2392 : << "\n";
2393 : }
2394 : }
2395 : #endif
2396 :
2397 449870 : if (suspiciousFromShape && shape.length() > 1) {
2398 16972 : myStartAngle = myStartAngle2;
2399 41128 : } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myStartAngle, myStartAngle3)) > 90
2400 : // don't trust footpath angles
2401 436678 : && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
2402 2496 : myStartAngle = myStartAngle3;
2403 2496 : if (myStartAngle < 0) {
2404 1232 : myStartAngle += 360;
2405 : }
2406 : }
2407 :
2408 449870 : if (suspiciousToShape && shape.length() > 1) {
2409 22050 : myEndAngle = myEndAngle2;
2410 21621 : } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myEndAngle, myEndAngle3)) > 90
2411 : // don't trust footpath angles
2412 432537 : && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
2413 2872 : myEndAngle = myEndAngle3;
2414 2872 : if (myEndAngle < 0) {
2415 1386 : myEndAngle += 360;
2416 : }
2417 : }
2418 :
2419 449870 : myTotalAngle = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(myTo->getPosition()), true);
2420 : #ifdef DEBUG_ANGLES
2421 : if (DEBUGCOND) std::cout << "computeAngle edge=" << getID()
2422 : << " fromCenter=" << fromCenter << " toCenter=" << toCenter
2423 : << " refStart=" << referencePosStart << " refEnd=" << referencePosEnd << " shape=" << shape
2424 : << " hasFromShape=" << hasFromShape
2425 : << " hasToShape=" << hasToShape
2426 : << " numLanes=" << getNumLanes()
2427 : << " shapeLane=" << getNumLanes() / 2
2428 : << " startA=" << myStartAngle << " endA=" << myEndAngle << " totA=" << myTotalAngle << "\n";
2429 : #endif
2430 449870 : }
2431 :
2432 :
2433 : double
2434 194704 : NBEdge::getShapeStartAngle() const {
2435 194704 : const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
2436 194704 : const Position referencePosStart = myGeom.positionAtOffset2D(angleLookahead);
2437 194704 : return GeomHelper::legacyDegree(myGeom.front().angleTo2D(referencePosStart), true);
2438 : }
2439 :
2440 :
2441 : double
2442 93066 : NBEdge::getShapeEndAngle() const {
2443 93066 : const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
2444 93066 : const Position referencePosEnd = myGeom.positionAtOffset2D(myGeom.length2D() - angleLookahead);
2445 93066 : return GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myGeom.back()), true);
2446 : }
2447 :
2448 :
2449 : bool
2450 0 : NBEdge::hasPermissions() const {
2451 0 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2452 0 : if ((*i).permissions != SVCAll) {
2453 : return true;
2454 : }
2455 : }
2456 : return false;
2457 : }
2458 :
2459 :
2460 : bool
2461 4944 : NBEdge::hasLaneSpecificPermissions() const {
2462 : std::vector<Lane>::const_iterator i = myLanes.begin();
2463 4944 : SVCPermissions firstLanePermissions = i->permissions;
2464 : i++;
2465 6538 : for (; i != myLanes.end(); ++i) {
2466 3129 : if (i->permissions != firstLanePermissions) {
2467 : return true;
2468 : }
2469 : }
2470 : return false;
2471 : }
2472 :
2473 :
2474 : bool
2475 4671 : NBEdge::hasLaneSpecificSpeed() const {
2476 14340 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2477 9684 : if (i->speed != getSpeed()) {
2478 : return true;
2479 : }
2480 : }
2481 : return false;
2482 : }
2483 :
2484 : bool
2485 1886 : NBEdge::hasLaneSpecificFriction() const {
2486 5157 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2487 3275 : if (i->friction != myLanes.begin()->friction) {
2488 : return true;
2489 : }
2490 : }
2491 : return false;
2492 : }
2493 :
2494 : bool
2495 36300 : NBEdge::hasLaneSpecificWidth() const {
2496 81664 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2497 47136 : if (i->width != myLanes.begin()->width) {
2498 : return true;
2499 : }
2500 : }
2501 : return false;
2502 : }
2503 :
2504 :
2505 : bool
2506 1606 : NBEdge::hasLaneSpecificType() const {
2507 3870 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2508 2266 : if (i->type != myLanes.begin()->type) {
2509 : return true;
2510 : }
2511 : }
2512 : return false;
2513 : }
2514 :
2515 :
2516 : bool
2517 35600 : NBEdge::hasLaneSpecificEndOffset() const {
2518 82235 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2519 46652 : if (i->endOffset != myLanes.begin()->endOffset) {
2520 : return true;
2521 : }
2522 : }
2523 : return false;
2524 : }
2525 :
2526 :
2527 : bool
2528 37462 : NBEdge::hasLaneSpecificStopOffsets() const {
2529 87325 : for (const auto& lane : myLanes) {
2530 49868 : if (lane.laneStopOffset.isDefined()) {
2531 5 : if (myEdgeStopOffset.isDefined() || (myEdgeStopOffset != lane.laneStopOffset)) {
2532 : return true;
2533 : }
2534 : }
2535 : }
2536 : return false;
2537 : }
2538 :
2539 :
2540 : bool
2541 1598 : NBEdge::hasAccelLane() const {
2542 3839 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2543 2242 : if (i->accelRamp) {
2544 : return true;
2545 : }
2546 : }
2547 : return false;
2548 : }
2549 :
2550 :
2551 : bool
2552 1597 : NBEdge::hasCustomLaneShape() const {
2553 3828 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2554 2236 : if (i->customShape.size() > 0) {
2555 : return true;
2556 : }
2557 : }
2558 : return false;
2559 : }
2560 :
2561 :
2562 : bool
2563 1592 : NBEdge::hasLaneParams() const {
2564 3819 : for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2565 2229 : if (i->getParametersMap().size() > 0) {
2566 : return true;
2567 : }
2568 : }
2569 : return false;
2570 : }
2571 :
2572 : bool
2573 1590 : NBEdge::prohibitsChanging() const {
2574 3799 : for (const Lane& lane : myLanes) {
2575 2216 : if (lane.changeLeft != SVCAll || lane.changeRight != SVCAll) {
2576 : return true;
2577 : }
2578 : }
2579 : return false;
2580 : }
2581 :
2582 : bool
2583 1886 : NBEdge::needsLaneSpecificOutput() const {
2584 1886 : return (hasLaneSpecificPermissions()
2585 1613 : || hasLaneSpecificSpeed()
2586 1610 : || hasLaneSpecificWidth()
2587 1606 : || hasLaneSpecificType()
2588 1604 : || hasLaneSpecificEndOffset()
2589 1598 : || hasLaneSpecificStopOffsets()
2590 1598 : || hasAccelLane()
2591 1597 : || hasCustomLaneShape()
2592 1592 : || hasLaneParams()
2593 1590 : || prohibitsChanging()
2594 3469 : || (!myLanes.empty() && myLanes.back().oppositeID != ""));
2595 : }
2596 :
2597 :
2598 :
2599 : bool
2600 102504 : NBEdge::computeEdge2Edges(bool noLeftMovers) {
2601 : #ifdef DEBUG_CONNECTION_GUESSING
2602 : if (DEBUGCOND) {
2603 : std::cout << "computeEdge2Edges edge=" << getID() << " step=" << (int)myStep << " noLeftMovers=" << noLeftMovers << "\n";
2604 : for (Connection& c : myConnections) {
2605 : std::cout << " conn " << c.getDescription(this) << "\n";
2606 : }
2607 : for (Connection& c : myConnectionsToDelete) {
2608 : std::cout << " connToDelete " << c.getDescription(this) << "\n";
2609 : }
2610 : }
2611 : #endif
2612 : // return if this relationship has been build in previous steps or
2613 : // during the import
2614 102504 : if (myStep >= EdgeBuildingStep::EDGE2EDGES) {
2615 : return true;
2616 : }
2617 66458 : const bool fromRail = isRailway(getPermissions());
2618 231613 : for (NBEdge* out : myTo->getOutgoingEdges()) {
2619 165155 : if (noLeftMovers && myTo->isLeftMover(this, out)) {
2620 1140 : continue;
2621 : }
2622 : // avoid sharp railway turns
2623 164015 : if (fromRail && isRailway(out->getPermissions())) {
2624 9390 : const double angle = fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), out->getAngleAtNode(myTo)));
2625 9390 : if (angle > 150) {
2626 3757 : continue;
2627 5633 : } else if (angle > 90) {
2628 : // possibly the junction is large enough to achieve a plausible radius:
2629 121 : const PositionVector& fromShape = myLanes.front().shape;
2630 121 : const PositionVector& toShape = out->getLanes().front().shape;
2631 121 : PositionVector shape = myTo->computeSmoothShape(fromShape, toShape, 5, getTurnDestination() == out, 5, 5);
2632 121 : const double radius = shape.length2D() / DEG2RAD(angle);
2633 121 : const double minRadius = (getPermissions() & SVC_TRAM) != 0 ? 20 : 80;
2634 : //std::cout << getID() << " to=" << out->getID() << " radius=" << radius << " minRadius=" << minRadius << "\n";
2635 121 : if (radius < minRadius) {
2636 : continue;
2637 : }
2638 121 : }
2639 : }
2640 160138 : if (out == myTurnDestination) {
2641 : // will be added by appendTurnaround
2642 42367 : continue;
2643 : }
2644 117771 : if ((getPermissions() & out->getPermissions() & ~SVC_PEDESTRIAN) == 0) {
2645 : // no common permissions
2646 28919 : continue;
2647 : }
2648 177704 : myConnections.push_back(Connection(-1, out, -1));
2649 : }
2650 66458 : myStep = EdgeBuildingStep::EDGE2EDGES;
2651 66458 : return true;
2652 : }
2653 :
2654 :
2655 : bool
2656 102504 : NBEdge::computeLanes2Edges() {
2657 : #ifdef DEBUG_CONNECTION_GUESSING
2658 : if (DEBUGCOND) {
2659 : std::cout << "computeLanes2Edges edge=" << getID() << " step=" << (int)myStep << "\n";
2660 : for (Connection& c : myConnections) {
2661 : std::cout << " conn " << c.getDescription(this) << "\n";
2662 : }
2663 : for (Connection& c : myConnectionsToDelete) {
2664 : std::cout << " connToDelete " << c.getDescription(this) << "\n";
2665 : }
2666 : }
2667 : #endif
2668 : // return if this relationship has been build in previous steps or
2669 : // during the import
2670 102504 : if (myStep >= EdgeBuildingStep::LANES2EDGES) {
2671 35578 : if (myStep == EdgeBuildingStep::LANES2LANES_USER && myConnections.size() > 1) {
2672 75439 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
2673 55157 : if ((*i).toEdge == nullptr) {
2674 3 : WRITE_WARNINGF("Inconsistent connection definitions at edge '%'.", getID());
2675 1 : i = myConnections.erase(i);
2676 : } else {
2677 : i++;
2678 : }
2679 : }
2680 : }
2681 35578 : return true;
2682 : }
2683 : assert(myStep == EdgeBuildingStep::EDGE2EDGES);
2684 : // get list of possible outgoing edges sorted by direction clockwise
2685 : // the edge in the backward direction (turnaround) is not in the list
2686 66926 : const EdgeVector* edges = getConnectedSorted();
2687 66926 : if (myConnections.size() != 0 && edges->size() == 0) {
2688 : // dead end per definition!?
2689 : myConnections.clear();
2690 : } else {
2691 : // divide the lanes on reachable edges
2692 66904 : divideOnEdges(edges);
2693 : }
2694 66926 : delete edges;
2695 66926 : myStep = EdgeBuildingStep::LANES2EDGES;
2696 66926 : return true;
2697 : }
2698 :
2699 :
2700 : std::vector<LinkDirection>
2701 394 : NBEdge::decodeTurnSigns(int turnSigns, int shift) {
2702 : std::vector<LinkDirection> result;
2703 3546 : for (int i = 0; i < 8; i++) {
2704 : // see LinkDirection in SUMOXMLDefinitions.h
2705 3152 : if ((turnSigns & (1 << (i + shift))) != 0) {
2706 323 : result.push_back((LinkDirection)(1 << i));
2707 : }
2708 : }
2709 394 : return result;
2710 0 : }
2711 :
2712 : void
2713 276 : NBEdge::updateTurnPermissions(SVCPermissions& perm, LinkDirection dir, SVCPermissions spec, std::vector<LinkDirection> dirs) {
2714 276 : if (dirs.size() > 0) {
2715 77 : if (std::find(dirs.begin(), dirs.end(), dir) == dirs.end()) {
2716 2 : perm &= ~spec;
2717 : } else {
2718 75 : perm |= spec;
2719 : }
2720 : }
2721 276 : }
2722 :
2723 : bool
2724 34 : NBEdge::applyTurnSigns() {
2725 : #ifdef DEBUG_TURNSIGNS
2726 : std::cout << "applyTurnSigns edge=" << getID() << "\n";
2727 : #endif
2728 : // build a map of target edges and lanes
2729 : std::vector<const NBEdge*> targets;
2730 : std::map<const NBEdge*, std::vector<int> > toLaneMap;
2731 144 : for (const Connection& c : myConnections) {
2732 110 : if (myLanes[c.fromLane].turnSigns != 0) {
2733 110 : if (std::find(targets.begin(), targets.end(), c.toEdge) == targets.end()) {
2734 90 : targets.push_back(c.toEdge);
2735 : }
2736 110 : toLaneMap[c.toEdge].push_back(c.toLane);
2737 : }
2738 : }
2739 : // might be unsorted due to bike lane connections
2740 124 : for (auto& item : toLaneMap) {
2741 90 : std::sort(item.second.begin(), item.second.end());
2742 : }
2743 :
2744 : // check number of distinct signed directions and count the number of signs for each direction
2745 : std::map<LinkDirection, int> signCons;
2746 : int allDirs = 0;
2747 114 : for (const Lane& lane : myLanes) {
2748 80 : allDirs |= lane.turnSigns;
2749 179 : for (LinkDirection dir : decodeTurnSigns(lane.turnSigns)) {
2750 99 : signCons[dir]++;
2751 80 : }
2752 : }
2753 34 : allDirs |= allDirs >> TURN_SIGN_SHIFT_BUS;
2754 34 : allDirs |= allDirs >> TURN_SIGN_SHIFT_TAXI;
2755 34 : allDirs |= allDirs >> TURN_SIGN_SHIFT_BICYCLE;
2756 :
2757 34 : if ((allDirs & (int)LinkDirection::NODIR) != 0) {
2758 3 : targets.push_back(nullptr); // dead end
2759 : }
2760 :
2761 : SVCPermissions defaultPermissions = SVC_PASSENGER | SVC_DELIVERY;
2762 : // build a mapping from sign directions to targets
2763 34 : std::vector<LinkDirection> signedDirs = decodeTurnSigns(allDirs);
2764 : std::map<LinkDirection, const NBEdge*> dirMap;
2765 : #ifdef DEBUG_TURNSIGNS
2766 : std::cout << " numDirs=" << signedDirs.size() << " numTargets=" << targets.size() << "\n";
2767 : #endif
2768 34 : if (signedDirs.size() > targets.size()) {
2769 18 : WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions but only % targets"), getID(), signedDirs.size(), targets.size());
2770 6 : return false;
2771 28 : } else if (signedDirs.size() < targets.size()) {
2772 : // we need to drop some targets (i.e. turn-around)
2773 : // use sumo-directions as a guide
2774 : std::vector<LinkDirection> sumoDirs;
2775 82 : for (const NBEdge* to : targets) {
2776 65 : sumoDirs.push_back(myTo->getDirection(this, to));
2777 : }
2778 : // remove targets to the left
2779 : bool checkMore = true;
2780 37 : while (signedDirs.size() < targets.size() && checkMore) {
2781 : checkMore = false;
2782 : //std::cout << getID() << " sumoDirs=" << joinToString(sumoDirs, ",") << " signedDirs=" << joinToString(signedDirs, ",") << "\n";
2783 20 : if (sumoDirs.back() != signedDirs.back()) {
2784 : targets.pop_back();
2785 : sumoDirs.pop_back();
2786 : checkMore = true;
2787 : }
2788 : }
2789 : // remove targets to the right
2790 : checkMore = true;
2791 20 : while (signedDirs.size() < targets.size() && checkMore) {
2792 : checkMore = false;
2793 3 : if (sumoDirs.front() != signedDirs.front()) {
2794 : targets.erase(targets.begin());
2795 : sumoDirs.erase(sumoDirs.begin());
2796 : checkMore = true;
2797 : }
2798 : }
2799 : // remove targets by permissions
2800 : int i = 0;
2801 25 : while (signedDirs.size() < targets.size() && i < (int)targets.size()) {
2802 8 : if (targets[i] != nullptr && (targets[i]->getPermissions() & defaultPermissions) == 0) {
2803 : targets.erase(targets.begin() + i);
2804 : sumoDirs.erase(sumoDirs.begin() + i);
2805 : } else {
2806 8 : i++;
2807 : }
2808 : }
2809 17 : if (signedDirs.size() != targets.size()) {
2810 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());
2811 : return false;
2812 : }
2813 17 : }
2814 : // directions and connections are both sorted from right to left
2815 87 : for (int i = 0; i < (int)signedDirs.size(); i++) {
2816 62 : dirMap[signedDirs[i]] = targets[i];
2817 : }
2818 : // check whether we have enough target lanes for a each signed direction
2819 83 : for (auto item : signCons) {
2820 60 : const LinkDirection dir = item.first;
2821 60 : if (dir == LinkDirection::NODIR) {
2822 2 : continue;
2823 : }
2824 58 : const NBEdge* to = dirMap[dir];
2825 58 : int candidates = to->getNumLanesThatAllow(defaultPermissions, false);
2826 58 : if (candidates == 0) {
2827 4 : WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because the target edge '%' has no suitable lanes"), getID(), to->getID());
2828 2 : return false;
2829 : }
2830 57 : std::vector<int>& knownTargets = toLaneMap[to];
2831 57 : if ((int)knownTargets.size() < item.second) {
2832 4 : if (candidates < item.second) {
2833 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"),
2834 : getID(), item.second, toString(dir), to->getID(), candidates);
2835 1 : return false;
2836 : }
2837 : int i;
2838 : int iInc;
2839 : int iEnd;
2840 3 : if (dir > LinkDirection::STRAIGHT) {
2841 : // set more targets on the left
2842 1 : i = to->getNumLanes() - 1;
2843 : iInc = -1;
2844 : iEnd = -1;
2845 : } else {
2846 : // set more targets on the right
2847 2 : i = 0;
2848 : iInc = 1;
2849 2 : iEnd = to->getNumLanes();
2850 : }
2851 8 : while ((int)knownTargets.size() < item.second && i != iEnd) {
2852 5 : if ((to->getPermissions(i) & defaultPermissions) != 0) {
2853 5 : if (std::find(knownTargets.begin(), knownTargets.end(), i) == knownTargets.end()) {
2854 3 : knownTargets.push_back(i);
2855 : }
2856 : }
2857 5 : i += iInc;
2858 : }
2859 3 : if ((int)knownTargets.size() != item.second) {
2860 0 : WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because not enough target lanes could be determined for direction '%'"), getID(), toString(dir));
2861 0 : return false;
2862 : }
2863 3 : std::sort(knownTargets.begin(), knownTargets.end());
2864 : }
2865 : }
2866 : std::map<const NBEdge*, int> toLaneIndex;
2867 79 : for (int i = 0; i < getNumLanes(); i++) {
2868 56 : const int turnSigns = myLanes[i].turnSigns;
2869 : // no turnSigns are given for bicycle lanes and sidewalks
2870 56 : if (turnSigns != 0) {
2871 : // clear existing connections
2872 253 : for (auto it = myConnections.begin(); it != myConnections.end();) {
2873 197 : if (it->fromLane == i) {
2874 80 : it = myConnections.erase(it);
2875 : } else {
2876 : it++;
2877 : }
2878 : }
2879 : // add new connections
2880 56 : int allSigns = (turnSigns
2881 56 : | turnSigns >> TURN_SIGN_SHIFT_BUS
2882 56 : | turnSigns >> TURN_SIGN_SHIFT_TAXI
2883 56 : | turnSigns >> TURN_SIGN_SHIFT_BICYCLE);
2884 56 : std::vector<LinkDirection> all = decodeTurnSigns(turnSigns);
2885 56 : std::vector<LinkDirection> bus = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BUS);
2886 56 : std::vector<LinkDirection> taxi = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_TAXI);
2887 56 : std::vector<LinkDirection> bike = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BICYCLE);
2888 : //std::cout << " allSigns=" << allSigns << " turnSigns=" << turnSigns << " bus=" << bus.size() << "\n";
2889 56 : SVCPermissions fromP = getPermissions(i);
2890 56 : if ((fromP & SVC_PASSENGER) != 0) {
2891 : // if the source permits passenger traffic, the target should too
2892 : fromP = SVC_PASSENGER;
2893 : }
2894 125 : for (LinkDirection dir : decodeTurnSigns(allSigns)) {
2895 69 : SVCPermissions perm = 0;
2896 69 : updateTurnPermissions(perm, dir, SVCAll, all);
2897 69 : updateTurnPermissions(perm, dir, SVC_BUS, bus);
2898 69 : updateTurnPermissions(perm, dir, SVC_TAXI, taxi);
2899 69 : updateTurnPermissions(perm, dir, SVC_BICYCLE, bike);
2900 69 : if (perm == SVCAll) {
2901 67 : perm = SVC_UNSPECIFIED;
2902 : }
2903 : //std::cout << " lane=" << i << " dir=" << toString(dir) << " perm=" << getVehicleClassNames(perm) << "\n";
2904 69 : NBEdge* to = const_cast<NBEdge*>(dirMap[dir]);
2905 69 : if (to != nullptr) {
2906 : if (toLaneIndex.count(to) == 0) {
2907 : // initialize to rightmost feasible lane
2908 55 : int toLane = toLaneMap[to][0];
2909 57 : while ((to->getPermissions(toLane) & fromP) == 0 && (toLane + 1 < to->getNumLanes())) {
2910 : toLane++;
2911 : /*
2912 : if (toLane == to->getNumLanes()) {
2913 : SOFT_ASSERT(false);
2914 : #ifdef DEBUG_TURNSIGNS
2915 : std::cout << " could not find passenger lane for target=" << to->getID() << "\n";
2916 : #endif
2917 : return false;
2918 : }
2919 : */
2920 : }
2921 : #ifdef DEBUG_TURNSIGNS
2922 : std::cout << " target=" << to->getID() << " initial toLane=" << toLane << "\n";
2923 : #endif
2924 55 : toLaneIndex[to] = toLane;
2925 : }
2926 : #ifdef DEBUG_TURNSIGNS
2927 : //std::cout << " set fromLane=" << i << " to=" << to->getID() << " toLane=" << toLaneIndex[to] << "\n";
2928 : #endif
2929 67 : setConnection(i, to, toLaneIndex[to], Lane2LaneInfoType::VALIDATED, true,
2930 : false, KEEPCLEAR_UNSPECIFIED, UNSPECIFIED_CONTPOS,
2931 : UNSPECIFIED_VISIBILITY_DISTANCE, UNSPECIFIED_SPEED, UNSPECIFIED_FRICTION,
2932 : myDefaultConnectionLength, PositionVector::EMPTY,
2933 : UNSPECIFIED_CONNECTION_UNCONTROLLED,
2934 : perm);
2935 67 : if (toLaneIndex[to] < to->getNumLanes() - 1
2936 67 : && (to->getPermissions(toLaneIndex[to] + 1) & fromP) != 0) {
2937 13 : toLaneIndex[to]++;
2938 54 : } else if (toLaneIndex[to] < to->getNumLanes() - 2
2939 54 : && (to->getPermissions(toLaneIndex[to] + 2) & fromP) != 0) {
2940 : // skip forbidden lane
2941 0 : toLaneIndex[to] += 2;
2942 : }
2943 : }
2944 56 : }
2945 56 : }
2946 : }
2947 23 : sortOutgoingConnectionsByAngle();
2948 23 : sortOutgoingConnectionsByIndex();
2949 : return true;
2950 68 : }
2951 :
2952 :
2953 : bool
2954 102504 : NBEdge::recheckLanes() {
2955 : #ifdef DEBUG_CONNECTION_GUESSING
2956 : if (DEBUGCOND) {
2957 : std::cout << "recheckLanes (initial) edge=" << getID() << "\n";
2958 : for (Connection& c : myConnections) {
2959 : std::cout << " conn " << c.getDescription(this) << "\n";
2960 : }
2961 : for (Connection& c : myConnectionsToDelete) {
2962 : std::cout << " connToDelete " << c.getDescription(this) << "\n";
2963 : }
2964 : }
2965 : #endif
2966 : // check delayed removals
2967 109607 : for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
2968 7103 : removeFromConnections(it->toEdge, it->fromLane, it->toLane, false, false, true);
2969 : }
2970 102504 : std::vector<int> connNumbersPerLane(myLanes.size(), 0);
2971 309512 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
2972 207008 : if ((*i).toEdge == nullptr || (*i).fromLane < 0 || (*i).toLane < 0) {
2973 105 : i = myConnections.erase(i);
2974 : } else {
2975 : if ((*i).fromLane >= 0) {
2976 206903 : ++connNumbersPerLane[(*i).fromLane];
2977 : }
2978 : ++i;
2979 : }
2980 : }
2981 102504 : if (myStep != EdgeBuildingStep::LANES2LANES_DONE && myStep != EdgeBuildingStep::LANES2LANES_USER) {
2982 : #ifdef DEBUG_TURNSIGNS
2983 : if (myLanes.back().turnSigns != 0) {
2984 : std::cout << getID() << " hasTurnSigns\n";
2985 : if (myTurnSignTarget != myTo->getID()) {
2986 : std::cout << " tst=" << myTurnSignTarget << " to=" << myTo->getID() << "\n";
2987 : }
2988 : }
2989 : #endif
2990 57471 : if (myLanes.back().turnSigns == 0 || myTurnSignTarget != myTo->getID() || !applyTurnSigns()) {
2991 : // check #1:
2992 : // If there is a lane with no connections and any neighbour lane has
2993 : // more than one connections, try to move one of them.
2994 : // This check is only done for edges which connections were assigned
2995 : // using the standard algorithm.
2996 132010 : for (int i = 0; i < (int)myLanes.size(); i++) {
2997 74562 : if (connNumbersPerLane[i] == 0 && !isForbidden(getPermissions(i) & ~SVC_PEDESTRIAN)) {
2998 : // dead-end lane found
2999 : bool hasDeadEnd = true;
3000 : // find lane with two connections or more to the right of the current lane
3001 3713 : for (int i2 = i - 1; hasDeadEnd && i2 >= 0; i2--) {
3002 2203 : if (getPermissions(i) != getPermissions(i2)) {
3003 : break;
3004 : }
3005 1537 : if (connNumbersPerLane[i2] > 1) {
3006 356 : connNumbersPerLane[i2]--;
3007 848 : for (int i3 = i2; i3 != i; i3++) {
3008 492 : moveConnectionToLeft(i3);
3009 492 : sortOutgoingConnectionsByAngle();
3010 492 : sortOutgoingConnectionsByIndex();
3011 : }
3012 : hasDeadEnd = false;
3013 : }
3014 : }
3015 2176 : if (hasDeadEnd) {
3016 : // find lane with two connections or more to the left of the current lane
3017 2835 : for (int i2 = i + 1; hasDeadEnd && i2 < getNumLanes(); i2++) {
3018 1102 : if (getPermissions(i) != getPermissions(i2)) {
3019 : break;
3020 : }
3021 1015 : if (connNumbersPerLane[i2] > 1) {
3022 75 : connNumbersPerLane[i2]--;
3023 176 : for (int i3 = i2; i3 != i; i3--) {
3024 101 : moveConnectionToRight(i3);
3025 101 : sortOutgoingConnectionsByAngle();
3026 101 : sortOutgoingConnectionsByIndex();
3027 : }
3028 : hasDeadEnd = false;
3029 : }
3030 : }
3031 : }
3032 1820 : if (hasDeadEnd && myTo->getOutgoingEdges().size() > 1) {
3033 : int passengerLanes = 0;
3034 : int passengerTargetLanes = 0;
3035 905 : for (const Lane& lane : myLanes) {
3036 694 : if ((lane.permissions & SVC_PASSENGER) != 0) {
3037 567 : passengerLanes++;
3038 : }
3039 : }
3040 787 : for (const NBEdge* out : myTo->getOutgoingEdges()) {
3041 576 : if (!isTurningDirectionAt(out)) {
3042 1028 : for (const Lane& lane : out->getLanes()) {
3043 636 : if ((lane.permissions & SVC_PASSENGER) != 0) {
3044 514 : passengerTargetLanes++;
3045 : }
3046 : }
3047 : }
3048 : }
3049 211 : if (passengerLanes > 0 && passengerLanes <= passengerTargetLanes) {
3050 : // no need for dead-ends
3051 39 : if (i > 0) {
3052 : // check if a connection to the right has a usable target to the left of its target
3053 28 : std::vector<Connection> rightCons = getConnectionsFromLane(i - 1);
3054 28 : if (rightCons.size() > 0) {
3055 : const Connection& rc = rightCons.back();
3056 28 : NBEdge* to = rc.toEdge;
3057 28 : int toLane = rc.toLane + 1;
3058 : if (toLane < to->getNumLanes()
3059 0 : && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
3060 28 : && !hasConnectionTo(to, toLane)) {
3061 : #ifdef DEBUG_CONNECTION_CHECKING
3062 : std::cout << " recheck1 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
3063 : #endif
3064 0 : setConnection(i, to, toLane, Lane2LaneInfoType::COMPUTED);
3065 : hasDeadEnd = false;
3066 0 : sortOutgoingConnectionsByAngle();
3067 0 : sortOutgoingConnectionsByIndex();
3068 : }
3069 : if (hasDeadEnd) {
3070 : // check if a connection to the right has a usable target to the right of its target
3071 28 : toLane = rc.toLane - 1;
3072 : if (toLane >= 0
3073 7 : && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(rc.toLane)) != 0
3074 7 : && (getPermissions(rc.fromLane) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
3075 34 : && !hasConnectionTo(to, toLane)) {
3076 : // shift the right lane connection target right and connect the dead lane to the old target
3077 1 : getConnectionRef(rc.fromLane, to, rc.toLane).toLane = toLane;
3078 : #ifdef DEBUG_CONNECTION_CHECKING
3079 : std::cout << " recheck2 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << (toLane + 1) << "\n";
3080 : #endif
3081 1 : setConnection(i, to, toLane + 1, Lane2LaneInfoType::COMPUTED);
3082 : hasDeadEnd = false;
3083 1 : sortOutgoingConnectionsByAngle();
3084 1 : sortOutgoingConnectionsByIndex();
3085 : }
3086 : }
3087 : }
3088 28 : }
3089 39 : if (hasDeadEnd && i < getNumLanes() - 1) {
3090 : // check if a connection to the left has a usable target to the right of its target
3091 30 : std::vector<Connection> leftCons = getConnectionsFromLane(i + 1);
3092 30 : if (leftCons.size() > 0) {
3093 30 : NBEdge* to = leftCons.front().toEdge;
3094 30 : int toLane = leftCons.front().toLane - 1;
3095 : if (toLane >= 0
3096 4 : && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
3097 34 : && !hasConnectionTo(to, toLane)) {
3098 : #ifdef DEBUG_CONNECTION_CHECKING
3099 : std::cout << " recheck3 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
3100 : #endif
3101 4 : setConnection(i, to, toLane, Lane2LaneInfoType::COMPUTED);
3102 : hasDeadEnd = false;
3103 4 : sortOutgoingConnectionsByAngle();
3104 4 : sortOutgoingConnectionsByIndex();
3105 : }
3106 : }
3107 30 : }
3108 : #ifdef ADDITIONAL_WARNINGS
3109 : if (hasDeadEnd) {
3110 : WRITE_WARNING("Found dead-end lane " + getLaneID(i));
3111 : }
3112 : #endif
3113 : }
3114 : }
3115 : }
3116 : }
3117 57448 : removeInvalidConnections();
3118 : }
3119 : }
3120 : // check involuntary dead end at "real" junctions
3121 102504 : if (getPermissions() != SVC_PEDESTRIAN) {
3122 88653 : if (myConnections.empty() && myTo->getOutgoingEdges().size() > 1 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
3123 220 : WRITE_WARNINGF(TL("Edge '%' is not connected to outgoing edges at junction '%'."), getID(), myTo->getID());
3124 : }
3125 88653 : const EdgeVector& incoming = myFrom->getIncomingEdges();
3126 88653 : if (incoming.size() > 1) {
3127 147844 : for (int i = 0; i < (int)myLanes.size(); i++) {
3128 82966 : if (getPermissions(i) != 0 && getPermissions(i) != SVC_PEDESTRIAN) {
3129 : bool connected = false;
3130 105323 : for (std::vector<NBEdge*>::const_iterator in = incoming.begin(); in != incoming.end(); ++in) {
3131 105183 : if ((*in)->hasConnectionTo(this, i)) {
3132 : connected = true;
3133 : break;
3134 : }
3135 : }
3136 78758 : if (!connected) {
3137 420 : WRITE_WARNINGF(TL("Lane '%' is not connected from any incoming edge at junction '%'."), getLaneID(i), myFrom->getID());
3138 : }
3139 : }
3140 : }
3141 : }
3142 : }
3143 : // avoid deadend due to change prohibitions
3144 102504 : if (getNumLanes() > 1 && myConnections.size() > 0) {
3145 66880 : for (int i = 0; i < (int)myLanes.size(); i++) {
3146 47341 : Lane& lane = myLanes[i];
3147 41068 : if ((connNumbersPerLane[i] == 0 || ((lane.accelRamp || (i > 0 && myLanes[i - 1].accelRamp && connNumbersPerLane[i - 1] > 0))
3148 53 : && getSuccessors(SVC_PASSENGER).size() > 1))
3149 47356 : && getPermissions(i) != SVC_PEDESTRIAN && !isForbidden(getPermissions(i))) {
3150 2314 : const bool forbiddenLeft = lane.changeLeft != SVCAll && lane.changeLeft != SVC_IGNORING && lane.changeLeft != SVC_UNSPECIFIED;
3151 2314 : const bool forbiddenRight = lane.changeRight != SVCAll && lane.changeRight != SVC_IGNORING && lane.changeRight != SVC_UNSPECIFIED;
3152 2314 : if (forbiddenLeft && (i == 0 || forbiddenRight)) {
3153 2 : lane.changeLeft = SVC_UNSPECIFIED;
3154 4 : WRITE_WARNINGF(TL("Ignoring changeLeft prohibition for '%' to avoid dead-end"), getLaneID(i));
3155 2312 : } else if (forbiddenRight && (i == getNumLanes() - 1 || (i > 0 && myLanes[i - 1].accelRamp))) {
3156 1 : lane.changeRight = SVC_UNSPECIFIED;
3157 2 : WRITE_WARNINGF(TL("Ignoring changeRight prohibition for '%' to avoid dead-end"), getLaneID(i));
3158 : }
3159 : }
3160 : }
3161 : }
3162 : #ifdef ADDITIONAL_WARNINGS
3163 : // check for connections with bad access permissions
3164 : for (const Connection& c : myConnections) {
3165 : SVCPermissions fromP = getPermissions(c.fromLane);
3166 : SVCPermissions toP = c.toEdge->getPermissions(c.toLane);
3167 : if ((fromP & SVC_PASSENGER) != 0
3168 : && toP == SVC_BICYCLE) {
3169 : bool hasAlternative = false;
3170 : for (const Connection& c2 : myConnections) {
3171 : if (c.fromLane == c2.fromLane && c.toEdge == c2.toEdge
3172 : && (c.toEdge->getPermissions(c2.toLane) & SVC_PASSENGER) != 0) {
3173 : hasAlternative = true;
3174 : }
3175 : }
3176 : if (!hasAlternative) {
3177 : WRITE_WARNING("Road lane ends on bikeLane for connection " + c.getDescription(this));
3178 : }
3179 : }
3180 : }
3181 :
3182 : #endif
3183 : #ifdef DEBUG_CONNECTION_GUESSING
3184 : if (DEBUGCOND) {
3185 : std::cout << "recheckLanes (final) edge=" << getID() << "\n";
3186 : for (Connection& c : myConnections) {
3187 : std::cout << " conn " << c.getDescription(this) << "\n";
3188 : }
3189 : }
3190 : #endif
3191 102504 : if (myStep != EdgeBuildingStep::LANES2LANES_USER) {
3192 69367 : myStep = EdgeBuildingStep::LANES2LANES_DONE;
3193 : }
3194 102504 : return true;
3195 102504 : }
3196 :
3197 :
3198 102551 : void NBEdge::recheckOpposite(const NBEdgeCont& ec, bool fixOppositeLengths) {
3199 102551 : if (getNumLanes() == 0) {
3200 0 : return;
3201 : }
3202 102551 : const int leftmostLane = getNumLanes() - 1;
3203 : // check oppositeID stored in other lanes
3204 133293 : for (int i = 0; i < leftmostLane; i++) {
3205 30742 : const std::string& oppositeID = getLanes()[i].oppositeID;
3206 61484 : NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
3207 30742 : if (oppositeID != "" && oppositeID != "-") {
3208 6 : if (getLanes().back().oppositeID == "" && oppEdge != nullptr) {
3209 1 : getLaneStruct(leftmostLane).oppositeID = oppositeID;
3210 3 : WRITE_WARNINGF(TL("Moving opposite lane '%' from invalid lane '%' to lane index %."), oppositeID, getLaneID(i), leftmostLane);
3211 : } else {
3212 15 : WRITE_WARNINGF(TL("Removing opposite lane '%' for invalid lane '%'."), oppositeID, getLaneID(i));
3213 : }
3214 6 : getLaneStruct(i).oppositeID = "";
3215 : }
3216 : }
3217 102551 : const std::string& oppositeID = getLanes().back().oppositeID;
3218 102551 : if (oppositeID != "" && oppositeID != "-") {
3219 112 : NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
3220 112 : if (oppEdge == nullptr) {
3221 3 : WRITE_WARNINGF(TL("Removing unknown opposite lane '%' for edge '%'."), oppositeID, getID());
3222 1 : getLaneStruct(leftmostLane).oppositeID = "";
3223 : } else {
3224 111 : if (oppEdge->getFromNode() != getToNode() || oppEdge->getToNode() != getFromNode()) {
3225 3 : WRITE_WARNINGF(TL("Opposite lane '%' does not reverse-connect the same nodes as edge '%'!"), oppositeID, getID());
3226 1 : getLaneStruct(getNumLanes() - 1).oppositeID = "";
3227 : } else {
3228 220 : if (oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
3229 1 : const std::string oppEdgeLeftmost = oppEdge->getLaneID(oppEdge->getNumLanes() - 1);
3230 4 : WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppositeID, getID(), oppEdgeLeftmost);
3231 1 : getLaneStruct(leftmostLane).oppositeID = oppEdgeLeftmost;
3232 : }
3233 110 : NBEdge::Lane& oppLane = oppEdge->getLaneStruct(oppEdge->getNumLanes() - 1);
3234 110 : const std::string leftmostID = getLaneID(leftmostLane);
3235 110 : if (oppLane.oppositeID == "") {
3236 15 : WRITE_WARNINGF(TL("Adapting missing opposite lane '%' for edge '%'."), leftmostID, oppEdge->getID());
3237 : oppLane.oppositeID = leftmostID;
3238 105 : } else if (oppLane.oppositeID != leftmostID && oppLane.oppositeID != "-") {
3239 1 : const std::string oppOpp = oppLane.oppositeID.substr(0, oppLane.oppositeID.rfind("_"));
3240 1 : NBEdge* oppOppEdge = ec.retrieve(oppOpp);
3241 1 : if (oppOppEdge == nullptr) {
3242 0 : WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppLane.oppositeID, oppEdge->getID(), leftmostID);
3243 : oppLane.oppositeID = leftmostID;
3244 : } else {
3245 1 : if (oppEdge->getFromNode() != oppOppEdge->getToNode() || oppEdge->getToNode() != oppOppEdge->getFromNode()) {
3246 0 : WRITE_ERRORF(TL("Opposite edge '%' does not reverse-connect the same nodes as edge '%'!"), oppEdge->getID(), oppOppEdge->getID());
3247 : } else {
3248 5 : WRITE_WARNINGF(TL("Adapting inconsistent opposite lanes for edges '%', '%' and '%'."), getID(), oppEdge->getID(), oppOpp);
3249 : }
3250 : oppLane.oppositeID = leftmostID;
3251 1 : NBEdge::Lane& oppOppLane = oppOppEdge->getLaneStruct(oppOppEdge->getNumLanes() - 1);
3252 1 : if (oppOppLane.oppositeID == oppEdge->getLaneID(oppEdge->getNumLanes() - 1)) {
3253 : oppOppLane.oppositeID = "";
3254 : }
3255 : }
3256 : }
3257 110 : if (fabs(oppEdge->getLoadedLength() - getLoadedLength()) > NUMERICAL_EPS) {
3258 3 : if (fixOppositeLengths) {
3259 1 : const double avgLength = 0.5 * (getFinalLength() + oppEdge->getFinalLength());
3260 4 : WRITE_WARNINGF(TL("Averaging edge lengths for lane '%' (length %) and edge '%' (length %)."),
3261 : oppositeID, oppEdge->getLoadedLength(), getID(), getLoadedLength());
3262 1 : setLoadedLength(avgLength);
3263 1 : oppEdge->setLoadedLength(avgLength);
3264 : } else {
3265 10 : WRITE_ERROR("Opposite lane '" + oppositeID + "' (length " + toString(oppEdge->getLoadedLength()) +
3266 : ") differs in length from edge '" + getID() + "' (length " +
3267 : toString(getLoadedLength()) + "). Set --opposites.guess.fix-lengths to fix this.");
3268 2 : getLaneStruct(getNumLanes() - 1).oppositeID = "";
3269 : }
3270 : }
3271 : }
3272 : }
3273 : }
3274 : // check for matching bidi lane shapes (at least for the simple case of 1-lane edges)
3275 102551 : const NBEdge* bidi = getBidiEdge();
3276 102551 : if (bidi != nullptr && getNumLanes() == 1 && bidi->getNumLanes() == 1 && getID() < bidi->getID()) {
3277 4864 : getLaneStruct(0).shape = bidi->getLaneStruct(0).shape.reverse();
3278 : }
3279 : // check for valid offset and speed
3280 107405 : const double startOffset = isBidiRail() ? getTurnDestination(true)->getEndOffset() : 0;
3281 : int i = 0;
3282 235844 : for (const NBEdge::Lane& l : getLanes()) {
3283 133293 : if (startOffset + l.endOffset > getLength()) {
3284 4 : WRITE_WARNINGF(TL("Invalid endOffset % at lane '%' with length % (startOffset %)."),
3285 : toString(l.endOffset), getLaneID(i), toString(l.shape.length()), toString(startOffset));
3286 133291 : } else if (l.speed < 0.) {
3287 0 : WRITE_WARNINGF(TL("Negative allowed speed (%) on lane '%', use --speed.minimum to prevent this."), toString(l.speed), getLaneID(i));
3288 133291 : } else if (l.speed == 0.) {
3289 0 : WRITE_WARNINGF(TL("Lane '%' has a maximum allowed speed of 0."), getLaneID(i));
3290 : }
3291 133293 : i++;
3292 : }
3293 : }
3294 :
3295 57448 : void NBEdge::removeInvalidConnections() {
3296 : // check restrictions
3297 189499 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
3298 : Connection& c = *i;
3299 132051 : const SVCPermissions common = getPermissions(c.fromLane) & c.toEdge->getPermissions(c.toLane);
3300 132051 : if (common == SVC_PEDESTRIAN || getPermissions(c.fromLane) == SVC_PEDESTRIAN) {
3301 : // these are computed in NBNode::buildWalkingAreas
3302 : #ifdef DEBUG_CONNECTION_CHECKING
3303 : std::cout << " remove pedCon " << c.getDescription(this) << "\n";
3304 : #endif
3305 475 : i = myConnections.erase(i);
3306 131576 : } else if (common == 0) {
3307 : // no common permissions.
3308 : // try to find a suitable target lane to the right
3309 83 : const int origToLane = c.toLane;
3310 83 : c.toLane = -1; // ignore this connection when calling hasConnectionTo
3311 : int toLane = origToLane;
3312 83 : while (toLane > 0
3313 45 : && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
3314 143 : && !hasConnectionTo(c.toEdge, toLane)
3315 : ) {
3316 30 : toLane--;
3317 : }
3318 83 : if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
3319 83 : && !hasConnectionTo(c.toEdge, toLane)) {
3320 4 : c.toLane = toLane;
3321 : ++i;
3322 : } else {
3323 : // try to find a suitable target lane to the left
3324 : toLane = origToLane;
3325 148 : while (toLane < (int)c.toEdge->getNumLanes() - 1
3326 95 : && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
3327 217 : && !hasConnectionTo(c.toEdge, toLane)
3328 : ) {
3329 69 : toLane++;
3330 : }
3331 79 : if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
3332 79 : && !hasConnectionTo(c.toEdge, toLane)) {
3333 0 : c.toLane = toLane;
3334 : ++i;
3335 : } else {
3336 : // no alternative target found
3337 : #ifdef DEBUG_CONNECTION_CHECKING
3338 : std::cout << " remove " << c.getDescription(this) << " with no alternative target\n";
3339 : #endif
3340 79 : i = myConnections.erase(i);
3341 : }
3342 : }
3343 139566 : } else if (isRailway(getPermissions(c.fromLane)) && isRailway(c.toEdge->getPermissions(c.toLane))
3344 139486 : && isTurningDirectionAt(c.toEdge)) {
3345 : // do not allow sharp rail turns
3346 : #ifdef DEBUG_CONNECTION_CHECKING
3347 : std::cout << " remove " << c.getDescription(this) << " (rail turnaround)\n";
3348 : #endif
3349 2603 : i = myConnections.erase(i);
3350 : } else {
3351 : ++i;
3352 : }
3353 : }
3354 57448 : }
3355 :
3356 : void
3357 66904 : NBEdge::divideOnEdges(const EdgeVector* outgoing) {
3358 66904 : if (outgoing->size() == 0) {
3359 : // we have to do this, because the turnaround may have been added before
3360 : myConnections.clear();
3361 11112 : return;
3362 : }
3363 :
3364 : #ifdef DEBUG_CONNECTION_GUESSING
3365 : if (DEBUGCOND) {
3366 : std::cout << " divideOnEdges " << getID() << " outgoing=" << toString(*outgoing) << "\n";
3367 : }
3368 : #endif
3369 :
3370 : // build connections for miv lanes
3371 : std::vector<int> availableLanes;
3372 127214 : for (int i = 0; i < (int)myLanes.size(); ++i) {
3373 71422 : if ((getPermissions(i) & SVC_PASSENGER) != 0) {
3374 45998 : availableLanes.push_back(i);
3375 : }
3376 : }
3377 55792 : if (availableLanes.size() > 0) {
3378 34264 : divideSelectedLanesOnEdges(outgoing, availableLanes);
3379 : }
3380 : // build connections for miscellaneous further modes (more than bike,peds,bus and without passenger)
3381 : availableLanes.clear();
3382 127214 : for (int i = 0; i < (int)myLanes.size(); ++i) {
3383 71422 : const SVCPermissions perms = getPermissions(i);
3384 71422 : if ((perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) == 0 || (perms & SVC_PASSENGER) != 0 || isForbidden(perms)) {
3385 61302 : continue;
3386 : }
3387 10120 : availableLanes.push_back(i);
3388 : }
3389 55792 : if (availableLanes.size() > 0) {
3390 10049 : divideSelectedLanesOnEdges(outgoing, availableLanes);
3391 : }
3392 : // build connections for busses from lanes that were excluded in the previous step
3393 : availableLanes.clear();
3394 127214 : for (int i = 0; i < (int)myLanes.size(); ++i) {
3395 71422 : const SVCPermissions perms = getPermissions(i);
3396 71422 : if ((perms & SVC_BUS) == 0 || (perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) != 0 || (perms & SVC_PASSENGER) != 0) {
3397 71166 : continue;
3398 : }
3399 256 : availableLanes.push_back(i);
3400 : }
3401 55792 : if (availableLanes.size() > 0) {
3402 253 : divideSelectedLanesOnEdges(outgoing, availableLanes);
3403 : }
3404 : // build connections for bicycles (possibly combined with pedestrians)
3405 : availableLanes.clear();
3406 127214 : for (int i = 0; i < (int)myLanes.size(); ++i) {
3407 71422 : const SVCPermissions perms = getPermissions(i);
3408 71422 : if (perms != SVC_BICYCLE && perms != (SVC_BICYCLE | SVC_PEDESTRIAN)) {
3409 69720 : continue;
3410 : }
3411 1702 : availableLanes.push_back(i);
3412 : }
3413 55792 : if (availableLanes.size() > 0) {
3414 1677 : divideSelectedLanesOnEdges(outgoing, availableLanes);
3415 : }
3416 : // clean up unassigned fromLanes
3417 : bool explicitTurnaround = false;
3418 55792 : SVCPermissions turnaroundPermissions = SVC_UNSPECIFIED;
3419 244991 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
3420 189199 : if ((*i).fromLane == -1) {
3421 89704 : if ((*i).toEdge == myTurnDestination && myTurnDestination != nullptr) {
3422 : explicitTurnaround = true;
3423 109 : turnaroundPermissions = (*i).permissions;
3424 : }
3425 89704 : if ((*i).permissions != SVC_UNSPECIFIED) {
3426 1490 : for (Connection& c : myConnections) {
3427 1294 : if (c.toLane == -1 && c.toEdge == (*i).toEdge) {
3428 : // carry over loaded edge2edge permissions
3429 353 : c.permissions = (*i).permissions;
3430 : }
3431 : }
3432 : }
3433 89704 : i = myConnections.erase(i);
3434 : } else {
3435 : ++i;
3436 : }
3437 : }
3438 55792 : if (explicitTurnaround) {
3439 218 : myConnections.push_back(Connection((int)myLanes.size() - 1, myTurnDestination, myTurnDestination->getNumLanes() - 1));
3440 109 : myConnections.back().permissions = turnaroundPermissions;
3441 : }
3442 55792 : sortOutgoingConnectionsByIndex();
3443 55792 : }
3444 :
3445 :
3446 : void
3447 46243 : NBEdge::divideSelectedLanesOnEdges(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
3448 46243 : const std::vector<int>& priorities = prepareEdgePriorities(outgoing, availableLanes);
3449 46243 : if (priorities.empty()) {
3450 : return;
3451 : }
3452 : #ifdef DEBUG_CONNECTION_GUESSING
3453 : if (DEBUGCOND) {
3454 : std::cout << "divideSelectedLanesOnEdges " << getID() << " out=" << toString(*outgoing) << " prios=" << toString(priorities) << " avail=" << toString(availableLanes) << "\n";
3455 : }
3456 : #endif
3457 : // compute the resulting number of lanes that should be used to reach the following edge
3458 46195 : const int numOutgoing = (int)outgoing->size();
3459 : std::vector<int> resultingLanesFactor;
3460 46195 : resultingLanesFactor.reserve(numOutgoing);
3461 : int minResulting = std::numeric_limits<int>::max();
3462 136775 : for (int i = 0; i < numOutgoing; i++) {
3463 : // res / minResulting will be the number of lanes which are meant to reach the current outgoing edge
3464 90580 : const int res = priorities[i] * (int)availableLanes.size();
3465 90580 : resultingLanesFactor.push_back(res);
3466 90580 : if (minResulting > res && res > 0) {
3467 : // prevent minResulting from becoming 0
3468 : minResulting = res;
3469 : }
3470 : }
3471 : // compute the number of virtual edges
3472 : // a virtual edge is used as a replacement for a real edge from now on
3473 : // it shall allow to divide the existing lanes on this structure without
3474 : // regarding the structure of outgoing edges
3475 : int numVirtual = 0;
3476 : // compute the transition from virtual to real edges
3477 : EdgeVector transition;
3478 46195 : transition.reserve(numOutgoing);
3479 136775 : for (int i = 0; i < numOutgoing; i++) {
3480 : // tmpNum will be the number of connections from this edge to the next edge
3481 : assert(i < (int)resultingLanesFactor.size());
3482 90580 : const int tmpNum = (resultingLanesFactor[i] + minResulting - 1) / minResulting; // integer division rounding up
3483 90580 : numVirtual += tmpNum;
3484 515295 : for (int j = 0; j < tmpNum; j++) {
3485 424715 : transition.push_back((*outgoing)[i]);
3486 : }
3487 : }
3488 : #ifdef DEBUG_CONNECTION_GUESSING
3489 : if (DEBUGCOND) {
3490 : std::cout << " minResulting=" << minResulting << " numVirtual=" << numVirtual << " availLanes=" << toString(availableLanes) << " resLanes=" << toString(resultingLanesFactor) << " transition=" << toString(transition) << "\n";
3491 : }
3492 : #endif
3493 :
3494 : // assign lanes to edges
3495 : // (conversion from virtual to real edges is done)
3496 : ToEdgeConnectionsAdder adder(transition);
3497 46195 : Bresenham::compute(&adder, static_cast<int>(availableLanes.size()), numVirtual);
3498 : const std::map<NBEdge*, std::vector<int> >& l2eConns = adder.getBuiltConnections();
3499 136775 : for (NBEdge* const target : *outgoing) {
3500 : assert(l2eConns.find(target) != l2eConns.end());
3501 191837 : for (const int j : l2eConns.find(target)->second) {
3502 101257 : const int fromIndex = availableLanes[j];
3503 101257 : if ((getPermissions(fromIndex) & target->getPermissions()) == 0) {
3504 : // exclude connection if fromLane and toEdge have no common permissions
3505 60 : continue;
3506 : }
3507 101197 : if ((getPermissions(fromIndex) & target->getPermissions()) == SVC_PEDESTRIAN) {
3508 : // exclude connection if the only commonly permitted class are pedestrians
3509 : // these connections are later built in NBNode::buildWalkingAreas
3510 251 : continue;
3511 : }
3512 : // avoid building more connections than the edge has viable lanes (earlier
3513 : // ones have precedence). This is necessary when running divideSelectedLanesOnEdges more than once.
3514 : // @todo To decide which target lanes are still available we need to do a
3515 : // preliminary lane-to-lane assignment in regard to permissions (rather than to ordering)
3516 100946 : const int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
3517 : int targetLanes = target->getNumLanes();
3518 100946 : if (target->getPermissions(0) == SVC_PEDESTRIAN) {
3519 7204 : --targetLanes;
3520 : }
3521 100946 : if (numConsToTarget >= targetLanes) {
3522 2610 : continue;
3523 : }
3524 98336 : if (myLanes[fromIndex].connectionsDone) {
3525 : // we already have complete information about connections from
3526 : // this lane. do not add anything else
3527 : #ifdef DEBUG_CONNECTION_GUESSING
3528 : if (DEBUGCOND) {
3529 : std::cout << " connectionsDone from " << getID() << "_" << fromIndex << ": ";
3530 : for (const Connection& c : getConnectionsFromLane(fromIndex)) {
3531 : std::cout << c.getDescription(this) << ", ";
3532 : }
3533 : std::cout << "\n";
3534 : }
3535 : #endif
3536 38 : continue;
3537 : }
3538 196596 : myConnections.push_back(Connection(fromIndex, target, -1));
3539 : #ifdef DEBUG_CONNECTION_GUESSING
3540 : if (DEBUGCOND) {
3541 : std::cout << " request connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3542 : }
3543 : #endif
3544 : }
3545 : }
3546 :
3547 46195 : addStraightConnections(outgoing, availableLanes, priorities);
3548 46243 : }
3549 :
3550 :
3551 : void
3552 46195 : NBEdge::addStraightConnections(const EdgeVector* outgoing, const std::vector<int>& availableLanes, const std::vector<int>& priorities) {
3553 : // ensure sufficient straight connections for the (highest-priority) straight target
3554 46195 : const int numOutgoing = (int) outgoing->size();
3555 : NBEdge* target = nullptr;
3556 : NBEdge* rightOfTarget = nullptr;
3557 : NBEdge* leftOfTarget = nullptr;
3558 : int maxPrio = 0;
3559 136775 : for (int i = 0; i < numOutgoing; i++) {
3560 90580 : if (maxPrio < priorities[i]) {
3561 75685 : const LinkDirection dir = myTo->getDirection(this, (*outgoing)[i]);
3562 75685 : if (dir == LinkDirection::STRAIGHT) {
3563 37993 : maxPrio = priorities[i];
3564 37993 : target = (*outgoing)[i];
3565 37993 : rightOfTarget = i == 0 ? outgoing->back() : (*outgoing)[i - 1];
3566 37993 : leftOfTarget = i + 1 == numOutgoing ? outgoing->front() : (*outgoing)[i + 1];
3567 : }
3568 : }
3569 : }
3570 46195 : if (target == nullptr) {
3571 : return;
3572 : }
3573 37828 : int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
3574 : int targetLanes = (int)target->getNumLanes();
3575 37828 : if (target->getPermissions(0) == SVC_PEDESTRIAN) {
3576 3211 : --targetLanes;
3577 : }
3578 37828 : const int numDesiredConsToTarget = MIN2(targetLanes, (int)availableLanes.size());
3579 : #ifdef DEBUG_CONNECTION_GUESSING
3580 : if (DEBUGCOND) {
3581 : std::cout << " checking extra lanes for target=" << target->getID() << " cons=" << numConsToTarget << " desired=" << numDesiredConsToTarget << "\n";
3582 : }
3583 : #endif
3584 : std::vector<int>::const_iterator it_avail = availableLanes.begin();
3585 38927 : while (numConsToTarget < numDesiredConsToTarget && it_avail != availableLanes.end()) {
3586 1099 : const int fromIndex = *it_avail;
3587 : if (
3588 : // not yet connected
3589 1099 : (count_if(myConnections.begin(), myConnections.end(), connections_finder(fromIndex, target, -1)) == 0)
3590 : // matching permissions
3591 651 : && ((getPermissions(fromIndex) & target->getPermissions()) != 0)
3592 : // more than pedestrians
3593 649 : && ((getPermissions(fromIndex) & target->getPermissions()) != SVC_PEDESTRIAN)
3594 : // lane not yet fully defined
3595 1746 : && !myLanes[fromIndex].connectionsDone
3596 : ) {
3597 : #ifdef DEBUG_CONNECTION_GUESSING
3598 : if (DEBUGCOND) {
3599 : std::cout << " candidate from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3600 : }
3601 : #endif
3602 : // prevent same-edge conflicts
3603 : if (
3604 : // no outgoing connections to the right from further left
3605 317 : ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
3606 : // no outgoing connections to the left from further right
3607 1262 : && (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)) {
3608 : #ifdef DEBUG_CONNECTION_GUESSING
3609 : if (DEBUGCOND) {
3610 : std::cout << " request additional connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3611 : }
3612 : #endif
3613 1146 : myConnections.push_back(Connection(fromIndex, target, -1));
3614 573 : numConsToTarget++;
3615 : } else {
3616 : #ifdef DEBUG_CONNECTION_GUESSING
3617 : if (DEBUGCOND) std::cout
3618 : << " fail check1="
3619 : << ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
3620 : << " check2=" << (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)
3621 : << " rightOfTarget=" << rightOfTarget->getID()
3622 : << " leftOfTarget=" << leftOfTarget->getID()
3623 : << "\n";
3624 : #endif
3625 :
3626 : }
3627 : }
3628 : ++it_avail;
3629 : }
3630 : }
3631 :
3632 :
3633 : const std::vector<int>
3634 46243 : NBEdge::prepareEdgePriorities(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
3635 : std::vector<int> priorities;
3636 46243 : MainDirections mainDirections(*outgoing, this, myTo, availableLanes);
3637 : const int dist = mainDirections.getStraightest();
3638 46243 : if (dist == -1) {
3639 : return priorities;
3640 : }
3641 : // copy the priorities first
3642 46195 : priorities.reserve(outgoing->size());
3643 136775 : for (const NBEdge* const out : *outgoing) {
3644 90580 : int prio = NBNode::isTrafficLight(myTo->getType()) ? 0 : out->getJunctionPriority(myTo);
3645 : assert((prio + 1) * 2 > 0);
3646 90580 : prio = (prio + 1) * 2;
3647 90580 : priorities.push_back(prio);
3648 : }
3649 : // when the right turning direction has not a higher priority, divide
3650 : // the importance by 2 due to the possibility to leave the junction
3651 : // faster from this lane
3652 : #ifdef DEBUG_CONNECTION_GUESSING
3653 : if (DEBUGCOND) std::cout << " prepareEdgePriorities " << getID()
3654 : << " outgoing=" << toString(*outgoing)
3655 : << " priorities1=" << toString(priorities)
3656 : << " dist=" << dist
3657 : << "\n";
3658 : #endif
3659 46195 : if (dist != 0 && !mainDirections.includes(MainDirections::Direction::RIGHTMOST)) {
3660 : assert(priorities.size() > 0);
3661 14628 : priorities[0] /= 2;
3662 : #ifdef DEBUG_CONNECTION_GUESSING
3663 : if (DEBUGCOND) {
3664 : std::cout << " priorities2=" << toString(priorities) << "\n";
3665 : }
3666 : #endif
3667 : }
3668 : // HEURISTIC:
3669 : // when no higher priority exists, let the forward direction be
3670 : // the main direction
3671 46195 : if (mainDirections.empty()) {
3672 : assert(dist < (int)priorities.size());
3673 10340 : priorities[dist] *= 2;
3674 : #ifdef DEBUG_CONNECTION_GUESSING
3675 : if (DEBUGCOND) {
3676 : std::cout << " priorities3=" << toString(priorities) << "\n";
3677 : }
3678 : #endif
3679 : }
3680 46195 : if (NBNode::isTrafficLight(myTo->getType())) {
3681 4364 : priorities[dist] += 1;
3682 : } else {
3683 : // try to ensure separation of left turns
3684 41831 : if (mainDirections.includes(MainDirections::Direction::RIGHTMOST) && mainDirections.includes(MainDirections::Direction::LEFTMOST)) {
3685 830 : priorities[0] /= 4;
3686 830 : priorities[(int)priorities.size() - 1] /= 2;
3687 : #ifdef DEBUG_CONNECTION_GUESSING
3688 : if (DEBUGCOND) {
3689 : std::cout << " priorities6=" << toString(priorities) << "\n";
3690 : }
3691 : #endif
3692 41001 : } else if (mainDirections.includes(MainDirections::Direction::RIGHTMOST)
3693 21529 : && outgoing->size() > 2
3694 4446 : && availableLanes.size() == 2
3695 41176 : && (*outgoing)[dist]->getPriority() == (*outgoing)[0]->getPriority()) {
3696 166 : priorities[0] /= 4;
3697 166 : priorities.back() /= 2;
3698 : #ifdef DEBUG_CONNECTION_GUESSING
3699 : if (DEBUGCOND) {
3700 : std::cout << " priorities7=" << toString(priorities) << "\n";
3701 : }
3702 : #endif
3703 : }
3704 : }
3705 46195 : if (mainDirections.includes(MainDirections::Direction::FORWARD)) {
3706 24459 : if (myLanes.size() > 2) {
3707 2933 : priorities[dist] *= 2;
3708 : #ifdef DEBUG_CONNECTION_GUESSING
3709 : if (DEBUGCOND) {
3710 : std::cout << " priorities4=" << toString(priorities) << "\n";
3711 : }
3712 : #endif
3713 : } else {
3714 21526 : priorities[dist] *= 3;
3715 : #ifdef DEBUG_CONNECTION_GUESSING
3716 : if (DEBUGCOND) {
3717 : std::cout << " priorities5=" << toString(priorities) << "\n";
3718 : }
3719 : #endif
3720 : }
3721 : }
3722 : return priorities;
3723 46243 : }
3724 :
3725 :
3726 : void
3727 65938 : NBEdge::appendTurnaround(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike, bool checkPermissions) {
3728 : // do nothing if no turnaround is known
3729 65938 : if (myTurnDestination == nullptr || myTo->getType() == SumoXMLNodeType::RAIL_CROSSING) {
3730 : return;
3731 : }
3732 : // do nothing if the destination node is controlled by a tls and no turnarounds
3733 : // shall be appended for such junctions
3734 43981 : if (noTLSControlled && myTo->isTLControlled()) {
3735 : return;
3736 : }
3737 43909 : if (noFringe && myTo->getFringeType() == FringeType::OUTER) {
3738 : return;
3739 : }
3740 : bool isDeadEnd = true;
3741 45540 : for (const Connection& c : myConnections) {
3742 36908 : if ((c.toEdge->getPermissions(c.toLane)
3743 36908 : & getPermissions(c.fromLane)
3744 36908 : & SVC_PASSENGER) != 0
3745 36908 : || (c.toEdge->getPermissions() & getPermissions()) == getPermissions()) {
3746 : isDeadEnd = false;
3747 : break;
3748 : }
3749 : }
3750 43895 : if (onlyDeadends && !isDeadEnd) {
3751 : return;
3752 : }
3753 43696 : const int fromLane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
3754 43696 : if (onlyTurnlane) {
3755 90 : for (const Connection& c : getConnectionsFromLane(fromLane)) {
3756 68 : LinkDirection dir = myTo->getDirection(this, c.toEdge);
3757 68 : if (dir != LinkDirection::LEFT && dir != LinkDirection::PARTLEFT) {
3758 : return;
3759 : }
3760 82 : }
3761 : }
3762 43636 : const int toLane = myTurnDestination->getFirstAllowedLaneIndex(NBNode::BACKWARD);
3763 43636 : if (checkPermissions) {
3764 43636 : if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == 0) {
3765 : // exclude connection if fromLane and toEdge have no common permissions
3766 : return;
3767 : }
3768 43476 : if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == SVC_PEDESTRIAN) {
3769 : // exclude connection if the only commonly permitted class are pedestrians
3770 : // these connections are later built in NBNode::buildWalkingAreas
3771 : return;
3772 : }
3773 : }
3774 : // avoid railway turn-arounds
3775 40881 : if (isRailway(getPermissions() & myTurnDestination->getPermissions())
3776 40881 : && fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), myTurnDestination->getAngleAtNode(myTo))) > 90) {
3777 : // except at dead-ends on bidi-edges where they model a reversal in train direction
3778 : // @todo #4382: once the network fringe is tagged, it also should not receive turn-arounds)
3779 2997 : if (isBidiRail() && isRailDeadEnd()) {
3780 : // add a slow connection because direction-reversal implies stopping
3781 2690 : setConnection(fromLane, myTurnDestination, toLane, Lane2LaneInfoType::VALIDATED, false, false, KEEPCLEAR_UNSPECIFIED, UNSPECIFIED_CONTPOS, UNSPECIFIED_VISIBILITY_DISTANCE, SUMO_const_haltingSpeed);
3782 2690 : return;
3783 : } else {
3784 307 : return;
3785 : }
3786 : };
3787 37884 : if (noGeometryLike && !isDeadEnd) {
3788 : // ignore paths and service entrances if this edge is for passenger traffic
3789 32047 : if (myTo->geometryLike() || ((getPermissions() & SVC_PASSENGER) != 0
3790 24210 : && !onlyTurnlane
3791 24202 : && myTo->geometryLike(
3792 56249 : NBEdge::filterByPermissions(myTo->getIncomingEdges(), ~(SVC_BICYCLE | SVC_PEDESTRIAN | SVC_DELIVERY)),
3793 56249 : NBEdge::filterByPermissions(myTo->getOutgoingEdges(), ~(SVC_BICYCLE | SVC_PEDESTRIAN | SVC_DELIVERY))))) {
3794 : // make sure the turnDestination has other incoming edges
3795 6059 : EdgeVector turnIncoming = myTurnDestination->getIncomingEdges();
3796 6059 : if (turnIncoming.size() > 1) {
3797 : // this edge is always part of incoming
3798 : return;
3799 : }
3800 6059 : }
3801 : }
3802 63860 : setConnection(fromLane, myTurnDestination, toLane, Lane2LaneInfoType::VALIDATED);
3803 : }
3804 :
3805 :
3806 : bool
3807 14398114 : NBEdge::isTurningDirectionAt(const NBEdge* const edge) const {
3808 : // maybe it was already set as the turning direction
3809 14398114 : if (edge == myTurnDestination) {
3810 : return true;
3811 11373599 : } else if (myTurnDestination != nullptr) {
3812 : // otherwise - it's not if a turning direction exists
3813 : return false;
3814 : }
3815 4094322 : return edge == myPossibleTurnDestination;
3816 : }
3817 :
3818 :
3819 : NBNode*
3820 0 : NBEdge::tryGetNodeAtPosition(double pos, double tolerance) const {
3821 : // return the from-node when the position is at the begin of the edge
3822 0 : if (pos < tolerance) {
3823 0 : return myFrom;
3824 : }
3825 : // return the to-node when the position is at the end of the edge
3826 0 : if (pos > myLength - tolerance) {
3827 0 : return myTo;
3828 : }
3829 : return nullptr;
3830 : }
3831 :
3832 :
3833 : void
3834 22 : NBEdge::moveOutgoingConnectionsFrom(NBEdge* e, int laneOff) {
3835 : int lanes = e->getNumLanes();
3836 49 : for (int i = 0; i < lanes; i++) {
3837 68 : for (const NBEdge::Connection& el : e->getConnectionsFromLane(i)) {
3838 : assert(el.tlID == "");
3839 82 : addLane2LaneConnection(i + laneOff, el.toEdge, el.toLane, Lane2LaneInfoType::COMPUTED);
3840 27 : }
3841 : }
3842 22 : }
3843 :
3844 :
3845 : bool
3846 93 : NBEdge::lanesWereAssigned() const {
3847 93 : return myStep == EdgeBuildingStep::LANES2LANES_DONE || myStep == EdgeBuildingStep::LANES2LANES_USER;
3848 : }
3849 :
3850 :
3851 : double
3852 0 : NBEdge::getMaxLaneOffset() {
3853 0 : return SUMO_const_laneWidth * (double)myLanes.size();
3854 : }
3855 :
3856 :
3857 : bool
3858 236291 : NBEdge::mayBeTLSControlled(int fromLane, NBEdge* toEdge, int toLane) const {
3859 1162882 : for (const Connection& c : myConnections) {
3860 927020 : if (c.fromLane == fromLane && c.toEdge == toEdge && c.toLane == toLane && c.uncontrolled) {
3861 : return false;
3862 : }
3863 : }
3864 : return true;
3865 : }
3866 :
3867 :
3868 : bool
3869 35112 : NBEdge::setControllingTLInformation(const NBConnection& c, const std::string& tlID) {
3870 35112 : const int fromLane = c.getFromLane();
3871 35112 : NBEdge* toEdge = c.getTo();
3872 35112 : const int toLane = c.getToLane();
3873 : const int tlIndex = c.getTLIndex();
3874 : const int tlIndex2 = c.getTLIndex2();
3875 : // check whether the connection was not set as not to be controled previously
3876 35112 : if (!mayBeTLSControlled(fromLane, toEdge, toLane)) {
3877 : return false;
3878 : }
3879 :
3880 : assert(fromLane < 0 || fromLane < (int) myLanes.size());
3881 : // try to use information about the connections if given
3882 35112 : if (fromLane >= 0 && toLane >= 0) {
3883 : // find the specified connection
3884 : std::vector<Connection>::iterator i =
3885 35112 : find_if(myConnections.begin(), myConnections.end(), connections_finder(fromLane, toEdge, toLane));
3886 : // ok, we have to test this as on the removal of self-loop edges some connections
3887 : // will be reassigned
3888 35112 : if (i != myConnections.end()) {
3889 : // get the connection
3890 : Connection& connection = *i;
3891 : // set the information about the tl
3892 35112 : connection.tlID = tlID;
3893 35112 : connection.tlLinkIndex = tlIndex;
3894 35112 : connection.tlLinkIndex2 = tlIndex2;
3895 : return true;
3896 : }
3897 : }
3898 : // if the original connection was not found, set the information for all
3899 : // connections
3900 : int no = 0;
3901 : bool hadError = false;
3902 0 : for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
3903 0 : if ((*i).toEdge != toEdge) {
3904 0 : continue;
3905 : }
3906 0 : if (fromLane >= 0 && fromLane != (*i).fromLane) {
3907 0 : continue;
3908 : }
3909 0 : if (toLane >= 0 && toLane != (*i).toLane) {
3910 0 : continue;
3911 : }
3912 0 : if ((*i).tlID == "") {
3913 : (*i).tlID = tlID;
3914 0 : (*i).tlLinkIndex = tlIndex;
3915 0 : (*i).tlLinkIndex2 = tlIndex2;
3916 0 : no++;
3917 : } else {
3918 0 : if ((*i).tlID != tlID && (*i).tlLinkIndex == tlIndex) {
3919 0 : WRITE_WARNINGF(TL("The lane '%' on edge '%' already had a traffic light signal."), i->fromLane, getID());
3920 : hadError = true;
3921 : }
3922 : }
3923 : }
3924 0 : if (hadError && no == 0) {
3925 0 : WRITE_WARNINGF(TL("Could not set any signal of the tlLogic '%' (unknown group)."), tlID);
3926 : }
3927 : return true;
3928 : }
3929 :
3930 :
3931 : void
3932 102572 : NBEdge::clearControllingTLInformation() {
3933 306521 : for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); it++) {
3934 203949 : it->tlID = "";
3935 : }
3936 102572 : }
3937 :
3938 :
3939 : PositionVector
3940 517504 : NBEdge::getCWBoundaryLine(const NBNode& n) const {
3941 517504 : PositionVector ret;
3942 : int lane;
3943 517504 : if (myFrom == (&n)) {
3944 : // outgoing
3945 277150 : lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
3946 277150 : ret = myLanes[lane].shape;
3947 : } else {
3948 : // incoming
3949 240354 : lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
3950 480708 : ret = myLanes[lane].shape.reverse();
3951 : }
3952 517504 : ret.move2side(getLaneWidth(lane) / 2.);
3953 517504 : return ret;
3954 0 : }
3955 :
3956 :
3957 : PositionVector
3958 515690 : NBEdge::getCCWBoundaryLine(const NBNode& n) const {
3959 515690 : PositionVector ret;
3960 : int lane;
3961 515690 : if (myFrom == (&n)) {
3962 : // outgoing
3963 240340 : lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
3964 240340 : ret = myLanes[lane].shape;
3965 : } else {
3966 : // incoming
3967 275350 : lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
3968 550700 : ret = myLanes[lane].shape.reverse();
3969 : }
3970 515690 : ret.move2side(-getLaneWidth(lane) / 2.);
3971 515690 : return ret;
3972 0 : }
3973 :
3974 :
3975 : bool
3976 8566 : NBEdge::expandableBy(NBEdge* possContinuation, std::string& reason) const {
3977 : // ok, the number of lanes must match
3978 8566 : if (myLanes.size() != possContinuation->myLanes.size()) {
3979 : reason = "laneNumber";
3980 327 : return false;
3981 : }
3982 : // do not create self loops
3983 8239 : if (myFrom == possContinuation->myTo) {
3984 : reason = "loop";
3985 1133 : return false;
3986 : }
3987 : // conserve bidi-rails
3988 7106 : if (isBidiRail() != possContinuation->isBidiRail()) {
3989 : reason = "bidi-rail";
3990 4 : return false;
3991 : }
3992 : // also, check whether the connections - if any exit do allow to join
3993 : // both edges
3994 : // This edge must have a one-to-one connection to the following lanes
3995 7102 : switch (myStep) {
3996 : case EdgeBuildingStep::INIT_REJECT_CONNECTIONS:
3997 : break;
3998 : case EdgeBuildingStep::INIT:
3999 : break;
4000 14 : case EdgeBuildingStep::EDGE2EDGES: {
4001 : // the following edge must be connected
4002 14 : const EdgeVector& conn = getConnectedEdges();
4003 14 : if (find(conn.begin(), conn.end(), possContinuation) == conn.end()) {
4004 : reason = "disconnected";
4005 : return false;
4006 : }
4007 14 : }
4008 : break;
4009 20 : case EdgeBuildingStep::LANES2EDGES:
4010 : case EdgeBuildingStep::LANES2LANES_RECHECK:
4011 : case EdgeBuildingStep::LANES2LANES_DONE:
4012 : case EdgeBuildingStep::LANES2LANES_USER: {
4013 : // the possible continuation must be connected
4014 20 : if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(possContinuation)) == myConnections.end()) {
4015 : reason = "disconnected";
4016 0 : return false;
4017 : }
4018 : // all lanes must go to the possible continuation
4019 20 : std::vector<int> conns = getConnectionLanes(possContinuation);
4020 20 : const int offset = MAX2(0, getFirstNonPedestrianLaneIndex(NBNode::FORWARD, true));
4021 20 : if (conns.size() < myLanes.size() - offset) {
4022 : reason = "some lanes disconnected";
4023 : return false;
4024 : }
4025 20 : }
4026 : break;
4027 : default:
4028 : break;
4029 : }
4030 7102 : const double minLength = OptionsCont::getOptions().getFloat("geometry.remove.min-length");
4031 7103 : if (minLength > 0 && (possContinuation->getLoadedLength() < minLength || getLoadedLength() < minLength)) {
4032 : return true;
4033 : }
4034 7101 : const double maxJunctionSize = OptionsCont::getOptions().getFloat("geometry.remove.max-junction-size");
4035 7101 : if (maxJunctionSize >= 0) {
4036 4 : const double junctionSize = myGeom.back().distanceTo2D(possContinuation->myGeom.front());
4037 4 : if (junctionSize > maxJunctionSize + POSITION_EPS) {
4038 8 : reason = "junction size (" + toString(junctionSize) + ") > max-junction-size (" + toString(maxJunctionSize) + ")";
4039 2 : return false;
4040 : }
4041 : }
4042 : // the priority, too (?)
4043 7099 : if (getPriority() != possContinuation->getPriority()) {
4044 : reason = "priority";
4045 70 : return false;
4046 : }
4047 : // the speed allowed
4048 7029 : if (mySpeed != possContinuation->mySpeed) {
4049 : reason = "speed";
4050 1064 : return false;
4051 : }
4052 : // the routingType
4053 5965 : if (myRoutingType != possContinuation->myRoutingType) {
4054 : reason = "routingType";
4055 0 : return false;
4056 : }
4057 : // spreadtype should match or it will look ugly
4058 5965 : if (myLaneSpreadFunction != possContinuation->myLaneSpreadFunction) {
4059 : reason = "spreadType";
4060 80 : return false;
4061 : }
4062 : // matching lanes must have identical properties
4063 13757 : for (int i = 0; i < (int)myLanes.size(); i++) {
4064 7955 : if (myLanes[i].speed != possContinuation->myLanes[i].speed) {
4065 0 : reason = "lane " + toString(i) + " speed";
4066 83 : return false;
4067 7955 : } else if (myLanes[i].permissions != possContinuation->myLanes[i].permissions) {
4068 112 : reason = "lane " + toString(i) + " permissions";
4069 56 : return false;
4070 7899 : } else if (myLanes[i].changeLeft != possContinuation->myLanes[i].changeLeft || myLanes[i].changeRight != possContinuation->myLanes[i].changeRight) {
4071 14 : reason = "lane " + toString(i) + " change restrictions";
4072 7 : return false;
4073 7971 : } else if (myLanes[i].width != possContinuation->myLanes[i].width &&
4074 8050 : fabs(myLanes[i].width - possContinuation->myLanes[i].width) > OptionsCont::getOptions().getFloat("geometry.remove.width-tolerance")) {
4075 40 : reason = "lane " + toString(i) + " width";
4076 20 : return false;
4077 : }
4078 : }
4079 : // if given identically osm names
4080 15927 : if (!OptionsCont::getOptions().isDefault("output.street-names") && myStreetName != possContinuation->getStreetName()
4081 5911 : && ((myStreetName != "" && possContinuation->getStreetName() != "")
4082 : // only permit merging a short unnamed road with a longer named road
4083 21 : || (myStreetName != "" && myLength <= possContinuation->getLength())
4084 16 : || (myStreetName == "" && myLength >= possContinuation->getLength()))) {
4085 : return false;
4086 : }
4087 :
4088 : return true;
4089 : }
4090 :
4091 :
4092 : void
4093 5505 : NBEdge::append(NBEdge* e) {
4094 : // append geometry
4095 5505 : myGeom.append(e->myGeom);
4096 12940 : for (int i = 0; i < (int)myLanes.size(); i++) {
4097 7435 : myLanes[i].customShape.append(e->myLanes[i].customShape);
4098 9520 : if (myLanes[i].hasParameter(SUMO_PARAM_ORIGID) || e->myLanes[i].hasParameter(SUMO_PARAM_ORIGID)
4099 16955 : || OptionsCont::getOptions().getBool("output.original-names")) {
4100 16062 : const std::string origID = myLanes[i].getParameter(SUMO_PARAM_ORIGID, getID());
4101 16062 : const std::string origID2 = e->myLanes[i].getParameter(SUMO_PARAM_ORIGID, e->getID());
4102 5354 : if (origID != origID2) {
4103 9756 : myLanes[i].setParameter(SUMO_PARAM_ORIGID, origID + " " + origID2);
4104 : }
4105 : }
4106 7435 : myLanes[i].connectionsDone = e->myLanes[i].connectionsDone;
4107 7435 : myLanes[i].turnSigns = e->myLanes[i].turnSigns;
4108 : }
4109 5505 : if (e->getLength() > myLength) {
4110 : // possibly some lane attributes differ (when using option geometry.remove.min-length)
4111 : // make sure to use the attributes from the longer edge
4112 6062 : for (int i = 0; i < (int)myLanes.size(); i++) {
4113 3397 : myLanes[i].width = e->myLanes[i].width;
4114 : }
4115 : // defined name prevails over undefined name of shorter road
4116 2665 : if (myStreetName == "") {
4117 1262 : myStreetName = e->myStreetName;
4118 : }
4119 : }
4120 : // recompute length
4121 5505 : myLength += e->myLength;
4122 5505 : if (myLoadedLength > 0 || e->myLoadedLength > 0) {
4123 1 : myLoadedLength = getFinalLength() + e->getFinalLength();
4124 : }
4125 : // copy the connections and the building step if given
4126 5505 : myStep = e->myStep;
4127 5505 : myConnections = e->myConnections;
4128 5505 : myTurnDestination = e->myTurnDestination;
4129 5505 : myPossibleTurnDestination = e->myPossibleTurnDestination;
4130 5505 : myConnectionsToDelete = e->myConnectionsToDelete;
4131 5505 : updateRemovedNodes(e->getParameter(SUMO_PARAM_REMOVED_NODES));
4132 : // set the node
4133 5505 : myTo = e->myTo;
4134 5505 : myTurnSignTarget = e->myTurnSignTarget;
4135 : myToBorder = e->myToBorder;
4136 11010 : mergeParameters(e->getParametersMap());
4137 : if (e->mySignalPosition != Position::INVALID) {
4138 1170 : mySignalPosition = e->mySignalPosition;
4139 : }
4140 5505 : computeAngle(); // myEndAngle may be different now
4141 5505 : }
4142 :
4143 :
4144 : void
4145 5512 : NBEdge::updateRemovedNodes(const std::string& removed) {
4146 11024 : std::string result = getParameter(SUMO_PARAM_REMOVED_NODES);
4147 5512 : if (!result.empty() && !removed.empty()) {
4148 : result += " ";
4149 : }
4150 : result += removed;
4151 5512 : if (!result.empty()) {
4152 9 : setParameter(SUMO_PARAM_REMOVED_NODES, result);
4153 : }
4154 5512 : }
4155 :
4156 :
4157 : bool
4158 917838 : NBEdge::hasSignalisedConnectionTo(const NBEdge* const e) const {
4159 3859273 : for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
4160 3245095 : if ((*i).toEdge == e && (*i).tlID != "") {
4161 : return true;
4162 : }
4163 : }
4164 : return false;
4165 : }
4166 :
4167 :
4168 : NBEdge*
4169 805595 : NBEdge::getTurnDestination(bool possibleDestination) const {
4170 805595 : if (myTurnDestination == nullptr && possibleDestination) {
4171 64319 : return myPossibleTurnDestination;
4172 : }
4173 : return myTurnDestination;
4174 : }
4175 :
4176 :
4177 : std::string
4178 278584 : NBEdge::getLaneID(int lane) const {
4179 557168 : return myID + "_" + toString(lane);
4180 : }
4181 :
4182 :
4183 : bool
4184 65 : NBEdge::isNearEnough2BeJoined2(NBEdge* e, double threshold) const {
4185 65 : std::vector<double> distances = myGeom.distances(e->getGeometry());
4186 : assert(distances.size() > 0);
4187 130 : return VectorHelper<double>::maxValue(distances) < threshold;
4188 65 : }
4189 :
4190 :
4191 : void
4192 63 : NBEdge::addLane(int index, bool recomputeShape, bool recomputeConnections, bool shiftIndices) {
4193 : assert(index <= (int)myLanes.size());
4194 189 : myLanes.insert(myLanes.begin() + index, Lane(this, ""));
4195 : // copy attributes
4196 63 : if (myLanes.size() > 1) {
4197 63 : int templateIndex = index > 0 ? index - 1 : index + 1;
4198 63 : myLanes[index].speed = myLanes[templateIndex].speed;
4199 63 : myLanes[index].friction = myLanes[templateIndex].friction;
4200 63 : myLanes[index].permissions = myLanes[templateIndex].permissions;
4201 63 : myLanes[index].preferred = myLanes[templateIndex].preferred;
4202 63 : myLanes[index].endOffset = myLanes[templateIndex].endOffset;
4203 63 : myLanes[index].width = myLanes[templateIndex].width;
4204 63 : myLanes[index].updateParameters(myLanes[templateIndex].getParametersMap());
4205 : }
4206 63 : const EdgeVector& incs = myFrom->getIncomingEdges();
4207 63 : if (recomputeShape) {
4208 47 : computeLaneShapes();
4209 : }
4210 63 : if (recomputeConnections) {
4211 121 : for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
4212 74 : (*i)->invalidateConnections(true);
4213 : }
4214 47 : invalidateConnections(true);
4215 16 : } else if (shiftIndices) {
4216 : // shift outgoing connections above the added lane to the left
4217 0 : for (Connection& c : myConnections) {
4218 0 : if (c.fromLane >= index) {
4219 0 : c.fromLane += 1;
4220 : }
4221 : }
4222 : // shift incoming connections above the added lane to the left
4223 0 : for (NBEdge* inc : myFrom->getIncomingEdges()) {
4224 0 : for (Connection& c : inc->myConnections) {
4225 0 : if (c.toEdge == this && c.toLane >= index) {
4226 0 : c.toLane += 1;
4227 : }
4228 : }
4229 : }
4230 0 : myFrom->shiftTLConnectionLaneIndex(this, +1, index - 1);
4231 0 : myTo->shiftTLConnectionLaneIndex(this, +1, index - 1);
4232 : }
4233 63 : }
4234 :
4235 : void
4236 50 : NBEdge::incLaneNo(int by) {
4237 50 : int newLaneNo = (int)myLanes.size() + by;
4238 113 : while ((int)myLanes.size() < newLaneNo) {
4239 : // recompute shapes on last addition
4240 63 : const bool recompute = ((int)myLanes.size() == newLaneNo - 1) && myStep < EdgeBuildingStep::LANES2LANES_USER;
4241 63 : addLane((int)myLanes.size(), recompute, recompute, false);
4242 : }
4243 50 : }
4244 :
4245 :
4246 : void
4247 68 : NBEdge::deleteLane(int index, bool recompute, bool shiftIndices) {
4248 : assert(index < (int)myLanes.size());
4249 68 : myLanes.erase(myLanes.begin() + index);
4250 68 : if (recompute) {
4251 16 : computeLaneShapes();
4252 16 : const EdgeVector& incs = myFrom->getIncomingEdges();
4253 17 : for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
4254 1 : (*i)->invalidateConnections(true);
4255 : }
4256 16 : invalidateConnections(true);
4257 52 : } else if (shiftIndices) {
4258 44 : removeFromConnections(nullptr, index, -1, false, true);
4259 120 : for (NBEdge* inc : myFrom->getIncomingEdges()) {
4260 76 : inc->removeFromConnections(this, -1, index, false, true);
4261 : }
4262 : }
4263 68 : }
4264 :
4265 :
4266 : void
4267 41 : NBEdge::decLaneNo(int by) {
4268 41 : int newLaneNo = (int) myLanes.size() - by;
4269 : assert(newLaneNo > 0);
4270 65 : while ((int)myLanes.size() > newLaneNo) {
4271 : // recompute shapes on last removal
4272 24 : const bool recompute = (int)myLanes.size() == newLaneNo + 1 && myStep < EdgeBuildingStep::LANES2LANES_USER;
4273 24 : deleteLane((int)myLanes.size() - 1, recompute, false);
4274 : }
4275 41 : }
4276 :
4277 :
4278 : void
4279 5242 : NBEdge::markAsInLane2LaneState() {
4280 : assert(myTo->getOutgoingEdges().size() == 0);
4281 5242 : myStep = EdgeBuildingStep::LANES2LANES_DONE;
4282 5242 : }
4283 :
4284 :
4285 : void
4286 7879 : NBEdge::allowVehicleClass(int lane, SUMOVehicleClass vclass) {
4287 7879 : if (lane < 0) { // all lanes are meant...
4288 7818 : for (int i = 0; i < (int)myLanes.size(); i++) {
4289 5510 : allowVehicleClass(i, vclass);
4290 : }
4291 : } else {
4292 : assert(lane < (int)myLanes.size());
4293 5571 : myLanes[lane].permissions |= vclass;
4294 : }
4295 7879 : }
4296 :
4297 :
4298 : void
4299 24710 : NBEdge::disallowVehicleClass(int lane, SUMOVehicleClass vclass) {
4300 24710 : if (lane < 0) { // all lanes are meant...
4301 24710 : for (int i = 0; i < (int)myLanes.size(); i++) {
4302 15289 : disallowVehicleClass((int) i, vclass);
4303 : }
4304 : } else {
4305 : assert(lane < (int)myLanes.size());
4306 15289 : myLanes[lane].permissions &= ~vclass;
4307 : }
4308 24710 : }
4309 :
4310 :
4311 : void
4312 68 : NBEdge::preferVehicleClass(int lane, SVCPermissions vclasses) {
4313 68 : if (lane < 0) { // all lanes are meant...
4314 0 : for (int i = 0; i < (int)myLanes.size(); i++) {
4315 0 : preferVehicleClass(i, vclasses);
4316 : }
4317 : } else {
4318 : assert(lane < (int)myLanes.size());
4319 68 : myLanes[lane].permissions |= vclasses;
4320 68 : myLanes[lane].preferred |= vclasses;
4321 : }
4322 68 : }
4323 :
4324 :
4325 : void
4326 52135 : NBEdge::setLaneWidth(int lane, double width) {
4327 52135 : if (lane < 0) {
4328 : // all lanes are meant...
4329 406 : myLaneWidth = width;
4330 833 : for (int i = 0; i < (int)myLanes.size(); i++) {
4331 : // ... do it for each lane
4332 427 : setLaneWidth(i, width);
4333 : }
4334 : return;
4335 : }
4336 : assert(lane < (int)myLanes.size());
4337 51729 : myLanes[lane].width = width;
4338 : }
4339 :
4340 : void
4341 3857 : NBEdge::setLaneType(int lane, const std::string& type) {
4342 3857 : if (lane < 0) {
4343 0 : for (int i = 0; i < (int)myLanes.size(); i++) {
4344 : // ... do it for each lane
4345 0 : setLaneType(i, type);
4346 : }
4347 : return;
4348 : }
4349 : assert(lane < (int)myLanes.size());
4350 3857 : myLanes[lane].type = type;
4351 : }
4352 :
4353 :
4354 : double
4355 3790628 : NBEdge::getLaneWidth(int lane) const {
4356 3790628 : return myLanes[lane].width != UNSPECIFIED_WIDTH
4357 3790628 : ? myLanes[lane].width
4358 3147583 : : getLaneWidth() != UNSPECIFIED_WIDTH ? getLaneWidth() : SUMO_const_laneWidth;
4359 : }
4360 :
4361 : double
4362 108914 : NBEdge::getInternalLaneWidth(
4363 : const NBNode& node,
4364 : const NBEdge::Connection& connection,
4365 : const NBEdge::Lane& successor,
4366 : bool isVia) const {
4367 :
4368 108914 : if (!isVia && node.isConstantWidthTransition() && getNumLanes() > connection.toEdge->getNumLanes()) {
4369 3 : return getLaneWidth(connection.fromLane);
4370 : }
4371 :
4372 108911 : return (isBikepath(getPermissions(connection.fromLane)) && (
4373 108911 : getLaneWidth(connection.fromLane) < successor.width || successor.width == UNSPECIFIED_WIDTH)) ?
4374 1021 : myLanes[connection.fromLane].width : successor.width; // getLaneWidth(connection.fromLane) never returns -1 (UNSPECIFIED_WIDTH)
4375 : }
4376 :
4377 : double
4378 884782 : NBEdge::getTotalWidth() const {
4379 : double result = 0;
4380 2047846 : for (int i = 0; i < (int)myLanes.size(); i++) {
4381 1163064 : result += getLaneWidth(i);
4382 : }
4383 884782 : return result;
4384 : }
4385 :
4386 : double
4387 34199 : NBEdge::getEndOffset(int lane) const {
4388 34199 : return myLanes[lane].endOffset != UNSPECIFIED_OFFSET ? myLanes[lane].endOffset : getEndOffset();
4389 : }
4390 :
4391 :
4392 : const StopOffset&
4393 533687 : NBEdge::getEdgeStopOffset() const {
4394 533687 : return myEdgeStopOffset;
4395 : }
4396 :
4397 :
4398 : const StopOffset&
4399 24 : NBEdge::getLaneStopOffset(int lane) const {
4400 24 : if (lane == -1) {
4401 12 : return myEdgeStopOffset;
4402 : } else {
4403 12 : return myLanes[lane].laneStopOffset;
4404 : }
4405 : }
4406 :
4407 :
4408 : void
4409 45859 : NBEdge::setEndOffset(int lane, double offset) {
4410 45859 : if (lane < 0) {
4411 : // all lanes are meant...
4412 1 : myEndOffset = offset;
4413 4 : for (int i = 0; i < (int)myLanes.size(); i++) {
4414 : // ... do it for each lane
4415 3 : setEndOffset(i, offset);
4416 : }
4417 : return;
4418 : }
4419 : assert(lane < (int)myLanes.size());
4420 45858 : myLanes[lane].endOffset = offset;
4421 : }
4422 :
4423 :
4424 : bool
4425 79833 : NBEdge::setEdgeStopOffset(int lane, const StopOffset& offset, bool overwrite) {
4426 79833 : if (lane < 0) {
4427 33990 : if (!overwrite && myEdgeStopOffset.isDefined()) {
4428 : return false;
4429 : }
4430 : // all lanes are meant...
4431 33986 : if (offset.getOffset() < 0) {
4432 : // Edge length unknown at parsing time, thus check here.
4433 3 : WRITE_WARNINGF(TL("Ignoring invalid stopOffset for edge '%' (negative offset)."), getID());
4434 1 : return false;
4435 : } else {
4436 33985 : myEdgeStopOffset = offset;
4437 : }
4438 45843 : } else if (lane < (int)myLanes.size()) {
4439 45843 : if (!myLanes[lane].laneStopOffset.isDefined() || overwrite) {
4440 45837 : if (offset.getOffset() < 0) {
4441 : // Edge length unknown at parsing time, thus check here.
4442 0 : WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (negative offset)."), getLaneID(lane));
4443 : } else {
4444 45837 : myLanes[lane].laneStopOffset = offset;
4445 : }
4446 : }
4447 : } else {
4448 0 : WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (invalid lane index)."), toString(lane));
4449 : }
4450 : return true;
4451 : }
4452 :
4453 :
4454 : void
4455 50332 : NBEdge::setSpeed(int lane, double speed) {
4456 50332 : if (lane < 0) {
4457 : // all lanes are meant...
4458 344 : mySpeed = speed;
4459 1085 : for (int i = 0; i < (int)myLanes.size(); i++) {
4460 : // ... do it for each lane
4461 741 : setSpeed(i, speed);
4462 : }
4463 : return;
4464 : }
4465 : assert(lane < (int)myLanes.size());
4466 49988 : myLanes[lane].speed = speed;
4467 : }
4468 :
4469 :
4470 : void
4471 50402 : NBEdge::setFriction(int lane, double friction) {
4472 50402 : if (lane < 0) {
4473 : // all lanes are meant...
4474 776 : myFriction = friction;
4475 2231 : for (int i = 0; i < (int)myLanes.size(); i++) {
4476 : // ... do it for each lane
4477 1455 : setFriction(i, friction);
4478 : }
4479 : return;
4480 : }
4481 : assert(lane < (int)myLanes.size());
4482 49626 : myLanes[lane].friction = friction;
4483 : }
4484 :
4485 :
4486 : void
4487 44436 : NBEdge::setAcceleration(int lane, bool accelRamp) {
4488 : assert(lane >= 0);
4489 : assert(lane < (int)myLanes.size());
4490 44436 : myLanes[lane].accelRamp = accelRamp;
4491 44436 : }
4492 :
4493 :
4494 : void
4495 77 : NBEdge::setLaneShape(int lane, const PositionVector& shape) {
4496 : assert(lane >= 0);
4497 : assert(lane < (int)myLanes.size());
4498 77 : myLanes[lane].customShape = shape;
4499 77 : }
4500 :
4501 :
4502 : void
4503 240764 : NBEdge::setPermissions(SVCPermissions permissions, int lane) {
4504 240764 : if (lane < 0) {
4505 184670 : for (int i = 0; i < (int)myLanes.size(); i++) {
4506 : // ... do it for each lane
4507 102153 : setPermissions(permissions, i);
4508 : }
4509 : } else {
4510 : assert(lane < (int)myLanes.size());
4511 158247 : myLanes[lane].permissions = permissions;
4512 : }
4513 240764 : }
4514 :
4515 :
4516 : void
4517 0 : NBEdge::setPreferredVehicleClass(SVCPermissions permissions, int lane) {
4518 0 : if (lane < 0) {
4519 0 : for (int i = 0; i < (int)myLanes.size(); i++) {
4520 : // ... do it for each lane
4521 0 : setPreferredVehicleClass(permissions, i);
4522 : }
4523 : } else {
4524 : assert(lane < (int)myLanes.size());
4525 0 : myLanes[lane].preferred = permissions;
4526 : }
4527 0 : }
4528 :
4529 :
4530 : void
4531 44683 : NBEdge::setPermittedChanging(int lane, SVCPermissions changeLeft, SVCPermissions changeRight) {
4532 : assert(lane >= 0);
4533 : assert(lane < (int)myLanes.size());
4534 44683 : myLanes[lane].changeLeft = changeLeft;
4535 44683 : myLanes[lane].changeRight = changeRight;
4536 44683 : }
4537 :
4538 :
4539 : SVCPermissions
4540 81276453 : NBEdge::getPermissions(int lane) const {
4541 81276453 : if (lane < 0) {
4542 : SVCPermissions result = 0;
4543 73274775 : for (int i = 0; i < (int)myLanes.size(); i++) {
4544 43903160 : result |= getPermissions(i);
4545 : }
4546 29371615 : return result;
4547 : } else {
4548 : assert(lane < (int)myLanes.size());
4549 51904838 : return myLanes[lane].permissions;
4550 : }
4551 : }
4552 :
4553 :
4554 : void
4555 53253 : NBEdge::setLoadedLength(double val) {
4556 53253 : myLoadedLength = val;
4557 53253 : }
4558 :
4559 : void
4560 10 : NBEdge::setAverageLengthWithOpposite(double val) {
4561 10 : myLength = val;
4562 10 : }
4563 :
4564 :
4565 : void
4566 200 : NBEdge::dismissVehicleClassInformation() {
4567 647 : for (std::vector<Lane>::iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
4568 447 : (*i).permissions = SVCAll;
4569 447 : (*i).preferred = 0;
4570 : }
4571 200 : }
4572 :
4573 :
4574 : bool
4575 353977 : NBEdge::connections_sorter(const Connection& c1, const Connection& c2) {
4576 353977 : if (c1.fromLane != c2.fromLane) {
4577 88069 : return c1.fromLane < c2.fromLane;
4578 : }
4579 265908 : if (c1.toEdge != c2.toEdge) {
4580 : return false; // do not change ordering among toEdges as this is determined by angle in an earlier step
4581 : }
4582 5762 : return c1.toLane < c2.toLane;
4583 : }
4584 :
4585 :
4586 : double
4587 9850 : NBEdge::getSignalOffset() const {
4588 : if (mySignalPosition == Position::INVALID) {
4589 : return UNSPECIFIED_SIGNAL_OFFSET;
4590 : } else {
4591 677 : Position laneEnd = myLaneSpreadFunction == LaneSpreadFunction::RIGHT ?
4592 677 : myLanes.back().shape.back() : myLanes[getNumLanes() / 2].shape.back();
4593 : //std::cout << getID() << " signalPos=" << mySignalPosition << " laneEnd=" << laneEnd << " toShape=" << myTo->getShape() << " toBorder=" << myToBorder << "\n";
4594 : return mySignalPosition.distanceTo2D(laneEnd);
4595 : }
4596 : }
4597 :
4598 :
4599 : int
4600 29481 : NBEdge::getFirstNonPedestrianLaneIndex(int direction, bool exclusive) const {
4601 : assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4602 29481 : const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4603 29481 : const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4604 38050 : for (int i = start; i != end; i += direction) {
4605 : // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
4606 : // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
4607 25744 : if ((exclusive && myLanes[i].permissions != SVC_PEDESTRIAN && myLanes[i].permissions != 0)
4608 38040 : || ((myLanes[i].permissions & SVC_PEDESTRIAN) == 0 && myLanes[i].permissions != 0)) {
4609 24040 : return i;
4610 : }
4611 : }
4612 : return -1;
4613 : }
4614 :
4615 : int
4616 51303 : NBEdge::getFirstNonPedestrianNonBicycleLaneIndex(int direction, bool exclusive) const {
4617 : assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4618 51303 : const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4619 51303 : const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4620 67035 : for (int i = start; i != end; i += direction) {
4621 : // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
4622 : // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
4623 53195 : SVCPermissions p = myLanes[i].permissions;
4624 53195 : if ((exclusive && p != SVC_PEDESTRIAN && p != SVC_BICYCLE && p != (SVC_PEDESTRIAN | SVC_BICYCLE) && p != 0)
4625 15732 : || (p == SVCAll || ((p & (SVC_PEDESTRIAN | SVC_BICYCLE)) == 0 && p != 0))) {
4626 37463 : return i;
4627 : }
4628 : }
4629 : return -1;
4630 : }
4631 :
4632 : int
4633 611094 : NBEdge::getSpecialLane(SVCPermissions permissions) const {
4634 1387248 : for (int i = 0; i < (int)myLanes.size(); i++) {
4635 779326 : if (myLanes[i].permissions == permissions) {
4636 3172 : return i;
4637 : }
4638 : }
4639 : return -1;
4640 : }
4641 :
4642 : int
4643 1120526 : NBEdge::getFirstAllowedLaneIndex(int direction) const {
4644 : assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4645 1120526 : const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4646 1120526 : const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4647 1120950 : for (int i = start; i != end; i += direction) {
4648 1120761 : if (myLanes[i].permissions != 0) {
4649 1120337 : return i;
4650 : }
4651 : }
4652 189 : return end - direction;
4653 : }
4654 :
4655 :
4656 : std::set<SVCPermissions>
4657 3943 : NBEdge::getPermissionVariants(int iStart, int iEnd) const {
4658 : std::set<SVCPermissions> result;
4659 3943 : if (iStart < 0 || iStart >= getNumLanes() || iEnd > getNumLanes()) {
4660 0 : throw ProcessError("invalid indices iStart " + toString(iStart) + " iEnd " + toString(iEnd) + " for edge with " + toString(getNumLanes()) + " lanes.");
4661 : }
4662 8817 : for (int i = iStart; i < iEnd; ++i) {
4663 4874 : result.insert(getPermissions(i));
4664 : }
4665 3943 : return result;
4666 : }
4667 :
4668 : int
4669 6827735 : NBEdge::getNumLanesThatAllow(SVCPermissions permissions, bool allPermissions) const {
4670 : int result = 0;
4671 17400220 : for (const Lane& lane : myLanes) {
4672 10572485 : if ((allPermissions && (lane.permissions & permissions) == permissions)
4673 458140 : || (!allPermissions && (lane.permissions & permissions) != 0)) {
4674 9325703 : result++;
4675 : }
4676 : }
4677 6827735 : return result;
4678 : }
4679 :
4680 : bool
4681 0 : NBEdge::allowsChangingLeft(int lane, SUMOVehicleClass vclass) const {
4682 : assert(lane >= 0 && lane < getNumLanes());
4683 0 : return myLanes[lane].changeLeft == SVC_UNSPECIFIED ? true : (myLanes[lane].changeLeft & vclass) == vclass;
4684 : }
4685 :
4686 : bool
4687 0 : NBEdge::allowsChangingRight(int lane, SUMOVehicleClass vclass) const {
4688 : assert(lane >= 0 && lane < getNumLanes());
4689 0 : return myLanes[lane].changeRight == SVC_UNSPECIFIED ? true : (myLanes[lane].changeRight & vclass) == vclass;
4690 : }
4691 :
4692 : double
4693 5763 : NBEdge::getCrossingAngle(NBNode* node) {
4694 5763 : double angle = getAngleAtNode(node) + (getFromNode() == node ? 180.0 : 0.0);
4695 5763 : if (angle < 0) {
4696 1482 : angle += 360.0;
4697 : }
4698 5763 : if (angle >= 360) {
4699 0 : angle -= 360.0;
4700 : }
4701 5763 : if (gDebugFlag1) {
4702 0 : std::cout << getID() << " angle=" << getAngleAtNode(node) << " convAngle=" << angle << "\n";
4703 : }
4704 5763 : return angle;
4705 : }
4706 :
4707 :
4708 : NBEdge::Lane
4709 0 : NBEdge::getFirstNonPedestrianLane(int direction) const {
4710 0 : int index = getFirstNonPedestrianLaneIndex(direction);
4711 0 : if (index < 0) {
4712 0 : throw ProcessError(TLF("Edge % allows pedestrians on all lanes", getID()));
4713 : }
4714 0 : return myLanes[index];
4715 : }
4716 :
4717 : std::string
4718 11116 : NBEdge::getSidewalkID() {
4719 : // see IntermodalEdge::getSidewalk()
4720 13634 : for (int i = 0; i < (int)myLanes.size(); i++) {
4721 11169 : if (myLanes[i].permissions == SVC_PEDESTRIAN) {
4722 8651 : return getLaneID(i);
4723 : }
4724 : }
4725 2465 : for (int i = 0; i < (int)myLanes.size(); i++) {
4726 2465 : if ((myLanes[i].permissions & SVC_PEDESTRIAN) != 0) {
4727 2465 : return getLaneID(i);
4728 : }
4729 : }
4730 0 : return getLaneID(0);
4731 : }
4732 :
4733 : void
4734 3160 : NBEdge::addSidewalk(double width) {
4735 3160 : addRestrictedLane(width, SVC_PEDESTRIAN);
4736 3160 : }
4737 :
4738 :
4739 : void
4740 0 : NBEdge::restoreSidewalk(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4741 0 : restoreRestrictedLane(SVC_PEDESTRIAN, oldLanes, oldGeometry, oldConnections);
4742 0 : }
4743 :
4744 :
4745 : void
4746 591 : NBEdge::addBikeLane(double width) {
4747 591 : addRestrictedLane(width, SVC_BICYCLE);
4748 591 : }
4749 :
4750 :
4751 : void
4752 0 : NBEdge::restoreBikelane(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4753 0 : restoreRestrictedLane(SVC_BICYCLE, oldLanes, oldGeometry, oldConnections);
4754 0 : }
4755 :
4756 : bool
4757 4406 : NBEdge::hasRestrictedLane(SUMOVehicleClass vclass) const {
4758 11265 : for (const Lane& lane : myLanes) {
4759 6981 : if (lane.permissions == vclass) {
4760 : return true;
4761 : }
4762 : }
4763 : return false;
4764 : }
4765 :
4766 :
4767 : void
4768 3987 : NBEdge::addRestrictedLane(double width, SUMOVehicleClass vclass) {
4769 3987 : if (hasRestrictedLane(vclass)) {
4770 12 : WRITE_WARNINGF(TL("Edge '%' already has a dedicated lane for %s. Not adding another one."), getID(), toString(vclass));
4771 4 : return;
4772 : }
4773 3983 : if (myLaneSpreadFunction == LaneSpreadFunction::CENTER) {
4774 1136 : myGeom.move2side(width / 2);
4775 : }
4776 : // disallow the designated vclass on all "old" lanes
4777 3983 : disallowVehicleClass(-1, vclass);
4778 : // don't create a restricted vehicle lane to the right of a sidewalk
4779 3983 : const int newIndex = (vclass != SVC_PEDESTRIAN && myLanes[0].permissions == SVC_PEDESTRIAN) ? 1 : 0;
4780 : if (newIndex == 0) {
4781 : // disallow pedestrians on all "higher" lanes to ensure that sidewalk remains the rightmost lane
4782 3942 : disallowVehicleClass(-1, SVC_PEDESTRIAN);
4783 : }
4784 : // add new lane
4785 11949 : myLanes.insert(myLanes.begin() + newIndex, Lane(this, myLanes[0].getParameter(SUMO_PARAM_ORIGID)));
4786 3983 : myLanes[newIndex].permissions = vclass;
4787 3983 : myLanes[newIndex].width = fabs(width);
4788 : // shift outgoing connections to the left
4789 4010 : for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
4790 : Connection& c = *it;
4791 27 : if (c.fromLane >= newIndex) {
4792 27 : c.fromLane += 1;
4793 : }
4794 : }
4795 : // shift incoming connections to the left
4796 3983 : const EdgeVector& incoming = myFrom->getIncomingEdges();
4797 8950 : for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4798 4967 : (*it)->shiftToLanesToEdge(this, 1);
4799 : }
4800 3983 : myFrom->shiftTLConnectionLaneIndex(this, 1);
4801 3983 : myTo->shiftTLConnectionLaneIndex(this, 1);
4802 3983 : computeLaneShapes();
4803 : }
4804 :
4805 :
4806 : void
4807 0 : NBEdge::restoreRestrictedLane(SUMOVehicleClass vclass, std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4808 : // check that previously lane was transformed
4809 0 : if (myLanes[0].permissions != vclass) {
4810 0 : WRITE_WARNINGF(TL("Edge '%' doesn't have a dedicated lane for %s. Cannot be restored."), getID(), toString(vclass));
4811 0 : return;
4812 : }
4813 : // restore old values
4814 : myGeom = oldGeometry;
4815 0 : myLanes = oldLanes;
4816 0 : myConnections = oldConnections;
4817 : // shift incoming connections to the right
4818 0 : const EdgeVector& incoming = myFrom->getIncomingEdges();
4819 0 : for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4820 0 : (*it)->shiftToLanesToEdge(this, 0);
4821 : }
4822 : // Shift TL conections
4823 0 : myFrom->shiftTLConnectionLaneIndex(this, 0);
4824 0 : myTo->shiftTLConnectionLaneIndex(this, 0);
4825 0 : computeLaneShapes();
4826 : }
4827 :
4828 :
4829 : void
4830 4967 : NBEdge::shiftToLanesToEdge(NBEdge* to, int laneOff) {
4831 : /// XXX could we repurpose the function replaceInConnections ?
4832 5072 : for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
4833 105 : if ((*it).toEdge == to && (*it).toLane >= 0) {
4834 27 : (*it).toLane += laneOff;
4835 : }
4836 : }
4837 4967 : }
4838 :
4839 :
4840 : bool
4841 66562 : NBEdge::shiftPositionAtNode(NBNode* node, NBEdge* other) {
4842 66562 : if (myLaneSpreadFunction == LaneSpreadFunction::CENTER
4843 17363 : && !isRailway(getPermissions())
4844 11031 : && !isRailway(other->getPermissions())
4845 77563 : && getBidiEdge() == nullptr) {
4846 10985 : const int i = (node == myTo ? -1 : 0);
4847 10985 : const int i2 = (node == myTo ? 0 : -1);
4848 10985 : const double dist = myGeom[i].distanceTo2D(node->getPosition());
4849 10985 : const double neededOffset = getTotalWidth() / 2;
4850 10985 : const double dist2 = MIN2(myGeom.distance2D(other->getGeometry()[i2]),
4851 10985 : other->getGeometry().distance2D(myGeom[i]));
4852 10985 : const double neededOffset2 = neededOffset + (other->getTotalWidth()) / 2;
4853 10985 : if (dist < neededOffset && dist2 < neededOffset2) {
4854 : PositionVector tmp = myGeom;
4855 : // @note this doesn't work well for vissim networks
4856 : //tmp.move2side(MIN2(neededOffset - dist, neededOffset2 - dist2));
4857 : try {
4858 8959 : tmp.move2side(neededOffset - dist);
4859 8959 : tmp[i].round(gPrecision);
4860 8959 : myGeom[i] = tmp[i];
4861 8959 : computeAngle();
4862 : return true;
4863 : //std::cout << getID() << " shiftPositionAtNode needed=" << neededOffset << " dist=" << dist << " needed2=" << neededOffset2 << " dist2=" << dist2 << " by=" << (neededOffset - dist) << " other=" << other->getID() << "\n";
4864 0 : } catch (InvalidArgument&) {
4865 0 : WRITE_WARNINGF(TL("Could not avoid overlapping shape at node '%' for edge '%'."), node->getID(), getID());
4866 0 : }
4867 8959 : }
4868 : }
4869 : return false;
4870 : }
4871 :
4872 :
4873 : Position
4874 80 : NBEdge::geometryPositionAtOffset(double offset) const {
4875 80 : if (myLoadedLength > 0) {
4876 2 : return myGeom.positionAtOffset(offset * myLength / myLoadedLength);
4877 : } else {
4878 78 : return myGeom.positionAtOffset(offset);
4879 : }
4880 : }
4881 :
4882 :
4883 : double
4884 114878 : NBEdge::getFinalLength() const {
4885 : double result = getLoadedLength();
4886 229756 : if (OptionsCont::getOptions().getBool("no-internal-links") && !hasLoadedLength()) {
4887 : // use length to junction center even if a modified geometry was given
4888 56324 : PositionVector geom = cutAtIntersection(myGeom);
4889 56324 : geom.push_back_noDoublePos(getToNode()->getCenter());
4890 56324 : geom.push_front_noDoublePos(getFromNode()->getCenter());
4891 56324 : result = geom.length();
4892 56324 : }
4893 : double avgEndOffset = 0;
4894 262175 : for (const Lane& lane : myLanes) {
4895 147297 : avgEndOffset += lane.endOffset;
4896 : }
4897 114878 : if (isBidiRail()) {
4898 10142 : avgEndOffset += myPossibleTurnDestination->getEndOffset();
4899 : }
4900 114878 : avgEndOffset /= (double)myLanes.size();
4901 114882 : return MAX2(result - avgEndOffset, POSITION_EPS);
4902 : }
4903 :
4904 :
4905 : void
4906 20961 : NBEdge::setOrigID(const std::string origID, const bool append, const int laneIdx) {
4907 20961 : if (laneIdx == -1) {
4908 20904 : for (int i = 0; i < (int)myLanes.size(); i++) {
4909 23408 : setOrigID(origID, append, i);
4910 : }
4911 : } else {
4912 11761 : if (origID != "") {
4913 11761 : if (append) {
4914 114 : std::vector<std::string> oldIDs = StringTokenizer(myLanes[laneIdx].getParameter(SUMO_PARAM_ORIGID)).getVector();
4915 57 : if (std::find(oldIDs.begin(), oldIDs.end(), origID) == oldIDs.end()) {
4916 57 : oldIDs.push_back(origID);
4917 : }
4918 57 : myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, toString(oldIDs));
4919 57 : } else {
4920 11704 : myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, origID);
4921 : }
4922 : } else {
4923 : // do not record empty origID parameter
4924 0 : myLanes[laneIdx].unsetParameter(SUMO_PARAM_ORIGID);
4925 : }
4926 : }
4927 20961 : }
4928 :
4929 :
4930 : const EdgeVector&
4931 550 : NBEdge::getSuccessors(SUMOVehicleClass vClass) const {
4932 : // @todo cache successors instead of recomputing them every time
4933 : mySuccessors.clear();
4934 : //std::cout << "getSuccessors edge=" << getID() << " svc=" << toString(vClass) << " cons=" << myConnections.size() << "\n";
4935 2940 : for (const Connection& con : myConnections) {
4936 2352 : if (con.fromLane >= 0 && con.toLane >= 0 && con.toEdge != nullptr &&
4937 159 : (vClass == SVC_IGNORING || (getPermissions(con.fromLane)
4938 159 : & con.toEdge->getPermissions(con.toLane) & vClass) != 0)
4939 4742 : && std::find(mySuccessors.begin(), mySuccessors.end(), con.toEdge) == mySuccessors.end()) {
4940 1596 : mySuccessors.push_back(con.toEdge);
4941 : //std::cout << " succ=" << con.toEdge->getID() << "\n";
4942 : }
4943 : }
4944 550 : return mySuccessors;
4945 : }
4946 :
4947 :
4948 : const ConstRouterEdgePairVector&
4949 39240 : NBEdge::getViaSuccessors(SUMOVehicleClass vClass, bool /*ignoreTransientPermissions*/) const {
4950 : // @todo cache successors instead of recomputing them every time
4951 : myViaSuccessors.clear();
4952 80970 : for (const Connection& con : myConnections) {
4953 : std::pair<const NBEdge*, const Connection*> pair(con.toEdge, nullptr);
4954 : // special case for Persons in Netedit
4955 41730 : if (vClass == SVC_PEDESTRIAN) {
4956 0 : myViaSuccessors.push_back(pair); // Pedestrians have complete freedom of movement in all sucessors
4957 41730 : } else if ((con.fromLane >= 0) && (con.toLane >= 0) &&
4958 41730 : (con.toEdge != nullptr) &&
4959 41730 : ((getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane) & vClass) == vClass)) {
4960 : // ignore duplicates
4961 41164 : if (con.getLength() > 0) {
4962 : pair.second = &con;
4963 : }
4964 82328 : myViaSuccessors.push_back(pair);
4965 : }
4966 : }
4967 39240 : return myViaSuccessors;
4968 : }
4969 :
4970 :
4971 : void
4972 0 : NBEdge::debugPrintConnections(bool outgoing, bool incoming) const {
4973 0 : if (outgoing) {
4974 0 : for (const Connection& c : myConnections) {
4975 0 : std::cout << " " << getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
4976 : }
4977 : }
4978 0 : if (incoming) {
4979 0 : for (NBEdge* inc : myFrom->getIncomingEdges()) {
4980 0 : for (Connection& c : inc->myConnections) {
4981 0 : if (c.toEdge == this) {
4982 0 : std::cout << " " << inc->getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
4983 : }
4984 : }
4985 : }
4986 : }
4987 0 : }
4988 :
4989 :
4990 : int
4991 85 : NBEdge::getLaneIndexFromLaneID(const std::string laneID) {
4992 170 : return StringUtils::toInt(laneID.substr(laneID.rfind("_") + 1));
4993 : }
4994 :
4995 : bool
4996 36 : NBEdge::joinLanes(SVCPermissions perms) {
4997 : bool haveJoined = false;
4998 : int i = 0;
4999 181 : while (i < getNumLanes() - 1) {
5000 145 : if ((getPermissions(i) == perms) && (getPermissions(i + 1) == perms)) {
5001 37 : const double newWidth = getLaneWidth(i) + getLaneWidth(i + 1);
5002 37 : const std::string newType = myLanes[i].type + "|" + myLanes[i + 1].type;
5003 37 : deleteLane(i, false, true);
5004 37 : setLaneWidth(i, newWidth);
5005 37 : setLaneType(i, newType);
5006 : haveJoined = true;
5007 : } else {
5008 108 : i++;
5009 : }
5010 : }
5011 36 : return haveJoined;
5012 : }
5013 :
5014 :
5015 : EdgeVector
5016 62033 : NBEdge::filterByPermissions(const EdgeVector& edges, SVCPermissions permissions) {
5017 : EdgeVector result;
5018 266923 : for (NBEdge* edge : edges) {
5019 204890 : if ((edge->getPermissions() & permissions) != 0) {
5020 189793 : result.push_back(edge);
5021 : }
5022 : }
5023 62033 : return result;
5024 0 : }
5025 :
5026 : NBEdge*
5027 2855 : NBEdge::getStraightContinuation(SVCPermissions permissions) const {
5028 2855 : EdgeVector cands = filterByPermissions(myTo->getOutgoingEdges(), permissions);
5029 2855 : if (cands.size() == 0) {
5030 : return nullptr;
5031 : }
5032 2851 : sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this));
5033 2851 : NBEdge* best = cands.front();
5034 2851 : if (isTurningDirectionAt(best)) {
5035 : return nullptr;
5036 : } else {
5037 : return best;
5038 : }
5039 2855 : }
5040 :
5041 : NBEdge*
5042 124 : NBEdge::getStraightPredecessor(SVCPermissions permissions) const {
5043 124 : EdgeVector cands = filterByPermissions(myFrom->getIncomingEdges(), permissions);
5044 124 : if (cands.size() == 0) {
5045 : return nullptr;
5046 : }
5047 112 : sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this, false));
5048 112 : NBEdge* best = cands.front();
5049 112 : if (best->isTurningDirectionAt(this)) {
5050 : return nullptr;
5051 : } else {
5052 : return best;
5053 : }
5054 124 : }
5055 :
5056 :
5057 : NBEdge*
5058 47 : NBEdge::guessOpposite(bool reguess) {
5059 : NBEdge* opposite = nullptr;
5060 47 : if (getNumLanes() > 0) {
5061 : NBEdge::Lane& lastLane = myLanes.back();
5062 47 : const double lastWidth = getLaneWidth(getNumLanes() - 1);
5063 47 : if (lastLane.oppositeID == "" || reguess) {
5064 76 : for (NBEdge* cand : getToNode()->getOutgoingEdges()) {
5065 44 : if (cand->getToNode() == getFromNode() && !cand->getLanes().empty()) {
5066 : const NBEdge::Lane& candLastLane = cand->getLanes().back();
5067 48 : if (candLastLane.oppositeID == "" || candLastLane.oppositeID == getLaneID(getNumLanes() - 1)) {
5068 30 : const double lastWidthCand = cand->getLaneWidth(cand->getNumLanes() - 1);
5069 : // in sharp corners, the difference may be higher
5070 : // factor (sqrt(2) for 90 degree corners
5071 30 : const double threshold = 1.42 * 0.5 * (lastWidth + lastWidthCand) + 0.5;
5072 60 : const double distance = VectorHelper<double>::maxValue(lastLane.shape.distances(cand->getLanes().back().shape));
5073 : //std::cout << " distance=" << distance << " threshold=" << threshold << " distances=" << toString(lastLane.shape.distances(cand->getLanes().back().shape)) << "\n";
5074 30 : if (distance < threshold) {
5075 : opposite = cand;
5076 : }
5077 : }
5078 : }
5079 : }
5080 32 : if (opposite != nullptr) {
5081 56 : lastLane.oppositeID = opposite->getLaneID(opposite->getNumLanes() - 1);
5082 : }
5083 : }
5084 : }
5085 47 : return opposite;
5086 : }
5087 :
5088 : double
5089 0 : NBEdge::getDistancAt(double pos) const {
5090 : // negative values of myDistances indicate descending kilometrage
5091 0 : return fabs(myDistance + pos);
5092 : }
5093 :
5094 : /****************************************************************************/
|