Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
GNEEdge.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-2023 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
18// A road/street connecting two junctions (netedit-version, adapted from GUIEdge)
19// Basically a container for an NBEdge with drawing and editing capabilities
20/****************************************************************************/
21#include <config.h>
22
23#include <netedit/GNENet.h>
24#include <netedit/GNEUndoList.h>
25#include <netedit/GNEViewNet.h>
39
40#include "GNEConnection.h"
41#include "GNECrossing.h"
42#include "GNEEdge.h"
43#include "GNEEdgeType.h"
44#include "GNELaneType.h"
45#include "GNEEdgeTemplate.h"
46#include "GNELaneTemplate.h"
47
48
49//#define DEBUG_SMOOTH_GEOM
50//#define DEBUGCOND(obj) (true)
51#define VEHICLE_GAP 1
52#define ENDPOINT_TOLERANCE 2
53
54// ===========================================================================
55// static
56// ===========================================================================
59
60// ===========================================================================
61// members methods
62// ===========================================================================
63
64GNEEdge::GNEEdge(GNENet* net, NBEdge* nbe, bool wasSplit, bool loaded):
65 GNENetworkElement(net, nbe->getID(), GLO_EDGE, SUMO_TAG_EDGE, GUIIconSubSys::getIcon(GUIIcon::EDGE), {
68},
69{}, {}, {}, {}, {}),
70myNBEdge(nbe),
71myLanes(0),
72myAmResponsible(false),
73myWasSplit(wasSplit),
74myConnectionStatus(loaded ? FEATURE_LOADED : FEATURE_GUESSED),
75myUpdateGeometry(true) {
76 // Create lanes
77 int numLanes = myNBEdge->getNumLanes();
78 myLanes.reserve(numLanes);
79 for (int i = 0; i < numLanes; i++) {
80 myLanes.push_back(new GNELane(this, i));
81 myLanes.back()->incRef("GNEEdge::GNEEdge");
82 }
83 // update Lane geometries
84 for (const auto& lane : myLanes) {
85 lane->updateGeometry();
86 }
87 // update centering boundary without updating grid
88 updateCenteringBoundary(false);
89}
90
91
93 // Delete references to this edge in lanes
94 for (const auto& lane : myLanes) {
95 lane->decRef("GNEEdge::~GNEEdge");
96 if (lane->unreferenced()) {
97 // check if remove it from Attribute Carriers
98 if (myNet->getAttributeCarriers()->getLanes().count(lane) > 0) {
100 }
101 // show extra information for tests
102 WRITE_DEBUG("Deleting unreferenced " + lane->getTagStr() + " '" + lane->getID() + "' in GNEEdge destructor");
103 delete lane;
104 }
105 }
106 // delete references to this edge in connections
107 for (const auto& connection : myGNEConnections) {
108 connection->decRef("GNEEdge::~GNEEdge");
109 if (connection->unreferenced()) {
110 // check if remove it from Attribute Carriers
111 if (myNet->getAttributeCarriers()->getConnections().count(connection) > 0) {
113 }
114 // show extra information for tests
115 WRITE_DEBUG("Deleting unreferenced " + connection->getTagStr() + " '" + connection->getID() + "' in GNEEdge destructor");
116 delete connection;
117 }
118 }
119 if (myAmResponsible) {
120 delete myNBEdge;
121 }
122}
123
124
125bool
127 if (getFromJunction()->getNBNode()->getPosition() != getToJunction()->getNBNode()->getPosition()) {
128 return true;
129 } else {
130 return false;
131 }
132}
133
134
135std::string
137 return "Parent junctions are in the same position: " +
138 toString(getFromJunction()->getNBNode()->getPosition().x()) + ", " +
139 toString(getFromJunction()->getNBNode()->getPosition().y());
140}
141
142
143void
145 // first check if myUpdateGeometry flag is enabled
146 if (myUpdateGeometry) {
147 // Update geometry of lanes
148 for (const auto& lane : myLanes) {
149 lane->updateGeometry();
150 }
151 // Update geometry of connections
152 for (const auto& connection : myGNEConnections) {
153 connection->updateGeometry();
154 }
155 // Update geometry of additionals children vinculated to this edge
156 for (const auto& childAdditional : getChildAdditionals()) {
157 childAdditional->updateGeometry();
158 }
159 // Update geometry of additionals demand elements vinculated to this edge
160 for (const auto& childDemandElement : getChildDemandElements()) {
161 childDemandElement->updateGeometry();
162 }
163 // Update geometry of additionals generic datas vinculated to this edge
164 for (const auto& childGenericData : getChildGenericDatas()) {
165 childGenericData->updateGeometry();
166 }
167 // compute geometry of path elements elements vinculated with this edge (depending of showDemandElements)
169 for (const auto& childAdditional : getChildAdditionals()) {
170 childAdditional->computePathElement();
171 }
172 for (const auto& childDemandElement : getChildDemandElements()) {
173 childDemandElement->computePathElement();
174 }
175 for (const auto& childGenericData : getChildGenericDatas()) {
176 childGenericData->computePathElement();
177 }
178 }
179 }
180 // update vehicle geometry
182 // update stack labels
186}
187
188
191 return myLanes.front()->getPositionInView();
192}
193
194
197 // get circle width
198 const double circleWidth = getSnapRadius(false);
199 // check if edge is selected
201 // check if both junctions are selected
210 // special case: when only a single edge is selected, move all shape points (including custom end points)
212 } else {
213 // synchronized movement of a single point
214 return processNoneJunctionSelected(circleWidth);
215 }
216 } else {
217 // calculate move shape operation (because there are only an edge selected)
219 }
220 } else {
221 // calculate move shape operation
223 }
224}
225
226
227void
228GNEEdge::removeGeometryPoint(const Position clickedPosition, GNEUndoList* undoList) {
229 // declare shape to move
231 // obtain flags for start and end positions
232 const bool customStartPosition = (myNBEdge->getGeometry().front().distanceSquaredTo2D(getFromJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE);
233 const bool customEndPosition = (myNBEdge->getGeometry().back().distanceSquaredTo2D(getToJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE);
234 // get variable for last index
235 const int lastIndex = (int)myNBEdge->getGeometry().size() - 1;
236 // flag to enable/disable remove geometry point
237 bool removeGeometryPoint = true;
238 // obtain index
239 const int index = myNBEdge->getGeometry().indexOfClosest(clickedPosition, true);
240 // check index
241 if (index == -1) {
242 removeGeometryPoint = false;
243 }
244 // check distance
245 if (shape[index].distanceSquaredTo2D(clickedPosition) > getSnapRadius(true)) {
246 removeGeometryPoint = false;
247 }
248 // check custom start position
249 if (!customStartPosition && (index == 0)) {
250 removeGeometryPoint = false;
251 }
252 // check custom end position
253 if (!customEndPosition && (index == lastIndex)) {
254 removeGeometryPoint = false;
255 }
256 // check if we can remove geometry point
258 // check if we're removing first geometry proint
259 if (index == 0) {
260 // commit new geometry start
261 undoList->begin(GUIIcon::EDGE, "remove first geometry point of " + getTagStr());
263 undoList->end();
264 } else if (index == lastIndex) {
265 // commit new geometry end
266 undoList->begin(GUIIcon::EDGE, "remove last geometry point of " + getTagStr());
268 undoList->end();
269 } else {
270 // remove geometry point
271 shape.erase(shape.begin() + index);
272 // get innen shape
273 shape.pop_front();
274 shape.pop_back();
275 // remove double points
276 shape.removeDoublePoints(getSnapRadius(false));
277 // commit new shape
278 undoList->begin(GUIIcon::EDGE, "remove geometry point of " + getTagStr());
279 undoList->changeAttribute(new GNEChange_Attribute(this, SUMO_ATTR_SHAPE, toString(shape)));
280 undoList->end();
281 }
282 }
283}
284
285
286bool
288 if (myNBEdge->getGeometry().front().distanceSquaredTo2D(getFromJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE) {
289 return true;
290 } else if (myNBEdge->getGeometry().back().distanceSquaredTo2D(getToJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE) {
291 return true;
292 } else {
293 return false;
294 }
295}
296
297
298bool
300 if (myNBEdge->getGeometry().front().distanceSquaredTo2D(getFromJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE) {
301 return (myNBEdge->getGeometry().front().distanceSquaredTo2D(pos) < getSnapRadius(true));
302 } else {
303 return false;
304 }
305}
306
307
308bool
310 if (myNBEdge->getGeometry().back().distanceSquaredTo2D(getToJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE) {
311 return (myNBEdge->getGeometry().back().distanceSquaredTo2D(pos) < getSnapRadius(true));
312 } else {
313 return false;
314 }
315}
316
317
318bool
320 // get snap radius
321 const auto snapRadius = getSnapRadius(true);
322 // first check inner geometry
323 const PositionVector innenShape = myNBEdge->getInnerGeometry();
324 // iterate over geometry point
325 for (const auto& geometryPoint : innenShape) {
326 if (geometryPoint.distanceSquaredTo2D(pos) < snapRadius) {
327 return true;
328 }
329 }
330 // check start and end shapes
332 return true;
333 } else {
334 return false;
335 }
336}
337
338
339void
341 Position delta = junction->getNBNode()->getPosition() - origPos;
343 // geometry endpoint need not equal junction position hence we modify it with delta
344 if (junction == getFromJunction()) {
345 geom[0].add(delta);
346 } else {
347 geom[-1].add(delta);
348 }
349 setGeometry(geom, false);
350}
351
352
353double
355 return s.addSize.getExaggeration(s, this);
356}
357
358
359void
360GNEEdge::updateCenteringBoundary(const bool updateGrid) {
361 // Remove object from net
362 if (updateGrid) {
364 }
365 // use as boundary the first lane boundary
366 myBoundary = myLanes.front()->getCenteringBoundary();
367 // add lane boundaries
368 for (const auto& lane : myLanes) {
369 lane->updateCenteringBoundary(false);
370 myBoundary.add(lane->getCenteringBoundary());
371 // add parkingArea boundaries
372 for (const auto& additional : lane->getChildAdditionals()) {
373 if (additional->getTagProperty().getTag() == SUMO_TAG_PARKING_AREA) {
374 myBoundary.add(additional->getCenteringBoundary());
375 }
376 }
377 }
378 // ensure that geometry points are selectable even if the lane geometry is strange
379 for (const Position& pos : myNBEdge->getGeometry()) {
380 myBoundary.add(pos);
381 }
382 // add junction positions
385 // grow boundary
386 myBoundary.grow(10);
387 // add object into net
388 if (updateGrid) {
390 }
391}
392
393
394const std::string
396 return myNBEdge->getStreetName();
397}
398
399
402 GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, *this);
403 buildPopupHeader(ret, app);
406 // build selection and show parameters menu
409 // build position copy entry
410 buildPositionCopyEntry(ret, app);
411 return ret;
412}
413
414
415std::vector<GNEEdge*>
419
420
421void
423 // check if boundary has to be drawn
424 if (s.drawBoundaries) {
426 }
427 // draw edge geometry points (always before lanes)
429 // draw edge shape (a red line only visible if lane shape is strange)
430 drawEdgeShape(s);
431 // draw lanes
432 for (const auto& lane : myLanes) {
433 lane->drawGL(s);
434 }
435 // draw edge stopOffset
437 // draw childrens
438 drawChildrens(s);
439 // draw lock icon
441 // draw edge name
442 drawEdgeName(s);
443 // draw dotted contours
444 if (myLanes.size() > 1) {
447 }
448 if ((myNet->getViewNet()->getFrontAttributeCarrier() == this)) {
450 }
451 if (myNet->getViewNet()->drawDeleteContour(this, this)) {
453 }
454 if (myNet->getViewNet()->drawSelectContour(this, this)) {
456 }
459 }
460 }
461}
462
463
464void
466 // Check if edge can be deleted
468 myNet->deleteEdge(this, myNet->getViewNet()->getUndoList(), false);
469 }
470}
471
472
473void
477
478
479NBEdge*
481 return myNBEdge;
482}
483
484
487 const PositionVector& geom = myNBEdge->getGeometry();
488 int index = geom.indexOfClosest(clickPos, true);
489 if (geom[index].distanceSquaredTo2D(clickPos) < getSnapRadius(true)) {
490 // split at existing geometry point
491 return myNet->getViewNet()->snapToActiveGrid(geom[index]);
492 } else {
493 // split straight between the next two points
495 }
496}
497
498
499void
501 if ((myNBEdge->getGeometry().front().distanceSquaredTo2D(getFromJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE) &&
502 (myNBEdge->getGeometry().front().distanceSquaredTo2D(pos) < getSnapRadius(true))) {
503 undoList->begin(GUIIcon::EDGE, "remove endpoint");
504 setAttribute(GNE_ATTR_SHAPE_START, "", undoList);
505 undoList->end();
506 } else if ((myNBEdge->getGeometry().back().distanceSquaredTo2D(getToJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE) &&
507 (myNBEdge->getGeometry().back().distanceSquaredTo2D(pos) < getSnapRadius(true))) {
508 undoList->begin(GUIIcon::EDGE, "remove endpoint");
509 setAttribute(GNE_ATTR_SHAPE_END, "", undoList);
510 undoList->end();
511 } else {
512 // we need to create new Start/End position over Edge shape, not over clicked position
514 if (offset != GeomHelper::INVALID_OFFSET) {
516 // calculate position over edge shape relative to clicked position
517 Position newPos = geom.positionAtOffset2D(offset);
518 // snap new position to grid
519 newPos = myNet->getViewNet()->snapToActiveGrid(newPos);
520 undoList->begin(GUIIcon::EDGE, "set endpoint");
521 const int index = geom.indexOfClosest(pos, true);
522 const Position destPos = getToJunction()->getNBNode()->getPosition();
523 const Position sourcePos = getFromJunction()->getNBNode()->getPosition();
524 if (pos.distanceTo2D(destPos) < pos.distanceTo2D(sourcePos)) {
525 // check if snap to existing geometrypoint
526 if (geom[index].distanceSquaredTo2D(pos) < getSnapRadius(true)) {
527 newPos = geom[index];
528 // remove existent geometry point to avoid double points
529 removeGeometryPoint(newPos, undoList);
530 }
531 setAttribute(GNE_ATTR_SHAPE_END, toString(newPos), undoList);
533 } else {
534 // check if snap to existing geometry point
535 if (geom[index].distanceSquaredTo2D(pos) < getSnapRadius(true)) {
536 newPos = geom[index];
537 // remove existent geometry point to avoid double points
538 removeGeometryPoint(newPos, undoList);
539 }
540 setAttribute(GNE_ATTR_SHAPE_START, toString(newPos), undoList);
542 }
543 undoList->end();
544 }
545 }
546}
547
548
549void
552 Position sourcePos = getFromJunction()->getNBNode()->getPosition();
553 if (pos.distanceTo2D(destPos) < pos.distanceTo2D(sourcePos)) {
554 setAttribute(GNE_ATTR_SHAPE_END, toString(destPos), undoList);
556 } else {
557 setAttribute(GNE_ATTR_SHAPE_START, toString(sourcePos), undoList);
559 }
560}
561
562
563void
565 // reset shape start
566 setAttribute(GNE_ATTR_SHAPE_END, "", undoList);
568 // reset shape end
569 setAttribute(GNE_ATTR_SHAPE_START, "", undoList);
571}
572
573void
575 // set new geometry
576 const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
577 if (lefthand) {
578 geom.mirrorX();
579 myNBEdge->mirrorX();
580 }
581 myNBEdge->setGeometry(geom, inner);
582 if (lefthand) {
583 myNBEdge->mirrorX();
584 }
585 // update geometry
587 // invalidate junction source shape
589 // iterate over first parent junction edges and update geometry
590 for (const auto& edge : getFromJunction()->getGNEIncomingEdges()) {
591 edge->updateGeometry();
592 }
593 for (const auto& edge : getFromJunction()->getGNEOutgoingEdges()) {
594 edge->updateGeometry();
595 }
596 // invalidate junction destiny shape
598 // iterate over second parent junction edges and update geometry
599 for (const auto& edge : getToJunction()->getGNEIncomingEdges()) {
600 edge->updateGeometry();
601 }
602 for (const auto& edge : getToJunction()->getGNEOutgoingEdges()) {
603 edge->updateGeometry();
604 }
605}
606
607
608const Position
610 PositionVector laneShape = myLanes.front()->getLaneShape();
611 laneShape.move2side(myLanes.front()->getParentEdge()->getNBEdge()->getLaneWidth(myLanes.front()->getIndex()) / 2);
612 return laneShape.front();
613}
614
615
616const Position
618 PositionVector laneShape = myLanes.back()->getLaneShape();
619 laneShape.move2side(-1 * myLanes.back()->getParentEdge()->getNBEdge()->getLaneWidth(myLanes.back()->getIndex()) / 2);
620 return laneShape.front();
621}
622
623
624const Position
626 PositionVector laneShape = myLanes.front()->getLaneShape();
627 laneShape.move2side(myLanes.front()->getParentEdge()->getNBEdge()->getLaneWidth(myLanes.front()->getIndex()) / 2);
628 return laneShape.back();
629}
630
631
632const Position
634 PositionVector laneShape = myLanes.back()->getLaneShape();
635 laneShape.move2side(-1 * myLanes.back()->getParentEdge()->getNBEdge()->getLaneWidth(myLanes.back()->getIndex()) / 2);
636 return laneShape.back();
637}
638
639void
640GNEEdge::remakeGNEConnections(bool junctionsReady) {
641 // create new and removed unused GNEConnections
642 const std::vector<NBEdge::Connection>& connections = myNBEdge->getConnections();
643 // create a vector to keep retrieved and created connections
644 std::vector<GNEConnection*> retrievedConnections;
645 // iterate over NBEdge::Connections of GNEEdge
646 for (const auto& connection : connections) {
647 // retrieve existent GNEConnection, or create it
648 GNEConnection* retrievedGNEConnection = retrieveGNEConnection(connection.fromLane, connection.toEdge, connection.toLane);
649 if (junctionsReady) {
650 retrievedGNEConnection->updateLinkState();
651 }
652 retrievedConnections.push_back(retrievedGNEConnection);
653 // check if previously this GNEConnections exists, and if true, remove it from myGNEConnections
654 std::vector<GNEConnection*>::iterator retrievedExists = std::find(myGNEConnections.begin(), myGNEConnections.end(), retrievedGNEConnection);
655 if (retrievedExists != myGNEConnections.end()) {
656 myGNEConnections.erase(retrievedExists);
657 } else {
658 // include reference to created GNEConnection
659 retrievedGNEConnection->incRef("GNEEdge::remakeGNEConnections");
660 }
661 // mark it as deprecated
662 retrievedGNEConnection->markConnectionGeometryDeprecated();
663 }
664 // delete non retrieved GNEConnections
665 for (const auto& connection : myGNEConnections) {
666 // decrease reference
667 connection->decRef();
668 // remove it from network
669 myNet->removeGLObjectFromGrid(connection);
670 // and from AttributeCarriers
671 if (myNet->getAttributeCarriers()->getConnections().count(connection) > 0) {
673 }
674 // delete GNEConnection if is unreferenced
675 if (connection->unreferenced()) {
676
677 // show extra information for tests
678 WRITE_DEBUG("Deleting unreferenced " + connection->getTagStr() + " '" + connection->getID() + "' in rebuildGNEConnections()");
679 delete connection;
680 }
681 }
682 // copy retrieved (existent and created) GNECrossings to myGNEConnections
683 myGNEConnections = retrievedConnections;
684}
685
686
687void
689 // Drop all existents connections that aren't referenced anymore
690 for (const auto& connection : myGNEConnections) {
691 // check if connection is selected
692 if (connection->isAttributeCarrierSelected()) {
693 connection->unselectAttributeCarrier();
694 }
695 // Dec reference of connection
696 connection->decRef("GNEEdge::clearGNEConnections");
697 // remove it from network
698 myNet->removeGLObjectFromGrid(connection);
699 // and from AttributeCarriers
700 if (myNet->getAttributeCarriers()->getConnections().count(connection) > 0) {
702 }
703 // Delete GNEConnectionToErase if is unreferenced
704 if (connection->unreferenced()) {
705 // show extra information for tests
706 WRITE_DEBUG("Deleting unreferenced " + connection->getTagStr() + " '" + connection->getID() + "' in clearGNEConnections()");
707 delete connection;
708 }
709 }
710 myGNEConnections.clear();
711}
712
713
714int
716 std::vector<GNEAdditional*> routeProbes;
717 for (auto i : getChildAdditionals()) {
718 if (i->getTagProperty().getTag() == routeProbe->getTagProperty().getTag()) {
719 routeProbes.push_back(i);
720 }
721 }
722 // return index of routeProbe in routeProbes vector
723 auto it = std::find(routeProbes.begin(), routeProbes.end(), routeProbe);
724 if (it == routeProbes.end()) {
725 return -1;
726 } else {
727 return (int)(it - routeProbes.begin());
728 }
729}
730
731
732std::vector<GNECrossing*>
734 std::vector<GNECrossing*> crossings;
735 for (auto i : getFromJunction()->getGNECrossings()) {
736 if (i->checkEdgeBelong(this)) {
737 crossings.push_back(i);
738 }
739 }
740 for (auto i : getToJunction()->getGNECrossings()) {
741 if (i->checkEdgeBelong(this)) {
742 crossings.push_back(i);
743 }
744 }
745 return crossings;
746}
747
748
749void
750GNEEdge::copyTemplate(const GNEEdgeTemplate* edgeTemplate, GNEUndoList* undoList) {
751 // copy edge-specific attributes
753 setAttribute(SUMO_ATTR_TYPE, edgeTemplate->getAttribute(SUMO_ATTR_TYPE), undoList);
758 // copy raw values for lane-specific attributes
760 setAttribute(SUMO_ATTR_SPEED, edgeTemplate->getAttribute(SUMO_ATTR_SPEED), undoList);
761 }
764 }
766 setAttribute(SUMO_ATTR_WIDTH, edgeTemplate->getAttribute(SUMO_ATTR_WIDTH), undoList);
767 }
770 }
771 // copy lane attributes as well
772 for (int i = 0; i < (int)myLanes.size(); i++) {
773 myLanes[i]->setAttribute(SUMO_ATTR_ALLOW, edgeTemplate->getLaneTemplates().at(i)->getAttribute(SUMO_ATTR_ALLOW), undoList);
774 myLanes[i]->setAttribute(SUMO_ATTR_SPEED, edgeTemplate->getLaneTemplates().at(i)->getAttribute(SUMO_ATTR_SPEED), undoList);
775 myLanes[i]->setAttribute(SUMO_ATTR_FRICTION, edgeTemplate->getLaneTemplates().at(i)->getAttribute(SUMO_ATTR_FRICTION), undoList);
776 myLanes[i]->setAttribute(SUMO_ATTR_WIDTH, edgeTemplate->getLaneTemplates().at(i)->getAttribute(SUMO_ATTR_WIDTH), undoList);
777 myLanes[i]->setAttribute(SUMO_ATTR_ENDOFFSET, edgeTemplate->getLaneTemplates().at(i)->getAttribute(SUMO_ATTR_ENDOFFSET), undoList);
778 myLanes[i]->setAttribute(GNE_ATTR_STOPOFFSET, edgeTemplate->getLaneTemplates().at(i)->getAttribute(GNE_ATTR_STOPOFFSET), undoList);
779 myLanes[i]->setAttribute(GNE_ATTR_STOPOEXCEPTION, edgeTemplate->getLaneTemplates().at(i)->getAttribute(GNE_ATTR_STOPOEXCEPTION), undoList);
780 }
781}
782
783
784void
785GNEEdge::copyEdgeType(const GNEEdgeType* edgeType, GNEUndoList* undoList) {
786 // set type (only for info)
788 // set num lanes
790 // set speed
792 // set friction
794 // set allow (no disallow)
796 // set spreadType
798 // set width
800 // set priority
802 // set parameters
804 // copy lane attributes as well
805 for (int i = 0; i < (int)myLanes.size(); i++) {
806 // now copy custom lane values
807 if (edgeType->getLaneTypes().at(i)->getAttribute(SUMO_ATTR_SPEED).size() > 0) {
808 myLanes[i]->setAttribute(SUMO_ATTR_SPEED, edgeType->getLaneTypes().at(i)->getAttribute(SUMO_ATTR_SPEED), undoList);
809 }
810 if (edgeType->getLaneTypes().at(i)->getAttribute(SUMO_ATTR_FRICTION).size() > 0) {
811 myLanes[i]->setAttribute(SUMO_ATTR_FRICTION, edgeType->getLaneTypes().at(i)->getAttribute(SUMO_ATTR_FRICTION), undoList);
812 }
813 if (edgeType->getLaneTypes().at(i)->getAttribute(SUMO_ATTR_ALLOW).size() > 0) {
814 myLanes[i]->setAttribute(SUMO_ATTR_ALLOW, edgeType->getLaneTypes().at(i)->getAttribute(SUMO_ATTR_ALLOW), undoList);
815 }
816 if (edgeType->getLaneTypes().at(i)->getAttribute(SUMO_ATTR_DISALLOW).size() > 0) {
817 myLanes[i]->setAttribute(SUMO_ATTR_DISALLOW, edgeType->getLaneTypes().at(i)->getAttribute(SUMO_ATTR_DISALLOW), undoList);
818 }
819 if (edgeType->getLaneTypes().at(i)->getAttribute(SUMO_ATTR_WIDTH).size() > 0) {
820 myLanes[i]->setAttribute(SUMO_ATTR_WIDTH, edgeType->getLaneTypes().at(i)->getAttribute(SUMO_ATTR_WIDTH), undoList);
821 }
822 if (edgeType->getLaneTypes().at(i)->getAttribute(GNE_ATTR_PARAMETERS).size() > 0) {
823 myLanes[i]->setAttribute(GNE_ATTR_PARAMETERS, edgeType->getLaneTypes().at(i)->getAttribute(GNE_ATTR_PARAMETERS), undoList);
824 }
825 }
826}
827
828
829std::set<GUIGlID>
831 std::set<GUIGlID> result;
832 for (auto i : myLanes) {
833 result.insert(i->getGlID());
834 }
835 return result;
836}
837
838
839const std::vector<GNELane*>&
841 return myLanes;
842}
843
844
845const std::vector<GNEConnection*>&
849
850
851bool
853 return myWasSplit;
854}
855
856
857std::string
859 switch (key) {
860 case SUMO_ATTR_ID:
861 return getMicrosimID();
862 case SUMO_ATTR_FROM:
863 return getFromJunction()->getID();
864 case SUMO_ATTR_TO:
865 return getToJunction()->getID();
867 return toString(myNBEdge->getNumLanes());
869 return toString(myNBEdge->getPriority());
870 case SUMO_ATTR_LENGTH:
872 case SUMO_ATTR_TYPE:
873 return myNBEdge->getTypeID();
874 case SUMO_ATTR_SHAPE:
878 case SUMO_ATTR_NAME:
879 return myNBEdge->getStreetName();
880 case SUMO_ATTR_ALLOW:
881 return (getVehicleClassNames(myNBEdge->getPermissions()) + (myNBEdge->hasLaneSpecificPermissions() ? " (combined!)" : ""));
882 case SUMO_ATTR_DISALLOW: {
884 }
885 case SUMO_ATTR_SPEED:
887 return "lane specific";
888 } else {
889 return toString(myNBEdge->getSpeed());
890 }
893 return "lane specific";
894 } else {
895 return toString(myNBEdge->getFriction());
896 }
897 case SUMO_ATTR_WIDTH:
899 return "lane specific";
901 return "default";
902 } else {
903 return toString(myNBEdge->getLaneWidth());
904 }
907 return "lane specific";
908 } else {
909 return toString(myNBEdge->getEndOffset());
910 }
912 return toString(myNBEdge->getDistance());
914 return myConnectionStatus;
916 if (myNBEdge->getGeometry().front().distanceSquaredTo2D(getFromJunction()->getNBNode()->getPosition()) <= ENDPOINT_TOLERANCE) {
917 return "";
918 } else {
919 return toString(myNBEdge->getGeometry().front());
920 }
922 if (myNBEdge->getGeometry().back().distanceSquaredTo2D(getToJunction()->getNBNode()->getPosition()) <= ENDPOINT_TOLERANCE) {
923 return "";
924 } else {
925 return toString(myNBEdge->getGeometry().back());
926 }
927 case GNE_ATTR_BIDIR:
928 return toString(myNBEdge->getBidiEdge() != nullptr);
934 } else {
935 return "";
936 }
940 return myNBEdge->getParametersStr();
941 default:
942 throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
943 }
944}
945
946
947std::string
949 std::string result = getAttribute(key);
950 if ((key == SUMO_ATTR_ALLOW || key == SUMO_ATTR_DISALLOW) && result.find("all") != std::string::npos) {
951 result += " " + getVehicleClassNames(SVCAll, true);
952 }
953 return result;
954}
955
956
957void
958GNEEdge::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {
959 // get template editor
961 // check if we have to update template
962 const bool updateTemplate = templateEditor->getEdgeTemplate() ? (templateEditor->getEdgeTemplate()->getID() == getID()) : false;
963 switch (key) {
964 case SUMO_ATTR_WIDTH:
966 case SUMO_ATTR_SPEED:
968 case SUMO_ATTR_ALLOW:
969 case SUMO_ATTR_DISALLOW: {
970 undoList->begin(GUIIcon::EDGE, "change " + getTagStr() + " attribute");
971 const std::string origValue = myLanes.at(0)->getAttribute(key); // will have intermediate value of "lane specific"
972 // lane specific attributes need to be changed via lanes to allow undo
973 for (auto it : myLanes) {
974 it->setAttribute(key, value, undoList);
975 }
976 // ensure that the edge value is also changed. Actually this sets the lane attributes again but it does not matter
977 undoList->changeAttribute(new GNEChange_Attribute(this, key, value, origValue));
978 undoList->end();
979 break;
980 }
981 case SUMO_ATTR_FROM: {
982 if (value != getAttribute(key)) {
983 undoList->begin(GUIIcon::EDGE, "change " + getTagStr() + " attribute");
984 // Remove edge from crossings of junction source
986 // continue changing from junction
987 GNEJunction* originalFirstParentJunction = getFromJunction();
988 getFromJunction()->setLogicValid(false, undoList);
989 undoList->changeAttribute(new GNEChange_Attribute(this, key, value));
990 getFromJunction()->setLogicValid(false, undoList);
991 myNet->getAttributeCarriers()->retrieveJunction(value)->setLogicValid(false, undoList);
992 setAttribute(GNE_ATTR_SHAPE_START, toString(getFromJunction()->getNBNode()->getPosition()), undoList);
994 undoList->end();
995 // update geometries of all implicated junctions
996 originalFirstParentJunction->updateGeometry();
999 }
1000 break;
1001 }
1002 case SUMO_ATTR_TO: {
1003 if (value != getAttribute(key)) {
1004 undoList->begin(GUIIcon::EDGE, "change " + getTagStr() + " attribute");
1005 // Remove edge from crossings of junction destiny
1007 // continue changing destiny junction
1008 GNEJunction* originalSecondParentJunction = getToJunction();
1009 getToJunction()->setLogicValid(false, undoList);
1010 undoList->changeAttribute(new GNEChange_Attribute(this, key, value));
1011 getToJunction()->setLogicValid(false, undoList);
1012 myNet->getAttributeCarriers()->retrieveJunction(value)->setLogicValid(false, undoList);
1013 setAttribute(GNE_ATTR_SHAPE_END, toString(getToJunction()->getNBNode()->getPosition()), undoList);
1015 undoList->end();
1016 // update geometries of all implicated junctions
1017 originalSecondParentJunction->updateGeometry();
1020 }
1021 break;
1022 }
1023 case SUMO_ATTR_ID:
1024 case SUMO_ATTR_PRIORITY:
1025 case SUMO_ATTR_LENGTH:
1026 case SUMO_ATTR_TYPE:
1028 case SUMO_ATTR_DISTANCE:
1030 case GNE_ATTR_SELECTED:
1034 undoList->changeAttribute(new GNEChange_Attribute(this, key, value));
1035 break;
1037 case GNE_ATTR_SHAPE_END: {
1038 auto change = new GNEChange_Attribute(this, key, value);
1039 // due to ENDPOINT_TOLERANCE, change might be ignored unless forced
1040 change->forceChange();
1041 undoList->changeAttribute(change);
1042 break;
1043 }
1044 case SUMO_ATTR_NAME:
1045 // user cares about street names. Make sure they appear in the output
1047 OptionsCont::getOptions().set("output.street-names", "true");
1048 undoList->changeAttribute(new GNEChange_Attribute(this, key, value));
1049 break;
1050 case SUMO_ATTR_NUMLANES:
1051 if (value != getAttribute(key)) {
1052 // set num lanes
1053 setNumLanes(parse<int>(value), undoList);
1054 }
1055 break;
1056 case GNE_ATTR_BIDIR:
1057 undoList->begin(GUIIcon::EDGE, "change " + getTagStr() + " attribute");
1058 undoList->changeAttribute(new GNEChange_Attribute(this, key, value));
1059 if (myNBEdge->getTurnDestination(true) != nullptr) {
1061 undoList->changeAttribute(new GNEChange_Attribute(bidi, key, value));
1062 if (myNBEdge->getGeometry() != bidi->getNBEdge()->getGeometry().reverse()
1063 && myNBEdge->getGeometry().size() == 2
1064 && bidi->getNBEdge()->getGeometry().size() == 2
1065 && myNBEdge->getBidiEdge() == nullptr) {
1066 // NBEdge::avoidOverlap was already active so we need to reset the
1067 // geometry to it's default
1068 resetBothEndpoint(undoList);
1069 bidi->resetBothEndpoint(undoList);
1070 }
1071 }
1072 undoList->end();
1073 break;
1074 case SUMO_ATTR_SHAPE:
1075 // @note: assumes value of inner geometry!
1076 // actually the geometry is already updated (incrementally
1077 // during mouse movement). We set the restore point to the end
1078 // of the last change-set
1079 undoList->changeAttribute(new GNEChange_Attribute(this, key, value));
1080 break;
1081 default:
1082 throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
1083 }
1084 // update template
1085 if (updateTemplate) {
1086 templateEditor->setEdgeTemplate(this);
1087 }
1088}
1089
1090
1091bool
1092GNEEdge::isValid(SumoXMLAttr key, const std::string& value) {
1093 switch (key) {
1094 case SUMO_ATTR_ID:
1095 return SUMOXMLDefinitions::isValidNetID(value) && (myNet->getAttributeCarriers()->retrieveEdge(value, false) == nullptr);
1096 case SUMO_ATTR_FROM: {
1097 // check that is a valid ID and is different of ID of junction destiny
1098 if (value == getFromJunction()->getID()) {
1099 return true;
1100 } else if (SUMOXMLDefinitions::isValidNetID(value) && (value != getToJunction()->getID())) {
1101 return (myNet->getAttributeCarriers()->retrieveJunction(value, false) != nullptr);
1102 } else {
1103 return false;
1104 }
1105 }
1106 case SUMO_ATTR_TO: {
1107 // check that is a valid ID and is different of ID of junction Source
1108 if (value == getToJunction()->getID()) {
1109 return true;
1110 } else if (SUMOXMLDefinitions::isValidNetID(value) && (value != getFromJunction()->getID())) {
1111 return (myNet->getAttributeCarriers()->retrieveJunction(value, false) != nullptr);
1112 } else {
1113 return false;
1114 }
1115 }
1116 case SUMO_ATTR_SPEED:
1117 return canParse<double>(value) && (parse<double>(value) > 0);
1118 case SUMO_ATTR_FRICTION:
1119 return canParse<double>(value) && (parse<double>(value) > 0);
1120 case SUMO_ATTR_NUMLANES:
1121 return canParse<int>(value) && (parse<double>(value) > 0);
1122 case SUMO_ATTR_PRIORITY:
1123 return canParse<int>(value);
1124 case SUMO_ATTR_LENGTH:
1125 if (value.empty()) {
1126 return true;
1127 } else {
1128 return canParse<double>(value) && ((parse<double>(value) > 0) || (parse<double>(value) == NBEdge::UNSPECIFIED_LOADED_LENGTH));
1129 }
1130 case SUMO_ATTR_ALLOW:
1131 case SUMO_ATTR_DISALLOW:
1132 return canParseVehicleClasses(value);
1133 case SUMO_ATTR_TYPE:
1134 return true;
1135 case SUMO_ATTR_SHAPE:
1136 // empty shapes are allowed
1137 return canParse<PositionVector>(value);
1140 case SUMO_ATTR_NAME:
1141 return true;
1142 case SUMO_ATTR_WIDTH:
1143 if (value.empty() || (value == "default")) {
1144 return true;
1145 } else {
1146 return canParse<double>(value) && ((parse<double>(value) >= -1) || (parse<double>(value) == NBEdge::UNSPECIFIED_WIDTH));
1147 }
1149 return canParse<double>(value) && parse<double>(value) >= 0 && parse<double>(value) < myNBEdge->getLoadedLength();
1150 case SUMO_ATTR_DISTANCE:
1151 if (value.empty()) {
1152 return true;
1153 } else {
1154 return canParse<double>(value);
1155 }
1156 case GNE_ATTR_SHAPE_START: {
1157 if (value.empty()) {
1158 return true;
1159 } else if (canParse<Position>(value)) {
1160 Position shapeStart = parse<Position>(value);
1161 return (shapeStart != myNBEdge->getGeometry()[-1]);
1162 } else {
1163 return false;
1164 }
1165 }
1166 case GNE_ATTR_SHAPE_END: {
1167 if (value.empty()) {
1168 return true;
1169 } else if (canParse<Position>(value)) {
1170 Position shapeEnd = parse<Position>(value);
1171 return (shapeEnd != myNBEdge->getGeometry()[0]);
1172 } else {
1173 return false;
1174 }
1175 }
1176 case GNE_ATTR_BIDIR:
1177 return canParse<bool>(value) && (!parse<bool>(value) || myNBEdge->isBidiEdge(true));
1179 return canParse<int>(value) && (parse<double>(value) >= 0);
1181 return canParseVehicleClasses(value);
1182 case GNE_ATTR_SELECTED:
1183 return canParse<bool>(value);
1186 default:
1187 throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
1188 }
1189}
1190
1191
1192bool
1194 switch (key) {
1195 case GNE_ATTR_BIDIR:
1196 return myNBEdge->isBidiEdge(true);
1197 default:
1198 return true;
1199 }
1200}
1201
1202
1203bool
1205 switch (key) {
1206 case SUMO_ATTR_LENGTH:
1207 return (myNBEdge->hasLoadedLength() == false);
1208 case SUMO_ATTR_WIDTH:
1210 return false;
1211 } else {
1213 }
1214 default:
1215 return false;
1216 }
1217}
1218
1219
1220const Parameterised::Map&
1224
1225
1226void
1228 myAmResponsible = newVal;
1229}
1230
1231
1232GNELane*
1234 // iterate over all NBEdge lanes
1235 for (int i = 0; i < (int)myNBEdge->getLanes().size(); i++) {
1236 // if given VClass is in permissions, return lane
1237 if (myNBEdge->getLanes().at(i).permissions & vClass) {
1238 // return GNELane
1239 return myLanes.at(i);
1240 }
1241 }
1242 // return first lane
1243 return myLanes.front();
1244}
1245
1246
1247GNELane*
1249 // iterate over all NBEdge lanes
1250 for (int i = 0; i < (int)myNBEdge->getLanes().size(); i++) {
1251 // if given VClass isn't in permissions, return lane
1252 if (~(myNBEdge->getLanes().at(i).permissions) & vClass) {
1253 // return GNELane
1254 return myLanes.at(i);
1255 }
1256 }
1257 // return first lane
1258 return myLanes.front();
1259}
1260
1261
1262void
1264 // get lane vehicles map
1265 const std::map<const GNELane*, std::vector<GNEDemandElement*> > laneVehiclesMap = getVehiclesOverEdgeMap();
1266 // iterate over every lane
1267 for (const auto& laneVehicle : laneVehiclesMap) {
1268 // obtain total length
1269 double totalLength = 0;
1270 for (const auto& vehicle : laneVehicle.second) {
1271 totalLength += vehicle->getAttributeDouble(SUMO_ATTR_LENGTH) + VEHICLE_GAP;
1272 }
1273 // calculate multiplier for vehicle positions
1274 double multiplier = 1;
1275 const double laneShapeLength = laneVehicle.first->getLaneShape().length();
1276 if (laneShapeLength == 0) {
1277 multiplier = 0;
1278 } else if (totalLength > laneShapeLength) {
1279 multiplier = (laneShapeLength / totalLength);
1280 }
1281 // declare current length
1282 double length = 0;
1283 // iterate over vehicles to calculate position and rotations
1284 for (const auto& vehicle : laneVehicle.second) {
1285 vehicle->updateDemandElementSpreadGeometry(laneVehicle.first, length * multiplier);
1286 // update length
1287 length += vehicle->getAttributeDouble(SUMO_ATTR_LENGTH) + VEHICLE_GAP;
1288 }
1289 }
1290}
1291
1292
1293void
1295 // get lane vehicles map
1296 const std::map<const GNELane*, std::vector<GNEDemandElement*> > laneVehiclesMap = getVehiclesOverEdgeMap();
1297 // iterate over laneVehiclesMap and obtain a vector with
1298 for (const auto& vehicleMap : laneVehiclesMap) {
1299 // declare map for sort vehicles using their departpos+length position (StackPosition)
1300 std::vector<std::pair<GNEEdge::StackPosition, GNEDemandElement*> > departPosVehicles;
1301 // declare vector of stack demand elements
1302 std::vector<GNEEdge::StackDemandElements> stackedVehicles;
1303 // iterate over vehicles
1304 for (const auto& vehicle : vehicleMap.second) {
1305 // get vehicle's depart pos and length
1306 const double departPos = vehicle->getAttributeDouble(SUMO_ATTR_DEPARTPOS);
1307 const double length = vehicle->getAttributeDouble(SUMO_ATTR_LENGTH);
1308 double posOverLane = vehicle->getAttributeDouble(SUMO_ATTR_DEPARTPOS);
1309 // check if we have to adapt posOverLane
1310 if (posOverLane < 0) {
1311 posOverLane += vehicleMap.first->getLaneShape().length();
1312 }
1313 // make a stack position using departPos and length
1314 departPosVehicles.push_back(std::make_pair(StackPosition(departPos, length), vehicle));
1315 // update depart element geometry
1316 vehicle->updateDemandElementGeometry(vehicleMap.first, posOverLane);
1317 // reset vehicle stack label
1318 vehicle->updateDemandElementStackLabel(0);
1319 }
1320
1321 // sort departPosVehicles
1322 std::sort(departPosVehicles.begin(), departPosVehicles.end());
1323 // iterate over departPosVehicles
1324 for (const auto& departPosVehicle : departPosVehicles) {
1325 // obtain stack position and vehicle
1326 const GNEEdge::StackPosition& vehicleStackPosition = departPosVehicle.first;
1327 GNEDemandElement* vehicle = departPosVehicle.second;
1328 // if stackedVehicles is empty, add a new StackDemandElements
1329 if (stackedVehicles.empty()) {
1330 stackedVehicles.push_back(GNEEdge::StackDemandElements(vehicleStackPosition, vehicle));
1331 } else if (areStackPositionOverlapped(vehicleStackPosition, stackedVehicles.back().getStackPosition())) {
1332 // add new vehicle to last inserted stackDemandElements
1333 stackedVehicles[stackedVehicles.size() - 1].addDemandElements(vehicle);
1334 } else {
1335 // No overlapping, then add a new StackDemandElements
1336 stackedVehicles.push_back(GNEEdge::StackDemandElements(vehicleStackPosition, vehicle));
1337 }
1338 }
1339 // iterate over stackedVehicles
1340 for (const auto& vehicle : stackedVehicles) {
1341 // only update vehicles with one or more stack
1342 if (vehicle.getDemandElements().size() > 1) {
1343 // set stack labels
1344 vehicle.getDemandElements().front()->updateDemandElementStackLabel((int)vehicle.getDemandElements().size());
1345 }
1346 }
1347 }
1348}
1349
1350
1351void
1353 // get lane persons map
1354 const std::map<const GNELane*, std::vector<GNEDemandElement*> > lanePersonsMap = getPersonsOverEdgeMap();
1355 // iterate over lanePersonsMap and obtain a vector with
1356 for (const auto& personMap : lanePersonsMap) {
1357 // declare map for sort persons using their departpos+length position (StackPosition)
1358 std::vector<std::pair<GNEEdge::StackPosition, GNEDemandElement*> > departPosPersons;
1359 // declare vector of stack demand elements
1360 std::vector<GNEEdge::StackDemandElements> stackedPersons;
1361 // iterate over persons
1362 for (const auto& person : personMap.second) {
1363 // get person's depart pos and length
1364 const double departPos = person->getAttributeDouble(SUMO_ATTR_DEPARTPOS);
1365 // make a stack position using departPos and length
1366 departPosPersons.push_back(std::make_pair(StackPosition(departPos, 1.8), person));
1367 // update depart element geometry
1368 person->updateDemandElementGeometry(personMap.first, departPos);
1369 // reset person stack label
1370 person->updateDemandElementStackLabel(0);
1371 }
1372
1373 // sort departPosPersons
1374 std::sort(departPosPersons.begin(), departPosPersons.end());
1375 // iterate over departPosPersons
1376 for (const auto& departPosPerson : departPosPersons) {
1377 // obtain stack position and person
1378 const GNEEdge::StackPosition& personStackPosition = departPosPerson.first;
1379 GNEDemandElement* person = departPosPerson.second;
1380 // if stackedPersons is empty, add a new StackDemandElements
1381 if (stackedPersons.empty()) {
1382 stackedPersons.push_back(GNEEdge::StackDemandElements(personStackPosition, person));
1383 } else if (areStackPositionOverlapped(personStackPosition, stackedPersons.back().getStackPosition())) {
1384 // add new person to last inserted stackDemandElements
1385 stackedPersons[stackedPersons.size() - 1].addDemandElements(person);
1386 } else {
1387 // No overlapping, then add a new StackDemandElements
1388 stackedPersons.push_back(GNEEdge::StackDemandElements(personStackPosition, person));
1389 }
1390 }
1391 // iterate over stackedPersons
1392 for (const auto& person : stackedPersons) {
1393 // only update persons with one or more stack
1394 if (person.getDemandElements().size() > 1) {
1395 // set stack labels
1396 person.getDemandElements().front()->updateDemandElementStackLabel((int)person.getDemandElements().size());
1397 }
1398 }
1399 }
1400}
1401
1402
1403void
1405 // get lane containers map
1406 const std::map<const GNELane*, std::vector<GNEDemandElement*> > laneContainersMap = getContainersOverEdgeMap();
1407 // iterate over laneContainersMap and obtain a vector with
1408 for (const auto& containerMap : laneContainersMap) {
1409 // declare map for sort containers using their departpos+length position (StackPosition)
1410 std::vector<std::pair<GNEEdge::StackPosition, GNEDemandElement*> > departPosContainers;
1411 // declare vector of stack demand elements
1412 std::vector<GNEEdge::StackDemandElements> stackedContainers;
1413 // iterate over containers
1414 for (const auto& container : containerMap.second) {
1415 // get container's depart pos and length
1416 const double departPos = container->getAttributeDouble(SUMO_ATTR_DEPARTPOS);
1417 // make a stack position using departPos and length
1418 departPosContainers.push_back(std::make_pair(StackPosition(departPos, 1.8), container));
1419 // update depart element geometry
1420 container->updateDemandElementGeometry(containerMap.first, departPos);
1421 // reset container stack label
1422 container->updateDemandElementStackLabel(0);
1423 }
1424
1425 // sort departPosContainers
1426 std::sort(departPosContainers.begin(), departPosContainers.end());
1427 // iterate over departPosContainers
1428 for (const auto& departPosContainer : departPosContainers) {
1429 // obtain stack position and container
1430 const GNEEdge::StackPosition& containerStackPosition = departPosContainer.first;
1431 GNEDemandElement* container = departPosContainer.second;
1432 // if stackedContainers is empty, add a new StackDemandElements
1433 if (stackedContainers.empty()) {
1434 stackedContainers.push_back(GNEEdge::StackDemandElements(containerStackPosition, container));
1435 } else if (areStackPositionOverlapped(containerStackPosition, stackedContainers.back().getStackPosition())) {
1436 // add new container to last inserted stackDemandElements
1437 stackedContainers[stackedContainers.size() - 1].addDemandElements(container);
1438 } else {
1439 // No overlapping, then add a new StackDemandElements
1440 stackedContainers.push_back(GNEEdge::StackDemandElements(containerStackPosition, container));
1441 }
1442 }
1443 // iterate over stackedContainers
1444 for (const auto& container : stackedContainers) {
1445 // only update containers with one or more stack
1446 if (container.getDemandElements().size() > 1) {
1447 // set stack labels
1448 container.getDemandElements().front()->updateDemandElementStackLabel((int)container.getDemandElements().size());
1449 }
1450 }
1451 }
1452}
1453
1454
1455void
1457 const bool drawFrontExtreme, const bool drawBackExtreme, const double exaggeration) {
1458 if (edge->getLanes().size() == 1) {
1459 GNELane::LaneDrawingConstants laneDrawingConstants(s, edge->getLanes().front());
1460 GUIDottedGeometry::drawDottedContourShape(s, type, edge->getLanes().front()->getLaneShape(),
1461 laneDrawingConstants.halfWidth * exaggeration, 1, drawFrontExtreme, drawBackExtreme);
1462 } else {
1463 // set left hand flag
1464 const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
1465 // obtain lanes
1466 const GNELane* topLane = lefthand ? edge->getLanes().back() : edge->getLanes().front();
1467 const GNELane* botLane = lefthand ? edge->getLanes().front() : edge->getLanes().back();
1468 // obtain a copy of both geometries
1469 GUIDottedGeometry dottedGeometryTop(s, topLane->getLaneGeometry().getShape(), false);
1470 GUIDottedGeometry dottedGeometryBot(s, botLane->getLaneGeometry().getShape(), false);
1471 // obtain both LaneDrawingConstants
1472 GNELane::LaneDrawingConstants laneDrawingConstantsFront(s, topLane);
1473 GNELane::LaneDrawingConstants laneDrawingConstantsBack(s, botLane);
1474 // move shapes to side
1475 dottedGeometryTop.moveShapeToSide(laneDrawingConstantsFront.halfWidth * exaggeration);
1476 dottedGeometryBot.moveShapeToSide(laneDrawingConstantsBack.halfWidth * -1 * exaggeration);
1477 // invert offset of top dotted geometry
1478 dottedGeometryTop.invertOffset();
1479 // declare DottedGeometryColor
1480 GUIDottedGeometry::DottedGeometryColor dottedGeometryColor(s);
1481 // calculate extremes
1482 GUIDottedGeometry extremes(s, dottedGeometryTop, drawFrontExtreme, dottedGeometryBot, drawBackExtreme);
1483 // Push draw matrix
1485 // translate to front
1486 glTranslated(0, 0, GLO_DOTTEDCONTOUR_INSPECTED);
1487 // draw top dotted geometry
1488 dottedGeometryTop.drawDottedGeometry(s, type, dottedGeometryColor);
1489 // reset color
1490 dottedGeometryColor.reset();
1491 // draw top dotted geometry
1492 dottedGeometryBot.drawDottedGeometry(s, type, dottedGeometryColor);
1493 // change color
1494 dottedGeometryColor.changeColor();
1495 // draw extrem dotted geometry
1496 extremes.drawDottedGeometry(s, type, dottedGeometryColor);
1497 // pop matrix
1499 }
1500}
1501
1502
1503bool
1505 // calculate angle between both junction positions
1506 double edgeAngle = RAD2DEG(getFromJunction()->getPositionInView().angleTo2D(getToJunction()->getPositionInView()));
1507 // adjust to 360 degrees
1508 while (edgeAngle < 0) {
1509 edgeAngle += 360;
1510 }
1511 // fmod round towards zero which is not want we want for negative numbers
1512 edgeAngle = fmod(edgeAngle, 360);
1513 // check angle
1514 return edgeAngle >= 0 && edgeAngle < 180;
1515}
1516
1517
1518bool
1520 // get incoming edges
1521 const auto incomingEdges = getFromJunction()->getGNEIncomingEdges();
1522 // iterate over connections
1523 for (const auto& incomingEdge : incomingEdges) {
1524 for (const auto& connection : incomingEdge->getGNEConnections()) {
1525 if (connection->getEdgeTo() == this) {
1526 return true;
1527 }
1528 }
1529 }
1530 return false;
1531}
1532
1533
1534bool
1536 return (myGNEConnections.size() > 0);
1537}
1538
1539
1540GNEEdge*
1542 for (const auto& outgoingEdge : getParentJunctions().back()->getGNEOutgoingEdges()) {
1543 if (outgoingEdge->getToJunction() == getFromJunction()) {
1544 return outgoingEdge;
1545 }
1546 }
1547 return nullptr;
1548}
1549
1550// ===========================================================================
1551// private
1552// ===========================================================================
1553
1554GNEEdge::StackPosition::StackPosition(const double departPos, const double length) :
1555 pair(departPos, departPos + length) {
1556}
1557
1558
1559double
1561 return first;
1562}
1563
1564
1565double
1567 return second;
1568}
1569
1570
1572 pair(stackedPosition, {
1573 demandElement
1574}) {
1575}
1576
1577
1578void
1580 second.push_back(demandElement);
1581}
1582
1583
1586 return first;
1587}
1588
1589
1590const std::vector<GNEDemandElement*>&
1592 return second;
1593}
1594
1595
1596void
1597GNEEdge::setAttribute(SumoXMLAttr key, const std::string& value) {
1598 switch (key) {
1599 case SUMO_ATTR_ID:
1600 myNet->getAttributeCarriers()->updateEdgeID(this, value);
1601 // enable save demand elements if there are stops
1602 for (const auto& stop : getChildDemandElements()) {
1603 if (stop->getTagProperty().isStop() || stop->getTagProperty().isStopPerson()) {
1605 }
1606 }
1607 // also for lanes
1608 for (const auto& lane : myLanes) {
1609 for (const auto& stop : lane->getChildDemandElements()) {
1610 if (stop->getTagProperty().isStop() || stop->getTagProperty().isStopPerson()) {
1612 }
1613 }
1614 }
1615 break;
1616 case SUMO_ATTR_FROM:
1617 myNet->changeEdgeEndpoints(this, value, getToJunction()->getID());
1618 // update this edge of list of outgoings edges of the old first parent junction
1620 // update first parent junction
1622 // update this edge of list of outgoings edges of the new first parent junction
1624 // update centering boundary and grid
1626 break;
1627 case SUMO_ATTR_TO:
1628 myNet->changeEdgeEndpoints(this, getFromJunction()->getID(), value);
1629 // update this edge of list of incomings edges of the old second parent junction
1631 // update second parent junction
1633 // update this edge of list of incomings edges of the new second parent junction
1635 // update centering boundary and grid
1637 break;
1638 case SUMO_ATTR_NUMLANES:
1639 throw InvalidArgument("GNEEdge::setAttribute (private) called for attr SUMO_ATTR_NUMLANES. This should never happen");
1640 case SUMO_ATTR_PRIORITY:
1641 myNBEdge->myPriority = parse<int>(value);
1642 break;
1643 case SUMO_ATTR_LENGTH:
1644 if (value.empty()) {
1646 } else {
1647 myNBEdge->setLoadedLength(parse<double>(value));
1648 }
1649 break;
1650 case SUMO_ATTR_TYPE:
1651 myNBEdge->myType = value;
1652 break;
1653 case SUMO_ATTR_SHAPE:
1654 // set new geometry
1655 setGeometry(parse<PositionVector>(value), true);
1656 // update centering boundary and grid
1658 break;
1661 break;
1662 case SUMO_ATTR_NAME:
1663 myNBEdge->setStreetName(value);
1664 break;
1665 case SUMO_ATTR_SPEED:
1666 myNBEdge->setSpeed(-1, parse<double>(value));
1667 break;
1668 case SUMO_ATTR_FRICTION:
1669 myNBEdge->setFriction(-1, parse<double>(value));
1670 break;
1671 case SUMO_ATTR_WIDTH:
1672 if (value.empty() || (value == "default")) {
1674 } else {
1675 myNBEdge->setLaneWidth(-1, parse<double>(value));
1676 }
1677 break;
1679 myNBEdge->setEndOffset(-1, parse<double>(value));
1680 break;
1681 case SUMO_ATTR_ALLOW:
1682 break; // no edge value
1683 case SUMO_ATTR_DISALLOW:
1684 break; // no edge value
1685 case SUMO_ATTR_DISTANCE:
1686 if (value.empty()) {
1687 myNBEdge->setDistance(0.0);
1688 } else {
1689 myNBEdge->setDistance(parse<double>(value));
1690 }
1691 break;
1693 myConnectionStatus = value;
1694 if (value == FEATURE_GUESSED) {
1695 WRITE_DEBUG("invalidating (removing) connections of edge '" + getID() + "' due it were guessed");
1698 } else if (value != FEATURE_GUESSED) {
1699 WRITE_DEBUG("declaring connections of edge '" + getID() + "' as loaded (It will not be removed)");
1701 }
1702 break;
1703 case GNE_ATTR_SHAPE_START: {
1704 // get geometry of NBEdge, remove FIRST element with the new value (or with the Junction Source position) and set it back to edge
1705 Position newShapeStart;
1706 if (value == "") {
1707 newShapeStart = getFromJunction()->getNBNode()->getPosition();
1708 } else {
1709 newShapeStart = parse<Position>(value);
1710 }
1711 // set shape start position
1712 setShapeStartPos(newShapeStart);
1713 // update centering boundary and grid
1715 break;
1716 }
1717 case GNE_ATTR_SHAPE_END: {
1718 // get geometry of NBEdge, remove LAST element with the new value (or with the Junction Destiny position) and set it back to edge
1719 Position newShapeEnd;
1720 if (value == "") {
1721 newShapeEnd = getToJunction()->getNBNode()->getPosition();
1722 } else {
1723 newShapeEnd = parse<Position>(value);
1724 }
1725 // set shape end position
1726 setShapeEndPos(newShapeEnd);
1727 // update centering boundary and grid
1729 break;
1730 }
1731 case GNE_ATTR_BIDIR:
1732 myNBEdge->setBidi(parse<bool>(value));
1733 break;
1734 case GNE_ATTR_SELECTED:
1735 if (parse<bool>(value)) {
1737 } else {
1739 }
1740 break;
1742 myNBEdge->myEdgeStopOffset.setOffset(parse<double>(value));
1743 break;
1745 if (value.empty()) {
1747 } else {
1749 }
1750 break;
1752 myNBEdge->setParametersStr(value);
1753 break;
1754 default:
1755 throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
1756 }
1757 // get template editor
1759 // check if update template (except for modification status)
1760 if (templateEditor->getEdgeTemplate() && (templateEditor->getEdgeTemplate()->getID() == getID()) &&
1763 }
1764 // invalidate path calculator
1766}
1767
1768
1769void
1771 // get start and end points
1772 const Position shapeStart = moveResult.shapeToUpdate.front();
1773 const Position shapeEnd = moveResult.shapeToUpdate.back();
1774 // get innen shape
1775 PositionVector innenShape = moveResult.shapeToUpdate;
1776 innenShape.pop_front();
1777 innenShape.pop_back();
1778 // set shape start
1779 if (std::find(moveResult.geometryPointsToMove.begin(), moveResult.geometryPointsToMove.end(), 0) != moveResult.geometryPointsToMove.end()) {
1780 setShapeStartPos(shapeStart);
1781 }
1782 // set innen geometry
1783 setGeometry(innenShape, true);
1784 // set shape end
1785 if (std::find(moveResult.geometryPointsToMove.begin(), moveResult.geometryPointsToMove.end(), ((int)moveResult.shapeToUpdate.size() - 1)) != moveResult.geometryPointsToMove.end()) {
1786 setShapeEndPos(shapeEnd);
1787 }
1788}
1789
1790
1791void
1793 // make sure that newShape isn't empty
1794 if (moveResult.shapeToUpdate.size() > 0) {
1795 // get innen shape
1796 PositionVector innenShapeToUpdate = moveResult.shapeToUpdate;
1797 innenShapeToUpdate.pop_front();
1798 innenShapeToUpdate.pop_back();
1799 // commit new shape
1800 undoList->begin(GUIIcon::EDGE, "moving " + toString(SUMO_ATTR_SHAPE) + " of " + getTagStr());
1801 // update start position
1802 if (std::find(moveResult.geometryPointsToMove.begin(), moveResult.geometryPointsToMove.end(), 0) != moveResult.geometryPointsToMove.end()) {
1803 undoList->changeAttribute(new GNEChange_Attribute(this, GNE_ATTR_SHAPE_START, toString(moveResult.shapeToUpdate.front())));
1804 }
1805 // check if update shape
1806 if (innenShapeToUpdate.size() > 0) {
1807 undoList->changeAttribute(new GNEChange_Attribute(this, SUMO_ATTR_SHAPE, toString(innenShapeToUpdate)));
1808 }
1809 // update end position
1810 if (std::find(moveResult.geometryPointsToMove.begin(), moveResult.geometryPointsToMove.end(), ((int)moveResult.shapeToUpdate.size() - 1)) != moveResult.geometryPointsToMove.end()) {
1811 undoList->changeAttribute(new GNEChange_Attribute(this, GNE_ATTR_SHAPE_END, toString(moveResult.shapeToUpdate.back())));
1812 }
1813 undoList->end();
1814 }
1815}
1816
1817
1818void
1819GNEEdge::setNumLanes(int numLanes, GNEUndoList* undoList) {
1820 // begin undo list
1821 undoList->begin(GUIIcon::EDGE, "change number of " + toString(SUMO_TAG_LANE) + "s");
1822 // invalidate logic of source/destiny edges
1823 getFromJunction()->setLogicValid(false, undoList);
1824 getToJunction()->setLogicValid(false, undoList);
1825 // disable update geometry (see #6336)
1826 myUpdateGeometry = false;
1827 // remove edge from grid
1829 // save old number of lanes
1830 const int oldNumLanes = (int)myLanes.size();
1831 // get opposite ID
1832 const auto oppositeID = myLanes.back()->getAttribute(GNE_ATTR_OPPOSITE);
1833 if (oppositeID != "") {
1834 // we'll have a different leftmost lane after adding/removing lanes
1836 }
1837 for (int i = oldNumLanes; i < numLanes; i++) {
1838 // since the GNELane does not exist yet, it cannot have yet been referenced so we only pass a zero-pointer
1839 undoList->add(new GNEChange_Lane(this, myNBEdge->getLaneStruct(oldNumLanes - 1)), true);
1840 }
1841 for (int i = (oldNumLanes - 1); i > (numLanes - 1); i--) {
1842 // delete leftmost lane
1843 undoList->add(new GNEChange_Lane(this, myLanes[i], myNBEdge->getLaneStruct(i), false), true);
1844 }
1845 if (oppositeID != "") {
1846 undoList->changeAttribute(new GNEChange_Attribute(myLanes.back(), GNE_ATTR_OPPOSITE, oppositeID));
1847 }
1848 // enable updateGeometry again
1849 myUpdateGeometry = true;
1850 // update geometry of entire edge
1852 // end undo list
1853 undoList->end();
1854 // update centering boundary (without updating RTREE)
1856 // insert edge in grid again
1858}
1859
1860
1861void
1862GNEEdge::updateFirstParentJunction(const std::string& value) {
1863 std::vector<GNEJunction*> parentJunctions = getParentJunctions();
1864 parentJunctions[0] = myNet->getAttributeCarriers()->retrieveJunction(value);
1865 // replace parent junctions
1866 replaceParentElements(this, parentJunctions);
1867}
1868
1869
1870void
1871GNEEdge::updateSecondParentJunction(const std::string& value) {
1872 std::vector<GNEJunction*> parentJunctions = getParentJunctions();
1873 parentJunctions[1] = myNet->getAttributeCarriers()->retrieveJunction(value);
1874 // replace parent junctions
1875 replaceParentElements(this, parentJunctions);
1876}
1877
1878
1879void
1880GNEEdge::addLane(GNELane* lane, const NBEdge::Lane& laneAttrs, bool recomputeConnections) {
1881 const int index = lane ? lane->getIndex() : myNBEdge->getNumLanes();
1882 // the laneStruct must be created first to ensure we have some geometry
1883 // unless the connections are fully recomputed, existing indices must be shifted
1884 myNBEdge->addLane(index, true, recomputeConnections, !recomputeConnections);
1885 if (lane) {
1886 // restore a previously deleted lane
1887 myLanes.insert(myLanes.begin() + index, lane);
1888 } else {
1889 // create a new lane by copying leftmost lane
1890 lane = new GNELane(this, index);
1891 myLanes.push_back(lane);
1892 }
1893 lane->incRef("GNEEdge::addLane");
1894 // add in attributeCarriers
1896 // check if lane is selected
1897 if (lane->isAttributeCarrierSelected()) {
1898 lane->selectAttributeCarrier();
1899 }
1900 // we copy all attributes except shape since this is recomputed from edge shape
1901 myNBEdge->setSpeed(lane->getIndex(), laneAttrs.speed);
1902 myNBEdge->setFriction(lane->getIndex(), laneAttrs.friction);
1903 myNBEdge->setPermissions(laneAttrs.permissions, lane->getIndex());
1905 myNBEdge->setEndOffset(lane->getIndex(), laneAttrs.endOffset);
1906 myNBEdge->setLaneWidth(lane->getIndex(), laneAttrs.width);
1907 // update indices
1908 for (int i = 0; i < (int)myLanes.size(); ++i) {
1909 myLanes[i]->setIndex(i);
1910 }
1911 /* while technically correct, this looks ugly
1912 getFromJunction()->invalidateShape();
1913 getToJunction()->invalidateShape();
1914 */
1915 // Remake connections for this edge and all edges that target this lane
1917 // remake connections of all edges of junction source and destiny
1918 for (const auto& fromEdge : getFromJunction()->getChildEdges()) {
1919 fromEdge->remakeGNEConnections();
1920 }
1921 // remake connections of all edges of junction source and destiny
1922 for (const auto& toEdge : getToJunction()->getChildEdges()) {
1923 toEdge->remakeGNEConnections();
1924 }
1925 // Update geometry with the new lane
1927 // update boundary and grid
1929}
1930
1931
1932void
1933GNEEdge::removeLane(GNELane* lane, bool recomputeConnections) {
1934 if (myLanes.size() == 0) {
1935 throw ProcessError("Should not remove the last " + toString(SUMO_TAG_LANE) + " from an " + getTagStr());
1936 }
1937 if (lane == nullptr) {
1938 lane = myLanes.back();
1939 }
1940 // check if lane is selected
1941 if (lane->isAttributeCarrierSelected()) {
1943 }
1944 // before removing, check that lane isn't being inspected
1947 // Delete lane of edge's container
1948 // unless the connections are fully recomputed, existing indices must be shifted
1949 myNBEdge->deleteLane(lane->getIndex(), recomputeConnections, !recomputeConnections);
1950 lane->decRef("GNEEdge::removeLane");
1951 myLanes.erase(myLanes.begin() + lane->getIndex());
1952 // remove from attributeCarriers
1954 // Delete lane if is unreferenced
1955 if (lane->unreferenced()) {
1956 // show extra information for tests
1957 WRITE_DEBUG("Deleting unreferenced " + lane->getTagStr() + " '" + lane->getID() + "' in removeLane()");
1958 delete lane;
1959 }
1960 // update indices
1961 for (int i = 0; i < (int)myLanes.size(); ++i) {
1962 myLanes[i]->setIndex(i);
1963 }
1964 /* while technically correct, this looks ugly
1965 getFromJunction()->invalidateShape();
1966 getToJunction()->invalidateShape();
1967 */
1968 // Remake connections of this edge
1970 // remake connections of all edges of junction source and destiny
1971 for (const auto& fromEdge : getFromJunction()->getChildEdges()) {
1972 fromEdge->remakeGNEConnections();
1973 }
1974 // remake connections of all edges of junction source and destiny
1975 for (const auto& toEdge : getToJunction()->getChildEdges()) {
1976 toEdge->remakeGNEConnections();
1977 }
1978 // Update element
1980 // update boundary and grid
1982}
1983
1984
1985void
1986GNEEdge::addConnection(NBEdge::Connection nbCon, bool selectAfterCreation) {
1987 // If a new connection was successfully created
1989 nbCon.keepClear, nbCon.contPos, nbCon.visibility,
1990 nbCon.speed, nbCon.friction, nbCon.customLength, nbCon.customShape, nbCon.uncontrolled)) {
1991 // Create or retrieve existent GNEConnection
1992 GNEConnection* con = retrieveGNEConnection(nbCon.fromLane, nbCon.toEdge, nbCon.toLane);
1993 // add it to GNEConnection container
1994 myGNEConnections.push_back(con);
1995 // Add reference
1996 myGNEConnections.back()->incRef("GNEEdge::addConnection");
1997 // select GNEConnection if needed
1998 if (selectAfterCreation) {
2000 }
2001 // update geometry
2002 con->updateGeometry();
2003 }
2004 // actually we only do this to force a redraw
2006}
2007
2008
2009void
2011 // check if is a explicit turnaround
2012 if (nbCon.toEdge == myNBEdge->getTurnDestination()) {
2014 }
2015 // remove NBEdge::connection from NBEdge
2017 // remove their associated GNEConnection
2018 GNEConnection* connection = retrieveGNEConnection(nbCon.fromLane, nbCon.toEdge, nbCon.toLane, false);
2019 if (connection != nullptr) {
2020 connection->decRef("GNEEdge::removeConnection");
2021 myGNEConnections.erase(std::find(myGNEConnections.begin(), myGNEConnections.end(), connection));
2022 // check if connection is selected
2023 if (connection->isAttributeCarrierSelected()) {
2024 connection->unselectAttributeCarrier();
2025 }
2026 // remove it from network
2027 myNet->removeGLObjectFromGrid(connection);
2028 // check if remove it from Attribute Carriers
2029 if (myNet->getAttributeCarriers()->getConnections().count(connection) > 0) {
2031 }
2032 if (connection->unreferenced()) {
2033 // show extra information for tests
2034 WRITE_DEBUG("Deleting unreferenced " + connection->getTagStr() + " '" + connection->getID() + "' in removeConnection()");
2035 // actually we only do this to force a redraw
2037 }
2038 }
2039}
2040
2041
2043GNEEdge::retrieveGNEConnection(int fromLane, NBEdge* to, int toLane, bool createIfNoExist) {
2044 for (const auto& connection : myGNEConnections) {
2045 if ((connection->getFromLaneIndex() == fromLane) && (connection->getEdgeTo()->getNBEdge() == to) && (connection->getToLaneIndex() == toLane)) {
2046 return connection;
2047 }
2048 }
2049 if (createIfNoExist) {
2050 // create new connection. Will be added to the rTree on first geometry computation
2051 GNEConnection* connection = new GNEConnection(myLanes[fromLane], myNet->getAttributeCarriers()->retrieveEdge(to->getID())->getLanes()[toLane]);
2052 // show extra information for tests
2053 WRITE_DEBUG("Created " + connection->getTagStr() + " '" + connection->getID() + "' in retrieveGNEConnection()");
2054 // add it into network
2055 myNet->addGLObjectIntoGrid(connection);
2056 // add it in attributeCarriers
2058 return connection;
2059 } else {
2060 return nullptr;
2061 }
2062}
2063
2064
2065void
2066GNEEdge::setMicrosimID(const std::string& newID) {
2068 for (const auto& lane : myLanes) {
2069 lane->setMicrosimID(getNBEdge()->getLaneID(lane->getIndex()));
2070 }
2071}
2072
2073
2074bool
2076 for (const auto& lane : myLanes) {
2077 if (lane->isRestricted(vclass)) {
2078 return true;
2079 }
2080 }
2081 return false;
2082}
2083
2084
2085void
2087 // Remove all crossings that contain this edge in parameter "edges"
2088 for (const auto& crossing : junction->getGNECrossings()) {
2089 if (crossing->checkEdgeBelong(this)) {
2090 myNet->deleteCrossing(crossing, undoList);
2091 }
2092 }
2093}
2094
2095
2096void
2101 PositionVector innerShape(modifiedShape.begin() + 1, modifiedShape.end() - 1);
2102 setAttribute(SUMO_ATTR_SHAPE, toString(innerShape), undoList);
2103}
2104
2105
2107GNEEdge::smoothShape(const PositionVector& old, bool forElevation) {
2108 const auto& neteditOptions = OptionsCont::getOptions();
2109 // distinguish 3 cases:
2110 // a) if the edge has exactly 3 or 4 points, use these as control points
2111 // b) if the edge has more than 4 points, use the first 2 and the last 2 as control points
2112 // c) if the edge is straight and both nodes are geometry-like nodes, use geometry of the continuation edges as control points
2113 PositionVector init;
2114#ifdef DEBUG_SMOOTH_GEOM
2115 if (DEBUGCOND(this)) std::cout << getID()
2116 << " forElevation=" << forElevation
2117 << " fromGeometryLike=" << myNBEdge->getFromNode()->geometryLike()
2118 << " toGeometryLike=" << myNBEdge->getToNode()->geometryLike()
2119 << " smoothShape old=" << old << "\n";
2120#endif
2121 if (old.size() == 3 || old.size() == 4) {
2122 init = old;
2123 } else if (old.size() > 4 && !forElevation) {
2124 // for elevation, the initial segments are not useful
2125 init.push_back(old[0]);
2126 init.push_back(old[1]);
2127 init.push_back(old[-2]);
2128 init.push_back(old[-1]);
2129 } else if (myNBEdge->getFromNode()->geometryLike() && myNBEdge->getToNode()->geometryLike()) {
2130 PositionVector begShape;
2131 PositionVector endShape;
2132 const EdgeVector& incoming = myNBEdge->getFromNode()->getIncomingEdges();
2133 const EdgeVector& outgoing = myNBEdge->getToNode()->getOutgoingEdges();
2134 if (incoming.size() == 1) {
2135 begShape = incoming[0]->getGeometry();
2136 } else {
2137 assert(incoming.size() == 2);
2138 begShape = myNBEdge->isTurningDirectionAt(incoming[0]) ? incoming[1]->getGeometry() : incoming[0]->getGeometry();
2139 }
2140 if (outgoing.size() == 1) {
2141 endShape = outgoing[0]->getGeometry();
2142 } else {
2143 assert(outgoing.size() == 2);
2144 endShape = myNBEdge->isTurningDirectionAt(outgoing[0]) ? outgoing[1]->getGeometry() : outgoing[0]->getGeometry();
2145 }
2146 const double dist = MIN2(old.length2D(), MAX2(old.length2D() / 8, fabs(old[0].z() - old[-1].z()) * OptionsCont::getOptions().getFloat("geometry.max-grade") / 3));
2147 if (forElevation) {
2148 // initialize control point elevation for smooth continuation
2149 init.push_back(old[0]);
2150 init.push_back(old.positionAtOffset2D(dist));
2151 init.push_back(old.positionAtOffset2D(old.length2D() - dist));
2152 init.push_back(old[-1]);
2153 double begZ = begShape.positionAtOffset2D(MAX2(0.0, begShape.length2D() - dist)).z();
2154 double endZ = endShape.positionAtOffset2D(MIN2(begShape.length2D(), dist)).z();
2155 // continue incline
2156 init[1].setz(2 * init[0].z() - begZ);
2157 init[2].setz(2 * init[-1].z() - endZ);
2158 } else {
2159 bool ok = true;
2160 const double straightThresh = DEG2RAD(neteditOptions.getFloat("opendrive-output.straight-threshold"));
2161 init = NBNode::bezierControlPoints(begShape, endShape, false, dist, dist, ok, nullptr, straightThresh);
2162 }
2163#ifdef DEBUG_SMOOTH_GEOM
2164 if (DEBUGCOND(this)) {
2165 std::cout << " begShape=" << begShape << " endShape=" << endShape << " forElevation=" << forElevation << " dist=" << dist << " ok=" << ok << " init=" << init << "\n";
2166 }
2167#endif
2168 }
2169 if (init.size() == 0) {
2170 return PositionVector::EMPTY;
2171 } else {
2172 const int numPoints = MAX2(neteditOptions.getInt("junctions.internal-link-detail"),
2173 int(old.length2D() / neteditOptions.getFloat("opendrive.curve-resolution")));
2174 return init.bezier(numPoints);
2175 }
2176}
2177
2178
2179void
2181 PositionVector modifiedShape = smoothShape(myNBEdge->getGeometry(), false);
2182 if (modifiedShape.size() < 2) {
2183 WRITE_WARNINGF(TL("Could not compute smooth shape for edge '%'"), getID());
2184 } else {
2185 PositionVector innerShape(modifiedShape.begin() + 1, modifiedShape.end() - 1);
2186 setAttribute(SUMO_ATTR_SHAPE, toString(innerShape), undoList);
2187 }
2188}
2189
2190
2191void
2193 PositionVector elevationBase;
2194 for (const Position& pos : myNBEdge->getGeometry()) {
2195 if (elevationBase.size() == 0 || elevationBase[-1].z() != pos.z()) {
2196 elevationBase.push_back(pos);
2197 }
2198 }
2199 PositionVector elevation = smoothShape(elevationBase, true);
2200 if (elevation.size() <= 2) {
2201 WRITE_WARNINGF(TL("Could not compute smooth elevation for edge '%'"), getID());
2202 } else {
2203 PositionVector modifiedShape = myNBEdge->getGeometry();
2204 if (modifiedShape.size() < 5) {
2205 modifiedShape = modifiedShape.resample(OptionsCont::getOptions().getFloat("opendrive.curve-resolution"), false);
2206 }
2207 const double scale = elevation.length2D() / modifiedShape.length2D();
2208 //std::cout << " elevation=" << elevation << "\n mod1=" << modifiedShape << " scale=" << scale << "\n";
2209 double seen = 0;
2210 for (int i = 1; i < (int)modifiedShape.size(); ++i) {
2211 seen += modifiedShape[i - 1].distanceTo2D(modifiedShape[i]);
2212 modifiedShape[i].setz(elevation.positionAtOffset2D(seen * scale).z());
2213 }
2214 //std::cout << " mod2=" << modifiedShape << "\n";
2215 PositionVector innerShape(modifiedShape.begin() + 1, modifiedShape.end() - 1);
2216 setAttribute(SUMO_ATTR_SHAPE, toString(innerShape), undoList);
2217 }
2218}
2219
2220
2221void
2223 // remove start position and add it the new position
2225 geom.pop_front();
2226 geom.push_front(pos);
2227 // restore modified shape
2228 setGeometry(geom, false);
2229}
2230
2231
2232void
2234 // remove end position and add it the new position
2236 geom.pop_back();
2237 geom.push_back(pos);
2238 // restore modified shape
2239 setGeometry(geom, false);
2240}
2241
2242
2243const std::map<const GNELane*, std::vector<GNEDemandElement*> >
2245 // declare vehicles over edge vector
2246 std::vector<GNEDemandElement*> vehiclesOverEdge;
2247 // declare solution map
2248 std::map<const GNELane*, std::vector<GNEDemandElement*> > vehiclesOverEdgeMap;
2249 // declare a set of vehicles (to avoid duplicates)
2250 std::set<std::pair<double, GNEDemandElement*> > vehicles;
2251 // first obtain all vehicles of this edge
2252 for (const auto& edgeChild : getChildDemandElements()) {
2253 if (((edgeChild->getTagProperty().getTag() == SUMO_TAG_TRIP) || (edgeChild->getTagProperty().getTag() == SUMO_TAG_FLOW)) &&
2254 (edgeChild->getParentEdges().front() == this)) {
2255 vehicles.insert(std::make_pair(edgeChild->getAttributeDouble(SUMO_ATTR_DEPART), edgeChild));
2256 vehicles.insert(std::make_pair(edgeChild->getAttributeDouble(SUMO_ATTR_DEPART), edgeChild));
2257 } else if ((edgeChild->getTagProperty().getTag() == SUMO_TAG_ROUTE) && (edgeChild->getParentEdges().front() == this)) {
2258 for (const auto& routeChild : edgeChild->getChildDemandElements()) {
2259 if ((routeChild->getTagProperty().getTag() == SUMO_TAG_VEHICLE) || (routeChild->getTagProperty().getTag() == GNE_TAG_FLOW_ROUTE)) {
2260 vehicles.insert(std::make_pair(routeChild->getAttributeDouble(SUMO_ATTR_DEPART), routeChild));
2261 }
2262 }
2263 } else if ((edgeChild->getTagProperty().getTag() == GNE_TAG_ROUTE_EMBEDDED) && (edgeChild->getParentEdges().front() == this)) {
2264 vehicles.insert(std::make_pair(edgeChild->getParentDemandElements().front()->getAttributeDouble(SUMO_ATTR_DEPART), edgeChild->getParentDemandElements().front()));
2265 }
2266 }
2267 // reserve
2268 vehiclesOverEdge.reserve(vehicles.size());
2269 // iterate over vehicles
2270 for (const auto& vehicle : vehicles) {
2271 // add it over vehiclesOverEdge;
2272 vehiclesOverEdge.push_back(vehicle.second);
2273 }
2274 // now split vehicles by lanes
2275 for (const auto& vehicle : vehiclesOverEdge) {
2276 const GNELane* vehicleLane = vehicle->getFirstPathLane();
2277 if (vehicleLane) {
2278 vehiclesOverEdgeMap[vehicleLane].push_back(vehicle);
2279 }
2280 }
2281 return vehiclesOverEdgeMap;
2282}
2283
2284
2285const std::map<const GNELane*, std::vector<GNEDemandElement*> >
2287 // declare persons over edge vector
2288 std::vector<GNEDemandElement*> personsOverEdge;
2289 // declare solution map
2290 std::map<const GNELane*, std::vector<GNEDemandElement*> > personsOverEdgeMap;
2291 // declare a set of persons (to avoid duplicates)
2292 std::set<std::pair<double, GNEDemandElement*> > persons;
2293 // first obtain all persons of this edge
2294 for (const auto& edgeChild : getChildDemandElements()) {
2295 if (edgeChild->getTagProperty().isPersonPlan()) {
2296 persons.insert(std::make_pair(edgeChild->getParentDemandElements().front()->getAttributeDouble(SUMO_ATTR_DEPARTPOS), edgeChild->getParentDemandElements().front()));
2297 }
2298 }
2299 // reserve
2300 personsOverEdge.reserve(persons.size());
2301 // iterate over persons
2302 for (const auto& person : persons) {
2303 // add it over personsOverEdge;
2304 personsOverEdge.push_back(person.second);
2305 }
2306 // now split persons by lanes
2307 for (const auto& person : personsOverEdge) {
2308 const GNELane* personLane = person->getFirstPathLane();
2309 if (personLane) {
2310 personsOverEdgeMap[personLane].push_back(person);
2311 }
2312 }
2313 return personsOverEdgeMap;
2314}
2315
2316
2317
2318const std::map<const GNELane*, std::vector<GNEDemandElement*> >
2320 // declare containers over edge vector
2321 std::vector<GNEDemandElement*> containersOverEdge;
2322 // declare solution map
2323 std::map<const GNELane*, std::vector<GNEDemandElement*> > containersOverEdgeMap;
2324 // declare a set of containers (to avoid duplicates)
2325 std::set<std::pair<double, GNEDemandElement*> > containers;
2326 // first obtain all containers of this edge
2327 for (const auto& edgeChild : getChildDemandElements()) {
2328 if (edgeChild->getTagProperty().isContainerPlan()) {
2329 containers.insert(std::make_pair(edgeChild->getParentDemandElements().front()->getAttributeDouble(SUMO_ATTR_DEPARTPOS), edgeChild->getParentDemandElements().front()));
2330 }
2331 }
2332 // reserve
2333 containersOverEdge.reserve(containers.size());
2334 // iterate over containers
2335 for (const auto& container : containers) {
2336 // add it over containersOverEdge;
2337 containersOverEdge.push_back(container.second);
2338 }
2339 // now split containers by lanes
2340 for (const auto& container : containersOverEdge) {
2341 const GNELane* containerLane = container->getFirstPathLane();
2342 if (containerLane) {
2343 containersOverEdgeMap[containerLane].push_back(container);
2344 }
2345 }
2346 return containersOverEdgeMap;
2347}
2348
2349
2350void
2352 // declare flags
2353 const bool validScale = (s.scale > 8.0);
2355 // first check conditions
2356 if (myLanes.size() > 0 && (validScale || elevationMode) && !myNet->getViewNet()->getEditModes().isCurrentSupermodeDemand()) {
2357 // check if draw geometry points
2358 const bool bigGeometryPoints = drawBigGeometryPoints();
2359 // Obtain exaggeration of the draw
2360 const double exaggeration = getExaggeration(s);
2361 // get circle width
2362 const double circleWidth = getSnapRadius(false);
2363 const double circleWidthSquared = circleWidth * circleWidth;
2364 // obtain color
2365 RGBColor geometryPointColor = s.junctionColorer.getSchemes()[0].getColor(2);
2366 if (drawUsingSelectColor() && s.laneColorer.getActive() != 1) {
2367 // override with special colors (unless the color scheme is based on selection)
2368 geometryPointColor = s.colorSettings.selectedEdgeColor.changedBrightness(-20);
2369 }
2370 // recognize full transparency and simply don't draw
2371 if (geometryPointColor.alpha() != 0) {
2372 // add edge layer matrix
2374 // Push lane name
2376 // translate to front depending of big points
2377 if (bigGeometryPoints) {
2378 glTranslated(0, 0, GLO_GEOMETRYPOINT);
2379 } else {
2380 glTranslated(0, 0, GLO_LANE + 1);
2381 }
2382 // draw geometry points expect initial and final
2383 for (int i = 1; i < (int)myNBEdge->getGeometry().size() - 1; i++) {
2384 const auto geometryPointPos = myNBEdge->getGeometry()[i];
2385 // check if mouse is near of geometry point in drawForRectangleSelection mode
2386 if (!s.drawForRectangleSelection || (myNet->getViewNet()->getPositionInformation().distanceSquaredTo2D(geometryPointPos) <= circleWidthSquared)) {
2387 // set geometry point color depending of bigGeometryPoints
2388 if (bigGeometryPoints) {
2389 setGeometryPointColor(geometryPointPos, circleWidth, geometryPointColor);
2390 } else {
2391 GLHelper::setColor(geometryPointColor);
2392 }
2393 // push geometry point drawing matrix
2395 // move to geometryPointPos
2396 glTranslated(geometryPointPos.x(), geometryPointPos.y(), 0.1);
2397 // draw filled circle (resolution of drawn circle depending of the zoom, to improve smoothness)
2399 // draw elevation or special symbols (Start, End and Block)
2401 // Translate to top
2402 glTranslated(0, 0, 0.2);
2403 // draw Z value
2404 GLHelper::drawText(toString(geometryPointPos.z()), Position(), 0, s.edgeValue.scaledSize(s.scale) / 2, s.edgeValue.color);
2405 }
2406 // pop geometry point drawing matrix
2408 }
2409 }
2410 // draw start and end points
2411 if (bigGeometryPoints) {
2412 drawStartGeometryPoint(s, circleWidth, exaggeration);
2413 drawEndGeometryPoint(s, circleWidth, exaggeration);
2414 }
2415 // pop edge layer matrix
2417 // Pop edge Name
2419 }
2420 }
2421}
2422
2423
2424void
2425GNEEdge::drawStartGeometryPoint(const GUIVisualizationSettings& s, const double circleWidth, const double exaggeration) const {
2426 // get first geometry point
2427 const auto& geometryPointPos = myNBEdge->getGeometry().front();
2428 // check if mouse is over start geometry point
2429 const bool mouseOver = myNet->getViewNet()->getPositionInformation().distanceSquaredTo2D(geometryPointPos) <= (circleWidth * circleWidth);
2430 // check drawing conditions
2431 if ((geometryPointPos.distanceSquaredTo2D(getFromJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE) && (!s.drawForRectangleSelection || mouseOver)) {
2432 // calculate angle
2433 const double angle = RAD2DEG(geometryPointPos.angleTo2D(myNBEdge->getGeometry()[1])) * -1;
2434 // obtain color
2435 RGBColor geometryPointColor = s.junctionColorer.getSchemes()[0].getColor(2);
2436 // override with special colors (unless the color scheme is based on selection)
2437 if (drawUsingSelectColor() && s.laneColorer.getActive() != 1) {
2438 geometryPointColor = s.colorSettings.selectedEdgeColor.changedBrightness(-20);
2439 }
2440 // set geometry point color
2441 setGeometryPointColor(geometryPointPos, circleWidth, geometryPointColor);
2442 // push drawing matrix
2444 // move to point position
2445 glTranslated(geometryPointPos.x(), geometryPointPos.y(), 0.1);
2446 // resolution of drawn circle depending of the zoom (To improve smoothness)
2447 GLHelper::drawFilledCircle(circleWidth, s.getCircleResolution(), angle + 90, angle + 270);
2448 // pop drawing matrix
2450 // draw a "s" over last point depending of drawForRectangleSelection
2452 // push drawing matrix
2454 // move top
2455 glTranslated(0, 0, 0.2);
2456 // draw S
2457 GLHelper::drawText("S", geometryPointPos, 0, circleWidth, RGBColor(0, 50, 255));
2458 // pop drawing matrix
2460 // check if draw line between junctions
2461 if (mouseOver) {
2462 // set base color
2464 // push drawing matrix
2466 // draw line between geometry point and from junction
2467 const PositionVector lineA = {geometryPointPos, getFromJunction()->getNBNode()->getPosition()};
2468 GLHelper::drawBoxLine(lineA[1], RAD2DEG(lineA[0].angleTo2D(lineA[1])) - 90, lineA[0].distanceTo2D(lineA[1]), .1);
2469 // draw line between begin point of last lane shape and the first edge shape point
2470 const PositionVector lineB = {geometryPointPos, myNBEdge->getLanes().back().shape.front()};
2471 GLHelper::drawBoxLine(lineB[1], RAD2DEG(lineB[0].angleTo2D(lineB[1])) - 90, lineB[0].distanceTo2D(lineB[1]), .1);
2472 // pop drawing matrix
2474 }
2475 }
2476 }
2477}
2478
2479
2480void
2481GNEEdge::drawEndGeometryPoint(const GUIVisualizationSettings& s, const double circleWidth, const double exaggeration) const {
2482 // get last geometry point
2483 const auto& geometryPointPos = myNBEdge->getGeometry().back();
2484 // check if mouse is over start geometry point
2485 const bool mouseOver = myNet->getViewNet()->getPositionInformation().distanceSquaredTo2D(geometryPointPos) <= (circleWidth * circleWidth);
2486 // check drawing condition
2487 if ((geometryPointPos.distanceSquaredTo2D(getToJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE) && (!s.drawForRectangleSelection || mouseOver)) {
2488 // calculate angle
2489 const double angle = RAD2DEG(myNBEdge->getGeometry()[-1].angleTo2D(myNBEdge->getGeometry()[-2])) * -1;
2490 // obtain color
2491 RGBColor geometryPointColor = s.junctionColorer.getSchemes()[0].getColor(2);
2492 // override with special colors (unless the color scheme is based on selection)
2493 if (drawUsingSelectColor() && s.laneColorer.getActive() != 1) {
2494 geometryPointColor = s.colorSettings.selectedEdgeColor.changedBrightness(-20);
2495 }
2496 // set geometry point color
2497 setGeometryPointColor(geometryPointPos, circleWidth, geometryPointColor);
2498 // push drawing matrix
2500 // move to point position
2501 glTranslated(geometryPointPos.x(), geometryPointPos.y(), 0.1);
2502 // resolution of drawn circle depending of the zoom (To improve smoothness)
2503 GLHelper::drawFilledCircle(circleWidth, s.getCircleResolution(), angle - 90, angle + 90);
2504 // pop drawing matrix
2506 // draw a "e" over last point depending of drawForRectangleSelection
2508 // push drawing matrix
2510 // move top
2511 glTranslated(0, 0, 0.2);
2512 // draw S
2513 GLHelper::drawText("E", geometryPointPos, 0, circleWidth, RGBColor(0, 50, 255));
2514 // pop drawing matrix
2516 // check if draw line between junctions
2517 if (mouseOver) {
2518 // set base color
2520 // push drawing matrix
2522 // draw line between geometry point and to junction
2523 const PositionVector lineA = {geometryPointPos, getToJunction()->getNBNode()->getPosition()};
2524 GLHelper::drawBoxLine(lineA[1], RAD2DEG(lineA[0].angleTo2D(lineA[1])) - 90, lineA[0].distanceTo2D(lineA[1]), .1);
2525 // draw line between last point of first lane shape and the last edge shape point
2526 const PositionVector lineB = {geometryPointPos, myNBEdge->getLanes().back().shape.back()};
2527 GLHelper::drawBoxLine(lineB[1], RAD2DEG(lineB[0].angleTo2D(lineB[1])) - 90, lineB[0].distanceTo2D(lineB[1]), .1);
2528 // pop drawing matrix
2530 }
2531 }
2532 }
2533}
2534
2535void
2537 // check if we can draw it
2539 // draw the name and/or the street name
2540 const bool drawStreetName = s.streetName.show(this) && (myNBEdge->getStreetName() != "");
2541 const bool spreadSuperposed = s.spreadSuperposed && myNBEdge->getBidiEdge() != nullptr;
2542 // check conditions
2543 if (s.edgeName.show(this) || drawStreetName || s.edgeValue.show(this)) {
2544 // get first and last lanes
2545 const GNELane* firstLane = myLanes[0];
2546 const GNELane* lastLane = myLanes[myLanes.size() - 1];
2547 // calculate draw position
2548 Position drawPosition = firstLane->getLaneShape().positionAtOffset(firstLane->getLaneShape().length() / (double) 2.);
2549 drawPosition.add(lastLane->getLaneShape().positionAtOffset(lastLane->getLaneShape().length() / (double) 2.));
2550 drawPosition.mul(.5);
2551 if (spreadSuperposed) {
2552 // move name to the right of the edge and towards its beginning
2553 const double dist = 0.6 * s.edgeName.scaledSize(s.scale);
2554 const double shiftA = firstLane->getLaneShape().rotationAtOffset(firstLane->getLaneShape().length() / (double) 2.) - DEG2RAD(135);
2555 const Position shift(dist * cos(shiftA), dist * sin(shiftA));
2556 drawPosition.add(shift);
2557 }
2558 // calculate drawing angle
2559 double drawAngle = firstLane->getLaneShape().rotationDegreeAtOffset(firstLane->getLaneShape().length() / (double) 2.);
2560 drawAngle += 90;
2561 // avoid draw inverted text
2562 if (drawAngle > 90 && drawAngle < 270) {
2563 drawAngle -= 180;
2564 }
2565 // draw edge name
2566 if (s.edgeName.show(this)) {
2567 drawName(drawPosition, s.scale, s.edgeName, drawAngle);
2568 }
2569 // draw street name
2570 if (drawStreetName) {
2571 GLHelper::drawTextSettings(s.streetName, myNBEdge->getStreetName(), drawPosition, s.scale, drawAngle);
2572 }
2573 // draw edge values
2574 if (s.edgeValue.show(this)) {
2575 // get current scheme
2576 const int activeScheme = s.laneColorer.getActive();
2577 // calculate value depending of active scheme
2578 std::string value;
2579 if (activeScheme == 12) {
2580 // edge param, could be non-numerical
2581 value = getNBEdge()->getParameter(s.edgeParam, "");
2582 } else if (activeScheme == 13) {
2583 // lane param, could be non-numerical
2584 value = getNBEdge()->getLaneStruct(lastLane->getIndex()).getParameter(s.laneParam, "");
2585 } else {
2586 // use numerical value value of leftmost lane to hopefully avoid sidewalks, bikelanes etc
2587 const double doubleValue = lastLane->getColorValue(s, activeScheme);
2588 const RGBColor color = s.laneColorer.getScheme().getColor(doubleValue);
2589 value = color.alpha() == 0 ? "" : toString(doubleValue);
2590 }
2591 // check if value is empty
2592 if (value != "") {
2593 GLHelper::drawTextSettings(s.edgeValue, value, drawPosition, s.scale, drawAngle);
2594 }
2595 }
2596 }
2597 }
2598}
2599
2600
2601void
2603 // Push stopOffset matrix
2605 // translate to front (note: Special case)
2606 if (myNet->getViewNet()->getFrontAttributeCarrier() == this) {
2607 glTranslated(0, 0, GLO_FRONTELEMENT);
2608 } else {
2610 }
2612 for (const auto& lane : getLanes()) {
2613 lane->drawLaneStopOffset(s, myNBEdge->myEdgeStopOffset.getOffset());
2614 }
2615 }
2616 // Push stopOffset matrix
2618}
2619
2620
2621void
2623 // draw child additional
2624 for (const auto& additional : getChildAdditionals()) {
2625 additional->drawGL(s);
2626 }
2627 // draw person stops
2629 for (const auto& stopEdge : getChildDemandElements()) {
2630 if ((stopEdge->getTagProperty().getTag() == GNE_TAG_STOPPERSON_EDGE) || (stopEdge->getTagProperty().getTag() == GNE_TAG_STOPCONTAINER_EDGE)) {
2631 stopEdge->drawGL(s);
2632 }
2633 }
2634 }
2635 // draw vehicles
2636 const std::map<const GNELane*, std::vector<GNEDemandElement*> > vehiclesMap = getVehiclesOverEdgeMap();
2637 for (const auto& vehicleMap : vehiclesMap) {
2638 for (const auto& vehicle : vehicleMap.second) {
2639 vehicle->drawGL(s);
2640 }
2641 }
2642 // draw TAZ elements
2643 drawTAZElements(s);
2644}
2645
2646
2647void
2649 // first check if draw TAZ Elements is enabled
2651 std::vector<GNEAdditional*> TAZSourceSinks;
2652 // get all TAZ source/sinks vinculated with this edge
2653 for (const auto& additional : getChildAdditionals()) {
2654 if ((additional->getTagProperty().getTag() == SUMO_TAG_TAZSOURCE) ||
2655 (additional->getTagProperty().getTag() == SUMO_TAG_TAZSINK)) {
2656 TAZSourceSinks.push_back(additional);
2657 }
2658 }
2659 if (TAZSourceSinks.size() > 0) {
2660 // check if current front element is a Source/sink
2661 const auto frontAC = myNet->getViewNet()->getFrontAttributeCarrier();
2662 // push all GLIDs
2663 for (const auto& TAZSourceSink : TAZSourceSinks) {
2664 if (TAZSourceSink == frontAC) {
2665 GLHelper::pushName(TAZSourceSink->getGUIGlObject()->getGlID());
2666 }
2667 }
2668 for (const auto& TAZSourceSink : TAZSourceSinks) {
2669 if (TAZSourceSink != frontAC) {
2670 GLHelper::pushName(TAZSourceSink->getGlID());
2671 }
2672 }
2673 // check if TAZ Source/sink is selected
2674 bool selected = false;
2675 for (const auto& TAZSourceSink : TAZSourceSinks) {
2676 if (TAZSourceSink->isAttributeCarrierSelected()) {
2677 selected = true;
2678 }
2679 }
2680 // iterate over lanes
2681 for (const auto& lane : myLanes) {
2682 // get lane drawing constants
2683 GNELane::LaneDrawingConstants laneDrawingConstants(s, lane);
2684 // Push layer matrix
2686 // translate to front (note: Special case)
2687 if (myNet->getViewNet()->getFrontAttributeCarrier() == this) {
2688 glTranslated(0, 0, GLO_FRONTELEMENT);
2689 } else if (lane->getLaneShape().length2D() <= (s.neteditSizeSettings.junctionBubbleRadius * 2)) {
2691 } else {
2693 }
2694 // move to front
2695 glTranslated(0, 0, 0.1);
2696 // set color
2697 if (selected) {
2699 } else {
2701 }
2702 // draw as box lines
2704 lane->getLaneGeometry(), laneDrawingConstants.halfWidth);
2705 // Pop layer matrix
2707 }
2708 // pop all GLIDs
2709 for (const auto& TAZSourceSink : TAZSourceSinks) {
2710 if (TAZSourceSink == frontAC) {
2712 }
2713 }
2714 for (const auto& TAZSourceSink : TAZSourceSinks) {
2715 if (TAZSourceSink != frontAC) {
2717 }
2718 }
2719 // check if curently we're inspecting a TAZ Source/Sink
2720 for (const auto& TAZSourceSink : TAZSourceSinks) {
2721 if (myNet->getViewNet()->isAttributeCarrierInspected(TAZSourceSink)) {
2723 } else if (TAZSourceSink == frontAC) {
2725 }
2726 }
2727 }
2728 }
2729}
2730
2731
2732void
2734 // avoid draw for railways
2735 if ((gPostDrawing.markedFirstGeometryPoint == this) && (s.laneWidthExaggeration >= 1) && !myLanes.front()->drawAsRailway(s)) {
2736 // push draw matrix
2738 // translate to front depending of big points
2739 if (drawBigGeometryPoints()) {
2740 glTranslated(0, 0, GLO_GEOMETRYPOINT - 1);
2741 } else {
2742 glTranslated(0, 0, GLO_EDGE);
2743 }
2744 // obtain color
2745 RGBColor geometryPointColor = s.junctionColorer.getSchemes()[0].getColor(2);
2746 if (drawUsingSelectColor() && s.laneColorer.getActive() != 1) {
2747 // override with special colors (unless the color scheme is based on selection)
2748 geometryPointColor = s.colorSettings.selectedEdgeColor.changedBrightness(-20);
2749 }
2750 // set color
2751 GLHelper::setColor(geometryPointColor);
2752 // iterate over NBEdge geometry
2753 for (int i = 1; i < (int)myNBEdge->getGeometry().size(); i++) {
2754 // calculate line between previous and current geometry point
2755 PositionVector line = {myNBEdge->getGeometry()[i - 1], myNBEdge->getGeometry()[i]};
2756 line.move2side(0.2);
2757 // draw box line
2758 GLHelper::drawBoxLine(line[1], RAD2DEG(line[0].angleTo2D(line[1])) - 90, line[0].distanceTo2D(line[1]), .1);
2759 }
2760 // pop draw matrix
2762 }
2763}
2764
2765
2766void
2767GNEEdge::setGeometryPointColor(const Position& geometryPointPos, const double circleWidth, const RGBColor& geometryPointColor) const {
2768 // by default use geometryPointColor
2769 RGBColor color = geometryPointColor;
2770 // set color depending if mouse is over geometry point
2771 if (gPostDrawing.markedFirstGeometryPoint == nullptr) {
2772 if (mouseWithinGeometry(geometryPointPos, circleWidth)) {
2774 color = RGBColor::ORANGE;
2775 }
2778 if (mouseWithinGeometry(geometryPointPos, circleWidth)) {
2780 color = RGBColor::CYAN;
2781 }
2782 }
2783 GLHelper::setColor(color);
2784}
2785
2786
2787bool
2790 return false;
2792 return true;
2795 return true;
2796 } else {
2797 return false;
2798 }
2799}
2800
2801
2802bool
2804 if ((vehicleA.beginPosition() == vehicleB.beginPosition()) && (vehicleA.endPosition() == vehicleB.endPosition())) {
2805 return true;
2806 } else if ((vehicleA.beginPosition() < vehicleB.beginPosition()) && (vehicleA.endPosition() > vehicleB.endPosition())) {
2807 return true;
2808 } else if ((vehicleA.beginPosition() < vehicleB.beginPosition()) && (vehicleA.endPosition() > vehicleB.beginPosition())) {
2809 return true;
2810 } else if ((vehicleA.beginPosition() < vehicleB.endPosition()) && (vehicleA.endPosition() > vehicleB.endPosition())) {
2811 return true;
2812 } else {
2813 return false;
2814 }
2815}
2816
2817
2819GNEEdge::processMoveFromJunctionSelected(const PositionVector originalShape, const Position mousePosition, const double snapRadius) {
2820 // calculate squared snapRadius
2821 const double squaredSnapRadius = (snapRadius * snapRadius);
2822 // declare shape to move
2823 PositionVector shapeToMove = originalShape;
2824 // obtain nearest index
2825 const int nearestIndex = originalShape.indexOfClosest(mousePosition);
2826 // obtain nearest position
2827 const Position nearestPosition = originalShape.positionAtOffset2D(originalShape.nearest_offset_to_point2D(mousePosition));
2828 // generate indexes
2829 std::vector<int> indexes;
2830 // check conditions
2831 if (nearestIndex == -1) {
2832 return nullptr;
2833 } else if (nearestPosition == Position::INVALID) {
2834 // special case for extremes
2835 if (mousePosition.distanceSquaredTo2D(shapeToMove[nearestIndex]) <= squaredSnapRadius) {
2836 for (int i = 1; i <= nearestIndex; i++) {
2837 indexes.push_back(i);
2838 }
2839 // move extrem without creating new geometry point
2840 return new GNEMoveOperation(this, originalShape, indexes, shapeToMove, indexes);
2841 } else {
2842 return nullptr;
2843 }
2844 } else if (nearestPosition.distanceSquaredTo2D(shapeToMove[nearestIndex]) <= squaredSnapRadius) {
2845 for (int i = 1; i <= nearestIndex; i++) {
2846 indexes.push_back(i);
2847 }
2848 // move geometry point without creating new geometry point
2849 return new GNEMoveOperation(this, originalShape, indexes, shapeToMove, indexes);
2850 } else {
2851 // create new geometry point and keep new index (if we clicked near of shape)
2852 const int newIndex = shapeToMove.insertAtClosest(nearestPosition, true);
2853 for (int i = 1; i <= newIndex; i++) {
2854 indexes.push_back(i);
2855 }
2856 // move after setting new geometry point in shapeToMove
2857 return new GNEMoveOperation(this, originalShape, indexes, shapeToMove, indexes);
2858 }
2859}
2860
2861
2863GNEEdge::processMoveToJunctionSelected(const PositionVector originalShape, const Position mousePosition, const double snapRadius) {
2864 // calculate squared snapRadius
2865 const double squaredSnapRadius = (snapRadius * snapRadius);
2866 // declare shape to move
2867 PositionVector shapeToMove = originalShape;
2868 // obtain nearest index
2869 const int nearestIndex = originalShape.indexOfClosest(mousePosition);
2870 // obtain nearest position
2871 const Position nearestPosition = originalShape.positionAtOffset2D(originalShape.nearest_offset_to_point2D(mousePosition));
2872 // generate indexes
2873 std::vector<int> indexes;
2874 // check conditions
2875 if (nearestIndex == -1) {
2876 return nullptr;
2877 } else if (nearestPosition == Position::INVALID) {
2878 // special case for extremes
2879 if (mousePosition.distanceSquaredTo2D(shapeToMove[nearestIndex]) <= squaredSnapRadius) {
2880 for (int i = nearestIndex; i < ((int)originalShape.size() - 1); i++) {
2881 indexes.push_back(i);
2882 }
2883 // move extrem without creating new geometry point
2884 return new GNEMoveOperation(this, originalShape, indexes, shapeToMove, indexes);
2885 } else {
2886 return nullptr;
2887 }
2888 } else if (nearestPosition.distanceSquaredTo2D(shapeToMove[nearestIndex]) <= squaredSnapRadius) {
2889 for (int i = nearestIndex; i < ((int)originalShape.size() - 1); i++) {
2890 indexes.push_back(i);
2891 }
2892 // move geometry point without creating new geometry point
2893 return new GNEMoveOperation(this, originalShape, indexes, shapeToMove, indexes);
2894 } else {
2895 // create new geometry point and keep new index (if we clicked near of shape)
2896 const int newIndex = shapeToMove.insertAtClosest(nearestPosition, true);
2897 for (int i = newIndex; i < ((int)originalShape.size() - 1); i++) {
2898 indexes.push_back(i);
2899 }
2900 // move after setting new geometry point in shapeToMove
2901 return new GNEMoveOperation(this, originalShape, indexes, shapeToMove, indexes);
2902 }
2903}
2904
2905
2908 std::vector<int> geometryPointsToMove;
2909 for (int i = 0; i < (int)myNBEdge->getGeometry().size(); i++) {
2910 geometryPointsToMove.push_back(i);
2911 }
2912 // move entire shape (including extremes)
2913 return new GNEMoveOperation(this, myNBEdge->getGeometry(), geometryPointsToMove, myNBEdge->getGeometry(), geometryPointsToMove);
2914}
2915
2916
2919 // get move multiple element values
2920 const auto& moveMultipleElementValues = myNet->getViewNet()->getMoveMultipleElementValues();
2921 // declare shape to move
2922 PositionVector shapeToMove = myNBEdge->getGeometry();
2923 // first check if kept offset is larger than geometry
2924 if (shapeToMove.length2D() < moveMultipleElementValues.getEdgeOffset()) {
2925 return nullptr;
2926 }
2927 // declare offset
2928 double offset = 0;
2929 // set offset depending of convex angle
2930 if (isConvexAngle()) {
2931 offset = moveMultipleElementValues.getEdgeOffset();
2932 } else {
2933 offset = shapeToMove.length2D() - moveMultipleElementValues.getEdgeOffset();
2934 }
2935 // obtain offset position
2936 const Position offsetPosition = myNBEdge->getGeometry().positionAtOffset2D(offset);
2937 // obtain nearest index to offset position
2938 const int nearestIndex = myNBEdge->getGeometry().indexOfClosest(offsetPosition);
2939 // check conditions
2940 if ((nearestIndex == -1) || (offsetPosition == Position::INVALID)) {
2941 return nullptr;
2942 } else if (offsetPosition.