Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file GUIBusStop.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @author Johannes Rummel
19 : /// @date Wed, 07.12.2005
20 : ///
21 : // A lane area vehicles can halt at (gui-version)
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <string>
26 : #include <utils/common/MsgHandler.h>
27 : #include <utils/common/RGBColor.h>
28 : #include <utils/geom/PositionVector.h>
29 : #include <utils/geom/Boundary.h>
30 : #include <utils/gui/div/GLHelper.h>
31 : #include <utils/common/ToString.h>
32 : #include <microsim/MSNet.h>
33 : #include <microsim/MSLane.h>
34 : #include <microsim/MSEdge.h>
35 : #include <microsim/transportables/MSTransportable.h>
36 : #include <microsim/transportables/MSStageDriving.h>
37 : #include "GUINet.h"
38 : #include "GUIEdge.h"
39 : #include <utils/gui/globjects/GUIGLObjectPopupMenu.h>
40 : #include <utils/gui/windows/GUIAppEnum.h>
41 : #include <gui/GUIGlobals.h>
42 : #include <utils/gui/div/GUIParameterTableWindow.h>
43 : #include <gui/GUIApplicationWindow.h>
44 : #include <microsim/logging/FunctionBinding.h>
45 : #include <utils/gui/div/GUIGlobalSelection.h>
46 : #include <foreign/fontstash/fontstash.h>
47 : #include <utils/geom/GeomHelper.h>
48 : #include <guisim/GUIBusStop.h>
49 : #include <utils/common/MsgHandler.h>
50 : #include <utils/gui/globjects/GLIncludes.h>
51 :
52 :
53 :
54 : // ===========================================================================
55 : // method definitions
56 : // ===========================================================================
57 1813 : GUIBusStop::GUIBusStop(const std::string& id, SumoXMLTag element, const std::vector<std::string>& lines, MSLane& lane,
58 : double frompos, double topos, const std::string name, int personCapacity,
59 1813 : double parkingLength, const RGBColor& color, double angle) :
60 : MSStoppingPlace(id, element, lines, lane, frompos, topos, name, personCapacity, parkingLength, color, angle),
61 : GUIGlObject_AbstractAdd(GLO_BUS_STOP, id, GUIIconSubSys::getIcon(GUIIcon::BUSSTOP)),
62 3626 : myEmptyColor(RGBColor::INVISIBLE) {
63 : // see MSVehicleControl defContainerType
64 1813 : myWidth = MAX2(1.0, ceil((double)myTransportableCapacity / getTransportablesAbreast()) * myTransportableDepth);
65 1813 : initShape(myFGShape, myFGShapeRotations, myFGShapeLengths, myFGSignPos, myFGSignRot);
66 1813 : if (myLane.getShape(true).size() > 0) {
67 1813 : initShape(myFGShape2, myFGShapeRotations2, myFGShapeLengths2, myFGSignPos2, myFGSignRot2, true);
68 : }
69 1813 : }
70 :
71 :
72 3624 : GUIBusStop::~GUIBusStop() {}
73 :
74 :
75 : void
76 3626 : GUIBusStop::initShape(PositionVector& fgShape,
77 : std::vector<double>& fgShapeRotations, std::vector<double>& fgShapeLengths,
78 : Position& fgSignPos, double& fgSignRot,
79 : bool secondaryShape) {
80 3626 : const double offsetSign = MSGlobals::gLefthand ? -1 : 1;
81 3626 : const double lgf = myLane.getLengthGeometryFactor(secondaryShape);
82 3626 : fgShape = myLane.getShape(secondaryShape);
83 7252 : fgShape = fgShape.getSubpart(lgf * myBegPos, lgf * myEndPos);
84 3626 : fgShape.move2side(((myLane.getWidth() + myWidth) * 0.5 - 0.2) * offsetSign);
85 3626 : fgShapeRotations.reserve(fgShape.size() - 1);
86 3626 : fgShapeLengths.reserve(fgShape.size() - 1);
87 3626 : int e = (int) fgShape.size() - 1;
88 8722 : for (int i = 0; i < e; ++i) {
89 5096 : const Position& f = fgShape[i];
90 5096 : const Position& s = fgShape[i + 1];
91 5096 : fgShapeLengths.push_back(f.distanceTo(s));
92 5096 : fgShapeRotations.push_back((double) atan2((s.x() - f.x()), (f.y() - s.y())) * (double) 180.0 / (double) M_PI);
93 : }
94 : PositionVector tmp = fgShape;
95 3626 : tmp.move2side(myWidth / 2 * offsetSign);
96 3626 : fgSignPos = tmp.getLineCenter();
97 3626 : fgSignRot = 0;
98 3626 : if (tmp.length() != 0) {
99 3626 : fgSignRot = fgShape.rotationDegreeAtOffset(double((fgShape.length() / 2.)));
100 3626 : const double rotSign = MSGlobals::gLefthand ? -1 : 1;
101 3626 : fgSignRot -= 90 * rotSign;
102 : }
103 3626 : }
104 :
105 :
106 : void
107 1810 : GUIBusStop::finishedLoading() {
108 1810 : MSStoppingPlace::finishedLoading();
109 3620 : if (hasParameter("emptyColor")) {
110 : try {
111 0 : myEmptyColor = RGBColor::parseColor(getParameter("emptyColor"));
112 0 : } catch (ProcessError& e) {
113 0 : WRITE_WARNINGF("Could not parse color '%' (%)", getParameter("emptyColor"), e.what());
114 0 : }
115 : }
116 1810 : }
117 :
118 :
119 : bool
120 258 : GUIBusStop::addAccess(MSLane* const lane, const double startPos, const double endPos, double length, const MSStoppingPlace::AccessExit exit) {
121 258 : const bool added = MSStoppingPlace::addAccess(lane, startPos, endPos, length, exit);
122 258 : if (added) {
123 256 : myAccessCoords.push_back(lane->geometryPositionAtOffset((startPos + endPos) / 2.));
124 : }
125 258 : return added;
126 : }
127 :
128 :
129 : GUIGLObjectPopupMenu*
130 0 : GUIBusStop::getPopUpMenu(GUIMainWindow& app,
131 : GUISUMOAbstractView& parent) {
132 0 : GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);
133 0 : buildPopupHeader(ret, app);
134 0 : buildCenterPopupEntry(ret);
135 0 : buildNameCopyPopupEntry(ret);
136 0 : buildSelectionPopupEntry(ret);
137 0 : buildShowParamsPopupEntry(ret);
138 0 : buildPositionCopyEntry(ret, app);
139 0 : return ret;
140 : }
141 :
142 :
143 : GUIParameterTableWindow*
144 0 : GUIBusStop::getParameterWindow(GUIMainWindow& app,
145 : GUISUMOAbstractView&) {
146 : GUIParameterTableWindow* ret =
147 0 : new GUIParameterTableWindow(app, *this);
148 : // add items
149 0 : ret->mkItem(TL("name"), false, getMyName());
150 0 : ret->mkItem(TL("begin position [m]"), false, myBegPos);
151 0 : ret->mkItem(TL("end position [m]"), false, myEndPos);
152 0 : ret->mkItem(TL("lines"), false, joinToString(myLines, " "));
153 0 : ret->mkItem(TL("parking length [m]"), false, (myEndPos - myBegPos) / myParkingFactor);
154 0 : const std::string transportable = (myElement == SUMO_TAG_CONTAINER_STOP ? "container" : "person");
155 0 : ret->mkItem((transportable + " capacity [#]").c_str(), false, myTransportableCapacity);
156 0 : ret->mkItem((transportable + " number [#]").c_str(), true, new FunctionBinding<GUIBusStop, int>(this, &MSStoppingPlace::getTransportableNumber));
157 0 : ret->mkItem(TL("stopped vehicles [#]"), true, new FunctionBinding<GUIBusStop, int>(this, &MSStoppingPlace::getStoppedVehicleNumber));
158 0 : ret->mkItem(TL("last free pos [m]"), true, new FunctionBinding<GUIBusStop, double>(this, &GUIBusStop::getCroppedLastFreePos));
159 : // rides-being-waited-on statistic
160 : std::map<std::string, int> stats;
161 0 : for (const MSTransportable* t : getTransportables()) {
162 0 : MSStageDriving* s = dynamic_cast<MSStageDriving*>(t->getCurrentStage());
163 0 : if (s != nullptr) {
164 0 : if (s->getIntendedVehicleID() != "") {
165 0 : stats[s->getIntendedVehicleID()] += 1;
166 : } else {
167 0 : stats[joinToString(s->getLines(), " ")] += 1;
168 : }
169 : }
170 0 : }
171 0 : if (stats.size() > 0) {
172 0 : ret->mkItem(TL("waiting for:"), false, "[#]");
173 0 : for (auto item : stats) {
174 0 : ret->mkItem(item.first.c_str(), false, toString(item.second));
175 : }
176 : }
177 :
178 : // close building
179 0 : ret->closeBuilding();
180 0 : return ret;
181 : }
182 :
183 :
184 : void
185 60115 : GUIBusStop::drawGL(const GUIVisualizationSettings& s) const {
186 : // get colors
187 60115 : RGBColor color, colorSign;
188 60115 : if (myElement == SUMO_TAG_CONTAINER_STOP) {
189 7304 : color = s.colorSettings.containerStopColor;
190 7304 : colorSign = s.colorSettings.containerStopColorSign;
191 52811 : } else if (myElement == SUMO_TAG_TRAIN_STOP) {
192 9254 : color = s.colorSettings.trainStopColor;
193 9254 : colorSign = s.colorSettings.trainStopColorSign;
194 : } else {
195 43557 : color = s.colorSettings.busStopColor;
196 43557 : colorSign = s.colorSettings.busStopColorSign;
197 : }
198 : // set color
199 60115 : if (getColor() != RGBColor::INVISIBLE) {
200 18 : color = getColor();
201 : }
202 60115 : if (myEmptyColor != RGBColor::INVISIBLE && myEndPositions.empty() && myWaitingTransportables.empty()) {
203 0 : color = myEmptyColor;
204 : }
205 60115 : const bool s2 = s.secondaryShape;
206 60115 : const Position& signPos = s2 ? myFGSignPos2 : myFGSignPos;
207 : // recognize full transparency and simply don't draw
208 60115 : if (color.alpha() != 0) {
209 60115 : GLHelper::pushName(getGlID());
210 60115 : GLHelper::pushMatrix();
211 : // draw the area
212 60115 : glTranslated(0, 0, getType());
213 60115 : GLHelper::setColor(color);
214 60115 : const double exaggeration = getExaggeration(s);
215 : // only shrink the box but never enlarge it (only enlarge the sign)
216 60115 : if (s2) {
217 0 : GLHelper::drawBoxLines(myFGShape2, myFGShapeRotations2, myFGShapeLengths2, myWidth * 0.5 * MIN2(1.0, exaggeration), 0, 0);
218 : } else {
219 120230 : GLHelper::drawBoxLines(myFGShape, myFGShapeRotations, myFGShapeLengths, myWidth * 0.5 * MIN2(1.0, exaggeration), 0, 0);
220 : }
221 60115 : const double signRot = s2 ? myFGSignRot2 : myFGSignRot;
222 : // draw details unless zoomed out to far
223 60115 : if (s.drawDetail(10, exaggeration)) {
224 11 : GLHelper::pushMatrix();
225 : // draw the lines
226 11 : const double rotSign = MSGlobals::gLefthand ? 1 : -1;
227 11 : const double lineAngle = s.getTextAngle(signRot);
228 : // Iterate over every line
229 11 : RGBColor lineColor = color.changedBrightness(-51);
230 11 : const double textOffset = s.flippedTextAngle(rotSign * signRot) ? -1 : 1;
231 11 : const double textOffset2 = s.flippedTextAngle(rotSign * signRot) ? -1 : 0.3;
232 11 : for (int i = 0; i < (int)myLines.size(); ++i) {
233 : // push a new matrix for every line
234 0 : GLHelper::pushMatrix();
235 : // traslate and rotate
236 0 : glTranslated(signPos.x(), signPos.y(), 0);
237 0 : glRotated(-lineAngle, 0, 0, 1);
238 : // draw line
239 0 : GLHelper::drawText(myLines[i].c_str(), Position(1.2, i * textOffset + textOffset2), .1, 1.f, lineColor, 0, FONS_ALIGN_LEFT);
240 : // pop matrix for every line
241 0 : GLHelper::popMatrix();
242 : }
243 11 : GLHelper::setColor(color);
244 11 : const Position accessOrigin = getCenterPos();
245 11 : for (std::vector<Position>::const_iterator i = myAccessCoords.begin(); i != myAccessCoords.end(); ++i) {
246 0 : GLHelper::drawBoxLine(*i, RAD2DEG(accessOrigin.angleTo2D(*i)) - 90, accessOrigin.distanceTo2D(*i), .05);
247 : }
248 : // draw the sign
249 11 : glTranslated(signPos.x(), signPos.y(), 0);
250 : int noPoints = 9;
251 11 : if (s.scale * exaggeration > 25) {
252 5 : noPoints = MIN2((int)(9.0 + (s.scale * exaggeration) / 10.0), 36);
253 : }
254 11 : glScaled(exaggeration, exaggeration, 1);
255 11 : GLHelper::drawFilledCircle((double) 1.1, noPoints);
256 11 : glTranslated(0, 0, .1);
257 11 : GLHelper::setColor(colorSign);
258 11 : GLHelper::drawFilledCircle((double) 0.9, noPoints);
259 11 : if (myElement == SUMO_TAG_CONTAINER_STOP) {
260 0 : GLHelper::drawText("C", Position(), .1, 1.6, color, signRot);
261 11 : } else if (myElement == SUMO_TAG_TRAIN_STOP) {
262 0 : GLHelper::drawText("T", Position(), .1, 1.6, color, signRot);
263 : } else {
264 22 : GLHelper::drawText("H", Position(), .1, 1.6, color, signRot);
265 : }
266 11 : GLHelper::popMatrix();
267 : }
268 60115 : if (s.addFullName.show(this) && getMyName() != "") {
269 0 : GLHelper::drawTextSettings(s.addFullName, getMyName(), signPos, s.scale, s.getTextAngle(signRot), GLO_MAX - getType());
270 : }
271 60115 : GLHelper::popMatrix();
272 60115 : GLHelper::popName();
273 : }
274 60115 : drawName(signPos, s.scale, s.addName, s.angle);
275 60115 : }
276 :
277 :
278 : double
279 60115 : GUIBusStop::getExaggeration(const GUIVisualizationSettings& s) const {
280 60115 : return s.addSize.getExaggeration(s, this);
281 : }
282 :
283 :
284 : Boundary
285 1810 : GUIBusStop::getCenteringBoundary() const {
286 1810 : const PositionVector& shape = GUIGlobals::gSecondaryShape ? myFGShape2 : myFGShape;
287 1810 : Boundary b = shape.getBoxBoundary();
288 1810 : b.grow(myWidth);
289 2066 : for (const Position& p : myAccessCoords) {
290 256 : b.add(p);
291 : }
292 1810 : return b;
293 : }
294 :
295 : const std::string
296 0 : GUIBusStop::getOptionalName() const {
297 0 : return myName;
298 : }
299 :
300 : double
301 0 : GUIBusStop::getCroppedLastFreePos() const {
302 0 : return MAX2(0., getLastFreePos());
303 : }
304 :
305 : /****************************************************************************/
|