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    GNEBusStop.cpp
11 /// @author  Pablo Alvarez Lopez
12 /// @date    Nov 2015
13 /// @version $Id$
14 ///
15 // A lane area vehicles can halt at (GNE version)
16 /****************************************************************************/
17 
18 // ===========================================================================
19 // included modules
20 // ===========================================================================
21 
22 #include <foreign/fontstash/fontstash.h>
23 #include <netedit/GNENet.h>
24 #include <netedit/GNEUndoList.h>
25 #include <netedit/GNEViewNet.h>
26 #include <netedit/changes/GNEChange_Attribute.h>
27 #include <netedit/demandelements/GNEDemandElement.h>
28 #include <netedit/netelements/GNEEdge.h>
29 #include <netedit/netelements/GNELane.h>
30 #include <utils/options/OptionsCont.h>
31 #include <utils/gui/globjects/GLIncludes.h>
32 #include <utils/gui/div/GLHelper.h>
33 #include <utils/geom/GeomHelper.h>
34 
35 #include "GNEBusStop.h"
36 #include "GNEAccess.h"
37 #include "GNEAdditionalHandler.h"
38 
39 // ===========================================================================
40 // method definitions
41 // ===========================================================================
42 
GNEBusStop(const std::string & id,GNELane * lane,GNEViewNet * viewNet,const std::string & startPos,const std::string & endPos,const std::string & name,const std::vector<std::string> & lines,int personCapacity,bool friendlyPosition,bool blockMovement)43 GNEBusStop::GNEBusStop(const std::string& id, GNELane* lane, GNEViewNet* viewNet, const std::string& startPos, const std::string& endPos, const std::string& name, const std::vector<std::string>& lines, int personCapacity, bool friendlyPosition, bool blockMovement) :
44     GNEStoppingPlace(id, viewNet, GLO_BUS_STOP, SUMO_TAG_BUS_STOP, lane, startPos, endPos, name, friendlyPosition, blockMovement),
45     myLines(lines),
46     myPersonCapacity(personCapacity) {
47 }
48 
49 
~GNEBusStop()50 GNEBusStop::~GNEBusStop() {}
51 
52 
53 void
updateGeometry(bool updateGrid)54 GNEBusStop::updateGeometry(bool updateGrid) {
55     // first check if object has to be removed from grid (SUMOTree)
56     if (updateGrid) {
57         myViewNet->getNet()->removeGLObjectFromGrid(this);
58     }
59 
60     // Get value of option "lefthand"
61     double offsetSign = OptionsCont::getOptions().getBool("lefthand") ? -1 : 1;
62 
63     // Update common geometry of stopping place
64     setStoppingPlaceGeometry(getLaneParents().front()->getParentEdge().getNBEdge()->getLaneWidth(getLaneParents().front()->getIndex()) / 2);
65 
66     // Obtain a copy of the shape
67     PositionVector tmpShape = myGeometry.shape;
68 
69     // Move shape to side
70     tmpShape.move2side(1.5 * offsetSign);
71 
72     // Get position of the sign
73     mySignPos = tmpShape.getLineCenter();
74 
75     // Set block icon position
76     myBlockIcon.position = myGeometry.shape.getLineCenter();
77 
78     // Set block icon rotation, and using their rotation for sign
79     myBlockIcon.setRotation(getLaneParents().front());
80 
81     // update demand element childs (GNEStops)
82     for (const auto& i : getDemandElementChilds()) {
83         i->updateGeometry(updateGrid);
84     }
85 
86     // last step is to check if object has to be added into grid (SUMOTree) again
87     if (updateGrid) {
88         myViewNet->getNet()->addGLObjectIntoGrid(this);
89     }
90 }
91 
92 
93 void
drawGL(const GUIVisualizationSettings & s) const94 GNEBusStop::drawGL(const GUIVisualizationSettings& s) const {
95     // obtain circle resolution
96     int circleResolution = getCircleResolution(s);
97     // Obtain exaggeration of the draw
98     const double exaggeration = s.addSize.getExaggeration(s, this);
99     // Start drawing adding an gl identificator
100     glPushName(getGlID());
101     // Add a draw matrix
102     glPushMatrix();
103     // Start with the drawing of the area traslating matrix to origin
104     glTranslated(0, 0, getType());
105     // Set color of the base
106     if (drawUsingSelectColor()) {
107         GLHelper::setColor(s.selectedAdditionalColor);
108     } else {
109         GLHelper::setColor(s.SUMO_color_busStop);
110     }
111     // Draw the area using shape, shapeRotations, shapeLengths and value of exaggeration
112     GLHelper::drawBoxLines(myGeometry.shape, myGeometry.shapeRotations, myGeometry.shapeLengths, exaggeration);
113     // Check if the distance is enought to draw details and if is being drawn for selecting
114     if (s.drawForSelecting) {
115         // only draw circle depending of distance between sign and mouse cursor
116         if (myViewNet->getPositionInformation().distanceSquaredTo2D(mySignPos) <= (myCircleWidthSquared + 2)) {
117             // Add a draw matrix for details
118             glPushMatrix();
119             // Start drawing sign traslating matrix to signal position
120             glTranslated(mySignPos.x(), mySignPos.y(), 0);
121             // scale matrix depending of the exaggeration
122             glScaled(exaggeration, exaggeration, 1);
123             // set color
124             GLHelper::setColor(s.SUMO_color_busStop);
125             // Draw circle
126             GLHelper::drawFilledCircle(myCircleWidth, circleResolution);
127             // pop draw matrix
128             glPopMatrix();
129         }
130     } else if (s.scale * exaggeration >= 10) {
131         // draw lines between BusStops and Acces
132         for (auto i : getAdditionalChilds()) {
133             GLHelper::drawBoxLine(i->getShape()[0], RAD2DEG(mySignPos.angleTo2D(i->getShape()[0])) - 90, mySignPos.distanceTo2D(i->getShape()[0]), .05);
134         }
135         // Add a draw matrix for details
136         glPushMatrix();
137         // Iterate over every line
138         for (int i = 0; i < (int)myLines.size(); ++i) {
139             // push a new matrix for every line
140             glPushMatrix();
141             // Rotate and traslaste
142             glTranslated(mySignPos.x(), mySignPos.y(), 0);
143             glRotated(-1 * myBlockIcon.rotation, 0, 0, 1);
144             // draw line with a color depending of the selection status
145             if (drawUsingSelectColor()) {
146                 GLHelper::drawText(myLines[i].c_str(), Position(1.2, (double)i), .1, 1.f, s.selectionColor, 0, FONS_ALIGN_LEFT);
147             } else {
148                 GLHelper::drawText(myLines[i].c_str(), Position(1.2, (double)i), .1, 1.f, s.SUMO_color_busStop, 0, FONS_ALIGN_LEFT);
149             }
150             // pop matrix for every line
151             glPopMatrix();
152         }
153         // Start drawing sign traslating matrix to signal position
154         glTranslated(mySignPos.x(), mySignPos.y(), 0);
155         // scale matrix depending of the exaggeration
156         glScaled(exaggeration, exaggeration, 1);
157         // Set color of the externe circle
158         if (drawUsingSelectColor()) {
159             GLHelper::setColor(s.selectedAdditionalColor);
160         } else {
161             GLHelper::setColor(s.SUMO_color_busStop);
162         }
163         // Draw circle
164         GLHelper::drawFilledCircle(myCircleWidth, circleResolution);
165         // Traslate to front
166         glTranslated(0, 0, .1);
167         // Set color of the interne circle
168         if (drawUsingSelectColor()) {
169             GLHelper::setColor(s.selectionColor);
170         } else {
171             GLHelper::setColor(s.SUMO_color_busStop_sign);
172         }
173         // draw another circle in the same position, but a little bit more small
174         GLHelper::drawFilledCircle(myCircleInWidth, circleResolution);
175         // If the scale * exageration is equal or more than 4.5, draw H
176         if (s.scale * exaggeration >= 4.5) {
177             if (drawUsingSelectColor()) {
178                 GLHelper::drawText("H", Position(), .1, myCircleInText, s.selectedAdditionalColor, myBlockIcon.rotation);
179             } else {
180                 GLHelper::drawText("H", Position(), .1, myCircleInText, s.SUMO_color_busStop, myBlockIcon.rotation);
181             }
182         }
183         // pop draw matrix
184         glPopMatrix();
185         // Show Lock icon depending of the Edit mode
186         myBlockIcon.draw();
187     }
188     // pop draw matrix
189     glPopMatrix();
190     // Draw name if isn't being drawn for selecting
191     drawName(getCenteringBoundary().getCenter(), s.scale, s.addName);
192     if (s.addFullName.show && (myAdditionalName != "") && !s.drawForSelecting) {
193         GLHelper::drawText(myAdditionalName, mySignPos, GLO_MAX - getType(), s.addFullName.scaledSize(s.scale), s.addFullName.color, myBlockIcon.rotation);
194     }
195     // check if dotted contour has to be drawn
196     if (!s.drawForSelecting && (myViewNet->getDottedAC() == this)) {
197         GLHelper::drawShapeDottedContour(getType(), myGeometry.shape, exaggeration);
198     }
199     // Pop name
200     glPopName();
201 }
202 
203 
204 std::string
getAttribute(SumoXMLAttr key) const205 GNEBusStop::getAttribute(SumoXMLAttr key) const {
206     switch (key) {
207         case SUMO_ATTR_ID:
208             return getAdditionalID();
209         case SUMO_ATTR_LANE:
210             return getLaneParents().front()->getID();
211         case SUMO_ATTR_STARTPOS:
212             return toString(myStartPosition);
213         case SUMO_ATTR_ENDPOS:
214             return myEndPosition;
215         case SUMO_ATTR_NAME:
216             return myAdditionalName;
217         case SUMO_ATTR_FRIENDLY_POS:
218             return toString(myFriendlyPosition);
219         case SUMO_ATTR_LINES:
220             return joinToString(myLines, " ");
221         case SUMO_ATTR_PERSON_CAPACITY:
222             return toString(myPersonCapacity);
223         case GNE_ATTR_BLOCK_MOVEMENT:
224             return toString(myBlockMovement);
225         case GNE_ATTR_SELECTED:
226             return toString(isAttributeCarrierSelected());
227         case GNE_ATTR_GENERIC:
228             return getGenericParametersStr();
229         default:
230             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
231     }
232 }
233 
234 
235 void
setAttribute(SumoXMLAttr key,const std::string & value,GNEUndoList * undoList)236 GNEBusStop::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {
237     if (value == getAttribute(key)) {
238         return; //avoid needless changes, later logic relies on the fact that attributes have changed
239     }
240     switch (key) {
241         case SUMO_ATTR_ID: {
242             // change ID of BusStop
243             undoList->p_add(new GNEChange_Attribute(this, myViewNet->getNet(), key, value));
244             // Change Ids of all Acces childs
245             for (auto i : getAdditionalChilds()) {
246                 i->setAttribute(SUMO_ATTR_ID, generateChildID(SUMO_TAG_ACCESS), undoList);
247             }
248             break;
249         }
250         case SUMO_ATTR_LANE:
251         case SUMO_ATTR_STARTPOS:
252         case SUMO_ATTR_ENDPOS:
253         case SUMO_ATTR_NAME:
254         case SUMO_ATTR_FRIENDLY_POS:
255         case SUMO_ATTR_LINES:
256         case SUMO_ATTR_PERSON_CAPACITY:
257         case GNE_ATTR_BLOCK_MOVEMENT:
258         case GNE_ATTR_SELECTED:
259         case GNE_ATTR_GENERIC:
260             undoList->p_add(new GNEChange_Attribute(this, myViewNet->getNet(), key, value));
261             break;
262         default:
263             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
264     }
265 }
266 
267 
268 bool
isValid(SumoXMLAttr key,const std::string & value)269 GNEBusStop::isValid(SumoXMLAttr key, const std::string& value) {
270     switch (key) {
271         case SUMO_ATTR_ID:
272             return isValidAdditionalID(value);
273         case SUMO_ATTR_LANE:
274             if (myViewNet->getNet()->retrieveLane(value, false) != nullptr) {
275                 return true;
276             } else {
277                 return false;
278             }
279         case SUMO_ATTR_STARTPOS:
280             if (value.empty()) {
281                 return true;
282             } else if (canParse<double>(value)) {
283                 return checkStoppinPlacePosition(value, myEndPosition, getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength(), myFriendlyPosition);
284             } else {
285                 return false;
286             }
287         case SUMO_ATTR_ENDPOS:
288             if (value.empty()) {
289                 return true;
290             } else if (canParse<double>(value)) {
291                 return checkStoppinPlacePosition(myStartPosition, value, getLaneParents().front()->getParentEdge().getNBEdge()->getFinalLength(), myFriendlyPosition);
292             } else {
293                 return false;
294             }
295         case SUMO_ATTR_NAME:
296             return SUMOXMLDefinitions::isValidAttribute(value);
297         case SUMO_ATTR_FRIENDLY_POS:
298             return canParse<bool>(value);
299         case SUMO_ATTR_LINES:
300             return canParse<std::vector<std::string> >(value);
301         case SUMO_ATTR_PERSON_CAPACITY:
302             return canParse<int>(value) && (parse<int>(value) > 0 || parse<int>(value) == -1);
303         case GNE_ATTR_BLOCK_MOVEMENT:
304             return canParse<bool>(value);
305         case GNE_ATTR_SELECTED:
306             return canParse<bool>(value);
307         case GNE_ATTR_GENERIC:
308             return isGenericParametersValid(value);
309         default:
310             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
311     }
312 }
313 
314 // ===========================================================================
315 // private
316 // ===========================================================================
317 
318 void
setAttribute(SumoXMLAttr key,const std::string & value)319 GNEBusStop::setAttribute(SumoXMLAttr key, const std::string& value) {
320     switch (key) {
321         case SUMO_ATTR_ID:
322             changeAdditionalID(value);
323             break;
324         case SUMO_ATTR_LANE:
325             changeLaneParents(this, value);
326             break;
327         case SUMO_ATTR_STARTPOS:
328             myStartPosition = value;
329             break;
330         case SUMO_ATTR_ENDPOS:
331             myEndPosition = value;
332             break;
333         case SUMO_ATTR_NAME:
334             myAdditionalName = value;
335             break;
336         case SUMO_ATTR_FRIENDLY_POS:
337             myFriendlyPosition = parse<bool>(value);
338             break;
339         case SUMO_ATTR_LINES:
340             myLines = GNEAttributeCarrier::parse<std::vector<std::string> >(value);
341             break;
342         case SUMO_ATTR_PERSON_CAPACITY:
343             myPersonCapacity = GNEAttributeCarrier::parse<int>(value);
344             break;
345         case GNE_ATTR_BLOCK_MOVEMENT:
346             myBlockMovement = parse<bool>(value);
347             break;
348         case GNE_ATTR_SELECTED:
349             if (parse<bool>(value)) {
350                 selectAttributeCarrier();
351             } else {
352                 unselectAttributeCarrier();
353             }
354             break;
355         case GNE_ATTR_GENERIC:
356             setGenericParametersStr(value);
357             break;
358         default:
359             throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");
360     }
361     // check if updated attribute requieres update geometry
362     if (myTagProperty.hasAttribute(key) && myTagProperty.getAttributeProperties(key).requiereUpdateGeometry()) {
363         updateGeometry(true);
364     }
365 }
366 
367 /****************************************************************************/
368