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