1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file GNEEdge.cpp
11 /// @author Jakob Erdmann
12 /// @date Feb 2011
13 /// @version $Id$
14 ///
15 // A road/street connecting two junctions (netedit-version, adapted from GUIEdge)
16 // Basically a container for an NBEdge with drawing and editing capabilities
17 /****************************************************************************/
18
19
20 // ===========================================================================
21 // included modules
22 // ===========================================================================
23 #include <config.h>
24
25 #include <utils/common/StringTokenizer.h>
26 #include <utils/gui/globjects/GUIGLObjectPopupMenu.h>
27 #include <utils/gui/div/GLHelper.h>
28 #include <netedit/changes/GNEChange_Attribute.h>
29 #include <netedit/changes/GNEChange_Lane.h>
30 #include <netedit/changes/GNEChange_Additional.h>
31 #include <netedit/GNENet.h>
32 #include <netedit/GNEViewNet.h>
33 #include <netedit/GNEUndoList.h>
34 #include <netedit/additionals/GNERouteProbe.h>
35 #include <netedit/additionals/GNEDetectorE2.h>
36 #include <netedit/demandelements/GNEDemandElement.h>
37 #include <utils/options/OptionsCont.h>
38 #include <utils/gui/globjects/GLIncludes.h>
39
40 #include "GNEConnection.h"
41 #include "GNECrossing.h"
42 #include "GNEJunction.h"
43 #include "GNELane.h"
44 #include "GNEEdge.h"
45
46 //#define DEBUG_SMOOTH_GEOM
47 //#define DEBUGCOND(obj) (true)
48
49 // ===========================================================================
50 // static
51 // ===========================================================================
52 const double GNEEdge::SNAP_RADIUS = SUMO_const_halfLaneWidth;
53
getDummyEdge()54 GNEEdge& GNEEdge::getDummyEdge() {
55 // @note: using local static idiom to avoid static initialization order problem
56 static GNEEdge* dummy = new GNEEdge(); // 'static local variable', this line is called only once
57 return *dummy; // this line gets called with the same 'dummy' every time the function is called
58 }
59
60 // ===========================================================================
61 // members methods
62 // ===========================================================================
63
GNEEdge(NBEdge & nbe,GNENet * net,bool wasSplit,bool loaded)64 GNEEdge::GNEEdge(NBEdge& nbe, GNENet* net, bool wasSplit, bool loaded):
65 GNENetElement(net, nbe.getID(), GLO_EDGE, SUMO_TAG_EDGE),
66 myNBEdge(nbe),
67 myGNEJunctionSource(myNet->retrieveJunction(myNBEdge.getFromNode()->getID())),
68 myGNEJunctionDestiny(myNet->retrieveJunction(myNBEdge.getToNode()->getID())),
69 myLanes(0),
70 myAmResponsible(false),
71 myWasSplit(wasSplit),
72 myConnectionStatus(loaded ? FEATURE_LOADED : FEATURE_GUESSED) {
73 // Create lanes
74 int numLanes = myNBEdge.getNumLanes();
75 myLanes.reserve(numLanes);
76 for (int i = 0; i < numLanes; i++) {
77 myLanes.push_back(new GNELane(*this, i));
78 myLanes.back()->incRef("GNEEdge::GNEEdge");
79 }
80 // update Lane geometries
81 for (auto i : myLanes) {
82 i->updateGeometry(true);
83 }
84 }
85
GNEEdge()86 GNEEdge::GNEEdge() :
87 GNENetElement(nullptr, "DUMMY", GLO_EDGE, SUMO_TAG_NOTHING),
88 myNBEdge(NBEdge::DummyEdge) {
89 }
90
~GNEEdge()91 GNEEdge::~GNEEdge() {
92 // Delete references to this eddge in lanes
93 for (auto i : myLanes) {
94 i->decRef("GNEEdge::~GNEEdge");
95 if (i->unreferenced()) {
96 // show extra information for tests
97 WRITE_DEBUG("Deleting unreferenced " + i->getTagStr() + " '" + i->getID() + "' in GNEEdge destructor");
98 delete i;
99 }
100 }
101 // delete references to this eddge in connections
102 for (auto i : myGNEConnections) {
103 i->decRef("GNEEdge::~GNEEdge");
104 if (i->unreferenced()) {
105 // show extra information for tests
106 WRITE_DEBUG("Deleting unreferenced " + i->getTagStr() + " '" + i->getID() + "' in GNEEdge destructor");
107 delete i;
108 }
109 }
110 if (myAmResponsible) {
111 delete &myNBEdge;
112 }
113 }
114
115
116 std::string
generateChildID(SumoXMLTag)117 GNEEdge::generateChildID(SumoXMLTag /*childTag*/) {
118 // currently unused
119 return "";
120 }
121
122
123 void
updateGeometry(bool updateGrid)124 GNEEdge::updateGeometry(bool updateGrid) {
125 // first check if object has to be removed from grid (SUMOTree)
126 if (updateGrid) {
127 myNet->removeGLObjectFromGrid(this);
128 }
129 // Update geometry of lanes
130 for (auto i : myLanes) {
131 i->updateGeometry(updateGrid);
132 }
133 // Update geometry of connections (Only if updateGrid is enabled, because in move mode connections are hidden
134 // (note: only the previous marked as deprecated will be updated)
135 if (updateGrid) {
136 for (auto i : myGNEConnections) {
137 i->updateGeometry(updateGrid);
138 }
139 }
140 // Update geometry of additionals childs vinculated to this edge
141 for (auto i : getAdditionalChilds()) {
142 i->updateGeometry(updateGrid);
143 }
144 // Update geometry of additional parents that have this edge as parent
145 for (auto i : getAdditionalParents()) {
146 i->updateGeometry(updateGrid);
147 }
148 // Update geometry of demand elements childs vinculated to this edge
149 for (auto i : getDemandElementChilds()) {
150 i->updateGeometry(updateGrid);
151 }
152 // Update geometry of demand elements parents that have this edge as parent
153 for (auto i : getDemandElementParents()) {
154 i->updateGeometry(updateGrid);
155 }
156 // last step is to check if object has to be added into grid (SUMOTree) again
157 if (updateGrid) {
158 myNet->addGLObjectIntoGrid(this);
159 }
160 }
161
162
163 Position
getPositionInView() const164 GNEEdge::getPositionInView() const {
165 // currently unused
166 return Position(0, 0);
167 }
168
169
170 bool
clickedOverShapeStart(const Position & pos)171 GNEEdge::clickedOverShapeStart(const Position& pos) {
172 if (myNBEdge.getGeometry().front() != myGNEJunctionSource->getPositionInView()) {
173 return (myNBEdge.getGeometry().front().distanceTo2D(pos) < SNAP_RADIUS);
174 } else {
175 return false;
176 }
177 }
178
179
180 bool
clickedOverShapeEnd(const Position & pos)181 GNEEdge::clickedOverShapeEnd(const Position& pos) {
182 if (myNBEdge.getGeometry().back() != myGNEJunctionDestiny->getPositionInView()) {
183 return (myNBEdge.getGeometry().back().distanceTo2D(pos) < SNAP_RADIUS);
184 } else {
185 return false;
186 }
187 }
188
189
190 void
moveShapeStart(const Position & oldPos,const Position & offset)191 GNEEdge::moveShapeStart(const Position& oldPos, const Position& offset) {
192 // change shape startPosition using oldPosition and offset
193 Position shapeStartEdited = oldPos;
194 shapeStartEdited.add(offset);
195 // snap to active grid
196 shapeStartEdited = myNet->getViewNet()->snapToActiveGrid(shapeStartEdited);
197 // make sure that start and end position are different
198 if (shapeStartEdited != myNBEdge.getGeometry().back()) {
199 // set shape start position without updating grid
200 setShapeStartPos(shapeStartEdited, false);
201 updateGeometry(false);
202 }
203 }
204
205
206 void
moveShapeEnd(const Position & oldPos,const Position & offset)207 GNEEdge::moveShapeEnd(const Position& oldPos, const Position& offset) {
208 // change shape endPosition using oldPosition and offset
209 Position shapeEndEdited = oldPos;
210 shapeEndEdited.add(offset);
211 // snap to active grid
212 shapeEndEdited = myNet->getViewNet()->snapToActiveGrid(shapeEndEdited);
213 // make sure that start and end position are different
214 if (shapeEndEdited != myNBEdge.getGeometry().front()) {
215 // set shape end position without updating grid
216 setShapeEndPos(shapeEndEdited, false);
217 updateGeometry(false);
218 }
219 }
220
221
222 void
commitShapeStartChange(const Position & oldPos,GNEUndoList * undoList)223 GNEEdge::commitShapeStartChange(const Position& oldPos, GNEUndoList* undoList) {
224 // first save current shape start position
225 Position modifiedShapeStartPos = myNBEdge.getGeometry().front();
226 // restore old shape start position
227 setShapeStartPos(oldPos, true);
228 // set attribute using undolist
229 undoList->p_begin("shape start of " + getTagStr());
230 undoList->p_add(new GNEChange_Attribute(this, myNet, GNE_ATTR_SHAPE_START, toString(modifiedShapeStartPos), true, toString(oldPos)));
231 undoList->p_end();
232 }
233
234
235 void
commitShapeEndChange(const Position & oldPos,GNEUndoList * undoList)236 GNEEdge::commitShapeEndChange(const Position& oldPos, GNEUndoList* undoList) {
237 // first save current shape end position
238 Position modifiedShapeEndPos = myNBEdge.getGeometry().back();
239 // restore old shape end position
240 setShapeEndPos(oldPos, true);
241 // set attribute using undolist
242 undoList->p_begin("shape end of " + getTagStr());
243 undoList->p_add(new GNEChange_Attribute(this, myNet, GNE_ATTR_SHAPE_END, toString(modifiedShapeEndPos), true, toString(oldPos)));
244 undoList->p_end();
245 }
246
247
248 void
startGeometryMoving()249 GNEEdge::startGeometryMoving() {
250 // save current centering boundary
251 myMovingGeometryBoundary = getCenteringBoundary();
252 // Save current centering boundary of lanes (and their childs)
253 for (auto i : myLanes) {
254 i->startGeometryMoving();
255 }
256 // Save current centering boundary of additionals childs vinculated to this edge
257 for (auto i : getAdditionalChilds()) {
258 i->startGeometryMoving();
259 }
260 // Save current centering boundary of additional parents that have this edge as parent
261 for (auto i : getAdditionalParents()) {
262 i->startGeometryMoving();
263 }
264 // Save current centering boundary of demand elements childs vinculated to this edge
265 for (auto i : getDemandElementChilds()) {
266 i->startGeometryMoving();
267 }
268 // Save current centering boundary of demand elements parents that have this edge as parent
269 for (auto i : getDemandElementParents()) {
270 i->startGeometryMoving();
271 }
272 }
273
274
275 void
endGeometryMoving()276 GNEEdge::endGeometryMoving() {
277 // check that endGeometryMoving was called only once
278 if (myMovingGeometryBoundary.isInitialised()) {
279 // Remove object from net
280 myNet->removeGLObjectFromGrid(this);
281 // reset myMovingGeometryBoundary
282 myMovingGeometryBoundary.reset();
283 // update geometry without updating grid
284 updateGeometry(false);
285 // Restore centering boundary of lanes (and their childs)
286 for (auto i : myLanes) {
287 i->endGeometryMoving();
288 }
289 // Restore centering boundary of additionals childs vinculated to this edge
290 for (auto i : getAdditionalChilds()) {
291 i->endGeometryMoving();
292 }
293 // Restore centering boundary of additional parents that have this edge as parent
294 for (auto i : getAdditionalParents()) {
295 i->endGeometryMoving();
296 }
297 // Restore centering boundary of demand elements childs vinculated to this edge
298 for (auto i : getDemandElementChilds()) {
299 i->endGeometryMoving();
300 }
301 // Restore centering boundary of demand elements parents that have this edge as parent
302 for (auto i : getDemandElementParents()) {
303 i->endGeometryMoving();
304 }
305 // add object into grid again (using the new centering boundary)
306 myNet->addGLObjectIntoGrid(this);
307 }
308 }
309
310
311 int
getVertexIndex(Position pos,bool createIfNoExist,bool snapToGrid)312 GNEEdge::getVertexIndex(Position pos, bool createIfNoExist, bool snapToGrid) {
313 PositionVector entireGeometry = myNBEdge.getGeometry();
314 // check if position has to be snapped to grid
315 if (snapToGrid) {
316 pos = myNet->getViewNet()->snapToActiveGrid(pos);
317 }
318 double offset = entireGeometry.nearest_offset_to_point2D(pos, true);
319 if (offset == GeomHelper::INVALID_OFFSET) {
320 return -1;
321 }
322 Position newPos = entireGeometry.positionAtOffset2D(offset);
323 // first check if vertex already exists in the inner geometry
324 for (int i = 0; i < (int)entireGeometry.size(); i++) {
325 if (entireGeometry[i].distanceTo2D(newPos) < SNAP_RADIUS) {
326 if (i == 0 || i == (int)(entireGeometry.size() - 1)) {
327 return -1;
328 }
329 // index refers to inner geometry
330 return i - 1;
331 }
332 }
333 // if vertex doesn't exist, insert it
334 if (createIfNoExist) {
335 // check if position has to be snapped to grid
336 if (snapToGrid) {
337 newPos = myNet->getViewNet()->snapToActiveGrid(newPos);
338 }
339 int index = entireGeometry.insertAtClosest(myNet->getViewNet()->snapToActiveGrid(newPos));
340 setGeometry(entireGeometry, false, true);
341 // index refers to inner geometry
342 return (index - 1);
343 } else {
344 return -1;
345 }
346 }
347
348
349 int
getVertexIndex(const double offset,bool createIfNoExist,bool snapToGrid)350 GNEEdge::getVertexIndex(const double offset, bool createIfNoExist, bool snapToGrid) {
351 return getVertexIndex(myNBEdge.getGeometry().positionAtOffset2D(offset), createIfNoExist, snapToGrid);
352 }
353
354
355 int
moveVertexShape(const int index,const Position & oldPos,const Position & offset)356 GNEEdge::moveVertexShape(const int index, const Position& oldPos, const Position& offset) {
357 // obtain inner geometry of edge
358 PositionVector edgeGeometry = myNBEdge.getInnerGeometry();
359 // Make sure that index is valid AND ins't the first and last index
360 if (index != -1) {
361 // check that index is correct before change position
362 if (index < (int)edgeGeometry.size()) {
363 // change position of vertex
364 edgeGeometry[index] = oldPos;
365 edgeGeometry[index].add(offset);
366 // filtern position using snap to active grid
367 edgeGeometry[index] = myNet->getViewNet()->snapToActiveGrid(edgeGeometry[index]);
368 // update edge's geometry without updating RTree (To avoid unnecesary changes in RTree)
369 setGeometry(edgeGeometry, true, false);
370 return index;
371 } else {
372 throw InvalidArgument("Index greater than shape size");
373 }
374 } else {
375 return index;
376 }
377 }
378
379
380 void
moveEntireShape(const PositionVector & oldShape,const Position & offset)381 GNEEdge::moveEntireShape(const PositionVector& oldShape, const Position& offset) {
382 // make a copy of the old shape to change it
383 PositionVector modifiedShape = oldShape;
384 // change all points of the inner geometry using offset
385 for (auto& i : modifiedShape) {
386 i.add(offset);
387 }
388 // restore modified shape
389 setGeometry(modifiedShape, true, false);
390 }
391
392
393 void
commitShapeChange(const PositionVector & oldShape,GNEUndoList * undoList)394 GNEEdge::commitShapeChange(const PositionVector& oldShape, GNEUndoList* undoList) {
395 // restore original shape into shapeToCommit
396 PositionVector innerShapeToCommit = myNBEdge.getInnerGeometry();
397 // first check if second and penultimate isn't in Junction's buubles
398 double buubleRadius = GNEJunction::BUBBLE_RADIUS * myNet->getViewNet()->getVisualisationSettings()->junctionSize.exaggeration;
399 if (myNBEdge.getGeometry().size() > 2 && myNBEdge.getGeometry()[0].distanceTo(myNBEdge.getGeometry()[1]) < buubleRadius) {
400 innerShapeToCommit.removeClosest(innerShapeToCommit[0]);
401 }
402 if (myNBEdge.getGeometry().size() > 2 && myNBEdge.getGeometry()[(int)myNBEdge.getGeometry().size() - 2].distanceTo(myNBEdge.getGeometry()[(int)myNBEdge.getGeometry().size() - 1]) < buubleRadius) {
403 innerShapeToCommit.removeClosest(innerShapeToCommit[(int)innerShapeToCommit.size() - 1]);
404 }
405 // second check if double points has to be removed
406 innerShapeToCommit.removeDoublePoints(SNAP_RADIUS);
407 // show warning if some of edge's shape was merged
408 if (innerShapeToCommit.size() != myNBEdge.getInnerGeometry().size()) {
409 WRITE_WARNING("Merged shape's point")
410 }
411 // finish geometry moving
412 endGeometryMoving();
413 updateGeometry(false);
414 // restore old geometry to allow change attribute (And restore shape if during movement a new point was created
415 setGeometry(oldShape, true, true);
416 // commit new shape
417 undoList->p_begin("moving " + toString(SUMO_ATTR_SHAPE) + " of " + getTagStr());
418 undoList->p_add(new GNEChange_Attribute(this, myNet, SUMO_ATTR_SHAPE, toString(innerShapeToCommit)));
419 undoList->p_end();
420 }
421
422
423 void
deleteGeometryPoint(const Position & pos,bool allowUndo)424 GNEEdge::deleteGeometryPoint(const Position& pos, bool allowUndo) {
425 // obtain index and remove point
426 PositionVector modifiedShape = myNBEdge.getInnerGeometry();
427 int index = modifiedShape.indexOfClosest(pos);
428 modifiedShape.erase(modifiedShape.begin() + index);
429 // set new shape depending of allowUndo
430 if (allowUndo) {
431 myNet->getViewNet()->getUndoList()->p_begin("delete geometry point");
432 setAttribute(SUMO_ATTR_SHAPE, toString(modifiedShape), myNet->getViewNet()->getUndoList());
433 myNet->getViewNet()->getUndoList()->p_end();
434 } else {
435 // set new shape
436 setGeometry(modifiedShape, true, true);
437 }
438 }
439
440
441 void
updateJunctionPosition(GNEJunction * junction,const Position & origPos,bool updateGrid)442 GNEEdge::updateJunctionPosition(GNEJunction* junction, const Position& origPos, bool updateGrid) {
443 Position delta = junction->getNBNode()->getPosition() - origPos;
444 PositionVector geom = myNBEdge.getGeometry();
445 // geometry endpoint need not equal junction position hence we modify it with delta
446 if (junction == myGNEJunctionSource) {
447 geom[0].add(delta);
448 } else {
449 geom[-1].add(delta);
450 }
451 setGeometry(geom, false, updateGrid);
452 }
453
454
455 Boundary
getBoundary() const456 GNEEdge::getBoundary() const {
457 Boundary ret;
458 for (auto i : myLanes) {
459 ret.add(i->getBoundary());
460 }
461 // ensure that geometry points are selectable even if the lane geometry is strange
462 for (const Position& pos : myNBEdge.getGeometry()) {
463 ret.add(pos);
464 }
465 ret.grow(10); // !!! magic value
466 return ret;
467 }
468
469
470 Boundary
getCenteringBoundary() const471 GNEEdge::getCenteringBoundary() const {
472 // Return Boundary depending if myMovingGeometryBoundary is initialised (important for move geometry)
473 if (myMovingGeometryBoundary.isInitialised()) {
474 return myMovingGeometryBoundary;
475 } else {
476 Boundary b = getBoundary();
477 b.grow(20);
478 return b;
479 }
480 }
481
482 const std::string
getOptionalName() const483 GNEEdge::getOptionalName() const {
484 return myNBEdge.getStreetName();
485 }
486
487 GUIGLObjectPopupMenu*
getPopUpMenu(GUIMainWindow & app,GUISUMOAbstractView & parent)488 GNEEdge::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {
489 GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, *this);
490 buildPopupHeader(ret, app);
491 buildCenterPopupEntry(ret);
492 buildNameCopyPopupEntry(ret);
493 // build selection and show parameters menu
494 myNet->getViewNet()->buildSelectionACPopupEntry(ret, this);
495 buildShowParamsPopupEntry(ret);
496 // build position copy entry
497 buildPositionCopyEntry(ret, false);
498 return ret;
499 }
500
501
502 GNEJunction*
getGNEJunctionSource() const503 GNEEdge::getGNEJunctionSource() const {
504 return myGNEJunctionSource;
505 }
506
507
508 GNEJunction*
getGNEJunctionDestiny() const509 GNEEdge::getGNEJunctionDestiny() const {
510 return myGNEJunctionDestiny;
511 }
512
513
514 GNEEdge*
getOppositeEdge() const515 GNEEdge::getOppositeEdge() const {
516 return myNet->retrieveEdge(myGNEJunctionDestiny, myGNEJunctionSource, false);
517 }
518
519
520 void
drawGL(const GUIVisualizationSettings & s) const521 GNEEdge::drawGL(const GUIVisualizationSettings& s) const {
522 /* do something different for connectors?
523 if (myNBEdge.isMacroscopicConnector()) {
524 }
525 */
526 // obtain circle width
527 double circleWidth = SNAP_RADIUS * MIN2((double)1, s.laneWidthExaggeration);
528 double circleWidthSquared = circleWidth * circleWidth;
529 int circleResolution = GNEAttributeCarrier::getCircleResolution(s);
530 // draw the lanes
531 for (auto i : myLanes) {
532 i->drawGL(s);
533 }
534 // draw geometry points if isnt's too small
535 if (s.scale > 8.0) {
536 RGBColor color = s.junctionColorer.getSchemes()[0].getColor(2);
537 if (drawUsingSelectColor() && s.laneColorer.getActive() != 1) {
538 // override with special colors (unless the color scheme is based on selection)
539 color = s.selectedEdgeColor.changedBrightness(-20);
540 }
541 GLHelper::setColor(color);
542 // recognize full transparency and simply don't draw
543 if (color.alpha() > 0) {
544 // push name
545 glPushName(getGlID());
546 // draw geometry points expect initial and final
547 for (int i = 1; i < (int)myNBEdge.getGeometry().size() - 1; i++) {
548 Position pos = myNBEdge.getGeometry()[i];
549 if (!s.drawForSelecting || (myNet->getViewNet()->getPositionInformation().distanceSquaredTo2D(pos) <= (circleWidthSquared + 2))) {
550 glPushMatrix();
551 glTranslated(pos.x(), pos.y(), GLO_JUNCTION - 0.01);
552 // resolution of drawn circle depending of the zoom (To improve smothness)
553 GLHelper::drawFilledCircle(circleWidth, circleResolution);
554 glPopMatrix();
555 // draw elevation or special symbols (Start, End and Block)
556 if (!s.drawForSelecting && myNet->getViewNet()->getMoveOptions().editingElevation()) {
557 glPushMatrix();
558 // Translate to geometry point
559 glTranslated(pos.x(), pos.y(), GLO_JUNCTION);
560 // draw Z value
561 GLHelper::drawText(toString(pos.z()), Position(), GLO_MAX - 5, s.edgeValue.scaledSize(s.scale) / 2, s.edgeValue.color);
562 glPopMatrix();
563 }
564 }
565 }
566 // draw line geometry, start and end points if shapeStart or shape end is edited, and depending of drawForSelecting
567 if (myNet->getViewNet()->getEditModes().networkEditMode == GNE_NMODE_MOVE) {
568 if ((myNBEdge.getGeometry().front() != myGNEJunctionSource->getPositionInView()) &&
569 (!s.drawForSelecting || (myNet->getViewNet()->getPositionInformation().distanceSquaredTo2D(myNBEdge.getGeometry().front()) <= (circleWidthSquared + 2)))) {
570 glPushMatrix();
571 glTranslated(myNBEdge.getGeometry().front().x(), myNBEdge.getGeometry().front().y(), GLO_JUNCTION + 0.01);
572 // resolution of drawn circle depending of the zoom (To improve smothness)
573 GLHelper::drawFilledCircle(circleWidth, circleResolution);
574 glPopMatrix();
575 // draw a "s" over last point depending of drawForSelecting
576 if (!s.drawForSelecting) {
577 glPushMatrix();
578 glTranslated(myNBEdge.getGeometry().front().x(), myNBEdge.getGeometry().front().y(), GLO_JUNCTION + 0.02);
579 GLHelper::drawText("S", Position(), 0, circleWidth, RGBColor::WHITE);
580 glPopMatrix();
581 // draw line between Junction and point
582 glPushMatrix();
583 glTranslated(0, 0, GLO_JUNCTION - 0.01);
584 glLineWidth(4);
585 GLHelper::drawLine(myNBEdge.getGeometry().front(), myGNEJunctionSource->getPositionInView());
586 // draw line between begin point of last lane shape and the first edge shape point
587 GLHelper::drawLine(myNBEdge.getGeometry().front(), myNBEdge.getLanes().back().shape.front());
588 glPopMatrix();
589 }
590 }
591 if ((myNBEdge.getGeometry().back() != myGNEJunctionDestiny->getPositionInView()) &&
592 (!s.drawForSelecting || (myNet->getViewNet()->getPositionInformation().distanceSquaredTo2D(myNBEdge.getGeometry().back()) <= (circleWidthSquared + 2)))) {
593 glPushMatrix();
594 glTranslated(myNBEdge.getGeometry().back().x(), myNBEdge.getGeometry().back().y(), GLO_JUNCTION + 0.01);
595 // resolution of drawn circle depending of the zoom (To improve smothness)
596 GLHelper::drawFilledCircle(circleWidth, circleResolution);
597 glPopMatrix();
598 // draw a "e" over last point depending of drawForSelecting
599 if (!s.drawForSelecting) {
600 glPushMatrix();
601 glTranslated(myNBEdge.getGeometry().back().x(), myNBEdge.getGeometry().back().y(), GLO_JUNCTION + 0.02);
602 GLHelper::drawText("E", Position(), 0, circleWidth, RGBColor::WHITE);
603 glPopMatrix();
604 // draw line between Junction and point
605 glPushMatrix();
606 glTranslated(0, 0, GLO_JUNCTION - 0.01);
607 glLineWidth(4);
608 GLHelper::drawLine(myNBEdge.getGeometry().back(), myGNEJunctionDestiny->getPositionInView());
609 // draw line between last point of first lane shape and the last edge shape point
610 GLHelper::drawLine(myNBEdge.getGeometry().back(), myNBEdge.getLanes().back().shape.back());
611 glPopMatrix();
612 }
613 }
614 }
615 // pop name
616 glPopName();
617 }
618 }
619
620 // (optionally) draw the name and/or the street name if isn't being drawn for selecting
621 const bool drawStreetName = s.streetName.show && (myNBEdge.getStreetName() != "");
622 const bool spreadSuperposed = s.spreadSuperposed && myLanes.back()->drawAsRailway(s) && myNBEdge.isBidiRail();
623 if (!s.drawForSelecting && (s.edgeName.show || drawStreetName || s.edgeValue.show)) {
624 glPushName(getGlID());
625 GNELane* lane1 = myLanes[0];
626 GNELane* lane2 = myLanes[myLanes.size() - 1];
627 Position p = lane1->getShape().positionAtOffset(lane1->getShape().length() / (double) 2.);
628 p.add(lane2->getShape().positionAtOffset(lane2->getShape().length() / (double) 2.));
629 p.mul(.5);
630 if (spreadSuperposed) {
631 // move name to the right of the edge and towards its beginning
632 const double dist = 0.6 * s.edgeName.scaledSize(s.scale);
633 const double shiftA = lane1->getShape().rotationAtOffset(lane1->getShape().length() / (double) 2.) - DEG2RAD(135);
634 Position shift(dist * cos(shiftA), dist * sin(shiftA));
635 p.add(shift);
636 }
637 double angle = lane1->getShape().rotationDegreeAtOffset(lane1->getShape().length() / (double) 2.);
638 angle += 90;
639 if (angle > 90 && angle < 270) {
640 angle -= 180;
641 }
642 if (s.edgeName.show) {
643 drawName(p, s.scale, s.edgeName, angle);
644 }
645 if (drawStreetName) {
646 GLHelper::drawTextSettings(s.streetName, myNBEdge.getStreetName(), p, s.scale, angle);
647 }
648 if (s.edgeValue.show) {
649 double value = lane2->getColorValue(s, s.laneColorer.getActive());
650 GLHelper::drawTextSettings(s.edgeValue, toString(value), p, s.scale, angle);
651 }
652 glPopName();
653 }
654 if (!s.drawForSelecting && (myNet->getViewNet()->getDottedAC() == this)) {
655 // draw dotted contor around the first and last lane
656 const double myHalfLaneWidthFront = myNBEdge.getLaneWidth(myLanes.front()->getIndex()) / 2;
657 const double myHalfLaneWidthBack = spreadSuperposed ? 0 : myNBEdge.getLaneWidth(myLanes.back()->getIndex()) / 2;
658 GLHelper::drawShapeDottedContour(GLO_JUNCTION, myLanes.front()->getShape(), myHalfLaneWidthFront, myLanes.back()->getShape(), -1 * myHalfLaneWidthBack);
659 }
660 glPopMatrix();
661 }
662
663
664 NBEdge*
getNBEdge()665 GNEEdge::getNBEdge() {
666 return &myNBEdge;
667 }
668
669
670 Position
getSplitPos(const Position & clickPos)671 GNEEdge::getSplitPos(const Position& clickPos) {
672 const PositionVector& geom = myNBEdge.getGeometry();
673 int index = geom.indexOfClosest(clickPos);
674 if (geom[index].distanceTo(clickPos) < SNAP_RADIUS) {
675 // split at existing geometry point
676 return geom[index];
677 } else {
678 // split straight between the next two points
679 return geom.positionAtOffset(geom.nearest_offset_to_point2D(clickPos));
680 }
681 }
682
683
684 void
editEndpoint(Position pos,GNEUndoList * undoList)685 GNEEdge::editEndpoint(Position pos, GNEUndoList* undoList) {
686 if ((myNBEdge.getGeometry().front() != myGNEJunctionSource->getPositionInView()) && (myNBEdge.getGeometry().front().distanceTo(pos) < SNAP_RADIUS)) {
687 undoList->p_begin("remove endpoint");
688 setAttribute(GNE_ATTR_SHAPE_START, "", undoList);
689 undoList->p_end();
690 } else if ((myNBEdge.getGeometry().back() != myGNEJunctionDestiny->getPositionInView()) && (myNBEdge.getGeometry().back().distanceTo(pos) < SNAP_RADIUS)) {
691 undoList->p_begin("remove endpoint");
692 setAttribute(GNE_ATTR_SHAPE_END, "", undoList);
693 undoList->p_end();
694 } else {
695 // we need to create new Start/End position over Edge shape, not over clicked position
696 double offset = myNBEdge.getGeometry().nearest_offset_to_point2D(myNet->getViewNet()->snapToActiveGrid(pos), true);
697 if (offset != GeomHelper::INVALID_OFFSET) {
698 PositionVector geom = myNBEdge.getGeometry();
699 // calculate position over edge shape relative to clicked positino
700 Position newPos = geom.positionAtOffset2D(offset);
701 // snap new position to grid
702 newPos = myNet->getViewNet()->snapToActiveGrid(newPos);
703 undoList->p_begin("set endpoint");
704 int index = geom.indexOfClosest(pos);
705 // check if snap to existing geometry
706 if (geom[index].distanceTo(pos) < SNAP_RADIUS) {
707 pos = geom[index];
708 }
709 Position destPos = myGNEJunctionDestiny->getNBNode()->getPosition();
710 Position sourcePos = myGNEJunctionSource->getNBNode()->getPosition();
711 if (pos.distanceTo2D(destPos) < pos.distanceTo2D(sourcePos)) {
712 setAttribute(GNE_ATTR_SHAPE_END, toString(newPos), undoList);
713 myGNEJunctionDestiny->invalidateShape();
714 } else {
715 setAttribute(GNE_ATTR_SHAPE_START, toString(newPos), undoList);
716 myGNEJunctionSource->invalidateShape();
717 }
718 // possibly existing inner point is no longer needed
719 if (myNBEdge.getInnerGeometry().size() > 0 && getVertexIndex(pos, false, false) != -1) {
720 deleteGeometryPoint(pos, false);
721 }
722 undoList->p_end();
723 }
724 }
725 }
726
727
728 void
resetEndpoint(const Position & pos,GNEUndoList * undoList)729 GNEEdge::resetEndpoint(const Position& pos, GNEUndoList* undoList) {
730 Position destPos = myGNEJunctionDestiny->getNBNode()->getPosition();
731 Position sourcePos = myGNEJunctionSource->getNBNode()->getPosition();
732 if (pos.distanceTo2D(destPos) < pos.distanceTo2D(sourcePos)) {
733 setAttribute(GNE_ATTR_SHAPE_END, toString(destPos), undoList);
734 myGNEJunctionDestiny->invalidateShape();
735 } else {
736 setAttribute(GNE_ATTR_SHAPE_START, toString(sourcePos), undoList);
737 myGNEJunctionSource->invalidateShape();
738 }
739 }
740
741
742 void
setGeometry(PositionVector geom,bool inner,bool updateGrid)743 GNEEdge::setGeometry(PositionVector geom, bool inner, bool updateGrid) {
744 if (updateGrid) {
745 // first remove object from net grid
746 myNet->removeGLObjectFromGrid(this);
747 }
748 // set new geometry
749 myNBEdge.setGeometry(geom, inner);
750 if (updateGrid) {
751 // add object into net again
752 myNet->addGLObjectIntoGrid(this);
753 }
754 updateGeometry(updateGrid);
755 myGNEJunctionSource->invalidateShape();
756 myGNEJunctionDestiny->invalidateShape();
757 }
758
759
760 void
remakeGNEConnections()761 GNEEdge::remakeGNEConnections() {
762 // create new and removed unused GNEConnectinos
763 const std::vector<NBEdge::Connection>& connections = myNBEdge.getConnections();
764 // create a vector to keep retrieved and created connections
765 std::vector<GNEConnection*> retrievedConnections;
766 // iterate over NBEdge::Connections of GNEEdge
767 for (auto it : connections) {
768 // retrieve existent GNEConnection, or create it
769 GNEConnection* retrievedGNEConnection = retrieveGNEConnection(it.fromLane, it.toEdge, it.toLane);
770 retrievedGNEConnection->updateLinkState();
771 retrievedConnections.push_back(retrievedGNEConnection);
772 // check if previously this GNEConnections exists, and if true, remove it from myGNEConnections
773 std::vector<GNEConnection*>::iterator retrievedExists = std::find(myGNEConnections.begin(), myGNEConnections.end(), retrievedGNEConnection);
774 if (retrievedExists != myGNEConnections.end()) {
775 myGNEConnections.erase(retrievedExists);
776 } else {
777 // include reference to created GNEConnection
778 retrievedGNEConnection->incRef("GNEEdge::remakeGNEConnections");
779 }
780 // mark it as deprecated
781 retrievedGNEConnection->markConnectionGeometryDeprecated();
782 }
783 // delete non retrieved GNEConnections
784 for (auto it : myGNEConnections) {
785 // remove it from Tree
786 myNet->removeGLObjectFromGrid(it);
787 it->decRef();
788 if (it->unreferenced()) {
789 // show extra information for tests
790 WRITE_DEBUG("Deleting unreferenced " + it->getTagStr() + " '" + it->getID() + "' in rebuildGNEConnections()");
791 delete it;
792 }
793 }
794 // copy retrieved (existent and created) GNECrossigns to myGNEConnections
795 myGNEConnections = retrievedConnections;
796 }
797
798
799 void
clearGNEConnections()800 GNEEdge::clearGNEConnections() {
801 // Drop all existents connections that aren't referenced anymore
802 for (auto i : myGNEConnections) {
803 // check if connection is selected
804 if (i->isAttributeCarrierSelected()) {
805 i->unselectAttributeCarrier();
806 }
807 // remove it from Tree
808 myNet->removeGLObjectFromGrid(i);
809 // Dec reference of connection
810 i->decRef("GNEEdge::clearGNEConnections");
811 // Delete GNEConnectionToErase if is unreferenced
812 if (i->unreferenced()) {
813 // show extra information for tests
814 WRITE_DEBUG("Deleting unreferenced " + i->getTagStr() + " '" + i->getID() + "' in clearGNEConnections()");
815 delete i;
816 }
817 }
818 myGNEConnections.clear();
819 }
820
821
822 int
getRouteProbeRelativePosition(GNERouteProbe * routeProbe) const823 GNEEdge::getRouteProbeRelativePosition(GNERouteProbe* routeProbe) const {
824 std::vector<GNEAdditional*> routeProbes;
825 for (auto i : getAdditionalChilds()) {
826 if (i->getTagProperty().getTag() == routeProbe->getTagProperty().getTag()) {
827 routeProbes.push_back(i);
828 }
829 }
830 // return index of routeProbe in routeProbes vector
831 auto it = std::find(routeProbes.begin(), routeProbes.end(), routeProbe);
832 if (it == routeProbes.end()) {
833 return -1;
834 } else {
835 return (int)(it - routeProbes.begin());
836 }
837 }
838
839
840 std::vector<GNECrossing*>
getGNECrossings()841 GNEEdge::getGNECrossings() {
842 std::vector<GNECrossing*> crossings;
843 for (auto i : myGNEJunctionSource->getGNECrossings()) {
844 if (i->checkEdgeBelong(this)) {
845 crossings.push_back(i);
846 }
847 }
848 for (auto i : myGNEJunctionDestiny->getGNECrossings()) {
849 if (i->checkEdgeBelong(this)) {
850 crossings.push_back(i);
851 }
852 }
853 return crossings;
854 }
855
856
857 void
copyTemplate(GNEEdge * tpl,GNEUndoList * undoList)858 GNEEdge::copyTemplate(GNEEdge* tpl, GNEUndoList* undoList) {
859 undoList->p_begin("copy template");
860 setAttribute(SUMO_ATTR_NUMLANES, tpl->getAttribute(SUMO_ATTR_NUMLANES), undoList);
861 setAttribute(SUMO_ATTR_TYPE, tpl->getAttribute(SUMO_ATTR_TYPE), undoList);
862 setAttribute(SUMO_ATTR_PRIORITY, tpl->getAttribute(SUMO_ATTR_PRIORITY), undoList);
863 setAttribute(SUMO_ATTR_SPREADTYPE, tpl->getAttribute(SUMO_ATTR_SPREADTYPE), undoList);
864 // copy raw values for lane-specific attributes
865 setAttribute(SUMO_ATTR_SPEED, toString(myNBEdge.getSpeed()), undoList);
866 setAttribute(SUMO_ATTR_WIDTH, toString(myNBEdge.getLaneWidth()), undoList);
867 setAttribute(SUMO_ATTR_ENDOFFSET, toString(myNBEdge.getEndOffset()), undoList);
868 // copy lane attributes as well
869 for (int i = 0; i < (int)myLanes.size(); i++) {
870 myLanes[i]->setAttribute(SUMO_ATTR_ALLOW, tpl->myLanes[i]->getAttribute(SUMO_ATTR_ALLOW), undoList);
871 myLanes[i]->setAttribute(SUMO_ATTR_SPEED, tpl->myLanes[i]->getAttribute(SUMO_ATTR_SPEED), undoList);
872 myLanes[i]->setAttribute(SUMO_ATTR_WIDTH, tpl->myLanes[i]->getAttribute(SUMO_ATTR_WIDTH), undoList);
873 myLanes[i]->setAttribute(SUMO_ATTR_ENDOFFSET, tpl->myLanes[i]->getAttribute(SUMO_ATTR_ENDOFFSET), undoList);
874 }
875 undoList->p_end();
876 }
877
878
879 std::set<GUIGlID>
getLaneGlIDs()880 GNEEdge::getLaneGlIDs() {
881 std::set<GUIGlID> result;
882 for (auto i : myLanes) {
883 result.insert(i->getGlID());
884 }
885 return result;
886 }
887
888
889 const std::vector<GNELane*>&
getLanes()890 GNEEdge::getLanes() {
891 return myLanes;
892 }
893
894
895 const std::vector<GNEConnection*>&
getGNEConnections()896 GNEEdge::getGNEConnections() {
897 return myGNEConnections;
898 }
899
900
901 bool
wasSplit()902 GNEEdge::wasSplit() {
903 return myWasSplit;
904 }
905
906
907 std::string
getAttribute(SumoXMLAttr key) const908 GNEEdge::getAttribute(SumoXMLAttr key) const {
909 switch (key) {
910 case SUMO_ATTR_ID:
911 return getMicrosimID();
912 case SUMO_ATTR_FROM:
913 return myGNEJunctionSource->getMicrosimID();
914 case SUMO_ATTR_TO:
915 return myGNEJunctionDestiny->getMicrosimID();
916 case SUMO_ATTR_NUMLANES:
917 return toString(myNBEdge.getNumLanes());
918 case SUMO_ATTR_PRIORITY:
919 return toString(myNBEdge.getPriority());
920 case SUMO_ATTR_LENGTH:
921 return toString(myNBEdge.getFinalLength());
922 case SUMO_ATTR_TYPE:
923 return myNBEdge.getTypeID();
924 case SUMO_ATTR_SHAPE:
925 return toString(myNBEdge.getInnerGeometry());
926 case SUMO_ATTR_SPREADTYPE:
927 return toString(myNBEdge.getLaneSpreadFunction());
928 case SUMO_ATTR_NAME:
929 return myNBEdge.getStreetName();
930 case SUMO_ATTR_ALLOW:
931 return (getVehicleClassNames(myNBEdge.getPermissions()) + (myNBEdge.hasLaneSpecificPermissions() ? " (combined!)" : ""));
932 case SUMO_ATTR_DISALLOW: {
933 return (getVehicleClassNames(invertPermissions(myNBEdge.getPermissions())) + (myNBEdge.hasLaneSpecificPermissions() ? " (combined!)" : ""));
934 }
935 case SUMO_ATTR_SPEED:
936 if (myNBEdge.hasLaneSpecificSpeed()) {
937 return "lane specific";
938 } else {
939 return toString(myNBEdge.getSpeed());
940 }
941 case SUMO_ATTR_WIDTH:
942 if (myNBEdge.hasLaneSpecificWidth()) {
943 return "lane specific";
944 } else {
945 return toString(myNBEdge.getLaneWidth());
946 }
947 case SUMO_ATTR_ENDOFFSET:
948 if (myNBEdge.hasLaneSpecificEndOffset()) {
949 return "lane specific";
950 } else {
951 return toString(myNBEdge.getEndOffset());
952 }
953 case GNE_ATTR_MODIFICATION_STATUS:
954 return myConnectionStatus;
955 case GNE_ATTR_SHAPE_START:
956 if (myNBEdge.getGeometry().front() == myGNEJunctionSource->getPositionInView()) {
957 return "";
958 } else {
959 return toString(myNBEdge.getGeometry().front());
960 }
961 case GNE_ATTR_SHAPE_END:
962 if (myNBEdge.getGeometry().back() == myGNEJunctionDestiny->getPositionInView()) {
963 return "";
964 } else {
965 return toString(myNBEdge.getGeometry().back());
966 }
967 case GNE_ATTR_BIDIR:
968 return toString(myNBEdge.isBidiRail());
969 case GNE_ATTR_SELECTED:
970 return toString(isAttributeCarrierSelected());
971 case GNE_ATTR_GENERIC:
972 return getGenericParametersStr();
973 default:
974 throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
975 }
976 }
977
978 std::string
getAttributeForSelection(SumoXMLAttr key) const979 GNEEdge::getAttributeForSelection(SumoXMLAttr key) const {
980 std::string result = getAttribute(key);
981 if ((key == SUMO_ATTR_ALLOW || key == SUMO_ATTR_DISALLOW) && result.find("all") != std::string::npos) {
982 result += " " + getVehicleClassNames(SVCAll, true);
983 }
984 return result;
985 }
986
987 void
setAttribute(SumoXMLAttr key,const std::string & value,GNEUndoList * undoList)988 GNEEdge::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {
989 switch (key) {
990 case SUMO_ATTR_WIDTH:
991 case SUMO_ATTR_ENDOFFSET:
992 case SUMO_ATTR_SPEED:
993 case SUMO_ATTR_ALLOW:
994 case SUMO_ATTR_DISALLOW: {
995 undoList->p_begin("change " + getTagStr() + " attribute");
996 const std::string origValue = myLanes.at(0)->getAttribute(key); // will have intermediate value of "lane specific"
997 // lane specific attributes need to be changed via lanes to allow undo
998 for (auto it : myLanes) {
999 it->setAttribute(key, value, undoList);
1000 }
1001 // ensure that the edge value is also changed. Actually this sets the lane attributes again but it does not matter
1002 undoList->p_add(new GNEChange_Attribute(this, myNet, key, value, true, origValue));
1003 undoList->p_end();
1004 break;
1005 }
1006 case SUMO_ATTR_FROM: {
1007 undoList->p_begin("change " + getTagStr() + " attribute");
1008 // Remove edge from crossings of junction source
1009 removeEdgeFromCrossings(myGNEJunctionSource, undoList);
1010 // continue changing from junction
1011 GNEJunction* oldGNEJunctionSource = myGNEJunctionSource;
1012 myGNEJunctionSource->setLogicValid(false, undoList);
1013 undoList->p_add(new GNEChange_Attribute(this, myNet, key, value));
1014 myGNEJunctionSource->setLogicValid(false, undoList);
1015 myNet->retrieveJunction(value)->setLogicValid(false, undoList);
1016 setAttribute(GNE_ATTR_SHAPE_START, toString(myGNEJunctionSource->getNBNode()->getPosition()), undoList);
1017 myGNEJunctionSource->invalidateShape();
1018 undoList->p_end();
1019 // update geometries of all implicated junctions
1020 oldGNEJunctionSource->updateGeometry(true);
1021 myGNEJunctionSource->updateGeometry(true);
1022 myGNEJunctionDestiny->updateGeometry(true);
1023 break;
1024 }
1025 case SUMO_ATTR_TO: {
1026 undoList->p_begin("change " + getTagStr() + " attribute");
1027 // Remove edge from crossings of junction destiny
1028 removeEdgeFromCrossings(myGNEJunctionDestiny, undoList);
1029 // continue changing destiny junction
1030 GNEJunction* oldGNEJunctionDestiny = myGNEJunctionDestiny;
1031 myGNEJunctionDestiny->setLogicValid(false, undoList);
1032 undoList->p_add(new GNEChange_Attribute(this, myNet, key, value));
1033 myGNEJunctionDestiny->setLogicValid(false, undoList);
1034 myNet->retrieveJunction(value)->setLogicValid(false, undoList);
1035 setAttribute(GNE_ATTR_SHAPE_END, toString(myGNEJunctionDestiny->getNBNode()->getPosition()), undoList);
1036 myGNEJunctionDestiny->invalidateShape();
1037 undoList->p_end();
1038 // update geometries of all implicated junctions
1039 oldGNEJunctionDestiny->updateGeometry(true);
1040 myGNEJunctionDestiny->updateGeometry(true);
1041 myGNEJunctionSource->updateGeometry(true);
1042 break;
1043 }
1044 case SUMO_ATTR_ID:
1045 case SUMO_ATTR_PRIORITY:
1046 case SUMO_ATTR_LENGTH:
1047 case SUMO_ATTR_TYPE:
1048 case SUMO_ATTR_SPREADTYPE:
1049 case GNE_ATTR_MODIFICATION_STATUS:
1050 case GNE_ATTR_SHAPE_START:
1051 case GNE_ATTR_SHAPE_END:
1052 case GNE_ATTR_SELECTED:
1053 case GNE_ATTR_GENERIC:
1054 undoList->p_add(new GNEChange_Attribute(this, myNet, key, value));
1055 break;
1056 case SUMO_ATTR_NAME:
1057 // user cares about street names. Make sure they appear in the output
1058 OptionsCont::getOptions().resetWritable();
1059 OptionsCont::getOptions().set("output.street-names", "true");
1060 undoList->p_add(new GNEChange_Attribute(this, myNet, key, value));
1061 break;
1062 case SUMO_ATTR_NUMLANES:
1063 if (value != getAttribute(key)) {
1064 // Remove edge from crossings of junction source
1065 removeEdgeFromCrossings(myGNEJunctionSource, undoList);
1066 // Remove edge from crossings of junction destiny
1067 removeEdgeFromCrossings(myGNEJunctionDestiny, undoList);
1068 // set num lanes
1069 setNumLanes(parse<int>(value), undoList);
1070 }
1071 break;
1072 case SUMO_ATTR_SHAPE:
1073 // @note: assumes value of inner geometry!
1074 // actually the geometry is already updated (incrementally
1075 // during mouse movement). We set the restore point to the end
1076 // of the last change-set
1077 undoList->p_add(new GNEChange_Attribute(this, myNet, key, value));
1078 break;
1079 case GNE_ATTR_BIDIR:
1080 throw InvalidArgument("Attribute of '" + toString(key) + "' cannot be modified");
1081 default:
1082 throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
1083 }
1084 }
1085
1086
1087 bool
isValid(SumoXMLAttr key,const std::string & value)1088 GNEEdge::isValid(SumoXMLAttr key, const std::string& value) {
1089 switch (key) {
1090 case SUMO_ATTR_ID:
1091 return SUMOXMLDefinitions::isValidNetID(value) && (myNet->retrieveEdge(value, false) == nullptr);
1092 case SUMO_ATTR_FROM: {
1093 // check that is a valid ID and is different of ID of junction destiny
1094 if (SUMOXMLDefinitions::isValidNetID(value) && (value != myGNEJunctionDestiny->getMicrosimID())) {
1095 GNEJunction* junctionFrom = myNet->retrieveJunction(value, false);
1096 // check that there isn't already another edge with the same From and To Edge
1097 if ((junctionFrom != nullptr) && (myNet->retrieveEdge(junctionFrom, myGNEJunctionDestiny, false) == nullptr)) {
1098 return true;
1099 } else {
1100 return false;
1101 }
1102 } else {
1103 return false;
1104 }
1105 }
1106 case SUMO_ATTR_TO: {
1107 // check that is a valid ID and is different of ID of junction Source
1108 if (SUMOXMLDefinitions::isValidNetID(value) && (value != myGNEJunctionSource->getMicrosimID())) {
1109 GNEJunction* junctionTo = myNet->retrieveJunction(value, false);
1110 // check that there isn't already another edge with the same From and To Edge
1111 if ((junctionTo != nullptr) && (myNet->retrieveEdge(myGNEJunctionSource, junctionTo, false) == nullptr)) {
1112 return true;
1113 } else {
1114 return false;
1115 }
1116 } else {
1117 return false;
1118 }
1119 }
1120 case SUMO_ATTR_SPEED:
1121 return canParse<double>(value) && (parse<double>(value) > 0);
1122 case SUMO_ATTR_NUMLANES:
1123 return canParse<int>(value) && (parse<double>(value) > 0);
1124 case SUMO_ATTR_PRIORITY:
1125 return canParse<int>(value);
1126 case SUMO_ATTR_LENGTH:
1127 return canParse<double>(value) && ((parse<double>(value) > 0) || (parse<double>(value) == NBEdge::UNSPECIFIED_LOADED_LENGTH));
1128 case SUMO_ATTR_ALLOW:
1129 case SUMO_ATTR_DISALLOW:
1130 return canParseVehicleClasses(value);
1131 case SUMO_ATTR_TYPE:
1132 return true;
1133 case SUMO_ATTR_SHAPE:
1134 // empty shapes are allowed
1135 return canParse<PositionVector>(value);
1136 case SUMO_ATTR_SPREADTYPE:
1137 return SUMOXMLDefinitions::LaneSpreadFunctions.hasString(value);
1138 case SUMO_ATTR_NAME:
1139 return true;
1140 case SUMO_ATTR_WIDTH:
1141 return canParse<double>(value) && ((parse<double>(value) > 0) || (parse<double>(value) == NBEdge::UNSPECIFIED_WIDTH));
1142 case SUMO_ATTR_ENDOFFSET:
1143 return canParse<double>(value) && parse<double>(value) >= 0 && parse<double>(value) < myNBEdge.getLoadedLength();
1144 case GNE_ATTR_SHAPE_START: {
1145 if (value.empty()) {
1146 return true;
1147 } else if (canParse<Position>(value)) {
1148 Position shapeStart = parse<Position>(value);
1149 return (shapeStart != myNBEdge.getGeometry()[-1]);
1150 } else {
1151 return false;
1152 }
1153 }
1154 case GNE_ATTR_SHAPE_END: {
1155 if (value.empty()) {
1156 return true;
1157 } else if (canParse<Position>(value)) {
1158 Position shapeEnd = parse<Position>(value);
1159 return (shapeEnd != myNBEdge.getGeometry()[0]);
1160 } else {
1161 return false;
1162 }
1163 }
1164 case GNE_ATTR_BIDIR:
1165 return false;
1166 case GNE_ATTR_SELECTED:
1167 return canParse<bool>(value);
1168 case GNE_ATTR_GENERIC:
1169 return isGenericParametersValid(value);
1170 default:
1171 throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
1172 }
1173 }
1174
1175
1176 std::string
getGenericParametersStr() const1177 GNEEdge::getGenericParametersStr() const {
1178 std::string result;
1179 // Generate an string using the following structure: "key1=value1|key2=value2|...
1180 for (auto i : myNBEdge.getParametersMap()) {
1181 result += i.first + "=" + i.second + "|";
1182 }
1183 // remove the last "|"
1184 if (!result.empty()) {
1185 result.pop_back();
1186 }
1187 return result;
1188 }
1189
1190
1191 std::vector<std::pair<std::string, std::string> >
getGenericParameters() const1192 GNEEdge::getGenericParameters() const {
1193 std::vector<std::pair<std::string, std::string> > result;
1194 // iterate over parameters map and fill result
1195 for (auto i : myNBEdge.getParametersMap()) {
1196 result.push_back(std::make_pair(i.first, i.second));
1197 }
1198 return result;
1199 }
1200
1201
1202 void
setGenericParametersStr(const std::string & value)1203 GNEEdge::setGenericParametersStr(const std::string& value) {
1204 // clear parameters
1205 myNBEdge.clearParameter();
1206 // separate value in a vector of string using | as separator
1207 std::vector<std::string> parsedValues;
1208 StringTokenizer stValues(value, "|", true);
1209 while (stValues.hasNext()) {
1210 parsedValues.push_back(stValues.next());
1211 }
1212 // check that parsed values (A=B)can be parsed in generic parameters
1213 for (auto i : parsedValues) {
1214 std::vector<std::string> parsedParameters;
1215 StringTokenizer stParam(i, "=", true);
1216 while (stParam.hasNext()) {
1217 parsedParameters.push_back(stParam.next());
1218 }
1219 // Check that parsed parameters are exactly two and contains valid chracters
1220 if (parsedParameters.size() == 2 && SUMOXMLDefinitions::isValidGenericParameterKey(parsedParameters.front()) && SUMOXMLDefinitions::isValidGenericParameterValue(parsedParameters.back())) {
1221 myNBEdge.setParameter(parsedParameters.front(), parsedParameters.back());
1222 }
1223 }
1224 }
1225
1226
1227 void
setResponsible(bool newVal)1228 GNEEdge::setResponsible(bool newVal) {
1229 myAmResponsible = newVal;
1230 }
1231
1232 // ===========================================================================
1233 // private
1234 // ===========================================================================
1235
1236 void
setAttribute(SumoXMLAttr key,const std::string & value)1237 GNEEdge::setAttribute(SumoXMLAttr key, const std::string& value) {
1238 switch (key) {
1239 case SUMO_ATTR_ID:
1240 myNet->renameEdge(this, value);
1241 break;
1242 case SUMO_ATTR_FROM:
1243 myNet->changeEdgeEndpoints(this, value, myGNEJunctionDestiny->getMicrosimID());
1244 // update this edge of list of outgoings edges of the old GNEJunctionSource
1245 myGNEJunctionSource->removeOutgoingGNEEdge(this);
1246 // update GNEJunctionSource
1247 myGNEJunctionSource = myNet->retrieveJunction(myNBEdge.getFromNode()->getID());
1248 // update this edge of list of outgoings edges of the new GNEJunctionSource
1249 myGNEJunctionSource->addOutgoingGNEEdge(this);
1250 break;
1251 case SUMO_ATTR_TO:
1252 myNet->changeEdgeEndpoints(this, myGNEJunctionSource->getMicrosimID(), value);
1253 // update this edge of list of incomings edges of the old GNEJunctionDestiny
1254 myGNEJunctionDestiny->removeIncomingGNEEdge(this);
1255 // update GNEJunctionDestiny
1256 myGNEJunctionDestiny = myNet->retrieveJunction(myNBEdge.getToNode()->getID());
1257 // update this edge of list of incomings edges of the new GNEJunctionDestiny
1258 myGNEJunctionDestiny->addIncomingGNEEdge(this);
1259 break;
1260 case SUMO_ATTR_NUMLANES:
1261 throw InvalidArgument("GNEEdge::setAttribute (private) called for attr SUMO_ATTR_NUMLANES. This should never happen");
1262 break;
1263 case SUMO_ATTR_PRIORITY:
1264 myNBEdge.myPriority = parse<int>(value);
1265 break;
1266 case SUMO_ATTR_LENGTH:
1267 myNBEdge.setLoadedLength(parse<double>(value));
1268 break;
1269 case SUMO_ATTR_TYPE:
1270 myNBEdge.myType = value;
1271 break;
1272 case SUMO_ATTR_SHAPE:
1273 setGeometry(parse<PositionVector>(value), true, true);
1274 break;
1275 case SUMO_ATTR_SPREADTYPE:
1276 myNBEdge.setLaneSpreadFunction(SUMOXMLDefinitions::LaneSpreadFunctions.get(value));
1277 break;
1278 case SUMO_ATTR_NAME:
1279 myNBEdge.setStreetName(value);
1280 break;
1281 case SUMO_ATTR_SPEED:
1282 myNBEdge.setSpeed(-1, parse<double>(value));
1283 break;
1284 case SUMO_ATTR_WIDTH:
1285 myNBEdge.setLaneWidth(-1, parse<double>(value));
1286 break;
1287 case SUMO_ATTR_ENDOFFSET:
1288 myNBEdge.setEndOffset(-1, parse<double>(value));
1289 break;
1290 case SUMO_ATTR_ALLOW:
1291 break; // no edge value
1292 case SUMO_ATTR_DISALLOW:
1293 break; // no edge value
1294 case GNE_ATTR_MODIFICATION_STATUS:
1295 myConnectionStatus = value;
1296 if (value == FEATURE_GUESSED) {
1297 WRITE_DEBUG("invalidating (removing) connections of edge '" + getID() + "' due it were guessed");
1298 myNBEdge.invalidateConnections(true);
1299 clearGNEConnections();
1300 } else if (value != FEATURE_GUESSED) {
1301 WRITE_DEBUG("declaring connections of edge '" + getID() + "' as loaded (It will not be removed)");
1302 myNBEdge.declareConnectionsAsLoaded();
1303 }
1304 break;
1305 case GNE_ATTR_SHAPE_START: {
1306 // get geometry of NBEdge, remove FIRST element with the new value (or with the Junction Source position) and set it back to edge
1307 Position newShapeStart;
1308 if (value == "") {
1309 newShapeStart = myGNEJunctionSource->getPositionInView();
1310 } else {
1311 newShapeStart = parse<Position>(value);
1312 }
1313 // set shape start position
1314 setShapeStartPos(newShapeStart, true);
1315 break;
1316 }
1317 case GNE_ATTR_SHAPE_END: {
1318 // get geometry of NBEdge, remove LAST element with the new value (or with the Junction Destiny position) and set it back to edge
1319 Position newShapeEnd;
1320 if (value == "") {
1321 newShapeEnd = myGNEJunctionDestiny->getPositionInView();
1322 } else {
1323 newShapeEnd = parse<Position>(value);
1324 }
1325 // set shape end position
1326 setShapeEndPos(newShapeEnd, true);
1327 break;
1328 }
1329 case GNE_ATTR_BIDIR:
1330 throw InvalidArgument("Attribute of '" + toString(key) + "' cannot be modified");
1331 case GNE_ATTR_SELECTED:
1332 if (parse<bool>(value)) {
1333 selectAttributeCarrier();
1334 } else {
1335 unselectAttributeCarrier();
1336 }
1337 break;
1338 case GNE_ATTR_GENERIC:
1339 setGenericParametersStr(value);
1340 break;
1341 default:
1342 throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
1343 }
1344 // check if updated attribute requieres update geometry
1345 if (myTagProperty.hasAttribute(key) && myTagProperty.getAttributeProperties(key).requiereUpdateGeometry()) {
1346 updateGeometry(true);
1347 }
1348 }
1349
1350
1351 void
setNumLanes(int numLanes,GNEUndoList * undoList)1352 GNEEdge::setNumLanes(int numLanes, GNEUndoList* undoList) {
1353 undoList->p_begin("change number of " + toString(SUMO_TAG_LANE) + "s");
1354 myGNEJunctionSource->setLogicValid(false, undoList);
1355 myGNEJunctionDestiny->setLogicValid(false, undoList);
1356
1357 const int oldNumLanes = (int)myLanes.size();
1358 for (int i = oldNumLanes; i < numLanes; i++) {
1359 // since the GNELane does not exist yet, it cannot have yet been referenced so we only pass a zero-pointer
1360 undoList->add(new GNEChange_Lane(this, nullptr,
1361 myNBEdge.getLaneStruct(oldNumLanes - 1), true), true);
1362 }
1363 for (int i = oldNumLanes - 1; i > numLanes - 1; i--) {
1364 // delete leftmost lane
1365 undoList->add(new GNEChange_Lane(this, myLanes[i], myNBEdge.getLaneStruct(i), false), true);
1366 }
1367 undoList->p_end();
1368 }
1369
1370
1371 void
addLane(GNELane * lane,const NBEdge::Lane & laneAttrs,bool recomputeConnections)1372 GNEEdge::addLane(GNELane* lane, const NBEdge::Lane& laneAttrs, bool recomputeConnections) {
1373 // boundary of edge depends of number of lanes. We need to extract if before add or remove lane
1374 myNet->removeGLObjectFromGrid(this);
1375 const int index = lane ? lane->getIndex() : myNBEdge.getNumLanes();
1376 // the laneStruct must be created first to ensure we have some geometry
1377 // unless the connections are fully recomputed, existing indices must be shifted
1378 myNBEdge.addLane(index, true, recomputeConnections, !recomputeConnections);
1379 if (lane) {
1380 // restore a previously deleted lane
1381 myLanes.insert(myLanes.begin() + index, lane);
1382
1383 } else {
1384 // create a new lane by copying leftmost lane
1385 lane = new GNELane(*this, index);
1386 myLanes.push_back(lane);
1387 }
1388 lane->incRef("GNEEdge::addLane");
1389 // check if lane is selected
1390 if (lane->isAttributeCarrierSelected()) {
1391 lane->selectAttributeCarrier();
1392 }
1393 // we copy all attributes except shape since this is recomputed from edge shape
1394 myNBEdge.setSpeed(lane->getIndex(), laneAttrs.speed);
1395 myNBEdge.setPermissions(laneAttrs.permissions, lane->getIndex());
1396 myNBEdge.setPreferredVehicleClass(laneAttrs.preferred, lane->getIndex());
1397 myNBEdge.setEndOffset(lane->getIndex(), laneAttrs.endOffset);
1398 myNBEdge.setLaneWidth(lane->getIndex(), laneAttrs.width);
1399 // udate indices
1400 for (int i = 0; i < (int)myLanes.size(); ++i) {
1401 myLanes[i]->setIndex(i);
1402 }
1403 /* while technically correct, this looks ugly
1404 myGNEJunctionSource->invalidateShape();
1405 myGNEJunctionDestiny->invalidateShape();
1406 */
1407 // Remake connections for this edge and all edges that target this lane
1408 remakeGNEConnections();
1409 // remake connections of all edges of junction source and destiny
1410 for (auto i : myGNEJunctionSource->getGNEEdges()) {
1411 i->remakeGNEConnections();
1412 }
1413 // remake connections of all edges of junction source and destiny
1414 for (auto i : myGNEJunctionDestiny->getGNEEdges()) {
1415 i->remakeGNEConnections();
1416 }
1417 // add object again
1418 myNet->addGLObjectIntoGrid(this);
1419 // Update geometry with the new lane
1420 updateGeometry(true);
1421 }
1422
1423
1424 void
removeLane(GNELane * lane,bool recomputeConnections)1425 GNEEdge::removeLane(GNELane* lane, bool recomputeConnections) {
1426 // boundary of edge depends of number of lanes. We need to extract if before add or remove lane
1427 myNet->removeGLObjectFromGrid(this);
1428 if (myLanes.size() == 0) {
1429 throw ProcessError("Should not remove the last " + toString(SUMO_TAG_LANE) + " from an " + getTagStr());
1430 }
1431 if (lane == nullptr) {
1432 lane = myLanes.back();
1433 }
1434 // check if lane is selected
1435 if (lane->isAttributeCarrierSelected()) {
1436 lane->unselectAttributeCarrier();
1437 }
1438 // Delete lane of edge's container
1439 // unless the connections are fully recomputed, existing indices must be shifted
1440 myNBEdge.deleteLane(lane->getIndex(), recomputeConnections, !recomputeConnections);
1441 lane->decRef("GNEEdge::removeLane");
1442 myLanes.erase(myLanes.begin() + lane->getIndex());
1443 // Delete lane if is unreferenced
1444 if (lane->unreferenced()) {
1445 // show extra information for tests
1446 WRITE_DEBUG("Deleting unreferenced " + lane->getTagStr() + " '" + lane->getID() + "' in removeLane()");
1447 delete lane;
1448 }
1449 // udate indices
1450 for (int i = 0; i < (int)myLanes.size(); ++i) {
1451 myLanes[i]->setIndex(i);
1452 }
1453 /* while technically correct, this looks ugly
1454 myGNEJunctionSource->invalidateShape();
1455 myGNEJunctionDestiny->invalidateShape();
1456 */
1457 // Remake connections of this edge
1458 remakeGNEConnections();
1459 // remake connections of all edges of junction source and destiny
1460 for (auto i : myGNEJunctionSource->getGNEEdges()) {
1461 i->remakeGNEConnections();
1462 }
1463 // remake connections of all edges of junction source and destiny
1464 for (auto i : myGNEJunctionDestiny->getGNEEdges()) {
1465 i->remakeGNEConnections();
1466 }
1467 // add object again
1468 myNet->addGLObjectIntoGrid(this);
1469 // Update element
1470 updateGeometry(true);
1471 }
1472
1473
1474 void
addConnection(NBEdge::Connection nbCon,bool selectAfterCreation)1475 GNEEdge::addConnection(NBEdge::Connection nbCon, bool selectAfterCreation) {
1476 // If a new connection was sucesfully created
1477 if (myNBEdge.setConnection(nbCon.fromLane, nbCon.toEdge, nbCon.toLane, NBEdge::L2L_USER, true, nbCon.mayDefinitelyPass,
1478 nbCon.keepClear, nbCon.contPos, nbCon.visibility,
1479 nbCon.speed, nbCon.customShape, nbCon.uncontrolled)) {
1480 // Create or retrieve existent GNEConection
1481 GNEConnection* con = retrieveGNEConnection(nbCon.fromLane, nbCon.toEdge, nbCon.toLane);
1482 // add it to GNEConnection container
1483 myGNEConnections.push_back(con);
1484 // Add reference
1485 myGNEConnections.back()->incRef("GNEEdge::addConnection");
1486 // select GNEConnection if needed
1487 if (selectAfterCreation) {
1488 con->selectAttributeCarrier();
1489 }
1490 // update geometry
1491 con->updateGeometry(true);
1492 // iterate over all additionals from "from" lane and check E2 multilane integrity
1493 for (auto i : con->getLaneFrom()->getAdditionalChilds()) {
1494 if (i->getTagProperty().getTag() == SUMO_TAG_E2DETECTOR_MULTILANE) {
1495 dynamic_cast<GNEDetectorE2*>(i)->checkE2MultilaneIntegrity();
1496 }
1497 }
1498 // iterate over all additionals from "to" lane and check E2 multilane integrity
1499 for (auto i : con->getLaneTo()->getAdditionalChilds()) {
1500 if (i->getTagProperty().getTag() == SUMO_TAG_E2DETECTOR_MULTILANE) {
1501 dynamic_cast<GNEDetectorE2*>(i)->checkE2MultilaneIntegrity();
1502 }
1503 }
1504 }
1505 // actually we only do this to force a redraw
1506 updateGeometry(true);
1507 }
1508
1509
1510 void
removeConnection(NBEdge::Connection nbCon)1511 GNEEdge::removeConnection(NBEdge::Connection nbCon) {
1512 // check if is a explicit turnaround
1513 if (nbCon.toEdge == myNBEdge.getTurnDestination()) {
1514 myNet->removeExplicitTurnaround(getMicrosimID());
1515 }
1516 // remove NBEdge::connection from NBEdge
1517 myNBEdge.removeFromConnections(nbCon);
1518 // remove their associated GNEConnection
1519 GNEConnection* con = retrieveGNEConnection(nbCon.fromLane, nbCon.toEdge, nbCon.toLane, false);
1520 if (con != nullptr) {
1521 con->decRef("GNEEdge::removeConnection");
1522 myGNEConnections.erase(std::find(myGNEConnections.begin(), myGNEConnections.end(), con));
1523 // iterate over all additionals from "from" lane and check E2 multilane integrity
1524 for (auto i : con->getLaneFrom()->getAdditionalChilds()) {
1525 if (i->getTagProperty().getTag() == SUMO_TAG_E2DETECTOR_MULTILANE) {
1526 dynamic_cast<GNEDetectorE2*>(i)->checkE2MultilaneIntegrity();
1527 }
1528 }
1529 // iterate over all additionals from "to" lane and check E2 multilane integrity
1530 for (auto i : con->getLaneTo()->getAdditionalChilds()) {
1531 if (i->getTagProperty().getTag() == SUMO_TAG_E2DETECTOR_MULTILANE) {
1532 dynamic_cast<GNEDetectorE2*>(i)->checkE2MultilaneIntegrity();
1533 }
1534 }
1535 // remove it from Tree
1536 myNet->removeGLObjectFromGrid(con);
1537 // check if connection is selected
1538 if (con->isAttributeCarrierSelected()) {
1539 con->unselectAttributeCarrier();
1540 }
1541 if (con->unreferenced()) {
1542 // show extra information for tests
1543 WRITE_DEBUG("Deleting unreferenced " + con->getTagStr() + " '" + con->getID() + "' in removeConnection()");
1544 delete con;
1545 // actually we only do this to force a redraw
1546 updateGeometry(true);
1547 }
1548 }
1549 }
1550
1551
1552 GNEConnection*
retrieveGNEConnection(int fromLane,NBEdge * to,int toLane,bool createIfNoExist)1553 GNEEdge::retrieveGNEConnection(int fromLane, NBEdge* to, int toLane, bool createIfNoExist) {
1554 for (auto i : myGNEConnections) {
1555 if ((i->getFromLaneIndex() == fromLane) && (i->getEdgeTo()->getNBEdge() == to) && (i->getToLaneIndex() == toLane)) {
1556 return i;
1557 }
1558 }
1559 if (createIfNoExist) {
1560 // create new connection. Will be added to the rTree on first geometry computation
1561 GNEConnection* createdConnection = new GNEConnection(myLanes[fromLane], myNet->retrieveEdge(to->getID())->getLanes()[toLane]);
1562 // show extra information for tests
1563 WRITE_DEBUG("Created " + createdConnection->getTagStr() + " '" + createdConnection->getID() + "' in retrieveGNEConnection()");
1564 // insert it in Tree
1565 myNet->addGLObjectIntoGrid(createdConnection);
1566 // iterate over all additionals from "from" lane and check E2 multilane integrity
1567 for (auto i : createdConnection->getLaneFrom()->getAdditionalChilds()) {
1568 if (i->getTagProperty().getTag() == SUMO_TAG_E2DETECTOR_MULTILANE) {
1569 dynamic_cast<GNEDetectorE2*>(i)->checkE2MultilaneIntegrity();
1570 }
1571 }
1572 // iterate over all additionals from "to" lane and check E2 multilane integrity
1573 for (auto i : createdConnection->getLaneTo()->getAdditionalChilds()) {
1574 if (i->getTagProperty().getTag() == SUMO_TAG_E2DETECTOR_MULTILANE) {
1575 dynamic_cast<GNEDetectorE2*>(i)->checkE2MultilaneIntegrity();
1576 }
1577 }
1578 return createdConnection;
1579 } else {
1580 return nullptr;
1581 }
1582 }
1583
1584
1585
1586 void
setMicrosimID(const std::string & newID)1587 GNEEdge::setMicrosimID(const std::string& newID) {
1588 GUIGlObject::setMicrosimID(newID);
1589 for (auto i : myLanes) {
1590 i->setMicrosimID(getNBEdge()->getLaneID(i->getIndex()));
1591 }
1592 }
1593
1594
1595 bool
hasRestrictedLane(SUMOVehicleClass vclass) const1596 GNEEdge::hasRestrictedLane(SUMOVehicleClass vclass) const {
1597 for (auto i : myLanes) {
1598 if (i->isRestricted(vclass)) {
1599 return true;
1600 }
1601 }
1602 return false;
1603 }
1604
1605
1606 void
removeEdgeFromCrossings(GNEJunction * junction,GNEUndoList * undoList)1607 GNEEdge::removeEdgeFromCrossings(GNEJunction* junction, GNEUndoList* undoList) {
1608 // Remove all crossings that contain this edge in parameter "edges"
1609 for (GNECrossing* const i : junction->getGNECrossings()) {
1610 if (i->checkEdgeBelong(this)) {
1611 myNet->deleteCrossing(i, undoList);
1612 }
1613 }
1614 }
1615
1616
1617 void
straightenElevation(GNEUndoList * undoList)1618 GNEEdge::straightenElevation(GNEUndoList* undoList) {
1619 PositionVector modifiedShape = myNBEdge.getGeometry().interpolateZ(
1620 myNBEdge.getFromNode()->getPosition().z(),
1621 myNBEdge.getToNode()->getPosition().z());
1622 PositionVector innerShape(modifiedShape.begin() + 1, modifiedShape.end() - 1);
1623 setAttribute(SUMO_ATTR_SHAPE, toString(innerShape), undoList);
1624 }
1625
1626
1627 PositionVector
smoothShape(const PositionVector & old,bool forElevation)1628 GNEEdge::smoothShape(const PositionVector& old, bool forElevation) {
1629 const OptionsCont& oc = OptionsCont::getOptions();
1630 // distinguish 3 cases:
1631 // a) if the edge has exactly 3 or 4 points, use these as control points
1632 // b) if the edge has more than 4 points, use the first 2 and the last 2 as control points
1633 // c) if the edge is straight and both nodes are geometry-like nodes, use geometry of the continuation edges as control points
1634 PositionVector init;
1635 #ifdef DEBUG_SMOOTH_GEOM
1636 if (DEBUGCOND(this)) std::cout << getID()
1637 << " forElevation=" << forElevation
1638 << " fromGeometryLike=" << myNBEdge.getFromNode()->geometryLike()
1639 << " toGeometryLike=" << myNBEdge.getToNode()->geometryLike()
1640 << " smoothShape old=" << old << "\n";
1641 #endif
1642 if (old.size() == 3 || old.size() == 4) {
1643 init = old;
1644 } else if (old.size() > 4 && !forElevation) {
1645 // for elevation, the initial segments are not useful
1646 init.push_back(old[0]);
1647 init.push_back(old[1]);
1648 init.push_back(old[-2]);
1649 init.push_back(old[-1]);
1650 } else if (myNBEdge.getFromNode()->geometryLike() && myNBEdge.getToNode()->geometryLike()) {
1651 PositionVector begShape;
1652 PositionVector endShape;
1653 const EdgeVector& incoming = myNBEdge.getFromNode()->getIncomingEdges();
1654 const EdgeVector& outgoing = myNBEdge.getToNode()->getOutgoingEdges();
1655 if (incoming.size() == 1) {
1656 begShape = incoming[0]->getGeometry();
1657 } else {
1658 assert(incoming.size() == 2);
1659 begShape = myNBEdge.isTurningDirectionAt(incoming[0]) ? incoming[1]->getGeometry() : incoming[0]->getGeometry();
1660 }
1661 if (outgoing.size() == 1) {
1662 endShape = outgoing[0]->getGeometry();
1663 } else {
1664 assert(outgoing.size() == 2);
1665 endShape = myNBEdge.isTurningDirectionAt(outgoing[0]) ? outgoing[1]->getGeometry() : outgoing[0]->getGeometry();
1666 }
1667 const double dist = MIN2(old.length2D(), MAX2(old.length2D() / 8, fabs(old[0].z() - old[-1].z()) * OptionsCont::getOptions().getFloat("geometry.max-grade") / 3));
1668 if (forElevation) {
1669 // initialize control point elevation for smooth continuation
1670 init.push_back(old[0]);
1671 init.push_back(old.positionAtOffset2D(dist));
1672 init.push_back(old.positionAtOffset2D(old.length2D() - dist));
1673 init.push_back(old[-1]);
1674 double begZ = begShape.positionAtOffset2D(MAX2(0.0, begShape.length2D() - dist)).z();
1675 double endZ = endShape.positionAtOffset2D(MIN2(begShape.length2D(), dist)).z();
1676 // continue incline
1677 init[1].setz(2 * init[0].z() - begZ);
1678 init[2].setz(2 * init[-1].z() - endZ);
1679 } else {
1680 bool ok = true;
1681 const double straightThresh = DEG2RAD(oc.getFloat("opendrive-output.straight-threshold"));
1682 init = NBNode::bezierControlPoints(begShape, endShape, false, dist, dist, ok, nullptr, straightThresh);
1683 }
1684 #ifdef DEBUG_SMOOTH_GEOM
1685 if (DEBUGCOND(this)) {
1686 std::cout << " begShape=" << begShape << " endShape=" << endShape << " forElevation=" << forElevation << " dist=" << dist << " ok=" << ok << " init=" << init << "\n";
1687 }
1688 #endif
1689 }
1690 if (init.size() == 0) {
1691 return PositionVector::EMPTY;
1692 } else {
1693 const int numPoints = MAX2(oc.getInt("junctions.internal-link-detail"),
1694 int(old.length2D() / oc.getFloat("opendrive.curve-resolution")));
1695 return init.bezier(numPoints);
1696 }
1697 }
1698
1699
1700 void
smooth(GNEUndoList * undoList)1701 GNEEdge::smooth(GNEUndoList* undoList) {
1702 PositionVector modifiedShape = smoothShape(myNBEdge.getGeometry(), false);
1703 if (modifiedShape.size() < 2) {
1704 WRITE_WARNING("Could not compute smooth shape for edge '" + getID() + "'");
1705 } else {
1706 PositionVector innerShape(modifiedShape.begin() + 1, modifiedShape.end() - 1);
1707 setAttribute(SUMO_ATTR_SHAPE, toString(innerShape), undoList);
1708 }
1709 }
1710
1711
1712 void
smoothElevation(GNEUndoList * undoList)1713 GNEEdge::smoothElevation(GNEUndoList* undoList) {
1714 PositionVector elevationBase;
1715 for (const Position& pos : myNBEdge.getGeometry()) {
1716 if (elevationBase.size() == 0 || elevationBase[-1].z() != pos.z()) {
1717 elevationBase.push_back(pos);
1718 }
1719 }
1720 PositionVector elevation = smoothShape(elevationBase, true);
1721 if (elevation.size() <= 2) {
1722 WRITE_WARNING("Could not compute smooth elevation for edge '" + getID() + "'");
1723 } else {
1724 PositionVector modifiedShape = myNBEdge.getGeometry();
1725 if (modifiedShape.size() < 5) {
1726 modifiedShape = modifiedShape.resample(OptionsCont::getOptions().getFloat("opendrive.curve-resolution"));
1727 }
1728 const double scale = elevation.length2D() / modifiedShape.length2D();
1729 //std::cout << " elevation=" << elevation << "\n mod1=" << modifiedShape << " scale=" << scale << "\n";
1730 double seen = 0;
1731 for (int i = 1; i < (int)modifiedShape.size(); ++i) {
1732 seen += modifiedShape[i - 1].distanceTo2D(modifiedShape[i]);
1733 modifiedShape[i].setz(elevation.positionAtOffset2D(seen * scale).z());
1734 }
1735 //std::cout << " mod2=" << modifiedShape << "\n";
1736 PositionVector innerShape(modifiedShape.begin() + 1, modifiedShape.end() - 1);
1737 setAttribute(SUMO_ATTR_SHAPE, toString(innerShape), undoList);
1738 }
1739 }
1740
1741
1742 void
setShapeStartPos(const Position & pos,bool updateGrid)1743 GNEEdge::setShapeStartPos(const Position& pos, bool updateGrid) {
1744 // remove start position and add it the new position
1745 PositionVector geom = myNBEdge.getGeometry();
1746 geom.erase(geom.begin());
1747 geom.push_front_noDoublePos(pos);
1748 // restore modified shape
1749 setGeometry(geom, false, updateGrid);
1750 }
1751
1752
1753 void
setShapeEndPos(const Position & pos,bool updateGrid)1754 GNEEdge::setShapeEndPos(const Position& pos, bool updateGrid) {
1755 // remove end position and add it the new position
1756 PositionVector geom = myNBEdge.getGeometry();
1757 geom.pop_back();
1758 geom.push_back_noDoublePos(pos);
1759 // restore modified shape
1760 setGeometry(geom, false, updateGrid);
1761 }
1762
1763 /****************************************************************************/
1764