Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file NBEdgeCont.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Sascha Krieg
18 : /// @author Michael Behrisch
19 : /// @date Tue, 20 Nov 2001
20 : ///
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>
32 : #include <utils/geom/GeoConvHelper.h>
33 : #include <utils/geom/GeomConvHelper.h>
34 : #include <utils/common/MsgHandler.h>
35 : #include <utils/common/ToString.h>
36 : #include <utils/common/StringUtils.h>
37 : #include <utils/common/IDSupplier.h>
38 : #include <utils/common/StringUtils.h>
39 : #include <utils/common/StringTokenizer.h>
40 : #include <utils/common/UtilExceptions.h>
41 : #include <utils/iodevices/OutputDevice.h>
42 : #include <utils/options/OptionsCont.h>
43 : #include "NBNetBuilder.h"
44 : #include "NBEdgeCont.h"
45 : #include "NBNodeCont.h"
46 : #include "NBPTLineCont.h"
47 : #include "NBPTStop.h"
48 : #include "NBHelpers.h"
49 : #include "NBCont.h"
50 : #include "NBTrafficLightLogicCont.h"
51 : #include "NBDistrictCont.h"
52 : #include "NBTypeCont.h"
53 :
54 : #define JOIN_TRAM_MAX_ANGLE 10
55 : #define JOIN_TRAM_MIN_LENGTH 3
56 :
57 : //#define DEBUG_GUESS_ROUNDABOUT
58 : //#define DEBUG_JOIN_TRAM
59 : #define DEBUG_EDGE_ID ""
60 :
61 : // ===========================================================================
62 : // method definitions
63 : // ===========================================================================
64 2141 : NBEdgeCont::NBEdgeCont(NBTypeCont& tc) :
65 2141 : myTypeCont(tc),
66 2141 : myVehicleClasses2Keep(0),
67 2141 : myVehicleClasses2Remove(0),
68 2141 : myNeedGeoTransformedPruningBoundary(false) {
69 2141 : }
70 :
71 :
72 2141 : NBEdgeCont::~NBEdgeCont() {
73 2141 : clear();
74 2141 : }
75 :
76 :
77 : void
78 2141 : NBEdgeCont::applyOptions(OptionsCont& oc) {
79 : // set edges dismiss/accept options
80 2141 : myEdgesMinSpeed = oc.getFloat("keep-edges.min-speed");
81 4182 : myRemoveEdgesAfterLoading = oc.exists("keep-edges.postload") && oc.getBool("keep-edges.postload");
82 : // we possibly have to load the edges to keep/remove
83 4282 : if (oc.isSet("keep-edges.input-file")) {
84 4 : NBHelpers::loadEdgesFromFile(oc.getString("keep-edges.input-file"), myEdges2Keep);
85 : }
86 4282 : if (oc.isSet("remove-edges.input-file")) {
87 2 : NBHelpers::loadEdgesFromFile(oc.getString("remove-edges.input-file"), myEdges2Remove);
88 : }
89 4282 : if (oc.isSet("keep-edges.explicit")) {
90 22 : const std::vector<std::string> edges = oc.getStringVector("keep-edges.explicit");
91 : myEdges2Keep.insert(edges.begin(), edges.end());
92 11 : }
93 4282 : if (oc.isSet("remove-edges.explicit")) {
94 28 : const std::vector<std::string> edges = oc.getStringVector("remove-edges.explicit");
95 : myEdges2Remove.insert(edges.begin(), edges.end());
96 14 : }
97 4182 : if (oc.exists("keep-edges.by-vclass") && oc.isSet("keep-edges.by-vclass")) {
98 72 : myVehicleClasses2Keep = parseVehicleClasses(oc.getStringVector("keep-edges.by-vclass"));
99 : }
100 4182 : if (oc.exists("remove-edges.by-vclass") && oc.isSet("remove-edges.by-vclass")) {
101 39 : myVehicleClasses2Remove = parseVehicleClasses(oc.getStringVector("remove-edges.by-vclass"));
102 : }
103 4168 : if (oc.exists("keep-edges.by-type") && oc.isSet("keep-edges.by-type")) {
104 14 : const std::vector<std::string> types = oc.getStringVector("keep-edges.by-type");
105 : myTypes2Keep.insert(types.begin(), types.end());
106 7 : }
107 4168 : if (oc.exists("remove-edges.by-type") && oc.isSet("remove-edges.by-type")) {
108 18 : const std::vector<std::string> types = oc.getStringVector("remove-edges.by-type");
109 : myTypes2Remove.insert(types.begin(), types.end());
110 9 : }
111 :
112 4258 : if (oc.isSet("keep-edges.in-boundary") || oc.isSet("keep-edges.in-geo-boundary")) {
113 :
114 14 : std::string polyPlainString = oc.getValueString(oc.isSet("keep-edges.in-boundary") ?
115 18 : "keep-edges.in-boundary" : "keep-edges.in-geo-boundary");
116 : // try interpreting the boundary like shape attribute with spaces
117 14 : bool ok = true;
118 17 : PositionVector boundaryShape = GeomConvHelper::parseShapeReporting(polyPlainString, "pruning-boundary", 0, ok, false, false);
119 14 : if (ok) {
120 2 : if (boundaryShape.size() < 2) {
121 2 : throw ProcessError(TL("Invalid boundary: need at least 2 coordinates"));
122 1 : } else if (boundaryShape.size() == 2) {
123 : // prunning boundary (box)
124 0 : myPruningBoundary.push_back(boundaryShape[0]);
125 0 : myPruningBoundary.push_back(Position(boundaryShape[1].x(), boundaryShape[0].y()));
126 0 : myPruningBoundary.push_back(boundaryShape[1]);
127 0 : 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 28 : std::vector<std::string> polyS = oc.getStringVector(oc.isSet("keep-edges.in-boundary") ?
134 12 : "keep-edges.in-boundary" : "keep-edges.in-geo-boundary");
135 : std::vector<double> poly;
136 83 : for (std::vector<std::string>::iterator i = polyS.begin(); i != polyS.end(); ++i) {
137 72 : poly.push_back(StringUtils::toDouble((*i))); // !!! may throw something anyhow...
138 : }
139 11 : if (poly.size() < 4) {
140 0 : throw ProcessError(TL("Invalid boundary: need at least 2 coordinates"));
141 11 : } else if (poly.size() % 2 != 0) {
142 2 : throw ProcessError(TL("Invalid boundary: malformed coordinate"));
143 10 : } else if (poly.size() == 4) {
144 : // prunning boundary (box)
145 8 : myPruningBoundary.push_back(Position(poly[0], poly[1]));
146 8 : myPruningBoundary.push_back(Position(poly[2], poly[1]));
147 8 : myPruningBoundary.push_back(Position(poly[2], poly[3]));
148 8 : myPruningBoundary.push_back(Position(poly[0], poly[3]));
149 : } else {
150 19 : for (std::vector<double>::iterator j = poly.begin(); j != poly.end();) {
151 17 : double x = *j++;
152 17 : double y = *j++;
153 17 : myPruningBoundary.push_back(Position(x, y));
154 : }
155 : }
156 14 : }
157 11 : myNeedGeoTransformedPruningBoundary = oc.isSet("keep-edges.in-geo-boundary");
158 14 : }
159 2131 : }
160 :
161 :
162 : void
163 2141 : NBEdgeCont::clear() {
164 106019 : for (const auto& i : myEdges) {
165 103878 : delete i.second;
166 : }
167 : myEdges.clear();
168 11397 : for (const auto& i : myExtractedEdges) {
169 9256 : delete i.second;
170 : }
171 : myExtractedEdges.clear();
172 2161 : for (NBEdge* const e : myEdgeCemetery) {
173 20 : delete e;
174 : }
175 : myEdgeCemetery.clear();
176 2141 : }
177 :
178 :
179 :
180 : // ----- edge access methods
181 : bool
182 121276 : NBEdgeCont::insert(NBEdge* edge, bool ignorePrunning) {
183 121276 : if (myEdges.count(edge->getID()) != 0) {
184 0 : return false;
185 : }
186 121276 : if (!ignorePrunning && ignoreFilterMatch(edge)) {
187 7734 : edge->getFromNode()->removeEdge(edge);
188 7734 : edge->getToNode()->removeEdge(edge);
189 7734 : myIgnoredEdges.insert(edge->getID());
190 7734 : delete edge;
191 : } else {
192 113542 : OptionsCont& oc = OptionsCont::getOptions();
193 211665 : if (oc.exists("dismiss-vclasses") && oc.getBool("dismiss-vclasses")) {
194 200 : edge->dismissVehicleClassInformation();
195 : }
196 113542 : myEdges[edge->getID()] = edge;
197 : }
198 : return true;
199 : }
200 :
201 :
202 : bool
203 154395 : NBEdgeCont::ignoreFilterMatch(NBEdge* edge) {
204 154395 : if (!myRemoveEdgesAfterLoading) {
205 : // check whether the edge is a named edge to keep
206 154282 : if (myEdges2Keep.size() != 0) {
207 142 : if (myEdges2Keep.count(edge->getID()) == 0) {
208 : // explicit whitelisting may be combined additively with other filters
209 63 : if (myVehicleClasses2Keep == 0 && myVehicleClasses2Remove == 0
210 63 : && myTypes2Keep.size() == 0 && myTypes2Remove.size() == 0
211 104 : && myPruningBoundary.size() == 0) {
212 : return true;
213 : }
214 : } else {
215 : // explicit whitelisting overrides other filters
216 79 : return false;
217 : }
218 : }
219 : // remove edges which allow a speed below a set one (set using "keep-edges.min-speed")
220 154162 : if (edge->getSpeed() < myEdgesMinSpeed) {
221 : return true;
222 : }
223 : // check whether the edge shall be removed because it does not allow any of the wished classes
224 154156 : if (myVehicleClasses2Keep != 0 && (myVehicleClasses2Keep & edge->getPermissions()) == 0) {
225 : return true;
226 : }
227 : // check whether the edge shall be removed due to allowing unwished classes only
228 149067 : if (myVehicleClasses2Remove != 0 && (myVehicleClasses2Remove | edge->getPermissions()) == myVehicleClasses2Remove) {
229 : return true;
230 : }
231 : }
232 : // check whether the edge is a named edge to remove
233 148861 : if (myEdges2Remove.size() != 0) {
234 292 : if (myEdges2Remove.count(edge->getID()) != 0) {
235 33 : return true;
236 : }
237 : }
238 : // check whether the edge shall be removed because it does not have one of the requested types
239 148828 : if (myTypes2Keep.size() != 0) {
240 : if (myTypes2Keep.count(edge->getTypeID()) == 0) {
241 1443 : return true;
242 : }
243 : }
244 : // check whether the edge shall be removed because it has one of the forbidden types
245 147385 : if (myTypes2Remove.size() != 0) {
246 : if (myTypes2Remove.count(edge->getTypeID()) > 0) {
247 52 : return true;
248 : }
249 : }
250 : // check whether the edge is within the pruning boundary
251 147333 : if (myPruningBoundary.size() != 0) {
252 987 : if (myNeedGeoTransformedPruningBoundary) {
253 4 : if (GeoConvHelper::getProcessing().usingGeoProjection()) {
254 2 : NBNetBuilder::transformCoordinates(myPruningBoundary, false);
255 2 : } else if (GeoConvHelper::getLoaded().usingGeoProjection()) {
256 : // XXX what if input file with different projections are loaded?
257 10 : for (int i = 0; i < (int) myPruningBoundary.size(); i++) {
258 8 : GeoConvHelper::getLoaded().x2cartesian_const(myPruningBoundary[i]);
259 : }
260 : } else {
261 0 : WRITE_ERROR(TL("Cannot prune edges using a geo-boundary because no projection has been loaded"));
262 : }
263 4 : myNeedGeoTransformedPruningBoundary = false;
264 : }
265 987 : if (!(edge->getGeometry().getBoxBoundary().grow(POSITION_EPS).overlapsWith(myPruningBoundary))) {
266 : return true;
267 691 : } else if (!(edge->getGeometry().partialWithin(myPruningBoundary, 2 * POSITION_EPS) || edge->getGeometry().intersects(myPruningBoundary))) {
268 : // a more detailed check is necessary because the bounding box may be much bigger than the edge
269 : // @note: overlapsWith implicitly closes the edge shape but this is not wanted here
270 : return true;
271 : }
272 : }
273 147035 : if (myTypeCont.knows(edge->getTypeID()) && myTypeCont.getEdgeTypeShallBeDiscarded(edge->getTypeID())) {
274 : return true;
275 : }
276 : return false;
277 : }
278 :
279 :
280 : NBEdge*
281 342234 : NBEdgeCont::retrieve(const std::string& id, bool retrieveExtracted) const {
282 : EdgeCont::const_iterator i = myEdges.find(id);
283 342234 : if (i == myEdges.end()) {
284 90969 : if (retrieveExtracted) {
285 : i = myExtractedEdges.find(id);
286 18735 : if (i == myExtractedEdges.end()) {
287 : return nullptr;
288 : }
289 : } else {
290 : return nullptr;
291 : }
292 : }
293 251303 : return (*i).second;
294 : }
295 :
296 : // FIXME: This can't work
297 : /*
298 : NBEdge*
299 : NBEdgeCont::retrievePossiblySplit(const std::string& id, bool downstream) const {
300 : NBEdge* edge = retrieve(id);
301 : if (edge == 0) {
302 : return 0;
303 : }
304 : const EdgeVector* candidates = downstream ? &edge->getToNode()->getOutgoingEdges() : &edge->getFromNode()->getIncomingEdges();
305 : while (candidates->size() == 1) {
306 : const std::string& nextID = candidates->front()->getID();
307 : if (nextID.find(id) != 0 || nextID.size() <= id.size() + 1 || (nextID[id.size()] != '.' && nextID[id.size()] != '-')) {
308 : break;
309 : }
310 : edge = candidates->front();
311 : candidates = downstream ? &edge->getToNode()->getOutgoingEdges() : &edge->getFromNode()->getIncomingEdges();
312 : }
313 : return edge;
314 : }*/
315 :
316 : NBEdge*
317 104 : NBEdgeCont::retrievePossiblySplit(const std::string& id, bool downstream) const {
318 104 : NBEdge* edge = retrieve(id);
319 104 : if (edge != nullptr) {
320 : return edge;
321 : }
322 : // NOTE: (TODO) for multiply split edges (e.g. 15[0][0]) one could try recursion
323 0 : if ((retrieve(id + "[0]") != nullptr) && (retrieve(id + "[1]") != nullptr)) {
324 : // Edge was split during the netbuilding process
325 0 : if (downstream) {
326 0 : return retrieve(id + "[1]");
327 : } else {
328 0 : return retrieve(id + "[0]");
329 : }
330 : }
331 : return edge;
332 : }
333 :
334 :
335 : NBEdge*
336 2402 : NBEdgeCont::retrievePossiblySplit(const std::string& id, const std::string& hint, bool incoming) const {
337 : // try to retrieve using the given name (iterative)
338 2402 : NBEdge* edge = retrieve(id);
339 2402 : if (edge != nullptr) {
340 : return edge;
341 : }
342 : // now, we did not find it; we have to look over all possibilities
343 : EdgeVector hints;
344 : // check whether at least the hint was not splitted
345 710 : NBEdge* hintedge = retrieve(hint);
346 710 : if (hintedge == nullptr) {
347 132 : hints = getGeneratedFrom(hint);
348 : } else {
349 578 : hints.push_back(hintedge);
350 : }
351 710 : EdgeVector candidates = getGeneratedFrom(id);
352 965 : for (const NBEdge* const currHint : hints) {
353 3555 : for (NBEdge* const poss_searched : candidates) {
354 3300 : const NBNode* const node = incoming ? poss_searched->myTo : poss_searched->myFrom;
355 3300 : const EdgeVector& cont = incoming ? node->getOutgoingEdges() : node->getIncomingEdges();
356 3300 : if (find(cont.begin(), cont.end(), currHint) != cont.end()) {
357 : return poss_searched;
358 : }
359 : }
360 : }
361 33 : return nullptr;
362 710 : }
363 :
364 :
365 : NBEdge*
366 261 : NBEdgeCont::retrievePossiblySplit(const std::string& id, double pos) const {
367 : // check whether the edge was not split, yet
368 261 : NBEdge* edge = retrieve(id);
369 261 : if (edge != nullptr) {
370 : return edge;
371 : }
372 : int maxLength = 0;
373 111 : std::string tid = id + "[";
374 21423 : for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
375 21312 : if ((*i).first.find(tid) == 0) {
376 660 : maxLength = MAX2(maxLength, (int)(*i).first.length());
377 : }
378 : }
379 : // find the part of the edge which matches the position
380 : double seen = 0;
381 : std::vector<std::string> names;
382 111 : names.push_back(id + "[1]");
383 111 : names.push_back(id + "[0]");
384 576 : while (names.size() > 0) {
385 : // retrieve the first subelement (to follow)
386 : std::string cid = names.back();
387 : names.pop_back();
388 558 : edge = retrieve(cid);
389 : // The edge was splitted; check its subparts within the
390 : // next step
391 558 : if (edge == nullptr) {
392 231 : if ((int)cid.length() + 3 < maxLength) {
393 213 : names.push_back(cid + "[1]");
394 426 : names.push_back(cid + "[0]");
395 : }
396 : }
397 : // an edge with the name was found,
398 : // check whether the position lies within it
399 : else {
400 327 : seen += edge->getLength();
401 327 : if (seen >= pos) {
402 : return edge;
403 : }
404 : }
405 : }
406 : return nullptr;
407 111 : }
408 :
409 :
410 : void
411 363 : NBEdgeCont::erase(NBDistrictCont& dc, NBEdge* edge) {
412 363 : extract(dc, edge);
413 363 : delete edge;
414 363 : }
415 :
416 :
417 : void
418 9666 : NBEdgeCont::extract(NBDistrictCont& dc, NBEdge* edge, bool remember) {
419 9666 : if (remember) {
420 9278 : const auto& prevExtracted = myExtractedEdges.find(edge->getID());
421 9278 : if (prevExtracted != myExtractedEdges.end()) {
422 22 : if (edge != prevExtracted->second) {
423 20 : myEdgeCemetery.insert(prevExtracted->second);
424 20 : prevExtracted->second = edge;
425 : }
426 : } else {
427 9256 : myExtractedEdges[edge->getID()] = edge;
428 : }
429 : }
430 9666 : myEdges.erase(edge->getID());
431 9666 : edge->myFrom->removeEdge(edge);
432 9666 : edge->myTo->removeEdge(edge);
433 9666 : dc.removeFromSinksAndSources(edge);
434 9666 : }
435 :
436 :
437 : void
438 193 : NBEdgeCont::rename(NBEdge* edge, const std::string& newID) {
439 : if (myEdges.count(newID) != 0) {
440 0 : throw ProcessError(TLF("Attempt to rename edge using existing id '%'", newID));
441 : }
442 193 : myEdges.erase(edge->getID());
443 193 : edge->setID(newID);
444 193 : myEdges[newID] = edge;
445 : // update oppositeID
446 193 : if (edge->getLanes().back().oppositeID != "") {
447 0 : NBEdge* oppo = retrieve(SUMOXMLDefinitions::getEdgeIDFromLane(edge->getLanes().back().oppositeID));
448 0 : if (oppo != nullptr) {
449 0 : oppo->getLaneStruct(oppo->getNumLanes() - 1).oppositeID = edge->getLaneID(edge->getNumLanes() - 1);
450 : }
451 : }
452 193 : }
453 :
454 :
455 : // ----- explicit edge manipulation methods
456 :
457 : void
458 19052 : NBEdgeCont::processSplits(NBEdge* e, std::vector<Split> splits,
459 : NBNodeCont& nc, NBDistrictCont& dc, NBTrafficLightLogicCont& tlc) {
460 19052 : if (splits.empty()) {
461 18815 : return;
462 : }
463 239 : const std::string origID = e->getID();
464 239 : sort(splits.begin(), splits.end(), split_sorter());
465 : int maxNumLanes = e->getNumLanes();
466 : // compute the node positions and sort the lanes
467 562 : for (Split& split : splits) {
468 323 : sort(split.lanes.begin(), split.lanes.end());
469 323 : maxNumLanes = MAX2(maxNumLanes, (int)split.lanes.size());
470 : }
471 : // split the edge
472 : std::vector<int> currLanes;
473 684 : for (int l = 0; l < e->getNumLanes(); ++l) {
474 445 : currLanes.push_back(l);
475 : }
476 239 : if (e->getNumLanes() != (int)splits.back().lanes.size()) {
477 : // invalidate traffic light definitions loaded from a SUMO network
478 178 : e->getToNode()->invalidateTLS(tlc, true, true);
479 : // if the number of lanes changes the connections should be
480 : // recomputed
481 178 : e->invalidateConnections(true);
482 : }
483 :
484 239 : std::string firstID = "";
485 : double seen = 0;
486 560 : for (const Split& exp : splits) {
487 : assert(exp.lanes.size() != 0);
488 603 : if (exp.pos > 0 && e->getLoadedLength() + seen > exp.pos && exp.pos > seen) {
489 279 : nc.insert(exp.node);
490 279 : nc.markAsSplit(exp.node);
491 : // split the edge
492 279 : const std::string idBefore = exp.idBefore == "" ? e->getID() : exp.idBefore;
493 279 : const std::string idAfter = exp.idAfter == "" ? exp.nameID : exp.idAfter;
494 279 : if (firstID == "") {
495 : firstID = idBefore;
496 : }
497 279 : const bool ok = splitAt(dc, e, exp.pos - seen, exp.node,
498 279 : idBefore, idAfter, e->getNumLanes(), (int) exp.lanes.size(), exp.speed);
499 279 : if (!ok) {
500 6 : WRITE_WARNINGF(TL("Error on parsing a split (edge '%')."), origID);
501 : return;
502 : }
503 277 : seen = exp.pos;
504 277 : std::vector<int> newLanes = exp.lanes;
505 277 : NBEdge* pe = retrieve(idBefore);
506 277 : NBEdge* ne = retrieve(idAfter);
507 : // reconnect lanes
508 277 : pe->invalidateConnections(true);
509 : // new on right
510 277 : int rightMostP = currLanes[0];
511 277 : int rightMostN = newLanes[0];
512 316 : for (int l = 0; l < (int) rightMostP - (int) rightMostN; ++l) {
513 78 : pe->addLane2LaneConnection(0, ne, l, NBEdge::Lane2LaneInfoType::VALIDATED, true);
514 : }
515 : // new on left
516 277 : int leftMostP = currLanes.back();
517 277 : int leftMostN = newLanes.back();
518 450 : for (int l = 0; l < (int) leftMostN - (int) leftMostP; ++l) {
519 346 : pe->addLane2LaneConnection(pe->getNumLanes() - 1, ne, leftMostN - l - rightMostN, NBEdge::Lane2LaneInfoType::VALIDATED, true);
520 : }
521 : // all other connected
522 989 : for (int l = 0; l < maxNumLanes; ++l) {
523 712 : if (find(currLanes.begin(), currLanes.end(), l) == currLanes.end()) {
524 226 : continue;
525 : }
526 486 : if (find(newLanes.begin(), newLanes.end(), l) == newLanes.end()) {
527 56 : continue;
528 : }
529 860 : pe->addLane2LaneConnection(l - rightMostP, ne, l - rightMostN, NBEdge::Lane2LaneInfoType::VALIDATED, true);
530 : }
531 : // if there are edges at this node which are not connected
532 : // we can assume that this split was attached to an
533 : // existing node. Reset all connections to let the default
534 : // algorithm recompute them
535 277 : if (exp.node->getIncomingEdges().size() > 1 || exp.node->getOutgoingEdges().size() > 1 || exp.node->getType() == SumoXMLNodeType::ZIPPER) {
536 30 : for (NBEdge* in : exp.node->getIncomingEdges()) {
537 20 : in->invalidateConnections(true);
538 : }
539 : }
540 : // move to next
541 : e = ne;
542 277 : currLanes = newLanes;
543 321 : } else if (exp.pos == 0) {
544 43 : const int laneCountDiff = e->getNumLanes() - (int)exp.lanes.size();
545 43 : if (laneCountDiff < 0) {
546 1 : e->incLaneNo(-laneCountDiff);
547 : } else {
548 42 : e->decLaneNo(laneCountDiff);
549 : }
550 43 : currLanes = exp.lanes;
551 : // invalidate traffic light definition loaded from a SUMO network
552 : // XXX it would be preferable to reconstruct the phase definitions heuristically
553 43 : e->getFromNode()->invalidateTLS(tlc, true, true);
554 43 : if (exp.speed != -1.) {
555 43 : e->setSpeed(-1, exp.speed);
556 : }
557 : } else {
558 3 : WRITE_WARNINGF(TL("Split at '%' lies beyond the edge's length (edge '%')."), toString(exp.pos), origID);
559 : }
560 : }
561 : // patch lane offsets
562 237 : e = retrieve(firstID);
563 237 : if (e != nullptr) {
564 234 : if (splits.front().pos != 0) {
565 : // add a dummy split at the beginning to ensure correct offset
566 : Split start;
567 193 : start.pos = 0;
568 544 : for (int lane = 0; lane < (int)e->getNumLanes(); ++lane) {
569 351 : start.lanes.push_back(lane);
570 : }
571 193 : start.offset = splits.front().offset;
572 193 : start.offsetFactor = splits.front().offsetFactor;
573 193 : splits.insert(splits.begin(), start);
574 193 : }
575 744 : for (const Split& split : splits) {
576 510 : int maxLeft = split.lanes.back();
577 510 : double offset = split.offset;
578 510 : if (maxLeft < maxNumLanes) {
579 510 : if (e->getLaneSpreadFunction() == LaneSpreadFunction::RIGHT) {
580 425 : offset += split.offsetFactor * SUMO_const_laneWidth * (maxNumLanes - 1 - maxLeft);
581 : } else {
582 85 : offset += split.offsetFactor * SUMO_const_halfLaneWidth * (maxNumLanes - 1 - maxLeft);
583 : }
584 : }
585 510 : int maxRight = split.lanes.front();
586 510 : if (maxRight > 0 && e->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
587 19 : offset -= split.offsetFactor * SUMO_const_halfLaneWidth * maxRight;
588 : }
589 : //std::cout << " processSplits " << origID << " splitOffset=" << (*i).offset << " offset=" << offset << "\n";
590 510 : if (offset != 0) {
591 : PositionVector g = e->getGeometry();
592 269 : g.move2side(offset);
593 269 : e->setGeometry(g);
594 269 : }
595 510 : if (e->getToNode()->getOutgoingEdges().size() != 0) {
596 429 : e = e->getToNode()->getOutgoingEdges()[0];
597 : }
598 : }
599 : }
600 239 : }
601 :
602 :
603 : bool
604 188 : NBEdgeCont::splitAt(NBDistrictCont& dc, NBEdge* edge, NBNode* node) {
605 376 : return splitAt(dc, edge, node, edge->getID() + "[0]", edge->getID() + "[1]",
606 188 : (int) edge->myLanes.size(), (int) edge->myLanes.size());
607 : }
608 :
609 :
610 : bool
611 468 : NBEdgeCont::splitAt(NBDistrictCont& dc, NBEdge* edge, NBNode* node,
612 : const std::string& firstEdgeName,
613 : const std::string& secondEdgeName,
614 : int noLanesFirstEdge, int noLanesSecondEdge,
615 : const double speed, const double friction,
616 : const int changedLeft) {
617 : double pos;
618 468 : pos = edge->getGeometry().nearest_offset_to_point2D(node->getPosition());
619 468 : if (pos <= 0) {
620 3 : pos = GeomHelper::nearest_offset_on_line_to_point2D(
621 3 : edge->myFrom->getPosition(), edge->myTo->getPosition(),
622 : node->getPosition());
623 : }
624 468 : if (pos <= 0 || pos + POSITION_EPS > edge->getGeometry().length()) {
625 3 : return false;
626 : }
627 465 : return splitAt(dc, edge, pos, node, firstEdgeName, secondEdgeName,
628 465 : noLanesFirstEdge, noLanesSecondEdge, speed, friction, changedLeft);
629 : }
630 :
631 :
632 : bool
633 781 : NBEdgeCont::splitAt(NBDistrictCont& dc,
634 : NBEdge* edge, double pos, NBNode* node,
635 : const std::string& firstEdgeName,
636 : const std::string& secondEdgeName,
637 : int noLanesFirstEdge, int noLanesSecondEdge,
638 : const double speed, const double friction,
639 : const int changedLeft) {
640 781 : if (firstEdgeName != edge->getID() && myEdges.count(firstEdgeName) != 0) {
641 0 : WRITE_ERRORF(TL("Could not insert edge '%' before split of edge '%'."), firstEdgeName, edge->getID());
642 0 : return false;
643 : }
644 781 : if (secondEdgeName == firstEdgeName || (secondEdgeName != edge->getID() && myEdges.count(secondEdgeName) != 0)) {
645 6 : WRITE_ERRORF(TL("Could not insert edge '%' after split of edge '%'."), secondEdgeName, edge->getID());
646 2 : return false;
647 : }
648 : // there must be at least some overlap between first and second edge
649 : assert(changedLeft > -((int)noLanesFirstEdge));
650 : assert(changedLeft < (int)noLanesSecondEdge);
651 :
652 : // build the new edges' geometries
653 : double geomPos = pos;
654 779 : if (edge->hasLoadedLength()) {
655 7 : geomPos = edge->getGeometry().nearest_offset_to_point2D(node->getPosition());
656 7 : if (geomPos <= 0) {
657 0 : geomPos = pos * edge->getGeometry().length() / edge->getLoadedLength();
658 : }
659 : }
660 779 : std::pair<PositionVector, PositionVector> geoms = edge->getGeometry().splitAt(geomPos);
661 : // reduce inaccuracies and preserve bidi
662 1558 : if (geoms.first[-1].almostSame(node->getPosition()) || edge->isBidi()) {
663 624 : geoms.first[-1] = node->getPosition();
664 624 : geoms.second[0] = node->getPosition();
665 : }
666 : // build and insert the edges
667 779 : NBEdge* one = new NBEdge(firstEdgeName, edge->myFrom, node, edge, geoms.first, noLanesFirstEdge);
668 779 : NBEdge* two = new NBEdge(secondEdgeName, node, edge->myTo, edge, geoms.second, noLanesSecondEdge);
669 1558 : if (OptionsCont::getOptions().getBool("output.original-names")) {
670 54 : const std::string origID = edge->getLaneStruct(0).getParameter(SUMO_PARAM_ORIGID, edge->getID());
671 18 : if (firstEdgeName != origID) {
672 34 : one->setOrigID(origID, false);
673 : }
674 18 : if (secondEdgeName != origID) {
675 20 : two->setOrigID(origID, false);
676 : }
677 : }
678 779 : two->copyConnectionsFrom(edge);
679 779 : if (speed != -1.) {
680 277 : two->setSpeed(-1, speed);
681 : }
682 779 : if (friction != -1.) {
683 779 : two->setFriction(-1, friction);
684 : }
685 779 : if (edge->getDistance() != 0) {
686 : one->setDistance(edge->getDistance());
687 3 : two->setDistance(one->getDistance() + pos);
688 : }
689 779 : if (edge->hasLoadedLength()) {
690 7 : one->setLoadedLength(pos);
691 7 : two->setLoadedLength(edge->getLoadedLength() - pos);
692 : }
693 : // replace information about this edge within the nodes
694 779 : edge->myFrom->replaceOutgoing(edge, one, 0);
695 779 : edge->myTo->replaceIncoming(edge, two, 0);
696 : // patch tls
697 803 : for (NBTrafficLightDefinition* const tld : edge->myFrom->getControllingTLS()) {
698 24 : tld->replaceRemoved(edge, -1, one, -1, false);
699 : }
700 829 : for (NBTrafficLightDefinition* const tld : edge->myTo->getControllingTLS()) {
701 50 : tld->replaceRemoved(edge, -1, two, -1, true);
702 : }
703 : // the edge is now occuring twice in both nodes...
704 : // clean up
705 779 : edge->myFrom->removeDoubleEdges();
706 779 : edge->myTo->removeDoubleEdges();
707 : // add connections from the first to the second edge
708 : // there will be as many connections as there are lanes on the second edge
709 : // by default lanes will be added / discontinued on the right side
710 : // (appropriate for highway on-/off-ramps)
711 779 : const int offset = (int)one->getNumLanes() - (int)two->getNumLanes() + changedLeft;
712 2243 : for (int i2 = 0; i2 < (int)two->getNumLanes(); i2++) {
713 1464 : const int i1 = MIN2(MAX2((int)0, i2 + offset), (int)one->getNumLanes());
714 2928 : if (!one->addLane2LaneConnection(i1, two, i2, NBEdge::Lane2LaneInfoType::COMPUTED)) {
715 0 : throw ProcessError(TL("Could not set connection!"));
716 : }
717 : }
718 779 : if (myRemoveEdgesAfterLoading) {
719 0 : if (myEdges2Keep.count(edge->getID()) != 0) {
720 0 : myEdges2Keep.insert(one->getID());
721 0 : myEdges2Keep.insert(two->getID());
722 : }
723 0 : if (myEdges2Remove.count(edge->getID()) != 0) {
724 0 : myEdges2Remove.insert(one->getID());
725 0 : myEdges2Remove.insert(two->getID());
726 : }
727 : }
728 : // erase the splitted edge
729 779 : patchRoundabouts(edge, one, two, myRoundabouts);
730 779 : patchRoundabouts(edge, one, two, myGuessedRoundabouts);
731 779 : const std::string oldID = edge->getID();
732 779 : extract(dc, edge, true);
733 779 : insert(one, true); // duplicate id check happened earlier
734 779 : insert(two, true); // duplicate id check happened earlier
735 779 : myEdgesSplit[edge] = {one, two};
736 : myWasSplit.insert(one);
737 : myWasSplit.insert(two);
738 : return true;
739 : }
740 :
741 :
742 : void
743 1558 : NBEdgeCont::patchRoundabouts(NBEdge* orig, NBEdge* part1, NBEdge* part2, std::set<EdgeSet>& roundabouts) {
744 : std::set<EdgeSet> addLater;
745 1561 : for (std::set<EdgeSet>::iterator it = roundabouts.begin(); it != roundabouts.end(); ++it) {
746 : EdgeSet roundaboutSet = *it;
747 : if (roundaboutSet.count(orig) > 0) {
748 : roundaboutSet.erase(orig);
749 : roundaboutSet.insert(part1);
750 : roundaboutSet.insert(part2);
751 : }
752 : addLater.insert(roundaboutSet);
753 : }
754 : roundabouts.clear();
755 1558 : roundabouts.insert(addLater.begin(), addLater.end());
756 1558 : }
757 :
758 :
759 : // ----- container access methods
760 : std::vector<std::string>
761 86 : NBEdgeCont::getAllNames() const {
762 : std::vector<std::string> ret;
763 27710 : for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
764 27624 : ret.push_back((*i).first);
765 : }
766 86 : return ret;
767 0 : }
768 :
769 :
770 : NBEdge*
771 3 : NBEdgeCont::getSplitBase(const std::string& edgeID) const {
772 : NBEdge* longest = nullptr;
773 7 : for (auto item : myEdgesSplit) {
774 4 : if (item.first->getID() == edgeID) {
775 3 : if (longest == nullptr || longest->getLoadedLength() < item.first->getLoadedLength()) {
776 : longest = const_cast<NBEdge*>(item.first);
777 : }
778 : }
779 : }
780 3 : return longest;
781 : }
782 :
783 : // ----- Adapting the input
784 : int
785 2 : NBEdgeCont::removeUnwishedEdges(NBDistrictCont& dc) {
786 : EdgeVector toRemove;
787 32 : for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
788 30 : NBEdge* edge = (*i).second;
789 30 : if (!myEdges2Keep.count(edge->getID())) {
790 13 : edge->getFromNode()->removeEdge(edge);
791 13 : edge->getToNode()->removeEdge(edge);
792 13 : toRemove.push_back(edge);
793 : }
794 : }
795 15 : for (EdgeVector::iterator j = toRemove.begin(); j != toRemove.end(); ++j) {
796 13 : erase(dc, *j);
797 : }
798 2 : return (int)toRemove.size();
799 2 : }
800 :
801 :
802 : void
803 4 : NBEdgeCont::splitGeometry(NBDistrictCont& dc, NBNodeCont& nc) {
804 : // make a copy of myEdges because splitting will modify it
805 : EdgeCont edges = myEdges;
806 208 : for (auto& item : edges) {
807 204 : NBEdge* edge = item.second;
808 204 : if (edge->getGeometry().size() < 3) {
809 108 : continue;
810 : }
811 : PositionVector geom = edge->getGeometry();
812 96 : const std::string id = edge->getID();
813 : double offset = 0;
814 361 : for (int i = 1; i < (int)geom.size() - 1; i++) {
815 265 : offset += geom[i - 1].distanceTo(geom[i]);
816 530 : std::string nodeID = id + "." + toString((int)offset);
817 265 : if (!nc.insert(nodeID, geom[i])) {
818 126 : WRITE_WARNING("Could not split geometry of edge '" + id + "' at index " + toString(i));
819 : continue;
820 : }
821 223 : NBNode* node = nc.retrieve(nodeID);
822 223 : splitAt(dc, edge, node, edge->getID(), nodeID, edge->getNumLanes(), edge->getNumLanes());
823 223 : edge = retrieve(nodeID);
824 : }
825 96 : }
826 4 : }
827 :
828 :
829 : void
830 12 : NBEdgeCont::reduceGeometries(const double minDist) {
831 528 : for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
832 516 : (*i).second->reduceGeometry(minDist);
833 : }
834 12 : }
835 :
836 :
837 : void
838 1792 : NBEdgeCont::checkGeometries(const double maxAngle, bool fixAngle, const double minRadius, bool fix, bool fixRailways, bool silent) {
839 1792 : if (maxAngle > 0 || minRadius > 0) {
840 98462 : for (auto& item : myEdges) {
841 96670 : if ((item.second->getPermissions() & (SVC_PUBLIC_CLASSES | SVC_PASSENGER)) == 0) {
842 25541 : continue;
843 : }
844 142258 : item.second->checkGeometry(maxAngle, fixAngle, minRadius, fix || (fixRailways && isRailway(item.second->getPermissions())), silent);
845 : }
846 : }
847 1792 : }
848 :
849 :
850 : // ----- processing methods
851 : void
852 1841 : NBEdgeCont::clearControllingTLInformation() const {
853 105286 : for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); i++) {
854 103445 : (*i).second->clearControllingTLInformation();
855 : }
856 1841 : }
857 :
858 :
859 : void
860 1841 : NBEdgeCont::sortOutgoingLanesConnections() {
861 105286 : for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
862 103445 : (*i).second->sortOutgoingConnectionsByAngle();
863 : }
864 1841 : }
865 :
866 :
867 : void
868 1841 : NBEdgeCont::computeEdge2Edges(bool noLeftMovers) {
869 105286 : for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
870 103445 : (*i).second->computeEdge2Edges(noLeftMovers);
871 : }
872 1841 : }
873 :
874 :
875 : void
876 1841 : NBEdgeCont::computeLanes2Edges() {
877 105286 : for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
878 103445 : (*i).second->computeLanes2Edges();
879 : }
880 1841 : }
881 :
882 :
883 : void
884 1841 : NBEdgeCont::recheckLanes() {
885 3682 : const bool fixOppositeLengths = OptionsCont::getOptions().getBool("opposites.guess.fix-lengths");
886 105286 : for (const auto& edgeIt : myEdges) {
887 103445 : NBEdge* const edge = edgeIt.second;
888 103445 : edge->recheckLanes();
889 103445 : edge->recheckOpposite(*this, fixOppositeLengths);
890 : }
891 1841 : }
892 :
893 :
894 : void
895 1083 : NBEdgeCont::appendTurnarounds(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike) {
896 67220 : for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
897 66137 : (*i).second->appendTurnaround(noTLSControlled, noFringe, onlyDeadends, onlyTurnlane, noGeometryLike, true);
898 : }
899 1083 : }
900 :
901 :
902 : void
903 758 : NBEdgeCont::appendTurnarounds(const std::set<std::string>& ids, bool noTLSControlled) {
904 758 : for (std::set<std::string>::const_iterator it = ids.begin(); it != ids.end(); it++) {
905 0 : myEdges[*it]->appendTurnaround(noTLSControlled, false, false, false, false, false);
906 : }
907 758 : }
908 :
909 :
910 : void
911 28 : NBEdgeCont::appendRailwayTurnarounds(const NBPTStopCont& sc) {
912 : std::set<std::string> stopEdgeIDs;
913 444 : for (auto& stopItem : sc.getStops()) {
914 416 : stopEdgeIDs.insert(stopItem.second->getEdgeId());
915 : }
916 7193 : for (auto& item : myEdges) {
917 7165 : NBEdge* edge = item.second;
918 7165 : if (edge->isBidiRail()
919 7165 : && (stopEdgeIDs.count(item.first) > 0 ||
920 2147 : stopEdgeIDs.count(edge->getTurnDestination(true)->getID()) > 0)) {
921 314 : NBEdge* to = edge->getTurnDestination(true);
922 : assert(to != 0);
923 628 : edge->setConnection(edge->getNumLanes() - 1,
924 : to, to->getNumLanes() - 1, NBEdge::Lane2LaneInfoType::VALIDATED, false, false,
925 : KEEPCLEAR_UNSPECIFIED,
926 : NBEdge::UNSPECIFIED_CONTPOS, NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE,
927 : SUMO_const_haltingSpeed);
928 : }
929 : }
930 28 : }
931 :
932 : void
933 1999 : NBEdgeCont::computeEdgeShapes(double smoothElevationThreshold) {
934 138646 : for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
935 136647 : (*i).second->computeEdgeShape(smoothElevationThreshold);
936 : }
937 : // equalize length of opposite edges
938 138646 : for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
939 136647 : NBEdge* edge = i->second;
940 136647 : const std::string& oppositeID = edge->getLanes().back().oppositeID;
941 136647 : if (oppositeID != "" && oppositeID != "-") {
942 94 : NBEdge* oppEdge = retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
943 187 : if (oppEdge == nullptr || oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
944 1 : continue;
945 : }
946 93 : if (fabs(oppEdge->getLength() - edge->getLength()) > NUMERICAL_EPS) {
947 5 : double avgLength = (oppEdge->getLength() + edge->getLength()) / 2;
948 5 : edge->setAverageLengthWithOpposite(avgLength);
949 5 : oppEdge->setAverageLengthWithOpposite(avgLength);
950 : }
951 : }
952 : }
953 1999 : }
954 :
955 :
956 : void
957 1999 : NBEdgeCont::computeLaneShapes() {
958 138646 : for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
959 136647 : (*i).second->computeLaneShapes();
960 : }
961 1999 : }
962 :
963 :
964 : void
965 12 : NBEdgeCont::joinSameNodeConnectingEdges(NBDistrictCont& dc,
966 : NBTrafficLightLogicCont& tlc,
967 : EdgeVector edges) {
968 : // !!! Attention!
969 : // No merging of the geometry to come is being done
970 : // The connections are moved from one edge to another within
971 : // the replacement where the edge is a node's incoming edge.
972 :
973 : // count the number of lanes, the speed and the id
974 : int nolanes = 0;
975 : double speed = 0;
976 : int priority = -1;
977 : bool joinEdges = true;
978 : std::string id;
979 12 : sort(edges.begin(), edges.end(), NBContHelper::same_connection_edge_sorter());
980 : // retrieve the connected nodes
981 12 : NBEdge* tpledge = *(edges.begin());
982 : NBNode* from = tpledge->getFromNode();
983 : NBNode* to = tpledge->getToNode();
984 : EdgeVector::const_iterator i;
985 : int myPriority = (*edges.begin())->getPriority();
986 36 : for (i = edges.begin(); i != edges.end(); i++) {
987 : // some assertions
988 : assert((*i)->getFromNode() == from);
989 : assert((*i)->getToNode() == to);
990 : // ad the number of lanes the current edge has
991 24 : nolanes += (*i)->getNumLanes();
992 : // build the id
993 24 : if (i != edges.begin()) {
994 : id += "+";
995 : }
996 24 : id += (*i)->getID();
997 : // compute the speed
998 24 : speed += (*i)->getSpeed();
999 : // build the priority
1000 : // merged edges should have the same inherited priority
1001 24 : if (myPriority == (*i)->getPriority()) {
1002 : priority = myPriority;
1003 : } else {
1004 : priority = -1;
1005 : joinEdges = false;
1006 : }
1007 : }
1008 12 : if (joinEdges) {
1009 11 : speed /= (double)edges.size();
1010 : // build the new edge
1011 : NBEdge* newEdge = new NBEdge(id, from, to, "", speed, NBEdge::UNSPECIFIED_FRICTION, nolanes, priority,
1012 : NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_OFFSET,
1013 22 : tpledge->myLaneSpreadFunction, tpledge->getStreetName());
1014 : // copy lane attributes
1015 : int laneIndex = 0;
1016 33 : for (i = edges.begin(); i != edges.end(); ++i) {
1017 22 : const std::vector<NBEdge::Lane>& lanes = (*i)->getLanes();
1018 49 : for (int j = 0; j < (int)lanes.size(); ++j) {
1019 27 : newEdge->setPermissions(lanes[j].permissions, laneIndex);
1020 27 : newEdge->setLaneWidth(laneIndex, lanes[j].width);
1021 27 : newEdge->setEndOffset(laneIndex, lanes[j].endOffset);
1022 27 : laneIndex++;
1023 : }
1024 : }
1025 11 : insert(newEdge, true);
1026 : // replace old edge by current within the nodes
1027 : // and delete the old
1028 11 : from->replaceOutgoing(edges, newEdge);
1029 11 : to->replaceIncoming(edges, newEdge);
1030 : // patch connections
1031 : // add edge2edge-information
1032 33 : for (i = edges.begin(); i != edges.end(); i++) {
1033 22 : EdgeVector ev = (*i)->getConnectedEdges();
1034 59 : for (EdgeVector::iterator j = ev.begin(); j != ev.end(); j++) {
1035 37 : newEdge->addEdge2EdgeConnection(*j);
1036 : }
1037 22 : }
1038 : // copy outgoing connections to the new edge
1039 : int currLane = 0;
1040 33 : for (i = edges.begin(); i != edges.end(); i++) {
1041 22 : newEdge->moveOutgoingConnectionsFrom(*i, currLane);
1042 22 : currLane += (*i)->getNumLanes();
1043 : }
1044 : // patch tl-information
1045 : currLane = 0;
1046 33 : for (i = edges.begin(); i != edges.end(); i++) {
1047 22 : int noLanes = (*i)->getNumLanes();
1048 49 : for (int j = 0; j < noLanes; j++, currLane++) {
1049 : // replace in traffic lights
1050 27 : tlc.replaceRemoved(*i, j, newEdge, currLane, true);
1051 27 : tlc.replaceRemoved(*i, j, newEdge, currLane, false);
1052 : }
1053 : }
1054 : // delete joined edges
1055 33 : for (i = edges.begin(); i != edges.end(); i++) {
1056 22 : extract(dc, *i, true);
1057 : }
1058 : }
1059 12 : }
1060 :
1061 :
1062 : void
1063 16 : NBEdgeCont::guessOpposites() {
1064 : //@todo magic values
1065 32 : const bool fixOppositeLengths = OptionsCont::getOptions().getBool("opposites.guess.fix-lengths");
1066 : // ensure consistency of loaded values before starting to guess
1067 63 : for (const auto& edgeIt : myEdges) {
1068 47 : NBEdge* const edge = edgeIt.second;
1069 47 : edge->recheckOpposite(*this, fixOppositeLengths);
1070 : }
1071 63 : for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1072 47 : NBEdge* edge = i->second;
1073 47 : edge->guessOpposite();
1074 : }
1075 16 : }
1076 :
1077 :
1078 : void
1079 49 : NBEdgeCont::recheckLaneSpread() {
1080 704 : for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1081 655 : NBEdge* opposite = getOppositeByID(i->first);
1082 655 : if (opposite != nullptr) {
1083 470 : i->second->setLaneSpreadFunction(LaneSpreadFunction::RIGHT);
1084 470 : opposite->setLaneSpreadFunction(LaneSpreadFunction::RIGHT);
1085 : } else {
1086 185 : i->second->setLaneSpreadFunction(LaneSpreadFunction::CENTER);
1087 : }
1088 : }
1089 49 : }
1090 :
1091 :
1092 : NBEdge*
1093 1144 : NBEdgeCont::getOppositeByID(const std::string& edgeID) const {
1094 1144 : const std::string oppositeID = edgeID[0] == '-' ? edgeID.substr(1) : "-" + edgeID;
1095 : EdgeCont::const_iterator it = myEdges.find(oppositeID);
1096 1144 : return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
1097 : }
1098 :
1099 : NBEdge*
1100 19864 : NBEdgeCont::getByID(const std::string& edgeID) const {
1101 : EdgeCont::const_iterator it = myEdges.find(edgeID);
1102 19864 : return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
1103 : }
1104 :
1105 : // ----- other
1106 : void
1107 18 : NBEdgeCont::addPostProcessConnection(const std::string& from, int fromLane, const std::string& to, int toLane, bool mayDefinitelyPass,
1108 : KeepClear keepClear, double contPos, double visibility, double speed, double friction, double length,
1109 : const PositionVector& customShape, bool uncontrolled, bool warnOnly,
1110 : SVCPermissions permissions, bool indirectLeft, const std::string& edgeType, SVCPermissions changeLeft, SVCPermissions changeRight) {
1111 36 : myConnections[from].push_back(PostProcessConnection(from, fromLane, to, toLane, mayDefinitelyPass, keepClear, contPos, visibility,
1112 : speed, friction, length, customShape, uncontrolled, warnOnly, permissions, indirectLeft, edgeType, changeLeft, changeRight));
1113 18 : }
1114 :
1115 : bool
1116 8130 : NBEdgeCont::hasPostProcessConnection(const std::string& from, const std::string& to) {
1117 : if (myConnections.count(from) == 0) {
1118 8126 : return false;
1119 : } else {
1120 4 : if (to == "") {
1121 : // wildcard
1122 : return true;
1123 : }
1124 0 : for (const auto& ppc : myConnections[from]) {
1125 0 : if (ppc.to == to) {
1126 : return true;
1127 : }
1128 : }
1129 : return false;
1130 : }
1131 : }
1132 :
1133 : void
1134 1841 : NBEdgeCont::recheckPostProcessConnections() {
1135 3594 : const bool warnOnly = OptionsCont::getOptions().exists("ignore-errors.connections") && OptionsCont::getOptions().getBool("ignore-errors.connections");
1136 1848 : for (const auto& item : myConnections) {
1137 25 : for (std::vector<PostProcessConnection>::const_iterator i = item.second.begin(); i != item.second.end(); ++i) {
1138 18 : NBEdge* from = retrievePossiblySplit((*i).from, true);
1139 18 : NBEdge* to = retrievePossiblySplit((*i).to, false);
1140 36 : if (from == nullptr || to == nullptr ||
1141 18 : !from->addLane2LaneConnection((*i).fromLane, to, (*i).toLane, NBEdge::Lane2LaneInfoType::USER, true, (*i).mayDefinitelyPass,
1142 18 : (*i).keepClear, (*i).contPos, (*i).visibility, (*i).speed, (*i).friction, (*i).customLength, (*i).customShape,
1143 18 : (*i).uncontrolled, (*i).permissions, (*i).indirectLeft, (*i).edgeType, (*i).changeLeft, (*i).changeRight,
1144 : true)) {
1145 1 : const std::string msg = "Could not insert connection between '" + (*i).from + "' and '" + (*i).to + "' after build.";
1146 1 : if (warnOnly || (*i).warnOnly) {
1147 0 : WRITE_WARNING(msg);
1148 : } else {
1149 3 : WRITE_ERROR(msg);
1150 : }
1151 : }
1152 : }
1153 : }
1154 : // during loading we also kept some ambiguous connections in hope they might be valid after processing
1155 : // we need to make sure that all invalid connections are removed now
1156 105286 : for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
1157 103445 : NBEdge* edge = it->second;
1158 : NBNode* to = edge->getToNode();
1159 : // make a copy because we may delete connections
1160 103445 : std::vector<NBEdge::Connection> connections = edge->getConnections();
1161 176700 : for (std::vector<NBEdge::Connection>::iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
1162 : NBEdge::Connection& c = *it_con;
1163 73255 : if (c.toEdge != nullptr && c.toEdge->getFromNode() != to) {
1164 12 : WRITE_WARNING("Found and removed invalid connection from edge '" + edge->getID() +
1165 : "' to edge '" + c.toEdge->getID() + "' via junction '" + to->getID() + "'.");
1166 6 : edge->removeFromConnections(c.toEdge);
1167 : }
1168 : }
1169 103445 : }
1170 1841 : }
1171 :
1172 :
1173 : EdgeVector
1174 842 : NBEdgeCont::getGeneratedFrom(const std::string& id) const {
1175 842 : int len = (int)id.length();
1176 : EdgeVector ret;
1177 149420 : for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1178 : std::string curr = (*i).first;
1179 : // the next check makes it possibly faster - we don not have
1180 : // to compare the names
1181 148578 : if ((int)curr.length() <= len) {
1182 55988 : continue;
1183 : }
1184 : // the name must be the same as the given id but something
1185 : // beginning with a '[' must be appended to it
1186 185180 : if (curr.substr(0, len) == id && curr[len] == '[') {
1187 4828 : ret.push_back((*i).second);
1188 4828 : continue;
1189 : }
1190 : // ok, maybe the edge is a compound made during joining of edges
1191 : std::string::size_type pos = curr.find(id);
1192 : // surely not
1193 87762 : if (pos == std::string::npos) {
1194 75438 : continue;
1195 : }
1196 : // check leading char
1197 12324 : if (pos > 0) {
1198 3975 : if (curr[pos - 1] != ']' && curr[pos - 1] != '+') {
1199 : // actually, this is another id
1200 3975 : continue;
1201 : }
1202 : }
1203 8349 : if (pos + id.length() < curr.length()) {
1204 8349 : if (curr[pos + id.length()] != '[' && curr[pos + id.length()] != '+') {
1205 : // actually, this is another id
1206 8349 : continue;
1207 : }
1208 : }
1209 0 : ret.push_back((*i).second);
1210 : }
1211 842 : return ret;
1212 0 : }
1213 :
1214 :
1215 : int
1216 2016 : NBEdgeCont::guessRoundabouts() {
1217 : myGuessedRoundabouts.clear();
1218 : std::set<NBEdge*> loadedRoundaboutEdges;
1219 2103 : for (std::set<EdgeSet>::const_iterator it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
1220 87 : loadedRoundaboutEdges.insert(it->begin(), it->end());
1221 : }
1222 : // step 1: keep only those edges which have no turnarounds and which are not
1223 : // part of a loaded roundabout
1224 : std::set<NBEdge*> candidates;
1225 2016 : SVCPermissions valid = SVCAll & ~SVC_PEDESTRIAN;
1226 139679 : for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1227 137663 : NBEdge* e = (*i).second;
1228 : NBNode* const to = e->getToNode();
1229 137663 : if (e->getTurnDestination() == nullptr
1230 67138 : && to->getConnectionTo(e->getFromNode()) == nullptr
1231 188693 : && (e->getPermissions() & valid) != 0) {
1232 : candidates.insert(e);
1233 : }
1234 : }
1235 :
1236 : // step 2:
1237 : std::set<NBEdge*> visited;
1238 33317 : for (std::set<NBEdge*>::const_iterator i = candidates.begin(); i != candidates.end(); ++i) {
1239 : EdgeVector loopEdges;
1240 : // start with a random edge (this doesn't have to be a roundabout edge)
1241 : // loop over connected edges (using always the leftmost one)
1242 : // and keep the list in loopEdges
1243 : // continue until we loop back onto a loopEdges and extract the loop
1244 31301 : NBEdge* e = (*i);
1245 14121 : if (visited.count(e) > 0) {
1246 : // already seen
1247 : continue;
1248 : }
1249 17180 : loopEdges.push_back(e);
1250 : bool doLoop = true;
1251 : #ifdef DEBUG_GUESS_ROUNDABOUT
1252 : gDebugFlag1 = e->getID() == DEBUG_EDGE_ID;
1253 : #endif
1254 21611 : do {
1255 : #ifdef DEBUG_GUESS_ROUNDABOUT
1256 : if (gDebugFlag1) {
1257 : std::cout << " e=" << e->getID() << " loopEdges=" << toString(loopEdges) << "\n";
1258 : gDebugFlag1 = true;
1259 : }
1260 : #endif
1261 : visited.insert(e);
1262 34215 : const EdgeVector& edges = e->getToNode()->getEdges();
1263 32805 : if ((e->getToNode()->getType() == SumoXMLNodeType::RIGHT_BEFORE_LEFT || e->getToNode()->getType() == SumoXMLNodeType::LEFT_BEFORE_RIGHT)
1264 34224 : && !e->getToNode()->typeWasGuessed()) {
1265 : doLoop = false;
1266 : #ifdef DEBUG_GUESS_ROUNDABOUT
1267 : if (gDebugFlag1) {
1268 : std::cout << " rbl\n";
1269 : }
1270 : gDebugFlag1 = false;
1271 : #endif
1272 12604 : break;
1273 : }
1274 33175 : if (e->getToNode()->getRoundaboutType() == RoundaboutType::NO) {
1275 : doLoop = false;
1276 : #ifdef DEBUG_GUESS_ROUNDABOUT
1277 : if (gDebugFlag1) {
1278 : std::cout << " disabled\n";
1279 : }
1280 : gDebugFlag1 = false;
1281 : #endif
1282 : break;
1283 : }
1284 33174 : if (edges.size() < 2) {
1285 : doLoop = false;
1286 : #ifdef DEBUG_GUESS_ROUNDABOUT
1287 : if (gDebugFlag1) {
1288 : std::cout << " deadend\n";
1289 : }
1290 : gDebugFlag1 = false;
1291 : #endif
1292 : break;
1293 : }
1294 29812 : if (e->getTurnDestination(true) != nullptr || e->getToNode()->getConnectionTo(e->getFromNode()) != nullptr) {
1295 : // do not follow turn-arounds while in a (tentative) loop
1296 : doLoop = false;
1297 : #ifdef DEBUG_GUESS_ROUNDABOUT
1298 : if (gDebugFlag1) {
1299 : std::cout << " invalid turnAround e=" << e->getID() << " dest=" << Named::getIDSecure(e->getTurnDestination()) << "\n";
1300 : }
1301 : gDebugFlag1 = false;
1302 : #endif
1303 : break;
1304 : }
1305 26219 : EdgeVector::const_iterator me = std::find(edges.begin(), edges.end(), e);
1306 26219 : NBContHelper::nextCW(edges, me);
1307 26219 : NBEdge* left = *me;
1308 27870 : while ((left->getPermissions() & valid) == 0 && left != e) {
1309 1651 : NBContHelper::nextCW(edges, me);
1310 1651 : left = *me;
1311 : }
1312 26219 : if (left == e) {
1313 : // no usable continuation edge found
1314 : doLoop = false;
1315 : #ifdef DEBUG_GUESS_ROUNDABOUT
1316 : if (gDebugFlag1) {
1317 : std::cout << " noContinuation\n";
1318 : }
1319 : gDebugFlag1 = false;
1320 : #endif
1321 : break;
1322 : }
1323 26190 : NBContHelper::nextCW(edges, me);
1324 26190 : NBEdge* nextLeft = *me;
1325 26190 : double angle = fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), left->getAngleAtNode(e->getToNode())));
1326 26190 : double nextAngle = nextLeft == e ? 180 : fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), nextLeft->getAngleAtNode(e->getToNode())));
1327 : #ifdef DEBUG_GUESS_ROUNDABOUT
1328 : if (gDebugFlag1) {
1329 : std::cout << " e=" << e->getID() << " left=" << left->getID() << " nextLeft=" << nextLeft->getID() << " angle=" << angle << " nextAngle=" << nextAngle << " eLength=" << e->getLength() << " lLength=" << left->getLength() << " dist=" << e->getLaneShape(0).back().distanceTo2D(left->getLaneShape(0).front()) << "\n";
1330 : }
1331 : #endif
1332 : // there should be no straigher edge further left
1333 26190 : if (angle >= 90 && nextAngle < 45) {
1334 : doLoop = false;
1335 : #ifdef DEBUG_GUESS_ROUNDABOUT
1336 : if (gDebugFlag1) {
1337 : std::cout << " failed nextAngle=" << nextAngle << "\n";
1338 : }
1339 : gDebugFlag1 = false;
1340 : #endif
1341 : break;
1342 : }
1343 : // roundabouts do not have sharp turns (or they wouldn't be called 'round')
1344 : // however, if the roundabout is very small then most of the roundness may be in the junction so the angle may be as high as 180 (for smooth attachments at a joined junction)
1345 : if (angle >= 90) {
1346 2496 : double edgeAngle = fabs(NBHelpers::relAngle(e->getStartAngle(), e->getEndAngle()));
1347 2496 : double edgeAngle2 = fabs(NBHelpers::relAngle(left->getStartAngle(), left->getEndAngle()));
1348 2496 : double edgeRadius = e->getGeometry().length2D() / DEG2RAD(edgeAngle);
1349 2496 : double edgeRadius2 = left->getGeometry().length2D() / DEG2RAD(edgeAngle2);
1350 2496 : const double avgRadius = 0.5 * (edgeRadius + edgeRadius2);
1351 2496 : double junctionRadius = e->getLaneShape(0).back().distanceTo2D(left->getLaneShape(0).front()) / DEG2RAD(angle);
1352 : //std::cout << " junction=" << e->getToNode()->getID() << " e=" << e->getID() << " left=" << left->getID() << " angle=" << angle << " eRadius=" << edgeRadius << " eRadius2=" << edgeRadius2 << " jRadius3=" << junctionRadius << "\n";
1353 2496 : if (junctionRadius < 0.8 * avgRadius) {
1354 : doLoop = false;
1355 : #ifdef DEBUG_GUESS_ROUNDABOUT
1356 : if (gDebugFlag1) {
1357 : std::cout << " failed angle=" << angle << " eRadius=" << edgeRadius << " eRadius2=" << edgeRadius2 << " jRadius3=" << junctionRadius << "\n";
1358 : }
1359 : gDebugFlag1 = false;
1360 : #endif
1361 : break;
1362 : }
1363 : }
1364 :
1365 21789 : EdgeVector::const_iterator loopClosed = std::find(loopEdges.begin(), loopEdges.end(), left);
1366 21789 : const int loopSize = (int)(loopEdges.end() - loopClosed);
1367 21789 : if (loopSize > 0) {
1368 : // loop found
1369 178 : if (loopSize < 3) {
1370 : doLoop = false; // need at least 3 edges for a roundabout
1371 110 : } else if (loopSize < (int)loopEdges.size()) {
1372 : // remove initial edges not belonging to the loop
1373 46 : EdgeVector(loopEdges.begin() + (loopEdges.size() - loopSize), loopEdges.end()).swap(loopEdges);
1374 : }
1375 : // count attachments to the outside. need at least 3 or a roundabout doesn't make much sense
1376 : int attachments = 0;
1377 997 : for (EdgeVector::const_iterator j = loopEdges.begin(); j != loopEdges.end(); ++j) {
1378 819 : if ((*j)->getToNode()->getEdges().size() > 2) {
1379 542 : attachments++;
1380 : }
1381 : }
1382 178 : if (attachments < 3) {
1383 : doLoop = false;
1384 : #ifdef DEBUG_GUESS_ROUNDABOUT
1385 : if (gDebugFlag1) {
1386 : std::cout << " attachments=" << attachments << "\n";
1387 : }
1388 : gDebugFlag1 = false;
1389 : #endif
1390 : }
1391 : break;
1392 : }
1393 : if (visited.count(left) > 0) {
1394 : doLoop = false;
1395 : } else {
1396 : // keep going
1397 17035 : loopEdges.push_back(left);
1398 17035 : e = left;
1399 : }
1400 : } while (doLoop);
1401 17108 : if (doLoop) {
1402 : // check form factor to avoid elongated shapes (circle: 1, square: ~0.79)
1403 : #ifdef DEBUG_GUESS_ROUNDABOUT
1404 : if (gDebugFlag1) {
1405 : std::cout << " formFactor=" << formFactor(loopEdges) << "\n";
1406 : }
1407 : #endif
1408 : double loopLength = 0;
1409 637 : for (const NBEdge* const le : loopEdges) {
1410 536 : loopLength += le->getLoadedLength();
1411 : }
1412 101 : if (formFactor(loopEdges) > 0.6
1413 199 : && loopLength < OptionsCont::getOptions().getFloat("roundabouts.guess.max-length")) {
1414 : // collected edges are marked in markRoundabouts
1415 96 : EdgeSet guessed(loopEdges.begin(), loopEdges.end());
1416 : if (loadedRoundaboutEdges.count(loopEdges.front()) != 0) {
1417 79 : if (find(myRoundabouts.begin(), myRoundabouts.end(), guessed) == myRoundabouts.end()) {
1418 2 : for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); it++) {
1419 : if ((*it).count(loopEdges.front()) != 0) {
1420 4 : WRITE_WARNINGF(TL("Replacing loaded roundabout '%' with '%'."), toString(*it), toString(guessed));
1421 : myRoundabouts.erase(it);
1422 : break;
1423 : }
1424 : }
1425 : myGuessedRoundabouts.insert(guessed);
1426 : }
1427 : } else {
1428 : myGuessedRoundabouts.insert(guessed);
1429 : #ifdef DEBUG_GUESS_ROUNDABOUT
1430 : if (gDebugFlag1) {
1431 : std::cout << " foundRoundabout=" << toString(loopEdges) << "\n";
1432 : }
1433 : #endif
1434 : }
1435 : }
1436 : }
1437 : #ifdef DEBUG_GUESS_ROUNDABOUT
1438 : gDebugFlag1 = false;
1439 : #endif
1440 31301 : }
1441 4032 : return (int)myGuessedRoundabouts.size();
1442 : }
1443 :
1444 :
1445 : int
1446 188 : NBEdgeCont::extractRoundabouts() {
1447 : std::set<NBEdge*> candidateEdges;
1448 41827 : for (const auto& edge : myEdges) {
1449 41639 : NBEdge* const e = edge.second;
1450 41639 : if (e->getJunctionPriority(e->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT || e->getJunctionPriority(e->getFromNode()) == NBEdge::JunctionPriority::ROUNDABOUT) {
1451 : candidateEdges.insert(e);
1452 : }
1453 : }
1454 : std::set<NBEdge*> visited;
1455 : int extracted = 0;
1456 247 : for (const auto& edgeIt : candidateEdges) {
1457 : EdgeVector loopEdges;
1458 59 : NBEdge* e = edgeIt;
1459 48 : if (visited.count(e) > 0) {
1460 : // already seen
1461 : continue;
1462 : }
1463 11 : loopEdges.push_back(e);
1464 : bool doLoop = true;
1465 : //
1466 59 : do {
1467 70 : if (std::find(visited.begin(), visited.end(), e) != visited.end()) {
1468 11 : if (loopEdges.size() > 1) {
1469 11 : addRoundabout(EdgeSet(loopEdges.begin(), loopEdges.end()));
1470 11 : ++extracted;
1471 : }
1472 : doLoop = false;
1473 : break;
1474 : }
1475 : visited.insert(e);
1476 59 : loopEdges.push_back(e);
1477 59 : const EdgeVector& outgoingEdges = e->getToNode()->getOutgoingEdges();
1478 : EdgeVector::const_iterator me = std::find_if(outgoingEdges.begin(), outgoingEdges.end(), [](const NBEdge * outgoingEdge) {
1479 83 : return outgoingEdge->getJunctionPriority(outgoingEdge->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT;
1480 : });
1481 59 : if (me == outgoingEdges.end()) { // no closed loop
1482 : doLoop = false;
1483 : } else {
1484 59 : e = *me;
1485 : }
1486 : } while (doLoop);
1487 59 : }
1488 188 : return extracted;
1489 : }
1490 :
1491 :
1492 : void
1493 18 : NBEdgeCont::cleanupRoundabouts() {
1494 : // only loaded roundabouts are of concern here since guessing comes later
1495 : std::set<EdgeSet> validRoundabouts;
1496 : std::set<NBEdge*> validEdges;
1497 1320 : for (auto item : myEdges) {
1498 : validEdges.insert(item.second);
1499 : }
1500 21 : for (EdgeSet roundabout : myRoundabouts) {
1501 : EdgeSet validRoundabout;
1502 5 : for (NBEdge* cand : roundabout) {
1503 : if (validEdges.count(cand) != 0) {
1504 : validRoundabout.insert(cand);
1505 : }
1506 : }
1507 3 : if (validRoundabout.size() > 0) {
1508 : validRoundabouts.insert(validRoundabout);
1509 : }
1510 : }
1511 : myRoundabouts = validRoundabouts;
1512 18 : }
1513 :
1514 :
1515 : double
1516 101 : NBEdgeCont::formFactor(const EdgeVector& loopEdges) {
1517 : // A circle (which maximizes area per circumference) has a formfactor of 1, non-circular shapes have a smaller value
1518 101 : PositionVector points;
1519 637 : for (EdgeVector::const_iterator it = loopEdges.begin(); it != loopEdges.end(); ++it) {
1520 536 : points.append((*it)->getGeometry());
1521 : }
1522 101 : double circumference = points.length2D();
1523 202 : return 4 * M_PI * points.area() / (circumference * circumference);
1524 101 : }
1525 :
1526 :
1527 : const std::set<EdgeSet>
1528 7566 : NBEdgeCont::getRoundabouts() const {
1529 : std::set<EdgeSet> result = myRoundabouts;
1530 7566 : result.insert(myGuessedRoundabouts.begin(), myGuessedRoundabouts.end());
1531 7566 : return result;
1532 : }
1533 :
1534 :
1535 : void
1536 82 : NBEdgeCont::addRoundabout(const EdgeSet& roundabout) {
1537 82 : if (roundabout.size() > 0) {
1538 80 : if (find(myRoundabouts.begin(), myRoundabouts.end(), roundabout) != myRoundabouts.end()) {
1539 0 : WRITE_WARNING("Ignoring duplicate roundabout: " + toString(roundabout));
1540 : } else {
1541 : myRoundabouts.insert(roundabout);
1542 : }
1543 : }
1544 82 : }
1545 :
1546 : void
1547 4 : NBEdgeCont::removeRoundabout(const NBNode* node) {
1548 4 : for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
1549 2 : for (NBEdge* e : *it) {
1550 2 : if (e->getToNode() == node) {
1551 : myRoundabouts.erase(it);
1552 : return;
1553 : }
1554 : }
1555 : }
1556 : }
1557 :
1558 : void
1559 23 : NBEdgeCont::removeRoundaboutEdges(const EdgeSet& toRemove) {
1560 23 : removeRoundaboutEdges(toRemove, myRoundabouts);
1561 23 : removeRoundaboutEdges(toRemove, myGuessedRoundabouts);
1562 23 : }
1563 :
1564 : void
1565 46 : NBEdgeCont::removeRoundaboutEdges(const EdgeSet& toRemove, std::set<EdgeSet>& roundabouts) {
1566 : // members of a set are constant so we have to do some tricks
1567 : std::vector<EdgeSet> rList;
1568 48 : for (const EdgeSet& r : roundabouts) {
1569 : EdgeSet r2;
1570 2 : std::set_difference(r.begin(), r.end(), toRemove.begin(), toRemove.end(), std::inserter(r2, r2.end()));
1571 2 : rList.push_back(r2);
1572 : }
1573 : roundabouts.clear();
1574 : roundabouts.insert(rList.begin(), rList.end());
1575 46 : }
1576 :
1577 :
1578 : void
1579 1889 : NBEdgeCont::markRoundabouts() {
1580 1981 : for (const EdgeSet& roundaboutSet : getRoundabouts()) {
1581 536 : for (NBEdge* const edge : roundaboutSet) {
1582 : // disable turnarounds on incoming edges
1583 : NBNode* const node = edge->getToNode();
1584 1201 : for (NBEdge* const inEdge : node->getIncomingEdges()) {
1585 444 : if (roundaboutSet.count(inEdge) > 0) {
1586 444 : continue;
1587 : }
1588 313 : if (inEdge->getStep() >= NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
1589 199 : continue;
1590 : }
1591 114 : if (inEdge->getTurnDestination() != nullptr) {
1592 52 : inEdge->removeFromConnections(inEdge->getTurnDestination(), -1);
1593 : } else {
1594 : // also remove connections that are effecively a turnaround but
1595 : // where not correctly detector due to geometrical quirks
1596 62 : const std::vector<NBEdge::Connection> cons = inEdge->getConnections();
1597 126 : for (const NBEdge::Connection& con : cons) {
1598 64 : if (con.toEdge && roundaboutSet.count(con.toEdge) == 0) {
1599 10 : const double angle = fabs(NBHelpers::normRelAngle(inEdge->getAngleAtNode(node), con.toEdge->getAngleAtNode(node)));
1600 10 : if (angle > 160) {
1601 1 : inEdge->removeFromConnections(con.toEdge, -1);
1602 : }
1603 : }
1604 : }
1605 62 : }
1606 :
1607 : }
1608 : // let the connections to succeeding roundabout edge have a higher priority
1609 444 : edge->setJunctionPriority(node, NBEdge::JunctionPriority::ROUNDABOUT);
1610 444 : edge->setJunctionPriority(edge->getFromNode(), NBEdge::JunctionPriority::ROUNDABOUT);
1611 444 : node->setRoundabout();
1612 : }
1613 : }
1614 1889 : }
1615 :
1616 :
1617 : void
1618 1 : NBEdgeCont::generateStreetSigns() {
1619 23 : for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1620 22 : NBEdge* e = i->second;
1621 22 : const double offset = MAX2(0., e->getLength() - 3);
1622 22 : if (e->getToNode()->isSimpleContinuation(false)) {
1623 : // not a "real" junction?
1624 10 : continue;
1625 : }
1626 : const SumoXMLNodeType nodeType = e->getToNode()->getType();
1627 12 : switch (nodeType) {
1628 4 : case SumoXMLNodeType::PRIORITY:
1629 : // yield or major?
1630 4 : if (e->getJunctionPriority(e->getToNode()) > 0) {
1631 6 : e->addSign(NBSign(NBSign::SIGN_TYPE_PRIORITY, offset));
1632 : } else {
1633 6 : e->addSign(NBSign(NBSign::SIGN_TYPE_YIELD, offset));
1634 : }
1635 : break;
1636 0 : case SumoXMLNodeType::PRIORITY_STOP:
1637 : // yield or major?
1638 0 : if (e->getJunctionPriority(e->getToNode()) > 0) {
1639 0 : e->addSign(NBSign(NBSign::SIGN_TYPE_PRIORITY, offset));
1640 : } else {
1641 0 : e->addSign(NBSign(NBSign::SIGN_TYPE_STOP, offset));
1642 : }
1643 : break;
1644 0 : case SumoXMLNodeType::ALLWAY_STOP:
1645 0 : e->addSign(NBSign(NBSign::SIGN_TYPE_ALLWAY_STOP, offset));
1646 0 : break;
1647 4 : case SumoXMLNodeType::RIGHT_BEFORE_LEFT:
1648 8 : e->addSign(NBSign(NBSign::SIGN_TYPE_RIGHT_BEFORE_LEFT, offset));
1649 4 : break;
1650 0 : case SumoXMLNodeType::LEFT_BEFORE_RIGHT:
1651 0 : e->addSign(NBSign(NBSign::SIGN_TYPE_LEFT_BEFORE_RIGHT, offset));
1652 0 : break;
1653 : default:
1654 : break;
1655 : }
1656 : }
1657 1 : }
1658 :
1659 :
1660 : int
1661 21 : NBEdgeCont::guessSpecialLanes(SUMOVehicleClass svc, double width, double minSpeed, double maxSpeed, bool fromPermissions, const std::string& excludeOpt,
1662 : NBTrafficLightLogicCont& tlc) {
1663 : int lanesCreated = 0;
1664 : std::vector<std::string> edges;
1665 21 : if (excludeOpt != "") {
1666 21 : edges = OptionsCont::getOptions().getStringVector(excludeOpt);
1667 : }
1668 21 : std::set<std::string> exclude(edges.begin(), edges.end());
1669 443 : for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1670 422 : NBEdge* edge = it->second;
1671 : if (// not excluded
1672 422 : exclude.count(edge->getID()) == 0
1673 : // does not yet have a sidewalk/bikelane
1674 419 : && !edge->hasRestrictedLane(svc)
1675 : // needs a sidewalk/bikelane
1676 301 : && ((edge->getPermissions() & ~SVC_VULNERABLE) != 0 || (edge->getPermissions() & svc) == 0)
1677 284 : && (
1678 : // guess.from-permissions
1679 138 : (fromPermissions && (edge->getPermissions() & svc) != 0)
1680 : // guess from speed
1681 146 : || (!fromPermissions && edge->getSpeed() > minSpeed && edge->getSpeed() <= maxSpeed)
1682 : )) {
1683 236 : edge->addRestrictedLane(width, svc);
1684 236 : lanesCreated += 1;
1685 236 : if (svc != SVC_PEDESTRIAN) {
1686 36 : if (edge->getStep() == NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
1687 : // preserve existing connections and only add new ones
1688 : edge->declareConnectionsAsLoaded(NBEdge::EdgeBuildingStep::LANES2LANES_DONE);
1689 9 : edge->getFromNode()->recheckVClassConnections(edge);
1690 30 : for (NBEdge* to : edge->getToNode()->getOutgoingEdges()) {
1691 21 : edge->getToNode()->recheckVClassConnections(to);
1692 : }
1693 : // patching TLS is not feasible because existing states may
1694 : // change from 'G' to 'g' when bike lanes are added (i.e. right-turns)
1695 : } else {
1696 27 : edge->invalidateConnections(true);
1697 27 : edge->getFromNode()->invalidateOutgoingConnections(true);
1698 : }
1699 36 : edge->getFromNode()->invalidateTLS(tlc, true, false);
1700 36 : edge->getToNode()->invalidateTLS(tlc, true, false);
1701 : }
1702 : }
1703 : }
1704 21 : return lanesCreated;
1705 21 : }
1706 :
1707 :
1708 : void
1709 3 : NBEdgeCont::updateAllChangeRestrictions(SVCPermissions ignoring) {
1710 9 : for (auto item : myEdges) {
1711 6 : item.second->updateChangeRestrictions(ignoring);
1712 : }
1713 3 : }
1714 :
1715 :
1716 : void
1717 0 : NBEdgeCont::addPrefix(const std::string& prefix) {
1718 : // make a copy of node containers
1719 : const auto nodeContainerCopy = myEdges;
1720 : myEdges.clear();
1721 0 : for (const auto& node : nodeContainerCopy) {
1722 0 : node.second->setID(prefix + node.second->getID());
1723 0 : myEdges[node.second->getID()] = node.second;
1724 : }
1725 0 : }
1726 :
1727 :
1728 : int
1729 1841 : NBEdgeCont::remapIDs(bool numericaIDs, bool reservedIDs, bool keptIDs, const std::string& prefix, NBPTStopCont& sc) {
1730 1841 : bool startGiven = !OptionsCont::getOptions().isDefault("numerical-ids.edge-start");
1731 1841 : if (!numericaIDs && !reservedIDs && prefix == "" && !startGiven) {
1732 : return 0;
1733 : }
1734 : std::vector<std::string> avoid;
1735 66 : if (startGiven) {
1736 6 : avoid.push_back(toString(OptionsCont::getOptions().getInt("numerical-ids.edge-start") - 1));
1737 : } else {
1738 63 : avoid = getAllNames();
1739 : }
1740 : std::set<std::string> reserve;
1741 66 : if (reservedIDs) {
1742 4 : NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("reserved-ids"), "edge:", reserve);
1743 2 : avoid.insert(avoid.end(), reserve.begin(), reserve.end());
1744 : }
1745 132 : IDSupplier idSupplier("", avoid);
1746 : std::set<NBEdge*, ComparatorIdLess> toChange;
1747 25635 : for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1748 25569 : if (startGiven) {
1749 78 : toChange.insert(it->second);
1750 78 : continue;
1751 : }
1752 25491 : if (numericaIDs) {
1753 : try {
1754 25301 : StringUtils::toLong(it->first);
1755 9637 : } catch (NumberFormatException&) {
1756 9637 : toChange.insert(it->second);
1757 9637 : }
1758 : }
1759 25491 : if (reservedIDs && reserve.count(it->first) > 0) {
1760 2 : toChange.insert(it->second);
1761 : }
1762 : }
1763 : std::set<std::string> keep;
1764 66 : if (keptIDs) {
1765 4 : NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("kept-ids"), "edge:", keep);
1766 10 : for (auto it = toChange.begin(); it != toChange.end();) {
1767 8 : if (keep.count((*it)->getID()) != 0) {
1768 : toChange.erase(it++);
1769 : } else {
1770 : it++;
1771 : }
1772 : }
1773 : }
1774 :
1775 : std::map<std::string, std::vector<std::shared_ptr<NBPTStop> > > stopsOnEdge;
1776 92 : for (const auto& item : sc.getStops()) {
1777 26 : stopsOnEdge[item.second->getEdgeId()].push_back(item.second);
1778 : }
1779 :
1780 132 : const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
1781 9779 : for (NBEdge* edge : toChange) {
1782 9713 : myEdges.erase(edge->getID());
1783 : }
1784 9779 : for (NBEdge* edge : toChange) {
1785 9713 : const std::string origID = edge->getID();
1786 9713 : if (origNames) {
1787 18898 : edge->setOrigID(origID, false);
1788 : }
1789 9713 : edge->setID(idSupplier.getNext());
1790 9713 : myEdges[edge->getID()] = edge;
1791 9732 : for (std::shared_ptr<NBPTStop> stop : stopsOnEdge[origID]) {
1792 57 : stop->setEdgeId(prefix + edge->getID(), *this);
1793 : }
1794 : }
1795 66 : if (prefix.empty()) {
1796 56 : return (int)toChange.size();
1797 : } else {
1798 : int renamed = 0;
1799 : // make a copy because we will modify the map
1800 : auto oldEdges = myEdges;
1801 208 : for (auto item : oldEdges) {
1802 396 : if (!StringUtils::startsWith(item.first, prefix) && keep.count(item.first) == 0) {
1803 193 : rename(item.second, prefix + item.first);
1804 193 : renamed++;
1805 : }
1806 : }
1807 : return renamed;
1808 : }
1809 132 : }
1810 :
1811 :
1812 : void
1813 0 : NBEdgeCont::checkOverlap(double threshold, double zThreshold) const {
1814 0 : for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1815 0 : const NBEdge* e1 = it->second;
1816 0 : Boundary b1 = e1->getGeometry().getBoxBoundary();
1817 0 : b1.grow(e1->getTotalWidth());
1818 0 : PositionVector outline1 = e1->getCCWBoundaryLine(*e1->getFromNode());
1819 0 : outline1.append(e1->getCCWBoundaryLine(*e1->getToNode()));
1820 : // check is symmetric. only check once per pair
1821 0 : for (EdgeCont::const_iterator it2 = it; it2 != myEdges.end(); it2++) {
1822 0 : const NBEdge* e2 = it2->second;
1823 0 : if (e1 == e2) {
1824 0 : continue;
1825 : }
1826 0 : Boundary b2 = e2->getGeometry().getBoxBoundary();
1827 0 : b2.grow(e2->getTotalWidth());
1828 0 : if (b1.overlapsWith(b2)) {
1829 0 : PositionVector outline2 = e2->getCCWBoundaryLine(*e2->getFromNode());
1830 0 : outline2.append(e2->getCCWBoundaryLine(*e2->getToNode()));
1831 0 : const double overlap = outline1.getOverlapWith(outline2, zThreshold);
1832 0 : if (overlap > threshold) {
1833 0 : WRITE_WARNINGF(TL("Edge '%' overlaps with edge '%' by %."), e1->getID(), e2->getID(), overlap);
1834 : }
1835 0 : }
1836 : }
1837 0 : }
1838 0 : }
1839 :
1840 :
1841 : void
1842 25 : NBEdgeCont::checkGrade(double threshold) const {
1843 3251 : for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1844 3226 : const NBEdge* edge = it->second;
1845 6176 : for (int i = 0; i < (int)edge->getNumLanes(); i++) {
1846 3308 : double maxJump = 0;
1847 3308 : const double grade = edge->getLaneShape(i).getMaxGrade(maxJump);
1848 3308 : if (maxJump > 0.01) {
1849 0 : WRITE_WARNINGF(TL("Edge '%' has a vertical jump of %m."), edge->getID(), maxJump);
1850 3308 : } else if (grade > threshold) {
1851 1074 : WRITE_WARNINGF(TL("Edge '%' has a grade of %%."), edge->getID(), grade * 100, "%");
1852 358 : break;
1853 : }
1854 : }
1855 : const std::vector<NBEdge::Connection>& connections = edge->getConnections();
1856 4306 : for (std::vector<NBEdge::Connection>::const_iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
1857 : const NBEdge::Connection& c = *it_con;
1858 1084 : double maxJump = 0;
1859 1084 : const double grade = MAX2(c.shape.getMaxGrade(maxJump), c.viaShape.getMaxGrade(maxJump));
1860 1084 : if (maxJump > 0.01) {
1861 0 : WRITE_WARNINGF(TL("Connection '%' has a vertical jump of %m."), c.getDescription(edge), maxJump);
1862 1084 : } else if (grade > threshold) {
1863 8 : WRITE_WARNINGF(TL("Connection '%' has a grade of %%."), c.getDescription(edge), grade * 100, "%");
1864 4 : break;
1865 : }
1866 : }
1867 : }
1868 25 : }
1869 :
1870 :
1871 : int
1872 2 : NBEdgeCont::joinLanes(SVCPermissions perms) {
1873 : int affectedEdges = 0;
1874 38 : for (auto item : myEdges) {
1875 36 : if (item.second->joinLanes(perms)) {
1876 18 : affectedEdges++;
1877 : }
1878 : }
1879 2 : return affectedEdges;
1880 : }
1881 :
1882 :
1883 : bool
1884 2940 : NBEdgeCont::MinLaneComparatorIdLess::operator()(const std::pair<NBEdge*, int>& a, const std::pair<NBEdge*, int>& b) const {
1885 2940 : if (a.first->getID() == b.first->getID()) {
1886 44 : return a.second < b.second;
1887 : }
1888 2896 : return a.first->getID() < b.first->getID();
1889 : }
1890 :
1891 : int
1892 7 : NBEdgeCont::joinTramEdges(NBDistrictCont& dc, NBPTStopCont& sc, NBPTLineCont& lc, double maxDist) {
1893 : // this is different from joinSimilarEdges because there don't need to be
1894 : // shared nodes and tram edges may be split
1895 : std::vector<NBEdge*> tramEdges;
1896 : std::vector<NBEdge*> targetEdges;
1897 2250 : for (auto item : myEdges) {
1898 2243 : SVCPermissions permissions = item.second->getPermissions();
1899 2243 : if (isTram(permissions)) {
1900 494 : if (item.second->getNumLanes() == 1) {
1901 466 : tramEdges.push_back(item.second);
1902 : } else {
1903 84 : WRITE_WARNINGF(TL("Not joining tram edge '%' with % lanes."), item.second->getID(), item.second->getNumLanes());
1904 : }
1905 1749 : } else if ((permissions & (SVC_PASSENGER | SVC_BUS)) != 0) {
1906 1181 : targetEdges.push_back(item.second);
1907 : }
1908 : }
1909 7 : if (tramEdges.empty() || targetEdges.empty()) {
1910 : return 0;
1911 : }
1912 : int numJoined = 0;
1913 : NamedRTree tramTree;
1914 473 : for (NBEdge* const edge : tramEdges) {
1915 466 : const Boundary& bound = edge->getGeometry().getBoxBoundary();
1916 466 : float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1917 466 : float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1918 932 : tramTree.Insert(min, max, edge);
1919 : }
1920 : // {targetEdge, laneIndex : tramEdge}
1921 : std::map<std::pair<NBEdge*, int>, NBEdge*, MinLaneComparatorIdLess> matches;
1922 :
1923 1188 : for (NBEdge* const edge : targetEdges) {
1924 1181 : Boundary bound = edge->getGeometry().getBoxBoundary();
1925 1181 : bound.grow(maxDist + edge->getTotalWidth());
1926 1181 : float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1927 1181 : float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1928 : std::set<const Named*> near;
1929 : Named::StoringVisitor visitor(near);
1930 : tramTree.Search(min, max, visitor);
1931 : // the nearby set is actually just re-sorting according to the id to make the tests comparable
1932 : std::set<NBEdge*, ComparatorIdLess> nearby;
1933 4795 : for (const Named* namedEdge : near) {
1934 3614 : nearby.insert(const_cast<NBEdge*>(static_cast<const NBEdge*>(namedEdge)));
1935 : }
1936 4795 : for (NBEdge* const tramEdge : nearby) {
1937 : // find a continous stretch of tramEdge that runs along one of the lanes of the road edge
1938 : PositionVector tramShape = tramEdge->getGeometry();
1939 3614 : if (tramEdge->getToNode() == edge->getToNode()) {
1940 87 : tramShape.extrapolate(tramShape.back().distanceTo2D(edge->getGeometry().back()), false, true);
1941 : }
1942 3614 : double minEdgeDist = maxDist + 1;
1943 : int minLane = -1;
1944 : // find the lane where the maximum distance from the tram geometry
1945 : // is minimal and within maxDist
1946 10529 : for (int i = 0; i < edge->getNumLanes(); i++) {
1947 : double maxLaneDist = -1;
1948 6915 : if ((edge->getPermissions(i) & (SVC_PASSENGER | SVC_BUS)) != 0) {
1949 6393 : const PositionVector& laneShape = edge->getLaneShape(i);
1950 7475 : for (Position pos : laneShape) {
1951 7216 : const double dist = tramShape.distance2D(pos, false);
1952 : #ifdef DEBUG_JOIN_TRAM
1953 : //if (edge->getID() == "106838214#1") {
1954 : // std::cout << " edge=" << edge->getID() << " tramEdge=" << tramEdge->getID() << " lane=" << i << " pos=" << pos << " dist=" << dist << "\n";
1955 : //}
1956 : #endif
1957 7216 : if (dist == GeomHelper::INVALID_OFFSET || dist > maxDist) {
1958 : maxLaneDist = -1;
1959 : break;
1960 : }
1961 : maxLaneDist = MAX2(maxLaneDist, dist);
1962 : }
1963 6393 : if (maxLaneDist >= 0 && maxLaneDist < minEdgeDist) {
1964 : minEdgeDist = maxLaneDist;
1965 : minLane = i;
1966 : }
1967 : }
1968 : }
1969 3614 : if (minLane >= 0) {
1970 : // edge could run in the wrong direction and still fit the threshold we check the angle as well
1971 253 : const PositionVector& laneShape = edge->getLaneShape(minLane);
1972 253 : const double offset1 = tramShape.nearest_offset_to_point2D(laneShape.front(), false);
1973 253 : const double offset2 = tramShape.nearest_offset_to_point2D(laneShape.back(), false);
1974 253 : Position p1 = tramShape.positionAtOffset2D(offset1);
1975 253 : Position p2 = tramShape.positionAtOffset2D(offset2);
1976 253 : double tramAngle = GeomHelper::legacyDegree(p1.angleTo2D(p2), true);
1977 253 : bool angleOK = GeomHelper::getMinAngleDiff(tramAngle, edge->getTotalAngle()) < JOIN_TRAM_MAX_ANGLE;
1978 253 : if (angleOK && offset2 > offset1) {
1979 205 : std::pair<NBEdge*, int> key = std::make_pair(edge, minLane);
1980 : if (matches.count(key) == 0) {
1981 195 : matches[key] = tramEdge;
1982 : } else {
1983 40 : WRITE_WARNINGF(TL("Ambiguous tram edges '%' and '%' for lane '%'."), matches[key]->getID(), tramEdge->getID(), edge->getLaneID(minLane));
1984 : }
1985 : #ifdef DEBUG_JOIN_TRAM
1986 : std::cout << edge->getLaneID(minLane) << " is close to tramEdge " << tramEdge->getID() << " maxLaneDist=" << minEdgeDist << " tramLength=" << tramEdge->getLength() << " edgeLength=" << edge->getLength() << " tramAngle=" << tramAngle << " edgeAngle=" << edge->getTotalAngle() << "\n";
1987 : #endif
1988 : }
1989 : }
1990 3614 : }
1991 : }
1992 7 : if (matches.size() == 0) {
1993 : return 0;
1994 : }
1995 14 : const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
1996 : // find continous runs of matched edges for each tramEdge
1997 473 : for (NBEdge* tramEdge : tramEdges) {
1998 : std::vector<std::pair<double, std::pair<NBEdge*, int> > > roads;
1999 14774 : for (auto item : matches) {
2000 14308 : if (item.second == tramEdge) {
2001 : NBEdge* road = item.first.first;
2002 : int laneIndex = item.first.second;
2003 195 : const PositionVector& laneShape = road->getLaneShape(laneIndex);
2004 195 : double tramPos = tramEdge->getGeometry().nearest_offset_to_point2D(laneShape.front(), false);
2005 : //std::cout << " road=" << road->getID() << " tramEdge=" << tramEdge->getID() << " tramShape=" << tramEdge->getGeometry() << " laneFront=" << laneShape.front() << " tramPos=" << tramPos << "\n";
2006 195 : roads.push_back(std::make_pair(tramPos, item.first));
2007 : }
2008 : }
2009 466 : if (roads.size() != 0) {
2010 :
2011 62 : sort(roads.begin(), roads.end());
2012 : #ifdef DEBUG_JOIN_TRAM
2013 : std::cout << " tramEdge=" << tramEdge->getID() << " roads=";
2014 : for (auto item : roads) {
2015 : std::cout << item.second.first->getLaneID(item.second.second) << ",";
2016 : }
2017 : std::cout << " offsets=";
2018 : for (auto item : roads) {
2019 : std::cout << item.first << ",";
2020 : }
2021 : std::cout << "\n";
2022 : #endif
2023 : // merge tramEdge into road lanes
2024 : EdgeVector replacement;
2025 : double pos = 0;
2026 : int tramPart = 0;
2027 62 : std::string tramEdgeID = tramEdge->getID();
2028 : NBNode* tramFrom = tramEdge->getFromNode();
2029 : PositionVector tramShape = tramEdge->getGeometry();
2030 62 : const double tramLength = tramShape.length();
2031 62 : EdgeVector incoming = tramFrom->getIncomingEdges();
2032 : bool erasedLast = false;
2033 257 : for (const auto& item : roads) {
2034 195 : const double gap = item.first - pos;
2035 195 : NBEdge* road = item.second.first;
2036 195 : int laneIndex = item.second.second;
2037 195 : if (gap >= JOIN_TRAM_MIN_LENGTH && road->getFromNode() != tramEdge->getFromNode()) {
2038 : #ifdef DEBUG_JOIN_TRAM
2039 : std::cout << " splitting tramEdge=" << tramEdge->getID() << " at " << item.first << " (gap=" << gap << ")\n";
2040 : #endif
2041 74 : const std::string firstPartID = tramEdgeID + "#" + toString(tramPart++);
2042 37 : splitAt(dc, tramEdge, gap, road->getFromNode(), firstPartID, tramEdgeID, 1, 1);
2043 37 : tramEdge = retrieve(tramEdgeID); // second part;
2044 37 : NBEdge* firstPart = retrieve(firstPartID);
2045 37 : firstPart->invalidateConnections(true);
2046 : incoming.clear();
2047 37 : incoming.push_back(firstPart);
2048 37 : replacement.push_back(firstPart);
2049 : }
2050 195 : pos = item.first + road->getGeometry().length();
2051 195 : numJoined++;
2052 195 : replacement.push_back(road);
2053 : // merge section of tramEdge into road lane
2054 195 : if (road->getToNode() != tramEdge->getToNode() && (tramLength - pos) >= JOIN_TRAM_MIN_LENGTH) {
2055 164 : tramEdge->reinitNodes(road->getToNode(), tramEdge->getToNode());
2056 164 : tramEdge->setGeometry(tramShape.getSubpart(pos, tramShape.length()));
2057 : erasedLast = false;
2058 : #ifdef DEBUG_JOIN_TRAM
2059 : std::cout << " shorted tramEdge=" << tramEdge->getID() << " (joined with roadEdge=" << road->getID() << "\n";
2060 : #endif
2061 : } else {
2062 : #ifdef DEBUG_JOIN_TRAM
2063 : std::cout << " erased tramEdge=" << tramEdge->getID() << "\n";
2064 : #endif
2065 31 : extract(dc, tramEdge, true);
2066 : erasedLast = true;
2067 : }
2068 195 : road->setPermissions(road->getPermissions(laneIndex) | SVC_TRAM, laneIndex);
2069 195 : if (origNames) {
2070 114 : road->setOrigID(tramEdgeID, true, laneIndex);
2071 : }
2072 296 : for (NBEdge* in : incoming) {
2073 101 : if (isTram(in->getPermissions()) && !in->isConnectedTo(road)) {
2074 53 : if (in->getFromNode() != road->getFromNode()) {
2075 50 : in->reinitNodes(in->getFromNode(), road->getFromNode());
2076 : } else {
2077 3 : extract(dc, in, true);
2078 : #ifdef DEBUG_JOIN_TRAM
2079 : std::cout << " erased incoming tramEdge=" << in->getID() << "\n";
2080 : #endif
2081 : }
2082 : }
2083 : }
2084 : incoming.clear();
2085 : }
2086 62 : NBEdge* lastRoad = roads.back().second.first;
2087 62 : if (erasedLast) {
2088 : // copy to avoid concurrent modification
2089 29 : auto outEdges = tramEdge->getToNode()->getOutgoingEdges();
2090 94 : for (NBEdge* out : outEdges) {
2091 65 : if (isTram(out->getPermissions()) && !lastRoad->isConnectedTo(out)) {
2092 31 : if (lastRoad->getToNode() != out->getToNode()) {
2093 30 : out->reinitNodes(lastRoad->getToNode(), out->getToNode());
2094 : } else {
2095 1 : extract(dc, out, true);
2096 : #ifdef DEBUG_JOIN_TRAM
2097 : std::cout << " erased outgoing tramEdge=" << out->getID() << "\n";
2098 : #endif
2099 :
2100 : }
2101 : }
2102 : }
2103 29 : } else {
2104 33 : replacement.push_back(tramEdge);
2105 : }
2106 : // update ptstops and ptlines
2107 62 : sc.replaceEdge(tramEdgeID, replacement);
2108 62 : lc.replaceEdge(tramEdgeID, replacement);
2109 124 : }
2110 466 : }
2111 :
2112 7 : return numJoined;
2113 7 : }
2114 :
2115 :
2116 : EdgeVector
2117 179 : NBEdgeCont::getAllEdges() const {
2118 : EdgeVector result;
2119 53401 : for (auto item : myEdges) {
2120 53222 : item.second->setNumericalID((int)result.size());
2121 53222 : result.push_back(item.second);
2122 : }
2123 179 : return result;
2124 0 : }
2125 :
2126 : RouterEdgeVector
2127 76 : NBEdgeCont::getAllRouterEdges() const {
2128 76 : EdgeVector all = getAllEdges();
2129 152 : return RouterEdgeVector(all.begin(), all.end());
2130 76 : }
2131 :
2132 : bool
2133 1932 : NBEdgeCont::checkConsistency(const NBNodeCont& nc) {
2134 : bool ok = true;
2135 97954 : for (const auto& item : myEdges) {
2136 96022 : NBEdge* e = item.second;
2137 96022 : if (nc.retrieve(e->getFromNode()->getID()) == nullptr) {
2138 4 : WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), e->getID(), e->getFromNode()->getID());
2139 : ok = false;
2140 : }
2141 96022 : if (nc.retrieve(e->getToNode()->getID()) == nullptr) {
2142 4 : WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), e->getID(), e->getToNode()->getID());
2143 : ok = false;
2144 : }
2145 :
2146 : }
2147 1932 : return ok;
2148 : }
2149 :
2150 :
2151 : void
2152 85 : NBEdgeCont::fixSplitCustomLength() {
2153 2241 : for (auto item : myEdges) {
2154 : NBEdge* e = item.second;
2155 2156 : if (e->hasLoadedLength() && myWasSplit.count(e) != 0) {
2156 : // subtract half the length of the longest incoming / outgoing connection
2157 : double maxLengthOut = 0;
2158 15 : for (const NBEdge::Connection& c : e->getConnections()) {
2159 9 : maxLengthOut = MAX2(maxLengthOut, c.length + c.viaLength);
2160 : }
2161 : double maxLengthIn = 0;
2162 10 : for (const NBEdge* in : e->getIncomingEdges()) {
2163 15 : for (const NBEdge::Connection& c : in->getConnectionsFromLane(-1, e, -1)) {
2164 11 : maxLengthIn = MAX2(maxLengthIn, c.length + c.viaLength);
2165 4 : }
2166 6 : }
2167 12 : e->setLoadedLength(MAX2(POSITION_EPS, e->getLoadedLength() - (maxLengthIn + maxLengthOut) / 2));
2168 : }
2169 : }
2170 85 : }
2171 :
2172 : void
2173 156 : NBEdgeCont::computeAngles() {
2174 25296 : for (auto item : myEdges) {
2175 25140 : item.second->computeAngle();
2176 : }
2177 156 : }
2178 :
2179 :
2180 : std::set<std::string>
2181 1769 : NBEdgeCont::getUsedTypes() const {
2182 : std::set<std::string> result;
2183 103406 : for (auto item : myEdges) {
2184 101637 : if (item.second->getTypeID() != "") {
2185 : result.insert(item.second->getTypeID());
2186 : }
2187 : }
2188 1769 : return result;
2189 : }
2190 :
2191 :
2192 : int
2193 4 : NBEdgeCont::removeEdgesBySpeed(NBDistrictCont& dc) {
2194 : EdgeSet toRemove;
2195 84 : for (auto item : myEdges) {
2196 80 : NBEdge* edge = item.second;
2197 : // remove edges which allow a speed below a set one (set using "keep-edges.min-speed")
2198 80 : if (edge->getSpeed() < myEdgesMinSpeed) {
2199 : toRemove.insert(edge);
2200 : }
2201 : }
2202 : int numRemoved = 0;
2203 4 : for (NBEdge* edge : toRemove) {
2204 : // explicit whitelist overrides removal
2205 0 : if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
2206 0 : extract(dc, edge);
2207 0 : numRemoved++;
2208 : }
2209 : }
2210 4 : return numRemoved;
2211 : }
2212 :
2213 :
2214 : int
2215 4 : NBEdgeCont::removeEdgesByPermissions(NBDistrictCont& dc) {
2216 : EdgeSet toRemove;
2217 84 : for (auto item : myEdges) {
2218 80 : NBEdge* edge = item.second;
2219 : // check whether the edge shall be removed because it does not allow any of the wished classes
2220 80 : if (myVehicleClasses2Keep != 0 && (myVehicleClasses2Keep & edge->getPermissions()) == 0) {
2221 : toRemove.insert(edge);
2222 : }
2223 : // check whether the edge shall be removed due to allowing unwished classes only
2224 80 : if (myVehicleClasses2Remove != 0 && (myVehicleClasses2Remove | edge->getPermissions()) == myVehicleClasses2Remove) {
2225 : toRemove.insert(edge);
2226 : }
2227 : }
2228 : int numRemoved = 0;
2229 29 : for (NBEdge* edge : toRemove) {
2230 : // explicit whitelist overrides removal
2231 25 : if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
2232 25 : extract(dc, edge);
2233 25 : numRemoved++;
2234 : }
2235 : }
2236 4 : return numRemoved;
2237 : }
2238 :
2239 :
2240 : int
2241 1841 : NBEdgeCont::removeLanesByWidth(NBDistrictCont& dc, const double minWidth) {
2242 : EdgeSet toRemove;
2243 112319 : for (auto item : myEdges) {
2244 110478 : NBEdge* const edge = item.second;
2245 : std::vector<int> indices;
2246 110478 : int idx = 0;
2247 255110 : for (const auto& lane : edge->getLanes()) {
2248 144632 : if (lane.width != NBEdge::UNSPECIFIED_WIDTH && lane.width < minWidth) {
2249 4 : indices.push_back(idx);
2250 : }
2251 144632 : idx++;
2252 : }
2253 110478 : if ((int)indices.size() == edge->getNumLanes()) {
2254 : toRemove.insert(edge);
2255 : } else {
2256 : std::reverse(indices.begin(), indices.end());
2257 110482 : for (const int i : indices) {
2258 4 : edge->deleteLane(i, false, true);
2259 : }
2260 : }
2261 110478 : }
2262 : int numRemoved = 0;
2263 1841 : for (NBEdge* edge : toRemove) {
2264 : // explicit whitelist overrides removal
2265 0 : if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
2266 0 : extract(dc, edge);
2267 0 : numRemoved++;
2268 : }
2269 : }
2270 1841 : return numRemoved;
2271 : }
2272 :
2273 :
2274 : int
2275 1 : NBEdgeCont::attachRemoved(NBNodeCont& nc, NBDistrictCont& dc, const double maxDist) {
2276 : int numSplit = 0;
2277 : std::map<std::string, std::vector<std::string> > node2edge;
2278 172 : for (auto item : myEdges) {
2279 171 : if (item.second->hasParameter(SUMO_PARAM_REMOVED_NODES)) {
2280 45 : for (std::string& nodeID : StringTokenizer(item.second->getParameter(SUMO_PARAM_REMOVED_NODES)).getVector()) {
2281 19 : node2edge[nodeID].push_back(item.first);
2282 13 : }
2283 : }
2284 : }
2285 84 : for (auto item : nc) {
2286 : NBNode* n = item.second;
2287 : auto itRN = node2edge.find(n->getID());
2288 83 : if (itRN != node2edge.end()) {
2289 : bool rebuildConnections = false;
2290 : // make a copy because we modify the original
2291 6 : std::vector<std::string> edgeIDs = itRN->second;
2292 20 : for (const std::string& eID : edgeIDs) {
2293 14 : NBEdge* edge = retrieve(eID);
2294 : assert(edge != nullptr);
2295 14 : const double dist = edge->getGeometry().distance2D(n->getPosition(), true);
2296 14 : if (dist != GeomHelper::INVALID_OFFSET && dist <= maxDist) {
2297 12 : std::string idAfter = edge->getID();
2298 12 : int index = 1;
2299 : size_t spos = idAfter.find("#");
2300 12 : if (spos != std::string::npos && spos > 1) {
2301 24 : idAfter = idAfter.substr(0, spos);
2302 : }
2303 62 : while (retrieve(idAfter + "#" + toString(index), true) != nullptr) {
2304 19 : index++;
2305 : }
2306 24 : idAfter += "#" + toString(index);
2307 12 : const bool ok = splitAt(dc, edge, n, edge->getID(), idAfter, edge->getNumLanes(), edge->getNumLanes());
2308 12 : if (ok) {
2309 : rebuildConnections = true;
2310 12 : numSplit++;
2311 12 : NBEdge* secondEdge = retrieve(eID); // original was extracted on splitting
2312 44 : for (std::string& nodeID : StringTokenizer(secondEdge->getParameter(SUMO_PARAM_REMOVED_NODES)).getVector()) {
2313 20 : node2edge[nodeID].push_back(idAfter);
2314 12 : }
2315 : }
2316 : }
2317 : }
2318 6 : if (rebuildConnections) {
2319 24 : for (NBEdge* e : n->getIncomingEdges()) {
2320 18 : e->invalidateConnections(true);
2321 : }
2322 : }
2323 6 : }
2324 : }
2325 1 : return numSplit;
2326 : }
2327 :
2328 : /****************************************************************************/
|