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