Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
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-2024 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
75
76
77void
79 // set edges dismiss/accept options
80 myEdgesMinSpeed = oc.getFloat("keep-edges.min-speed");
81 myRemoveEdgesAfterLoading = 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
205 // check whether the edge is a named edge to keep
206 if (myEdges2Keep.size() != 0) {
207 if (myEdges2Keep.count(edge->getID()) == 0) {
208 // explicit whitelisting may be combined additively with other filters
210 && myTypes2Keep.size() == 0 && myTypes2Remove.size() == 0
211 && myPruningBoundary.size() == 0) {
212 return true;
213 }
214 } else {
215 // explicit whitelisting overrides other filters
216 return false;
217 }
218 }
219 // remove edges which allow a speed below a set one (set using "keep-edges.min-speed")
220 if (edge->getSpeed() < myEdgesMinSpeed) {
221 return true;
222 }
223 // check whether the edge shall be removed because it does not allow any of the wished classes
224 if (myVehicleClasses2Keep != 0 && (myVehicleClasses2Keep & edge->getPermissions()) == 0) {
225 return true;
226 }
227 // check whether the edge shall be removed due to allowing unwished classes only
229 return true;
230 }
231 }
232 // check whether the edge is a named edge to remove
233 if (myEdges2Remove.size() != 0) {
234 if (myEdges2Remove.count(edge->getID()) != 0) {
235 return true;
236 }
237 }
238 // check whether the edge shall be removed because it does not have one of the requested types
239 if (myTypes2Keep.size() != 0) {
240 if (myTypes2Keep.count(edge->getTypeID()) == 0) {
241 return true;
242 }
243 }
244 // check whether the edge shall be removed because it has one of the forbidden types
245 if (myTypes2Remove.size() != 0) {
246 if (myTypes2Remove.count(edge->getTypeID()) > 0) {
247 return true;
248 }
249 }
250 // check whether the edge is within the pruning boundary
251 if (myPruningBoundary.size() != 0) {
253 if (GeoConvHelper::getProcessing().usingGeoProjection()) {
255 } else if (GeoConvHelper::getLoaded().usingGeoProjection()) {
256 // XXX what if input file with different projections are loaded?
257 for (int i = 0; i < (int) myPruningBoundary.size(); i++) {
259 }
260 } else {
261 WRITE_ERROR(TL("Cannot prune edges using a geo-boundary because no projection has been loaded"));
262 }
264 }
265 if (!(edge->getGeometry().getBoxBoundary().grow(POSITION_EPS).overlapsWith(myPruningBoundary))) {
266 return true;
267 } else if (!(edge->getGeometry().partialWithin(myPruningBoundary, 2 * POSITION_EPS) || edge->getGeometry().intersects(myPruningBoundary))) {
268 // a more detailed check is necessary because the bounding box may be much bigger than the edge
269 // @note: overlapsWith implicitly closes the edge shape but this is not wanted here
270 return true;
271 }
272 }
274 return true;
275 }
276 return false;
277}
278
279
280NBEdge*
281NBEdgeCont::retrieve(const std::string& id, bool retrieveExtracted) const {
282 EdgeCont::const_iterator i = myEdges.find(id);
283 if (i == myEdges.end()) {
284 if (retrieveExtracted) {
285 i = myExtractedEdges.find(id);
286 if (i == myExtractedEdges.end()) {
287 return nullptr;
288 }
289 } else {
290 return nullptr;
291 }
292 }
293 return (*i).second;
294}
295
296// FIXME: This can't work
297/*
298NBEdge*
299NBEdgeCont::retrievePossiblySplit(const std::string& id, bool downstream) const {
300 NBEdge* edge = retrieve(id);
301 if (edge == 0) {
302 return 0;
303 }
304 const EdgeVector* candidates = downstream ? &edge->getToNode()->getOutgoingEdges() : &edge->getFromNode()->getIncomingEdges();
305 while (candidates->size() == 1) {
306 const std::string& nextID = candidates->front()->getID();
307 if (nextID.find(id) != 0 || nextID.size() <= id.size() + 1 || (nextID[id.size()] != '.' && nextID[id.size()] != '-')) {
308 break;
309 }
310 edge = candidates->front();
311 candidates = downstream ? &edge->getToNode()->getOutgoingEdges() : &edge->getFromNode()->getIncomingEdges();
312 }
313 return edge;
314}*/
315
316NBEdge*
317NBEdgeCont::retrievePossiblySplit(const std::string& id, bool downstream) const {
318 NBEdge* edge = retrieve(id);
319 if (edge != nullptr) {
320 return edge;
321 }
322 // NOTE: (TODO) for multiply split edges (e.g. 15[0][0]) one could try recursion
323 if ((retrieve(id + "[0]") != nullptr) && (retrieve(id + "[1]") != nullptr)) {
324 // Edge was split during the netbuilding process
325 if (downstream) {
326 return retrieve(id + "[1]");
327 } else {
328 return retrieve(id + "[0]");
329 }
330 }
331 return edge;
332}
333
334
335NBEdge*
336NBEdgeCont::retrievePossiblySplit(const std::string& id, const std::string& hint, bool incoming) const {
337 // try to retrieve using the given name (iterative)
338 NBEdge* edge = retrieve(id);
339 if (edge != nullptr) {
340 return edge;
341 }
342 // now, we did not find it; we have to look over all possibilities
343 EdgeVector hints;
344 // check whether at least the hint was not splitted
345 NBEdge* hintedge = retrieve(hint);
346 if (hintedge == nullptr) {
347 hints = getGeneratedFrom(hint);
348 } else {
349 hints.push_back(hintedge);
350 }
351 EdgeVector candidates = getGeneratedFrom(id);
352 for (const NBEdge* const currHint : hints) {
353 for (NBEdge* const poss_searched : candidates) {
354 const NBNode* const node = incoming ? poss_searched->myTo : poss_searched->myFrom;
355 const EdgeVector& cont = incoming ? node->getOutgoingEdges() : node->getIncomingEdges();
356 if (find(cont.begin(), cont.end(), currHint) != cont.end()) {
357 return poss_searched;
358 }
359 }
360 }
361 return nullptr;
362}
363
364
365NBEdge*
366NBEdgeCont::retrievePossiblySplit(const std::string& id, double pos) const {
367 // check whether the edge was not split, yet
368 NBEdge* edge = retrieve(id);
369 if (edge != nullptr) {
370 return edge;
371 }
372 int maxLength = 0;
373 std::string tid = id + "[";
374 for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
375 if ((*i).first.find(tid) == 0) {
376 maxLength = MAX2(maxLength, (int)(*i).first.length());
377 }
378 }
379 // find the part of the edge which matches the position
380 double seen = 0;
381 std::vector<std::string> names;
382 names.push_back(id + "[1]");
383 names.push_back(id + "[0]");
384 while (names.size() > 0) {
385 // retrieve the first subelement (to follow)
386 std::string cid = names.back();
387 names.pop_back();
388 edge = retrieve(cid);
389 // The edge was splitted; check its subparts within the
390 // next step
391 if (edge == nullptr) {
392 if ((int)cid.length() + 3 < maxLength) {
393 names.push_back(cid + "[1]");
394 names.push_back(cid + "[0]");
395 }
396 }
397 // an edge with the name was found,
398 // check whether the position lies within it
399 else {
400 seen += edge->getLength();
401 if (seen >= pos) {
402 return edge;
403 }
404 }
405 }
406 return nullptr;
407}
408
409
410void
412 extract(dc, edge);
413 delete edge;
414}
415
416
417void
418NBEdgeCont::extract(NBDistrictCont& dc, NBEdge* edge, bool remember) {
419 if (remember) {
420 const auto& prevExtracted = myExtractedEdges.find(edge->getID());
421 if (prevExtracted != myExtractedEdges.end()) {
422 if (edge != prevExtracted->second) {
423 myEdgeCemetery.insert(prevExtracted->second);
424 prevExtracted->second = edge;
425 }
426 } else {
427 myExtractedEdges[edge->getID()] = edge;
428 }
429 }
430 myEdges.erase(edge->getID());
431 edge->myFrom->removeEdge(edge);
432 edge->myTo->removeEdge(edge);
434}
435
436
437void
438NBEdgeCont::rename(NBEdge* edge, const std::string& newID) {
439 if (myEdges.count(newID) != 0) {
440 throw ProcessError(TLF("Attempt to rename edge using existing id '%'", newID));
441 }
442 myEdges.erase(edge->getID());
443 edge->setID(newID);
444 myEdges[newID] = edge;
445 // update oppositeID
446 if (edge->getLanes().back().oppositeID != "") {
447 NBEdge* oppo = retrieve(SUMOXMLDefinitions::getEdgeIDFromLane(edge->getLanes().back().oppositeID));
448 if (oppo != nullptr) {
449 oppo->getLaneStruct(oppo->getNumLanes() - 1).oppositeID = edge->getLaneID(edge->getNumLanes() - 1);
450 }
451 }
452}
453
454
455// ----- explicit edge manipulation methods
456
457void
458NBEdgeCont::processSplits(NBEdge* e, std::vector<Split> splits,
460 if (splits.empty()) {
461 return;
462 }
463 const std::string origID = e->getID();
464 sort(splits.begin(), splits.end(), split_sorter());
465 int maxNumLanes = e->getNumLanes();
466 // compute the node positions and sort the lanes
467 for (Split& split : splits) {
468 sort(split.lanes.begin(), split.lanes.end());
469 maxNumLanes = MAX2(maxNumLanes, (int)split.lanes.size());
470 }
471 // split the edge
472 std::vector<int> currLanes;
473 for (int l = 0; l < e->getNumLanes(); ++l) {
474 currLanes.push_back(l);
475 }
476 if (e->getNumLanes() != (int)splits.back().lanes.size()) {
477 // invalidate traffic light definitions loaded from a SUMO network
478 e->getToNode()->invalidateTLS(tlc, true, true);
479 // if the number of lanes changes the connections should be
480 // recomputed
481 e->invalidateConnections(true);
482 }
483
484 std::string firstID = "";
485 double seen = 0;
486 for (const Split& exp : splits) {
487 assert(exp.lanes.size() != 0);
488 if (exp.pos > 0 && e->getLoadedLength() + seen > exp.pos && exp.pos > seen) {
489 nc.insert(exp.node);
490 nc.markAsSplit(exp.node);
491 // split the edge
492 const std::string idBefore = exp.idBefore == "" ? e->getID() : exp.idBefore;
493 const std::string idAfter = exp.idAfter == "" ? exp.nameID : exp.idAfter;
494 if (firstID == "") {
495 firstID = idBefore;
496 }
497 const bool ok = splitAt(dc, e, exp.pos - seen, exp.node,
498 idBefore, idAfter, e->getNumLanes(), (int) exp.lanes.size(), exp.speed);
499 if (!ok) {
500 WRITE_WARNINGF(TL("Error on parsing a split (edge '%')."), origID);
501 return;
502 }
503 seen = exp.pos;
504 std::vector<int> newLanes = exp.lanes;
505 NBEdge* pe = retrieve(idBefore);
506 NBEdge* ne = retrieve(idAfter);
507 // reconnect lanes
508 pe->invalidateConnections(true);
509 // new on right
510 int rightMostP = currLanes[0];
511 int rightMostN = newLanes[0];
512 for (int l = 0; l < (int) rightMostP - (int) rightMostN; ++l) {
514 }
515 // new on left
516 int leftMostP = currLanes.back();
517 int leftMostN = newLanes.back();
518 for (int l = 0; l < (int) leftMostN - (int) leftMostP; ++l) {
519 pe->addLane2LaneConnection(pe->getNumLanes() - 1, ne, leftMostN - l - rightMostN, NBEdge::Lane2LaneInfoType::VALIDATED, true);
520 }
521 // all other connected
522 for (int l = 0; l < maxNumLanes; ++l) {
523 if (find(currLanes.begin(), currLanes.end(), l) == currLanes.end()) {
524 continue;
525 }
526 if (find(newLanes.begin(), newLanes.end(), l) == newLanes.end()) {
527 continue;
528 }
529 pe->addLane2LaneConnection(l - rightMostP, ne, l - rightMostN, NBEdge::Lane2LaneInfoType::VALIDATED, true);
530 }
531 // if there are edges at this node which are not connected
532 // we can assume that this split was attached to an
533 // existing node. Reset all connections to let the default
534 // algorithm recompute them
535 if (exp.node->getIncomingEdges().size() > 1 || exp.node->getOutgoingEdges().size() > 1) {
536 for (NBEdge* in : exp.node->getIncomingEdges()) {
537 in->invalidateConnections(true);
538 }
539 }
540 // move to next
541 e = ne;
542 currLanes = newLanes;
543 } else if (exp.pos == 0) {
544 const int laneCountDiff = e->getNumLanes() - (int)exp.lanes.size();
545 if (laneCountDiff < 0) {
546 e->incLaneNo(-laneCountDiff);
547 } else {
548 e->decLaneNo(laneCountDiff);
549 }
550 currLanes = exp.lanes;
551 // invalidate traffic light definition loaded from a SUMO network
552 // XXX it would be preferable to reconstruct the phase definitions heuristically
553 e->getFromNode()->invalidateTLS(tlc, true, true);
554 if (exp.speed != -1.) {
555 e->setSpeed(-1, exp.speed);
556 }
557 } else {
558 WRITE_WARNINGF(TL("Split at '%' lies beyond the edge's length (edge '%')."), toString(exp.pos), origID);
559 }
560 }
561 // patch lane offsets
562 e = retrieve(firstID);
563 if (e != nullptr) {
564 if (splits.front().pos != 0) {
565 // add a dummy split at the beginning to ensure correct offset
566 Split start;
567 start.pos = 0;
568 for (int lane = 0; lane < (int)e->getNumLanes(); ++lane) {
569 start.lanes.push_back(lane);
570 }
571 start.offset = splits.front().offset;
572 start.offsetFactor = splits.front().offsetFactor;
573 splits.insert(splits.begin(), start);
574 }
575 for (const Split& split : splits) {
576 int maxLeft = split.lanes.back();
577 double offset = split.offset;
578 if (maxLeft < maxNumLanes) {
580 offset += split.offsetFactor * SUMO_const_laneWidth * (maxNumLanes - 1 - maxLeft);
581 } else {
582 offset += split.offsetFactor * SUMO_const_halfLaneWidth * (maxNumLanes - 1 - maxLeft);
583 }
584 }
585 int maxRight = split.lanes.front();
586 if (maxRight > 0 && e->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
587 offset -= split.offsetFactor * SUMO_const_halfLaneWidth * maxRight;
588 }
589 //std::cout << " processSplits " << origID << " splitOffset=" << (*i).offset << " offset=" << offset << "\n";
590 if (offset != 0) {
592 g.move2side(offset);
593 e->setGeometry(g);
594 }
595 if (e->getToNode()->getOutgoingEdges().size() != 0) {
596 e = e->getToNode()->getOutgoingEdges()[0];
597 }
598 }
599 }
600}
601
602
603bool
605 return splitAt(dc, edge, node, edge->getID() + "[0]", edge->getID() + "[1]",
606 (int) edge->myLanes.size(), (int) edge->myLanes.size());
607}
608
609
610bool
612 const std::string& firstEdgeName,
613 const std::string& secondEdgeName,
614 int noLanesFirstEdge, int noLanesSecondEdge,
615 const double speed, const double friction,
616 const int changedLeft) {
617 double pos;
619 if (pos <= 0) {
621 edge->myFrom->getPosition(), edge->myTo->getPosition(),
622 node->getPosition());
623 }
624 if (pos <= 0 || pos + POSITION_EPS > edge->getGeometry().length()) {
625 return false;
626 }
627 return splitAt(dc, edge, pos, node, firstEdgeName, secondEdgeName,
628 noLanesFirstEdge, noLanesSecondEdge, speed, friction, changedLeft);
629}
630
631
632bool
634 NBEdge* edge, double pos, NBNode* node,
635 const std::string& firstEdgeName,
636 const std::string& secondEdgeName,
637 int noLanesFirstEdge, int noLanesSecondEdge,
638 const double speed, const double friction,
639 const int changedLeft) {
640 if (firstEdgeName != edge->getID() && myEdges.count(firstEdgeName) != 0) {
641 WRITE_ERRORF(TL("Could not insert edge '%' before split of edge '%'."), firstEdgeName, edge->getID());
642 return false;
643 }
644 if (secondEdgeName == firstEdgeName || (secondEdgeName != edge->getID() && myEdges.count(secondEdgeName) != 0)) {
645 WRITE_ERRORF(TL("Could not insert edge '%' after split of edge '%'."), secondEdgeName, edge->getID());
646 return false;
647 }
648 // there must be at least some overlap between first and second edge
649 assert(changedLeft > -((int)noLanesFirstEdge));
650 assert(changedLeft < (int)noLanesSecondEdge);
651
652 // build the new edges' geometries
653 double geomPos = pos;
654 if (edge->hasLoadedLength()) {
655 geomPos *= edge->getGeometry().length() / edge->getLoadedLength();
656 }
657 std::pair<PositionVector, PositionVector> geoms = edge->getGeometry().splitAt(geomPos);
658 // reduce inaccuracies and preserve bidi
659 if (geoms.first[-1].almostSame(node->getPosition()) || edge->isBidi()) {
660 geoms.first[-1] = node->getPosition();
661 geoms.second[0] = node->getPosition();
662 }
663 // build and insert the edges
664 NBEdge* one = new NBEdge(firstEdgeName, edge->myFrom, node, edge, geoms.first, noLanesFirstEdge);
665 NBEdge* two = new NBEdge(secondEdgeName, node, edge->myTo, edge, geoms.second, noLanesSecondEdge);
666 if (OptionsCont::getOptions().getBool("output.original-names")) {
667 const std::string origID = edge->getLaneStruct(0).getParameter(SUMO_PARAM_ORIGID, edge->getID());
668 if (firstEdgeName != origID) {
669 one->setOrigID(origID, false);
670 }
671 if (secondEdgeName != origID) {
672 two->setOrigID(origID, false);
673 }
674 }
675 two->copyConnectionsFrom(edge);
676 if (speed != -1.) {
677 two->setSpeed(-1, speed);
678 }
679 if (friction != -1.) {
680 two->setFriction(-1, friction);
681 }
682 if (edge->getDistance() != 0) {
683 one->setDistance(edge->getDistance());
684 two->setDistance(one->getDistance() + pos);
685 }
686 if (edge->hasLoadedLength()) {
687 one->setLoadedLength(pos);
688 two->setLoadedLength(edge->getLoadedLength() - pos);
689 }
690 // replace information about this edge within the nodes
691 edge->myFrom->replaceOutgoing(edge, one, 0);
692 edge->myTo->replaceIncoming(edge, two, 0);
693 // patch tls
694 for (NBTrafficLightDefinition* const tld : edge->myFrom->getControllingTLS()) {
695 tld->replaceRemoved(edge, -1, one, -1, false);
696 }
697 for (NBTrafficLightDefinition* const tld : edge->myTo->getControllingTLS()) {
698 tld->replaceRemoved(edge, -1, two, -1, true);
699 }
700 // the edge is now occuring twice in both nodes...
701 // clean up
702 edge->myFrom->removeDoubleEdges();
703 edge->myTo->removeDoubleEdges();
704 // add connections from the first to the second edge
705 // there will be as many connections as there are lanes on the second edge
706 // by default lanes will be added / discontinued on the right side
707 // (appropriate for highway on-/off-ramps)
708 const int offset = (int)one->getNumLanes() - (int)two->getNumLanes() + changedLeft;
709 for (int i2 = 0; i2 < (int)two->getNumLanes(); i2++) {
710 const int i1 = MIN2(MAX2((int)0, i2 + offset), (int)one->getNumLanes());
712 throw ProcessError(TL("Could not set connection!"));
713 }
714 }
716 if (myEdges2Keep.count(edge->getID()) != 0) {
717 myEdges2Keep.insert(one->getID());
718 myEdges2Keep.insert(two->getID());
719 }
720 if (myEdges2Remove.count(edge->getID()) != 0) {
721 myEdges2Remove.insert(one->getID());
722 myEdges2Remove.insert(two->getID());
723 }
724 }
725 // erase the splitted edge
726 patchRoundabouts(edge, one, two, myRoundabouts);
727 patchRoundabouts(edge, one, two, myGuessedRoundabouts);
728 const std::string oldID = edge->getID();
729 extract(dc, edge, true);
730 insert(one, true); // duplicate id check happened earlier
731 insert(two, true); // duplicate id check happened earlier
732 myEdgesSplit[edge] = {one, two};
733 myWasSplit.insert(one);
734 myWasSplit.insert(two);
735 return true;
736}
737
738
739void
740NBEdgeCont::patchRoundabouts(NBEdge* orig, NBEdge* part1, NBEdge* part2, std::set<EdgeSet>& roundabouts) {
741 std::set<EdgeSet> addLater;
742 for (std::set<EdgeSet>::iterator it = roundabouts.begin(); it != roundabouts.end(); ++it) {
743 EdgeSet roundaboutSet = *it;
744 if (roundaboutSet.count(orig) > 0) {
745 roundaboutSet.erase(orig);
746 roundaboutSet.insert(part1);
747 roundaboutSet.insert(part2);
748 }
749 addLater.insert(roundaboutSet);
750 }
751 roundabouts.clear();
752 roundabouts.insert(addLater.begin(), addLater.end());
753}
754
755
756// ----- container access methods
757std::vector<std::string>
759 std::vector<std::string> ret;
760 for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
761 ret.push_back((*i).first);
762 }
763 return ret;
764}
765
766
767// ----- Adapting the input
768int
770 EdgeVector toRemove;
771 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
772 NBEdge* edge = (*i).second;
773 if (!myEdges2Keep.count(edge->getID())) {
774 edge->getFromNode()->removeEdge(edge);
775 edge->getToNode()->removeEdge(edge);
776 toRemove.push_back(edge);
777 }
778 }
779 for (EdgeVector::iterator j = toRemove.begin(); j != toRemove.end(); ++j) {
780 erase(dc, *j);
781 }
782 return (int)toRemove.size();
783}
784
785
786void
788 // make a copy of myEdges because splitting will modify it
789 EdgeCont edges = myEdges;
790 for (auto& item : edges) {
791 NBEdge* edge = item.second;
792 if (edge->getGeometry().size() < 3) {
793 continue;
794 }
795 PositionVector geom = edge->getGeometry();
796 const std::string id = edge->getID();
797 double offset = 0;
798 for (int i = 1; i < (int)geom.size() - 1; i++) {
799 offset += geom[i - 1].distanceTo(geom[i]);
800 std::string nodeID = id + "." + toString((int)offset);
801 if (!nc.insert(nodeID, geom[i])) {
802 WRITE_WARNING("Could not split geometry of edge '" + id + "' at index " + toString(i));
803 continue;
804 }
805 NBNode* node = nc.retrieve(nodeID);
806 splitAt(dc, edge, node, edge->getID(), nodeID, edge->getNumLanes(), edge->getNumLanes());
807 edge = retrieve(nodeID);
808 }
809 }
810}
811
812
813void
814NBEdgeCont::reduceGeometries(const double minDist) {
815 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
816 (*i).second->reduceGeometry(minDist);
817 }
818}
819
820
821void
822NBEdgeCont::checkGeometries(const double maxAngle, bool fixAngle, const double minRadius, bool fix, bool fixRailways, bool silent) {
823 if (maxAngle > 0 || minRadius > 0) {
824 for (auto& item : myEdges) {
825 if (isSidewalk(item.second->getPermissions()) || isForbidden(item.second->getPermissions())) {
826 continue;
827 }
828 item.second->checkGeometry(maxAngle, fixAngle, minRadius, fix || (fixRailways && isRailway(item.second->getPermissions())), silent);
829 }
830 }
831}
832
833
834// ----- processing methods
835void
837 for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); i++) {
838 (*i).second->clearControllingTLInformation();
839 }
840}
841
842
843void
845 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
846 (*i).second->sortOutgoingConnectionsByAngle();
847 }
848}
849
850
851void
853 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
854 (*i).second->computeEdge2Edges(noLeftMovers);
855 }
856}
857
858
859void
861 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
862 (*i).second->computeLanes2Edges();
863 }
864}
865
866
867void
869 const bool fixOppositeLengths = OptionsCont::getOptions().getBool("opposites.guess.fix-lengths");
870 for (const auto& edgeIt : myEdges) {
871 NBEdge* const edge = edgeIt.second;
872 edge->recheckLanes();
873 // check opposites
874 if (edge->getNumLanes() > 0) {
875 const int leftmostLane = edge->getNumLanes() - 1;
876 // check oppositeID stored in other lanes
877 for (int i = 0; i < leftmostLane; i++) {
878 const std::string& oppositeID = edge->getLanes()[i].oppositeID;
879 NBEdge* oppEdge = retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
880 if (oppositeID != "" && oppositeID != "-") {
881 if (edge->getLanes().back().oppositeID == "" && oppEdge != nullptr) {
882 edge->getLaneStruct(leftmostLane).oppositeID = oppositeID;
883 WRITE_WARNINGF(TL("Moving opposite lane '%' from invalid lane '%' to lane index %."), oppositeID, edge->getLaneID(i), leftmostLane);
884 } else {
885 WRITE_WARNINGF(TL("Removing opposite lane '%' for invalid lane '%'."), oppositeID, edge->getLaneID(i));
886 }
887 edge->getLaneStruct(i).oppositeID = "";
888 }
889 }
890 const std::string& oppositeID = edge->getLanes().back().oppositeID;
891 if (oppositeID != "" && oppositeID != "-") {
892 NBEdge* oppEdge = retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
893 if (oppEdge == nullptr) {
894 WRITE_WARNINGF(TL("Removing unknown opposite lane '%' for edge '%'."), oppositeID, edge->getID());
895 edge->getLaneStruct(leftmostLane).oppositeID = "";
896 continue;
897 } else if (oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
898 const std::string oppEdgeLeftmost = oppEdge->getLaneID(oppEdge->getNumLanes() - 1);
899 WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppositeID, edge->getID(), oppEdgeLeftmost);
900 edge->getLaneStruct(leftmostLane).oppositeID = oppEdgeLeftmost;
901 }
902 NBEdge::Lane& oppLane = oppEdge->getLaneStruct(oppEdge->getNumLanes() - 1);
903 if (oppLane.oppositeID == "") {
904 const std::string leftmostID = edge->getLaneID(leftmostLane);
905 WRITE_WARNINGF(TL("Adapting missing opposite lane '%' for edge '%'."), leftmostID, oppEdge->getID());
906 oppLane.oppositeID = leftmostID;
907 }
908 if (fabs(oppEdge->getLoadedLength() - edge->getLoadedLength()) > NUMERICAL_EPS) {
909 if (fixOppositeLengths) {
910 const double avgLength = 0.5 * (edge->getFinalLength() + oppEdge->getFinalLength());
911 WRITE_WARNINGF(TL("Averaging edge lengths for lane '%' (length %) and edge '%' (length %)."),
912 oppositeID, oppEdge->getLoadedLength(), edge->getID(), edge->getLoadedLength());
913 edge->setLoadedLength(avgLength);
914 oppEdge->setLoadedLength(avgLength);
915 } else {
916 WRITE_ERROR("Opposite lane '" + oppositeID + "' (length " + toString(oppEdge->getLoadedLength()) +
917 ") differs in length from edge '" + edge->getID() + "' (length " +
918 toString(edge->getLoadedLength()) + "). Set --opposites.guess.fix-lengths to fix this.");
919 edge->getLaneStruct(edge->getNumLanes() - 1).oppositeID = "";
920 continue;
921 }
922 }
923 if (oppEdge->getFromNode() != edge->getToNode() || oppEdge->getToNode() != edge->getFromNode()) {
924 WRITE_ERRORF(TL("Opposite lane '%' does not connect the same nodes as edge '%'!"), oppositeID, edge->getID());
925 edge->getLaneStruct(edge->getNumLanes() - 1).oppositeID = "";
926 }
927 }
928 }
929 // check for matching bidi lane shapes (at least for the simple case of 1-lane edges)
930 const NBEdge* bidi = edge->getBidiEdge();
931 if (bidi != nullptr && edge->getNumLanes() == 1 && bidi->getNumLanes() == 1 && edge->getID() < bidi->getID()) {
932 edge->getLaneStruct(0).shape = bidi->getLaneStruct(0).shape.reverse();
933 }
934
935 // check for valid offset and speed
936 const double startOffset = edge->isBidiRail() ? edge->getTurnDestination(true)->getEndOffset() : 0;
937 int i = 0;
938 for (const NBEdge::Lane& l : edge->getLanes()) {
939 if (startOffset + l.endOffset > edge->getLength()) {
940 WRITE_WARNINGF(TL("Invalid endOffset % at lane '%' with length % (startOffset %)."),
941 toString(l.endOffset), edge->getLaneID(i), toString(l.shape.length()), toString(startOffset));
942 } else if (l.speed < 0.) {
943 WRITE_WARNINGF(TL("Negative allowed speed (%) on lane '%', use --speed.minimum to prevent this."), toString(l.speed), edge->getLaneID(i));
944 } else if (l.speed == 0.) {
945 WRITE_WARNINGF(TL("Lane '%' has a maximum allowed speed of 0."), edge->getLaneID(i));
946 }
947 i++;
948 }
949 }
950}
951
952
953void
954NBEdgeCont::appendTurnarounds(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike) {
955 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
956 (*i).second->appendTurnaround(noTLSControlled, noFringe, onlyDeadends, onlyTurnlane, noGeometryLike, true);
957 }
958}
959
960
961void
962NBEdgeCont::appendTurnarounds(const std::set<std::string>& ids, bool noTLSControlled) {
963 for (std::set<std::string>::const_iterator it = ids.begin(); it != ids.end(); it++) {
964 myEdges[*it]->appendTurnaround(noTLSControlled, false, false, false, false, false);
965 }
966}
967
968
969void
971 std::set<std::string> stopEdgeIDs;
972 for (auto& stopItem : sc.getStops()) {
973 stopEdgeIDs.insert(stopItem.second->getEdgeId());
974 }
975 for (auto& item : myEdges) {
976 NBEdge* edge = item.second;
977 if (edge->isBidiRail()
978 && (stopEdgeIDs.count(item.first) > 0 ||
979 stopEdgeIDs.count(edge->getTurnDestination(true)->getID()) > 0)) {
980 NBEdge* to = edge->getTurnDestination(true);
981 assert(to != 0);
982 edge->setConnection(edge->getNumLanes() - 1,
983 to, to->getNumLanes() - 1, NBEdge::Lane2LaneInfoType::VALIDATED, false, false,
987 }
988 }
989}
990
991void
992NBEdgeCont::computeEdgeShapes(double smoothElevationThreshold) {
993 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
994 (*i).second->computeEdgeShape(smoothElevationThreshold);
995 }
996 // equalize length of opposite edges
997 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
998 NBEdge* edge = i->second;
999 const std::string& oppositeID = edge->getLanes().back().oppositeID;
1000 if (oppositeID != "" && oppositeID != "-") {
1001 NBEdge* oppEdge = retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
1002 if (oppEdge == nullptr || oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
1003 continue;
1004 }
1005 if (fabs(oppEdge->getLength() - edge->getLength()) > NUMERICAL_EPS) {
1006 double avgLength = (oppEdge->getLength() + edge->getLength()) / 2;
1007 edge->setAverageLengthWithOpposite(avgLength);
1008 oppEdge->setAverageLengthWithOpposite(avgLength);
1009 }
1010 }
1011 }
1012}
1013
1014
1015void
1017 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1018 (*i).second->computeLaneShapes();
1019 }
1020}
1021
1022
1023void
1026 EdgeVector edges) {
1027 // !!! Attention!
1028 // No merging of the geometry to come is being done
1029 // The connections are moved from one edge to another within
1030 // the replacement where the edge is a node's incoming edge.
1031
1032 // count the number of lanes, the speed and the id
1033 int nolanes = 0;
1034 double speed = 0;
1035 int priority = -1;
1036 bool joinEdges = true;
1037 std::string id;
1038 sort(edges.begin(), edges.end(), NBContHelper::same_connection_edge_sorter());
1039 // retrieve the connected nodes
1040 NBEdge* tpledge = *(edges.begin());
1041 NBNode* from = tpledge->getFromNode();
1042 NBNode* to = tpledge->getToNode();
1043 EdgeVector::const_iterator i;
1044 int myPriority = (*edges.begin())->getPriority();
1045 for (i = edges.begin(); i != edges.end(); i++) {
1046 // some assertions
1047 assert((*i)->getFromNode() == from);
1048 assert((*i)->getToNode() == to);
1049 // ad the number of lanes the current edge has
1050 nolanes += (*i)->getNumLanes();
1051 // build the id
1052 if (i != edges.begin()) {
1053 id += "+";
1054 }
1055 id += (*i)->getID();
1056 // compute the speed
1057 speed += (*i)->getSpeed();
1058 // build the priority
1059 // merged edges should have the same inherited priority
1060 if (myPriority == (*i)->getPriority()) {
1061 priority = myPriority;
1062 } else {
1063 priority = -1;
1064 joinEdges = false;
1065 }
1066 }
1067 if (joinEdges) {
1068 speed /= (double)edges.size();
1069 // build the new edge
1070 NBEdge* newEdge = new NBEdge(id, from, to, "", speed, NBEdge::UNSPECIFIED_FRICTION, nolanes, priority,
1072 tpledge->myLaneSpreadFunction, tpledge->getStreetName());
1073 // copy lane attributes
1074 int laneIndex = 0;
1075 for (i = edges.begin(); i != edges.end(); ++i) {
1076 const std::vector<NBEdge::Lane>& lanes = (*i)->getLanes();
1077 for (int j = 0; j < (int)lanes.size(); ++j) {
1078 newEdge->setPermissions(lanes[j].permissions, laneIndex);
1079 newEdge->setLaneWidth(laneIndex, lanes[j].width);
1080 newEdge->setEndOffset(laneIndex, lanes[j].endOffset);
1081 laneIndex++;
1082 }
1083 }
1084 insert(newEdge, true);
1085 // replace old edge by current within the nodes
1086 // and delete the old
1087 from->replaceOutgoing(edges, newEdge);
1088 to->replaceIncoming(edges, newEdge);
1089 // patch connections
1090 // add edge2edge-information
1091 for (i = edges.begin(); i != edges.end(); i++) {
1092 EdgeVector ev = (*i)->getConnectedEdges();
1093 for (EdgeVector::iterator j = ev.begin(); j != ev.end(); j++) {
1094 newEdge->addEdge2EdgeConnection(*j);
1095 }
1096 }
1097 // copy outgoing connections to the new edge
1098 int currLane = 0;
1099 for (i = edges.begin(); i != edges.end(); i++) {
1100 newEdge->moveOutgoingConnectionsFrom(*i, currLane);
1101 currLane += (*i)->getNumLanes();
1102 }
1103 // patch tl-information
1104 currLane = 0;
1105 for (i = edges.begin(); i != edges.end(); i++) {
1106 int noLanes = (*i)->getNumLanes();
1107 for (int j = 0; j < noLanes; j++, currLane++) {
1108 // replace in traffic lights
1109 tlc.replaceRemoved(*i, j, newEdge, currLane, true);
1110 tlc.replaceRemoved(*i, j, newEdge, currLane, false);
1111 }
1112 }
1113 // delete joined edges
1114 for (i = edges.begin(); i != edges.end(); i++) {
1115 extract(dc, *i, true);
1116 }
1117 }
1118}
1119
1120
1121void
1123 //@todo magic values
1124 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1125 NBEdge* edge = i->second;
1126 edge->guessOpposite();
1127 }
1128}
1129
1130
1131void
1133 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1134 NBEdge* opposite = getOppositeByID(i->first);
1135 if (opposite != nullptr) {
1136 i->second->setLaneSpreadFunction(LaneSpreadFunction::RIGHT);
1138 } else {
1139 i->second->setLaneSpreadFunction(LaneSpreadFunction::CENTER);
1140 }
1141 }
1142}
1143
1144
1145NBEdge*
1146NBEdgeCont::getOppositeByID(const std::string& edgeID) const {
1147 const std::string oppositeID = edgeID[0] == '-' ? edgeID.substr(1) : "-" + edgeID;
1148 EdgeCont::const_iterator it = myEdges.find(oppositeID);
1149 return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
1150}
1151
1152NBEdge*
1153NBEdgeCont::getByID(const std::string& edgeID) const {
1154 EdgeCont::const_iterator it = myEdges.find(edgeID);
1155 return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
1156}
1157
1158// ----- other
1159void
1160NBEdgeCont::addPostProcessConnection(const std::string& from, int fromLane, const std::string& to, int toLane, bool mayDefinitelyPass,
1161 KeepClear keepClear, double contPos, double visibility, double speed, double friction, double length,
1162 const PositionVector& customShape, bool uncontrolled, bool warnOnly,
1163 SVCPermissions permissions, bool indirectLeft, const std::string& edgeType, SVCPermissions changeLeft, SVCPermissions changeRight) {
1164 myConnections[from].push_back(PostProcessConnection(from, fromLane, to, toLane, mayDefinitelyPass, keepClear, contPos, visibility,
1165 speed, friction, length, customShape, uncontrolled, warnOnly, permissions, indirectLeft, edgeType, changeLeft, changeRight));
1166}
1167
1168bool
1169NBEdgeCont::hasPostProcessConnection(const std::string& from, const std::string& to) {
1170 if (myConnections.count(from) == 0) {
1171 return false;
1172 } else {
1173 if (to == "") {
1174 // wildcard
1175 return true;
1176 }
1177 for (const auto& ppc : myConnections[from]) {
1178 if (ppc.to == to) {
1179 return true;
1180 }
1181 }
1182 return false;
1183 }
1184}
1185
1186void
1188 const bool warnOnly = OptionsCont::getOptions().exists("ignore-errors.connections") && OptionsCont::getOptions().getBool("ignore-errors.connections");
1189 for (const auto& item : myConnections) {
1190 for (std::vector<PostProcessConnection>::const_iterator i = item.second.begin(); i != item.second.end(); ++i) {
1191 NBEdge* from = retrievePossiblySplit((*i).from, true);
1192 NBEdge* to = retrievePossiblySplit((*i).to, false);
1193 if (from == nullptr || to == nullptr ||
1194 !from->addLane2LaneConnection((*i).fromLane, to, (*i).toLane, NBEdge::Lane2LaneInfoType::USER, true, (*i).mayDefinitelyPass,
1195 (*i).keepClear, (*i).contPos, (*i).visibility, (*i).speed, (*i).friction, (*i).customLength, (*i).customShape,
1196 (*i).uncontrolled, (*i).permissions, (*i).indirectLeft, (*i).edgeType, (*i).changeLeft, (*i).changeRight,
1197 true)) {
1198 const std::string msg = "Could not insert connection between '" + (*i).from + "' and '" + (*i).to + "' after build.";
1199 if (warnOnly || (*i).warnOnly) {
1200 WRITE_WARNING(msg);
1201 } else {
1202 WRITE_ERROR(msg);
1203 }
1204 }
1205 }
1206 }
1207 // during loading we also kept some ambiguous connections in hope they might be valid after processing
1208 // we need to make sure that all invalid connections are removed now
1209 for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
1210 NBEdge* edge = it->second;
1211 NBNode* to = edge->getToNode();
1212 // make a copy because we may delete connections
1213 std::vector<NBEdge::Connection> connections = edge->getConnections();
1214 for (std::vector<NBEdge::Connection>::iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
1215 NBEdge::Connection& c = *it_con;
1216 if (c.toEdge != nullptr && c.toEdge->getFromNode() != to) {
1217 WRITE_WARNING("Found and removed invalid connection from edge '" + edge->getID() +
1218 "' to edge '" + c.toEdge->getID() + "' via junction '" + to->getID() + "'.");
1220 }
1221 }
1222 }
1223}
1224
1225
1227NBEdgeCont::getGeneratedFrom(const std::string& id) const {
1228 int len = (int)id.length();
1229 EdgeVector ret;
1230 for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1231 std::string curr = (*i).first;
1232 // the next check makes it possibly faster - we don not have
1233 // to compare the names
1234 if ((int)curr.length() <= len) {
1235 continue;
1236 }
1237 // the name must be the same as the given id but something
1238 // beginning with a '[' must be appended to it
1239 if (curr.substr(0, len) == id && curr[len] == '[') {
1240 ret.push_back((*i).second);
1241 continue;
1242 }
1243 // ok, maybe the edge is a compound made during joining of edges
1244 std::string::size_type pos = curr.find(id);
1245 // surely not
1246 if (pos == std::string::npos) {
1247 continue;
1248 }
1249 // check leading char
1250 if (pos > 0) {
1251 if (curr[pos - 1] != ']' && curr[pos - 1] != '+') {
1252 // actually, this is another id
1253 continue;
1254 }
1255 }
1256 if (pos + id.length() < curr.length()) {
1257 if (curr[pos + id.length()] != '[' && curr[pos + id.length()] != '+') {
1258 // actually, this is another id
1259 continue;
1260 }
1261 }
1262 ret.push_back((*i).second);
1263 }
1264 return ret;
1265}
1266
1267
1268int
1270 myGuessedRoundabouts.clear();
1271 std::set<NBEdge*> loadedRoundaboutEdges;
1272 for (std::set<EdgeSet>::const_iterator it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
1273 loadedRoundaboutEdges.insert(it->begin(), it->end());
1274 }
1275 // step 1: keep only those edges which have no turnarounds and which are not
1276 // part of a loaded roundabout
1277 std::set<NBEdge*> candidates;
1278 SVCPermissions valid = SVCAll & ~SVC_PEDESTRIAN;
1279 for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1280 NBEdge* e = (*i).second;
1281 NBNode* const to = e->getToNode();
1282 if (e->getTurnDestination() == nullptr
1283 && to->getConnectionTo(e->getFromNode()) == nullptr
1284 && (e->getPermissions() & valid) != 0) {
1285 candidates.insert(e);
1286 }
1287 }
1288
1289 // step 2:
1290 std::set<NBEdge*> visited;
1291 for (std::set<NBEdge*>::const_iterator i = candidates.begin(); i != candidates.end(); ++i) {
1292 EdgeVector loopEdges;
1293 // start with a random edge (this doesn't have to be a roundabout edge)
1294 // loop over connected edges (using always the leftmost one)
1295 // and keep the list in loopEdges
1296 // continue until we loop back onto a loopEdges and extract the loop
1297 NBEdge* e = (*i);
1298 if (visited.count(e) > 0) {
1299 // already seen
1300 continue;
1301 }
1302 loopEdges.push_back(e);
1303 bool doLoop = true;
1304#ifdef DEBUG_GUESS_ROUNDABOUT
1306#endif
1307 do {
1308#ifdef DEBUG_GUESS_ROUNDABOUT
1309 if (gDebugFlag1) {
1310 std::cout << " e=" << e->getID() << " loopEdges=" << toString(loopEdges) << "\n";
1311 gDebugFlag1 = true;
1312 }
1313#endif
1314 visited.insert(e);
1315 const EdgeVector& edges = e->getToNode()->getEdges();
1317 && !e->getToNode()->typeWasGuessed()) {
1318 doLoop = false;
1319#ifdef DEBUG_GUESS_ROUNDABOUT
1320 if (gDebugFlag1) {
1321 std::cout << " rbl\n";
1322 }
1323 gDebugFlag1 = false;
1324#endif
1325 break;
1326 }
1327 if (edges.size() < 2) {
1328 doLoop = false;
1329#ifdef DEBUG_GUESS_ROUNDABOUT
1330 if (gDebugFlag1) {
1331 std::cout << " deadend\n";
1332 }
1333 gDebugFlag1 = false;
1334#endif
1335 break;
1336 }
1337 if (e->getTurnDestination() != nullptr || e->getToNode()->getConnectionTo(e->getFromNode()) != nullptr) {
1338 // do not follow turn-arounds while in a (tentative) loop
1339 doLoop = false;
1340#ifdef DEBUG_GUESS_ROUNDABOUT
1341 if (gDebugFlag1) {
1342 std::cout << " invalid turnAround e=" << e->getID() << " dest=" << Named::getIDSecure(e->getTurnDestination()) << "\n";
1343 }
1344 gDebugFlag1 = false;
1345#endif
1346 break;
1347 }
1348 EdgeVector::const_iterator me = std::find(edges.begin(), edges.end(), e);
1349 NBContHelper::nextCW(edges, me);
1350 NBEdge* left = *me;
1351 while ((left->getPermissions() & valid) == 0 && left != e) {
1352 NBContHelper::nextCW(edges, me);
1353 left = *me;
1354 }
1355 if (left == e) {
1356 // no usable continuation edge found
1357 doLoop = false;
1358#ifdef DEBUG_GUESS_ROUNDABOUT
1359 if (gDebugFlag1) {
1360 std::cout << " noContinuation\n";
1361 }
1362 gDebugFlag1 = false;
1363#endif
1364 break;
1365 }
1366 NBContHelper::nextCW(edges, me);
1367 NBEdge* nextLeft = *me;
1368 double angle = fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), left->getAngleAtNode(e->getToNode())));
1369 double nextAngle = nextLeft == e ? 180 : fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), nextLeft->getAngleAtNode(e->getToNode())));
1370#ifdef DEBUG_GUESS_ROUNDABOUT
1371 if (gDebugFlag1) {
1372 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";
1373 }
1374#endif
1375 if (angle >= 120
1376 || (angle >= 90 &&
1377 // if the edges are long or the junction shape is small we should expect roundness (low angles)
1378 (MAX2(e->getLength(), left->getLength()) > 5
1379 || e->getLaneShape(0).back().distanceTo2D(left->getLaneShape(0).front()) < 10
1380 // there should be no straigher edge further left
1381 || (nextAngle < 45)
1382 ))) {
1383 // roundabouts do not have sharp turns (or they wouldn't be called 'round')
1384 // 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
1385 doLoop = false;
1386#ifdef DEBUG_GUESS_ROUNDABOUT
1387 if (gDebugFlag1) {
1388 std::cout << " failed angle=" << angle << "\n";
1389 }
1390 gDebugFlag1 = false;
1391#endif
1392 break;
1393 }
1394 EdgeVector::const_iterator loopClosed = std::find(loopEdges.begin(), loopEdges.end(), left);
1395 const int loopSize = (int)(loopEdges.end() - loopClosed);
1396 if (loopSize > 0) {
1397 // loop found
1398 if (loopSize < 3) {
1399 doLoop = false; // need at least 3 edges for a roundabout
1400 } else if (loopSize < (int)loopEdges.size()) {
1401 // remove initial edges not belonging to the loop
1402 EdgeVector(loopEdges.begin() + (loopEdges.size() - loopSize), loopEdges.end()).swap(loopEdges);
1403 }
1404 // count attachments to the outside. need at least 3 or a roundabout doesn't make much sense
1405 int attachments = 0;
1406 for (EdgeVector::const_iterator j = loopEdges.begin(); j != loopEdges.end(); ++j) {
1407 if ((*j)->getToNode()->getEdges().size() > 2) {
1408 attachments++;
1409 }
1410 }
1411 if (attachments < 3) {
1412 doLoop = false;
1413#ifdef DEBUG_GUESS_ROUNDABOUT
1414 if (gDebugFlag1) {
1415 std::cout << " attachments=" << attachments << "\n";
1416 }
1417 gDebugFlag1 = false;
1418#endif
1419 }
1420 break;
1421 }
1422 if (visited.count(left) > 0) {
1423 doLoop = false;
1424 } else {
1425 // keep going
1426 loopEdges.push_back(left);
1427 e = left;
1428 }
1429 } while (doLoop);
1430 if (doLoop) {
1431 // check form factor to avoid elongated shapes (circle: 1, square: ~0.79)
1432#ifdef DEBUG_GUESS_ROUNDABOUT
1433 if (gDebugFlag1) {
1434 std::cout << " formFactor=" << formFactor(loopEdges) << "\n";
1435 }
1436#endif
1437 double loopLength = 0;
1438 for (const NBEdge* const le : loopEdges) {
1439 loopLength += le->getLoadedLength();
1440 }
1441 if (formFactor(loopEdges) > 0.6
1442 && loopLength < OptionsCont::getOptions().getFloat("roundabouts.guess.max-length")) {
1443 // collected edges are marked in markRoundabouts
1444 EdgeSet guessed(loopEdges.begin(), loopEdges.end());
1445 if (loadedRoundaboutEdges.count(loopEdges.front()) != 0) {
1446 if (find(myRoundabouts.begin(), myRoundabouts.end(), guessed) == myRoundabouts.end()) {
1447 for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); it++) {
1448 if ((*it).count(loopEdges.front()) != 0) {
1449 WRITE_WARNINGF(TL("Replacing loaded roundabout '%' with '%'."), toString(*it), toString(guessed));
1450 myRoundabouts.erase(it);
1451 break;
1452 }
1453 }
1454 myGuessedRoundabouts.insert(guessed);
1455 }
1456 } else {
1457 myGuessedRoundabouts.insert(guessed);
1458#ifdef DEBUG_GUESS_ROUNDABOUT
1459 if (gDebugFlag1) {
1460 std::cout << " foundRoundabout=" << toString(loopEdges) << "\n";
1461 }
1462#endif
1463 }
1464 }
1465 }
1466#ifdef DEBUG_GUESS_ROUNDABOUT
1467 gDebugFlag1 = false;
1468#endif
1469 }
1470 return (int)myGuessedRoundabouts.size();
1471}
1472
1473
1474int
1476 std::set<NBEdge*> candidateEdges;
1477 for (const auto& edge : myEdges) {
1478 NBEdge* const e = edge.second;
1480 candidateEdges.insert(e);
1481 }
1482 }
1483 std::set<NBEdge*> visited;
1484 int extracted = 0;
1485 for (const auto& edgeIt : candidateEdges) {
1486 EdgeVector loopEdges;
1487 NBEdge* e = edgeIt;
1488 if (visited.count(e) > 0) {
1489 // already seen
1490 continue;
1491 }
1492 loopEdges.push_back(e);
1493 bool doLoop = true;
1494 //
1495 do {
1496 if (std::find(visited.begin(), visited.end(), e) != visited.end()) {
1497 if (loopEdges.size() > 1) {
1498 addRoundabout(EdgeSet(loopEdges.begin(), loopEdges.end()));
1499 ++extracted;
1500 }
1501 doLoop = false;
1502 break;
1503 }
1504 visited.insert(e);
1505 loopEdges.push_back(e);
1506 const EdgeVector& outgoingEdges = e->getToNode()->getOutgoingEdges();
1507 EdgeVector::const_iterator me = std::find_if(outgoingEdges.begin(), outgoingEdges.end(), [](const NBEdge * outgoingEdge) {
1508 return outgoingEdge->getJunctionPriority(outgoingEdge->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT;
1509 });
1510 if (me == outgoingEdges.end()) { // no closed loop
1511 doLoop = false;
1512 } else {
1513 e = *me;
1514 }
1515 } while (doLoop);
1516 }
1517 return extracted;
1518}
1519
1520
1521void
1523 // only loaded roundabouts are of concern here since guessing comes later
1524 std::set<EdgeSet> validRoundabouts;
1525 std::set<NBEdge*> validEdges;
1526 for (auto item : myEdges) {
1527 validEdges.insert(item.second);
1528 }
1529 for (EdgeSet roundabout : myRoundabouts) {
1530 EdgeSet validRoundabout;
1531 for (NBEdge* cand : roundabout) {
1532 if (validEdges.count(cand) != 0) {
1533 validRoundabout.insert(cand);
1534 }
1535 }
1536 if (validRoundabout.size() > 0) {
1537 validRoundabouts.insert(validRoundabout);
1538 }
1539 }
1540 myRoundabouts = validRoundabouts;
1541}
1542
1543
1544double
1546 // A circle (which maximizes area per circumference) has a formfactor of 1, non-circular shapes have a smaller value
1547 PositionVector points;
1548 for (EdgeVector::const_iterator it = loopEdges.begin(); it != loopEdges.end(); ++it) {
1549 points.append((*it)->getGeometry());
1550 }
1551 double circumference = points.length2D();
1552 return 4 * M_PI * points.area() / (circumference * circumference);
1553}
1554
1555
1556const std::set<EdgeSet>
1558 std::set<EdgeSet> result = myRoundabouts;
1559 result.insert(myGuessedRoundabouts.begin(), myGuessedRoundabouts.end());
1560 return result;
1561}
1562
1563
1564void
1566 if (roundabout.size() > 0) {
1567 if (find(myRoundabouts.begin(), myRoundabouts.end(), roundabout) != myRoundabouts.end()) {
1568 WRITE_WARNING("Ignoring duplicate roundabout: " + toString(roundabout));
1569 } else {
1570 myRoundabouts.insert(roundabout);
1571 }
1572 }
1573}
1574
1575void
1577 for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
1578 for (NBEdge* e : *it) {
1579 if (e->getToNode() == node) {
1580 myRoundabouts.erase(it);
1581 return;
1582 }
1583 }
1584 }
1585}
1586
1587void
1592
1593void
1594NBEdgeCont::removeRoundaboutEdges(const EdgeSet& toRemove, std::set<EdgeSet>& roundabouts) {
1595 // members of a set are constant so we have to do some tricks
1596 std::vector<EdgeSet> rList;
1597 for (const EdgeSet& r : roundabouts) {
1598 EdgeSet r2;
1599 std::set_difference(r.begin(), r.end(), toRemove.begin(), toRemove.end(), std::inserter(r2, r2.end()));
1600 rList.push_back(r2);
1601 }
1602 roundabouts.clear();
1603 roundabouts.insert(rList.begin(), rList.end());
1604}
1605
1606
1607void
1609 for (const EdgeSet& roundaboutSet : getRoundabouts()) {
1610 for (NBEdge* const edge : roundaboutSet) {
1611 // disable turnarounds on incoming edges
1612 NBNode* const node = edge->getToNode();
1613 for (NBEdge* const inEdge : node->getIncomingEdges()) {
1614 if (roundaboutSet.count(inEdge) > 0) {
1615 continue;
1616 }
1617 if (inEdge->getStep() >= NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
1618 continue;
1619 }
1620 if (inEdge->getTurnDestination() != nullptr) {
1621 inEdge->removeFromConnections(inEdge->getTurnDestination(), -1);
1622 } else {
1623 // also remove connections that are effecively a turnaround but
1624 // where not correctly detector due to geometrical quirks
1625 const std::vector<NBEdge::Connection> cons = inEdge->getConnections();
1626 for (const NBEdge::Connection& con : cons) {
1627 if (con.toEdge && roundaboutSet.count(con.toEdge) == 0) {
1628 const double angle = fabs(NBHelpers::normRelAngle(inEdge->getAngleAtNode(node), con.toEdge->getAngleAtNode(node)));
1629 if (angle > 160) {
1630 inEdge->removeFromConnections(con.toEdge, -1);
1631 }
1632 }
1633 }
1634 }
1635
1636 }
1637 // let the connections to succeeding roundabout edge have a higher priority
1638 edge->setJunctionPriority(node, NBEdge::JunctionPriority::ROUNDABOUT);
1639 edge->setJunctionPriority(edge->getFromNode(), NBEdge::JunctionPriority::ROUNDABOUT);
1640 node->setRoundabout();
1641 }
1642 }
1643}
1644
1645
1646void
1648 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1649 NBEdge* e = i->second;
1650 const double offset = MAX2(0., e->getLength() - 3);
1651 if (e->getToNode()->isSimpleContinuation(false)) {
1652 // not a "real" junction?
1653 continue;
1654 }
1655 const SumoXMLNodeType nodeType = e->getToNode()->getType();
1656 switch (nodeType) {
1658 // yield or major?
1659 if (e->getJunctionPriority(e->getToNode()) > 0) {
1661 } else {
1663 }
1664 break;
1666 // yield or major?
1667 if (e->getJunctionPriority(e->getToNode()) > 0) {
1669 } else {
1671 }
1672 break;
1675 break;
1678 break;
1681 break;
1682 default:
1683 break;
1684 }
1685 }
1686}
1687
1688
1689int
1690NBEdgeCont::guessSpecialLanes(SUMOVehicleClass svc, double width, double minSpeed, double maxSpeed, bool fromPermissions, const std::string& excludeOpt,
1692 int lanesCreated = 0;
1693 std::vector<std::string> edges;
1694 if (excludeOpt != "") {
1695 edges = OptionsCont::getOptions().getStringVector(excludeOpt);
1696 }
1697 std::set<std::string> exclude(edges.begin(), edges.end());
1698 for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1699 NBEdge* edge = it->second;
1700 if (// not excluded
1701 exclude.count(edge->getID()) == 0
1702 // does not yet have a sidewalk
1703 && !edge->hasRestrictedLane(svc)
1704 && (
1705 // guess.from-permissions
1706 (fromPermissions && (edge->getPermissions() & svc) != 0)
1707 // guess from speed
1708 || (!fromPermissions && edge->getSpeed() > minSpeed && edge->getSpeed() <= maxSpeed)
1709 )) {
1710 edge->addRestrictedLane(width, svc);
1711 lanesCreated += 1;
1712 if (svc != SVC_PEDESTRIAN) {
1713 edge->invalidateConnections(true);
1715 edge->getFromNode()->invalidateTLS(tlc, true, true);
1716 edge->getToNode()->invalidateTLS(tlc, true, true);
1717 }
1718 }
1719 }
1720 return lanesCreated;
1721}
1722
1723
1724void
1726 for (auto item : myEdges) {
1727 item.second->updateChangeRestrictions(ignoring);
1728 }
1729}
1730
1731
1732void
1733NBEdgeCont::addPrefix(const std::string& prefix) {
1734 // make a copy of node containers
1735 const auto nodeContainerCopy = myEdges;
1736 myEdges.clear();
1737 for (const auto& node : nodeContainerCopy) {
1738 node.second->setID(prefix + node.second->getID());
1739 myEdges[node.second->getID()] = node.second;
1740 }
1741}
1742
1743
1744int
1745NBEdgeCont::remapIDs(bool numericaIDs, bool reservedIDs, const std::string& prefix, NBPTStopCont& sc) {
1746 bool startGiven = !OptionsCont::getOptions().isDefault("numerical-ids.edge-start");
1747 if (!numericaIDs && !reservedIDs && prefix == "" && !startGiven) {
1748 return 0;
1749 }
1750 std::vector<std::string> avoid;
1751 if (startGiven) {
1752 avoid.push_back(toString(OptionsCont::getOptions().getInt("numerical-ids.edge-start") - 1));
1753 } else {
1754 avoid = getAllNames();
1755 }
1756 std::set<std::string> reserve;
1757 if (reservedIDs) {
1758 NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("reserved-ids"), "edge:", reserve);
1759 avoid.insert(avoid.end(), reserve.begin(), reserve.end());
1760 }
1761 IDSupplier idSupplier("", avoid);
1762 std::set<NBEdge*, ComparatorIdLess> toChange;
1763 for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1764 if (startGiven) {
1765 toChange.insert(it->second);
1766 continue;
1767 }
1768 if (numericaIDs) {
1769 try {
1770 StringUtils::toLong(it->first);
1771 } catch (NumberFormatException&) {
1772 toChange.insert(it->second);
1773 }
1774 }
1775 if (reservedIDs && reserve.count(it->first) > 0) {
1776 toChange.insert(it->second);
1777 }
1778 }
1779
1780 std::map<std::string, std::vector<std::shared_ptr<NBPTStop> > > stopsOnEdge;
1781 for (const auto& item : sc.getStops()) {
1782 stopsOnEdge[item.second->getEdgeId()].push_back(item.second);
1783 }
1784
1785 const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
1786 for (NBEdge* edge : toChange) {
1787 myEdges.erase(edge->getID());
1788 }
1789 for (NBEdge* edge : toChange) {
1790 const std::string origID = edge->getID();
1791 if (origNames) {
1792 edge->setOrigID(origID, false);
1793 }
1794 edge->setID(idSupplier.getNext());
1795 myEdges[edge->getID()] = edge;
1796 for (std::shared_ptr<NBPTStop> stop : stopsOnEdge[origID]) {
1797 stop->setEdgeId(prefix + edge->getID(), *this);
1798 }
1799 }
1800 if (prefix.empty()) {
1801 return (int)toChange.size();
1802 } else {
1803 int renamed = 0;
1804 // make a copy because we will modify the map
1805 auto oldEdges = myEdges;
1806 for (auto item : oldEdges) {
1807 if (!StringUtils::startsWith(item.first, prefix)) {
1808 rename(item.second, prefix + item.first);
1809 renamed++;
1810 }
1811 }
1812 return renamed;
1813 }
1814}
1815
1816
1817void
1818NBEdgeCont::checkOverlap(double threshold, double zThreshold) const {
1819 for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1820 const NBEdge* e1 = it->second;
1821 Boundary b1 = e1->getGeometry().getBoxBoundary();
1822 b1.grow(e1->getTotalWidth());
1823 PositionVector outline1 = e1->getCCWBoundaryLine(*e1->getFromNode());
1824 outline1.append(e1->getCCWBoundaryLine(*e1->getToNode()));
1825 // check is symmetric. only check once per pair
1826 for (EdgeCont::const_iterator it2 = it; it2 != myEdges.end(); it2++) {
1827 const NBEdge* e2 = it2->second;
1828 if (e1 == e2) {
1829 continue;
1830 }
1831 Boundary b2 = e2->getGeometry().getBoxBoundary();
1832 b2.grow(e2->getTotalWidth());
1833 if (b1.overlapsWith(b2)) {
1834 PositionVector outline2 = e2->getCCWBoundaryLine(*e2->getFromNode());
1835 outline2.append(e2->getCCWBoundaryLine(*e2->getToNode()));
1836 const double overlap = outline1.getOverlapWith(outline2, zThreshold);
1837 if (overlap > threshold) {
1838 WRITE_WARNINGF(TL("Edge '%' overlaps with edge '%' by %."), e1->getID(), e2->getID(), overlap);
1839 }
1840 }
1841 }
1842 }
1843}
1844
1845
1846void
1847NBEdgeCont::checkGrade(double threshold) const {
1848 for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1849 const NBEdge* edge = it->second;
1850 for (int i = 0; i < (int)edge->getNumLanes(); i++) {
1851 double maxJump = 0;
1852 const double grade = edge->getLaneShape(i).getMaxGrade(maxJump);
1853 if (maxJump > 0.01) {
1854 WRITE_WARNINGF(TL("Edge '%' has a vertical jump of %m."), edge->getID(), maxJump);
1855 } else if (grade > threshold) {
1856 WRITE_WARNINGF(TL("Edge '%' has a grade of %%."), edge->getID(), grade * 100, "%");
1857 break;
1858 }
1859 }
1860 const std::vector<NBEdge::Connection>& connections = edge->getConnections();
1861 for (std::vector<NBEdge::Connection>::const_iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
1862 const NBEdge::Connection& c = *it_con;
1863 double maxJump = 0;
1864 const double grade = MAX2(c.shape.getMaxGrade(maxJump), c.viaShape.getMaxGrade(maxJump));
1865 if (maxJump > 0.01) {
1866 WRITE_WARNINGF(TL("Connection '%' has a vertical jump of %m."), c.getDescription(edge), maxJump);
1867 } else if (grade > threshold) {
1868 WRITE_WARNINGF(TL("Connection '%' has a grade of %%."), c.getDescription(edge), grade * 100, "%");
1869 break;
1870 }
1871 }
1872 }
1873}
1874
1875
1876int
1878 int affectedEdges = 0;
1879 for (auto item : myEdges) {
1880 if (item.second->joinLanes(perms)) {
1881 affectedEdges++;
1882 }
1883 }
1884 return affectedEdges;
1885}
1886
1887
1888bool
1889NBEdgeCont::MinLaneComparatorIdLess::operator()(const std::pair<NBEdge*, int>& a, const std::pair<NBEdge*, int>& b) const {
1890 if (a.first->getID() == b.first->getID()) {
1891 return a.second < b.second;
1892 }
1893 return a.first->getID() < b.first->getID();
1894}
1895
1896int
1898 // this is different from joinSimilarEdges because there don't need to be
1899 // shared nodes and tram edges may be split
1900 std::vector<NBEdge*> tramEdges;
1901 std::vector<NBEdge*> targetEdges;
1902 for (auto item : myEdges) {
1903 SVCPermissions permissions = item.second->getPermissions();
1904 if (isTram(permissions)) {
1905 if (item.second->getNumLanes() == 1) {
1906 tramEdges.push_back(item.second);
1907 } else {
1908 WRITE_WARNINGF(TL("Not joining tram edge '%' with % lanes."), item.second->getID(), item.second->getNumLanes());
1909 }
1910 } else if ((permissions & (SVC_PASSENGER | SVC_BUS)) != 0) {
1911 targetEdges.push_back(item.second);
1912 }
1913 }
1914 if (tramEdges.empty() || targetEdges.empty()) {
1915 return 0;
1916 }
1917 int numJoined = 0;
1918 NamedRTree tramTree;
1919 for (NBEdge* const edge : tramEdges) {
1920 const Boundary& bound = edge->getGeometry().getBoxBoundary();
1921 float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1922 float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1923 tramTree.Insert(min, max, edge);
1924 }
1925 // {targetEdge, laneIndex : tramEdge}
1926 std::map<std::pair<NBEdge*, int>, NBEdge*, MinLaneComparatorIdLess> matches;
1927
1928 for (NBEdge* const edge : targetEdges) {
1929 Boundary bound = edge->getGeometry().getBoxBoundary();
1930 bound.grow(maxDist + edge->getTotalWidth());
1931 float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1932 float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1933 std::set<const Named*> near;
1934 Named::StoringVisitor visitor(near);
1935 tramTree.Search(min, max, visitor);
1936 // the nearby set is actually just re-sorting according to the id to make the tests comparable
1937 std::set<NBEdge*, ComparatorIdLess> nearby;
1938 for (const Named* namedEdge : near) {
1939 nearby.insert(const_cast<NBEdge*>(static_cast<const NBEdge*>(namedEdge)));
1940 }
1941 for (NBEdge* const tramEdge : nearby) {
1942 // find a continous stretch of tramEdge that runs along one of the
1943 // lanes of the road edge
1944 const PositionVector& tramShape = tramEdge->getGeometry();
1945 double minEdgeDist = maxDist + 1;
1946 int minLane = -1;
1947 // find the lane where the maximum distance from the tram geometry
1948 // is minimal and within maxDist
1949 for (int i = 0; i < edge->getNumLanes(); i++) {
1950 double maxLaneDist = -1;
1951 if ((edge->getPermissions(i) & (SVC_PASSENGER | SVC_BUS)) != 0) {
1952 const PositionVector& laneShape = edge->getLaneShape(i);
1953 for (Position pos : laneShape) {
1954 const double dist = tramShape.distance2D(pos, false);
1955#ifdef DEBUG_JOIN_TRAM
1956 //if (edge->getID() == "106838214#1") {
1957 // std::cout << " edge=" << edge->getID() << " tramEdge=" << tramEdge->getID() << " lane=" << i << " pos=" << pos << " dist=" << dist << "\n";
1958 //}
1959#endif
1960 if (dist == GeomHelper::INVALID_OFFSET || dist > maxDist) {
1961 maxLaneDist = -1;
1962 break;
1963 }
1964 maxLaneDist = MAX2(maxLaneDist, dist);
1965 }
1966 if (maxLaneDist >= 0 && maxLaneDist < minEdgeDist) {
1967 minEdgeDist = maxLaneDist;
1968 minLane = i;
1969 }
1970 }
1971 }
1972 if (minLane >= 0) {
1973 // edge could run in the wrong direction and still fit the threshold we check the angle as well
1974 const PositionVector& laneShape = edge->getLaneShape(minLane);
1975 const double offset1 = tramShape.nearest_offset_to_point2D(laneShape.front(), false);
1976 const double offset2 = tramShape.nearest_offset_to_point2D(laneShape.back(), false);
1977 Position p1 = tramShape.positionAtOffset2D(offset1);
1978 Position p2 = tramShape.positionAtOffset2D(offset2);
1979 double tramAngle = GeomHelper::legacyDegree(p1.angleTo2D(p2), true);
1980 bool angleOK = GeomHelper::getMinAngleDiff(tramAngle, edge->getTotalAngle()) < JOIN_TRAM_MAX_ANGLE;
1981 if (angleOK && offset2 > offset1) {
1982 std::pair<NBEdge*, int> key = std::make_pair(edge, minLane);
1983 if (matches.count(key) == 0) {
1984 matches[key] = tramEdge;
1985 } else {
1986 WRITE_WARNINGF(TL("Ambiguous tram edges '%' and '%' for lane '%'."), matches[key]->getID(), tramEdge->getID(), edge->getLaneID(minLane));
1987 }
1988#ifdef DEBUG_JOIN_TRAM
1989 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";
1990#endif
1991 }
1992 }
1993 }
1994 }
1995 if (matches.size() == 0) {
1996 return 0;
1997 }
1998 const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
1999 // find continous runs of matched edges for each tramEdge
2000 for (NBEdge* tramEdge : tramEdges) {
2001 std::vector<std::pair<double, std::pair<NBEdge*, int> > > roads;
2002 for (auto item : matches) {
2003 if (item.second == tramEdge) {
2004 NBEdge* road = item.first.first;
2005 int laneIndex = item.first.second;
2006 const PositionVector& laneShape = road->getLaneShape(laneIndex);
2007 double tramPos = tramEdge->getGeometry().nearest_offset_to_point2D(laneShape.front(), false);
2008 roads.push_back(std::make_pair(tramPos, item.first));
2009 }
2010 }
2011 if (roads.size() != 0) {
2012
2013 sort(roads.begin(), roads.end());
2014#ifdef DEBUG_JOIN_TRAM
2015 std::cout << " tramEdge=" << tramEdge->getID() << " roads=";
2016 for (auto item : roads) {
2017 std::cout << item.second.first->getLaneID(item.second.second) << ",";
2018 }
2019 std::cout << " offsets=";
2020 for (auto item : roads) {
2021 std::cout << item.first << ",";
2022 }
2023 std::cout << "\n";
2024#endif
2025 // merge tramEdge into road lanes
2026 EdgeVector replacement;
2027 double pos = 0;
2028 int tramPart = 0;
2029 std::string tramEdgeID = tramEdge->getID();
2030 NBNode* tramFrom = tramEdge->getFromNode();
2031 PositionVector tramShape = tramEdge->getGeometry();
2032 const double tramLength = tramShape.length();
2033 EdgeVector incoming = tramFrom->getIncomingEdges();
2034 bool erasedLast = false;
2035 for (const auto& item : roads) {
2036 const double gap = item.first - pos;
2037 NBEdge* road = item.second.first;
2038 int laneIndex = item.second.second;
2039 if (gap >= JOIN_TRAM_MIN_LENGTH) {
2040#ifdef DEBUG_JOIN_TRAM
2041 std::cout << " splitting tramEdge=" << tramEdge->getID() << " at " << item.first << " (gap=" << gap << ")\n";
2042#endif
2043 const std::string firstPartID = tramEdgeID + "#" + toString(tramPart++);
2044 splitAt(dc, tramEdge, gap, road->getFromNode(), firstPartID, tramEdgeID, 1, 1);
2045 tramEdge = retrieve(tramEdgeID); // second part;
2046 NBEdge* firstPart = retrieve(firstPartID);
2047 firstPart->invalidateConnections(true);
2048 incoming.clear();
2049 incoming.push_back(firstPart);
2050 replacement.push_back(firstPart);
2051 }
2052 pos = item.first + road->getGeometry().length();
2053 numJoined++;
2054 replacement.push_back(road);
2055 // merge section of tramEdge into road lane
2056 if (road->getToNode() != tramEdge->getToNode() && (tramLength - pos) >= JOIN_TRAM_MIN_LENGTH) {
2057 tramEdge->reinitNodes(road->getToNode(), tramEdge->getToNode());
2058 tramEdge->setGeometry(tramShape.getSubpart(pos, tramShape.length()));
2059 erasedLast = false;
2060#ifdef DEBUG_JOIN_TRAM
2061 std::cout << " shorted tramEdge=" << tramEdge->getID() << " (joined with roadEdge=" << road->getID() << "\n";
2062#endif
2063 } else {
2064#ifdef DEBUG_JOIN_TRAM
2065 std::cout << " erased tramEdge=" << tramEdge->getID() << "\n";
2066#endif
2067 extract(dc, tramEdge, true);
2068 erasedLast = true;
2069 }
2070 road->setPermissions(road->getPermissions(laneIndex) | SVC_TRAM, laneIndex);
2071 if (origNames) {
2072 road->setOrigID(tramEdgeID, true, laneIndex);
2073 }
2074 for (NBEdge* in : incoming) {
2075 if (isTram(in->getPermissions()) && !in->isConnectedTo(road)) {
2076 if (in->getFromNode() != road->getFromNode()) {
2077 in->reinitNodes(in->getFromNode(), road->getFromNode());
2078 } else {
2079 extract(dc, in, true);
2080#ifdef DEBUG_JOIN_TRAM
2081 std::cout << " erased incoming tramEdge=" << in->getID() << "\n";
2082#endif
2083 }
2084 }
2085 }
2086 incoming.clear();
2087 }
2088 NBEdge* lastRoad = roads.back().second.first;
2089 if (erasedLast) {
2090 // copy to avoid concurrent modification
2091 auto outEdges = tramEdge->getToNode()->getOutgoingEdges();
2092 for (NBEdge* out : outEdges) {
2093 if (isTram(out->getPermissions()) && !lastRoad->isConnectedTo(out)) {
2094 if (lastRoad->getToNode() != out->getToNode()) {
2095 out->reinitNodes(lastRoad->getToNode(), out->getToNode());
2096 } else {
2097 extract(dc, out, true);
2098#ifdef DEBUG_JOIN_TRAM
2099 std::cout << " erased outgoing tramEdge=" << out->getID() << "\n";
2100#endif
2101
2102 }
2103 }
2104 }
2105 } else {
2106 replacement.push_back(tramEdge);
2107 }
2108 // update ptstops and ptlines
2109 sc.replaceEdge(tramEdgeID, replacement);
2110 lc.replaceEdge(tramEdgeID, replacement);
2111 }
2112 }
2113
2114 return numJoined;
2115}
2116
2117
2120 EdgeVector result;
2121 for (auto item : myEdges) {
2122 item.second->setNumericalID((int)result.size());
2123 result.push_back(item.second);
2124 }
2125 return result;
2126}
2127
2130 EdgeVector all = getAllEdges();
2131 return RouterEdgeVector(all.begin(), all.end());
2132}
2133
2134bool
2136 bool ok = true;
2137 for (const auto& item : myEdges) {
2138 NBEdge* e = item.second;
2139 if (nc.retrieve(e->getFromNode()->getID()) == nullptr) {
2140 WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), e->getID(), e->getFromNode()->getID());
2141 ok = false;
2142 }
2143 if (nc.retrieve(e->getToNode()->getID()) == nullptr) {
2144 WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), e->getID(), e->getToNode()->getID());
2145 ok = false;
2146 }
2147
2148 }
2149 return ok;
2150}
2151
2152
2153void
2155 for (auto item : myEdges) {
2156 NBEdge* e = item.second;
2157 if (e->hasLoadedLength() && myWasSplit.count(e) != 0) {
2158 // subtract half the length of the longest incoming / outgoing connection
2159 double maxLengthOut = 0;
2160 for (const NBEdge::Connection& c : e->getConnections()) {
2161 maxLengthOut = MAX2(maxLengthOut, c.length + c.viaLength);
2162 }
2163 double maxLengthIn = 0;
2164 for (const NBEdge* in : e->getIncomingEdges()) {
2165 for (const NBEdge::Connection& c : in->getConnectionsFromLane(-1, e, -1)) {
2166 maxLengthIn = MAX2(maxLengthIn, c.length + c.viaLength);
2167 }
2168 }
2169 e->setLoadedLength(MAX2(POSITION_EPS, e->getLoadedLength() - (maxLengthIn + maxLengthOut) / 2));
2170 }
2171 }
2172}
2173
2174void
2176 for (auto item : myEdges) {
2177 item.second->computeAngle();
2178 }
2179}
2180
2181
2182std::set<std::string>
2184 std::set<std::string> result;
2185 for (auto item : myEdges) {
2186 if (item.second->getTypeID() != "") {
2187 result.insert(item.second->getTypeID());
2188 }
2189 }
2190 return result;
2191}
2192
2193
2194int
2196 EdgeSet toRemove;
2197 for (auto item : myEdges) {
2198 NBEdge* edge = item.second;
2199 // remove edges which allow a speed below a set one (set using "keep-edges.min-speed")
2200 if (edge->getSpeed() < myEdgesMinSpeed) {
2201 toRemove.insert(edge);
2202 }
2203 }
2204 int numRemoved = 0;
2205 for (NBEdge* edge : toRemove) {
2206 // explicit whitelist overrides removal
2207 if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
2208 extract(dc, edge);
2209 numRemoved++;
2210 }
2211 }
2212 return numRemoved;
2213}
2214
2215
2216int
2218 EdgeSet toRemove;
2219 for (auto item : myEdges) {
2220 NBEdge* edge = item.second;
2221 // check whether the edge shall be removed because it does not allow any of the wished classes
2222 if (myVehicleClasses2Keep != 0 && (myVehicleClasses2Keep & edge->getPermissions()) == 0) {
2223 toRemove.insert(edge);
2224 }
2225 // check whether the edge shall be removed due to allowing unwished classes only
2227 toRemove.insert(edge);
2228 }
2229 }
2230 int numRemoved = 0;
2231 for (NBEdge* edge : toRemove) {
2232 // explicit whitelist overrides removal
2233 if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
2234 extract(dc, edge);
2235 numRemoved++;
2236 }
2237 }
2238 return numRemoved;
2239}
2240
2241
2242int
2244 EdgeSet toRemove;
2245 for (auto item : myEdges) {
2246 NBEdge* const edge = item.second;
2247 std::vector<int> indices;
2248 int idx = 0;
2249 for (const auto& lane : edge->getLanes()) {
2250 if (lane.width != NBEdge::UNSPECIFIED_WIDTH && lane.width < minWidth) {
2251 indices.push_back(idx);
2252 }
2253 idx++;
2254 }
2255 if ((int)indices.size() == edge->getNumLanes()) {
2256 toRemove.insert(edge);
2257 } else {
2258 for (const int i : indices) {
2259 edge->deleteLane(i, false, true);
2260 }
2261 }
2262 }
2263 int numRemoved = 0;
2264 for (NBEdge* edge : toRemove) {
2265 // explicit whitelist overrides removal
2266 if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
2267 extract(dc, edge);
2268 numRemoved++;
2269 }
2270 }
2271 return numRemoved;
2272}
2273
2274
2275/****************************************************************************/
std::vector< std::string > & split(const std::string &s, char delim, std::vector< std::string > &elems)
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:296
#define WRITE_ERRORF(...)
Definition MsgHandler.h:305
#define WRITE_ERROR(msg)
Definition MsgHandler.h:304
#define WRITE_WARNING(msg)
Definition MsgHandler.h:295
#define TL(string)
Definition MsgHandler.h:315
#define TLF(string,...)
Definition MsgHandler.h:317
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
#define JOIN_TRAM_MAX_ANGLE
#define DEBUG_EDGE_ID
const SVCPermissions SVCAll
all VClasses are allowed
bool isRailway(SVCPermissions permissions)
Returns whether an edge with the given permissions is a railway edge.
bool isTram(SVCPermissions permissions)
Returns whether an edge with the given permissions is a tram edge.
bool isForbidden(SVCPermissions permissions)
Returns whether an edge with the given permissions 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 permissions is a sidewalk.
long long int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
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
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:37
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:343
bool overlapsWith(const AbstractPoly &poly, double offset=0) const
Returns whether the boundary overlaps with the given polygon.
Definition Boundary.cpp:197
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
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
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)
static double legacyDegree(const double angle, const bool positive=false)
static double getMinAngleDiff(double angle1, double angle2)
Returns the minimum distance (clockwise/counter-clockwise) between both angles.
std::string getNext()
Returns the next id.
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:797
void patchRoundabouts(NBEdge *orig, NBEdge *part1, NBEdge *part2, std::set< EdgeSet > &roundabouts)
fix roundabout information after splitting an edge
void computeEdgeShapes(double smoothElevationThreshold=-1)
Computes the shapes of all edges stored in the container.
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.
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:785
~NBEdgeCont()
Destructor.
void sortOutgoingLanesConnections()
Sorts all lanes of all edges within the container by their direction.
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:789
void appendRailwayTurnarounds(const NBPTStopCont &sc)
Appends turnarounds to all bidiRail edges with stops.
std::set< std::string > myEdges2Remove
Set of ids of edges which shall explicitly be removed.
Definition NBEdgeCont.h:767
std::set< std::string > myIgnoredEdges
The ids of ignored edges.
Definition NBEdgeCont.h:747
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:758
void recheckPostProcessConnections()
Try to set any stored connections.
void extract(NBDistrictCont &dc, NBEdge *edge, bool remember=false)
Removes the given edge from the container like erase but does not delete it.
void processSplits(NBEdge *e, std::vector< Split > splits, NBNodeCont &nc, NBDistrictCont &dc, NBTrafficLightLogicCont &tlc)
process splits
EdgeVector getAllEdges() const
return all edges
void erase(NBDistrictCont &dc, NBEdge *edge)
Removes the given edge from the container (deleting it)
NBEdge * retrieve(const std::string &id, bool retrieveExtracted=false) const
Returns the edge that has the given id.
std::set< std::string > myTypes2Keep
Set of edges types which shall be kept.
Definition NBEdgeCont.h:776
void recheckLanes()
Rechecks whether all lanes have a successor for each of the stored edges.
NBEdge * getOppositeByID(const std::string &edgeID) const
Returns the edge with negated id if it exists.
void checkGeometries(const double maxAngle, bool fixAngle, const double minRadius, bool fix, bool fixRailways, bool silent=false)
EdgeCont myExtractedEdges
The extracted edges which are kept for reference.
Definition NBEdgeCont.h:741
void reduceGeometries(const double minDist)
void recheckLaneSpread()
Rechecks whether the lane spread is proper.
bool ignoreFilterMatch(NBEdge *edge)
Returns true if this edge matches one of the removal criteria.
void removeRoundabout(const NBNode *node)
remove roundabout that contains the given node
void cleanupRoundabouts()
void splitGeometry(NBDistrictCont &dc, NBNodeCont &nc)
Splits edges into multiple if they have a complex geometry.
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.
int extractRoundabouts()
Determines which edges have been marked as roundabouts and stores them internally.
NBEdge * retrievePossiblySplit(const std::string &id, bool downstream) const
Tries to retrieve an edge, even if it is splitted.
RouterEdgeVector getAllRouterEdges() const
return all router edges
std::set< const NBEdge * > myWasSplit
the edges that were created as result of splitting
Definition NBEdgeCont.h:752
void rename(NBEdge *edge, const std::string &newID)
Renames the edge. Throws exception if newID already exists.
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:738
int removeUnwishedEdges(NBDistrictCont &dc)
Removes unwished edges (not in keep-edges)
std::set< std::string > myEdges2Keep
Set of ids of edges which shall explicitly be kept.
Definition NBEdgeCont.h:764
NBTypeCont & myTypeCont
The network builder; used to obtain type information.
Definition NBEdgeCont.h:628
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.
std::set< EdgeSet > myGuessedRoundabouts
Edges marked as belonging to a roundabout after guessing.
Definition NBEdgeCont.h:792
void computeAngles()
compute all edge angles
void clear()
Deletes all edges.
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:779
void applyOptions(OptionsCont &oc)
Initialises the storage by applying given options.
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:782
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:773
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.
SVCPermissions myVehicleClasses2Keep
Set of vehicle types which must be allowed on edges in order to keep them.
Definition NBEdgeCont.h:770
void computeLaneShapes()
Computes the shapes of all lanes of all edges stored in the container.
void joinSameNodeConnectingEdges(NBDistrictCont &dc, NBTrafficLightLogicCont &tlc, EdgeVector edges)
Joins the given edges because they connect the same nodes.
std::map< std::string, NBEdge * > EdgeCont
The type of the dictionary where an edge may be found by its id.
Definition NBEdgeCont.h:735
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:750
std::map< std::string, std::vector< PostProcessConnection > > myConnections
The list of connections to recheck.
Definition NBEdgeCont.h:732
bool insert(NBEdge *edge, bool ignorePrunning=false)
Adds an edge to the dictionary.
NBEdgeCont(NBTypeCont &tc)
Constructor.
std::set< NBEdge * > myEdgeCemetery
The edges which got extracted twice but may still be referenced somewhere TODO smart_ptr?
Definition NBEdgeCont.h:744
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
int removeLanesByWidth(NBDistrictCont &dc, const double minWidth)
bool splitAt(NBDistrictCont &dc, NBEdge *edge, NBNode *node)
Splits the edge at the position nearest to the given node.
std::vector< std::string > getAllNames() const
Returns all ids of known edges.
int removeEdgesBySpeed(NBDistrictCont &dc)
return number of edges removed
void checkGrade(double threshold) const
check whether edges are to steep
int removeEdgesByPermissions(NBDistrictCont &dc)
bool myRemoveEdgesAfterLoading
Whether edges shall be joined and patched first, then removed.
Definition NBEdgeCont.h:761
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:4888
double getLength() const
Returns the computed length of the edge.
Definition NBEdge.h:593
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition NBEdge.cpp:4378
const std::vector< Connection > & getConnections() const
Returns the connections.
Definition NBEdge.h:1041
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:4341
@ ROUNDABOUT
Definition NBEdge.h:387
double getLoadedLength() const
Returns the length was set explicitly or the computed length if it wasn't set.
Definition NBEdge.h:602
void setSpeed(int lane, double speed)
set lane specific speed (negative lane implies set for all lanes)
Definition NBEdge.cpp:4293
NBNode * getToNode() const
Returns the destination node of the edge.
Definition NBEdge.h:546
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:3815
static const double UNSPECIFIED_FRICTION
unspecified lane friction
Definition NBEdge.h:355
void incLaneNo(int by)
increment lane
Definition NBEdge.cpp:4074
Lane & getLaneStruct(int lane)
Definition NBEdge.h:1428
void setAverageLengthWithOpposite(double val)
patch average lane length in regard to the opposite edge
Definition NBEdge.cpp:4398
bool isBidi()
return whether this edge should be a bidi edge
Definition NBEdge.h:1423
const PositionVector & getGeometry() const
Returns the geometry of the edge.
Definition NBEdge.h:783
LaneSpreadFunction getLaneSpreadFunction() const
Returns how this edge's lanes' lateral offset is computed.
Definition NBEdge.cpp:998
bool isBidiRail(bool ignoreSpread=false) const
whether this edge is part of a bidirectional railway
Definition NBEdge.cpp:749
void dismissVehicleClassInformation()
dimiss vehicle class information
Definition NBEdge.cpp:4404
LaneSpreadFunction myLaneSpreadFunction
The information about how to spread the lanes.
Definition NBEdge.h:1791
bool hasLoadedLength() const
Returns whether a length was set explicitly.
Definition NBEdge.h:612
bool addEdge2EdgeConnection(NBEdge *dest, bool overrideRemoval=false, SVCPermissions permission=SVC_UNSPECIFIED)
Adds a connection to another edge.
Definition NBEdge.cpp:1097
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:1132
void setDistance(double distance)
set kilometrage at start of edge (negative value implies couting down along the edge)
Definition NBEdge.h:1413
const std::vector< NBEdge::Lane > & getLanes() const
Returns the lane definitions.
Definition NBEdge.h:730
@ 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:619
NBNode * myTo
Definition NBEdge.h:1740
const std::string & getID() const
Definition NBEdge.h:1528
double getDistance() const
get distance
Definition NBEdge.h:679
void setLaneWidth(int lane, double width)
set lane specific width (negative lane implies set for all lanes)
Definition NBEdge.cpp:4164
void setLaneSpreadFunction(LaneSpreadFunction spread)
(Re)sets how the lanes lateral offset shall be computed
Definition NBEdge.cpp:992
std::vector< Lane > myLanes
Lane information.
Definition NBEdge.h:1808
int getNumLanes() const
Returns the number of lanes.
Definition NBEdge.h:520
void setFriction(int lane, double friction)
set lane specific friction (negative lane implies set for all lanes)
Definition NBEdge.cpp:4309
static const double UNSPECIFIED_CONTPOS
unspecified internal junction position
Definition NBEdge.h:358
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:4606
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:1447
void invalidateConnections(bool reallowSetting=false)
invalidate current connections of edge
Definition NBEdge.cpp:1528
double getTotalWidth() const
Returns the combined width of all lanes of this edge.
Definition NBEdge.cpp:4216
static const double UNSPECIFIED_VISIBILITY_DISTANCE
unspecified foe visibility for connections
Definition NBEdge.h:361
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:1330
void addSign(NBSign sign)
add Sign
Definition NBEdge.h:1460
void deleteLane(int index, bool recompute, bool shiftIndices)
delete lane
Definition NBEdge.cpp:4085
void moveOutgoingConnectionsFrom(NBEdge *e, int laneOff)
move outgoing connection
Definition NBEdge.cpp:3691
std::string getLaneID(int lane) const
get lane ID
Definition NBEdge.cpp:4016
@ 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:1185
int getJunctionPriority(const NBNode *const node) const
Returns the junction priority (normalised for the node currently build)
Definition NBEdge.cpp:2132
const std::string & getTypeID() const
get ID of type
Definition NBEdge.h:1181
const std::string & getStreetName() const
Returns the street name of this edge.
Definition NBEdge.h:669
const NBEdge * getBidiEdge() const
Definition NBEdge.h:1514
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition NBEdge.h:539
NBEdge * getTurnDestination(bool possibleDestination=false) const
Definition NBEdge.cpp:4007
double getAngleAtNode(const NBNode *const node) const
Returns the angle of the edge's geometry at the given node.
Definition NBEdge.cpp:2158
static const double UNSPECIFIED_WIDTH
unspecified lane width
Definition NBEdge.h:346
bool hasRestrictedLane(SUMOVehicleClass vclass) const
returns whether any lane already allows the given vclass exclusively
Definition NBEdge.cpp:4595
void copyConnectionsFrom(NBEdge *src)
copy connections from antoher edge
Definition NBEdge.cpp:1643
double getEndOffset() const
Returns the offset to the destination node.
Definition NBEdge.h:689
void setEndOffset(int lane, double offset)
set lane specific end-offset (negative lane implies set for all lanes)
Definition NBEdge.cpp:4247
static const double UNSPECIFIED_OFFSET
unspecified lane offset
Definition NBEdge.h:349
bool recheckLanes()
recheck whether all lanes within the edge are all right and optimises the connections once again
Definition NBEdge.cpp:2911
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:4736
const PositionVector & getLaneShape(int i) const
Returns the shape of the nth lane.
Definition NBEdge.cpp:986
void setLoadedLength(double val)
set loaded length
Definition NBEdge.cpp:4393
void decLaneNo(int by)
decrement lane
Definition NBEdge.cpp:4105
NBNode * myFrom
The source and the destination node.
Definition NBEdge.h:1740
double getFinalLength() const
get length that will be assigned to the lanes in the final network
Definition NBEdge.cpp:4714
void setGeometry(const PositionVector &g, bool inner=false)
(Re)sets the edge's geometry
Definition NBEdge.cpp:637
EdgeVector getIncomingEdges() const
Returns the list of incoming edges unsorted.
Definition NBEdge.cpp:1392
static void loadPrefixedIDsFomFile(const std::string &file, const std::string prefix, std::set< std::string > &into)
Add prefixed ids defined in file.
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.
NBNode * retrieve(const std::string &id) const
Returns the node with the given name.
void markAsSplit(const NBNode *node)
mark a node as being created form a split
Definition NBNodeCont.h:363
Represents a single node (junction) during network building.
Definition NBNode.h:66
void invalidateOutgoingConnections(bool reallowSetting=false)
invalidate outgoing connections
Definition NBNode.cpp:1998
void removeEdge(NBEdge *edge, bool removeFromConnections=true)
Removes edge from this node and optionally removes connections as well.
Definition NBNode.cpp:1929
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:336
bool isSimpleContinuation(bool checkLaneNumbers=true, bool checkWidth=false) const
check if node is a simple continuation
Definition NBNode.cpp:510
SumoXMLNodeType getType() const
Returns the type of this node.
Definition NBNode.h:285
const EdgeVector & getIncomingEdges() const
Returns this node's incoming edges (The edges which yield in this node)
Definition NBNode.h:268
const EdgeVector & getOutgoingEdges() const
Returns this node's outgoing edges (The edges which start at this node)
Definition NBNode.h:273
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:1714
void setRoundabout()
update the type of this node as a roundabout
Definition NBNode.cpp:3822
void invalidateTLS(NBTrafficLightLogicCont &tlCont, bool removedConnections, bool addedConnections)
causes the traffic light to be computed anew
Definition NBNode.cpp:426
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:1750
const Position & getPosition() const
Definition NBNode.h:260
const EdgeVector & getEdges() const
Returns all edges which participate in this node (Edges that start or end at this node)
Definition NBNode.h:278
bool typeWasGuessed() const
return whether a priority road turns at this node
Definition NBNode.h:840
void removeDoubleEdges()
remove duble edges
Definition NBNode.cpp:1818
NBEdge * getConnectionTo(NBNode *n) const
get connection to certain node
Definition NBNode.cpp:2634
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.
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.
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.
bool knows(const std::string &edgeType) const
Returns whether the named edgeType is in the container.
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.
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 (in radians bet...
Definition Position.h:286
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 amount
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.
PositionVector reverse() const
reverse position vector
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:104
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
PositionVector shape
The lane's shape.
Definition NBEdge.h:148
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:633
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