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