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    GNEAdditional.cpp
11 /// @author  Pablo Alvarez Lopez
12 /// @date    Dec 2015
13 /// @version $Id$
14 ///
15 // A abstract class for representation of additional elements
16 /****************************************************************************/
17 
18 // ===========================================================================
19 // included modules
20 // ===========================================================================
21 #include <config.h>
22 
23 #include <netedit/GNENet.h>
24 #include <netedit/GNEViewNet.h>
25 #include <netedit/GNEViewParent.h>
26 #include <netedit/frames/GNESelectorFrame.h>
27 #include <netedit/netelements/GNEEdge.h>
28 #include <netedit/netelements/GNEJunction.h>
29 #include <netedit/netelements/GNELane.h>
30 #include <netedit/demandelements/GNEDemandElement.h>
31 #include <utils/common/StringTokenizer.h>
32 #include <utils/gui/div/GLHelper.h>
33 #include <utils/gui/div/GUIGlobalSelection.h>
34 #include <utils/gui/div/GUIParameterTableWindow.h>
35 #include <utils/gui/globjects/GUIGLObjectPopupMenu.h>
36 #include <utils/gui/images/GUITextureSubSys.h>
37 #include <utils/options/OptionsCont.h>
38 #include <utils/gui/globjects/GLIncludes.h>
39 
40 #include "GNEAdditional.h"
41 
42 // ===========================================================================
43 // member method definitions
44 // ===========================================================================
45 
46 // ---------------------------------------------------------------------------
47 // GNEAdditional::AdditionalGeometry - methods
48 // ---------------------------------------------------------------------------
49 
AdditionalGeometry()50 GNEAdditional::AdditionalGeometry::AdditionalGeometry() {}
51 
52 
53 void
clearGeometry()54 GNEAdditional::AdditionalGeometry::clearGeometry() {
55     shape.clear();
56     multiShape.clear();
57     shapeRotations.clear();
58     shapeLengths.clear();
59     multiShapeRotations.clear();
60     multiShapeLengths.clear();
61     multiShapeUnified.clear();
62 }
63 
64 
65 void
calculateMultiShapeUnified()66 GNEAdditional::AdditionalGeometry::calculateMultiShapeUnified() {
67     // merge all multishape parts in a single shape
68     for (auto i : multiShape) {
69         multiShapeUnified.append(i);
70     }
71 }
72 
73 
74 void
calculateShapeRotationsAndLengths()75 GNEAdditional::AdditionalGeometry::calculateShapeRotationsAndLengths() {
76     // Get number of parts of the shape
77     int numberOfSegments = (int)shape.size() - 1;
78     // If number of segments is more than 0
79     if (numberOfSegments >= 0) {
80         // Reserve memory (To improve efficiency)
81         shapeRotations.reserve(numberOfSegments);
82         shapeLengths.reserve(numberOfSegments);
83         // For every part of the shape
84         for (int i = 0; i < numberOfSegments; ++i) {
85             // Obtain first position
86             const Position& f = shape[i];
87             // Obtain next position
88             const Position& s = shape[i + 1];
89             // Save distance between position into myShapeLengths
90             shapeLengths.push_back(f.distanceTo(s));
91             // Save rotation (angle) of the vector constructed by points f and s
92             shapeRotations.push_back((double)atan2((s.x() - f.x()), (f.y() - s.y())) * (double) 180.0 / (double)M_PI);
93         }
94     }
95 }
96 
97 
98 void
calculateMultiShapeRotationsAndLengths()99 GNEAdditional::AdditionalGeometry::calculateMultiShapeRotationsAndLengths() {
100     // Get number of parts of the shape for every part shape
101     std::vector<int> numberOfSegments;
102     for (auto i : multiShape) {
103         // numseg cannot be 0
104         int numSeg = (int)i.size() - 1;
105         numberOfSegments.push_back((numSeg >= 0) ? numSeg : 0);
106         multiShapeRotations.push_back(std::vector<double>());
107         multiShapeLengths.push_back(std::vector<double>());
108     }
109     // If number of segments is more than 0
110     for (int i = 0; i < (int)multiShape.size(); i++) {
111         // Reserve size for every part
112         multiShapeRotations.back().reserve(numberOfSegments.at(i));
113         multiShapeLengths.back().reserve(numberOfSegments.at(i));
114         // iterate over each segment
115         for (int j = 0; j < numberOfSegments.at(i); j++) {
116             // Obtain first position
117             const Position& f = multiShape[i][j];
118             // Obtain next position
119             const Position& s = multiShape[i][j + 1];
120             // Save distance between position into myShapeLengths
121             multiShapeLengths.at(i).push_back(f.distanceTo(s));
122             // Save rotation (angle) of the vector constructed by points f and s
123             multiShapeRotations.at(i).push_back((double)atan2((s.x() - f.x()), (f.y() - s.y())) * (double) 180.0 / (double)M_PI);
124         }
125     }
126 }
127 
128 // ---------------------------------------------------------------------------
129 // GNEAdditional - methods
130 // ---------------------------------------------------------------------------
131 
GNEAdditional(const std::string & id,GNEViewNet * viewNet,GUIGlObjectType type,SumoXMLTag tag,std::string additionalName,bool blockMovement,const std::vector<GNEEdge * > & edgeParents,const std::vector<GNELane * > & laneParents,const std::vector<GNEShape * > & shapeParents,const std::vector<GNEAdditional * > & additionalParents,const std::vector<GNEDemandElement * > & demandElementParents,const std::vector<GNEEdge * > & edgeChilds,const std::vector<GNELane * > & laneChilds,const std::vector<GNEShape * > & shapeChilds,const std::vector<GNEAdditional * > & additionalChilds,const std::vector<GNEDemandElement * > & demandElementChilds)132 GNEAdditional::GNEAdditional(const std::string& id, GNEViewNet* viewNet, GUIGlObjectType type, SumoXMLTag tag, std::string additionalName, bool blockMovement,
133                              const std::vector<GNEEdge*>& edgeParents,
134                              const std::vector<GNELane*>& laneParents,
135                              const std::vector<GNEShape*>& shapeParents,
136                              const std::vector<GNEAdditional*>& additionalParents,
137                              const std::vector<GNEDemandElement*>& demandElementParents,
138                              const std::vector<GNEEdge*>& edgeChilds,
139                              const std::vector<GNELane*>& laneChilds,
140                              const std::vector<GNEShape*>& shapeChilds,
141                              const std::vector<GNEAdditional*>& additionalChilds,
142                              const std::vector<GNEDemandElement*>& demandElementChilds) :
143     GUIGlObject(type, id),
144     GNEAttributeCarrier(tag),
145     Parameterised(),
146     GNEHierarchicalElementParents(this, edgeParents, laneParents, shapeParents, additionalParents, demandElementParents),
147     GNEHierarchicalElementChilds(this, edgeChilds, laneChilds, shapeChilds, additionalChilds, demandElementChilds),
148     myViewNet(viewNet),
149     myAdditionalName(additionalName),
150     myBlockMovement(blockMovement),
151     myBlockIcon(this) {
152 }
153 
154 
GNEAdditional(GNEAdditional * additionalParent,GNEViewNet * viewNet,GUIGlObjectType type,SumoXMLTag tag,std::string additionalName,bool blockMovement,const std::vector<GNEEdge * > & edgeParents,const std::vector<GNELane * > & laneParents,const std::vector<GNEShape * > & shapeParents,const std::vector<GNEAdditional * > & additionalParents,const std::vector<GNEDemandElement * > & demandElementParents,const std::vector<GNEEdge * > & edgeChilds,const std::vector<GNELane * > & laneChilds,const std::vector<GNEShape * > & shapeChilds,const std::vector<GNEAdditional * > & additionalChilds,const std::vector<GNEDemandElement * > & demandElementChilds)155 GNEAdditional::GNEAdditional(GNEAdditional* additionalParent, GNEViewNet* viewNet, GUIGlObjectType type, SumoXMLTag tag, std::string additionalName, bool blockMovement,
156                              const std::vector<GNEEdge*>& edgeParents,
157                              const std::vector<GNELane*>& laneParents,
158                              const std::vector<GNEShape*>& shapeParents,
159                              const std::vector<GNEAdditional*>& additionalParents,
160                              const std::vector<GNEDemandElement*>& demandElementParents,
161                              const std::vector<GNEEdge*>& edgeChilds,
162                              const std::vector<GNELane*>& laneChilds,
163                              const std::vector<GNEShape*>& shapeChilds,
164                              const std::vector<GNEAdditional*>& additionalChilds,
165                              const std::vector<GNEDemandElement*>& demandElementChilds) :
166     GUIGlObject(type, additionalParent->generateChildID(tag)),
167     GNEAttributeCarrier(tag),
168     Parameterised(),
169     GNEHierarchicalElementParents(this, edgeParents, laneParents, shapeParents, additionalParents, demandElementParents),
170     GNEHierarchicalElementChilds(this, edgeChilds, laneChilds, shapeChilds, additionalChilds, demandElementChilds),
171     myViewNet(viewNet),
172     myAdditionalName(additionalName),
173     myBlockMovement(blockMovement),
174     myBlockIcon(this) {
175 }
176 
177 
~GNEAdditional()178 GNEAdditional::~GNEAdditional() {}
179 
180 
181 std::string
generateChildID(SumoXMLTag childTag)182 GNEAdditional::generateChildID(SumoXMLTag childTag) {
183     int counter = (int)getAdditionalChilds().size();
184     while (myViewNet->getNet()->retrieveAdditional(childTag, getID() + toString(childTag) + toString(counter), false) != nullptr) {
185         counter++;
186     }
187     return (getID() + toString(childTag) + toString(counter));
188 }
189 
190 
191 const GNEAdditional::AdditionalGeometry&
getAdditionalGeometry() const192 GNEAdditional::getAdditionalGeometry() const {
193     return myGeometry;
194 }
195 
196 
197 void
writeAdditional(OutputDevice & device) const198 GNEAdditional::writeAdditional(OutputDevice& device) const {
199     // first check if minimum number of childs is correct
200     if ((myTagProperty.hasMinimumNumberOfChilds() || myTagProperty.hasMinimumNumberOfChilds()) && !checkAdditionalChildRestriction()) {
201         WRITE_WARNING(getTagStr() + " with ID='" + getID() + "' cannot be written");
202     } else {
203         // Open Tag or synonym Tag
204         if (myTagProperty.hasTagSynonym()) {
205             device.openTag(myTagProperty.getTagSynonym());
206         } else {
207             device.openTag(myTagProperty.getTag());
208         }
209         // iterate over attributes and write it
210         for (auto i : myTagProperty) {
211             // obtain attribute
212             std::string attribute = getAttribute(i.first);
213             if (i.second.isOptional() && i.second.hasDefaultValue() && !i.second.isCombinable()) {
214                 // Only write attributes with default value if is different from original
215                 if (i.second.getDefaultValue() != attribute) {
216                     // check if attribute must be written using a synonim
217                     if (i.second.hasAttrSynonym()) {
218                         device.writeAttr(i.second.getAttrSynonym(), attribute);
219                     } else {
220                         // SVC permissions uses their own writting function
221                         if (i.second.isSVCPermission()) {
222                             // disallow attribute musn't be written
223                             if (i.first != SUMO_ATTR_DISALLOW) {
224                                 writePermissions(device, parseVehicleClasses(attribute));
225                             }
226                         } else if (myTagProperty.canMaskXYZPositions() && (i.first == SUMO_ATTR_POSITION)) {
227                             // get position attribute and write it separate
228                             Position pos = parse<Position>(getAttribute(SUMO_ATTR_POSITION));
229                             device.writeAttr(SUMO_ATTR_X, toString(pos.x()));
230                             device.writeAttr(SUMO_ATTR_Y, toString(pos.y()));
231                             // write 0 only if is different from 0 (the default value)
232                             if (pos.z() != 0) {
233                                 device.writeAttr(SUMO_ATTR_Z, toString(pos.z()));
234                             }
235                         } else {
236                             device.writeAttr(i.first, attribute);
237                         }
238                     }
239                 }
240             } else {
241                 // Attributes without default values are always writted
242                 if (i.second.hasAttrSynonym()) {
243                     device.writeAttr(i.second.getAttrSynonym(), attribute);
244                 } else {
245                     // SVC permissions uses their own writting function
246                     if (i.second.isSVCPermission()) {
247                         // disallow attribute musn't be written
248                         if (i.first != SUMO_ATTR_DISALLOW) {
249                             writePermissions(device, parseVehicleClasses(attribute));
250                         }
251                     } else if (myTagProperty.canMaskXYZPositions() && (i.first == SUMO_ATTR_POSITION)) {
252                         // get position attribute and write it separate
253                         Position pos = parse<Position>(getAttribute(SUMO_ATTR_POSITION));
254                         device.writeAttr(SUMO_ATTR_X, toString(pos.x()));
255                         device.writeAttr(SUMO_ATTR_Y, toString(pos.y()));
256                         // write 0 only if is different from 0 (the default value)
257                         if (pos.z() != 0) {
258                             device.writeAttr(SUMO_ATTR_Z, toString(pos.z()));
259                         }
260                     } else {
261                         device.writeAttr(i.first, attribute);
262                     }
263                 }
264             }
265         }
266         // iterate over childs and write it in XML (or in a different file)
267         if (myTagProperty.canWriteChildsSeparate() && myTagProperty.hasAttribute(SUMO_ATTR_FILE) && !getAttribute(SUMO_ATTR_FILE).empty()) {
268             // we assume that rerouter values files is placed in the same folder as the additional file
269             OutputDevice& deviceChilds = OutputDevice::getDevice(FileHelpers::getFilePath(OptionsCont::getOptions().getString("additional-files")) + getAttribute(SUMO_ATTR_FILE));
270             deviceChilds.writeXMLHeader("rerouterValue", "additional_file.xsd");
271             // save childs in a different filename
272             for (auto i : getAdditionalChilds()) {
273                 // avoid to write two times additionals that haben two parents (Only write as child of first parent)
274                 if (i->getAdditionalParents().size() < 1) {
275                     i->writeAdditional(deviceChilds);
276                 } else if (myTagProperty.getTag() == i->getTagProperty().getParentTag()) {
277                     i->writeAdditional(deviceChilds);
278                 }
279             }
280             deviceChilds.close();
281         } else {
282             for (auto i : getAdditionalChilds()) {
283                 // avoid to write two times additionals that haben two parents (Only write as child of first parent)
284                 if (i->getAdditionalParents().size() < 2) {
285                     i->writeAdditional(device);
286                 } else if (myTagProperty.getTag() == i->getTagProperty().getParentTag()) {
287                     i->writeAdditional(device);
288                 }
289             }
290         }
291         // save generic parameters (Always after childs to avoid problems with additionals.xsd)
292         writeParams(device);
293         // Close tag
294         device.closeTag();
295     }
296 }
297 
298 
299 bool
isAdditionalValid() const300 GNEAdditional::isAdditionalValid() const {
301     return true;
302 }
303 
304 
305 std::string
getAdditionalProblem() const306 GNEAdditional::getAdditionalProblem() const {
307     return "";
308 }
309 
310 
311 void
fixAdditionalProblem()312 GNEAdditional::fixAdditionalProblem() {
313     throw InvalidArgument(getTagStr() + " cannot fix any problem");
314 }
315 
316 
317 void
openAdditionalDialog()318 GNEAdditional::openAdditionalDialog() {
319     throw InvalidArgument(getTagStr() + " doesn't have an additional dialog");
320 }
321 
322 
323 void
startGeometryMoving()324 GNEAdditional::startGeometryMoving() {
325     // only move if additional is drawable
326     if (myTagProperty.isDrawable()) {
327         // always save original position over view
328         myMove.originalViewPosition = getPositionInView();
329         // check if position over lane or lanes has to be saved
330         if (myTagProperty.hasAttribute(SUMO_ATTR_LANE)) {
331             if (myTagProperty.canMaskStartEndPos()) {
332                 // obtain start and end position
333                 myMove.firstOriginalLanePosition = getAttribute(SUMO_ATTR_STARTPOS);
334                 myMove.secondOriginalPosition = getAttribute(SUMO_ATTR_ENDPOS);
335             } else {
336                 // obtain position attribute
337                 myMove.firstOriginalLanePosition = getAttribute(SUMO_ATTR_POSITION);
338             }
339         } else if (myTagProperty.hasAttribute(SUMO_ATTR_LANES) &&
340                    myTagProperty.hasAttribute(SUMO_ATTR_POSITION) &&
341                    myTagProperty.hasAttribute(SUMO_ATTR_ENDPOS)) {
342             // obtain start and end position
343             myMove.firstOriginalLanePosition = getAttribute(SUMO_ATTR_POSITION);
344             myMove.secondOriginalPosition = getAttribute(SUMO_ATTR_ENDPOS);
345         }
346         // save current centering boundary
347         myMove.movingGeometryBoundary = getCenteringBoundary();
348         // start geometry in all childs
349         for (const auto& i : getDemandElementChilds()) {
350             i->startGeometryMoving();
351         }
352     }
353 }
354 
355 
356 void
endGeometryMoving()357 GNEAdditional::endGeometryMoving() {
358     // check that endGeometryMoving was called only once
359     if (myTagProperty.isDrawable() && myMove.movingGeometryBoundary.isInitialised()) {
360         // Remove object from net
361         myViewNet->getNet()->removeGLObjectFromGrid(this);
362         // reset myMovingGeometryBoundary
363         myMove.movingGeometryBoundary.reset();
364         // update geometry without updating grid
365         updateGeometry(false);
366         // add object into grid again (using the new centering boundary)
367         myViewNet->getNet()->addGLObjectIntoGrid(this);
368         // start geometry in all childs
369         for (const auto& i : getDemandElementChilds()) {
370             i->endGeometryMoving();
371         }
372     }
373 }
374 
375 
376 GNEViewNet*
getViewNet() const377 GNEAdditional::getViewNet() const {
378     return myViewNet;
379 }
380 
381 
382 PositionVector
getShape() const383 GNEAdditional::getShape() const {
384     return myGeometry.shape;
385 }
386 
387 
388 bool
isAdditionalBlocked() const389 GNEAdditional::isAdditionalBlocked() const {
390     return myBlockMovement;
391 }
392 
393 
394 GUIGLObjectPopupMenu*
getPopUpMenu(GUIMainWindow & app,GUISUMOAbstractView & parent)395 GNEAdditional::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {
396     GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, *this);
397     // build header
398     buildPopupHeader(ret, app);
399     // build menu command for center button and copy cursor position to clipboard
400     buildCenterPopupEntry(ret);
401     buildPositionCopyEntry(ret, false);
402     // buld menu commands for names
403     new FXMenuCommand(ret, ("Copy " + getTagStr() + " name to clipboard").c_str(), nullptr, ret, MID_COPY_NAME);
404     new FXMenuCommand(ret, ("Copy " + getTagStr() + " typed name to clipboard").c_str(), nullptr, ret, MID_COPY_TYPED_NAME);
405     new FXMenuSeparator(ret);
406     // build selection and show parameters menu
407     myViewNet->buildSelectionACPopupEntry(ret, this);
408     buildShowParamsPopupEntry(ret);
409     // show option to open additional dialog
410     if (myTagProperty.hasDialog()) {
411         new FXMenuCommand(ret, ("Open " + getTagStr() + " Dialog").c_str(), getIcon(), &parent, MID_OPEN_ADDITIONAL_DIALOG);
412         new FXMenuSeparator(ret);
413     }
414     // Show position parameters
415     if (myTagProperty.hasAttribute(SUMO_ATTR_LANE)) {
416         GNELane* lane = myViewNet->getNet()->retrieveLane(getAttribute(SUMO_ATTR_LANE));
417         // Show menu command inner position
418         const double innerPos = myGeometry.shape.nearest_offset_to_point2D(parent.getPositionInformation());
419         new FXMenuCommand(ret, ("Cursor position inner additional: " + toString(innerPos)).c_str(), nullptr, nullptr, 0);
420         // If shape isn't empty, show menu command lane position
421         if (myGeometry.shape.size() > 0) {
422             const double lanePos = lane->getShape().nearest_offset_to_point2D(myGeometry.shape[0]);
423             new FXMenuCommand(ret, ("Cursor position over " + toString(SUMO_TAG_LANE) + ": " + toString(innerPos + lanePos)).c_str(), nullptr, nullptr, 0);
424         }
425     } else if (myTagProperty.hasAttribute(SUMO_ATTR_EDGE)) {
426         GNEEdge* edge = myViewNet->getNet()->retrieveEdge(getAttribute(SUMO_ATTR_EDGE));
427         // Show menu command inner position
428         const double innerPos = myGeometry.shape.nearest_offset_to_point2D(parent.getPositionInformation());
429         new FXMenuCommand(ret, ("Cursor position inner additional: " + toString(innerPos)).c_str(), nullptr, nullptr, 0);
430         // If shape isn't empty, show menu command edge position
431         if (myGeometry.shape.size() > 0) {
432             const double edgePos = edge->getLanes().at(0)->getShape().nearest_offset_to_point2D(myGeometry.shape[0]);
433             new FXMenuCommand(ret, ("Mouse position over " + toString(SUMO_TAG_EDGE) + ": " + toString(innerPos + edgePos)).c_str(), nullptr, nullptr, 0);
434         }
435     } else {
436         new FXMenuCommand(ret, ("Cursor position in view: " + toString(getPositionInView().x()) + "," + toString(getPositionInView().y())).c_str(), nullptr, nullptr, 0);
437     }
438     return ret;
439 }
440 
441 
442 GUIParameterTableWindow*
getParameterWindow(GUIMainWindow & app,GUISUMOAbstractView &)443 GNEAdditional::getParameterWindow(GUIMainWindow& app, GUISUMOAbstractView&) {
444     // Create table
445     GUIParameterTableWindow* ret = new GUIParameterTableWindow(app, *this, myTagProperty.getNumberOfAttributes());
446     // Iterate over attributes
447     for (const auto& i : myTagProperty) {
448         // Add attribute and set it dynamic if aren't unique
449         if (i.second.isUnique()) {
450             ret->mkItem(toString(i.first).c_str(), false, getAttribute(i.first));
451         } else {
452             ret->mkItem(toString(i.first).c_str(), true, getAttribute(i.first));
453         }
454     }
455     // close building
456     ret->closeBuilding();
457     return ret;
458 }
459 
460 
461 Boundary
getCenteringBoundary() const462 GNEAdditional::getCenteringBoundary() const {
463     // Return Boundary depending if myMovingGeometryBoundary is initialised (important for move geometry)
464     if (myMove.movingGeometryBoundary.isInitialised()) {
465         return myMove.movingGeometryBoundary;
466     } else if (myGeometry.shape.size() > 0) {
467         Boundary b = myGeometry.shape.getBoxBoundary();
468         b.grow(20);
469         return b;
470     } else if (myGeometry.multiShape.size() > 0) {
471         // obtain boundary of multishape fixed
472         Boundary b = myGeometry.multiShapeUnified.getBoxBoundary();
473         b.grow(20);
474         return b;
475     } else if (getAdditionalParents().size() > 0) {
476         return getAdditionalParents().at(0)->getCenteringBoundary();
477     } else {
478         return Boundary(-0.1, -0.1, 0.1, 0.1);
479     }
480 }
481 
482 // ---------------------------------------------------------------------------
483 // GNEAdditional::BlockIcon - methods
484 // ---------------------------------------------------------------------------
485 
BlockIcon(GNEAdditional * additional)486 GNEAdditional::BlockIcon::BlockIcon(GNEAdditional* additional) :
487     myAdditional(additional),
488     rotation(0.) {}
489 
490 
491 void
setRotation(GNELane * additionalLane)492 GNEAdditional::BlockIcon::setRotation(GNELane* additionalLane) {
493     if (myAdditional->myGeometry.shape.size() > 0 && myAdditional->myGeometry.shape.length() != 0) {
494         // If length of the shape is distint to 0, Obtain rotation of center of shape
495         rotation = myAdditional->myGeometry.shape.rotationDegreeAtOffset((myAdditional->myGeometry.shape.length() / 2.)) - 90;
496     } else if (additionalLane) {
497         // If additional is over a lane, set rotation in the position over lane
498         double posOverLane = additionalLane->getShape().nearest_offset_to_point2D(myAdditional->getPositionInView());
499         rotation = additionalLane->getShape().rotationDegreeAtOffset(posOverLane) - 90;
500     } else {
501         // In other case, rotation is 0
502         rotation = 0;
503     }
504 }
505 
506 
507 void
draw(double size) const508 GNEAdditional::BlockIcon::draw(double size) const {
509     if (myAdditional->myViewNet->showLockIcon()) {
510         // Start pushing matrix
511         glPushMatrix();
512         // Traslate to middle of shape
513         glTranslated(position.x(), position.y(), myAdditional->getType() + 0.1);
514         // Set draw color
515         glColor3d(1, 1, 1);
516         // Rotate depending of rotation
517         glRotated(rotation, 0, 0, -1);
518         // Rotate 180 degrees
519         glRotated(180, 0, 0, 1);
520         // Traslate depending of the offset
521         glTranslated(offset.x(), offset.y(), 0);
522         // Draw icon depending of the state of additional
523         if (myAdditional->mySelected) {
524             if (!myAdditional->getTagProperty().canBlockMovement()) {
525                 // Draw not movable texture if additional isn't movable and is selected
526                 GUITexturesHelper::drawTexturedBox(GUITextureSubSys::getTexture(GNETEXTURE_NOTMOVINGSELECTED), size);
527             } else if (myAdditional->myBlockMovement) {
528                 // Draw lock texture if additional is movable, is blocked and is selected
529                 GUITexturesHelper::drawTexturedBox(GUITextureSubSys::getTexture(GNETEXTURE_LOCKSELECTED), size);
530             } else {
531                 // Draw empty texture if additional is movable, isn't blocked and is selected
532                 GUITexturesHelper::drawTexturedBox(GUITextureSubSys::getTexture(GNETEXTURE_EMPTYSELECTED), size);
533             }
534         } else {
535             if (!myAdditional->getTagProperty().canBlockMovement()) {
536                 // Draw not movable texture if additional isn't movable
537                 GUITexturesHelper::drawTexturedBox(GUITextureSubSys::getTexture(GNETEXTURE_NOTMOVING), size);
538             } else if (myAdditional->myBlockMovement) {
539                 // Draw lock texture if additional is movable and is blocked
540                 GUITexturesHelper::drawTexturedBox(GUITextureSubSys::getTexture(GNETEXTURE_LOCK), size);
541             } else {
542                 // Draw empty texture if additional is movable and isn't blocked
543                 GUITexturesHelper::drawTexturedBox(GUITextureSubSys::getTexture(GNETEXTURE_EMPTY), size);
544             }
545         }
546         // Pop matrix
547         glPopMatrix();
548     }
549 }
550 
551 // ---------------------------------------------------------------------------
552 // GNEAdditional - protected methods
553 // ---------------------------------------------------------------------------
554 
555 void
setDefaultValues()556 GNEAdditional::setDefaultValues() {
557     // iterate over attributes and set default value
558     for (const auto& i : myTagProperty) {
559         if (i.second.hasDefaultValue()) {
560             setAttribute(i.first, i.second.getDefaultValue());
561         }
562     }
563 }
564 
565 
566 const std::string&
getAdditionalID() const567 GNEAdditional::getAdditionalID() const {
568     return getMicrosimID();
569 }
570 
571 
572 bool
isValidAdditionalID(const std::string & newID) const573 GNEAdditional::isValidAdditionalID(const std::string& newID) const {
574     if (SUMOXMLDefinitions::isValidNetID(newID) && (myViewNet->getNet()->retrieveAdditional(myTagProperty.getTag(), newID, false) == nullptr)) {
575         return true;
576     } else {
577         return false;
578     }
579 }
580 
581 
582 bool
isValidDetectorID(const std::string & newID) const583 GNEAdditional::isValidDetectorID(const std::string& newID) const {
584     if (SUMOXMLDefinitions::isValidDetectorID(newID) && (myViewNet->getNet()->retrieveAdditional(myTagProperty.getTag(), newID, false) == nullptr)) {
585         return true;
586     } else {
587         return false;
588     }
589 }
590 
591 
592 void
changeAdditionalID(const std::string & newID)593 GNEAdditional::changeAdditionalID(const std::string& newID) {
594     if (myViewNet->getNet()->retrieveAdditional(myTagProperty.getTag(), newID, false) != nullptr) {
595         throw InvalidArgument("An Additional with tag " + getTagStr() + " and ID = " + newID + " already exists");
596     } else {
597         // Save old ID
598         std::string oldID = getMicrosimID();
599         // set New ID
600         setMicrosimID(newID);
601         // update additional ID in the container of net
602         myViewNet->getNet()->updateAdditionalID(oldID, this);
603     }
604 }
605 
606 
607 void
selectAttributeCarrier(bool changeFlag)608 GNEAdditional::selectAttributeCarrier(bool changeFlag) {
609     if (!myViewNet) {
610         throw ProcessError("ViewNet cannot be nullptr");
611     } else {
612         gSelected.select(dynamic_cast<GUIGlObject*>(this)->getGlID());
613         // add object of list into selected objects
614         myViewNet->getViewParent()->getSelectorFrame()->getLockGLObjectTypes()->addedLockedObject(GLO_ADDITIONAL);
615         if (changeFlag) {
616             mySelected = true;
617         }
618     }
619 }
620 
621 
622 void
unselectAttributeCarrier(bool changeFlag)623 GNEAdditional::unselectAttributeCarrier(bool changeFlag) {
624     if (!myViewNet) {
625         throw ProcessError("ViewNet cannot be nullptr");
626     } else {
627         gSelected.deselect(dynamic_cast<GUIGlObject*>(this)->getGlID());
628         // remove object of list of selected objects
629         myViewNet->getViewParent()->getSelectorFrame()->getLockGLObjectTypes()->removeLockedObject(GLO_ADDITIONAL);
630         if (changeFlag) {
631             mySelected = false;
632 
633         }
634     }
635 }
636 
637 
638 bool
isAttributeCarrierSelected() const639 GNEAdditional::isAttributeCarrierSelected() const {
640     return mySelected;
641 }
642 
643 
644 bool
drawUsingSelectColor() const645 GNEAdditional::drawUsingSelectColor() const {
646     if (mySelected && (myViewNet->getEditModes().currentSupermode == GNE_SUPERMODE_NETWORK)) {
647         return true;
648     } else {
649         return false;
650     }
651 }
652 
653 
654 bool
checkAdditionalChildRestriction() const655 GNEAdditional::checkAdditionalChildRestriction() const {
656     // throw exception because this function mus be implemented in child (see GNEE3Detector)
657     throw ProcessError("Calling non-implemented function checkAdditionalChildRestriction during saving of " + getTagStr() + ". It muss be reimplemented in child class");
658 }
659 
660 
661 std::string
getGenericParametersStr() const662 GNEAdditional::getGenericParametersStr() const {
663     std::string result;
664     // Generate an string using the following structure: "key1=value1|key2=value2|...
665     for (auto i : getParametersMap()) {
666         result += i.first + "=" + i.second + "|";
667     }
668     // remove the last "|"
669     if (!result.empty()) {
670         result.pop_back();
671     }
672     return result;
673 }
674 
675 
676 std::vector<std::pair<std::string, std::string> >
getGenericParameters() const677 GNEAdditional::getGenericParameters() const {
678     std::vector<std::pair<std::string, std::string> >  result;
679     // iterate over parameters map and fill result
680     for (auto i : getParametersMap()) {
681         result.push_back(std::make_pair(i.first, i.second));
682     }
683     return result;
684 }
685 
686 
687 void
setGenericParametersStr(const std::string & value)688 GNEAdditional::setGenericParametersStr(const std::string& value) {
689     // clear parameters
690     clearParameter();
691     // separate value in a vector of string using | as separator
692     std::vector<std::string> parsedValues;
693     StringTokenizer stValues(value, "|", true);
694     while (stValues.hasNext()) {
695         parsedValues.push_back(stValues.next());
696     }
697     // check that parsed values (A=B)can be parsed in generic parameters
698     for (auto i : parsedValues) {
699         std::vector<std::string> parsedParameters;
700         StringTokenizer stParam(i, "=", true);
701         while (stParam.hasNext()) {
702             parsedParameters.push_back(stParam.next());
703         }
704         // Check that parsed parameters are exactly two and contains valid chracters
705         if (parsedParameters.size() == 2 && SUMOXMLDefinitions::isValidGenericParameterKey(parsedParameters.front()) && SUMOXMLDefinitions::isValidGenericParameterValue(parsedParameters.back())) {
706             setParameter(parsedParameters.front(), parsedParameters.back());
707         }
708     }
709 }
710 
711 /****************************************************************************/
712