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