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