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    GNEDetectorE1.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 "GNEDetectorE1.h"
32 #include "GNEAdditionalHandler.h"
33 
34 
35 // ===========================================================================
36 // member method definitions
37 // ===========================================================================
38 
GNEDetectorE1(const std::string & id,GNELane * lane,GNEViewNet * viewNet,double pos,double freq,const std::string & filename,const std::string & vehicleTypes,const std::string & name,bool friendlyPos,bool blockMovement)39 GNEDetectorE1::GNEDetectorE1(const std::string& id, GNELane* lane, GNEViewNet* viewNet, double pos, double freq, const std::string& filename, const std::string& vehicleTypes, const std::string& name, bool friendlyPos, bool blockMovement) :
40     GNEDetector(id, viewNet, GLO_E1DETECTOR, SUMO_TAG_E1DETECTOR, pos, freq, filename, vehicleTypes, name, friendlyPos, blockMovement, {
41     lane
42 }) {
43 }
44 
45 
~GNEDetectorE1()46 GNEDetectorE1::~GNEDetectorE1() {
47 }
48 
49 
50 bool
isAdditionalValid() const51 GNEDetectorE1::isAdditionalValid() const {
52     // with friendly position enabled position are "always fixed"
53     if (myFriendlyPosition) {
54         return true;
55     } else {
56         return fabs(myPositionOverLane) <= getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength();
57     }
58 }
59 
60 
61 std::string
getAdditionalProblem() const62 GNEDetectorE1::getAdditionalProblem() const {
63     // declare variable for error position
64     std::string errorPosition;
65     const double len = getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength();
66     // check positions over lane
67     if (myPositionOverLane < -len) {
68         errorPosition = (toString(SUMO_ATTR_POSITION) + " < 0");
69     }
70     if (myPositionOverLane > len) {
71         errorPosition = (toString(SUMO_ATTR_POSITION) + " > lanes's length");
72     }
73     return errorPosition;
74 }
75 
76 
77 void
fixAdditionalProblem()78 GNEDetectorE1::fixAdditionalProblem() {
79     // declare new position
80     double newPositionOverLane = myPositionOverLane;
81     // fix pos and lenght  checkAndFixDetectorPosition
82     GNEAdditionalHandler::checkAndFixDetectorPosition(newPositionOverLane, getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength(), true);
83     // set new position
84     setAttribute(SUMO_ATTR_POSITION, toString(newPositionOverLane), myViewNet->getUndoList());
85 }
86 
87 
88 void
moveGeometry(const Position & offset)89 GNEDetectorE1::moveGeometry(const Position& offset) {
90     // Calculate new position using old position
91     Position newPosition = myMove.originalViewPosition;
92     newPosition.add(offset);
93     // filtern position using snap to active grid
94     newPosition = myViewNet->snapToActiveGrid(newPosition);
95     const bool storeNegative = myPositionOverLane < 0;
96     myPositionOverLane = getLaneParents().front()->getShape().nearest_offset_to_point2D(newPosition, false);
97     if (storeNegative) {
98         myPositionOverLane -= getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength();
99     }
100     // Update geometry
101     updateGeometry(false);
102 }
103 
104 
105 void
commitGeometryMoving(GNEUndoList * undoList)106 GNEDetectorE1::commitGeometryMoving(GNEUndoList* undoList) {
107     // commit new position allowing undo/redo
108     undoList->p_begin("position of " + getTagStr());
109     undoList->p_add(new GNEChange_Attribute(this, myViewNet->getNet(), SUMO_ATTR_POSITION, toString(myPositionOverLane), true, myMove.firstOriginalLanePosition));
110     undoList->p_end();
111 }
112 
113 
114 void
updateGeometry(bool updateGrid)115 GNEDetectorE1::updateGeometry(bool updateGrid) {
116     // first check if object has to be removed from grid (SUMOTree)
117     if (updateGrid) {
118         myViewNet->getNet()->removeGLObjectFromGrid(this);
119     }
120 
121     // Clear all containers
122     myGeometry.clearGeometry();
123 
124     // obtain position over lane
125     myGeometry.shape.push_back(getPositionInView());
126 
127     // Obtain first position
128     Position f = myGeometry.shape[0] - Position(1, 0);
129 
130     // Obtain next position
131     Position s = myGeometry.shape[0] + Position(1, 0);
132 
133     // Save rotation (angle) of the vector constructed by points f and s
134     myGeometry.shapeRotations.push_back(getLaneParents().front()->getShape().rotationDegreeAtOffset(getGeometryPositionOverLane()) * -1);
135 
136     // Set block icon position
137     myBlockIcon.position = myGeometry.shape.getLineCenter();
138 
139     // Set offset of the block icon
140     myBlockIcon.offset = Position(-1, 0);
141 
142     // Set block icon rotation, and using their rotation for logo
143     myBlockIcon.setRotation(getLaneParents().front());
144 
145     // last step is to check if object has to be added into grid (SUMOTree) again
146     if (updateGrid) {
147         myViewNet->getNet()->addGLObjectIntoGrid(this);
148     }
149 }
150 
151 
152 void
drawGL(const GUIVisualizationSettings & s) const153 GNEDetectorE1::drawGL(const GUIVisualizationSettings& s) const {
154     // get values
155     glPushName(getGlID());
156     double width = (double) 2.0 * s.scale;
157     glLineWidth(1.0);
158     const double exaggeration = s.addSize.getExaggeration(s, this);
159 
160     // set color
161     if (drawUsingSelectColor()) {
162         GLHelper::setColor(s.selectedAdditionalColor);
163     } else {
164         GLHelper::setColor(s.SUMO_color_E1);
165     }
166     // draw shape
167     glPushMatrix();
168     glTranslated(0, 0, getType());
169     glTranslated(myGeometry.shape[0].x(), myGeometry.shape[0].y(), 0);
170     glRotated(myGeometry.shapeRotations[0], 0, 0, 1);
171     glScaled(exaggeration, exaggeration, 1);
172     glBegin(GL_QUADS);
173     glVertex2d(-1.0,  2);
174     glVertex2d(-1.0, -2);
175     glVertex2d(1.0, -2);
176     glVertex2d(1.0,  2);
177     glEnd();
178     glTranslated(0, 0, .01);
179     glBegin(GL_LINES);
180     glVertex2d(0, 2 - .1);
181     glVertex2d(0, -2 + .1);
182     glEnd();
183 
184     // outline if isn't being drawn for selecting
185     if ((width * exaggeration > 1) && !s.drawForSelecting) {
186         // set color
187         if (drawUsingSelectColor()) {
188             GLHelper::setColor(s.selectionColor);
189         } else {
190             GLHelper::setColor(RGBColor::WHITE);
191         }
192         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
193         glBegin(GL_QUADS);
194         glVertex2f(-1.0,  2);
195         glVertex2f(-1.0, -2);
196         glVertex2f(1.0, -2);
197         glVertex2f(1.0,  2);
198         glEnd();
199         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
200     }
201 
202     // position indicator if isn't being drawn for selecting
203     if ((width * exaggeration > 1) && !s.drawForSelecting) {
204         // set color
205         if (drawUsingSelectColor()) {
206             GLHelper::setColor(s.selectionColor);
207         } else {
208             GLHelper::setColor(RGBColor::WHITE);
209         }
210         glRotated(90, 0, 0, -1);
211         glBegin(GL_LINES);
212         glVertex2d(0, 1.7);
213         glVertex2d(0, -1.7);
214         glEnd();
215     }
216 
217     // Pop shape matrix
218     glPopMatrix();
219 
220     // Check if the distance is enought to draw details and isn't being drawn for selecting
221     if ((s.scale * exaggeration >= 10) && !s.drawForSelecting) {
222         // Push matrix
223         glPushMatrix();
224         // Traslate to center of detector
225         glTranslated(myGeometry.shape.getLineCenter().x(), myGeometry.shape.getLineCenter().y(), getType() + 0.1);
226         // Rotate depending of myBlockIcon.rotation
227         glRotated(myBlockIcon.rotation, 0, 0, -1);
228         //move to logo position
229         glTranslated(-1, 0, 0);
230         // draw E1 logo
231         if (drawUsingSelectColor()) {
232             GLHelper::drawText("E1", Position(), .1, 1.5, s.selectionColor);
233         } else {
234             GLHelper::drawText("E1", Position(), .1, 1.5, RGBColor::BLACK);
235         }
236         // pop matrix
237         glPopMatrix();
238         // Show Lock icon depending of the Edit mode
239         myBlockIcon.draw();
240     }
241 
242     // Finish draw if isn't being drawn for selecting
243     if (!s.drawForSelecting) {
244         drawName(getCenteringBoundary().getCenter(), s.scale, s.addName);
245     }
246 
247     // check if dotted contour has to be drawn
248     if (!s.drawForSelecting && (myViewNet->getDottedAC() == this)) {
249         GLHelper::drawShapeDottedContour(getType(), myGeometry.shape[0], 2, 4, myGeometry.shapeRotations[0]);
250     }
251 
252     // pop name
253     glPopName();
254 }
255 
256 
257 std::string
getAttribute(SumoXMLAttr key) const258 GNEDetectorE1::getAttribute(SumoXMLAttr key) const {
259     switch (key) {
260         case SUMO_ATTR_ID:
261             return getAdditionalID();
262         case SUMO_ATTR_LANE:
263             return getLaneParents().front()->getID();
264         case SUMO_ATTR_POSITION:
265             return toString(myPositionOverLane);
266         case SUMO_ATTR_FREQUENCY:
267             return toString(myFreq);
268         case SUMO_ATTR_NAME:
269             return myAdditionalName;
270         case SUMO_ATTR_FILE:
271             return myFilename;
272         case SUMO_ATTR_VTYPES:
273             return myVehicleTypes;
274         case SUMO_ATTR_FRIENDLY_POS:
275             return toString(myFriendlyPosition);
276         case GNE_ATTR_BLOCK_MOVEMENT:
277             return toString(myBlockMovement);
278         case GNE_ATTR_SELECTED:
279             return toString(isAttributeCarrierSelected());
280         case GNE_ATTR_GENERIC:
281             return getGenericParametersStr();
282         default:
283             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
284     }
285 }
286 
287 
288 void
setAttribute(SumoXMLAttr key,const std::string & value,GNEUndoList * undoList)289 GNEDetectorE1::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {
290     if (value == getAttribute(key)) {
291         return; //avoid needless changes, later logic relies on the fact that attributes have changed
292     }
293     switch (key) {
294         case SUMO_ATTR_ID:
295         case SUMO_ATTR_LANE:
296         case SUMO_ATTR_POSITION:
297         case SUMO_ATTR_FREQUENCY:
298         case SUMO_ATTR_NAME:
299         case SUMO_ATTR_FILE:
300         case SUMO_ATTR_VTYPES:
301         case SUMO_ATTR_FRIENDLY_POS:
302         case GNE_ATTR_BLOCK_MOVEMENT:
303         case GNE_ATTR_SELECTED:
304         case GNE_ATTR_GENERIC:
305             undoList->p_add(new GNEChange_Attribute(this, myViewNet->getNet(), key, value));
306             break;
307         default:
308             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
309     }
310 
311 }
312 
313 
314 bool
isValid(SumoXMLAttr key,const std::string & value)315 GNEDetectorE1::isValid(SumoXMLAttr key, const std::string& value) {
316     switch (key) {
317         case SUMO_ATTR_ID:
318             return isValidDetectorID(value);
319         case SUMO_ATTR_LANE:
320             if (myViewNet->getNet()->retrieveLane(value, false) != nullptr) {
321                 return true;
322             } else {
323                 return false;
324             }
325         case SUMO_ATTR_POSITION:
326             return canParse<double>(value) && fabs(parse<double>(value)) < getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength();
327         case SUMO_ATTR_FREQUENCY:
328             return (canParse<double>(value) && (parse<double>(value) >= 0));
329         case SUMO_ATTR_NAME:
330             return SUMOXMLDefinitions::isValidAttribute(value);
331         case SUMO_ATTR_FILE:
332             return SUMOXMLDefinitions::isValidFilename(value);
333         case SUMO_ATTR_VTYPES:
334             if (value.empty()) {
335                 return true;
336             } else {
337                 return SUMOXMLDefinitions::isValidListOfTypeID(value);
338             }
339         case SUMO_ATTR_FRIENDLY_POS:
340             return canParse<bool>(value);
341         case GNE_ATTR_BLOCK_MOVEMENT:
342             return canParse<bool>(value);
343         case GNE_ATTR_SELECTED:
344             return canParse<bool>(value);
345         case GNE_ATTR_GENERIC:
346             return isGenericParametersValid(value);
347         default:
348             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
349     }
350 }
351 
352 // ===========================================================================
353 // private
354 // ===========================================================================
355 
356 void
setAttribute(SumoXMLAttr key,const std::string & value)357 GNEDetectorE1::setAttribute(SumoXMLAttr key, const std::string& value) {
358     switch (key) {
359         case SUMO_ATTR_ID:
360             changeAdditionalID(value);
361             break;
362         case SUMO_ATTR_LANE:
363             changeLaneParents(this, value);
364             break;
365         case SUMO_ATTR_POSITION:
366             myPositionOverLane = parse<double>(value);
367             break;
368         case SUMO_ATTR_FREQUENCY:
369             myFreq = parse<double>(value);
370             break;
371         case SUMO_ATTR_FILE:
372             myFilename = value;
373             break;
374         case SUMO_ATTR_NAME:
375             myAdditionalName = value;
376             break;
377         case SUMO_ATTR_VTYPES:
378             myVehicleTypes = value;
379             break;
380         case SUMO_ATTR_FRIENDLY_POS:
381             myFriendlyPosition = parse<bool>(value);
382             break;
383         case GNE_ATTR_BLOCK_MOVEMENT:
384             myBlockMovement = parse<bool>(value);
385             break;
386         case GNE_ATTR_SELECTED:
387             if (parse<bool>(value)) {
388                 selectAttributeCarrier();
389             } else {
390                 unselectAttributeCarrier();
391             }
392             break;
393         case GNE_ATTR_GENERIC:
394             setGenericParametersStr(value);
395             break;
396         default:
397             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
398     }
399     // check if updated attribute requieres update geometry
400     if (myTagProperty.hasAttribute(key) && myTagProperty.getAttributeProperties(key).requiereUpdateGeometry()) {
401         updateGeometry(true);
402     }
403 }
404 
405 /****************************************************************************/
406