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