Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file NBNode.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Sascha Krieg
18 : /// @author Michael Behrisch
19 : /// @date Tue, 20 Nov 2001
20 : ///
21 : // The representation of a single node
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <string>
26 : #include <map>
27 : #include <cassert>
28 : #include <algorithm>
29 : #include <vector>
30 : #include <deque>
31 : #include <set>
32 : #include <cmath>
33 : #include <iterator>
34 : #include <utils/common/UtilExceptions.h>
35 : #include <utils/common/StringUtils.h>
36 : #include <utils/options/OptionsCont.h>
37 : #include <utils/geom/GeomHelper.h>
38 : #include <utils/common/MsgHandler.h>
39 : #include <utils/common/StdDefs.h>
40 : #include <utils/common/ToString.h>
41 : #include <utils/geom/GeoConvHelper.h>
42 : #include <utils/iodevices/OutputDevice.h>
43 : #include <iomanip>
44 : #include "NBNode.h"
45 : #include "NBAlgorithms.h"
46 : #include "NBNodeCont.h"
47 : #include "NBNodeShapeComputer.h"
48 : #include "NBEdgeCont.h"
49 : #include "NBTypeCont.h"
50 : #include "NBHelpers.h"
51 : #include "NBDistrict.h"
52 : #include "NBContHelper.h"
53 : #include "NBRequest.h"
54 : #include "NBOwnTLDef.h"
55 : #include "NBLoadedSUMOTLDef.h"
56 : #include "NBTrafficLightLogicCont.h"
57 : #include "NBTrafficLightDefinition.h"
58 :
59 : // allow to extend a crossing across multiple edges
60 : #define EXTEND_CROSSING_ANGLE_THRESHOLD 35.0 // degrees
61 : // create intermediate walking areas if either of the following thresholds is exceeded
62 : #define SPLIT_CROSSING_WIDTH_THRESHOLD 1.5 // meters
63 : #define SPLIT_CROSSING_ANGLE_THRESHOLD 5 // degrees
64 :
65 : // minimum length for a weaving section at a combined on-off ramp
66 : #define MIN_WEAVE_LENGTH 20.0
67 :
68 : //#define DEBUG_CONNECTION_GUESSING
69 : //#define DEBUG_SMOOTH_GEOM
70 : //#define DEBUG_PED_STRUCTURES
71 : //#define DEBUG_EDGE_SORTING
72 : //#define DEBUG_CROSSING_OUTLINE
73 : //#define DEBUGCOND true
74 : #define DEBUG_NODE_ID "C"
75 : #define DEBUGCOND (getID() == DEBUG_NODE_ID)
76 : #define DEBUGCOND2(obj) ((obj != 0 && (obj)->getID() == DEBUG_NODE_ID))
77 : #ifdef DEBUG_PED_STRUCTURES
78 : #define DEBUGCOUT(cond, msg) DEBUGOUT(cond, msg)
79 : #else
80 : #define DEBUGCOUT(cond, msg)
81 : #endif
82 :
83 : // ===========================================================================
84 : // static members
85 : // ===========================================================================
86 : const int NBNode::FORWARD(1);
87 : const int NBNode::BACKWARD(-1);
88 : const double NBNode::UNSPECIFIED_RADIUS = -1;
89 : const int NBNode::AVOID_WIDE_LEFT_TURN(1);
90 : const int NBNode::AVOID_WIDE_RIGHT_TURN(2);
91 : const int NBNode::FOUR_CONTROL_POINTS(4);
92 : const int NBNode::AVOID_INTERSECTING_LEFT_TURNS(8);
93 : const int NBNode::SCURVE_IGNORE(16);
94 : const int NBNode::INDIRECT_LEFT(32);
95 :
96 : SVCPermissions NBNode::myHaveRailSignalClasses;
97 : SVCPermissions NBNode::myPermitUnsignalizedClasses;
98 :
99 : // ===========================================================================
100 : // method definitions
101 : // ===========================================================================
102 : /* -------------------------------------------------------------------------
103 : * NBNode::ApproachingDivider-methods
104 : * ----------------------------------------------------------------------- */
105 72893 : NBNode::ApproachingDivider::ApproachingDivider(
106 72893 : const EdgeVector& approaching, NBEdge* currentOutgoing) :
107 72893 : myApproaching(approaching),
108 72893 : myCurrentOutgoing(currentOutgoing),
109 72893 : myNumStraight(0),
110 72893 : myIsBikeEdge(currentOutgoing->getPermissions() == SVC_BICYCLE) {
111 : // collect lanes which are expliclity targeted
112 : std::set<int> approachedLanes;
113 : bool hasIncomingBusLane = false;
114 212339 : for (const NBEdge* const approachingEdge : myApproaching) {
115 510572 : for (const NBEdge::Connection& con : approachingEdge->getConnections()) {
116 371126 : if (con.toEdge == myCurrentOutgoing) {
117 157239 : approachedLanes.insert(con.toLane);
118 : }
119 : }
120 139446 : myDirections.push_back(approachingEdge->getToNode()->getDirection(approachingEdge, currentOutgoing));
121 139446 : if (myDirections.back() == LinkDirection::STRAIGHT) {
122 59189 : myNumStraight++;
123 : }
124 139446 : hasIncomingBusLane |= (approachingEdge->getSpecialLane(SVC_BUS) != -1);
125 : }
126 : // compute the indices of lanes that should be targeted (excluding pedestrian
127 : // lanes that will be connected from walkingAreas and forbidden lanes)
128 : // if the lane is targeted by an explicitly set connection we need
129 : // to make it available anyway
130 167933 : for (int i = 0; i < currentOutgoing->getNumLanes(); ++i) {
131 95040 : const SVCPermissions lp = currentOutgoing->getPermissions(i);
132 3876 : if ((lp == SVC_PEDESTRIAN
133 : // don't consider bicycle lanes as targets unless the target
134 : // edge is exclusively for bicycles
135 90514 : || (lp == SVC_BICYCLE && !myIsBikeEdge)
136 89966 : || (lp == SVC_BUS && hasIncomingBusLane)
137 89950 : || isForbidden(lp))
138 95040 : && approachedLanes.count(i) == 0) {
139 3876 : continue;
140 : }
141 91164 : myAvailableLanes.push_back(i);
142 : }
143 72893 : }
144 :
145 :
146 72893 : NBNode::ApproachingDivider::~ApproachingDivider() {}
147 :
148 :
149 : void
150 148263 : NBNode::ApproachingDivider::execute(const int src, const int dest) {
151 : assert((int)myApproaching.size() > src);
152 : // get the origin edge
153 148263 : NBEdge* incomingEdge = myApproaching[src];
154 148263 : if (incomingEdge->getStep() == NBEdge::EdgeBuildingStep::LANES2LANES_DONE || incomingEdge->getStep() == NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
155 58486 : return;
156 : }
157 89911 : if (myAvailableLanes.size() == 0) {
158 : return;
159 : }
160 90407 : std::vector<int> approachingLanes = incomingEdge->getConnectionLanes(myCurrentOutgoing, myIsBikeEdge || incomingEdge->getPermissions() == SVC_BICYCLE);
161 89821 : if (approachingLanes.size() == 0) {
162 : return;
163 : }
164 : #ifdef DEBUG_CONNECTION_GUESSING
165 : if (DEBUGCOND2(incomingEdge->getToNode())) {
166 : std::cout << "Bre:ex src=" << src << " dest=" << dest << " in=" << incomingEdge->getID() << " apLanes=" << toString(approachingLanes) << "\n";
167 : }
168 :
169 : #endif
170 89777 : int numConnections = (int)approachingLanes.size();
171 : double factor = 1;
172 : const bool rightOnRed = incomingEdge->getToNode()->getType() == SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED;
173 89777 : if (myNumStraight == 1 && myDirections[src] == LinkDirection::STRAIGHT && (
174 : // we do not want to destroy ramp-like assignments where the
175 : // on-connection-per-lane rule avoids conflicts
176 : // - at a traffic light the phases are seperated so there is no conflict anyway
177 3751 : (incomingEdge->getToNode()->isTLControlled() && !rightOnRed)
178 : // - there are no incoming edges to the right
179 31794 : || src == 0
180 : // - a minor straight road is likely in conflict anyway
181 16212 : || (incomingEdge->getJunctionPriority(incomingEdge->getToNode()) == NBEdge::MINOR_ROAD && !rightOnRed))) {
182 26474 : numConnections = (int)myAvailableLanes.size();
183 26474 : factor = (double)approachingLanes.size() / (double)numConnections;
184 26474 : if (factor > 0.5) {
185 : factor = 1;
186 : }
187 : }
188 89777 : std::deque<int>* approachedLanes = spread(numConnections, dest);
189 : assert(approachedLanes->size() <= myAvailableLanes.size());
190 : // set lanes
191 89777 : const int maxFrom = (int)approachingLanes.size() - 1;
192 192474 : for (int i = 0; i < (int)approachedLanes->size(); i++) {
193 : // distribute i evenly on approaching lanes in case we are building more
194 : // connections than there are lanes
195 102697 : int fromLane = approachingLanes[MIN2((int)(i * factor), maxFrom)];
196 102697 : int approached = myAvailableLanes[(*approachedLanes)[i]];
197 205394 : incomingEdge->setConnection(fromLane, myCurrentOutgoing, approached, NBEdge::Lane2LaneInfoType::COMPUTED);
198 : }
199 89777 : delete approachedLanes;
200 89821 : }
201 :
202 :
203 : std::deque<int>*
204 89777 : NBNode::ApproachingDivider::spread(int numLanes, int dest) const {
205 89777 : std::deque<int>* ret = new std::deque<int>();
206 : // when only one lane is approached, we check, whether the double-value
207 : // is assigned more to the left or right lane
208 89777 : if (numLanes == 1) {
209 : ret->push_back(dest);
210 80446 : return ret;
211 : }
212 :
213 9331 : const int numOutgoingLanes = (int)myAvailableLanes.size();
214 : //
215 : ret->push_back(dest);
216 : int noSet = 1;
217 : int roffset = 1;
218 : int loffset = 1;
219 13011 : while (noSet < numLanes) {
220 : // It may be possible, that there are not enough lanes the source
221 : // lanes may be divided on
222 : // In this case, they remain unset
223 : // !!! this is only a hack. It is possible, that this yields in
224 : // uncommon divisions
225 9674 : if (numOutgoingLanes == noSet) {
226 : return ret;
227 : }
228 :
229 : // as due to the conversion of double->uint the numbers will be lower
230 : // than they should be, we try to append to the left side first
231 : //
232 : // check whether the left boundary of the approached street has
233 : // been overridden; if so, move all lanes to the right
234 9671 : if (dest + loffset >= numOutgoingLanes) {
235 4881 : loffset -= 1;
236 4881 : roffset += 1;
237 10072 : for (int i = 0; i < (int)ret->size(); i++) {
238 5191 : (*ret)[i] = (*ret)[i] - 1;
239 : }
240 : }
241 : // append the next lane to the left of all edges
242 : // increase the position (destination edge)
243 9671 : ret->push_back(dest + loffset);
244 9671 : noSet++;
245 9671 : loffset += 1;
246 :
247 : // as above
248 9671 : if (numOutgoingLanes == noSet) {
249 : return ret;
250 : }
251 :
252 : // now we try to append the next lane to the right side, when needed
253 3680 : if (noSet < numLanes) {
254 : // check whether the right boundary of the approached street has
255 : // been overridden; if so, move all lanes to the right
256 3249 : if (dest < roffset) {
257 763 : loffset += 1;
258 763 : roffset -= 1;
259 2341 : for (int i = 0; i < (int)ret->size(); i++) {
260 1578 : (*ret)[i] = (*ret)[i] + 1;
261 : }
262 : }
263 3249 : ret->push_front(dest - roffset);
264 3249 : noSet++;
265 3249 : roffset += 1;
266 : }
267 : }
268 : return ret;
269 : }
270 :
271 :
272 : /* -------------------------------------------------------------------------
273 : * NBNode::Crossing-methods
274 : * ----------------------------------------------------------------------- */
275 2045 : NBNode::Crossing::Crossing(const NBNode* _node, const EdgeVector& _edges, double _width, bool _priority, int _customTLIndex, int _customTLIndex2, const PositionVector& _customShape) :
276 : Parameterised(),
277 2045 : node(_node),
278 2045 : edges(_edges),
279 2045 : customWidth(_width),
280 2045 : width(_width),
281 2045 : priority(_priority),
282 : customShape(_customShape),
283 2045 : tlLinkIndex(_customTLIndex),
284 2045 : tlLinkIndex2(_customTLIndex2),
285 2045 : customTLIndex(_customTLIndex),
286 2045 : customTLIndex2(_customTLIndex2),
287 2045 : valid(true) {
288 2045 : }
289 :
290 :
291 : /* -------------------------------------------------------------------------
292 : * NBNode-methods
293 : * ----------------------------------------------------------------------- */
294 36628 : NBNode::NBNode(const std::string& id, const Position& position,
295 36628 : SumoXMLNodeType type) :
296 36628 : Named(StringUtils::convertUmlaute(id)),
297 36628 : myPosition(position),
298 36628 : myType(type),
299 36628 : myDistrict(nullptr),
300 36628 : myHaveCustomPoly(false),
301 36628 : myRequest(nullptr),
302 36628 : myRadius(UNSPECIFIED_RADIUS),
303 36628 : myKeepClear(OptionsCont::getOptions().getBool("default.junctions.keep-clear")),
304 36630 : myRightOfWay(SUMOXMLDefinitions::RightOfWayValues.get(OptionsCont::getOptions().getString("default.right-of-way"))),
305 36628 : myFringeType(FringeType::DEFAULT),
306 36628 : myRoundaboutType(RoundaboutType::DEFAULT),
307 36628 : myDiscardAllCrossings(false),
308 36628 : myCrossingsLoadedFromSumoNet(0),
309 36628 : myDisplacementError(0),
310 36628 : myIsBentPriority(false),
311 36628 : myTypeWasGuessed(false) {
312 36628 : if (!SUMOXMLDefinitions::isValidNetID(myID)) {
313 3 : throw ProcessError(TLF("Invalid node id '%'.", myID));
314 : }
315 : if (myPosition.isNAN()) {
316 3 : throw ProcessError(TLF("Invalid position '%' for node '%'", myPosition, myID));
317 : }
318 36644 : }
319 :
320 :
321 33728 : NBNode::NBNode(const std::string& id, const Position& position, NBDistrict* district) :
322 33728 : Named(StringUtils::convertUmlaute(id)),
323 33728 : myPosition(position),
324 33728 : myType(district == nullptr ? SumoXMLNodeType::UNKNOWN : SumoXMLNodeType::DISTRICT),
325 33728 : myDistrict(district),
326 33728 : myHaveCustomPoly(false),
327 33728 : myRequest(nullptr),
328 33728 : myRadius(UNSPECIFIED_RADIUS),
329 33728 : myKeepClear(OptionsCont::getOptions().getBool("default.junctions.keep-clear")),
330 33728 : myRightOfWay(SUMOXMLDefinitions::RightOfWayValues.get(OptionsCont::getOptions().getString("default.right-of-way"))),
331 33728 : myFringeType(FringeType::DEFAULT),
332 33728 : myRoundaboutType(RoundaboutType::DEFAULT),
333 33728 : myDiscardAllCrossings(false),
334 33728 : myCrossingsLoadedFromSumoNet(0),
335 33728 : myDisplacementError(0),
336 33728 : myIsBentPriority(false),
337 33728 : myTypeWasGuessed(false) {
338 33728 : if (!SUMOXMLDefinitions::isValidNetID(myID)) {
339 0 : throw ProcessError(TLF("Invalid node id '%'.", myID));
340 : }
341 : if (myPosition.isNAN()) {
342 0 : throw ProcessError(TLF("Invalid position '%' for node '%'", myPosition, myID));
343 : }
344 33728 : }
345 :
346 :
347 140708 : NBNode::~NBNode() {
348 70354 : delete myRequest;
349 281416 : }
350 :
351 :
352 : void
353 5007 : NBNode::reinit(const Position& position, SumoXMLNodeType type,
354 : bool updateEdgeGeometries) {
355 5007 : myPosition = position;
356 : if (myPosition.isNAN()) {
357 0 : throw ProcessError(TLF("Invalid position '%' for node '%'", myPosition, myID));
358 : }
359 : // patch type
360 5007 : myType = type;
361 5007 : if (!isTrafficLight(myType)) {
362 4741 : removeTrafficLights();
363 : }
364 5007 : if (updateEdgeGeometries) {
365 3269 : for (EdgeVector::iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
366 1926 : PositionVector geom = (*i)->getGeometry();
367 1926 : geom[-1] = myPosition;
368 1926 : (*i)->setGeometry(geom);
369 1926 : }
370 3275 : for (EdgeVector::iterator i = myOutgoingEdges.begin(); i != myOutgoingEdges.end(); i++) {
371 1932 : PositionVector geom = (*i)->getGeometry();
372 1932 : geom[0] = myPosition;
373 1932 : (*i)->setGeometry(geom);
374 1932 : }
375 : }
376 5007 : }
377 :
378 :
379 :
380 : // ----------- Applying offset
381 : void
382 37773 : NBNode::reshiftPosition(double xoff, double yoff) {
383 : myPosition.add(xoff, yoff, 0);
384 37773 : myPoly.add(xoff, yoff, 0);
385 37780 : for (auto& wacs : myWalkingAreaCustomShapes) {
386 7 : wacs.shape.add(xoff, yoff, 0);
387 : }
388 38078 : for (auto& c : myCrossings) {
389 305 : c->customShape.add(xoff, yoff, 0);
390 : }
391 37773 : }
392 :
393 :
394 : void
395 57430 : NBNode::roundGeometry() {
396 57430 : myPosition.round(gPrecision);
397 57430 : if (myHaveCustomPoly) {
398 52 : myPoly.round(gPrecision);
399 : }
400 57441 : for (auto& wacs : myWalkingAreaCustomShapes) {
401 11 : wacs.shape.round(gPrecision);
402 : }
403 57886 : for (auto& c : myCrossings) {
404 456 : c->customShape.round(gPrecision);
405 : }
406 57430 : }
407 :
408 :
409 : void
410 829 : NBNode::mirrorX() {
411 : myPosition.mul(1, -1);
412 829 : myPoly.mirrorX();
413 : // mirror pre-computed geometry of crossings and walkingareas
414 861 : for (auto& c : myCrossings) {
415 32 : c->customShape.mirrorX();
416 32 : c->shape.mirrorX();
417 : }
418 861 : for (auto& wa : myWalkingAreas) {
419 32 : wa.shape.mirrorX();
420 : }
421 831 : for (auto& wacs : myWalkingAreaCustomShapes) {
422 2 : wacs.shape.mirrorX();
423 : }
424 829 : }
425 :
426 :
427 : // ----------- Methods for dealing with assigned traffic lights
428 : void
429 23618 : NBNode::addTrafficLight(NBTrafficLightDefinition* tlDef) {
430 : myTrafficLights.insert(tlDef);
431 : // rail signals receive a temporary traffic light in order to set connection tl-linkIndex
432 23618 : if (!isTrafficLight(myType) && myType != SumoXMLNodeType::RAIL_SIGNAL && myType != SumoXMLNodeType::RAIL_CROSSING) {
433 2745 : myType = SumoXMLNodeType::TRAFFIC_LIGHT;
434 : }
435 23618 : }
436 :
437 :
438 : void
439 5839 : NBNode::removeTrafficLight(NBTrafficLightDefinition* tlDef) {
440 5839 : tlDef->removeNode(this);
441 : myTrafficLights.erase(tlDef);
442 5839 : }
443 :
444 :
445 : void
446 17249 : NBNode::removeTrafficLights(bool setAsPriority) {
447 : std::set<NBTrafficLightDefinition*> trafficLights = myTrafficLights; // make a copy because we will modify the original
448 18253 : for (std::set<NBTrafficLightDefinition*>::const_iterator i = trafficLights.begin(); i != trafficLights.end(); ++i) {
449 1004 : removeTrafficLight(*i);
450 : }
451 17249 : if (setAsPriority) {
452 24 : myType = myRequest != nullptr ? SumoXMLNodeType::PRIORITY : (
453 3 : myType == SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION ? SumoXMLNodeType::NOJUNCTION : SumoXMLNodeType::DEAD_END);
454 : }
455 17249 : }
456 :
457 : bool
458 3973 : NBNode::hadSignal() const {
459 12750 : for (NBEdge* e : getIncomingEdges()) {
460 : if (e->getSignalPosition() != Position::INVALID) {
461 : return true;
462 : }
463 : }
464 : return false;
465 : }
466 :
467 :
468 : void
469 1576 : NBNode::invalidateTLS(NBTrafficLightLogicCont& tlCont, bool addedConnections, bool removedConnections) {
470 1576 : if (isTLControlled()) {
471 : std::set<NBTrafficLightDefinition*> oldDefs(myTrafficLights);
472 1380 : for (std::set<NBTrafficLightDefinition*>::iterator it = oldDefs.begin(); it != oldDefs.end(); ++it) {
473 690 : NBTrafficLightDefinition* orig = *it;
474 690 : if (dynamic_cast<NBLoadedSUMOTLDef*>(orig) != nullptr) {
475 13 : dynamic_cast<NBLoadedSUMOTLDef*>(orig)->registerModifications(addedConnections, removedConnections);
476 677 : } else if (dynamic_cast<NBOwnTLDef*>(orig) == nullptr) {
477 0 : NBTrafficLightDefinition* newDef = new NBOwnTLDef(orig->getID(), orig->getOffset(), orig->getType());
478 : const std::vector<NBNode*>& nodes = orig->getNodes();
479 0 : while (!nodes.empty()) {
480 0 : newDef->addNode(nodes.front());
481 0 : nodes.front()->removeTrafficLight(orig);
482 : }
483 0 : tlCont.removeFully(orig->getID());
484 0 : tlCont.insert(newDef);
485 : }
486 : }
487 : }
488 1576 : }
489 :
490 :
491 : void
492 7966 : NBNode::shiftTLConnectionLaneIndex(NBEdge* edge, int offset, int threshold) {
493 9688 : for (std::set<NBTrafficLightDefinition*>::iterator it = myTrafficLights.begin(); it != myTrafficLights.end(); ++it) {
494 1722 : (*it)->shiftTLConnectionLaneIndex(edge, offset, threshold);
495 : }
496 7966 : }
497 :
498 : // ----------- Prunning the input
499 : int
500 68519 : NBNode::removeSelfLoops(NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tc) {
501 : int ret = 0;
502 : int pos = 0;
503 : EdgeVector::const_iterator j = myIncomingEdges.begin();
504 179372 : while (j != myIncomingEdges.end()) {
505 : // skip edges which are only incoming and not outgoing
506 110853 : if (find(myOutgoingEdges.begin(), myOutgoingEdges.end(), *j) == myOutgoingEdges.end()) {
507 : ++j;
508 110853 : ++pos;
509 110853 : continue;
510 : }
511 : // an edge with both its origin and destination being the current
512 : // node should be removed
513 0 : NBEdge* dummy = *j;
514 0 : WRITE_WARNINGF(TL(" Removing self-looping edge '%'"), dummy->getID());
515 : // get the list of incoming edges connected to the self-loop
516 0 : EdgeVector incomingConnected = dummy->getIncomingEdges();
517 : // get the list of outgoing edges connected to the self-loop
518 0 : EdgeVector outgoingConnected = dummy->getConnectedEdges();
519 : // let the self-loop remap its connections
520 0 : dummy->remapConnections(incomingConnected);
521 0 : remapRemoved(tc, dummy, incomingConnected, outgoingConnected);
522 : // delete the self-loop
523 0 : ec.erase(dc, dummy);
524 : j = myIncomingEdges.begin() + pos;
525 0 : ++ret;
526 0 : }
527 68519 : return ret;
528 : }
529 :
530 :
531 : // -----------
532 : void
533 126658 : NBNode::addIncomingEdge(NBEdge* edge) {
534 : assert(edge != 0);
535 126658 : if (find(myIncomingEdges.begin(), myIncomingEdges.end(), edge) == myIncomingEdges.end()) {
536 124544 : myIncomingEdges.push_back(edge);
537 124544 : myAllEdges.push_back(edge);
538 : }
539 126658 : }
540 :
541 :
542 : void
543 126829 : NBNode::addOutgoingEdge(NBEdge* edge) {
544 : assert(edge != 0);
545 126829 : if (find(myOutgoingEdges.begin(), myOutgoingEdges.end(), edge) == myOutgoingEdges.end()) {
546 124714 : myOutgoingEdges.push_back(edge);
547 124714 : myAllEdges.push_back(edge);
548 : }
549 126829 : }
550 :
551 :
552 : bool
553 72768 : NBNode::isSimpleContinuation(bool checkLaneNumbers, bool checkWidth) const {
554 : // one in, one out->continuation
555 72768 : if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1) {
556 10885 : NBEdge* in = myIncomingEdges.front();
557 10885 : NBEdge* out = myOutgoingEdges.front();
558 : // both must have the same number of lanes
559 10877 : return ((!checkLaneNumbers || in->getNumLanes() == out->getNumLanes())
560 19958 : && (!checkWidth || in->getTotalWidth() == out->getTotalWidth()));
561 : }
562 : // two in and two out and both in reverse direction
563 61883 : if (myIncomingEdges.size() == 2 && myOutgoingEdges.size() == 2) {
564 32182 : for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
565 25063 : NBEdge* in = *i;
566 25063 : EdgeVector::const_iterator opposite = find_if(myOutgoingEdges.begin(), myOutgoingEdges.end(), NBContHelper::opposite_finder(in));
567 : // must have an opposite edge
568 25063 : if (opposite == myOutgoingEdges.end()) {
569 8989 : return false;
570 : }
571 : // both must have the same number of lanes
572 18037 : NBContHelper::nextCW(myOutgoingEdges, opposite);
573 18037 : if (checkLaneNumbers && in->getNumLanes() != (*opposite)->getNumLanes()) {
574 : return false;
575 : }
576 16322 : if (checkWidth && in->getTotalWidth() != (*opposite)->getTotalWidth()) {
577 : return false;
578 : }
579 : }
580 : return true;
581 : }
582 : // nope
583 : return false;
584 : }
585 :
586 :
587 : PositionVector
588 265650 : NBNode::computeSmoothShape(const PositionVector& begShape,
589 : const PositionVector& endShape,
590 : int numPoints,
591 : bool isTurnaround,
592 : double extrapolateBeg,
593 : double extrapolateEnd,
594 : NBNode* recordError,
595 : int shapeFlag) const {
596 :
597 265650 : bool ok = true;
598 265650 : if ((shapeFlag & INDIRECT_LEFT) != 0) {
599 32 : return indirectLeftShape(begShape, endShape, numPoints);
600 : }
601 265618 : PositionVector init = bezierControlPoints(begShape, endShape, isTurnaround, extrapolateBeg, extrapolateEnd, ok, recordError, DEG2RAD(5), shapeFlag);
602 : #ifdef DEBUG_SMOOTH_GEOM
603 : if (DEBUGCOND) {
604 : std::cout << "computeSmoothShape node " << getID() << " begShape=" << begShape << " endShape=" << endShape << " init=" << init << " shapeFlag=" << shapeFlag << "\n";
605 : }
606 : #endif
607 265618 : if (init.size() == 0) {
608 81983 : PositionVector ret;
609 81983 : ret.push_back(begShape.back());
610 81983 : ret.push_back(endShape.front());
611 : return ret;
612 81983 : } else {
613 183635 : return init.bezier(numPoints).smoothedZFront();
614 : }
615 265618 : }
616 :
617 : PositionVector
618 267635 : NBNode::bezierControlPoints(
619 : const PositionVector& begShape,
620 : const PositionVector& endShape,
621 : bool isTurnaround,
622 : double extrapolateBeg,
623 : double extrapolateEnd,
624 : bool& ok,
625 : NBNode* recordError,
626 : double straightThresh,
627 : int shapeFlag) {
628 :
629 267635 : const Position beg = begShape.back();
630 267635 : const Position end = endShape.front();
631 : const double dist = beg.distanceTo2D(end);
632 267635 : PositionVector init;
633 267635 : if (dist < POSITION_EPS || beg.distanceTo2D(begShape[-2]) < POSITION_EPS || end.distanceTo2D(endShape[1]) < POSITION_EPS) {
634 : #ifdef DEBUG_SMOOTH_GEOM
635 : if (DEBUGCOND2(recordError)) std::cout << " bezierControlPoints failed beg=" << beg << " end=" << end
636 : << " dist=" << dist
637 : << " distBegLast=" << beg.distanceTo2D(begShape[-2])
638 : << " distEndFirst=" << end.distanceTo2D(endShape[1])
639 : << "\n";
640 : #endif
641 : // typically, this node a is a simpleContinuation. see also #2539
642 : return init;
643 : } else {
644 258254 : init.push_back(beg);
645 258254 : if (isTurnaround) {
646 : // turnarounds:
647 : // - end of incoming lane
648 : // - position between incoming/outgoing end/begin shifted by the distance orthogonally
649 : // - begin of outgoing lane
650 22343 : Position center = PositionVector::positionAtOffset2D(beg, end, beg.distanceTo2D(end) / (double) 2.);
651 22343 : center.sub(beg.y() - end.y(), end.x() - beg.x());
652 22343 : init.push_back(center);
653 : } else {
654 : const double EXT = 100;
655 235911 : const double angle = GeomHelper::angleDiff(begShape.angleAt2D(-2), endShape.angleAt2D(0));
656 235911 : PositionVector endShapeBegLine(endShape[0], endShape[1]);
657 235911 : PositionVector begShapeEndLineRev(begShape[-1], begShape[-2]);
658 235911 : endShapeBegLine.extrapolate2D(EXT, true);
659 235911 : begShapeEndLineRev.extrapolate2D(EXT, true);
660 : #ifdef DEBUG_SMOOTH_GEOM
661 : if (DEBUGCOND2(recordError)) std::cout
662 : << " endShapeBegLine=" << endShapeBegLine
663 : << " begShapeEndLineRev=" << begShapeEndLineRev
664 : << " angle=" << RAD2DEG(angle) << "\n";
665 : #endif
666 235911 : if (fabs(angle) < M_PI / 4.) {
667 : // very low angle: could be an s-shape or a straight line
668 104126 : const double displacementAngle = GeomHelper::angleDiff(begShape.angleAt2D(-2), beg.angleTo2D(end));
669 104126 : const double bendDeg = RAD2DEG(fabs(displacementAngle - angle));
670 104126 : const double halfDistance = dist / 2;
671 104126 : if (fabs(displacementAngle) <= straightThresh && fabs(angle) <= straightThresh) {
672 : #ifdef DEBUG_SMOOTH_GEOM
673 : if (DEBUGCOND2(recordError)) std::cout << " bezierControlPoints identified straight line beg=" << beg << " end=" << end
674 : << " angle=" << RAD2DEG(angle) << " displacementAngle=" << RAD2DEG(displacementAngle) << "\n";
675 : #endif
676 70185 : return PositionVector();
677 33941 : } else if (bendDeg > 22.5 && pow(bendDeg / 45, 2) / dist > 0.13) {
678 : // do not allow s-curves with extreme bends
679 : // (a linear dependency is to restrictive at low displacementAngles and too permisive at high angles)
680 : #ifdef DEBUG_SMOOTH_GEOM
681 : if (DEBUGCOND2(recordError)) std::cout << " bezierControlPoints found extreme s-curve, falling back to straight line beg=" << beg << " end=" << end
682 : << " angle=" << RAD2DEG(angle) << " displacementAngle=" << RAD2DEG(displacementAngle)
683 : << " dist=" << dist << " bendDeg=" << bendDeg << " bd2=" << pow(bendDeg / 45, 2)
684 : << " displacementError=" << sin(displacementAngle) * dist
685 : << " begShape=" << begShape << " endShape=" << endShape << "\n";
686 : #endif
687 1300 : ok = false;
688 1300 : if (recordError != nullptr && (shapeFlag & SCURVE_IGNORE) == 0) {
689 849 : recordError->myDisplacementError = MAX2(recordError->myDisplacementError, (double)fabs(sin(displacementAngle) * dist));
690 : }
691 1300 : return PositionVector();
692 : } else {
693 32641 : const double endLength = begShape[-2].distanceTo2D(begShape[-1]);
694 32641 : const double off1 = endLength + MIN2(extrapolateBeg, halfDistance);
695 65282 : init.push_back(PositionVector::positionAtOffset2D(begShapeEndLineRev[1], begShapeEndLineRev[0], off1));
696 32641 : const double off2 = EXT - MIN2(extrapolateEnd, halfDistance);
697 65282 : init.push_back(PositionVector::positionAtOffset2D(endShapeBegLine[0], endShapeBegLine[1], off2));
698 : #ifdef DEBUG_SMOOTH_GEOM
699 : if (DEBUGCOND2(recordError)) std::cout << " bezierControlPoints found s-curve beg=" << beg << " end=" << end
700 : << " angle=" << RAD2DEG(angle) << " displacementAngle=" << RAD2DEG(displacementAngle)
701 : << " halfDistance=" << halfDistance << "\n";
702 : #endif
703 : }
704 : } else {
705 : // turning
706 : // - end of incoming lane
707 : // - intersection of the extrapolated lanes
708 : // - begin of outgoing lane
709 : // attention: if there is no intersection, use a straight line
710 131785 : Position intersect = endShapeBegLine.intersectionPosition2D(begShapeEndLineRev);
711 : if (intersect == Position::INVALID) {
712 : #ifdef DEBUG_SMOOTH_GEOM
713 : if (DEBUGCOND2(recordError)) {
714 : std::cout << " bezierControlPoints failed beg=" << beg << " end=" << end << " intersect=" << intersect
715 : << " endShapeBegLine=" << endShapeBegLine
716 : << " begShapeEndLineRev=" << begShapeEndLineRev
717 : << "\n";
718 : }
719 : #endif
720 1570 : ok = false;
721 1570 : if (recordError != nullptr && (shapeFlag & SCURVE_IGNORE) == 0) {
722 : // it's unclear if this error can be solved via stretching the intersection.
723 524 : recordError->myDisplacementError = MAX2(recordError->myDisplacementError, (double)1.0);
724 : }
725 1570 : return PositionVector();
726 : }
727 130215 : const double begOffset = begShapeEndLineRev.nearest_offset_to_point2D(intersect);
728 130215 : const double endOffset = endShapeBegLine.nearest_offset_to_point2D(intersect);
729 : /*
730 : if ((shapeFlag & FOUR_CONTROL_POINTS) == 0 && (begOffset >= EXT || endOffset >= EXT)) {
731 : // intersection point lies within begShape / endShape so we cannot use it
732 : if (dist < 2) {
733 : return PositionVector();
734 : }
735 : shapeFlag |= FOUR_CONTROL_POINTS;
736 : extrapolateBeg = MIN2(10.0, dist / 2);
737 : extrapolateEnd = extrapolateBeg;
738 : }
739 : */
740 130215 : const double minControlLength = MIN2((double)1.0, dist / 2);
741 : const double distBeg = intersect.distanceTo2D(beg);
742 : const double distEnd = intersect.distanceTo2D(end);
743 130215 : const bool lengthenBeg = distBeg <= minControlLength;
744 130215 : const bool lengthenEnd = distEnd <= minControlLength;
745 : #ifdef DEBUG_SMOOTH_GEOM
746 : if (DEBUGCOND2(recordError)) std::cout
747 : << " beg=" << beg << " end=" << end << " intersect=" << intersect
748 : << " distBeg=" << distBeg << " distEnd=" << distEnd
749 : << " begOffset=" << begOffset << " endOffset=" << endOffset
750 : << " lEnd=" << lengthenEnd << " lBeg=" << lengthenBeg
751 : << "\n";
752 : #endif
753 130215 : if (lengthenBeg && lengthenEnd) {
754 : #ifdef DEBUG_SMOOTH_GEOM
755 : if (DEBUGCOND2(recordError)) {
756 : std::cout << " bezierControlPoints failed\n";
757 : }
758 : #endif
759 0 : if (recordError != nullptr && (shapeFlag & SCURVE_IGNORE) == 0) {
760 : // This should be fixable with minor stretching
761 0 : recordError->myDisplacementError = MAX2(recordError->myDisplacementError, (double)1.0);
762 : }
763 0 : ok = false;
764 0 : return PositionVector();
765 130215 : } else if ((shapeFlag & FOUR_CONTROL_POINTS)) {
766 45 : init.push_back(begShapeEndLineRev.positionAtOffset2D(EXT - extrapolateBeg));
767 90 : init.push_back(endShapeBegLine.positionAtOffset2D(EXT - extrapolateEnd));
768 130170 : } else if (lengthenBeg || lengthenEnd) {
769 355 : init.push_back(begShapeEndLineRev.positionAtOffset2D(EXT - minControlLength));
770 710 : init.push_back(endShapeBegLine.positionAtOffset2D(EXT - minControlLength));
771 129815 : } else if ((shapeFlag & AVOID_WIDE_LEFT_TURN) != 0
772 : // there are two reasons for enabling special geometry rules:
773 : // 1) sharp edge angles which could cause overshoot
774 : // 2) junction geometries with a large displacement between opposite left turns
775 : // which would cause the default geometry to overlap
776 96870 : && ((shapeFlag & AVOID_INTERSECTING_LEFT_TURNS) != 0
777 92419 : || (angle > DEG2RAD(95) && (distBeg > 20 || distEnd > 20)))) {
778 : //std::cout << " bezierControlPoints intersect=" << intersect << " dist=" << dist << " distBeg=" << distBeg << " distEnd=" << distEnd << " angle=" << RAD2DEG(angle) << " flag=" << shapeFlag << "\n";
779 5536 : const double factor = ((shapeFlag & AVOID_INTERSECTING_LEFT_TURNS) == 0 ? 1
780 4451 : : MIN2(0.6, 16 / dist));
781 10021 : init.push_back(begShapeEndLineRev.positionAtOffset2D(EXT - MIN2(distBeg * factor / 1.2, dist * factor / 1.8)));
782 9912 : init.push_back(endShapeBegLine.positionAtOffset2D(EXT - MIN2(distEnd * factor / 1.2, dist * factor / 1.8)));
783 129815 : } else if ((shapeFlag & AVOID_WIDE_RIGHT_TURN) != 0 && angle < DEG2RAD(-95) && (distBeg > 20 || distEnd > 20)) {
784 : //std::cout << " bezierControlPoints intersect=" << intersect << " distBeg=" << distBeg << " distEnd=" << distEnd << "\n";
785 0 : init.push_back(begShapeEndLineRev.positionAtOffset2D(EXT - MIN2(distBeg / 1.4, dist / 2)));
786 0 : init.push_back(endShapeBegLine.positionAtOffset2D(EXT - MIN2(distEnd / 1.4, dist / 2)));
787 : } else {
788 : double z;
789 124279 : const double z1 = begShapeEndLineRev.positionAtOffset2D(begOffset).z();
790 124279 : const double z2 = endShapeBegLine.positionAtOffset2D(endOffset).z();
791 124279 : const double z3 = 0.5 * (beg.z() + end.z());
792 : // if z1 and z2 are on the same side in regard to z3 then we
793 : // can use their avarage. Otherwise, the intersection in 3D
794 : // is not good and we are better of using z3
795 124279 : if ((z1 <= z3 && z2 <= z3) || (z1 >= z3 && z2 >= z3)) {
796 124227 : z = 0.5 * (z1 + z2);
797 : } else {
798 : z = z3;
799 : }
800 : intersect.set(intersect.x(), intersect.y(), z);
801 124279 : init.push_back(intersect);
802 : }
803 : }
804 235911 : }
805 185199 : init.push_back(end);
806 : }
807 : return init;
808 267635 : }
809 :
810 : PositionVector
811 32 : NBNode::indirectLeftShape(const PositionVector& begShape, const PositionVector& endShape, int numPoints) const {
812 : UNUSED_PARAMETER(numPoints);
813 32 : PositionVector result;
814 32 : result.push_back(begShape.back());
815 : //const double angle = GeomHelper::angleDiff(begShape.angleAt2D(-2), endShape.angleAt2D(0));
816 32 : PositionVector endShapeBegLine(endShape[0], endShape[1]);
817 32 : PositionVector begShapeEndLineRev(begShape[-1], begShape[-2]);
818 32 : endShapeBegLine.extrapolate2D(100, true);
819 32 : begShapeEndLineRev.extrapolate2D(100, true);
820 32 : Position intersect = endShapeBegLine.intersectionPosition2D(begShapeEndLineRev);
821 : if (intersect == Position::INVALID) {
822 0 : WRITE_WARNINGF(TL("Could not compute indirect left turn shape at node '%'"), getID());
823 : } else {
824 32 : Position dir = intersect;
825 32 : dir.sub(endShape[0]);
826 32 : dir.norm2D();
827 32 : const double radius = myRadius == NBNode::UNSPECIFIED_RADIUS ? OptionsCont::getOptions().getFloat("default.junctions.radius") : myRadius;
828 : dir.mul(radius);
829 32 : result.push_back(intersect + dir);
830 : }
831 32 : result.push_back(endShape.front());
832 32 : return result;
833 32 : }
834 :
835 : PositionVector
836 149616 : NBNode::computeInternalLaneShape(const NBEdge* fromE, const NBEdge::Connection& con, int numPoints, NBNode* recordError, int shapeFlag) const {
837 149616 : if (con.fromLane >= fromE->getNumLanes()) {
838 0 : throw ProcessError(TLF("Connection '%' starts at a non-existant lane.", con.getDescription(fromE)));
839 : }
840 149616 : if (con.toLane >= con.toEdge->getNumLanes()) {
841 0 : throw ProcessError(TLF("Connection '%' targets a non-existant lane.", con.getDescription(fromE)));
842 : }
843 149616 : PositionVector fromShape = fromE->getLaneShape(con.fromLane);
844 149616 : PositionVector toShape = con.toEdge->getLaneShape(con.toLane);
845 149616 : PositionVector ret;
846 149616 : bool useCustomShape = con.customShape.size() > 0;
847 149616 : if (useCustomShape) {
848 : // ensure that the shape starts and ends at the intersection boundary
849 164 : PositionVector startBorder = fromE->getNodeBorder(this);
850 164 : if (startBorder.size() == 0) {
851 246 : startBorder = fromShape.getOrthogonal(fromShape.back(), 1, true);
852 : }
853 164 : PositionVector tmp = NBEdge::startShapeAt(con.customShape, this, startBorder);
854 164 : if (tmp.size() < 2) {
855 0 : WRITE_WARNINGF(TL("Could not use custom shape for connection %."), con.getDescription(fromE));
856 : useCustomShape = false;
857 : } else {
858 164 : if (tmp.length2D() > con.customShape.length2D() + POSITION_EPS) {
859 : // shape was lengthened at the start, make sure it attaches at the center of the lane
860 80 : tmp[0] = fromShape.back();
861 84 : } else if (recordError != nullptr) {
862 22 : const double offset = tmp[0].distanceTo2D(fromShape.back());
863 22 : if (offset > fromE->getLaneWidth(con.fromLane) / 2) {
864 6 : WRITE_WARNINGF(TL("Custom shape has distance % to incoming lane for connection %."), offset, con.getDescription(fromE));
865 : }
866 : }
867 164 : PositionVector endBorder = con.toEdge->getNodeBorder(this);
868 164 : if (endBorder.size() == 0) {
869 246 : endBorder = toShape.getOrthogonal(toShape.front(), 1, false);
870 : }
871 328 : ret = NBEdge::startShapeAt(tmp.reverse(), this, endBorder).reverse();
872 164 : if (ret.size() < 2) {
873 0 : WRITE_WARNINGF(TL("Could not use custom shape for connection %."), con.getDescription(fromE));
874 : useCustomShape = false;
875 164 : } else if (ret.length2D() > tmp.length2D() + POSITION_EPS) {
876 : // shape was lengthened at the end, make sure it attaches at the center of the lane
877 50 : ret[-1] = toShape.front();
878 114 : } else if (recordError != nullptr) {
879 26 : const double offset = ret[-1].distanceTo2D(toShape.front());
880 26 : if (offset > con.toEdge->getLaneWidth(con.toLane) / 2) {
881 2 : WRITE_WARNINGF(TL("Custom shape has distance % to outgoing lane for connection %."), offset, con.getDescription(fromE));
882 : }
883 : }
884 164 : }
885 164 : }
886 149616 : if (!useCustomShape) {
887 149452 : displaceShapeAtWidthChange(fromE, con, fromShape, toShape);
888 149452 : double extrapolateBeg = 5. * fromE->getNumLanes();
889 149452 : double extrapolateEnd = 5. * con.toEdge->getNumLanes();
890 149452 : LinkDirection dir = getDirection(fromE, con.toEdge);
891 149452 : if (dir == LinkDirection::LEFT || dir == LinkDirection::TURN) {
892 57520 : shapeFlag += AVOID_WIDE_LEFT_TURN;
893 : }
894 149452 : if (con.indirectLeft) {
895 32 : shapeFlag += INDIRECT_LEFT;
896 : }
897 : #ifdef DEBUG_SMOOTH_GEOM
898 : if (DEBUGCOND) {
899 : std::cout << "computeInternalLaneShape node " << getID() << " fromE=" << fromE->getID() << " toE=" << con.toEdge->getID() << "\n";
900 : }
901 : #endif
902 149452 : ret = computeSmoothShape(fromShape, toShape,
903 149452 : numPoints, fromE->getTurnDestination() == con.toEdge,
904 : extrapolateBeg, extrapolateEnd, recordError, shapeFlag);
905 : }
906 149616 : const NBEdge::Lane& lane = fromE->getLaneStruct(con.fromLane);
907 149616 : if (lane.endOffset > 0) {
908 72 : PositionVector beg = lane.shape.getSubpart(lane.shape.length() - lane.endOffset, lane.shape.length());
909 72 : beg.append(ret);
910 : ret = beg;
911 72 : }
912 149616 : if (con.toEdge->isBidiRail() && con.toEdge->getTurnDestination(true)->getEndOffset() > 0) {
913 4 : PositionVector end = toShape.getSubpart(0, con.toEdge->getTurnDestination(true)->getEndOffset());
914 4 : ret.append(end);
915 4 : }
916 149616 : return ret;
917 149616 : }
918 :
919 :
920 : bool
921 245966 : NBNode::isConstantWidthTransition() const {
922 : return (myIncomingEdges.size() == 1
923 22049 : && myOutgoingEdges.size() == 1
924 16610 : && myIncomingEdges[0]->getNumLanes() != myOutgoingEdges[0]->getNumLanes()
925 253666 : && myIncomingEdges[0]->getTotalWidth() == myOutgoingEdges[0]->getTotalWidth());
926 : }
927 :
928 : void
929 149452 : NBNode::displaceShapeAtWidthChange(const NBEdge* from, const NBEdge::Connection& con,
930 : PositionVector& fromShape, PositionVector& toShape) const {
931 149452 : if (isConstantWidthTransition()) {
932 : // displace shapes
933 14 : NBEdge* in = myIncomingEdges[0];
934 14 : NBEdge* out = myOutgoingEdges[0];
935 14 : double outCenter = out->getLaneWidth(con.toLane) / 2;
936 27 : for (int i = 0; i < con.toLane; ++i) {
937 13 : outCenter += out->getLaneWidth(i);
938 : }
939 14 : double inCenter = in->getLaneWidth(con.fromLane) / 2;
940 25 : for (int i = 0; i < con.fromLane; ++i) {
941 11 : inCenter += in->getLaneWidth(i);
942 : }
943 : //std::cout << "displaceShapeAtWidthChange inCenter=" << inCenter << " outCenter=" << outCenter << "\n";
944 : try {
945 14 : if (in->getNumLanes() > out->getNumLanes()) {
946 : // shift toShape so the internal lane ends straight at the displaced entry point
947 3 : toShape.move2side(outCenter - inCenter);
948 : } else {
949 : // shift fromShape so the internal lane starts straight at the displaced exit point
950 11 : fromShape.move2side(inCenter - outCenter);
951 :
952 : }
953 0 : } catch (InvalidArgument&) { }
954 : } else {
955 149438 : SVCPermissions fromP = from->getPermissions(con.fromLane);
956 149438 : SVCPermissions toP = con.toEdge->getPermissions(con.toLane);
957 149438 : if ((fromP & toP) == SVC_BICYCLE && (fromP | toP) != SVC_BICYCLE) {
958 1964 : double shift = (from->getLaneWidth(con.fromLane) - con.toEdge->getLaneWidth(con.toLane)) / 2;
959 1964 : if (toP == SVC_BICYCLE) {
960 : // let connection to dedicated bicycle lane start on the right side of a mixed lane for straight an right-going connections
961 : // (on the left side for left turns)
962 : // XXX indirect left turns should also start on the right side
963 779 : LinkDirection dir = getDirection(from, con.toEdge);
964 779 : if ((dir == LinkDirection::LEFT) || (dir == LinkDirection::PARTLEFT) || (dir == LinkDirection::TURN)) {
965 316 : fromShape.move2side(-shift);
966 : } else {
967 463 : fromShape.move2side(shift);
968 : }
969 1185 : } else if (fromP == SVC_BICYCLE) {
970 : // let connection from dedicated bicycle end on the right side of a mixed lane
971 604 : toShape.move2side(-shift);
972 : }
973 : }
974 : }
975 149452 : }
976 :
977 : bool
978 775660 : NBNode::needsCont(const NBEdge* fromE, const NBEdge* otherFromE,
979 : const NBEdge::Connection& c, const NBEdge::Connection& otherC, bool checkOnlyTLS) const {
980 775660 : const NBEdge* toE = c.toEdge;
981 775660 : const NBEdge* otherToE = otherC.toEdge;
982 :
983 775660 : if (!checkOnlyTLS) {
984 760280 : if (myType == SumoXMLNodeType::RIGHT_BEFORE_LEFT
985 : || myType == SumoXMLNodeType::LEFT_BEFORE_RIGHT
986 : || myType == SumoXMLNodeType::ALLWAY_STOP
987 760280 : || myType == SumoXMLNodeType::ZIPPER) {
988 : return false;
989 : }
990 694293 : LinkDirection d1 = getDirection(fromE, toE);
991 694293 : const bool thisRight = (d1 == LinkDirection::RIGHT || d1 == LinkDirection::PARTRIGHT);
992 939092 : const bool rightTurnConflict = (thisRight &&
993 244799 : NBNode::rightTurnConflict(fromE, toE, c.fromLane, otherFromE, otherToE, otherC.fromLane));
994 694293 : if (thisRight && !rightTurnConflict) {
995 : return false;
996 : }
997 449808 : if (myRequest && myRequest->indirectLeftTurnConflict(fromE, c, otherFromE, otherC, false)) {
998 : return true;
999 : }
1000 449776 : if (!(foes(otherFromE, otherToE, fromE, toE) || myRequest == nullptr || rightTurnConflict)) {
1001 : // if they do not cross, no waiting place is needed
1002 : return false;
1003 : }
1004 159758 : LinkDirection d2 = getDirection(otherFromE, otherToE);
1005 159758 : if (d2 == LinkDirection::TURN) {
1006 : return false;
1007 : }
1008 144868 : if (fromE == otherFromE && !thisRight) {
1009 : // ignore same edge links except for right-turns
1010 : return false;
1011 : }
1012 143404 : if (thisRight && d2 != LinkDirection::STRAIGHT) {
1013 : return false;
1014 : }
1015 : }
1016 158669 : if (c.tlID != "") {
1017 : assert(myTrafficLights.size() > 0 || myType == SumoXMLNodeType::RAIL_CROSSING || myType == SumoXMLNodeType::RAIL_SIGNAL);
1018 78435 : for (std::set<NBTrafficLightDefinition*>::const_iterator it = myTrafficLights.begin(); it != myTrafficLights.end(); ++it) {
1019 48045 : if ((*it)->needsCont(fromE, toE, otherFromE, otherToE)) {
1020 : return true;
1021 : }
1022 : }
1023 : return false;
1024 : }
1025 110822 : if (fromE->getJunctionPriority(this) > 0 && otherFromE->getJunctionPriority(this) > 0) {
1026 21841 : return mustBrake(fromE, toE, c.fromLane, c.toLane, false);
1027 : }
1028 : return false;
1029 : }
1030 :
1031 : bool
1032 1285740 : NBNode::tlsStrandedConflict(const NBEdge* from, const NBEdge::Connection& c,
1033 : const NBEdge* foeFrom, const NBEdge::Connection& foe) const {
1034 68795 : return (foe.haveVia && isTLControlled() && c.tlLinkIndex >= 0 && foe.tlLinkIndex >= 0
1035 68543 : && !foeFrom->isTurningDirectionAt(foe.toEdge)
1036 38714 : && foes(from, c.toEdge, foeFrom, foe.toEdge)
1037 1301120 : && !needsCont(foeFrom, from, foe, c, true));
1038 : }
1039 :
1040 :
1041 : void
1042 14874 : NBNode::removeJoinedTrafficLights() {
1043 : std::set<NBTrafficLightDefinition*> trafficLights = myTrafficLights; // make a copy because we will modify the original
1044 14946 : for (std::set<NBTrafficLightDefinition*>::const_iterator i = trafficLights.begin(); i != trafficLights.end(); ++i) {
1045 : // if this is the only controlled node we keep the tlDef as it is to generate a warning later
1046 72 : if ((*i)->getNodes().size() > 1) {
1047 : myTrafficLights.erase(*i);
1048 7 : (*i)->removeNode(this);
1049 7 : (*i)->setParticipantsInformation();
1050 7 : (*i)->setTLControllingInformation();
1051 : }
1052 : }
1053 14874 : }
1054 :
1055 :
1056 : void
1057 57628 : NBNode::computeLogic(const NBEdgeCont& ec) {
1058 57628 : delete myRequest; // possibly recomputation step
1059 57628 : myRequest = nullptr;
1060 57628 : if (myIncomingEdges.size() == 0 || myOutgoingEdges.size() == 0) {
1061 : // no logic if nothing happens here
1062 8430 : myType = SumoXMLNodeType::DEAD_END;
1063 8430 : removeJoinedTrafficLights();
1064 8430 : return;
1065 : }
1066 : // compute the logic if necessary or split the junction
1067 49198 : if (myType != SumoXMLNodeType::NOJUNCTION && myType != SumoXMLNodeType::DISTRICT && myType != SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION) {
1068 : // build the request
1069 49076 : myRequest = new NBRequest(ec, this, myAllEdges, myIncomingEdges, myOutgoingEdges, myBlockedConnections);
1070 : // check whether it is not too large
1071 49076 : int numConnections = numNormalConnections();
1072 49076 : if (numConnections >= SUMO_MAX_CONNECTIONS) {
1073 : // yep -> make it untcontrolled, warn
1074 0 : delete myRequest;
1075 0 : myRequest = nullptr;
1076 0 : if (myType == SumoXMLNodeType::TRAFFIC_LIGHT) {
1077 0 : myType = SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION;
1078 : } else {
1079 0 : myType = SumoXMLNodeType::NOJUNCTION;
1080 : }
1081 0 : WRITE_WARNINGF(TL("Junction '%' is too complicated (% connections, max %); will be set to %."),
1082 : getID(), numConnections, SUMO_MAX_CONNECTIONS, toString(myType));
1083 49076 : } else if (numConnections == 0) {
1084 6444 : delete myRequest;
1085 6444 : myRequest = nullptr;
1086 6444 : myType = SumoXMLNodeType::DEAD_END;
1087 6444 : removeJoinedTrafficLights();
1088 : } else {
1089 42632 : myRequest->buildBitfieldLogic();
1090 : }
1091 : }
1092 : }
1093 :
1094 :
1095 : void
1096 57628 : NBNode::computeLogic2(bool checkLaneFoes) {
1097 57628 : if (myRequest != nullptr) {
1098 42632 : myRequest->computeLogic(checkLaneFoes);
1099 : }
1100 57628 : }
1101 :
1102 : void
1103 57628 : NBNode::computeKeepClear() {
1104 57628 : if (hasConflict()) {
1105 19730 : if (!myKeepClear) {
1106 10 : for (NBEdge* incoming : myIncomingEdges) {
1107 : std::vector<NBEdge::Connection>& connections = incoming->getConnections();
1108 40 : for (NBEdge::Connection& c : connections) {
1109 32 : c.keepClear = KEEPCLEAR_FALSE;
1110 : }
1111 : }
1112 19728 : } else if (geometryLike() && myCrossings.size() == 0 && !isTLControlled()) {
1113 : int linkIndex = 0;
1114 795 : for (NBEdge* incoming : myIncomingEdges) {
1115 : std::vector<NBEdge::Connection>& connections = incoming->getConnections();
1116 1719 : for (NBEdge::Connection& c : connections) {
1117 1195 : if (c.keepClear == KEEPCLEAR_UNSPECIFIED && myRequest->hasConflictAtLink(linkIndex)) {
1118 576 : const LinkState linkState = getLinkState(incoming, c.toEdge, c.fromLane, c.toLane, c.mayDefinitelyPass, c.tlID);
1119 576 : if (linkState == LINKSTATE_MAJOR) {
1120 331 : c.keepClear = KEEPCLEAR_FALSE;
1121 : }
1122 : }
1123 : }
1124 524 : linkIndex++;
1125 : }
1126 : }
1127 : }
1128 57628 : }
1129 :
1130 :
1131 : bool
1132 41724 : NBNode::writeLogic(OutputDevice& into) const {
1133 41724 : if (myRequest) {
1134 41640 : myRequest->writeLogic(into);
1135 41640 : return true;
1136 : }
1137 : return false;
1138 : }
1139 :
1140 :
1141 : const std::string
1142 0 : NBNode::getFoes(int linkIndex) const {
1143 0 : if (myRequest == nullptr) {
1144 0 : return "";
1145 : } else {
1146 0 : return myRequest->getFoes(linkIndex);
1147 : }
1148 : }
1149 :
1150 :
1151 : const std::string
1152 0 : NBNode::getResponse(int linkIndex) const {
1153 0 : if (myRequest == nullptr) {
1154 0 : return "";
1155 : } else {
1156 0 : return myRequest->getResponse(linkIndex);
1157 : }
1158 : }
1159 :
1160 : bool
1161 57813 : NBNode::hasConflict() const {
1162 57813 : if (myRequest == nullptr) {
1163 : return false;
1164 : } else {
1165 42814 : return myRequest->hasConflict();
1166 : }
1167 : }
1168 :
1169 :
1170 : bool
1171 439 : NBNode::hasConflict(const NBEdge* e) const {
1172 439 : if (myRequest == nullptr) {
1173 : return false;
1174 : }
1175 507 : for (const auto& con : e->getConnections()) {
1176 505 : const int index = getConnectionIndex(e, con);
1177 505 : if (myRequest->hasConflictAtLink(index)) {
1178 : return true;
1179 : }
1180 : }
1181 : return false;
1182 : }
1183 :
1184 :
1185 : void
1186 56 : NBNode::updateSurroundingGeometry() {
1187 56 : NBTurningDirectionsComputer::computeTurnDirectionsForNode(this, false);
1188 56 : sortEdges(false);
1189 56 : computeNodeShape(-1);
1190 435 : for (NBEdge* edge : myAllEdges) {
1191 379 : edge->computeEdgeShape();
1192 : }
1193 56 : }
1194 :
1195 : void
1196 81356 : NBNode::computeNodeShape(double mismatchThreshold) {
1197 81356 : if (myHaveCustomPoly) {
1198 : return;
1199 : }
1200 81298 : if (myIncomingEdges.size() == 0 && myOutgoingEdges.size() == 0) {
1201 : // may be an intermediate step during network editing
1202 : myPoly.clear();
1203 2557 : myPoly.push_back(myPosition);
1204 2557 : return;
1205 : }
1206 157482 : if (OptionsCont::getOptions().getFloat("default.junctions.radius") < 0) {
1207 : // skip shape computation by option
1208 : return;
1209 : }
1210 : try {
1211 78736 : NBNodeShapeComputer computer(*this);
1212 157472 : myPoly = computer.compute(OptionsCont::getOptions().getBool("junctions.minimal-shape"));
1213 236019 : if (myRadius == UNSPECIFIED_RADIUS && !OptionsCont::getOptions().isDefault("default.junctions.radius")) {
1214 51 : myRadius = computer.getRadius();
1215 : }
1216 78736 : if (myPoly.size() > 0) {
1217 : PositionVector tmp = myPoly;
1218 78736 : tmp.push_back_noDoublePos(tmp[0]); // need closed shape
1219 : if (mismatchThreshold >= 0
1220 52110 : && !tmp.around(myPosition)
1221 95644 : && tmp.distance2D(myPosition) > mismatchThreshold) {
1222 231 : WRITE_WARNINGF(TL("Shape for junction '%' has distance % to its given position."), myID, tmp.distance2D(myPosition));
1223 : }
1224 78736 : }
1225 78736 : } catch (InvalidArgument&) {
1226 0 : WRITE_WARNINGF(TL("For junction '%': could not compute shape."), myID);
1227 : // make sure our shape is not empty because our XML schema forbids empty attributes
1228 : myPoly.clear();
1229 0 : myPoly.push_back(myPosition);
1230 0 : }
1231 : }
1232 :
1233 :
1234 : void
1235 57632 : NBNode::computeLanes2Lanes() {
1236 : // special case a):
1237 : // one in, one out, the outgoing has more lanes
1238 57632 : if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1) {
1239 16149 : NBEdge* in = myIncomingEdges[0];
1240 16149 : NBEdge* out = myOutgoingEdges[0];
1241 : // check if it's not the turnaround
1242 16149 : if (in->getTurnDestination() == out) {
1243 : // will be added later or not...
1244 10149 : return;
1245 : }
1246 : int inOffset, inEnd, outOffset, outEnd, addedLanes;
1247 6549 : getReduction(out, in, outOffset, outEnd, inOffset, inEnd, addedLanes);
1248 : if (in->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES
1249 4010 : && addedLanes > 0
1250 7100 : && in->isConnectedTo(out)) {
1251 549 : const int addedRight = addedLanesRight(out, addedLanes);
1252 549 : const int addedLeft = addedLanes - addedRight;
1253 : #ifdef DEBUG_CONNECTION_GUESSING
1254 : if (DEBUGCOND) {
1255 : std::cout << "l2l node=" << getID() << " specialCase a. addedRight=" << addedRight << " addedLeft=" << addedLeft << " inOff=" << inOffset << " outOff=" << outOffset << " inEnd=" << inEnd << " outEnd=" << outEnd << "\n";
1256 : }
1257 : #endif
1258 : // "straight" connections
1259 1694 : for (int i = inOffset; i < inEnd; ++i) {
1260 2290 : in->setConnection(i, out, i - inOffset + outOffset + addedRight, NBEdge::Lane2LaneInfoType::COMPUTED);
1261 : }
1262 : // connect extra lane on the right
1263 604 : for (int i = 0; i < addedRight; ++i) {
1264 110 : in->setConnection(inOffset, out, outOffset + i, NBEdge::Lane2LaneInfoType::COMPUTED);
1265 : }
1266 : // connect extra lane on the left
1267 549 : const int inLeftMost = inEnd - 1;;
1268 549 : const int outOffset2 = outOffset + addedRight + inEnd - inOffset;
1269 1073 : for (int i = 0; i < addedLeft; ++i) {
1270 1048 : in->setConnection(inLeftMost, out, outOffset2 + i, NBEdge::Lane2LaneInfoType::COMPUTED);
1271 : }
1272 549 : if (out->getSpecialLane(SVC_BICYCLE) >= 0) {
1273 16 : recheckVClassConnections(out);
1274 : }
1275 549 : return;
1276 : }
1277 : }
1278 : // special case b):
1279 : // two in, one out, the outgoing has the same number of lanes as the sum of the incoming
1280 : // --> highway on-ramp
1281 47483 : if (myIncomingEdges.size() == 2 && myOutgoingEdges.size() == 1) {
1282 3533 : NBEdge* const out = myOutgoingEdges[0];
1283 3533 : NBEdge* in1 = myIncomingEdges[0];
1284 3533 : NBEdge* in2 = myIncomingEdges[1];
1285 3533 : const int outOffset = MAX2(0, out->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1286 3533 : int in1Offset = MAX2(0, in1->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1287 3533 : int in2Offset = MAX2(0, in2->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1288 3533 : if (in1->getNumLanes() + in2->getNumLanes() - in1Offset - in2Offset == out->getNumLanes() - outOffset
1289 158 : && (in1->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES)
1290 47 : && (in2->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES)
1291 47 : && in1 != out
1292 47 : && in2 != out
1293 47 : && in1->isConnectedTo(out)
1294 46 : && in2->isConnectedTo(out)
1295 46 : && in1->getSpecialLane(SVC_BICYCLE) == -1
1296 44 : && in2->getSpecialLane(SVC_BICYCLE) == -1
1297 44 : && out->getSpecialLane(SVC_BICYCLE) == -1
1298 44 : && in1->getSpecialLane(SVC_TRAM) == -1
1299 43 : && in2->getSpecialLane(SVC_TRAM) == -1
1300 43 : && out->getSpecialLane(SVC_TRAM) == -1
1301 3576 : && isLongEnough(out, MIN_WEAVE_LENGTH)) {
1302 : #ifdef DEBUG_CONNECTION_GUESSING
1303 : if (DEBUGCOND) {
1304 : std::cout << "l2l node=" << getID() << " specialCase b\n";
1305 : }
1306 : #endif
1307 : // for internal: check which one is the rightmost
1308 24 : double a1 = in1->getAngleAtNode(this);
1309 24 : double a2 = in2->getAngleAtNode(this);
1310 24 : double ccw = GeomHelper::getCCWAngleDiff(a1, a2);
1311 24 : double cw = GeomHelper::getCWAngleDiff(a1, a2);
1312 24 : if (ccw > cw) {
1313 : std::swap(in1, in2);
1314 : std::swap(in1Offset, in2Offset);
1315 : }
1316 24 : in1->addLane2LaneConnections(in1Offset, out, outOffset, in1->getNumLanes() - in1Offset, NBEdge::Lane2LaneInfoType::COMPUTED, true);
1317 24 : in2->addLane2LaneConnections(in2Offset, out, in1->getNumLanes() + outOffset - in1Offset, in2->getNumLanes() - in2Offset, NBEdge::Lane2LaneInfoType::COMPUTED, true);
1318 24 : if (out->getSpecialLane(SVC_BICYCLE) >= 0) {
1319 0 : recheckVClassConnections(out);
1320 : }
1321 : return;
1322 : }
1323 : }
1324 : // special case c):
1325 : // one in, two out, the incoming has the same number of lanes or only 1 lane less than the sum of the outgoing lanes
1326 : // --> highway off-ramp
1327 47459 : if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 2) {
1328 4541 : NBEdge* in = myIncomingEdges[0];
1329 4541 : NBEdge* out1 = myOutgoingEdges[0];
1330 4541 : NBEdge* out2 = myOutgoingEdges[1];
1331 4541 : const int inOffset = MAX2(0, in->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1332 4541 : int out1Offset = MAX2(0, out1->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1333 4541 : int out2Offset = MAX2(0, out2->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1334 4541 : const int deltaLaneSum = (out2->getNumLanes() + out1->getNumLanes() - out1Offset - out2Offset) - (in->getNumLanes() - inOffset);
1335 12522 : if ((deltaLaneSum == 0 || (deltaLaneSum == 1 && in->getPermissionVariants(inOffset, in->getNumLanes()).size() == 1))
1336 4303 : && (in->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES)
1337 2568 : && in != out1
1338 2568 : && in != out2
1339 2568 : && in->isConnectedTo(out1)
1340 956 : && in->isConnectedTo(out2)
1341 780 : && !in->isTurningDirectionAt(out1)
1342 5261 : && !in->isTurningDirectionAt(out2)
1343 : ) {
1344 : #ifdef DEBUG_CONNECTION_GUESSING
1345 : if (DEBUGCOND) {
1346 : std::cout << "l2l node=" << getID() << " specialCase c\n";
1347 : }
1348 : #endif
1349 : // for internal: check which one is the rightmost
1350 631 : if (NBContHelper::relative_outgoing_edge_sorter(in)(out2, out1)) {
1351 : std::swap(out1, out2);
1352 : std::swap(out1Offset, out2Offset);
1353 : }
1354 631 : in->addLane2LaneConnections(inOffset, out1, out1Offset, out1->getNumLanes() - out1Offset, NBEdge::Lane2LaneInfoType::COMPUTED, true);
1355 631 : in->addLane2LaneConnections(out1->getNumLanes() + inOffset - out1Offset - deltaLaneSum, out2, out2Offset, out2->getNumLanes() - out2Offset, NBEdge::Lane2LaneInfoType::COMPUTED, false);
1356 631 : if (in->getSpecialLane(SVC_BICYCLE) >= 0) {
1357 10 : recheckVClassConnections(out1);
1358 10 : recheckVClassConnections(out2);
1359 : }
1360 : return;
1361 : }
1362 : }
1363 : // special case d):
1364 : // one in, one out, the outgoing has one lane less and node has type 'zipper'
1365 46828 : if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1 && myType == SumoXMLNodeType::ZIPPER) {
1366 8 : NBEdge* in = myIncomingEdges[0];
1367 8 : NBEdge* out = myOutgoingEdges[0];
1368 : // check if it's not the turnaround
1369 8 : if (in->getTurnDestination() == out) {
1370 : // will be added later or not...
1371 : return;
1372 : }
1373 : #ifdef DEBUG_CONNECTION_GUESSING
1374 : if (DEBUGCOND) {
1375 : std::cout << "l2l node=" << getID() << " specialCase d\n";
1376 : }
1377 : #endif
1378 8 : const int inOffset = MAX2(0, in->getFirstNonPedestrianLaneIndex(FORWARD, true));
1379 8 : const int outOffset = MAX2(0, out->getFirstNonPedestrianLaneIndex(FORWARD, true));
1380 : if (in->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES
1381 2 : && in->getNumLanes() - inOffset == out->getNumLanes() - outOffset + 1
1382 2 : && in != out
1383 10 : && in->isConnectedTo(out)) {
1384 7 : for (int i = inOffset; i < in->getNumLanes(); ++i) {
1385 10 : in->setConnection(i, out, MIN2(outOffset + i, out->getNumLanes() - 1), NBEdge::Lane2LaneInfoType::COMPUTED, true);
1386 : }
1387 : return;
1388 : }
1389 : }
1390 : // special case f):
1391 : // one in, one out, out has reduced or same number of lanes
1392 46826 : if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1) {
1393 5998 : NBEdge* in = myIncomingEdges[0];
1394 5998 : NBEdge* out = myOutgoingEdges[0];
1395 : // check if it's not the turnaround
1396 5998 : if (in->getTurnDestination() == out) {
1397 : // will be added later or not...
1398 2140 : return;
1399 : }
1400 : int inOffset, inEnd, outOffset, outEnd, reduction;
1401 5998 : getReduction(in, out, inOffset, inEnd, outOffset, outEnd, reduction);
1402 : if (in->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES
1403 3459 : && reduction >= 0
1404 3457 : && in != out
1405 9455 : && in->isConnectedTo(out)) {
1406 : #ifdef DEBUG_CONNECTION_GUESSING
1407 : if (DEBUGCOND) {
1408 : std::cout << "l2l node=" << getID() << " specialCase f inOff=" << inOffset << " outOff=" << outOffset << " inEnd=" << inEnd << " outEnd=" << outEnd << " reduction=" << reduction << "\n";
1409 : }
1410 : #endif
1411 : // in case of reduced lane number, let the rightmost lanes end
1412 2140 : inOffset += reduction;
1413 5562 : for (int i = outOffset; i < outEnd; ++i) {
1414 6844 : in->setConnection(i + inOffset - outOffset, out, i, NBEdge::Lane2LaneInfoType::COMPUTED);
1415 : }
1416 : //std::cout << " special case f at node=" << getID() << " inOffset=" << inOffset << " outOffset=" << outOffset << "\n";
1417 2140 : recheckVClassConnections(out);
1418 2140 : return;
1419 : }
1420 : }
1421 :
1422 : // go through this node's outgoing edges
1423 : // for every outgoing edge, compute the distribution of the node's
1424 : // incoming edges on this edge when approaching this edge
1425 : // the incoming edges' steps will then also be marked as LANE2LANE_RECHECK...
1426 : EdgeVector approaching;
1427 134554 : for (NBEdge* currentOutgoing : myOutgoingEdges) {
1428 : // get the information about edges that do approach this edge
1429 89868 : getEdgesThatApproach(currentOutgoing, approaching);
1430 89868 : const int numApproaching = (int)approaching.size();
1431 89868 : if (numApproaching != 0) {
1432 72893 : ApproachingDivider divider(approaching, currentOutgoing);
1433 72893 : Bresenham::compute(÷r, numApproaching, divider.numAvailableLanes());
1434 72893 : }
1435 : #ifdef DEBUG_CONNECTION_GUESSING
1436 : if (DEBUGCOND) {
1437 : std::cout << "l2l node=" << getID() << " outgoing=" << currentOutgoing->getID() << " bresenham:\n";
1438 : for (NBEdge* e : myIncomingEdges) {
1439 : const std::vector<NBEdge::Connection>& elv = e->getConnections();
1440 : for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
1441 : std::cout << " " << e->getID() << "_" << (*k).fromLane << " -> " << Named::getIDSecure((*k).toEdge) << "_" << (*k).toLane << "\n";
1442 : }
1443 : }
1444 : }
1445 : #endif
1446 89868 : recheckVClassConnections(currentOutgoing);
1447 :
1448 : // in case of lane change restrictions on the outgoing edge, ensure that
1449 : // all its lanes can be reached from each connected incoming edge
1450 : bool targetProhibitsChange = false;
1451 203633 : for (int i = 0; i < currentOutgoing->getNumLanes(); i++) {
1452 113787 : const NBEdge::Lane& lane = currentOutgoing->getLanes()[i];
1453 186 : if ((lane.changeLeft != SVCAll && lane.changeLeft != SVC_IGNORING && i + 1 < currentOutgoing->getNumLanes())
1454 113953 : || (lane.changeRight != SVCAll && lane.changeRight != SVC_IGNORING && i > 0)) {
1455 : targetProhibitsChange = true;
1456 : break;
1457 : }
1458 : }
1459 89868 : if (targetProhibitsChange) {
1460 : //std::cout << " node=" << getID() << " outgoing=" << currentOutgoing->getID() << " targetProhibitsChange\n";
1461 56 : for (NBEdge* incoming : myIncomingEdges) {
1462 34 : if (incoming->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_DONE) {
1463 : std::map<int, int> outToIn;
1464 116 : for (const NBEdge::Connection& c : incoming->getConnections()) {
1465 91 : if (c.toEdge == currentOutgoing) {
1466 25 : outToIn[c.toLane] = c.fromLane;
1467 : }
1468 : }
1469 80 : for (int toLane = 0; toLane < currentOutgoing->getNumLanes(); toLane++) {
1470 : if (outToIn.count(toLane) == 0) {
1471 : bool added = false;
1472 : // find incoming lane for neighboring outgoing
1473 42 : for (int i = 0; i < toLane; i++) {
1474 : if (outToIn.count(i) != 0) {
1475 : #ifdef DEBUG_CONNECTION_GUESSING
1476 : if (DEBUGCOND) {
1477 : std::cout << "l2l node=" << getID() << " from=" << incoming->getID() << " to " << currentOutgoing->getLaneID(toLane) << " (changeProhibited, secondTarget)\n";
1478 : }
1479 : #endif
1480 8 : incoming->setConnection(outToIn[i], currentOutgoing, toLane, NBEdge::Lane2LaneInfoType::COMPUTED);
1481 : added = true;
1482 : break;
1483 : }
1484 : }
1485 : if (!added) {
1486 64 : for (int i = toLane; i < currentOutgoing->getNumLanes(); i++) {
1487 : if (outToIn.count(i) != 0) {
1488 : #ifdef DEBUG_CONNECTION_GUESSING
1489 : if (DEBUGCOND) {
1490 : std::cout << "l2l node=" << getID() << " from=" << incoming->getID() << " to " << currentOutgoing->getLaneID(toLane) << " (changeProhibited, newTarget)\n";
1491 : }
1492 : #endif
1493 10 : incoming->setConnection(outToIn[i], currentOutgoing, toLane, NBEdge::Lane2LaneInfoType::COMPUTED);
1494 : added = true;
1495 10 : break;
1496 : }
1497 : }
1498 : }
1499 : }
1500 : }
1501 : }
1502 : }
1503 : }
1504 : }
1505 : // special case e): rail_crossing
1506 : // there should only be straight connections here
1507 44686 : if (myType == SumoXMLNodeType::RAIL_CROSSING) {
1508 632 : for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
1509 464 : const std::vector<NBEdge::Connection> cons = (*i)->getConnections();
1510 787 : for (std::vector<NBEdge::Connection>::const_iterator k = cons.begin(); k != cons.end(); ++k) {
1511 323 : if (getDirection(*i, (*k).toEdge) == LinkDirection::TURN) {
1512 0 : (*i)->removeFromConnections((*k).toEdge);
1513 : }
1514 : }
1515 464 : }
1516 : }
1517 :
1518 : // ... but we may have the case that there are no outgoing edges
1519 : // In this case, we have to mark the incoming edges as being in state
1520 : // LANE2LANE( not RECHECK) by hand
1521 44686 : if (myOutgoingEdges.size() == 0) {
1522 9975 : for (NBEdge* incoming : myIncomingEdges) {
1523 5317 : incoming->markAsInLane2LaneState();
1524 : }
1525 : }
1526 :
1527 : #ifdef DEBUG_CONNECTION_GUESSING
1528 : if (DEBUGCOND) {
1529 : std::cout << "final connections at " << getID() << "\n";
1530 : for (NBEdge* e : myIncomingEdges) {
1531 : const std::vector<NBEdge::Connection>& elv = e->getConnections();
1532 : for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
1533 : std::cout << " " << e->getID() << "_" << (*k).fromLane << " -> " << Named::getIDSecure((*k).toEdge) << "_" << (*k).toLane << "\n";
1534 : }
1535 : }
1536 : }
1537 : #endif
1538 44686 : }
1539 :
1540 : void
1541 92074 : NBNode::recheckVClassConnections(NBEdge* currentOutgoing) {
1542 : // ensure that all modes have a connection if possible
1543 329678 : for (NBEdge* incoming : myIncomingEdges) {
1544 385266 : if (incoming->getConnectionLanes(currentOutgoing).size() > 0 && incoming->getStep() <= NBEdge::EdgeBuildingStep::LANES2LANES_DONE) {
1545 : // no connections are needed for pedestrians during this step
1546 : // no satisfaction is possible if the outgoing edge disallows
1547 89942 : SVCPermissions unsatisfied = incoming->getPermissions() & currentOutgoing->getPermissions() & ~SVC_PEDESTRIAN;
1548 : //std::cout << "initial unsatisfied modes from edge=" << incoming->getID() << " toEdge=" << currentOutgoing->getID() << " deadModes=" << getVehicleClassNames(unsatisfied) << "\n";
1549 : const std::vector<NBEdge::Connection>& elv = incoming->getConnections();
1550 324909 : for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
1551 : const NBEdge::Connection& c = *k;
1552 234967 : if (c.toEdge == currentOutgoing && c.toLane >= 0) {
1553 100903 : const SVCPermissions satisfied = (incoming->getPermissions(c.fromLane) & c.toEdge->getPermissions(c.toLane));
1554 : //std::cout << " from=" << incoming->getID() << "_" << c.fromLane << " to=" << c.toEdge->getID() << "_" << c.toLane << " satisfied=" << getVehicleClassNames(satisfied) << "\n";
1555 100903 : unsatisfied &= ~satisfied;
1556 : }
1557 : }
1558 89942 : if (unsatisfied != 0) {
1559 : #ifdef DEBUG_CONNECTION_GUESSING
1560 : if (DEBUGCOND) {
1561 : std::cout << " unsatisfied modes from edge=" << incoming->getID() << " toEdge=" << currentOutgoing->getID() << " deadModes=" << getVehicleClassNames(unsatisfied) << "\n";
1562 : }
1563 : #endif
1564 : int fromLane = 0;
1565 : // first attempt: try to use a dedicated fromLane
1566 3373 : while (unsatisfied != 0 && fromLane < incoming->getNumLanes()) {
1567 2209 : if (incoming->getPermissions(fromLane) == unsatisfied) {
1568 856 : unsatisfied = findToLaneForPermissions(currentOutgoing, fromLane, incoming, unsatisfied);
1569 : }
1570 2209 : fromLane++;
1571 : }
1572 : // second attempt: try to re-use a fromLane that already connects to currentOutgoing
1573 : // (because we don't wont to create extra turn lanes)
1574 : fromLane = 0;
1575 1725 : while (unsatisfied != 0 && fromLane < incoming->getNumLanes()) {
1576 561 : if ((incoming->getPermissions(fromLane) & unsatisfied) != 0
1577 807 : && incoming->getConnectionsFromLane(fromLane, currentOutgoing, -1).size() > 0) {
1578 246 : unsatisfied = findToLaneForPermissions(currentOutgoing, fromLane, incoming, unsatisfied);
1579 : }
1580 561 : fromLane++;
1581 : }
1582 : // third attempt: use any possible fromLane
1583 : fromLane = 0;
1584 1289 : while (unsatisfied != 0 && fromLane < incoming->getNumLanes()) {
1585 125 : if ((incoming->getPermissions(fromLane) & unsatisfied) != 0) {
1586 64 : unsatisfied = findToLaneForPermissions(currentOutgoing, fromLane, incoming, unsatisfied);
1587 : }
1588 125 : fromLane++;
1589 : }
1590 : #ifdef DEBUG_CONNECTION_GUESSING
1591 : if (DEBUGCOND) {
1592 : if (unsatisfied != 0) {
1593 : std::cout << " still unsatisfied modes from edge=" << incoming->getID() << " toEdge=" << currentOutgoing->getID() << " deadModes=" << getVehicleClassNames(unsatisfied) << "\n";
1594 : }
1595 : }
1596 : #endif
1597 : }
1598 : }
1599 : // prevent dead-end bus and bicycle lanes (they were excluded by the ApproachingDivider)
1600 : // and the bus/bicycle class might already be satisfied by other lanes
1601 237604 : recheckSpecialConnections(incoming, currentOutgoing, SVC_BUS);
1602 237604 : recheckSpecialConnections(incoming, currentOutgoing, SVC_BICYCLE);
1603 : }
1604 92074 : }
1605 :
1606 :
1607 : void
1608 475208 : NBNode::recheckSpecialConnections(NBEdge* incoming, NBEdge* currentOutgoing, SVCPermissions svcSpecial) {
1609 : // assume that left-turns and turn-arounds are better satisfied from lanes to the left
1610 475208 : const int specialTarget = currentOutgoing->getSpecialLane(svcSpecial);
1611 475208 : const LinkDirection dir = getDirection(incoming, currentOutgoing);
1612 : if (incoming->getStep() <= NBEdge::EdgeBuildingStep::LANES2LANES_DONE
1613 475208 : && ((specialTarget >= 0 && dir != LinkDirection::TURN)
1614 322173 : || dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT || dir == LinkDirection::STRAIGHT)) {
1615 : bool builtConnection = false;
1616 393545 : for (int i = 0; i < (int)incoming->getNumLanes(); i++) {
1617 222893 : if (incoming->getPermissions(i) == svcSpecial
1618 223410 : && incoming->getConnectionsFromLane(i, currentOutgoing).size() == 0) {
1619 : // find a dedicated bike lane as target
1620 517 : if (specialTarget >= 0) {
1621 162 : incoming->setConnection(i, currentOutgoing, specialTarget, NBEdge::Lane2LaneInfoType::COMPUTED);
1622 : #ifdef DEBUG_CONNECTION_GUESSING
1623 : if (DEBUGCOND) {
1624 : std::cout << " extra " << getVehicleClassNames(svcSpecial) << " connection from=" << incoming->getLaneID(i) << " (dedicated) to=" << currentOutgoing->getLaneID(specialTarget) << "\n";
1625 : }
1626 : #endif
1627 : builtConnection = true;
1628 : } else {
1629 : // do not create turns that create a conflict with neighboring lanes
1630 436 : if (avoidConfict(incoming, currentOutgoing, svcSpecial, dir, i)) {
1631 50 : continue;
1632 : }
1633 : // use any lane that allows the special class
1634 780 : for (int i2 = 0; i2 < (int)currentOutgoing->getNumLanes(); i2++) {
1635 527 : if ((currentOutgoing->getPermissions(i2) & svcSpecial) != 0) {
1636 : // possibly a double-connection
1637 133 : const bool allowDouble = (incoming->getPermissions(i) == svcSpecial
1638 133 : && (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT || dir == LinkDirection::STRAIGHT));
1639 133 : incoming->setConnection(i, currentOutgoing, i2, NBEdge::Lane2LaneInfoType::COMPUTED, allowDouble);
1640 : #ifdef DEBUG_CONNECTION_GUESSING
1641 : if (DEBUGCOND) {
1642 : std::cout << " extra " << getVehicleClassNames(svcSpecial) << " connection from=" << incoming->getLaneID(i) << " to=" << currentOutgoing->getLaneID(i2) << "\n";
1643 : }
1644 : #endif
1645 : builtConnection = true;
1646 133 : break;
1647 : }
1648 : }
1649 : }
1650 : }
1651 : }
1652 341304 : if (!builtConnection && specialTarget >= 0
1653 171108 : && incoming->getConnectionsFromLane(-1, currentOutgoing, specialTarget).size() == 0) {
1654 : // find origin lane that allows bicycles
1655 : int start = 0;
1656 : int end = incoming->getNumLanes();
1657 : int inc = 1;
1658 456 : if (dir == LinkDirection::TURN || dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT) {
1659 : std::swap(start, end);
1660 : inc = -1;
1661 : }
1662 826 : for (int i = start; i < end; i += inc) {
1663 376 : if ((incoming->getPermissions(i) & svcSpecial) != 0) {
1664 6 : incoming->setConnection(i, currentOutgoing, specialTarget, NBEdge::Lane2LaneInfoType::COMPUTED);
1665 : #ifdef DEBUG_CONNECTION_GUESSING
1666 : if (DEBUGCOND) {
1667 : std::cout << " extra " << getVehicleClassNames(svcSpecial) << " connection from=" << incoming->getLaneID(i) << " (final) to=" << currentOutgoing->getLaneID(specialTarget) << "\n";
1668 : }
1669 : #endif
1670 6 : break;
1671 : }
1672 : }
1673 : }
1674 : }
1675 475208 : }
1676 :
1677 :
1678 : bool
1679 436 : NBNode::avoidConfict(NBEdge* incoming, NBEdge* currentOutgoing, SVCPermissions svcSpecial, LinkDirection dir, int i) {
1680 2009 : for (const auto& c : incoming->getConnections()) {
1681 1580 : if (incoming->getPermissions(c.fromLane) == svcSpecial && c.toEdge == currentOutgoing) {
1682 : return true;
1683 : }
1684 : }
1685 429 : if (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT) {
1686 1180 : for (const auto& c : incoming->getConnections()) {
1687 932 : if (c.fromLane < i && (c.toEdge != currentOutgoing || incoming->getPermissions(c.fromLane) == svcSpecial)) {
1688 : return true;
1689 : }
1690 : }
1691 : } else if (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT) {
1692 : for (const auto& c : incoming->getConnections()) {
1693 : if (c.fromLane > i && (c.toEdge != currentOutgoing || incoming->getPermissions(c.fromLane) == svcSpecial)) {
1694 : return true;
1695 : }
1696 : }
1697 157 : } else if (svcSpecial != SVC_BICYCLE && dir == LinkDirection::STRAIGHT) {
1698 70 : for (const auto& c : incoming->getConnections()) {
1699 59 : const LinkDirection dir2 = getDirection(incoming, c.toEdge);
1700 59 : if (c.fromLane < i && (dir2 == LinkDirection::LEFT || dir2 == LinkDirection::PARTLEFT)) {
1701 : return true;
1702 49 : } else if (c.fromLane > i && (dir2 == LinkDirection::RIGHT || dir2 == LinkDirection::PARTRIGHT)) {
1703 : return true;
1704 : }
1705 : }
1706 : }
1707 : return false;
1708 : }
1709 :
1710 :
1711 : void
1712 12591 : NBNode::getReduction(const NBEdge* in, const NBEdge* out, int& inOffset, int& inEnd, int& outOffset, int& outEnd, int& reduction) const {
1713 12591 : inOffset = MAX2(0, in->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1714 12591 : outOffset = MAX2(0, out->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1715 12591 : inEnd = in->getFirstNonPedestrianLaneIndex(BACKWARD, true) + 1;
1716 12591 : outEnd = out->getFirstNonPedestrianLaneIndex(BACKWARD, true) + 1;
1717 12591 : reduction = (inEnd - inOffset) - (outEnd - outOffset);
1718 12591 : }
1719 :
1720 :
1721 : SVCPermissions
1722 1166 : NBNode::findToLaneForPermissions(NBEdge* currentOutgoing, int fromLane, NBEdge* incoming, SVCPermissions unsatisfied) {
1723 4484 : for (int toLane = 0; toLane < currentOutgoing->getNumLanes(); ++toLane) {
1724 3318 : const SVCPermissions satisfied = incoming->getPermissions(fromLane) & currentOutgoing->getPermissions(toLane) & unsatisfied;
1725 3318 : if (satisfied != 0 && !incoming->getLaneStruct(fromLane).connectionsDone) {
1726 1166 : if (incoming->hasConnectionTo(currentOutgoing, toLane)
1727 285 : && unsatisfied == SVC_TRAM
1728 1172 : && incoming->getPermissions(fromLane) == currentOutgoing->getPermissions(toLane)) {
1729 : // avoid double tram connection by shifting an existing connection
1730 11 : for (auto con : incoming->getConnections()) {
1731 11 : if (con.toEdge == currentOutgoing && con.toLane == toLane) {
1732 : #ifdef DEBUG_CONNECTION_GUESSING
1733 : if (DEBUGCOND) {
1734 : std::cout << " shifting connection from=" << con.fromLane << " to=" << currentOutgoing->getID() << "_" << toLane << ": newFromLane=" << fromLane << " satisfies=" << getVehicleClassNames(satisfied) << "\n";
1735 : }
1736 : #endif
1737 5 : incoming->getConnectionRef(con.fromLane, con.toEdge, toLane).fromLane = fromLane;
1738 : unsatisfied &= ~satisfied;
1739 : break;
1740 : }
1741 11 : }
1742 : } else {
1743 : // other modes (i.e. bus) can fix lane permissions NBPTLineCont::fixPermissions but do not wish to create parallel tram tracks here
1744 1161 : bool mayUseSameDestination = unsatisfied == SVC_TRAM || (unsatisfied & SVC_PASSENGER) != 0;
1745 1161 : incoming->setConnection((int)fromLane, currentOutgoing, toLane, NBEdge::Lane2LaneInfoType::COMPUTED, mayUseSameDestination);
1746 : #ifdef DEBUG_CONNECTION_GUESSING
1747 : if (DEBUGCOND) {
1748 : std::cout << " new connection from=" << fromLane << " to=" << currentOutgoing->getID() << "_" << toLane << " satisfies=" << getVehicleClassNames(satisfied) << "\n";
1749 : }
1750 : #endif
1751 1161 : unsatisfied &= ~satisfied;
1752 : }
1753 : }
1754 : }
1755 1166 : return unsatisfied;
1756 : }
1757 :
1758 :
1759 : int
1760 549 : NBNode::addedLanesRight(NBEdge* out, int addedLanes) const {
1761 549 : if (out->isOffRamp()) {
1762 : return addedLanes;
1763 : }
1764 : NBNode* to = out->getToNode();
1765 : // check whether a right lane ends
1766 : if (to->getIncomingEdges().size() == 1
1767 541 : && to->getOutgoingEdges().size() == 1) {
1768 : int inOffset, inEnd, outOffset, outEnd, reduction;
1769 44 : to->getReduction(out, to->getOutgoingEdges()[0], inOffset, inEnd, outOffset, outEnd, reduction);
1770 :
1771 44 : if (reduction > 0) {
1772 8 : return reduction;
1773 : }
1774 : }
1775 : // check for the presence of right and left turns at the next intersection
1776 : int outLanesRight = 0;
1777 : int outLanesLeft = 0;
1778 : int outLanesStraight = 0;
1779 2374 : for (NBEdge* succ : to->getOutgoingEdges()) {
1780 1841 : if (out->isConnectedTo(succ)) {
1781 1705 : const int outOffset = MAX2(0, succ->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1782 1705 : const int usableLanes = succ->getNumLanes() - outOffset;
1783 1705 : LinkDirection dir = to->getDirection(out, succ);
1784 1705 : if (dir == LinkDirection::STRAIGHT) {
1785 494 : outLanesStraight += usableLanes;
1786 1211 : } else if (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT) {
1787 426 : outLanesRight += usableLanes;
1788 : } else {
1789 785 : outLanesLeft += usableLanes;
1790 : }
1791 : }
1792 : }
1793 533 : const int outOffset = MAX2(0, out->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1794 533 : const int outEnd = out->getFirstNonPedestrianLaneIndex(BACKWARD, true) + 1;
1795 533 : const int usableLanes = outEnd - outOffset;
1796 533 : int addedTurnLanes = MIN3(
1797 : addedLanes,
1798 : MAX2(0, usableLanes - outLanesStraight),
1799 : outLanesRight + outLanesLeft);
1800 : #ifdef DEBUG_CONNECTION_GUESSING
1801 : if (DEBUGCOND) {
1802 : std::cout << "out=" << out->getID() << " usableLanes=" << usableLanes << " addedTurnLanes=" << addedTurnLanes << " addedLanes=" << addedLanes << " outLanesStraight=" << outLanesStraight << " outLanesLeft=" << outLanesLeft << " outLanesRight=" << outLanesRight << "\n";
1803 : }
1804 : #endif
1805 533 : if (outLanesLeft == 0) {
1806 : return addedTurnLanes;
1807 : } else {
1808 416 : return MIN2(addedTurnLanes / 2, outLanesRight);
1809 : }
1810 : }
1811 :
1812 :
1813 : bool
1814 43 : NBNode::isLongEnough(NBEdge* out, double minLength) {
1815 : double seen = out->getLoadedLength();
1816 44 : while (seen < minLength) {
1817 : // advance along trivial continuations
1818 : if (out->getToNode()->getOutgoingEdges().size() != 1
1819 20 : || out->getToNode()->getIncomingEdges().size() != 1) {
1820 : return false;
1821 : } else {
1822 1 : out = out->getToNode()->getOutgoingEdges()[0];
1823 1 : seen += out->getLoadedLength();
1824 : }
1825 : }
1826 : return true;
1827 : }
1828 :
1829 :
1830 : void
1831 89868 : NBNode::getEdgesThatApproach(NBEdge* currentOutgoing, EdgeVector& approaching) {
1832 : // get the position of the node to get the approaching nodes of
1833 89868 : EdgeVector::const_iterator i = std::find(myAllEdges.begin(),
1834 : myAllEdges.end(), currentOutgoing);
1835 : // get the first possible approaching edge
1836 89868 : NBContHelper::nextCW(myAllEdges, i);
1837 : // go through the list of edges clockwise and add the edges
1838 : approaching.clear();
1839 483769 : for (; *i != currentOutgoing;) {
1840 : // check only incoming edges
1841 393901 : if ((*i)->getToNode() == this && (*i)->getTurnDestination() != currentOutgoing) {
1842 190487 : std::vector<int> connLanes = (*i)->getConnectionLanes(currentOutgoing);
1843 190487 : if (connLanes.size() != 0) {
1844 139446 : approaching.push_back(*i);
1845 : }
1846 190487 : }
1847 393901 : NBContHelper::nextCW(myAllEdges, i);
1848 : }
1849 89868 : }
1850 :
1851 :
1852 : void
1853 801 : NBNode::replaceOutgoing(NBEdge* which, NBEdge* by, int laneOff) {
1854 : // replace the edge in the list of outgoing nodes
1855 801 : EdgeVector::iterator i = std::find(myOutgoingEdges.begin(), myOutgoingEdges.end(), which);
1856 801 : if (i != myOutgoingEdges.end()) {
1857 801 : (*i) = by;
1858 801 : i = std::find(myAllEdges.begin(), myAllEdges.end(), which);
1859 801 : (*i) = by;
1860 : }
1861 : // replace the edge in connections of incoming edges
1862 1988 : for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); ++i) {
1863 1187 : (*i)->replaceInConnections(which, by, laneOff);
1864 : }
1865 : // replace within the connetion prohibition dependencies
1866 801 : replaceInConnectionProhibitions(which, by, 0, laneOff);
1867 801 : }
1868 :
1869 :
1870 : void
1871 11 : NBNode::replaceOutgoing(const EdgeVector& which, NBEdge* by) {
1872 : // replace edges
1873 : int laneOff = 0;
1874 33 : for (EdgeVector::const_iterator i = which.begin(); i != which.end(); i++) {
1875 22 : replaceOutgoing(*i, by, laneOff);
1876 22 : laneOff += (*i)->getNumLanes();
1877 : }
1878 : // removed double occurrences
1879 11 : removeDoubleEdges();
1880 : // check whether this node belongs to a district and the edges
1881 : // must here be also remapped
1882 11 : if (myDistrict != nullptr) {
1883 0 : myDistrict->replaceOutgoing(which, by);
1884 : }
1885 11 : }
1886 :
1887 :
1888 : void
1889 6309 : NBNode::replaceIncoming(NBEdge* which, NBEdge* by, int laneOff) {
1890 : // replace the edge in the list of incoming nodes
1891 6309 : EdgeVector::iterator i = std::find(myIncomingEdges.begin(), myIncomingEdges.end(), which);
1892 6309 : if (i != myIncomingEdges.end()) {
1893 6309 : (*i) = by;
1894 6309 : i = std::find(myAllEdges.begin(), myAllEdges.end(), which);
1895 6309 : (*i) = by;
1896 : }
1897 : // replace within the connetion prohibition dependencies
1898 6309 : replaceInConnectionProhibitions(which, by, laneOff, 0);
1899 6309 : }
1900 :
1901 :
1902 : void
1903 11 : NBNode::replaceIncoming(const EdgeVector& which, NBEdge* by) {
1904 : // replace edges
1905 : int laneOff = 0;
1906 33 : for (EdgeVector::const_iterator i = which.begin(); i != which.end(); i++) {
1907 22 : replaceIncoming(*i, by, laneOff);
1908 22 : laneOff += (*i)->getNumLanes();
1909 : }
1910 : // removed double occurrences
1911 11 : removeDoubleEdges();
1912 : // check whether this node belongs to a district and the edges
1913 : // must here be also remapped
1914 11 : if (myDistrict != nullptr) {
1915 0 : myDistrict->replaceIncoming(which, by);
1916 : }
1917 11 : }
1918 :
1919 :
1920 :
1921 : void
1922 7110 : NBNode::replaceInConnectionProhibitions(NBEdge* which, NBEdge* by,
1923 : int whichLaneOff, int byLaneOff) {
1924 : // replace in keys
1925 : NBConnectionProhibits::iterator j = myBlockedConnections.begin();
1926 7310 : while (j != myBlockedConnections.end()) {
1927 : bool changed = false;
1928 200 : NBConnection c = (*j).first;
1929 200 : if (c.replaceFrom(which, whichLaneOff, by, byLaneOff)) {
1930 : changed = true;
1931 : }
1932 200 : if (c.replaceTo(which, whichLaneOff, by, byLaneOff)) {
1933 : changed = true;
1934 : }
1935 200 : if (changed) {
1936 17 : myBlockedConnections[c] = (*j).second;
1937 : myBlockedConnections.erase(j);
1938 : j = myBlockedConnections.begin();
1939 : } else {
1940 : j++;
1941 : }
1942 200 : }
1943 : // replace in values
1944 7208 : for (j = myBlockedConnections.begin(); j != myBlockedConnections.end(); j++) {
1945 : NBConnectionVector& prohibiting = (*j).second;
1946 322 : for (NBConnectionVector::iterator k = prohibiting.begin(); k != prohibiting.end(); k++) {
1947 : NBConnection& sprohibiting = *k;
1948 224 : sprohibiting.replaceFrom(which, whichLaneOff, by, byLaneOff);
1949 224 : sprohibiting.replaceTo(which, whichLaneOff, by, byLaneOff);
1950 : }
1951 : }
1952 7110 : }
1953 :
1954 :
1955 :
1956 : void
1957 1580 : NBNode::removeDoubleEdges() {
1958 : // check incoming
1959 3385 : for (int i = 0; myIncomingEdges.size() > 0 && i < (int)myIncomingEdges.size() - 1; i++) {
1960 1805 : int j = i + 1;
1961 5085 : while (j < (int)myIncomingEdges.size()) {
1962 3280 : if (myIncomingEdges[i] == myIncomingEdges[j]) {
1963 801 : myIncomingEdges.erase(myIncomingEdges.begin() + j);
1964 : } else {
1965 2479 : j++;
1966 : }
1967 : }
1968 : }
1969 : // check outgoing
1970 3407 : for (int i = 0; myOutgoingEdges.size() > 0 && i < (int)myOutgoingEdges.size() - 1; i++) {
1971 1827 : int j = i + 1;
1972 4851 : while (j < (int)myOutgoingEdges.size()) {
1973 3024 : if (myOutgoingEdges[i] == myOutgoingEdges[j]) {
1974 801 : myOutgoingEdges.erase(myOutgoingEdges.begin() + j);
1975 : } else {
1976 2223 : j++;
1977 : }
1978 : }
1979 : }
1980 : // check all
1981 6141 : for (int i = 0; myAllEdges.size() > 0 && i < (int)myAllEdges.size() - 1; i++) {
1982 4561 : int j = i + 1;
1983 18504 : while (j < (int)myAllEdges.size()) {
1984 13943 : if (myAllEdges[i] == myAllEdges[j]) {
1985 1602 : myAllEdges.erase(myAllEdges.begin() + j);
1986 : } else {
1987 12341 : j++;
1988 : }
1989 : }
1990 : }
1991 1580 : }
1992 :
1993 :
1994 : bool
1995 100640 : NBNode::hasIncoming(const NBEdge* const e) const {
1996 100640 : return std::find(myIncomingEdges.begin(), myIncomingEdges.end(), e) != myIncomingEdges.end();
1997 : }
1998 :
1999 :
2000 : bool
2001 14782 : NBNode::hasOutgoing(const NBEdge* const e) const {
2002 14782 : return std::find(myOutgoingEdges.begin(), myOutgoingEdges.end(), e) != myOutgoingEdges.end();
2003 : }
2004 :
2005 :
2006 : NBEdge*
2007 19719 : NBNode::getOppositeIncoming(NBEdge* e) const {
2008 19719 : EdgeVector edges = myIncomingEdges;
2009 19719 : if (find(edges.begin(), edges.end(), e) != edges.end()) {
2010 19719 : edges.erase(find(edges.begin(), edges.end(), e));
2011 : }
2012 19719 : if (edges.size() == 0) {
2013 : return nullptr;
2014 : }
2015 19719 : if (e->getToNode() == this) {
2016 19719 : sort(edges.begin(), edges.end(), NBContHelper::edge_opposite_direction_sorter(e, this, false));
2017 : } else {
2018 0 : sort(edges.begin(), edges.end(), NBContHelper::edge_similar_direction_sorter(e));
2019 : }
2020 19719 : return edges[0];
2021 19719 : }
2022 :
2023 :
2024 : void
2025 199 : NBNode::addSortedLinkFoes(const NBConnection& mayDrive,
2026 : const NBConnection& mustStop) {
2027 398 : if (mayDrive.getFrom() == nullptr ||
2028 398 : mayDrive.getTo() == nullptr ||
2029 597 : mustStop.getFrom() == nullptr ||
2030 199 : mustStop.getTo() == nullptr) {
2031 :
2032 0 : WRITE_WARNING(TL("Something went wrong during the building of a connection..."));
2033 0 : return; // !!! mark to recompute connections
2034 : }
2035 199 : NBConnectionVector conn = myBlockedConnections[mustStop];
2036 199 : conn.push_back(mayDrive);
2037 199 : myBlockedConnections[mustStop] = conn;
2038 199 : }
2039 :
2040 :
2041 : NBEdge*
2042 266 : NBNode::getPossiblySplittedIncoming(const std::string& edgeid) {
2043 266 : int size = (int) edgeid.length();
2044 1042 : for (EdgeVector::iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2045 1042 : std::string id = (*i)->getID();
2046 1042 : if (id.substr(0, size) == edgeid) {
2047 266 : return *i;
2048 : }
2049 : }
2050 : return nullptr;
2051 : }
2052 :
2053 :
2054 : NBEdge*
2055 266 : NBNode::getPossiblySplittedOutgoing(const std::string& edgeid) {
2056 266 : int size = (int) edgeid.length();
2057 605 : for (EdgeVector::iterator i = myOutgoingEdges.begin(); i != myOutgoingEdges.end(); i++) {
2058 549 : std::string id = (*i)->getID();
2059 549 : if (id.substr(0, size) == edgeid) {
2060 210 : return *i;
2061 : }
2062 : }
2063 : return nullptr;
2064 : }
2065 :
2066 :
2067 : void
2068 41532 : NBNode::removeEdge(NBEdge* edge, bool removeFromConnections) {
2069 41532 : EdgeVector::iterator i = std::find(myAllEdges.begin(), myAllEdges.end(), edge);
2070 41532 : if (i != myAllEdges.end()) {
2071 34392 : myAllEdges.erase(i);
2072 34392 : i = std::find(myOutgoingEdges.begin(), myOutgoingEdges.end(), edge);
2073 34392 : if (i != myOutgoingEdges.end()) {
2074 20035 : myOutgoingEdges.erase(i);
2075 : // potential self-loop
2076 20035 : i = std::find(myIncomingEdges.begin(), myIncomingEdges.end(), edge);
2077 20035 : if (i != myIncomingEdges.end()) {
2078 0 : myIncomingEdges.erase(i);
2079 : }
2080 : } else {
2081 14357 : i = std::find(myIncomingEdges.begin(), myIncomingEdges.end(), edge);
2082 14357 : if (i != myIncomingEdges.end()) {
2083 14357 : myIncomingEdges.erase(i);
2084 : } else {
2085 : // edge must have been either incoming or outgoing
2086 : assert(false);
2087 : }
2088 : }
2089 34392 : if (removeFromConnections) {
2090 62734 : for (i = myAllEdges.begin(); i != myAllEdges.end(); ++i) {
2091 35048 : (*i)->removeFromConnections(edge);
2092 : }
2093 : }
2094 : // invalidate controlled connections for loaded traffic light plans
2095 34392 : const bool incoming = edge->getToNode() == this;
2096 38076 : for (NBTrafficLightDefinition* const tld : myTrafficLights) {
2097 3684 : tld->replaceRemoved(edge, -1, nullptr, -1, incoming);
2098 : }
2099 : }
2100 41532 : }
2101 :
2102 :
2103 : Position
2104 0 : NBNode::getEmptyDir() const {
2105 : Position pos(0, 0);
2106 0 : for (const NBEdge* const in : myIncomingEdges) {
2107 0 : Position toAdd = in->getFromNode()->getPosition();
2108 : toAdd.sub(myPosition);
2109 0 : toAdd.norm2D();
2110 : pos.add(toAdd);
2111 : }
2112 0 : for (const NBEdge* const out : myOutgoingEdges) {
2113 0 : Position toAdd = out->getToNode()->getPosition();
2114 : toAdd.sub(myPosition);
2115 0 : toAdd.norm2D();
2116 : pos.add(toAdd);
2117 : }
2118 0 : pos.mul(-1. / (double)(myIncomingEdges.size() + myOutgoingEdges.size()));
2119 0 : if (pos.x() == 0. && pos.y() == 0.) {
2120 0 : pos = Position(1, 0);
2121 : }
2122 0 : pos.norm2D();
2123 0 : return pos;
2124 : }
2125 :
2126 :
2127 :
2128 : void
2129 0 : NBNode::invalidateIncomingConnections(bool reallowSetting) {
2130 0 : for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2131 0 : (*i)->invalidateConnections(reallowSetting);
2132 : }
2133 0 : }
2134 :
2135 :
2136 : void
2137 27 : NBNode::invalidateOutgoingConnections(bool reallowSetting) {
2138 93 : for (EdgeVector::const_iterator i = myOutgoingEdges.begin(); i != myOutgoingEdges.end(); i++) {
2139 66 : (*i)->invalidateConnections(reallowSetting);
2140 : }
2141 27 : }
2142 :
2143 :
2144 : bool
2145 229834 : NBNode::mustBrake(const NBEdge* const from, const NBEdge* const to, int fromLane, int toLane, bool includePedCrossings) const {
2146 : // unregulated->does not need to brake
2147 229834 : if (myRequest == nullptr) {
2148 : return false;
2149 : }
2150 : // vehicles which do not have a following lane must always decelerate to the end
2151 228946 : if (to == nullptr) {
2152 : return true;
2153 : }
2154 : // maybe we need to brake due to entering a bidi-edge
2155 228946 : if (to->isBidiEdge() && !from->isBidiEdge()) {
2156 : return true;
2157 : }
2158 : // check whether any other connection on this node prohibits this connection
2159 228767 : return myRequest->mustBrake(from, to, fromLane, toLane, includePedCrossings);
2160 : }
2161 :
2162 : bool
2163 6526 : NBNode::mustBrakeForCrossing(const NBEdge* const from, const NBEdge* const to, const NBNode::Crossing& crossing) const {
2164 6526 : return NBRequest::mustBrakeForCrossing(this, from, to, crossing);
2165 : }
2166 :
2167 : bool
2168 17274 : NBNode::brakeForCrossingOnExit(const NBEdge* to, LinkDirection dir, bool indirect) const {
2169 : // code is called for connections exiting after an internal junction.
2170 : // If the connection is turning we do not check for crossing priority anymore.
2171 17274 : if (dir == LinkDirection::STRAIGHT && !indirect) {
2172 : return false;
2173 : }
2174 19971 : for (auto& c : myCrossings) {
2175 4114 : if (std::find(c->edges.begin(), c->edges.end(), to) != c->edges.end()) {
2176 : return true;
2177 : }
2178 : }
2179 : return false;
2180 : }
2181 :
2182 :
2183 : bool
2184 4285553 : NBNode::rightTurnConflict(const NBEdge* from, const NBEdge* to, int fromLane,
2185 : const NBEdge* prohibitorFrom, const NBEdge* prohibitorTo, int prohibitorFromLane) {
2186 4285553 : if (from != prohibitorFrom) {
2187 : return false;
2188 : }
2189 1266959 : if (from->isTurningDirectionAt(to)
2190 1266959 : || prohibitorFrom->isTurningDirectionAt(prohibitorTo)) {
2191 : // XXX should warn if there are any non-turning connections left of this
2192 376628 : return false;
2193 : }
2194 : // conflict if to is between prohibitorTo and from when going clockwise
2195 890331 : if (to->getStartAngle() == prohibitorTo->getStartAngle()) {
2196 : // reduce rounding errors
2197 : return false;
2198 : }
2199 480803 : const LinkDirection d1 = from->getToNode()->getDirection(from, to);
2200 : // must be a right turn to qualify as rightTurnConflict
2201 480803 : if (d1 == LinkDirection::STRAIGHT) {
2202 : // no conflict for straight going connections
2203 : // XXX actually this should check the main direction (which could also
2204 : // be a turn)
2205 : return false;
2206 : } else {
2207 352966 : const LinkDirection d2 = prohibitorFrom->getToNode()->getDirection(prohibitorFrom, prohibitorTo);
2208 : /* std::cout
2209 : << "from=" << from->getID() << " to=" << to->getID() << " fromLane=" << fromLane
2210 : << " pFrom=" << prohibitorFrom->getID() << " pTo=" << prohibitorTo->getID() << " pFromLane=" << prohibitorFromLane
2211 : << " d1=" << toString(d1) << " d2=" << toString(d2)
2212 : << "\n"; */
2213 : bool flip = false;
2214 352966 : if (d1 == LinkDirection::LEFT || d1 == LinkDirection::PARTLEFT) {
2215 : // check for leftTurnConflicht
2216 : flip = !flip;
2217 167855 : if (d2 == LinkDirection::RIGHT || d2 == LinkDirection::PARTRIGHT) {
2218 : // assume that the left-turning bicycle goes straight at first
2219 : // and thus gets precedence over a right turning vehicle
2220 : return false;
2221 : }
2222 : }
2223 286056 : if ((!flip && fromLane <= prohibitorFromLane) ||
2224 103855 : (flip && fromLane >= prohibitorFromLane)) {
2225 : return false;
2226 : }
2227 4801 : const double toAngleAtNode = fmod(to->getStartAngle() + 180, (double)360.0);
2228 4801 : const double prohibitorToAngleAtNode = fmod(prohibitorTo->getStartAngle() + 180, (double)360.0);
2229 4801 : return (flip != (GeomHelper::getCWAngleDiff(from->getEndAngle(), toAngleAtNode) <
2230 4801 : GeomHelper::getCWAngleDiff(from->getEndAngle(), prohibitorToAngleAtNode)));
2231 : }
2232 : }
2233 :
2234 : bool
2235 306 : NBNode::mergeConflictYields(const NBEdge* from, int fromLane, int fromLaneFoe, NBEdge* to, int toLane) const {
2236 306 : if (myRequest == nullptr) {
2237 : return false;
2238 : }
2239 306 : const NBEdge::Connection& con = from->getConnection(fromLane, to, toLane);
2240 306 : const NBEdge::Connection& prohibitorCon = from->getConnection(fromLaneFoe, to, toLane);
2241 306 : return myRequest->mergeConflict(from, con, from, prohibitorCon, false);
2242 : }
2243 :
2244 :
2245 : bool
2246 1524856 : NBNode::mergeConflict(const NBEdge* from, const NBEdge::Connection& con,
2247 : const NBEdge* prohibitorFrom, const NBEdge::Connection& prohibitorCon, bool foes) const {
2248 1524856 : if (myRequest == nullptr) {
2249 : return false;
2250 : }
2251 1475908 : return myRequest->mergeConflict(from, con, prohibitorFrom, prohibitorCon, foes);
2252 : }
2253 :
2254 : bool
2255 762428 : NBNode::bidiConflict(const NBEdge* from, const NBEdge::Connection& con,
2256 : const NBEdge* prohibitorFrom, const NBEdge::Connection& prohibitorCon, bool foes) const {
2257 762428 : if (myRequest == nullptr) {
2258 : return false;
2259 : }
2260 737954 : return myRequest->bidiConflict(from, con, prohibitorFrom, prohibitorCon, foes);
2261 : }
2262 :
2263 : bool
2264 1473743 : NBNode::turnFoes(const NBEdge* from, const NBEdge* to, int fromLane,
2265 : const NBEdge* from2, const NBEdge* to2, int fromLane2,
2266 : bool lefthand) const {
2267 : UNUSED_PARAMETER(lefthand);
2268 1473743 : if (from != from2 || to == to2 || fromLane == fromLane2) {
2269 : return false;
2270 : }
2271 73523 : if (from->isTurningDirectionAt(to)
2272 73523 : || from2->isTurningDirectionAt(to2)) {
2273 : // XXX should warn if there are any non-turning connections left of this
2274 22594 : return false;
2275 : }
2276 : bool result = false;
2277 50929 : EdgeVector::const_iterator it = std::find(myAllEdges.begin(), myAllEdges.end(), from);
2278 50929 : if (fromLane < fromLane2) {
2279 : // conflict if 'to' comes before 'to2' going clockwise starting at 'from'
2280 106286 : while (*it != to2) {
2281 80761 : if (*it == to) {
2282 : result = true;
2283 : }
2284 80761 : NBContHelper::nextCW(myAllEdges, it);
2285 : }
2286 : } else {
2287 : // conflict if 'to' comes before 'to2' going counter-clockwise starting at 'from'
2288 69332 : while (*it != to2) {
2289 43928 : if (*it == to) {
2290 : result = true;
2291 : }
2292 43928 : NBContHelper::nextCCW(myAllEdges, it);
2293 : }
2294 : }
2295 : /*
2296 : if (result) {
2297 : std::cout << "turnFoes node=" << getID()
2298 : << " from=" << from->getLaneID(fromLane)
2299 : << " to=" << to->getID()
2300 : << " from2=" << from2->getLaneID(fromLane2)
2301 : << " to2=" << to2->getID()
2302 : << "\n";
2303 : }
2304 : */
2305 : return result;
2306 : }
2307 :
2308 :
2309 : bool
2310 4812 : NBNode::isLeftMover(const NBEdge* const from, const NBEdge* const to) const {
2311 : // when the junction has only one incoming edge, there are no
2312 : // problems caused by left blockings
2313 4812 : if (myIncomingEdges.size() == 1 || myOutgoingEdges.size() == 1) {
2314 : return false;
2315 : }
2316 4764 : double fromAngle = from->getAngleAtNode(this);
2317 4764 : double toAngle = to->getAngleAtNode(this);
2318 4764 : double cw = GeomHelper::getCWAngleDiff(fromAngle, toAngle);
2319 4764 : double ccw = GeomHelper::getCCWAngleDiff(fromAngle, toAngle);
2320 4764 : std::vector<NBEdge*>::const_iterator i = std::find(myAllEdges.begin(), myAllEdges.end(), from);
2321 : do {
2322 14292 : NBContHelper::nextCW(myAllEdges, i);
2323 19056 : } while ((!hasOutgoing(*i) || from->isTurningDirectionAt(*i)) && *i != from);
2324 4764 : return cw < ccw && (*i) == to && myOutgoingEdges.size() > 2;
2325 : }
2326 :
2327 :
2328 : bool
2329 1486296 : NBNode::forbids(const NBEdge* const possProhibitorFrom, const NBEdge* const possProhibitorTo,
2330 : const NBEdge* const possProhibitedFrom, const NBEdge* const possProhibitedTo,
2331 : bool regardNonSignalisedLowerPriority) const {
2332 1486296 : return myRequest != nullptr && myRequest->forbids(possProhibitorFrom, possProhibitorTo,
2333 : possProhibitedFrom, possProhibitedTo,
2334 1486296 : regardNonSignalisedLowerPriority);
2335 : }
2336 :
2337 :
2338 : bool
2339 1483952 : NBNode::foes(const NBEdge* const from1, const NBEdge* const to1,
2340 : const NBEdge* const from2, const NBEdge* const to2) const {
2341 1483952 : return myRequest != nullptr && myRequest->foes(from1, to1, from2, to2);
2342 : }
2343 :
2344 :
2345 : void
2346 0 : NBNode::remapRemoved(NBTrafficLightLogicCont& tc,
2347 : NBEdge* removed, const EdgeVector& incoming,
2348 : const EdgeVector& outgoing) {
2349 : assert(find(incoming.begin(), incoming.end(), removed) == incoming.end());
2350 : bool changed = true;
2351 0 : while (changed) {
2352 : changed = false;
2353 : NBConnectionProhibits blockedConnectionsTmp = myBlockedConnections;
2354 : NBConnectionProhibits blockedConnectionsNew;
2355 : // remap in connections
2356 0 : for (NBConnectionProhibits::iterator i = blockedConnectionsTmp.begin(); i != blockedConnectionsTmp.end(); i++) {
2357 0 : const NBConnection& blocker = (*i).first;
2358 0 : const NBConnectionVector& blocked = (*i).second;
2359 : // check the blocked connections first
2360 : // check whether any of the blocked must be changed
2361 : bool blockedChanged = false;
2362 : NBConnectionVector newBlocked;
2363 : NBConnectionVector::const_iterator j;
2364 0 : for (j = blocked.begin(); j != blocked.end(); j++) {
2365 : const NBConnection& sblocked = *j;
2366 0 : if (sblocked.getFrom() == removed || sblocked.getTo() == removed) {
2367 : blockedChanged = true;
2368 : }
2369 : }
2370 : // adapt changes if so
2371 0 : for (j = blocked.begin(); blockedChanged && j != blocked.end(); j++) {
2372 : const NBConnection& sblocked = *j;
2373 0 : if (sblocked.getFrom() == removed && sblocked.getTo() == removed) {
2374 : /* for(EdgeVector::const_iterator k=incoming.begin(); k!=incoming.end(); k++) {
2375 : !!! newBlocked.push_back(NBConnection(*k, *k));
2376 : }*/
2377 0 : } else if (sblocked.getFrom() == removed) {
2378 : assert(sblocked.getTo() != removed);
2379 0 : for (EdgeVector::const_iterator k = incoming.begin(); k != incoming.end(); k++) {
2380 0 : newBlocked.push_back(NBConnection(*k, sblocked.getTo()));
2381 : }
2382 0 : } else if (sblocked.getTo() == removed) {
2383 : assert(sblocked.getFrom() != removed);
2384 0 : for (EdgeVector::const_iterator k = outgoing.begin(); k != outgoing.end(); k++) {
2385 0 : newBlocked.push_back(NBConnection(sblocked.getFrom(), *k));
2386 : }
2387 : } else {
2388 0 : newBlocked.push_back(NBConnection(sblocked.getFrom(), sblocked.getTo()));
2389 : }
2390 : }
2391 0 : if (blockedChanged) {
2392 0 : blockedConnectionsNew[blocker] = newBlocked;
2393 : changed = true;
2394 : }
2395 : // if the blocked were kept
2396 : else {
2397 0 : if (blocker.getFrom() == removed && blocker.getTo() == removed) {
2398 : changed = true;
2399 : /* for(EdgeVector::const_iterator k=incoming.begin(); k!=incoming.end(); k++) {
2400 : !!! blockedConnectionsNew[NBConnection(*k, *k)] = blocked;
2401 : }*/
2402 0 : } else if (blocker.getFrom() == removed) {
2403 : assert(blocker.getTo() != removed);
2404 : changed = true;
2405 0 : for (EdgeVector::const_iterator k = incoming.begin(); k != incoming.end(); k++) {
2406 0 : blockedConnectionsNew[NBConnection(*k, blocker.getTo())] = blocked;
2407 : }
2408 0 : } else if (blocker.getTo() == removed) {
2409 : assert(blocker.getFrom() != removed);
2410 : changed = true;
2411 0 : for (EdgeVector::const_iterator k = outgoing.begin(); k != outgoing.end(); k++) {
2412 0 : blockedConnectionsNew[NBConnection(blocker.getFrom(), *k)] = blocked;
2413 : }
2414 : } else {
2415 0 : blockedConnectionsNew[blocker] = blocked;
2416 : }
2417 : }
2418 0 : }
2419 : myBlockedConnections = blockedConnectionsNew;
2420 : }
2421 : // remap in traffic lights
2422 0 : tc.remapRemoved(removed, incoming, outgoing);
2423 0 : }
2424 :
2425 :
2426 : NBEdge*
2427 3643158 : NBNode::getNextCompatibleOutgoing(const NBEdge* incoming, SVCPermissions vehPerm, EdgeVector::const_iterator itOut, bool clockwise) const {
2428 3643158 : EdgeVector::const_iterator i = itOut;
2429 7545160 : while (*i != incoming) {
2430 5996469 : if (clockwise) {
2431 2582976 : NBContHelper::nextCW(myAllEdges, i);
2432 : } else {
2433 3413493 : NBContHelper::nextCCW(myAllEdges, i);
2434 : }
2435 5996469 : if ((*i)->getFromNode() != this) {
2436 : // only look for outgoing edges
2437 : // @note we use myAllEdges to stop at the incoming edge
2438 3761500 : continue;
2439 : }
2440 2234969 : if (incoming->isTurningDirectionAt(*i)) {
2441 : return nullptr;
2442 : }
2443 1152099 : if ((vehPerm & (*i)->getPermissions()) != 0 || vehPerm == 0) {
2444 1011597 : return *i;
2445 : }
2446 : }
2447 : return nullptr;
2448 : }
2449 :
2450 :
2451 : bool
2452 1093609 : NBNode::isStraighter(const NBEdge* const incoming, const double angle, const SVCPermissions vehPerm, const int modeLanes, const NBEdge* const candidate) const {
2453 1093609 : if (candidate != nullptr) {
2454 737658 : const double candAngle = NBHelpers::normRelAngle(incoming->getAngleAtNode(this), candidate->getAngleAtNode(this));
2455 : // they are too similar it does not matter
2456 737658 : if (fabs(angle - candAngle) < 5.) {
2457 : return false;
2458 : }
2459 : // the other edge is at least 5 degree straighter
2460 715221 : if (fabs(candAngle) < fabs(angle) - 5.) {
2461 : return true;
2462 : }
2463 628895 : if (fabs(angle) < fabs(candAngle) - 5.) {
2464 : return false;
2465 : }
2466 28179 : if (fabs(candAngle) < 44.) {
2467 : // the lane count for the same modes is larger
2468 26812 : const int candModeLanes = candidate->getNumLanesThatAllow(vehPerm);
2469 26812 : if (candModeLanes > modeLanes) {
2470 : return true;
2471 : }
2472 25156 : if (candModeLanes < modeLanes) {
2473 : return false;
2474 : }
2475 : // we would create a left turn
2476 21709 : if (candAngle < 0 && angle > 0) {
2477 : return true;
2478 : }
2479 : if (angle < 0 && candAngle > 0) {
2480 : return false;
2481 : }
2482 : }
2483 : }
2484 : return false;
2485 : }
2486 :
2487 : EdgeVector
2488 8588 : NBNode::getPassengerEdges(bool incoming) const {
2489 : EdgeVector result;
2490 26490 : for (NBEdge* e : (incoming ? myIncomingEdges : myOutgoingEdges)) {
2491 17902 : if ((e->getPermissions() & SVC_PASSENGER) != 0) {
2492 12573 : result.push_back(e);
2493 : }
2494 : }
2495 8588 : return result;
2496 0 : }
2497 :
2498 : LinkDirection
2499 7794494 : NBNode::getDirection(const NBEdge* const incoming, const NBEdge* const outgoing, bool leftHand) const {
2500 : // ok, no connection at all -> dead end
2501 7794494 : if (outgoing == nullptr) {
2502 : return LinkDirection::NODIR;
2503 : }
2504 : assert(incoming->getToNode() == this);
2505 : assert(outgoing->getFromNode() == this);
2506 7794493 : if (incoming->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT && outgoing->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT) {
2507 : return LinkDirection::STRAIGHT;
2508 : }
2509 : // turning direction
2510 7780733 : if (incoming->isTurningDirectionAt(outgoing)) {
2511 1334488 : if (isExplicitRailNoBidi(incoming, outgoing)) {
2512 : return LinkDirection::STRAIGHT;
2513 : }
2514 2667261 : return leftHand ? LinkDirection::TURN_LEFTHAND : LinkDirection::TURN;
2515 : }
2516 : // get the angle between incoming/outgoing at the junction
2517 6446245 : const double angle = NBHelpers::normRelAngle(incoming->getAngleAtNode(this), outgoing->getAngleAtNode(this));
2518 : // ok, should be a straight connection
2519 6446245 : EdgeVector::const_iterator itOut = std::find(myAllEdges.begin(), myAllEdges.end(), outgoing);
2520 6446245 : SVCPermissions vehPerm = incoming->getPermissions() & outgoing->getPermissions();
2521 6446245 : if (vehPerm != SVC_PEDESTRIAN) {
2522 6351990 : vehPerm &= ~SVC_PEDESTRIAN;
2523 : }
2524 6446245 : const int modeLanes = outgoing->getNumLanesThatAllow(vehPerm);
2525 6446245 : if (fabs(angle) < 44.) {
2526 2416398 : if (fabs(angle) > 6.) {
2527 569825 : if (isStraighter(incoming, angle, vehPerm, modeLanes, getNextCompatibleOutgoing(incoming, vehPerm, itOut, true))) {
2528 90718 : return angle > 0 ? LinkDirection::PARTRIGHT : LinkDirection::PARTLEFT;
2529 : }
2530 523784 : if (isStraighter(incoming, angle, vehPerm, modeLanes, getNextCompatibleOutgoing(incoming, vehPerm, itOut, false))) {
2531 56677 : return angle > 0 ? LinkDirection::PARTRIGHT : LinkDirection::PARTLEFT;
2532 : }
2533 : }
2534 2315141 : if (angle > 0 && incoming->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT) {
2535 4130 : return angle > 15 ? LinkDirection::RIGHT : LinkDirection::PARTRIGHT;
2536 : }
2537 2312975 : return LinkDirection::STRAIGHT;
2538 : }
2539 :
2540 4029847 : if (angle > 0) {
2541 : // check whether any other edge goes further to the right
2542 2158103 : if (angle > 90 + NUMERICAL_EPS) {
2543 : return LinkDirection::RIGHT;
2544 : }
2545 1379949 : NBEdge* outCW = getNextCompatibleOutgoing(incoming, vehPerm, itOut, !leftHand);
2546 1379949 : if (outCW != nullptr) {
2547 : return LinkDirection::PARTRIGHT;
2548 : } else {
2549 : return LinkDirection::RIGHT;
2550 : }
2551 : } else {
2552 : // check whether any other edge goes further to the left
2553 1871744 : if (angle < -170 && incoming->getGeometry().reverse() == outgoing->getGeometry()) {
2554 1078 : if (isExplicitRailNoBidi(incoming, outgoing)) {
2555 : return LinkDirection::STRAIGHT;
2556 : }
2557 2140 : return leftHand ? LinkDirection::TURN_LEFTHAND : LinkDirection::TURN;
2558 1870666 : } else if (angle < -(90 + NUMERICAL_EPS)) {
2559 : return LinkDirection::LEFT;
2560 : }
2561 1169600 : NBEdge* outCCW = getNextCompatibleOutgoing(incoming, vehPerm, itOut, leftHand);
2562 1169600 : if (outCCW != nullptr) {
2563 : return LinkDirection::PARTLEFT;
2564 : } else {
2565 : return LinkDirection::LEFT;
2566 : }
2567 : }
2568 : }
2569 :
2570 :
2571 : bool
2572 1335566 : NBNode::isExplicitRailNoBidi(const NBEdge* incoming, const NBEdge* outgoing) {
2573 : // assume explicit connections at sharp turn-arounds are either for reversal or due to a geometry glitch
2574 : // (but should not have been guessed)
2575 : // @note this function is also called from NBAlgorithms when there aren't any connections ready
2576 : return (incoming->getStep() >= NBEdge::EdgeBuildingStep::LANES2LANES_RECHECK
2577 1265462 : && isRailway(incoming->getPermissions())
2578 9266 : && isRailway(outgoing->getPermissions())
2579 1344782 : && incoming->getBidiEdge() != outgoing);
2580 : }
2581 :
2582 :
2583 : LinkState
2584 198673 : NBNode::getLinkState(const NBEdge* incoming, const NBEdge* outgoing, int fromLane, int toLane,
2585 : bool mayDefinitelyPass, const std::string& tlID) const {
2586 198673 : if (myType == SumoXMLNodeType::RAIL_CROSSING && isRailway(incoming->getPermissions())) {
2587 : return LINKSTATE_MAJOR; // the trains must run on time
2588 : }
2589 198389 : if (tlID != "") {
2590 23775 : if (getRightOfWay() == RightOfWay::ALLWAYSTOP) {
2591 : return LINKSTATE_ALLWAY_STOP;
2592 : }
2593 23592 : return mustBrake(incoming, outgoing, fromLane, toLane, true) ? LINKSTATE_TL_OFF_BLINKING : LINKSTATE_TL_OFF_NOSIGNAL;
2594 : }
2595 174614 : if (outgoing == nullptr) { // always off
2596 : return LINKSTATE_TL_OFF_NOSIGNAL;
2597 : }
2598 174614 : if ((myType == SumoXMLNodeType::RIGHT_BEFORE_LEFT || myType == SumoXMLNodeType::LEFT_BEFORE_RIGHT)
2599 174614 : && mustBrake(incoming, outgoing, fromLane, toLane, true)) {
2600 : return LINKSTATE_EQUAL; // all the same
2601 : }
2602 160383 : if (myType == SumoXMLNodeType::ALLWAY_STOP) {
2603 : return LINKSTATE_ALLWAY_STOP; // all drive, first one to arrive may drive first
2604 : }
2605 160367 : if (myType == SumoXMLNodeType::ZIPPER && zipperConflict(incoming, outgoing, fromLane, toLane)) {
2606 : return LINKSTATE_ZIPPER;
2607 : }
2608 : if (!mayDefinitelyPass
2609 160224 : && mustBrake(incoming, outgoing, fromLane, toLane, true)
2610 : // legacy mode
2611 71417 : && (!incoming->isInsideTLS() || getDirection(incoming, outgoing) != LinkDirection::STRAIGHT)
2612 : // avoid linkstate minor at pure railway nodes
2613 231653 : && (!NBNodeTypeComputer::isRailwayNode(this) || unsignalizedOperation())) {
2614 70314 : return myType == SumoXMLNodeType::PRIORITY_STOP && incoming->getJunctionPriority(this) == NBEdge::JunctionPriority::MINOR_ROAD ? LINKSTATE_STOP : LINKSTATE_MINOR; // minor road
2615 : }
2616 : // traffic lights are not regarded here
2617 : return LINKSTATE_MAJOR;
2618 : }
2619 :
2620 :
2621 : bool
2622 152 : NBNode::zipperConflict(const NBEdge* incoming, const NBEdge* outgoing, int fromLane, int toLane) const {
2623 152 : if (mustBrake(incoming, outgoing, fromLane, toLane, false)) {
2624 : // there should be another connection with the same target (not just some intersecting trajectories)
2625 186 : for (const NBEdge* in : getIncomingEdges()) {
2626 261 : for (const NBEdge::Connection& c : in->getConnections()) {
2627 199 : if ((in != incoming || c.fromLane != fromLane) && c.toEdge == outgoing && c.toLane == toLane) {
2628 : return true;
2629 : }
2630 : }
2631 : }
2632 : }
2633 : return false;
2634 : }
2635 :
2636 :
2637 : bool
2638 1745 : NBNode::unsignalizedOperation() const {
2639 : SVCPermissions railClasses = 0;
2640 6110 : for (NBEdge* e : myIncomingEdges) {
2641 4365 : railClasses |= (e->getPermissions() & SVC_RAIL_CLASSES);
2642 : }
2643 : assert(railClasses != 0);
2644 1745 : return ((railClasses & myPermitUnsignalizedClasses) == railClasses
2645 1745 : && (railClasses & myHaveRailSignalClasses) == 0);
2646 : }
2647 :
2648 :
2649 : void
2650 1841 : NBNode::initRailSignalClasses(const NBNodeCont& nc) {
2651 1841 : myPermitUnsignalizedClasses = parseVehicleClasses(OptionsCont::getOptions().getStringVector("railway.signal.permit-unsignalized"));
2652 1841 : myHaveRailSignalClasses = 0;
2653 59473 : for (auto it : nc) {
2654 : const NBNode* n = it.second;
2655 57632 : if (n->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
2656 3902 : for (const NBEdge* in : n->getIncomingEdges()) {
2657 2386 : myHaveRailSignalClasses |= in->getPermissions();
2658 : }
2659 : }
2660 : }
2661 1841 : }
2662 :
2663 :
2664 : bool
2665 17386 : NBNode::checkIsRemovable() const {
2666 : std::string reason;
2667 34772 : return checkIsRemovableReporting(reason);
2668 : }
2669 :
2670 : bool
2671 17386 : NBNode::checkIsRemovableReporting(std::string& reason) const {
2672 17386 : if (getEdges().empty()) {
2673 : return true;
2674 : }
2675 : // check whether this node is included in a traffic light or crossing
2676 17386 : if (myTrafficLights.size() != 0) {
2677 : reason = "TLS";
2678 525 : return false;
2679 : }
2680 16861 : if (myType == SumoXMLNodeType::RAIL_SIGNAL) {
2681 : reason = "rail_signal";
2682 220 : return false;
2683 : }
2684 16641 : if (myCrossings.size() != 0) {
2685 : reason = "crossing";
2686 0 : return false;
2687 : }
2688 : EdgeVector::const_iterator i;
2689 : // one in, one out -> just a geometry ...
2690 16641 : if (myOutgoingEdges.size() == 1 && myIncomingEdges.size() == 1) {
2691 : // ... if types match ...
2692 4990 : if (!myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason)) {
2693 2350 : reason = "edges incompatible: " + reason;
2694 2350 : return false;
2695 : }
2696 2640 : if (myIncomingEdges[0]->getTurnDestination(true) == myOutgoingEdges[0]) {
2697 : reason = "turnaround";
2698 30 : return false;
2699 : }
2700 : return true;
2701 : }
2702 : // two in, two out -> may be something else
2703 11651 : if (myOutgoingEdges.size() == 2 && myIncomingEdges.size() == 2) {
2704 : // check whether the origin nodes of the incoming edges differ
2705 : std::set<NBNode*> origSet;
2706 8586 : for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2707 5724 : origSet.insert((*i)->getFromNode());
2708 : }
2709 2862 : if (origSet.size() < 2) {
2710 : // overlapping case
2711 217 : if (myIncomingEdges[0]->getGeometry() == myIncomingEdges[1]->getGeometry() &&
2712 75 : myOutgoingEdges[0]->getGeometry() == myOutgoingEdges[1]->getGeometry()) {
2713 137 : return ((myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason) &&
2714 67 : myIncomingEdges[1]->expandableBy(myOutgoingEdges[1], reason))
2715 71 : || (myIncomingEdges[0]->expandableBy(myOutgoingEdges[1], reason) &&
2716 1 : myIncomingEdges[1]->expandableBy(myOutgoingEdges[0], reason)));
2717 : }
2718 : }
2719 : // check whether this node is an intermediate node of
2720 : // a two-directional street
2721 5658 : for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2722 : // each of the edges must have an opposite direction edge
2723 4255 : NBEdge* opposite = (*i)->getTurnDestination(true);
2724 4255 : if (opposite != nullptr) {
2725 : // the other outgoing edges must be the continuation of the current
2726 3371 : NBEdge* continuation = opposite == myOutgoingEdges.front() ? myOutgoingEdges.back() : myOutgoingEdges.front();
2727 : // check whether the types allow joining
2728 3371 : if (!(*i)->expandableBy(continuation, reason)) {
2729 505 : reason = "edges incompatible: " + reason;
2730 505 : return false;
2731 : }
2732 : } else {
2733 : // ok, at least one outgoing edge is not an opposite
2734 : // of an incoming one
2735 : reason = "not opposites";
2736 : return false;
2737 : }
2738 : }
2739 : return true;
2740 : }
2741 : // ok, a real node
2742 : reason = "intersection";
2743 : return false;
2744 : }
2745 :
2746 :
2747 : std::vector<std::pair<NBEdge*, NBEdge*> >
2748 8950 : NBNode::getEdgesToJoin() const {
2749 : assert(checkIsRemovable());
2750 : std::vector<std::pair<NBEdge*, NBEdge*> > ret;
2751 : // one in, one out-case
2752 8950 : if (myOutgoingEdges.size() == 1 && myIncomingEdges.size() == 1) {
2753 2576 : ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[0]));
2754 2576 : return ret;
2755 : }
2756 6374 : if (myIncomingEdges.size() == 2 && myOutgoingEdges.size() == 2) {
2757 : // two in, two out-case
2758 1534 : if (myIncomingEdges[0]->getGeometry() == myIncomingEdges[1]->getGeometry() &&
2759 68 : myOutgoingEdges[0]->getGeometry() == myOutgoingEdges[1]->getGeometry()) {
2760 : // overlapping edges
2761 : std::string reason;
2762 68 : if (myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason)) {
2763 67 : ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[0]));
2764 67 : ret.push_back(std::make_pair(myIncomingEdges[1], myOutgoingEdges[1]));
2765 : } else {
2766 1 : ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[1]));
2767 1 : ret.push_back(std::make_pair(myIncomingEdges[1], myOutgoingEdges[0]));
2768 : }
2769 : return ret;
2770 : }
2771 : }
2772 9102 : for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2773 : // join with the edge that is not a turning direction
2774 2796 : NBEdge* opposite = (*i)->getTurnDestination(true);
2775 : assert(opposite != 0);
2776 2796 : NBEdge* continuation = opposite == myOutgoingEdges.front() ? myOutgoingEdges.back() : myOutgoingEdges.front();
2777 2796 : ret.push_back(std::pair<NBEdge*, NBEdge*>(*i, continuation));
2778 : }
2779 : return ret;
2780 0 : }
2781 :
2782 :
2783 : const PositionVector&
2784 2620123 : NBNode::getShape() const {
2785 2620123 : return myPoly;
2786 : }
2787 :
2788 :
2789 : void
2790 56 : NBNode::setCustomShape(const PositionVector& shape) {
2791 : myPoly = shape;
2792 56 : myHaveCustomPoly = (myPoly.size() > 1);
2793 56 : if (myHaveCustomPoly) {
2794 55 : for (EdgeVector::iterator i = myAllEdges.begin(); i != myAllEdges.end(); i++) {
2795 0 : (*i)->resetNodeBorder(this);
2796 : }
2797 : }
2798 56 : }
2799 :
2800 :
2801 : NBEdge*
2802 154297 : NBNode::getConnectionTo(NBNode* n) const {
2803 387876 : for (NBEdge* e : myOutgoingEdges) {
2804 261366 : if (e->getToNode() == n && e->getPermissions() != 0) {
2805 : return e;
2806 : }
2807 : }
2808 : return nullptr;
2809 : }
2810 :
2811 :
2812 : bool
2813 0 : NBNode::isNearDistrict() const {
2814 0 : if (isDistrict()) {
2815 : return false;
2816 : }
2817 0 : for (const NBEdge* const t : getEdges()) {
2818 0 : const NBNode* const other = t->getToNode() == this ? t->getFromNode() : t->getToNode();
2819 0 : for (const NBEdge* const k : other->getEdges()) {
2820 0 : if (k->getFromNode()->isDistrict() || k->getToNode()->isDistrict()) {
2821 : return true;
2822 : }
2823 : }
2824 : }
2825 : return false;
2826 : }
2827 :
2828 :
2829 : bool
2830 0 : NBNode::isDistrict() const {
2831 0 : return myType == SumoXMLNodeType::DISTRICT;
2832 : }
2833 :
2834 :
2835 : int
2836 3385 : NBNode::guessCrossings() {
2837 : #ifdef DEBUG_PED_STRUCTURES
2838 : gDebugFlag1 = DEBUGCOND;
2839 : #endif
2840 : int numGuessed = 0;
2841 3385 : if (myCrossings.size() > 0 || myDiscardAllCrossings) {
2842 : // user supplied crossings, do not guess
2843 : return numGuessed;
2844 : }
2845 : DEBUGCOUT(gDebugFlag1, "guess crossings for " << getID() << "\n")
2846 3382 : EdgeVector allEdges = getEdgesSortedByAngleAtNodeCenter();
2847 : // check for pedestrial lanes going clockwise around the node
2848 : std::vector<std::pair<NBEdge*, bool> > normalizedLanes;
2849 15104 : for (EdgeVector::const_iterator it = allEdges.begin(); it != allEdges.end(); ++it) {
2850 11722 : NBEdge* edge = *it;
2851 : const std::vector<NBEdge::Lane>& lanes = edge->getLanes();
2852 11722 : if (edge->getFromNode() == this) {
2853 14438 : for (std::vector<NBEdge::Lane>::const_reverse_iterator it_l = lanes.rbegin(); it_l != lanes.rend(); ++it_l) {
2854 8577 : normalizedLanes.push_back(std::make_pair(edge, ((*it_l).permissions & SVC_PEDESTRIAN) != 0));
2855 : }
2856 : } else {
2857 14438 : for (std::vector<NBEdge::Lane>::const_iterator it_l = lanes.begin(); it_l != lanes.end(); ++it_l) {
2858 8577 : normalizedLanes.push_back(std::make_pair(edge, ((*it_l).permissions & SVC_PEDESTRIAN) != 0));
2859 : }
2860 : }
2861 : }
2862 : // do we even have a pedestrian lane?
2863 : int firstSidewalk = -1;
2864 5548 : for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
2865 5194 : if (normalizedLanes[i].second) {
2866 : firstSidewalk = i;
2867 : break;
2868 : }
2869 : }
2870 : int hadCandidates = 0;
2871 : std::vector<int> connectedCandidates; // number of crossings that were built for each connected candidate
2872 3382 : if (firstSidewalk != -1) {
2873 : // rotate lanes to ensure that the first one allows pedestrians
2874 : std::vector<std::pair<NBEdge*, bool> > tmp;
2875 : copy(normalizedLanes.begin() + firstSidewalk, normalizedLanes.end(), std::back_inserter(tmp));
2876 : copy(normalizedLanes.begin(), normalizedLanes.begin() + firstSidewalk, std::back_inserter(tmp));
2877 3028 : normalizedLanes = tmp;
2878 : // find candidates
2879 : EdgeVector candidates;
2880 18958 : for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
2881 15930 : NBEdge* edge = normalizedLanes[i].first;
2882 15930 : const bool allowsPed = normalizedLanes[i].second;
2883 : DEBUGCOUT(gDebugFlag1, " cands=" << toString(candidates) << " edge=" << edge->getID() << " allowsPed=" << allowsPed << "\n")
2884 15930 : if (!allowsPed && (candidates.size() == 0 || candidates.back() != edge)) {
2885 4144 : candidates.push_back(edge);
2886 11786 : } else if (allowsPed) {
2887 9849 : if (candidates.size() > 0) {
2888 1625 : if (hadCandidates > 0 || forbidsPedestriansAfter(normalizedLanes, i)) {
2889 1537 : hadCandidates++;
2890 1537 : const int n = checkCrossing(candidates);
2891 1537 : numGuessed += n;
2892 1537 : if (n > 0) {
2893 1327 : connectedCandidates.push_back(n);
2894 : }
2895 : }
2896 : candidates.clear();
2897 : }
2898 : }
2899 : }
2900 3028 : if (hadCandidates > 0 && candidates.size() > 0) {
2901 : // avoid wrapping around to the same sidewalk
2902 197 : hadCandidates++;
2903 197 : const int n = checkCrossing(candidates);
2904 197 : numGuessed += n;
2905 197 : if (n > 0) {
2906 155 : connectedCandidates.push_back(n);
2907 : }
2908 : }
2909 3028 : }
2910 : // Avoid duplicate crossing between the same pair of walkingareas
2911 : DEBUGCOUT(gDebugFlag1, " hadCandidates=" << hadCandidates << " connectedCandidates=" << toString(connectedCandidates) << "\n")
2912 3028 : if (hadCandidates == 2 && connectedCandidates.size() == 2) {
2913 : // One or both of them might be split: remove the one with less splits
2914 383 : if (connectedCandidates.back() <= connectedCandidates.front()) {
2915 374 : numGuessed -= connectedCandidates.back();
2916 374 : myCrossings.erase(myCrossings.end() - connectedCandidates.back(), myCrossings.end());
2917 : } else {
2918 9 : numGuessed -= connectedCandidates.front();
2919 9 : myCrossings.erase(myCrossings.begin(), myCrossings.begin() + connectedCandidates.front());
2920 : }
2921 : }
2922 3382 : std::sort(myCrossings.begin(), myCrossings.end(), NBNodesEdgesSorter::crossing_by_junction_angle_sorter(this, myAllEdges));
2923 : #ifdef DEBUG_PED_STRUCTURES
2924 : if (gDebugFlag1) {
2925 : std::cout << "guessedCrossings:\n";
2926 : for (auto& crossing : myCrossings) {
2927 : std::cout << " edges=" << toString(crossing->edges) << "\n";
2928 : }
2929 : }
2930 : #endif
2931 3382 : if (numGuessed > 0 && isSimpleContinuation(true, true)) {
2932 : // avoid narrow node shape when there is a crossing
2933 18 : computeNodeShape(-1);
2934 90 : for (NBEdge* e : myAllEdges) {
2935 72 : e->computeEdgeShape();
2936 : }
2937 : }
2938 : return numGuessed;
2939 3382 : }
2940 :
2941 :
2942 : int
2943 1940 : NBNode::checkCrossing(EdgeVector candidates, bool checkOnly) {
2944 : DEBUGCOUT(gDebugFlag1, "checkCrossing candidates=" << toString(candidates) << "\n")
2945 1940 : if (candidates.size() == 0) {
2946 : DEBUGCOUT(gDebugFlag1, "no crossing added (numCandidates=" << candidates.size() << ")\n")
2947 : return 0;
2948 : } else {
2949 : // check whether the edges may be part of a common crossing due to having similar angle
2950 : double prevAngle = -100000; // dummy
2951 5082 : for (int i = 0; i < (int)candidates.size(); ++i) {
2952 3394 : NBEdge* edge = candidates[i];
2953 3394 : double angle = edge->getCrossingAngle(this);
2954 : // edges should be sorted by angle but this only holds true approximately
2955 3394 : if (i > 0 && fabs(NBHelpers::relAngle(angle, prevAngle)) > EXTEND_CROSSING_ANGLE_THRESHOLD) {
2956 : DEBUGCOUT(gDebugFlag1, "no crossing added (found angle difference of " << fabs(NBHelpers::relAngle(angle, prevAngle)) << " at i=" << i << "\n")
2957 : return 0;
2958 : }
2959 8198 : if (!checkOnly && !isTLControlled() && myType != SumoXMLNodeType::RAIL_CROSSING && edge->getSpeed() > OptionsCont::getOptions().getFloat("crossings.guess.speed-threshold")) {
2960 : DEBUGCOUT(gDebugFlag1, "no crossing added (uncontrolled, edge with speed > " << edge->getSpeed() << ")\n")
2961 : return 0;
2962 : }
2963 : prevAngle = angle;
2964 : }
2965 1688 : if (candidates.size() == 1 || getType() == SumoXMLNodeType::RAIL_CROSSING) {
2966 549 : if (!checkOnly) {
2967 549 : addCrossing(candidates, NBEdge::UNSPECIFIED_WIDTH, isTLControlled()
2968 1098 : || (isRoundabout() && OptionsCont::getOptions().getBool("crossings.guess.roundabout-priority")));
2969 : DEBUGCOUT(gDebugFlag1, "adding crossing: " << toString(candidates) << "\n")
2970 : }
2971 549 : return 1;
2972 : } else {
2973 : // check for intermediate walking areas
2974 : prevAngle = -100000; // dummy
2975 3393 : for (EdgeVector::iterator it = candidates.begin(); it != candidates.end(); ++it) {
2976 2357 : double angle = (*it)->getCrossingAngle(this);
2977 2357 : if (it != candidates.begin()) {
2978 1218 : NBEdge* prev = *(it - 1);
2979 1218 : NBEdge* curr = *it;
2980 : Position prevPos, currPos;
2981 : int laneI;
2982 : // compute distance between candiate edges
2983 : double intermediateWidth = 0;
2984 1218 : if (prev->getToNode() == this) {
2985 1157 : laneI = prev->getNumLanes() - 1;
2986 1157 : prevPos = prev->getLanes()[laneI].shape[-1];
2987 : } else {
2988 : laneI = 0;
2989 61 : prevPos = prev->getLanes()[laneI].shape[0];
2990 : }
2991 1218 : intermediateWidth -= 0.5 * prev->getLaneWidth(laneI);
2992 1218 : if (curr->getFromNode() == this) {
2993 1152 : laneI = curr->getNumLanes() - 1;
2994 1152 : currPos = curr->getLanes()[laneI].shape[0];
2995 : } else {
2996 : laneI = 0;
2997 66 : currPos = curr->getLanes()[laneI].shape[-1];
2998 : }
2999 1218 : intermediateWidth -= 0.5 * curr->getLaneWidth(laneI);
3000 1218 : intermediateWidth += currPos.distanceTo2D(prevPos);
3001 : DEBUGCOUT(gDebugFlag1, " prevAngle=" << prevAngle << " angle=" << angle << " intermediateWidth=" << intermediateWidth << "\n")
3002 1218 : if (fabs(NBHelpers::relAngle(prevAngle, angle)) > SPLIT_CROSSING_ANGLE_THRESHOLD
3003 1218 : || (intermediateWidth > SPLIT_CROSSING_WIDTH_THRESHOLD)) {
3004 206 : return checkCrossing(EdgeVector(candidates.begin(), it), checkOnly)
3005 103 : + checkCrossing(EdgeVector(it, candidates.end()), checkOnly);
3006 : }
3007 : }
3008 : prevAngle = angle;
3009 : }
3010 1036 : if (!checkOnly) {
3011 1036 : addCrossing(candidates, NBEdge::UNSPECIFIED_WIDTH, isTLControlled()
3012 2080 : || (isRoundabout() && OptionsCont::getOptions().getBool("crossings.guess.roundabout-priority")));
3013 : DEBUGCOUT(gDebugFlag1, "adding crossing: " << toString(candidates) << "\n")
3014 : }
3015 1036 : return 1;
3016 : }
3017 : }
3018 : }
3019 :
3020 :
3021 : bool
3022 317 : NBNode::checkCrossingDuplicated(EdgeVector edges) {
3023 : // sort edge vector
3024 317 : std::sort(edges.begin(), edges.end());
3025 : // iterate over crossing to find a crossing with the same edges
3026 845 : for (auto& crossing : myCrossings) {
3027 : // sort edges of crossing before compare
3028 531 : EdgeVector edgesOfCrossing = crossing->edges;
3029 531 : std::sort(edgesOfCrossing.begin(), edgesOfCrossing.end());
3030 531 : if (edgesOfCrossing == edges) {
3031 : return true;
3032 : }
3033 531 : }
3034 : return false;
3035 : }
3036 :
3037 :
3038 : bool
3039 793 : NBNode::forbidsPedestriansAfter(std::vector<std::pair<NBEdge*, bool> > normalizedLanes, int startIndex) {
3040 2407 : for (int i = startIndex; i < (int)normalizedLanes.size(); ++i) {
3041 2319 : if (!normalizedLanes[i].second) {
3042 : return true;
3043 : }
3044 : }
3045 : return false;
3046 : }
3047 :
3048 :
3049 : void
3050 4202 : NBNode::buildCrossingsAndWalkingAreas() {
3051 4202 : buildCrossings();
3052 8404 : buildWalkingAreas(OptionsCont::getOptions().getInt("junctions.corner-detail"),
3053 4202 : OptionsCont::getOptions().getFloat("walkingareas.join-dist"));
3054 4202 : buildCrossingOutlines();
3055 : // ensure that all crossings are properly connected
3056 4202 : bool recheck = myCrossings.size() > 0;
3057 5025 : while (recheck) {
3058 : recheck = false;
3059 : std::set<std::string> waIDs;
3060 : int numSidewalks = 0;
3061 3115 : for (WalkingArea& wa : myWalkingAreas) {
3062 2292 : waIDs.insert(wa.id);
3063 2292 : numSidewalks += (int)(wa.prevSidewalks.size() + wa.nextSidewalks.size());
3064 : }
3065 823 : if (numSidewalks < 2) {
3066 : // all crossings are invalid if there are fewer than 2 sidewalks involved
3067 : waIDs.clear();
3068 : }
3069 2510 : for (auto& crossing : myCrossings) {
3070 1687 : if (waIDs.count(crossing->prevWalkingArea) == 0 || waIDs.count(crossing->nextWalkingArea) == 0 || !crossing->valid) {
3071 40 : if (crossing->valid) {
3072 30 : WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (no walkingarea found)."),
3073 : crossing->id, getID(), toString(crossing->edges));
3074 : recheck = true;
3075 : }
3076 151 : for (auto waIt = myWalkingAreas.begin(); waIt != myWalkingAreas.end();) {
3077 : WalkingArea& wa = *waIt;
3078 111 : std::vector<std::string>::iterator it_nc = std::find(wa.nextCrossings.begin(), wa.nextCrossings.end(), crossing->id);
3079 111 : if (it_nc != wa.nextCrossings.end()) {
3080 11 : wa.nextCrossings.erase(it_nc);
3081 : }
3082 111 : if (wa.prevSidewalks.size() + wa.nextSidewalks.size() + wa.nextCrossings.size() + wa.prevCrossings.size() < 2) {
3083 9 : waIt = myWalkingAreas.erase(waIt);
3084 : recheck = true;
3085 : } else {
3086 : waIt++;
3087 : }
3088 : }
3089 40 : crossing->valid = false;
3090 40 : crossing->prevWalkingArea = "";
3091 40 : crossing->nextWalkingArea = "";
3092 : }
3093 : }
3094 : }
3095 4202 : }
3096 :
3097 :
3098 : std::vector<NBNode::Crossing*>
3099 1079908 : NBNode::getCrossings() const {
3100 : std::vector<Crossing*> result;
3101 1365621 : for (auto& c : myCrossings) {
3102 285713 : if (c->valid) {
3103 282657 : result.push_back(c.get());
3104 : }
3105 : }
3106 : //if (myCrossings.size() > 0) {
3107 : // std::cout << "valid crossings at " << getID() << "\n";
3108 : // for (std::vector<NBNode::Crossing*>::const_iterator it = result.begin(); it != result.end(); ++it) {
3109 : // std::cout << " " << toString((*it)->edges) << "\n";
3110 : // }
3111 : //}
3112 1079908 : return result;
3113 0 : }
3114 :
3115 :
3116 : void
3117 29865 : NBNode::discardAllCrossings(bool rejectAll) {
3118 : myCrossings.clear();
3119 : // also discard all further crossings
3120 29865 : if (rejectAll) {
3121 1 : myDiscardAllCrossings = true;
3122 : }
3123 29865 : }
3124 :
3125 :
3126 : void
3127 53430 : NBNode::discardWalkingareas() {
3128 : myWalkingAreas.clear();
3129 53430 : }
3130 :
3131 :
3132 : double
3133 27777 : NBNode::buildInnerEdges() {
3134 : // myDisplacementError is computed during this operation. reset first
3135 27777 : myDisplacementError = 0.;
3136 : // build inner edges for vehicle movements across the junction
3137 : int noInternalNoSplits = 0;
3138 76256 : for (const NBEdge* const edge : myIncomingEdges) {
3139 144868 : for (const NBEdge::Connection& con : edge->getConnections()) {
3140 96389 : if (con.toEdge == nullptr) {
3141 0 : continue;
3142 : }
3143 96389 : noInternalNoSplits++;
3144 : }
3145 : }
3146 27777 : int lno = 0;
3147 27777 : int splitNo = 0;
3148 : double maxCrossingSeconds = 0.;
3149 76256 : for (NBEdge* const edge : myIncomingEdges) {
3150 48479 : maxCrossingSeconds = MAX2(maxCrossingSeconds, edge->buildInnerEdges(*this, noInternalNoSplits, lno, splitNo));
3151 : }
3152 27777 : return maxCrossingSeconds;
3153 : }
3154 :
3155 :
3156 : int
3157 4202 : NBNode::buildCrossings() {
3158 : #ifdef DEBUG_PED_STRUCTURES
3159 : gDebugFlag1 = DEBUGCOND;
3160 : #endif
3161 : DEBUGCOUT(gDebugFlag1, "build crossings for " << getID() << ":\n")
3162 4202 : if (myDiscardAllCrossings) {
3163 : myCrossings.clear();
3164 : }
3165 : int index = 0;
3166 8404 : const double defaultWidth = OptionsCont::getOptions().getFloat("default.crossing-width");
3167 5851 : for (auto& c : myCrossings) {
3168 1649 : c->valid = true;
3169 1649 : if (!isTLControlled()) {
3170 1003 : c->tlID = ""; // reset for Netedit, set via setCrossingTLIndices()
3171 : }
3172 4947 : c->id = ":" + getID() + "_c" + toString(index++);
3173 1649 : c->width = (c->customWidth == NBEdge::UNSPECIFIED_WIDTH) ? defaultWidth : c->customWidth;
3174 : // reset fields, so repeated computation (Netedit) will successfully perform the checks
3175 : // in buildWalkingAreas (split crossings) and buildInnerEdges (sanity check)
3176 1649 : c->nextWalkingArea = "";
3177 1649 : c->prevWalkingArea = "";
3178 : EdgeVector& edges = c->edges;
3179 : DEBUGCOUT(gDebugFlag1, " crossing=" << c->id << " edges=" << toString(edges))
3180 : // sorting the edges in the right way is imperative. We want to sort
3181 : // them by getAngleAtNodeToCenter() but need to be extra carefull to avoid wrapping around 0 somewhere in between
3182 1649 : std::sort(edges.begin(), edges.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
3183 : DEBUGCOUT(gDebugFlag1, " sortedEdges=" << toString(edges) << "\n")
3184 : // rotate the edges so that the largest relative angle difference comes at the end
3185 : std::vector<double> rawAngleDiffs;
3186 : double maxAngleDiff = 0;
3187 : int maxAngleDiffIndex = 0; // index before maxDist
3188 4481 : for (int i = 0; i < (int) edges.size(); i++) {
3189 2832 : double diff = NBHelpers::relAngle(edges[i]->getAngleAtNodeToCenter(this),
3190 2832 : edges[(i + 1) % edges.size()]->getAngleAtNodeToCenter(this));
3191 2832 : if (diff < 0) {
3192 1083 : diff += 360;
3193 : }
3194 2832 : const double rawDiff = NBHelpers::relAngle(
3195 : edges[i]->getAngleAtNodeNormalized(this),
3196 2832 : edges[(i + 1) % edges.size()]->getAngleAtNodeNormalized(this));
3197 2832 : rawAngleDiffs.push_back(fabs(rawDiff));
3198 :
3199 : DEBUGCOUT(gDebugFlag1, " i=" << i << " a1=" << edges[i]->getAngleAtNodeToCenter(this) << " a2=" << edges[(i + 1) % edges.size()]->getAngleAtNodeToCenter(this) << " diff=" << diff << "\n")
3200 2832 : if (diff > maxAngleDiff) {
3201 : maxAngleDiff = diff;
3202 : maxAngleDiffIndex = i;
3203 : }
3204 : }
3205 1649 : if (maxAngleDiff > 2 && maxAngleDiff < 360 - 2) {
3206 : // if the angle differences is too small, we better not rotate
3207 1033 : std::rotate(edges.begin(), edges.begin() + (maxAngleDiffIndex + 1) % edges.size(), edges.end());
3208 : DEBUGCOUT(gDebugFlag1, " rotatedEdges=" << toString(edges))
3209 : }
3210 : bool diagonalCrossing = false;
3211 1649 : std::sort(rawAngleDiffs.begin(), rawAngleDiffs.end());
3212 1649 : if (rawAngleDiffs.size() >= 2 && rawAngleDiffs[rawAngleDiffs.size() - 2] > 30) {
3213 : diagonalCrossing = true;
3214 : #ifdef DEBUG_PED_STRUCTURES
3215 : if (gDebugFlag1) {
3216 : std::cout << " detected pedScramble " << c->id << " edges=" << toString(edges) << " rawDiffs=" << toString(rawAngleDiffs) << "\n";
3217 : for (auto e : edges) {
3218 : std::cout << " e=" << e->getID()
3219 : << " aC=" << e->getAngleAtNodeToCenter(this)
3220 : << " a=" << e->getAngleAtNode(this)
3221 : << " aN=" << e->getAngleAtNodeNormalized(this)
3222 : << "\n";
3223 : }
3224 : }
3225 : #endif
3226 : }
3227 : // reverse to get them in CCW order (walking direction around the node)
3228 : std::reverse(edges.begin(), edges.end());
3229 : // compute shape
3230 : c->shape.clear();
3231 1649 : const int begDir = (edges.front()->getFromNode() == this ? FORWARD : BACKWARD);
3232 1649 : const int endDir = (edges.back()->getToNode() == this ? FORWARD : BACKWARD);
3233 1649 : int firstNonPedLane = edges.front()->getFirstNonPedestrianLaneIndex(begDir);
3234 1649 : int lastNonPedLane = edges.back()->getFirstNonPedestrianLaneIndex(endDir);
3235 : DEBUGCOUT(gDebugFlag1, " finalEdges=" << toString(edges) << " firstNonPedLane=" << firstNonPedLane << " lastNonPedLane=" << lastNonPedLane << "\n")
3236 1649 : if (firstNonPedLane < 0 || lastNonPedLane < 0) {
3237 : // invalid crossing
3238 12 : WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (no vehicle lanes to cross)."), c->id, getID(), toString(c->edges));
3239 4 : c->valid = false;
3240 : // compute surrogate shape to make it visible in netedit
3241 4 : firstNonPedLane = begDir == FORWARD ? 0 : edges.front()->getNumLanes() - 1;
3242 4 : lastNonPedLane = endDir == FORWARD ? 0 : edges.back()->getNumLanes() - 1;
3243 : }
3244 1649 : if (c->customShape.size() != 0) {
3245 : c->shape = c->customShape;
3246 : } else {
3247 1630 : NBEdge::Lane crossingBeg = edges.front()->getLanes()[firstNonPedLane];
3248 1630 : NBEdge::Lane crossingEnd = edges.back()->getLanes()[lastNonPedLane];
3249 1630 : crossingBeg.width = (crossingBeg.width == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : crossingBeg.width);
3250 1630 : crossingEnd.width = (crossingEnd.width == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : crossingEnd.width);
3251 1630 : crossingBeg.shape.move2side(begDir * crossingBeg.width / 2);
3252 1630 : crossingEnd.shape.move2side(endDir * crossingEnd.width / 2);
3253 1630 : double offset = c->width / 2;
3254 1630 : patchOffset_pathAcrossStreet(offset);
3255 1630 : crossingBeg.shape.extrapolate(offset);
3256 1630 : crossingEnd.shape.extrapolate(offset);
3257 : // check if after all changes shape are NAN (in these case, discard)
3258 1630 : if (crossingBeg.shape.isNAN() || crossingEnd.shape.isNAN()) {
3259 0 : WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (invalid shape)."), c->id, getID(), toString(c->edges));
3260 0 : c->valid = false;
3261 : } else {
3262 1926 : c->shape.push_back(crossingBeg.shape[begDir == FORWARD ? 0 : -1]);
3263 1881 : c->shape.push_back(crossingEnd.shape[endDir == FORWARD ? -1 : 0]);
3264 : }
3265 1630 : if (diagonalCrossing) {
3266 7 : c->shape.move2side(-c->width);
3267 : }
3268 1630 : }
3269 1649 : }
3270 4202 : return index;
3271 : }
3272 :
3273 :
3274 : void
3275 1630 : NBNode::patchOffset_pathAcrossStreet(double& offset) {
3276 1630 : if (myCrossings.size() == 1 && myAllEdges.size() >= 3) {
3277 : EdgeVector nonPedIncoming;
3278 : EdgeVector nonPedOutgoing;
3279 : EdgeVector pedIncoming;
3280 : EdgeVector pedOutgoing;
3281 1772 : for (NBEdge* e : getIncomingEdges()) {
3282 1273 : if (e->getPermissions() != SVC_PEDESTRIAN) {
3283 1058 : nonPedIncoming.push_back(e);
3284 : } else {
3285 215 : pedIncoming.push_back(e);
3286 : }
3287 : }
3288 1772 : for (NBEdge* e : getOutgoingEdges()) {
3289 1273 : if (e->getPermissions() != SVC_PEDESTRIAN) {
3290 1058 : nonPedOutgoing.push_back(e);
3291 : } else {
3292 215 : pedOutgoing.push_back(e);
3293 : }
3294 : }
3295 499 : if (geometryLike(nonPedIncoming, nonPedOutgoing) && (pedIncoming.size() > 0 || pedOutgoing.size() > 0)) {
3296 : double maxAngle = 0;
3297 212 : const NBEdge* in = nonPedIncoming.front();
3298 212 : const NBEdge* out = nonPedOutgoing.front();
3299 212 : if (nonPedIncoming.size() == 1) {
3300 90 : maxAngle = fabs(NBHelpers::relAngle(in->getAngleAtNode(this), out->getAngleAtNode(this)));
3301 : } else {
3302 366 : for (const NBEdge* const in2 : nonPedIncoming) {
3303 : double minAngle = 180;
3304 732 : for (const NBEdge* const out2 : nonPedOutgoing) {
3305 488 : double angle = fabs(NBHelpers::relAngle(in2->getAngleAtNode(this), out2->getAngleAtNode(this)));
3306 488 : if (angle < minAngle) {
3307 : minAngle = angle;
3308 : in = in2;
3309 : out = out2;
3310 : }
3311 : }
3312 : maxAngle = MAX2(maxAngle, minAngle);
3313 : }
3314 : }
3315 : // changing the offset only handles the simple case where the road stays straight
3316 212 : if (maxAngle < 15) {
3317 205 : const int inLane = in->getFirstNonPedestrianLaneIndex(FORWARD);
3318 205 : const int outLane = out->getFirstNonPedestrianLaneIndex(FORWARD);
3319 205 : if (inLane >= 0 && outLane >= 0) {
3320 205 : const Position& p0 = in->getLaneShape(inLane).back();
3321 205 : const Position& p1 = out->getLaneShape(outLane).front();
3322 205 : PositionVector road;
3323 205 : road.push_back(p0);
3324 205 : road.push_back(p1);
3325 : Position mid = (p0 + p1) / 2;
3326 : double maxPathDist = 0;
3327 363 : for (NBEdge* e : pedIncoming) {
3328 158 : const Position roadPos = road.positionAtOffset2D(road.nearest_offset_to_point2D(e->getLaneShape(0).back()));
3329 : maxPathDist = MAX2(maxPathDist, mid.distanceTo2D(roadPos));
3330 : }
3331 355 : for (NBEdge* e : pedOutgoing) {
3332 150 : const Position roadPos = road.positionAtOffset2D(road.nearest_offset_to_point2D(e->getLaneShape(0).front()));
3333 : maxPathDist = MAX2(maxPathDist, mid.distanceTo2D(roadPos));
3334 : }
3335 : // if the junction is stretched, the crossing should stay close to the paths
3336 408 : if (maxPathDist < MAX2(myCrossings.front()->width, 4.0)) {
3337 190 : offset = p0.distanceTo2D(p1) / 2;
3338 : }
3339 205 : }
3340 : }
3341 : }
3342 499 : }
3343 1630 : }
3344 :
3345 :
3346 : void
3347 4202 : NBNode::buildWalkingAreas(int cornerDetail, double joinMinDist) {
3348 : #ifdef DEBUG_PED_STRUCTURES
3349 : gDebugFlag1 = DEBUGCOND;
3350 : #endif
3351 : int index = 0;
3352 : myWalkingAreas.clear();
3353 : DEBUGCOUT(gDebugFlag1, "build walkingAreas for " << getID() << ":\n")
3354 4202 : if (myAllEdges.size() == 0) {
3355 0 : return;
3356 : }
3357 4202 : EdgeVector allEdges = getEdgesSortedByAngleAtNodeCenter();
3358 : // shapes are all pointing away from the intersection
3359 : std::vector<std::pair<NBEdge*, NBEdge::Lane> > normalizedLanes;
3360 18330 : for (EdgeVector::const_iterator it = allEdges.begin(); it != allEdges.end(); ++it) {
3361 14128 : NBEdge* edge = *it;
3362 : const std::vector<NBEdge::Lane>& lanes = edge->getLanes();
3363 : std::vector<NBEdge::Lane> tmp;
3364 : bool hadSidewalk = false;
3365 : bool hadNonSidewalk = false;
3366 37822 : for (int i = 0; i < (int)lanes.size(); i++) {
3367 23694 : NBEdge::Lane l = lanes[i];
3368 23694 : const bool sidewalk = (l.permissions & SVC_PEDESTRIAN) != 0;
3369 23694 : if (sidewalk) {
3370 11892 : if (hadSidewalk && hadNonSidewalk) {
3371 4 : if (edge->getFromNode() == this) {
3372 6 : WRITE_WARNINGF(TL("Ignoring additional sidewalk lane % on edge '%' for walkingareas."),
3373 : i, edge->getID());
3374 : }
3375 : continue;
3376 : }
3377 : hadSidewalk = true;
3378 : } else {
3379 : hadNonSidewalk = true;
3380 : }
3381 23690 : tmp.push_back(l);
3382 23694 : }
3383 14128 : if (edge->getFromNode() == this) {
3384 : std::reverse(tmp.begin(), tmp.end());
3385 : } else {
3386 18909 : for (NBEdge::Lane& l : tmp) {
3387 23690 : l.shape = l.shape.reverse();
3388 : }
3389 : }
3390 37818 : for (NBEdge::Lane& l : tmp) {
3391 47380 : l.shape = l.shape.getSubpartByIndex(0, 2);
3392 35544 : l.width = (l.width == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : l.width);
3393 23690 : normalizedLanes.push_back(std::make_pair(edge, l));
3394 : }
3395 14128 : }
3396 : //if (gDebugFlag1) std::cout << " normalizedLanes=" << normalizedLanes.size() << "\n";
3397 : // collect [start,count[ indices in normalizedLanes that belong to a walkingArea
3398 : std::vector<std::pair<int, int> > waIndices;
3399 : int start = -1;
3400 4202 : NBEdge* prevEdge = normalizedLanes.back().first;
3401 27892 : for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
3402 23690 : NBEdge* edge = normalizedLanes[i].first;
3403 : NBEdge::Lane& l = normalizedLanes[i].second;
3404 23690 : if (start == -1) {
3405 14742 : if ((l.permissions & SVC_PEDESTRIAN) != 0) {
3406 : start = i;
3407 : }
3408 : } else {
3409 8948 : if ((l.permissions & SVC_PEDESTRIAN) == 0
3410 6007 : || crossingBetween(edge, prevEdge)
3411 6001 : || alreadyConnectedPaths(edge, prevEdge, joinMinDist)
3412 14882 : || crossesFringe(edge, prevEdge)
3413 : ) {
3414 3026 : waIndices.push_back(std::make_pair(start, i - start));
3415 3026 : if ((l.permissions & SVC_PEDESTRIAN) != 0) {
3416 : start = i;
3417 : } else {
3418 : start = -1;
3419 : }
3420 :
3421 : }
3422 : }
3423 : DEBUGCOUT(gDebugFlag1, " i=" << i << " edge=" << edge->getID() << " start=" << start << " ped=" << ((l.permissions & SVC_PEDESTRIAN) != 0)
3424 : << " waI=" << waIndices.size() << " crossingBetween=" << crossingBetween(edge, prevEdge) << "\n")
3425 : prevEdge = edge;
3426 : }
3427 : // deal with wrap-around issues
3428 4202 : if (start != - 1) {
3429 2940 : const int waNumLanes = (int)normalizedLanes.size() - start;
3430 2940 : if (waIndices.size() == 0) {
3431 2064 : waIndices.push_back(std::make_pair(start, waNumLanes));
3432 : DEBUGCOUT(gDebugFlag1, " single wa, end at wrap-around\n")
3433 : } else {
3434 876 : if (waIndices.front().first == 0) {
3435 785 : NBEdge* edge = normalizedLanes.front().first;
3436 785 : if (crossingBetween(edge, normalizedLanes.back().first)
3437 785 : || crossesFringe(edge, normalizedLanes.back().first)) {
3438 : // do not wrap-around (see above)
3439 7 : waIndices.push_back(std::make_pair(start, waNumLanes));
3440 : DEBUGCOUT(gDebugFlag1, " do not wrap around\n")
3441 : } else {
3442 : // first walkingArea wraps around
3443 778 : waIndices.front().first = start;
3444 778 : waIndices.front().second = waNumLanes + waIndices.front().second;
3445 : DEBUGCOUT(gDebugFlag1, " wrapping around\n")
3446 : }
3447 : } else {
3448 : // last walkingArea ends at the wrap-around
3449 91 : waIndices.push_back(std::make_pair(start, waNumLanes));
3450 : DEBUGCOUT(gDebugFlag1, " end at wrap-around\n")
3451 : }
3452 : }
3453 : }
3454 : #ifdef DEBUG_PED_STRUCTURES
3455 : if (gDebugFlag1) {
3456 : std::cout << " normalizedLanes=" << normalizedLanes.size() << " waIndices:\n";
3457 : for (int i = 0; i < (int)waIndices.size(); ++i) {
3458 : std::cout << " " << waIndices[i].first << ", " << waIndices[i].second << "\n";
3459 : }
3460 : }
3461 : #endif
3462 : // build walking areas connected to a sidewalk
3463 9390 : for (int i = 0; i < (int)waIndices.size(); ++i) {
3464 5188 : const bool buildExtensions = waIndices[i].second != (int)normalizedLanes.size();
3465 5188 : int startIdx = waIndices[i].first;
3466 5188 : const int prev = startIdx > 0 ? startIdx - 1 : (int)normalizedLanes.size() - 1;
3467 : const int count = waIndices[i].second;
3468 5188 : const int end = (startIdx + count) % normalizedLanes.size();
3469 5188 : int lastIdx = (startIdx + count - 1) % normalizedLanes.size();
3470 :
3471 15564 : WalkingArea wa(":" + getID() + "_w" + toString(index++), 1);
3472 : DEBUGCOUT(gDebugFlag1, "build walkingArea " << wa.id << " start=" << startIdx << " end=" << end << " count=" << count << " prev=" << prev << ":\n")
3473 : double endCrossingWidth = 0;
3474 : double startCrossingWidth = 0;
3475 5188 : PositionVector endCrossingShape;
3476 5188 : PositionVector startCrossingShape;
3477 : // check for connected crossings
3478 : bool connectsCrossing = false;
3479 : bool crossingNearSidewalk = false;
3480 : int numCrossings = 0;
3481 : std::vector<Position> connectedPoints;
3482 10681 : for (auto c : getCrossings()) {
3483 : DEBUGCOUT(gDebugFlag1, " crossing=" << c->id << " sortedEdges=" << toString(c->edges) << "\n")
3484 5493 : if (c->edges.back() == normalizedLanes[end].first
3485 5493 : && (normalizedLanes[end].second.permissions & SVC_PEDESTRIAN) == 0) {
3486 : // crossing ends
3487 1493 : if (c->nextWalkingArea != "") {
3488 3 : WRITE_WARNINGF(TL("Invalid pedestrian topology at junction '%'; crossing '%' targets '%' and '%'."),
3489 : getID(), c->id, c->nextWalkingArea, wa.id);
3490 1 : c->valid = false;
3491 : }
3492 : c->nextWalkingArea = wa.id;
3493 1493 : wa.prevCrossings.push_back(c->id);
3494 1493 : if ((int)c->edges.size() < wa.minPrevCrossingEdges) {
3495 : // if there are multiple crossings, use the shape of the one that crosses fewer edges
3496 1493 : endCrossingWidth = c->width;
3497 : endCrossingShape = c->shape;
3498 1493 : wa.width = MAX2(wa.width, endCrossingWidth);
3499 : connectsCrossing = true;
3500 1493 : connectedPoints.push_back(c->shape[-1]);
3501 1493 : wa.minPrevCrossingEdges = (int)c->edges.size();
3502 1493 : numCrossings++;
3503 1493 : if (normalizedLanes[lastIdx].second.shape[0].distanceTo2D(connectedPoints.back()) < endCrossingWidth) {
3504 : crossingNearSidewalk = true;
3505 : DEBUGCOUT(gDebugFlag1, " nearSidewalk\n")
3506 : }
3507 : }
3508 : DEBUGCOUT(gDebugFlag1, " crossing " << c->id << " ends\n")
3509 : }
3510 5493 : if (c->edges.front() == normalizedLanes[prev].first
3511 5493 : && (normalizedLanes[prev].second.permissions & SVC_PEDESTRIAN) == 0) {
3512 : // crossing starts
3513 1493 : if (c->prevWalkingArea != "") {
3514 0 : WRITE_WARNINGF(TL("Invalid pedestrian topology at junction '%'; crossing '%' is targeted by '%' and '%'."),
3515 : getID(), c->id, c->prevWalkingArea, wa.id);
3516 0 : c->valid = false;
3517 : }
3518 1493 : if (c->valid && std::find(wa.prevCrossings.begin(), wa.prevCrossings.end(), c->id) != wa.prevCrossings.end()) {
3519 9 : WRITE_WARNINGF(TL("Invalid pedestrian topology at junction '%'; crossing '%' starts and ends at walkingarea '%'."),
3520 : getID(), c->id, wa.id);
3521 3 : c->valid = false;
3522 : }
3523 : c->prevWalkingArea = wa.id;
3524 1493 : wa.nextCrossings.push_back(c->id);
3525 1493 : if ((int)c->edges.size() < wa.minNextCrossingEdges) {
3526 : // if there are multiple crossings, use the shape of the one that crosses fewer edges
3527 1493 : startCrossingWidth = c->width;
3528 : startCrossingShape = c->shape;
3529 1493 : wa.width = MAX2(wa.width, startCrossingWidth);
3530 : connectsCrossing = true;
3531 1493 : connectedPoints.push_back(c->shape[0]);
3532 1493 : wa.minNextCrossingEdges = (int)c->edges.size();
3533 1493 : numCrossings++;
3534 1493 : if (normalizedLanes[startIdx].second.shape[0].distanceTo2D(connectedPoints.back()) < startCrossingWidth) {
3535 : crossingNearSidewalk = true;
3536 : DEBUGCOUT(gDebugFlag1, " nearSidewalk\n")
3537 : }
3538 : }
3539 : DEBUGCOUT(gDebugFlag1, " crossing " << c->id << " starts\n")
3540 : }
3541 : DEBUGCOUT(gDebugFlag1, " check connections to crossing " << c->id
3542 : << " cFront=" << c->edges.front()->getID() << " cBack=" << c->edges.back()->getID()
3543 : << " wEnd=" << normalizedLanes[end].first->getID() << " wStart=" << normalizedLanes[startIdx].first->getID()
3544 : << " wStartPrev=" << normalizedLanes[prev].first->getID()
3545 : << "\n")
3546 5188 : }
3547 5188 : if (count < 2 && !connectsCrossing) {
3548 : // not relevant for walking
3549 : DEBUGCOUT(gDebugFlag1, " not relevant for walking: count=" << count << " connectsCrossing=" << connectsCrossing << "\n")
3550 732 : continue;
3551 : }
3552 : // build shape and connections
3553 : std::set<const NBEdge*, ComparatorIdLess>& connected = wa.refEdges;
3554 15612 : for (int j = 0; j < count; ++j) {
3555 11156 : const int nlI = (startIdx + j) % normalizedLanes.size();
3556 11156 : NBEdge* edge = normalizedLanes[nlI].first;
3557 11156 : NBEdge::Lane l = normalizedLanes[nlI].second;
3558 17776 : wa.width = MAX2(wa.width, l.width);
3559 : if (connected.count(edge) == 0) {
3560 11104 : if (edge->getFromNode() == this) {
3561 5582 : wa.nextSidewalks.push_back(edge->getSidewalkID());
3562 5582 : connectedPoints.push_back(edge->getLaneShape(0)[0]);
3563 : } else {
3564 5522 : wa.prevSidewalks.push_back(edge->getSidewalkID());
3565 5522 : connectedPoints.push_back(edge->getLaneShape(0)[-1]);
3566 : }
3567 : DEBUGCOUT(gDebugFlag1, " connectedEdge=" << edge->getID() << " connectedPoint=" << connectedPoints.back() << "\n")
3568 : connected.insert(edge);
3569 : }
3570 11156 : l.shape.move2side(-l.width / 2);
3571 11156 : wa.shape.push_back_noDoublePos(l.shape[0]);
3572 11156 : l.shape.move2side(l.width);
3573 11156 : wa.shape.push_back(l.shape[0]);
3574 11156 : }
3575 4456 : if (buildExtensions) {
3576 : // extension at starting crossing
3577 2917 : if (startCrossingShape.size() > 0) {
3578 1483 : startCrossingShape.move2side(startCrossingWidth / 2);
3579 1483 : wa.shape.push_front_noDoublePos(startCrossingShape[0]); // right corner
3580 1483 : startCrossingShape.move2side(-startCrossingWidth);
3581 1483 : wa.shape.push_front_noDoublePos(startCrossingShape[0]); // left corner goes first
3582 : DEBUGCOUT(gDebugFlag1, " extension at startCrossingShape=" << endCrossingShape << " waShape=" << wa.shape << "\n")
3583 : }
3584 : // extension at ending crossing
3585 2917 : if (endCrossingShape.size() > 0) {
3586 1483 : endCrossingShape.move2side(endCrossingWidth / 2);
3587 1483 : wa.shape.push_back_noDoublePos(endCrossingShape[-1]);
3588 1483 : endCrossingShape.move2side(-endCrossingWidth);
3589 1483 : wa.shape.push_back_noDoublePos(endCrossingShape[-1]);
3590 : DEBUGCOUT(gDebugFlag1, " extension at endCrossingShape=" << endCrossingShape << " waShape=" << wa.shape << "\n")
3591 : }
3592 : }
3593 2492 : if (connected.size() == 2 && !connectsCrossing && wa.nextSidewalks.size() == 1 && wa.prevSidewalks.size() == 1
3594 5583 : && normalizedLanes.size() == 2) {
3595 : // do not build a walkingArea since a normal connection exists
3596 434 : const NBEdge* e1 = *connected.begin();
3597 434 : const NBEdge* e2 = *(++connected.begin());
3598 434 : if (e1->hasConnectionTo(e2, 0, 0) || e2->hasConnectionTo(e1, 0, 0)) {
3599 : DEBUGCOUT(gDebugFlag1, " not building a walkingarea since normal connections exist\n")
3600 156 : continue;
3601 : }
3602 : }
3603 4300 : if (count == (int)normalizedLanes.size()) {
3604 : // junction is covered by the whole walkingarea
3605 : wa.shape = myPoly;
3606 : // increase walking width if the walkingare is wider than a single lane
3607 3690 : for (const NBEdge* in : myIncomingEdges) {
3608 6666 : for (const NBEdge* out : myOutgoingEdges) {
3609 5184 : if (in->getFromNode() == out->getToNode() && in->getInnerGeometry().reverse() == out->getInnerGeometry()
3610 825 : && (in->getPermissions() & SVC_PEDESTRIAN)
3611 5184 : && (out->getPermissions() & SVC_PEDESTRIAN)) {
3612 : // doesn't catch all cases but probably most
3613 1621 : wa.width = MAX2(wa.width, in->getTotalWidth() + out->getTotalWidth());
3614 : }
3615 : }
3616 : }
3617 2917 : } else if (cornerDetail > 0) {
3618 : // build smooth inner curve (optional)
3619 : int smoothEnd = end;
3620 : int smoothPrev = prev;
3621 : // extend to green verge
3622 2741 : if (endCrossingWidth > 0 && normalizedLanes[smoothEnd].second.permissions == 0) {
3623 154 : smoothEnd = (smoothEnd + 1) % normalizedLanes.size();
3624 : }
3625 2741 : if (startCrossingWidth > 0 && normalizedLanes[smoothPrev].second.permissions == 0) {
3626 150 : if (smoothPrev == 0) {
3627 0 : smoothPrev = (int)normalizedLanes.size() - 1;
3628 : } else {
3629 150 : smoothPrev--;
3630 : }
3631 : }
3632 2741 : PositionVector begShape = normalizedLanes[smoothEnd].second.shape;
3633 5482 : begShape = begShape.reverse();
3634 : double shiftBegExtra = 0;
3635 : double shiftEndExtra = 0;
3636 2741 : if (lastIdx == startIdx) {
3637 488 : lastIdx = (startIdx + 1) % normalizedLanes.size();
3638 : DEBUGCOUT(gDebugFlag1, " new lastIdx=" << lastIdx << " startEdge=" << normalizedLanes[startIdx].first->getID() << " lastEdge=" << normalizedLanes[lastIdx].first->getID() << "\n")
3639 488 : if (normalizedLanes[startIdx].first == normalizedLanes[lastIdx].first) {
3640 : lastIdx = startIdx;
3641 132 : startIdx--;
3642 132 : if (startIdx < 0) {
3643 34 : startIdx = (int)normalizedLanes.size() - 1;
3644 : }
3645 : DEBUGCOUT(gDebugFlag1, " new startIdx=" << startIdx << " startEdge=" << normalizedLanes[startIdx].first->getID() << " lastEdge=" << normalizedLanes[lastIdx].first->getID() << "\n")
3646 132 : shiftEndExtra += OptionsCont::getOptions().getFloat("default.sidewalk-width");
3647 : } else {
3648 356 : shiftBegExtra += OptionsCont::getOptions().getFloat("default.sidewalk-width");
3649 : }
3650 : }
3651 2741 : PositionVector begShapeOuter = normalizedLanes[lastIdx].second.shape;
3652 5482 : begShapeOuter = begShapeOuter.reverse();
3653 : //begShape.extrapolate(endCrossingWidth);
3654 2741 : begShape.move2side(normalizedLanes[smoothEnd].second.width / 2);
3655 2741 : begShapeOuter.move2side(normalizedLanes[lastIdx].second.width / 2 + shiftBegExtra);
3656 2741 : PositionVector endShape = normalizedLanes[smoothPrev].second.shape;
3657 2741 : PositionVector endShapeOuter = normalizedLanes[startIdx].second.shape;;
3658 2741 : endShape.move2side(normalizedLanes[smoothPrev].second.width / 2);
3659 2741 : endShapeOuter.move2side(normalizedLanes[startIdx].second.width / 2 + shiftEndExtra);
3660 : //endShape.extrapolate(startCrossingWidth);
3661 2741 : PositionVector curve;
3662 2741 : if (count != (int)normalizedLanes.size() || count == 2) {
3663 2741 : const double angle = GeomHelper::angleDiff(begShape.angleAt2D(-2), endShape.angleAt2D(0));
3664 2741 : if (count == 1 && angle > 0 && crossingNearSidewalk && numCrossings < 2) {
3665 : // do not build smooth shape for an unconnected left turn
3666 : // (the walkingArea would get bigger without a reason to
3667 : // walk there)
3668 2651 : } else if ((normalizedLanes[smoothEnd].first->getPermissions() & normalizedLanes[smoothPrev].first->getPermissions() &
3669 : ~(SVC_PEDESTRIAN | SVC_RAIL_CLASSES)) != 0) {
3670 : DEBUGCOUT(gDebugFlag1, " traffic curve\n")
3671 7359 : curve = computeSmoothShape(begShape, endShape, cornerDetail + 2, false, 25, 25, gDebugFlag1 ? this : nullptr);
3672 2453 : if (curve.length2D() - begShape.back().distanceTo2D(endShape.front()) > 5) {
3673 : DEBUGCOUT(gDebugFlag1, " reduceBulge directLength=" << begShape.back().distanceTo2D(endShape.front())
3674 : << " curveLength=" << curve.length2D()
3675 : << " delta=" << curve.length2D() - begShape.back().distanceTo2D(endShape.front())
3676 : << "\n")
3677 72 : curve = computeSmoothShape(begShape, endShape, cornerDetail + 2, false, 25, 25, nullptr, AVOID_WIDE_LEFT_TURN | AVOID_INTERSECTING_LEFT_TURNS);
3678 : }
3679 : } else {
3680 : DEBUGCOUT(gDebugFlag1, " nonTraffic curve\n")
3681 198 : const double extend = MIN2(10.0, begShape.back().distanceTo2D(endShape.front()) / 2);
3682 396 : curve = computeSmoothShape(begShape, endShape, cornerDetail + 2, false, extend, extend, nullptr, FOUR_CONTROL_POINTS);
3683 : }
3684 2741 : if (curve.size() > 2) {
3685 : curve.erase(curve.begin());
3686 : curve.pop_back();
3687 1227 : if (endCrossingWidth > 0) {
3688 : wa.shape.pop_back();
3689 : }
3690 1227 : if (startCrossingWidth > 0) {
3691 : wa.shape.erase(wa.shape.begin());
3692 : }
3693 1227 : if (count == (int)normalizedLanes.size()) {
3694 0 : curve = curve.reverse();
3695 : }
3696 1227 : wa.shape.append(curve, 0);
3697 : }
3698 : DEBUGCOUT(gDebugFlag1, " end=" << smoothEnd << " prev=" << smoothPrev
3699 : << " endCrossingWidth=" << endCrossingWidth << " startCrossingWidth=" << startCrossingWidth
3700 : << " begShape=" << begShape << " endShape=" << endShape << " smooth curve=" << curve
3701 : << " begShapeOuter=" << begShapeOuter << " endShapeOuter=" << endShapeOuter
3702 : << " waShape=" << wa.shape
3703 : << "\n")
3704 : }
3705 2741 : if (curve.size() > 2 && (count == 2 || (count == 1 && numCrossings > 0))) {
3706 1107 : const double innerDist = begShape.back().distanceTo2D(endShape[0]);
3707 1107 : const double outerDist = begShapeOuter.back().distanceTo2D(endShapeOuter[0]);
3708 : DEBUGCOUT(gDebugFlag1, " innerDist=" << innerDist << " outerDist=" << outerDist << "\n")
3709 1107 : if (outerDist > innerDist) {
3710 : // we also need a rounded outer curve (unless we have only a single walkingarea)
3711 100 : const double extend = MIN2(10.0, begShapeOuter.back().distanceTo2D(endShapeOuter.front()) / 2);
3712 200 : curve = computeSmoothShape(begShapeOuter, endShapeOuter, cornerDetail + 2, false, extend, extend, nullptr);
3713 100 : if (curve.length2D() - begShapeOuter.back().distanceTo2D(endShapeOuter.front()) > 5) {
3714 : DEBUGCOUT(gDebugFlag1, " reduceBulge directLength=" << begShapeOuter.back().distanceTo2D(endShapeOuter.front())
3715 : << " curveLength=" << curve.length2D()
3716 : << " delta=" << curve.length2D() - begShapeOuter.back().distanceTo2D(endShapeOuter.front())
3717 : << "\n")
3718 44 : curve = computeSmoothShape(begShapeOuter, endShapeOuter, cornerDetail + 2, false, 25, 25, nullptr, AVOID_WIDE_LEFT_TURN | AVOID_INTERSECTING_LEFT_TURNS);
3719 : }
3720 200 : curve = curve.reverse();
3721 : // keep the points in case of extraShift
3722 100 : if (shiftBegExtra != 0) {
3723 9 : curve.push_front_noDoublePos(wa.shape[1]);
3724 9 : curve.push_back_noDoublePos(wa.shape[2]);
3725 91 : } else if (shiftEndExtra != 0) {
3726 3 : curve.push_back_noDoublePos(wa.shape[1]);
3727 3 : curve.push_back_noDoublePos(wa.shape[2]);
3728 : }
3729 : DEBUGCOUT(gDebugFlag1, " outerCurveRaw=" << curve << " wa1=" << wa.shape[1] << " wa2=" << wa.shape[2] << "\n")
3730 : wa.shape.erase(wa.shape.begin() + 1, wa.shape.begin() + 3);
3731 100 : wa.shape.insert(wa.shape.begin() + 1, curve.begin(), curve.end());
3732 : DEBUGCOUT(gDebugFlag1, " outerCurve=" << curve << "\n")
3733 : }
3734 : }
3735 2741 : }
3736 : // apply custom shapes
3737 4300 : if (myWalkingAreaCustomShapes.size() > 0) {
3738 72 : for (auto wacs : myWalkingAreaCustomShapes) {
3739 : // every edge in wasc.edges must be part of connected
3740 44 : if ((wacs.shape.size() != 0 || wacs.width != NBEdge::UNSPECIFIED_WIDTH) && includes(connected, wacs.edges)) {
3741 5 : if (wacs.shape.size() != 0) {
3742 : wa.shape = wacs.shape;
3743 : }
3744 5 : if (wacs.width != NBEdge::UNSPECIFIED_WIDTH) {
3745 2 : wa.width = wacs.width;
3746 : }
3747 5 : wa.hasCustomShape = true;
3748 : }
3749 : }
3750 : }
3751 : // determine length (average of all possible connections)
3752 : double lengthSum = 0;
3753 : int combinations = 0;
3754 18078 : for (std::vector<Position>::const_iterator it1 = connectedPoints.begin(); it1 != connectedPoints.end(); ++it1) {
3755 64078 : for (std::vector<Position>::const_iterator it2 = connectedPoints.begin(); it2 != connectedPoints.end(); ++it2) {
3756 : const Position& p1 = *it1;
3757 : const Position& p2 = *it2;
3758 : if (p1 != p2) {
3759 36434 : lengthSum += p1.distanceTo2D(p2);
3760 36434 : combinations += 1;
3761 : }
3762 : }
3763 : }
3764 : DEBUGCOUT(gDebugFlag1, " combinations=" << combinations << " connectedPoints=" << connectedPoints << "\n")
3765 4300 : wa.length = POSITION_EPS;
3766 4300 : if (combinations > 0) {
3767 8517 : wa.length = MAX2(POSITION_EPS, lengthSum / combinations);
3768 : }
3769 4300 : myWalkingAreas.push_back(wa);
3770 5188 : }
3771 : // build walkingAreas between split crossings
3772 4202 : std::vector<Crossing*> validCrossings = getCrossings();
3773 5843 : for (std::vector<Crossing*>::iterator it = validCrossings.begin(); it != validCrossings.end(); ++it) {
3774 1641 : Crossing& prev = **it;
3775 1641 : Crossing& next = (it != validCrossings.begin() ? **(it - 1) :** (validCrossings.end() - 1));
3776 : DEBUGCOUT(gDebugFlag1, " checkIntermediate: prev=" << prev.id << " next=" << next.id << " prev.nextWA=" << prev.nextWalkingArea << " next.prevWA=" << next.prevWalkingArea << "\n")
3777 1641 : if (prev.nextWalkingArea == "") {
3778 153 : if (next.prevWalkingArea != "" || &prev == &next) {
3779 12 : WRITE_WARNINGF(TL("Invalid pedestrian topology: crossing '%' across [%] has no target."), prev.id, toString(prev.edges));
3780 4 : prev.valid = false;
3781 4 : continue;
3782 : }
3783 447 : WalkingArea wa(":" + getID() + "_w" + toString(index++), prev.width);
3784 : prev.nextWalkingArea = wa.id;
3785 149 : wa.nextCrossings.push_back(next.id);
3786 : next.prevWalkingArea = wa.id;
3787 : // back of previous crossing
3788 : PositionVector tmp = prev.shape;
3789 149 : tmp.move2side(-prev.width / 2);
3790 149 : wa.shape.push_back(tmp[-1]);
3791 149 : tmp.move2side(prev.width);
3792 149 : wa.shape.push_back(tmp[-1]);
3793 : // front of next crossing
3794 : tmp = next.shape;
3795 149 : tmp.move2side(prev.width / 2);
3796 149 : wa.shape.push_back(tmp[0]);
3797 149 : tmp.move2side(-prev.width);
3798 149 : wa.shape.push_back(tmp[0]);
3799 : wa.refEdges.insert(prev.edges.begin(), prev.edges.end());
3800 : wa.refEdges.insert(next.edges.begin(), next.edges.end());
3801 : // apply custom shapes
3802 149 : if (myWalkingAreaCustomShapes.size() > 0) {
3803 48 : for (auto wacs : myWalkingAreaCustomShapes) {
3804 : // every edge in wacs.edges must be part of crossed
3805 30 : if (wacs.shape.size() != 0 && wacs.edges.size() > 1 && includes(wa.refEdges, wacs.edges)) {
3806 : wa.shape = wacs.shape;
3807 6 : wa.hasCustomShape = true;
3808 : }
3809 : }
3810 : }
3811 : // length (special case)
3812 149 : wa.length = MAX2(POSITION_EPS, prev.shape.back().distanceTo2D(next.shape.front()));
3813 149 : myWalkingAreas.push_back(wa);
3814 : DEBUGCOUT(gDebugFlag1, " build wa=" << wa.id << "\n")
3815 149 : }
3816 : }
3817 4202 : }
3818 :
3819 :
3820 : void
3821 4202 : NBNode::buildCrossingOutlines() {
3822 : #ifdef DEBUG_CROSSING_OUTLINE
3823 : if (myCrossings.size() > 0) {
3824 : std::cerr << "<add>\n";
3825 : }
3826 : #endif
3827 : std::map<std::string, PositionVector> waShapes;
3828 8651 : for (auto wa : myWalkingAreas) {
3829 4449 : waShapes[wa.id] = wa.shape;
3830 4449 : }
3831 5839 : for (auto c : getCrossings()) {
3832 1637 : PositionVector wa1 = waShapes[c->prevWalkingArea];
3833 1637 : PositionVector wa2 = waShapes[c->nextWalkingArea];
3834 1637 : if (wa1.empty() || wa2.empty()) {
3835 : continue;
3836 : }
3837 1636 : wa1.closePolygon();
3838 1636 : wa2.closePolygon();
3839 : PositionVector side1 = c->shape;
3840 1636 : PositionVector side2 = c->shape.reverse();
3841 1636 : side1.move2side(c->width / 2);
3842 1636 : side2.move2side(c->width / 2);
3843 : PositionVector side1default = side1;
3844 : PositionVector side2default = side2;
3845 1636 : side1.extrapolate(POSITION_EPS);
3846 1636 : side2.extrapolate(c->width);
3847 3272 : side1 = cutAtShapes(side1, wa1, wa2, side1default);
3848 3272 : side2 = cutAtShapes(side2, wa1, wa2, side2default);
3849 : PositionVector side1ex = side1;
3850 : PositionVector side2ex = side2;
3851 1636 : side1ex.extrapolate(POSITION_EPS);
3852 1636 : side2ex.extrapolate(side2 == side2default ? c->width / 2 : POSITION_EPS);
3853 1636 : PositionVector side3 = cutAtShapes(wa2, side1ex, side2ex, PositionVector());
3854 1636 : PositionVector side4 = cutAtShapes(wa1, side1ex, side2ex, PositionVector());
3855 : c->outlineShape = side1;
3856 1636 : c->outlineShape.append(side3, POSITION_EPS);
3857 1636 : c->outlineShape.append(side2, POSITION_EPS);
3858 1636 : c->outlineShape.append(side4, POSITION_EPS);
3859 1636 : c->outlineShape.removeDoublePoints();
3860 1636 : if (c->outlineShape.back().almostSame(c->outlineShape.front())) {
3861 : c->outlineShape.pop_back();
3862 : }
3863 : // DEBUG
3864 : #ifdef DEBUG_CROSSING_OUTLINE
3865 : std::cout << " side1=" << side1 << "\n side2=" << side2 << "\n side3=" << side3 << "\n side4=" << side4 << "\n";
3866 : std::cerr << "<poly id=\"" << c->id << "\" shape=\"" << c->outlineShape << "\" color=\"blue\" lineWidth=\"0.2\" layer=\"100\"/>\n";
3867 : #endif
3868 5839 : }
3869 : #ifdef DEBUG_CROSSING_OUTLINE
3870 : if (myCrossings.size() > 0) {
3871 : std::cerr << "</add>\n";
3872 : }
3873 : #endif
3874 4202 : }
3875 :
3876 :
3877 : PositionVector
3878 6544 : NBNode::cutAtShapes(const PositionVector& cut, const PositionVector& border1, const PositionVector& border2, const PositionVector& def) {
3879 6544 : std::vector<double> is1 = cut.intersectsAtLengths2D(border1);
3880 6544 : std::vector<double> is2 = cut.intersectsAtLengths2D(border2);
3881 : #ifdef DEBUG_CROSSING_OUTLINE
3882 : std::cout << "is1=" << is1 << " is2=" << is2 << " cut=" << cut << " border1=" << border1 << " border2=" << border2 << "\n";
3883 : #endif
3884 6544 : if (is1.size() == 0 && border1.size() == 2) {
3885 1050 : const double d1 = cut.distance2D(border1.front());
3886 1050 : const double d2 = cut.distance2D(border1.back());
3887 1050 : Position closer = d1 < d2 ? border1.front() : border1.back();
3888 1050 : double nOp = cut.nearest_offset_to_point2D(closer, false);
3889 : #ifdef DEBUG_CROSSING_OUTLINE
3890 : std::cout << " closer=" << closer << " nOp=" << nOp << "\n";
3891 : #endif
3892 1050 : if (nOp <= 2 * POSITION_EPS && cut.back().distanceTo2D(closer) <= 2 * POSITION_EPS) {
3893 276 : is1.push_back(cut.length2D());
3894 : } else {
3895 774 : is1.push_back(nOp);
3896 : }
3897 : }
3898 6544 : if (is2.size() == 0 && border2.size() == 2) {
3899 848 : const double d1 = cut.distance2D(border2.front());
3900 848 : const double d2 = cut.distance2D(border2.back());
3901 848 : Position closer = d1 < d2 ? border2.front() : border2.back();
3902 848 : double nOp = cut.nearest_offset_to_point2D(closer, false);
3903 848 : if (nOp <= 2 * POSITION_EPS && cut.back().distanceTo2D(closer) <= 2 * POSITION_EPS) {
3904 4 : is2.push_back(cut.length2D());
3905 : } else {
3906 844 : is2.push_back(nOp);
3907 : }
3908 : }
3909 6544 : if (is1.size() > 0 && is2.size() > 0) {
3910 : double of1 = VectorHelper<double>::maxValue(is1);
3911 : double of2 = VectorHelper<double>::minValue(is2);
3912 : #ifdef DEBUG_CROSSING_OUTLINE
3913 : std::cout << " of1=" << of1 << " of2=" << of2 << "\n";
3914 : #endif
3915 4979 : if (of1 > of2) {
3916 : of1 = VectorHelper<double>::maxValue(is2);
3917 : of2 = VectorHelper<double>::minValue(is1);
3918 : #ifdef DEBUG_CROSSING_OUTLINE
3919 : std::cout << " of1=" << of1 << " of2=" << of2 << "\n";
3920 : #endif
3921 : }
3922 4979 : if (of1 > of2) {
3923 : of2 = VectorHelper<double>::maxValue(is1);
3924 : of1 = VectorHelper<double>::minValue(is2);
3925 : #ifdef DEBUG_CROSSING_OUTLINE
3926 : std::cout << " of1=" << of1 << " of2=" << of2 << "\n";
3927 : #endif
3928 : }
3929 : assert(of1 <= of2);
3930 4979 : return cut.getSubpart(of1, of2);
3931 : } else {
3932 : return def;
3933 : }
3934 6544 : }
3935 :
3936 :
3937 : bool
3938 62 : NBNode::includes(const std::set<const NBEdge*, ComparatorIdLess>& super,
3939 : const std::set<const NBEdge*, ComparatorIdLess>& sub) {
3940 : // for some reason std::include does not work reliably
3941 85 : for (const NBEdge* e : sub) {
3942 148 : if (super.count(const_cast<NBEdge*>(e)) == 0) {
3943 : return false;
3944 : }
3945 : }
3946 : return true;
3947 : }
3948 :
3949 :
3950 : bool
3951 6792 : NBNode::crossingBetween(const NBEdge* e1, const NBEdge* e2) const {
3952 6792 : if (e1 == e2) {
3953 : return false;
3954 : }
3955 6740 : if (myAllEdges.size() > 3) {
3956 : // pedestrian scramble
3957 : return false;
3958 : }
3959 2399 : for (auto c : getCrossings()) {
3960 : const EdgeVector& edges = c->edges;
3961 79 : EdgeVector::const_iterator it1 = std::find(edges.begin(), edges.end(), e1);
3962 79 : EdgeVector::const_iterator it2 = std::find(edges.begin(), edges.end(), e2);
3963 79 : if (it1 != edges.end() && it2 != edges.end()) {
3964 6 : return true;
3965 : }
3966 2326 : }
3967 2320 : return false;
3968 : }
3969 :
3970 :
3971 : bool
3972 6001 : NBNode::alreadyConnectedPaths(const NBEdge* e1, const NBEdge* e2, double dist) const {
3973 6001 : if (e1 == e2) {
3974 : return false;
3975 : }
3976 5949 : if (e1->getPermissions() != SVC_PEDESTRIAN
3977 5949 : || e2->getPermissions() != SVC_PEDESTRIAN) {
3978 : // no paths
3979 3995 : return false;
3980 : }
3981 2624 : if (e1->getFinalLength() > dist &&
3982 670 : e2->getFinalLength() > dist) {
3983 : // too long
3984 : return false;
3985 : }
3986 1582 : NBNode* other1 = e1->getFromNode() == this ? e1->getToNode() : e1->getFromNode();
3987 1582 : NBNode* other2 = e2->getFromNode() == this ? e2->getToNode() : e2->getFromNode();
3988 1582 : return other1 == other2;
3989 : }
3990 :
3991 :
3992 : bool
3993 6719 : NBNode::crossesFringe(const NBEdge* e1, const NBEdge* e2) const {
3994 6719 : return myFringeType != FringeType::DEFAULT
3995 19 : && myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1
3996 6738 : && (e1->isTurningDirectionAt(e2) || e2->isTurningDirectionAt(e1));
3997 : }
3998 :
3999 :
4000 : EdgeVector
4001 0 : NBNode::edgesBetween(const NBEdge* e1, const NBEdge* e2) const {
4002 : EdgeVector result;
4003 0 : EdgeVector::const_iterator it = std::find(myAllEdges.begin(), myAllEdges.end(), e1);
4004 : assert(it != myAllEdges.end());
4005 0 : NBContHelper::nextCW(myAllEdges, it);
4006 0 : EdgeVector::const_iterator it_end = std::find(myAllEdges.begin(), myAllEdges.end(), e2);
4007 : assert(it_end != myAllEdges.end());
4008 0 : while (it != it_end) {
4009 0 : result.push_back(*it);
4010 0 : NBContHelper::nextCW(myAllEdges, it);
4011 : }
4012 0 : return result;
4013 0 : }
4014 :
4015 :
4016 : void
4017 11 : NBNode::addWalkingAreaShape(EdgeVector edges, const PositionVector& shape, double width) {
4018 11 : WalkingAreaCustomShape wacs;
4019 : wacs.edges.insert(edges.begin(), edges.end());
4020 : wacs.shape = shape;
4021 11 : wacs.width = width;
4022 11 : myWalkingAreaCustomShapes.push_back(wacs);
4023 11 : }
4024 :
4025 :
4026 : bool
4027 292532 : NBNode::geometryLike() const {
4028 292532 : return geometryLike(myIncomingEdges, myOutgoingEdges);
4029 : }
4030 :
4031 : bool
4032 327769 : NBNode::geometryLike(const EdgeVector& incoming, const EdgeVector& outgoing) {
4033 327769 : if (incoming.size() == 1 && outgoing.size() == 1) {
4034 : return true;
4035 : }
4036 263062 : if (incoming.size() == 2 && outgoing.size() == 2) {
4037 : // check whether the incoming and outgoing edges are pairwise (near) parallel and
4038 : // thus the only cross-connections could be turn-arounds
4039 53947 : NBEdge* in0 = incoming[0];
4040 53947 : NBEdge* in1 = incoming[1];
4041 53947 : NBEdge* out0 = outgoing[0];
4042 53947 : NBEdge* out1 = outgoing[1];
4043 95491 : if ((in0->isTurningDirectionAt(out0) || in0->isTurningDirectionAt(out1))
4044 55629 : && (in1->isTurningDirectionAt(out0) || in1->isTurningDirectionAt(out1))) {
4045 11131 : return true;
4046 : }
4047 42816 : if (in0->getGeometry() == in1->getGeometry() && out0->getGeometry() == out1->getGeometry()) {
4048 : // overlapping edges
4049 : return true;
4050 : }
4051 88952 : for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4052 70112 : NBEdge* inEdge = *it;
4053 70112 : double angle0 = fabs(NBHelpers::relAngle(inEdge->getAngleAtNode(inEdge->getToNode()), out0->getAngleAtNode(out0->getFromNode())));
4054 70112 : double angle1 = fabs(NBHelpers::relAngle(inEdge->getAngleAtNode(inEdge->getToNode()), out1->getAngleAtNode(out1->getFromNode())));
4055 70112 : if (MAX2(angle0, angle1) <= 160) {
4056 : // neither of the outgoing edges is parallel to inEdge
4057 : return false;
4058 : }
4059 : }
4060 : return true;
4061 : }
4062 : return false;
4063 : }
4064 :
4065 : void
4066 444 : NBNode::setRoundabout() {
4067 444 : if (myType == SumoXMLNodeType::RIGHT_BEFORE_LEFT || myType == SumoXMLNodeType::LEFT_BEFORE_RIGHT) {
4068 10 : myType = SumoXMLNodeType::PRIORITY;
4069 : }
4070 444 : }
4071 :
4072 : bool
4073 1419 : NBNode::isRoundabout() const {
4074 5643 : for (NBEdge* out : myOutgoingEdges) {
4075 4242 : if (out->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT) {
4076 : return true;
4077 : }
4078 : }
4079 : return false;
4080 : }
4081 :
4082 : NBNode::Crossing*
4083 2045 : NBNode::addCrossing(EdgeVector edges, double width, bool priority, int tlIndex, int tlIndex2,
4084 : const PositionVector& customShape, bool fromSumoNet, const Parameterised* params) {
4085 2045 : Crossing* c = new Crossing(this, edges, width, priority, tlIndex, tlIndex2, customShape);
4086 2045 : if (params != nullptr) {
4087 145 : c->updateParameters(params->getParametersMap());
4088 : }
4089 2045 : myCrossings.push_back(std::unique_ptr<Crossing>(c));
4090 2045 : if (fromSumoNet) {
4091 145 : myCrossingsLoadedFromSumoNet += 1;
4092 : }
4093 2045 : return c;
4094 : }
4095 :
4096 :
4097 : void
4098 4 : NBNode::removeCrossing(const EdgeVector& edges) {
4099 4 : EdgeSet edgeSet(edges.begin(), edges.end());
4100 20 : for (auto it = myCrossings.begin(); it != myCrossings.end();) {
4101 16 : EdgeSet edgeSet2((*it)->edges.begin(), (*it)->edges.end());
4102 16 : if (edgeSet == edgeSet2) {
4103 4 : it = myCrossings.erase(it);
4104 : } else {
4105 : ++it;
4106 : }
4107 : }
4108 4 : }
4109 :
4110 :
4111 : NBNode::Crossing*
4112 1569 : NBNode::getCrossing(const std::string& id) const {
4113 3424 : for (auto& c : myCrossings) {
4114 3424 : if (c->id == id) {
4115 1569 : return c.get();
4116 : }
4117 : }
4118 0 : throw ProcessError(TLF("Request for unknown crossing '%'", id));
4119 : }
4120 :
4121 :
4122 : NBNode::Crossing*
4123 3 : NBNode::getCrossing(const EdgeVector& edges, bool hardFail) const {
4124 3 : const EdgeSet edgeSet(edges.begin(), edges.end());
4125 13 : for (auto& crossing : myCrossings) {
4126 13 : const EdgeSet edgeSet2(crossing->edges.begin(), crossing->edges.end());
4127 13 : if (edgeSet == edgeSet2) {
4128 : return crossing.get();
4129 : }
4130 : }
4131 0 : if (!hardFail) {
4132 : return nullptr;
4133 : }
4134 0 : throw ProcessError(TL("Request for unknown crossing for the given Edges"));
4135 : }
4136 :
4137 :
4138 : NBNode::WalkingArea&
4139 0 : NBNode::getWalkingArea(const std::string& id) {
4140 0 : for (auto& walkingArea : myWalkingAreas) {
4141 0 : if (walkingArea.id == id) {
4142 : return walkingArea;
4143 : }
4144 : }
4145 : // not found, maybe we need to rebuild
4146 0 : updateSurroundingGeometry();
4147 0 : sortEdges(true);
4148 0 : buildCrossingsAndWalkingAreas();
4149 0 : for (auto& walkingArea : myWalkingAreas) {
4150 0 : if (walkingArea.id == id) {
4151 : return walkingArea;
4152 : }
4153 : }
4154 0 : if (myWalkingAreas.size() > 0) {
4155 : // don't crash
4156 0 : WRITE_WARNINGF("Could not retrieve walkingarea '%' (edge ordering changed after recompute).", id);
4157 0 : return myWalkingAreas.front();
4158 : }
4159 0 : throw ProcessError(TLF("Request for unknown walkingarea '%'.", id));
4160 : }
4161 :
4162 :
4163 : bool
4164 7001 : NBNode::setCrossingTLIndices(const std::string& tlID, int startIndex, bool ignoreCustom) {
4165 : bool usedCustom = false;
4166 8452 : for (auto c : getCrossings()) {
4167 1451 : c->tlLinkIndex = startIndex++;
4168 1451 : c->tlID = tlID;
4169 1451 : if (c->customTLIndex != -1 && !ignoreCustom) {
4170 288 : usedCustom |= (c->tlLinkIndex != c->customTLIndex);
4171 288 : c->tlLinkIndex = c->customTLIndex;
4172 : }
4173 1451 : if (c->customTLIndex2 != -1 && !ignoreCustom) {
4174 : usedCustom = true;
4175 49 : c->tlLinkIndex2 = c->customTLIndex2;
4176 : }
4177 7001 : }
4178 7001 : return usedCustom;
4179 : }
4180 :
4181 :
4182 : int
4183 49804 : NBNode::numNormalConnections() const {
4184 49804 : if (myRequest == nullptr) {
4185 : // could be an uncontrolled type
4186 : int result = 0;
4187 534 : for (const NBEdge* const edge : myIncomingEdges) {
4188 252 : result += (int)edge->getConnections().size();
4189 : }
4190 282 : return result;
4191 : } else {
4192 49522 : return myRequest->getSizes().second;
4193 : }
4194 : }
4195 :
4196 :
4197 : int
4198 229272 : NBNode::getConnectionIndex(const NBEdge* from, const NBEdge::Connection& con) const {
4199 : int result = 0;
4200 464756 : for (const NBEdge* const e : myIncomingEdges) {
4201 1558681 : for (const NBEdge::Connection& cand : e->getConnections()) {
4202 1323197 : if (e == from && cand.fromLane == con.fromLane && cand.toLane == con.toLane && cand.toEdge == con.toEdge) {
4203 : return result;
4204 : }
4205 1093925 : result++;
4206 : }
4207 : }
4208 : return -1;
4209 : }
4210 :
4211 :
4212 : Position
4213 115710 : NBNode::getCenter() const {
4214 : /* Conceptually, the center point would be identical with myPosition.
4215 : * However, if the shape is influenced by custom geometry endpoints of the adjoining edges,
4216 : * myPosition may fall outside the shape. In this case it is better to use
4217 : * the center of the shape
4218 : **/
4219 : PositionVector tmp = myPoly;
4220 115710 : tmp.closePolygon();
4221 : //std::cout << getID() << " around=" << tmp.around(myPosition) << " dist=" << tmp.distance2D(myPosition) << "\n";
4222 115710 : if (tmp.size() < 3 || tmp.around(myPosition) || tmp.distance2D(myPosition) < POSITION_EPS) {
4223 111486 : return myPosition;
4224 : }
4225 4224 : return myPoly.getPolygonCenter();
4226 115710 : }
4227 :
4228 :
4229 : EdgeVector
4230 7584 : NBNode::getEdgesSortedByAngleAtNodeCenter() const {
4231 7584 : EdgeVector result = myAllEdges;
4232 : #ifdef DEBUG_PED_STRUCTURES
4233 : if (gDebugFlag1) {
4234 : std::cout << " angles:\n";
4235 : for (EdgeVector::const_iterator it = result.begin(); it != result.end(); ++it) {
4236 : std::cout << " edge=" << (*it)->getID() << " edgeAngle=" << (*it)->getAngleAtNode(this) << " angleToShape=" << (*it)->getAngleAtNodeToCenter(this) << "\n";
4237 : }
4238 : std::cout << " allEdges before: " << toString(result) << "\n";
4239 : }
4240 : #endif
4241 7584 : sort(result.begin(), result.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
4242 : // let the first edge in myAllEdges remain the first
4243 : DEBUGCOUT(gDebugFlag1, " allEdges sorted: " << toString(result) << "\n")
4244 7584 : rotate(result.begin(), std::find(result.begin(), result.end(), *myAllEdges.begin()), result.end());
4245 : DEBUGCOUT(gDebugFlag1, " allEdges rotated: " << toString(result) << "\n")
4246 7584 : return result;
4247 0 : }
4248 :
4249 :
4250 : void
4251 32529 : NBNode::avoidOverlap() {
4252 : // simple case: edges with LaneSpreadFunction::CENTER and a (possible) turndirection at the same node
4253 : bool haveModifications = false;
4254 85974 : for (EdgeVector::iterator it = myIncomingEdges.begin(); it != myIncomingEdges.end(); it++) {
4255 53445 : NBEdge* edge = *it;
4256 53445 : NBEdge* turnDest = edge->getTurnDestination(true);
4257 53445 : if (turnDest != nullptr) {
4258 33440 : haveModifications |= edge->shiftPositionAtNode(this, turnDest);
4259 33440 : haveModifications |= turnDest->shiftPositionAtNode(this, edge);
4260 : }
4261 : }
4262 32529 : if (haveModifications) {
4263 2371 : NBTurningDirectionsComputer::computeTurnDirectionsForNode(this, false);
4264 : }
4265 : // @todo: edges in the same direction with sharp angles starting/ending at the same position
4266 32529 : }
4267 :
4268 :
4269 : bool
4270 303420 : NBNode::isTrafficLight(SumoXMLNodeType type) {
4271 : return type == SumoXMLNodeType::TRAFFIC_LIGHT
4272 : || type == SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION
4273 303420 : || type == SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED;
4274 : }
4275 :
4276 :
4277 : bool
4278 1285780 : NBNode::extraConflict(int index, int foeIndex) const {
4279 1506962 : for (NBTrafficLightDefinition* def : myTrafficLights) {
4280 221223 : if (def->extraConflict(index, foeIndex)) {
4281 : return true;
4282 : }
4283 : }
4284 : return false;
4285 : }
4286 :
4287 :
4288 : void
4289 190675 : NBNode::sortEdges(bool useNodeShape) {
4290 190675 : if (myAllEdges.size() == 0) {
4291 2557 : return;
4292 : }
4293 188118 : EdgeVector allEdgesOriginal = myAllEdges;
4294 : EdgeVector& allEdges = myAllEdges;
4295 : EdgeVector& incoming = myIncomingEdges;
4296 : EdgeVector& outgoing = myOutgoingEdges;
4297 :
4298 : // sort the edges by angle (this is the canonical sorting)
4299 188118 : std::sort(allEdges.begin(), allEdges.end(), NBNodesEdgesSorter::edge_by_junction_angle_sorter(this));
4300 188118 : std::sort(incoming.begin(), incoming.end(), NBNodesEdgesSorter::edge_by_junction_angle_sorter(this));
4301 188118 : std::sort(outgoing.begin(), outgoing.end(), NBNodesEdgesSorter::edge_by_junction_angle_sorter(this));
4302 : std::vector<NBEdge*>::iterator j;
4303 656769 : for (j = allEdges.begin(); j != allEdges.end() - 1 && j != allEdges.end(); ++j) {
4304 468651 : NBNodesEdgesSorter::swapWhenReversed(this, j, j + 1);
4305 : }
4306 188118 : if (allEdges.size() > 1 && j != allEdges.end()) {
4307 163311 : NBNodesEdgesSorter::swapWhenReversed(this, allEdges.end() - 1, allEdges.begin());
4308 : }
4309 :
4310 : // sort again using additional geometry information
4311 188118 : NBEdge* firstOfAll = allEdges.front();
4312 188118 : NBEdge* firstOfIncoming = incoming.size() > 0 ? incoming.front() : 0;
4313 188118 : NBEdge* firstOfOutgoing = outgoing.size() > 0 ? outgoing.front() : 0;
4314 : // sort by the angle between the node shape center and the point where the edge meets the node shape
4315 188118 : std::sort(allEdges.begin(), allEdges.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
4316 188118 : std::sort(incoming.begin(), incoming.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
4317 188118 : std::sort(outgoing.begin(), outgoing.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
4318 : // let the first edge remain the first
4319 188118 : rotate(allEdges.begin(), std::find(allEdges.begin(), allEdges.end(), firstOfAll), allEdges.end());
4320 188118 : if (firstOfIncoming != nullptr) {
4321 175465 : rotate(incoming.begin(), std::find(incoming.begin(), incoming.end(), firstOfIncoming), incoming.end());
4322 : }
4323 188118 : if (firstOfOutgoing != nullptr) {
4324 172584 : rotate(outgoing.begin(), std::find(outgoing.begin(), outgoing.end(), firstOfOutgoing), outgoing.end());
4325 : }
4326 : #ifdef DEBUG_EDGE_SORTING
4327 : if (DEBUGCOND) {
4328 : std::cout << "sortedEdges (useNodeShape=" << useNodeShape << "):\n";
4329 : for (NBEdge* e : allEdges) {
4330 : std::cout << " " << e->getID()
4331 : << " angleToCenter=" << e->getAngleAtNodeToCenter(this)
4332 : << " junctionAngle=" << e->getAngleAtNode(this) << "\n";
4333 : }
4334 : }
4335 : #endif
4336 :
4337 : // fixing some pathological all edges orderings
4338 : // if every of the edges a,b,c has a turning edge a',b',c' the all edges ordering should be a,a',b,b',c,c'
4339 188118 : if (incoming.size() == outgoing.size() && incoming.front() == allEdges.front()) {
4340 : std::vector<NBEdge*>::const_iterator in, out;
4341 : std::vector<NBEdge*> allTmp;
4342 201995 : for (in = incoming.begin(), out = outgoing.begin(); in != incoming.end(); ++in, ++out) {
4343 157615 : if ((*in)->isTurningDirectionAt(*out)) {
4344 134509 : allTmp.push_back(*in);
4345 134509 : allTmp.push_back(*out);
4346 : } else {
4347 : break;
4348 : }
4349 : }
4350 67486 : if (allTmp.size() == allEdges.size()) {
4351 44380 : allEdges = allTmp;
4352 : }
4353 67486 : }
4354 : // sort the crossings
4355 188118 : std::sort(myCrossings.begin(), myCrossings.end(), NBNodesEdgesSorter::crossing_by_junction_angle_sorter(this, allEdges));
4356 : //if (crossings.size() > 0) {
4357 : // std::cout << " crossings at " << getID() << "\n";
4358 : // for (std::vector<NBNode::Crossing*>::iterator it = crossings.begin(); it != crossings.end(); ++it) {
4359 : // std::cout << " " << toString((*it)->edges) << "\n";
4360 : // }
4361 : //}
4362 :
4363 188118 : if (useNodeShape && myAllEdges != allEdgesOriginal) {
4364 : // sorting order changed after node shape was computed.
4365 404 : computeNodeShape(-1);
4366 3139 : for (NBEdge* e : myAllEdges) {
4367 2735 : e->computeEdgeShape();
4368 : }
4369 : }
4370 188118 : }
4371 :
4372 : std::vector<std::pair<Position, std::string> >
4373 0 : NBNode::getEndPoints() const {
4374 : // using a set would be nicer but we want to have some slack in position identification
4375 : std::vector<std::pair<Position, std::string> >result;
4376 0 : for (NBEdge* e : myAllEdges) {
4377 0 : Position pos = this == e->getFromNode() ? e->getGeometry().front() : e->getGeometry().back();
4378 0 : const std::string origID = e->getParameter(this == e->getFromNode() ? "origFrom" : "origTo");
4379 : bool unique = true;
4380 0 : for (const auto& pair : result) {
4381 0 : if (pos.almostSame(pair.first) || (origID != "" && pair.second == origID)) {
4382 : unique = false;
4383 : break;
4384 : }
4385 : }
4386 0 : if (unique) {
4387 0 : result.push_back(std::make_pair(pos, origID));
4388 : }
4389 : }
4390 0 : return result;
4391 0 : }
4392 :
4393 :
4394 : /****************************************************************************/
|