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    GUILane.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Michael Behrisch
14 /// @date    Sept 2002
15 /// @version $Id$
16 ///
17 // Representation of a lane in the micro simulation (gui-version)
18 /****************************************************************************/
19 
20 
21 // ===========================================================================
22 // included modules
23 // ===========================================================================
24 #include <config.h>
25 
26 #include <string>
27 #include <utility>
28 #include <fx.h>
29 #include <utils/geom/GeomHelper.h>
30 #include <utils/geom/Position.h>
31 #include <microsim/logging/FunctionBinding.h>
32 #include <utils/options/OptionsCont.h>
33 #include <utils/common/MsgHandler.h>
34 #include <utils/common/StdDefs.h>
35 #include <utils/geom/GeomHelper.h>
36 #include <utils/gui/div/GLHelper.h>
37 #include <utils/gui/globjects/GLIncludes.h>
38 #include <utils/gui/windows/GUISUMOAbstractView.h>
39 #include <utils/gui/div/GUIParameterTableWindow.h>
40 #include <utils/gui/div/GUIGlobalSelection.h>
41 #include <utils/gui/windows/GUIAppEnum.h>
42 #include <microsim/MSGlobals.h>
43 #include <microsim/MSLane.h>
44 #include <microsim/MSVehicleControl.h>
45 #include <microsim/MSInsertionControl.h>
46 #include <microsim/MSVehicleTransfer.h>
47 #include <microsim/MSNet.h>
48 #include <microsim/MSEdgeWeightsStorage.h>
49 #include <microsim/devices/MSDevice_Routing.h>
50 #include <mesosim/MELoop.h>
51 #include <mesosim/MESegment.h>
52 #include "GUILane.h"
53 #include "GUIEdge.h"
54 #include "GUIVehicle.h"
55 #include "GUINet.h"
56 
57 #ifdef HAVE_OSG
58 #include <osg/Geometry>
59 #endif
60 
61 //#define GUILane_DEBUG_DRAW_WALKING_AREA_VERTICES
62 //#define GUILane_DEBUG_DRAW_VERTICES
63 //#define GUILane_DEBUG_DRAW_FOE_INTERSECTIONS
64 
65 // ===========================================================================
66 // static member declaration
67 // ===========================================================================
68 const RGBColor GUILane::MESO_USE_LANE_COLOR(0, 0, 0, 0);
69 
70 
71 // ===========================================================================
72 // method definitions
73 // ===========================================================================
GUILane(const std::string & id,double maxSpeed,double length,MSEdge * const edge,int numericalID,const PositionVector & shape,double width,SVCPermissions permissions,int index,bool isRampAccel)74 GUILane::GUILane(const std::string& id, double maxSpeed, double length,
75                  MSEdge* const edge, int numericalID,
76                  const PositionVector& shape, double width,
77                  SVCPermissions permissions, int index, bool isRampAccel) :
78     MSLane(id, maxSpeed, length, edge, numericalID, shape, width, permissions, index, isRampAccel),
79     GUIGlObject(GLO_LANE, id),
80 #ifdef HAVE_OSG
81     myGeom(0),
82 #endif
83     myAmClosed(false),
84     myLock(true) {
85     if (MSGlobals::gUseMesoSim) {
86         myShape = splitAtSegments(shape);
87         assert(fabs(myShape.length() - shape.length()) < POSITION_EPS);
88         assert(myShapeSegments.size() == myShape.size());
89     }
90     myShapeRotations.reserve(myShape.size() - 1);
91     myShapeLengths.reserve(myShape.size() - 1);
92     myShapeColors.reserve(myShape.size() - 1);
93     int e = (int) myShape.size() - 1;
94     for (int i = 0; i < e; ++i) {
95         const Position& f = myShape[i];
96         const Position& s = myShape[i + 1];
97         myShapeLengths.push_back(f.distanceTo2D(s));
98         myShapeRotations.push_back(RAD2DEG(atan2(s.x() - f.x(), f.y() - s.y())));
99     }
100     //
101     myHalfLaneWidth = myWidth / 2.;
102     myQuarterLaneWidth = myWidth / 4.;
103 }
104 
105 
~GUILane()106 GUILane::~GUILane() {
107     // just to quit cleanly on a failure
108     if (myLock.locked()) {
109         myLock.unlock();
110     }
111 }
112 
113 
114 // ------ Vehicle insertion ------
115 void
incorporateVehicle(MSVehicle * veh,double pos,double speed,double posLat,const MSLane::VehCont::iterator & at,MSMoveReminder::Notification notification)116 GUILane::incorporateVehicle(MSVehicle* veh, double pos, double speed, double posLat,
117                             const MSLane::VehCont::iterator& at,
118                             MSMoveReminder::Notification notification) {
119     FXMutexLock locker(myLock);
120     MSLane::incorporateVehicle(veh, pos, speed, posLat, at, notification);
121 }
122 
123 
124 // ------ Access to vehicles ------
125 const MSLane::VehCont&
getVehiclesSecure() const126 GUILane::getVehiclesSecure() const {
127     myLock.lock();
128     return myVehicles;
129 }
130 
131 
132 void
releaseVehicles() const133 GUILane::releaseVehicles() const {
134     myLock.unlock();
135 }
136 
137 
138 void
planMovements(const SUMOTime t)139 GUILane::planMovements(const SUMOTime t) {
140     FXMutexLock locker(myLock);
141     MSLane::planMovements(t);
142 }
143 
144 void
setJunctionApproaches(const SUMOTime t) const145 GUILane::setJunctionApproaches(const SUMOTime t) const {
146     FXMutexLock locker(myLock);
147     MSLane::setJunctionApproaches(t);
148 }
149 
150 
151 void
executeMovements(const SUMOTime t)152 GUILane::executeMovements(const SUMOTime t) {
153     FXMutexLock locker(myLock);
154     MSLane::executeMovements(t);
155 }
156 
157 
158 MSVehicle*
removeVehicle(MSVehicle * remVehicle,MSMoveReminder::Notification notification,bool notify)159 GUILane::removeVehicle(MSVehicle* remVehicle, MSMoveReminder::Notification notification, bool notify) {
160     FXMutexLock locker(myLock);
161     return MSLane::removeVehicle(remVehicle, notification, notify);
162 }
163 
164 
165 void
removeParking(MSVehicle * remVehicle)166 GUILane::removeParking(MSVehicle* remVehicle) {
167     FXMutexLock locker(myLock);
168     return MSLane::removeParking(remVehicle);
169 }
170 
171 
172 void
swapAfterLaneChange(SUMOTime t)173 GUILane::swapAfterLaneChange(SUMOTime t) {
174     FXMutexLock locker(myLock);
175     MSLane::swapAfterLaneChange(t);
176 }
177 
178 
179 void
integrateNewVehicles()180 GUILane::integrateNewVehicles() {
181     FXMutexLock locker(myLock);
182     MSLane::integrateNewVehicles();
183 }
184 
185 
186 void
detectCollisions(SUMOTime timestep,const std::string & stage)187 GUILane::detectCollisions(SUMOTime timestep, const std::string& stage) {
188     FXMutexLock locker(myLock);
189     MSLane::detectCollisions(timestep, stage);
190 }
191 
192 
193 double
setPartialOccupation(MSVehicle * v)194 GUILane::setPartialOccupation(MSVehicle* v) {
195     FXMutexLock locker(myLock);
196     return MSLane::setPartialOccupation(v);
197 }
198 
199 
200 void
resetPartialOccupation(MSVehicle * v)201 GUILane::resetPartialOccupation(MSVehicle* v) {
202     FXMutexLock locker(myLock);
203     MSLane::resetPartialOccupation(v);
204 }
205 
206 
207 // ------ Drawing methods ------
208 void
drawLinkNo(const GUIVisualizationSettings & s) const209 GUILane::drawLinkNo(const GUIVisualizationSettings& s) const {
210     int noLinks = (int)myLinks.size();
211     if (noLinks == 0) {
212         return;
213     }
214     // draw all links
215     if (getEdge().isCrossing()) {
216         // draw indices at the start and end of the crossing
217         MSLink* link = MSLinkContHelper::getConnectingLink(*getLogicalPredecessorLane(), *this);
218         PositionVector shape = getShape();
219         shape.extrapolate(0.5); // draw on top of the walking area
220         GLHelper::drawTextAtEnd(toString(link->getIndex()), shape, 0, s.drawLinkJunctionIndex.size, s.drawLinkJunctionIndex.color);
221         GLHelper::drawTextAtEnd(toString(link->getIndex()), shape.reverse(), 0, s.drawLinkJunctionIndex.size, s.drawLinkJunctionIndex.color);
222         return;
223     }
224     // draw all links
225     double w = myWidth / (double) noLinks;
226     double x1 = myHalfLaneWidth;
227     const bool lefthand = MSNet::getInstance()->lefthand();
228     for (int i = noLinks; --i >= 0;) {
229         double x2 = x1 - (double)(w / 2.);
230         GLHelper::drawTextAtEnd(toString(myLinks[lefthand ? noLinks - 1 - i : i]->getIndex()), getShape(), x2, s.drawLinkJunctionIndex.size, s.drawLinkJunctionIndex.color);
231         x1 -= w;
232     }
233 }
234 
235 
236 void
drawTLSLinkNo(const GUIVisualizationSettings & s,const GUINet & net) const237 GUILane::drawTLSLinkNo(const GUIVisualizationSettings& s, const GUINet& net) const {
238     int noLinks = (int)myLinks.size();
239     if (noLinks == 0) {
240         return;
241     }
242     if (getEdge().isCrossing()) {
243         // draw indices at the start and end of the crossing
244         MSLink* link = MSLinkContHelper::getConnectingLink(*getLogicalPredecessorLane(), *this);
245         int linkNo = net.getLinkTLIndex(link);
246         // maybe the reverse link is controlled separately
247         int linkNo2 = net.getLinkTLIndex(myLinks.front());
248         // otherwise, use the same index as the forward link
249         if (linkNo2 < 0) {
250             linkNo2 = linkNo;
251         }
252         if (linkNo >= 0) {
253             PositionVector shape = getShape();
254             shape.extrapolate(0.5); // draw on top of the walking area
255             GLHelper::drawTextAtEnd(toString(linkNo2), shape, 0, s.drawLinkTLIndex.size, s.drawLinkTLIndex.color);
256             GLHelper::drawTextAtEnd(toString(linkNo), shape.reverse(), 0, s.drawLinkTLIndex.size, s.drawLinkTLIndex.color);
257         }
258         return;
259     }
260     // draw all links
261     double w = myWidth / (double) noLinks;
262     double x1 = myHalfLaneWidth;
263     const bool lefthand = MSNet::getInstance()->lefthand();
264     for (int i = noLinks; --i >= 0;) {
265         double x2 = x1 - (double)(w / 2.);
266         int linkNo = net.getLinkTLIndex(myLinks[lefthand ? noLinks - 1 - i : i]);
267         if (linkNo < 0) {
268             continue;
269         }
270         GLHelper::drawTextAtEnd(toString(linkNo), getShape(), x2, s.drawLinkTLIndex.size, s.drawLinkTLIndex.color);
271         x1 -= w;
272     }
273 }
274 
275 
276 void
drawLinkRules(const GUIVisualizationSettings & s,const GUINet & net) const277 GUILane::drawLinkRules(const GUIVisualizationSettings& s, const GUINet& net) const {
278     int noLinks = (int)myLinks.size();
279     if (noLinks == 0) {
280         drawLinkRule(s, net, nullptr, getShape(), 0, 0);
281         return;
282     }
283     if (getEdge().isCrossing()) {
284         // draw rules at the start and end of the crossing
285         MSLink* link = MSLinkContHelper::getConnectingLink(*getLogicalPredecessorLane(), *this);
286         MSLink* link2 = myLinks.front();
287         if (link2->getTLLogic() == nullptr) {
288             link2 = link;
289         }
290         PositionVector shape = getShape();
291         shape.extrapolate(0.5); // draw on top of the walking area
292         drawLinkRule(s, net, link2, shape, 0, myWidth);
293         drawLinkRule(s, net, link, shape.reverse(), 0, myWidth);
294         return;
295     }
296     // draw all links
297     const double w = myWidth / (double) noLinks;
298     double x1 = myEdge->getToJunction()->getType() == NODETYPE_RAIL_SIGNAL ? -myWidth * 0.5 : 0;
299     const bool lefthand = MSNet::getInstance()->lefthand();
300     for (int i = 0; i < noLinks; ++i) {
301         double x2 = x1 + w;
302         drawLinkRule(s, net, myLinks[lefthand ? noLinks - 1 - i : i], getShape(), x1, x2);
303         x1 = x2;
304     }
305     // draw stopOffset for passenger cars
306     if (myStopOffsets.size() != 0 && (myStopOffsets.begin()->first & SVC_PASSENGER) != 0) {
307         const double stopOffsetPassenger = myStopOffsets.begin()->second;
308         const Position& end = myShape.back();
309         const Position& f = myShape[-2];
310         const double rot = RAD2DEG(atan2((end.x() - f.x()), (f.y() - end.y())));
311         GLHelper::setColor(s.getLinkColor(LINKSTATE_MAJOR));
312         glPushMatrix();
313         glTranslated(end.x(), end.y(), 0);
314         glRotated(rot, 0, 0, 1);
315         glTranslated(0, stopOffsetPassenger, 0);
316         glBegin(GL_QUADS);
317         glVertex2d(-myHalfLaneWidth, 0.0);
318         glVertex2d(-myHalfLaneWidth, 0.2);
319         glVertex2d(myHalfLaneWidth, 0.2);
320         glVertex2d(myHalfLaneWidth, 0.0);
321         glEnd();
322         glPopMatrix();
323     }
324 }
325 
326 
327 void
drawLinkRule(const GUIVisualizationSettings & s,const GUINet & net,MSLink * link,const PositionVector & shape,double x1,double x2) const328 GUILane::drawLinkRule(const GUIVisualizationSettings& s, const GUINet& net, MSLink* link, const PositionVector& shape, double x1, double x2) const {
329     const Position& end = shape.back();
330     const Position& f = shape[-2];
331     const double rot = RAD2DEG(atan2((end.x() - f.x()), (f.y() - end.y())));
332     if (link == nullptr) {
333         GLHelper::setColor(GUIVisualizationSettings::getLinkColor(LINKSTATE_DEADEND));
334         glPushMatrix();
335         glTranslated(end.x(), end.y(), 0);
336         glRotated(rot, 0, 0, 1);
337         glBegin(GL_QUADS);
338         glVertex2d(-myHalfLaneWidth, 0.0);
339         glVertex2d(-myHalfLaneWidth, 0.5);
340         glVertex2d(myHalfLaneWidth, 0.5);
341         glVertex2d(myHalfLaneWidth, 0.0);
342         glEnd();
343         glPopMatrix();
344     } else {
345         glPushMatrix();
346         glTranslated(end.x(), end.y(), 0);
347         glRotated(rot, 0, 0, 1);
348         // select glID
349         switch (link->getState()) {
350             case LINKSTATE_TL_GREEN_MAJOR:
351             case LINKSTATE_TL_GREEN_MINOR:
352             case LINKSTATE_TL_RED:
353             case LINKSTATE_TL_REDYELLOW:
354             case LINKSTATE_TL_YELLOW_MAJOR:
355             case LINKSTATE_TL_YELLOW_MINOR:
356             case LINKSTATE_TL_OFF_BLINKING:
357             case LINKSTATE_TL_OFF_NOSIGNAL:
358                 glPushName(net.getLinkTLID(link));
359                 break;
360             case LINKSTATE_MAJOR:
361             case LINKSTATE_MINOR:
362             case LINKSTATE_EQUAL:
363             default:
364                 glPushName(getGlID());
365                 break;
366         }
367         GLHelper::setColor(GUIVisualizationSettings::getLinkColor(link->getState()));
368         if (!(drawAsRailway(s) || drawAsWaterway(s)) || link->getState() != LINKSTATE_MAJOR) {
369             // the white bar should be the default for most railway
370             // links and looks ugly so we do not draw it
371             double scale = isInternal() ? 0.5 : 1;
372             if (myEdge->getToJunction()->getType() == NODETYPE_RAIL_SIGNAL) {
373                 scale *= MAX2(s.laneWidthExaggeration, s.junctionSize.getExaggeration(s, this, 10));
374             }
375             glScaled(scale, scale, 1);
376             glBegin(GL_QUADS);
377             glVertex2d(x1 - myHalfLaneWidth, 0.0);
378             glVertex2d(x1 - myHalfLaneWidth, 0.5);
379             glVertex2d(x2 - myHalfLaneWidth, 0.5);
380             glVertex2d(x2 - myHalfLaneWidth, 0.0);
381             glEnd();
382         }
383         glPopName();
384         glPopMatrix();
385     }
386 }
387 
388 void
drawArrows() const389 GUILane::drawArrows() const {
390     if (myLinks.size() == 0) {
391         return;
392     }
393     // draw all links
394     const Position& end = getShape().back();
395     const Position& f = getShape()[-2];
396     const double rot = RAD2DEG(atan2((end.x() - f.x()), (f.y() - end.y())));
397     glPushMatrix();
398     glColor3d(1, 1, 1);
399     glTranslated(end.x(), end.y(), 0);
400     glRotated(rot, 0, 0, 1);
401     if (myWidth < SUMO_const_laneWidth) {
402         glScaled(myWidth / SUMO_const_laneWidth, 1, 1);
403     }
404     for (std::vector<MSLink*>::const_iterator i = myLinks.begin(); i != myLinks.end(); ++i) {
405         LinkDirection dir = (*i)->getDirection();
406         LinkState state = (*i)->getState();
407         if (state == LINKSTATE_DEADEND || dir == LINKDIR_NODIR) {
408             continue;
409         }
410         switch (dir) {
411             case LINKDIR_STRAIGHT:
412                 GLHelper::drawBoxLine(Position(0, 4), 0, 2, .05);
413                 GLHelper::drawTriangleAtEnd(Position(0, 4), Position(0, 1), (double) 1, (double) .25);
414                 break;
415             case LINKDIR_TURN:
416                 GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);
417                 GLHelper::drawBoxLine(Position(0, 2.5), 90, .5, .05);
418                 GLHelper::drawBoxLine(Position(0.5, 2.5), 180, 1, .05);
419                 GLHelper::drawTriangleAtEnd(Position(0.5, 2.5), Position(0.5, 4), (double) 1, (double) .25);
420                 break;
421             case LINKDIR_TURN_LEFTHAND:
422                 GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);
423                 GLHelper::drawBoxLine(Position(0, 2.5), -90, 1, .05);
424                 GLHelper::drawBoxLine(Position(-0.5, 2.5), -180, 1, .05);
425                 GLHelper::drawTriangleAtEnd(Position(-0.5, 2.5), Position(-0.5, 4), (double) 1, (double) .25);
426                 break;
427             case LINKDIR_LEFT:
428                 GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);
429                 GLHelper::drawBoxLine(Position(0, 2.5), 90, 1, .05);
430                 GLHelper::drawTriangleAtEnd(Position(0, 2.5), Position(1.5, 2.5), (double) 1, (double) .25);
431                 break;
432             case LINKDIR_RIGHT:
433                 GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);
434                 GLHelper::drawBoxLine(Position(0, 2.5), -90, 1, .05);
435                 GLHelper::drawTriangleAtEnd(Position(0, 2.5), Position(-1.5, 2.5), (double) 1, (double) .25);
436                 break;
437             case LINKDIR_PARTLEFT:
438                 GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);
439                 GLHelper::drawBoxLine(Position(0, 2.5), 45, .7, .05);
440                 GLHelper::drawTriangleAtEnd(Position(0, 2.5), Position(1.2, 1.3), (double) 1, (double) .25);
441                 break;
442             case LINKDIR_PARTRIGHT:
443                 GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);
444                 GLHelper::drawBoxLine(Position(0, 2.5), -45, .7, .05);
445                 GLHelper::drawTriangleAtEnd(Position(0, 2.5), Position(-1.2, 1.3), (double) 1, (double) .25);
446                 break;
447             default:
448                 break;
449         }
450     }
451     glPopMatrix();
452 }
453 
454 
455 void
drawLane2LaneConnections(double exaggeration) const456 GUILane::drawLane2LaneConnections(double exaggeration) const {
457     Position centroid;
458     if (exaggeration > 1) {
459         centroid = myEdge->getToJunction()->getShape().getCentroid();
460     }
461     for (std::vector<MSLink*>::const_iterator i = myLinks.begin(); i != myLinks.end(); ++i) {
462         const MSLane* connected = (*i)->getLane();
463         if (connected == nullptr) {
464             continue;
465         }
466         GLHelper::setColor(GUIVisualizationSettings::getLinkColor((*i)->getState()));
467         glBegin(GL_LINES);
468         Position p1 = myEdge->isWalkingArea() ? getShape().getCentroid() : getShape()[-1];
469         Position p2 = connected->getEdge().isWalkingArea() ? connected->getShape().getCentroid() : connected->getShape()[0];
470         if (exaggeration > 1) {
471             p1 = centroid + ((p1 - centroid) * exaggeration);
472             p2 = centroid + ((p2 - centroid) * exaggeration);
473         }
474         glVertex2d(p1.x(), p1.y());
475         glVertex2d(p2.x(), p2.y());
476         glEnd();
477         GLHelper::drawTriangleAtEnd(p1, p2, (double) .4, (double) .2);
478     }
479 }
480 
481 
482 void
drawGL(const GUIVisualizationSettings & s) const483 GUILane::drawGL(const GUIVisualizationSettings& s) const {
484     glPushMatrix();
485     glPushName(getGlID());
486     const bool isCrossing = myEdge->isCrossing();
487     const bool isWalkingArea = myEdge->isWalkingArea();
488     const bool isInternal = isCrossing || isWalkingArea || myEdge->isInternal();
489     bool mustDrawMarkings = false;
490     double exaggeration = s.laneWidthExaggeration;
491     if (MSGlobals::gUseMesoSim) {
492         GUIEdge* myGUIEdge = dynamic_cast<GUIEdge*>(myEdge);
493         exaggeration *= s.edgeScaler.getScheme().getColor(myGUIEdge->getScaleValue(s.edgeScaler.getActive()));
494     } else {
495         exaggeration *= s.laneScaler.getScheme().getColor(getScaleValue(s.laneScaler.getActive()));
496     }
497     const bool hasRailSignal = myEdge->getToJunction()->getType() == NODETYPE_RAIL_SIGNAL;
498     const bool detailZoom = s.scale * exaggeration > 5;
499     const bool drawDetails = (detailZoom || s.junctionSize.minSize == 0 || hasRailSignal) && !s.drawForSelecting;
500     if (isCrossing || isWalkingArea) {
501         // draw internal lanes on top of junctions
502         glTranslated(0, 0, GLO_JUNCTION + 0.1);
503     } else if (isWaterway(myPermissions)) {
504         // draw waterways below normal roads
505         glTranslated(0, 0, getType() - 0.2);
506     } else {
507         glTranslated(0, 0, getType());
508     }
509     // set lane color
510     const RGBColor color = setColor(s);
511     if (MSGlobals::gUseMesoSim) {
512         myShapeColors.clear();
513         const std::vector<RGBColor>& segmentColors = static_cast<const GUIEdge*>(myEdge)->getSegmentColors();
514         if (segmentColors.size() > 0) {
515             // apply segment specific shape colors
516             //std::cout << getID() << " shape=" << myShape << " shapeSegs=" << toString(myShapeSegments) << "\n";
517             for (int ii = 0; ii < (int)myShape.size() - 1; ++ii) {
518                 myShapeColors.push_back(segmentColors[myShapeSegments[ii]]);
519             }
520         }
521     }
522     // recognize full transparency and simply don't draw
523     bool hiddenBidi = myEdge->getBidiEdge() != nullptr && myEdge->getNumericalID() > myEdge->getBidiEdge()->getNumericalID();
524     if (color.alpha() != 0 && s.scale * exaggeration > s.laneMinSize) {
525         // scale tls-controlled lane2lane-arrows along with their junction shapes
526         double junctionExaggeration = 1;
527         if (!isInternal
528                 && myEdge->getToJunction()->getType() <= NODETYPE_RAIL_CROSSING
529                 && (s.junctionSize.constantSize || s.junctionSize.exaggeration > 1)) {
530             junctionExaggeration = MAX2(1.001, s.junctionSize.getExaggeration(s, this, 4));
531         }
532         // draw lane
533         // check whether it is not too small
534         if (s.scale * exaggeration < 1. && junctionExaggeration == 1 && s.junctionSize.minSize != 0) {
535             if (!isInternal || hasRailSignal) {
536                 if (myShapeColors.size() > 0) {
537                     GLHelper::drawLine(myShape, myShapeColors);
538                 } else {
539                     GLHelper::drawLine(myShape);
540                 }
541             }
542             glPopMatrix();
543         } else {
544             GUINet* net = (GUINet*) MSNet::getInstance();
545             const bool spreadSuperposed = s.spreadSuperposed && myEdge->getBidiEdge() != nullptr && drawAsRailway(s);
546             if (hiddenBidi && !spreadSuperposed) {
547                 // do not draw shape
548             } else if (drawAsRailway(s) && (!s.drawForSelecting || spreadSuperposed)) {
549                 // draw as railway: assume standard gauge of 1435mm when lane width is not set
550                 // draw foot width 150mm, assume that distance between rail feet inner sides is reduced on both sides by 39mm with regard to the gauge
551                 // assume crosstie length of 181% gauge (2600mm for standard gauge)
552                 PositionVector shape = myShape;
553                 const double width = myWidth;
554                 double halfGauge = 0.5 * (width == SUMO_const_laneWidth ?  1.4350 : width) * exaggeration;
555                 if (spreadSuperposed) {
556                     shape.move2side(halfGauge * 0.8);
557                     halfGauge *= 0.4;
558                 }
559                 const double halfInnerFeetWidth = halfGauge - 0.039 * exaggeration;
560                 const double halfRailWidth = detailZoom ? (halfInnerFeetWidth + 0.15 * exaggeration) : SUMO_const_halfLaneWidth;
561                 const double halfCrossTieWidth = halfGauge * 1.81;
562                 if (myShapeColors.size() > 0) {
563                     GLHelper::drawBoxLines(shape, myShapeRotations, myShapeLengths, myShapeColors, halfRailWidth);
564                 } else {
565                     GLHelper::drawBoxLines(shape, myShapeRotations, myShapeLengths, halfRailWidth);
566                 }
567                 // Draw white on top with reduced width (the area between the two tracks)
568                 if (detailZoom) {
569                     glColor3d(1, 1, 1);
570                     glTranslated(0, 0, .1);
571                     GLHelper::drawBoxLines(shape, myShapeRotations, myShapeLengths, halfInnerFeetWidth);
572                     setColor(s);
573                     GLHelper::drawCrossTies(shape, myShapeRotations, myShapeLengths, 0.26 * exaggeration, 0.6 * exaggeration, halfCrossTieWidth, s.drawForSelecting);
574                 }
575             } else if (isCrossing) {
576                 if (s.drawCrossingsAndWalkingareas && (s.scale > 3.0 || s.junctionSize.minSize == 0)) {
577                     glTranslated(0, 0, .2);
578                     GLHelper::drawCrossTies(myShape, myShapeRotations, myShapeLengths, 0.5, 1.0, getWidth() * 0.5, s.drawForSelecting);
579                     glTranslated(0, 0, -.2);
580                 }
581             } else if (isWalkingArea) {
582                 if (s.drawCrossingsAndWalkingareas && (s.scale > 3.0 || s.junctionSize.minSize == 0)) {
583                     glTranslated(0, 0, .2);
584                     if (s.scale * exaggeration < 20.) {
585                         GLHelper::drawFilledPoly(myShape, true);
586                     } else {
587                         GLHelper::drawFilledPolyTesselated(myShape, true);
588                     }
589                     glTranslated(0, 0, -.2);
590 #ifdef GUILane_DEBUG_DRAW_WALKING_AREA_VERTICES
591                     GLHelper::debugVertices(myShape, 80 / s.scale);
592 #endif
593                 }
594             } else {
595                 // we draw the lanes with reduced width so that the lane markings below are visible
596                 // (this avoids artifacts at geometry corners without having to
597                 // compute lane-marking intersection points)
598                 const double halfWidth = isInternal ? myQuarterLaneWidth : (myHalfLaneWidth - SUMO_const_laneMarkWidth / 2);
599                 mustDrawMarkings = !isInternal && myPermissions != 0 && myPermissions != SVC_PEDESTRIAN && exaggeration == 1.0 && !isWaterway(myPermissions);
600                 const int cornerDetail = drawDetails && !isInternal ? (int)(s.scale * exaggeration) : 0;
601                 const double offset = halfWidth * MAX2(0., (exaggeration - 1));
602                 if (myShapeColors.size() > 0) {
603                     GLHelper::drawBoxLines(myShape, myShapeRotations, myShapeLengths, myShapeColors, halfWidth * exaggeration, cornerDetail, offset);
604                 } else {
605                     GLHelper::drawBoxLines(myShape, myShapeRotations, myShapeLengths, halfWidth * exaggeration, cornerDetail, offset);
606                 }
607             }
608 #ifdef GUILane_DEBUG_DRAW_VERTICES
609             GLHelper::debugVertices(myShape, 80 / s.scale);
610 #endif
611 #ifdef GUILane_DEBUG_DRAW_FOE_INTERSECTIONS
612             if (myEdge->isInternal() && gSelected.isSelected(getType(), getGlID())) {
613                 debugDrawFoeIntersections();
614             }
615 #endif
616             glPopMatrix();
617             // draw details
618             if ((!isInternal || isCrossing || !s.drawJunctionShape) && (drawDetails || s.drawForSelecting || junctionExaggeration > 1)) {
619                 glPushMatrix();
620                 glTranslated(0, 0, GLO_JUNCTION); // must draw on top of junction shape
621                 glTranslated(0, 0, .5);
622                 if (drawDetails) {
623                     if (s.showLaneDirection) {
624                         if (drawAsRailway(s)) {
625                             // improve visibility of superposed rail edges
626                             GLHelper::setColor(setColor(s).changedBrightness(100));
627                         } else {
628                             glColor3d(0.3, 0.3, 0.3);
629                         }
630                         if (!isCrossing || s.drawCrossingsAndWalkingareas) {
631                             drawDirectionIndicators(exaggeration, spreadSuperposed);
632                         }
633                     }
634                     if ((!isInternal || isCrossing)) {
635                         if (MSGlobals::gLateralResolution > 0 && s.showSublanes && !hiddenBidi && !isCrossing) {
636                             // draw sublane-borders
637                             GLHelper::setColor(GLHelper::getColor().changedBrightness(51));
638                             for (double offset = -myHalfLaneWidth; offset < myHalfLaneWidth; offset += MSGlobals::gLateralResolution) {
639                                 GLHelper::drawBoxLines(myShape, myShapeRotations, myShapeLengths, 0.01, 0, -offset);
640                             }
641                         }
642                         if (s.showLinkDecals && !drawAsRailway(s) && !drawAsWaterway(s) && myPermissions != SVC_PEDESTRIAN) {
643                             drawArrows();
644                         }
645                         glTranslated(0, 0, 1000);
646                         if (s.drawLinkJunctionIndex.show) {
647                             drawLinkNo(s);
648                         }
649                         if (s.drawLinkTLIndex.show) {
650                             drawTLSLinkNo(s, *net);
651                         }
652                         glTranslated(0, 0, -1000);
653                     }
654                     glTranslated(0, 0, .1);
655                 }
656                 // make sure link rules are drawn so tls can be selected via right-click
657                 if (s.showLinkRules && (drawDetails || s.drawForSelecting)
658                         && (!myEdge->isInternal() || getLinkCont()[0]->isInternalJunctionLink())) {
659                     drawLinkRules(s, *net);
660                 }
661                 if ((drawDetails || junctionExaggeration > 1) && s.showLane2Lane) {
662                     //  draw from end of first to the begin of second but respect junction scaling
663                     drawLane2LaneConnections(junctionExaggeration);
664                 }
665                 glPopMatrix();
666             }
667         }
668         if (mustDrawMarkings && drawDetails && s.laneShowBorders && !hiddenBidi) { // needs matrix reset
669             drawMarkings(s, exaggeration);
670         }
671         if (drawDetails && isInternal && s.showBikeMarkings && myPermissions == SVC_BICYCLE && exaggeration == 1.0 && s.showLinkDecals && s.laneShowBorders && !hiddenBidi) {
672             drawBikeMarkings();
673         }
674     } else {
675         glPopMatrix();
676     }
677     // draw vehicles
678     if (s.scale * s.vehicleSize.getExaggeration(s, nullptr) > s.vehicleSize.minSize) {
679         // retrieve vehicles from lane; disallow simulation
680         const MSLane::VehCont& vehicles = getVehiclesSecure();
681         for (MSLane::VehCont::const_iterator v = vehicles.begin(); v != vehicles.end(); ++v) {
682             if ((*v)->getLane() == this) {
683                 static_cast<const GUIVehicle* const>(*v)->drawGL(s);
684             } // else: this is the shadow during a continuous lane change
685         }
686         // draw parking vehicles
687         for (std::set<const MSVehicle*>::const_iterator v = myParkingVehicles.begin(); v != myParkingVehicles.end(); ++v) {
688             static_cast<const GUIVehicle* const>(*v)->drawGL(s);
689         }
690         // allow lane simulation
691         releaseVehicles();
692     }
693     glPopName();
694 }
695 
696 
697 void
drawMarkings(const GUIVisualizationSettings & s,double scale) const698 GUILane::drawMarkings(const GUIVisualizationSettings& s, double scale) const {
699     glPushMatrix();
700     glTranslated(0, 0, GLO_EDGE);
701     setColor(s);
702     // optionally draw inverse markings
703     if (myIndex > 0 && (myEdge->getLanes()[myIndex - 1]->getPermissions() & myPermissions) != 0) {
704         double mw = (myHalfLaneWidth + SUMO_const_laneMarkWidth) * scale;
705         double mw2 = (myHalfLaneWidth - SUMO_const_laneMarkWidth) * scale;
706         if (MSNet::getInstance()->lefthand()) {
707             mw *= -1;
708             mw2 *= -1;
709         }
710         int e = (int) getShape().size() - 1;
711         for (int i = 0; i < e; ++i) {
712             glPushMatrix();
713             glTranslated(getShape()[i].x(), getShape()[i].y(), 2.1);
714             glRotated(myShapeRotations[i], 0, 0, 1);
715             for (double t = 0; t < myShapeLengths[i]; t += 6) {
716                 const double length = MIN2((double)3, myShapeLengths[i] - t);
717                 glBegin(GL_QUADS);
718                 glVertex2d(-mw, -t);
719                 glVertex2d(-mw, -t - length);
720                 glVertex2d(-mw2, -t - length);
721                 glVertex2d(-mw2, -t);
722                 glEnd();
723             }
724             glPopMatrix();
725         }
726     }
727     // draw white boundings and white markings
728     glColor3d(1, 1, 1);
729     GLHelper::drawBoxLines(
730         getShape(),
731         getShapeRotations(),
732         getShapeLengths(),
733         (myHalfLaneWidth + SUMO_const_laneMarkWidth) * scale);
734     glPopMatrix();
735 }
736 
737 
738 void
drawBikeMarkings() const739 GUILane::drawBikeMarkings() const {
740     // draw bike lane markings onto the intersection
741     glColor3d(1, 1, 1);
742     const int e = (int) getShape().size() - 1;
743     const double markWidth = 0.1;
744     const double mw = myHalfLaneWidth;
745     for (int i = 0; i < e; ++i) {
746         glPushMatrix();
747         glTranslated(getShape()[i].x(), getShape()[i].y(), GLO_JUNCTION + 0.4);
748         glRotated(myShapeRotations[i], 0, 0, 1);
749         for (double t = 0; t < myShapeLengths[i]; t += 0.5) {
750             // left and right marking
751             for (int side = -1; side <= 1; side += 2) {
752                 glBegin(GL_QUADS);
753                 glVertex2d(side * mw, -t);
754                 glVertex2d(side * mw, -t - 0.35);
755                 glVertex2d(side * (mw + markWidth), -t - 0.35);
756                 glVertex2d(side * (mw + markWidth), -t);
757                 glEnd();
758             }
759         }
760         glPopMatrix();
761     }
762 }
763 
764 void
drawDirectionIndicators(double exaggeration,bool spreadSuperposed) const765 GUILane::drawDirectionIndicators(double exaggeration, bool spreadSuperposed) const {
766     glPushMatrix();
767     glTranslated(0, 0, GLO_EDGE);
768     int e = (int) getShape().size() - 1;
769     const double widthFactor = spreadSuperposed ? 0.4 : 1;
770     const double w = MAX2(POSITION_EPS, myWidth * widthFactor);
771     const double w2 = MAX2(POSITION_EPS, myHalfLaneWidth * widthFactor);
772     const double w4 = MAX2(POSITION_EPS, myQuarterLaneWidth * widthFactor);
773     const double sideOffset = spreadSuperposed ? w * -0.5 : 0;
774     for (int i = 0; i < e; ++i) {
775         glPushMatrix();
776         glTranslated(getShape()[i].x(), getShape()[i].y(), 0.1);
777         glRotated(myShapeRotations[i], 0, 0, 1);
778         for (double t = 0; t < myShapeLengths[i]; t += w) {
779             const double length = MIN2(w2, myShapeLengths[i] - t) * exaggeration;
780             glBegin(GL_TRIANGLES);
781             glVertex2d(sideOffset, -t - length);
782             glVertex2d(sideOffset - w4 * exaggeration, -t);
783             glVertex2d(sideOffset + w4 * exaggeration, -t);
784             glEnd();
785         }
786         glPopMatrix();
787     }
788     glPopMatrix();
789 }
790 
791 
792 void
debugDrawFoeIntersections() const793 GUILane::debugDrawFoeIntersections() const {
794     glPushMatrix();
795     glColor3d(1.0, 0.3, 0.3);
796     const double orthoLength = 0.5;
797     const MSLink* link = getLinkCont().front();
798     const std::vector<const MSLane*>& foeLanes = link->getFoeLanes();
799     const std::vector<std::pair<double, double> >& lengthsBehind = link->getLengthsBehindCrossing();
800     if (foeLanes.size() == lengthsBehind.size()) {
801         for (int i = 0; i < (int)foeLanes.size(); ++i) {
802             const MSLane* l = foeLanes[i];
803             Position pos = l->geometryPositionAtOffset(l->getLength() - lengthsBehind[i].second);
804             PositionVector ortho = l->getShape().getOrthogonal(pos, 10, true, orthoLength);
805             if (ortho.length() < orthoLength) {
806                 ortho.extrapolate(orthoLength - ortho.length(), false, true);
807             }
808             GLHelper::drawLine(ortho);
809             //std::cout << "foe=" << l->getID() << " lanePos=" << l->getLength() - lengthsBehind[i].second << " pos=" << pos << "\n";
810         }
811     }
812     glPopMatrix();
813 }
814 
815 
816 // ------ inherited from GUIGlObject
817 GUIGLObjectPopupMenu*
getPopUpMenu(GUIMainWindow & app,GUISUMOAbstractView & parent)818 GUILane::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {
819     GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, *this);
820     buildPopupHeader(ret, app);
821     buildCenterPopupEntry(ret);
822     //
823     new FXMenuCommand(ret, "Copy edge name to clipboard", nullptr, ret, MID_COPY_EDGE_NAME);
824     buildNameCopyPopupEntry(ret);
825     buildSelectionPopupEntry(ret);
826     //
827     buildShowParamsPopupEntry(ret, false);
828     const double pos = interpolateGeometryPosToLanePos(myShape.nearest_offset_to_point25D(parent.getPositionInformation()));
829     const double height = myShape.positionAtOffset(pos).z();
830     new FXMenuCommand(ret, ("pos: " + toString(pos) + " height: " + toString(height)).c_str(), nullptr, nullptr, 0);
831     new FXMenuSeparator(ret);
832     buildPositionCopyEntry(ret, false);
833     new FXMenuSeparator(ret);
834     if (myAmClosed) {
835         if (myPermissionChanges.empty()) {
836             new FXMenuCommand(ret, "Reopen lane", nullptr, &parent, MID_CLOSE_LANE);
837             new FXMenuCommand(ret, "Reopen edge", nullptr, &parent, MID_CLOSE_EDGE);
838         } else {
839             new FXMenuCommand(ret, "Reopen lane (override rerouter)", nullptr, &parent, MID_CLOSE_LANE);
840             new FXMenuCommand(ret, "Reopen edge (override rerouter)", nullptr, &parent, MID_CLOSE_EDGE);
841         }
842     } else {
843         new FXMenuCommand(ret, "Close lane", nullptr, &parent, MID_CLOSE_LANE);
844         new FXMenuCommand(ret, "Close edge", nullptr, &parent, MID_CLOSE_EDGE);
845     }
846     new FXMenuCommand(ret, "Add rerouter", nullptr, &parent, MID_ADD_REROUTER);
847     return ret;
848 }
849 
850 
851 GUIParameterTableWindow*
getParameterWindow(GUIMainWindow & app,GUISUMOAbstractView &)852 GUILane::getParameterWindow(GUIMainWindow& app, GUISUMOAbstractView&) {
853     GUIParameterTableWindow* ret = new GUIParameterTableWindow(app, *this, 16 + (int)myEdge->getParametersMap().size());
854     // add items
855     ret->mkItem("maxspeed [m/s]", false, getSpeedLimit());
856     ret->mkItem("length [m]", false, myLength);
857     ret->mkItem("width [m]", false, myWidth);
858     ret->mkItem("street name", false, myEdge->getStreetName());
859     ret->mkItem("stored traveltime [s]", true, new FunctionBinding<GUILane, double>(this, &GUILane::getStoredEdgeTravelTime));
860     ret->mkItem("loaded weight", true, new FunctionBinding<GUILane, double>(this, &GUILane::getLoadedEdgeWeight));
861     ret->mkItem("routing speed [m/s]", true, new FunctionBinding<MSEdge, double>(myEdge, &MSEdge::getRoutingSpeed));
862     ret->mkItem("brutto occupancy [%]", true, new FunctionBinding<GUILane, double>(this, &GUILane::getBruttoOccupancy, 100.));
863     ret->mkItem("netto occupancy [%]", true, new FunctionBinding<GUILane, double>(this, &GUILane::getNettoOccupancy, 100.));
864     ret->mkItem("pending insertions [#]", true, new FunctionBinding<GUILane, double>(this, &GUILane::getPendingEmits));
865     ret->mkItem("edge type", false, myEdge->getEdgeType());
866     ret->mkItem("priority", false, myEdge->getPriority());
867     ret->mkItem("allowed vehicle class", false, getVehicleClassNames(myPermissions));
868     ret->mkItem("disallowed vehicle class", false, getVehicleClassNames(~myPermissions));
869     ret->mkItem("permission code", false, myPermissions);
870     if (myEdge->getBidiEdge() != nullptr) {
871         ret->mkItem("bidi-edge", false, myEdge->getBidiEdge()->getID());
872     }
873     for (const auto& kv : myEdge->getParametersMap()) {
874         ret->mkItem(("edgeParam:" + kv.first).c_str(), false, kv.second);
875     }
876     ret->closeBuilding();
877     return ret;
878 }
879 
880 
881 Boundary
getCenteringBoundary() const882 GUILane::getCenteringBoundary() const {
883     Boundary b;
884     b.add(myShape[0]);
885     b.add(myShape[-1]);
886     b.grow(10);
887     // ensure that vehicles and persons on the side are drawn even if the edge
888     // is outside the view
889     return b;
890 }
891 
892 
893 const PositionVector&
getShape() const894 GUILane::getShape() const {
895     return myShape;
896 }
897 
898 
899 const std::vector<double>&
getShapeRotations() const900 GUILane::getShapeRotations() const {
901     return myShapeRotations;
902 }
903 
904 
905 const std::vector<double>&
getShapeLengths() const906 GUILane::getShapeLengths() const {
907     return myShapeLengths;
908 }
909 
910 
911 double
firstWaitingTime() const912 GUILane::firstWaitingTime() const {
913     return myVehicles.size() == 0 ? 0 : myVehicles.back()->getWaitingSeconds();
914 }
915 
916 
917 double
getEdgeLaneNumber() const918 GUILane::getEdgeLaneNumber() const {
919     return (double) myEdge->getLanes().size();
920 }
921 
922 
923 double
getStoredEdgeTravelTime() const924 GUILane::getStoredEdgeTravelTime() const {
925     MSEdgeWeightsStorage& ews = MSNet::getInstance()->getWeightsStorage();
926     if (!ews.knowsTravelTime(myEdge)) {
927         return -1;
928     } else {
929         double value(0);
930         ews.retrieveExistingTravelTime(myEdge, STEPS2TIME(MSNet::getInstance()->getCurrentTimeStep()), value);
931         return value;
932     }
933 }
934 
935 
936 double
getLoadedEdgeWeight() const937 GUILane::getLoadedEdgeWeight() const {
938     MSEdgeWeightsStorage& ews = MSNet::getInstance()->getWeightsStorage();
939     if (!ews.knowsEffort(myEdge)) {
940         return -1;
941     } else {
942         double value(-1);
943         ews.retrieveExistingEffort(myEdge, STEPS2TIME(MSNet::getInstance()->getCurrentTimeStep()), value);
944         return value;
945     }
946 }
947 
948 
949 RGBColor
setColor(const GUIVisualizationSettings & s) const950 GUILane::setColor(const GUIVisualizationSettings& s) const {
951     // setting and retrieving the color does not work in OSGView so we return it explicitliy
952     RGBColor col;
953     if (MSGlobals::gUseMesoSim && static_cast<const GUIEdge*>(myEdge)->getMesoColor() != MESO_USE_LANE_COLOR) {
954         col = static_cast<const GUIEdge*>(myEdge)->getMesoColor();
955     } else {
956         const GUIColorer& c = s.laneColorer;
957         if (!setFunctionalColor(c, col) && !setMultiColor(s, c, col)) {
958             col = c.getScheme().getColor(getColorValue(s, c.getActive()));
959         }
960     }
961     GLHelper::setColor(col);
962     return col;
963 }
964 
965 
966 bool
setFunctionalColor(const GUIColorer & c,RGBColor & col,int activeScheme) const967 GUILane::setFunctionalColor(const GUIColorer& c, RGBColor& col, int activeScheme) const {
968     if (activeScheme < 0) {
969         activeScheme = c.getActive();
970     }
971     switch (activeScheme) {
972         case 0:
973             if (myEdge->isCrossing()) {
974                 // determine priority to decide color
975                 MSLink* link = MSLinkContHelper::getConnectingLink(*getLogicalPredecessorLane(), *this);
976                 if (link->havePriority() || link->getTLLogic() != nullptr) {
977                     col = RGBColor(230, 230, 230);
978                 } else {
979                     col = RGBColor(26, 26, 26);
980                 }
981                 GLHelper::setColor(col);
982                 return true;
983             } else {
984                 return false;
985             }
986         case 18: {
987             double hue = GeomHelper::naviDegree(myShape.beginEndAngle()); // [0-360]
988             col = RGBColor::fromHSV(hue, 1., 1.);
989             GLHelper::setColor(col);
990             return true;
991         }
992         case 30: { // taz color
993             col = c.getScheme().getColor(0);
994             std::vector<RGBColor> tazColors;
995             for (MSEdge* e : myEdge->getPredecessors()) {
996                 if (e->isTazConnector() && e->knowsParameter("tazColor")) {
997                     tazColors.push_back(RGBColor::parseColor(e->getParameter("tazColor")));
998                 }
999             }
1000             for (MSEdge* e : myEdge->getSuccessors()) {
1001                 if (e->isTazConnector() && e->knowsParameter("tazColor")) {
1002                     tazColors.push_back(RGBColor::parseColor(e->getParameter("tazColor")));
1003                 }
1004             }
1005             if (tazColors.size() > 0) {
1006                 int randColor = RandHelper::rand((int)tazColors.size(), RGBColor::getColorRNG());
1007                 col = tazColors[randColor];
1008             }
1009             GLHelper::setColor(col);
1010             return true;
1011         }
1012         default:
1013             return false;
1014     }
1015 }
1016 
1017 
1018 bool
setMultiColor(const GUIVisualizationSettings & s,const GUIColorer & c,RGBColor & col) const1019 GUILane::setMultiColor(const GUIVisualizationSettings& s, const GUIColorer& c, RGBColor& col) const {
1020     const int activeScheme = c.getActive();
1021     myShapeColors.clear();
1022     switch (activeScheme) {
1023         case 22: // color by height at segment start
1024             for (PositionVector::const_iterator ii = myShape.begin(); ii != myShape.end() - 1; ++ii) {
1025                 myShapeColors.push_back(c.getScheme().getColor(ii->z()));
1026             }
1027             // osg fallback (edge height at start)
1028             col = c.getScheme().getColor(getColorValue(s, 21));
1029             return true;
1030         case 24: // color by inclination  at segment start
1031             for (int ii = 1; ii < (int)myShape.size(); ++ii) {
1032                 const double inc = (myShape[ii].z() - myShape[ii - 1].z()) / MAX2(POSITION_EPS, myShape[ii].distanceTo2D(myShape[ii - 1]));
1033                 myShapeColors.push_back(c.getScheme().getColor(inc));
1034             }
1035             col = c.getScheme().getColor(getColorValue(s, 23));
1036             return true;
1037         default:
1038             return false;
1039     }
1040 }
1041 
1042 
1043 double
getColorValue(const GUIVisualizationSettings & s,int activeScheme) const1044 GUILane::getColorValue(const GUIVisualizationSettings& s, int activeScheme) const {
1045     switch (activeScheme) {
1046         case 0:
1047             switch (myPermissions) {
1048                 case SVC_PEDESTRIAN:
1049                     return 1;
1050                 case SVC_BICYCLE:
1051                     return 2;
1052                 case 0:
1053                     return 3;
1054                 case SVC_SHIP:
1055                     return 4;
1056                 case SVC_AUTHORITY:
1057                     return 7;
1058                 default:
1059                     break;
1060             }
1061             if (myEdge->isTazConnector()) {
1062                 return 8;
1063             } else if (isRailway(myPermissions)) {
1064                 return 5;
1065             } else if ((myPermissions & SVC_PASSENGER) != 0) {
1066                 return 0;
1067             } else {
1068                 return 6;
1069             }
1070         case 1:
1071             return isLaneOrEdgeSelected();
1072         case 2:
1073             return (double)myPermissions;
1074         case 3:
1075             return getSpeedLimit();
1076         case 4:
1077             return getBruttoOccupancy();
1078         case 5:
1079             return getNettoOccupancy();
1080         case 6:
1081             return firstWaitingTime();
1082         case 7:
1083             return getEdgeLaneNumber();
1084         case 8:
1085             return getCO2Emissions() / myLength;
1086         case 9:
1087             return getCOEmissions() / myLength;
1088         case 10:
1089             return getPMxEmissions() / myLength;
1090         case 11:
1091             return getNOxEmissions() / myLength;
1092         case 12:
1093             return getHCEmissions() / myLength;
1094         case 13:
1095             return getFuelConsumption() / myLength;
1096         case 14:
1097             return getHarmonoise_NoiseEmissions();
1098         case 15: {
1099             return getStoredEdgeTravelTime();
1100         }
1101         case 16: {
1102             MSEdgeWeightsStorage& ews = MSNet::getInstance()->getWeightsStorage();
1103             if (!ews.knowsTravelTime(myEdge)) {
1104                 return -1;
1105             } else {
1106                 double value(0);
1107                 ews.retrieveExistingTravelTime(myEdge, 0, value);
1108                 return 100 * myLength / value / getSpeedLimit();
1109             }
1110         }
1111         case 17: {
1112             // geometrical length has no meaning for walkingAreas since it describes the outer boundary
1113             return myEdge->isWalkingArea() ? 1 :  1 / myLengthGeometryFactor;
1114         }
1115         case 19: {
1116             return getLoadedEdgeWeight();
1117         }
1118         case 20: {
1119             return myEdge->getPriority();
1120         }
1121         case 21: {
1122             // color by z of first shape point
1123             return getShape()[0].z();
1124         }
1125         case 23: {
1126             // color by incline
1127             return (getShape()[-1].z() - getShape()[0].z()) / getLength();
1128         }
1129         case 25: {
1130             // color by average speed
1131             return getMeanSpeed();
1132         }
1133         case 26: {
1134             // color by average relative speed
1135             return getMeanSpeed() / myMaxSpeed;
1136         }
1137         case 27: {
1138             // color by routing device assumed speed
1139             return myEdge->getRoutingSpeed();
1140         }
1141         case 28:
1142             return getElectricityConsumption() / myLength;
1143         case 29:
1144             return getPendingEmits();
1145         case 31: {
1146             // by numerical edge param value
1147             try {
1148                 return StringUtils::toDouble(myEdge->getParameter(s.edgeParam, "0"));
1149             } catch (NumberFormatException&) {
1150                 try {
1151                     return StringUtils::toBool(myEdge->getParameter(s.edgeParam, "0"));
1152                 } catch (BoolFormatException&) {
1153                     WRITE_WARNING("Edge parameter '" + myEdge->getParameter(s.edgeParam, "0") + "' key '" + s.edgeParam + "' is not a number for edge '" + myEdge->getID() + "'");
1154                     return -1;
1155                 }
1156             }
1157         }
1158         case 32: {
1159             // by numerical lane param value
1160             try {
1161                 return StringUtils::toDouble(getParameter(s.laneParam, "0"));
1162             } catch (NumberFormatException&) {
1163                 try {
1164                     return StringUtils::toBool(getParameter(s.laneParam, "0"));
1165                 } catch (BoolFormatException&) {
1166                     WRITE_WARNING("Lane parameter '" + getParameter(s.laneParam, "0") + "' key '" + s.laneParam + "' is not a number for lane '" + getID() + "'");
1167                     return -1;
1168                 }
1169             }
1170         }
1171         case 33: {
1172             // by edge data value
1173             return GUINet::getGUIInstance()->getEdgeData(myEdge, s.edgeData);
1174         }
1175     }
1176     return 0;
1177 }
1178 
1179 
1180 double
getScaleValue(int activeScheme) const1181 GUILane::getScaleValue(int activeScheme) const {
1182     switch (activeScheme) {
1183         case 0:
1184             return 0;
1185         case 1:
1186             return isLaneOrEdgeSelected();
1187         case 2:
1188             return getSpeedLimit();
1189         case 3:
1190             return getBruttoOccupancy();
1191         case 4:
1192             return getNettoOccupancy();
1193         case 5:
1194             return firstWaitingTime();
1195         case 6:
1196             return getEdgeLaneNumber();
1197         case 7:
1198             return getCO2Emissions() / myLength;
1199         case 8:
1200             return getCOEmissions() / myLength;
1201         case 9:
1202             return getPMxEmissions() / myLength;
1203         case 10:
1204             return getNOxEmissions() / myLength;
1205         case 11:
1206             return getHCEmissions() / myLength;
1207         case 12:
1208             return getFuelConsumption() / myLength;
1209         case 13:
1210             return getHarmonoise_NoiseEmissions();
1211         case 14: {
1212             return getStoredEdgeTravelTime();
1213         }
1214         case 15: {
1215             MSEdgeWeightsStorage& ews = MSNet::getInstance()->getWeightsStorage();
1216             if (!ews.knowsTravelTime(myEdge)) {
1217                 return -1;
1218             } else {
1219                 double value(0);
1220                 ews.retrieveExistingTravelTime(myEdge, 0, value);
1221                 return 100 * myLength / value / getSpeedLimit();
1222             }
1223         }
1224         case 16: {
1225             return 1 / myLengthGeometryFactor;
1226         }
1227         case 17: {
1228             return getLoadedEdgeWeight();
1229         }
1230         case 18: {
1231             return myEdge->getPriority();
1232         }
1233         case 19: {
1234             // scale by average speed
1235             return getMeanSpeed();
1236         }
1237         case 20: {
1238             // scale by average relative speed
1239             return getMeanSpeed() / myMaxSpeed;
1240         }
1241         case 21:
1242             return getElectricityConsumption() / myLength;
1243         case 22:
1244             return MSNet::getInstance()->getInsertionControl().getPendingEmits(this);
1245     }
1246     return 0;
1247 }
1248 
1249 
1250 bool
drawAsRailway(const GUIVisualizationSettings & s) const1251 GUILane::drawAsRailway(const GUIVisualizationSettings& s) const {
1252     return isRailway(myPermissions) && s.showRails && (!s.drawForSelecting || s.spreadSuperposed);
1253 }
1254 
1255 
1256 bool
drawAsWaterway(const GUIVisualizationSettings & s) const1257 GUILane::drawAsWaterway(const GUIVisualizationSettings& s) const {
1258     return isWaterway(myPermissions) && s.showRails && !s.drawForSelecting; // reusing the showRails setting
1259 }
1260 
1261 
1262 #ifdef HAVE_OSG
1263 void
updateColor(const GUIVisualizationSettings & s)1264 GUILane::updateColor(const GUIVisualizationSettings& s) {
1265     if (myGeom == 0) {
1266         // not drawn
1267         return;
1268     }
1269     const RGBColor col = setColor(s);
1270     osg::Vec4ubArray* colors = dynamic_cast<osg::Vec4ubArray*>(myGeom->getColorArray());
1271     (*colors)[0].set(col.red(), col.green(), col.blue(), col.alpha());
1272     myGeom->setColorArray(colors);
1273 }
1274 #endif
1275 
1276 
1277 void
closeTraffic(bool rebuildAllowed)1278 GUILane::closeTraffic(bool rebuildAllowed) {
1279     MSGlobals::gCheckRoutes = false;
1280     if (myAmClosed) {
1281         myPermissionChanges.clear(); // reset rerouters
1282         resetPermissions(CHANGE_PERMISSIONS_GUI);
1283     } else {
1284         setPermissions(SVC_AUTHORITY, CHANGE_PERMISSIONS_GUI);
1285     }
1286     myAmClosed = !myAmClosed;
1287     if (rebuildAllowed) {
1288         getEdge().rebuildAllowedLanes();
1289         for (MSEdge* const pred : getEdge().getPredecessors()) {
1290             pred->rebuildAllowedTargets();
1291         }
1292     }
1293 }
1294 
1295 
1296 PositionVector
splitAtSegments(const PositionVector & shape)1297 GUILane::splitAtSegments(const PositionVector& shape) {
1298     assert(MSGlobals::gUseMesoSim);
1299     int no = MELoop::numSegmentsFor(myLength, OptionsCont::getOptions().getFloat("meso-edgelength"));
1300     const double slength = myLength / no;
1301     PositionVector result = shape;
1302     double offset = 0;
1303     for (int i = 0; i < no; ++i) {
1304         offset += slength;
1305         Position pos = shape.positionAtOffset(offset);
1306         int index = result.indexOfClosest(pos);
1307         if (pos.distanceTo(result[index]) > POSITION_EPS) {
1308             index = result.insertAtClosest(pos);
1309         }
1310         while ((int)myShapeSegments.size() < index) {
1311             myShapeSegments.push_back(i);
1312         }
1313         //std::cout << "splitAtSegments " << getID() << " no=" << no << " i=" << i << " offset=" << offset << " index=" << index << " segs=" << toString(myShapeSegments) << " resultSize=" << result.size() << " result=" << toString(result) << "\n";
1314     }
1315     while (myShapeSegments.size() < result.size()) {
1316         myShapeSegments.push_back(no - 1);
1317     }
1318     return result;
1319 }
1320 
1321 bool
isSelected() const1322 GUILane::isSelected() const {
1323     return gSelected.isSelected(GLO_LANE, getGlID());
1324 }
1325 
1326 bool
isLaneOrEdgeSelected() const1327 GUILane::isLaneOrEdgeSelected() const {
1328     return isSelected() || gSelected.isSelected(GLO_EDGE, dynamic_cast<GUIEdge*>(myEdge)->getGlID());
1329 }
1330 
1331 double
getPendingEmits() const1332 GUILane::getPendingEmits() const {
1333     return MSNet::getInstance()->getInsertionControl().getPendingEmits(this);
1334 }
1335 
1336 
1337 /****************************************************************************/
1338