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