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