Eclipse SUMO - Simulation of Urban MObility
NBEdgeCont.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-2023 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/****************************************************************************/
21// Storage for edges, including some functionality operating on multiple edges
22/****************************************************************************/
23#include <config.h>
24
25#include <vector>
26#include <string>
27#include <cassert>
28#include <algorithm>
29#include <cmath>
30#include <utils/geom/Boundary.h>
43#include "NBNetBuilder.h"
44#include "NBEdgeCont.h"
45#include "NBNodeCont.h"
46#include "NBPTLineCont.h"
47#include "NBPTStop.h"
48#include "NBHelpers.h"
49#include "NBCont.h"
51#include "NBDistrictCont.h"
52#include "NBTypeCont.h"
53
54#define JOIN_TRAM_MAX_ANGLE 10
55#define JOIN_TRAM_MIN_LENGTH 3
56
57//#define DEBUG_GUESS_ROUNDABOUT
58//#define DEBUG_JOIN_TRAM
59#define DEBUG_EDGE_ID ""
60
61// ===========================================================================
62// method definitions
63// ===========================================================================
65 myTypeCont(tc),
66 myVehicleClasses2Keep(0),
67 myVehicleClasses2Remove(0),
68 myNeedGeoTransformedPruningBoundary(false) {
69}
70
71
73 clear();
74}
75
76
77void
79 // set edges dismiss/accept options
80 myEdgesMinSpeed = oc.getFloat("keep-edges.min-speed");
81 myRemoveEdgesAfterJoining = oc.exists("keep-edges.postload") && oc.getBool("keep-edges.postload");
82 // we possibly have to load the edges to keep/remove
83 if (oc.isSet("keep-edges.input-file")) {
84 NBHelpers::loadEdgesFromFile(oc.getString("keep-edges.input-file"), myEdges2Keep);
85 }
86 if (oc.isSet("remove-edges.input-file")) {
87 NBHelpers::loadEdgesFromFile(oc.getString("remove-edges.input-file"), myEdges2Remove);
88 }
89 if (oc.isSet("keep-edges.explicit")) {
90 const std::vector<std::string> edges = oc.getStringVector("keep-edges.explicit");
91 myEdges2Keep.insert(edges.begin(), edges.end());
92 }
93 if (oc.isSet("remove-edges.explicit")) {
94 const std::vector<std::string> edges = oc.getStringVector("remove-edges.explicit");
95 myEdges2Remove.insert(edges.begin(), edges.end());
96 }
97 if (oc.exists("keep-edges.by-vclass") && oc.isSet("keep-edges.by-vclass")) {
98 myVehicleClasses2Keep = parseVehicleClasses(oc.getStringVector("keep-edges.by-vclass"));
99 }
100 if (oc.exists("remove-edges.by-vclass") && oc.isSet("remove-edges.by-vclass")) {
101 myVehicleClasses2Remove = parseVehicleClasses(oc.getStringVector("remove-edges.by-vclass"));
102 }
103 if (oc.exists("keep-edges.by-type") && oc.isSet("keep-edges.by-type")) {
104 const std::vector<std::string> types = oc.getStringVector("keep-edges.by-type");
105 myTypes2Keep.insert(types.begin(), types.end());
106 }
107 if (oc.exists("remove-edges.by-type") && oc.isSet("remove-edges.by-type")) {
108 const std::vector<std::string> types = oc.getStringVector("remove-edges.by-type");
109 myTypes2Remove.insert(types.begin(), types.end());
110 }
111
112 if (oc.isSet("keep-edges.in-boundary") || oc.isSet("keep-edges.in-geo-boundary")) {
113
114 std::string polyPlainString = oc.getValueString(oc.isSet("keep-edges.in-boundary") ?
115 "keep-edges.in-boundary" : "keep-edges.in-geo-boundary");
116 // try interpreting the boundary like shape attribute with spaces
117 bool ok = true;
118 PositionVector boundaryShape = GeomConvHelper::parseShapeReporting(polyPlainString, "pruning-boundary", 0, ok, false, false);
119 if (ok) {
120 if (boundaryShape.size() < 2) {
121 throw ProcessError(TL("Invalid boundary: need at least 2 coordinates"));
122 } else if (boundaryShape.size() == 2) {
123 // prunning boundary (box)
124 myPruningBoundary.push_back(boundaryShape[0]);
125 myPruningBoundary.push_back(Position(boundaryShape[1].x(), boundaryShape[0].y()));
126 myPruningBoundary.push_back(boundaryShape[1]);
127 myPruningBoundary.push_back(Position(boundaryShape[0].x(), boundaryShape[1].y()));
128 } else {
129 myPruningBoundary = boundaryShape;
130 }
131 } else {
132 // maybe positions are separated by ',' instead of ' '
133 std::vector<std::string> polyS = oc.getStringVector(oc.isSet("keep-edges.in-boundary") ?
134 "keep-edges.in-boundary" : "keep-edges.in-geo-boundary");
135 std::vector<double> poly;
136 for (std::vector<std::string>::iterator i = polyS.begin(); i != polyS.end(); ++i) {
137 poly.push_back(StringUtils::toDouble((*i))); // !!! may throw something anyhow...
138 }
139 if (poly.size() < 4) {
140 throw ProcessError(TL("Invalid boundary: need at least 2 coordinates"));
141 } else if (poly.size() % 2 != 0) {
142 throw ProcessError(TL("Invalid boundary: malformed coordinate"));
143 } else if (poly.size() == 4) {
144 // prunning boundary (box)
145 myPruningBoundary.push_back(Position(poly[0], poly[1]));
146 myPruningBoundary.push_back(Position(poly[2], poly[1]));
147 myPruningBoundary.push_back(Position(poly[2], poly[3]));
148 myPruningBoundary.push_back(Position(poly[0], poly[3]));
149 } else {
150 for (std::vector<double>::iterator j = poly.begin(); j != poly.end();) {
151 double x = *j++;
152 double y = *j++;
153 myPruningBoundary.push_back(Position(x, y));
154 }
155 }
156 }
157 myNeedGeoTransformedPruningBoundary = oc.isSet("keep-edges.in-geo-boundary");
158 }
159}
160
161
162void
164 for (const auto& i : myEdges) {
165 delete i.second;
166 }
167 myEdges.clear();
168 for (const auto& i : myExtractedEdges) {
169 delete i.second;
170 }
171 myExtractedEdges.clear();
172 for (NBEdge* const e : myEdgeCemetery) {
173 delete e;
174 }
175 myEdgeCemetery.clear();
176}
177
178
179
180// ----- edge access methods
181bool
182NBEdgeCont::insert(NBEdge* edge, bool ignorePrunning) {
183 if (myEdges.count(edge->getID()) != 0) {
184 return false;
185 }
186 if (!ignorePrunning && ignoreFilterMatch(edge)) {
187 edge->getFromNode()->removeEdge(edge);
188 edge->getToNode()->removeEdge(edge);
189 myIgnoredEdges.insert(edge->getID());
190 delete edge;
191 } else {
193 if (oc.exists("dismiss-vclasses") && oc.getBool("dismiss-vclasses")) {
195 }
196 myEdges[edge->getID()] = edge;
197 }
198 return true;
199}
200
201
202bool
204 // remove edges which allow a speed below a set one (set using "keep-edges.min-speed")
205 if (edge->getSpeed() < myEdgesMinSpeed) {
206 return true;
207 }
208 // check whether the edge is a named edge to keep
209 if (!myRemoveEdgesAfterJoining && myEdges2Keep.size() != 0) {
210 if (myEdges2Keep.count(edge->getID()) == 0) {
211 // explicit whitelisting may be combined additively with other filters
213 && myTypes2Keep.size() == 0 && myTypes2Remove.size() == 0
214 && myPruningBoundary.size() == 0) {
215 return true;
216 }
217 } else {
218 // explicit whitelisting overrides other filters
219 return false;
220 }
221 }
222 // check whether the edge is a named edge to remove
223 if (myEdges2Remove.size() != 0) {
224 if (myEdges2Remove.count(edge->getID()) != 0) {
225 return true;
226 }
227 }
228 // check whether the edge shall be removed because it does not allow any of the wished classes
229 if (myVehicleClasses2Keep != 0 && (myVehicleClasses2Keep & edge->getPermissions()) == 0) {
230 return true;
231 }
232 // check whether the edge shall be removed due to allowing unwished classes only
234 return true;
235 }
236 // check whether the edge shall be removed because it does not have one of the requested types
237 if (myTypes2Keep.size() != 0) {
238 if (myTypes2Keep.count(edge->getTypeID()) == 0) {
239 return true;
240 }
241 }
242 // check whether the edge shall be removed because it has one of the forbidden types
243 if (myTypes2Remove.size() != 0) {
244 if (myTypes2Remove.count(edge->getTypeID()) > 0) {
245 return true;
246 }
247 }
248 // check whether the edge is within the pruning boundary
249 if (myPruningBoundary.size() != 0) {
251 if (GeoConvHelper::getProcessing().usingGeoProjection()) {
253 } else if (GeoConvHelper::getLoaded().usingGeoProjection()) {
254 // XXX what if input file with different projections are loaded?
255 for (int i = 0; i < (int) myPruningBoundary.size(); i++) {
257 }
258 } else {
259 WRITE_ERROR(TL("Cannot prune edges using a geo-boundary because no projection has been loaded"));
260 }
262 }
263 if (!(edge->getGeometry().getBoxBoundary().grow(POSITION_EPS).overlapsWith(myPruningBoundary))) {
264 return true;
265 } else if (!(edge->getGeometry().partialWithin(myPruningBoundary, 2 * POSITION_EPS) || edge->getGeometry().intersects(myPruningBoundary))) {
266 // a more detailed check is necessary because the bounding box may be much bigger than the edge
267 // @note: overlapsWith implicitly closes the edge shape but this is not wanted here
268 return true;
269 }
270 }
272 return true;
273 }
274 return false;
275}
276
277
278NBEdge*
279NBEdgeCont::retrieve(const std::string& id, bool retrieveExtracted) const {
280 EdgeCont::const_iterator i = myEdges.find(id);
281 if (i == myEdges.end()) {
282 if (retrieveExtracted) {
283 i = myExtractedEdges.find(id);
284 if (i == myExtractedEdges.end()) {
285 return nullptr;
286 }
287 } else {
288 return nullptr;
289 }
290 }
291 return (*i).second;
292}
293
294// FIXME: This can't work
295/*
296NBEdge*
297NBEdgeCont::retrievePossiblySplit(const std::string& id, bool downstream) const {
298 NBEdge* edge = retrieve(id);
299 if (edge == 0) {
300 return 0;
301 }
302 const EdgeVector* candidates = downstream ? &edge->getToNode()->getOutgoingEdges() : &edge->getFromNode()->getIncomingEdges();
303 while (candidates->size() == 1) {
304 const std::string& nextID = candidates->front()->getID();
305 if (nextID.find(id) != 0 || nextID.size() <= id.size() + 1 || (nextID[id.size()] != '.' && nextID[id.size()] != '-')) {
306 break;
307 }
308 edge = candidates->front();
309 candidates = downstream ? &edge->getToNode()->getOutgoingEdges() : &edge->getFromNode()->getIncomingEdges();
310 }
311 return edge;
312}*/
313
314NBEdge*
315NBEdgeCont::retrievePossiblySplit(const std::string& id, bool downstream) const {
316 NBEdge* edge = retrieve(id);
317 if (edge != nullptr) {
318 return edge;
319 }
320 // NOTE: (TODO) for multiply split edges (e.g. 15[0][0]) one could try recursion
321 if ((retrieve(id + "[0]") != nullptr) && (retrieve(id + "[1]") != nullptr)) {
322 // Edge was split during the netbuilding process
323 if (downstream == true) {
324 return retrieve(id + "[1]");
325 } else {
326 return retrieve(id + "[0]");
327 }
328 }
329 return edge;
330}
331
332
333NBEdge*
334NBEdgeCont::retrievePossiblySplit(const std::string& id, const std::string& hint, bool incoming) const {
335 // try to retrieve using the given name (iterative)
336 NBEdge* edge = retrieve(id);
337 if (edge != nullptr) {
338 return edge;
339 }
340 // now, we did not find it; we have to look over all possibilities
341 EdgeVector hints;
342 // check whether at least the hint was not splitted
343 NBEdge* hintedge = retrieve(hint);
344 if (hintedge == nullptr) {
345 hints = getGeneratedFrom(hint);
346 } else {
347 hints.push_back(hintedge);
348 }
349 EdgeVector candidates = getGeneratedFrom(id);
350 for (const NBEdge* const currHint : hints) {
351 for (NBEdge* const poss_searched : candidates) {
352 const NBNode* const node = incoming ? poss_searched->myTo : poss_searched->myFrom;
353 const EdgeVector& cont = incoming ? node->getOutgoingEdges() : node->getIncomingEdges();
354 if (find(cont.begin(), cont.end(), currHint) != cont.end()) {
355 return poss_searched;
356 }
357 }
358 }
359 return nullptr;
360}
361
362
363NBEdge*
364NBEdgeCont::retrievePossiblySplit(const std::string& id, double pos) const {
365 // check whether the edge was not split, yet
366 NBEdge* edge = retrieve(id);
367 if (edge != nullptr) {
368 return edge;
369 }
370 int maxLength = 0;
371 std::string tid = id + "[";
372 for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
373 if ((*i).first.find(tid) == 0) {
374 maxLength = MAX2(maxLength, (int)(*i).first.length());
375 }
376 }
377 // find the part of the edge which matches the position
378 double seen = 0;
379 std::vector<std::string> names;
380 names.push_back(id + "[1]");
381 names.push_back(id + "[0]");
382 while (names.size() > 0) {
383 // retrieve the first subelement (to follow)
384 std::string cid = names.back();
385 names.pop_back();
386 edge = retrieve(cid);
387 // The edge was splitted; check its subparts within the
388 // next step
389 if (edge == nullptr) {
390 if ((int)cid.length() + 3 < maxLength) {
391 names.push_back(cid + "[1]");
392 names.push_back(cid + "[0]");
393 }
394 }
395 // an edge with the name was found,
396 // check whether the position lies within it
397 else {
398 seen += edge->getLength();
399 if (seen >= pos) {
400 return edge;
401 }
402 }
403 }
404 return nullptr;
405}
406
407
408void
410 extract(dc, edge);
411 delete edge;
412}
413
414
415void
416NBEdgeCont::extract(NBDistrictCont& dc, NBEdge* edge, bool remember) {
417 if (remember) {
418 const auto& prevExtracted = myExtractedEdges.find(edge->getID());
419 if (prevExtracted != myExtractedEdges.end()) {
420 if (edge != prevExtracted->second) {
421 myEdgeCemetery.insert(prevExtracted->second);
422 prevExtracted->second = edge;
423 }
424 } else {
425 myExtractedEdges[edge->getID()] = edge;
426 }
427 }
428 myEdges.erase(edge->getID());
429 edge->myFrom->removeEdge(edge);
430 edge->myTo->removeEdge(edge);
432}
433
434
435void
436NBEdgeCont::rename(NBEdge* edge, const std::string& newID) {
437 if (myEdges.count(newID) != 0) {
438 throw ProcessError(TLF("Attempt to rename edge using existing id '%'", newID));
439 }
440 myEdges.erase(edge->getID());
441 edge->setID(newID);
442 myEdges[newID] = edge;
443 // update oppositeID
444 if (edge->getLanes().back().oppositeID != "") {
445 NBEdge* oppo = retrieve(SUMOXMLDefinitions::getEdgeIDFromLane(edge->getLanes().back().oppositeID));
446 if (oppo != nullptr) {
447 oppo->getLaneStruct(oppo->getNumLanes() - 1).oppositeID = edge->getLaneID(edge->getNumLanes() - 1);
448 }
449 }
450}
451
452
453// ----- explicit edge manipulation methods
454
455void
456NBEdgeCont::processSplits(NBEdge* e, std::vector<Split> splits,
458 if (splits.empty()) {
459 return;
460 }
461 const std::string origID = e->getID();
462 sort(splits.begin(), splits.end(), split_sorter());
463 int maxNumLanes = e->getNumLanes();
464 // compute the node positions and sort the lanes
465 for (Split& split : splits) {
466 sort(split.lanes.begin(), split.lanes.end());
467 maxNumLanes = MAX2(maxNumLanes, (int)split.lanes.size());
468 }
469 // split the edge
470 std::vector<int> currLanes;
471 for (int l = 0; l < e->getNumLanes(); ++l) {
472 currLanes.push_back(l);
473 }
474 if (e->getNumLanes() != (int)splits.back().lanes.size()) {
475 // invalidate traffic light definitions loaded from a SUMO network
476 e->getToNode()->invalidateTLS(tlc, true, true);
477 // if the number of lanes changes the connections should be
478 // recomputed
479 e->invalidateConnections(true);
480 }
481
482 std::string firstID = "";
483 double seen = 0;
484 for (const Split& exp : splits) {
485 assert(exp.lanes.size() != 0);
486 if (exp.pos > 0 && e->getLoadedLength() + seen > exp.pos && exp.pos > seen) {
487 nc.insert(exp.node);
488 nc.markAsSplit(exp.node);
489 // split the edge
490 const std::string idBefore = exp.idBefore == "" ? e->getID() : exp.idBefore;
491 const std::string idAfter = exp.idAfter == "" ? exp.nameID : exp.idAfter;
492 if (firstID == "") {
493 firstID = idBefore;
494 }
495 const bool ok = splitAt(dc, e, exp.pos - seen, exp.node,
496 idBefore, idAfter, e->getNumLanes(), (int) exp.lanes.size(), exp.speed);
497 if (!ok) {
498 WRITE_WARNINGF(TL("Error on parsing a split (edge '%')."), origID);
499 return;
500 }
501 seen = exp.pos;
502 std::vector<int> newLanes = exp.lanes;
503 NBEdge* pe = retrieve(idBefore);
504 NBEdge* ne = retrieve(idAfter);
505 // reconnect lanes
506 pe->invalidateConnections(true);
507 // new on right
508 int rightMostP = currLanes[0];
509 int rightMostN = newLanes[0];
510 for (int l = 0; l < (int) rightMostP - (int) rightMostN; ++l) {
512 }
513 // new on left
514 int leftMostP = currLanes.back();
515 int leftMostN = newLanes.back();
516 for (int l = 0; l < (int) leftMostN - (int) leftMostP; ++l) {
517 pe->addLane2LaneConnection(pe->getNumLanes() - 1, ne, leftMostN - l - rightMostN, NBEdge::Lane2LaneInfoType::VALIDATED, true);
518 }
519 // all other connected
520 for (int l = 0; l < maxNumLanes; ++l) {
521 if (find(currLanes.begin(), currLanes.end(), l) == currLanes.end()) {
522 continue;
523 }
524 if (find(newLanes.begin(), newLanes.end(), l) == newLanes.end()) {
525 continue;
526 }
527 pe->addLane2LaneConnection(l - rightMostP, ne, l - rightMostN, NBEdge::Lane2LaneInfoType::VALIDATED, true);
528 }
529 // if there are edges at this node which are not connected
530 // we can assume that this split was attached to an
531 // existing node. Reset all connections to let the default
532 // algorithm recompute them
533 if (exp.node->getIncomingEdges().size() > 1 || exp.node->getOutgoingEdges().size() > 1) {
534 for (NBEdge* in : exp.node->getIncomingEdges()) {
535 in->invalidateConnections(true);
536 }
537 }
538 // move to next
539 e = ne;
540 currLanes = newLanes;
541 } else if (exp.pos == 0) {
542 const int laneCountDiff = e->getNumLanes() - (int)exp.lanes.size();
543 if (laneCountDiff < 0) {
544 e->incLaneNo(-laneCountDiff);
545 } else {
546 e->decLaneNo(laneCountDiff);
547 }
548 currLanes = exp.lanes;
549 // invalidate traffic light definition loaded from a SUMO network
550 // XXX it would be preferable to reconstruct the phase definitions heuristically
551 e->getFromNode()->invalidateTLS(tlc, true, true);
552 if (exp.speed != -1.) {
553 e->setSpeed(-1, exp.speed);
554 }
555 } else {
556 WRITE_WARNINGF(TL("Split at '%' lies beyond the edge's length (edge '%')."), toString(exp.pos), origID);
557 }
558 }
559 // patch lane offsets
560 e = retrieve(firstID);
561 if (e != nullptr) {
562 if (splits.front().pos != 0) {
563 // add a dummy split at the beginning to ensure correct offset
564 Split start;
565 start.pos = 0;
566 for (int lane = 0; lane < (int)e->getNumLanes(); ++lane) {
567 start.lanes.push_back(lane);
568 }
569 start.offset = splits.front().offset;
570 start.offsetFactor = splits.front().offsetFactor;
571 splits.insert(splits.begin(), start);
572 }
573 for (const Split& split : splits) {
574 int maxLeft = split.lanes.back();
575 double offset = split.offset;
576 if (maxLeft < maxNumLanes) {
578 offset += split.offsetFactor * SUMO_const_laneWidth * (maxNumLanes - 1 - maxLeft);
579 } else {
580 offset += split.offsetFactor * SUMO_const_halfLaneWidth * (maxNumLanes - 1 - maxLeft);
581 }
582 }
583 int maxRight = split.lanes.front();
584 if (maxRight > 0 && e->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
585 offset -= split.offsetFactor * SUMO_const_halfLaneWidth * maxRight;
586 }
587 //std::cout << " processSplits " << origID << " splitOffset=" << (*i).offset << " offset=" << offset << "\n";
588 if (offset != 0) {
590 g.move2side(offset);
591 e->setGeometry(g);
592 }
593 if (e->getToNode()->getOutgoingEdges().size() != 0) {
594 e = e->getToNode()->getOutgoingEdges()[0];
595 }
596 }
597 }
598}
599
600
601bool
603 return splitAt(dc, edge, node, edge->getID() + "[0]", edge->getID() + "[1]",
604 (int) edge->myLanes.size(), (int) edge->myLanes.size());
605}
606
607
608bool
610 const std::string& firstEdgeName,
611 const std::string& secondEdgeName,
612 int noLanesFirstEdge, int noLanesSecondEdge,
613 const double speed, const double friction,
614 const int changedLeft) {
615 double pos;
617 if (pos <= 0) {
619 edge->myFrom->getPosition(), edge->myTo->getPosition(),
620 node->getPosition());
621 }
622 if (pos <= 0 || pos + POSITION_EPS > edge->getGeometry().length()) {
623 return false;
624 }
625 return splitAt(dc, edge, pos, node, firstEdgeName, secondEdgeName,
626 noLanesFirstEdge, noLanesSecondEdge, speed, friction, changedLeft);
627}
628
629
630bool
632 NBEdge* edge, double pos, NBNode* node,
633 const std::string& firstEdgeName,
634 const std::string& secondEdgeName,
635 int noLanesFirstEdge, int noLanesSecondEdge,
636 const double speed, const double friction,
637 const int changedLeft) {
638 if (firstEdgeName != edge->getID() && myEdges.count(firstEdgeName) != 0) {
639 WRITE_ERRORF(TL("Could not insert edge '%' before split of edge '%'."), firstEdgeName, edge->getID());
640 return false;
641 }
642 if (secondEdgeName == firstEdgeName || (secondEdgeName != edge->getID() && myEdges.count(secondEdgeName) != 0)) {
643 WRITE_ERRORF(TL("Could not insert edge '%' after split of edge '%'."), secondEdgeName, edge->getID());
644 return false;
645 }
646 // there must be at least some overlap between first and second edge
647 assert(changedLeft > -((int)noLanesFirstEdge));
648 assert(changedLeft < (int)noLanesSecondEdge);
649
650 // build the new edges' geometries
651 double geomPos = pos;
652 if (edge->hasLoadedLength()) {
653 geomPos *= edge->getGeometry().length() / edge->getLoadedLength();
654 }
655 std::pair<PositionVector, PositionVector> geoms = edge->getGeometry().splitAt(geomPos);
656 // reduce inaccuracies and preserve bidi
657 if (geoms.first[-1].almostSame(node->getPosition()) || edge->isBidi()) {
658 geoms.first[-1] = node->getPosition();
659 geoms.second[0] = node->getPosition();
660 }
661 // build and insert the edges
662 NBEdge* one = new NBEdge(firstEdgeName, edge->myFrom, node, edge, geoms.first, noLanesFirstEdge);
663 NBEdge* two = new NBEdge(secondEdgeName, node, edge->myTo, edge, geoms.second, noLanesSecondEdge);
664 if (OptionsCont::getOptions().getBool("output.original-names")) {
665 const std::string origID = edge->getLaneStruct(0).getParameter(SUMO_PARAM_ORIGID, edge->getID());
666 if (firstEdgeName != origID) {
667 one->setOrigID(origID, false);
668 }
669 if (secondEdgeName != origID) {
670 two->setOrigID(origID, false);
671 }
672 }
673 two->copyConnectionsFrom(edge);
674 if (speed != -1.) {
675 two->setSpeed(-1, speed);
676 }
677 if (friction != -1.) {
678 two->setFriction(-1, friction);
679 }
680 if (edge->getDistance() != 0) {
681 one->setDistance(edge->getDistance());
682 two->setDistance(one->getDistance() + pos);
683 }
684 if (edge->hasLoadedLength()) {
685 one->setLoadedLength(pos);
686 two->setLoadedLength(edge->getLoadedLength() - pos);
687 }
688 // replace information about this edge within the nodes
689 edge->myFrom->replaceOutgoing(edge, one, 0);
690 edge->myTo->replaceIncoming(edge, two, 0);
691 // patch tls
692 for (NBTrafficLightDefinition* const tld : edge->myFrom->getControllingTLS()) {
693 tld->replaceRemoved(edge, -1, one, -1, false);
694 }
695 for (NBTrafficLightDefinition* const tld : edge->myTo->getControllingTLS()) {
696 tld->replaceRemoved(edge, -1, two, -1, true);
697 }
698 // the edge is now occuring twice in both nodes...
699 // clean up
700 edge->myFrom->removeDoubleEdges();
701 edge->myTo->removeDoubleEdges();
702 // add connections from the first to the second edge
703 // there will be as many connections as there are lanes on the second edge
704 // by default lanes will be added / discontinued on the right side
705 // (appropriate for highway on-/off-ramps)
706 const int offset = (int)one->getNumLanes() - (int)two->getNumLanes() + changedLeft;
707 for (int i2 = 0; i2 < (int)two->getNumLanes(); i2++) {
708 const int i1 = MIN2(MAX2((int)0, i2 + offset), (int)one->getNumLanes());
710 throw ProcessError(TL("Could not set connection!"));
711 }
712 }
714 if (myEdges2Keep.count(edge->getID()) != 0) {
715 myEdges2Keep.insert(one->getID());
716 myEdges2Keep.insert(two->getID());
717 }
718 if (myEdges2Remove.count(edge->getID()) != 0) {
719 myEdges2Remove.insert(one->getID());
720 myEdges2Remove.insert(two->getID());
721 }
722 }
723 // erase the splitted edge
724 patchRoundabouts(edge, one, two, myRoundabouts);
725 patchRoundabouts(edge, one, two, myGuessedRoundabouts);
726 const std::string oldID = edge->getID();
727 extract(dc, edge, true);
728 insert(one, true); // duplicate id check happened earlier
729 insert(two, true); // duplicate id check happened earlier
730 myEdgesSplit[edge] = {one, two};
731 myWasSplit.insert(one);
732 myWasSplit.insert(two);
733 return true;
734}
735
736
737void
738NBEdgeCont::patchRoundabouts(NBEdge* orig, NBEdge* part1, NBEdge* part2, std::set<EdgeSet>& roundabouts) {
739 std::set<EdgeSet> addLater;
740 for (std::set<EdgeSet>::iterator it = roundabouts.begin(); it != roundabouts.end(); ++it) {
741 EdgeSet roundaboutSet = *it;
742 if (roundaboutSet.count(orig) > 0) {
743 roundaboutSet.erase(orig);
744 roundaboutSet.insert(part1);
745 roundaboutSet.insert(part2);
746 }
747 addLater.insert(roundaboutSet);
748 }
749 roundabouts.clear();
750 roundabouts.insert(addLater.begin(), addLater.end());
751}
752
753
754// ----- container access methods
755std::vector<std::string>
757 std::vector<std::string> ret;
758 for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
759 ret.push_back((*i).first);
760 }
761 return ret;
762}
763
764
765// ----- Adapting the input
766void
768 EdgeVector toRemove;
769 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
770 NBEdge* edge = (*i).second;
771 if (!myEdges2Keep.count(edge->getID())) {
772 edge->getFromNode()->removeEdge(edge);
773 edge->getToNode()->removeEdge(edge);
774 toRemove.push_back(edge);
775 }
776 }
777 for (EdgeVector::iterator j = toRemove.begin(); j != toRemove.end(); ++j) {
778 erase(dc, *j);
779 }
780}
781
782
783void
785 // make a copy of myEdges because splitting will modify it
786 EdgeCont edges = myEdges;
787 for (auto& item : edges) {
788 NBEdge* edge = item.second;
789 if (edge->getGeometry().size() < 3) {
790 continue;
791 }
792 PositionVector geom = edge->getGeometry();
793 const std::string id = edge->getID();
794 double offset = 0;
795 for (int i = 1; i < (int)geom.size() - 1; i++) {
796 offset += geom[i - 1].distanceTo(geom[i]);
797 std::string nodeID = id + "." + toString((int)offset);
798 if (!nc.insert(nodeID, geom[i])) {
799 WRITE_WARNING("Could not split geometry of edge '" + id + "' at index " + toString(i));
800 continue;
801 }
802 NBNode* node = nc.retrieve(nodeID);
803 splitAt(dc, edge, node, edge->getID(), nodeID, edge->getNumLanes(), edge->getNumLanes());
804 edge = retrieve(nodeID);
805 }
806 }
807}
808
809
810void
811NBEdgeCont::reduceGeometries(const double minDist) {
812 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
813 (*i).second->reduceGeometry(minDist);
814 }
815}
816
817
818void
819NBEdgeCont::checkGeometries(const double maxAngle, const double minRadius, bool fix, bool fixRailways, bool silent) {
820 if (maxAngle > 0 || minRadius > 0) {
821 for (auto& item : myEdges) {
822 if (isSidewalk(item.second->getPermissions()) || isForbidden(item.second->getPermissions())) {
823 continue;
824 }
825 item.second->checkGeometry(maxAngle, minRadius, fix || (fixRailways && isRailway(item.second->getPermissions())), silent);
826 }
827 }
828}
829
830
831// ----- processing methods
832void
834 for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); i++) {
835 (*i).second->clearControllingTLInformation();
836 }
837}
838
839
840void
842 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
843 (*i).second->sortOutgoingConnectionsByAngle();
844 }
845}
846
847
848void
850 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
851 (*i).second->computeEdge2Edges(noLeftMovers);
852 }
853}
854
855
856void
858 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
859 (*i).second->computeLanes2Edges();
860 }
861}
862
863
864void
866 const bool fixOppositeLengths = OptionsCont::getOptions().getBool("opposites.guess.fix-lengths");
867 for (const auto& edgeIt : myEdges) {
868 NBEdge* const edge = edgeIt.second;
869 edge->recheckLanes();
870 // check opposites
871 if (edge->getNumLanes() > 0) {
872 const int leftmostLane = edge->getNumLanes() - 1;
873 // check oppositeID stored in other lanes
874 for (int i = 0; i < leftmostLane; i++) {
875 const std::string& oppositeID = edge->getLanes()[i].oppositeID;
876 NBEdge* oppEdge = retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
877 if (oppositeID != "" && oppositeID != "-") {
878 if (edge->getLanes().back().oppositeID == "" && oppEdge != nullptr) {
879 edge->getLaneStruct(leftmostLane).oppositeID = oppositeID;
880 WRITE_WARNINGF(TL("Moving opposite lane '%' from invalid lane '%' to lane index %."), oppositeID, edge->getLaneID(i), leftmostLane);
881 } else {
882 WRITE_WARNINGF(TL("Removing opposite lane '%' for invalid lane '%'."), oppositeID, edge->getLaneID(i));
883 }
884 edge->getLaneStruct(i).oppositeID = "";
885 }
886 }
887 const std::string& oppositeID = edge->getLanes().back().oppositeID;
888 if (oppositeID != "" && oppositeID != "-") {
889 NBEdge* oppEdge = retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
890 if (oppEdge == nullptr) {
891 WRITE_WARNINGF(TL("Removing unknown opposite lane '%' for edge '%'."), oppositeID, edge->getID());
892 edge->getLaneStruct(leftmostLane).oppositeID = "";
893 continue;
894 } else if (oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
895 const std::string oppEdgeLeftmost = oppEdge->getLaneID(oppEdge->getNumLanes() - 1);
896 WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppositeID, edge->getID(), oppEdgeLeftmost);
897 edge->getLaneStruct(leftmostLane).oppositeID = oppEdgeLeftmost;
898 }
899 NBEdge::Lane& oppLane = oppEdge->getLaneStruct(oppEdge->getNumLanes() - 1);
900 if (oppLane.oppositeID == "") {
901 const std::string leftmostID = edge->getLaneID(leftmostLane);
902 WRITE_WARNINGF(TL("Adapting missing opposite lane '%' for edge '%'."), leftmostID, oppEdge->getID());
903 oppLane.oppositeID = leftmostID;
904 }
905 if (fabs(oppEdge->getLoadedLength() - edge->getLoadedLength()) > NUMERICAL_EPS) {
906 if (fixOppositeLengths) {
907 const double avgLength = 0.5 * (edge->getFinalLength() + oppEdge->getFinalLength());
908 WRITE_WARNINGF(TL("Averaging edge lengths for lane '%' (length %) and edge '%' (length %)."),
909 oppositeID, oppEdge->getLoadedLength(), edge->getID(), edge->getLoadedLength());
910 edge->setLoadedLength(avgLength);
911 oppEdge->setLoadedLength(avgLength);
912 } else {
913 WRITE_ERROR("Opposite lane '" + oppositeID + "' (length " + toString(oppEdge->getLoadedLength()) +
914 ") differs in length from edge '" + edge->getID() + "' (length " +
915 toString(edge->getLoadedLength()) + "). Set --opposites.guess.fix-lengths to fix this.");
916 edge->getLaneStruct(edge->getNumLanes() - 1).oppositeID = "";
917 continue;
918 }
919 }
920 if (oppEdge->getFromNode() != edge->getToNode() || oppEdge->getToNode() != edge->getFromNode()) {
921 WRITE_ERRORF(TL("Opposite lane '%' does not connect the same nodes as edge '%'!"), oppositeID, edge->getID());
922 edge->getLaneStruct(edge->getNumLanes() - 1).oppositeID = "";
923 }
924 }
925 }
926 }
927}
928
929
930void
931NBEdgeCont::appendTurnarounds(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike) {
932 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
933 (*i).second->appendTurnaround(noTLSControlled, noFringe, onlyDeadends, onlyTurnlane, noGeometryLike, true);
934 }
935}
936
937
938void
939NBEdgeCont::appendTurnarounds(const std::set<std::string>& ids, bool noTLSControlled) {
940 for (std::set<std::string>::const_iterator it = ids.begin(); it != ids.end(); it++) {
941 myEdges[*it]->appendTurnaround(noTLSControlled, false, false, false, false, false);
942 }
943}
944
945
946void
948 std::set<std::string> stopEdgeIDs;
949 for (auto& stopItem : sc.getStops()) {
950 stopEdgeIDs.insert(stopItem.second->getEdgeId());
951 }
952 for (auto& item : myEdges) {
953 NBEdge* edge = item.second;
954 if (edge->isBidiRail()
955 && (stopEdgeIDs.count(item.first) > 0 ||
956 stopEdgeIDs.count(edge->getTurnDestination(true)->getID()) > 0)) {
957 NBEdge* to = edge->getTurnDestination(true);
958 assert(to != 0);
959 edge->setConnection(edge->getNumLanes() - 1,
960 to, to->getNumLanes() - 1, NBEdge::Lane2LaneInfoType::VALIDATED, false, false,
964 }
965 }
966}
967
968void
969NBEdgeCont::computeEdgeShapes(double smoothElevationThreshold) {
970 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
971 (*i).second->computeEdgeShape(smoothElevationThreshold);
972 }
973 // equalize length of opposite edges
974 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
975 NBEdge* edge = i->second;
976 const std::string& oppositeID = edge->getLanes().back().oppositeID;
977 if (oppositeID != "" && oppositeID != "-") {
978 NBEdge* oppEdge = retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
979 if (oppEdge == nullptr || oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
980 continue;
981 }
982 if (fabs(oppEdge->getLength() - edge->getLength()) > NUMERICAL_EPS) {
983 double avgLength = (oppEdge->getLength() + edge->getLength()) / 2;
984 edge->setAverageLengthWithOpposite(avgLength);
985 oppEdge->setAverageLengthWithOpposite(avgLength);
986 }
987 }
988 }
989}
990
991
992void
994 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
995 (*i).second->computeLaneShapes();
996 }
997}
998
999
1000void
1003 EdgeVector edges) {
1004 // !!! Attention!
1005 // No merging of the geometry to come is being done
1006 // The connections are moved from one edge to another within
1007 // the replacement where the edge is a node's incoming edge.
1008
1009 // count the number of lanes, the speed and the id
1010 int nolanes = 0;
1011 double speed = 0;
1012 int priority = -1;
1013 bool joinEdges = true;
1014 std::string id;
1015 sort(edges.begin(), edges.end(), NBContHelper::same_connection_edge_sorter());
1016 // retrieve the connected nodes
1017 NBEdge* tpledge = *(edges.begin());
1018 NBNode* from = tpledge->getFromNode();
1019 NBNode* to = tpledge->getToNode();
1020 EdgeVector::const_iterator i;
1021 int myPriority = (*edges.begin())->getPriority();
1022 for (i = edges.begin(); i != edges.end(); i++) {
1023 // some assertions
1024 assert((*i)->getFromNode() == from);
1025 assert((*i)->getToNode() == to);
1026 // ad the number of lanes the current edge has
1027 nolanes += (*i)->getNumLanes();
1028 // build the id
1029 if (i != edges.begin()) {
1030 id += "+";
1031 }
1032 id += (*i)->getID();
1033 // compute the speed
1034 speed += (*i)->getSpeed();
1035 // build the priority
1036 // merged edges should have the same inherited priority
1037 if (myPriority == (*i)->getPriority()) {
1038 priority = myPriority;
1039 } else {
1040 priority = -1;
1041 joinEdges = false;
1042 }
1043 }
1044 if (joinEdges) {
1045 speed /= (double)edges.size();
1046 // build the new edge
1047 NBEdge* newEdge = new NBEdge(id, from, to, "", speed, NBEdge::UNSPECIFIED_FRICTION, nolanes, priority,
1049 tpledge->myLaneSpreadFunction, tpledge->getStreetName());
1050 // copy lane attributes
1051 int laneIndex = 0;
1052 for (i = edges.begin(); i != edges.end(); ++i) {
1053 const std::vector<NBEdge::Lane>& lanes = (*i)->getLanes();
1054 for (int j = 0; j < (int)lanes.size(); ++j) {
1055 newEdge->setPermissions(lanes[j].permissions, laneIndex);
1056 newEdge->setLaneWidth(laneIndex, lanes[j].width);
1057 newEdge->setEndOffset(laneIndex, lanes[j].endOffset);
1058 laneIndex++;
1059 }
1060 }
1061 insert(newEdge, true);
1062 // replace old edge by current within the nodes
1063 // and delete the old
1064 from->replaceOutgoing(edges, newEdge);
1065 to->replaceIncoming(edges, newEdge);
1066 // patch connections
1067 // add edge2edge-information
1068 for (i = edges.begin(); i != edges.end(); i++) {
1069 EdgeVector ev = (*i)->getConnectedEdges();
1070 for (EdgeVector::iterator j = ev.begin(); j != ev.end(); j++) {
1071 newEdge->addEdge2EdgeConnection(*j);
1072 }
1073 }
1074 // copy outgoing connections to the new edge
1075 int currLane = 0;
1076 for (i = edges.begin(); i != edges.end(); i++) {
1077 newEdge->moveOutgoingConnectionsFrom(*i, currLane);
1078 currLane += (*i)->getNumLanes();
1079 }
1080 // patch tl-information
1081 currLane = 0;
1082 for (i = edges.begin(); i != edges.end(); i++) {
1083 int noLanes = (*i)->getNumLanes();
1084 for (int j = 0; j < noLanes; j++, currLane++) {
1085 // replace in traffic lights
1086 tlc.replaceRemoved(*i, j, newEdge, currLane, true);
1087 tlc.replaceRemoved(*i, j, newEdge, currLane, false);
1088 }
1089 }
1090 // delete joined edges
1091 for (i = edges.begin(); i != edges.end(); i++) {
1092 extract(dc, *i, true);
1093 }
1094 }
1095}
1096
1097
1098void
1100 //@todo magic values
1101 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1102 NBEdge* edge = i->second;
1103 edge->guessOpposite();
1104 }
1105}
1106
1107
1108void
1110 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1111 NBEdge* opposite = getOppositeByID(i->first);
1112 if (opposite != nullptr) {
1113 i->second->setLaneSpreadFunction(LaneSpreadFunction::RIGHT);
1115 } else {
1116 i->second->setLaneSpreadFunction(LaneSpreadFunction::CENTER);
1117 }
1118 }
1119}
1120
1121
1122NBEdge*
1123NBEdgeCont::getOppositeByID(const std::string& edgeID) const {
1124 const std::string oppositeID = edgeID[0] == '-' ? edgeID.substr(1) : "-" + edgeID;
1125 EdgeCont::const_iterator it = myEdges.find(oppositeID);
1126 return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
1127}
1128
1129NBEdge*
1130NBEdgeCont::getByID(const std::string& edgeID) const {
1131 EdgeCont::const_iterator it = myEdges.find(edgeID);
1132 return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
1133}
1134
1135// ----- other
1136void
1137NBEdgeCont::addPostProcessConnection(const std::string& from, int fromLane, const std::string& to, int toLane, bool mayDefinitelyPass,
1138 KeepClear keepClear, double contPos, double visibility, double speed, double friction, double length,
1139 const PositionVector& customShape, bool uncontrolled, bool warnOnly,
1140 SVCPermissions permissions, bool indirectLeft, const std::string& edgeType, SVCPermissions changeLeft, SVCPermissions changeRight) {
1141 myConnections[from].push_back(PostProcessConnection(from, fromLane, to, toLane, mayDefinitelyPass, keepClear, contPos, visibility,
1142 speed, friction, length, customShape, uncontrolled, warnOnly, permissions, indirectLeft, edgeType, changeLeft, changeRight));
1143}
1144
1145bool
1146NBEdgeCont::hasPostProcessConnection(const std::string& from, const std::string& to) {
1147 if (myConnections.count(from) == 0) {
1148 return false;
1149 } else {
1150 if (to == "") {
1151 // wildcard
1152 return true;
1153 }
1154 for (const auto& ppc : myConnections[from]) {
1155 if (ppc.to == to) {
1156 return true;
1157 }
1158 }
1159 return false;
1160 }
1161}
1162
1163void
1165 const bool warnOnly = OptionsCont::getOptions().exists("ignore-errors.connections") && OptionsCont::getOptions().getBool("ignore-errors.connections");
1166 for (const auto& item : myConnections) {
1167 for (std::vector<PostProcessConnection>::const_iterator i = item.second.begin(); i != item.second.end(); ++i) {
1168 NBEdge* from = retrievePossiblySplit((*i).from, true);
1169 NBEdge* to = retrievePossiblySplit((*i).to, false);
1170 if (from == nullptr || to == nullptr ||
1171 !from->addLane2LaneConnection((*i).fromLane, to, (*i).toLane, NBEdge::Lane2LaneInfoType::USER, true, (*i).mayDefinitelyPass,
1172 (*i).keepClear, (*i).contPos, (*i).visibility, (*i).speed, (*i).friction, (*i).customLength, (*i).customShape,
1173 (*i).uncontrolled, (*i).permissions, (*i).indirectLeft, (*i).edgeType, (*i).changeLeft, (*i).changeRight,
1174 true)) {
1175 const std::string msg = "Could not insert connection between '" + (*i).from + "' and '" + (*i).to + "' after build.";
1176 if (warnOnly || (*i).warnOnly) {
1177 WRITE_WARNING(msg);
1178 } else {
1179 WRITE_ERROR(msg);
1180 }
1181 }
1182 }
1183 }
1184 // during loading we also kept some ambiguous connections in hope they might be valid after processing
1185 // we need to make sure that all invalid connections are removed now
1186 for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
1187 NBEdge* edge = it->second;
1188 NBNode* to = edge->getToNode();
1189 // make a copy because we may delete connections
1190 std::vector<NBEdge::Connection> connections = edge->getConnections();
1191 for (std::vector<NBEdge::Connection>::iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
1192 NBEdge::Connection& c = *it_con;
1193 if (c.toEdge != nullptr && c.toEdge->getFromNode() != to) {
1194 WRITE_WARNING("Found and removed invalid connection from edge '" + edge->getID() +
1195 "' to edge '" + c.toEdge->getID() + "' via junction '" + to->getID() + "'.");
1197 }
1198 }
1199 }
1200}
1201
1202
1204NBEdgeCont::getGeneratedFrom(const std::string& id) const {
1205 int len = (int)id.length();
1206 EdgeVector ret;
1207 for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1208 std::string curr = (*i).first;
1209 // the next check makes it possibly faster - we don not have
1210 // to compare the names
1211 if ((int)curr.length() <= len) {
1212 continue;
1213 }
1214 // the name must be the same as the given id but something
1215 // beginning with a '[' must be appended to it
1216 if (curr.substr(0, len) == id && curr[len] == '[') {
1217 ret.push_back((*i).second);
1218 continue;
1219 }
1220 // ok, maybe the edge is a compound made during joining of edges
1221 std::string::size_type pos = curr.find(id);
1222 // surely not
1223 if (pos == std::string::npos) {
1224 continue;
1225 }
1226 // check leading char
1227 if (pos > 0) {
1228 if (curr[pos - 1] != ']' && curr[pos - 1] != '+') {
1229 // actually, this is another id
1230 continue;
1231 }
1232 }
1233 if (pos + id.length() < curr.length()) {
1234 if (curr[pos + id.length()] != '[' && curr[pos + id.length()] != '+') {
1235 // actually, this is another id
1236 continue;
1237 }
1238 }
1239 ret.push_back((*i).second);
1240 }
1241 return ret;
1242}
1243
1244
1245int
1247 myGuessedRoundabouts.clear();
1248 std::set<NBEdge*> loadedRoundaboutEdges;
1249 for (std::set<EdgeSet>::const_iterator it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
1250 loadedRoundaboutEdges.insert(it->begin(), it->end());
1251 }
1252 // step 1: keep only those edges which have no turnarounds and which are not
1253 // part of a loaded roundabout
1254 std::set<NBEdge*> candidates;
1255 SVCPermissions valid = SVCAll & ~SVC_PEDESTRIAN;
1256 for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1257 NBEdge* e = (*i).second;
1258 NBNode* const to = e->getToNode();
1259 if (e->getTurnDestination() == nullptr
1260 && to->getConnectionTo(e->getFromNode()) == nullptr
1261 && (e->getPermissions() & valid) != 0) {
1262 candidates.insert(e);
1263 }
1264 }
1265
1266 // step 2:
1267 std::set<NBEdge*> visited;
1268 for (std::set<NBEdge*>::const_iterator i = candidates.begin(); i != candidates.end(); ++i) {
1269 EdgeVector loopEdges;
1270 // start with a random edge (this doesn't have to be a roundabout edge)
1271 // loop over connected edges (using always the leftmost one)
1272 // and keep the list in loopEdges
1273 // continue until we loop back onto a loopEdges and extract the loop
1274 NBEdge* e = (*i);
1275 if (visited.count(e) > 0) {
1276 // already seen
1277 continue;
1278 }
1279 loopEdges.push_back(e);
1280 bool doLoop = true;
1281#ifdef DEBUG_GUESS_ROUNDABOUT
1283#endif
1284 do {
1285#ifdef DEBUG_GUESS_ROUNDABOUT
1286 if (gDebugFlag1) {
1287 std::cout << " e=" << e->getID() << " loopEdges=" << toString(loopEdges) << "\n";
1288 gDebugFlag1 = true;
1289 }
1290#endif
1291 visited.insert(e);
1292 const EdgeVector& edges = e->getToNode()->getEdges();
1294 && !e->getToNode()->typeWasGuessed()) {
1295 doLoop = false;
1296#ifdef DEBUG_GUESS_ROUNDABOUT
1297 if (gDebugFlag1) {
1298 std::cout << " rbl\n";
1299 }
1300 gDebugFlag1 = false;
1301#endif
1302 break;
1303 }
1304 if (edges.size() < 2) {
1305 doLoop = false;
1306#ifdef DEBUG_GUESS_ROUNDABOUT
1307 if (gDebugFlag1) {
1308 std::cout << " deadend\n";
1309 }
1310 gDebugFlag1 = false;
1311#endif
1312 break;
1313 }
1314 if (e->getTurnDestination() != nullptr || e->getToNode()->getConnectionTo(e->getFromNode()) != nullptr) {
1315 // do not follow turn-arounds while in a (tentative) loop
1316 doLoop = false;
1317#ifdef DEBUG_GUESS_ROUNDABOUT
1318 if (gDebugFlag1) {
1319 std::cout << " invalid turnAround e=" << e->getID() << " dest=" << Named::getIDSecure(e->getTurnDestination()) << "\n";
1320 }
1321 gDebugFlag1 = false;
1322#endif
1323 break;
1324 }
1325 EdgeVector::const_iterator me = std::find(edges.begin(), edges.end(), e);
1326 NBContHelper::nextCW(edges, me);
1327 NBEdge* left = *me;
1328 while ((left->getPermissions() & valid) == 0 && left != e) {
1329 NBContHelper::nextCW(edges, me);
1330 left = *me;
1331 }
1332 if (left == e) {
1333 // no usable continuation edge found
1334 doLoop = false;
1335#ifdef DEBUG_GUESS_ROUNDABOUT
1336 if (gDebugFlag1) {
1337 std::cout << " noContinuation\n";
1338 }
1339 gDebugFlag1 = false;
1340#endif
1341 break;
1342 }
1343 NBContHelper::nextCW(edges, me);
1344 NBEdge* nextLeft = *me;
1345 double angle = fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), left->getAngleAtNode(e->getToNode())));
1346 double nextAngle = nextLeft == e ? 180 : fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), nextLeft->getAngleAtNode(e->getToNode())));
1347#ifdef DEBUG_GUESS_ROUNDABOUT
1348 if (gDebugFlag1) {
1349 std::cout << " e=" << e->getID() << " left=" << left->getID() << " nextLeft=" << nextLeft->getID() << " angle=" << angle << " nextAngle=" << nextAngle << " eLength=" << e->getLength() << " lLength=" << left->getLength() << " dist=" << e->getLaneShape(0).back().distanceTo2D(left->getLaneShape(0).front()) << "\n";
1350 }
1351#endif
1352 if (angle >= 120
1353 || (angle >= 90 &&
1354 // if the edges are long or the junction shape is small we should expect roundness (low angles)
1355 (MAX2(e->getLength(), left->getLength()) > 5
1356 || e->getLaneShape(0).back().distanceTo2D(left->getLaneShape(0).front()) < 10
1357 // there should be no straigher edge further left
1358 || (nextAngle < 45)
1359 ))) {
1360 // roundabouts do not have sharp turns (or they wouldn't be called 'round')
1361 // however, if the roundabout is very small then most of the roundness may be in the junction so the angle may be as high as 120
1362 doLoop = false;
1363#ifdef DEBUG_GUESS_ROUNDABOUT
1364 if (gDebugFlag1) {
1365 std::cout << " failed angle=" << angle << "\n";
1366 }
1367 gDebugFlag1 = false;
1368#endif
1369 break;
1370 }
1371 EdgeVector::const_iterator loopClosed = std::find(loopEdges.begin(), loopEdges.end(), left);
1372 const int loopSize = (int)(loopEdges.end() - loopClosed);
1373 if (loopSize > 0) {
1374 // loop found
1375 if (loopSize < 3) {
1376 doLoop = false; // need at least 3 edges for a roundabout
1377 } else if (loopSize < (int)loopEdges.size()) {
1378 // remove initial edges not belonging to the loop
1379 EdgeVector(loopEdges.begin() + (loopEdges.size() - loopSize), loopEdges.end()).swap(loopEdges);
1380 }
1381 // count attachments to the outside. need at least 3 or a roundabout doesn't make much sense
1382 int attachments = 0;
1383 for (EdgeVector::const_iterator j = loopEdges.begin(); j != loopEdges.end(); ++j) {
1384 if ((*j)->getToNode()->getEdges().size() > 2) {
1385 attachments++;
1386 }
1387 }
1388 if (attachments < 3) {
1389 doLoop = false;
1390#ifdef DEBUG_GUESS_ROUNDABOUT
1391 if (gDebugFlag1) {
1392 std::cout << " attachments=" << attachments << "\n";
1393 }
1394 gDebugFlag1 = false;
1395#endif
1396 }
1397 break;
1398 }
1399 if (visited.count(left) > 0) {
1400 doLoop = false;
1401 } else {
1402 // keep going
1403 loopEdges.push_back(left);
1404 e = left;
1405 }
1406 } while (doLoop);
1407 if (doLoop) {
1408 // check form factor to avoid elongated shapes (circle: 1, square: ~0.79)
1409#ifdef DEBUG_GUESS_ROUNDABOUT
1410 if (gDebugFlag1) {
1411 std::cout << " formFactor=" << formFactor(loopEdges) << "\n";
1412 }
1413#endif
1414 if (formFactor(loopEdges) > 0.6) {
1415 // collected edges are marked in markRoundabouts
1416 EdgeSet guessed(loopEdges.begin(), loopEdges.end());
1417 if (loadedRoundaboutEdges.count(loopEdges.front()) != 0) {
1418 if (find(myRoundabouts.begin(), myRoundabouts.end(), guessed) == myRoundabouts.end()) {
1419 for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); it++) {
1420 if ((*it).count(loopEdges.front()) != 0) {
1421 WRITE_WARNINGF(TL("Replacing loaded roundabout '%' with '%'."), toString(*it), toString(guessed));
1422 myRoundabouts.erase(it);
1423 break;
1424 }
1425 }
1426 myGuessedRoundabouts.insert(guessed);
1427 }
1428 } else {
1429 myGuessedRoundabouts.insert(guessed);
1430#ifdef DEBUG_GUESS_ROUNDABOUT
1431 if (gDebugFlag1) {
1432 std::cout << " foundRoundabout=" << toString(loopEdges) << "\n";
1433 }
1434#endif
1435 }
1436 }
1437 }
1438#ifdef DEBUG_GUESS_ROUNDABOUT
1439 gDebugFlag1 = false;
1440#endif
1441 }
1442 return (int)myGuessedRoundabouts.size();
1443}
1444
1445
1446double
1448 PositionVector points;
1449 for (EdgeVector::const_iterator it = loopEdges.begin(); it != loopEdges.end(); ++it) {
1450 points.append((*it)->getGeometry());
1451 }
1452 double circumference = points.length2D();
1453 return 4 * M_PI * points.area() / (circumference * circumference);
1454}
1455
1456
1457const std::set<EdgeSet>
1459 std::set<EdgeSet> result = myRoundabouts;
1460 result.insert(myGuessedRoundabouts.begin(), myGuessedRoundabouts.end());
1461 return result;
1462}
1463
1464
1465void
1467 if (roundabout.size() > 0) {
1468 if (find(myRoundabouts.begin(), myRoundabouts.end(), roundabout) != myRoundabouts.end()) {
1469 WRITE_WARNING("Ignoring duplicate roundabout: " + toString(roundabout));
1470 } else {
1471 myRoundabouts.insert(roundabout);
1472 }
1473 }
1474}
1475
1476void
1478 for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
1479 for (NBEdge* e : *it) {
1480 if (e->getToNode() == node) {
1481 myRoundabouts.erase(it);
1482 return;
1483 }
1484 }
1485 }
1486}
1487
1488void
1492}
1493
1494void
1495NBEdgeCont::removeRoundaboutEdges(const EdgeSet& toRemove, std::set<EdgeSet>& roundabouts) {
1496 // members of a set are constant so we have to do some tricks
1497 std::vector<EdgeSet> rList;
1498 for (const EdgeSet& r : roundabouts) {
1499 EdgeSet r2;
1500 std::set_difference(r.begin(), r.end(), toRemove.begin(), toRemove.end(), std::inserter(r2, r2.end()));
1501 rList.push_back(r2);
1502 }
1503 roundabouts.clear();
1504 roundabouts.insert(rList.begin(), rList.end());
1505}
1506
1507
1508void
1510 for (const EdgeSet& roundaboutSet : getRoundabouts()) {
1511 for (NBEdge* const edge : roundaboutSet) {
1512 // disable turnarounds on incoming edges
1513 NBNode* const node = edge->getToNode();
1514 for (NBEdge* const inEdge : node->getIncomingEdges()) {
1515 if (roundaboutSet.count(inEdge) > 0) {
1516 continue;
1517 }
1518 if (inEdge->getStep() >= NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
1519 continue;
1520 }
1521 if (inEdge->getTurnDestination() != nullptr) {
1522 inEdge->removeFromConnections(inEdge->getTurnDestination(), -1);
1523 } else {
1524 // also remove connections that are effecively a turnaround but
1525 // where not correctly detector due to geometrical quirks
1526 const std::vector<NBEdge::Connection> cons = inEdge->getConnections();
1527 for (const NBEdge::Connection& con : cons) {
1528 if (con.toEdge && roundaboutSet.count(con.toEdge) == 0) {
1529 const double angle = fabs(NBHelpers::normRelAngle(inEdge->getAngleAtNode(node), con.toEdge->getAngleAtNode(node)));
1530 if (angle > 160) {
1531 inEdge->removeFromConnections(con.toEdge, -1);
1532 }
1533 }
1534 }
1535 }
1536
1537 }
1538 // let the connections to succeeding roundabout edge have a higher priority
1539 edge->setJunctionPriority(node, NBEdge::JunctionPriority::ROUNDABOUT);
1540 edge->setJunctionPriority(edge->getFromNode(), NBEdge::JunctionPriority::ROUNDABOUT);
1541 node->setRoundabout();
1542 }
1543 }
1544}
1545
1546
1547void
1549 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1550 NBEdge* e = i->second;
1551 const double offset = MAX2(0., e->getLength() - 3);
1552 if (e->getToNode()->isSimpleContinuation(false)) {
1553 // not a "real" junction?
1554 continue;
1555 }
1556 const SumoXMLNodeType nodeType = e->getToNode()->getType();
1557 switch (nodeType) {
1559 // yield or major?
1560 if (e->getJunctionPriority(e->getToNode()) > 0) {
1562 } else {
1564 }
1565 break;
1567 // yield or major?
1568 if (e->getJunctionPriority(e->getToNode()) > 0) {
1570 } else {
1572 }
1573 break;
1576 break;
1579 break;
1582 break;
1583 default:
1584 break;
1585 }
1586 }
1587}
1588
1589
1590int
1591NBEdgeCont::guessSpecialLanes(SUMOVehicleClass svc, double width, double minSpeed, double maxSpeed, bool fromPermissions, const std::string& excludeOpt,
1593 int lanesCreated = 0;
1594 std::vector<std::string> edges;
1595 if (excludeOpt != "") {
1596 edges = OptionsCont::getOptions().getStringVector(excludeOpt);
1597 }
1598 std::set<std::string> exclude(edges.begin(), edges.end());
1599 for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1600 NBEdge* edge = it->second;
1601 if (// not excluded
1602 exclude.count(edge->getID()) == 0
1603 // does not yet have a sidewalk
1604 && !edge->hasRestrictedLane(svc)
1605 && (
1606 // guess.from-permissions
1607 (fromPermissions && (edge->getPermissions() & svc) != 0)
1608 // guess from speed
1609 || (!fromPermissions && edge->getSpeed() > minSpeed && edge->getSpeed() <= maxSpeed)
1610 )) {
1611 edge->addRestrictedLane(width, svc);
1612 lanesCreated += 1;
1613 if (svc != SVC_PEDESTRIAN) {
1614 edge->invalidateConnections(true);
1616 edge->getFromNode()->invalidateTLS(tlc, true, true);
1617 edge->getToNode()->invalidateTLS(tlc, true, true);
1618 }
1619 }
1620 }
1621 return lanesCreated;
1622}
1623
1624
1625void
1627 for (auto item : myEdges) {
1628 item.second->updateChangeRestrictions(ignoring);
1629 }
1630}
1631
1632
1633void
1634NBEdgeCont::addPrefix(const std::string& prefix) {
1635 // make a copy of node containers
1636 const auto nodeContainerCopy = myEdges;
1637 myEdges.clear();
1638 for (const auto& node : nodeContainerCopy) {
1639 node.second->setID(prefix + node.second->getID());
1640 myEdges[node.second->getID()] = node.second;
1641 }
1642}
1643
1644
1645int
1646NBEdgeCont::remapIDs(bool numericaIDs, bool reservedIDs, const std::string& prefix, NBPTStopCont& sc) {
1647 bool startGiven = !OptionsCont::getOptions().isDefault("numerical-ids.edge-start");
1648 if (!numericaIDs && !reservedIDs && prefix == "" && !startGiven) {
1649 return 0;
1650 }
1651 std::vector<std::string> avoid;
1652 if (startGiven) {
1653 avoid.push_back(toString(OptionsCont::getOptions().getInt("numerical-ids.edge-start") - 1));
1654 } else {
1655 avoid = getAllNames();
1656 }
1657 std::set<std::string> reserve;
1658 if (reservedIDs) {
1659 NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("reserved-ids"), "edge:", reserve);
1660 avoid.insert(avoid.end(), reserve.begin(), reserve.end());
1661 }
1662 IDSupplier idSupplier("", avoid);
1663 std::set<NBEdge*, ComparatorIdLess> toChange;
1664 for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1665 if (startGiven) {
1666 toChange.insert(it->second);
1667 continue;
1668 }
1669 if (numericaIDs) {
1670 try {
1671 StringUtils::toLong(it->first);
1672 } catch (NumberFormatException&) {
1673 toChange.insert(it->second);
1674 }
1675 }
1676 if (reservedIDs && reserve.count(it->first) > 0) {
1677 toChange.insert(it->second);
1678 }
1679 }
1680
1681 std::map<std::string, std::vector<std::shared_ptr<NBPTStop> > > stopsOnEdge;
1682 for (const auto& item : sc.getStops()) {
1683 stopsOnEdge[item.second->getEdgeId()].push_back(item.second);
1684 }
1685
1686 const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
1687 for (NBEdge* edge : toChange) {
1688 myEdges.erase(edge->getID());
1689 }
1690 for (NBEdge* edge : toChange) {
1691 const std::string origID = edge->getID();
1692 if (origNames) {
1693 edge->setOrigID(origID, false);
1694 }
1695 edge->setID(idSupplier.getNext());
1696 myEdges[edge->getID()] = edge;
1697 for (std::shared_ptr<NBPTStop> stop : stopsOnEdge[origID]) {
1698 stop->setEdgeId(prefix + edge->getID(), *this);
1699 }
1700 }
1701 if (prefix.empty()) {
1702 return (int)toChange.size();
1703 } else {
1704 int renamed = 0;
1705 // make a copy because we will modify the map
1706 auto oldEdges = myEdges;
1707 for (auto item : oldEdges) {
1708 if (!StringUtils::startsWith(item.first, prefix)) {
1709 rename(item.second, prefix + item.first);
1710 renamed++;
1711 }
1712 }
1713 return renamed;
1714 }
1715}
1716
1717
1718void
1719NBEdgeCont::checkOverlap(double threshold, double zThreshold) const {
1720 for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1721 const NBEdge* e1 = it->second;
1722 Boundary b1 = e1->getGeometry().getBoxBoundary();
1723 b1.grow(e1->getTotalWidth());
1724 PositionVector outline1 = e1->getCCWBoundaryLine(*e1->getFromNode());
1725 outline1.append(e1->getCCWBoundaryLine(*e1->getToNode()));
1726 // check is symmetric. only check once per pair
1727 for (EdgeCont::const_iterator it2 = it; it2 != myEdges.end(); it2++) {
1728 const NBEdge* e2 = it2->second;
1729 if (e1 == e2) {
1730 continue;
1731 }
1732 Boundary b2 = e2->getGeometry().getBoxBoundary();
1733 b2.grow(e2->getTotalWidth());
1734 if (b1.overlapsWith(b2)) {
1735 PositionVector outline2 = e2->getCCWBoundaryLine(*e2->getFromNode());
1736 outline2.append(e2->getCCWBoundaryLine(*e2->getToNode()));
1737 const double overlap = outline1.getOverlapWith(outline2, zThreshold);
1738 if (overlap > threshold) {
1739 WRITE_WARNINGF(TL("Edge '%' overlaps with edge '%' by %."), e1->getID(), e2->getID(), overlap);
1740 }
1741 }
1742 }
1743 }
1744}
1745
1746
1747void
1748NBEdgeCont::checkGrade(double threshold) const {
1749 for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1750 const NBEdge* edge = it->second;
1751 for (int i = 0; i < (int)edge->getNumLanes(); i++) {
1752 double maxJump = 0;
1753 const double grade = edge->getLaneShape(i).getMaxGrade(maxJump);
1754 if (maxJump > 0.01) {
1755 WRITE_WARNINGF(TL("Edge '%' has a vertical jump of %m."), edge->getID(), maxJump);
1756 } else if (grade > threshold) {
1757 WRITE_WARNINGF(TL("Edge '%' has a grade of %%."), edge->getID(), grade * 100, "%");
1758 break;
1759 }
1760 }
1761 const std::vector<NBEdge::Connection>& connections = edge->getConnections();
1762 for (std::vector<NBEdge::Connection>::const_iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
1763 const NBEdge::Connection& c = *it_con;
1764 double maxJump = 0;
1765 const double grade = MAX2(c.shape.getMaxGrade(maxJump), c.viaShape.getMaxGrade(maxJump));
1766 if (maxJump > 0.01) {
1767 WRITE_WARNINGF(TL("Connection '%' has a vertical jump of %m."), c.getDescription(edge), maxJump);
1768 } else if (grade > threshold) {
1769 WRITE_WARNINGF(TL("Connection '%' has a grade of %%."), c.getDescription(edge), grade * 100, "%");
1770 break;
1771 }
1772 }
1773 }
1774}
1775
1776
1777int
1779 int affectedEdges = 0;
1780 for (auto item : myEdges) {
1781 if (item.second->joinLanes(perms)) {
1782 affectedEdges++;
1783 }
1784 }
1785 return affectedEdges;
1786}
1787
1788
1789bool
1790NBEdgeCont::MinLaneComparatorIdLess::operator()(const std::pair<NBEdge*, int>& a, const std::pair<NBEdge*, int>& b) const {
1791 if (a.first->getID() == b.first->getID()) {
1792 return a.second < b.second;
1793 }
1794 return a.first->getID() < b.first->getID();
1795}
1796
1797int
1799 // this is different from joinSimilarEdges because there don't need to be
1800 // shared nodes and tram edges may be split
1801 std::vector<NBEdge*> tramEdges;
1802 std::vector<NBEdge*> targetEdges;
1803 for (auto item : myEdges) {
1804 SVCPermissions permissions = item.second->getPermissions();
1805 if (isTram(permissions)) {
1806 if (item.second->getNumLanes() == 1) {
1807 tramEdges.push_back(item.second);
1808 } else {
1809 WRITE_WARNINGF(TL("Not joining tram edge '%' with % lanes."), item.second->getID(), item.second->getNumLanes());
1810 }
1811 } else if ((permissions & (SVC_PASSENGER | SVC_BUS)) != 0) {
1812 targetEdges.push_back(item.second);
1813 }
1814 }
1815 if (tramEdges.empty() || targetEdges.empty()) {
1816 return 0;
1817 }
1818 int numJoined = 0;
1819 NamedRTree tramTree;
1820 for (NBEdge* const edge : tramEdges) {
1821 const Boundary& bound = edge->getGeometry().getBoxBoundary();
1822 float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1823 float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1824 tramTree.Insert(min, max, edge);
1825 }
1826 // {targetEdge, laneIndex : tramEdge}
1827 std::map<std::pair<NBEdge*, int>, NBEdge*, MinLaneComparatorIdLess> matches;
1828
1829 for (NBEdge* edge : targetEdges) {
1830 Boundary bound = edge->getGeometry().getBoxBoundary();
1831 bound.grow(maxDist + edge->getTotalWidth());
1832 float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1833 float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1834 std::set<const Named*> nearby;
1835 Named::StoringVisitor visitor(nearby);
1836 tramTree.Search(min, max, visitor);
1837 for (const Named* namedEdge : nearby) {
1838 // find a continous stretch of tramEdge that runs along one of the
1839 // lanes of the road edge
1840 NBEdge* tramEdge = const_cast<NBEdge*>(dynamic_cast<const NBEdge*>(namedEdge));
1841 const PositionVector& tramShape = tramEdge->getGeometry();
1842 double minEdgeDist = maxDist + 1;
1843 int minLane = -1;
1844 // find the lane where the maximum distance from the tram geometry
1845 // is minimal and within maxDist
1846 for (int i = 0; i < edge->getNumLanes(); i++) {
1847 double maxLaneDist = -1;
1848 if ((edge->getPermissions(i) & (SVC_PASSENGER | SVC_BUS)) != 0) {
1849 const PositionVector& laneShape = edge->getLaneShape(i);
1850 for (Position pos : laneShape) {
1851 const double dist = tramShape.distance2D(pos, false);
1852#ifdef DEBUG_JOIN_TRAM
1853 //if (edge->getID() == "106838214#1") {
1854 // std::cout << " edge=" << edge->getID() << " tramEdge=" << tramEdge->getID() << " lane=" << i << " pos=" << pos << " dist=" << dist << "\n";
1855 //}
1856#endif
1857 if (dist == GeomHelper::INVALID_OFFSET || dist > maxDist) {
1858 maxLaneDist = -1;
1859 break;
1860 }
1861 maxLaneDist = MAX2(maxLaneDist, dist);
1862 }
1863 if (maxLaneDist >= 0 && maxLaneDist < minEdgeDist) {
1864 minEdgeDist = maxLaneDist;
1865 minLane = i;
1866 }
1867 }
1868 }
1869 if (minLane >= 0) {
1870 // edge could run in the wrong direction and still fit the threshold we check the angle as well
1871 const PositionVector& laneShape = edge->getLaneShape(minLane);
1872 const double offset1 = tramShape.nearest_offset_to_point2D(laneShape.front(), false);
1873 const double offset2 = tramShape.nearest_offset_to_point2D(laneShape.back(), false);
1874 Position p1 = tramShape.positionAtOffset2D(offset1);
1875 Position p2 = tramShape.positionAtOffset2D(offset2);
1876 double tramAngle = GeomHelper::legacyDegree(p1.angleTo2D(p2), true);
1877 bool angleOK = GeomHelper::getMinAngleDiff(tramAngle, edge->getTotalAngle()) < JOIN_TRAM_MAX_ANGLE;
1878 if (angleOK && offset2 > offset1) {
1879 std::pair<NBEdge*, int> key = std::make_pair(edge, minLane);
1880 if (matches.count(key) == 0) {
1881 matches[key] = tramEdge;
1882 } else {
1883 WRITE_WARNINGF(TL("Ambiguous tram edges '%' and '%' for lane '%'."), matches[key]->getID(), tramEdge->getID(), edge->getLaneID(minLane));
1884 }
1885#ifdef DEBUG_JOIN_TRAM
1886 std::cout << edge->getLaneID(minLane) << " is close to tramEdge " << tramEdge->getID() << " maxLaneDist=" << minEdgeDist << " tramLength=" << tramEdge->getLength() << " edgeLength=" << edge->getLength() << " tramAngle=" << tramAngle << " edgeAngle=" << edge->getTotalAngle() << "\n";
1887#endif
1888 }
1889 }
1890 }
1891 }
1892 if (matches.size() == 0) {
1893 return 0;
1894 }
1895 const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
1896 // find continous runs of matched edges for each tramEdge
1897 for (NBEdge* tramEdge : tramEdges) {
1898 std::vector<std::pair<double, std::pair<NBEdge*, int> > > roads;
1899 for (auto item : matches) {
1900 if (item.second == tramEdge) {
1901 NBEdge* road = item.first.first;
1902 int laneIndex = item.first.second;
1903 const PositionVector& laneShape = road->getLaneShape(laneIndex);
1904 double tramPos = tramEdge->getGeometry().nearest_offset_to_point2D(laneShape.front(), false);
1905 roads.push_back(std::make_pair(tramPos, item.first));
1906 }
1907 }
1908 if (roads.size() != 0) {
1909
1910 sort(roads.begin(), roads.end());
1911#ifdef DEBUG_JOIN_TRAM
1912 std::cout << " tramEdge=" << tramEdge->getID() << " roads=";
1913 for (auto item : roads) {
1914 std::cout << item.second.first->getLaneID(item.second.second) << ",";
1915 }
1916 std::cout << " offsets=";
1917 for (auto item : roads) {
1918 std::cout << item.first << ",";
1919 }
1920 std::cout << "\n";
1921#endif
1922 // merge tramEdge into road lanes
1923 EdgeVector replacement;
1924 double pos = 0;
1925 int tramPart = 0;
1926 std::string tramEdgeID = tramEdge->getID();
1927 NBNode* tramFrom = tramEdge->getFromNode();
1928 PositionVector tramShape = tramEdge->getGeometry();
1929 const double tramLength = tramShape.length();
1930 EdgeVector incoming = tramFrom->getIncomingEdges();
1931 bool erasedLast = false;
1932 for (const auto& item : roads) {
1933 const double gap = item.first - pos;
1934 NBEdge* road = item.second.first;
1935 int laneIndex = item.second.second;
1936 if (gap >= JOIN_TRAM_MIN_LENGTH) {
1937#ifdef DEBUG_JOIN_TRAM
1938 std::cout << " splitting tramEdge=" << tramEdge->getID() << " at " << item.first << " (gap=" << gap << ")\n";
1939#endif
1940 const std::string firstPartID = tramEdgeID + "#" + toString(tramPart++);
1941 splitAt(dc, tramEdge, gap, road->getFromNode(), firstPartID, tramEdgeID, 1, 1);
1942 tramEdge = retrieve(tramEdgeID); // second part;
1943 NBEdge* firstPart = retrieve(firstPartID);
1944 firstPart->invalidateConnections(true);
1945 incoming.clear();
1946 incoming.push_back(firstPart);
1947 replacement.push_back(firstPart);
1948 }
1949 pos = item.first + road->getGeometry().length();
1950 numJoined++;
1951 replacement.push_back(road);
1952 // merge section of tramEdge into road lane
1953 if (road->getToNode() != tramEdge->getToNode() && (tramLength - pos) >= JOIN_TRAM_MIN_LENGTH) {
1954 tramEdge->reinitNodes(road->getToNode(), tramEdge->getToNode());
1955 tramEdge->setGeometry(tramShape.getSubpart(pos, tramShape.length()));
1956 erasedLast = false;
1957#ifdef DEBUG_JOIN_TRAM
1958 std::cout << " shorted tramEdge=" << tramEdge->getID() << " (joined with roadEdge=" << road->getID() << "\n";
1959#endif
1960 } else {
1961#ifdef DEBUG_JOIN_TRAM
1962 std::cout << " erased tramEdge=" << tramEdge->getID() << "\n";
1963#endif
1964 extract(dc, tramEdge, true);
1965 erasedLast = true;
1966 }
1967 road->setPermissions(road->getPermissions(laneIndex) | SVC_TRAM, laneIndex);
1968 if (origNames) {
1969 road->setOrigID(tramEdgeID, true, laneIndex);
1970 }
1971 for (NBEdge* in : incoming) {
1972 if (isTram(in->getPermissions()) && !in->isConnectedTo(road)) {
1973 if (in->getFromNode() != road->getFromNode()) {
1974 in->reinitNodes(in->getFromNode(), road->getFromNode());
1975 } else {
1976 extract(dc, in, true);
1977#ifdef DEBUG_JOIN_TRAM
1978 std::cout << " erased incoming tramEdge=" << in->getID() << "\n";
1979#endif
1980 }
1981 }
1982 }
1983 incoming.clear();
1984 }
1985 NBEdge* lastRoad = roads.back().second.first;
1986 if (erasedLast) {
1987 // copy to avoid concurrent modification
1988 auto outEdges = tramEdge->getToNode()->getOutgoingEdges();
1989 for (NBEdge* out : outEdges) {
1990 if (isTram(out->getPermissions()) && !lastRoad->isConnectedTo(out)) {
1991 if (lastRoad->getToNode() != out->getToNode()) {
1992 out->reinitNodes(lastRoad->getToNode(), out->getToNode());
1993 } else {
1994 extract(dc, out, true);
1995#ifdef DEBUG_JOIN_TRAM
1996 std::cout << " erased outgoing tramEdge=" << out->getID() << "\n";
1997#endif
1998
1999 }
2000 }
2001 }
2002 } else {
2003 replacement.push_back(tramEdge);
2004 }
2005 // update ptstops and ptlines
2006 sc.replaceEdge(tramEdgeID, replacement);
2007 lc.replaceEdge(tramEdgeID, replacement);
2008 }
2009 }
2010
2011 return numJoined;
2012}
2013
2014
2017 EdgeVector result;
2018 for (auto item : myEdges) {
2019 item.second->setNumericalID((int)result.size());
2020 result.push_back(item.second);
2021 }
2022 return result;
2023}
2024
2027 EdgeVector all = getAllEdges();
2028 return RouterEdgeVector(all.begin(), all.end());
2029}
2030
2031bool
2033 bool ok = true;
2034 for (const auto& item : myEdges) {
2035 NBEdge* e = item.second;
2036 if (nc.retrieve(e->getFromNode()->getID()) == nullptr) {
2037 WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), e->getID(), e->getFromNode()->getID());
2038 ok = false;
2039 }
2040 if (nc.retrieve(e->getToNode()->getID()) == nullptr) {
2041 WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), e->getID(), e->getToNode()->getID());
2042 ok = false;
2043 }
2044
2045 }
2046 return ok;
2047}
2048
2049
2050void
2052 for (auto item : myEdges) {
2053 NBEdge* e = item.second;
2054 if (e->hasLoadedLength() && myWasSplit.count(e) != 0) {
2055 // subtract half the length of the longest incoming / outgoing connection
2056 double maxLengthOut = 0;
2057 for (const NBEdge::Connection& c : e->getConnections()) {
2058 maxLengthOut = MAX2(maxLengthOut, c.length + c.viaLength);
2059 }
2060 double maxLengthIn = 0;
2061 for (const NBEdge* in : e->getIncomingEdges()) {
2062 for (const NBEdge::Connection& c : in->getConnectionsFromLane(-1, e, -1)) {
2063 maxLengthIn = MAX2(maxLengthIn, c.length + c.viaLength);
2064 }
2065 }
2066 e->setLoadedLength(MAX2(POSITION_EPS, e->getLoadedLength() - (maxLengthIn + maxLengthOut) / 2));
2067 }
2068 }
2069}
2070
2071void
2073 for (auto item : myEdges) {
2074 item.second->computeAngle();
2075 }
2076}
2077
2078
2079std::set<std::string>
2081 std::set<std::string> result;
2082 for (auto item : myEdges) {
2083 if (item.second->getTypeID() != "") {
2084 result.insert(item.second->getTypeID());
2085 }
2086 }
2087 return result;
2088}
2089
2090/****************************************************************************/
std::vector< std::string > & split(const std::string &s, char delim, std::vector< std::string > &elems)
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:271
#define WRITE_ERRORF(...)
Definition: MsgHandler.h:280
#define WRITE_ERROR(msg)
Definition: MsgHandler.h:279
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:270
#define TL(string)
Definition: MsgHandler.h:287
#define TLF(string,...)
Definition: MsgHandler.h:288
std::set< NBEdge * > EdgeSet
container for unique edges
Definition: NBCont.h:50
std::vector< NBEdge * > EdgeVector
container for (sorted) edges
Definition: NBCont.h:42
KeepClear
keepClear status of connections
Definition: NBCont.h:58
@ KEEPCLEAR_UNSPECIFIED
Definition: NBCont.h:61
std::vector< NBRouterEdge * > RouterEdgeVector
Definition: NBCont.h:43
#define JOIN_TRAM_MIN_LENGTH
Definition: NBEdgeCont.cpp:55
#define JOIN_TRAM_MAX_ANGLE
Definition: NBEdgeCont.cpp:54
#define DEBUG_EDGE_ID
Definition: NBEdgeCont.cpp:59
const SVCPermissions SVCAll
all VClasses are allowed
bool isRailway(SVCPermissions permissions)
Returns whether an edge with the given permission is a railway edge.
bool isTram(SVCPermissions permissions)
Returns whether an edge with the given permission is a tram edge.
bool isForbidden(SVCPermissions permissions)
Returns whether an edge with the given permission is a forbidden edge.
SVCPermissions parseVehicleClasses(const std::string &allowedS)
Parses the given definition of allowed vehicle classes into the given containers Deprecated classes g...
bool isSidewalk(SVCPermissions permissions)
Returns whether an edge with the given permission is a sidewalk.
SUMOVehicleClass
Definition of vehicle classes to differ between different lane usage and authority types.
@ SVC_PASSENGER
vehicle is a passenger car (a "normal" car)
@ SVC_TRAM
vehicle is a light rail
@ SVC_BUS
vehicle is a bus
@ SVC_PEDESTRIAN
pedestrian
int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
const std::string SUMO_PARAM_ORIGID
SumoXMLNodeType
Numbers representing special SUMO-XML-attribute values for representing node- (junction-) types used ...
bool gDebugFlag1
global utility flags for debugging
Definition: StdDefs.cpp:35
const double SUMO_const_laneWidth
Definition: StdDefs.h:48
T MIN2(T a, T b)
Definition: StdDefs.h:76
const double SUMO_const_haltingSpeed
the speed threshold at which vehicles are considered as halting
Definition: StdDefs.h:58
T MAX2(T a, T b)
Definition: StdDefs.h:82
const double SUMO_const_halfLaneWidth
Definition: StdDefs.h:49
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
A class that stores a 2D geometrical boundary.
Definition: Boundary.h:39
double ymin() const
Returns minimum y-coordinate.
Definition: Boundary.cpp:130
double xmin() const
Returns minimum x-coordinate.
Definition: Boundary.cpp:118
Boundary & grow(double by)
extends the boundary by the given amount
Definition: Boundary.cpp:300
bool overlapsWith(const AbstractPoly &poly, double offset=0) const
Returns whether the boundary overlaps with the given polygon.
Definition: Boundary.cpp:181
double ymax() const
Returns maximum y-coordinate.
Definition: Boundary.cpp:136
double xmax() const
Returns maximum x-coordinate.
Definition: Boundary.cpp:124
static GeoConvHelper & getProcessing()
the coordinate transformation to use for input conversion and processing
Definition: GeoConvHelper.h:84
bool x2cartesian_const(Position &from) const
Converts the given coordinate into a cartesian using the previous initialisation.
static GeoConvHelper & getLoaded()
the coordinate transformation that was loaded fron an input file
Definition: GeoConvHelper.h:89
static PositionVector parseShapeReporting(const std::string &shpdef, const std::string &objecttype, const char *objectid, bool &ok, bool allowEmpty, bool report=true)
Builds a PositionVector from a string representation, reporting occurred errors.
static const double INVALID_OFFSET
a value to signify offsets outside the range of [0, Line.length()]
Definition: GeomHelper.h:50
static double nearest_offset_on_line_to_point2D(const Position &lineStart, const Position &lineEnd, const Position &p, bool perpendicular=true)
Definition: GeomHelper.cpp:88
static double legacyDegree(const double angle, const bool positive=false)
Definition: GeomHelper.cpp:215
static double getMinAngleDiff(double angle1, double angle2)
Returns the minimum distance (clockwise/counter-clockwise) between both angles.
Definition: GeomHelper.cpp:173
std::string getNext()
Returns the next id.
Definition: IDSupplier.cpp:51
static void nextCW(const EdgeVector &edges, EdgeVector::const_iterator &from)
A container for districts.
void removeFromSinksAndSources(NBEdge *const e)
Removes the given edge from the lists of sources and sinks in all stored districts.
Sorts splits by their position (increasing)
Definition: NBEdgeCont.h:784
void patchRoundabouts(NBEdge *orig, NBEdge *part1, NBEdge *part2, std::set< EdgeSet > &roundabouts)
fix roundabout information after splitting an edge
Definition: NBEdgeCont.cpp:738
void computeEdgeShapes(double smoothElevationThreshold=-1)
Computes the shapes of all edges stored in the container.
Definition: NBEdgeCont.cpp:969
void removeUnwishedEdges(NBDistrictCont &dc)
Removes unwished edges (not in keep-edges)
Definition: NBEdgeCont.cpp:767
NBEdge * getByID(const std::string &edgeID) const
Returns the edge with id if it exists.
const std::set< EdgeSet > getRoundabouts() const
Returns the determined roundabouts.
void computeEdge2Edges(bool noLeftMovers)
Computes for each edge the approached edges.
Definition: NBEdgeCont.cpp:849
int guessRoundabouts()
Determines which edges belong to roundabouts and increases their priority.
bool myNeedGeoTransformedPruningBoundary
whether a geo transform has been applied to the pruning boundary
Definition: NBEdgeCont.h:772
~NBEdgeCont()
Destructor.
Definition: NBEdgeCont.cpp:72
void sortOutgoingLanesConnections()
Sorts all lanes of all edges within the container by their direction.
Definition: NBEdgeCont.cpp:841
void addRoundabout(const EdgeSet &roundabout)
add user specified roundabout
std::set< EdgeSet > myRoundabouts
Edges marked as belonging to a roundabout by the user (each EdgeVector is a roundabout)
Definition: NBEdgeCont.h:776
void appendRailwayTurnarounds(const NBPTStopCont &sc)
Appends turnarounds to all bidiRail edges with stops.
Definition: NBEdgeCont.cpp:947
std::set< std::string > myEdges2Remove
Set of ids of edges which shall explicitly be removed.
Definition: NBEdgeCont.h:754
std::set< std::string > myIgnoredEdges
The ids of ignored edges.
Definition: NBEdgeCont.h:734
void updateAllChangeRestrictions(SVCPermissions ignoring)
modify all restrictions on lane changing for edges and connections
double myEdgesMinSpeed
The minimum speed an edge may have in order to be kept (default: -1)
Definition: NBEdgeCont.h:745
void recheckPostProcessConnections()
Try to set any stored connections.
void checkGeometries(const double maxAngle, const double minRadius, bool fix, bool fixRailways, bool silent=false)
Definition: NBEdgeCont.cpp:819
void extract(NBDistrictCont &dc, NBEdge *edge, bool remember=false)
Removes the given edge from the container like erase but does not delete it.
Definition: NBEdgeCont.cpp:416
void processSplits(NBEdge *e, std::vector< Split > splits, NBNodeCont &nc, NBDistrictCont &dc, NBTrafficLightLogicCont &tlc)
process splits
Definition: NBEdgeCont.cpp:456
EdgeVector getAllEdges() const
return all edges
void erase(NBDistrictCont &dc, NBEdge *edge)
Removes the given edge from the container (deleting it)
Definition: NBEdgeCont.cpp:409
NBEdge * retrieve(const std::string &id, bool retrieveExtracted=false) const
Returns the edge that has the given id.
Definition: NBEdgeCont.cpp:279
std::set< std::string > myTypes2Keep
Set of edges types which shall be kept.
Definition: NBEdgeCont.h:763
void recheckLanes()
Rechecks whether all lanes have a successor for each of the stored edges.
Definition: NBEdgeCont.cpp:865
NBEdge * getOppositeByID(const std::string &edgeID) const
Returns the edge with negated id if it exists.
EdgeCont myExtractedEdges
The extracted edges which are kept for reference.
Definition: NBEdgeCont.h:728
void reduceGeometries(const double minDist)
Definition: NBEdgeCont.cpp:811
void recheckLaneSpread()
Rechecks whether the lane spread is proper.
bool ignoreFilterMatch(NBEdge *edge)
Returns true if this edge matches one of the removal criteria.
Definition: NBEdgeCont.cpp:203
void removeRoundabout(const NBNode *node)
remove roundabout that contains the given node
void splitGeometry(NBDistrictCont &dc, NBNodeCont &nc)
Splits edges into multiple if they have a complex geometry.
Definition: NBEdgeCont.cpp:784
void addPostProcessConnection(const std::string &from, int fromLane, const std::string &to, int toLane, bool mayDefinitelyPass, KeepClear keepClear, double contPos, double visibility, double speed, double friction, double length, const PositionVector &customShape, bool uncontrolled, bool warnOnly, SVCPermissions permissions=SVC_UNSPECIFIED, bool indirectLeft=false, const std::string &edgeType="", SVCPermissions changeLeft=SVC_UNSPECIFIED, SVCPermissions changeRight=SVC_UNSPECIFIED)
Adds a connection which could not be set during loading.
std::set< std::string > getUsedTypes() const
return all edge types in used
void computeLanes2Edges()
Computes for each edge which lanes approach the next edges.
Definition: NBEdgeCont.cpp:857
NBEdge * retrievePossiblySplit(const std::string &id, bool downstream) const
Tries to retrieve an edge, even if it is splitted.
Definition: NBEdgeCont.cpp:315
RouterEdgeVector getAllRouterEdges() const
return all router edges
std::set< const NBEdge * > myWasSplit
the edges that were created as result of splitting
Definition: NBEdgeCont.h:739
void rename(NBEdge *edge, const std::string &newID)
Renames the edge. Throws exception if newID already exists.
Definition: NBEdgeCont.cpp:436
int joinTramEdges(NBDistrictCont &dc, NBPTStopCont &sc, NBPTLineCont &lc, double maxDist)
join tram edges into adjacent lanes
bool hasPostProcessConnection(const std::string &from, const std::string &to="")
add post process connections
EdgeCont myEdges
The instance of the dictionary (id->edge)
Definition: NBEdgeCont.h:725
std::set< std::string > myEdges2Keep
Set of ids of edges which shall explicitly be kept.
Definition: NBEdgeCont.h:751
NBTypeCont & myTypeCont
The network builder; used to obtain type information.
Definition: NBEdgeCont.h:615
void generateStreetSigns()
assigns street signs to edges based on toNode types
void clearControllingTLInformation() const
Clears information about controlling traffic lights for all connenections of all edges.
Definition: NBEdgeCont.cpp:833
std::set< EdgeSet > myGuessedRoundabouts
Edges marked as belonging to a roundabout after guessing.
Definition: NBEdgeCont.h:779
void computeAngles()
compute all edge angles
void clear()
Deletes all edges.
Definition: NBEdgeCont.cpp:163
void guessOpposites()
Sets opposite lane information for geometrically close edges.
void markRoundabouts()
mark edge priorities and prohibit turn-arounds for all roundabout edges
std::set< std::string > myTypes2Remove
Set of edges types which shall be removed.
Definition: NBEdgeCont.h:766
void applyOptions(OptionsCont &oc)
Initialises the storage by applying given options.
Definition: NBEdgeCont.cpp:78
void removeRoundaboutEdges(const EdgeSet &toRemove)
remove edges from all stored roundabouts
PositionVector myPruningBoundary
Boundary within which an edge must be located in order to be kept.
Definition: NBEdgeCont.h:769
int joinLanes(SVCPermissions perms)
join adjacent lanes with the given permissions
void checkOverlap(double threshold, double zThreshold) const
check whether edges overlap
SVCPermissions myVehicleClasses2Remove
Set of vehicle types which need not be supported (edges which allow ONLY these are removed)
Definition: NBEdgeCont.h:760
int guessSpecialLanes(SUMOVehicleClass svc, double width, double minSpeed, double maxSpeed, bool fromPermissions, const std::string &excludeOpt, NBTrafficLightLogicCont &tlc)
add sidwalks to edges within the given limits or permissions and return the number of edges affected
EdgeVector getGeneratedFrom(const std::string &id) const
Returns the edges which have been built by splitting the edge of the given id.
void appendTurnarounds(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike)
Appends turnarounds to all edges stored in the container.
Definition: NBEdgeCont.cpp:931
SVCPermissions myVehicleClasses2Keep
Set of vehicle types which must be allowed on edges in order to keep them.
Definition: NBEdgeCont.h:757
void computeLaneShapes()
Computes the shapes of all lanes of all edges stored in the container.
Definition: NBEdgeCont.cpp:993
void joinSameNodeConnectingEdges(NBDistrictCont &dc, NBTrafficLightLogicCont &tlc, EdgeVector edges)
Joins the given edges because they connect the same nodes.
bool myRemoveEdgesAfterJoining
Whether edges shall be joined first, then removed.
Definition: NBEdgeCont.h:748
std::map< std::string, NBEdge * > EdgeCont
The type of the dictionary where an edge may be found by its id.
Definition: NBEdgeCont.h:722
void addPrefix(const std::string &prefix)
add prefix to all edges
void fixSplitCustomLength()
adapt custom lengths of split edges to account for intersection size
std::map< const NBEdge *, std::pair< NBEdge *, NBEdge * > > myEdgesSplit
the number of splits of edges during the building
Definition: NBEdgeCont.h:737
std::map< std::string, std::vector< PostProcessConnection > > myConnections
The list of connections to recheck.
Definition: NBEdgeCont.h:719
bool insert(NBEdge *edge, bool ignorePrunning=false)
Adds an edge to the dictionary.
Definition: NBEdgeCont.cpp:182
NBEdgeCont(NBTypeCont &tc)
Constructor.
Definition: NBEdgeCont.cpp:64
std::set< NBEdge * > myEdgeCemetery
The edges which got extracted twice but may still be referenced somewhere TODO smart_ptr?
Definition: NBEdgeCont.h:731
int remapIDs(bool numericaIDs, bool reservedIDs, const std::string &prefix, NBPTStopCont &sc)
remap node IDs accoring to options –numerical-ids and –reserved-ids
bool checkConsistency(const NBNodeCont &nc)
ensure that all edges have valid nodes
static double formFactor(const EdgeVector &loopEdges)
compute the form factor for a loop of edges
bool splitAt(NBDistrictCont &dc, NBEdge *edge, NBNode *node)
Splits the edge at the position nearest to the given node.
Definition: NBEdgeCont.cpp:602
std::vector< std::string > getAllNames() const
Returns all ids of known edges.
Definition: NBEdgeCont.cpp:756
void checkGrade(double threshold) const
check whether edges are to steep
The representation of a single edge during network building.
Definition: NBEdge.h:92
NBEdge * guessOpposite(bool reguess=false)
set oppositeID and return opposite edge if found
Definition: NBEdge.cpp:4738
double getLength() const
Returns the computed length of the edge.
Definition: NBEdge.h:583
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition: NBEdge.cpp:4232
const std::vector< Connection > & getConnections() const
Returns the connections.
Definition: NBEdge.h:1027
void setPermissions(SVCPermissions permissions, int lane=-1)
set allowed/disallowed classes for the given lane or for all lanes if -1 is given
Definition: NBEdge.cpp:4195
double getLoadedLength() const
Returns the length was set explicitly or the computed length if it wasn't set.
Definition: NBEdge.h:592
void setSpeed(int lane, double speed)
set lane specific speed (negative lane implies set for all lanes)
Definition: NBEdge.cpp:4147
NBNode * getToNode() const
Returns the destination node of the edge.
Definition: NBEdge.h:536
PositionVector getCCWBoundaryLine(const NBNode &n) const
get the outer boundary of this edge when going counter-clock-wise around the given node
Definition: NBEdge.cpp:3667
static const double UNSPECIFIED_FRICTION
unspecified lane friction
Definition: NBEdge.h:350
void incLaneNo(int by)
increment lane
Definition: NBEdge.cpp:3928
Lane & getLaneStruct(int lane)
Definition: NBEdge.h:1415
void setAverageLengthWithOpposite(double val)
patch average lane length in regard to the opposite edge
Definition: NBEdge.cpp:4252
bool isBidi()
return whether this edge should be a bidi edge
Definition: NBEdge.h:1410
const PositionVector & getGeometry() const
Returns the geometry of the edge.
Definition: NBEdge.h:771
LaneSpreadFunction getLaneSpreadFunction() const
Returns how this edge's lanes' lateral offset is computed.
Definition: NBEdge.cpp:948
bool isBidiRail(bool ignoreSpread=false) const
whether this edge is part of a bidirectional railway
Definition: NBEdge.cpp:729
void dismissVehicleClassInformation()
dimiss vehicle class information
Definition: NBEdge.cpp:4258
LaneSpreadFunction myLaneSpreadFunction
The information about how to spread the lanes.
Definition: NBEdge.h:1773
bool hasLoadedLength() const
Returns whether a length was set explicitly.
Definition: NBEdge.h:602
bool addEdge2EdgeConnection(NBEdge *dest, bool overrideRemoval=false, SVCPermissions permission=SVC_UNSPECIFIED)
Adds a connection to another edge.
Definition: NBEdge.cpp:1032
bool addLane2LaneConnection(int fromLane, NBEdge *dest, int toLane, Lane2LaneInfoType type, bool mayUseSameDestination=false, bool mayDefinitelyPass=false, KeepClear keepClear=KEEPCLEAR_UNSPECIFIED, double contPos=UNSPECIFIED_CONTPOS, double visibility=UNSPECIFIED_VISIBILITY_DISTANCE, double speed=UNSPECIFIED_SPEED, double friction=UNSPECIFIED_FRICTION, double length=myDefaultConnectionLength, const PositionVector &customShape=PositionVector::EMPTY, const bool uncontrolled=UNSPECIFIED_CONNECTION_UNCONTROLLED, SVCPermissions permissions=SVC_UNSPECIFIED, const bool indirectLeft=false, const std::string &edgeType="", SVCPermissions changeLeft=SVC_UNSPECIFIED, SVCPermissions changeRight=SVC_UNSPECIFIED, bool postProcess=false)
Adds a connection between the specified this edge's lane and an approached one.
Definition: NBEdge.cpp:1067
void setDistance(double distance)
set kilometrage at start of edge (negative value implies couting down along the edge)
Definition: NBEdge.h:1400
const std::vector< NBEdge::Lane > & getLanes() const
Returns the lane definitions.
Definition: NBEdge.h:720
@ LANES2LANES_USER
Lanes to lanes - relationships are loaded; no recheck is necessary/wished.
double getSpeed() const
Returns the speed allowed on this edge.
Definition: NBEdge.h:609
NBNode * myTo
Definition: NBEdge.h:1722
const std::string & getID() const
Definition: NBEdge.h:1515
double getDistance() const
get distance
Definition: NBEdge.h:669
void setLaneWidth(int lane, double width)
set lane specific width (negative lane implies set for all lanes)
Definition: NBEdge.cpp:4018
void setLaneSpreadFunction(LaneSpreadFunction spread)
(Re)sets how the lanes lateral offset shall be computed
Definition: NBEdge.cpp:942
std::vector< Lane > myLanes
Lane information.
Definition: NBEdge.h:1790
int getNumLanes() const
Returns the number of lanes.
Definition: NBEdge.h:510
void setFriction(int lane, double friction)
set lane specific friction (negative lane implies set for all lanes)
Definition: NBEdge.cpp:4163
static const double UNSPECIFIED_CONTPOS
unspecified internal junction position
Definition: NBEdge.h:353
void addRestrictedLane(double width, SUMOVehicleClass vclass)
add a lane of the given width, restricted to the given class and shift existing connections
Definition: NBEdge.cpp:4459
void removeFromConnections(NBEdge *toEdge, int fromLane=-1, int toLane=-1, bool tryLater=false, const bool adaptToLaneRemoval=false, const bool keepPossibleTurns=false)
Removes the specified connection(s)
Definition: NBEdge.cpp:1382
void invalidateConnections(bool reallowSetting=false)
invalidate current connections of edge
Definition: NBEdge.cpp:1463
double getTotalWidth() const
Returns the combined width of all lanes of this edge.
Definition: NBEdge.cpp:4070
static const double UNSPECIFIED_VISIBILITY_DISTANCE
unspecified foe visibility for connections
Definition: NBEdge.h:356
bool isConnectedTo(const NBEdge *e, const bool ignoreTurnaround=false) const
Returns the information whethe a connection to the given edge has been added (or computed)
Definition: NBEdge.cpp:1265
void addSign(NBSign sign)
add Sign
Definition: NBEdge.h:1447
void moveOutgoingConnectionsFrom(NBEdge *e, int laneOff)
move outgoing connection
Definition: NBEdge.cpp:3543
std::string getLaneID(int lane) const
get lane ID
Definition: NBEdge.cpp:3870
@ USER
The connection was given by the user.
@ VALIDATED
The connection was computed and validated.
@ COMPUTED
The connection was computed.
bool setConnection(int lane, NBEdge *destEdge, int destLane, Lane2LaneInfoType type, bool mayUseSameDestination=false, bool mayDefinitelyPass=false, KeepClear keepClear=KEEPCLEAR_UNSPECIFIED, double contPos=UNSPECIFIED_CONTPOS, double visibility=UNSPECIFIED_VISIBILITY_DISTANCE, double speed=UNSPECIFIED_SPEED, double friction=UNSPECIFIED_FRICTION, double length=myDefaultConnectionLength, const PositionVector &customShape=PositionVector::EMPTY, const bool uncontrolled=UNSPECIFIED_CONNECTION_UNCONTROLLED, SVCPermissions permissions=SVC_UNSPECIFIED, bool indirectLeft=false, const std::string &edgeType="", SVCPermissions changeLeft=SVC_UNSPECIFIED, SVCPermissions changeRight=SVC_UNSPECIFIED, bool postProcess=false)
Adds a connection to a certain lane of a certain edge.
Definition: NBEdge.cpp:1120
int getJunctionPriority(const NBNode *const node) const
Returns the junction priority (normalised for the node currently build)
Definition: NBEdge.cpp:2051
const std::string & getTypeID() const
get ID of type
Definition: NBEdge.h:1167
const std::string & getStreetName() const
Returns the street name of this edge.
Definition: NBEdge.h:659
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition: NBEdge.h:529
NBEdge * getTurnDestination(bool possibleDestination=false) const
Definition: NBEdge.cpp:3861
double getAngleAtNode(const NBNode *const node) const
Returns the angle of the edge's geometry at the given node.
Definition: NBEdge.cpp:2077
static const double UNSPECIFIED_WIDTH
unspecified lane width
Definition: NBEdge.h:341
bool hasRestrictedLane(SUMOVehicleClass vclass) const
returns whether any lane already allows the given vclass exclusively
Definition: NBEdge.cpp:4448
void copyConnectionsFrom(NBEdge *src)
copy connections from antoher edge
Definition: NBEdge.cpp:1578
void setEndOffset(int lane, double offset)
set lane specific end-offset (negative lane implies set for all lanes)
Definition: NBEdge.cpp:4101
static const double UNSPECIFIED_OFFSET
unspecified lane offset
Definition: NBEdge.h:344
bool recheckLanes()
recheck whether all lanes within the edge are all right and optimises the connections once again
Definition: NBEdge.cpp:2790
void setOrigID(const std::string origID, const bool append, const int laneIdx=-1)
set origID for all lanes or for a specific lane
Definition: NBEdge.cpp:4586
const PositionVector & getLaneShape(int i) const
Returns the shape of the nth lane.
Definition: NBEdge.cpp:936
void setLoadedLength(double val)
set loaded length
Definition: NBEdge.cpp:4247
void decLaneNo(int by)
decrement lane
Definition: NBEdge.cpp:3959
NBNode * myFrom
The source and the destination node.
Definition: NBEdge.h:1722
double getFinalLength() const
get length that will be assigned to the lanes in the final network
Definition: NBEdge.cpp:4564
void setGeometry(const PositionVector &g, bool inner=false)
(Re)sets the edge's geometry
Definition: NBEdge.cpp:617
EdgeVector getIncomingEdges() const
Returns the list of incoming edges unsorted.
Definition: NBEdge.cpp:1327
static void loadPrefixedIDsFomFile(const std::string &file, const std::string prefix, std::set< std::string > &into)
Add prefixed ids defined in file.
Definition: NBHelpers.cpp:104
static double relAngle(double angle1, double angle2)
computes the relative angle between the two angles
Definition: NBHelpers.cpp:45
static double normRelAngle(double angle1, double angle2)
ensure that reverse relAngles (>=179.999) always count as turnarounds (-180)
Definition: NBHelpers.cpp:58
static void loadEdgesFromFile(const std::string &file, std::set< std::string > &into)
Add edge ids defined in file (either ID or edge:ID per line) into the given set.
Definition: NBHelpers.cpp:86
static bool transformCoordinates(PositionVector &from, bool includeInBoundary=true, GeoConvHelper *from_srs=nullptr)
Container for nodes during the netbuilding process.
Definition: NBNodeCont.h:57
bool insert(const std::string &id, const Position &position, NBDistrict *district=0)
Inserts a node into the map.
Definition: NBNodeCont.cpp:87
NBNode * retrieve(const std::string &id) const
Returns the node with the given name.
Definition: NBNodeCont.cpp:116
void markAsSplit(const NBNode *node)
mark a node as being created form a split
Definition: NBNodeCont.h:354
Represents a single node (junction) during network building.
Definition: NBNode.h:66
void invalidateOutgoingConnections(bool reallowSetting=false)
invalidate outgoing connections
Definition: NBNode.cpp:1955
void removeEdge(NBEdge *edge, bool removeFromConnections=true)
Removes edge from this node and optionally removes connections as well.
Definition: NBNode.cpp:1886
const std::set< NBTrafficLightDefinition * > & getControllingTLS() const
Returns the traffic lights that were assigned to this node (The set of tls that control this node)
Definition: NBNode.h:334
bool isSimpleContinuation(bool checkLaneNumbers=true, bool checkWidth=false) const
check if node is a simple continuation
Definition: NBNode.cpp:504
SumoXMLNodeType getType() const
Returns the type of this node.
Definition: NBNode.h:283
const EdgeVector & getIncomingEdges() const
Returns this node's incoming edges (The edges which yield in this node)
Definition: NBNode.h:266
const EdgeVector & getOutgoingEdges() const
Returns this node's outgoing edges (The edges which start at this node)
Definition: NBNode.h:271
void replaceOutgoing(NBEdge *which, NBEdge *by, int laneOff)
Replaces occurrences of the first edge within the list of outgoing by the second Connections are rema...
Definition: NBNode.cpp:1671
void setRoundabout()
update the type of this node as a roundabout
Definition: NBNode.cpp:3643
void invalidateTLS(NBTrafficLightLogicCont &tlCont, bool removedConnections, bool addedConnections)
causes the traffic light to be computed anew
Definition: NBNode.cpp:420
void replaceIncoming(NBEdge *which, NBEdge *by, int laneOff)
Replaces occurrences of the first edge within the list of incoming by the second Connections are rema...
Definition: NBNode.cpp:1707
const Position & getPosition() const
Definition: NBNode.h:258
const EdgeVector & getEdges() const
Returns all edges which participate in this node (Edges that start or end at this node)
Definition: NBNode.h:276
bool typeWasGuessed() const
return whether a priority road turns at this node
Definition: NBNode.h:831
void removeDoubleEdges()
remove duble edges
Definition: NBNode.cpp:1775
NBEdge * getConnectionTo(NBNode *n) const
get connection to certain node
Definition: NBNode.cpp:2589
void replaceEdge(const std::string &edgeID, const EdgeVector &replacement)
replace the edge with the given edge list in all lines
Container for public transport stops during the net building process.
Definition: NBPTStopCont.h:44
void replaceEdge(const std::string &edgeID, const std::vector< NBEdge * > &replacement)
replace the edge with the closes edge on the given edge list in all stops
const std::map< std::string, std::shared_ptr< NBPTStop > > & getStops() const
Returns an unmodifiable reference to the stored pt stops.
Definition: NBPTStopCont.h:62
A class representing a single street sign.
Definition: NBSign.h:41
@ SIGN_TYPE_ALLWAY_STOP
Definition: NBSign.h:48
@ SIGN_TYPE_YIELD
Definition: NBSign.h:46
@ SIGN_TYPE_STOP
Definition: NBSign.h:47
@ SIGN_TYPE_PRIORITY
Definition: NBSign.h:50
@ SIGN_TYPE_RIGHT_BEFORE_LEFT
Definition: NBSign.h:51
@ SIGN_TYPE_LEFT_BEFORE_RIGHT
Definition: NBSign.h:52
The base class for traffic light logic definitions.
A container for traffic light definitions and built programs.
void replaceRemoved(NBEdge *removed, int removedLane, NBEdge *by, int byLane, bool incoming)
Replaces occurrences of the removed edge/lane in all definitions by the given edge.
A storage for available edgeTypes of edges.
Definition: NBTypeCont.h:52
bool getEdgeTypeShallBeDiscarded(const std::string &edgeType) const
Returns the information whether edges of this edgeType shall be discarded.
Definition: NBTypeCont.cpp:526
bool knows(const std::string &edgeType) const
Returns whether the named edgeType is in the container.
Definition: NBTypeCont.cpp:303
Allows to store the object; used as context while traveling the rtree in TraCI.
Definition: Named.h:90
Base class for objects which have an id.
Definition: Named.h:54
virtual void setID(const std::string &newID)
resets the id
Definition: Named.h:82
static std::string getIDSecure(const T *obj, const std::string &fallBack="NULL")
get an identifier for Named-like object which may be Null
Definition: Named.h:67
const std::string & getID() const
Returns the id.
Definition: Named.h:74
A RT-tree for efficient storing of SUMO's Named objects.
Definition: NamedRTree.h:61
void Insert(const float a_min[2], const float a_max[2], Named *const &a_data)
Insert entry.
Definition: NamedRTree.h:79
int Search(const float a_min[2], const float a_max[2], const Named::StoringVisitor &c) const
Find all within search rectangle.
Definition: NamedRTree.h:112
A storage for options typed value containers)
Definition: OptionsCont.h:89
bool isSet(const std::string &name, bool failOnNonExistant=true) const
Returns the information whether the named option is set.
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
bool isDefault(const std::string &name) const
Returns the information whether the named option has still the default value.
bool exists(const std::string &name) const
Returns the information whether the named option is known.
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
const StringVector & getStringVector(const std::string &name) const
Returns the list of string-value of the named option (only for Option_StringVector)
std::string getValueString(const std::string &name) const
Returns the string-value of the named option (all options)
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:60
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const
Returns the value for a given key.
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
double angleTo2D(const Position &other) const
returns the angle in the plane of the vector pointing from here to the other position
Definition: Position.h:264
A list of positions.
double length2D() const
Returns the length.
void append(const PositionVector &v, double sameThreshold=2.0)
double length() const
Returns the length.
double distance2D(const Position &p, bool perpendicular=false) const
closest 2D-distance to point p (or -1 if perpendicular is true and the point is beyond this vector)
double nearest_offset_to_point2D(const Position &p, bool perpendicular=true) const
return the nearest offest to point 2D
std::pair< PositionVector, PositionVector > splitAt(double where, bool use2D=false) const
Returns the two lists made when this list vector is splitted at the given point.
void move2side(double amount, double maxExtension=100)
move position vector to side using certain ammount
Boundary getBoxBoundary() const
Returns a boundary enclosing this list of lines.
double getOverlapWith(const PositionVector &poly, double zThreshold) const
Returns the maximum overlaps between this and the given polygon (when not separated by at least zThre...
bool partialWithin(const AbstractPoly &poly, double offset=0) const
Returns the information whether this polygon lies partially within the given polygon.
double getMaxGrade(double &maxJump) const
double area() const
Returns the area (0 for non-closed)
bool intersects(const Position &p1, const Position &p2) const
Returns the information whether this list of points interesects the given line.
Position positionAtOffset2D(double pos, double lateralOffset=0) const
Returns the position at the given length.
PositionVector getSubpart(double beginOffset, double endOffset) const
get subpart of a position vector
static std::string getEdgeIDFromLane(const std::string laneID)
return edge id when given the lane ID
static long long int toLong(const std::string &sData)
converts a string into the long value described by it by calling the char-type converter,...
static double toDouble(const std::string &sData)
converts a string into the double value described by it by calling the char-type converter
static bool startsWith(const std::string &str, const std::string prefix)
Checks whether a given string starts with the prefix.
#define M_PI
Definition: odrSpiral.cpp:45
A structure which describes a connection between edges or lanes.
Definition: NBEdge.h:201
NBEdge * toEdge
The edge the connections yields in.
Definition: NBEdge.h:213
PositionVector viaShape
shape of via
Definition: NBEdge.h:282
std::string getDescription(const NBEdge *parent) const
get string describing this connection
Definition: NBEdge.cpp:96
PositionVector shape
shape of Connection
Definition: NBEdge.h:270
An (internal) definition of a single lane of an edge.
Definition: NBEdge.h:143
std::string oppositeID
An opposite lane ID, if given.
Definition: NBEdge.h:179
bool operator()(const std::pair< NBEdge *, int > &a, const std::pair< NBEdge *, int > &b) const
A structure representing a connection between two lanes.
Definition: NBEdgeCont.h:620
A structure which describes changes of lane number or speed along the road.
Definition: NBEdgeCont.h:189
int offsetFactor
direction in which to apply the offset (used by netgenerate for lefthand networks)
Definition: NBEdgeCont.h:209
double offset
lateral offset to edge geometry
Definition: NBEdgeCont.h:207
double pos
The position of this change.
Definition: NBEdgeCont.h:193
std::vector< int > lanes
The lanes after this change.
Definition: NBEdgeCont.h:191