Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netbuild/NBAlgorithms_Railway.cpp
193735 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2012-2026 German Aerospace Center (DLR) and others.
4
// This program and the accompanying materials are made available under the
5
// terms of the Eclipse Public License 2.0 which is available at
6
// https://www.eclipse.org/legal/epl-2.0/
7
// This Source Code may also be made available under the following Secondary
8
// Licenses when the conditions for such availability set forth in the Eclipse
9
// Public License 2.0 are satisfied: GNU General Public License, version 2
10
// or later which is available at
11
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
/****************************************************************************/
14
/// @file NBAlgorithms_Railway.cpp
15
/// @author Jakob Erdmann
16
/// @author Melanie Weber
17
/// @date 29. March 2018
18
///
19
// Algorithms for highway on-/off-ramps computation
20
/****************************************************************************/
21
#include <config.h>
22
23
#include <cassert>
24
#include <utils/options/OptionsCont.h>
25
#include <utils/common/MsgHandler.h>
26
#include <utils/common/ToString.h>
27
#include <utils/common/StringUtils.h>
28
#include <utils/iodevices/OutputDevice.h>
29
#include <utils/iodevices/OutputDevice_String.h>
30
#include <utils/router/DijkstraRouter.h>
31
#include "NBNetBuilder.h"
32
#include "NBAlgorithms.h"
33
#include "NBNodeCont.h"
34
#include "NBEdgeCont.h"
35
#include "NBNode.h"
36
#include "NBEdge.h"
37
#include "NBPTStop.h"
38
#include "NBVehicle.h"
39
#include "NBAlgorithms_Railway.h"
40
41
//#define DEBUG_SEQSTOREVERSE
42
//#define DEBUG_DIRECTION_PRIORITY
43
44
#define DEBUGNODEID "gneJ34"
45
#define DEBUGNODEID2 "28842974"
46
#define DEBUGEDGEID "22820560#0"
47
#define DEBUGCOND(obj) ((obj != 0 && (obj)->getID() == DEBUGNODEID))
48
49
#define SHARP_THRESHOLD_SAMEDIR 100
50
#define SHARP_THRESHOLD 80
51
52
53
// ===========================================================================
54
// method definitions
55
// ===========================================================================
56
// ---------------------------------------------------------------------------
57
// Track methods
58
// ---------------------------------------------------------------------------
59
void
60
NBRailwayTopologyAnalyzer::Track::addSuccessor(Track* track) {
61
successors.push_back(track);
62
viaSuccessors.push_back(std::make_pair(track, nullptr));
63
minPermissions &= track->edge->getPermissions();
64
}
65
66
67
const std::vector<NBRailwayTopologyAnalyzer::Track*>&
68
NBRailwayTopologyAnalyzer::Track::getSuccessors(SUMOVehicleClass svc) const {
69
if ((minPermissions & svc) != 0) {
70
return successors;
71
} else {
72
if (svcSuccessors.count(svc) == 0) {
73
std::vector<Track*> succ;
74
for (Track* t : successors) {
75
if ((t->edge->getPermissions() & svc) != 0) {
76
succ.push_back(t);
77
}
78
}
79
svcSuccessors[svc] = succ;
80
}
81
return svcSuccessors[svc];
82
}
83
}
84
85
86
const std::vector<std::pair<const NBRailwayTopologyAnalyzer::Track*, const NBRailwayTopologyAnalyzer::Track*> >&
87
NBRailwayTopologyAnalyzer::Track::getViaSuccessors(SUMOVehicleClass svc, bool /*ignoreTransientPermissions*/) const {
88
if ((minPermissions & svc) != 0) {
89
return viaSuccessors;
90
} else {
91
if (svcViaSuccessors.count(svc) == 0) {
92
std::vector<std::pair<const Track*, const Track*> >& succ = svcViaSuccessors[svc];
93
for (const Track* const t : successors) {
94
if ((t->edge->getPermissions() & svc) != 0) {
95
succ.push_back(std::make_pair(t, nullptr));
96
}
97
}
98
}
99
return svcViaSuccessors[svc];
100
}
101
}
102
103
104
// ---------------------------------------------------------------------------
105
// NBRailwayTopologyAnalyzer methods
106
// ---------------------------------------------------------------------------
107
void
108
NBRailwayTopologyAnalyzer::analyzeTopology(NBEdgeCont& ec) {
109
getBrokenRailNodes(ec, true);
110
}
111
112
113
int
114
NBRailwayTopologyAnalyzer::repairTopology(NBEdgeCont& ec, NBPTStopCont& sc, NBPTLineCont& lc) {
115
const bool minimal = OptionsCont::getOptions().getBool("railway.topology.repair.minimal");
116
int addedBidi = 0;
117
if (!minimal) {
118
addedBidi += extendBidiEdges(ec);
119
addedBidi += reverseEdges(ec, sc, lc); // technically not bidi but new edges nevertheless
120
addedBidi += addBidiEdgesForBufferStops(ec);
121
addedBidi += addBidiEdgesBetweenSwitches(ec);
122
}
123
if (lc.getLines().size() > 0) {
124
addedBidi += addBidiEdgesForStops(ec, lc, sc, minimal);
125
}
126
if (OptionsCont::getOptions().getBool("railway.topology.repair.connect-straight")) {
127
addedBidi += addBidiEdgesForStraightConnectivity(ec, true);
128
addedBidi += addBidiEdgesForStraightConnectivity(ec, false);
129
addedBidi += extendBidiEdges(ec);
130
}
131
return addedBidi;
132
}
133
134
135
int
136
NBRailwayTopologyAnalyzer::makeAllBidi(NBEdgeCont& ec) {
137
int numNotCenterEdges = 0;
138
int numAddedBidiEdges = 0;
139
std::string inputfile = OptionsCont::getOptions().getString("railway.topology.all-bidi.input-file");
140
std::vector<NBEdge*> edges;
141
if (inputfile == "") {
142
for (NBEdge* edge : ec.getAllEdges()) {
143
edges.push_back(edge);
144
}
145
} else {
146
std::set<std::string> edgeIDs;
147
NBHelpers::loadEdgesFromFile(inputfile, edgeIDs);
148
for (const std::string& edgeID : edgeIDs) {
149
NBEdge* edge = ec.retrieve(edgeID);
150
if (edge != nullptr) {
151
edges.push_back(edge);
152
}
153
}
154
}
155
for (NBEdge* edge : edges) {
156
if (hasRailway(edge->getPermissions())) {
157
// rebuild connections if given from an earlier network
158
edge->invalidateConnections(true);
159
if (!edge->isBidiRail()) {
160
if (edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
161
NBEdge* e2 = addBidiEdge(ec, edge, false);
162
if (e2 != nullptr) {
163
numAddedBidiEdges++;
164
}
165
} else {
166
numNotCenterEdges++;
167
}
168
}
169
}
170
}
171
WRITE_MESSAGEF(TL("Added % bidi-edges to ensure that all tracks are usable in both directions."), toString(numAddedBidiEdges));
172
if (numNotCenterEdges) {
173
WRITE_WARNINGF(TL("Ignore % edges because they have the wrong spreadType"), toString(numNotCenterEdges));
174
}
175
return numAddedBidiEdges;
176
}
177
178
179
NBEdge*
180
NBRailwayTopologyAnalyzer::addBidiEdge(NBEdgeCont& ec, NBEdge* edge, bool update) {
181
assert(edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER);
182
assert(!edge->isBidiRail());
183
const std::string id2 = (edge->getID()[0] == '-'
184
? edge->getID().substr(1)
185
: "-" + edge->getID());
186
if (ec.wasIgnored(id2)) {
187
// we had it before so the warning is already there
188
return nullptr;
189
}
190
if (ec.retrieve(id2) == nullptr) {
191
NBEdge* e2 = new NBEdge(id2, edge->getToNode(), edge->getFromNode(),
192
edge, edge->getGeometry().reverse());
193
if (edge->getParameter(NBTrafficLightDefinition::OSM_DIRECTION) == "forward") {
194
e2->setParameter(NBTrafficLightDefinition::OSM_DIRECTION, "backward");
195
}
196
ec.insert(e2);
197
if (ec.retrieve(id2) == nullptr) {
198
WRITE_WARNINGF(TL("Bidi-edge '%' prevented by filtering rules."), id2);
199
return nullptr;
200
}
201
if (update) {
202
updateTurns(edge);
203
// reconnected added edges
204
for (NBEdge* incoming : e2->getFromNode()->getIncomingEdges()) {
205
if (hasRailway(incoming->getPermissions())) {
206
incoming->invalidateConnections(true);
207
}
208
}
209
}
210
return e2;
211
} else {
212
WRITE_WARNINGF(TL("Could not add bidi-edge '%'."), id2);
213
return nullptr;
214
}
215
}
216
217
218
void
219
NBRailwayTopologyAnalyzer::getRailEdges(const NBNode* node,
220
EdgeVector& inEdges, EdgeVector& outEdges) {
221
for (NBEdge* e : node->getIncomingEdges()) {
222
if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
223
inEdges.push_back(e);
224
}
225
}
226
for (NBEdge* e : node->getOutgoingEdges()) {
227
if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
228
outEdges.push_back(e);
229
}
230
}
231
}
232
233
234
std::set<NBNode*>
235
NBRailwayTopologyAnalyzer::getBrokenRailNodes(NBEdgeCont& ec, bool verbose) {
236
std::set<NBNode*> brokenNodes;
237
OutputDevice& device = OutputDevice::getDevice(verbose
238
? OptionsCont::getOptions().getString("railway.topology.output")
239
: "/dev/null");
240
241
device.writeXMLHeader("railwayTopology", "");
242
std::set<NBNode*> railNodes = getRailNodes(ec, verbose);
243
std::map<std::pair<int, int>, std::set<NBNode*, ComparatorIdLess> > types;
244
std::set<NBEdge*, ComparatorIdLess> bidiEdges;
245
std::set<NBEdge*, ComparatorIdLess> bufferStops;
246
for (NBNode* node : railNodes) {
247
EdgeVector inEdges, outEdges;
248
getRailEdges(node, inEdges, outEdges);
249
types[std::make_pair((int)inEdges.size(), (int)outEdges.size())].insert(node);
250
for (NBEdge* e : outEdges) {
251
if (e->isBidiRail() && bidiEdges.count(e->getTurnDestination(true)) == 0) {
252
NBEdge* primary = e;
253
NBEdge* secondary = e->getTurnDestination(true);
254
if (e->getID()[0] == '-') {
255
std::swap(primary, secondary);
256
} else if (primary->getID()[0] != '-' && secondary->getID()[0] != '-' && secondary->getID() < primary->getID()) {
257
std::swap(primary, secondary);
258
}
259
if (bidiEdges.count(secondary) == 0) {
260
// avoid duplicate when both ids start with '-'
261
bidiEdges.insert(primary);
262
}
263
}
264
}
265
}
266
267
int numBrokenA = 0;
268
int numBrokenB = 0;
269
int numBrokenC = 0;
270
int numBrokenD = 0;
271
int numBufferStops = 0;
272
if (verbose && types.size() > 0) {
273
WRITE_MESSAGE(TL("Railway nodes by number of incoming,outgoing edges:"))
274
}
275
device.openTag("legend");
276
device.openTag("error");
277
device.writeAttr(SUMO_ATTR_ID, "a");
278
device.writeAttr("meaning", "edge pair angle supports driving but both are outgoing");
279
device.closeTag();
280
device.openTag("error");
281
device.writeAttr(SUMO_ATTR_ID, "b");
282
device.writeAttr("meaning", "edge pair angle supports driving but both are incoming");
283
device.closeTag();
284
device.openTag("error");
285
device.writeAttr(SUMO_ATTR_ID, "c");
286
device.writeAttr("meaning", "an incoming edge has a sharp angle to all outgoing edges");
287
device.closeTag();
288
device.openTag("error");
289
device.writeAttr(SUMO_ATTR_ID, "d");
290
device.writeAttr("meaning", "an outgoing edge has a sharp angle from all incoming edges");
291
device.closeTag();
292
device.closeTag();
293
294
for (auto it : types) {
295
int numBrokenType = 0;
296
device.openTag("railNodeType");
297
int in = it.first.first;
298
int out = it.first.second;
299
device.writeAttr("in", in);
300
device.writeAttr("out", out);
301
for (NBNode* n : it.second) {
302
device.openTag(SUMO_TAG_NODE);
303
device.writeAttr(SUMO_ATTR_ID, n->getID());
304
EdgeVector inRail, outRail;
305
getRailEdges(n, inRail, outRail);
306
// check if there is a mismatch between angle and edge direction
307
// (see above)
308
309
std::string broken = "";
310
if (in < 2 && hasStraightPair(n, outRail, outRail)) {
311
broken += "a";
312
numBrokenA++;
313
}
314
if (out < 2 && hasStraightPair(n, inRail, inRail)) {
315
broken += "b";
316
numBrokenB++;
317
}
318
if (out > 0) {
319
for (NBEdge* e : inRail) {
320
EdgeVector tmp;
321
tmp.push_back(e);
322
if (allSharp(n, tmp, outRail)) {
323
broken += "c";
324
numBrokenC++;
325
break;
326
}
327
}
328
}
329
if (in > 0) {
330
for (NBEdge* e : outRail) {
331
EdgeVector tmp;
332
tmp.push_back(e);
333
if (allSharp(n, inRail, tmp)) {
334
broken += "d";
335
numBrokenD++;
336
break;
337
}
338
}
339
}
340
// do not mark bidi nodes as broken
341
if (((in == 1 && out == 1) || (in == 2 && out == 2))
342
&& allBidi(inRail) && allBidi(outRail)) {
343
broken = "";
344
}
345
346
if (broken.size() > 0) {
347
device.writeAttr("broken", broken);
348
brokenNodes.insert(n);
349
numBrokenType++;
350
}
351
if (StringUtils::toBool(n->getParameter("buffer_stop", "false"))) {
352
device.writeAttr("buffer_stop", "true");
353
numBufferStops++;
354
}
355
device.closeTag();
356
}
357
device.closeTag();
358
if (verbose) {
359
WRITE_MESSAGE(" " + toString(it.first.first) + "," + toString(it.first.second)
360
+ " count: " + toString(it.second.size()) + " broken: " + toString(numBrokenType));
361
}
362
363
}
364
if (verbose) {
365
WRITE_MESSAGE("Found " + toString(brokenNodes.size()) + " broken railway nodes "
366
+ "(A=" + toString(numBrokenA)
367
+ " B=" + toString(numBrokenB)
368
+ " C=" + toString(numBrokenC)
369
+ " D=" + toString(numBrokenD)
370
+ ")");
371
WRITE_MESSAGEF(TL("Found % railway nodes marked as buffer_stop"), toString(numBufferStops));
372
}
373
374
for (NBEdge* e : bidiEdges) {
375
device.openTag("bidiEdge");
376
device.writeAttr(SUMO_ATTR_ID, e->getID());
377
device.writeAttr("bidi", e->getTurnDestination(true)->getID());
378
device.closeTag();
379
}
380
if (verbose) {
381
WRITE_MESSAGEF(TL("Found % bidirectional rail edges"), toString(bidiEdges.size()));
382
}
383
384
device.close();
385
return brokenNodes;
386
}
387
388
389
std::set<NBNode*>
390
NBRailwayTopologyAnalyzer::getRailNodes(NBEdgeCont& ec, bool verbose) {
391
std::set<NBNode*> railNodes;
392
int numRailEdges = 0;
393
for (auto it = ec.begin(); it != ec.end(); it++) {
394
if (hasRailway(it->second->getPermissions())) {
395
numRailEdges++;
396
railNodes.insert(it->second->getFromNode());
397
railNodes.insert(it->second->getToNode());
398
}
399
}
400
int numRailSignals = 0;
401
for (const NBNode* const node : railNodes) {
402
if (node->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
403
numRailSignals++;
404
}
405
}
406
if (verbose) {
407
WRITE_MESSAGEF(TL("Found % railway edges and % railway nodes (% signals)."), toString(numRailEdges), toString(railNodes.size()), toString(numRailSignals));
408
}
409
return railNodes;
410
}
411
412
413
bool
414
NBRailwayTopologyAnalyzer::isStraight(const NBNode* node, const NBEdge* e1, const NBEdge* e2) {
415
const double relAngle = NBHelpers::normRelAngle(e1->getAngleAtNode(node), e2->getAngleAtNode(node));
416
/*
417
std::cout << " isStraight n=" << node->getID()
418
<< " e1=" << e1->getID()
419
<< " e2=" << e2->getID()
420
<< " a1=" << e1->getAngleAtNode(node)
421
<< " a2=" << e2->getAngleAtNode(node)
422
<< " rel=" << relAngle
423
<< "\n";
424
*/
425
if ((e1->getToNode() == node && e2->getFromNode() == node)
426
|| (e1->getFromNode() == node && e2->getToNode() == node)) {
427
// edges go in the same direction
428
return fabs(relAngle) < SHARP_THRESHOLD;
429
} else {
430
// edges go in the opposite direction (both incoming or outgoing)
431
return fabs(relAngle) > SHARP_THRESHOLD_SAMEDIR;
432
}
433
}
434
435
436
bool
437
NBRailwayTopologyAnalyzer::hasStraightPair(const NBNode* node, const EdgeVector& edges,
438
const EdgeVector& edges2) {
439
#ifdef DEBUG_SEQSTOREVERSE
440
//if (node->getID() == DEBUGNODEID2) {
441
// std::cout << " edges=" << toString(edges) << " edges2=" << toString(edges2) << "\n";
442
//}
443
#endif
444
for (NBEdge* e1 : edges) {
445
for (NBEdge* e2 : edges2) {
446
//if (e1->getID() == "195411601#2" && e2->getID() == "93584120#3") {
447
// std::cout
448
// << " DEBUG normRelA=" << NBHelpers::normRelAngle(
449
// e1->getAngleAtNode(node),
450
// e2->getAngleAtNode(node))
451
// << "\n";
452
//}
453
if (e1 != e2 && isStraight(node, e1, e2)) {
454
return true;
455
}
456
}
457
}
458
return false;
459
}
460
461
462
bool
463
NBRailwayTopologyAnalyzer::allBroken(const NBNode* node, NBEdge* candOut, const EdgeVector& in, const EdgeVector& out) {
464
for (NBEdge* e : in) {
465
if (e != candOut && isStraight(node, e, candOut)) {
466
if (gDebugFlag1) {
467
std::cout << " isStraight e=" << e->getID() << " candOut=" << candOut->getID() << "\n";
468
}
469
return false;
470
}
471
}
472
for (NBEdge* e : out) {
473
if (e != candOut && !isStraight(node, e, candOut)) {
474
if (gDebugFlag1) {
475
std::cout << " isSharp e=" << e->getID() << " candOut=" << candOut->getID() << "\n";
476
}
477
return false;
478
}
479
}
480
return true;
481
}
482
483
484
bool
485
NBRailwayTopologyAnalyzer::allSharp(const NBNode* node, const EdgeVector& in, const EdgeVector& out, bool countBidiAsSharp) {
486
bool allBidi = true;
487
for (NBEdge* e1 : in) {
488
for (NBEdge* e2 : out) {
489
if (e1 != e2 && isStraight(node, e1, e2)) {
490
return false;
491
}
492
if (!e1->isBidiRail(true)) {
493
//std::cout << " allSharp node=" << node->getID() << " e1=" << e1->getID() << " is not bidi\n";
494
allBidi = false;
495
}
496
}
497
}
498
return !allBidi || countBidiAsSharp;
499
}
500
501
502
bool
503
NBRailwayTopologyAnalyzer::allBidi(const EdgeVector& edges) {
504
for (NBEdge* e : edges) {
505
if (!e->isBidiRail()) {
506
return false;
507
}
508
}
509
return true;
510
}
511
512
513
int
514
NBRailwayTopologyAnalyzer::extendBidiEdges(NBEdgeCont& ec) {
515
int added = 0;
516
for (auto it = ec.begin(); it != ec.end(); it++) {
517
NBEdge* e = it->second;
518
if (e->isBidiRail()) {
519
added += extendBidiEdges(ec, e->getFromNode(), e->getTurnDestination(true));
520
added += extendBidiEdges(ec, e->getToNode(), e);
521
}
522
}
523
if (added > 0) {
524
WRITE_MESSAGEF(TL("Added % bidi-edges as extension of existing bidi edges."), toString(added));
525
}
526
return added;
527
}
528
529
530
int
531
NBRailwayTopologyAnalyzer::extendBidiEdges(NBEdgeCont& ec, NBNode* node, NBEdge* bidiIn) {
532
assert(bidiIn->getToNode() == node);
533
NBEdge* bidiOut = bidiIn->getTurnDestination(true);
534
if (bidiOut == nullptr) {
535
WRITE_WARNINGF(TL("Could not find bidi-edge for edge '%'"), bidiIn->getID());
536
return 0;
537
}
538
EdgeVector tmpBidiOut;
539
tmpBidiOut.push_back(bidiOut);
540
EdgeVector tmpBidiIn;
541
tmpBidiIn.push_back(bidiIn);
542
int added = 0;
543
EdgeVector inRail, outRail;
544
getRailEdges(node, inRail, outRail);
545
for (NBEdge* cand : outRail) {
546
//std::cout << " extendBidiEdges n=" << node->getID() << " bidiIn=" << bidiIn->getID() << " cand=" << cand->getID() << " isStraight=" << isStraight(node, bidiIn, cand) << " allSharp=" << allSharp(node, inRail, tmpBidiOut, true) << "\n";
547
if (!cand->isBidiRail() && isStraight(node, bidiIn, cand)
548
&& cand->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
549
&& allSharp(node, inRail, tmpBidiOut, true)) {
550
NBEdge* e2 = addBidiEdge(ec, cand);
551
if (e2 != nullptr) {
552
added += 1 + extendBidiEdges(ec, cand->getToNode(), cand);
553
}
554
}
555
}
556
for (NBEdge* cand : inRail) {
557
//std::cout << " extendBidiEdges n=" << node->getID() << " bidiOut=" << bidiOut->getID() << " cand=" << cand->getID() << " isStraight=" << isStraight(node, cand, bidiOut) << " allSharp=" << allSharp(node, outRail, tmpBidiIn, true) << "\n";
558
if (!cand->isBidiRail() && isStraight(node, cand, bidiOut)
559
&& cand->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
560
&& allSharp(node, outRail, tmpBidiIn, true)) {
561
NBEdge* e2 = addBidiEdge(ec, cand);
562
if (e2 != nullptr) {
563
added += 1 + extendBidiEdges(ec, cand->getFromNode(), e2);
564
}
565
}
566
}
567
return added;
568
}
569
570
571
int
572
NBRailwayTopologyAnalyzer::reverseEdges(NBEdgeCont& ec, NBPTStopCont& sc, NBPTLineCont& lc) {
573
std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
574
// find reversible edge sequences between broken nodes
575
std::vector<EdgeVector> seqsToReverse;
576
EdgeSet lineEdges;
577
for (auto item : lc.getLines()) {
578
const EdgeVector& route = item.second->getEdges();
579
lineEdges.insert(route.begin(), route.end());
580
}
581
for (NBNode* n : brokenNodes) {
582
EdgeVector inRail, outRail;
583
getRailEdges(n, inRail, outRail);
584
for (NBEdge* start : outRail) {
585
if (lineEdges.count(start)) {
586
// ptline edges should never be reversed. They are fixed by a different algorithm require their original ids and orientation
587
continue;
588
}
589
EdgeVector tmp;
590
tmp.push_back(start);
591
// only reverse edges where the node would be unbroken afterwards
592
if (!allBroken(n, start, inRail, outRail)
593
|| (inRail.size() == 1 && outRail.size() == 1)) {
594
#ifdef DEBUG_SEQSTOREVERSE
595
if (n->getID() == DEBUGNODEID) {
596
std::cout << " abort at start n=" << n->getID() << " (not all broken)\n";
597
}
598
#endif
599
continue;
600
}
601
//std::cout << " get sequences from " << start->getID() << "\n";
602
bool forward = true;
603
EdgeVector seq;
604
while (forward) {
605
seq.push_back(start);
606
//std::cout << " seq=" << toString(seq) << "\n";
607
NBNode* n2 = start->getToNode();
608
EdgeVector inRail2, outRail2;
609
getRailEdges(n2, inRail2, outRail2);
610
if (brokenNodes.count(n2) != 0) {
611
EdgeVector tmp2;
612
tmp2.push_back(start);
613
if (allBroken(n2, start, outRail2, inRail2)) {
614
seqsToReverse.push_back(seq);
615
} else {
616
#ifdef DEBUG_SEQSTOREVERSE
617
if (n->getID() == DEBUGNODEID) {
618
std::cout << " abort at n2=" << n2->getID() << " (not all broken)\n";
619
}
620
#endif
621
}
622
forward = false;
623
} else {
624
if (outRail2.size() == 0) {
625
// stop at network border
626
forward = false;
627
#ifdef DEBUG_SEQSTOREVERSE
628
if (n->getID() == DEBUGNODEID) {
629
std::cout << " abort at n2=" << n2->getID() << " (border)\n";
630
}
631
#endif
632
} else if (outRail2.size() > 1 || inRail2.size() > 1) {
633
// stop at switch
634
forward = false;
635
#ifdef DEBUG_SEQSTOREVERSE
636
if (n->getID() == DEBUGNODEID) {
637
std::cout << " abort at n2=" << n2->getID() << " (switch)\n";
638
}
639
#endif
640
} else {
641
start = outRail2.front();
642
}
643
}
644
}
645
}
646
}
647
// sort by sequence length
648
if (seqsToReverse.size() > 0) {
649
WRITE_MESSAGEF(TL("Found % reversible edge sequences between broken rail nodes"), toString(seqsToReverse.size()));
650
}
651
std::sort(seqsToReverse.begin(), seqsToReverse.end(),
652
[](const EdgeVector & a, const EdgeVector & b) {
653
return a.size() < b.size();
654
});
655
int numReversed = 0;
656
std::set<NBNode*> affectedEndpoints;
657
std::set<std::string> reversedIDs;
658
std::map<int, int> seqLengths;
659
for (EdgeVector& seq : seqsToReverse) {
660
NBNode* seqStart = seq.front()->getFromNode();
661
NBNode* seqEnd = seq.back()->getToNode();
662
// avoid reversing sequences on both sides of a broken node
663
if (affectedEndpoints.count(seqStart) == 0
664
&& affectedEndpoints.count(seqEnd) == 0) {
665
affectedEndpoints.insert(seqStart);
666
affectedEndpoints.insert(seqEnd);
667
//WRITE_MESSAGE(" reversed seq=" + toString(seq));
668
for (NBEdge* e : seq) {
669
e->reinitNodes(e->getToNode(), e->getFromNode());
670
e->setGeometry(e->getGeometry().reverse());
671
reversedIDs.insert(e->getID());
672
if (e->getParameter(NBTrafficLightDefinition::OSM_DIRECTION) == "forward") {
673
e->setParameter(NBTrafficLightDefinition::OSM_DIRECTION, "backward");
674
}
675
}
676
seqLengths[(int)seq.size()]++;
677
numReversed++;
678
}
679
}
680
if (numReversed > 0) {
681
WRITE_MESSAGEF(TL("Reversed % sequences (count by length: %)"), toString(numReversed), joinToString(seqLengths, " ", ":"));
682
for (auto& item : sc.getStops()) {
683
if (reversedIDs.count(item.second->getEdgeId())) {
684
item.second->findLaneAndComputeBusStopExtent(ec);
685
}
686
}
687
}
688
return numReversed;
689
}
690
691
692
int
693
NBRailwayTopologyAnalyzer::addBidiEdgesForBufferStops(NBEdgeCont& ec) {
694
std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
695
std::set<NBNode*> railNodes = getRailNodes(ec);
696
// find buffer stops and ensure that they are connect to the network in both directions
697
int numBufferStops = 0;
698
int numAddedBidiTotal = 0;
699
for (NBNode* node : railNodes) {
700
if (StringUtils::toBool(node->getParameter("buffer_stop", "false"))) {
701
if (node->getEdges().size() != 1) {
702
WRITE_WARNINGF(TL("Ignoring buffer stop junction '%' with % edges."), node->getID(), node->getEdges().size());
703
continue;
704
}
705
// int numAddedBidi = 0;
706
numBufferStops++;
707
NBEdge* prev = nullptr;
708
NBEdge* prev2 = nullptr;
709
EdgeVector inRail, outRail;
710
getRailEdges(node, inRail, outRail);
711
bool addAway = true; // add new edges away from buffer stop
712
while (prev == nullptr || (inRail.size() + outRail.size()) == 3) {
713
NBEdge* e = nullptr;
714
if (prev == nullptr) {
715
assert(node->getEdges().size() == 1);
716
e = node->getEdges().front();
717
addAway = node == e->getToNode();
718
} else {
719
if (addAway) {
720
// XXX if node is broken we need to switch direction
721
assert(inRail.size() == 2);
722
e = inRail.front() == prev2 ? inRail.back() : inRail.front();
723
} else {
724
// XXX if node is broken we need to switch direction
725
assert(outRail.size() == 2);
726
e = outRail.front() == prev2 ? outRail.back() : outRail.front();
727
}
728
}
729
e->setLaneSpreadFunction(LaneSpreadFunction::CENTER);
730
NBNode* e2From = nullptr;
731
NBNode* e2To = nullptr;
732
if (addAway) {
733
e2From = node;
734
e2To = e->getFromNode();
735
node = e2To;
736
} else {
737
e2From = e->getToNode();
738
e2To = node;
739
node = e2From;
740
}
741
NBEdge* e2 = addBidiEdge(ec, e);
742
if (e2 == nullptr) {
743
break;
744
}
745
prev = e;
746
prev2 = e2;
747
// numAddedBidi++;
748
numAddedBidiTotal++;
749
inRail.clear();
750
outRail.clear();
751
getRailEdges(node, inRail, outRail);
752
}
753
//if (numAddedBidi > 0) {
754
// WRITE_MESSAGEF(TL(" added % edges between buffer stop junction '%' and junction '%'"), toString(numAddedBidi), bufferStop->getID(), node->getID());
755
//}
756
}
757
}
758
if (numAddedBidiTotal > 0) {
759
WRITE_MESSAGEF(TL("Added % edges to connect % buffer stops in both directions."), toString(numAddedBidiTotal), toString(numBufferStops));
760
}
761
return numAddedBidiTotal;
762
}
763
764
NBEdge*
765
NBRailwayTopologyAnalyzer::isBidiSwitch(const NBNode* n) {
766
EdgeVector inRail, outRail;
767
getRailEdges(n, inRail, outRail);
768
if (inRail.size() == 2 && outRail.size() == 1 && isStraight(n, inRail.front(), inRail.back())) {
769
if (isStraight(n, inRail.front(), outRail.front())) {
770
return inRail.front();
771
} else if (isStraight(n, inRail.back(), outRail.front())) {
772
return inRail.back();
773
}
774
}
775
if (inRail.size() == 1 && outRail.size() == 2 && isStraight(n, outRail.front(), outRail.back())) {
776
if (isStraight(n, outRail.front(), inRail.front())) {
777
return outRail.front();
778
} else if (isStraight(n, outRail.back(), inRail.front())) {
779
return outRail.back();
780
}
781
}
782
return nullptr;
783
}
784
785
786
int
787
NBRailwayTopologyAnalyzer::addBidiEdgesBetweenSwitches(NBEdgeCont& ec) {
788
std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
789
std::map<int, int> seqLengths;
790
int numAdded = 0;
791
int numSeqs = 0;
792
for (NBNode* n : brokenNodes) {
793
NBEdge* edge = isBidiSwitch(n);
794
if (edge != nullptr && edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
795
std::vector<NBNode*> nodeSeq;
796
EdgeVector edgeSeq;
797
NBNode* prev = n;
798
nodeSeq.push_back(prev);
799
edgeSeq.push_back(edge);
800
bool forward = true;
801
//std::cout << "Looking for potential bidi-edge sequence starting at junction '" << n->getID() << "' with edge '" + edge->getID() << "'\n";
802
// find a suitable end point for adding bidi edges
803
while (forward) {
804
NBNode* next = edge->getFromNode() == prev ? edge->getToNode() : edge->getFromNode();
805
EdgeVector allRail;
806
getRailEdges(next, allRail, allRail);
807
if (allRail.size() == 2 && isStraight(next, allRail.front(), allRail.back())) {
808
prev = next;
809
edge = allRail.front() == edge ? allRail.back() : allRail.front();
810
nodeSeq.push_back(prev);
811
edgeSeq.push_back(edge);
812
} else {
813
forward = false;
814
EdgeVector inRail2, outRail2;
815
getRailEdges(next, inRail2, outRail2);
816
if (isBidiSwitch(next) == edge) {
817
// suitable switch found as endpoint, add reverse edges
818
//WRITE_MESSAGE("Adding " + toString(edgeSeq.size())
819
// + " bidi-edges between switches junction '" + n->getID() + "' and junction '" + next->getID() + "'");
820
for (NBEdge* e : edgeSeq) {
821
addBidiEdge(ec, e);
822
}
823
seqLengths[(int)edgeSeq.size()]++;
824
numSeqs++;
825
numAdded += (int)edgeSeq.size();
826
} else {
827
//std::cout << " sequence ended at junction " << next->getID()
828
// << " in=" << inRail2.size()
829
// << " out=" << outRail2.size()
830
// << " bidiSwitch=" << Named::getIDSecure(isBidiSwitch(next))
831
// << "\n";
832
}
833
834
}
835
}
836
837
}
838
}
839
if (seqLengths.size() > 0) {
840
WRITE_MESSAGEF(TL("Added % bidi-edges between % pairs of railway switches (count by length: %)"), toString(numAdded), toString(numSeqs), joinToString(seqLengths, " ", ":"));
841
}
842
return numAdded;
843
}
844
845
846
std::set<NBPTLine*>
847
NBRailwayTopologyAnalyzer::findBidiCandidates(NBPTLineCont& lc) {
848
std::set<NBPTLine*> result;
849
std::set<std::pair<std::shared_ptr<NBPTStop>, std::shared_ptr<NBPTStop> > > visited;
850
for (const auto& item : lc.getLines()) {
851
const std::vector<std::shared_ptr<NBPTStop> >& stops = item.second->getStops();
852
if (stops.size() > 1) {
853
for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
854
std::shared_ptr<NBPTStop> fromStop = *it;
855
std::shared_ptr<NBPTStop> toStop = *(it + 1);
856
visited.insert({fromStop, toStop});
857
}
858
}
859
}
860
for (const auto& item : lc.getLines()) {
861
const std::vector<std::shared_ptr<NBPTStop> >& stops = item.second->getStops();
862
if (stops.size() > 1) {
863
for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
864
std::shared_ptr<NBPTStop> fromStop = *it;
865
std::shared_ptr<NBPTStop> toStop = *(it + 1);
866
std::pair<std::shared_ptr<NBPTStop>, std::shared_ptr<NBPTStop> > reverseTrip({toStop, fromStop});
867
if (visited.count(reverseTrip)) {
868
result.insert(item.second);
869
break;
870
}
871
}
872
}
873
}
874
return result;
875
}
876
877
int
878
NBRailwayTopologyAnalyzer::addBidiEdgesForStops(NBEdgeCont& ec, NBPTLineCont& lc, NBPTStopCont& sc, bool minimal) {
879
const double penalty = OptionsCont::getOptions().getFloat("railway.topology.repair.bidi-penalty");
880
// generate bidirectional routing graph
881
std::vector<Track*> tracks;
882
for (NBEdge* edge : ec.getAllEdges()) {
883
tracks.push_back(new Track(edge));
884
}
885
const int numEdges = (int)tracks.size();
886
for (NBEdge* edge : ec.getAllEdges()) {
887
tracks.push_back(new Track(edge, (int)tracks.size(), edge->getID() + "_reverse", penalty));
888
}
889
// add special tracks for starting end ending in both directions
890
std::map<NBEdge*, std::pair<Track*, Track*> > stopTracks;
891
for (NBEdge* edge : ec.getAllEdges()) {
892
if ((edge->getPermissions() & SVC_RAIL_CLASSES) != 0) {
893
Track* start = new Track(edge, (int)tracks.size(), edge->getID() + "_start");
894
tracks.push_back(start);
895
Track* end = new Track(edge, (int)tracks.size(), edge->getID() + "_end");
896
tracks.push_back(end);
897
stopTracks[edge] = {start, end};
898
}
899
}
900
// set successors based on angle (connections are not yet built)
901
for (NBNode* node : getRailNodes(ec)) {
902
EdgeVector railEdges;
903
getRailEdges(node, railEdges, railEdges);
904
for (NBEdge* e1 : railEdges) {
905
for (NBEdge* e2 : railEdges) {
906
if (e1 != e2 && isStraight(node, e1, e2)) {
907
int i = e1->getNumericalID();
908
int i2 = e2->getNumericalID();
909
if (e1->getToNode() == node) {
910
if (e2->getFromNode() == node) {
911
// case 1) plain forward connection
912
tracks[i]->addSuccessor(tracks[i2]);
913
// reverse edge (numerical id incremented by numEdges)
914
tracks[i2 + numEdges]->addSuccessor(tracks[i + numEdges]);
915
} else {
916
// case 2) both edges pointing towards each other
917
tracks[i]->addSuccessor(tracks[i2 + numEdges]);
918
tracks[i2]->addSuccessor(tracks[i + numEdges]);
919
}
920
} else {
921
if (e2->getFromNode() == node) {
922
// case 3) both edges pointing away from each other
923
tracks[i + numEdges]->addSuccessor(tracks[i2]);
924
tracks[i2 + numEdges]->addSuccessor(tracks[i]);
925
} else {
926
// already handled via case 1)
927
}
928
}
929
930
}
931
}
932
}
933
}
934
// define start and end successors
935
for (auto& item : stopTracks) {
936
const int index = item.first->getNumericalID();
937
// start
938
item.second.first->addSuccessor(tracks[index]);
939
item.second.first->addSuccessor(tracks[index + numEdges]);
940
// end
941
tracks[index]->addSuccessor(item.second.second);
942
tracks[index + numEdges]->addSuccessor(item.second.second);
943
}
944
// DEBUG
945
/*
946
for (Track* t : tracks) {
947
std::cout << "track " << t->getID() << " e=" << t->edge->getID() << " i=" << t->getNumericalID() << " succ:\n";
948
for (Track* s : t->getSuccessors(SVC_IGNORING)) {
949
std::cout << " succ=" << s->getID() << "\n";
950
}
951
}
952
*/
953
954
SUMOAbstractRouter<Track, NBVehicle>* const router = new DijkstraRouter<Track, NBVehicle>(
955
tracks, true, &NBRailwayTopologyAnalyzer::getTravelTimeStatic, nullptr, true);
956
957
int added = 0;
958
int numDisconnected = 0;
959
std::set<std::shared_ptr<NBPTStop>> addBidiStops;
960
std::set<NBEdge*, ComparatorIdLess> addBidiStopEdges;
961
std::set<NBEdge*, ComparatorIdLess> addBidiEdges;
962
std::set<std::pair<std::string, std::string> > visited;
963
964
// the isConsistent heuristic may fail in some cases. If we observe that a
965
// specific sequence of stop ids in encoded in both directions, we take this
966
// as a reason to overrule the heuristic
967
std::set<NBPTLine*> requireBidi = findBidiCandidates(lc);
968
969
for (const auto& item : lc.getLines()) {
970
NBPTLine* line = item.second;
971
std::vector<NBPTLine::PTStopInfo> stops = line->getStopEdges(ec);
972
std::vector<NBEdge*> stopEdges;
973
for (auto it : stops) {
974
stopEdges.push_back(it.edge);
975
}
976
NBEdge* routeStart = line->getRouteStart(ec);
977
NBEdge* routeEnd = line->getRouteEnd(ec);
978
if (routeStart != nullptr && (stopEdges.empty() || routeStart != stopEdges.front())) {
979
stops.insert(stops.begin(), NBPTLine::PTStopInfo(routeStart, routeStart->getID(), 0, false));
980
}
981
if (routeEnd != nullptr && (stopEdges.empty() || routeEnd != stopEdges.back())) {
982
stops.push_back(NBPTLine::PTStopInfo(routeEnd, routeEnd->getID(), routeEnd->getLength(), false));
983
}
984
if (stops.size() < 2) {
985
continue;
986
}
987
if (!line->isConsistent(stopEdges) && requireBidi.count(line) == 0) {
988
WRITE_WARNINGF(TL("Edge sequence is not consistent with stop sequence in line '%', not adding bidi edges."), item.first);
989
continue;
990
}
991
for (auto it = stops.begin(); it + 1 != stops.end(); ++it) {
992
NBEdge* fromEdge = it->edge;
993
NBEdge* toEdge = (it + 1)->edge;
994
const std::string fromStop = it->stopID;
995
const std::string toStop = (it + 1)->stopID;
996
const double fromPos = it->pos;
997
const double toPos = (it + 1)->pos;
998
bool fromRevised = it->revised;
999
bool toRevised = (it + 1)->revised;
1000
std::pair<std::string, std::string> trip(fromStop, toStop);
1001
std::pair<std::string, std::string> reverseTrip(toStop, fromStop);
1002
//if (line->getLineID() == "147471") {
1003
// std::cout << " line=" << line->getLineID() << " trip=" << Named::getIDSecure(fromEdge) << "->" << Named::getIDSecure(toEdge) << " visited=" << (visited.count(trip) != 0) << " fromStop=" << fromStop << " toStop=" << toStop << " fromRevised=" << fromRevised << " toRevised=" << toRevised << "\n";
1004
//}
1005
if (visited.count(trip) != 0) {
1006
continue;
1007
} else {
1008
visited.insert(trip);
1009
}
1010
if (stopTracks.count(fromEdge) == 0
1011
|| stopTracks.count(toEdge) == 0) {
1012
continue;
1013
}
1014
bool needBidi = visited.count(reverseTrip) != 0;
1015
NBVehicle veh(line->getRef(), (SUMOVehicleClass)(fromEdge->getPermissions() & SVC_RAIL_CLASSES));
1016
std::vector<const Track*> route;
1017
Track* from = fromRevised ? tracks[fromEdge->getNumericalID()] : stopTracks[fromEdge].first;
1018
Track* to = toRevised ? tracks[toEdge->getNumericalID()] : stopTracks[toEdge].second;
1019
int iStart = fromRevised ? 0 : 1;
1020
int iDeltaEnd = toRevised ? 0 : 1;
1021
if (fromEdge == toEdge && fromPos > toPos) {
1022
// must use reverse edge
1023
route.push_back(tracks[fromEdge->getNumericalID() + numEdges]);
1024
iStart = 0;
1025
iDeltaEnd = 0;
1026
needBidi = true;
1027
} else {
1028
router->compute(from, to, &veh, 0, route);
1029
}
1030
//if (line->getLineID() == "147471") {
1031
// std::cout << "DEBUG: route=" << toString(route) << "\n";
1032
//}
1033
if (route.size() > 0) {
1034
assert((int)route.size() > iStart + iDeltaEnd);
1035
for (int i = iStart; i < (int)route.size() - iDeltaEnd; ++i) {
1036
const bool isBidi = route[i]->getNumericalID() >= numEdges;
1037
bool isStop = i == iStart || i == (int)route.size() - 1 - iDeltaEnd;
1038
if (isBidi || needBidi) {
1039
NBEdge* edge = route[i]->edge;
1040
if (addBidiEdges.count(edge) == 0) {
1041
if (!edge->isBidiRail(true)) {
1042
if (edge->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
1043
addBidiEdges.insert(edge);
1044
if (isStop) {
1045
addBidiStopEdges.insert(edge);
1046
}
1047
} else {
1048
if (isStop) {
1049
WRITE_WARNINGF(TL("Stop on edge '%' can only be reached in reverse but edge has the wrong spreadType."), fromEdge->getID());
1050
}
1051
}
1052
}
1053
}
1054
}
1055
if (isStop && isBidi) {
1056
std::shared_ptr<NBPTStop> fs = sc.get(fromStop);
1057
if (fs) {
1058
addBidiStops.insert(fs);
1059
}
1060
std::shared_ptr<NBPTStop> ts = sc.get(toStop);
1061
if (ts) {
1062
addBidiStops.insert(ts);
1063
}
1064
}
1065
}
1066
} else {
1067
WRITE_WARNINGF(TL("No connection found between stops on edge '%' and edge '%'."), fromEdge->getID(), toEdge->getID());
1068
numDisconnected++;
1069
}
1070
}
1071
}
1072
for (NBEdge* edge : addBidiEdges) {
1073
if (!edge->isBidiRail()) {
1074
NBEdge* e2 = addBidiEdge(ec, edge);
1075
//std::cout << " add bidiEdge for stop at edge " << edge->getID() << "\n";
1076
if (e2 != nullptr) {
1077
added++;
1078
if (!minimal) {
1079
added += extendBidiEdges(ec, edge->getToNode(), edge);
1080
added += extendBidiEdges(ec, edge->getFromNode(), e2);
1081
}
1082
}
1083
}
1084
}
1085
for (std::shared_ptr<NBPTStop> stop : addBidiStops) {
1086
std::shared_ptr<NBPTStop> fromReverse = sc.getReverseStop(stop, ec);
1087
if (fromReverse) {
1088
sc.insert(fromReverse);
1089
stop->setBidiStop(fromReverse);
1090
}
1091
}
1092
if (addBidiEdges.size() > 0 || numDisconnected > 0) {
1093
WRITE_MESSAGE("Added " + toString(addBidiStopEdges.size()) + " bidi-edges for public transport stops and a total of "
1094
+ toString(added) + " bidi-edges to ensure connectivity of stops ("
1095
+ toString(numDisconnected) + " stops remain disconnected)");
1096
}
1097
1098
// clean up
1099
for (Track* t : tracks) {
1100
delete t;
1101
}
1102
delete router;
1103
return (int)addBidiEdges.size();
1104
}
1105
1106
1107
int
1108
NBRailwayTopologyAnalyzer::addBidiEdgesForStraightConnectivity(NBEdgeCont& ec, bool geometryLike) {
1109
int added = 0;
1110
std::set<NBNode*> brokenNodes = getBrokenRailNodes(ec);
1111
for (const auto& e : ec) {
1112
if (!hasRailway(e.second->getPermissions())) {
1113
continue;
1114
}
1115
NBNode* const from = e.second->getFromNode();
1116
NBNode* const to = e.second->getToNode();
1117
if (brokenNodes.count(from) == 0 && brokenNodes.count(to) == 0) {
1118
continue;
1119
}
1120
if (e.second->isBidiRail()) {
1121
continue;
1122
}
1123
EdgeVector inRailFrom, outRailFrom, inRailTo, outRailTo;
1124
getRailEdges(from, inRailFrom, outRailFrom);
1125
getRailEdges(to, inRailTo, outRailTo);
1126
// check whether there is a straight edge pointing away from this one at the from-node
1127
// and there is no straight incoming edge at the from-node
1128
bool haveStraight = false;
1129
bool haveStraightReverse = false;
1130
if (!geometryLike || outRailFrom.size() + inRailFrom.size() == 2) {
1131
for (const NBEdge* fromStraightCand : outRailFrom) {
1132
if (fromStraightCand != e.second && isStraight(from, fromStraightCand, e.second)) {
1133
haveStraightReverse = true;
1134
//std::cout << " haveStraightReverse outRailFrom=" << fromStraightCand->getID() << "\n";
1135
break;
1136
}
1137
}
1138
if (haveStraightReverse) {
1139
for (const NBEdge* fromStraightCand : inRailFrom) {
1140
if (fromStraightCand != e.second && isStraight(from, fromStraightCand, e.second)) {
1141
haveStraight = true;
1142
//std::cout << " haveStraight inRailFrom=" << fromStraightCand->getID() << "\n";
1143
break;
1144
}
1145
}
1146
}
1147
}
1148
if ((!haveStraightReverse || haveStraight) && (!geometryLike || outRailTo.size() + inRailTo.size() == 2)) {
1149
// check whether there is a straight edge pointing towards this one at the to-node
1150
// and there is no straight outgoing edge at the to-node
1151
haveStraight = false;
1152
haveStraightReverse = false;
1153
for (const NBEdge* toStraightCand : inRailTo) {
1154
if (toStraightCand != e.second && isStraight(to, toStraightCand, e.second)) {
1155
haveStraightReverse = true;
1156
//std::cout << " haveStraightReverse inRailTo=" << toStraightCand->getID() << "\n";
1157
break;
1158
}
1159
}
1160
if (haveStraightReverse) {
1161
for (const NBEdge* toStraightCand : outRailTo) {
1162
if (toStraightCand != e.second && isStraight(to, toStraightCand, e.second)) {
1163
haveStraight = true;
1164
//std::cout << " haveStraightReverse outRailTo=" << toStraightCand->getID() << "\n";
1165
break;
1166
}
1167
}
1168
}
1169
}
1170
//std::cout << "edge=" << e.second->getID() << " haveStraight=" << haveStraight << " haveStraightReverse=" << haveStraightReverse << "\n";
1171
if (haveStraightReverse && !haveStraight) {
1172
NBEdge* e2 = addBidiEdge(ec, e.second);
1173
//std::cout << " add bidiEdge for straight connectivity at edge " << e.second->getID() << " fromBroken=" << brokenNodes.count(from) << " toBroken=" << brokenNodes.count(to) << "\n";
1174
if (e2 != nullptr) {
1175
added++;
1176
added += extendBidiEdges(ec, to, e.second);
1177
added += extendBidiEdges(ec, from, e2);
1178
}
1179
}
1180
}
1181
if (added > 0) {
1182
if (geometryLike) {
1183
WRITE_MESSAGEF(TL("Added % bidi-edges to ensure connectivity of straight tracks at geometry-like nodes."), toString(added));
1184
} else {
1185
WRITE_MESSAGEF(TL("Added % bidi-edges to ensure connectivity of straight tracks at switches."), toString(added));
1186
}
1187
}
1188
return added;
1189
}
1190
1191
1192
void
1193
NBRailwayTopologyAnalyzer::updateTurns(NBEdge* edge) {
1194
NBTurningDirectionsComputer::computeTurnDirectionsForNode(edge->getFromNode(), false);
1195
NBTurningDirectionsComputer::computeTurnDirectionsForNode(edge->getToNode(), false);
1196
}
1197
1198
1199
double
1200
NBRailwayTopologyAnalyzer::getTravelTimeStatic(const Track* const track, const NBVehicle* const veh, double time) {
1201
return NBEdge::getTravelTimeStatic(track->edge, veh, time) * track->penalty;
1202
}
1203
1204
1205
void
1206
NBRailwayTopologyAnalyzer::extendDirectionPriority(NBEdgeCont& ec, bool fromUniDir) {
1207
// if fromUniDir=true, assign priority value for each railway edge:
1208
// 4: edge is unidirectional
1209
// 3: edge is in main direction of bidirectional track
1210
// 2: edge is part of bidirectional track, main direction unknown - both edges are extensions of unidirectional edges
1211
// 1: edge is part of bidirectional track, main direction unknown - neither edge is an extension of a unidirectional edge
1212
// 0: edge is part of bidirectional track in reverse of main direction
1213
//
1214
// otherwise:
1215
// assign priority value for each railway edge with priority -1 (undefined):
1216
// x: edges with priority >= 0 keep their priority
1217
// x-1 : edge is in direct (no switch) sequence of an edge with initial priority x
1218
// x-2 : edge and its opposite-direction are in direct (no switch) sequence of an edge with initial priority x
1219
// x-3 : edge is part of bidirectional track, both directions are indirect extensions of x-1 edges
1220
// x-4 : edge is reverse direction of an x-1 edge
1221
1222
std::set<NBEdge*, ComparatorIdLess> bidi;
1223
EdgeSet uni;
1224
for (NBEdge* edge : ec.getAllEdges()) {
1225
if (hasRailway(edge->getPermissions())) {
1226
if (fromUniDir) {
1227
if (!edge->isBidiRail()) {
1228
edge->setPriority(4);
1229
edge->setRoutingType("4");
1230
uni.insert(edge);
1231
} else {
1232
bidi.insert(edge);
1233
}
1234
} else {
1235
if (edge->getPriority() >= 0) {
1236
uni.insert(edge);
1237
} else {
1238
bidi.insert(edge);
1239
}
1240
}
1241
}
1242
}
1243
1244
if (uni.size() == 0) {
1245
if (bidi.size() != 0) {
1246
WRITE_WARNING(TL("Cannot extend track direction priority because there are no track edges with positive priority"));
1247
}
1248
return;
1249
}
1250
EdgeSet seen;
1251
EdgeSet check = uni;
1252
EdgeSet forward;
1253
while (!check.empty()) {
1254
NBEdge* edge = *check.begin();
1255
check.erase(edge);
1256
if (seen.count(edge) != 0) {
1257
continue;
1258
}
1259
seen.insert(edge);
1260
NBEdge* straightOut = edge->getStraightContinuation(edge->getPermissions());
1261
if (straightOut != nullptr && straightOut->getStraightPredecessor(straightOut->getPermissions()) == edge) {
1262
forward.insert(straightOut);
1263
check.insert(straightOut);
1264
}
1265
NBEdge* straightIn = edge->getStraightPredecessor(edge->getPermissions());
1266
if (straightIn != nullptr && straightIn->getStraightContinuation(straightIn->getPermissions()) == edge) {
1267
forward.insert(straightIn);
1268
check.insert(straightIn);
1269
}
1270
#ifdef DEBUG_DIRECTION_PRIORITY
1271
std::cout << "edge=" << edge->getID() << " in=" << Named::getIDSecure(straightIn) << " out=" << Named::getIDSecure(straightOut)
1272
<< " outPred=" << (straightOut != nullptr ? Named::getIDSecure(straightOut->getStraightPredecessor(straightOut->getPermissions())) : "")
1273
<< " inSucc=" << (straightIn != nullptr ? Named::getIDSecure(straightIn->getStraightContinuation(straightIn->getPermissions())) : "")
1274
<< "\n";
1275
#endif
1276
}
1277
1278
for (NBEdge* edge : bidi) {
1279
NBEdge* bidiEdge = const_cast<NBEdge*>(edge->getBidiEdge());
1280
int prio;
1281
int bidiPrio;
1282
if (forward.count(edge) != 0) {
1283
if (forward.count(bidiEdge) == 0) {
1284
prio = 3;
1285
bidiPrio = 0;
1286
} else {
1287
// both forward
1288
prio = 2;
1289
bidiPrio = 2;
1290
}
1291
} else {
1292
if (forward.count(bidiEdge) != 0) {
1293
prio = 0;
1294
bidiPrio = 3;
1295
} else {
1296
// neither forward
1297
prio = 1;
1298
bidiPrio = 1;
1299
}
1300
}
1301
if (bidiEdge == nullptr) {
1302
WRITE_WARNINGF(TL("Edge '%' was loaded with undefined priority (%) but has unambiguous main direction (no bidi edge)"), edge->getID(), edge->getPriority());
1303
}
1304
if (edge->getPriority() >= 0) {
1305
bidiPrio = 0;
1306
}
1307
if (bidiEdge != nullptr && bidiEdge->getPriority() >= 0) {
1308
prio = 0;
1309
}
1310
if (edge->getPriority() < 0) {
1311
edge->setPriority(prio);
1312
edge->setRoutingType(toString(prio));
1313
}
1314
if (bidiEdge != nullptr && bidiEdge->getPriority() < 0) {
1315
bidiEdge->setPriority(bidiPrio);
1316
bidiEdge->setRoutingType(toString(bidiPrio));
1317
}
1318
}
1319
std::map<int, int> numPrios;
1320
for (NBEdge* edge : bidi) {
1321
numPrios[edge->getPriority()]++;
1322
}
1323
if (fromUniDir) {
1324
WRITE_MESSAGE("Assigned edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
1325
} else {
1326
WRITE_MESSAGE("Extended edge priority based on main direction: " + joinToString(numPrios, " ", ":") + ".")
1327
}
1328
}
1329
1330
// ---------------------------------------------------------------------------
1331
// NBRailwaySignalGuesser methods
1332
// ---------------------------------------------------------------------------
1333
1334
int
1335
NBRailwaySignalGuesser::guessRailSignals(NBEdgeCont& ec, NBPTStopCont& sc) {
1336
const OptionsCont& oc = OptionsCont::getOptions();
1337
int addedSignals = 0;
1338
if (oc.exists("railway.signal.guess.by-stops")) {
1339
if (oc.getBool("railway.signal.guess.by-stops")) {
1340
const double minLength = oc.getFloat("osm.stop-output.length.train");
1341
addedSignals += guessByStops(ec, sc, minLength);
1342
}
1343
}
1344
return addedSignals;
1345
}
1346
1347
1348
bool
1349
NBRailwaySignalGuesser::canBeSignal(const NBNode* node) {
1350
return (node->getType() != SumoXMLNodeType::RAIL_SIGNAL && node->geometryLike());
1351
}
1352
1353
int
1354
NBRailwaySignalGuesser::guessByStops(NBEdgeCont& ec, NBPTStopCont& sc, double minLength) {
1355
int addedSignals = 0;
1356
for (auto& item : sc.getStops()) {
1357
const NBEdge* stopEdge = ec.retrieve(item.second->getEdgeId());
1358
if (stopEdge != nullptr && isRailway(stopEdge->getPermissions())) {
1359
NBNode* to = stopEdge->getToNode();
1360
if (canBeSignal(to)) {
1361
to->reinit(to->getPosition(), SumoXMLNodeType::RAIL_SIGNAL);
1362
addedSignals++;
1363
}
1364
NBNode* from = stopEdge->getFromNode();
1365
if (stopEdge->getLoadedLength() >= minLength) {
1366
/// XXX should split edge if it is too long
1367
if (canBeSignal(from)) {
1368
from->reinit(from->getPosition(), SumoXMLNodeType::RAIL_SIGNAL);
1369
addedSignals++;
1370
}
1371
} else {
1372
double searchDist = minLength - stopEdge->getLoadedLength();
1373
while (searchDist > 0 && from->geometryLike()) {
1374
for (const NBEdge* in : from->getIncomingEdges()) {
1375
if (in->getFromNode() != stopEdge->getToNode()) {
1376
// found edge that isn't a bidi predecessor
1377
stopEdge = in;
1378
break;
1379
}
1380
}
1381
if (stopEdge->getFromNode() == from) {
1382
// bidi edge without predecessor
1383
break;
1384
} else {
1385
from = stopEdge->getFromNode();
1386
}
1387
searchDist -= stopEdge->getLoadedLength();
1388
}
1389
if (searchDist <= 0 && canBeSignal(from)) {
1390
from->reinit(from->getPosition(), SumoXMLNodeType::RAIL_SIGNAL);
1391
addedSignals++;
1392
}
1393
}
1394
}
1395
}
1396
WRITE_MESSAGEF(TL("Added % rail signals at % stops."), addedSignals, sc.getStops().size());
1397
return addedSignals;
1398
}
1399
1400
1401
int
1402
NBRailwayGeometryHelper::straigthenCorrdidor(NBEdgeCont& ec, double maxAngle) {
1403
int moved = 0;
1404
int numCorridors = 0;
1405
std::set<NBNode*> railNodes = NBRailwayTopologyAnalyzer::getRailNodes(ec);
1406
std::set<NBNode*> railGeomNodes;
1407
for (NBNode* n : railNodes) {
1408
if (n->geometryLike()) {
1409
railGeomNodes.insert(n);
1410
}
1411
}
1412
std::set<NBNode*, ComparatorIdLess> kinkNodes;;
1413
for (NBNode* n : railGeomNodes) {
1414
NBEdge* in = n->getIncomingEdges().front();
1415
NBEdge* out = n->getOutgoingEdges().size() == 1 || n->getOutgoingEdges()[1]->isTurningDirectionAt(in) ? n->getOutgoingEdges().front() : n->getOutgoingEdges().back();
1416
const double relAngle = fabs(RAD2DEG(GeomHelper::angleDiff(DEG2RAD(in->getAngleAtNode(n)), DEG2RAD(out->getAngleAtNode(n)))));
1417
if (maxAngle > 0 && relAngle > maxAngle) {
1418
kinkNodes.insert(n);
1419
}
1420
}
1421
while (!kinkNodes.empty()) {
1422
std::vector<NBNode*> corridor;
1423
std::vector<NBEdge*> corridorEdges;
1424
Boundary corridorBox;
1425
double length = 0;
1426
NBNode* n = *kinkNodes.begin();
1427
kinkNodes.erase(kinkNodes.begin());
1428
// go downstream and upstream, add kinkNodes until a "long" enough
1429
// non-kink stretch is found
1430
NBEdge* in = n->getIncomingEdges().front();
1431
NBEdge* out = n->getOutgoingEdges().size() == 1 || n->getOutgoingEdges()[1]->isTurningDirectionAt(in) ? n->getOutgoingEdges().front() : n->getOutgoingEdges().back();
1432
NBEdge* const centerIn = in;
1433
NBEdge* const centerOut = out;
1434
NBNode* up = in->getFromNode();
1435
NBNode* down = out->getToNode();
1436
corridor.push_back(up);
1437
corridor.push_back(n);
1438
corridor.push_back(down);
1439
corridorBox.add(up->getPosition());
1440
corridorBox.add(down->getPosition());
1441
corridorEdges.push_back(in);
1442
corridorEdges.push_back(out);
1443
length += in->getLoadedLength();
1444
length += out->getLoadedLength();
1445
Position cBeg, cEnd, delta;
1446
while (kinkNodes.count(up) != 0) {
1447
NBEdge* const out2 = in;
1448
NBEdge* const in2 = up->getIncomingEdges().size() == 1 || up->getIncomingEdges()[1]->isTurningDirectionAt(out2) ? up->getIncomingEdges().front() : up->getIncomingEdges().back();
1449
length += in2->getLoadedLength();
1450
up = in2->getFromNode();
1451
corridor.insert(corridor.begin(), up);
1452
corridorEdges.insert(corridorEdges.begin(), in2);
1453
kinkNodes.erase(up);
1454
corridorBox.add(up->getPosition());
1455
}
1456
cBeg = up->getPosition();
1457
cEnd = down->getPosition();
1458
delta = cEnd - cBeg;
1459
while (delta.length2D() <= POSITION_EPS * (double)corridor.size() && railGeomNodes.count(up) != 0) {
1460
NBEdge* const out2 = in;
1461
NBEdge* const in2 = up->getIncomingEdges().size() == 1 || up->getIncomingEdges()[1]->isTurningDirectionAt(out2) ? up->getIncomingEdges().front() : up->getIncomingEdges().back();
1462
length += in2->getLoadedLength();
1463
up = in2->getFromNode();
1464
corridor.insert(corridor.begin(), up);
1465
corridorEdges.insert(corridorEdges.begin(), in2);
1466
kinkNodes.erase(up);
1467
corridorBox.add(up->getPosition());
1468
cBeg = up->getPosition();
1469
cEnd = down->getPosition();
1470
delta = cEnd - cBeg;
1471
}
1472
in = centerIn;
1473
out = centerOut;
1474
while (kinkNodes.count(down) != 0) {
1475
NBEdge* const in2 = out;
1476
NBEdge* const out2 = down->getOutgoingEdges().size() == 1 || down->getOutgoingEdges()[1]->isTurningDirectionAt(in2) ? down->getOutgoingEdges().front() : down->getOutgoingEdges().back();
1477
down = out2->getToNode();
1478
length += out2->getLoadedLength();
1479
corridor.push_back(down);
1480
corridorEdges.push_back(out2);
1481
kinkNodes.erase(down);
1482
corridorBox.add(down->getPosition());
1483
}
1484
cBeg = up->getPosition();
1485
cEnd = down->getPosition();
1486
delta = cEnd - cBeg;
1487
while (delta.length2D() <= POSITION_EPS * (double)corridor.size() && railGeomNodes.count(down) != 0) {
1488
NBEdge* const in2 = out;
1489
NBEdge* const out2 = down->getOutgoingEdges().size() == 1 || down->getOutgoingEdges()[1]->isTurningDirectionAt(in2) ? down->getOutgoingEdges().front() : down->getOutgoingEdges().back();
1490
down = out2->getToNode();
1491
length += out2->getLoadedLength();
1492
corridor.push_back(down);
1493
corridorEdges.push_back(out2);
1494
kinkNodes.erase(down);
1495
corridorBox.add(down->getPosition());
1496
cBeg = up->getPosition();
1497
cEnd = down->getPosition();
1498
delta = cEnd - cBeg;
1499
}
1500
// straighten all edges in corridor (corridorEdges doesn't include bidi)
1501
std::set<NBNode*> corridorNodes(corridor.begin(), corridor.end());
1502
for (NBNode* n2 : corridorNodes) {
1503
for (NBEdge* e : n2->getEdges()) {
1504
if (corridorNodes.count(e->getFromNode()) != 0
1505
&& corridorNodes.count(e->getToNode()) != 0) {
1506
PositionVector simpleGeom;
1507
simpleGeom.push_back(e->getFromNode()->getPosition());
1508
simpleGeom.push_back(e->getToNode()->getPosition());
1509
e->setGeometry(simpleGeom);
1510
}
1511
}
1512
}
1513
if (delta.length2D() > 0) {
1514
double currLength = 0;
1515
for (int i = 1; i < (int)corridor.size() - 1; i++) {
1516
currLength += corridorEdges[i - 1]->getLoadedLength();
1517
const Position newPos = cBeg + delta * (currLength / length);
1518
NBNode* const n2 = corridor[i];
1519
n2->reinit(newPos, n2->getType());
1520
for (NBEdge* e : n2->getEdges()) {
1521
e->resetEndpointAtNode(n2);
1522
}
1523
moved += 1;
1524
}
1525
numCorridors += 1;
1526
} else {
1527
WRITE_WARNINGF(TL("Could not straighten corridor %."), toString(corridor));
1528
}
1529
}
1530
//std::cout << " railNodes=" << railNodes.size() << " railGeomNodes=" << railGeomNodes.size() << " kinkNodes=" << kinkNodes.size() << "\n";
1531
1532
WRITE_MESSAGEF(TL("Moved % rail junctions for straightening % corridors."), moved, numCorridors);
1533
return moved;
1534
}
1535
1536
/****************************************************************************/
1537
1538