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