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    GUIViewTraffic.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Christian Roessel
14 /// @author  Michael Behrisch
15 /// @author  Andreas Gaubatz
16 /// @date    Sept 2002
17 /// @version $Id$
18 ///
19 // A view on the simulation; this view is a microscopic one
20 /****************************************************************************/
21 
22 
23 // ===========================================================================
24 // included modules
25 // ===========================================================================
26 #include <config.h>
27 
28 #ifdef HAVE_FFMPEG
29 #include <utils/gui/div/GUIVideoEncoder.h>
30 #endif
31 
32 #include <iostream>
33 #include <utility>
34 #include <cmath>
35 #include <limits>
36 #include <guisim/GUINet.h>
37 #include <guisim/GUIEdge.h>
38 #include <guisim/GUILane.h>
39 #include <guisim/GUIVehicle.h>
40 #include <microsim/MSGlobals.h>
41 #include <microsim/MSEdge.h>
42 #include <microsim/MSLane.h>
43 #include <microsim/MSJunctionControl.h>
44 #include <microsim/traffic_lights/MSTLLogicControl.h>
45 #include <microsim/traffic_lights/MSSimpleTrafficLightLogic.h>
46 #include <utils/common/RGBColor.h>
47 #include <utils/geom/PositionVector.h>
48 #include "GUISUMOViewParent.h"
49 #include "GUIViewTraffic.h"
50 #include <utils/gui/windows/GUISUMOAbstractView.h>
51 #include <utils/gui/windows/GUIPerspectiveChanger.h>
52 #include <utils/gui/windows/GUIAppEnum.h>
53 #include <utils/foxtools/MFXCheckableButton.h>
54 #include <utils/gui/images/GUIIconSubSys.h>
55 #include <gui/GUIApplicationWindow.h>
56 #include <utils/gui/windows/GUIDialog_ViewSettings.h>
57 #include <utils/gui/settings/GUICompleteSchemeStorage.h>
58 #include <utils/foxtools/MFXImageHelper.h>
59 #include <utils/gui/globjects/GUIGlObjectStorage.h>
60 #include <foreign/rtree/SUMORTree.h>
61 #include <utils/gui/div/GLHelper.h>
62 #include <utils/gui/div/GUIGlobalSelection.h>
63 #include <utils/gui/globjects/GLIncludes.h>
64 
65 /* -------------------------------------------------------------------------
66  * GUIViewTraffic - FOX callback mapping
67  * ----------------------------------------------------------------------- */
68 FXDEFMAP(GUIViewTraffic) GUIViewTrafficMap[] = {
69     FXMAPFUNC(SEL_COMMAND, MID_CLOSE_LANE, GUIViewTraffic::onCmdCloseLane),
70     FXMAPFUNC(SEL_COMMAND, MID_CLOSE_EDGE, GUIViewTraffic::onCmdCloseEdge),
71     FXMAPFUNC(SEL_COMMAND, MID_ADD_REROUTER, GUIViewTraffic::onCmdAddRerouter),
72 };
73 
74 
FXIMPLEMENT_ABSTRACT(GUIViewTraffic,GUISUMOAbstractView,GUIViewTrafficMap,ARRAYNUMBER (GUIViewTrafficMap))75 FXIMPLEMENT_ABSTRACT(GUIViewTraffic, GUISUMOAbstractView, GUIViewTrafficMap, ARRAYNUMBER(GUIViewTrafficMap))
76 
77 
78 // ===========================================================================
79 // member method definitions
80 // ===========================================================================
81 GUIViewTraffic::GUIViewTraffic(
82     FXComposite* p,
83     GUIMainWindow& app,
84     GUISUMOViewParent* parent,
85     GUINet& net, FXGLVisual* glVis,
86     FXGLCanvas* share) :
87     GUISUMOAbstractView(p, app, parent, net.getVisualisationSpeedUp(), glVis, share),
88     myTrackedID(GUIGlObject::INVALID_ID),
89     myTLSGame(OptionsCont::getOptions().getString("game.mode") == "tls")
90 #ifdef HAVE_FFMPEG
91     , myCurrentVideo(nullptr)
92 #endif
93 {}
94 
95 
~GUIViewTraffic()96 GUIViewTraffic::~GUIViewTraffic() {
97     endSnapshot();
98 }
99 
100 
101 void
buildViewToolBars(GUIGlChildWindow & v)102 GUIViewTraffic::buildViewToolBars(GUIGlChildWindow& v) {
103     // build coloring tools
104     {
105         const std::vector<std::string>& names = gSchemeStorage.getNames();
106         for (std::vector<std::string>::const_iterator i = names.begin(); i != names.end(); ++i) {
107             v.getColoringSchemesCombo()->appendItem(i->c_str());
108             if ((*i) == myVisualizationSettings->name) {
109                 v.getColoringSchemesCombo()->setCurrentItem(v.getColoringSchemesCombo()->getNumItems() - 1);
110             }
111         }
112         v.getColoringSchemesCombo()->setNumVisible(MAX2(5, (int)names.size() + 1));
113     }
114     // for junctions
115     new FXButton(v.getLocatorPopup(),
116                  "\tLocate Junction\tLocate a junction within the network.",
117                  GUIIconSubSys::getIcon(ICON_LOCATEJUNCTION), &v, MID_LOCATEJUNCTION,
118                  ICON_ABOVE_TEXT | FRAME_THICK | FRAME_RAISED);
119     // for edges
120     new FXButton(v.getLocatorPopup(),
121                  "\tLocate Street\tLocate a street within the network.",
122                  GUIIconSubSys::getIcon(ICON_LOCATEEDGE), &v, MID_LOCATEEDGE,
123                  ICON_ABOVE_TEXT | FRAME_THICK | FRAME_RAISED);
124 
125     // for vehicles
126     new FXButton(v.getLocatorPopup(),
127                  "\tLocate Vehicle\tLocate a vehicle within the network.",
128                  GUIIconSubSys::getIcon(ICON_LOCATEVEHICLE), &v, MID_LOCATEVEHICLE,
129                  ICON_ABOVE_TEXT | FRAME_THICK | FRAME_RAISED);
130 
131     // for persons
132     if (!MSGlobals::gUseMesoSim) { // there are no persons in mesosim (yet)
133         new FXButton(v.getLocatorPopup(),
134                      "\tLocate Vehicle\tLocate a person within the network.",
135                      GUIIconSubSys::getIcon(ICON_LOCATEPERSON), &v, MID_LOCATEPERSON,
136                      ICON_ABOVE_TEXT | FRAME_THICK | FRAME_RAISED);
137     }
138 
139     // for tls
140     new FXButton(v.getLocatorPopup(),
141                  "\tLocate TLS\tLocate a tls within the network.",
142                  GUIIconSubSys::getIcon(ICON_LOCATETLS), &v, MID_LOCATETLS,
143                  ICON_ABOVE_TEXT | FRAME_THICK | FRAME_RAISED);
144     // for additional stuff
145     new FXButton(v.getLocatorPopup(),
146                  "\tLocate Additional\tLocate an additional structure within the network.",
147                  GUIIconSubSys::getIcon(ICON_LOCATEADD), &v, MID_LOCATEADD,
148                  ICON_ABOVE_TEXT | FRAME_THICK | FRAME_RAISED);
149     // for pois
150     new FXButton(v.getLocatorPopup(),
151                  "\tLocate PoI\tLocate a PoI within the network.",
152                  GUIIconSubSys::getIcon(ICON_LOCATEPOI), &v, MID_LOCATEPOI,
153                  ICON_ABOVE_TEXT | FRAME_THICK | FRAME_RAISED);
154     // for polygons
155     new FXButton(v.getLocatorPopup(),
156                  "\tLocate Polygon\tLocate a Polygon within the network.",
157                  GUIIconSubSys::getIcon(ICON_LOCATEPOLY), &v, MID_LOCATEPOLY,
158                  ICON_ABOVE_TEXT | FRAME_THICK | FRAME_RAISED);
159 }
160 
161 
162 bool
setColorScheme(const std::string & name)163 GUIViewTraffic::setColorScheme(const std::string& name) {
164     if (!gSchemeStorage.contains(name)) {
165         return false;
166     }
167     if (myVisualizationChanger != nullptr) {
168         if (myVisualizationChanger->getCurrentScheme() != name) {
169             myVisualizationChanger->setCurrentScheme(name);
170         }
171     }
172     myVisualizationSettings = &gSchemeStorage.get(name.c_str());
173     myVisualizationSettings->gaming = myApp->isGaming();
174     update();
175     return true;
176 }
177 
178 
179 void
buildColorRainbow(const GUIVisualizationSettings & s,GUIColorScheme & scheme,int active,GUIGlObjectType objectType)180 GUIViewTraffic::buildColorRainbow(const GUIVisualizationSettings& s, GUIColorScheme& scheme, int active, GUIGlObjectType objectType) {
181     assert(!scheme.isFixed());
182     double minValue = std::numeric_limits<double>::infinity();
183     double maxValue = -std::numeric_limits<double>::infinity();
184     // retrieve range
185     if (objectType == GLO_LANE) {
186         // XXX (see #3409) multi-colors are not currently handled. this is a quick hack
187         if (active == 22) {
188             active = 21; // segment height, fall back to start height
189         } else if (active == 24) {
190             active = 23; // segment incline, fall back to total incline
191         }
192         const MSEdgeVector& edges = MSEdge::getAllEdges();
193         for (MSEdgeVector::const_iterator it = edges.begin(); it != edges.end(); ++it) {
194             if (MSGlobals::gUseMesoSim) {
195                 const double val = static_cast<GUIEdge*>(*it)->getColorValue(s, active);
196                 minValue = MIN2(minValue, val);
197                 maxValue = MAX2(maxValue, val);
198             } else {
199                 const std::vector<MSLane*>& lanes = (*it)->getLanes();
200                 for (std::vector<MSLane*>::const_iterator it_l = lanes.begin(); it_l != lanes.end(); it_l++) {
201                     const double val = static_cast<GUILane*>(*it_l)->getColorValue(s, active);
202                     minValue = MIN2(minValue, val);
203                     maxValue = MAX2(maxValue, val);
204                 }
205             }
206         }
207     } else if (objectType == GLO_JUNCTION) {
208         if (active == 3) {
209             std::set<const MSJunction*> junctions;
210             for (MSEdge* edge : MSEdge::getAllEdges()) {
211                 junctions.insert(edge->getFromJunction());
212                 junctions.insert(edge->getToJunction());
213             }
214             for (const MSJunction* junction : junctions) {
215                 minValue = MIN2(minValue, junction->getPosition().z());
216                 maxValue = MAX2(maxValue, junction->getPosition().z());
217             }
218         }
219     }
220     if (minValue != std::numeric_limits<double>::infinity()) {
221         scheme.clear();
222         // add new thresholds
223         double range = maxValue - minValue;
224         scheme.addColor(RGBColor::RED, (minValue));
225         scheme.addColor(RGBColor::ORANGE, (minValue + range * 1 / 6.0));
226         scheme.addColor(RGBColor::YELLOW, (minValue + range * 2 / 6.0));
227         scheme.addColor(RGBColor::GREEN, (minValue + range * 3 / 6.0));
228         scheme.addColor(RGBColor::CYAN, (minValue + range * 4 / 6.0));
229         scheme.addColor(RGBColor::BLUE, (minValue + range * 5 / 6.0));
230         scheme.addColor(RGBColor::MAGENTA, (maxValue));
231     }
232 }
233 
234 
235 std::vector<std::string>
getEdgeDataAttrs() const236 GUIViewTraffic::getEdgeDataAttrs() const {
237     if (GUINet::getGUIInstance() != nullptr) {
238         return GUINet::getGUIInstance()->getEdgeDataAttrs();
239     }
240     return std::vector<std::string>();
241 }
242 
243 
244 std::vector<std::string>
getEdgeLaneParamKeys(bool edgeKeys) const245 GUIViewTraffic::getEdgeLaneParamKeys(bool edgeKeys) const {
246     std::set<std::string> keys;
247     for (const MSEdge* e : MSEdge::getAllEdges()) {
248         if (edgeKeys) {
249             for (const auto& item : e->getParametersMap()) {
250                 keys.insert(item.first);
251             }
252         } else {
253             for (const auto lane : e->getLanes()) {
254                 for (const auto& item : lane->getParametersMap()) {
255                     keys.insert(item.first);
256                 }
257             }
258         }
259     }
260     return std::vector<std::string>(keys.begin(), keys.end());
261 }
262 
263 
264 int
doPaintGL(int mode,const Boundary & bound)265 GUIViewTraffic::doPaintGL(int mode, const Boundary& bound) {
266     // (uncomment the next line to check select mode)
267     //myVisualizationSettings->drawForSelecting = true;
268     // init view settings
269     glRenderMode(mode);
270     glMatrixMode(GL_MODELVIEW);
271     glPushMatrix();
272     glDisable(GL_TEXTURE_2D);
273     glDisable(GL_ALPHA_TEST);
274     glEnable(GL_BLEND);
275     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
276     glEnable(GL_DEPTH_TEST);
277 
278     // draw decals (if not in grabbing mode)
279     if (!myUseToolTips) {
280         drawDecals();
281         if (myVisualizationSettings->showGrid) {
282             paintGLGrid();
283         }
284     }
285 
286     glLineWidth(1);
287     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
288     const float minB[2] = { (float)bound.xmin(), (float)bound.ymin() };
289     const float maxB[2] = { (float)bound.xmax(), (float)bound.ymax() };
290     myVisualizationSettings->scale = m2p(SUMO_const_laneWidth);
291     glEnable(GL_POLYGON_OFFSET_FILL);
292     glEnable(GL_POLYGON_OFFSET_LINE);
293     int hits2 = myGrid->Search(minB, maxB, *myVisualizationSettings);
294     // Draw additional objects
295     if (myAdditionallyDrawn.size() > 0) {
296         glTranslated(0, 0, -.01);
297         GUINet::getGUIInstance()->lock();
298         for (auto i : myAdditionallyDrawn) {
299             i.first->drawGLAdditional(this, *myVisualizationSettings);
300         }
301         GUINet::getGUIInstance()->unlock();
302         glTranslated(0, 0, .01);
303     }
304     glPopMatrix();
305     /*
306     // draw legends
307     glMatrixMode(GL_MODELVIEW);
308     glLoadIdentity();
309     glTranslated(1.-.2, 1.-.5, 0.);
310     glScaled(.2, .5, 1.);
311     GUIColoringSchemesMap<GUILane> &sm = GUIViewTraffic::getLaneSchemesMap(); //!!!
312     sm.getColorer(myVisualizationSettings->laneEdgeMode)->drawLegend();
313     */
314     return hits2;
315 }
316 
317 
318 void
startTrack(int id)319 GUIViewTraffic::startTrack(int id) {
320     myTrackedID = id;
321 }
322 
323 
324 void
stopTrack()325 GUIViewTraffic::stopTrack() {
326     myTrackedID = GUIGlObject::INVALID_ID;
327 }
328 
329 
330 GUIGlID
getTrackedID() const331 GUIViewTraffic::getTrackedID() const {
332     return myTrackedID;
333 }
334 
335 
336 void
onGamingClick(Position pos)337 GUIViewTraffic::onGamingClick(Position pos) {
338     if (myTLSGame) {
339         MSTLLogicControl& tlsControl = MSNet::getInstance()->getTLSControl();
340         const std::vector<MSTrafficLightLogic*>& logics = tlsControl.getAllLogics();
341         MSTrafficLightLogic* minTll = nullptr;
342         double minDist = std::numeric_limits<double>::infinity();
343         for (std::vector<MSTrafficLightLogic*>::const_iterator i = logics.begin(); i != logics.end(); ++i) {
344             // get the logic
345             MSTrafficLightLogic* tll = (*i);
346             if (tlsControl.isActive(tll) && tll->getProgramID() != "off") {
347                 // get the links
348                 const MSTrafficLightLogic::LaneVector& lanes = tll->getLanesAt(0);
349                 if (lanes.size() > 0) {
350                     const Position& endPos = lanes[0]->getShape().back();
351                     if (endPos.distanceTo(pos) < minDist) {
352                         minDist = endPos.distanceTo(pos);
353                         minTll = tll;
354                     }
355                 }
356             }
357         }
358         if (minTll != nullptr) {
359             const MSTLLogicControl::TLSLogicVariants& vars = tlsControl.get(minTll->getID());
360             const std::vector<MSTrafficLightLogic*> logics = vars.getAllLogics();
361             if (logics.size() > 1) {
362                 MSSimpleTrafficLightLogic* l = (MSSimpleTrafficLightLogic*) logics[0];
363                 for (int i = 0; i < (int)logics.size() - 1; ++i) {
364                     if (minTll->getProgramID() == logics[i]->getProgramID()) {
365                         l = (MSSimpleTrafficLightLogic*) logics[i + 1];
366                         tlsControl.switchTo(minTll->getID(), l->getProgramID());
367                     }
368                 }
369                 if (l == logics[0]) {
370                     tlsControl.switchTo(minTll->getID(), l->getProgramID());
371                 }
372                 l->changeStepAndDuration(tlsControl, MSNet::getInstance()->getCurrentTimeStep(), 0, l->getPhase(0).duration);
373                 update();
374             }
375         }
376     } else {
377         // DRT game
378         if (MSGlobals::gUseMesoSim) {
379             return;
380         }
381         const std::set<GUIGlID>& sel = gSelected.getSelected(GLO_VEHICLE);
382         if (sel.size() == 0) {
383             // find closest pt vehicle
384             double minDist = std::numeric_limits<double>::infinity();
385             GUIVehicle* closest = nullptr;
386             MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
387             MSVehicleControl::constVehIt it = vc.loadedVehBegin();
388             MSVehicleControl::constVehIt end = vc.loadedVehEnd();
389             for (it = vc.loadedVehBegin(); it != end; ++it) {
390                 GUIVehicle* veh = dynamic_cast<GUIVehicle*>(it->second);
391                 assert(veh != 0);
392                 if (veh->getParameter().line != "") {
393                     const double dist = veh->getPosition().distanceTo2D(pos);
394                     if (dist < minDist) {
395                         minDist = dist;
396                         closest = veh;
397                     }
398                 }
399             }
400             if (closest != nullptr) {
401                 gSelected.select(closest->getGlID());
402                 closest->addActiveAddVisualisation(this, GUIBaseVehicle::VO_SHOW_FUTURE_ROUTE);
403             }
404         } else {
405             // find closest pt stop
406             double minDist = std::numeric_limits<double>::infinity();
407             MSStoppingPlace* closestStop = nullptr;
408             const NamedObjectCont<MSStoppingPlace*>& stops = MSNet::getInstance()->getStoppingPlaces(SUMO_TAG_BUS_STOP);
409             for (auto it = stops.begin(); it != stops.end(); ++it) {
410                 MSStoppingPlace* stop = it->second;
411                 const double dist = pos.distanceTo2D(stop->getLane().geometryPositionAtOffset(stop->getEndLanePosition()));
412                 if (dist < minDist) {
413                     minDist = dist;
414                     closestStop = stop;
415                 }
416             }
417             if (closestStop != 0) {
418                 GUIGlID id = *sel.begin();
419                 GUIVehicle* veh = dynamic_cast<GUIVehicle*>(GUIGlObjectStorage::gIDStorage.getObjectBlocking(id));
420                 assert(veh != 0);
421                 MSLane* lane = veh->getLane();
422                 lane->getVehiclesSecure();
423                 veh->rerouteDRTStop(closestStop);
424                 GUIGlObjectStorage::gIDStorage.unblockObject(id);
425                 lane->releaseVehicles();
426             }
427         }
428     }
429 }
430 
431 
432 void
onGamingRightClick(Position)433 GUIViewTraffic::onGamingRightClick(Position /*pos*/) {
434     const std::set<GUIGlID>& sel = gSelected.getSelected(GLO_VEHICLE);
435     if (sel.size() > 0) {
436         GUIGlID id = *sel.begin();
437         GUIVehicle* veh = dynamic_cast<GUIVehicle*>(GUIGlObjectStorage::gIDStorage.getObjectBlocking(id));
438         if (veh != 0) {
439             veh->removeActiveAddVisualisation(this, GUIBaseVehicle::VO_SHOW_FUTURE_ROUTE);
440         }
441         GUIGlObjectStorage::gIDStorage.unblockObject(id);
442     }
443     gSelected.clear();
444 }
445 
446 
447 SUMOTime
getCurrentTimeStep() const448 GUIViewTraffic::getCurrentTimeStep() const {
449     return MSNet::getInstance()->getCurrentTimeStep();
450 }
451 
452 
453 GUILane*
getLaneUnderCursor()454 GUIViewTraffic::getLaneUnderCursor() {
455     if (makeCurrent()) {
456         int id = getObjectUnderCursor();
457         if (id != 0) {
458             GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(id);
459             if (o != nullptr) {
460                 return dynamic_cast<GUILane*>(o);
461             }
462         }
463         makeNonCurrent();
464     }
465     return nullptr;
466 }
467 
468 long
onCmdCloseLane(FXObject *,FXSelector,void *)469 GUIViewTraffic::onCmdCloseLane(FXObject*, FXSelector, void*) {
470     GUILane* lane = getLaneUnderCursor();
471     if (lane != nullptr) {
472         lane->closeTraffic();
473         GUIGlObjectStorage::gIDStorage.unblockObject(lane->getGlID());
474         update();
475     }
476     return 1;
477 }
478 
479 
480 long
onCmdCloseEdge(FXObject *,FXSelector,void *)481 GUIViewTraffic::onCmdCloseEdge(FXObject*, FXSelector, void*) {
482     GUILane* lane = getLaneUnderCursor();
483     if (lane != nullptr) {
484         dynamic_cast<GUIEdge*>(&lane->getEdge())->closeTraffic(lane);
485         GUIGlObjectStorage::gIDStorage.unblockObject(lane->getGlID());
486         update();
487     }
488     return 1;
489 }
490 
491 
492 long
onCmdAddRerouter(FXObject *,FXSelector,void *)493 GUIViewTraffic::onCmdAddRerouter(FXObject*, FXSelector, void*) {
494     GUILane* lane = getLaneUnderCursor();
495     if (lane != nullptr) {
496         dynamic_cast<GUIEdge*>(&lane->getEdge())->addRerouter();
497         GUIGlObjectStorage::gIDStorage.unblockObject(lane->getGlID());
498         update();
499     }
500     return 1;
501 }
502 
503 
504 long
onDoubleClicked(FXObject *,FXSelector,void *)505 GUIViewTraffic::onDoubleClicked(FXObject*, FXSelector, void*) {
506     // leave fullscreen mode
507     if (myApp->isFullScreen()) {
508         myApp->onCmdFullScreen(nullptr, 0, nullptr);
509     } else {
510         stopTrack();
511     }
512     return 1;
513 }
514 
515 
516 
517 void
saveFrame(const std::string & destFile,FXColor * buf)518 GUIViewTraffic::saveFrame(const std::string& destFile, FXColor* buf) {
519 #ifdef HAVE_FFMPEG
520     if (myCurrentVideo == nullptr) {
521         myCurrentVideo = new GUIVideoEncoder(destFile.c_str(), getWidth(), getHeight(), myApp->getDelay());
522     }
523     myCurrentVideo->writeFrame((uint8_t*)buf);
524 #else
525     UNUSED_PARAMETER(destFile);
526     UNUSED_PARAMETER(buf);
527 #endif
528 }
529 
530 
531 void
endSnapshot()532 GUIViewTraffic::endSnapshot() {
533 #ifdef HAVE_FFMPEG
534     if (myCurrentVideo != nullptr) {
535         delete myCurrentVideo;
536         myCurrentVideo = nullptr;
537     }
538 #endif
539 }
540 
541 
542 void
checkSnapshots()543 GUIViewTraffic::checkSnapshots() {
544 #ifdef HAVE_FFMPEG
545     if (myCurrentVideo != nullptr) {
546         addSnapshot(getCurrentTimeStep() - DELTA_T, "");
547     }
548 #endif
549     GUISUMOAbstractView::checkSnapshots();
550 }
551 
552 
553 const std::vector<SUMOTime>
retrieveBreakpoints() const554 GUIViewTraffic::retrieveBreakpoints() const {
555     return myApp->retrieveBreakpoints();
556 }
557 
558 
559 /****************************************************************************/
560