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