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-2026 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
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 || exp.node->getType() == SumoXMLNodeType::ZIPPER) {
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().nearest_offset_to_point2D(node->getPosition());
656 if (geomPos <= 0) {
657 geomPos = pos * edge->getGeometry().length() / edge->getLoadedLength();
658 }
659 }
660 std::pair<PositionVector, PositionVector> geoms = edge->getGeometry().splitAt(geomPos);
661 // reduce inaccuracies and preserve bidi
662 if (geoms.first[-1].almostSame(node->getPosition()) || edge->isBidi()) {
663 geoms.first[-1] = node->getPosition();
664 geoms.second[0] = node->getPosition();
665 }
666 // build and insert the edges
667 NBEdge* one = new NBEdge(firstEdgeName, edge->myFrom, node, edge, geoms.first, noLanesFirstEdge);
668 NBEdge* two = new NBEdge(secondEdgeName, node, edge->myTo, edge, geoms.second, noLanesSecondEdge);
669 if (OptionsCont::getOptions().getBool("output.original-names")) {
670 const std::string origID = edge->getLaneStruct(0).getParameter(SUMO_PARAM_ORIGID, edge->getID());
671 if (firstEdgeName != origID) {
672 one->setOrigID(origID, false);
673 }
674 if (secondEdgeName != origID) {
675 two->setOrigID(origID, false);
676 }
677 }
678 two->copyConnectionsFrom(edge);
679 if (speed != -1.) {
680 two->setSpeed(-1, speed);
681 }
682 if (friction != -1.) {
683 two->setFriction(-1, friction);
684 }
685 if (edge->getDistance() != 0) {
686 one->setDistance(edge->getDistance());
687 two->setDistance(one->getDistance() + pos);
688 }
689 if (edge->hasLoadedLength()) {
690 one->setLoadedLength(pos);
691 two->setLoadedLength(edge->getLoadedLength() - pos);
692 }
693 // replace information about this edge within the nodes
694 edge->myFrom->replaceOutgoing(edge, one, 0);
695 edge->myTo->replaceIncoming(edge, two, 0);
696 // patch tls
697 for (NBTrafficLightDefinition* const tld : edge->myFrom->getControllingTLS()) {
698 tld->replaceRemoved(edge, -1, one, -1, false);
699 }
700 for (NBTrafficLightDefinition* const tld : edge->myTo->getControllingTLS()) {
701 tld->replaceRemoved(edge, -1, two, -1, true);
702 }
703 // the edge is now occuring twice in both nodes...
704 // clean up
705 edge->myFrom->removeDoubleEdges();
706 edge->myTo->removeDoubleEdges();
707 // add connections from the first to the second edge
708 // there will be as many connections as there are lanes on the second edge
709 // by default lanes will be added / discontinued on the right side
710 // (appropriate for highway on-/off-ramps)
711 const int offset = (int)one->getNumLanes() - (int)two->getNumLanes() + changedLeft;
712 for (int i2 = 0; i2 < (int)two->getNumLanes(); i2++) {
713 const int i1 = MIN2(MAX2((int)0, i2 + offset), (int)one->getNumLanes());
715 throw ProcessError(TL("Could not set connection!"));
716 }
717 }
719 if (myEdges2Keep.count(edge->getID()) != 0) {
720 myEdges2Keep.insert(one->getID());
721 myEdges2Keep.insert(two->getID());
722 }
723 if (myEdges2Remove.count(edge->getID()) != 0) {
724 myEdges2Remove.insert(one->getID());
725 myEdges2Remove.insert(two->getID());
726 }
727 }
728 // erase the splitted edge
729 patchRoundabouts(edge, one, two, myRoundabouts);
730 patchRoundabouts(edge, one, two, myGuessedRoundabouts);
731 const std::string oldID = edge->getID();
732 extract(dc, edge, true);
733 insert(one, true); // duplicate id check happened earlier
734 insert(two, true); // duplicate id check happened earlier
735 myEdgesSplit[edge] = {one, two};
736 myWasSplit.insert(one);
737 myWasSplit.insert(two);
738 return true;
739}
740
741
742void
743NBEdgeCont::patchRoundabouts(NBEdge* orig, NBEdge* part1, NBEdge* part2, std::set<EdgeSet>& roundabouts) {
744 std::set<EdgeSet> addLater;
745 for (std::set<EdgeSet>::iterator it = roundabouts.begin(); it != roundabouts.end(); ++it) {
746 EdgeSet roundaboutSet = *it;
747 if (roundaboutSet.count(orig) > 0) {
748 roundaboutSet.erase(orig);
749 roundaboutSet.insert(part1);
750 roundaboutSet.insert(part2);
751 }
752 addLater.insert(roundaboutSet);
753 }
754 roundabouts.clear();
755 roundabouts.insert(addLater.begin(), addLater.end());
756}
757
758
759// ----- container access methods
760std::vector<std::string>
762 std::vector<std::string> ret;
763 for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
764 ret.push_back((*i).first);
765 }
766 return ret;
767}
768
769
770NBEdge*
771NBEdgeCont::getSplitBase(const std::string& edgeID) const {
772 NBEdge* longest = nullptr;
773 for (auto item : myEdgesSplit) {
774 if (item.first->getID() == edgeID) {
775 if (longest == nullptr || longest->getLoadedLength() < item.first->getLoadedLength()) {
776 longest = const_cast<NBEdge*>(item.first);
777 }
778 }
779 }
780 return longest;
781}
782
783// ----- Adapting the input
784int
786 EdgeVector toRemove;
787 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
788 NBEdge* edge = (*i).second;
789 if (!myEdges2Keep.count(edge->getID())) {
790 edge->getFromNode()->removeEdge(edge);
791 edge->getToNode()->removeEdge(edge);
792 toRemove.push_back(edge);
793 }
794 }
795 for (EdgeVector::iterator j = toRemove.begin(); j != toRemove.end(); ++j) {
796 erase(dc, *j);
797 }
798 return (int)toRemove.size();
799}
800
801
802void
804 // make a copy of myEdges because splitting will modify it
805 EdgeCont edges = myEdges;
806 for (auto& item : edges) {
807 NBEdge* edge = item.second;
808 if (edge->getGeometry().size() < 3) {
809 continue;
810 }
811 PositionVector geom = edge->getGeometry();
812 const std::string id = edge->getID();
813 double offset = 0;
814 for (int i = 1; i < (int)geom.size() - 1; i++) {
815 offset += geom[i - 1].distanceTo(geom[i]);
816 std::string nodeID = id + "." + toString((int)offset);
817 if (!nc.insert(nodeID, geom[i])) {
818 WRITE_WARNING("Could not split geometry of edge '" + id + "' at index " + toString(i));
819 continue;
820 }
821 NBNode* node = nc.retrieve(nodeID);
822 splitAt(dc, edge, node, edge->getID(), nodeID, edge->getNumLanes(), edge->getNumLanes());
823 edge = retrieve(nodeID);
824 }
825 }
826}
827
828
829void
830NBEdgeCont::reduceGeometries(const double minDist) {
831 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
832 (*i).second->reduceGeometry(minDist);
833 }
834}
835
836
837void
838NBEdgeCont::checkGeometries(const double maxAngle, bool fixAngle, const double minRadius, bool fix, bool fixRailways, bool silent) {
839 if (maxAngle > 0 || minRadius > 0) {
840 for (auto& item : myEdges) {
841 if ((item.second->getPermissions() & (SVC_PUBLIC_CLASSES | SVC_PASSENGER)) == 0) {
842 continue;
843 }
844 item.second->checkGeometry(maxAngle, fixAngle, minRadius, fix || (fixRailways && isRailway(item.second->getPermissions())), silent);
845 }
846 }
847}
848
849
850// ----- processing methods
851void
853 for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); i++) {
854 (*i).second->clearControllingTLInformation();
855 }
856}
857
858
859void
861 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
862 (*i).second->sortOutgoingConnectionsByAngle();
863 }
864}
865
866
867void
869 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
870 (*i).second->computeEdge2Edges(noLeftMovers);
871 }
872}
873
874
875void
877 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
878 (*i).second->computeLanes2Edges();
879 }
880}
881
882
883void
885 const bool fixOppositeLengths = OptionsCont::getOptions().getBool("opposites.guess.fix-lengths");
886 for (const auto& edgeIt : myEdges) {
887 NBEdge* const edge = edgeIt.second;
888 edge->recheckLanes();
889 edge->recheckOpposite(*this, fixOppositeLengths);
890 }
891}
892
893
894void
895NBEdgeCont::appendTurnarounds(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike) {
896 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
897 (*i).second->appendTurnaround(noTLSControlled, noFringe, onlyDeadends, onlyTurnlane, noGeometryLike, true);
898 }
899}
900
901
902void
903NBEdgeCont::appendTurnarounds(const std::set<std::string>& ids, bool noTLSControlled) {
904 for (std::set<std::string>::const_iterator it = ids.begin(); it != ids.end(); it++) {
905 myEdges[*it]->appendTurnaround(noTLSControlled, false, false, false, false, false);
906 }
907}
908
909
910void
912 std::set<std::string> stopEdgeIDs;
913 for (auto& stopItem : sc.getStops()) {
914 stopEdgeIDs.insert(stopItem.second->getEdgeId());
915 }
916 for (auto& item : myEdges) {
917 NBEdge* edge = item.second;
918 if (edge->isBidiRail()
919 && (stopEdgeIDs.count(item.first) > 0 ||
920 stopEdgeIDs.count(edge->getTurnDestination(true)->getID()) > 0)) {
921 NBEdge* to = edge->getTurnDestination(true);
922 assert(to != 0);
923 edge->setConnection(edge->getNumLanes() - 1,
924 to, to->getNumLanes() - 1, NBEdge::Lane2LaneInfoType::VALIDATED, false, false,
928 }
929 }
930}
931
932void
933NBEdgeCont::computeEdgeShapes(double smoothElevationThreshold) {
934 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
935 (*i).second->computeEdgeShape(smoothElevationThreshold);
936 }
937 // equalize length of opposite edges
938 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
939 NBEdge* edge = i->second;
940 const std::string& oppositeID = edge->getLanes().back().oppositeID;
941 if (oppositeID != "" && oppositeID != "-") {
942 NBEdge* oppEdge = retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
943 if (oppEdge == nullptr || oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
944 continue;
945 }
946 if (fabs(oppEdge->getLength() - edge->getLength()) > NUMERICAL_EPS) {
947 double avgLength = (oppEdge->getLength() + edge->getLength()) / 2;
948 edge->setAverageLengthWithOpposite(avgLength);
949 oppEdge->setAverageLengthWithOpposite(avgLength);
950 }
951 }
952 }
953}
954
955
956void
958 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
959 (*i).second->computeLaneShapes();
960 }
961}
962
963
964void
967 EdgeVector edges) {
968 // !!! Attention!
969 // No merging of the geometry to come is being done
970 // The connections are moved from one edge to another within
971 // the replacement where the edge is a node's incoming edge.
972
973 // count the number of lanes, the speed and the id
974 int nolanes = 0;
975 double speed = 0;
976 int priority = -1;
977 bool joinEdges = true;
978 std::string id;
979 sort(edges.begin(), edges.end(), NBContHelper::same_connection_edge_sorter());
980 // retrieve the connected nodes
981 NBEdge* tpledge = *(edges.begin());
982 NBNode* from = tpledge->getFromNode();
983 NBNode* to = tpledge->getToNode();
984 EdgeVector::const_iterator i;
985 int myPriority = (*edges.begin())->getPriority();
986 for (i = edges.begin(); i != edges.end(); i++) {
987 // some assertions
988 assert((*i)->getFromNode() == from);
989 assert((*i)->getToNode() == to);
990 // ad the number of lanes the current edge has
991 nolanes += (*i)->getNumLanes();
992 // build the id
993 if (i != edges.begin()) {
994 id += "+";
995 }
996 id += (*i)->getID();
997 // compute the speed
998 speed += (*i)->getSpeed();
999 // build the priority
1000 // merged edges should have the same inherited priority
1001 if (myPriority == (*i)->getPriority()) {
1002 priority = myPriority;
1003 } else {
1004 priority = -1;
1005 joinEdges = false;
1006 }
1007 }
1008 if (joinEdges) {
1009 speed /= (double)edges.size();
1010 // build the new edge
1011 NBEdge* newEdge = new NBEdge(id, from, to, "", speed, NBEdge::UNSPECIFIED_FRICTION, nolanes, priority,
1013 tpledge->myLaneSpreadFunction, tpledge->getStreetName());
1014 // copy lane attributes
1015 int laneIndex = 0;
1016 for (i = edges.begin(); i != edges.end(); ++i) {
1017 const std::vector<NBEdge::Lane>& lanes = (*i)->getLanes();
1018 for (int j = 0; j < (int)lanes.size(); ++j) {
1019 newEdge->setPermissions(lanes[j].permissions, laneIndex);
1020 newEdge->setLaneWidth(laneIndex, lanes[j].width);
1021 newEdge->setEndOffset(laneIndex, lanes[j].endOffset);
1022 laneIndex++;
1023 }
1024 }
1025 insert(newEdge, true);
1026 // replace old edge by current within the nodes
1027 // and delete the old
1028 from->replaceOutgoing(edges, newEdge);
1029 to->replaceIncoming(edges, newEdge);
1030 // patch connections
1031 // add edge2edge-information
1032 for (i = edges.begin(); i != edges.end(); i++) {
1033 EdgeVector ev = (*i)->getConnectedEdges();
1034 for (EdgeVector::iterator j = ev.begin(); j != ev.end(); j++) {
1035 newEdge->addEdge2EdgeConnection(*j);
1036 }
1037 }
1038 // copy outgoing connections to the new edge
1039 int currLane = 0;
1040 for (i = edges.begin(); i != edges.end(); i++) {
1041 newEdge->moveOutgoingConnectionsFrom(*i, currLane);
1042 currLane += (*i)->getNumLanes();
1043 }
1044 // patch tl-information
1045 currLane = 0;
1046 for (i = edges.begin(); i != edges.end(); i++) {
1047 int noLanes = (*i)->getNumLanes();
1048 for (int j = 0; j < noLanes; j++, currLane++) {
1049 // replace in traffic lights
1050 tlc.replaceRemoved(*i, j, newEdge, currLane, true);
1051 tlc.replaceRemoved(*i, j, newEdge, currLane, false);
1052 }
1053 }
1054 // delete joined edges
1055 for (i = edges.begin(); i != edges.end(); i++) {
1056 extract(dc, *i, true);
1057 }
1058 }
1059}
1060
1061
1062void
1064 //@todo magic values
1065 const bool fixOppositeLengths = OptionsCont::getOptions().getBool("opposites.guess.fix-lengths");
1066 // ensure consistency of loaded values before starting to guess
1067 for (const auto& edgeIt : myEdges) {
1068 NBEdge* const edge = edgeIt.second;
1069 edge->recheckOpposite(*this, fixOppositeLengths);
1070 }
1071 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1072 NBEdge* edge = i->second;
1073 edge->guessOpposite();
1074 }
1075}
1076
1077
1078void
1080 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1081 NBEdge* opposite = getOppositeByID(i->first);
1082 if (opposite != nullptr) {
1083 i->second->setLaneSpreadFunction(LaneSpreadFunction::RIGHT);
1085 } else {
1086 i->second->setLaneSpreadFunction(LaneSpreadFunction::CENTER);
1087 }
1088 }
1089}
1090
1091
1092NBEdge*
1093NBEdgeCont::getOppositeByID(const std::string& edgeID) const {
1094 const std::string oppositeID = edgeID[0] == '-' ? edgeID.substr(1) : "-" + edgeID;
1095 EdgeCont::const_iterator it = myEdges.find(oppositeID);
1096 return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
1097}
1098
1099NBEdge*
1100NBEdgeCont::getByID(const std::string& edgeID) const {
1101 EdgeCont::const_iterator it = myEdges.find(edgeID);
1102 return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
1103}
1104
1105// ----- other
1106void
1107NBEdgeCont::addPostProcessConnection(const std::string& from, int fromLane, const std::string& to, int toLane, bool mayDefinitelyPass,
1108 KeepClear keepClear, double contPos, double visibility, double speed, double friction, double length,
1109 const PositionVector& customShape, bool uncontrolled, bool warnOnly,
1110 SVCPermissions permissions, bool indirectLeft, const std::string& edgeType, SVCPermissions changeLeft, SVCPermissions changeRight) {
1111 myConnections[from].push_back(PostProcessConnection(from, fromLane, to, toLane, mayDefinitelyPass, keepClear, contPos, visibility,
1112 speed, friction, length, customShape, uncontrolled, warnOnly, permissions, indirectLeft, edgeType, changeLeft, changeRight));
1113}
1114
1115bool
1116NBEdgeCont::hasPostProcessConnection(const std::string& from, const std::string& to) {
1117 if (myConnections.count(from) == 0) {
1118 return false;
1119 } else {
1120 if (to == "") {
1121 // wildcard
1122 return true;
1123 }
1124 for (const auto& ppc : myConnections[from]) {
1125 if (ppc.to == to) {
1126 return true;
1127 }
1128 }
1129 return false;
1130 }
1131}
1132
1133void
1135 const bool warnOnly = OptionsCont::getOptions().exists("ignore-errors.connections") && OptionsCont::getOptions().getBool("ignore-errors.connections");
1136 for (const auto& item : myConnections) {
1137 for (std::vector<PostProcessConnection>::const_iterator i = item.second.begin(); i != item.second.end(); ++i) {
1138 NBEdge* from = retrievePossiblySplit((*i).from, true);
1139 NBEdge* to = retrievePossiblySplit((*i).to, false);
1140 if (from == nullptr || to == nullptr ||
1141 !from->addLane2LaneConnection((*i).fromLane, to, (*i).toLane, NBEdge::Lane2LaneInfoType::USER, true, (*i).mayDefinitelyPass,
1142 (*i).keepClear, (*i).contPos, (*i).visibility, (*i).speed, (*i).friction, (*i).customLength, (*i).customShape,
1143 (*i).uncontrolled, (*i).permissions, (*i).indirectLeft, (*i).edgeType, (*i).changeLeft, (*i).changeRight,
1144 true)) {
1145 const std::string msg = "Could not insert connection between '" + (*i).from + "' and '" + (*i).to + "' after build.";
1146 if (warnOnly || (*i).warnOnly) {
1147 WRITE_WARNING(msg);
1148 } else {
1149 WRITE_ERROR(msg);
1150 }
1151 }
1152 }
1153 }
1154 // during loading we also kept some ambiguous connections in hope they might be valid after processing
1155 // we need to make sure that all invalid connections are removed now
1156 for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
1157 NBEdge* edge = it->second;
1158 NBNode* to = edge->getToNode();
1159 // make a copy because we may delete connections
1160 std::vector<NBEdge::Connection> connections = edge->getConnections();
1161 for (std::vector<NBEdge::Connection>::iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
1162 NBEdge::Connection& c = *it_con;
1163 if (c.toEdge != nullptr && c.toEdge->getFromNode() != to) {
1164 WRITE_WARNING("Found and removed invalid connection from edge '" + edge->getID() +
1165 "' to edge '" + c.toEdge->getID() + "' via junction '" + to->getID() + "'.");
1167 }
1168 }
1169 }
1170}
1171
1172
1174NBEdgeCont::getGeneratedFrom(const std::string& id) const {
1175 int len = (int)id.length();
1176 EdgeVector ret;
1177 for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1178 std::string curr = (*i).first;
1179 // the next check makes it possibly faster - we don not have
1180 // to compare the names
1181 if ((int)curr.length() <= len) {
1182 continue;
1183 }
1184 // the name must be the same as the given id but something
1185 // beginning with a '[' must be appended to it
1186 if (curr.substr(0, len) == id && curr[len] == '[') {
1187 ret.push_back((*i).second);
1188 continue;
1189 }
1190 // ok, maybe the edge is a compound made during joining of edges
1191 std::string::size_type pos = curr.find(id);
1192 // surely not
1193 if (pos == std::string::npos) {
1194 continue;
1195 }
1196 // check leading char
1197 if (pos > 0) {
1198 if (curr[pos - 1] != ']' && curr[pos - 1] != '+') {
1199 // actually, this is another id
1200 continue;
1201 }
1202 }
1203 if (pos + id.length() < curr.length()) {
1204 if (curr[pos + id.length()] != '[' && curr[pos + id.length()] != '+') {
1205 // actually, this is another id
1206 continue;
1207 }
1208 }
1209 ret.push_back((*i).second);
1210 }
1211 return ret;
1212}
1213
1214
1215int
1217 myGuessedRoundabouts.clear();
1218 std::set<NBEdge*> loadedRoundaboutEdges;
1219 for (std::set<EdgeSet>::const_iterator it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
1220 loadedRoundaboutEdges.insert(it->begin(), it->end());
1221 }
1222 // step 1: keep only those edges which have no turnarounds and which are not
1223 // part of a loaded roundabout
1224 std::set<NBEdge*> candidates;
1225 SVCPermissions valid = SVCAll & ~SVC_PEDESTRIAN;
1226 for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1227 NBEdge* e = (*i).second;
1228 NBNode* const to = e->getToNode();
1229 if (e->getTurnDestination() == nullptr
1230 && to->getConnectionTo(e->getFromNode()) == nullptr
1231 && (e->getPermissions() & valid) != 0) {
1232 candidates.insert(e);
1233 }
1234 }
1235
1236 // step 2:
1237 std::set<NBEdge*> visited;
1238 for (std::set<NBEdge*>::const_iterator i = candidates.begin(); i != candidates.end(); ++i) {
1239 EdgeVector loopEdges;
1240 // start with a random edge (this doesn't have to be a roundabout edge)
1241 // loop over connected edges (using always the leftmost one)
1242 // and keep the list in loopEdges
1243 // continue until we loop back onto a loopEdges and extract the loop
1244 NBEdge* e = (*i);
1245 if (visited.count(e) > 0) {
1246 // already seen
1247 continue;
1248 }
1249 loopEdges.push_back(e);
1250 bool doLoop = true;
1251#ifdef DEBUG_GUESS_ROUNDABOUT
1253#endif
1254 do {
1255#ifdef DEBUG_GUESS_ROUNDABOUT
1256 if (gDebugFlag1) {
1257 std::cout << " e=" << e->getID() << " loopEdges=" << toString(loopEdges) << "\n";
1258 gDebugFlag1 = true;
1259 }
1260#endif
1261 visited.insert(e);
1262 const EdgeVector& edges = e->getToNode()->getEdges();
1264 && !e->getToNode()->typeWasGuessed()) {
1265 doLoop = false;
1266#ifdef DEBUG_GUESS_ROUNDABOUT
1267 if (gDebugFlag1) {
1268 std::cout << " rbl\n";
1269 }
1270 gDebugFlag1 = false;
1271#endif
1272 break;
1273 }
1275 doLoop = false;
1276#ifdef DEBUG_GUESS_ROUNDABOUT
1277 if (gDebugFlag1) {
1278 std::cout << " disabled\n";
1279 }
1280 gDebugFlag1 = false;
1281#endif
1282 break;
1283 }
1284 if (edges.size() < 2) {
1285 doLoop = false;
1286#ifdef DEBUG_GUESS_ROUNDABOUT
1287 if (gDebugFlag1) {
1288 std::cout << " deadend\n";
1289 }
1290 gDebugFlag1 = false;
1291#endif
1292 break;
1293 }
1294 if (e->getTurnDestination(true) != nullptr || e->getToNode()->getConnectionTo(e->getFromNode()) != nullptr) {
1295 // do not follow turn-arounds while in a (tentative) loop
1296 doLoop = false;
1297#ifdef DEBUG_GUESS_ROUNDABOUT
1298 if (gDebugFlag1) {
1299 std::cout << " invalid turnAround e=" << e->getID() << " dest=" << Named::getIDSecure(e->getTurnDestination()) << "\n";
1300 }
1301 gDebugFlag1 = false;
1302#endif
1303 break;
1304 }
1305 EdgeVector::const_iterator me = std::find(edges.begin(), edges.end(), e);
1306 NBContHelper::nextCW(edges, me);
1307 NBEdge* left = *me;
1308 while ((left->getPermissions() & valid) == 0 && left != e) {
1309 NBContHelper::nextCW(edges, me);
1310 left = *me;
1311 }
1312 if (left == e) {
1313 // no usable continuation edge found
1314 doLoop = false;
1315#ifdef DEBUG_GUESS_ROUNDABOUT
1316 if (gDebugFlag1) {
1317 std::cout << " noContinuation\n";
1318 }
1319 gDebugFlag1 = false;
1320#endif
1321 break;
1322 }
1323 NBContHelper::nextCW(edges, me);
1324 NBEdge* nextLeft = *me;
1325 double angle = fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), left->getAngleAtNode(e->getToNode())));
1326 double nextAngle = nextLeft == e ? 180 : fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), nextLeft->getAngleAtNode(e->getToNode())));
1327#ifdef DEBUG_GUESS_ROUNDABOUT
1328 if (gDebugFlag1) {
1329 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";
1330 }
1331#endif
1332 // there should be no straigher edge further left
1333 if (angle >= 90 && nextAngle < 45) {
1334 doLoop = false;
1335#ifdef DEBUG_GUESS_ROUNDABOUT
1336 if (gDebugFlag1) {
1337 std::cout << " failed nextAngle=" << nextAngle << "\n";
1338 }
1339 gDebugFlag1 = false;
1340#endif
1341 break;
1342 }
1343 // roundabouts do not have sharp turns (or they wouldn't be called 'round')
1344 // 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 180 (for smooth attachments at a joined junction)
1345 if (angle >= 90) {
1346 double edgeAngle = fabs(NBHelpers::relAngle(e->getStartAngle(), e->getEndAngle()));
1347 double edgeAngle2 = fabs(NBHelpers::relAngle(left->getStartAngle(), left->getEndAngle()));
1348 double edgeRadius = e->getGeometry().length2D() / DEG2RAD(edgeAngle);
1349 double edgeRadius2 = left->getGeometry().length2D() / DEG2RAD(edgeAngle2);
1350 const double avgRadius = 0.5 * (edgeRadius + edgeRadius2);
1351 double junctionRadius = e->getLaneShape(0).back().distanceTo2D(left->getLaneShape(0).front()) / DEG2RAD(angle);
1352 //std::cout << " junction=" << e->getToNode()->getID() << " e=" << e->getID() << " left=" << left->getID() << " angle=" << angle << " eRadius=" << edgeRadius << " eRadius2=" << edgeRadius2 << " jRadius3=" << junctionRadius << "\n";
1353 if (junctionRadius < 0.8 * avgRadius) {
1354 doLoop = false;
1355#ifdef DEBUG_GUESS_ROUNDABOUT
1356 if (gDebugFlag1) {
1357 std::cout << " failed angle=" << angle << " eRadius=" << edgeRadius << " eRadius2=" << edgeRadius2 << " jRadius3=" << junctionRadius << "\n";
1358 }
1359 gDebugFlag1 = false;
1360#endif
1361 break;
1362 }
1363 }
1364
1365 EdgeVector::const_iterator loopClosed = std::find(loopEdges.begin(), loopEdges.end(), left);
1366 const int loopSize = (int)(loopEdges.end() - loopClosed);
1367 if (loopSize > 0) {
1368 // loop found
1369 if (loopSize < 3) {
1370 doLoop = false; // need at least 3 edges for a roundabout
1371 } else if (loopSize < (int)loopEdges.size()) {
1372 // remove initial edges not belonging to the loop
1373 EdgeVector(loopEdges.begin() + (loopEdges.size() - loopSize), loopEdges.end()).swap(loopEdges);
1374 }
1375 // count attachments to the outside. need at least 3 or a roundabout doesn't make much sense
1376 int attachments = 0;
1377 for (EdgeVector::const_iterator j = loopEdges.begin(); j != loopEdges.end(); ++j) {
1378 if ((*j)->getToNode()->getEdges().size() > 2) {
1379 attachments++;
1380 }
1381 }
1382 if (attachments < 3) {
1383 doLoop = false;
1384#ifdef DEBUG_GUESS_ROUNDABOUT
1385 if (gDebugFlag1) {
1386 std::cout << " attachments=" << attachments << "\n";
1387 }
1388 gDebugFlag1 = false;
1389#endif
1390 }
1391 break;
1392 }
1393 if (visited.count(left) > 0) {
1394 doLoop = false;
1395 } else {
1396 // keep going
1397 loopEdges.push_back(left);
1398 e = left;
1399 }
1400 } while (doLoop);
1401 if (doLoop) {
1402 // check form factor to avoid elongated shapes (circle: 1, square: ~0.79)
1403#ifdef DEBUG_GUESS_ROUNDABOUT
1404 if (gDebugFlag1) {
1405 std::cout << " formFactor=" << formFactor(loopEdges) << "\n";
1406 }
1407#endif
1408 double loopLength = 0;
1409 for (const NBEdge* const le : loopEdges) {
1410 loopLength += le->getLoadedLength();
1411 }
1412 if (formFactor(loopEdges) > 0.6
1413 && loopLength < OptionsCont::getOptions().getFloat("roundabouts.guess.max-length")) {
1414 // collected edges are marked in markRoundabouts
1415 EdgeSet guessed(loopEdges.begin(), loopEdges.end());
1416 if (loadedRoundaboutEdges.count(loopEdges.front()) != 0) {
1417 if (find(myRoundabouts.begin(), myRoundabouts.end(), guessed) == myRoundabouts.end()) {
1418 for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); it++) {
1419 if ((*it).count(loopEdges.front()) != 0) {
1420 WRITE_WARNINGF(TL("Replacing loaded roundabout '%' with '%'."), toString(*it), toString(guessed));
1421 myRoundabouts.erase(it);
1422 break;
1423 }
1424 }
1425 myGuessedRoundabouts.insert(guessed);
1426 }
1427 } else {
1428 myGuessedRoundabouts.insert(guessed);
1429#ifdef DEBUG_GUESS_ROUNDABOUT
1430 if (gDebugFlag1) {
1431 std::cout << " foundRoundabout=" << toString(loopEdges) << "\n";
1432 }
1433#endif
1434 }
1435 }
1436 }
1437#ifdef DEBUG_GUESS_ROUNDABOUT
1438 gDebugFlag1 = false;
1439#endif
1440 }
1441 return (int)myGuessedRoundabouts.size();
1442}
1443
1444
1445int
1447 std::set<NBEdge*> candidateEdges;
1448 for (const auto& edge : myEdges) {
1449 NBEdge* const e = edge.second;
1451 candidateEdges.insert(e);
1452 }
1453 }
1454 std::set<NBEdge*> visited;
1455 int extracted = 0;
1456 for (const auto& edgeIt : candidateEdges) {
1457 EdgeVector loopEdges;
1458 NBEdge* e = edgeIt;
1459 if (visited.count(e) > 0) {
1460 // already seen
1461 continue;
1462 }
1463 loopEdges.push_back(e);
1464 bool doLoop = true;
1465 //
1466 do {
1467 if (std::find(visited.begin(), visited.end(), e) != visited.end()) {
1468 if (loopEdges.size() > 1) {
1469 addRoundabout(EdgeSet(loopEdges.begin(), loopEdges.end()));
1470 ++extracted;
1471 }
1472 doLoop = false;
1473 break;
1474 }
1475 visited.insert(e);
1476 loopEdges.push_back(e);
1477 const EdgeVector& outgoingEdges = e->getToNode()->getOutgoingEdges();
1478 EdgeVector::const_iterator me = std::find_if(outgoingEdges.begin(), outgoingEdges.end(), [](const NBEdge * outgoingEdge) {
1479 return outgoingEdge->getJunctionPriority(outgoingEdge->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT;
1480 });
1481 if (me == outgoingEdges.end()) { // no closed loop
1482 doLoop = false;
1483 } else {
1484 e = *me;
1485 }
1486 } while (doLoop);
1487 }
1488 return extracted;
1489}
1490
1491
1492void
1494 // only loaded roundabouts are of concern here since guessing comes later
1495 std::set<EdgeSet> validRoundabouts;
1496 std::set<NBEdge*> validEdges;
1497 for (auto item : myEdges) {
1498 validEdges.insert(item.second);
1499 }
1500 for (EdgeSet roundabout : myRoundabouts) {
1501 EdgeSet validRoundabout;
1502 for (NBEdge* cand : roundabout) {
1503 if (validEdges.count(cand) != 0) {
1504 validRoundabout.insert(cand);
1505 }
1506 }
1507 if (validRoundabout.size() > 0) {
1508 validRoundabouts.insert(validRoundabout);
1509 }
1510 }
1511 myRoundabouts = validRoundabouts;
1512}
1513
1514
1515double
1517 // A circle (which maximizes area per circumference) has a formfactor of 1, non-circular shapes have a smaller value
1518 PositionVector points;
1519 for (EdgeVector::const_iterator it = loopEdges.begin(); it != loopEdges.end(); ++it) {
1520 points.append((*it)->getGeometry());
1521 }
1522 double circumference = points.length2D();
1523 return 4 * M_PI * points.area() / (circumference * circumference);
1524}
1525
1526
1527const std::set<EdgeSet>
1529 std::set<EdgeSet> result = myRoundabouts;
1530 result.insert(myGuessedRoundabouts.begin(), myGuessedRoundabouts.end());
1531 return result;
1532}
1533
1534
1535void
1537 if (roundabout.size() > 0) {
1538 if (find(myRoundabouts.begin(), myRoundabouts.end(), roundabout) != myRoundabouts.end()) {
1539 WRITE_WARNING("Ignoring duplicate roundabout: " + toString(roundabout));
1540 } else {
1541 myRoundabouts.insert(roundabout);
1542 }
1543 }
1544}
1545
1546void
1548 for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
1549 for (NBEdge* e : *it) {
1550 if (e->getToNode() == node) {
1551 myRoundabouts.erase(it);
1552 return;
1553 }
1554 }
1555 }
1556}
1557
1558void
1563
1564void
1565NBEdgeCont::removeRoundaboutEdges(const EdgeSet& toRemove, std::set<EdgeSet>& roundabouts) {
1566 // members of a set are constant so we have to do some tricks
1567 std::vector<EdgeSet> rList;
1568 for (const EdgeSet& r : roundabouts) {
1569 EdgeSet r2;
1570 std::set_difference(r.begin(), r.end(), toRemove.begin(), toRemove.end(), std::inserter(r2, r2.end()));
1571 rList.push_back(r2);
1572 }
1573 roundabouts.clear();
1574 roundabouts.insert(rList.begin(), rList.end());
1575}
1576
1577
1578void
1580 for (const EdgeSet& roundaboutSet : getRoundabouts()) {
1581 for (NBEdge* const edge : roundaboutSet) {
1582 // disable turnarounds on incoming edges
1583 NBNode* const node = edge->getToNode();
1584 for (NBEdge* const inEdge : node->getIncomingEdges()) {
1585 if (roundaboutSet.count(inEdge) > 0) {
1586 continue;
1587 }
1588 if (inEdge->getStep() >= NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
1589 continue;
1590 }
1591 if (inEdge->getTurnDestination() != nullptr) {
1592 inEdge->removeFromConnections(inEdge->getTurnDestination(), -1);
1593 } else {
1594 // also remove connections that are effecively a turnaround but
1595 // where not correctly detector due to geometrical quirks
1596 const std::vector<NBEdge::Connection> cons = inEdge->getConnections();
1597 for (const NBEdge::Connection& con : cons) {
1598 if (con.toEdge && roundaboutSet.count(con.toEdge) == 0) {
1599 const double angle = fabs(NBHelpers::normRelAngle(inEdge->getAngleAtNode(node), con.toEdge->getAngleAtNode(node)));
1600 if (angle > 160) {
1601 inEdge->removeFromConnections(con.toEdge, -1);
1602 }
1603 }
1604 }
1605 }
1606
1607 }
1608 // let the connections to succeeding roundabout edge have a higher priority
1609 edge->setJunctionPriority(node, NBEdge::JunctionPriority::ROUNDABOUT);
1610 edge->setJunctionPriority(edge->getFromNode(), NBEdge::JunctionPriority::ROUNDABOUT);
1611 node->setRoundabout();
1612 }
1613 }
1614}
1615
1616
1617void
1619 for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1620 NBEdge* e = i->second;
1621 const double offset = MAX2(0., e->getLength() - 3);
1622 if (e->getToNode()->isSimpleContinuation(false)) {
1623 // not a "real" junction?
1624 continue;
1625 }
1626 const SumoXMLNodeType nodeType = e->getToNode()->getType();
1627 switch (nodeType) {
1629 // yield or major?
1630 if (e->getJunctionPriority(e->getToNode()) > 0) {
1632 } else {
1634 }
1635 break;
1637 // yield or major?
1638 if (e->getJunctionPriority(e->getToNode()) > 0) {
1640 } else {
1642 }
1643 break;
1646 break;
1649 break;
1652 break;
1653 default:
1654 break;
1655 }
1656 }
1657}
1658
1659
1660int
1661NBEdgeCont::guessSpecialLanes(SUMOVehicleClass svc, double width, double minSpeed, double maxSpeed, bool fromPermissions, const std::string& excludeOpt,
1663 int lanesCreated = 0;
1664 std::vector<std::string> edges;
1665 if (excludeOpt != "") {
1666 edges = OptionsCont::getOptions().getStringVector(excludeOpt);
1667 }
1668 std::set<std::string> exclude(edges.begin(), edges.end());
1669 for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1670 NBEdge* edge = it->second;
1671 if (// not excluded
1672 exclude.count(edge->getID()) == 0
1673 // does not yet have a sidewalk/bikelane
1674 && !edge->hasRestrictedLane(svc)
1675 // needs a sidewalk/bikelane
1676 && ((edge->getPermissions() & ~SVC_VULNERABLE) != 0 || (edge->getPermissions() & svc) == 0)
1677 && (
1678 // guess.from-permissions
1679 (fromPermissions && (edge->getPermissions() & svc) != 0)
1680 // guess from speed
1681 || (!fromPermissions && edge->getSpeed() > minSpeed && edge->getSpeed() <= maxSpeed)
1682 )) {
1683 edge->addRestrictedLane(width, svc);
1684 lanesCreated += 1;
1685 if (svc != SVC_PEDESTRIAN) {
1687 // preserve existing connections and only add new ones
1690 for (NBEdge* to : edge->getToNode()->getOutgoingEdges()) {
1692 }
1693 // patching TLS is not feasible because existing states may
1694 // change from 'G' to 'g' when bike lanes are added (i.e. right-turns)
1695 } else {
1696 edge->invalidateConnections(true);
1698 }
1699 edge->getFromNode()->invalidateTLS(tlc, true, false);
1700 edge->getToNode()->invalidateTLS(tlc, true, false);
1701 }
1702 }
1703 }
1704 return lanesCreated;
1705}
1706
1707
1708void
1710 for (auto item : myEdges) {
1711 item.second->updateChangeRestrictions(ignoring);
1712 }
1713}
1714
1715
1716void
1717NBEdgeCont::addPrefix(const std::string& prefix) {
1718 // make a copy of node containers
1719 const auto nodeContainerCopy = myEdges;
1720 myEdges.clear();
1721 for (const auto& node : nodeContainerCopy) {
1722 node.second->setID(prefix + node.second->getID());
1723 myEdges[node.second->getID()] = node.second;
1724 }
1725}
1726
1727
1728int
1729NBEdgeCont::remapIDs(bool numericaIDs, bool reservedIDs, bool keptIDs, const std::string& prefix, NBPTStopCont& sc) {
1730 bool startGiven = !OptionsCont::getOptions().isDefault("numerical-ids.edge-start");
1731 if (!numericaIDs && !reservedIDs && prefix == "" && !startGiven) {
1732 return 0;
1733 }
1734 std::vector<std::string> avoid;
1735 if (startGiven) {
1736 avoid.push_back(toString(OptionsCont::getOptions().getInt("numerical-ids.edge-start") - 1));
1737 } else {
1738 avoid = getAllNames();
1739 }
1740 std::set<std::string> reserve;
1741 if (reservedIDs) {
1742 NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("reserved-ids"), "edge:", reserve);
1743 avoid.insert(avoid.end(), reserve.begin(), reserve.end());
1744 }
1745 IDSupplier idSupplier("", avoid);
1746 std::set<NBEdge*, ComparatorIdLess> toChange;
1747 for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1748 if (startGiven) {
1749 toChange.insert(it->second);
1750 continue;
1751 }
1752 if (numericaIDs) {
1753 try {
1754 StringUtils::toLong(it->first);
1755 } catch (NumberFormatException&) {
1756 toChange.insert(it->second);
1757 }
1758 }
1759 if (reservedIDs && reserve.count(it->first) > 0) {
1760 toChange.insert(it->second);
1761 }
1762 }
1763 std::set<std::string> keep;
1764 if (keptIDs) {
1765 NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("kept-ids"), "edge:", keep);
1766 for (auto it = toChange.begin(); it != toChange.end();) {
1767 if (keep.count((*it)->getID()) != 0) {
1768 toChange.erase(it++);
1769 } else {
1770 it++;
1771 }
1772 }
1773 }
1774
1775 std::map<std::string, std::vector<std::shared_ptr<NBPTStop> > > stopsOnEdge;
1776 for (const auto& item : sc.getStops()) {
1777 stopsOnEdge[item.second->getEdgeId()].push_back(item.second);
1778 }
1779
1780 const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
1781 for (NBEdge* edge : toChange) {
1782 myEdges.erase(edge->getID());
1783 }
1784 for (NBEdge* edge : toChange) {
1785 const std::string origID = edge->getID();
1786 if (origNames) {
1787 edge->setOrigID(origID, false);
1788 }
1789 edge->setID(idSupplier.getNext());
1790 myEdges[edge->getID()] = edge;
1791 for (std::shared_ptr<NBPTStop> stop : stopsOnEdge[origID]) {
1792 stop->setEdgeId(prefix + edge->getID(), *this);
1793 }
1794 }
1795 if (prefix.empty()) {
1796 return (int)toChange.size();
1797 } else {
1798 int renamed = 0;
1799 // make a copy because we will modify the map
1800 auto oldEdges = myEdges;
1801 for (auto item : oldEdges) {
1802 if (!StringUtils::startsWith(item.first, prefix) && keep.count(item.first) == 0) {
1803 rename(item.second, prefix + item.first);
1804 renamed++;
1805 }
1806 }
1807 return renamed;
1808 }
1809}
1810
1811
1812void
1813NBEdgeCont::checkOverlap(double threshold, double zThreshold) const {
1814 for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1815 const NBEdge* e1 = it->second;
1816 Boundary b1 = e1->getGeometry().getBoxBoundary();
1817 b1.grow(e1->getTotalWidth());
1818 PositionVector outline1 = e1->getCCWBoundaryLine(*e1->getFromNode());
1819 outline1.append(e1->getCCWBoundaryLine(*e1->getToNode()));
1820 // check is symmetric. only check once per pair
1821 for (EdgeCont::const_iterator it2 = it; it2 != myEdges.end(); it2++) {
1822 const NBEdge* e2 = it2->second;
1823 if (e1 == e2) {
1824 continue;
1825 }
1826 Boundary b2 = e2->getGeometry().getBoxBoundary();
1827 b2.grow(e2->getTotalWidth());
1828 if (b1.overlapsWith(b2)) {
1829 PositionVector outline2 = e2->getCCWBoundaryLine(*e2->getFromNode());
1830 outline2.append(e2->getCCWBoundaryLine(*e2->getToNode()));
1831 const double overlap = outline1.getOverlapWith(outline2, zThreshold);
1832 if (overlap > threshold) {
1833 WRITE_WARNINGF(TL("Edge '%' overlaps with edge '%' by %."), e1->getID(), e2->getID(), overlap);
1834 }
1835 }
1836 }
1837 }
1838}
1839
1840
1841void
1842NBEdgeCont::checkGrade(double threshold) const {
1843 for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1844 const NBEdge* edge = it->second;
1845 for (int i = 0; i < (int)edge->getNumLanes(); i++) {
1846 double maxJump = 0;
1847 const double grade = edge->getLaneShape(i).getMaxGrade(maxJump);
1848 if (maxJump > 0.01) {
1849 WRITE_WARNINGF(TL("Edge '%' has a vertical jump of %m."), edge->getID(), maxJump);
1850 } else if (grade > threshold) {
1851 WRITE_WARNINGF(TL("Edge '%' has a grade of %%."), edge->getID(), grade * 100, "%");
1852 break;
1853 }
1854 }
1855 const std::vector<NBEdge::Connection>& connections = edge->getConnections();
1856 for (std::vector<NBEdge::Connection>::const_iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
1857 const NBEdge::Connection& c = *it_con;
1858 double maxJump = 0;
1859 const double grade = MAX2(c.shape.getMaxGrade(maxJump), c.viaShape.getMaxGrade(maxJump));
1860 if (maxJump > 0.01) {
1861 WRITE_WARNINGF(TL("Connection '%' has a vertical jump of %m."), c.getDescription(edge), maxJump);
1862 } else if (grade > threshold) {
1863 WRITE_WARNINGF(TL("Connection '%' has a grade of %%."), c.getDescription(edge), grade * 100, "%");
1864 break;
1865 }
1866 }
1867 }
1868}
1869
1870
1871int
1873 int affectedEdges = 0;
1874 for (auto item : myEdges) {
1875 if (item.second->joinLanes(perms)) {
1876 affectedEdges++;
1877 }
1878 }
1879 return affectedEdges;
1880}
1881
1882
1883bool
1884NBEdgeCont::MinLaneComparatorIdLess::operator()(const std::pair<NBEdge*, int>& a, const std::pair<NBEdge*, int>& b) const {
1885 if (a.first->getID() == b.first->getID()) {
1886 return a.second < b.second;
1887 }
1888 return a.first->getID() < b.first->getID();
1889}
1890
1891int
1893 // this is different from joinSimilarEdges because there don't need to be
1894 // shared nodes and tram edges may be split
1895 std::vector<NBEdge*> tramEdges;
1896 std::vector<NBEdge*> targetEdges;
1897 for (auto item : myEdges) {
1898 SVCPermissions permissions = item.second->getPermissions();
1899 if (isTram(permissions)) {
1900 if (item.second->getNumLanes() == 1) {
1901 tramEdges.push_back(item.second);
1902 } else {
1903 WRITE_WARNINGF(TL("Not joining tram edge '%' with % lanes."), item.second->getID(), item.second->getNumLanes());
1904 }
1905 } else if ((permissions & (SVC_PASSENGER | SVC_BUS)) != 0) {
1906 targetEdges.push_back(item.second);
1907 }
1908 }
1909 if (tramEdges.empty() || targetEdges.empty()) {
1910 return 0;
1911 }
1912 int numJoined = 0;
1913 NamedRTree tramTree;
1914 for (NBEdge* const edge : tramEdges) {
1915 const Boundary& bound = edge->getGeometry().getBoxBoundary();
1916 float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1917 float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1918 tramTree.Insert(min, max, edge);
1919 }
1920 // {targetEdge, laneIndex : tramEdge}
1921 std::map<std::pair<NBEdge*, int>, NBEdge*, MinLaneComparatorIdLess> matches;
1922
1923 for (NBEdge* const edge : targetEdges) {
1924 Boundary bound = edge->getGeometry().getBoxBoundary();
1925 bound.grow(maxDist + edge->getTotalWidth());
1926 float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1927 float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1928 std::set<const Named*> near;
1929 Named::StoringVisitor visitor(near);
1930 tramTree.Search(min, max, visitor);
1931 // the nearby set is actually just re-sorting according to the id to make the tests comparable
1932 std::set<NBEdge*, ComparatorIdLess> nearby;
1933 for (const Named* namedEdge : near) {
1934 nearby.insert(const_cast<NBEdge*>(static_cast<const NBEdge*>(namedEdge)));
1935 }
1936 for (NBEdge* const tramEdge : nearby) {
1937 // find a continous stretch of tramEdge that runs along one of the lanes of the road edge
1938 PositionVector tramShape = tramEdge->getGeometry();
1939 if (tramEdge->getToNode() == edge->getToNode()) {
1940 tramShape.extrapolate(tramShape.back().distanceTo2D(edge->getGeometry().back()), false, true);
1941 }
1942 double minEdgeDist = maxDist + 1;
1943 int minLane = -1;
1944 // find the lane where the maximum distance from the tram geometry
1945 // is minimal and within maxDist
1946 for (int i = 0; i < edge->getNumLanes(); i++) {
1947 double maxLaneDist = -1;
1948 if ((edge->getPermissions(i) & (SVC_PASSENGER | SVC_BUS)) != 0) {
1949 const PositionVector& laneShape = edge->getLaneShape(i);
1950 for (Position pos : laneShape) {
1951 const double dist = tramShape.distance2D(pos, false);
1952#ifdef DEBUG_JOIN_TRAM
1953 //if (edge->getID() == "106838214#1") {
1954 // std::cout << " edge=" << edge->getID() << " tramEdge=" << tramEdge->getID() << " lane=" << i << " pos=" << pos << " dist=" << dist << "\n";
1955 //}
1956#endif
1957 if (dist == GeomHelper::INVALID_OFFSET || dist > maxDist) {
1958 maxLaneDist = -1;
1959 break;
1960 }
1961 maxLaneDist = MAX2(maxLaneDist, dist);
1962 }
1963 if (maxLaneDist >= 0 && maxLaneDist < minEdgeDist) {
1964 minEdgeDist = maxLaneDist;
1965 minLane = i;
1966 }
1967 }
1968 }
1969 if (minLane >= 0) {
1970 // edge could run in the wrong direction and still fit the threshold we check the angle as well
1971 const PositionVector& laneShape = edge->getLaneShape(minLane);
1972 const double offset1 = tramShape.nearest_offset_to_point2D(laneShape.front(), false);
1973 const double offset2 = tramShape.nearest_offset_to_point2D(laneShape.back(), false);
1974 Position p1 = tramShape.positionAtOffset2D(offset1);
1975 Position p2 = tramShape.positionAtOffset2D(offset2);
1976 double tramAngle = GeomHelper::legacyDegree(p1.angleTo2D(p2), true);
1977 bool angleOK = GeomHelper::getMinAngleDiff(tramAngle, edge->getTotalAngle()) < JOIN_TRAM_MAX_ANGLE;
1978 if (angleOK && offset2 > offset1) {
1979 std::pair<NBEdge*, int> key = std::make_pair(edge, minLane);
1980 if (matches.count(key) == 0) {
1981 matches[key] = tramEdge;
1982 } else {
1983 WRITE_WARNINGF(TL("Ambiguous tram edges '%' and '%' for lane '%'."), matches[key]->getID(), tramEdge->getID(), edge->getLaneID(minLane));
1984 }
1985#ifdef DEBUG_JOIN_TRAM
1986 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";
1987#endif
1988 }
1989 }
1990 }
1991 }
1992 if (matches.size() == 0) {
1993 return 0;
1994 }
1995 const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
1996 // find continous runs of matched edges for each tramEdge
1997 for (NBEdge* tramEdge : tramEdges) {
1998 std::vector<std::pair<double, std::pair<NBEdge*, int> > > roads;
1999 for (auto item : matches) {
2000 if (item.second == tramEdge) {
2001 NBEdge* road = item.first.first;
2002 int laneIndex = item.first.second;
2003 const PositionVector& laneShape = road->getLaneShape(laneIndex);
2004 double tramPos = tramEdge->getGeometry().nearest_offset_to_point2D(laneShape.front(), false);
2005 //std::cout << " road=" << road->getID() << " tramEdge=" << tramEdge->getID() << " tramShape=" << tramEdge->getGeometry() << " laneFront=" << laneShape.front() << " tramPos=" << tramPos << "\n";
2006 roads.push_back(std::make_pair(tramPos, item.first));
2007 }
2008 }
2009 if (roads.size() != 0) {
2010
2011 sort(roads.begin(), roads.end());
2012#ifdef DEBUG_JOIN_TRAM
2013 std::cout << " tramEdge=" << tramEdge->getID() << " roads=";
2014 for (auto item : roads) {
2015 std::cout << item.second.first->getLaneID(item.second.second) << ",";
2016 }
2017 std::cout << " offsets=";
2018 for (auto item : roads) {
2019 std::cout << item.first << ",";
2020 }
2021 std::cout << "\n";
2022#endif
2023 // merge tramEdge into road lanes
2024 EdgeVector replacement;
2025 double pos = 0;
2026 int tramPart = 0;
2027 std::string tramEdgeID = tramEdge->getID();
2028 NBNode* tramFrom = tramEdge->getFromNode();
2029 PositionVector tramShape = tramEdge->getGeometry();
2030 const double tramLength = tramShape.length();
2031 EdgeVector incoming = tramFrom->getIncomingEdges();
2032 bool erasedLast = false;
2033 for (const auto& item : roads) {
2034 const double gap = item.first - pos;
2035 NBEdge* road = item.second.first;
2036 int laneIndex = item.second.second;
2037 if (gap >= JOIN_TRAM_MIN_LENGTH && road->getFromNode() != tramEdge->getFromNode()) {
2038#ifdef DEBUG_JOIN_TRAM
2039 std::cout << " splitting tramEdge=" << tramEdge->getID() << " at " << item.first << " (gap=" << gap << ")\n";
2040#endif
2041 const std::string firstPartID = tramEdgeID + "#" + toString(tramPart++);
2042 splitAt(dc, tramEdge, gap, road->getFromNode(), firstPartID, tramEdgeID, 1, 1);
2043 tramEdge = retrieve(tramEdgeID); // second part;
2044 NBEdge* firstPart = retrieve(firstPartID);
2045 firstPart->invalidateConnections(true);
2046 incoming.clear();
2047 incoming.push_back(firstPart);
2048 replacement.push_back(firstPart);
2049 }
2050 pos = item.first + road->getGeometry().length();
2051 numJoined++;
2052 replacement.push_back(road);
2053 // merge section of tramEdge into road lane
2054 if (road->getToNode() != tramEdge->getToNode() && (tramLength - pos) >= JOIN_TRAM_MIN_LENGTH) {
2055 tramEdge->reinitNodes(road->getToNode(), tramEdge->getToNode());
2056 tramEdge->setGeometry(tramShape.getSubpart(pos, tramShape.length()));
2057 erasedLast = false;
2058#ifdef DEBUG_JOIN_TRAM
2059 std::cout << " shorted tramEdge=" << tramEdge->getID() << " (joined with roadEdge=" << road->getID() << "\n";
2060#endif
2061 } else {
2062#ifdef DEBUG_JOIN_TRAM
2063 std::cout << " erased tramEdge=" << tramEdge->getID() << "\n";
2064#endif
2065 extract(dc, tramEdge, true);
2066 erasedLast = true;
2067 }
2068 road->setPermissions(road->getPermissions(laneIndex) | SVC_TRAM, laneIndex);
2069 if (origNames) {
2070 road->setOrigID(tramEdgeID, true, laneIndex);
2071 }
2072 for (NBEdge* in : incoming) {
2073 if (isTram(in->getPermissions()) && !in->isConnectedTo(road)) {
2074 if (in->getFromNode() != road->getFromNode()) {
2075 in->reinitNodes(in->getFromNode(), road->getFromNode());
2076 } else {
2077 extract(dc, in, true);
2078#ifdef DEBUG_JOIN_TRAM
2079 std::cout << " erased incoming tramEdge=" << in->getID() << "\n";
2080#endif
2081 }
2082 }
2083 }
2084 incoming.clear();
2085 }
2086 NBEdge* lastRoad = roads.back().second.first;
2087 if (erasedLast) {
2088 // copy to avoid concurrent modification
2089 auto outEdges = tramEdge->getToNode()->getOutgoingEdges();
2090 for (NBEdge* out : outEdges) {
2091 if (isTram(out->getPermissions()) && !lastRoad->isConnectedTo(out)) {
2092 if (lastRoad->getToNode() != out->getToNode()) {
2093 out->reinitNodes(lastRoad->getToNode(), out->getToNode());
2094 } else {
2095 extract(dc, out, true);
2096#ifdef DEBUG_JOIN_TRAM
2097 std::cout << " erased outgoing tramEdge=" << out->getID() << "\n";
2098#endif
2099
2100 }
2101 }
2102 }
2103 } else {
2104 replacement.push_back(tramEdge);
2105 }
2106 // update ptstops and ptlines
2107 sc.replaceEdge(tramEdgeID, replacement);
2108 lc.replaceEdge(tramEdgeID, replacement);
2109 }
2110 }
2111
2112 return numJoined;
2113}
2114
2115
2118 EdgeVector result;
2119 for (auto item : myEdges) {
2120 item.second->setNumericalID((int)result.size());
2121 result.push_back(item.second);
2122 }
2123 return result;
2124}
2125
2128 EdgeVector all = getAllEdges();
2129 return RouterEdgeVector(all.begin(), all.end());
2130}
2131
2132bool
2134 bool ok = true;
2135 for (const auto& item : myEdges) {
2136 NBEdge* e = item.second;
2137 if (nc.retrieve(e->getFromNode()->getID()) == nullptr) {
2138 WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), e->getID(), e->getFromNode()->getID());
2139 ok = false;
2140 }
2141 if (nc.retrieve(e->getToNode()->getID()) == nullptr) {
2142 WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), e->getID(), e->getToNode()->getID());
2143 ok = false;
2144 }
2145
2146 }
2147 return ok;
2148}
2149
2150
2151void
2153 for (auto item : myEdges) {
2154 NBEdge* e = item.second;
2155 if (e->hasLoadedLength() && myWasSplit.count(e) != 0) {
2156 // subtract half the length of the longest incoming / outgoing connection
2157 double maxLengthOut = 0;
2158 for (const NBEdge::Connection& c : e->getConnections()) {
2159 maxLengthOut = MAX2(maxLengthOut, c.length + c.viaLength);
2160 }
2161 double maxLengthIn = 0;
2162 for (const NBEdge* in : e->getIncomingEdges()) {
2163 for (const NBEdge::Connection& c : in->getConnectionsFromLane(-1, e, -1)) {
2164 maxLengthIn = MAX2(maxLengthIn, c.length + c.viaLength);
2165 }
2166 }
2167 e->setLoadedLength(MAX2(POSITION_EPS, e->getLoadedLength() - (maxLengthIn + maxLengthOut) / 2));
2168 }
2169 }
2170}
2171
2172void
2174 for (auto item : myEdges) {
2175 item.second->computeAngle();
2176 }
2177}
2178
2179
2180std::set<std::string>
2182 std::set<std::string> result;
2183 for (auto item : myEdges) {
2184 if (item.second->getTypeID() != "") {
2185 result.insert(item.second->getTypeID());
2186 }
2187 }
2188 return result;
2189}
2190
2191
2192int
2194 EdgeSet toRemove;
2195 for (auto item : myEdges) {
2196 NBEdge* edge = item.second;
2197 // remove edges which allow a speed below a set one (set using "keep-edges.min-speed")
2198 if (edge->getSpeed() < myEdgesMinSpeed) {
2199 toRemove.insert(edge);
2200 }
2201 }
2202 int numRemoved = 0;
2203 for (NBEdge* edge : toRemove) {
2204 // explicit whitelist overrides removal
2205 if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
2206 extract(dc, edge);
2207 numRemoved++;
2208 }
2209 }
2210 return numRemoved;
2211}
2212
2213
2214int
2216 EdgeSet toRemove;
2217 for (auto item : myEdges) {
2218 NBEdge* edge = item.second;
2219 // check whether the edge shall be removed because it does not allow any of the wished classes
2220 if (myVehicleClasses2Keep != 0 && (myVehicleClasses2Keep & edge->getPermissions()) == 0) {
2221 toRemove.insert(edge);
2222 }
2223 // check whether the edge shall be removed due to allowing unwished classes only
2225 toRemove.insert(edge);
2226 }
2227 }
2228 int numRemoved = 0;
2229 for (NBEdge* edge : toRemove) {
2230 // explicit whitelist overrides removal
2231 if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
2232 extract(dc, edge);
2233 numRemoved++;
2234 }
2235 }
2236 return numRemoved;
2237}
2238
2239
2240int
2242 EdgeSet toRemove;
2243 for (auto item : myEdges) {
2244 NBEdge* const edge = item.second;
2245 std::vector<int> indices;
2246 int idx = 0;
2247 for (const auto& lane : edge->getLanes()) {
2248 if (lane.width != NBEdge::UNSPECIFIED_WIDTH && lane.width < minWidth) {
2249 indices.push_back(idx);
2250 }
2251 idx++;
2252 }
2253 if ((int)indices.size() == edge->getNumLanes()) {
2254 toRemove.insert(edge);
2255 } else {
2256 std::reverse(indices.begin(), indices.end());
2257 for (const int i : indices) {
2258 edge->deleteLane(i, false, true);
2259 }
2260 }
2261 }
2262 int numRemoved = 0;
2263 for (NBEdge* edge : toRemove) {
2264 // explicit whitelist overrides removal
2265 if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
2266 extract(dc, edge);
2267 numRemoved++;
2268 }
2269 }
2270 return numRemoved;
2271}
2272
2273
2274int
2275NBEdgeCont::attachRemoved(NBNodeCont& nc, NBDistrictCont& dc, const double maxDist) {
2276 int numSplit = 0;
2277 std::map<std::string, std::vector<std::string> > node2edge;
2278 for (auto item : myEdges) {
2279 if (item.second->hasParameter(SUMO_PARAM_REMOVED_NODES)) {
2280 for (std::string& nodeID : StringTokenizer(item.second->getParameter(SUMO_PARAM_REMOVED_NODES)).getVector()) {
2281 node2edge[nodeID].push_back(item.first);
2282 }
2283 }
2284 }
2285 for (auto item : nc) {
2286 NBNode* n = item.second;
2287 auto itRN = node2edge.find(n->getID());
2288 if (itRN != node2edge.end()) {
2289 bool rebuildConnections = false;
2290 // make a copy because we modify the original
2291 std::vector<std::string> edgeIDs = itRN->second;
2292 for (const std::string& eID : edgeIDs) {
2293 NBEdge* edge = retrieve(eID);
2294 assert(edge != nullptr);
2295 const double dist = edge->getGeometry().distance2D(n->getPosition(), true);
2296 if (dist != GeomHelper::INVALID_OFFSET && dist <= maxDist) {
2297 std::string idAfter = edge->getID();
2298 int index = 1;
2299 size_t spos = idAfter.find("#");
2300 if (spos != std::string::npos && spos > 1) {
2301 idAfter = idAfter.substr(0, spos);
2302 }
2303 while (retrieve(idAfter + "#" + toString(index), true) != nullptr) {
2304 index++;
2305 }
2306 idAfter += "#" + toString(index);
2307 const bool ok = splitAt(dc, edge, n, edge->getID(), idAfter, edge->getNumLanes(), edge->getNumLanes());
2308 if (ok) {
2309 rebuildConnections = true;
2310 numSplit++;
2311 NBEdge* secondEdge = retrieve(eID); // original was extracted on splitting
2312 for (std::string& nodeID : StringTokenizer(secondEdge->getParameter(SUMO_PARAM_REMOVED_NODES)).getVector()) {
2313 node2edge[nodeID].push_back(idAfter);
2314 }
2315 }
2316 }
2317 }
2318 if (rebuildConnections) {
2319 for (NBEdge* e : n->getIncomingEdges()) {
2320 e->invalidateConnections(true);
2321 }
2322 }
2323 }
2324 }
2325 return numSplit;
2326}
2327
2328/****************************************************************************/
#define DEG2RAD(x)
Definition GeomHelper.h:35
std::vector< std::string > & split(const std::string &s, char delim, std::vector< std::string > &elems)
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:287
#define WRITE_ERRORF(...)
Definition MsgHandler.h:296
#define WRITE_ERROR(msg)
Definition MsgHandler.h:295
#define WRITE_WARNING(msg)
Definition MsgHandler.h:286
#define TL(string)
Definition MsgHandler.h:304
#define TLF(string,...)
Definition MsgHandler.h:306
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 (exclusive) railway edge.
bool isTram(SVCPermissions permissions)
Returns whether an edge with the given permissions is a tram edge.
SVCPermissions parseVehicleClasses(const std::string &allowedS)
Parses the given definition of allowed vehicle classes into the given containers Deprecated classes g...
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_PUBLIC_CLASSES
public transport
@ SVC_BUS
vehicle is a bus
@ SVC_PEDESTRIAN
pedestrian
const std::string SUMO_PARAM_ORIGID
const std::string SUMO_PARAM_REMOVED_NODES
SumoXMLNodeType
Numbers representing special SUMO-XML-attribute values for representing node- (junction-) types used ...
bool gDebugFlag1
global utility flags for debugging
Definition StdDefs.cpp:43
const double SUMO_const_laneWidth
Definition StdDefs.h:52
T MIN2(T a, T b)
Definition StdDefs.h:80
const double SUMO_const_haltingSpeed
the speed threshold at which vehicles are considered as halting
Definition StdDefs.h:62
T MAX2(T a, T b)
Definition StdDefs.h:86
const double SUMO_const_halfLaneWidth
Definition StdDefs.h:53
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:127
double xmin() const
Returns minimum x-coordinate.
Definition Boundary.cpp:115
Boundary & grow(double by)
extends the boundary by the given amount
Definition Boundary.cpp:340
bool overlapsWith(const AbstractPoly &poly, double offset=0) const
Returns whether the boundary overlaps with the given polygon.
Definition Boundary.cpp:194
double ymax() const
Returns maximum y-coordinate.
Definition Boundary.cpp:133
double xmax() const
Returns maximum x-coordinate.
Definition Boundary.cpp:121
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:802
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:790
~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:794
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:772
std::set< std::string > myIgnoredEdges
The ids of ignored edges.
Definition NBEdgeCont.h:752
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:763
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:781
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:746
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:757
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:743
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:769
NBTypeCont & myTypeCont
The network builder; used to obtain type information.
Definition NBEdgeCont.h:633
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:797
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:784
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:787
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:778
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:775
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:740
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:755
NBEdge * getSplitBase(const std::string &edgeID) const
std::map< std::string, std::vector< PostProcessConnection > > myConnections
The list of connections to recheck.
Definition NBEdgeCont.h:737
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:749
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
int remapIDs(bool numericaIDs, bool reservedIDs, bool keptIDs, const std::string &prefix, NBPTStopCont &sc)
remap node IDs accoring to options –numerical-ids and –reserved-ids
void checkGrade(double threshold) const
check whether edges are to steep
int attachRemoved(NBNodeCont &nc, NBDistrictCont &dc, const double maxDist)
return number of edges split
int removeEdgesByPermissions(NBDistrictCont &dc)
bool myRemoveEdgesAfterLoading
Whether edges shall be joined and patched first, then removed.
Definition NBEdgeCont.h:766
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:5058
double getLength() const
Returns the computed length of the edge.
Definition NBEdge.h:599
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition NBEdge.cpp:4540
const std::vector< Connection > & getConnections() const
Returns the connections.
Definition NBEdge.h:1047
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:4503
@ 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:608
void recheckOpposite(const NBEdgeCont &ec, bool fixOppositeLengths)
recheck whether all opposite and bidi settings are consistent
Definition NBEdge.cpp:3198
void setSpeed(int lane, double speed)
set lane specific speed (negative lane implies set for all lanes)
Definition NBEdge.cpp:4455
NBNode * getToNode() const
Returns the destination node of the edge.
Definition NBEdge.h:552
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:3958
static const double UNSPECIFIED_FRICTION
unspecified lane friction
Definition NBEdge.h:355
void incLaneNo(int by)
increment lane
Definition NBEdge.cpp:4236
Lane & getLaneStruct(int lane)
Definition NBEdge.h:1451
void setAverageLengthWithOpposite(double val)
patch average lane length in regard to the opposite edge
Definition NBEdge.cpp:4560
const PositionVector & getGeometry() const
Returns the geometry of the edge.
Definition NBEdge.h:789
LaneSpreadFunction getLaneSpreadFunction() const
Returns how this edge's lanes' lateral offset is computed.
Definition NBEdge.cpp:1021
bool isBidiRail(bool ignoreSpread=false) const
whether this edge is part of a bidirectional railway
Definition NBEdge.cpp:772
void dismissVehicleClassInformation()
dimiss vehicle class information
Definition NBEdge.cpp:4566
EdgeBuildingStep getStep() const
The building step of this edge.
Definition NBEdge.h:641
LaneSpreadFunction myLaneSpreadFunction
The information about how to spread the lanes.
Definition NBEdge.h:1814
bool hasLoadedLength() const
Returns whether a length was set explicitly.
Definition NBEdge.h:618
bool addEdge2EdgeConnection(NBEdge *dest, bool overrideRemoval=false, SVCPermissions permission=SVC_UNSPECIFIED)
Adds a connection to another edge.
Definition NBEdge.cpp:1120
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:1156
void setDistance(double distance)
set kilometrage at start of edge (negative value implies couting down along the edge)
Definition NBEdge.h:1425
const std::vector< NBEdge::Lane > & getLanes() const
Returns the lane definitions.
Definition NBEdge.h:736
@ LANES2LANES_DONE
Lanes to lanes - relationships are computed; no recheck is necessary/wished.
@ 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:625
NBNode * myTo
Definition NBEdge.h:1763
const std::string & getID() const
Definition NBEdge.h:1551
double getDistance() const
get distance
Definition NBEdge.h:685
void setLaneWidth(int lane, double width)
set lane specific width (negative lane implies set for all lanes)
Definition NBEdge.cpp:4326
void setLaneSpreadFunction(LaneSpreadFunction spread)
(Re)sets how the lanes lateral offset shall be computed
Definition NBEdge.cpp:1015
std::vector< Lane > myLanes
Lane information.
Definition NBEdge.h:1831
int getNumLanes() const
Returns the number of lanes.
Definition NBEdge.h:526
void setFriction(int lane, double friction)
set lane specific friction (negative lane implies set for all lanes)
Definition NBEdge.cpp:4471
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:4768
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:1471
void invalidateConnections(bool reallowSetting=false)
invalidate current connections of edge
Definition NBEdge.cpp:1552
double getTotalWidth() const
Returns the combined width of all lanes of this edge.
Definition NBEdge.cpp:4378
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:1354
void addSign(NBSign sign)
add Sign
Definition NBEdge.h:1483
void deleteLane(int index, bool recompute, bool shiftIndices)
delete lane
Definition NBEdge.cpp:4247
void moveOutgoingConnectionsFrom(NBEdge *e, int laneOff)
move outgoing connection
Definition NBEdge.cpp:3834
std::string getLaneID(int lane) const
get lane ID
Definition NBEdge.cpp:4178
@ USER
The connection was given by the user.
@ VALIDATED
The connection was computed and validated.
@ COMPUTED
The connection was computed.
double getStartAngle() const
Returns the angle at the start of the edge (relative to the node shape center) The angle is computed ...
Definition NBEdge.h:561
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:1209
int getJunctionPriority(const NBNode *const node) const
Returns the junction priority (normalised for the node currently build)
Definition NBEdge.cpp:2157
const std::string & getTypeID() const
get ID of type
Definition NBEdge.h:1187
const std::string & getStreetName() const
Returns the street name of this edge.
Definition NBEdge.h:675
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition NBEdge.h:545
NBEdge * getTurnDestination(bool possibleDestination=false) const
Definition NBEdge.cpp:4169
double getAngleAtNode(const NBNode *const node) const
Returns the angle of the edge's geometry at the given node.
Definition NBEdge.cpp:2183
bool isBidi() const
return whether this edge should be a bidi edge
Definition NBEdge.h:1435
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:4757
void copyConnectionsFrom(NBEdge *src)
copy connections from antoher edge
Definition NBEdge.cpp:1667
double getEndAngle() const
Returns the angle at the end of the edge (relative to the node shape center) The angle is computed in...
Definition NBEdge.h:570
void declareConnectionsAsLoaded(EdgeBuildingStep step=EdgeBuildingStep::LANES2LANES_USER)
declares connections as fully loaded. This is needed to avoid recomputing connections if an edge has ...
Definition NBEdge.h:1465
void setEndOffset(int lane, double offset)
set lane specific end-offset (negative lane implies set for all lanes)
Definition NBEdge.cpp:4409
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:2954
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:4906
const PositionVector & getLaneShape(int i) const
Returns the shape of the nth lane.
Definition NBEdge.cpp:1009
void setLoadedLength(double val)
set loaded length
Definition NBEdge.cpp:4555
void decLaneNo(int by)
decrement lane
Definition NBEdge.cpp:4267
NBNode * myFrom
The source and the destination node.
Definition NBEdge.h:1763
void setGeometry(const PositionVector &g, bool inner=false)
(Re)sets the edge's geometry
Definition NBEdge.cpp:660
EdgeVector getIncomingEdges() const
Returns the list of incoming edges unsorted.
Definition NBEdge.cpp:1416
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:2136
void removeEdge(NBEdge *edge, bool removeFromConnections=true)
Removes edge from this node and optionally removes connections as well.
Definition NBNode.cpp:2067
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:345
bool isSimpleContinuation(bool checkLaneNumbers=true, bool checkWidth=false) const
check if node is a simple continuation
Definition NBNode.cpp:553
void recheckVClassConnections(NBEdge *currentOutgoing)
ensure connectivity for all vClasses
Definition NBNode.cpp:1540
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
void invalidateTLS(NBTrafficLightLogicCont &tlCont, bool addedConnections, bool removedConnections)
causes the traffic light to be computed anew
Definition NBNode.cpp:469
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:1852
void setRoundabout()
update the type of this node as a roundabout
Definition NBNode.cpp:4065
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:1888
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:857
RoundaboutType getRoundaboutType() const
Returns roundabout type.
Definition NBNode.h:310
void removeDoubleEdges()
remove duble edges
Definition NBNode.cpp:1956
NBEdge * getConnectionTo(NBNode *n) const
get connection to certain node
Definition NBNode.cpp:2801
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:283
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.
void extrapolate(const double val, const bool onlyFirst=false, const bool onlyLast=false)
extrapolate position vector
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...
Position positionAtOffset2D(double pos, double lateralOffset=0, bool extrapolateBeyond=false) const
Returns the position at the given length.
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 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
std::vector< std::string > getVector()
return vector of strings
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
std::string oppositeID
An opposite lane ID, if given.
Definition NBEdge.h:179
bool operator()(const std::pair< NBEdge *, int > &a, const std::pair< NBEdge *, int > &b) const
A structure representing a connection between two lanes.
Definition NBEdgeCont.h:638
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