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