Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 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 GUIEdge.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @author Laura Bieker
19 : /// @date Sept 2002
20 : ///
21 : // A road/street connecting two junctions (gui-version)
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <vector>
26 : #include <cmath>
27 : #include <string>
28 : #include <algorithm>
29 : #include <utils/common/MsgHandler.h>
30 : #include <utils/foxtools/fxheader.h>
31 : #include <utils/gui/globjects/GUIGLObjectPopupMenu.h>
32 : #include <utils/gui/windows/GUIMainWindow.h>
33 : #include <utils/gui/windows/GUISUMOAbstractView.h>
34 : #include <utils/geom/GeomHelper.h>
35 : #include <utils/gui/div/GUIParameterTableWindow.h>
36 : #include <utils/gui/div/GLHelper.h>
37 : #include <utils/gui/div/GUIGlobalSelection.h>
38 : #include <utils/gui/globjects/GLIncludes.h>
39 : #include <gui/GUIGlobals.h>
40 : #include <microsim/MSBaseVehicle.h>
41 : #include <microsim/MSEdge.h>
42 : #include <microsim/MSJunction.h>
43 : #include <microsim/MSLaneChanger.h>
44 : #include <microsim/MSInsertionControl.h>
45 : #include <microsim/MSGlobals.h>
46 : #include <microsim/logging/CastingFunctionBinding.h>
47 : #include <microsim/logging/FunctionBinding.h>
48 : #include <utils/gui/div/GUIDesigns.h>
49 : #include <mesogui/GUIMEVehicleControl.h>
50 : #include <mesogui/GUIMEVehicle.h>
51 : #include <mesosim/MESegment.h>
52 : #include <mesosim/MELoop.h>
53 : #include <mesosim/MEVehicle.h>
54 :
55 : #include "GUITriggeredRerouter.h"
56 : #include "GUIEdge.h"
57 : #include "GUIVehicle.h"
58 : #include "GUINet.h"
59 : #include "GUILane.h"
60 : #include "GUIPerson.h"
61 : #include "GUIContainer.h"
62 :
63 :
64 389675 : GUIEdge::GUIEdge(const std::string& id, int numericalID,
65 : const SumoXMLEdgeFunc function,
66 : const std::string& streetName, const std::string& edgeType,
67 : const std::string& routingType, int priority,
68 389675 : double distance) :
69 : MSEdge(id, numericalID, function, streetName, edgeType, routingType, priority, distance),
70 : GUIGlObject(GLO_EDGE, id, GUIIconSubSys::getIcon(GUIIcon::EDGE)),
71 389675 : myLock(true)
72 389675 : {}
73 :
74 :
75 777894 : GUIEdge::~GUIEdge() {
76 : // just to quit cleanly on a failure
77 388947 : if (myLock.locked()) {
78 0 : myLock.unlock();
79 : }
80 777894 : }
81 :
82 : void
83 383143 : GUIEdge::closeBuilding() {
84 383143 : MSEdge::closeBuilding();
85 : bool hasNormalSuccessors = false;
86 383143 : for (const MSEdge* out : getSuccessors()) {
87 374286 : if (!out->isTazConnector()) {
88 : hasNormalSuccessors = true;
89 : break;
90 : }
91 : }
92 383143 : myShowDeadEnd = (!isTazConnector() && !hasNormalSuccessors && getToJunction()->getOutgoing().size() > 0
93 3491 : && (getPermissions() & ~SVC_PEDESTRIAN) != 0
94 386402 : && (getToJunction()->getOutgoing().size() > 1 ||
95 2696 : getToJunction()->getOutgoing().front()->getToJunction() != getFromJunction()));
96 383143 : }
97 :
98 : MSLane&
99 0 : GUIEdge::getLane(int laneNo) {
100 : assert(laneNo < (int)myLanes->size());
101 0 : return *((*myLanes)[laneNo]);
102 : }
103 :
104 :
105 : std::vector<GUIGlID>
106 0 : GUIEdge::getIDs(bool includeInternal) {
107 : std::vector<GUIGlID> ret;
108 0 : ret.reserve(MSEdge::myDict.size());
109 0 : for (MSEdge::DictType::const_iterator i = MSEdge::myDict.begin(); i != MSEdge::myDict.end(); ++i) {
110 0 : const GUIEdge* edge = dynamic_cast<const GUIEdge*>(i->second);
111 : assert(edge);
112 0 : if (includeInternal || edge->isNormal()) {
113 0 : ret.push_back(edge->getGlID());
114 : }
115 : }
116 0 : return ret;
117 0 : }
118 :
119 :
120 : double
121 0 : GUIEdge::getTotalLength(bool includeInternal, bool eachLane) {
122 : double result = 0;
123 0 : for (MSEdge::DictType::const_iterator i = MSEdge::myDict.begin(); i != MSEdge::myDict.end(); ++i) {
124 0 : const MSEdge* edge = i->second;
125 0 : if (includeInternal || !edge->isInternal()) {
126 : // @note needs to be change once lanes may have different length
127 0 : result += edge->getLength() * (eachLane ? (double)edge->getLanes().size() : 1.);
128 : }
129 : }
130 0 : return result;
131 : }
132 :
133 :
134 : Boundary
135 0 : GUIEdge::getBoundary() const {
136 0 : Boundary ret;
137 0 : const bool s2 = GUIGlobals::gSecondaryShape;
138 0 : if (!isTazConnector()) {
139 0 : for (std::vector<MSLane*>::const_iterator i = myLanes->begin(); i != myLanes->end(); ++i) {
140 0 : ret.add((*i)->getShape(s2).getBoxBoundary());
141 : }
142 : } else {
143 : // take the starting coordinates of all follower edges and the endpoints
144 : // of all successor edges
145 0 : for (MSEdgeVector::const_iterator it = mySuccessors.begin(); it != mySuccessors.end(); ++it) {
146 0 : const std::vector<MSLane*>& lanes = (*it)->getLanes();
147 0 : for (std::vector<MSLane*>::const_iterator it_lane = lanes.begin(); it_lane != lanes.end(); ++it_lane) {
148 0 : ret.add((*it_lane)->getShape(s2).front());
149 : }
150 : }
151 0 : for (MSEdgeVector::const_iterator it = myPredecessors.begin(); it != myPredecessors.end(); ++it) {
152 0 : const std::vector<MSLane*>& lanes = (*it)->getLanes();
153 0 : for (std::vector<MSLane*>::const_iterator it_lane = lanes.begin(); it_lane != lanes.end(); ++it_lane) {
154 0 : ret.add((*it_lane)->getShape(s2).back());
155 : }
156 : }
157 : }
158 0 : ret.grow(10);
159 0 : return ret;
160 : }
161 :
162 :
163 : GUIGLObjectPopupMenu*
164 0 : GUIEdge::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {
165 0 : GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);
166 0 : buildPopupHeader(ret, app);
167 0 : buildCenterPopupEntry(ret);
168 0 : buildNameCopyPopupEntry(ret);
169 0 : buildSelectionPopupEntry(ret);
170 0 : if (MSGlobals::gUseMesoSim) {
171 0 : buildShowParamsPopupEntry(ret, false);
172 : // find lane under cursor for building a lane-param menu entry
173 : // @note: cannot use getLaneUnderCursor because this returns nullptr in meso
174 0 : GUILane* lane = dynamic_cast<GUILane*>(myLanes->front());
175 0 : const Position pos = parent.getPositionInformation();
176 0 : for (MSLane* l : *myLanes) {
177 0 : GUILane* cand = dynamic_cast<GUILane*>(l);
178 0 : if (cand->getCenter().distanceTo2D(pos) < lane->getCenter().distanceTo2D(pos)) {
179 : lane = cand;
180 : }
181 : }
182 0 : setGLObjectParent(lane);
183 0 : GUIDesigns::buildFXMenuCommand(ret, "Show Lane Param", GUIIconSubSys::getIcon(GUIIcon::APP_TABLE), ret, MID_SHOWPARS_PARENT);
184 0 : buildShowTypeParamsPopupEntry(ret);
185 : }
186 0 : MESegment* segment = getSegmentAtPosition(parent.getPositionInformation());
187 0 : GUIDesigns::buildFXMenuCommand(ret, "segment: " + toString(segment->getIndex()), nullptr, nullptr, 0);
188 0 : buildPositionCopyEntry(ret, app);
189 0 : return ret;
190 : }
191 :
192 :
193 : GUIParameterTableWindow*
194 0 : GUIEdge::getParameterWindow(GUIMainWindow& app,
195 : GUISUMOAbstractView& parent) {
196 : GUIParameterTableWindow* ret = nullptr;
197 0 : ret = new GUIParameterTableWindow(app, *this);
198 : // add edge items
199 0 : ret->mkItem(TL("max speed [m/s]"), false, getAllowedSpeed());
200 0 : ret->mkItem(TL("length [m]"), false, (*myLanes)[0]->getLength());
201 0 : ret->mkItem(TL("street name"), false, getStreetName());
202 0 : ret->mkItem(TL("pending insertions [#]"), true, new FunctionBinding<GUIEdge, double>(this, &GUIEdge::getPendingEmits));
203 0 : ret->mkItem(TL("mean friction [%]"), true, new FunctionBinding<GUIEdge, double>(this, &MSEdge::getMeanFriction, 100.));
204 0 : ret->mkItem(TL("mean vehicle speed [m/s]"), true, new FunctionBinding<GUIEdge, double>(this, &GUIEdge::getMeanSpeed));
205 0 : ret->mkItem(TL("routing speed [m/s]"), true, new FunctionBinding<MSEdge, double>(this, &MSEdge::getRoutingSpeed));
206 0 : ret->mkItem(TL("time penalty [s]"), true, new FunctionBinding<MSEdge, double>(this, &MSEdge::getTimePenalty));
207 0 : ret->mkItem(TL("brutto occupancy [%]"), true, new FunctionBinding<GUIEdge, double>(this, &GUIEdge::getBruttoOccupancy, 100.));
208 0 : ret->mkItem(TL("edge flow [veh/h/m]"), true, new FunctionBinding<GUIEdge, double>(this, &GUIEdge::getFlow));
209 0 : ret->mkItem(TL("vehicles [#]"), true, new CastingFunctionBinding<GUIEdge, int, int>(this, &MSEdge::getVehicleNumber));
210 : // add segment items
211 0 : MESegment* segment = getSegmentAtPosition(parent.getPositionInformation());
212 0 : ret->mkItem(TL("segment index"), false, segment->getIndex());
213 0 : ret->mkItem(TL("segment queues"), false, segment->numQueues());
214 0 : ret->mkItem(TL("segment length [m]"), false, segment->getLength());
215 0 : ret->mkItem(TL("segment allowed speed [m/s]"), false, segment->getEdge().getSpeedLimit());
216 0 : ret->mkItem(TL("segment jam threshold [%]"), false, segment->getRelativeJamThreshold() * 100);
217 0 : ret->mkItem(TL("segment brutto occupancy [%]"), true, new FunctionBinding<MESegment, double>(segment, &MESegment::getRelativeOccupancy, 100));
218 0 : ret->mkItem(TL("segment mean vehicle speed [m/s]"), true, new FunctionBinding<MESegment, double>(segment, &MESegment::getMeanSpeed));
219 0 : ret->mkItem(TL("segment flow [veh/h/m]"), true, new FunctionBinding<MESegment, double>(segment, &MESegment::getFlow));
220 0 : ret->mkItem(TL("segment vehicles [#]"), true, new CastingFunctionBinding<MESegment, int, int>(segment, &MESegment::getCarNumber));
221 0 : ret->mkItem(TL("segment leader leave time"), true, new FunctionBinding<MESegment, double>(segment, &MESegment::getEventTimeSeconds));
222 0 : ret->mkItem(TL("segment headway [s]"), true, new FunctionBinding<MESegment, double>(segment, &MESegment::getLastHeadwaySeconds));
223 0 : ret->mkItem(TL("segment entry block time [s]"), true, new FunctionBinding<MESegment, double>(segment, &MESegment::getEntryBlockTimeSeconds));
224 : // lane params
225 0 : for (MSLane* lane : *myLanes) {
226 0 : for (const auto& kv : lane->getParametersMap()) {
227 0 : ret->mkItem(("laneParam " + toString(lane->getIndex()) + ":" + kv.first).c_str(), false, kv.second);
228 : }
229 : }
230 : // close building
231 0 : ret->closeBuilding();
232 0 : return ret;
233 : }
234 :
235 : GUIParameterTableWindow*
236 0 : GUIEdge::getTypeParameterWindow(GUIMainWindow& app,
237 : GUISUMOAbstractView&) {
238 0 : GUIParameterTableWindow* ret = new GUIParameterTableWindow(app, *this);
239 0 : const MESegment::MesoEdgeType& edgeType = MSNet::getInstance()->getMesoType(getEdgeType());
240 : // add items
241 0 : ret->mkItem(TL("Type Information:"), false, "");
242 0 : ret->mkItem(TL("type [id]"), false, getEdgeType());
243 0 : ret->mkItem(TL("routing type [id]"), false, getRoutingType());
244 0 : ret->mkItem(TL("tauff"), false, STEPS2TIME(edgeType.tauff));
245 0 : ret->mkItem(TL("taufj"), false, STEPS2TIME(edgeType.taufj));
246 0 : ret->mkItem(TL("taujf"), false, STEPS2TIME(edgeType.taujf));
247 0 : ret->mkItem(TL("taujj"), false, STEPS2TIME(edgeType.taujj));
248 0 : ret->mkItem(TL("jam threshold"), false, edgeType.jamThreshold);
249 0 : ret->mkItem(TL("junction control"), false, edgeType.junctionControl);
250 0 : ret->mkItem(TL("tls penalty"), false, edgeType.tlsPenalty);
251 0 : ret->mkItem(TL("tls flow penalty"), false, edgeType.tlsFlowPenalty);
252 0 : ret->mkItem(TL("minor penalty"), false, STEPS2TIME(edgeType.minorPenalty));
253 0 : ret->mkItem(TL("overtaking"), false, edgeType.overtaking);
254 : // close building
255 0 : ret->closeBuilding();
256 0 : return ret;
257 : }
258 :
259 :
260 : Boundary
261 0 : GUIEdge::getCenteringBoundary() const {
262 0 : Boundary b = getBoundary();
263 : // ensure that vehicles and persons on the side are drawn even if the edge
264 : // is outside the view
265 0 : b.grow(10);
266 0 : return b;
267 : }
268 :
269 : const std::string
270 0 : GUIEdge::getOptionalName() const {
271 0 : return myStreetName;
272 : }
273 :
274 : void
275 40149326 : GUIEdge::drawGL(const GUIVisualizationSettings& s) const {
276 40149326 : if (s.hideConnectors && myFunction == SumoXMLEdgeFunc::CONNECTOR) {
277 : return;
278 : }
279 40149326 : GLHelper::pushName(getGlID());
280 : // draw the lanes
281 40149326 : if (MSGlobals::gUseMesoSim) {
282 5084276 : setColor(s);
283 : }
284 88589610 : for (std::vector<MSLane*>::const_iterator i = myLanes->begin(); i != myLanes->end(); ++i) {
285 48440284 : static_cast<GUILane*>(*i)->drawGL(s);
286 : }
287 40149326 : if (MSGlobals::gUseMesoSim) {
288 5084276 : if (s.scale * s.vehicleSize.getExaggeration(s, nullptr) > s.vehicleSize.minSize) {
289 1526916 : drawMesoVehicles(s);
290 : }
291 : }
292 40149326 : GLHelper::popName();
293 : // (optionally) draw the name and/or the street name
294 40149326 : GUILane* lane2 = dynamic_cast<GUILane*>((*myLanes).back());
295 40149326 : const GUIGlObject* selCheck = gSelected.isSelected(this) ? (GUIGlObject*)this : (GUIGlObject*)anySelectedLane();
296 40149326 : const bool drawEdgeName = s.edgeName.show(selCheck) && myFunction == SumoXMLEdgeFunc::NORMAL;
297 40149326 : const bool drawInternalEdgeName = s.internalEdgeName.show(selCheck) && myFunction == SumoXMLEdgeFunc::INTERNAL;
298 40149326 : const bool drawCwaEdgeName = s.cwaEdgeName.show(selCheck) && (myFunction == SumoXMLEdgeFunc::CROSSING || myFunction == SumoXMLEdgeFunc::WALKINGAREA);
299 40149326 : const bool drawStreetName = s.streetName.show(selCheck) && myStreetName != "";
300 40149326 : const bool drawEdgeValue = s.edgeValue.show(selCheck) && (myFunction == SumoXMLEdgeFunc::NORMAL
301 0 : || (myFunction == SumoXMLEdgeFunc::INTERNAL && !s.drawJunctionShape)
302 0 : || ((myFunction == SumoXMLEdgeFunc::CROSSING || myFunction == SumoXMLEdgeFunc::WALKINGAREA) && s.drawCrossingsAndWalkingareas));
303 40149326 : const bool drawEdgeScaleValue = s.edgeScaleValue.show(selCheck) && (myFunction == SumoXMLEdgeFunc::NORMAL
304 0 : || (myFunction == SumoXMLEdgeFunc::INTERNAL && !s.drawJunctionShape)
305 0 : || ((myFunction == SumoXMLEdgeFunc::CROSSING || myFunction == SumoXMLEdgeFunc::WALKINGAREA) && s.drawCrossingsAndWalkingareas));
306 40149326 : if (drawEdgeName || drawInternalEdgeName || drawCwaEdgeName || drawStreetName || drawEdgeValue || drawEdgeScaleValue) {
307 0 : GUILane* lane1 = dynamic_cast<GUILane*>((*myLanes)[0]);
308 0 : if (lane1 != nullptr && lane2 != nullptr) {
309 0 : const bool s2 = s.secondaryShape;
310 0 : const bool spreadSuperposed = s.spreadSuperposed && getBidiEdge() != nullptr;
311 0 : Position p = lane1->getShape(s2).positionAtOffset(lane1->getShape(s2).length() / (double) 2.);
312 0 : p.add(lane2->getShape(s2).positionAtOffset(lane2->getShape(s2).length() / (double) 2.));
313 : p.mul(.5);
314 0 : if (spreadSuperposed) {
315 : // move name to the right of the edge and towards its beginning
316 0 : const double dist = 0.6 * s.edgeName.scaledSize(s.scale);
317 0 : const double shiftA = lane1->getShape(s2).rotationAtOffset(lane1->getShape(s2).length() / (double) 2.) - DEG2RAD(135);
318 0 : Position shift(dist * cos(shiftA), dist * sin(shiftA));
319 : p.add(shift);
320 : }
321 0 : double angle = s.getTextAngle(lane1->getShape(s2).rotationDegreeAtOffset(lane1->getShape(s2).length() / (double) 2.) + 90);
322 0 : if (drawEdgeName) {
323 0 : drawName(p, s.scale, s.edgeName, angle, true);
324 0 : } else if (drawInternalEdgeName) {
325 0 : drawName(p, s.scale, s.internalEdgeName, angle, true);
326 0 : } else if (drawCwaEdgeName) {
327 0 : drawName(p, s.scale, s.cwaEdgeName, angle, true);
328 : }
329 0 : if (drawStreetName) {
330 0 : GLHelper::drawTextSettings(s.streetName, getStreetName(), p, s.scale, angle);
331 : }
332 0 : if (drawEdgeValue) {
333 0 : const int activeScheme = s.getLaneEdgeMode();
334 0 : std::string value = "";
335 0 : if (activeScheme == 31) {
336 : // edge param, could be non-numerical
337 0 : value = getParameter(s.edgeParam, "");
338 0 : } else if (activeScheme == 32) {
339 : // lane param, could be non-numerical
340 0 : value = lane2->getParameter(s.laneParam, "");
341 : } else {
342 : // use numerical value value of leftmost lane to hopefully avoid sidewalks, bikelanes etc
343 : const double doubleValue = (MSGlobals::gUseMesoSim
344 0 : ? getColorValue(s, activeScheme)
345 0 : : lane2->getColorValueWithFunctional(s, activeScheme));
346 0 : const RGBColor color = (MSGlobals::gUseMesoSim ? s.edgeColorer : s.laneColorer).getScheme().getColor(doubleValue);
347 0 : if (doubleValue != s.MISSING_DATA
348 0 : && color.alpha() != 0
349 0 : && (!s.edgeValueRainBow.hideMin || doubleValue > s.edgeValueRainBow.minThreshold)
350 0 : && (!s.edgeValueRainBow.hideMax || doubleValue < s.edgeValueRainBow.maxThreshold)
351 : ) {
352 0 : value = toString(doubleValue);
353 : }
354 : }
355 0 : if (value != "") {
356 0 : if (drawEdgeName || drawInternalEdgeName || drawCwaEdgeName) {
357 0 : const double dist = 0.4 * (s.edgeName.scaledSize(s.scale) + s.edgeValue.scaledSize(s.scale));
358 0 : const double shiftA = lane1->getShape(s2).rotationAtOffset(lane1->getShape(s2).length() / (double) 2.) - DEG2RAD(90);
359 0 : Position shift(dist * cos(shiftA), dist * sin(shiftA));
360 : p.add(shift);
361 : }
362 0 : GLHelper::drawTextSettings(s.edgeValue, value, p, s.scale, angle);
363 : }
364 : }
365 0 : if (drawEdgeScaleValue) {
366 0 : const int activeScheme = s.getLaneEdgeScaleMode();
367 0 : std::string value = "";
368 : // use numerical value value of leftmost lane to hopefully avoid sidewalks, bikelanes etc
369 : const double doubleValue = (MSGlobals::gUseMesoSim
370 0 : ? getScaleValue(s, activeScheme)
371 0 : : lane2->getScaleValue(s, activeScheme, s2));
372 0 : if (doubleValue != s.MISSING_DATA) {
373 0 : value = toString(doubleValue);
374 : }
375 0 : if (value != "") {
376 0 : if (drawEdgeName || drawInternalEdgeName || drawCwaEdgeName || drawEdgeValue) {
377 0 : const double dist = 0.4 * (s.edgeName.scaledSize(s.scale) + s.edgeScaleValue.scaledSize(s.scale));
378 0 : const double shiftA = lane1->getShape(s2).rotationAtOffset(lane1->getShape(s2).length() / (double) 2.) - DEG2RAD(90);
379 0 : Position shift(dist * cos(shiftA), dist * sin(shiftA));
380 : p.add(shift);
381 : }
382 0 : GLHelper::drawTextSettings(s.edgeScaleValue, value, p, s.scale, angle);
383 : }
384 : }
385 : }
386 : }
387 40149326 : if (s.scale * s.personSize.getExaggeration(s, nullptr) > s.personSize.minSize) {
388 14492269 : FXMutexLock locker(myLock);
389 15599193 : for (MSTransportable* t : myPersons) {
390 1106924 : GUIPerson* person = dynamic_cast<GUIPerson*>(t);
391 : assert(person != 0);
392 1106924 : person->drawGL(s);
393 : }
394 : }
395 40149326 : if (s.scale * s.containerSize.getExaggeration(s, nullptr) > s.containerSize.minSize) {
396 14492269 : FXMutexLock locker(myLock);
397 14508756 : for (MSTransportable* t : myContainers) {
398 16487 : GUIContainer* container = dynamic_cast<GUIContainer*>(t);
399 : assert(container != 0);
400 16487 : container->drawGL(s);
401 : }
402 : }
403 : }
404 :
405 :
406 : void
407 1526916 : GUIEdge::drawMesoVehicles(const GUIVisualizationSettings& s) const {
408 1526916 : GUIMEVehicleControl* vehicleControl = GUINet::getGUIInstance()->getGUIMEVehicleControl();
409 1526916 : if (vehicleControl != nullptr) {
410 1526916 : vehicleControl->secureVehicles();
411 1526916 : FXMutexLock locker(myLock);
412 4466060 : for (const auto& item : getMesoPositions()) {
413 2939144 : const GUIMEVehicle* const veh = static_cast<const GUIMEVehicle*>(item.first);
414 2939144 : const GUILane* const lane = static_cast<GUILane*>((*myLanes)[veh->getQueIndex()]);
415 : assert(this == &veh->getSegment()->getEdge());
416 2939144 : const Position p = lane->geometryPositionAtOffset(item.second.first, (double)item.second.second * 0.5);
417 2939144 : const double angle = lane->getShape(s.secondaryShape).rotationAtOffset(lane->interpolateLanePosToGeometryPos(item.second.first));
418 2939144 : veh->drawOnPos(s, p, angle);
419 : }
420 1526916 : vehicleControl->releaseVehicles();
421 : }
422 1526916 : }
423 :
424 :
425 :
426 : double
427 0 : GUIEdge::getAllowedSpeed() const {
428 0 : return (*myLanes)[0]->getSpeedLimit();
429 : }
430 :
431 :
432 : double
433 0 : GUIEdge::getRelativeSpeed() const {
434 0 : return getMeanSpeed() / getAllowedSpeed();
435 : }
436 :
437 :
438 : void
439 5084276 : GUIEdge::setColor(const GUIVisualizationSettings& s) const {
440 5084276 : myMesoColor = RGBColor(0, 0, 0); // default background color when using multiColor
441 5084276 : const GUIColorer& c = s.edgeColorer;
442 : mySegmentColors.clear();
443 5084276 : if (!setFunctionalColor(c) && !setMultiColor(c)) {
444 5075577 : myMesoColor = c.getScheme().getColor(getColorValue(s, c.getActive()));
445 : }
446 5084276 : }
447 :
448 :
449 : bool
450 5084276 : GUIEdge::setFunctionalColor(const GUIColorer& c) const {
451 : const int activeScheme = c.getActive();
452 : int activeMicroScheme = -1;
453 5084276 : switch (activeScheme) {
454 : case 0:
455 : activeMicroScheme = 0; // color uniform
456 : break;
457 0 : case 9:
458 : activeMicroScheme = 18; // color by angle
459 0 : break;
460 0 : case 17:
461 : activeMicroScheme = 30; // color by TAZ
462 0 : break;
463 : default:
464 : return false;
465 : }
466 5084276 : GUILane* guiLane = static_cast<GUILane*>(getLanes()[0]);
467 5084276 : return guiLane->setFunctionalColor(c, myMesoColor, activeMicroScheme);
468 : }
469 :
470 :
471 : bool
472 5075577 : GUIEdge::setMultiColor(const GUIColorer& c) const {
473 : const int activeScheme = c.getActive();
474 5075577 : switch (activeScheme) {
475 0 : case 10: // alternating segments
476 0 : for (MESegment* segment = MSGlobals::gMesoNet->getSegmentForEdge(*this);
477 0 : segment != nullptr; segment = segment->getNextSegment()) {
478 0 : mySegmentColors.push_back(c.getScheme().getColor(segment->getIndex() % 2));
479 : }
480 : //std::cout << getID() << " scheme=" << c.getScheme().getName() << " schemeCols=" << c.getScheme().getColors().size() << " thresh=" << toString(c.getScheme().getThresholds()) << " segmentColors=" << mySegmentColors.size() << " [0]=" << mySegmentColors[0] << " [1]=" << mySegmentColors[1] << "\n";
481 : return true;
482 0 : case 11: // by segment jammed state
483 0 : for (MESegment* segment = MSGlobals::gMesoNet->getSegmentForEdge(*this);
484 0 : segment != nullptr; segment = segment->getNextSegment()) {
485 0 : mySegmentColors.push_back(
486 0 : c.getScheme().getColor(segment->getRelativeOccupancy() > segment->getRelativeJamThreshold() ? 2 :
487 0 : (segment->getRelativeOccupancy() * 2 < segment->getRelativeJamThreshold() ? 0 : 1)));
488 : }
489 : return true;
490 0 : case 12: // by segment occupancy
491 0 : for (MESegment* segment = MSGlobals::gMesoNet->getSegmentForEdge(*this);
492 0 : segment != nullptr; segment = segment->getNextSegment()) {
493 0 : mySegmentColors.push_back(c.getScheme().getColor(segment->getRelativeOccupancy()));
494 : }
495 : return true;
496 0 : case 13: // by segment speed
497 0 : for (MESegment* segment = MSGlobals::gMesoNet->getSegmentForEdge(*this);
498 0 : segment != nullptr; segment = segment->getNextSegment()) {
499 0 : mySegmentColors.push_back(c.getScheme().getColor(segment->getMeanSpeed()));
500 : }
501 : return true;
502 0 : case 14: // by segment flow
503 0 : for (MESegment* segment = MSGlobals::gMesoNet->getSegmentForEdge(*this);
504 0 : segment != nullptr; segment = segment->getNextSegment()) {
505 0 : mySegmentColors.push_back(c.getScheme().getColor(3600 * segment->getCarNumber() * segment->getMeanSpeed() / segment->getLength()));
506 : }
507 : return true;
508 0 : case 15: // by segment relative speed
509 0 : for (MESegment* segment = MSGlobals::gMesoNet->getSegmentForEdge(*this);
510 0 : segment != nullptr; segment = segment->getNextSegment()) {
511 0 : mySegmentColors.push_back(c.getScheme().getColor(segment->getMeanSpeed() / getAllowedSpeed()));
512 : }
513 : return true;
514 : default:
515 : return false;
516 : }
517 : }
518 :
519 :
520 : double
521 5075577 : GUIEdge::getColorValue(const GUIVisualizationSettings& s, int activeScheme) const {
522 5075577 : switch (activeScheme) {
523 0 : case 1:
524 0 : return gSelected.isSelected(getType(), getGlID());
525 0 : case 2:
526 0 : return (double)getFunction();
527 0 : case 3:
528 0 : return getAllowedSpeed();
529 0 : case 4:
530 0 : return getBruttoOccupancy();
531 0 : case 5:
532 0 : return getMeanSpeed();
533 0 : case 6:
534 0 : return getFlow();
535 0 : case 7:
536 0 : return getRelativeSpeed();
537 0 : case 8:
538 0 : return getRoutingSpeed();
539 0 : case 16:
540 0 : return getPendingEmits();
541 0 : case 18:
542 : // by numerical edge param value
543 : try {
544 0 : return StringUtils::toDouble(getParameter(s.edgeParam, "0"));
545 0 : } catch (NumberFormatException&) {
546 : try {
547 0 : return StringUtils::toBool(getParameter(s.edgeParam, "0"));
548 0 : } catch (BoolFormatException&) {
549 : return -1;
550 0 : }
551 0 : }
552 0 : case 19:
553 : // by edge data value
554 0 : return GUINet::getGUIInstance()->getEdgeData(this, s.edgeData);
555 : }
556 : return 0;
557 : }
558 :
559 :
560 : double
561 6444023 : GUIEdge::getScaleValue(const GUIVisualizationSettings& s, int activeScheme) const {
562 6444023 : switch (activeScheme) {
563 0 : case 1:
564 0 : return gSelected.isSelected(getType(), getGlID());
565 0 : case 2:
566 0 : return getAllowedSpeed();
567 0 : case 3:
568 0 : return getBruttoOccupancy();
569 0 : case 4:
570 0 : return getMeanSpeed();
571 0 : case 5:
572 0 : return getFlow();
573 0 : case 6:
574 0 : return getRelativeSpeed();
575 0 : case 7:
576 0 : return getPendingEmits();
577 0 : case 8:
578 : // by edge data value
579 0 : return GUINet::getGUIInstance()->getEdgeData(this, s.edgeDataScaling);
580 : }
581 : return 0;
582 : }
583 :
584 :
585 : MESegment*
586 0 : GUIEdge::getSegmentAtPosition(const Position& pos) {
587 0 : const PositionVector& shape = getLanes()[0]->getShape();
588 0 : const double lanePos = shape.nearest_offset_to_point2D(pos);
589 0 : return MSGlobals::gMesoNet->getSegmentForEdge(*this, lanePos);
590 : }
591 :
592 :
593 :
594 : void
595 0 : GUIEdge::closeTraffic(const GUILane* lane) {
596 : const std::vector<MSLane*>& lanes = getLanes();
597 : const bool isClosed = lane->isClosed();
598 0 : for (std::vector<MSLane*>::const_iterator i = lanes.begin(); i != lanes.end(); ++i) {
599 0 : GUILane* l = dynamic_cast<GUILane*>(*i);
600 0 : if (l->isClosed() == isClosed) {
601 0 : l->closeTraffic(false);
602 : }
603 : }
604 0 : rebuildAllowedLanes();
605 0 : }
606 :
607 :
608 : void
609 0 : GUIEdge::addRerouter() {
610 : MSEdgeVector edges;
611 0 : edges.push_back(this);
612 0 : GUITriggeredRerouter* rr = new GUITriggeredRerouter(getID() + "_dynamic_rerouter", edges, 1, false, false, 0, "", Position::INVALID,
613 0 : std::numeric_limits<double>::max(), GUINet::getGUIInstance()->getVisualisationSpeedUp());
614 :
615 0 : MSTriggeredRerouter::RerouteInterval ri;
616 0 : ri.begin = MSNet::getInstance()->getCurrentTimeStep();
617 0 : ri.end = SUMOTime_MAX;
618 0 : ri.edgeProbs.add(&MSTriggeredRerouter::mySpecialDest_keepDestination, 1.);
619 0 : rr->myIntervals.push_back(ri);
620 :
621 : // trigger rerouting for vehicles already on this edge
622 : const std::vector<MSLane*>& lanes = getLanes();
623 0 : for (std::vector<MSLane*>::const_iterator i = lanes.begin(); i != lanes.end(); ++i) {
624 0 : const MSLane::VehCont& vehicles = (*i)->getVehiclesSecure();
625 0 : for (MSLane::VehCont::const_iterator v = vehicles.begin(); v != vehicles.end(); ++v) {
626 0 : if ((*v)->getLane() == (*i)) {
627 0 : rr->notifyEnter(**v, MSMoveReminder::NOTIFICATION_JUNCTION);
628 : } // else: this is the shadow during a continuous lane change
629 : }
630 0 : (*i)->releaseVehicles();
631 : }
632 0 : }
633 :
634 :
635 : bool
636 0 : GUIEdge::isSelected() const {
637 0 : return gSelected.isSelected(GLO_EDGE, getGlID());
638 : }
639 :
640 : double
641 0 : GUIEdge::getPendingEmits() const {
642 0 : return MSNet::getInstance()->getInsertionControl().getPendingEmits(getLanes()[0]);
643 : }
644 :
645 : double
646 0 : GUIEdge::getClickPriority() const {
647 0 : if (!MSGlobals::gUseMesoSim) {
648 : // do not select edgse in meso mode
649 0 : return INVALID_PRIORITY;
650 : }
651 : return GLO_EDGE;
652 : }
653 :
654 :
655 : GUILane*
656 40149007 : GUIEdge::anySelectedLane() const {
657 40149007 : MSLane* result = myLanes->back();
658 88588972 : for (MSLane* lane : *myLanes) {
659 48439965 : if (lane->isSelected()) {
660 : result = lane;
661 : break;
662 : }
663 : }
664 40149007 : return dynamic_cast<GUILane*>(result);
665 : }
666 : /****************************************************************************/
|