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    GNEDetectorE2.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/changes/GNEChange_Connection.h>
27 #include <netedit/netelements/GNEConnection.h>
28 #include <netedit/netelements/GNEEdge.h>
29 #include <netedit/netelements/GNELane.h>
30 #include <utils/gui/div/GLHelper.h>
31 #include <utils/gui/globjects/GLIncludes.h>
32 
33 #include "GNEDetectorE2.h"
34 #include "GNEAdditionalHandler.h"
35 
36 
37 // ===========================================================================
38 // member method definitions
39 // ===========================================================================
40 
GNEDetectorE2(const std::string & id,GNELane * lane,GNEViewNet * viewNet,double pos,double length,double freq,const std::string & filename,const std::string & vehicleTypes,const std::string & name,const double timeThreshold,double speedThreshold,double jamThreshold,bool friendlyPos,bool blockMovement)41 GNEDetectorE2::GNEDetectorE2(const std::string& id, GNELane* lane, GNEViewNet* viewNet, double pos, double length, double freq, const std::string& filename, const std::string& vehicleTypes,
42                              const std::string& name, const double timeThreshold, double speedThreshold, double jamThreshold, bool friendlyPos, bool blockMovement) :
43     GNEDetector(id, viewNet, GLO_E2DETECTOR, SUMO_TAG_E2DETECTOR, pos, freq, filename, vehicleTypes, name, friendlyPos, blockMovement, {
44     lane
45 }),
46 myLength(length),
47 myEndPositionOverLane(0.),
48 myTimeThreshold(timeThreshold),
49 mySpeedThreshold(speedThreshold),
50 myJamThreshold(jamThreshold),
51 myE2valid(true) {
52 }
53 
54 
GNEDetectorE2(const std::string & id,std::vector<GNELane * > lanes,GNEViewNet * viewNet,double pos,double endPos,double freq,const std::string & filename,const std::string & vehicleTypes,const std::string & name,const double timeThreshold,double speedThreshold,double jamThreshold,bool friendlyPos,bool blockMovement)55 GNEDetectorE2::GNEDetectorE2(const std::string& id, std::vector<GNELane*> lanes, GNEViewNet* viewNet, double pos, double endPos, double freq, const std::string& filename, const std::string& vehicleTypes,
56                              const std::string& name, const double timeThreshold, double speedThreshold, double jamThreshold, bool friendlyPos, bool blockMovement) :
57     GNEDetector(id, viewNet, GLO_E2DETECTOR, SUMO_TAG_E2DETECTOR_MULTILANE, pos, freq, filename, vehicleTypes, name, friendlyPos, blockMovement, lanes),
58     myEndPositionOverLane(endPos),
59     myTimeThreshold(timeThreshold),
60     mySpeedThreshold(speedThreshold),
61     myJamThreshold(jamThreshold),
62     myE2valid(true) {
63 }
64 
65 
~GNEDetectorE2()66 GNEDetectorE2::~GNEDetectorE2() {
67 }
68 
69 
70 bool
isAdditionalValid() const71 GNEDetectorE2::isAdditionalValid() const {
72     if (getLaneParents().size() == 1) {
73         // with friendly position enabled position are "always fixed"
74         if (myFriendlyPosition) {
75             return true;
76         } else {
77             return (myPositionOverLane >= 0) && ((myPositionOverLane + myLength) <= getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength());
78         }
79     } else {
80         // first check if there is connection between all consecutive lanes
81         if (myE2valid) {
82             // with friendly position enabled position are "always fixed"
83             if (myFriendlyPosition) {
84                 return true;
85             } else {
86                 return (myPositionOverLane >= 0) && ((myPositionOverLane) <= getLaneParents().back()->getParentEdge().getNBEdge()->getFinalLength() &&
87                                                      myEndPositionOverLane >= 0) && ((myEndPositionOverLane) <= getLaneParents().back()->getParentEdge().getNBEdge()->getFinalLength());
88             }
89         } else {
90             return false;
91         }
92     }
93 }
94 
95 
96 std::string
getAdditionalProblem() const97 GNEDetectorE2::getAdditionalProblem() const {
98     // declare variable for error position
99     std::string errorFirstLanePosition, separator, errorLastLanePosition;
100     if (getLaneParents().size() == 1) {
101         // check positions over lane
102         if (myPositionOverLane < 0) {
103             errorFirstLanePosition = (toString(SUMO_ATTR_POSITION) + " < 0");
104         }
105         if (myPositionOverLane > getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength()) {
106             errorFirstLanePosition = (toString(SUMO_ATTR_POSITION) + " > lanes's length");
107         }
108         if ((myPositionOverLane + myLength) > getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength()) {
109             errorFirstLanePosition = (toString(SUMO_ATTR_POSITION) + " + " + toString(SUMO_ATTR_LENGTH) + " > lanes's length");
110         }
111     } else {
112         if (myE2valid) {
113             // check positions over first lane
114             if (myPositionOverLane < 0) {
115                 errorFirstLanePosition = (toString(SUMO_ATTR_POSITION) + " < 0");
116             }
117             if (myPositionOverLane > getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength()) {
118                 errorFirstLanePosition = (toString(SUMO_ATTR_POSITION) + " > lanes's length");
119             }
120             // check positions over last lane
121             if (myEndPositionOverLane < 0) {
122                 errorLastLanePosition = (toString(SUMO_ATTR_ENDPOS) + " < 0");
123             }
124             if (myEndPositionOverLane > getLaneParents().back()->getParentEdge().getNBEdge()->getFinalLength()) {
125                 errorLastLanePosition = (toString(SUMO_ATTR_ENDPOS) + " > lanes's length");
126             }
127         } else {
128             errorFirstLanePosition = "lanes aren't consecutives";
129         }
130     }
131     // check separator
132     if ((errorFirstLanePosition.size() > 0) && (errorLastLanePosition.size() > 0)) {
133         separator = " and ";
134     }
135     // return error message
136     return errorFirstLanePosition + separator + errorLastLanePosition;
137 }
138 
139 
140 void
fixAdditionalProblem()141 GNEDetectorE2::fixAdditionalProblem() {
142     if (getLaneParents().size() == 1) {
143         // obtain position and lenght
144         double newPositionOverLane = myPositionOverLane;
145         double newLength = myLength;
146         // fix pos and lenght using fixE2DetectorPosition
147         GNEAdditionalHandler::fixE2DetectorPosition(newPositionOverLane, newLength, getLaneParents().at(0)->getParentEdge().getNBEdge()->getFinalLength(), true);
148         // set new position and length
149         setAttribute(SUMO_ATTR_POSITION, toString(newPositionOverLane), myViewNet->getUndoList());
150         setAttribute(SUMO_ATTR_LENGTH, toString(myLength), myViewNet->getUndoList());
151     } else {
152         if (!myE2valid) {
153             // build connections between all consecutive lanes
154             bool foundConnection = true;
155             int i = 0;
156             // iterate over all lanes, and stop if myE2valid is false
157             while (i < ((int)getLaneParents().size() - 1)) {
158                 // change foundConnection to false
159                 foundConnection = false;
160                 // if a connection betwen "from" lane and "to" lane of connection is found, change myE2valid to true again
161                 for (auto j : getLaneParents().at(i)->getParentEdge().getGNEConnections()) {
162                     if (j->getLaneFrom() == getLaneParents().at(i) && j->getLaneTo() == getLaneParents().at(i + 1)) {
163                         foundConnection = true;
164                     }
165                 }
166                 // if connection wasn't found
167                 if (!foundConnection) {
168                     // create new connection manually
169                     NBEdge::Connection newCon(getLaneParents().at(i)->getIndex(), getLaneParents().at(i + 1)->getParentEdge().getNBEdge(), getLaneParents().at(i + 1)->getIndex());
170                     // allow to undo creation of new lane
171                     myViewNet->getUndoList()->add(new GNEChange_Connection(&getLaneParents().at(i)->getParentEdge(), newCon, false, true), true);
172                 }
173                 // update lane iterator
174                 i++;
175             }
176         } else {
177             // declare new position
178             double newPositionOverLane = myPositionOverLane;
179             // fix pos and lenght  checkAndFixDetectorPosition
180             GNEAdditionalHandler::checkAndFixDetectorPosition(newPositionOverLane, getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength(), true);
181             // set new position
182             setAttribute(SUMO_ATTR_POSITION, toString(newPositionOverLane), myViewNet->getUndoList());
183             // declare new end position
184             double newEndPositionOverLane = myEndPositionOverLane;
185             // fix pos and lenght  checkAndFixDetectorPosition
186             GNEAdditionalHandler::checkAndFixDetectorPosition(newEndPositionOverLane, getLaneParents().back()->getParentEdge().getNBEdge()->getFinalLength(), true);
187             // set new position
188             setAttribute(SUMO_ATTR_ENDPOS, toString(newEndPositionOverLane), myViewNet->getUndoList());
189         }
190     }
191 }
192 
193 
194 void
moveGeometry(const Position & offset)195 GNEDetectorE2::moveGeometry(const Position& offset) {
196     // Calculate new position using old position
197     Position newPosition = myMove.originalViewPosition;
198     newPosition.add(offset);
199     // filtern position using snap to active grid
200     newPosition = myViewNet->snapToActiveGrid(newPosition);
201     double offsetLane = getLaneParents().front()->getShape().nearest_offset_to_point2D(newPosition, false) - getLaneParents().front()->getShape().nearest_offset_to_point2D(myMove.originalViewPosition, false);
202     // move geometry depending of number of lanes
203     if (getLaneParents().size() == 1) {
204         // calculate new position over lane
205         double newPositionOverLane = parse<double>(myMove.firstOriginalLanePosition) + offsetLane;
206         // obtain lane length
207         double laneLenght = getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength() * getLane()->getLengthGeometryFactor();
208         if (newPositionOverLane < 0) {
209             myPositionOverLane = 0;
210         } else if (newPositionOverLane + myLength > laneLenght) {
211             myPositionOverLane = laneLenght - myLength;
212         } else {
213             myPositionOverLane = newPositionOverLane;
214         }
215     } else {
216         // calculate new start and end positions
217         double newStartPosition = parse<double>(myMove.firstOriginalLanePosition) + offsetLane;
218         double newEndPosition = parse<double>(myMove.secondOriginalPosition) + offsetLane;
219         // change start and end position of E2 detector ONLY if both extremes aren't overpassed
220         if ((newStartPosition >= 0) && (newStartPosition <= getLaneParents().front()->getLaneShapeLength()) &&
221                 (newEndPosition >= 0) && (newEndPosition <= getLaneParents().back()->getLaneShapeLength())) {
222             myPositionOverLane = newStartPosition;
223             myEndPositionOverLane = newEndPosition;
224         }
225     }
226     // Update geometry
227     updateGeometry(false);
228 }
229 
230 
231 void
commitGeometryMoving(GNEUndoList * undoList)232 GNEDetectorE2::commitGeometryMoving(GNEUndoList* undoList) {
233     // commit geometry moving depending of number of lanes
234     if (getLaneParents().size() == 1) {
235         // commit new position allowing undo/redo
236         undoList->p_begin("position of " + getTagStr());
237         undoList->p_add(new GNEChange_Attribute(this, myViewNet->getNet(), SUMO_ATTR_POSITION, toString(myPositionOverLane), true, myMove.firstOriginalLanePosition));
238         undoList->p_end();
239     } else {
240         undoList->p_begin("position of " + getTagStr());
241         undoList->p_add(new GNEChange_Attribute(this, myViewNet->getNet(), SUMO_ATTR_POSITION, toString(myPositionOverLane), true, myMove.firstOriginalLanePosition));
242         undoList->p_add(new GNEChange_Attribute(this, myViewNet->getNet(), SUMO_ATTR_ENDPOS, toString(myEndPositionOverLane), true, myMove.secondOriginalPosition));
243         undoList->p_end();
244     }
245 }
246 
247 
248 void
updateGeometry(bool updateGrid)249 GNEDetectorE2::updateGeometry(bool updateGrid) {
250     // first check if object has to be removed from grid (SUMOTree)
251     if (updateGrid) {
252         myViewNet->getNet()->removeGLObjectFromGrid(this);
253     }
254 
255     // Clear all containers
256     myGeometry.clearGeometry();
257 
258     // declare variables for start and end positions
259     double startPosFixed, endPosFixed;
260 
261     // calculate start and end positions dependin of number of lanes
262     if (getLaneParents().size() == 1) {
263         // set shape lane as detector shape
264         myGeometry.shape = getLaneParents().front()->getShape();
265 
266         // set start position
267         if (myPositionOverLane < 0) {
268             startPosFixed = 0;
269         } else if (myPositionOverLane > getLaneParents().back()->getParentEdge().getNBEdge()->getFinalLength()) {
270             startPosFixed = getLaneParents().back()->getParentEdge().getNBEdge()->getFinalLength();
271         } else {
272             startPosFixed = myPositionOverLane;
273         }
274 
275         // set end position
276         if ((myPositionOverLane + myLength) < 0) {
277             endPosFixed = 0;
278         } else if ((myPositionOverLane + myLength) > getLaneParents().back()->getParentEdge().getNBEdge()->getFinalLength()) {
279             endPosFixed = getLaneParents().back()->getParentEdge().getNBEdge()->getFinalLength();
280         } else {
281             endPosFixed = (myPositionOverLane + myLength);
282         }
283 
284         // Cut shape using as delimitators fixed start position and fixed end position
285         myGeometry.shape = myGeometry.shape.getSubpart(startPosFixed * getLaneParents().front()->getLengthGeometryFactor(), endPosFixed * getLaneParents().back()->getLengthGeometryFactor());
286 
287         // Get calculate lenghts and rotations
288         myGeometry.calculateShapeRotationsAndLengths();
289 
290         // Set block icon position
291         myBlockIcon.position = myGeometry.shape.getLineCenter();
292 
293     } else if (getLaneParents().size() > 1) {
294         // start with the first lane shape
295         myGeometry.multiShape.push_back(getLaneParents().front()->getShape());
296 
297         // set start position
298         if (myPositionOverLane < 0) {
299             startPosFixed = 0;
300         } else if (myPositionOverLane > getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength()) {
301             startPosFixed = getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength();
302         } else {
303             startPosFixed = myPositionOverLane;
304         }
305         // Cut shape using as delimitators fixed start position and fixed end position
306         myGeometry.multiShape[0] = myGeometry.multiShape[0].getSubpart(startPosFixed * getLaneParents().front()->getLengthGeometryFactor(), getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength());
307 
308         // declare last shape
309         PositionVector lastShape = getLaneParents().back()->getShape();
310 
311         // set end position
312         if (myEndPositionOverLane < 0) {
313             endPosFixed = 0;
314         } else if (myEndPositionOverLane > getLaneParents().back()->getParentEdge().getNBEdge()->getFinalLength()) {
315             endPosFixed = getLaneParents().back()->getParentEdge().getNBEdge()->getFinalLength();
316         } else {
317             endPosFixed = myEndPositionOverLane;
318         }
319 
320         // Cut shape using as delimitators fixed start position and fixed end position
321         lastShape = lastShape.getSubpart(0, endPosFixed * getLaneParents().back()->getLengthGeometryFactor());
322 
323         // add first shape connection (if exist, in other case leave it empty)
324         myGeometry.multiShape.push_back(PositionVector{getLaneParents().at(0)->getShape().back(), getLaneParents().at(1)->getShape().front()});
325         for (auto j : getLaneParents().at(0)->getParentEdge().getGNEConnections()) {
326             if (j->getLaneTo() == getLaneParents().at(1)) {
327                 myGeometry.multiShape.back() = j->getShape();
328             }
329         }
330 
331         // append shapes of intermediate lanes AND connections (if exist)
332         for (int i = 1; i < ((int)getLaneParents().size() - 1); i++) {
333             // add lane shape
334             myGeometry.multiShape.push_back(getLaneParents().at(i)->getShape());
335             // add empty shape for connection
336             myGeometry.multiShape.push_back(PositionVector{getLaneParents().at(i)->getShape().back(), getLaneParents().at(i + 1)->getShape().front()});
337             // set connection shape (if exist). In other case, insert an empty shape
338             for (auto j : getLaneParents().at(i)->getParentEdge().getGNEConnections()) {
339                 if (j->getLaneTo() == getLaneParents().at(i + 1)) {
340                     myGeometry.multiShape.back() = j->getShape();
341                 }
342             }
343         }
344 
345         // append last shape
346         myGeometry.multiShape.push_back(lastShape);
347 
348         // calculate multi shape rotation and lengths
349         myGeometry.calculateMultiShapeRotationsAndLengths();
350 
351         // calculate unified shape
352         myGeometry.calculateMultiShapeUnified();
353 
354         // Set block icon position
355         myBlockIcon.position = myGeometry.multiShape.front().getLineCenter();
356 
357         // check integrity
358         checkE2MultilaneIntegrity();
359     }
360 
361     // Set offset of the block icon
362     myBlockIcon.offset = Position(-0.75, 0);
363 
364     // Set block icon rotation, and using their rotation for draw logo
365     myBlockIcon.setRotation(getLaneParents().front());
366 
367     // last step is to check if object has to be added into grid (SUMOTree) again
368     if (updateGrid) {
369         myViewNet->getNet()->addGLObjectIntoGrid(this);
370     }
371 }
372 
373 
374 double
getLength() const375 GNEDetectorE2::getLength() const {
376     return myLength;
377 }
378 
379 
380 void
checkE2MultilaneIntegrity()381 GNEDetectorE2::checkE2MultilaneIntegrity() {
382     // we assume that E2 is valid
383     myE2valid = true;
384     int i = 0;
385     // iterate over all lanes, and stop if myE2valid is false
386     while (i < ((int)getLaneParents().size() - 1) && myE2valid) {
387         // set myE2valid to false
388         myE2valid = false;
389         // if a connection betwen "from" lane and "to" lane of connection is found, change myE2valid to true again
390         for (auto j : getLaneParents().at(i)->getParentEdge().getGNEConnections()) {
391             if (j->getLaneFrom() == getLaneParents().at(i) && j->getLaneTo() == getLaneParents().at(i + 1)) {
392                 myE2valid = true;
393             }
394         }
395         // update iterator
396         i++;
397     }
398 }
399 
400 
401 void
drawGL(const GUIVisualizationSettings & s) const402 GNEDetectorE2::drawGL(const GUIVisualizationSettings& s) const {
403     // Start drawing adding an gl identificator
404     glPushName(getGlID());
405 
406     // Add a draw matrix
407     glPushMatrix();
408 
409     // Start with the drawing of the area traslating matrix to origin
410     glTranslated(0, 0, getType());
411 
412     // Set color of the base
413     if (drawUsingSelectColor()) {
414         GLHelper::setColor(s.selectedAdditionalColor);
415     } else {
416         // set color depending if is or isn't valid
417         if (myE2valid) {
418             GLHelper::setColor(s.SUMO_color_E2);
419         } else {
420             GLHelper::setColor(RGBColor::RED);
421         }
422     }
423 
424     // Obtain exaggeration of the draw
425     const double exaggeration = s.addSize.getExaggeration(s, this);
426 
427     // check if we have to drawn a E2 single lane or a E2 multiLane
428     if (myGeometry.shape.size() > 0) {
429         // Draw the area using shape, shapeRotations, shapeLengths and value of exaggeration
430         GLHelper::drawBoxLines(myGeometry.shape, myGeometry.shapeRotations, myGeometry.shapeLengths, exaggeration);
431     } else {
432         // iterate over multishapes
433         for (int i = 0; i < (int)myGeometry.multiShape.size(); i++) {
434             // don't draw shapes over connections if "show connections" is enabled
435             if (!myViewNet->getViewOptions().showConnections() || (i % 2 == 0)) {
436                 GLHelper::drawBoxLines(myGeometry.multiShape.at(i), myGeometry.multiShapeRotations.at(i), myGeometry.multiShapeLengths.at(i), exaggeration);
437             }
438         }
439     }
440 
441     // Pop last matrix
442     glPopMatrix();
443 
444     // Check if the distance is enougth to draw details and isn't being drawn for selecting
445     if ((s.scale * exaggeration >= 10) && !s.drawForSelecting) {
446         // draw logo depending if this is an Multilane E2 detector
447         if (myTagProperty.getTag() == SUMO_TAG_E2DETECTOR) {
448             // Push matrix
449             glPushMatrix();
450             // Traslate to center of detector
451             glTranslated(myGeometry.shape.getLineCenter().x(), myGeometry.shape.getLineCenter().y(), getType() + 0.1);
452             // Rotate depending of myBlockIcon.rotation
453             glRotated(myBlockIcon.rotation, 0, 0, -1);
454             //move to logo position
455             glTranslated(-0.75, 0, 0);
456             // draw E2 logo
457             if (drawUsingSelectColor()) {
458                 GLHelper::drawText("E2", Position(), .1, 1.5, s.selectionColor);
459             } else {
460                 GLHelper::drawText("E2", Position(), .1, 1.5, RGBColor::BLACK);
461             }
462         } else {
463             // Push matrix
464             glPushMatrix();
465             // Traslate to center of detector
466             glTranslated(myBlockIcon.position.x(), myBlockIcon.position.y(), getType() + 0.1);
467             // Rotate depending of myBlockIcon.rotation
468             glRotated(myBlockIcon.rotation, 0, 0, -1);
469             //move to logo position
470             glTranslated(-1.5, 0, 0);
471             // draw E2 logo
472             if (drawUsingSelectColor()) {
473                 GLHelper::drawText("E2", Position(), .1, 1.5, s.selectionColor);
474             } else {
475                 GLHelper::drawText("E2", Position(), .1, 1.5, RGBColor::BLACK);
476             }
477             //move to logo position
478             glTranslated(1.2, 0, 0);
479             // Rotate depending of myBlockIcon.rotation
480             glRotated(90, 0, 0, 1);
481             if (drawUsingSelectColor()) {
482                 GLHelper::drawText("multi", Position(), .1, 0.9, s.selectedAdditionalColor);
483             } else {
484                 GLHelper::drawText("multi", Position(), .1, 0.9, RGBColor::BLACK);
485             }
486         }
487         // pop matrix
488         glPopMatrix();
489 
490         // Show Lock icon depending of the Edit mode
491         myBlockIcon.draw();
492     }
493 
494     // Draw name if isn't being drawn for selecting
495     if (!s.drawForSelecting) {
496         drawName(getCenteringBoundary().getCenter(), s.scale, s.addName);
497     }
498     // check if dotted contour has to be drawn
499     if (!s.drawForSelecting && (myViewNet->getDottedAC() == this)) {
500         if (myGeometry.shape.size() > 0) {
501             GLHelper::drawShapeDottedContour(getType(), myGeometry.shape, exaggeration);
502         } else {
503             GLHelper::drawShapeDottedContour(getType(), myGeometry.multiShapeUnified, exaggeration);
504         }
505     }
506     // Pop name
507     glPopName();
508 }
509 
510 
511 std::string
getAttribute(SumoXMLAttr key) const512 GNEDetectorE2::getAttribute(SumoXMLAttr key) const {
513     switch (key) {
514         case SUMO_ATTR_ID:
515             return getAdditionalID();
516         case SUMO_ATTR_LANE:
517         case SUMO_ATTR_LANES:
518             return parseIDs(getLaneParents());
519         case SUMO_ATTR_POSITION:
520             return toString(myPositionOverLane);
521         case SUMO_ATTR_ENDPOS:
522             return toString(myEndPositionOverLane);
523         case SUMO_ATTR_FREQUENCY:
524             return toString(myFreq);
525         case SUMO_ATTR_LENGTH:
526             return toString(myLength);
527         case SUMO_ATTR_NAME:
528             return myAdditionalName;
529         case SUMO_ATTR_FILE:
530             return myFilename;
531         case SUMO_ATTR_VTYPES:
532             return myVehicleTypes;
533         case SUMO_ATTR_HALTING_TIME_THRESHOLD:
534             return toString(myTimeThreshold);
535         case SUMO_ATTR_HALTING_SPEED_THRESHOLD:
536             return toString(mySpeedThreshold);
537         case SUMO_ATTR_JAM_DIST_THRESHOLD:
538             return toString(myJamThreshold);
539         case SUMO_ATTR_FRIENDLY_POS:
540             return toString(myFriendlyPosition);
541         case GNE_ATTR_BLOCK_MOVEMENT:
542             return toString(myBlockMovement);
543         case GNE_ATTR_SELECTED:
544             return toString(isAttributeCarrierSelected());
545         case GNE_ATTR_GENERIC:
546             return getGenericParametersStr();
547         default:
548             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
549     }
550 }
551 
552 
553 void
setAttribute(SumoXMLAttr key,const std::string & value,GNEUndoList * undoList)554 GNEDetectorE2::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {
555     if (value == getAttribute(key)) {
556         return; //avoid needless changes, later logic relies on the fact that attributes have changed
557     }
558     switch (key) {
559         case SUMO_ATTR_ID:
560         case SUMO_ATTR_LANE:
561         case SUMO_ATTR_LANES:
562         case SUMO_ATTR_POSITION:
563         case SUMO_ATTR_ENDPOS:
564         case SUMO_ATTR_FREQUENCY:
565         case SUMO_ATTR_LENGTH:
566         case SUMO_ATTR_NAME:
567         case SUMO_ATTR_FILE:
568         case SUMO_ATTR_VTYPES:
569         case SUMO_ATTR_HALTING_TIME_THRESHOLD:
570         case SUMO_ATTR_HALTING_SPEED_THRESHOLD:
571         case SUMO_ATTR_JAM_DIST_THRESHOLD:
572         case SUMO_ATTR_FRIENDLY_POS:
573         case GNE_ATTR_BLOCK_MOVEMENT:
574         case GNE_ATTR_SELECTED:
575         case GNE_ATTR_GENERIC:
576             undoList->p_add(new GNEChange_Attribute(this, myViewNet->getNet(), key, value));
577             break;
578         default:
579             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
580     }
581 }
582 
583 
584 bool
isValid(SumoXMLAttr key,const std::string & value)585 GNEDetectorE2::isValid(SumoXMLAttr key, const std::string& value) {
586     switch (key) {
587         case SUMO_ATTR_ID:
588             return isValidDetectorID(value);
589         case SUMO_ATTR_LANE:
590             if (value.empty()) {
591                 return false;
592             } else {
593                 return canParse<std::vector<GNELane*> >(myViewNet->getNet(), value, false);
594             }
595         case SUMO_ATTR_LANES:
596             if (value.empty()) {
597                 return false;
598             } else if (canParse<std::vector<GNELane*> >(myViewNet->getNet(), value, false)) {
599                 // check if lanes are consecutives
600                 return lanesConsecutives(parse<std::vector<GNELane*> >(myViewNet->getNet(), value));
601             } else {
602                 return false;
603             }
604         case SUMO_ATTR_POSITION:
605             return canParse<double>(value);
606         case SUMO_ATTR_ENDPOS:
607             return canParse<double>(value);
608         case SUMO_ATTR_FREQUENCY:
609             return (canParse<double>(value) && (parse<double>(value) >= 0));
610         case SUMO_ATTR_LENGTH:
611             return (canParse<double>(value) && (parse<double>(value) >= 0));
612         case SUMO_ATTR_NAME:
613             return SUMOXMLDefinitions::isValidAttribute(value);
614         case SUMO_ATTR_FILE:
615             return SUMOXMLDefinitions::isValidFilename(value);
616         case SUMO_ATTR_VTYPES:
617             if (value.empty()) {
618                 return true;
619             } else {
620                 return SUMOXMLDefinitions::isValidListOfTypeID(value);
621             }
622         case SUMO_ATTR_HALTING_TIME_THRESHOLD:
623             return (canParse<double>(value) && (parse<double>(value) >= 0));
624         case SUMO_ATTR_HALTING_SPEED_THRESHOLD:
625             return (canParse<double>(value) && (parse<double>(value) >= 0));
626         case SUMO_ATTR_JAM_DIST_THRESHOLD:
627             return (canParse<double>(value) && (parse<double>(value) >= 0));
628         case SUMO_ATTR_FRIENDLY_POS:
629             return canParse<bool>(value);
630         case GNE_ATTR_BLOCK_MOVEMENT:
631             return canParse<bool>(value);
632         case GNE_ATTR_SELECTED:
633             return canParse<bool>(value);
634         case GNE_ATTR_GENERIC:
635             return isGenericParametersValid(value);
636         default:
637             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
638     }
639 }
640 
641 // ===========================================================================
642 // private
643 // ===========================================================================
644 
645 void
setAttribute(SumoXMLAttr key,const std::string & value)646 GNEDetectorE2::setAttribute(SumoXMLAttr key, const std::string& value) {
647     switch (key) {
648         case SUMO_ATTR_ID:
649             changeAdditionalID(value);
650             break;
651         case SUMO_ATTR_LANE:
652         case SUMO_ATTR_LANES:
653             changeLaneParents(this, value);
654             checkE2MultilaneIntegrity();
655             break;
656         case SUMO_ATTR_POSITION:
657             myPositionOverLane = parse<double>(value);
658             break;
659         case SUMO_ATTR_ENDPOS:
660             myEndPositionOverLane = parse<double>(value);
661             break;
662         case SUMO_ATTR_FREQUENCY:
663             myFreq = parse<double>(value);
664             break;
665         case SUMO_ATTR_LENGTH:
666             myLength = parse<double>(value);
667             break;
668         case SUMO_ATTR_NAME:
669             myAdditionalName = value;
670             break;
671         case SUMO_ATTR_FILE:
672             myFilename = value;
673             break;
674         case SUMO_ATTR_VTYPES:
675             myVehicleTypes = value;
676             break;
677         case SUMO_ATTR_HALTING_TIME_THRESHOLD:
678             myTimeThreshold = parse<double>(value);
679             break;
680         case SUMO_ATTR_HALTING_SPEED_THRESHOLD:
681             mySpeedThreshold = parse<double>(value);
682             break;
683         case SUMO_ATTR_JAM_DIST_THRESHOLD:
684             myJamThreshold = parse<double>(value);
685             break;
686         case SUMO_ATTR_FRIENDLY_POS:
687             myFriendlyPosition = parse<bool>(value);
688             break;
689         case GNE_ATTR_BLOCK_MOVEMENT:
690             myBlockMovement = parse<bool>(value);
691             break;
692         case GNE_ATTR_SELECTED:
693             if (parse<bool>(value)) {
694                 selectAttributeCarrier();
695             } else {
696                 unselectAttributeCarrier();
697             }
698             break;
699         case GNE_ATTR_GENERIC:
700             setGenericParametersStr(value);
701             break;
702         default:
703             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
704     }
705     // check if updated attribute requieres update geometry
706     if (myTagProperty.hasAttribute(key) && myTagProperty.getAttributeProperties(key).requiereUpdateGeometry()) {
707         updateGeometry(true);
708     }
709 }
710 
711 /****************************************************************************/
712