1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    GNEDetectorEntryExit.cpp
11 /// @author  Pablo Alvarez Lopez
12 /// @date    Nov 2015
13 /// @version $Id$
14 ///
15 //
16 /****************************************************************************/
17 
18 // ===========================================================================
19 // included modules
20 // ===========================================================================
21 
22 #include <netedit/GNENet.h>
23 #include <netedit/GNEUndoList.h>
24 #include <netedit/GNEViewNet.h>
25 #include <netedit/changes/GNEChange_Attribute.h>
26 #include <netedit/netelements/GNEEdge.h>
27 #include <netedit/netelements/GNELane.h>
28 #include <utils/gui/div/GLHelper.h>
29 #include <utils/gui/globjects/GLIncludes.h>
30 
31 #include "GNEDetectorEntryExit.h"
32 #include "GNEAdditionalHandler.h"
33 
34 
35 // ===========================================================================
36 // member method definitions
37 // ===========================================================================
38 
GNEDetectorEntryExit(SumoXMLTag entryExitTag,GNEViewNet * viewNet,GNEAdditional * parent,GNELane * lane,double pos,bool friendlyPos,bool blockMovement)39 GNEDetectorEntryExit::GNEDetectorEntryExit(SumoXMLTag entryExitTag, GNEViewNet* viewNet, GNEAdditional* parent, GNELane* lane, double pos, bool friendlyPos, bool blockMovement) :
40     GNEDetector(parent, viewNet, GLO_DET_ENTRY, entryExitTag, pos, 0, "", "", friendlyPos, blockMovement, {
41     lane
42 }) {
43     //check that this is a TAZ Source OR a TAZ Sink
44     if ((entryExitTag != SUMO_TAG_DET_ENTRY) && (entryExitTag != SUMO_TAG_DET_EXIT)) {
45         throw InvalidArgument("Invalid E3 Child Tag");
46     }
47 }
48 
49 
~GNEDetectorEntryExit()50 GNEDetectorEntryExit::~GNEDetectorEntryExit() {}
51 
52 
53 bool
isAdditionalValid() const54 GNEDetectorEntryExit::isAdditionalValid() const {
55     // with friendly position enabled position are "always fixed"
56     if (myFriendlyPosition) {
57         return true;
58     } else {
59         return fabs(myPositionOverLane) <= getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength();
60     }
61 }
62 
63 
64 std::string
getAdditionalProblem() const65 GNEDetectorEntryExit::getAdditionalProblem() const {
66     // declare variable for error position
67     std::string errorPosition;
68     const double len = getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength();
69     // check positions over lane
70     if (myPositionOverLane < -len) {
71         errorPosition = (toString(SUMO_ATTR_POSITION) + " < 0");
72     }
73     if (myPositionOverLane > len) {
74         errorPosition = (toString(SUMO_ATTR_POSITION) + " > lanes's length");
75     }
76     return errorPosition;
77 }
78 
79 
80 void
fixAdditionalProblem()81 GNEDetectorEntryExit::fixAdditionalProblem() {
82     // declare new position
83     double newPositionOverLane = myPositionOverLane;
84     // fix pos and lenght  checkAndFixDetectorPosition
85     GNEAdditionalHandler::checkAndFixDetectorPosition(newPositionOverLane, getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength(), true);
86     // set new position
87     setAttribute(SUMO_ATTR_POSITION, toString(newPositionOverLane), myViewNet->getUndoList());
88 }
89 
90 
91 void
moveGeometry(const Position & offset)92 GNEDetectorEntryExit::moveGeometry(const Position& offset) {
93     // Calculate new position using old position
94     Position newPosition = myMove.originalViewPosition;
95     newPosition.add(offset);
96     // filtern position using snap to active grid
97     newPosition = myViewNet->snapToActiveGrid(newPosition);
98     const bool storeNegative = myPositionOverLane < 0;
99     myPositionOverLane = getLaneParents().front()->getShape().nearest_offset_to_point2D(newPosition, false);
100     if (storeNegative) {
101         myPositionOverLane -= getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength();
102     }
103     // Update geometry
104     updateGeometry(false);
105 }
106 
107 
108 void
commitGeometryMoving(GNEUndoList * undoList)109 GNEDetectorEntryExit::commitGeometryMoving(GNEUndoList* undoList) {
110     // commit new position allowing undo/redo
111     undoList->p_begin("position of " + getTagStr());
112     undoList->p_add(new GNEChange_Attribute(this, myViewNet->getNet(), SUMO_ATTR_POSITION, toString(myPositionOverLane), true, myMove.firstOriginalLanePosition));
113     undoList->p_end();
114 }
115 
116 
117 void
updateGeometry(bool updateGrid)118 GNEDetectorEntryExit::updateGeometry(bool updateGrid) {
119     // first check if object has to be removed from grid (SUMOTree)
120     if (updateGrid) {
121         myViewNet->getNet()->removeGLObjectFromGrid(this);
122     }
123 
124     // Clear all containers
125     myGeometry.clearGeometry();
126 
127     // obtain position over lane
128     myGeometry.shape.push_back(getPositionInView());
129 
130     // Obtain first position
131     Position f = myGeometry.shape[0] - Position(1, 0);
132 
133     // Obtain next position
134     Position s = myGeometry.shape[0] + Position(1, 0);
135 
136     // Save rotation (angle) of the vector constructed by points f and s
137     myGeometry.shapeRotations.push_back(getLaneParents().front()->getShape().rotationDegreeAtOffset(getGeometryPositionOverLane()) * -1);
138 
139     // Set block icon position
140     myBlockIcon.position = myGeometry.shape.getLineCenter();
141 
142     // Set offset of the block icon
143     myBlockIcon.offset = Position(-1, 0);
144 
145     // Set block icon rotation, and using their rotation for logo
146     myBlockIcon.setRotation(getLaneParents().front());
147 
148     // last step is to check if object has to be added into grid (SUMOTree) again
149     if (updateGrid) {
150         myViewNet->getNet()->addGLObjectIntoGrid(this);
151     }
152 
153     // update E3 parent Geometry
154     getAdditionalParents().at(0)->updateGeometry(updateGrid);
155 }
156 
157 
158 void
drawGL(const GUIVisualizationSettings & s) const159 GNEDetectorEntryExit::drawGL(const GUIVisualizationSettings& s) const {
160     // Start drawing adding gl identificator
161     glPushName(getGlID());
162 
163     // Push detector matrix
164     glPushMatrix();
165     glTranslated(0, 0, getType());
166 
167     // Set color
168     if (drawUsingSelectColor()) {
169         GLHelper::setColor(s.selectedAdditionalColor);
170     } else if (myTagProperty.getTag() == SUMO_TAG_DET_ENTRY) {
171         GLHelper::setColor(s.SUMO_color_E3Entry);
172     } else if (myTagProperty.getTag() == SUMO_TAG_DET_EXIT) {
173         GLHelper::setColor(s.SUMO_color_E3Exit);
174     }
175 
176     // Set initial values
177     const double exaggeration = s.addSize.getExaggeration(s, this);
178     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
179 
180     // Push polygon matrix
181     glPushMatrix();
182     glScaled(exaggeration, exaggeration, 1);
183     glTranslated(myGeometry.shape[0].x(), myGeometry.shape[0].y(), 0);
184     glRotated(myGeometry.shapeRotations[0], 0, 0, 1);
185 
186     // draw details if isn't being drawn for selecting
187     if (!s.drawForSelecting) {
188         // Draw polygon
189         glBegin(GL_LINES);
190         glVertex2d(1.7, 0);
191         glVertex2d(-1.7, 0);
192         glEnd();
193         glBegin(GL_QUADS);
194         glVertex2d(-1.7, .5);
195         glVertex2d(-1.7, -.5);
196         glVertex2d(1.7, -.5);
197         glVertex2d(1.7, .5);
198         glEnd();
199 
200         // first Arrow
201         glTranslated(1.5, 0, 0);
202         GLHelper::drawBoxLine(Position(0, 4), 0, 2, .05);
203         GLHelper::drawTriangleAtEnd(Position(0, 4), Position(0, 1), (double) 1, (double) .25);
204 
205         // second Arrow
206         glTranslated(-3, 0, 0);
207         GLHelper::drawBoxLine(Position(0, 4), 0, 2, .05);
208         GLHelper::drawTriangleAtEnd(Position(0, 4), Position(0, 1), (double) 1, (double) .25);
209     } else {
210         // Draw square in drawy for selecting mode
211         glBegin(GL_QUADS);
212         glVertex2d(-1.7, 4.3);
213         glVertex2d(-1.7, -.5);
214         glVertex2d(1.7, -.5);
215         glVertex2d(1.7, 4.3);
216         glEnd();
217     }
218 
219     // Pop polygon matrix
220     glPopMatrix();
221 
222     // Pop detector matrix
223     glPopMatrix();
224 
225     // Check if the distance is enought to draw details
226     if (!s.drawForSelecting && ((s.scale * exaggeration) >= 10)) {
227         // Push matrix
228         glPushMatrix();
229         // Traslate to center of detector
230         glTranslated(myGeometry.shape.getLineCenter().x(), myGeometry.shape.getLineCenter().y(), getType() + 0.1);
231         // Rotate depending of myBlockIcon.rotation
232         glRotated(myBlockIcon.rotation, 0, 0, -1);
233         //move to logo position
234         glTranslated(1.9, 0, 0);
235         // draw Entry or Exit logo if isn't being drawn for selecting
236         if (s.drawForSelecting) {
237             GLHelper::setColor(s.SUMO_color_E3Entry);
238             GLHelper::drawBoxLine(Position(0, 1), 0, 2, 1);
239         } else if (drawUsingSelectColor()) {
240             GLHelper::drawText("E3", Position(), .1, 2.8, s.selectedAdditionalColor);
241         } else if (myTagProperty.getTag() == SUMO_TAG_DET_ENTRY) {
242             GLHelper::drawText("E3", Position(), .1, 2.8, s.SUMO_color_E3Entry);
243         } else if (myTagProperty.getTag() == SUMO_TAG_DET_EXIT) {
244             GLHelper::drawText("E3", Position(), .1, 2.8, s.SUMO_color_E3Exit);
245         }
246         //move to logo position
247         glTranslated(1.7, 0, 0);
248         // Rotate depending of myBlockIcon.rotation
249         glRotated(90, 0, 0, 1);
250         // draw Entry or Exit text if isn't being drawn for selecting
251         if (s.drawForSelecting) {
252             GLHelper::setColor(s.SUMO_color_E3Entry);
253             GLHelper::drawBoxLine(Position(0, 1), 0, 2, 1);
254         } else if (drawUsingSelectColor()) {
255             if (myTagProperty.getTag() == SUMO_TAG_DET_ENTRY) {
256                 GLHelper::drawText("Entry", Position(), .1, 1, s.selectedAdditionalColor);
257             } else if (myTagProperty.getTag() == SUMO_TAG_DET_EXIT) {
258                 GLHelper::drawText("Exit", Position(), .1, 1, s.selectedAdditionalColor);
259             }
260         } else {
261             if (myTagProperty.getTag() == SUMO_TAG_DET_ENTRY) {
262                 GLHelper::drawText("Entry", Position(), .1, 1, s.SUMO_color_E3Entry);
263             } else if (myTagProperty.getTag() == SUMO_TAG_DET_EXIT) {
264                 GLHelper::drawText("Exit", Position(), .1, 1, s.SUMO_color_E3Exit);
265             }
266         }
267         // pop matrix
268         glPopMatrix();
269         // Show Lock icon depending of the Edit mode and if isn't being drawn for selecting
270         if (!s.drawForSelecting) {
271             myBlockIcon.draw(0.4);
272         }
273     }
274     // Draw name if isn't being drawn for selecting
275     if (!s.drawForSelecting) {
276         drawName(getCenteringBoundary().getCenter(), s.scale, s.addName);
277     }
278     // check if dotted contour has to be drawn
279     if (!s.drawForSelecting && (myViewNet->getDottedAC() == this)) {
280         GLHelper::drawShapeDottedContour(getType(), myGeometry.shape[0], 3.4, 5, myGeometry.shapeRotations[0], 0, 2);
281     }
282     // pop gl identificator
283     glPopName();
284 }
285 
286 
287 std::string
getAttribute(SumoXMLAttr key) const288 GNEDetectorEntryExit::getAttribute(SumoXMLAttr key) const {
289     switch (key) {
290         case SUMO_ATTR_ID:
291             return getAdditionalID();
292         case SUMO_ATTR_LANE:
293             return getLaneParents().front()->getID();
294         case SUMO_ATTR_POSITION:
295             return toString(myPositionOverLane);
296         case SUMO_ATTR_FRIENDLY_POS:
297             return toString(myFriendlyPosition);
298         case GNE_ATTR_BLOCK_MOVEMENT:
299             return toString(myBlockMovement);
300         case GNE_ATTR_PARENT:
301             return getAdditionalParents().at(0)->getID();
302         case GNE_ATTR_SELECTED:
303             return toString(isAttributeCarrierSelected());
304         case GNE_ATTR_GENERIC:
305             return getGenericParametersStr();
306         default:
307             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
308     }
309 }
310 
311 
312 void
setAttribute(SumoXMLAttr key,const std::string & value,GNEUndoList * undoList)313 GNEDetectorEntryExit::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {
314     if (value == getAttribute(key)) {
315         return; //avoid needless changes, later logic relies on the fact that attributes have changed
316     }
317     switch (key) {
318         case SUMO_ATTR_ID:
319         case SUMO_ATTR_LANE:
320         case SUMO_ATTR_POSITION:
321         case SUMO_ATTR_FRIENDLY_POS:
322         case GNE_ATTR_BLOCK_MOVEMENT:
323         case GNE_ATTR_PARENT:
324         case GNE_ATTR_SELECTED:
325         case GNE_ATTR_GENERIC:
326             undoList->p_add(new GNEChange_Attribute(this, myViewNet->getNet(), key, value));
327             break;
328         default:
329             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
330     }
331 }
332 
333 
334 bool
isValid(SumoXMLAttr key,const std::string & value)335 GNEDetectorEntryExit::isValid(SumoXMLAttr key, const std::string& value) {
336     switch (key) {
337         case SUMO_ATTR_ID:
338             return isValidAdditionalID(value);
339         case SUMO_ATTR_LANE:
340             return (myViewNet->getNet()->retrieveLane(value, false) != nullptr);
341         case SUMO_ATTR_POSITION:
342             return canParse<double>(value) && fabs(parse<double>(value)) < getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength();
343         case SUMO_ATTR_FRIENDLY_POS:
344             return canParse<bool>(value);
345         case GNE_ATTR_BLOCK_MOVEMENT:
346             return canParse<bool>(value);
347         case GNE_ATTR_PARENT:
348             return (myViewNet->getNet()->retrieveAdditional(SUMO_TAG_E3DETECTOR, value, false) != nullptr);
349         case GNE_ATTR_SELECTED:
350             return canParse<bool>(value);
351         case GNE_ATTR_GENERIC:
352             return isGenericParametersValid(value);
353         default:
354             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
355     }
356 }
357 
358 void
setAttribute(SumoXMLAttr key,const std::string & value)359 GNEDetectorEntryExit::setAttribute(SumoXMLAttr key, const std::string& value) {
360     switch (key) {
361         case SUMO_ATTR_ID:
362             changeAdditionalID(value);
363             break;
364         case SUMO_ATTR_LANE:
365             changeLaneParents(this, value);
366             break;
367         case SUMO_ATTR_POSITION:
368             myPositionOverLane = parse<double>(value);
369             break;
370         case SUMO_ATTR_FRIENDLY_POS:
371             myFriendlyPosition = parse<bool>(value);
372             break;
373         case GNE_ATTR_BLOCK_MOVEMENT:
374             myBlockMovement = parse<bool>(value);
375             break;
376         case GNE_ATTR_PARENT:
377             changeAdditionalParent(this, value, 0);
378             break;
379         case GNE_ATTR_SELECTED:
380             if (parse<bool>(value)) {
381                 selectAttributeCarrier();
382             } else {
383                 unselectAttributeCarrier();
384             }
385             break;
386         case GNE_ATTR_GENERIC:
387             setGenericParametersStr(value);
388             break;
389         default:
390             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
391     }
392     // check if updated attribute requieres update geometry
393     if (myTagProperty.hasAttribute(key) && myTagProperty.getAttributeProperties(key).requiereUpdateGeometry()) {
394         updateGeometry(true);
395     }
396 }
397 
398 /****************************************************************************/
399