Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netbuild/NBNode.cpp
169665 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
4
// This program and the accompanying materials are made available under the
5
// terms of the Eclipse Public License 2.0 which is available at
6
// https://www.eclipse.org/legal/epl-2.0/
7
// This Source Code may also be made available under the following Secondary
8
// Licenses when the conditions for such availability set forth in the Eclipse
9
// Public License 2.0 are satisfied: GNU General Public License, version 2
10
// or later which is available at
11
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
/****************************************************************************/
14
/// @file NBNode.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Sascha Krieg
18
/// @author Michael Behrisch
19
/// @date Tue, 20 Nov 2001
20
///
21
// The representation of a single node
22
/****************************************************************************/
23
#include <config.h>
24
25
#include <string>
26
#include <map>
27
#include <cassert>
28
#include <algorithm>
29
#include <vector>
30
#include <deque>
31
#include <set>
32
#include <cmath>
33
#include <iterator>
34
#include <utils/common/UtilExceptions.h>
35
#include <utils/common/StringUtils.h>
36
#include <utils/options/OptionsCont.h>
37
#include <utils/geom/GeomHelper.h>
38
#include <utils/common/MsgHandler.h>
39
#include <utils/common/StdDefs.h>
40
#include <utils/common/ToString.h>
41
#include <utils/geom/GeoConvHelper.h>
42
#include <utils/iodevices/OutputDevice.h>
43
#include <iomanip>
44
#include "NBNode.h"
45
#include "NBAlgorithms.h"
46
#include "NBNodeCont.h"
47
#include "NBNodeShapeComputer.h"
48
#include "NBEdgeCont.h"
49
#include "NBTypeCont.h"
50
#include "NBHelpers.h"
51
#include "NBDistrict.h"
52
#include "NBContHelper.h"
53
#include "NBRequest.h"
54
#include "NBOwnTLDef.h"
55
#include "NBLoadedSUMOTLDef.h"
56
#include "NBTrafficLightLogicCont.h"
57
#include "NBTrafficLightDefinition.h"
58
59
// allow to extend a crossing across multiple edges
60
#define EXTEND_CROSSING_ANGLE_THRESHOLD 35.0 // degrees
61
// create intermediate walking areas if either of the following thresholds is exceeded
62
#define SPLIT_CROSSING_WIDTH_THRESHOLD 1.5 // meters
63
#define SPLIT_CROSSING_ANGLE_THRESHOLD 5 // degrees
64
65
// minimum length for a weaving section at a combined on-off ramp
66
#define MIN_WEAVE_LENGTH 20.0
67
68
//#define DEBUG_CONNECTION_GUESSING
69
//#define DEBUG_SMOOTH_GEOM
70
//#define DEBUG_PED_STRUCTURES
71
//#define DEBUG_EDGE_SORTING
72
//#define DEBUG_CROSSING_OUTLINE
73
//#define DEBUGCOND true
74
#define DEBUG_NODE_ID "C"
75
#define DEBUGCOND (getID() == DEBUG_NODE_ID)
76
#define DEBUGCOND2(obj) ((obj != 0 && (obj)->getID() == DEBUG_NODE_ID))
77
#ifdef DEBUG_PED_STRUCTURES
78
#define DEBUGCOUT(cond, msg) DEBUGOUT(cond, msg)
79
#else
80
#define DEBUGCOUT(cond, msg)
81
#endif
82
83
// ===========================================================================
84
// static members
85
// ===========================================================================
86
const int NBNode::FORWARD(1);
87
const int NBNode::BACKWARD(-1);
88
const double NBNode::UNSPECIFIED_RADIUS = -1;
89
const int NBNode::AVOID_WIDE_LEFT_TURN(1);
90
const int NBNode::AVOID_WIDE_RIGHT_TURN(2);
91
const int NBNode::FOUR_CONTROL_POINTS(4);
92
const int NBNode::AVOID_INTERSECTING_LEFT_TURNS(8);
93
const int NBNode::SCURVE_IGNORE(16);
94
const int NBNode::INDIRECT_LEFT(32);
95
96
SVCPermissions NBNode::myHaveRailSignalClasses;
97
SVCPermissions NBNode::myPermitUnsignalizedClasses;
98
99
// ===========================================================================
100
// method definitions
101
// ===========================================================================
102
/* -------------------------------------------------------------------------
103
* NBNode::ApproachingDivider-methods
104
* ----------------------------------------------------------------------- */
105
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
myDiscardAllCrossings(false),
307
myCrossingsLoadedFromSumoNet(0),
308
myDisplacementError(0),
309
myIsBentPriority(false),
310
myTypeWasGuessed(false) {
311
if (!SUMOXMLDefinitions::isValidNetID(myID)) {
312
throw ProcessError(TLF("Invalid node id '%'.", myID));
313
}
314
}
315
316
317
NBNode::NBNode(const std::string& id, const Position& position, NBDistrict* district) :
318
Named(StringUtils::convertUmlaute(id)),
319
myPosition(position),
320
myType(district == nullptr ? SumoXMLNodeType::UNKNOWN : SumoXMLNodeType::DISTRICT),
321
myDistrict(district),
322
myHaveCustomPoly(false),
323
myRequest(nullptr),
324
myRadius(UNSPECIFIED_RADIUS),
325
myKeepClear(OptionsCont::getOptions().getBool("default.junctions.keep-clear")),
326
myRightOfWay(SUMOXMLDefinitions::RightOfWayValues.get(OptionsCont::getOptions().getString("default.right-of-way"))),
327
myFringeType(FringeType::DEFAULT),
328
myDiscardAllCrossings(false),
329
myCrossingsLoadedFromSumoNet(0),
330
myDisplacementError(0),
331
myIsBentPriority(false),
332
myTypeWasGuessed(false) {
333
if (!SUMOXMLDefinitions::isValidNetID(myID)) {
334
throw ProcessError(TLF("Invalid node id '%'.", myID));
335
}
336
}
337
338
339
NBNode::~NBNode() {
340
delete myRequest;
341
}
342
343
344
void
345
NBNode::reinit(const Position& position, SumoXMLNodeType type,
346
bool updateEdgeGeometries) {
347
myPosition = position;
348
// patch type
349
myType = type;
350
if (!isTrafficLight(myType)) {
351
removeTrafficLights();
352
}
353
if (updateEdgeGeometries) {
354
for (EdgeVector::iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
355
PositionVector geom = (*i)->getGeometry();
356
geom[-1] = myPosition;
357
(*i)->setGeometry(geom);
358
}
359
for (EdgeVector::iterator i = myOutgoingEdges.begin(); i != myOutgoingEdges.end(); i++) {
360
PositionVector geom = (*i)->getGeometry();
361
geom[0] = myPosition;
362
(*i)->setGeometry(geom);
363
}
364
}
365
}
366
367
368
369
// ----------- Applying offset
370
void
371
NBNode::reshiftPosition(double xoff, double yoff) {
372
myPosition.add(xoff, yoff, 0);
373
myPoly.add(xoff, yoff, 0);
374
for (auto& wacs : myWalkingAreaCustomShapes) {
375
wacs.shape.add(xoff, yoff, 0);
376
}
377
for (auto& c : myCrossings) {
378
c->customShape.add(xoff, yoff, 0);
379
}
380
}
381
382
383
void
384
NBNode::roundGeometry() {
385
myPosition.round(gPrecision);
386
if (myHaveCustomPoly) {
387
myPoly.round(gPrecision);
388
}
389
for (auto& wacs : myWalkingAreaCustomShapes) {
390
wacs.shape.round(gPrecision);
391
}
392
for (auto& c : myCrossings) {
393
c->customShape.round(gPrecision);
394
}
395
}
396
397
398
void
399
NBNode::mirrorX() {
400
myPosition.mul(1, -1);
401
myPoly.mirrorX();
402
// mirror pre-computed geometry of crossings and walkingareas
403
for (auto& c : myCrossings) {
404
c->customShape.mirrorX();
405
c->shape.mirrorX();
406
}
407
for (auto& wa : myWalkingAreas) {
408
wa.shape.mirrorX();
409
}
410
for (auto& wacs : myWalkingAreaCustomShapes) {
411
wacs.shape.mirrorX();
412
}
413
}
414
415
416
// ----------- Methods for dealing with assigned traffic lights
417
void
418
NBNode::addTrafficLight(NBTrafficLightDefinition* tlDef) {
419
myTrafficLights.insert(tlDef);
420
// rail signals receive a temporary traffic light in order to set connection tl-linkIndex
421
if (!isTrafficLight(myType) && myType != SumoXMLNodeType::RAIL_SIGNAL && myType != SumoXMLNodeType::RAIL_CROSSING) {
422
myType = SumoXMLNodeType::TRAFFIC_LIGHT;
423
}
424
}
425
426
427
void
428
NBNode::removeTrafficLight(NBTrafficLightDefinition* tlDef) {
429
tlDef->removeNode(this);
430
myTrafficLights.erase(tlDef);
431
}
432
433
434
void
435
NBNode::removeTrafficLights(bool setAsPriority) {
436
std::set<NBTrafficLightDefinition*> trafficLights = myTrafficLights; // make a copy because we will modify the original
437
for (std::set<NBTrafficLightDefinition*>::const_iterator i = trafficLights.begin(); i != trafficLights.end(); ++i) {
438
removeTrafficLight(*i);
439
}
440
if (setAsPriority) {
441
myType = myRequest != nullptr ? SumoXMLNodeType::PRIORITY : (
442
myType == SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION ? SumoXMLNodeType::NOJUNCTION : SumoXMLNodeType::DEAD_END);
443
}
444
}
445
446
bool
447
NBNode::hadSignal() const {
448
for (NBEdge* e : getIncomingEdges()) {
449
if (e->getSignalPosition() != Position::INVALID) {
450
return true;
451
}
452
}
453
return false;
454
}
455
456
457
void
458
NBNode::invalidateTLS(NBTrafficLightLogicCont& tlCont, bool addedConnections, bool removedConnections) {
459
if (isTLControlled()) {
460
std::set<NBTrafficLightDefinition*> oldDefs(myTrafficLights);
461
for (std::set<NBTrafficLightDefinition*>::iterator it = oldDefs.begin(); it != oldDefs.end(); ++it) {
462
NBTrafficLightDefinition* orig = *it;
463
if (dynamic_cast<NBLoadedSUMOTLDef*>(orig) != nullptr) {
464
dynamic_cast<NBLoadedSUMOTLDef*>(orig)->registerModifications(addedConnections, removedConnections);
465
} else if (dynamic_cast<NBOwnTLDef*>(orig) == nullptr) {
466
NBTrafficLightDefinition* newDef = new NBOwnTLDef(orig->getID(), orig->getOffset(), orig->getType());
467
const std::vector<NBNode*>& nodes = orig->getNodes();
468
while (!nodes.empty()) {
469
newDef->addNode(nodes.front());
470
nodes.front()->removeTrafficLight(orig);
471
}
472
tlCont.removeFully(orig->getID());
473
tlCont.insert(newDef);
474
}
475
}
476
}
477
}
478
479
480
void
481
NBNode::shiftTLConnectionLaneIndex(NBEdge* edge, int offset, int threshold) {
482
for (std::set<NBTrafficLightDefinition*>::iterator it = myTrafficLights.begin(); it != myTrafficLights.end(); ++it) {
483
(*it)->shiftTLConnectionLaneIndex(edge, offset, threshold);
484
}
485
}
486
487
// ----------- Prunning the input
488
int
489
NBNode::removeSelfLoops(NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tc) {
490
int ret = 0;
491
int pos = 0;
492
EdgeVector::const_iterator j = myIncomingEdges.begin();
493
while (j != myIncomingEdges.end()) {
494
// skip edges which are only incoming and not outgoing
495
if (find(myOutgoingEdges.begin(), myOutgoingEdges.end(), *j) == myOutgoingEdges.end()) {
496
++j;
497
++pos;
498
continue;
499
}
500
// an edge with both its origin and destination being the current
501
// node should be removed
502
NBEdge* dummy = *j;
503
WRITE_WARNINGF(TL(" Removing self-looping edge '%'"), dummy->getID());
504
// get the list of incoming edges connected to the self-loop
505
EdgeVector incomingConnected = dummy->getIncomingEdges();
506
// get the list of outgoing edges connected to the self-loop
507
EdgeVector outgoingConnected = dummy->getConnectedEdges();
508
// let the self-loop remap its connections
509
dummy->remapConnections(incomingConnected);
510
remapRemoved(tc, dummy, incomingConnected, outgoingConnected);
511
// delete the self-loop
512
ec.erase(dc, dummy);
513
j = myIncomingEdges.begin() + pos;
514
++ret;
515
}
516
return ret;
517
}
518
519
520
// -----------
521
void
522
NBNode::addIncomingEdge(NBEdge* edge) {
523
assert(edge != 0);
524
if (find(myIncomingEdges.begin(), myIncomingEdges.end(), edge) == myIncomingEdges.end()) {
525
myIncomingEdges.push_back(edge);
526
myAllEdges.push_back(edge);
527
}
528
}
529
530
531
void
532
NBNode::addOutgoingEdge(NBEdge* edge) {
533
assert(edge != 0);
534
if (find(myOutgoingEdges.begin(), myOutgoingEdges.end(), edge) == myOutgoingEdges.end()) {
535
myOutgoingEdges.push_back(edge);
536
myAllEdges.push_back(edge);
537
}
538
}
539
540
541
bool
542
NBNode::isSimpleContinuation(bool checkLaneNumbers, bool checkWidth) const {
543
// one in, one out->continuation
544
if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1) {
545
NBEdge* in = myIncomingEdges.front();
546
NBEdge* out = myOutgoingEdges.front();
547
// both must have the same number of lanes
548
return ((!checkLaneNumbers || in->getNumLanes() == out->getNumLanes())
549
&& (!checkWidth || in->getTotalWidth() == out->getTotalWidth()));
550
}
551
// two in and two out and both in reverse direction
552
if (myIncomingEdges.size() == 2 && myOutgoingEdges.size() == 2) {
553
for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
554
NBEdge* in = *i;
555
EdgeVector::const_iterator opposite = find_if(myOutgoingEdges.begin(), myOutgoingEdges.end(), NBContHelper::opposite_finder(in));
556
// must have an opposite edge
557
if (opposite == myOutgoingEdges.end()) {
558
return false;
559
}
560
// both must have the same number of lanes
561
NBContHelper::nextCW(myOutgoingEdges, opposite);
562
if (checkLaneNumbers && in->getNumLanes() != (*opposite)->getNumLanes()) {
563
return false;
564
}
565
if (checkWidth && in->getTotalWidth() != (*opposite)->getTotalWidth()) {
566
return false;
567
}
568
}
569
return true;
570
}
571
// nope
572
return false;
573
}
574
575
576
PositionVector
577
NBNode::computeSmoothShape(const PositionVector& begShape,
578
const PositionVector& endShape,
579
int numPoints,
580
bool isTurnaround,
581
double extrapolateBeg,
582
double extrapolateEnd,
583
NBNode* recordError,
584
int shapeFlag) const {
585
586
bool ok = true;
587
if ((shapeFlag & INDIRECT_LEFT) != 0) {
588
return indirectLeftShape(begShape, endShape, numPoints);
589
}
590
PositionVector init = bezierControlPoints(begShape, endShape, isTurnaround, extrapolateBeg, extrapolateEnd, ok, recordError, DEG2RAD(5), shapeFlag);
591
#ifdef DEBUG_SMOOTH_GEOM
592
if (DEBUGCOND) {
593
std::cout << "computeSmoothShape node " << getID() << " begShape=" << begShape << " endShape=" << endShape << " init=" << init << " shapeFlag=" << shapeFlag << "\n";
594
}
595
#endif
596
if (init.size() == 0) {
597
PositionVector ret;
598
ret.push_back(begShape.back());
599
ret.push_back(endShape.front());
600
return ret;
601
} else {
602
return init.bezier(numPoints).smoothedZFront();
603
}
604
}
605
606
PositionVector
607
NBNode::bezierControlPoints(
608
const PositionVector& begShape,
609
const PositionVector& endShape,
610
bool isTurnaround,
611
double extrapolateBeg,
612
double extrapolateEnd,
613
bool& ok,
614
NBNode* recordError,
615
double straightThresh,
616
int shapeFlag) {
617
618
const Position beg = begShape.back();
619
const Position end = endShape.front();
620
const double dist = beg.distanceTo2D(end);
621
PositionVector init;
622
if (dist < POSITION_EPS || beg.distanceTo2D(begShape[-2]) < POSITION_EPS || end.distanceTo2D(endShape[1]) < POSITION_EPS) {
623
#ifdef DEBUG_SMOOTH_GEOM
624
if (DEBUGCOND2(recordError)) std::cout << " bezierControlPoints failed beg=" << beg << " end=" << end
625
<< " dist=" << dist
626
<< " distBegLast=" << beg.distanceTo2D(begShape[-2])
627
<< " distEndFirst=" << end.distanceTo2D(endShape[1])
628
<< "\n";
629
#endif
630
// typically, this node a is a simpleContinuation. see also #2539
631
return init;
632
} else {
633
init.push_back(beg);
634
if (isTurnaround) {
635
// turnarounds:
636
// - end of incoming lane
637
// - position between incoming/outgoing end/begin shifted by the distance orthogonally
638
// - begin of outgoing lane
639
Position center = PositionVector::positionAtOffset2D(beg, end, beg.distanceTo2D(end) / (double) 2.);
640
center.sub(beg.y() - end.y(), end.x() - beg.x());
641
init.push_back(center);
642
} else {
643
const double EXT = 100;
644
const double angle = GeomHelper::angleDiff(begShape.angleAt2D(-2), endShape.angleAt2D(0));
645
PositionVector endShapeBegLine(endShape[0], endShape[1]);
646
PositionVector begShapeEndLineRev(begShape[-1], begShape[-2]);
647
endShapeBegLine.extrapolate2D(EXT, true);
648
begShapeEndLineRev.extrapolate2D(EXT, true);
649
#ifdef DEBUG_SMOOTH_GEOM
650
if (DEBUGCOND2(recordError)) std::cout
651
<< " endShapeBegLine=" << endShapeBegLine
652
<< " begShapeEndLineRev=" << begShapeEndLineRev
653
<< " angle=" << RAD2DEG(angle) << "\n";
654
#endif
655
if (fabs(angle) < M_PI / 4.) {
656
// very low angle: could be an s-shape or a straight line
657
const double displacementAngle = GeomHelper::angleDiff(begShape.angleAt2D(-2), beg.angleTo2D(end));
658
const double bendDeg = RAD2DEG(fabs(displacementAngle - angle));
659
const double halfDistance = dist / 2;
660
if (fabs(displacementAngle) <= straightThresh && fabs(angle) <= straightThresh) {
661
#ifdef DEBUG_SMOOTH_GEOM
662
if (DEBUGCOND2(recordError)) std::cout << " bezierControlPoints identified straight line beg=" << beg << " end=" << end
663
<< " angle=" << RAD2DEG(angle) << " displacementAngle=" << RAD2DEG(displacementAngle) << "\n";
664
#endif
665
return PositionVector();
666
} else if (bendDeg > 22.5 && pow(bendDeg / 45, 2) / dist > 0.13) {
667
// do not allow s-curves with extreme bends
668
// (a linear dependency is to restrictive at low displacementAngles and too permisive at high angles)
669
#ifdef DEBUG_SMOOTH_GEOM
670
if (DEBUGCOND2(recordError)) std::cout << " bezierControlPoints found extreme s-curve, falling back to straight line beg=" << beg << " end=" << end
671
<< " angle=" << RAD2DEG(angle) << " displacementAngle=" << RAD2DEG(displacementAngle)
672
<< " dist=" << dist << " bendDeg=" << bendDeg << " bd2=" << pow(bendDeg / 45, 2)
673
<< " displacementError=" << sin(displacementAngle) * dist
674
<< " begShape=" << begShape << " endShape=" << endShape << "\n";
675
#endif
676
ok = false;
677
if (recordError != nullptr && (shapeFlag & SCURVE_IGNORE) == 0) {
678
recordError->myDisplacementError = MAX2(recordError->myDisplacementError, (double)fabs(sin(displacementAngle) * dist));
679
}
680
return PositionVector();
681
} else {
682
const double endLength = begShape[-2].distanceTo2D(begShape[-1]);
683
const double off1 = endLength + MIN2(extrapolateBeg, halfDistance);
684
init.push_back(PositionVector::positionAtOffset2D(begShapeEndLineRev[1], begShapeEndLineRev[0], off1));
685
const double off2 = EXT - MIN2(extrapolateEnd, halfDistance);
686
init.push_back(PositionVector::positionAtOffset2D(endShapeBegLine[0], endShapeBegLine[1], off2));
687
#ifdef DEBUG_SMOOTH_GEOM
688
if (DEBUGCOND2(recordError)) std::cout << " bezierControlPoints found s-curve beg=" << beg << " end=" << end
689
<< " angle=" << RAD2DEG(angle) << " displacementAngle=" << RAD2DEG(displacementAngle)
690
<< " halfDistance=" << halfDistance << "\n";
691
#endif
692
}
693
} else {
694
// turning
695
// - end of incoming lane
696
// - intersection of the extrapolated lanes
697
// - begin of outgoing lane
698
// attention: if there is no intersection, use a straight line
699
Position intersect = endShapeBegLine.intersectionPosition2D(begShapeEndLineRev);
700
if (intersect == Position::INVALID) {
701
#ifdef DEBUG_SMOOTH_GEOM
702
if (DEBUGCOND2(recordError)) {
703
std::cout << " bezierControlPoints failed beg=" << beg << " end=" << end << " intersect=" << intersect
704
<< " endShapeBegLine=" << endShapeBegLine
705
<< " begShapeEndLineRev=" << begShapeEndLineRev
706
<< "\n";
707
}
708
#endif
709
ok = false;
710
if (recordError != nullptr && (shapeFlag & SCURVE_IGNORE) == 0) {
711
// it's unclear if this error can be solved via stretching the intersection.
712
recordError->myDisplacementError = MAX2(recordError->myDisplacementError, (double)1.0);
713
}
714
return PositionVector();
715
}
716
const double begOffset = begShapeEndLineRev.nearest_offset_to_point2D(intersect);
717
const double endOffset = endShapeBegLine.nearest_offset_to_point2D(intersect);
718
/*
719
if ((shapeFlag & FOUR_CONTROL_POINTS) == 0 && (begOffset >= EXT || endOffset >= EXT)) {
720
// intersection point lies within begShape / endShape so we cannot use it
721
if (dist < 2) {
722
return PositionVector();
723
}
724
shapeFlag |= FOUR_CONTROL_POINTS;
725
extrapolateBeg = MIN2(10.0, dist / 2);
726
extrapolateEnd = extrapolateBeg;
727
}
728
*/
729
const double minControlLength = MIN2((double)1.0, dist / 2);
730
const double distBeg = intersect.distanceTo2D(beg);
731
const double distEnd = intersect.distanceTo2D(end);
732
const bool lengthenBeg = distBeg <= minControlLength;
733
const bool lengthenEnd = distEnd <= minControlLength;
734
#ifdef DEBUG_SMOOTH_GEOM
735
if (DEBUGCOND2(recordError)) std::cout
736
<< " beg=" << beg << " end=" << end << " intersect=" << intersect
737
<< " distBeg=" << distBeg << " distEnd=" << distEnd
738
<< " begOffset=" << begOffset << " endOffset=" << endOffset
739
<< " lEnd=" << lengthenEnd << " lBeg=" << lengthenBeg
740
<< "\n";
741
#endif
742
if (lengthenBeg && lengthenEnd) {
743
#ifdef DEBUG_SMOOTH_GEOM
744
if (DEBUGCOND2(recordError)) {
745
std::cout << " bezierControlPoints failed\n";
746
}
747
#endif
748
if (recordError != nullptr && (shapeFlag & SCURVE_IGNORE) == 0) {
749
// This should be fixable with minor stretching
750
recordError->myDisplacementError = MAX2(recordError->myDisplacementError, (double)1.0);
751
}
752
ok = false;
753
return PositionVector();
754
} else if ((shapeFlag & FOUR_CONTROL_POINTS)) {
755
init.push_back(begShapeEndLineRev.positionAtOffset2D(EXT - extrapolateBeg));
756
init.push_back(endShapeBegLine.positionAtOffset2D(EXT - extrapolateEnd));
757
} else if (lengthenBeg || lengthenEnd) {
758
init.push_back(begShapeEndLineRev.positionAtOffset2D(EXT - minControlLength));
759
init.push_back(endShapeBegLine.positionAtOffset2D(EXT - minControlLength));
760
} else if ((shapeFlag & AVOID_WIDE_LEFT_TURN) != 0
761
// there are two reasons for enabling special geometry rules:
762
// 1) sharp edge angles which could cause overshoot
763
// 2) junction geometries with a large displacement between opposite left turns
764
// which would cause the default geometry to overlap
765
&& ((shapeFlag & AVOID_INTERSECTING_LEFT_TURNS) != 0
766
|| (angle > DEG2RAD(95) && (distBeg > 20 || distEnd > 20)))) {
767
//std::cout << " bezierControlPoints intersect=" << intersect << " dist=" << dist << " distBeg=" << distBeg << " distEnd=" << distEnd << " angle=" << RAD2DEG(angle) << " flag=" << shapeFlag << "\n";
768
const double factor = ((shapeFlag & AVOID_INTERSECTING_LEFT_TURNS) == 0 ? 1
769
: MIN2(0.6, 16 / dist));
770
init.push_back(begShapeEndLineRev.positionAtOffset2D(EXT - MIN2(distBeg * factor / 1.2, dist * factor / 1.8)));
771
init.push_back(endShapeBegLine.positionAtOffset2D(EXT - MIN2(distEnd * factor / 1.2, dist * factor / 1.8)));
772
} else if ((shapeFlag & AVOID_WIDE_RIGHT_TURN) != 0 && angle < DEG2RAD(-95) && (distBeg > 20 || distEnd > 20)) {
773
//std::cout << " bezierControlPoints intersect=" << intersect << " distBeg=" << distBeg << " distEnd=" << distEnd << "\n";
774
init.push_back(begShapeEndLineRev.positionAtOffset2D(EXT - MIN2(distBeg / 1.4, dist / 2)));
775
init.push_back(endShapeBegLine.positionAtOffset2D(EXT - MIN2(distEnd / 1.4, dist / 2)));
776
} else {
777
double z;
778
const double z1 = begShapeEndLineRev.positionAtOffset2D(begOffset).z();
779
const double z2 = endShapeBegLine.positionAtOffset2D(endOffset).z();
780
const double z3 = 0.5 * (beg.z() + end.z());
781
// if z1 and z2 are on the same side in regard to z3 then we
782
// can use their avarage. Otherwise, the intersection in 3D
783
// is not good and we are better of using z3
784
if ((z1 <= z3 && z2 <= z3) || (z1 >= z3 && z2 >= z3)) {
785
z = 0.5 * (z1 + z2);
786
} else {
787
z = z3;
788
}
789
intersect.set(intersect.x(), intersect.y(), z);
790
init.push_back(intersect);
791
}
792
}
793
}
794
init.push_back(end);
795
}
796
return init;
797
}
798
799
PositionVector
800
NBNode::indirectLeftShape(const PositionVector& begShape, const PositionVector& endShape, int numPoints) const {
801
UNUSED_PARAMETER(numPoints);
802
PositionVector result;
803
result.push_back(begShape.back());
804
//const double angle = GeomHelper::angleDiff(begShape.angleAt2D(-2), endShape.angleAt2D(0));
805
PositionVector endShapeBegLine(endShape[0], endShape[1]);
806
PositionVector begShapeEndLineRev(begShape[-1], begShape[-2]);
807
endShapeBegLine.extrapolate2D(100, true);
808
begShapeEndLineRev.extrapolate2D(100, true);
809
Position intersect = endShapeBegLine.intersectionPosition2D(begShapeEndLineRev);
810
if (intersect == Position::INVALID) {
811
WRITE_WARNINGF(TL("Could not compute indirect left turn shape at node '%'"), getID());
812
} else {
813
Position dir = intersect;
814
dir.sub(endShape[0]);
815
dir.norm2D();
816
const double radius = myRadius == NBNode::UNSPECIFIED_RADIUS ? OptionsCont::getOptions().getFloat("default.junctions.radius") : myRadius;
817
dir.mul(radius);
818
result.push_back(intersect + dir);
819
}
820
result.push_back(endShape.front());
821
return result;
822
}
823
824
PositionVector
825
NBNode::computeInternalLaneShape(const NBEdge* fromE, const NBEdge::Connection& con, int numPoints, NBNode* recordError, int shapeFlag) const {
826
if (con.fromLane >= fromE->getNumLanes()) {
827
throw ProcessError(TLF("Connection '%' starts at a non-existant lane.", con.getDescription(fromE)));
828
}
829
if (con.toLane >= con.toEdge->getNumLanes()) {
830
throw ProcessError(TLF("Connection '%' targets a non-existant lane.", con.getDescription(fromE)));
831
}
832
PositionVector fromShape = fromE->getLaneShape(con.fromLane);
833
PositionVector toShape = con.toEdge->getLaneShape(con.toLane);
834
PositionVector ret;
835
bool useCustomShape = con.customShape.size() > 0;
836
if (useCustomShape) {
837
// ensure that the shape starts and ends at the intersection boundary
838
PositionVector startBorder = fromE->getNodeBorder(this);
839
if (startBorder.size() == 0) {
840
startBorder = fromShape.getOrthogonal(fromShape.back(), 1, true);
841
}
842
PositionVector tmp = NBEdge::startShapeAt(con.customShape, this, startBorder);
843
if (tmp.size() < 2) {
844
WRITE_WARNINGF(TL("Could not use custom shape for connection %."), con.getDescription(fromE));
845
useCustomShape = false;
846
} else {
847
if (tmp.length2D() > con.customShape.length2D() + POSITION_EPS) {
848
// shape was lengthened at the start, make sure it attaches at the center of the lane
849
tmp[0] = fromShape.back();
850
} else if (recordError != nullptr) {
851
const double offset = tmp[0].distanceTo2D(fromShape.back());
852
if (offset > fromE->getLaneWidth(con.fromLane) / 2) {
853
WRITE_WARNINGF(TL("Custom shape has distance % to incoming lane for connection %."), offset, con.getDescription(fromE));
854
}
855
}
856
PositionVector endBorder = con.toEdge->getNodeBorder(this);
857
if (endBorder.size() == 0) {
858
endBorder = toShape.getOrthogonal(toShape.front(), 1, false);
859
}
860
ret = NBEdge::startShapeAt(tmp.reverse(), this, endBorder).reverse();
861
if (ret.size() < 2) {
862
WRITE_WARNINGF(TL("Could not use custom shape for connection %."), con.getDescription(fromE));
863
useCustomShape = false;
864
} else if (ret.length2D() > tmp.length2D() + POSITION_EPS) {
865
// shape was lengthened at the end, make sure it attaches at the center of the lane
866
ret[-1] = toShape.front();
867
} else if (recordError != nullptr) {
868
const double offset = ret[-1].distanceTo2D(toShape.front());
869
if (offset > con.toEdge->getLaneWidth(con.toLane) / 2) {
870
WRITE_WARNINGF(TL("Custom shape has distance % to outgoing lane for connection %."), offset, con.getDescription(fromE));
871
}
872
}
873
}
874
}
875
if (!useCustomShape) {
876
displaceShapeAtWidthChange(fromE, con, fromShape, toShape);
877
double extrapolateBeg = 5. * fromE->getNumLanes();
878
double extrapolateEnd = 5. * con.toEdge->getNumLanes();
879
LinkDirection dir = getDirection(fromE, con.toEdge);
880
if (dir == LinkDirection::LEFT || dir == LinkDirection::TURN) {
881
shapeFlag += AVOID_WIDE_LEFT_TURN;
882
}
883
if (con.indirectLeft) {
884
shapeFlag += INDIRECT_LEFT;
885
}
886
#ifdef DEBUG_SMOOTH_GEOM
887
if (DEBUGCOND) {
888
std::cout << "computeInternalLaneShape node " << getID() << " fromE=" << fromE->getID() << " toE=" << con.toEdge->getID() << "\n";
889
}
890
#endif
891
ret = computeSmoothShape(fromShape, toShape,
892
numPoints, fromE->getTurnDestination() == con.toEdge,
893
extrapolateBeg, extrapolateEnd, recordError, shapeFlag);
894
}
895
const NBEdge::Lane& lane = fromE->getLaneStruct(con.fromLane);
896
if (lane.endOffset > 0) {
897
PositionVector beg = lane.shape.getSubpart(lane.shape.length() - lane.endOffset, lane.shape.length());
898
beg.append(ret);
899
ret = beg;
900
}
901
if (con.toEdge->isBidiRail() && con.toEdge->getTurnDestination(true)->getEndOffset() > 0) {
902
PositionVector end = toShape.getSubpart(0, con.toEdge->getTurnDestination(true)->getEndOffset());
903
ret.append(end);
904
}
905
return ret;
906
}
907
908
909
bool
910
NBNode::isConstantWidthTransition() const {
911
return (myIncomingEdges.size() == 1
912
&& myOutgoingEdges.size() == 1
913
&& myIncomingEdges[0]->getNumLanes() != myOutgoingEdges[0]->getNumLanes()
914
&& myIncomingEdges[0]->getTotalWidth() == myOutgoingEdges[0]->getTotalWidth());
915
}
916
917
void
918
NBNode::displaceShapeAtWidthChange(const NBEdge* from, const NBEdge::Connection& con,
919
PositionVector& fromShape, PositionVector& toShape) const {
920
if (isConstantWidthTransition()) {
921
// displace shapes
922
NBEdge* in = myIncomingEdges[0];
923
NBEdge* out = myOutgoingEdges[0];
924
double outCenter = out->getLaneWidth(con.toLane) / 2;
925
for (int i = 0; i < con.toLane; ++i) {
926
outCenter += out->getLaneWidth(i);
927
}
928
double inCenter = in->getLaneWidth(con.fromLane) / 2;
929
for (int i = 0; i < con.fromLane; ++i) {
930
inCenter += in->getLaneWidth(i);
931
}
932
//std::cout << "displaceShapeAtWidthChange inCenter=" << inCenter << " outCenter=" << outCenter << "\n";
933
try {
934
if (in->getNumLanes() > out->getNumLanes()) {
935
// shift toShape so the internal lane ends straight at the displaced entry point
936
toShape.move2side(outCenter - inCenter);
937
} else {
938
// shift fromShape so the internal lane starts straight at the displaced exit point
939
fromShape.move2side(inCenter - outCenter);
940
941
}
942
} catch (InvalidArgument&) { }
943
} else {
944
SVCPermissions fromP = from->getPermissions(con.fromLane);
945
SVCPermissions toP = con.toEdge->getPermissions(con.toLane);
946
if ((fromP & toP) == SVC_BICYCLE && (fromP | toP) != SVC_BICYCLE) {
947
double shift = (from->getLaneWidth(con.fromLane) - con.toEdge->getLaneWidth(con.toLane)) / 2;
948
if (toP == SVC_BICYCLE) {
949
// let connection to dedicated bicycle lane start on the right side of a mixed lane for straight an right-going connections
950
// (on the left side for left turns)
951
// XXX indirect left turns should also start on the right side
952
LinkDirection dir = getDirection(from, con.toEdge);
953
if ((dir == LinkDirection::LEFT) || (dir == LinkDirection::PARTLEFT) || (dir == LinkDirection::TURN)) {
954
fromShape.move2side(-shift);
955
} else {
956
fromShape.move2side(shift);
957
}
958
} else if (fromP == SVC_BICYCLE) {
959
// let connection from dedicated bicycle end on the right side of a mixed lane
960
toShape.move2side(-shift);
961
}
962
}
963
}
964
}
965
966
bool
967
NBNode::needsCont(const NBEdge* fromE, const NBEdge* otherFromE,
968
const NBEdge::Connection& c, const NBEdge::Connection& otherC, bool checkOnlyTLS) const {
969
const NBEdge* toE = c.toEdge;
970
const NBEdge* otherToE = otherC.toEdge;
971
972
if (!checkOnlyTLS) {
973
if (myType == SumoXMLNodeType::RIGHT_BEFORE_LEFT
974
|| myType == SumoXMLNodeType::LEFT_BEFORE_RIGHT
975
|| myType == SumoXMLNodeType::ALLWAY_STOP) {
976
return false;
977
}
978
LinkDirection d1 = getDirection(fromE, toE);
979
const bool thisRight = (d1 == LinkDirection::RIGHT || d1 == LinkDirection::PARTRIGHT);
980
const bool rightTurnConflict = (thisRight &&
981
NBNode::rightTurnConflict(fromE, toE, c.fromLane, otherFromE, otherToE, otherC.fromLane));
982
if (thisRight && !rightTurnConflict) {
983
return false;
984
}
985
if (myRequest && myRequest->indirectLeftTurnConflict(fromE, c, otherFromE, otherC, false)) {
986
return true;
987
}
988
if (!(foes(otherFromE, otherToE, fromE, toE) || myRequest == nullptr || rightTurnConflict)) {
989
// if they do not cross, no waiting place is needed
990
return false;
991
}
992
LinkDirection d2 = getDirection(otherFromE, otherToE);
993
if (d2 == LinkDirection::TURN) {
994
return false;
995
}
996
if (fromE == otherFromE && !thisRight) {
997
// ignore same edge links except for right-turns
998
return false;
999
}
1000
if (thisRight && d2 != LinkDirection::STRAIGHT) {
1001
return false;
1002
}
1003
}
1004
if (c.tlID != "") {
1005
assert(myTrafficLights.size() > 0 || myType == SumoXMLNodeType::RAIL_CROSSING || myType == SumoXMLNodeType::RAIL_SIGNAL);
1006
for (std::set<NBTrafficLightDefinition*>::const_iterator it = myTrafficLights.begin(); it != myTrafficLights.end(); ++it) {
1007
if ((*it)->needsCont(fromE, toE, otherFromE, otherToE)) {
1008
return true;
1009
}
1010
}
1011
return false;
1012
}
1013
if (fromE->getJunctionPriority(this) > 0 && otherFromE->getJunctionPriority(this) > 0) {
1014
return mustBrake(fromE, toE, c.fromLane, c.toLane, false);
1015
}
1016
return false;
1017
}
1018
1019
bool
1020
NBNode::tlsStrandedConflict(const NBEdge* from, const NBEdge::Connection& c,
1021
const NBEdge* foeFrom, const NBEdge::Connection& foe) const {
1022
return (foe.haveVia && isTLControlled() && c.tlLinkIndex >= 0 && foe.tlLinkIndex >= 0
1023
&& !foeFrom->isTurningDirectionAt(foe.toEdge)
1024
&& foes(from, c.toEdge, foeFrom, foe.toEdge)
1025
&& !needsCont(foeFrom, from, foe, c, true));
1026
}
1027
1028
1029
void
1030
NBNode::removeJoinedTrafficLights() {
1031
std::set<NBTrafficLightDefinition*> trafficLights = myTrafficLights; // make a copy because we will modify the original
1032
for (std::set<NBTrafficLightDefinition*>::const_iterator i = trafficLights.begin(); i != trafficLights.end(); ++i) {
1033
// if this is the only controlled node we keep the tlDef as it is to generate a warning later
1034
if ((*i)->getNodes().size() > 1) {
1035
myTrafficLights.erase(*i);
1036
(*i)->removeNode(this);
1037
(*i)->setParticipantsInformation();
1038
(*i)->setTLControllingInformation();
1039
}
1040
}
1041
}
1042
1043
1044
void
1045
NBNode::computeLogic(const NBEdgeCont& ec) {
1046
delete myRequest; // possibly recomputation step
1047
myRequest = nullptr;
1048
if (myIncomingEdges.size() == 0 || myOutgoingEdges.size() == 0) {
1049
// no logic if nothing happens here
1050
myType = SumoXMLNodeType::DEAD_END;
1051
removeJoinedTrafficLights();
1052
return;
1053
}
1054
// compute the logic if necessary or split the junction
1055
if (myType != SumoXMLNodeType::NOJUNCTION && myType != SumoXMLNodeType::DISTRICT && myType != SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION) {
1056
// build the request
1057
myRequest = new NBRequest(ec, this, myAllEdges, myIncomingEdges, myOutgoingEdges, myBlockedConnections);
1058
// check whether it is not too large
1059
int numConnections = numNormalConnections();
1060
if (numConnections >= SUMO_MAX_CONNECTIONS) {
1061
// yep -> make it untcontrolled, warn
1062
delete myRequest;
1063
myRequest = nullptr;
1064
if (myType == SumoXMLNodeType::TRAFFIC_LIGHT) {
1065
myType = SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION;
1066
} else {
1067
myType = SumoXMLNodeType::NOJUNCTION;
1068
}
1069
WRITE_WARNINGF(TL("Junction '%' is too complicated (% connections, max %); will be set to %."),
1070
getID(), numConnections, SUMO_MAX_CONNECTIONS, toString(myType));
1071
} else if (numConnections == 0) {
1072
delete myRequest;
1073
myRequest = nullptr;
1074
myType = SumoXMLNodeType::DEAD_END;
1075
removeJoinedTrafficLights();
1076
} else {
1077
myRequest->buildBitfieldLogic();
1078
}
1079
}
1080
}
1081
1082
1083
void
1084
NBNode::computeLogic2(bool checkLaneFoes) {
1085
if (myRequest != nullptr) {
1086
myRequest->computeLogic(checkLaneFoes);
1087
}
1088
}
1089
1090
void
1091
NBNode::computeKeepClear() {
1092
if (hasConflict()) {
1093
if (!myKeepClear) {
1094
for (NBEdge* incoming : myIncomingEdges) {
1095
std::vector<NBEdge::Connection>& connections = incoming->getConnections();
1096
for (NBEdge::Connection& c : connections) {
1097
c.keepClear = KEEPCLEAR_FALSE;
1098
}
1099
}
1100
} else if (geometryLike() && myCrossings.size() == 0 && !isTLControlled()) {
1101
int linkIndex = 0;
1102
for (NBEdge* incoming : myIncomingEdges) {
1103
std::vector<NBEdge::Connection>& connections = incoming->getConnections();
1104
for (NBEdge::Connection& c : connections) {
1105
if (c.keepClear == KEEPCLEAR_UNSPECIFIED && myRequest->hasConflictAtLink(linkIndex)) {
1106
const LinkState linkState = getLinkState(incoming, c.toEdge, c.fromLane, c.toLane, c.mayDefinitelyPass, c.tlID);
1107
if (linkState == LINKSTATE_MAJOR) {
1108
c.keepClear = KEEPCLEAR_FALSE;
1109
}
1110
}
1111
}
1112
linkIndex++;
1113
}
1114
}
1115
}
1116
}
1117
1118
1119
bool
1120
NBNode::writeLogic(OutputDevice& into) const {
1121
if (myRequest) {
1122
myRequest->writeLogic(into);
1123
return true;
1124
}
1125
return false;
1126
}
1127
1128
1129
const std::string
1130
NBNode::getFoes(int linkIndex) const {
1131
if (myRequest == nullptr) {
1132
return "";
1133
} else {
1134
return myRequest->getFoes(linkIndex);
1135
}
1136
}
1137
1138
1139
const std::string
1140
NBNode::getResponse(int linkIndex) const {
1141
if (myRequest == nullptr) {
1142
return "";
1143
} else {
1144
return myRequest->getResponse(linkIndex);
1145
}
1146
}
1147
1148
bool
1149
NBNode::hasConflict() const {
1150
if (myRequest == nullptr) {
1151
return false;
1152
} else {
1153
return myRequest->hasConflict();
1154
}
1155
}
1156
1157
1158
bool
1159
NBNode::hasConflict(const NBEdge* e) const {
1160
if (myRequest == nullptr) {
1161
return false;
1162
}
1163
for (const auto& con : e->getConnections()) {
1164
const int index = getConnectionIndex(e, con);
1165
if (myRequest->hasConflictAtLink(index)) {
1166
return true;
1167
}
1168
}
1169
return false;
1170
}
1171
1172
1173
void
1174
NBNode::updateSurroundingGeometry() {
1175
NBTurningDirectionsComputer::computeTurnDirectionsForNode(this, false);
1176
sortEdges(false);
1177
computeNodeShape(-1);
1178
for (NBEdge* edge : myAllEdges) {
1179
edge->computeEdgeShape();
1180
}
1181
}
1182
1183
void
1184
NBNode::computeNodeShape(double mismatchThreshold) {
1185
if (myHaveCustomPoly) {
1186
return;
1187
}
1188
if (myIncomingEdges.size() == 0 && myOutgoingEdges.size() == 0) {
1189
// may be an intermediate step during network editing
1190
myPoly.clear();
1191
myPoly.push_back(myPosition);
1192
return;
1193
}
1194
if (OptionsCont::getOptions().getFloat("default.junctions.radius") < 0) {
1195
// skip shape computation by option
1196
return;
1197
}
1198
try {
1199
NBNodeShapeComputer computer(*this);
1200
myPoly = computer.compute(OptionsCont::getOptions().getBool("junctions.minimal-shape"));
1201
if (myRadius == UNSPECIFIED_RADIUS && !OptionsCont::getOptions().isDefault("default.junctions.radius")) {
1202
myRadius = computer.getRadius();
1203
}
1204
if (myPoly.size() > 0) {
1205
PositionVector tmp = myPoly;
1206
tmp.push_back_noDoublePos(tmp[0]); // need closed shape
1207
if (mismatchThreshold >= 0
1208
&& !tmp.around(myPosition)
1209
&& tmp.distance2D(myPosition) > mismatchThreshold) {
1210
WRITE_WARNINGF(TL("Shape for junction '%' has distance % to its given position."), myID, tmp.distance2D(myPosition));
1211
}
1212
}
1213
} catch (InvalidArgument&) {
1214
WRITE_WARNINGF(TL("For junction '%': could not compute shape."), myID);
1215
// make sure our shape is not empty because our XML schema forbids empty attributes
1216
myPoly.clear();
1217
myPoly.push_back(myPosition);
1218
}
1219
}
1220
1221
1222
void
1223
NBNode::computeLanes2Lanes() {
1224
// special case a):
1225
// one in, one out, the outgoing has more lanes
1226
if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1) {
1227
NBEdge* in = myIncomingEdges[0];
1228
NBEdge* out = myOutgoingEdges[0];
1229
// check if it's not the turnaround
1230
if (in->getTurnDestination() == out) {
1231
// will be added later or not...
1232
return;
1233
}
1234
int inOffset, inEnd, outOffset, outEnd, addedLanes;
1235
getReduction(out, in, outOffset, outEnd, inOffset, inEnd, addedLanes);
1236
if (in->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES
1237
&& addedLanes > 0
1238
&& in->isConnectedTo(out)) {
1239
const int addedRight = addedLanesRight(out, addedLanes);
1240
const int addedLeft = addedLanes - addedRight;
1241
#ifdef DEBUG_CONNECTION_GUESSING
1242
if (DEBUGCOND) {
1243
std::cout << "l2l node=" << getID() << " specialCase a. addedRight=" << addedRight << " addedLeft=" << addedLeft << " inOff=" << inOffset << " outOff=" << outOffset << " inEnd=" << inEnd << " outEnd=" << outEnd << "\n";
1244
}
1245
#endif
1246
// "straight" connections
1247
for (int i = inOffset; i < inEnd; ++i) {
1248
in->setConnection(i, out, i - inOffset + outOffset + addedRight, NBEdge::Lane2LaneInfoType::COMPUTED);
1249
}
1250
// connect extra lane on the right
1251
for (int i = 0; i < addedRight; ++i) {
1252
in->setConnection(inOffset, out, outOffset + i, NBEdge::Lane2LaneInfoType::COMPUTED);
1253
}
1254
// connect extra lane on the left
1255
const int inLeftMost = inEnd - 1;;
1256
const int outOffset2 = outOffset + addedRight + inEnd - inOffset;
1257
for (int i = 0; i < addedLeft; ++i) {
1258
in->setConnection(inLeftMost, out, outOffset2 + i, NBEdge::Lane2LaneInfoType::COMPUTED);
1259
}
1260
if (out->getSpecialLane(SVC_BICYCLE) >= 0) {
1261
recheckVClassConnections(out);
1262
}
1263
return;
1264
}
1265
}
1266
// special case b):
1267
// two in, one out, the outgoing has the same number of lanes as the sum of the incoming
1268
// --> highway on-ramp
1269
if (myIncomingEdges.size() == 2 && myOutgoingEdges.size() == 1) {
1270
NBEdge* const out = myOutgoingEdges[0];
1271
NBEdge* in1 = myIncomingEdges[0];
1272
NBEdge* in2 = myIncomingEdges[1];
1273
const int outOffset = MAX2(0, out->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1274
int in1Offset = MAX2(0, in1->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1275
int in2Offset = MAX2(0, in2->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1276
if (in1->getNumLanes() + in2->getNumLanes() - in1Offset - in2Offset == out->getNumLanes() - outOffset
1277
&& (in1->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES)
1278
&& (in2->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES)
1279
&& in1 != out
1280
&& in2 != out
1281
&& in1->isConnectedTo(out)
1282
&& in2->isConnectedTo(out)
1283
&& in1->getSpecialLane(SVC_BICYCLE) == -1
1284
&& in2->getSpecialLane(SVC_BICYCLE) == -1
1285
&& out->getSpecialLane(SVC_BICYCLE) == -1
1286
&& in1->getSpecialLane(SVC_TRAM) == -1
1287
&& in2->getSpecialLane(SVC_TRAM) == -1
1288
&& out->getSpecialLane(SVC_TRAM) == -1
1289
&& isLongEnough(out, MIN_WEAVE_LENGTH)) {
1290
#ifdef DEBUG_CONNECTION_GUESSING
1291
if (DEBUGCOND) {
1292
std::cout << "l2l node=" << getID() << " specialCase b\n";
1293
}
1294
#endif
1295
// for internal: check which one is the rightmost
1296
double a1 = in1->getAngleAtNode(this);
1297
double a2 = in2->getAngleAtNode(this);
1298
double ccw = GeomHelper::getCCWAngleDiff(a1, a2);
1299
double cw = GeomHelper::getCWAngleDiff(a1, a2);
1300
if (ccw > cw) {
1301
std::swap(in1, in2);
1302
std::swap(in1Offset, in2Offset);
1303
}
1304
in1->addLane2LaneConnections(in1Offset, out, outOffset, in1->getNumLanes() - in1Offset, NBEdge::Lane2LaneInfoType::COMPUTED, true);
1305
in2->addLane2LaneConnections(in2Offset, out, in1->getNumLanes() + outOffset - in1Offset, in2->getNumLanes() - in2Offset, NBEdge::Lane2LaneInfoType::COMPUTED, true);
1306
if (out->getSpecialLane(SVC_BICYCLE) >= 0) {
1307
recheckVClassConnections(out);
1308
}
1309
return;
1310
}
1311
}
1312
// special case c):
1313
// one in, two out, the incoming has the same number of lanes or only 1 lane less than the sum of the outgoing lanes
1314
// --> highway off-ramp
1315
if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 2) {
1316
NBEdge* in = myIncomingEdges[0];
1317
NBEdge* out1 = myOutgoingEdges[0];
1318
NBEdge* out2 = myOutgoingEdges[1];
1319
const int inOffset = MAX2(0, in->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1320
int out1Offset = MAX2(0, out1->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1321
int out2Offset = MAX2(0, out2->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1322
const int deltaLaneSum = (out2->getNumLanes() + out1->getNumLanes() - out1Offset - out2Offset) - (in->getNumLanes() - inOffset);
1323
if ((deltaLaneSum == 0 || (deltaLaneSum == 1 && in->getPermissionVariants(inOffset, in->getNumLanes()).size() == 1))
1324
&& (in->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES)
1325
&& in != out1
1326
&& in != out2
1327
&& in->isConnectedTo(out1)
1328
&& in->isConnectedTo(out2)
1329
&& !in->isTurningDirectionAt(out1)
1330
&& !in->isTurningDirectionAt(out2)
1331
) {
1332
#ifdef DEBUG_CONNECTION_GUESSING
1333
if (DEBUGCOND) {
1334
std::cout << "l2l node=" << getID() << " specialCase c\n";
1335
}
1336
#endif
1337
// for internal: check which one is the rightmost
1338
if (NBContHelper::relative_outgoing_edge_sorter(in)(out2, out1)) {
1339
std::swap(out1, out2);
1340
std::swap(out1Offset, out2Offset);
1341
}
1342
in->addLane2LaneConnections(inOffset, out1, out1Offset, out1->getNumLanes() - out1Offset, NBEdge::Lane2LaneInfoType::COMPUTED, true);
1343
in->addLane2LaneConnections(out1->getNumLanes() + inOffset - out1Offset - deltaLaneSum, out2, out2Offset, out2->getNumLanes() - out2Offset, NBEdge::Lane2LaneInfoType::COMPUTED, false);
1344
if (in->getSpecialLane(SVC_BICYCLE) >= 0) {
1345
recheckVClassConnections(out1);
1346
recheckVClassConnections(out2);
1347
}
1348
return;
1349
}
1350
}
1351
// special case d):
1352
// one in, one out, the outgoing has one lane less and node has type 'zipper'
1353
if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1 && myType == SumoXMLNodeType::ZIPPER) {
1354
NBEdge* in = myIncomingEdges[0];
1355
NBEdge* out = myOutgoingEdges[0];
1356
// check if it's not the turnaround
1357
if (in->getTurnDestination() == out) {
1358
// will be added later or not...
1359
return;
1360
}
1361
#ifdef DEBUG_CONNECTION_GUESSING
1362
if (DEBUGCOND) {
1363
std::cout << "l2l node=" << getID() << " specialCase d\n";
1364
}
1365
#endif
1366
const int inOffset = MAX2(0, in->getFirstNonPedestrianLaneIndex(FORWARD, true));
1367
const int outOffset = MAX2(0, out->getFirstNonPedestrianLaneIndex(FORWARD, true));
1368
if (in->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES
1369
&& in->getNumLanes() - inOffset == out->getNumLanes() - outOffset + 1
1370
&& in != out
1371
&& in->isConnectedTo(out)) {
1372
for (int i = inOffset; i < in->getNumLanes(); ++i) {
1373
in->setConnection(i, out, MIN2(outOffset + i, out->getNumLanes() - 1), NBEdge::Lane2LaneInfoType::COMPUTED, true);
1374
}
1375
return;
1376
}
1377
}
1378
// special case f):
1379
// one in, one out, out has reduced or same number of lanes
1380
if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1) {
1381
NBEdge* in = myIncomingEdges[0];
1382
NBEdge* out = myOutgoingEdges[0];
1383
// check if it's not the turnaround
1384
if (in->getTurnDestination() == out) {
1385
// will be added later or not...
1386
return;
1387
}
1388
int inOffset, inEnd, outOffset, outEnd, reduction;
1389
getReduction(in, out, inOffset, inEnd, outOffset, outEnd, reduction);
1390
if (in->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES
1391
&& reduction >= 0
1392
&& in != out
1393
&& in->isConnectedTo(out)) {
1394
#ifdef DEBUG_CONNECTION_GUESSING
1395
if (DEBUGCOND) {
1396
std::cout << "l2l node=" << getID() << " specialCase f inOff=" << inOffset << " outOff=" << outOffset << " inEnd=" << inEnd << " outEnd=" << outEnd << " reduction=" << reduction << "\n";
1397
}
1398
#endif
1399
// in case of reduced lane number, let the rightmost lanes end
1400
inOffset += reduction;
1401
for (int i = outOffset; i < outEnd; ++i) {
1402
in->setConnection(i + inOffset - outOffset, out, i, NBEdge::Lane2LaneInfoType::COMPUTED);
1403
}
1404
//std::cout << " special case f at node=" << getID() << " inOffset=" << inOffset << " outOffset=" << outOffset << "\n";
1405
recheckVClassConnections(out);
1406
return;
1407
}
1408
}
1409
1410
// go through this node's outgoing edges
1411
// for every outgoing edge, compute the distribution of the node's
1412
// incoming edges on this edge when approaching this edge
1413
// the incoming edges' steps will then also be marked as LANE2LANE_RECHECK...
1414
EdgeVector approaching;
1415
for (NBEdge* currentOutgoing : myOutgoingEdges) {
1416
// get the information about edges that do approach this edge
1417
getEdgesThatApproach(currentOutgoing, approaching);
1418
const int numApproaching = (int)approaching.size();
1419
if (numApproaching != 0) {
1420
ApproachingDivider divider(approaching, currentOutgoing);
1421
Bresenham::compute(&divider, numApproaching, divider.numAvailableLanes());
1422
}
1423
#ifdef DEBUG_CONNECTION_GUESSING
1424
if (DEBUGCOND) {
1425
std::cout << "l2l node=" << getID() << " outgoing=" << currentOutgoing->getID() << " bresenham:\n";
1426
for (NBEdge* e : myIncomingEdges) {
1427
const std::vector<NBEdge::Connection>& elv = e->getConnections();
1428
for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
1429
std::cout << " " << e->getID() << "_" << (*k).fromLane << " -> " << Named::getIDSecure((*k).toEdge) << "_" << (*k).toLane << "\n";
1430
}
1431
}
1432
}
1433
#endif
1434
recheckVClassConnections(currentOutgoing);
1435
1436
// in case of lane change restrictions on the outgoing edge, ensure that
1437
// all its lanes can be reached from each connected incoming edge
1438
bool targetProhibitsChange = false;
1439
for (int i = 0; i < currentOutgoing->getNumLanes(); i++) {
1440
const NBEdge::Lane& lane = currentOutgoing->getLanes()[i];
1441
if ((lane.changeLeft != SVCAll && lane.changeLeft != SVC_IGNORING && i + 1 < currentOutgoing->getNumLanes())
1442
|| (lane.changeRight != SVCAll && lane.changeRight != SVC_IGNORING && i > 0)) {
1443
targetProhibitsChange = true;
1444
break;
1445
}
1446
}
1447
if (targetProhibitsChange) {
1448
//std::cout << " node=" << getID() << " outgoing=" << currentOutgoing->getID() << " targetProhibitsChange\n";
1449
for (NBEdge* incoming : myIncomingEdges) {
1450
if (incoming->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_DONE) {
1451
std::map<int, int> outToIn;
1452
for (const NBEdge::Connection& c : incoming->getConnections()) {
1453
if (c.toEdge == currentOutgoing) {
1454
outToIn[c.toLane] = c.fromLane;
1455
}
1456
}
1457
for (int toLane = 0; toLane < currentOutgoing->getNumLanes(); toLane++) {
1458
if (outToIn.count(toLane) == 0) {
1459
bool added = false;
1460
// find incoming lane for neighboring outgoing
1461
for (int i = 0; i < toLane; i++) {
1462
if (outToIn.count(i) != 0) {
1463
#ifdef DEBUG_CONNECTION_GUESSING
1464
if (DEBUGCOND) {
1465
std::cout << "l2l node=" << getID() << " from=" << incoming->getID() << " to " << currentOutgoing->getLaneID(toLane) << " (changeProhibited, secondTarget)\n";
1466
}
1467
#endif
1468
incoming->setConnection(outToIn[i], currentOutgoing, toLane, NBEdge::Lane2LaneInfoType::COMPUTED);
1469
added = true;
1470
break;
1471
}
1472
}
1473
if (!added) {
1474
for (int i = toLane; i < currentOutgoing->getNumLanes(); i++) {
1475
if (outToIn.count(i) != 0) {
1476
#ifdef DEBUG_CONNECTION_GUESSING
1477
if (DEBUGCOND) {
1478
std::cout << "l2l node=" << getID() << " from=" << incoming->getID() << " to " << currentOutgoing->getLaneID(toLane) << " (changeProhibited, newTarget)\n";
1479
}
1480
#endif
1481
incoming->setConnection(outToIn[i], currentOutgoing, toLane, NBEdge::Lane2LaneInfoType::COMPUTED);
1482
added = true;
1483
break;
1484
}
1485
}
1486
}
1487
}
1488
}
1489
}
1490
}
1491
}
1492
}
1493
// special case e): rail_crossing
1494
// there should only be straight connections here
1495
if (myType == SumoXMLNodeType::RAIL_CROSSING) {
1496
for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
1497
const std::vector<NBEdge::Connection> cons = (*i)->getConnections();
1498
for (std::vector<NBEdge::Connection>::const_iterator k = cons.begin(); k != cons.end(); ++k) {
1499
if (getDirection(*i, (*k).toEdge) == LinkDirection::TURN) {
1500
(*i)->removeFromConnections((*k).toEdge);
1501
}
1502
}
1503
}
1504
}
1505
1506
// ... but we may have the case that there are no outgoing edges
1507
// In this case, we have to mark the incoming edges as being in state
1508
// LANE2LANE( not RECHECK) by hand
1509
if (myOutgoingEdges.size() == 0) {
1510
for (NBEdge* incoming : myIncomingEdges) {
1511
incoming->markAsInLane2LaneState();
1512
}
1513
}
1514
1515
#ifdef DEBUG_CONNECTION_GUESSING
1516
if (DEBUGCOND) {
1517
std::cout << "final connections at " << getID() << "\n";
1518
for (NBEdge* e : myIncomingEdges) {
1519
const std::vector<NBEdge::Connection>& elv = e->getConnections();
1520
for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
1521
std::cout << " " << e->getID() << "_" << (*k).fromLane << " -> " << Named::getIDSecure((*k).toEdge) << "_" << (*k).toLane << "\n";
1522
}
1523
}
1524
}
1525
#endif
1526
}
1527
1528
void
1529
NBNode::recheckVClassConnections(NBEdge* currentOutgoing) {
1530
// ensure that all modes have a connection if possible
1531
for (NBEdge* incoming : myIncomingEdges) {
1532
if (incoming->getConnectionLanes(currentOutgoing).size() > 0 && incoming->getStep() <= NBEdge::EdgeBuildingStep::LANES2LANES_DONE) {
1533
// no connections are needed for pedestrians during this step
1534
// no satisfaction is possible if the outgoing edge disallows
1535
SVCPermissions unsatisfied = incoming->getPermissions() & currentOutgoing->getPermissions() & ~SVC_PEDESTRIAN;
1536
//std::cout << "initial unsatisfied modes from edge=" << incoming->getID() << " toEdge=" << currentOutgoing->getID() << " deadModes=" << getVehicleClassNames(unsatisfied) << "\n";
1537
const std::vector<NBEdge::Connection>& elv = incoming->getConnections();
1538
for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
1539
const NBEdge::Connection& c = *k;
1540
if (c.toEdge == currentOutgoing && c.toLane >= 0) {
1541
const SVCPermissions satisfied = (incoming->getPermissions(c.fromLane) & c.toEdge->getPermissions(c.toLane));
1542
//std::cout << " from=" << incoming->getID() << "_" << c.fromLane << " to=" << c.toEdge->getID() << "_" << c.toLane << " satisfied=" << getVehicleClassNames(satisfied) << "\n";
1543
unsatisfied &= ~satisfied;
1544
}
1545
}
1546
if (unsatisfied != 0) {
1547
#ifdef DEBUG_CONNECTION_GUESSING
1548
if (DEBUGCOND) {
1549
std::cout << " unsatisfied modes from edge=" << incoming->getID() << " toEdge=" << currentOutgoing->getID() << " deadModes=" << getVehicleClassNames(unsatisfied) << "\n";
1550
}
1551
#endif
1552
int fromLane = 0;
1553
// first attempt: try to use a dedicated fromLane
1554
while (unsatisfied != 0 && fromLane < incoming->getNumLanes()) {
1555
if (incoming->getPermissions(fromLane) == unsatisfied) {
1556
unsatisfied = findToLaneForPermissions(currentOutgoing, fromLane, incoming, unsatisfied);
1557
}
1558
fromLane++;
1559
}
1560
// second attempt: try to re-use a fromLane that already connects to currentOutgoing
1561
// (because we don't wont to create extra turn lanes)
1562
fromLane = 0;
1563
while (unsatisfied != 0 && fromLane < incoming->getNumLanes()) {
1564
if ((incoming->getPermissions(fromLane) & unsatisfied) != 0
1565
&& incoming->getConnectionsFromLane(fromLane, currentOutgoing, -1).size() > 0) {
1566
unsatisfied = findToLaneForPermissions(currentOutgoing, fromLane, incoming, unsatisfied);
1567
}
1568
fromLane++;
1569
}
1570
// third attempt: use any possible fromLane
1571
fromLane = 0;
1572
while (unsatisfied != 0 && fromLane < incoming->getNumLanes()) {
1573
if ((incoming->getPermissions(fromLane) & unsatisfied) != 0) {
1574
unsatisfied = findToLaneForPermissions(currentOutgoing, fromLane, incoming, unsatisfied);
1575
}
1576
fromLane++;
1577
}
1578
#ifdef DEBUG_CONNECTION_GUESSING
1579
if (DEBUGCOND) {
1580
if (unsatisfied != 0) {
1581
std::cout << " still unsatisfied modes from edge=" << incoming->getID() << " toEdge=" << currentOutgoing->getID() << " deadModes=" << getVehicleClassNames(unsatisfied) << "\n";
1582
}
1583
}
1584
#endif
1585
}
1586
}
1587
// prevent dead-end bus and bicycle lanes (they were excluded by the ApproachingDivider)
1588
// and the bus/bicycle class might already be satisfied by other lanes
1589
recheckSpecialConnections(incoming, currentOutgoing, SVC_BUS);
1590
recheckSpecialConnections(incoming, currentOutgoing, SVC_BICYCLE);
1591
}
1592
}
1593
1594
1595
void
1596
NBNode::recheckSpecialConnections(NBEdge* incoming, NBEdge* currentOutgoing, SVCPermissions svcSpecial) {
1597
// assume that left-turns and turn-arounds are better satisfied from lanes to the left
1598
const int specialTarget = currentOutgoing->getSpecialLane(svcSpecial);
1599
const LinkDirection dir = getDirection(incoming, currentOutgoing);
1600
if (incoming->getStep() <= NBEdge::EdgeBuildingStep::LANES2LANES_DONE
1601
&& ((specialTarget >= 0 && dir != LinkDirection::TURN)
1602
|| dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT || dir == LinkDirection::STRAIGHT)) {
1603
bool builtConnection = false;
1604
for (int i = 0; i < (int)incoming->getNumLanes(); i++) {
1605
if (incoming->getPermissions(i) == svcSpecial
1606
&& incoming->getConnectionsFromLane(i, currentOutgoing).size() == 0) {
1607
// find a dedicated bike lane as target
1608
if (specialTarget >= 0) {
1609
incoming->setConnection(i, currentOutgoing, specialTarget, NBEdge::Lane2LaneInfoType::COMPUTED);
1610
#ifdef DEBUG_CONNECTION_GUESSING
1611
if (DEBUGCOND) {
1612
std::cout << " extra " << getVehicleClassNames(svcSpecial) << " connection from=" << incoming->getLaneID(i) << " (dedicated) to=" << currentOutgoing->getLaneID(specialTarget) << "\n";
1613
}
1614
#endif
1615
builtConnection = true;
1616
} else {
1617
// do not create turns that create a conflict with neighboring lanes
1618
if (avoidConfict(incoming, currentOutgoing, svcSpecial, dir, i)) {
1619
continue;
1620
}
1621
// use any lane that allows the special class
1622
for (int i2 = 0; i2 < (int)currentOutgoing->getNumLanes(); i2++) {
1623
if ((currentOutgoing->getPermissions(i2) & svcSpecial) != 0) {
1624
// possibly a double-connection
1625
const bool allowDouble = (incoming->getPermissions(i) == svcSpecial
1626
&& (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT || dir == LinkDirection::STRAIGHT));
1627
incoming->setConnection(i, currentOutgoing, i2, NBEdge::Lane2LaneInfoType::COMPUTED, allowDouble);
1628
#ifdef DEBUG_CONNECTION_GUESSING
1629
if (DEBUGCOND) {
1630
std::cout << " extra " << getVehicleClassNames(svcSpecial) << " connection from=" << incoming->getLaneID(i) << " to=" << currentOutgoing->getLaneID(i2) << "\n";
1631
}
1632
#endif
1633
builtConnection = true;
1634
break;
1635
}
1636
}
1637
}
1638
}
1639
}
1640
if (!builtConnection && specialTarget >= 0
1641
&& incoming->getConnectionsFromLane(-1, currentOutgoing, specialTarget).size() == 0) {
1642
// find origin lane that allows bicycles
1643
int start = 0;
1644
int end = incoming->getNumLanes();
1645
int inc = 1;
1646
if (dir == LinkDirection::TURN || dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT) {
1647
std::swap(start, end);
1648
inc = -1;
1649
}
1650
for (int i = start; i < end; i += inc) {
1651
if ((incoming->getPermissions(i) & svcSpecial) != 0) {
1652
incoming->setConnection(i, currentOutgoing, specialTarget, NBEdge::Lane2LaneInfoType::COMPUTED);
1653
#ifdef DEBUG_CONNECTION_GUESSING
1654
if (DEBUGCOND) {
1655
std::cout << " extra " << getVehicleClassNames(svcSpecial) << " connection from=" << incoming->getLaneID(i) << " (final) to=" << currentOutgoing->getLaneID(specialTarget) << "\n";
1656
}
1657
#endif
1658
break;
1659
}
1660
}
1661
}
1662
}
1663
}
1664
1665
1666
bool
1667
NBNode::avoidConfict(NBEdge* incoming, NBEdge* currentOutgoing, SVCPermissions svcSpecial, LinkDirection dir, int i) {
1668
for (const auto& c : incoming->getConnections()) {
1669
if (incoming->getPermissions(c.fromLane) == svcSpecial && c.toEdge == currentOutgoing) {
1670
return true;
1671
}
1672
}
1673
if (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT) {
1674
for (const auto& c : incoming->getConnections()) {
1675
if (c.fromLane < i && (c.toEdge != currentOutgoing || incoming->getPermissions(c.fromLane) == svcSpecial)) {
1676
return true;
1677
}
1678
}
1679
} else if (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT) {
1680
for (const auto& c : incoming->getConnections()) {
1681
if (c.fromLane > i && (c.toEdge != currentOutgoing || incoming->getPermissions(c.fromLane) == svcSpecial)) {
1682
return true;
1683
}
1684
}
1685
} else if (svcSpecial != SVC_BICYCLE && dir == LinkDirection::STRAIGHT) {
1686
for (const auto& c : incoming->getConnections()) {
1687
const LinkDirection dir2 = getDirection(incoming, c.toEdge);
1688
if (c.fromLane < i && (dir2 == LinkDirection::LEFT || dir2 == LinkDirection::PARTLEFT)) {
1689
return true;
1690
} else if (c.fromLane > i && (dir2 == LinkDirection::RIGHT || dir2 == LinkDirection::PARTRIGHT)) {
1691
return true;
1692
}
1693
}
1694
}
1695
return false;
1696
}
1697
1698
1699
void
1700
NBNode::getReduction(const NBEdge* in, const NBEdge* out, int& inOffset, int& inEnd, int& outOffset, int& outEnd, int& reduction) const {
1701
inOffset = MAX2(0, in->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1702
outOffset = MAX2(0, out->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1703
inEnd = in->getFirstNonPedestrianLaneIndex(BACKWARD, true) + 1;
1704
outEnd = out->getFirstNonPedestrianLaneIndex(BACKWARD, true) + 1;
1705
reduction = (inEnd - inOffset) - (outEnd - outOffset);
1706
}
1707
1708
1709
SVCPermissions
1710
NBNode::findToLaneForPermissions(NBEdge* currentOutgoing, int fromLane, NBEdge* incoming, SVCPermissions unsatisfied) {
1711
for (int toLane = 0; toLane < currentOutgoing->getNumLanes(); ++toLane) {
1712
const SVCPermissions satisfied = incoming->getPermissions(fromLane) & currentOutgoing->getPermissions(toLane) & unsatisfied;
1713
if (satisfied != 0 && !incoming->getLaneStruct(fromLane).connectionsDone) {
1714
if (incoming->hasConnectionTo(currentOutgoing, toLane)
1715
&& unsatisfied == SVC_TRAM
1716
&& incoming->getPermissions(fromLane) == currentOutgoing->getPermissions(toLane)) {
1717
// avoid double tram connection by shifting an existing connection
1718
for (auto con : incoming->getConnections()) {
1719
if (con.toEdge == currentOutgoing && con.toLane == toLane) {
1720
#ifdef DEBUG_CONNECTION_GUESSING
1721
if (DEBUGCOND) {
1722
std::cout << " shifting connection from=" << con.fromLane << " to=" << currentOutgoing->getID() << "_" << toLane << ": newFromLane=" << fromLane << " satisfies=" << getVehicleClassNames(satisfied) << "\n";
1723
}
1724
#endif
1725
incoming->getConnectionRef(con.fromLane, con.toEdge, toLane).fromLane = fromLane;
1726
unsatisfied &= ~satisfied;
1727
break;
1728
}
1729
}
1730
} else {
1731
// other modes (i.e. bus) can fix lane permissions NBPTLineCont::fixPermissions but do not wish to create parallel tram tracks here
1732
bool mayUseSameDestination = unsatisfied == SVC_TRAM || (unsatisfied & SVC_PASSENGER) != 0;
1733
incoming->setConnection((int)fromLane, currentOutgoing, toLane, NBEdge::Lane2LaneInfoType::COMPUTED, mayUseSameDestination);
1734
#ifdef DEBUG_CONNECTION_GUESSING
1735
if (DEBUGCOND) {
1736
std::cout << " new connection from=" << fromLane << " to=" << currentOutgoing->getID() << "_" << toLane << " satisfies=" << getVehicleClassNames(satisfied) << "\n";
1737
}
1738
#endif
1739
unsatisfied &= ~satisfied;
1740
}
1741
}
1742
}
1743
return unsatisfied;
1744
}
1745
1746
1747
int
1748
NBNode::addedLanesRight(NBEdge* out, int addedLanes) const {
1749
if (out->isOffRamp()) {
1750
return addedLanes;
1751
}
1752
NBNode* to = out->getToNode();
1753
// check whether a right lane ends
1754
if (to->getIncomingEdges().size() == 1
1755
&& to->getOutgoingEdges().size() == 1) {
1756
int inOffset, inEnd, outOffset, outEnd, reduction;
1757
to->getReduction(out, to->getOutgoingEdges()[0], inOffset, inEnd, outOffset, outEnd, reduction);
1758
1759
if (reduction > 0) {
1760
return reduction;
1761
}
1762
}
1763
// check for the presence of right and left turns at the next intersection
1764
int outLanesRight = 0;
1765
int outLanesLeft = 0;
1766
int outLanesStraight = 0;
1767
for (NBEdge* succ : to->getOutgoingEdges()) {
1768
if (out->isConnectedTo(succ)) {
1769
const int outOffset = MAX2(0, succ->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1770
const int usableLanes = succ->getNumLanes() - outOffset;
1771
LinkDirection dir = to->getDirection(out, succ);
1772
if (dir == LinkDirection::STRAIGHT) {
1773
outLanesStraight += usableLanes;
1774
} else if (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT) {
1775
outLanesRight += usableLanes;
1776
} else {
1777
outLanesLeft += usableLanes;
1778
}
1779
}
1780
}
1781
const int outOffset = MAX2(0, out->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1782
const int outEnd = out->getFirstNonPedestrianLaneIndex(BACKWARD, true) + 1;
1783
const int usableLanes = outEnd - outOffset;
1784
int addedTurnLanes = MIN3(
1785
addedLanes,
1786
MAX2(0, usableLanes - outLanesStraight),
1787
outLanesRight + outLanesLeft);
1788
#ifdef DEBUG_CONNECTION_GUESSING
1789
if (DEBUGCOND) {
1790
std::cout << "out=" << out->getID() << " usableLanes=" << usableLanes << " addedTurnLanes=" << addedTurnLanes << " addedLanes=" << addedLanes << " outLanesStraight=" << outLanesStraight << " outLanesLeft=" << outLanesLeft << " outLanesRight=" << outLanesRight << "\n";
1791
}
1792
#endif
1793
if (outLanesLeft == 0) {
1794
return addedTurnLanes;
1795
} else {
1796
return MIN2(addedTurnLanes / 2, outLanesRight);
1797
}
1798
}
1799
1800
1801
bool
1802
NBNode::isLongEnough(NBEdge* out, double minLength) {
1803
double seen = out->getLoadedLength();
1804
while (seen < minLength) {
1805
// advance along trivial continuations
1806
if (out->getToNode()->getOutgoingEdges().size() != 1
1807
|| out->getToNode()->getIncomingEdges().size() != 1) {
1808
return false;
1809
} else {
1810
out = out->getToNode()->getOutgoingEdges()[0];
1811
seen += out->getLoadedLength();
1812
}
1813
}
1814
return true;
1815
}
1816
1817
1818
void
1819
NBNode::getEdgesThatApproach(NBEdge* currentOutgoing, EdgeVector& approaching) {
1820
// get the position of the node to get the approaching nodes of
1821
EdgeVector::const_iterator i = std::find(myAllEdges.begin(),
1822
myAllEdges.end(), currentOutgoing);
1823
// get the first possible approaching edge
1824
NBContHelper::nextCW(myAllEdges, i);
1825
// go through the list of edges clockwise and add the edges
1826
approaching.clear();
1827
for (; *i != currentOutgoing;) {
1828
// check only incoming edges
1829
if ((*i)->getToNode() == this && (*i)->getTurnDestination() != currentOutgoing) {
1830
std::vector<int> connLanes = (*i)->getConnectionLanes(currentOutgoing);
1831
if (connLanes.size() != 0) {
1832
approaching.push_back(*i);
1833
}
1834
}
1835
NBContHelper::nextCW(myAllEdges, i);
1836
}
1837
}
1838
1839
1840
void
1841
NBNode::replaceOutgoing(NBEdge* which, NBEdge* by, int laneOff) {
1842
// replace the edge in the list of outgoing nodes
1843
EdgeVector::iterator i = std::find(myOutgoingEdges.begin(), myOutgoingEdges.end(), which);
1844
if (i != myOutgoingEdges.end()) {
1845
(*i) = by;
1846
i = std::find(myAllEdges.begin(), myAllEdges.end(), which);
1847
(*i) = by;
1848
}
1849
// replace the edge in connections of incoming edges
1850
for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); ++i) {
1851
(*i)->replaceInConnections(which, by, laneOff);
1852
}
1853
// replace within the connetion prohibition dependencies
1854
replaceInConnectionProhibitions(which, by, 0, laneOff);
1855
}
1856
1857
1858
void
1859
NBNode::replaceOutgoing(const EdgeVector& which, NBEdge* by) {
1860
// replace edges
1861
int laneOff = 0;
1862
for (EdgeVector::const_iterator i = which.begin(); i != which.end(); i++) {
1863
replaceOutgoing(*i, by, laneOff);
1864
laneOff += (*i)->getNumLanes();
1865
}
1866
// removed double occurrences
1867
removeDoubleEdges();
1868
// check whether this node belongs to a district and the edges
1869
// must here be also remapped
1870
if (myDistrict != nullptr) {
1871
myDistrict->replaceOutgoing(which, by);
1872
}
1873
}
1874
1875
1876
void
1877
NBNode::replaceIncoming(NBEdge* which, NBEdge* by, int laneOff) {
1878
// replace the edge in the list of incoming nodes
1879
EdgeVector::iterator i = std::find(myIncomingEdges.begin(), myIncomingEdges.end(), which);
1880
if (i != myIncomingEdges.end()) {
1881
(*i) = by;
1882
i = std::find(myAllEdges.begin(), myAllEdges.end(), which);
1883
(*i) = by;
1884
}
1885
// replace within the connetion prohibition dependencies
1886
replaceInConnectionProhibitions(which, by, laneOff, 0);
1887
}
1888
1889
1890
void
1891
NBNode::replaceIncoming(const EdgeVector& which, NBEdge* by) {
1892
// replace edges
1893
int laneOff = 0;
1894
for (EdgeVector::const_iterator i = which.begin(); i != which.end(); i++) {
1895
replaceIncoming(*i, by, laneOff);
1896
laneOff += (*i)->getNumLanes();
1897
}
1898
// removed double occurrences
1899
removeDoubleEdges();
1900
// check whether this node belongs to a district and the edges
1901
// must here be also remapped
1902
if (myDistrict != nullptr) {
1903
myDistrict->replaceIncoming(which, by);
1904
}
1905
}
1906
1907
1908
1909
void
1910
NBNode::replaceInConnectionProhibitions(NBEdge* which, NBEdge* by,
1911
int whichLaneOff, int byLaneOff) {
1912
// replace in keys
1913
NBConnectionProhibits::iterator j = myBlockedConnections.begin();
1914
while (j != myBlockedConnections.end()) {
1915
bool changed = false;
1916
NBConnection c = (*j).first;
1917
if (c.replaceFrom(which, whichLaneOff, by, byLaneOff)) {
1918
changed = true;
1919
}
1920
if (c.replaceTo(which, whichLaneOff, by, byLaneOff)) {
1921
changed = true;
1922
}
1923
if (changed) {
1924
myBlockedConnections[c] = (*j).second;
1925
myBlockedConnections.erase(j);
1926
j = myBlockedConnections.begin();
1927
} else {
1928
j++;
1929
}
1930
}
1931
// replace in values
1932
for (j = myBlockedConnections.begin(); j != myBlockedConnections.end(); j++) {
1933
NBConnectionVector& prohibiting = (*j).second;
1934
for (NBConnectionVector::iterator k = prohibiting.begin(); k != prohibiting.end(); k++) {
1935
NBConnection& sprohibiting = *k;
1936
sprohibiting.replaceFrom(which, whichLaneOff, by, byLaneOff);
1937
sprohibiting.replaceTo(which, whichLaneOff, by, byLaneOff);
1938
}
1939
}
1940
}
1941
1942
1943
1944
void
1945
NBNode::removeDoubleEdges() {
1946
// check incoming
1947
for (int i = 0; myIncomingEdges.size() > 0 && i < (int)myIncomingEdges.size() - 1; i++) {
1948
int j = i + 1;
1949
while (j < (int)myIncomingEdges.size()) {
1950
if (myIncomingEdges[i] == myIncomingEdges[j]) {
1951
myIncomingEdges.erase(myIncomingEdges.begin() + j);
1952
} else {
1953
j++;
1954
}
1955
}
1956
}
1957
// check outgoing
1958
for (int i = 0; myOutgoingEdges.size() > 0 && i < (int)myOutgoingEdges.size() - 1; i++) {
1959
int j = i + 1;
1960
while (j < (int)myOutgoingEdges.size()) {
1961
if (myOutgoingEdges[i] == myOutgoingEdges[j]) {
1962
myOutgoingEdges.erase(myOutgoingEdges.begin() + j);
1963
} else {
1964
j++;
1965
}
1966
}
1967
}
1968
// check all
1969
for (int i = 0; myAllEdges.size() > 0 && i < (int)myAllEdges.size() - 1; i++) {
1970
int j = i + 1;
1971
while (j < (int)myAllEdges.size()) {
1972
if (myAllEdges[i] == myAllEdges[j]) {
1973
myAllEdges.erase(myAllEdges.begin() + j);
1974
} else {
1975
j++;
1976
}
1977
}
1978
}
1979
}
1980
1981
1982
bool
1983
NBNode::hasIncoming(const NBEdge* const e) const {
1984
return std::find(myIncomingEdges.begin(), myIncomingEdges.end(), e) != myIncomingEdges.end();
1985
}
1986
1987
1988
bool
1989
NBNode::hasOutgoing(const NBEdge* const e) const {
1990
return std::find(myOutgoingEdges.begin(), myOutgoingEdges.end(), e) != myOutgoingEdges.end();
1991
}
1992
1993
1994
NBEdge*
1995
NBNode::getOppositeIncoming(NBEdge* e) const {
1996
EdgeVector edges = myIncomingEdges;
1997
if (find(edges.begin(), edges.end(), e) != edges.end()) {
1998
edges.erase(find(edges.begin(), edges.end(), e));
1999
}
2000
if (edges.size() == 0) {
2001
return nullptr;
2002
}
2003
if (e->getToNode() == this) {
2004
sort(edges.begin(), edges.end(), NBContHelper::edge_opposite_direction_sorter(e, this, false));
2005
} else {
2006
sort(edges.begin(), edges.end(), NBContHelper::edge_similar_direction_sorter(e));
2007
}
2008
return edges[0];
2009
}
2010
2011
2012
void
2013
NBNode::addSortedLinkFoes(const NBConnection& mayDrive,
2014
const NBConnection& mustStop) {
2015
if (mayDrive.getFrom() == nullptr ||
2016
mayDrive.getTo() == nullptr ||
2017
mustStop.getFrom() == nullptr ||
2018
mustStop.getTo() == nullptr) {
2019
2020
WRITE_WARNING(TL("Something went wrong during the building of a connection..."));
2021
return; // !!! mark to recompute connections
2022
}
2023
NBConnectionVector conn = myBlockedConnections[mustStop];
2024
conn.push_back(mayDrive);
2025
myBlockedConnections[mustStop] = conn;
2026
}
2027
2028
2029
NBEdge*
2030
NBNode::getPossiblySplittedIncoming(const std::string& edgeid) {
2031
int size = (int) edgeid.length();
2032
for (EdgeVector::iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2033
std::string id = (*i)->getID();
2034
if (id.substr(0, size) == edgeid) {
2035
return *i;
2036
}
2037
}
2038
return nullptr;
2039
}
2040
2041
2042
NBEdge*
2043
NBNode::getPossiblySplittedOutgoing(const std::string& edgeid) {
2044
int size = (int) edgeid.length();
2045
for (EdgeVector::iterator i = myOutgoingEdges.begin(); i != myOutgoingEdges.end(); i++) {
2046
std::string id = (*i)->getID();
2047
if (id.substr(0, size) == edgeid) {
2048
return *i;
2049
}
2050
}
2051
return nullptr;
2052
}
2053
2054
2055
void
2056
NBNode::removeEdge(NBEdge* edge, bool removeFromConnections) {
2057
EdgeVector::iterator i = std::find(myAllEdges.begin(), myAllEdges.end(), edge);
2058
if (i != myAllEdges.end()) {
2059
myAllEdges.erase(i);
2060
i = std::find(myOutgoingEdges.begin(), myOutgoingEdges.end(), edge);
2061
if (i != myOutgoingEdges.end()) {
2062
myOutgoingEdges.erase(i);
2063
// potential self-loop
2064
i = std::find(myIncomingEdges.begin(), myIncomingEdges.end(), edge);
2065
if (i != myIncomingEdges.end()) {
2066
myIncomingEdges.erase(i);
2067
}
2068
} else {
2069
i = std::find(myIncomingEdges.begin(), myIncomingEdges.end(), edge);
2070
if (i != myIncomingEdges.end()) {
2071
myIncomingEdges.erase(i);
2072
} else {
2073
// edge must have been either incoming or outgoing
2074
assert(false);
2075
}
2076
}
2077
if (removeFromConnections) {
2078
for (i = myAllEdges.begin(); i != myAllEdges.end(); ++i) {
2079
(*i)->removeFromConnections(edge);
2080
}
2081
}
2082
// invalidate controlled connections for loaded traffic light plans
2083
const bool incoming = edge->getToNode() == this;
2084
for (NBTrafficLightDefinition* const tld : myTrafficLights) {
2085
tld->replaceRemoved(edge, -1, nullptr, -1, incoming);
2086
}
2087
}
2088
}
2089
2090
2091
Position
2092
NBNode::getEmptyDir() const {
2093
Position pos(0, 0);
2094
for (const NBEdge* const in : myIncomingEdges) {
2095
Position toAdd = in->getFromNode()->getPosition();
2096
toAdd.sub(myPosition);
2097
toAdd.norm2D();
2098
pos.add(toAdd);
2099
}
2100
for (const NBEdge* const out : myOutgoingEdges) {
2101
Position toAdd = out->getToNode()->getPosition();
2102
toAdd.sub(myPosition);
2103
toAdd.norm2D();
2104
pos.add(toAdd);
2105
}
2106
pos.mul(-1. / (double)(myIncomingEdges.size() + myOutgoingEdges.size()));
2107
if (pos.x() == 0. && pos.y() == 0.) {
2108
pos = Position(1, 0);
2109
}
2110
pos.norm2D();
2111
return pos;
2112
}
2113
2114
2115
2116
void
2117
NBNode::invalidateIncomingConnections(bool reallowSetting) {
2118
for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2119
(*i)->invalidateConnections(reallowSetting);
2120
}
2121
}
2122
2123
2124
void
2125
NBNode::invalidateOutgoingConnections(bool reallowSetting) {
2126
for (EdgeVector::const_iterator i = myOutgoingEdges.begin(); i != myOutgoingEdges.end(); i++) {
2127
(*i)->invalidateConnections(reallowSetting);
2128
}
2129
}
2130
2131
2132
bool
2133
NBNode::mustBrake(const NBEdge* const from, const NBEdge* const to, int fromLane, int toLane, bool includePedCrossings) const {
2134
// unregulated->does not need to brake
2135
if (myRequest == nullptr) {
2136
return false;
2137
}
2138
// vehicles which do not have a following lane must always decelerate to the end
2139
if (to == nullptr) {
2140
return true;
2141
}
2142
// maybe we need to brake due to entering a bidi-edge
2143
if (to->isBidiEdge() && !from->isBidiEdge()) {
2144
return true;
2145
}
2146
// check whether any other connection on this node prohibits this connection
2147
return myRequest->mustBrake(from, to, fromLane, toLane, includePedCrossings);
2148
}
2149
2150
bool
2151
NBNode::mustBrakeForCrossing(const NBEdge* const from, const NBEdge* const to, const NBNode::Crossing& crossing) const {
2152
return NBRequest::mustBrakeForCrossing(this, from, to, crossing);
2153
}
2154
2155
bool
2156
NBNode::brakeForCrossingOnExit(const NBEdge* to, LinkDirection dir, bool indirect) const {
2157
// code is called for connections exiting after an internal junction.
2158
// If the connection is turning we do not check for crossing priority anymore.
2159
if (dir == LinkDirection::STRAIGHT && !indirect) {
2160
return false;
2161
}
2162
for (auto& c : myCrossings) {
2163
if (std::find(c->edges.begin(), c->edges.end(), to) != c->edges.end()) {
2164
return true;
2165
}
2166
}
2167
return false;
2168
}
2169
2170
2171
bool
2172
NBNode::rightTurnConflict(const NBEdge* from, const NBEdge* to, int fromLane,
2173
const NBEdge* prohibitorFrom, const NBEdge* prohibitorTo, int prohibitorFromLane) {
2174
if (from != prohibitorFrom) {
2175
return false;
2176
}
2177
if (from->isTurningDirectionAt(to)
2178
|| prohibitorFrom->isTurningDirectionAt(prohibitorTo)) {
2179
// XXX should warn if there are any non-turning connections left of this
2180
return false;
2181
}
2182
// conflict if to is between prohibitorTo and from when going clockwise
2183
if (to->getStartAngle() == prohibitorTo->getStartAngle()) {
2184
// reduce rounding errors
2185
return false;
2186
}
2187
const LinkDirection d1 = from->getToNode()->getDirection(from, to);
2188
// must be a right turn to qualify as rightTurnConflict
2189
if (d1 == LinkDirection::STRAIGHT) {
2190
// no conflict for straight going connections
2191
// XXX actually this should check the main direction (which could also
2192
// be a turn)
2193
return false;
2194
} else {
2195
const LinkDirection d2 = prohibitorFrom->getToNode()->getDirection(prohibitorFrom, prohibitorTo);
2196
/* std::cout
2197
<< "from=" << from->getID() << " to=" << to->getID() << " fromLane=" << fromLane
2198
<< " pFrom=" << prohibitorFrom->getID() << " pTo=" << prohibitorTo->getID() << " pFromLane=" << prohibitorFromLane
2199
<< " d1=" << toString(d1) << " d2=" << toString(d2)
2200
<< "\n"; */
2201
bool flip = false;
2202
if (d1 == LinkDirection::LEFT || d1 == LinkDirection::PARTLEFT) {
2203
// check for leftTurnConflicht
2204
flip = !flip;
2205
if (d2 == LinkDirection::RIGHT || d2 == LinkDirection::PARTRIGHT) {
2206
// assume that the left-turning bicycle goes straight at first
2207
// and thus gets precedence over a right turning vehicle
2208
return false;
2209
}
2210
}
2211
if ((!flip && fromLane <= prohibitorFromLane) ||
2212
(flip && fromLane >= prohibitorFromLane)) {
2213
return false;
2214
}
2215
const double toAngleAtNode = fmod(to->getStartAngle() + 180, (double)360.0);
2216
const double prohibitorToAngleAtNode = fmod(prohibitorTo->getStartAngle() + 180, (double)360.0);
2217
return (flip != (GeomHelper::getCWAngleDiff(from->getEndAngle(), toAngleAtNode) <
2218
GeomHelper::getCWAngleDiff(from->getEndAngle(), prohibitorToAngleAtNode)));
2219
}
2220
}
2221
2222
bool
2223
NBNode::mergeConflictYields(const NBEdge* from, int fromLane, int fromLaneFoe, NBEdge* to, int toLane) const {
2224
if (myRequest == nullptr) {
2225
return false;
2226
}
2227
const NBEdge::Connection& con = from->getConnection(fromLane, to, toLane);
2228
const NBEdge::Connection& prohibitorCon = from->getConnection(fromLaneFoe, to, toLane);
2229
return myRequest->mergeConflict(from, con, from, prohibitorCon, false);
2230
}
2231
2232
2233
bool
2234
NBNode::mergeConflict(const NBEdge* from, const NBEdge::Connection& con,
2235
const NBEdge* prohibitorFrom, const NBEdge::Connection& prohibitorCon, bool foes) const {
2236
if (myRequest == nullptr) {
2237
return false;
2238
}
2239
return myRequest->mergeConflict(from, con, prohibitorFrom, prohibitorCon, foes);
2240
}
2241
2242
bool
2243
NBNode::bidiConflict(const NBEdge* from, const NBEdge::Connection& con,
2244
const NBEdge* prohibitorFrom, const NBEdge::Connection& prohibitorCon, bool foes) const {
2245
if (myRequest == nullptr) {
2246
return false;
2247
}
2248
return myRequest->bidiConflict(from, con, prohibitorFrom, prohibitorCon, foes);
2249
}
2250
2251
bool
2252
NBNode::turnFoes(const NBEdge* from, const NBEdge* to, int fromLane,
2253
const NBEdge* from2, const NBEdge* to2, int fromLane2,
2254
bool lefthand) const {
2255
UNUSED_PARAMETER(lefthand);
2256
if (from != from2 || to == to2 || fromLane == fromLane2) {
2257
return false;
2258
}
2259
if (from->isTurningDirectionAt(to)
2260
|| from2->isTurningDirectionAt(to2)) {
2261
// XXX should warn if there are any non-turning connections left of this
2262
return false;
2263
}
2264
bool result = false;
2265
EdgeVector::const_iterator it = std::find(myAllEdges.begin(), myAllEdges.end(), from);
2266
if (fromLane < fromLane2) {
2267
// conflict if 'to' comes before 'to2' going clockwise starting at 'from'
2268
while (*it != to2) {
2269
if (*it == to) {
2270
result = true;
2271
}
2272
NBContHelper::nextCW(myAllEdges, it);
2273
}
2274
} else {
2275
// conflict if 'to' comes before 'to2' going counter-clockwise starting at 'from'
2276
while (*it != to2) {
2277
if (*it == to) {
2278
result = true;
2279
}
2280
NBContHelper::nextCCW(myAllEdges, it);
2281
}
2282
}
2283
/*
2284
if (result) {
2285
std::cout << "turnFoes node=" << getID()
2286
<< " from=" << from->getLaneID(fromLane)
2287
<< " to=" << to->getID()
2288
<< " from2=" << from2->getLaneID(fromLane2)
2289
<< " to2=" << to2->getID()
2290
<< "\n";
2291
}
2292
*/
2293
return result;
2294
}
2295
2296
2297
bool
2298
NBNode::isLeftMover(const NBEdge* const from, const NBEdge* const to) const {
2299
// when the junction has only one incoming edge, there are no
2300
// problems caused by left blockings
2301
if (myIncomingEdges.size() == 1 || myOutgoingEdges.size() == 1) {
2302
return false;
2303
}
2304
double fromAngle = from->getAngleAtNode(this);
2305
double toAngle = to->getAngleAtNode(this);
2306
double cw = GeomHelper::getCWAngleDiff(fromAngle, toAngle);
2307
double ccw = GeomHelper::getCCWAngleDiff(fromAngle, toAngle);
2308
std::vector<NBEdge*>::const_iterator i = std::find(myAllEdges.begin(), myAllEdges.end(), from);
2309
do {
2310
NBContHelper::nextCW(myAllEdges, i);
2311
} while ((!hasOutgoing(*i) || from->isTurningDirectionAt(*i)) && *i != from);
2312
return cw < ccw && (*i) == to && myOutgoingEdges.size() > 2;
2313
}
2314
2315
2316
bool
2317
NBNode::forbids(const NBEdge* const possProhibitorFrom, const NBEdge* const possProhibitorTo,
2318
const NBEdge* const possProhibitedFrom, const NBEdge* const possProhibitedTo,
2319
bool regardNonSignalisedLowerPriority) const {
2320
return myRequest != nullptr && myRequest->forbids(possProhibitorFrom, possProhibitorTo,
2321
possProhibitedFrom, possProhibitedTo,
2322
regardNonSignalisedLowerPriority);
2323
}
2324
2325
2326
bool
2327
NBNode::foes(const NBEdge* const from1, const NBEdge* const to1,
2328
const NBEdge* const from2, const NBEdge* const to2) const {
2329
return myRequest != nullptr && myRequest->foes(from1, to1, from2, to2);
2330
}
2331
2332
2333
void
2334
NBNode::remapRemoved(NBTrafficLightLogicCont& tc,
2335
NBEdge* removed, const EdgeVector& incoming,
2336
const EdgeVector& outgoing) {
2337
assert(find(incoming.begin(), incoming.end(), removed) == incoming.end());
2338
bool changed = true;
2339
while (changed) {
2340
changed = false;
2341
NBConnectionProhibits blockedConnectionsTmp = myBlockedConnections;
2342
NBConnectionProhibits blockedConnectionsNew;
2343
// remap in connections
2344
for (NBConnectionProhibits::iterator i = blockedConnectionsTmp.begin(); i != blockedConnectionsTmp.end(); i++) {
2345
const NBConnection& blocker = (*i).first;
2346
const NBConnectionVector& blocked = (*i).second;
2347
// check the blocked connections first
2348
// check whether any of the blocked must be changed
2349
bool blockedChanged = false;
2350
NBConnectionVector newBlocked;
2351
NBConnectionVector::const_iterator j;
2352
for (j = blocked.begin(); j != blocked.end(); j++) {
2353
const NBConnection& sblocked = *j;
2354
if (sblocked.getFrom() == removed || sblocked.getTo() == removed) {
2355
blockedChanged = true;
2356
}
2357
}
2358
// adapt changes if so
2359
for (j = blocked.begin(); blockedChanged && j != blocked.end(); j++) {
2360
const NBConnection& sblocked = *j;
2361
if (sblocked.getFrom() == removed && sblocked.getTo() == removed) {
2362
/* for(EdgeVector::const_iterator k=incoming.begin(); k!=incoming.end(); k++) {
2363
!!! newBlocked.push_back(NBConnection(*k, *k));
2364
}*/
2365
} else if (sblocked.getFrom() == removed) {
2366
assert(sblocked.getTo() != removed);
2367
for (EdgeVector::const_iterator k = incoming.begin(); k != incoming.end(); k++) {
2368
newBlocked.push_back(NBConnection(*k, sblocked.getTo()));
2369
}
2370
} else if (sblocked.getTo() == removed) {
2371
assert(sblocked.getFrom() != removed);
2372
for (EdgeVector::const_iterator k = outgoing.begin(); k != outgoing.end(); k++) {
2373
newBlocked.push_back(NBConnection(sblocked.getFrom(), *k));
2374
}
2375
} else {
2376
newBlocked.push_back(NBConnection(sblocked.getFrom(), sblocked.getTo()));
2377
}
2378
}
2379
if (blockedChanged) {
2380
blockedConnectionsNew[blocker] = newBlocked;
2381
changed = true;
2382
}
2383
// if the blocked were kept
2384
else {
2385
if (blocker.getFrom() == removed && blocker.getTo() == removed) {
2386
changed = true;
2387
/* for(EdgeVector::const_iterator k=incoming.begin(); k!=incoming.end(); k++) {
2388
!!! blockedConnectionsNew[NBConnection(*k, *k)] = blocked;
2389
}*/
2390
} else if (blocker.getFrom() == removed) {
2391
assert(blocker.getTo() != removed);
2392
changed = true;
2393
for (EdgeVector::const_iterator k = incoming.begin(); k != incoming.end(); k++) {
2394
blockedConnectionsNew[NBConnection(*k, blocker.getTo())] = blocked;
2395
}
2396
} else if (blocker.getTo() == removed) {
2397
assert(blocker.getFrom() != removed);
2398
changed = true;
2399
for (EdgeVector::const_iterator k = outgoing.begin(); k != outgoing.end(); k++) {
2400
blockedConnectionsNew[NBConnection(blocker.getFrom(), *k)] = blocked;
2401
}
2402
} else {
2403
blockedConnectionsNew[blocker] = blocked;
2404
}
2405
}
2406
}
2407
myBlockedConnections = blockedConnectionsNew;
2408
}
2409
// remap in traffic lights
2410
tc.remapRemoved(removed, incoming, outgoing);
2411
}
2412
2413
2414
NBEdge*
2415
NBNode::getNextCompatibleOutgoing(const NBEdge* incoming, SVCPermissions vehPerm, EdgeVector::const_iterator itOut, bool clockwise) const {
2416
EdgeVector::const_iterator i = itOut;
2417
while (*i != incoming) {
2418
if (clockwise) {
2419
NBContHelper::nextCW(myAllEdges, i);
2420
} else {
2421
NBContHelper::nextCCW(myAllEdges, i);
2422
}
2423
if ((*i)->getFromNode() != this) {
2424
// only look for outgoing edges
2425
// @note we use myAllEdges to stop at the incoming edge
2426
continue;
2427
}
2428
if (incoming->isTurningDirectionAt(*i)) {
2429
return nullptr;
2430
}
2431
if ((vehPerm & (*i)->getPermissions()) != 0 || vehPerm == 0) {
2432
return *i;
2433
}
2434
}
2435
return nullptr;
2436
}
2437
2438
2439
bool
2440
NBNode::isStraighter(const NBEdge* const incoming, const double angle, const SVCPermissions vehPerm, const int modeLanes, const NBEdge* const candidate) const {
2441
if (candidate != nullptr) {
2442
const double candAngle = NBHelpers::normRelAngle(incoming->getAngleAtNode(this), candidate->getAngleAtNode(this));
2443
// they are too similar it does not matter
2444
if (fabs(angle - candAngle) < 5.) {
2445
return false;
2446
}
2447
// the other edge is at least 5 degree straighter
2448
if (fabs(candAngle) < fabs(angle) - 5.) {
2449
return true;
2450
}
2451
if (fabs(angle) < fabs(candAngle) - 5.) {
2452
return false;
2453
}
2454
if (fabs(candAngle) < 44.) {
2455
// the lane count for the same modes is larger
2456
const int candModeLanes = candidate->getNumLanesThatAllow(vehPerm);
2457
if (candModeLanes > modeLanes) {
2458
return true;
2459
}
2460
if (candModeLanes < modeLanes) {
2461
return false;
2462
}
2463
// we would create a left turn
2464
if (candAngle < 0 && angle > 0) {
2465
return true;
2466
}
2467
if (angle < 0 && candAngle > 0) {
2468
return false;
2469
}
2470
}
2471
}
2472
return false;
2473
}
2474
2475
EdgeVector
2476
NBNode::getPassengerEdges(bool incoming) const {
2477
EdgeVector result;
2478
for (NBEdge* e : (incoming ? myIncomingEdges : myOutgoingEdges)) {
2479
if ((e->getPermissions() & SVC_PASSENGER) != 0) {
2480
result.push_back(e);
2481
}
2482
}
2483
return result;
2484
}
2485
2486
LinkDirection
2487
NBNode::getDirection(const NBEdge* const incoming, const NBEdge* const outgoing, bool leftHand) const {
2488
// ok, no connection at all -> dead end
2489
if (outgoing == nullptr) {
2490
return LinkDirection::NODIR;
2491
}
2492
assert(incoming->getToNode() == this);
2493
assert(outgoing->getFromNode() == this);
2494
if (incoming->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT && outgoing->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT) {
2495
return LinkDirection::STRAIGHT;
2496
}
2497
// turning direction
2498
if (incoming->isTurningDirectionAt(outgoing)) {
2499
if (isExplicitRailNoBidi(incoming, outgoing)) {
2500
return LinkDirection::STRAIGHT;
2501
}
2502
return leftHand ? LinkDirection::TURN_LEFTHAND : LinkDirection::TURN;
2503
}
2504
// get the angle between incoming/outgoing at the junction
2505
const double angle = NBHelpers::normRelAngle(incoming->getAngleAtNode(this), outgoing->getAngleAtNode(this));
2506
// ok, should be a straight connection
2507
EdgeVector::const_iterator itOut = std::find(myAllEdges.begin(), myAllEdges.end(), outgoing);
2508
SVCPermissions vehPerm = incoming->getPermissions() & outgoing->getPermissions();
2509
if (vehPerm != SVC_PEDESTRIAN) {
2510
vehPerm &= ~SVC_PEDESTRIAN;
2511
}
2512
const int modeLanes = outgoing->getNumLanesThatAllow(vehPerm);
2513
if (fabs(angle) < 44.) {
2514
if (fabs(angle) > 6.) {
2515
if (isStraighter(incoming, angle, vehPerm, modeLanes, getNextCompatibleOutgoing(incoming, vehPerm, itOut, true))) {
2516
return angle > 0 ? LinkDirection::PARTRIGHT : LinkDirection::PARTLEFT;
2517
}
2518
if (isStraighter(incoming, angle, vehPerm, modeLanes, getNextCompatibleOutgoing(incoming, vehPerm, itOut, false))) {
2519
return angle > 0 ? LinkDirection::PARTRIGHT : LinkDirection::PARTLEFT;
2520
}
2521
}
2522
if (angle > 0 && incoming->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT) {
2523
return angle > 15 ? LinkDirection::RIGHT : LinkDirection::PARTRIGHT;
2524
}
2525
return LinkDirection::STRAIGHT;
2526
}
2527
2528
if (angle > 0) {
2529
// check whether any other edge goes further to the right
2530
if (angle > 90 + NUMERICAL_EPS) {
2531
return LinkDirection::RIGHT;
2532
}
2533
NBEdge* outCW = getNextCompatibleOutgoing(incoming, vehPerm, itOut, !leftHand);
2534
if (outCW != nullptr) {
2535
return LinkDirection::PARTRIGHT;
2536
} else {
2537
return LinkDirection::RIGHT;
2538
}
2539
} else {
2540
// check whether any other edge goes further to the left
2541
if (angle < -170 && incoming->getGeometry().reverse() == outgoing->getGeometry()) {
2542
if (isExplicitRailNoBidi(incoming, outgoing)) {
2543
return LinkDirection::STRAIGHT;
2544
}
2545
return leftHand ? LinkDirection::TURN_LEFTHAND : LinkDirection::TURN;
2546
} else if (angle < -(90 + NUMERICAL_EPS)) {
2547
return LinkDirection::LEFT;
2548
}
2549
NBEdge* outCCW = getNextCompatibleOutgoing(incoming, vehPerm, itOut, leftHand);
2550
if (outCCW != nullptr) {
2551
return LinkDirection::PARTLEFT;
2552
} else {
2553
return LinkDirection::LEFT;
2554
}
2555
}
2556
}
2557
2558
2559
bool
2560
NBNode::isExplicitRailNoBidi(const NBEdge* incoming, const NBEdge* outgoing) {
2561
// assume explicit connections at sharp turn-arounds are either for reversal or due to a geometry glitch
2562
// (but should not have been guessed)
2563
// @note this function is also called from NBAlgorithms when there aren't any connections ready
2564
return (incoming->getStep() >= NBEdge::EdgeBuildingStep::LANES2LANES_RECHECK
2565
&& isRailway(incoming->getPermissions())
2566
&& isRailway(outgoing->getPermissions())
2567
&& incoming->getBidiEdge() != outgoing);
2568
}
2569
2570
2571
LinkState
2572
NBNode::getLinkState(const NBEdge* incoming, const NBEdge* outgoing, int fromLane, int toLane,
2573
bool mayDefinitelyPass, const std::string& tlID) const {
2574
if (myType == SumoXMLNodeType::RAIL_CROSSING && isRailway(incoming->getPermissions())) {
2575
return LINKSTATE_MAJOR; // the trains must run on time
2576
}
2577
if (tlID != "") {
2578
if (getRightOfWay() == RightOfWay::ALLWAYSTOP) {
2579
return LINKSTATE_ALLWAY_STOP;
2580
}
2581
return mustBrake(incoming, outgoing, fromLane, toLane, true) ? LINKSTATE_TL_OFF_BLINKING : LINKSTATE_TL_OFF_NOSIGNAL;
2582
}
2583
if (outgoing == nullptr) { // always off
2584
return LINKSTATE_TL_OFF_NOSIGNAL;
2585
}
2586
if ((myType == SumoXMLNodeType::RIGHT_BEFORE_LEFT || myType == SumoXMLNodeType::LEFT_BEFORE_RIGHT)
2587
&& mustBrake(incoming, outgoing, fromLane, toLane, true)) {
2588
return LINKSTATE_EQUAL; // all the same
2589
}
2590
if (myType == SumoXMLNodeType::ALLWAY_STOP) {
2591
return LINKSTATE_ALLWAY_STOP; // all drive, first one to arrive may drive first
2592
}
2593
if (myType == SumoXMLNodeType::ZIPPER && zipperConflict(incoming, outgoing, fromLane, toLane)) {
2594
return LINKSTATE_ZIPPER;
2595
}
2596
if (!mayDefinitelyPass
2597
&& mustBrake(incoming, outgoing, fromLane, toLane, true)
2598
// legacy mode
2599
&& (!incoming->isInsideTLS() || getDirection(incoming, outgoing) != LinkDirection::STRAIGHT)
2600
// avoid linkstate minor at pure railway nodes
2601
&& (!NBNodeTypeComputer::isRailwayNode(this) || unsignalizedOperation())) {
2602
return myType == SumoXMLNodeType::PRIORITY_STOP && incoming->getJunctionPriority(this) == NBEdge::JunctionPriority::MINOR_ROAD ? LINKSTATE_STOP : LINKSTATE_MINOR; // minor road
2603
}
2604
// traffic lights are not regarded here
2605
return LINKSTATE_MAJOR;
2606
}
2607
2608
2609
bool
2610
NBNode::zipperConflict(const NBEdge* incoming, const NBEdge* outgoing, int fromLane, int toLane) const {
2611
if (mustBrake(incoming, outgoing, fromLane, toLane, false)) {
2612
// there should be another connection with the same target (not just some intersecting trajectories)
2613
for (const NBEdge* in : getIncomingEdges()) {
2614
for (const NBEdge::Connection& c : in->getConnections()) {
2615
if ((in != incoming || c.fromLane != fromLane) && c.toEdge == outgoing && c.toLane == toLane) {
2616
return true;
2617
}
2618
}
2619
}
2620
}
2621
return false;
2622
}
2623
2624
2625
bool
2626
NBNode::unsignalizedOperation() const {
2627
SVCPermissions railClasses = 0;
2628
for (NBEdge* e : myIncomingEdges) {
2629
railClasses |= (e->getPermissions() & SVC_RAIL_CLASSES);
2630
}
2631
assert(railClasses != 0);
2632
return ((railClasses & myPermitUnsignalizedClasses) == railClasses
2633
&& (railClasses & myHaveRailSignalClasses) == 0);
2634
}
2635
2636
2637
void
2638
NBNode::initRailSignalClasses(const NBNodeCont& nc) {
2639
myPermitUnsignalizedClasses = parseVehicleClasses(OptionsCont::getOptions().getStringVector("railway.signal.permit-unsignalized"));
2640
myHaveRailSignalClasses = 0;
2641
for (auto it : nc) {
2642
const NBNode* n = it.second;
2643
if (n->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
2644
for (const NBEdge* in : n->getIncomingEdges()) {
2645
myHaveRailSignalClasses |= in->getPermissions();
2646
}
2647
}
2648
}
2649
}
2650
2651
2652
bool
2653
NBNode::checkIsRemovable() const {
2654
std::string reason;
2655
return checkIsRemovableReporting(reason);
2656
}
2657
2658
bool
2659
NBNode::checkIsRemovableReporting(std::string& reason) const {
2660
if (getEdges().empty()) {
2661
return true;
2662
}
2663
// check whether this node is included in a traffic light or crossing
2664
if (myTrafficLights.size() != 0) {
2665
reason = "TLS";
2666
return false;
2667
}
2668
if (myType == SumoXMLNodeType::RAIL_SIGNAL) {
2669
reason = "rail_signal";
2670
return false;
2671
}
2672
if (myCrossings.size() != 0) {
2673
reason = "crossing";
2674
return false;
2675
}
2676
EdgeVector::const_iterator i;
2677
// one in, one out -> just a geometry ...
2678
if (myOutgoingEdges.size() == 1 && myIncomingEdges.size() == 1) {
2679
// ... if types match ...
2680
if (!myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason)) {
2681
reason = "edges incompatible: " + reason;
2682
return false;
2683
}
2684
if (myIncomingEdges[0]->getTurnDestination(true) == myOutgoingEdges[0]) {
2685
reason = "turnaround";
2686
return false;
2687
}
2688
return true;
2689
}
2690
// two in, two out -> may be something else
2691
if (myOutgoingEdges.size() == 2 && myIncomingEdges.size() == 2) {
2692
// check whether the origin nodes of the incoming edges differ
2693
std::set<NBNode*> origSet;
2694
for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2695
origSet.insert((*i)->getFromNode());
2696
}
2697
if (origSet.size() < 2) {
2698
// overlapping case
2699
if (myIncomingEdges[0]->getGeometry() == myIncomingEdges[1]->getGeometry() &&
2700
myOutgoingEdges[0]->getGeometry() == myOutgoingEdges[1]->getGeometry()) {
2701
return ((myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason) &&
2702
myIncomingEdges[1]->expandableBy(myOutgoingEdges[1], reason))
2703
|| (myIncomingEdges[0]->expandableBy(myOutgoingEdges[1], reason) &&
2704
myIncomingEdges[1]->expandableBy(myOutgoingEdges[0], reason)));
2705
}
2706
}
2707
// check whether this node is an intermediate node of
2708
// a two-directional street
2709
for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2710
// each of the edges must have an opposite direction edge
2711
NBEdge* opposite = (*i)->getTurnDestination(true);
2712
if (opposite != nullptr) {
2713
// the other outgoing edges must be the continuation of the current
2714
NBEdge* continuation = opposite == myOutgoingEdges.front() ? myOutgoingEdges.back() : myOutgoingEdges.front();
2715
// check whether the types allow joining
2716
if (!(*i)->expandableBy(continuation, reason)) {
2717
reason = "edges incompatible: " + reason;
2718
return false;
2719
}
2720
} else {
2721
// ok, at least one outgoing edge is not an opposite
2722
// of an incoming one
2723
reason = "not opposites";
2724
return false;
2725
}
2726
}
2727
return true;
2728
}
2729
// ok, a real node
2730
reason = "intersection";
2731
return false;
2732
}
2733
2734
2735
std::vector<std::pair<NBEdge*, NBEdge*> >
2736
NBNode::getEdgesToJoin() const {
2737
assert(checkIsRemovable());
2738
std::vector<std::pair<NBEdge*, NBEdge*> > ret;
2739
// one in, one out-case
2740
if (myOutgoingEdges.size() == 1 && myIncomingEdges.size() == 1) {
2741
ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[0]));
2742
return ret;
2743
}
2744
if (myIncomingEdges.size() == 2 && myOutgoingEdges.size() == 2) {
2745
// two in, two out-case
2746
if (myIncomingEdges[0]->getGeometry() == myIncomingEdges[1]->getGeometry() &&
2747
myOutgoingEdges[0]->getGeometry() == myOutgoingEdges[1]->getGeometry()) {
2748
// overlapping edges
2749
std::string reason;
2750
if (myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason)) {
2751
ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[0]));
2752
ret.push_back(std::make_pair(myIncomingEdges[1], myOutgoingEdges[1]));
2753
} else {
2754
ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[1]));
2755
ret.push_back(std::make_pair(myIncomingEdges[1], myOutgoingEdges[0]));
2756
}
2757
return ret;
2758
}
2759
}
2760
for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2761
// join with the edge that is not a turning direction
2762
NBEdge* opposite = (*i)->getTurnDestination(true);
2763
assert(opposite != 0);
2764
NBEdge* continuation = opposite == myOutgoingEdges.front() ? myOutgoingEdges.back() : myOutgoingEdges.front();
2765
ret.push_back(std::pair<NBEdge*, NBEdge*>(*i, continuation));
2766
}
2767
return ret;
2768
}
2769
2770
2771
const PositionVector&
2772
NBNode::getShape() const {
2773
return myPoly;
2774
}
2775
2776
2777
void
2778
NBNode::setCustomShape(const PositionVector& shape) {
2779
myPoly = shape;
2780
myHaveCustomPoly = (myPoly.size() > 1);
2781
if (myHaveCustomPoly) {
2782
for (EdgeVector::iterator i = myAllEdges.begin(); i != myAllEdges.end(); i++) {
2783
(*i)->resetNodeBorder(this);
2784
}
2785
}
2786
}
2787
2788
2789
NBEdge*
2790
NBNode::getConnectionTo(NBNode* n) const {
2791
for (NBEdge* e : myOutgoingEdges) {
2792
if (e->getToNode() == n && e->getPermissions() != 0) {
2793
return e;
2794
}
2795
}
2796
return nullptr;
2797
}
2798
2799
2800
bool
2801
NBNode::isNearDistrict() const {
2802
if (isDistrict()) {
2803
return false;
2804
}
2805
for (const NBEdge* const t : getEdges()) {
2806
const NBNode* const other = t->getToNode() == this ? t->getFromNode() : t->getToNode();
2807
for (const NBEdge* const k : other->getEdges()) {
2808
if (k->getFromNode()->isDistrict() || k->getToNode()->isDistrict()) {
2809
return true;
2810
}
2811
}
2812
}
2813
return false;
2814
}
2815
2816
2817
bool
2818
NBNode::isDistrict() const {
2819
return myType == SumoXMLNodeType::DISTRICT;
2820
}
2821
2822
2823
int
2824
NBNode::guessCrossings() {
2825
#ifdef DEBUG_PED_STRUCTURES
2826
gDebugFlag1 = DEBUGCOND;
2827
#endif
2828
int numGuessed = 0;
2829
if (myCrossings.size() > 0 || myDiscardAllCrossings) {
2830
// user supplied crossings, do not guess
2831
return numGuessed;
2832
}
2833
DEBUGCOUT(gDebugFlag1, "guess crossings for " << getID() << "\n")
2834
EdgeVector allEdges = getEdgesSortedByAngleAtNodeCenter();
2835
// check for pedestrial lanes going clockwise around the node
2836
std::vector<std::pair<NBEdge*, bool> > normalizedLanes;
2837
for (EdgeVector::const_iterator it = allEdges.begin(); it != allEdges.end(); ++it) {
2838
NBEdge* edge = *it;
2839
const std::vector<NBEdge::Lane>& lanes = edge->getLanes();
2840
if (edge->getFromNode() == this) {
2841
for (std::vector<NBEdge::Lane>::const_reverse_iterator it_l = lanes.rbegin(); it_l != lanes.rend(); ++it_l) {
2842
normalizedLanes.push_back(std::make_pair(edge, ((*it_l).permissions & SVC_PEDESTRIAN) != 0));
2843
}
2844
} else {
2845
for (std::vector<NBEdge::Lane>::const_iterator it_l = lanes.begin(); it_l != lanes.end(); ++it_l) {
2846
normalizedLanes.push_back(std::make_pair(edge, ((*it_l).permissions & SVC_PEDESTRIAN) != 0));
2847
}
2848
}
2849
}
2850
// do we even have a pedestrian lane?
2851
int firstSidewalk = -1;
2852
for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
2853
if (normalizedLanes[i].second) {
2854
firstSidewalk = i;
2855
break;
2856
}
2857
}
2858
int hadCandidates = 0;
2859
std::vector<int> connectedCandidates; // number of crossings that were built for each connected candidate
2860
if (firstSidewalk != -1) {
2861
// rotate lanes to ensure that the first one allows pedestrians
2862
std::vector<std::pair<NBEdge*, bool> > tmp;
2863
copy(normalizedLanes.begin() + firstSidewalk, normalizedLanes.end(), std::back_inserter(tmp));
2864
copy(normalizedLanes.begin(), normalizedLanes.begin() + firstSidewalk, std::back_inserter(tmp));
2865
normalizedLanes = tmp;
2866
// find candidates
2867
EdgeVector candidates;
2868
for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
2869
NBEdge* edge = normalizedLanes[i].first;
2870
const bool allowsPed = normalizedLanes[i].second;
2871
DEBUGCOUT(gDebugFlag1, " cands=" << toString(candidates) << " edge=" << edge->getID() << " allowsPed=" << allowsPed << "\n")
2872
if (!allowsPed && (candidates.size() == 0 || candidates.back() != edge)) {
2873
candidates.push_back(edge);
2874
} else if (allowsPed) {
2875
if (candidates.size() > 0) {
2876
if (hadCandidates > 0 || forbidsPedestriansAfter(normalizedLanes, i)) {
2877
hadCandidates++;
2878
const int n = checkCrossing(candidates);
2879
numGuessed += n;
2880
if (n > 0) {
2881
connectedCandidates.push_back(n);
2882
}
2883
}
2884
candidates.clear();
2885
}
2886
}
2887
}
2888
if (hadCandidates > 0 && candidates.size() > 0) {
2889
// avoid wrapping around to the same sidewalk
2890
hadCandidates++;
2891
const int n = checkCrossing(candidates);
2892
numGuessed += n;
2893
if (n > 0) {
2894
connectedCandidates.push_back(n);
2895
}
2896
}
2897
}
2898
// Avoid duplicate crossing between the same pair of walkingareas
2899
DEBUGCOUT(gDebugFlag1, " hadCandidates=" << hadCandidates << " connectedCandidates=" << toString(connectedCandidates) << "\n")
2900
if (hadCandidates == 2 && connectedCandidates.size() == 2) {
2901
// One or both of them might be split: remove the one with less splits
2902
if (connectedCandidates.back() <= connectedCandidates.front()) {
2903
numGuessed -= connectedCandidates.back();
2904
myCrossings.erase(myCrossings.end() - connectedCandidates.back(), myCrossings.end());
2905
} else {
2906
numGuessed -= connectedCandidates.front();
2907
myCrossings.erase(myCrossings.begin(), myCrossings.begin() + connectedCandidates.front());
2908
}
2909
}
2910
std::sort(myCrossings.begin(), myCrossings.end(), NBNodesEdgesSorter::crossing_by_junction_angle_sorter(this, myAllEdges));
2911
#ifdef DEBUG_PED_STRUCTURES
2912
if (gDebugFlag1) {
2913
std::cout << "guessedCrossings:\n";
2914
for (auto& crossing : myCrossings) {
2915
std::cout << " edges=" << toString(crossing->edges) << "\n";
2916
}
2917
}
2918
#endif
2919
if (numGuessed > 0 && isSimpleContinuation(true, true)) {
2920
// avoid narrow node shape when there is a crossing
2921
computeNodeShape(-1);
2922
for (NBEdge* e : myAllEdges) {
2923
e->computeEdgeShape();
2924
}
2925
}
2926
return numGuessed;
2927
}
2928
2929
2930
int
2931
NBNode::checkCrossing(EdgeVector candidates, bool checkOnly) {
2932
DEBUGCOUT(gDebugFlag1, "checkCrossing candidates=" << toString(candidates) << "\n")
2933
if (candidates.size() == 0) {
2934
DEBUGCOUT(gDebugFlag1, "no crossing added (numCandidates=" << candidates.size() << ")\n")
2935
return 0;
2936
} else {
2937
// check whether the edges may be part of a common crossing due to having similar angle
2938
double prevAngle = -100000; // dummy
2939
for (int i = 0; i < (int)candidates.size(); ++i) {
2940
NBEdge* edge = candidates[i];
2941
double angle = edge->getCrossingAngle(this);
2942
// edges should be sorted by angle but this only holds true approximately
2943
if (i > 0 && fabs(NBHelpers::relAngle(angle, prevAngle)) > EXTEND_CROSSING_ANGLE_THRESHOLD) {
2944
DEBUGCOUT(gDebugFlag1, "no crossing added (found angle difference of " << fabs(NBHelpers::relAngle(angle, prevAngle)) << " at i=" << i << "\n")
2945
return 0;
2946
}
2947
if (!checkOnly && !isTLControlled() && myType != SumoXMLNodeType::RAIL_CROSSING && edge->getSpeed() > OptionsCont::getOptions().getFloat("crossings.guess.speed-threshold")) {
2948
DEBUGCOUT(gDebugFlag1, "no crossing added (uncontrolled, edge with speed > " << edge->getSpeed() << ")\n")
2949
return 0;
2950
}
2951
prevAngle = angle;
2952
}
2953
if (candidates.size() == 1 || getType() == SumoXMLNodeType::RAIL_CROSSING) {
2954
if (!checkOnly) {
2955
addCrossing(candidates, NBEdge::UNSPECIFIED_WIDTH, isTLControlled()
2956
|| (isRoundabout() && OptionsCont::getOptions().getBool("crossings.guess.roundabout-priority")));
2957
DEBUGCOUT(gDebugFlag1, "adding crossing: " << toString(candidates) << "\n")
2958
}
2959
return 1;
2960
} else {
2961
// check for intermediate walking areas
2962
prevAngle = -100000; // dummy
2963
for (EdgeVector::iterator it = candidates.begin(); it != candidates.end(); ++it) {
2964
double angle = (*it)->getCrossingAngle(this);
2965
if (it != candidates.begin()) {
2966
NBEdge* prev = *(it - 1);
2967
NBEdge* curr = *it;
2968
Position prevPos, currPos;
2969
int laneI;
2970
// compute distance between candiate edges
2971
double intermediateWidth = 0;
2972
if (prev->getToNode() == this) {
2973
laneI = prev->getNumLanes() - 1;
2974
prevPos = prev->getLanes()[laneI].shape[-1];
2975
} else {
2976
laneI = 0;
2977
prevPos = prev->getLanes()[laneI].shape[0];
2978
}
2979
intermediateWidth -= 0.5 * prev->getLaneWidth(laneI);
2980
if (curr->getFromNode() == this) {
2981
laneI = curr->getNumLanes() - 1;
2982
currPos = curr->getLanes()[laneI].shape[0];
2983
} else {
2984
laneI = 0;
2985
currPos = curr->getLanes()[laneI].shape[-1];
2986
}
2987
intermediateWidth -= 0.5 * curr->getLaneWidth(laneI);
2988
intermediateWidth += currPos.distanceTo2D(prevPos);
2989
DEBUGCOUT(gDebugFlag1, " prevAngle=" << prevAngle << " angle=" << angle << " intermediateWidth=" << intermediateWidth << "\n")
2990
if (fabs(NBHelpers::relAngle(prevAngle, angle)) > SPLIT_CROSSING_ANGLE_THRESHOLD
2991
|| (intermediateWidth > SPLIT_CROSSING_WIDTH_THRESHOLD)) {
2992
return checkCrossing(EdgeVector(candidates.begin(), it), checkOnly)
2993
+ checkCrossing(EdgeVector(it, candidates.end()), checkOnly);
2994
}
2995
}
2996
prevAngle = angle;
2997
}
2998
if (!checkOnly) {
2999
addCrossing(candidates, NBEdge::UNSPECIFIED_WIDTH, isTLControlled()
3000
|| (isRoundabout() && OptionsCont::getOptions().getBool("crossings.guess.roundabout-priority")));
3001
DEBUGCOUT(gDebugFlag1, "adding crossing: " << toString(candidates) << "\n")
3002
}
3003
return 1;
3004
}
3005
}
3006
}
3007
3008
3009
bool
3010
NBNode::checkCrossingDuplicated(EdgeVector edges) {
3011
// sort edge vector
3012
std::sort(edges.begin(), edges.end());
3013
// iterate over crossing to find a crossing with the same edges
3014
for (auto& crossing : myCrossings) {
3015
// sort edges of crossing before compare
3016
EdgeVector edgesOfCrossing = crossing->edges;
3017
std::sort(edgesOfCrossing.begin(), edgesOfCrossing.end());
3018
if (edgesOfCrossing == edges) {
3019
return true;
3020
}
3021
}
3022
return false;
3023
}
3024
3025
3026
bool
3027
NBNode::forbidsPedestriansAfter(std::vector<std::pair<NBEdge*, bool> > normalizedLanes, int startIndex) {
3028
for (int i = startIndex; i < (int)normalizedLanes.size(); ++i) {
3029
if (!normalizedLanes[i].second) {
3030
return true;
3031
}
3032
}
3033
return false;
3034
}
3035
3036
3037
void
3038
NBNode::buildCrossingsAndWalkingAreas() {
3039
buildCrossings();
3040
buildWalkingAreas(OptionsCont::getOptions().getInt("junctions.corner-detail"),
3041
OptionsCont::getOptions().getFloat("walkingareas.join-dist"));
3042
buildCrossingOutlines();
3043
// ensure that all crossings are properly connected
3044
bool recheck = myCrossings.size() > 0;
3045
while (recheck) {
3046
recheck = false;
3047
std::set<std::string> waIDs;
3048
int numSidewalks = 0;
3049
for (WalkingArea& wa : myWalkingAreas) {
3050
waIDs.insert(wa.id);
3051
numSidewalks += (int)(wa.prevSidewalks.size() + wa.nextSidewalks.size());
3052
}
3053
if (numSidewalks < 2) {
3054
// all crossings are invalid if there are fewer than 2 sidewalks involved
3055
waIDs.clear();
3056
}
3057
for (auto& crossing : myCrossings) {
3058
if (waIDs.count(crossing->prevWalkingArea) == 0 || waIDs.count(crossing->nextWalkingArea) == 0 || !crossing->valid) {
3059
if (crossing->valid) {
3060
WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (no walkingarea found)."),
3061
crossing->id, getID(), toString(crossing->edges));
3062
recheck = true;
3063
}
3064
for (auto waIt = myWalkingAreas.begin(); waIt != myWalkingAreas.end();) {
3065
WalkingArea& wa = *waIt;
3066
std::vector<std::string>::iterator it_nc = std::find(wa.nextCrossings.begin(), wa.nextCrossings.end(), crossing->id);
3067
if (it_nc != wa.nextCrossings.end()) {
3068
wa.nextCrossings.erase(it_nc);
3069
}
3070
if (wa.prevSidewalks.size() + wa.nextSidewalks.size() + wa.nextCrossings.size() + wa.prevCrossings.size() < 2) {
3071
waIt = myWalkingAreas.erase(waIt);
3072
recheck = true;
3073
} else {
3074
waIt++;
3075
}
3076
}
3077
crossing->valid = false;
3078
crossing->prevWalkingArea = "";
3079
crossing->nextWalkingArea = "";
3080
}
3081
}
3082
}
3083
}
3084
3085
3086
std::vector<NBNode::Crossing*>
3087
NBNode::getCrossings() const {
3088
std::vector<Crossing*> result;
3089
for (auto& c : myCrossings) {
3090
if (c->valid) {
3091
result.push_back(c.get());
3092
}
3093
}
3094
//if (myCrossings.size() > 0) {
3095
// std::cout << "valid crossings at " << getID() << "\n";
3096
// for (std::vector<NBNode::Crossing*>::const_iterator it = result.begin(); it != result.end(); ++it) {
3097
// std::cout << " " << toString((*it)->edges) << "\n";
3098
// }
3099
//}
3100
return result;
3101
}
3102
3103
3104
void
3105
NBNode::discardAllCrossings(bool rejectAll) {
3106
myCrossings.clear();
3107
// also discard all further crossings
3108
if (rejectAll) {
3109
myDiscardAllCrossings = true;
3110
}
3111
}
3112
3113
3114
void
3115
NBNode::discardWalkingareas() {
3116
myWalkingAreas.clear();
3117
}
3118
3119
3120
double
3121
NBNode::buildInnerEdges() {
3122
// myDisplacementError is computed during this operation. reset first
3123
myDisplacementError = 0.;
3124
// build inner edges for vehicle movements across the junction
3125
int noInternalNoSplits = 0;
3126
for (const NBEdge* const edge : myIncomingEdges) {
3127
for (const NBEdge::Connection& con : edge->getConnections()) {
3128
if (con.toEdge == nullptr) {
3129
continue;
3130
}
3131
noInternalNoSplits++;
3132
}
3133
}
3134
int lno = 0;
3135
int splitNo = 0;
3136
double maxCrossingSeconds = 0.;
3137
for (NBEdge* const edge : myIncomingEdges) {
3138
maxCrossingSeconds = MAX2(maxCrossingSeconds, edge->buildInnerEdges(*this, noInternalNoSplits, lno, splitNo));
3139
}
3140
return maxCrossingSeconds;
3141
}
3142
3143
3144
int
3145
NBNode::buildCrossings() {
3146
#ifdef DEBUG_PED_STRUCTURES
3147
gDebugFlag1 = DEBUGCOND;
3148
#endif
3149
DEBUGCOUT(gDebugFlag1, "build crossings for " << getID() << ":\n")
3150
if (myDiscardAllCrossings) {
3151
myCrossings.clear();
3152
}
3153
int index = 0;
3154
const double defaultWidth = OptionsCont::getOptions().getFloat("default.crossing-width");
3155
for (auto& c : myCrossings) {
3156
c->valid = true;
3157
if (!isTLControlled()) {
3158
c->tlID = ""; // reset for Netedit, set via setCrossingTLIndices()
3159
}
3160
c->id = ":" + getID() + "_c" + toString(index++);
3161
c->width = (c->customWidth == NBEdge::UNSPECIFIED_WIDTH) ? defaultWidth : c->customWidth;
3162
// reset fields, so repeated computation (Netedit) will successfully perform the checks
3163
// in buildWalkingAreas (split crossings) and buildInnerEdges (sanity check)
3164
c->nextWalkingArea = "";
3165
c->prevWalkingArea = "";
3166
EdgeVector& edges = c->edges;
3167
DEBUGCOUT(gDebugFlag1, " crossing=" << c->id << " edges=" << toString(edges))
3168
// sorting the edges in the right way is imperative. We want to sort
3169
// them by getAngleAtNodeToCenter() but need to be extra carefull to avoid wrapping around 0 somewhere in between
3170
std::sort(edges.begin(), edges.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
3171
DEBUGCOUT(gDebugFlag1, " sortedEdges=" << toString(edges) << "\n")
3172
// rotate the edges so that the largest relative angle difference comes at the end
3173
std::vector<double> rawAngleDiffs;
3174
double maxAngleDiff = 0;
3175
int maxAngleDiffIndex = 0; // index before maxDist
3176
for (int i = 0; i < (int) edges.size(); i++) {
3177
double diff = NBHelpers::relAngle(edges[i]->getAngleAtNodeToCenter(this),
3178
edges[(i + 1) % edges.size()]->getAngleAtNodeToCenter(this));
3179
if (diff < 0) {
3180
diff += 360;
3181
}
3182
const double rawDiff = NBHelpers::relAngle(
3183
edges[i]->getAngleAtNodeNormalized(this),
3184
edges[(i + 1) % edges.size()]->getAngleAtNodeNormalized(this));
3185
rawAngleDiffs.push_back(fabs(rawDiff));
3186
3187
DEBUGCOUT(gDebugFlag1, " i=" << i << " a1=" << edges[i]->getAngleAtNodeToCenter(this) << " a2=" << edges[(i + 1) % edges.size()]->getAngleAtNodeToCenter(this) << " diff=" << diff << "\n")
3188
if (diff > maxAngleDiff) {
3189
maxAngleDiff = diff;
3190
maxAngleDiffIndex = i;
3191
}
3192
}
3193
if (maxAngleDiff > 2 && maxAngleDiff < 360 - 2) {
3194
// if the angle differences is too small, we better not rotate
3195
std::rotate(edges.begin(), edges.begin() + (maxAngleDiffIndex + 1) % edges.size(), edges.end());
3196
DEBUGCOUT(gDebugFlag1, " rotatedEdges=" << toString(edges))
3197
}
3198
bool diagonalCrossing = false;
3199
std::sort(rawAngleDiffs.begin(), rawAngleDiffs.end());
3200
if (rawAngleDiffs.size() >= 2 && rawAngleDiffs[rawAngleDiffs.size() - 2] > 30) {
3201
diagonalCrossing = true;
3202
#ifdef DEBUG_PED_STRUCTURES
3203
if (gDebugFlag1) {
3204
std::cout << " detected pedScramble " << c->id << " edges=" << toString(edges) << " rawDiffs=" << toString(rawAngleDiffs) << "\n";
3205
for (auto e : edges) {
3206
std::cout << " e=" << e->getID()
3207
<< " aC=" << e->getAngleAtNodeToCenter(this)
3208
<< " a=" << e->getAngleAtNode(this)
3209
<< " aN=" << e->getAngleAtNodeNormalized(this)
3210
<< "\n";
3211
}
3212
}
3213
#endif
3214
}
3215
// reverse to get them in CCW order (walking direction around the node)
3216
std::reverse(edges.begin(), edges.end());
3217
// compute shape
3218
c->shape.clear();
3219
const int begDir = (edges.front()->getFromNode() == this ? FORWARD : BACKWARD);
3220
const int endDir = (edges.back()->getToNode() == this ? FORWARD : BACKWARD);
3221
int firstNonPedLane = edges.front()->getFirstNonPedestrianLaneIndex(begDir);
3222
int lastNonPedLane = edges.back()->getFirstNonPedestrianLaneIndex(endDir);
3223
DEBUGCOUT(gDebugFlag1, " finalEdges=" << toString(edges) << " firstNonPedLane=" << firstNonPedLane << " lastNonPedLane=" << lastNonPedLane << "\n")
3224
if (firstNonPedLane < 0 || lastNonPedLane < 0) {
3225
// invalid crossing
3226
WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (no vehicle lanes to cross)."), c->id, getID(), toString(c->edges));
3227
c->valid = false;
3228
// compute surrogate shape to make it visible in netedit
3229
firstNonPedLane = begDir == FORWARD ? 0 : edges.front()->getNumLanes() - 1;
3230
lastNonPedLane = endDir == FORWARD ? 0 : edges.back()->getNumLanes() - 1;
3231
}
3232
if (c->customShape.size() != 0) {
3233
c->shape = c->customShape;
3234
} else {
3235
NBEdge::Lane crossingBeg = edges.front()->getLanes()[firstNonPedLane];
3236
NBEdge::Lane crossingEnd = edges.back()->getLanes()[lastNonPedLane];
3237
crossingBeg.width = (crossingBeg.width == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : crossingBeg.width);
3238
crossingEnd.width = (crossingEnd.width == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : crossingEnd.width);
3239
crossingBeg.shape.move2side(begDir * crossingBeg.width / 2);
3240
crossingEnd.shape.move2side(endDir * crossingEnd.width / 2);
3241
double offset = c->width / 2;
3242
patchOffset_pathAcrossStreet(offset);
3243
crossingBeg.shape.extrapolate(offset);
3244
crossingEnd.shape.extrapolate(offset);
3245
// check if after all changes shape are NAN (in these case, discard)
3246
if (crossingBeg.shape.isNAN() || crossingEnd.shape.isNAN()) {
3247
WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (invalid shape)."), c->id, getID(), toString(c->edges));
3248
c->valid = false;
3249
} else {
3250
c->shape.push_back(crossingBeg.shape[begDir == FORWARD ? 0 : -1]);
3251
c->shape.push_back(crossingEnd.shape[endDir == FORWARD ? -1 : 0]);
3252
}
3253
if (diagonalCrossing) {
3254
c->shape.move2side(-c->width);
3255
}
3256
}
3257
}
3258
return index;
3259
}
3260
3261
3262
void
3263
NBNode::patchOffset_pathAcrossStreet(double& offset) {
3264
if (myCrossings.size() == 1 && myAllEdges.size() >= 3) {
3265
EdgeVector nonPedIncoming;
3266
EdgeVector nonPedOutgoing;
3267
EdgeVector pedIncoming;
3268
EdgeVector pedOutgoing;
3269
for (NBEdge* e : getIncomingEdges()) {
3270
if (e->getPermissions() != SVC_PEDESTRIAN) {
3271
nonPedIncoming.push_back(e);
3272
} else {
3273
pedIncoming.push_back(e);
3274
}
3275
}
3276
for (NBEdge* e : getOutgoingEdges()) {
3277
if (e->getPermissions() != SVC_PEDESTRIAN) {
3278
nonPedOutgoing.push_back(e);
3279
} else {
3280
pedOutgoing.push_back(e);
3281
}
3282
}
3283
if (geometryLike(nonPedIncoming, nonPedOutgoing) && (pedIncoming.size() > 0 || pedOutgoing.size() > 0)) {
3284
double maxAngle = 0;
3285
double inWidth = 0;
3286
double outWidth = 0;
3287
NBEdge* in = nonPedIncoming.front();
3288
NBEdge* out = nonPedOutgoing.front();
3289
if (nonPedIncoming.size() == 1) {
3290
maxAngle = fabs(NBHelpers::relAngle(in->getAngleAtNode(this), out->getAngleAtNode(this)));
3291
inWidth = in->getTotalWidth();
3292
outWidth = out->getTotalWidth();
3293
3294
} else {
3295
for (NBEdge* in2 : nonPedIncoming) {
3296
double minAngle = 180;
3297
for (NBEdge* out2 : nonPedOutgoing) {
3298
double angle = fabs(NBHelpers::relAngle(in2->getAngleAtNode(this), out2->getAngleAtNode(this)));
3299
if (angle < minAngle) {
3300
minAngle = angle;
3301
in = in2;
3302
out = out2;
3303
inWidth += in->getTotalWidth();
3304
outWidth += out->getTotalWidth();
3305
}
3306
}
3307
maxAngle = MAX2(maxAngle, minAngle);
3308
}
3309
}
3310
// changing the offset only handles the simple case where the road stays straight and keeps its width
3311
if (maxAngle < 15 && inWidth == outWidth) {
3312
int inLane = in->getFirstNonPedestrianLaneIndex(FORWARD);
3313
int outLane = out->getFirstNonPedestrianLaneIndex(FORWARD);
3314
if (inLane >= 0 && outLane >= 0) {
3315
Position p0 = in->getLaneShape(inLane).back();
3316
Position p1 = out->getLaneShape(outLane).front();
3317
PositionVector road;
3318
road.push_back(p0);
3319
road.push_back(p1);
3320
Position mid = (p0 + p1) / 2;
3321
double maxPathDist = 0;
3322
for (NBEdge* e : pedIncoming) {
3323
Position roadPos = road.positionAtOffset2D(road.nearest_offset_to_point2D(e->getLaneShape(0).back()));
3324
maxPathDist = MAX2(maxPathDist, mid.distanceTo2D(roadPos));
3325
}
3326
for (NBEdge* e : pedOutgoing) {
3327
Position roadPos = road.positionAtOffset2D(road.nearest_offset_to_point2D(e->getLaneShape(0).front()));
3328
maxPathDist = MAX2(maxPathDist, mid.distanceTo2D(roadPos));
3329
}
3330
// if the junction is stretched, the crossing should stay close to the paths
3331
if (maxPathDist < myCrossings.front()->width) {
3332
offset = p0.distanceTo2D(p1) / 2;
3333
} else {
3334
//std::cout << getID() << " maxPathDist=" << maxPathDist << "\n";
3335
}
3336
}
3337
} else {
3338
//std::cout << getID() << " maxAngle=" << maxAngle << " inWidth=" << inWidth << " outWidth=" << outWidth << "\n";
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