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