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    GNEJunction.cpp
11 /// @author  Jakob Erdmann
12 /// @date    Feb 2011
13 /// @version $Id$
14 ///
15 // A class for visualizing and editing junctions in netedit (adapted from
16 // GUIJunctionWrapper)
17 /****************************************************************************/
18 
19 
20 // ===========================================================================
21 // included modules
22 // ===========================================================================
23 #include <config.h>
24 
25 #include <utils/common/StringTokenizer.h>
26 #include <utils/gui/windows/GUIAppEnum.h>
27 #include <utils/gui/globjects/GUIGLObjectPopupMenu.h>
28 #include <utils/gui/div/GLHelper.h>
29 #include <utils/gui/images/GUITextureSubSys.h>
30 #include <netbuild/NBOwnTLDef.h>
31 #include <netbuild/NBLoadedSUMOTLDef.h>
32 #include <netbuild/NBAlgorithms.h>
33 #include <netedit/changes/GNEChange_Attribute.h>
34 #include <netedit/changes/GNEChange_Connection.h>
35 #include <netedit/changes/GNEChange_TLS.h>
36 #include <netedit/GNENet.h>
37 #include <netedit/GNEUndoList.h>
38 #include <netedit/GNEViewNet.h>
39 #include <netbuild/NBNetBuilder.h>
40 #include <utils/gui/globjects/GLIncludes.h>
41 #include <utils/options/OptionsCont.h>
42 
43 #include "GNEEdge.h"
44 #include "GNEConnection.h"
45 #include "GNEJunction.h"
46 #include "GNECrossing.h"
47 
48 
49 // ===========================================================================
50 // static members
51 // ===========================================================================
52 
53 const double GNEJunction::BUBBLE_RADIUS(4);
54 
55 // ===========================================================================
56 // method definitions
57 // ===========================================================================
58 
GNEJunction(NBNode & nbn,GNENet * net,bool loaded)59 GNEJunction::GNEJunction(NBNode& nbn, GNENet* net, bool loaded) :
60     GNENetElement(net, nbn.getID(), GLO_JUNCTION, SUMO_TAG_JUNCTION),
61     myNBNode(nbn),
62     myAmCreateEdgeSource(false),
63     myLogicStatus(loaded ? FEATURE_LOADED : FEATURE_GUESSED),
64     myAmResponsible(false),
65     myHasValidLogic(loaded),
66     myAmTLSSelected(false) {
67     // give a temporal value for boundary
68     myJunctionBoundary = Boundary(myNBNode.getPosition().x() - 2, myNBNode.getPosition().y() - 2, myNBNode.getPosition().x() + 2, myNBNode.getPosition().y() + 2);
69 }
70 
71 
~GNEJunction()72 GNEJunction::~GNEJunction() {
73     // delete all GNECrossing
74     for (auto it : myGNECrossings) {
75         it->decRef();
76         if (it->unreferenced()) {
77             // show extra information for tests
78             WRITE_DEBUG("Deleting unreferenced " + it->getTagStr() + " '" + it->getID() + "' in GNEJunction destructor");
79             delete it;
80         }
81     }
82 
83     if (myAmResponsible) {
84         // show extra information for tests
85         WRITE_DEBUG("Deleting NBNode of '" + getID() + "' in GNEJunction destructor");
86         delete &myNBNode;
87     }
88 }
89 
90 
91 std::string
generateChildID(SumoXMLTag childTag)92 GNEJunction::generateChildID(SumoXMLTag childTag) {
93     int counter = 0;
94     while (myNet->retrieveJunction(getID() + toString(childTag) + toString(counter), false) != nullptr) {
95         counter++;
96     }
97     return (getID() + toString(childTag) + toString(counter));
98 }
99 
100 
101 void
updateGeometry(bool updateGrid)102 GNEJunction::updateGeometry(bool updateGrid) {
103     // first check if object has to be removed from grid (SUMOTree)
104     if (updateGrid) {
105         myNet->removeGLObjectFromGrid(this);
106     }
107     // calculate boundary using EXTENT as size
108     const double EXTENT = 2;
109     myJunctionBoundary = Boundary(myNBNode.getPosition().x() - EXTENT, myNBNode.getPosition().y() - EXTENT,
110                                   myNBNode.getPosition().x() + EXTENT, myNBNode.getPosition().y() + EXTENT);
111     // if junctio own a extra shape, add it to boundary
112     if (myNBNode.getShape().size() > 0) {
113         myJunctionBoundary.add(myNBNode.getShape().getBoxBoundary());
114     }
115     myMaxSize = MAX2(myJunctionBoundary.getWidth(), myJunctionBoundary.getHeight());
116     // last step is to check if object has to be added into grid (SUMOTree) again
117     if (updateGrid) {
118         myNet->addGLObjectIntoGrid(this);
119     }
120     // rebuild GNECrossings
121     rebuildGNECrossings(true);
122 }
123 
124 
125 Position
getPositionInView() const126 GNEJunction::getPositionInView() const {
127     return myNBNode.getPosition();
128 }
129 
130 
131 void
rebuildGNECrossings(bool rebuildNBNodeCrossings)132 GNEJunction::rebuildGNECrossings(bool rebuildNBNodeCrossings) {
133     // rebuild GNECrossings only if create crossings and walkingAreas in net is enabled
134     if (myNet->getNetBuilder()->haveNetworkCrossings()) {
135         if (rebuildNBNodeCrossings) {
136             // build new NBNode::Crossings and walking areas
137             mirrorXLeftHand();
138             myNBNode.buildCrossingsAndWalkingAreas();
139             mirrorXLeftHand();
140         }
141         // create a vector to keep retrieved and created crossings
142         std::vector<GNECrossing*> retrievedCrossings;
143         // iterate over NBNode::Crossings of GNEJunction
144         for (auto NBc : myNBNode.getCrossingsIncludingInvalid()) {
145             // retrieve existent GNECrossing, or create it
146             GNECrossing* retrievedGNECrossing = retrieveGNECrossing(NBc);
147             retrievedCrossings.push_back(retrievedGNECrossing);
148             // check if previously this GNECrossings exists, and if true, remove it from myGNECrossings and insert in tree again
149             std::vector<GNECrossing*>::iterator retrievedExists = std::find(myGNECrossings.begin(), myGNECrossings.end(), retrievedGNECrossing);
150             if (retrievedExists != myGNECrossings.end()) {
151                 myGNECrossings.erase(retrievedExists);
152                 // update geometry of retrieved crossing
153                 retrievedGNECrossing->updateGeometry(true);
154             } else {
155                 // include reference to created GNECrossing
156                 retrievedGNECrossing->incRef();
157             }
158         }
159         // delete non retrieved GNECrossings (we don't need to extract if from Tree two times)
160         for (auto it : myGNECrossings) {
161             it->decRef();
162             // check if crossing is selected
163             if (it->isAttributeCarrierSelected()) {
164                 it->unselectAttributeCarrier();
165             }
166             if (it->unreferenced()) {
167                 // show extra information for tests
168                 WRITE_DEBUG("Deleting unreferenced " + it->getTagStr() + " in rebuildGNECrossings()");
169                 delete it;
170             }
171         }
172         // copy retrieved (existent and created) GNECrossigns to myGNECrossings
173         myGNECrossings = retrievedCrossings;
174     }
175 }
176 
177 void
mirrorXLeftHand()178 GNEJunction::mirrorXLeftHand() {
179     if (OptionsCont::getOptions().getBool("lefthand")) {
180         myNBNode.mirrorX();
181         for (NBEdge* e : myNBNode.getEdges()) {
182             e->mirrorX();
183 
184         }
185     }
186 }
187 
188 
189 GUIGLObjectPopupMenu*
getPopUpMenu(GUIMainWindow & app,GUISUMOAbstractView & parent)190 GNEJunction::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {
191     GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, *this);
192     buildPopupHeader(ret, app);
193     buildCenterPopupEntry(ret);
194     buildNameCopyPopupEntry(ret);
195     // build selection and show parameters menu
196     myNet->getViewNet()->buildSelectionACPopupEntry(ret, this);
197     buildShowParamsPopupEntry(ret);
198     buildPositionCopyEntry(ret, false);
199     //if (parent.getVisualisationSettings()->editMode != GNE_MODE_CONNECT) {
200     //    // XXX if joinable
201     //    new FXMenuCommand(ret, "Join adjacent edges", 0, &parent, MID_GNE_JOIN_EDGES);
202     //}
203     const int numEndpoints = (int)myNBNode.getEndPoints().size();
204     // check if we're handling a selection
205     bool handlingSelection = isAttributeCarrierSelected() && (myNet->retrieveJunctions(true).size() > 1);
206     // check if menu commands has to be disabled
207     const bool wrongMode = (myNet->getViewNet()->getEditModes().networkEditMode == GNE_NMODE_CONNECT) ||
208                            (myNet->getViewNet()->getEditModes().networkEditMode == GNE_NMODE_TLS) ||
209                            (myNet->getViewNet()->getEditModes().networkEditMode == GNE_NMODE_CREATE_EDGE);
210     // create menu commands
211     FXMenuCommand* mcCustomShape = new FXMenuCommand(ret, "Set custom junction shape", nullptr, &parent, MID_GNE_JUNCTION_EDIT_SHAPE);
212     FXMenuCommand* mcResetCustomShape = new FXMenuCommand(ret, "Reset junction shape", nullptr, &parent, MID_GNE_JUNCTION_RESET_SHAPE);
213     FXMenuCommand* mcReplace = new FXMenuCommand(ret, "Replace junction by geometry point", nullptr, &parent, MID_GNE_JUNCTION_REPLACE);
214     FXMenuCommand* mcSplit = new FXMenuCommand(ret, ("Split junction (" + toString(numEndpoints) + " end points)").c_str(), nullptr, &parent, MID_GNE_JUNCTION_SPLIT);
215     FXMenuCommand* mcSplitReconnect = new FXMenuCommand(ret, "Split junction and reconnect", nullptr, &parent, MID_GNE_JUNCTION_SPLIT_RECONNECT);
216     FXMenuCommand* mcClearConnections = new FXMenuCommand(ret, "Clear connections", nullptr, &parent, MID_GNE_JUNCTION_CLEAR_CONNECTIONS);
217     FXMenuCommand* mcResetConnections = new FXMenuCommand(ret, "Reset connections", nullptr, &parent, MID_GNE_JUNCTION_RESET_CONNECTIONS);
218     // check if current mode  is correct
219     if (wrongMode) {
220         mcCustomShape->disable();
221         mcClearConnections->disable();
222         mcResetConnections->disable();
223     }
224     // check if we're handling a selection
225     if (handlingSelection) {
226         mcResetCustomShape->setText("Reset junction shapes");
227     }
228     // disable mcClearConnections if juction hasn't connections
229     if (getGNEConnections().empty()) {
230         mcClearConnections->disable();
231     }
232     // disable mcResetCustomShape if junction doesn't have a custom shape
233     if (myNBNode.getShape().size() == 0) {
234         mcResetCustomShape->disable();
235     }
236     // checkIsRemovable requiers turnarounds to be computed. This is ugly
237     if ((myNBNode.getIncomingEdges().size() == 2) && (myNBNode.getOutgoingEdges().size() == 2)) {
238         NBTurningDirectionsComputer::computeTurnDirectionsForNode(&myNBNode, false);
239     }
240     std::string reason = "wrong edit mode";
241     if (wrongMode || !myNBNode.checkIsRemovableReporting(reason)) {
242         mcReplace->setText(mcReplace->getText() + " (" + reason.c_str() + ")");
243         mcReplace->disable();
244     }
245     if (numEndpoints == 1) {
246         mcSplit->disable();
247         mcSplitReconnect->disable();
248     }
249     return ret;
250 }
251 
252 
253 Boundary
getCenteringBoundary() const254 GNEJunction::getCenteringBoundary() const {
255     // Return Boundary depending if myMovingGeometryBoundary is initialised (important for move geometry)
256     if (myMovingGeometryBoundary.isInitialised()) {
257         return myMovingGeometryBoundary;
258     } else {
259         Boundary b = myJunctionBoundary;
260         b.grow(20);
261         return b;
262     }
263 }
264 
265 
266 void
drawGL(const GUIVisualizationSettings & s) const267 GNEJunction::drawGL(const GUIVisualizationSettings& s) const {
268     // declare variables
269     double exaggeration = isAttributeCarrierSelected() ? s.selectionScale : 1;
270     exaggeration *= s.junctionSize.getExaggeration(s, this, 4);
271     // declare values for circles
272     double circleWidth = BUBBLE_RADIUS * exaggeration;
273     double circleWidthSquared = circleWidth * circleWidth;
274     int circleResolution = GNEAttributeCarrier::getCircleResolution(s);
275     // push name
276     if (s.scale * exaggeration * myMaxSize < 1.) {
277         // draw something simple so that selection still works
278         glPushName(getGlID());
279         GLHelper::drawBoxLine(myNBNode.getPosition(), 0, 1, 1);
280         glPopName();
281     } else {
282         // node shape has been computed and is valid for drawing
283         glPushName(getGlID());
284         const bool drawShape = myNBNode.getShape().size() > 0 && s.drawJunctionShape;
285         const bool drawBubble = (((!drawShape || myNBNode.getShape().area() < 4)
286                                   && s.drawJunctionShape)
287                                  || myNet->getViewNet()->showJunctionAsBubbles());
288 
289         if (drawShape) {
290             RGBColor color = setColor(s, false);
291             // recognize full transparency and simply don't draw
292             if (color.alpha() != 0) {
293                 glPushMatrix();
294                 glTranslated(0, 0, getType());
295                 PositionVector shape = myNBNode.getShape();
296                 shape.closePolygon();
297                 if (exaggeration > 1) {
298                     shape.scaleRelative(exaggeration);
299                 }
300                 if ((s.drawForSelecting) || (s.scale * exaggeration * myMaxSize < 40.)) {
301                     GLHelper::drawFilledPoly(shape, true);
302                 } else {
303                     GLHelper::drawFilledPolyTesselated(shape, true);
304                 }
305                 // check if dotted contour has to be drawn
306                 if (!s.drawForSelecting && (myNet->getViewNet()->getDottedAC() == this) && !drawBubble) {
307                     GLHelper::drawShapeDottedContour(getType(), shape);
308                 }
309                 glPopMatrix();
310             }
311         }
312         if (drawBubble) {
313             RGBColor color = setColor(s, true);
314             // recognize full transparency and simply don't draw
315             if (color.alpha() != 0) {
316                 glPushMatrix();
317                 glTranslated(myNBNode.getPosition().x(), myNBNode.getPosition().y(), getType() + 0.05);
318                 if (!s.drawForSelecting || (myNet->getViewNet()->getPositionInformation().distanceSquaredTo2D(myNBNode.getPosition()) <= (circleWidthSquared + 2))) {
319                     std::vector<Position> vertices = GLHelper::drawFilledCircleReturnVertices(circleWidth, circleResolution);
320                     // check if dotted contour has to be drawn
321                     if (!s.drawForSelecting && myNet->getViewNet()->getDottedAC() == this) {
322                         GLHelper::drawShapeDottedContour(getType(), vertices);
323                     }
324                 } else {
325                     GLHelper::drawBoxLine(Position(0, 1), 0, 2, 1);
326                 }
327                 glPopMatrix();
328             }
329         }
330         // draw TLS icon if isn't being drawn for selecting
331         if ((s.editMode == GNE_NMODE_TLS) && (myNBNode.isTLControlled()) && !myAmTLSSelected && !s.drawForSelecting) {
332             glPushMatrix();
333             Position pos = myNBNode.getPosition();
334             glTranslated(pos.x(), pos.y(), getType() + 0.1);
335             glColor3d(1, 1, 1);
336             const double halfWidth = 32 / s.scale;
337             const double halfHeight = 64 / s.scale;
338             GUITexturesHelper::drawTexturedBox(GUITextureSubSys::getTexture(GNETEXTURE_TLS), -halfWidth, -halfHeight, halfWidth, halfHeight);
339             glPopMatrix();
340         }
341         // (optional) draw name @todo expose this setting if isn't drawed if isn't being drawn for selecting
342         if (!s.drawForSelecting) {
343             drawName(myNBNode.getPosition(), s.scale, s.junctionName);
344         }
345         // draw elevation
346         if (!s.drawForSelecting && myNet->getViewNet()->getMoveOptions().editingElevation()) {
347             glPushMatrix();
348             // Translate to center of junction
349             glTranslated(myNBNode.getPosition().x(), myNBNode.getPosition().y(), getType() + 1);
350             // draw Z value
351             GLHelper::drawText(toString(myNBNode.getPosition().z()), Position(), GLO_MAX - 5, s.junctionName.scaledSize(s.scale), s.junctionName.color);
352             glPopMatrix();
353         }
354         // name must be removed from selection stack before drawing crossings
355         glPopName();
356         // draw crossings
357         for (auto it : myGNECrossings) {
358             it->drawGL(s);
359         }
360     }
361 }
362 
363 Boundary
getBoundary() const364 GNEJunction::getBoundary() const {
365     return myJunctionBoundary;
366 }
367 
368 
369 NBNode*
getNBNode() const370 GNEJunction::getNBNode() const {
371     return &myNBNode;
372 }
373 
374 
375 std::vector<GNEJunction*>
getJunctionNeighbours() const376 GNEJunction::getJunctionNeighbours() const {
377     // use set to avoid duplicates junctions
378     std::set<GNEJunction*> junctions;
379     for (auto i : myGNEIncomingEdges) {
380         junctions.insert(i->getGNEJunctionSource());
381     }
382     for (auto i : myGNEOutgoingEdges) {
383         junctions.insert(i->getGNEJunctionDestiny());
384     }
385     return std::vector<GNEJunction*>(junctions.begin(), junctions.end());
386 }
387 
388 
389 void
addIncomingGNEEdge(GNEEdge * edge)390 GNEJunction::addIncomingGNEEdge(GNEEdge* edge) {
391     // Check if incoming edge was already inserted
392     std::vector<GNEEdge*>::iterator i = std::find(myGNEIncomingEdges.begin(), myGNEIncomingEdges.end(), edge);
393     if (i != myGNEIncomingEdges.end()) {
394         throw InvalidArgument("Incoming " + toString(SUMO_TAG_EDGE) + " with ID '" + edge->getID() + "' was already inserted into " + getTagStr() + " with ID " + getID() + "'");
395     } else {
396         // Add edge into containers
397         myGNEIncomingEdges.push_back(edge);
398         myGNEEdges.push_back(edge);
399     }
400 }
401 
402 
403 
404 void
addOutgoingGNEEdge(GNEEdge * edge)405 GNEJunction::addOutgoingGNEEdge(GNEEdge* edge) {
406     // Check if outgoing edge was already inserted
407     std::vector<GNEEdge*>::iterator i = std::find(myGNEOutgoingEdges.begin(), myGNEOutgoingEdges.end(), edge);
408     if (i != myGNEOutgoingEdges.end()) {
409         throw InvalidArgument("Outgoing " + toString(SUMO_TAG_EDGE) + " with ID '" + edge->getID() + "' was already inserted into " + getTagStr() + " with ID " + getID() + "'");
410     } else {
411         // Add edge into containers
412         myGNEOutgoingEdges.push_back(edge);
413         myGNEEdges.push_back(edge);
414     }
415 }
416 
417 
418 void
removeIncomingGNEEdge(GNEEdge * edge)419 GNEJunction::removeIncomingGNEEdge(GNEEdge* edge) {
420     // Check if incoming edge was already inserted
421     std::vector<GNEEdge*>::iterator i = std::find(myGNEIncomingEdges.begin(), myGNEIncomingEdges.end(), edge);
422     if (i == myGNEIncomingEdges.end()) {
423         throw InvalidArgument("Incoming " + toString(SUMO_TAG_EDGE) + " with ID '" + edge->getID() + "' doesn't found into " + getTagStr() + " with ID " + getID() + "'");
424     } else {
425         // remove edge from containers
426         myGNEIncomingEdges.erase(i);
427         myGNEEdges.erase(std::find(myGNEEdges.begin(), myGNEEdges.end(), edge));
428     }
429 }
430 
431 
432 void
removeOutgoingGNEEdge(GNEEdge * edge)433 GNEJunction::removeOutgoingGNEEdge(GNEEdge* edge) {
434     // Check if outgoing edge was already inserted
435     std::vector<GNEEdge*>::iterator i = std::find(myGNEOutgoingEdges.begin(), myGNEOutgoingEdges.end(), edge);
436     if (i == myGNEOutgoingEdges.end()) {
437         throw InvalidArgument("Outgoing " + toString(SUMO_TAG_EDGE) + " with ID '" + edge->getID() + "' doesn't found into " + getTagStr() + " with ID " + getID() + "'");
438     } else {
439         // remove edge from containers
440         myGNEOutgoingEdges.erase(i);
441         myGNEEdges.erase(std::find(myGNEEdges.begin(), myGNEEdges.end(), edge));
442     }
443 }
444 
445 
446 const std::vector<GNEEdge*>&
getGNEEdges() const447 GNEJunction::getGNEEdges() const {
448     return myGNEEdges;
449 }
450 
451 
452 const std::vector<GNEEdge*>&
getGNEIncomingEdges() const453 GNEJunction::getGNEIncomingEdges() const {
454     return myGNEIncomingEdges;
455 }
456 
457 
458 const std::vector<GNEEdge*>&
getGNEOutgoingEdges() const459 GNEJunction::getGNEOutgoingEdges() const {
460     return myGNEOutgoingEdges;
461 }
462 
463 
464 const std::vector<GNECrossing*>&
getGNECrossings() const465 GNEJunction::getGNECrossings() const {
466     return myGNECrossings;
467 }
468 
469 
470 std::vector<GNEConnection*>
getGNEConnections() const471 GNEJunction::getGNEConnections() const {
472     std::vector<GNEConnection*> connections;
473     for (auto i : myGNEIncomingEdges) {
474         for (auto j : i->getGNEConnections()) {
475             connections.push_back(j);
476         }
477     }
478     return connections;
479 }
480 
481 
482 void
markAsCreateEdgeSource()483 GNEJunction::markAsCreateEdgeSource() {
484     myAmCreateEdgeSource = true;
485 }
486 
487 
488 void
unMarkAsCreateEdgeSource()489 GNEJunction::unMarkAsCreateEdgeSource() {
490     myAmCreateEdgeSource = false;
491 }
492 
493 
494 void
selectTLS(bool selected)495 GNEJunction::selectTLS(bool selected) {
496     myAmTLSSelected = selected;
497 }
498 
499 
500 void
startGeometryMoving(bool extendToNeighbors)501 GNEJunction::startGeometryMoving(bool extendToNeighbors) {
502     // save current centering boundary
503     myMovingGeometryBoundary = getCenteringBoundary();
504     // First declare three sets with all affected GNEJunctions, GNEEdges and GNEConnections
505     std::set<GNEJunction*> affectedJunctions;
506     std::set<GNEEdge*> affectedEdges;
507     // Iterate over GNEEdges
508     for (auto i : myGNEEdges) {
509         // Add source and destiny junctions
510         affectedJunctions.insert(i->getGNEJunctionSource());
511         affectedJunctions.insert(i->getGNEJunctionDestiny());
512         // Obtain neighbors of Junction source
513         for (auto j : i->getGNEJunctionSource()->getGNEEdges()) {
514             affectedEdges.insert(j);
515         }
516         // Obtain neighbors of Junction destiny
517         for (auto j : i->getGNEJunctionDestiny()->getGNEEdges()) {
518             affectedEdges.insert(j);
519         }
520     }
521     // Iterate over affected Junctions only if extendToNeighbors is enabled
522     if (extendToNeighbors) {
523         for (auto i : affectedJunctions) {
524             // don't include this junction (to avoid start more than one times)
525             if (i != this) {
526                 // start geometry moving in edges
527                 i->startGeometryMoving(false);
528             }
529         }
530     }
531     // Iterate over affected Edges
532     for (auto i : affectedEdges) {
533         // start geometry moving in edges
534         i->startGeometryMoving();
535     }
536 }
537 
538 
539 void
endGeometryMoving(bool extendToNeighbors)540 GNEJunction::endGeometryMoving(bool extendToNeighbors) {
541     // Remove object from net
542     myNet->removeGLObjectFromGrid(this);
543     // reset myMovingGeometryBoundary
544     myMovingGeometryBoundary.reset();
545     // update geometry without updating grid
546     updateGeometry(false);
547     // First declare three sets with all affected GNEJunctions, GNEEdges and GNEConnections
548     std::set<GNEJunction*> affectedJunctions;
549     std::set<GNEEdge*> affectedEdges;
550     // Iterate over GNEEdges
551     for (auto i : myGNEEdges) {
552         // Add source and destiny junctions
553         affectedJunctions.insert(i->getGNEJunctionSource());
554         affectedJunctions.insert(i->getGNEJunctionDestiny());
555         // Obtain neighbors of Junction source
556         for (auto j : i->getGNEJunctionSource()->getGNEEdges()) {
557             affectedEdges.insert(j);
558         }
559         // Obtain neighbors of Junction destiny
560         for (auto j : i->getGNEJunctionDestiny()->getGNEEdges()) {
561             affectedEdges.insert(j);
562         }
563     }
564     // Iterate over affected Junctions
565     if (extendToNeighbors) {
566         for (auto i : affectedJunctions) {
567             // don't include this junction (to avoid end it more than one times)
568             if (i != this) {
569                 // end geometry moving in edges
570                 i->endGeometryMoving(false);
571             }
572         }
573     }
574     // Iterate over affected Edges
575     for (auto i : affectedEdges) {
576         // end geometry moving in edges
577         i->endGeometryMoving();
578     }
579     // add object into grid again (using the new centering boundary)
580     myNet->addGLObjectIntoGrid(this);
581 }
582 
583 
584 void
moveGeometry(const Position & oldPos,const Position & offset)585 GNEJunction::moveGeometry(const Position& oldPos, const Position& offset) {
586     // Abort moving if there is another junction in the exactly target position
587     bool abort = false;
588     std::vector<GNEJunction*> junctionNeighbours = getJunctionNeighbours();
589     for (auto i : junctionNeighbours) {
590         if (i->getPositionInView() == myNBNode.getPosition()) {
591             abort = true;
592         }
593     }
594     if (!abort) {
595         Position newPosition = oldPos;
596         newPosition.add(offset);
597         // filtern position using snap to active grid
598         // filtern position using snap to active grid
599         newPosition = myNet->getViewNet()->snapToActiveGrid(newPosition);
600         moveJunctionGeometry(newPosition, false);
601     }
602 }
603 
604 
605 void
commitGeometryMoving(const Position & oldPos,GNEUndoList * undoList)606 GNEJunction::commitGeometryMoving(const Position& oldPos, GNEUndoList* undoList) {
607     if (isValid(SUMO_ATTR_POSITION, toString(myNBNode.getPosition()))) {
608         undoList->p_begin("position of " + getTagStr());
609         undoList->p_add(new GNEChange_Attribute(this, myNet, SUMO_ATTR_POSITION, toString(myNBNode.getPosition()), true, toString(oldPos)));
610         undoList->p_end();
611     } else {
612         // tried to set an invalid position, revert back to the previous one
613         moveJunctionGeometry(oldPos, true);
614     }
615 }
616 
617 
618 void
updateShapesAndGeometries(bool updateGrid)619 GNEJunction::updateShapesAndGeometries(bool updateGrid) {
620     // First declare three sets with all affected GNEJunctions, GNEEdges and GNEConnections
621     std::set<GNEJunction*> affectedJunctions;
622     std::set<GNEEdge*> affectedEdges;
623     // Iterate over GNEEdges
624     for (auto i : myGNEEdges) {
625         // Add source and destiny junctions
626         affectedJunctions.insert(i->getGNEJunctionSource());
627         affectedJunctions.insert(i->getGNEJunctionDestiny());
628         // Obtain neighbors of Junction source
629         for (auto j : i->getGNEJunctionSource()->getGNEEdges()) {
630             affectedEdges.insert(j);
631         }
632         // Obtain neighbors of Junction destiny
633         for (auto j : i->getGNEJunctionDestiny()->getGNEEdges()) {
634             affectedEdges.insert(j);
635         }
636     }
637     // Iterate over affected Junctions only if updateGrid is enabled
638     if (updateGrid) {
639         for (auto i : affectedJunctions) {
640             // Update geometry of Junction
641             i->updateGeometry(updateGrid);
642         }
643     }
644     // Iterate over affected Edges
645     for (auto i : affectedEdges) {
646         // Update edge geometry
647         i->updateGeometry(updateGrid);
648     }
649     // Finally update geometry of this junction
650     updateGeometry(updateGrid);
651     // Update view to show the new shapes
652     if (myNet->getViewNet()) {
653         myNet->getViewNet()->update();
654     }
655 }
656 
657 
658 void
invalidateShape()659 GNEJunction::invalidateShape() {
660     if (!myNBNode.hasCustomShape()) {
661         myNBNode.myPoly.clear();
662         myNet->requireRecompute();
663     }
664 }
665 
666 
667 void
setLogicValid(bool valid,GNEUndoList * undoList,const std::string & status)668 GNEJunction::setLogicValid(bool valid, GNEUndoList* undoList, const std::string& status) {
669     myHasValidLogic = valid;
670     if (!valid) {
671         assert(undoList != 0);
672         assert(undoList->hasCommandGroup());
673         NBTurningDirectionsComputer::computeTurnDirectionsForNode(&myNBNode, false);
674         EdgeVector incoming = myNBNode.getIncomingEdges();
675         for (EdgeVector::iterator it = incoming.begin(); it != incoming.end(); it++) {
676             GNEEdge* srcEdge = myNet->retrieveEdge((*it)->getID());
677             removeConnectionsFrom(srcEdge, undoList, false); // false, because the whole tls will be invalidated at the end
678             undoList->add(new GNEChange_Attribute(srcEdge, myNet, GNE_ATTR_MODIFICATION_STATUS, status), true);
679         }
680         undoList->add(new GNEChange_Attribute(this, myNet, GNE_ATTR_MODIFICATION_STATUS, status), true);
681         invalidateTLS(undoList);
682     } else {
683         // logic valed, then rebuild GNECrossings to adapt it to the new logic
684         // (but don't rebuild the crossings in NBNode because they are already finished)
685         rebuildGNECrossings(false);
686     }
687 }
688 
689 
690 void
removeConnectionsFrom(GNEEdge * edge,GNEUndoList * undoList,bool updateTLS,int lane)691 GNEJunction::removeConnectionsFrom(GNEEdge* edge, GNEUndoList* undoList, bool updateTLS, int lane) {
692     NBEdge* srcNBE = edge->getNBEdge();
693     NBEdge* turnEdge = srcNBE->getTurnDestination();
694     // Make a copy of connections
695     std::vector<NBEdge::Connection> connections = srcNBE->getConnections();
696     // delete in reverse so that undoing will add connections in the original order
697     for (std::vector<NBEdge::Connection>::reverse_iterator con_it = connections.rbegin(); con_it != connections.rend(); con_it++) {
698         if (lane >= 0 && (*con_it).fromLane != lane) {
699             continue;
700         }
701         bool hasTurn = con_it->toEdge == turnEdge;
702         undoList->add(new GNEChange_Connection(edge, *con_it, false, false), true);
703         // needs to come after GNEChange_Connection
704         // XXX bug: this code path will not be used on a redo!
705         if (hasTurn) {
706             myNet->addExplicitTurnaround(srcNBE->getID());
707         }
708     }
709     if (updateTLS) {
710         std::vector<NBConnection> removeConnections;
711         for (NBEdge::Connection con : connections) {
712             removeConnections.push_back(NBConnection(srcNBE, con.fromLane, con.toEdge, con.toLane));
713         }
714         removeTLSConnections(removeConnections, undoList);
715     }
716 }
717 
718 
719 void
removeConnectionsTo(GNEEdge * edge,GNEUndoList * undoList,bool updateTLS,int lane)720 GNEJunction::removeConnectionsTo(GNEEdge* edge, GNEUndoList* undoList, bool updateTLS, int lane) {
721     NBEdge* destNBE = edge->getNBEdge();
722     std::vector<NBConnection> removeConnections;
723     for (NBEdge* srcNBE : myNBNode.getIncomingEdges()) {
724         GNEEdge* srcEdge = myNet->retrieveEdge(srcNBE->getID());
725         std::vector<NBEdge::Connection> connections = srcNBE->getConnections();
726         for (std::vector<NBEdge::Connection>::reverse_iterator con_it = connections.rbegin(); con_it != connections.rend(); con_it++) {
727             if ((*con_it).toEdge == destNBE) {
728                 if (lane >= 0 && (*con_it).toLane != lane) {
729                     continue;
730                 }
731                 bool hasTurn = srcNBE->getTurnDestination() == destNBE;
732                 undoList->add(new GNEChange_Connection(srcEdge, *con_it, false, false), true);
733                 // needs to come after GNEChange_Connection
734                 // XXX bug: this code path will not be used on a redo!
735                 if (hasTurn) {
736                     myNet->addExplicitTurnaround(srcNBE->getID());
737                 }
738                 removeConnections.push_back(NBConnection(srcNBE, (*con_it).fromLane, destNBE, (*con_it).toLane));
739             }
740         }
741     }
742     if (updateTLS) {
743         removeTLSConnections(removeConnections, undoList);
744     }
745 }
746 
747 
748 void
removeTLSConnections(std::vector<NBConnection> & connections,GNEUndoList * undoList)749 GNEJunction::removeTLSConnections(std::vector<NBConnection>& connections, GNEUndoList* undoList) {
750     if (connections.size() == 0) {
751         return;
752     }
753     const std::set<NBTrafficLightDefinition*> coypOfTls = myNBNode.getControllingTLS(); // make a copy!
754     for (auto it : coypOfTls) {
755         NBLoadedSUMOTLDef* tlDef = dynamic_cast<NBLoadedSUMOTLDef*>(it);
756         // guessed TLS (NBOwnTLDef) do not need to be updated
757         if (tlDef != nullptr) {
758             std::string newID = tlDef->getID();
759             // create replacement before deleting the original because deletion will mess up saving original nodes
760             NBLoadedSUMOTLDef* replacementDef = new NBLoadedSUMOTLDef(tlDef, tlDef->getLogic());
761             for (NBConnection& con : connections) {
762                 replacementDef->removeConnection(con);
763             }
764             undoList->add(new GNEChange_TLS(this, tlDef, false), true);
765             undoList->add(new GNEChange_TLS(this, replacementDef, true, false, newID), true);
766             // the removed traffic light may have controlled more than one junction. These too have become invalid now
767             const std::vector<NBNode*> copyOfNodes = tlDef->getNodes(); // make a copy!
768             for (auto it_node : copyOfNodes) {
769                 GNEJunction* sharing = myNet->retrieveJunction(it_node->getID());
770                 undoList->add(new GNEChange_TLS(sharing, tlDef, false), true);
771                 undoList->add(new GNEChange_TLS(sharing, replacementDef, true, false, newID), true);
772             }
773         }
774     }
775 }
776 
777 
778 void
replaceIncomingConnections(GNEEdge * which,GNEEdge * by,GNEUndoList * undoList)779 GNEJunction::replaceIncomingConnections(GNEEdge* which, GNEEdge* by, GNEUndoList* undoList) {
780     // remap connections of the edge
781     assert(which->getLanes().size() == by->getLanes().size());
782     std::vector<NBEdge::Connection> connections = which->getNBEdge()->getConnections();
783     for (NBEdge::Connection& c : connections) {
784         undoList->add(new GNEChange_Connection(which, c, false, false), true);
785         undoList->add(new GNEChange_Connection(by, c, false, true), true);
786     }
787     // also remap tls connections
788     const std::set<NBTrafficLightDefinition*> coypOfTls = myNBNode.getControllingTLS(); // make a copy!
789     for (auto it : coypOfTls) {
790         NBLoadedSUMOTLDef* tlDef = dynamic_cast<NBLoadedSUMOTLDef*>(it);
791         // guessed TLS (NBOwnTLDef) do not need to be updated
792         if (tlDef != nullptr) {
793             std::string newID = tlDef->getID();
794             // create replacement before deleting the original because deletion will mess up saving original nodes
795             NBLoadedSUMOTLDef* replacementDef = new NBLoadedSUMOTLDef(tlDef, tlDef->getLogic());
796             for (int i = 0; i < (int)which->getLanes().size(); ++i) {
797                 replacementDef->replaceRemoved(which->getNBEdge(), i, by->getNBEdge(), i);
798             }
799             undoList->add(new GNEChange_TLS(this, tlDef, false), true);
800             undoList->add(new GNEChange_TLS(this, replacementDef, true, false, newID), true);
801             // the removed traffic light may have controlled more than one junction. These too have become invalid now
802             const std::vector<NBNode*> copyOfNodes = tlDef->getNodes(); // make a copy!
803             for (auto it_node : copyOfNodes) {
804                 GNEJunction* sharing = myNet->retrieveJunction(it_node->getID());
805                 undoList->add(new GNEChange_TLS(sharing, tlDef, false), true);
806                 undoList->add(new GNEChange_TLS(sharing, replacementDef, true, false, newID), true);
807             }
808         }
809     }
810 }
811 
812 
813 void
markAsModified(GNEUndoList * undoList)814 GNEJunction::markAsModified(GNEUndoList* undoList) {
815     EdgeVector incoming = myNBNode.getIncomingEdges();
816     for (EdgeVector::iterator it = incoming.begin(); it != incoming.end(); it++) {
817         NBEdge* srcNBE = *it;
818         GNEEdge* srcEdge = myNet->retrieveEdge(srcNBE->getID());
819         undoList->add(new GNEChange_Attribute(srcEdge, myNet, GNE_ATTR_MODIFICATION_STATUS, FEATURE_MODIFIED), true);
820     }
821 }
822 
823 
824 void
invalidateTLS(GNEUndoList * undoList,const NBConnection & deletedConnection,const NBConnection & addedConnection)825 GNEJunction::invalidateTLS(GNEUndoList* undoList, const NBConnection& deletedConnection, const NBConnection& addedConnection) {
826     assert(undoList->hasCommandGroup());
827     // NBLoadedSUMOTLDef becomes invalid, replace with NBOwnTLDef which will be dynamically recomputed
828     const std::set<NBTrafficLightDefinition*> coypOfTls = myNBNode.getControllingTLS(); // make a copy!
829     for (auto it : coypOfTls) {
830         NBLoadedSUMOTLDef* tlDef = dynamic_cast<NBLoadedSUMOTLDef*>(it);
831         if (tlDef != nullptr) {
832             NBTrafficLightDefinition* replacementDef = nullptr;
833             std::string newID = tlDef->getID(); // + "_reguessed"; // changes due to reguessing will be visible in diff
834             if (deletedConnection != NBConnection::InvalidConnection) {
835                 // create replacement before deleting the original because deletion will mess up saving original nodes
836                 NBLoadedSUMOTLDef* repl = new NBLoadedSUMOTLDef(tlDef, tlDef->getLogic());
837                 repl->removeConnection(deletedConnection);
838                 replacementDef = repl;
839             } else if (addedConnection != NBConnection::InvalidConnection) {
840                 if (addedConnection.getTLIndex() == NBConnection::InvalidTlIndex) {
841                     // custom tl indices of crossings might become invalid upon recomputation so we must save them
842                     // however, the could remain valud so we register a change but keep them at their old value
843                     for (GNECrossing* c : myGNECrossings) {
844                         const std::string oldValue = c->getAttribute(SUMO_ATTR_TLLINKINDEX);
845                         undoList->add(new GNEChange_Attribute(c, myNet, SUMO_ATTR_TLLINKINDEX, toString(NBConnection::InvalidTlIndex)), true);
846                         undoList->add(new GNEChange_Attribute(c, myNet, SUMO_ATTR_TLLINKINDEX, oldValue), true);
847                         const std::string oldValue2 = c->getAttribute(SUMO_ATTR_TLLINKINDEX);
848                         undoList->add(new GNEChange_Attribute(c, myNet, SUMO_ATTR_TLLINKINDEX2, toString(NBConnection::InvalidTlIndex)), true);
849                         undoList->add(new GNEChange_Attribute(c, myNet, SUMO_ATTR_TLLINKINDEX2, oldValue2), true);
850                     }
851                 }
852                 NBLoadedSUMOTLDef* repl = new NBLoadedSUMOTLDef(tlDef, tlDef->getLogic());
853                 repl->addConnection(addedConnection.getFrom(), addedConnection.getTo(),
854                                     addedConnection.getFromLane(), addedConnection.getToLane(), addedConnection.getTLIndex());
855                 replacementDef = repl;
856             } else {
857                 replacementDef = new NBOwnTLDef(newID, tlDef->getOffset(), tlDef->getType());
858                 replacementDef->setProgramID(tlDef->getProgramID());
859             }
860             undoList->add(new GNEChange_TLS(this, tlDef, false), true);
861             undoList->add(new GNEChange_TLS(this, replacementDef, true, false, newID), true);
862             // the removed traffic light may have controlled more than one junction. These too have become invalid now
863             const std::vector<NBNode*> copyOfNodes = tlDef->getNodes(); // make a copy!
864             for (auto it_node : copyOfNodes) {
865                 GNEJunction* sharing = myNet->retrieveJunction(it_node->getID());
866                 undoList->add(new GNEChange_TLS(sharing, tlDef, false), true);
867                 undoList->add(new GNEChange_TLS(sharing, replacementDef, true, false, newID), true);
868             }
869         }
870     }
871 }
872 
873 void
removeEdgeFromCrossings(GNEEdge * edge,GNEUndoList * undoList)874 GNEJunction::removeEdgeFromCrossings(GNEEdge* edge, GNEUndoList* undoList) {
875     // obtain a copy of GNECrossing of junctions
876     auto copyOfGNECrossings = myGNECrossings;
877     // iterate over copy of GNECrossings
878     for (int i = 0; i < (int)myGNECrossings.size(); i++) {
879         auto c = myGNECrossings.at(i);
880         // obtain the set of edges vinculated with the crossing (due it works as ID)
881         EdgeSet edgeSet(c->getCrossingEdges().begin(), c->getCrossingEdges().end());
882         // If this edge is part of the set of edges of crossing
883         if (edgeSet.count(edge->getNBEdge()) == 1) {
884             // delete crossing if this is their last edge
885             if ((c->getCrossingEdges().size() == 1) && (c->getCrossingEdges().front() == edge->getNBEdge())) {
886                 myNet->deleteCrossing(c, undoList);
887                 i = 0;
888             } else {
889                 // remove this edge of the edge's attribute of crossing (note: This can invalidate the crossing)
890                 std::vector<std::string> edges = GNEAttributeCarrier::parse<std::vector<std::string>>(c->getAttribute(SUMO_ATTR_EDGES));
891                 edges.erase(std::find(edges.begin(), edges.end(), edge->getID()));
892                 c->setAttribute(SUMO_ATTR_EDGES, joinToString(edges, " "), undoList);
893             }
894         }
895     }
896 }
897 
898 
899 bool
isLogicValid()900 GNEJunction::isLogicValid() {
901     return myHasValidLogic;
902 }
903 
904 
905 GNECrossing*
retrieveGNECrossing(NBNode::Crossing * crossing,bool createIfNoExist)906 GNEJunction::retrieveGNECrossing(NBNode::Crossing* crossing, bool createIfNoExist) {
907     // iterate over all crossing
908     for (auto i : myGNECrossings) {
909         // if found, return it
910         if (i->getCrossingEdges() == crossing->edges) {
911             return i;
912         }
913     }
914     if (createIfNoExist) {
915         // create new GNECrossing
916         GNECrossing* createdGNECrossing = new GNECrossing(this, crossing->edges);
917         // show extra information for tests
918         WRITE_DEBUG("Created " + createdGNECrossing->getTagStr() + " '" + createdGNECrossing->getID() + "' in retrieveGNECrossing()");
919         // update geometry after creating
920         createdGNECrossing->updateGeometry(true);
921         return createdGNECrossing;
922     } else {
923         return nullptr;
924     }
925 }
926 
927 
928 void
markConnectionsDeprecated(bool includingNeighbours)929 GNEJunction::markConnectionsDeprecated(bool includingNeighbours) {
930     // only it's needed to mark the connections of incoming edges
931     for (auto i : myGNEIncomingEdges) {
932         for (auto j : i->getGNEConnections()) {
933             j->markConnectionGeometryDeprecated();
934         }
935         if (includingNeighbours) {
936             i->getGNEJunctionSource()->markConnectionsDeprecated(false);
937         }
938     }
939 }
940 
941 
942 std::string
getAttribute(SumoXMLAttr key) const943 GNEJunction::getAttribute(SumoXMLAttr key) const {
944     switch (key) {
945         case SUMO_ATTR_ID:
946             return getMicrosimID();
947         case SUMO_ATTR_POSITION:
948             return toString(myNBNode.getPosition());
949         case SUMO_ATTR_TYPE:
950             return toString(myNBNode.getType());
951         case GNE_ATTR_MODIFICATION_STATUS:
952             return myLogicStatus;
953         case SUMO_ATTR_SHAPE:
954             return toString(myNBNode.getShape());
955         case SUMO_ATTR_RADIUS:
956             return toString(myNBNode.getRadius());
957         case SUMO_ATTR_TLTYPE:
958             // @todo this causes problems if the node were to have multiple programs of different type (plausible)
959             return myNBNode.isTLControlled() ? toString((*myNBNode.getControllingTLS().begin())->getType()) : "";
960         case SUMO_ATTR_TLID:
961             return myNBNode.isTLControlled() ? toString((*myNBNode.getControllingTLS().begin())->getID()) : "";
962         case SUMO_ATTR_KEEP_CLEAR:
963             // keep clear is only used as a convenience feature in plain xml
964             // input. When saving to .net.xml the status is saved only for the connections
965             // to show the correct state we must check all connections
966             if (!myNBNode.getKeepClear()) {
967                 return toString(false);
968             } else {
969                 for (auto i : myGNEIncomingEdges) {
970                     for (auto j : i->getGNEConnections()) {
971                         if (j->getNBEdgeConnection().keepClear) {
972                             return toString(true);
973                         }
974                     }
975                 }
976                 return toString(false);
977             }
978         case SUMO_ATTR_RIGHT_OF_WAY:
979             return SUMOXMLDefinitions::RightOfWayValues.getString(myNBNode.getRightOfWay());
980         case SUMO_ATTR_FRINGE:
981             return SUMOXMLDefinitions::FringeTypeValues.getString(myNBNode.getFringeType());
982         case GNE_ATTR_SELECTED:
983             return toString(isAttributeCarrierSelected());
984         case GNE_ATTR_GENERIC:
985             return getGenericParametersStr();
986         default:
987             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
988     }
989 }
990 
991 
992 void
setAttribute(SumoXMLAttr key,const std::string & value,GNEUndoList * undoList)993 GNEJunction::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {
994     if (value == getAttribute(key)) {
995         return; //avoid needless changes, later logic relies on the fact that attributes have changed
996     }
997     switch (key) {
998         case SUMO_ATTR_ID:
999         case SUMO_ATTR_POSITION:
1000         case GNE_ATTR_MODIFICATION_STATUS:
1001         case SUMO_ATTR_SHAPE:
1002         case SUMO_ATTR_RADIUS:
1003         case SUMO_ATTR_TLTYPE:
1004         case SUMO_ATTR_RIGHT_OF_WAY:
1005         case SUMO_ATTR_FRINGE:
1006         case GNE_ATTR_SELECTED:
1007         case GNE_ATTR_GENERIC:
1008             undoList->add(new GNEChange_Attribute(this, myNet, key, value), true);
1009             break;
1010         case SUMO_ATTR_KEEP_CLEAR:
1011             // change Keep Clear attribute in all connections
1012             undoList->p_begin("change keepClear for whole junction");
1013             for (auto i : myGNEIncomingEdges) {
1014                 for (auto j : i->getGNEConnections()) {
1015                     undoList->add(new GNEChange_Attribute(j, myNet, key, value), true);
1016                 }
1017             }
1018             undoList->add(new GNEChange_Attribute(this, myNet, key, value), true);
1019             undoList->p_end();
1020             break;
1021         case SUMO_ATTR_TYPE: {
1022             undoList->p_begin("change " + getTagStr() + " type");
1023             if (NBNode::isTrafficLight(SUMOXMLDefinitions::NodeTypes.get(value))) {
1024                 if (getNBNode()->isTLControlled() &&
1025                         // if switching changing from or to traffic_light_right_on_red we need to remove the old plan
1026                         (getNBNode()->getType() == NODETYPE_TRAFFIC_LIGHT_RIGHT_ON_RED
1027                          || SUMOXMLDefinitions::NodeTypes.get(value) == NODETYPE_TRAFFIC_LIGHT_RIGHT_ON_RED)
1028                    ) {
1029                     // make a copy because we will modify the original
1030                     const std::set<NBTrafficLightDefinition*> copyOfTls = myNBNode.getControllingTLS();
1031                     for (auto it : copyOfTls) {
1032                         undoList->add(new GNEChange_TLS(this, it, false), true);
1033                     }
1034                 }
1035                 if (!getNBNode()->isTLControlled()) {
1036                     // create new traffic light
1037                     undoList->add(new GNEChange_TLS(this, nullptr, true), true);
1038                 }
1039             } else if (getNBNode()->isTLControlled()) {
1040                 // delete old traffic light
1041                 // make a copy because we will modify the original
1042                 const std::set<NBTrafficLightDefinition*> copyOfTls = myNBNode.getControllingTLS();
1043                 for (auto it : copyOfTls) {
1044                     undoList->add(new GNEChange_TLS(this, it, false, false), true);
1045                 }
1046             }
1047             // must be the final step, otherwise we do not know which traffic lights to remove via GNEChange_TLS
1048             undoList->add(new GNEChange_Attribute(this, myNet, key, value), true);
1049             undoList->p_end();
1050             break;
1051         }
1052         case SUMO_ATTR_TLID: {
1053             undoList->p_begin("change " + toString(SUMO_TAG_TRAFFIC_LIGHT) + " id");
1054             const std::set<NBTrafficLightDefinition*> copyOfTls = myNBNode.getControllingTLS();
1055             assert(copyOfTls.size() > 0);
1056             NBTrafficLightDefinition* currentTLS = *copyOfTls.begin();
1057             NBTrafficLightDefinition* currentTLSCopy = nullptr;
1058             const bool currentIsSingle = currentTLS->getNodes().size() == 1;
1059             const bool currentIsLoaded = dynamic_cast<NBLoadedSUMOTLDef*>(currentTLS) != nullptr;
1060             if (currentIsLoaded) {
1061                 currentTLSCopy = new NBLoadedSUMOTLDef(currentTLS,
1062                                                        dynamic_cast<NBLoadedSUMOTLDef*>(currentTLS)->getLogic());
1063             }
1064             // remove from previous tls
1065             for (auto it : copyOfTls) {
1066                 undoList->add(new GNEChange_TLS(this, it, false), true);
1067             }
1068             NBTrafficLightLogicCont& tlCont = myNet->getTLLogicCont();
1069             // programs to which the current node shall be added
1070             const std::map<std::string, NBTrafficLightDefinition*> programs = tlCont.getPrograms(value);
1071             if (programs.size() > 0) {
1072                 for (auto it : programs) {
1073                     NBTrafficLightDefinition* oldTLS = it.second;
1074                     if (dynamic_cast<NBOwnTLDef*>(oldTLS) != nullptr) {
1075                         undoList->add(new GNEChange_TLS(this, oldTLS, true), true);
1076                     } else {
1077                         // delete and re-create the definition because the loaded phases are now invalid
1078                         if (dynamic_cast<NBLoadedSUMOTLDef*>(oldTLS) != nullptr &&
1079                                 dynamic_cast<NBLoadedSUMOTLDef*>(oldTLS)->usingSignalGroups()) {
1080                             // keep the old program and add all-red state for the added links
1081                             NBLoadedSUMOTLDef* newTLSJoined = new NBLoadedSUMOTLDef(oldTLS, dynamic_cast<NBLoadedSUMOTLDef*>(oldTLS)->getLogic());
1082                             newTLSJoined->joinLogic(currentTLSCopy);
1083                             undoList->add(new GNEChange_TLS(this, newTLSJoined, true, true), true);
1084                         } else {
1085                             undoList->add(new GNEChange_TLS(this, nullptr, true, false, value), true);
1086                         }
1087                         NBTrafficLightDefinition* newTLS = *myNBNode.getControllingTLS().begin();
1088                         // switch from old to new definition
1089                         const std::vector<NBNode*> copyOfNodes = oldTLS->getNodes();
1090                         for (auto it_node : copyOfNodes) {
1091                             GNEJunction* oldJunction = myNet->retrieveJunction(it_node->getID());
1092                             undoList->add(new GNEChange_TLS(oldJunction, oldTLS, false), true);
1093                             undoList->add(new GNEChange_TLS(oldJunction, newTLS, true), true);
1094                         }
1095                     }
1096                 }
1097             } else {
1098                 if (currentIsSingle && currentIsLoaded) {
1099                     // rename the traffic light but keep everything else
1100                     NBTrafficLightLogic* renamedLogic = dynamic_cast<NBLoadedSUMOTLDef*>(currentTLSCopy)->getLogic();
1101                     renamedLogic->setID(value);
1102                     NBLoadedSUMOTLDef* renamedTLS = new NBLoadedSUMOTLDef(currentTLSCopy, renamedLogic);
1103                     renamedTLS->setID(value);
1104                     undoList->add(new GNEChange_TLS(this, renamedTLS, true, true), true);
1105                 } else {
1106                     // create new traffic light
1107                     undoList->add(new GNEChange_TLS(this, nullptr, true, false, value), true);
1108                 }
1109             }
1110             delete currentTLSCopy;
1111             undoList->p_end();
1112             break;
1113         }
1114         default:
1115             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
1116     }
1117 }
1118 
1119 
1120 bool
isValid(SumoXMLAttr key,const std::string & value)1121 GNEJunction::isValid(SumoXMLAttr key, const std::string& value) {
1122     switch (key) {
1123         case SUMO_ATTR_ID:
1124             return SUMOXMLDefinitions::isValidNetID(value) && (myNet->retrieveJunction(value, false) == nullptr);
1125         case SUMO_ATTR_TYPE:
1126             return SUMOXMLDefinitions::NodeTypes.hasString(value);
1127         case SUMO_ATTR_POSITION:
1128             return canParse<Position>(value);
1129         case SUMO_ATTR_SHAPE:
1130             // empty shapes are allowed
1131             return canParse<PositionVector>(value);
1132         case SUMO_ATTR_RADIUS:
1133             return canParse<double>(value);
1134         case SUMO_ATTR_TLTYPE:
1135             return myNBNode.isTLControlled() && SUMOXMLDefinitions::TrafficLightTypes.hasString(value);
1136         case SUMO_ATTR_TLID:
1137             return myNBNode.isTLControlled() && (value != "");
1138         case SUMO_ATTR_KEEP_CLEAR:
1139             return canParse<bool>(value);
1140         case SUMO_ATTR_RIGHT_OF_WAY:
1141             return SUMOXMLDefinitions::RightOfWayValues.hasString(value);
1142         case SUMO_ATTR_FRINGE:
1143             return SUMOXMLDefinitions::FringeTypeValues.hasString(value);
1144         case GNE_ATTR_SELECTED:
1145             return canParse<bool>(value);
1146         case GNE_ATTR_GENERIC:
1147             return isGenericParametersValid(value);
1148         default:
1149             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
1150     }
1151 }
1152 
1153 
1154 std::string
getGenericParametersStr() const1155 GNEJunction::getGenericParametersStr() const {
1156     std::string result;
1157     // Generate an string using the following structure: "key1=value1|key2=value2|...
1158     for (auto i : myNBNode.getParametersMap()) {
1159         result += i.first + "=" + i.second + "|";
1160     }
1161     // remove the last "|"
1162     if (!result.empty()) {
1163         result.pop_back();
1164     }
1165     return result;
1166 }
1167 
1168 
1169 std::vector<std::pair<std::string, std::string> >
getGenericParameters() const1170 GNEJunction::getGenericParameters() const {
1171     std::vector<std::pair<std::string, std::string> >  result;
1172     // iterate over parameters map and fill result
1173     for (auto i : myNBNode.getParametersMap()) {
1174         result.push_back(std::make_pair(i.first, i.second));
1175     }
1176     return result;
1177 }
1178 
1179 
1180 void
setGenericParametersStr(const std::string & value)1181 GNEJunction::setGenericParametersStr(const std::string& value) {
1182     // clear parameters
1183     myNBNode.clearParameter();
1184     // separate value in a vector of string using | as separator
1185     std::vector<std::string> parsedValues;
1186     StringTokenizer stValues(value, "|", true);
1187     while (stValues.hasNext()) {
1188         parsedValues.push_back(stValues.next());
1189     }
1190     // check that parsed values (A=B)can be parsed in generic parameters
1191     for (auto i : parsedValues) {
1192         std::vector<std::string> parsedParameters;
1193         StringTokenizer stParam(i, "=", true);
1194         while (stParam.hasNext()) {
1195             parsedParameters.push_back(stParam.next());
1196         }
1197         // Check that parsed parameters are exactly two and contains valid chracters
1198         if (parsedParameters.size() == 2 && SUMOXMLDefinitions::isValidGenericParameterKey(parsedParameters.front()) && SUMOXMLDefinitions::isValidGenericParameterValue(parsedParameters.back())) {
1199             myNBNode.setParameter(parsedParameters.front(), parsedParameters.back());
1200         }
1201     }
1202 }
1203 
1204 
1205 void
setResponsible(bool newVal)1206 GNEJunction::setResponsible(bool newVal) {
1207     myAmResponsible = newVal;
1208 }
1209 
1210 // ===========================================================================
1211 // private
1212 // ===========================================================================
1213 
1214 void
setAttribute(SumoXMLAttr key,const std::string & value)1215 GNEJunction::setAttribute(SumoXMLAttr key, const std::string& value) {
1216     switch (key) {
1217         case SUMO_ATTR_ID: {
1218             myNet->renameJunction(this, value);
1219             break;
1220         }
1221         case SUMO_ATTR_TYPE: {
1222             SumoXMLNodeType type = SUMOXMLDefinitions::NodeTypes.get(value);
1223             if (myNBNode.getType() == NODETYPE_PRIORITY && type == NODETYPE_RIGHT_BEFORE_LEFT) {
1224                 myNet->getNetBuilder()->getEdgeCont().removeRoundabout(&myNBNode);
1225             }
1226             myNBNode.reinit(myNBNode.getPosition(), type);
1227             break;
1228         }
1229         case SUMO_ATTR_POSITION: {
1230             // set new position in NBNode (note: Junctions don't need to refresh it in the RTREE due the variable myJunctionBoundary
1231             moveJunctionGeometry(parse<Position>(value), true);
1232             // mark this connections and all of the junction's Neighbours as deprecated
1233             markConnectionsDeprecated(true);
1234             break;
1235         }
1236         case GNE_ATTR_MODIFICATION_STATUS:
1237             if (myLogicStatus == FEATURE_GUESSED && value != FEATURE_GUESSED) {
1238                 // clear guessed connections. previous connections will be restored
1239                 myNBNode.invalidateIncomingConnections();
1240                 // Clear GNEConnections of incoming edges
1241                 for (auto i : myGNEIncomingEdges) {
1242                     i->clearGNEConnections();
1243                 }
1244             }
1245             myLogicStatus = value;
1246             break;
1247         case SUMO_ATTR_SHAPE: {
1248             const PositionVector shape = parse<PositionVector>(value);
1249             myNBNode.setCustomShape(shape);
1250             // mark this connections and all of the junction's Neighbours as deprecated
1251             markConnectionsDeprecated(true);
1252             break;
1253         }
1254         case SUMO_ATTR_RADIUS: {
1255             myNBNode.setRadius(parse<double>(value));
1256             break;
1257         }
1258         case SUMO_ATTR_TLTYPE: {
1259             const std::set<NBTrafficLightDefinition*> copyOfTls = myNBNode.getControllingTLS();
1260             for (auto it : copyOfTls) {
1261                 it->setType(SUMOXMLDefinitions::TrafficLightTypes.get(value));
1262             }
1263             break;
1264         }
1265         case SUMO_ATTR_KEEP_CLEAR: {
1266             myNBNode.setKeepClear(parse<bool>(value));
1267             break;
1268         }
1269         case SUMO_ATTR_RIGHT_OF_WAY:
1270             myNBNode.setRightOfWay(SUMOXMLDefinitions::RightOfWayValues.get(value));
1271             break;
1272         case SUMO_ATTR_FRINGE:
1273             myNBNode.setFringeType(SUMOXMLDefinitions::FringeTypeValues.get(value));
1274             break;
1275         case GNE_ATTR_SELECTED:
1276             if (parse<bool>(value)) {
1277                 selectAttributeCarrier();
1278             } else {
1279                 unselectAttributeCarrier();
1280             }
1281             break;
1282         case GNE_ATTR_GENERIC:
1283             setGenericParametersStr(value);
1284             break;
1285         default:
1286             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
1287     }
1288     // check if updated attribute requieres update geometry
1289     if (myTagProperty.hasAttribute(key) && myTagProperty.getAttributeProperties(key).requiereUpdateGeometry()) {
1290         updateGeometry(true);
1291     }
1292 }
1293 
1294 
1295 double
getColorValue(const GUIVisualizationSettings & s,bool bubble) const1296 GNEJunction::getColorValue(const GUIVisualizationSettings& s, bool bubble) const {
1297     switch (s.junctionColorer.getActive()) {
1298         case 0:
1299             if (bubble
1300                     // ensure visibility of red connections
1301                     && !(s.editMode == GNE_NMODE_TLS && myNBNode.isTLControlled())
1302                ) {
1303                 return 1;
1304             } else {
1305                 return 0;
1306             }
1307         case 1:
1308             return isAttributeCarrierSelected();
1309         case 2:
1310             switch (myNBNode.getType()) {
1311                 case NODETYPE_TRAFFIC_LIGHT:
1312                     return 0;
1313                 case NODETYPE_TRAFFIC_LIGHT_NOJUNCTION:
1314                     return 1;
1315                 case NODETYPE_PRIORITY:
1316                     return 2;
1317                 case NODETYPE_PRIORITY_STOP:
1318                     return 3;
1319                 case NODETYPE_RIGHT_BEFORE_LEFT:
1320                     return 4;
1321                 case NODETYPE_ALLWAY_STOP:
1322                     return 5;
1323                 case NODETYPE_DISTRICT:
1324                     return 6;
1325                 case NODETYPE_NOJUNCTION:
1326                     return 7;
1327                 case NODETYPE_DEAD_END:
1328                 case NODETYPE_DEAD_END_DEPRECATED:
1329                     return 8;
1330                 case NODETYPE_UNKNOWN:
1331                 case NODETYPE_INTERNAL:
1332                     assert(false);
1333                     return 8;
1334                 case NODETYPE_RAIL_SIGNAL:
1335                     return 9;
1336                 case NODETYPE_ZIPPER:
1337                     return 10;
1338                 case NODETYPE_TRAFFIC_LIGHT_RIGHT_ON_RED:
1339                     return 11;
1340                 case NODETYPE_RAIL_CROSSING:
1341                     return 12;
1342             }
1343         case 3:
1344             return myNBNode.getPosition().z();
1345         default:
1346             assert(false);
1347             return 0;
1348     }
1349 }
1350 
1351 
1352 void
moveJunctionGeometry(const Position & pos,bool updateGrid)1353 GNEJunction::moveJunctionGeometry(const Position& pos, bool updateGrid) {
1354     const Position orig = myNBNode.getPosition();
1355     myNBNode.reinit(pos, myNBNode.getType());
1356     // set new position of adjacent edges
1357     for (auto i : getNBNode()->getEdges()) {
1358         myNet->retrieveEdge(i->getID())->updateJunctionPosition(this, orig, updateGrid);
1359     }
1360     // Update shapes without include connections, because the aren't showed in Move mode
1361     updateShapesAndGeometries(updateGrid);
1362 }
1363 
1364 
1365 RGBColor
setColor(const GUIVisualizationSettings & s,bool bubble) const1366 GNEJunction::setColor(const GUIVisualizationSettings& s, bool bubble) const {
1367     RGBColor color = s.junctionColorer.getScheme().getColor(getColorValue(s, bubble));
1368     // override with special colors (unless the color scheme is based on selection)
1369     if (drawUsingSelectColor() && s.junctionColorer.getActive() != 1) {
1370         color = s.selectionColor;
1371     }
1372     if (myAmCreateEdgeSource) {
1373         color = RGBColor(0, 255, 0);
1374     }
1375     GLHelper::setColor(color);
1376     return color;
1377 }
1378 
1379 void
addTrafficLight(NBTrafficLightDefinition * tlDef,bool forceInsert)1380 GNEJunction::addTrafficLight(NBTrafficLightDefinition* tlDef, bool forceInsert) {
1381     NBTrafficLightLogicCont& tlCont = myNet->getTLLogicCont();
1382     tlCont.insert(tlDef, forceInsert); // may return false for tlDef which controls multiple junctions
1383     tlDef->addNode(&myNBNode);
1384 }
1385 
1386 
1387 void
removeTrafficLight(NBTrafficLightDefinition * tlDef)1388 GNEJunction::removeTrafficLight(NBTrafficLightDefinition* tlDef) {
1389     NBTrafficLightLogicCont& tlCont = myNet->getTLLogicCont();
1390     if (tlDef->getNodes().size() == 1) {
1391         tlCont.extract(tlDef);
1392     }
1393     myNBNode.removeTrafficLight(tlDef);
1394 }
1395 
1396 /****************************************************************************/
1397