Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/guisim/GUILane.cpp
169666 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
4
// This program and the accompanying materials are made available under the
5
// terms of the Eclipse Public License 2.0 which is available at
6
// https://www.eclipse.org/legal/epl-2.0/
7
// This Source Code may also be made available under the following Secondary
8
// Licenses when the conditions for such availability set forth in the Eclipse
9
// Public License 2.0 are satisfied: GNU General Public License, version 2
10
// or later which is available at
11
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
/****************************************************************************/
14
/// @file GUILane.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Michael Behrisch
18
/// @date Sept 2002
19
///
20
// Representation of a lane in the micro simulation (gui-version)
21
/****************************************************************************/
22
#include <config.h>
23
24
#include <string>
25
#include <utility>
26
#include <utils/foxtools/fxheader.h>
27
#include <utils/geom/GeomHelper.h>
28
#include <utils/geom/Position.h>
29
#include <microsim/logging/FunctionBinding.h>
30
#include <utils/options/OptionsCont.h>
31
#include <utils/common/MsgHandler.h>
32
#include <utils/common/StdDefs.h>
33
#include <utils/geom/GeomHelper.h>
34
#include <utils/gui/div/GLHelper.h>
35
#include <utils/gui/div/GUIParameterTableWindow.h>
36
#include <utils/gui/div/GUIGlobalSelection.h>
37
#include <utils/gui/globjects/GLIncludes.h>
38
#include <utils/gui/globjects/GUIPolygon.h>
39
#include <utils/gui/images/VClassIcons.h>
40
#include <utils/gui/windows/GUISUMOAbstractView.h>
41
#include <utils/gui/windows/GUIAppEnum.h>
42
#include <gui/GUIGlobals.h>
43
#include <microsim/MSGlobals.h>
44
#include <microsim/MSLane.h>
45
#include <microsim/MSLink.h>
46
#include <microsim/MSVehicleControl.h>
47
#include <microsim/MSInsertionControl.h>
48
#include <microsim/MSVehicleTransfer.h>
49
#include <microsim/MSNet.h>
50
#include <microsim/MSEdgeWeightsStorage.h>
51
#include <microsim/MSParkingArea.h>
52
#include <microsim/devices/MSDevice_Routing.h>
53
#include <microsim/traffic_lights/MSRailSignal.h>
54
#include <microsim/traffic_lights/MSDriveWay.h>
55
#include <mesosim/MELoop.h>
56
#include <mesosim/MESegment.h>
57
#include "GUILane.h"
58
#include "GUIEdge.h"
59
#include "GUIVehicle.h"
60
#include "GUINet.h"
61
#include <utils/gui/div/GUIDesigns.h>
62
63
#include <osgview/GUIOSGHeader.h>
64
65
#define RENDERING_BUFFER 10
66
67
//#define GUILane_DEBUG_DRAW_FOE_INTERSECTIONS
68
//#define GUILane_DEBUG_DRAW_CROSSING_OUTLINE
69
70
// ===========================================================================
71
// static member declaration
72
// ===========================================================================
73
const RGBColor GUILane::MESO_USE_LANE_COLOR(0, 0, 0, 0);
74
GUIVisualizationSettings* GUILane::myCachedGUISettings(nullptr);
75
76
77
// ===========================================================================
78
// method definitions
79
// ===========================================================================
80
GUILane::GUILane(const std::string& id, double maxSpeed, double friction, double length,
81
MSEdge* const edge, int numericalID,
82
const PositionVector& shape, double width,
83
SVCPermissions permissions,
84
SVCPermissions changeLeft, SVCPermissions changeRight,
85
int index, bool isRampAccel,
86
const std::string& type,
87
const PositionVector& outlineShape) :
88
MSLane(id, maxSpeed, friction, length, edge, numericalID, shape, width, permissions, changeLeft, changeRight, index, isRampAccel, type, outlineShape),
89
GUIGlObject(GLO_LANE, id, GUIIconSubSys::getIcon(GUIIcon::LANE)),
90
myParkingAreas(nullptr),
91
myTesselation(nullptr),
92
#ifdef HAVE_OSG
93
myGeom(0),
94
#endif
95
myAmClosed(false),
96
myLengthGeometryFactor2(myLengthGeometryFactor),
97
myLock(true) {
98
if (MSGlobals::gUseMesoSim) {
99
myShape = splitAtSegments(shape);
100
assert(fabs(myShape.length() - shape.length()) < POSITION_EPS);
101
assert(myShapeSegments.size() == myShape.size());
102
}
103
initRotations(myShape, myShapeRotations, myShapeLengths, myShapeColors);
104
//
105
myHalfLaneWidth = myWidth / 2.;
106
myQuarterLaneWidth = myWidth / 4.;
107
}
108
109
110
GUILane::~GUILane() {
111
// just to quit cleanly on a failure
112
if (myLock.locked()) {
113
myLock.unlock();
114
}
115
delete myParkingAreas;
116
delete myTesselation;
117
}
118
119
120
void
121
GUILane::initRotations(const PositionVector& shape,
122
std::vector<double>& rotations,
123
std::vector<double>& lengths,
124
std::vector<RGBColor>& colors) {
125
rotations.clear();
126
lengths.clear();
127
colors.clear();
128
rotations.reserve(shape.size() - 1);
129
lengths.reserve(shape.size() - 1);
130
colors.reserve(shape.size() - 1);
131
int e = (int) shape.size() - 1;
132
for (int i = 0; i < e; ++i) {
133
const Position& f = shape[i];
134
const Position& s = shape[i + 1];
135
lengths.push_back(f.distanceTo2D(s));
136
rotations.push_back(RAD2DEG(atan2(s.x() - f.x(), f.y() - s.y())));
137
}
138
}
139
140
141
void
142
GUILane::addSecondaryShape(const PositionVector& shape) {
143
myShape2 = shape;
144
initRotations(myShape2, myShapeRotations2, myShapeLengths2, myShapeColors2);
145
myLengthGeometryFactor2 = MAX2(POSITION_EPS, myShape2.length()) / myLength;
146
}
147
148
149
// ------ Vehicle insertion ------
150
void
151
GUILane::incorporateVehicle(MSVehicle* veh, double pos, double speed, double posLat,
152
const MSLane::VehCont::iterator& at,
153
MSMoveReminder::Notification notification) {
154
FXMutexLock locker(myLock);
155
MSLane::incorporateVehicle(veh, pos, speed, posLat, at, notification);
156
}
157
158
159
// ------ Access to vehicles ------
160
const MSLane::VehCont&
161
GUILane::getVehiclesSecure() const {
162
myLock.lock();
163
return myVehicles;
164
}
165
166
167
void
168
GUILane::releaseVehicles() const {
169
myLock.unlock();
170
}
171
172
173
void
174
GUILane::planMovements(const SUMOTime t) {
175
FXMutexLock locker(myLock);
176
MSLane::planMovements(t);
177
}
178
179
void
180
GUILane::setJunctionApproaches() const {
181
FXMutexLock locker(myLock);
182
MSLane::setJunctionApproaches();
183
}
184
185
186
void
187
GUILane::executeMovements(const SUMOTime t) {
188
FXMutexLock locker(myLock);
189
MSLane::executeMovements(t);
190
}
191
192
193
MSVehicle*
194
GUILane::removeVehicle(MSVehicle* remVehicle, MSMoveReminder::Notification notification, bool notify) {
195
FXMutexLock locker(myLock);
196
return MSLane::removeVehicle(remVehicle, notification, notify);
197
}
198
199
200
void
201
GUILane::removeParking(MSBaseVehicle* remVehicle) {
202
FXMutexLock locker(myLock);
203
return MSLane::removeParking(remVehicle);
204
}
205
206
207
void
208
GUILane::swapAfterLaneChange(SUMOTime t) {
209
FXMutexLock locker(myLock);
210
MSLane::swapAfterLaneChange(t);
211
}
212
213
214
void
215
GUILane::integrateNewVehicles() {
216
FXMutexLock locker(myLock);
217
MSLane::integrateNewVehicles();
218
}
219
220
221
void
222
GUILane::detectCollisions(SUMOTime timestep, const std::string& stage) {
223
FXMutexLock locker(myLock);
224
MSLane::detectCollisions(timestep, stage);
225
}
226
227
228
double
229
GUILane::setPartialOccupation(MSVehicle* v) {
230
FXMutexLock locker(myLock);
231
return MSLane::setPartialOccupation(v);
232
}
233
234
235
void
236
GUILane::resetPartialOccupation(MSVehicle* v) {
237
FXMutexLock locker(myLock);
238
MSLane::resetPartialOccupation(v);
239
}
240
241
242
// ------ Drawing methods ------
243
void
244
GUILane::drawLinkNo(const GUIVisualizationSettings& s) const {
245
int noLinks = (int)myLinks.size();
246
if (noLinks == 0) {
247
return;
248
}
249
// draw all links
250
if (getEdge().isCrossing()) {
251
// draw indices at the start and end of the crossing
252
const MSLink* const link = getLogicalPredecessorLane()->getLinkTo(this);
253
PositionVector shape = getShape(s.secondaryShape);
254
shape.extrapolate(0.5); // draw on top of the walking area
255
GLHelper::drawTextAtEnd(toString(link->getIndex()), shape, 0, s.drawLinkJunctionIndex, s.scale);
256
GLHelper::drawTextAtEnd(toString(link->getIndex()), shape.reverse(), 0, s.drawLinkJunctionIndex, s.scale);
257
return;
258
}
259
// draw all links
260
double w = myWidth / (double) noLinks;
261
double x1 = myHalfLaneWidth;
262
for (int i = noLinks; --i >= 0;) {
263
double x2 = x1 - (double)(w / 2.);
264
GLHelper::drawTextAtEnd(toString(myLinks[MSGlobals::gLefthand ? noLinks - 1 - i : i]->getIndex()), getShape(s.secondaryShape), x2, s.drawLinkJunctionIndex, s.scale);
265
x1 -= w;
266
}
267
}
268
269
270
void
271
GUILane::drawTLSLinkNo(const GUIVisualizationSettings& s, const GUINet& net) const {
272
int noLinks = (int)myLinks.size();
273
if (noLinks == 0) {
274
return;
275
}
276
if (getEdge().isCrossing()) {
277
// draw indices at the start and end of the crossing
278
const MSLink* const link = getLogicalPredecessorLane()->getLinkTo(this);
279
int linkNo = net.getLinkTLIndex(link);
280
// maybe the reverse link is controlled separately
281
int linkNo2 = net.getLinkTLIndex(myLinks.front());
282
// otherwise, use the same index as the forward link
283
if (linkNo2 < 0) {
284
linkNo2 = linkNo;
285
}
286
if (linkNo >= 0) {
287
PositionVector shape = getShape(s.secondaryShape);
288
shape.extrapolate(0.5); // draw on top of the walking area
289
GLHelper::drawTextAtEnd(toString(linkNo2), shape, 0, s.drawLinkTLIndex, s.scale);
290
GLHelper::drawTextAtEnd(toString(linkNo), shape.reverse(), 0, s.drawLinkTLIndex, s.scale);
291
}
292
return;
293
}
294
// draw all links
295
double w = myWidth / (double) noLinks;
296
double x1 = myHalfLaneWidth;
297
for (int i = noLinks; --i >= 0;) {
298
double x2 = x1 - (double)(w / 2.);
299
int linkNo = net.getLinkTLIndex(myLinks[MSGlobals::gLefthand ? noLinks - 1 - i : i]);
300
if (linkNo < 0) {
301
continue;
302
}
303
GLHelper::drawTextAtEnd(toString(linkNo), getShape(s.secondaryShape), x2, s.drawLinkTLIndex, s.scale);
304
x1 -= w;
305
}
306
}
307
308
309
void
310
GUILane::drawLinkRules(const GUIVisualizationSettings& s, const GUINet& net) const {
311
int noLinks = (int)myLinks.size();
312
const PositionVector& shape = getShape(s.secondaryShape);
313
if (noLinks == 0) {
314
drawLinkRule(s, net, nullptr, shape, 0, 0);
315
return;
316
}
317
if (getEdge().isCrossing()) {
318
// draw rules at the start and end of the crossing
319
const MSLink* const link = getLogicalPredecessorLane()->getLinkTo(this);
320
const MSLink* link2 = myLinks.front();
321
if (link2->getTLLogic() == nullptr) {
322
link2 = link;
323
}
324
PositionVector tmp = shape;
325
tmp.extrapolate(0.5); // draw on top of the walking area
326
drawLinkRule(s, net, link2, tmp, 0, myWidth);
327
drawLinkRule(s, net, link, tmp.reverse(), 0, myWidth);
328
return;
329
}
330
// draw all links
331
const double isRailSignal = myEdge->getToJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL;
332
double w = myWidth / (double) noLinks;
333
if (isRailSignal && noLinks > 1 && myLinks.back()->isTurnaround() && s.showRails) {
334
w = myWidth / (double)(noLinks - 1);
335
}
336
double x1 = isRailSignal ? -myWidth * 0.5 : 0;
337
for (int i = 0; i < noLinks; ++i) {
338
double x2 = x1 + w;
339
drawLinkRule(s, net, myLinks[MSGlobals::gLefthand ? noLinks - 1 - i : i], shape, x1, x2);
340
x1 = x2;
341
}
342
// draw stopOffset for passenger cars
343
if (myLaneStopOffset.isDefined() && (myLaneStopOffset.getPermissions() & SVC_PASSENGER) != 0) {
344
const double stopOffsetPassenger = myLaneStopOffset.getOffset();
345
const Position& end = shape.back();
346
const Position& f = shape[-2];
347
const double rot = RAD2DEG(atan2((end.x() - f.x()), (f.y() - end.y())));
348
GLHelper::setColor(s.getLinkColor(LINKSTATE_MAJOR));
349
GLHelper::pushMatrix();
350
glTranslated(end.x(), end.y(), 0);
351
glRotated(rot, 0, 0, 1);
352
glTranslated(0, stopOffsetPassenger, 0);
353
glBegin(GL_QUADS);
354
glVertex2d(-myHalfLaneWidth, 0.0);
355
glVertex2d(-myHalfLaneWidth, 0.2);
356
glVertex2d(myHalfLaneWidth, 0.2);
357
glVertex2d(myHalfLaneWidth, 0.0);
358
glEnd();
359
GLHelper::popMatrix();
360
}
361
}
362
363
364
void
365
GUILane::drawLinkRule(const GUIVisualizationSettings& s, const GUINet& net, const MSLink* link, const PositionVector& shape, double x1, double x2) const {
366
const Position& end = shape.back();
367
const Position& f = shape[-2];
368
const double rot = RAD2DEG(atan2((end.x() - f.x()), (f.y() - end.y())));
369
if (link == nullptr) {
370
if (static_cast<GUIEdge*>(myEdge)->showDeadEnd()) {
371
GLHelper::setColor(GUIVisualizationColorSettings::SUMO_color_DEADEND_SHOW);
372
} else {
373
GLHelper::setColor(GUIVisualizationSettings::getLinkColor(LINKSTATE_DEADEND));
374
}
375
GLHelper::pushMatrix();
376
glTranslated(end.x(), end.y(), 0);
377
glRotated(rot, 0, 0, 1);
378
glBegin(GL_QUADS);
379
glVertex2d(-myHalfLaneWidth, 0.0);
380
glVertex2d(-myHalfLaneWidth, 0.5);
381
glVertex2d(myHalfLaneWidth, 0.5);
382
glVertex2d(myHalfLaneWidth, 0.0);
383
glEnd();
384
GLHelper::popMatrix();
385
} else {
386
GLHelper::pushMatrix();
387
glTranslated(end.x(), end.y(), 0);
388
glRotated(rot, 0, 0, 1);
389
// select glID
390
391
switch (link->getState()) {
392
case LINKSTATE_ALLWAY_STOP:
393
case LINKSTATE_STOP: {
394
// might be a traffic light link
395
int tlID = net.getLinkTLID(link);
396
GLHelper::pushName(tlID != 0 ? tlID : getGlID());
397
break;
398
}
399
case LINKSTATE_TL_GREEN_MAJOR:
400
case LINKSTATE_TL_GREEN_MINOR:
401
case LINKSTATE_TL_RED:
402
case LINKSTATE_TL_REDYELLOW:
403
case LINKSTATE_TL_YELLOW_MAJOR:
404
case LINKSTATE_TL_YELLOW_MINOR:
405
case LINKSTATE_TL_OFF_BLINKING:
406
case LINKSTATE_TL_OFF_NOSIGNAL:
407
GLHelper::pushName(net.getLinkTLID(link));
408
break;
409
case LINKSTATE_MAJOR:
410
case LINKSTATE_MINOR:
411
case LINKSTATE_EQUAL:
412
default:
413
GLHelper::pushName(getGlID());
414
break;
415
}
416
GLHelper::setColor(GUIVisualizationSettings::getLinkColor(link->getState(), s.realisticLinkRules));
417
if (!(drawAsRailway(s) || drawAsWaterway(s)) || link->getState() != LINKSTATE_MAJOR) {
418
// the white bar should be the default for most railway
419
// links and looks ugly so we do not draw it
420
double scale = isInternal() ? 0.5 : 1;
421
if (myEdge->getToJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
422
scale *= MAX2(s.laneWidthExaggeration, s.junctionSize.getExaggeration(s, this, 10));
423
}
424
glScaled(scale, scale, 1);
425
glBegin(GL_QUADS);
426
glVertex2d(x1 - myHalfLaneWidth, 0.0);
427
glVertex2d(x1 - myHalfLaneWidth, 0.5);
428
glVertex2d(x2 - myHalfLaneWidth, 0.5);
429
glVertex2d(x2 - myHalfLaneWidth, 0.0);
430
glEnd();
431
if (s.gaming && link->haveGreen()) {
432
const MSLane* lane = link->getLane();
433
// exaggerate green signals for railway game
434
if (isRailway(lane->getPermissions())) {
435
GLHelper::drawFilledCircle(lane->getWidth() / 2., 8, 90, 270);
436
}
437
}
438
}
439
GLHelper::popName();
440
GLHelper::popMatrix();
441
}
442
}
443
444
void
445
GUILane::drawArrows(bool secondaryShape) const {
446
if (myLinks.size() == 0) {
447
return;
448
}
449
// draw all links
450
const Position& end = getShape(secondaryShape).back();
451
const Position& f = getShape(secondaryShape)[-2];
452
const double rot = RAD2DEG(atan2((end.x() - f.x()), (f.y() - end.y())));
453
GLHelper::pushMatrix();
454
glColor3d(1, 1, 1);
455
glTranslated(end.x(), end.y(), 0);
456
glRotated(rot, 0, 0, 1);
457
if (myWidth < SUMO_const_laneWidth) {
458
glScaled(myWidth / SUMO_const_laneWidth, 1, 1);
459
}
460
for (const MSLink* const link : myLinks) {
461
LinkDirection dir = link->getDirection();
462
LinkState state = link->getState();
463
if (state == LINKSTATE_DEADEND || dir == LinkDirection::NODIR) {
464
continue;
465
}
466
switch (dir) {
467
case LinkDirection::STRAIGHT:
468
GLHelper::drawBoxLine(Position(0, 4), 0, 2, .05);
469
GLHelper::drawTriangleAtEnd(Position(0, 4), Position(0, 1), (double) 1, (double) .25);
470
break;
471
case LinkDirection::TURN:
472
GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);
473
GLHelper::drawBoxLine(Position(0, 2.5), 90, .5, .05);
474
GLHelper::drawBoxLine(Position(0.5, 2.5), 180, 1, .05);
475
GLHelper::drawTriangleAtEnd(Position(0.5, 2.5), Position(0.5, 4), (double) 1, (double) .25);
476
break;
477
case LinkDirection::TURN_LEFTHAND:
478
GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);
479
GLHelper::drawBoxLine(Position(0, 2.5), -90, .5, .05);
480
GLHelper::drawBoxLine(Position(-0.5, 2.5), -180, 1, .05);
481
GLHelper::drawTriangleAtEnd(Position(-0.5, 2.5), Position(-0.5, 4), (double) 1, (double) .25);
482
break;
483
case LinkDirection::LEFT:
484
GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);
485
GLHelper::drawBoxLine(Position(0, 2.5), 90, 1, .05);
486
GLHelper::drawTriangleAtEnd(Position(0, 2.5), Position(1.5, 2.5), (double) 1, (double) .25);
487
break;
488
case LinkDirection::RIGHT:
489
GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);
490
GLHelper::drawBoxLine(Position(0, 2.5), -90, 1, .05);
491
GLHelper::drawTriangleAtEnd(Position(0, 2.5), Position(-1.5, 2.5), (double) 1, (double) .25);
492
break;
493
case LinkDirection::PARTLEFT:
494
GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);
495
GLHelper::drawBoxLine(Position(0, 2.5), 45, .7, .05);
496
GLHelper::drawTriangleAtEnd(Position(0, 2.5), Position(1.2, 1.3), (double) 1, (double) .25);
497
break;
498
case LinkDirection::PARTRIGHT:
499
GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);
500
GLHelper::drawBoxLine(Position(0, 2.5), -45, .7, .05);
501
GLHelper::drawTriangleAtEnd(Position(0, 2.5), Position(-1.2, 1.3), (double) 1, (double) .25);
502
break;
503
default:
504
break;
505
}
506
}
507
GLHelper::popMatrix();
508
}
509
510
511
void
512
GUILane::drawLane2LaneConnections(double exaggeration, bool s2) const {
513
Position centroid;
514
if (exaggeration > 1) {
515
centroid = myEdge->getToJunction()->getShape().getCentroid();
516
}
517
for (const MSLink* const link : myLinks) {
518
const GUILane* connected = dynamic_cast<GUILane*>(link->getLane());
519
if (connected == nullptr) {
520
continue;
521
}
522
GLHelper::setColor(GUIVisualizationSettings::getLinkColor(link->getState()));
523
glBegin(GL_LINES);
524
Position p1 = myEdge->isWalkingArea() ? getShape(s2).getCentroid() : getShape(s2)[-1];
525
Position p2 = connected->getEdge().isWalkingArea() ? connected->getShape(s2).getCentroid() : connected->getShape(s2)[0];
526
if (exaggeration > 1) {
527
p1 = centroid + ((p1 - centroid) * exaggeration);
528
p2 = centroid + ((p2 - centroid) * exaggeration);
529
}
530
glVertex2d(p1.x(), p1.y());
531
glVertex2d(p2.x(), p2.y());
532
glEnd();
533
GLHelper::drawTriangleAtEnd(p1, p2, (double) .4, (double) .2);
534
}
535
}
536
537
538
void
539
GUILane::drawGL(const GUIVisualizationSettings& s) const {
540
GLHelper::pushMatrix();
541
GLHelper::pushName(getGlID());
542
const bool s2 = s.secondaryShape;
543
double exaggeration = s.laneWidthExaggeration;
544
if (MSGlobals::gUseMesoSim) {
545
GUIEdge* myGUIEdge = dynamic_cast<GUIEdge*>(myEdge);
546
exaggeration *= s.edgeScaler.getScheme().getColor(myGUIEdge->getScaleValue(s, s.edgeScaler.getActive()));
547
} else {
548
exaggeration *= s.laneScaler.getScheme().getColor(getScaleValue(s, s.laneScaler.getActive(), s2));
549
}
550
// set lane color
551
const RGBColor color = setColor(s);
552
// recognize full transparency and simply don't draw
553
if (color.alpha() != 0 && s.scale * exaggeration > s.laneMinSize) {
554
555
const bool isCrossing = myEdge->isCrossing();
556
const bool isWalkingArea = myEdge->isWalkingArea();
557
const bool isInternal = isCrossing || isWalkingArea || myEdge->isInternal();
558
const PositionVector& baseShape = getShape(s2);
559
const bool hasRailSignal = myEdge->getToJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL;
560
if (s.trueZ) {
561
glTranslated(0, 0, baseShape.getMinZ());
562
} else {
563
if (isCrossing) {
564
// draw internal lanes on top of junctions
565
glTranslated(0, 0, GLO_JUNCTION + 0.1);
566
} else if (isWalkingArea) {
567
// draw internal lanes on top of junctions
568
glTranslated(0, 0, GLO_JUNCTION + 0.3);
569
} else if (isWaterway(myPermissions)) {
570
// draw waterways below normal roads
571
glTranslated(0, 0, getType() - 0.2);
572
} else if (myPermissions == SVC_SUBWAY) {
573
// draw subways further below
574
glTranslated(0, 0, getType() - 0.4);
575
} else {
576
glTranslated(0, 0, getType());
577
}
578
}
579
auto& shapeColors = getShapeColors(s2);
580
if (MSGlobals::gUseMesoSim) {
581
shapeColors.clear();
582
const std::vector<RGBColor>& segmentColors = static_cast<const GUIEdge*>(myEdge)->getSegmentColors();
583
if (segmentColors.size() > 0) {
584
// apply segment specific shape colors
585
//std::cout << getID() << " shape=" << myShape << " shapeSegs=" << toString(myShapeSegments) << "\n";
586
for (int ii = 0; ii < (int)baseShape.size() - 1; ++ii) {
587
shapeColors.push_back(segmentColors[myShapeSegments[ii]]);
588
}
589
}
590
}
591
592
// scale tls-controlled lane2lane-arrows along with their junction shapes
593
double junctionExaggeration = 1;
594
if (!isInternal
595
&& myEdge->getToJunction()->getType() <= SumoXMLNodeType::RAIL_CROSSING
596
&& (s.junctionSize.constantSize || s.junctionSize.exaggeration > 1)) {
597
junctionExaggeration = MAX2(1.001, s.junctionSize.getExaggeration(s, this, 4));
598
}
599
// draw lane
600
// check whether it is not too small
601
if (s.scale * exaggeration < 1. && junctionExaggeration == 1 && s.junctionSize.minSize != 0) {
602
if (!isInternal || hasRailSignal) {
603
if (shapeColors.size() > 0) {
604
GLHelper::drawLine(baseShape, shapeColors);
605
} else {
606
GLHelper::drawLine(baseShape);
607
}
608
}
609
GLHelper::popMatrix();
610
611
} else {
612
613
GUINet* net = (GUINet*) MSNet::getInstance();
614
bool mustDrawMarkings = false;
615
bool hiddenBidi = getBidiLane() != nullptr && myEdge->getNumericalID() > myEdge->getBidiEdge()->getNumericalID();
616
const bool detailZoom = s.scale * exaggeration > 5;
617
const bool drawDetails = (detailZoom || s.junctionSize.minSize == 0 || hasRailSignal);
618
const bool drawRails = drawAsRailway(s);
619
const bool spreadSuperposed = s.spreadSuperposed && myBidiLane != nullptr;
620
if (hiddenBidi && !spreadSuperposed) {
621
// do not draw shape
622
mustDrawMarkings = !isInternal && myPermissions != 0 && myPermissions != SVC_PEDESTRIAN && exaggeration == 1.0 && !isWaterway(myPermissions) && neighLaneNotBidi();
623
} else if (drawRails) {
624
// draw as railway: assume standard gauge of 1435mm when lane width is not set
625
// draw foot width 150mm, assume that distance between rail feet inner sides is reduced on both sides by 39mm with regard to the gauge
626
// assume crosstie length of 181% gauge (2600mm for standard gauge)
627
PositionVector shape = baseShape;
628
const double width = myWidth;
629
double halfGauge = 0.5 * (width == SUMO_const_laneWidth ? 1.4350 : width) * exaggeration;
630
if (spreadSuperposed) {
631
try {
632
shape.move2side(halfGauge * 0.8);
633
} catch (InvalidArgument&) {}
634
halfGauge *= 0.4;
635
}
636
const double halfInnerFeetWidth = halfGauge - 0.039 * exaggeration;
637
const double halfRailWidth = detailZoom ? (halfInnerFeetWidth + 0.15 * exaggeration) : SUMO_const_halfLaneWidth * exaggeration;
638
const double halfCrossTieWidth = halfGauge * 1.81;
639
if (shapeColors.size() > 0) {
640
GLHelper::drawBoxLines(shape, getShapeRotations(s2), getShapeLengths(s2), getShapeColors(s2), halfRailWidth);
641
} else {
642
GLHelper::drawBoxLines(shape, getShapeRotations(s2), getShapeLengths(s2), halfRailWidth);
643
}
644
// Draw white on top with reduced width (the area between the two tracks)
645
if (detailZoom) {
646
glColor3d(1, 1, 1);
647
glTranslated(0, 0, .1);
648
GLHelper::drawBoxLines(shape, getShapeRotations(s2), getShapeLengths(s2), halfInnerFeetWidth);
649
setColor(s);
650
GLHelper::drawCrossTies(shape, getShapeRotations(s2), getShapeLengths(s2), 0.26 * exaggeration, 0.6 * exaggeration,
651
halfCrossTieWidth, 0, s.forceDrawForRectangleSelection);
652
}
653
} else if (isCrossing) {
654
if (s.drawCrossingsAndWalkingareas && (s.scale > 3.0 || s.junctionSize.minSize == 0)) {
655
glTranslated(0, 0, .2);
656
GLHelper::drawCrossTies(baseShape, getShapeRotations(s2), getShapeLengths(s2), 0.5, 1.0, getWidth() * 0.5,
657
0, s.drawForRectangleSelection);
658
#ifdef GUILane_DEBUG_DRAW_CROSSING_OUTLINE
659
if (myOutlineShape != nullptr) {
660
GLHelper::setColor(RGBColor::BLUE);
661
glTranslated(0, 0, 0.4);
662
GLHelper::drawBoxLines(*myOutlineShape, 0.1);
663
glTranslated(0, 0, -0.4);
664
if (s.geometryIndices.show(this)) {
665
GLHelper::debugVertices(*myOutlineShape, s.geometryIndices, s.scale);
666
}
667
}
668
#endif
669
glTranslated(0, 0, -.2);
670
}
671
} else if (isWalkingArea) {
672
if (s.drawCrossingsAndWalkingareas && (s.scale > 3.0 || s.junctionSize.minSize == 0)) {
673
glTranslated(0, 0, .2);
674
if (myTesselation == nullptr) {
675
myTesselation = new TesselatedPolygon(getID(), "", RGBColor::MAGENTA, PositionVector(), false, true, 0);
676
}
677
myTesselation->drawTesselation(baseShape);
678
glTranslated(0, 0, -.2);
679
if (s.geometryIndices.show(this)) {
680
GLHelper::debugVertices(baseShape, s.geometryIndices, s.scale);
681
}
682
}
683
} else {
684
// we draw the lanes with reduced width so that the lane markings below are visible
685
// (this avoids artifacts at geometry corners without having to
686
// compute lane-marking intersection points)
687
double halfWidth = isInternal ? myQuarterLaneWidth : (myHalfLaneWidth - SUMO_const_laneMarkWidth / 2);
688
mustDrawMarkings = !isInternal && myPermissions != 0 && myPermissions != SVC_PEDESTRIAN && exaggeration == 1.0 && !isWaterway(myPermissions) && !isAirway(myPermissions);
689
const int cornerDetail = drawDetails && !isInternal ? (s.drawForRectangleSelection ? 4 : MIN2(32, (int)(s.scale * exaggeration))) : 0;
690
double offset = halfWidth * MAX2(0., (exaggeration - 1)) * (MSGlobals::gLefthand ? -1 : 1);
691
if (spreadSuperposed) {
692
offset += halfWidth * 0.5 * (MSGlobals::gLefthand ? -1 : 1);
693
halfWidth *= 0.4; // create visible gap
694
}
695
if (shapeColors.size() > 0) {
696
GLHelper::drawBoxLines(baseShape, getShapeRotations(s2), getShapeLengths(s2), shapeColors, halfWidth * exaggeration, cornerDetail, offset);
697
} else {
698
GLHelper::drawBoxLines(baseShape, getShapeRotations(s2), getShapeLengths(s2), halfWidth * exaggeration, cornerDetail, offset);
699
}
700
}
701
GLHelper::popMatrix();
702
#ifdef GUILane_DEBUG_DRAW_FOE_INTERSECTIONS
703
if (myEdge->isInternal() && gSelected.isSelected(getType(), getGlID())) {
704
debugDrawFoeIntersections();
705
}
706
#endif
707
if (s.geometryIndices.show(this)) {
708
GLHelper::debugVertices(baseShape, s.geometryIndices, s.scale);
709
}
710
// draw details
711
if ((!isInternal || isCrossing || !s.drawJunctionShape) && (drawDetails || junctionExaggeration > 1)) {
712
GLHelper::pushMatrix();
713
glTranslated(0, 0, GLO_JUNCTION); // must draw on top of junction shape
714
glTranslated(0, 0, .5);
715
if (drawDetails) {
716
if (s.showLaneDirection) {
717
if (drawRails) {
718
// improve visibility of superposed rail edges
719
GLHelper::setColor(setColor(s).changedBrightness(100));
720
} else {
721
glColor3d(0.3, 0.3, 0.3);
722
}
723
if (!isCrossing || s.drawCrossingsAndWalkingareas) {
724
drawDirectionIndicators(exaggeration, spreadSuperposed, s.secondaryShape);
725
}
726
}
727
if (!isInternal || isCrossing
728
// controlled internal junction
729
|| (getLinkCont().size() != 0 && getLinkCont()[0]->isInternalJunctionLink() && getLinkCont()[0]->getTLLogic() != nullptr)) {
730
if (MSGlobals::gLateralResolution > 0 && s.showSublanes && !hiddenBidi && (myPermissions & ~(SVC_PEDESTRIAN | SVC_RAIL_CLASSES)) != 0) {
731
// draw sublane-borders
732
const double offsetSign = MSGlobals::gLefthand ? -1 : 1;
733
GLHelper::setColor(color.changedBrightness(51));
734
for (double offset = -myHalfLaneWidth; offset < myHalfLaneWidth; offset += MSGlobals::gLateralResolution) {
735
GLHelper::drawBoxLines(baseShape, getShapeRotations(s2), getShapeLengths(s2), 0.01, 0, -offset * offsetSign);
736
}
737
}
738
if (MSGlobals::gUseMesoSim && mySegmentStartIndex.size() > 0 && (myPermissions & ~SVC_PEDESTRIAN) != 0) {
739
// draw segment borders
740
GLHelper::setColor(color.changedBrightness(51));
741
for (int i : mySegmentStartIndex) {
742
if (shapeColors.size() > 0) {
743
GLHelper::setColor(shapeColors[i].changedBrightness(51));
744
}
745
GLHelper::drawBoxLine(baseShape[i], getShapeRotations(s2)[i] + 90, myWidth / 3, 0.2, 0);
746
GLHelper::drawBoxLine(baseShape[i], getShapeRotations(s2)[i] - 90, myWidth / 3, 0.2, 0);
747
}
748
}
749
if (s.showLinkDecals && !drawRails && !drawAsWaterway(s) && myPermissions != SVC_PEDESTRIAN) {
750
drawArrows(s.secondaryShape);
751
}
752
glTranslated(0, 0, 1000);
753
if (s.drawLinkJunctionIndex.show(nullptr)) {
754
drawLinkNo(s);
755
}
756
if (s.drawLinkTLIndex.show(nullptr)) {
757
drawTLSLinkNo(s, *net);
758
}
759
glTranslated(0, 0, -1000);
760
}
761
glTranslated(0, 0, .1);
762
}
763
if ((drawDetails || junctionExaggeration > 1) && s.showLane2Lane) {
764
// draw from end of first to the begin of second but respect junction scaling
765
drawLane2LaneConnections(junctionExaggeration, s.secondaryShape);
766
}
767
GLHelper::popMatrix();
768
// make sure link rules are drawn so tls can be selected via right-click
769
if (s.showLinkRules && drawDetails && !isWalkingArea &&
770
(!myEdge->isInternal() || (getLinkCont().size() > 0 && getLinkCont()[0]->isInternalJunctionLink()))) {
771
GLHelper::pushMatrix();
772
glTranslated(0, 0, GLO_SHAPE); // must draw on top of junction shape and additionals
773
drawLinkRules(s, *net);
774
GLHelper::popMatrix();
775
}
776
}
777
if (mustDrawMarkings && drawDetails && s.laneShowBorders) { // needs matrix reset
778
drawMarkings(s, exaggeration);
779
}
780
if (drawDetails && isInternal && s.showBikeMarkings && myPermissions == SVC_BICYCLE && exaggeration == 1.0 && s.showLinkDecals && s.laneShowBorders && !hiddenBidi
781
&& MSGlobals::gUsingInternalLanes
782
&& getNormalSuccessorLane()->getPermissions() == SVC_BICYCLE && getNormalPredecessorLane()->getPermissions() == SVC_BICYCLE) {
783
drawBikeMarkings();
784
}
785
if (drawDetails && isInternal && exaggeration == 1.0 && s.showLinkDecals && s.laneShowBorders && !hiddenBidi && myIndex > 0
786
&& !(myEdge->getLanes()[myIndex - 1]->allowsChangingLeft(SVC_PASSENGER) && allowsChangingRight(SVC_PASSENGER))) {
787
// draw lane changing prohibitions on junction
788
drawJunctionChangeProhibitions();
789
}
790
}
791
} else {
792
GLHelper::popMatrix();
793
}
794
// draw vehicles
795
if (s.scale * s.vehicleSize.getExaggeration(s, nullptr) > s.vehicleSize.minSize) {
796
// retrieve vehicles from lane; disallow simulation
797
const MSLane::VehCont& vehicles = getVehiclesSecure();
798
for (MSLane::VehCont::const_iterator v = vehicles.begin(); v != vehicles.end(); ++v) {
799
if ((*v)->getLane() == this) {
800
static_cast<const GUIVehicle*>(*v)->drawGL(s);
801
} // else: this is the shadow during a continuous lane change
802
}
803
// draw long partial vehicles (#14342)
804
for (const MSVehicle* veh : myPartialVehicles) {
805
if (veh->getLength() > RENDERING_BUFFER) {
806
// potential double rendering taken into account
807
static_cast<const GUIVehicle*>(veh)->drawGL(s);
808
}
809
}
810
// draw parking vehicles
811
for (const MSBaseVehicle* const v : myParkingVehicles) {
812
dynamic_cast<const GUIBaseVehicle*>(v)->drawGL(s);
813
}
814
// allow lane simulation
815
releaseVehicles();
816
}
817
GLHelper::popName();
818
}
819
820
bool
821
GUILane::neighLaneNotBidi() const {
822
const MSLane* right = getParallelLane(-1, false);
823
if (right && right->getBidiLane() == nullptr) {
824
return true;
825
}
826
const MSLane* left = getParallelLane(1, false);
827
if (left && left->getBidiLane() == nullptr) {
828
return true;
829
}
830
return false;
831
}
832
833
void
834
GUILane::drawMarkings(const GUIVisualizationSettings& s, double scale) const {
835
GLHelper::pushMatrix();
836
glTranslated(0, 0, GLO_EDGE);
837
setColor(s);
838
// optionally draw inverse markings
839
const bool s2 = s.secondaryShape;
840
if (myIndex > 0 && (myEdge->getLanes()[myIndex - 1]->getPermissions() & myPermissions) != 0) {
841
const bool cl = myEdge->getLanes()[myIndex - 1]->allowsChangingLeft(SVC_PASSENGER);
842
const bool cr = allowsChangingRight(SVC_PASSENGER);
843
GLHelper::drawInverseMarkings(getShape(s2), getShapeRotations(s2), getShapeLengths(s2), 3, 6, myHalfLaneWidth, cl, cr, MSGlobals::gLefthand, scale);
844
}
845
// draw white boundings and white markings
846
glColor3d(1, 1, 1);
847
GLHelper::drawBoxLines(
848
getShape(s2),
849
getShapeRotations(s2),
850
getShapeLengths(s2),
851
(myHalfLaneWidth + SUMO_const_laneMarkWidth) * scale);
852
GLHelper::popMatrix();
853
}
854
855
856
void
857
GUILane::drawBikeMarkings() const {
858
// draw bike lane markings onto the intersection
859
glColor3d(1, 1, 1);
860
/// fixme
861
const bool s2 = false;
862
const int e = (int) getShape(s2).size() - 1;
863
const double markWidth = 0.1;
864
const double mw = myHalfLaneWidth;
865
for (int i = 0; i < e; ++i) {
866
GLHelper::pushMatrix();
867
glTranslated(getShape(s2)[i].x(), getShape(s2)[i].y(), GLO_JUNCTION + 0.4);
868
glRotated(getShapeRotations(s2)[i], 0, 0, 1);
869
for (double t = 0; t < getShapeLengths(s2)[i]; t += 0.5) {
870
// left and right marking
871
for (int side = -1; side <= 1; side += 2) {
872
glBegin(GL_QUADS);
873
glVertex2d(side * mw, -t);
874
glVertex2d(side * mw, -t - 0.35);
875
glVertex2d(side * (mw + markWidth), -t - 0.35);
876
glVertex2d(side * (mw + markWidth), -t);
877
glEnd();
878
}
879
}
880
GLHelper::popMatrix();
881
}
882
}
883
884
885
void
886
GUILane::drawJunctionChangeProhibitions() const {
887
// fixme
888
const bool s2 = false;
889
// draw white markings
890
if (myIndex > 0 && (myEdge->getLanes()[myIndex - 1]->getPermissions() & myPermissions) != 0) {
891
glColor3d(1, 1, 1);
892
const bool cl = myEdge->getLanes()[myIndex - 1]->allowsChangingLeft(SVC_PASSENGER);
893
const bool cr = allowsChangingRight(SVC_PASSENGER);
894
// solid line marking
895
double mw, mw2;
896
// optional broken line marking
897
double mw3, mw4;
898
if (!cl && !cr) {
899
// draw a single solid line
900
mw = myHalfLaneWidth + SUMO_const_laneMarkWidth * 0.4;
901
mw2 = myHalfLaneWidth - SUMO_const_laneMarkWidth * 0.4;
902
mw3 = myHalfLaneWidth;
903
mw4 = myHalfLaneWidth;
904
} else {
905
// draw one solid and one broken line
906
if (cl) {
907
mw = myHalfLaneWidth - SUMO_const_laneMarkWidth * 0.2;
908
mw2 = myHalfLaneWidth - SUMO_const_laneMarkWidth * 0.6;
909
mw3 = myHalfLaneWidth + SUMO_const_laneMarkWidth * 0.2;
910
mw4 = myHalfLaneWidth + SUMO_const_laneMarkWidth * 0.6;
911
} else {
912
mw = myHalfLaneWidth + SUMO_const_laneMarkWidth * 0.2;
913
mw2 = myHalfLaneWidth + SUMO_const_laneMarkWidth * 0.6;
914
mw3 = myHalfLaneWidth - SUMO_const_laneMarkWidth * 0.2;
915
mw4 = myHalfLaneWidth - SUMO_const_laneMarkWidth * 0.6;
916
}
917
}
918
if (MSGlobals::gLefthand) {
919
mw *= -1;
920
mw2 *= -1;
921
}
922
int e = (int) getShape(s2).size() - 1;
923
for (int i = 0; i < e; ++i) {
924
GLHelper::pushMatrix();
925
glTranslated(getShape(s2)[i].x(), getShape(s2)[i].y(), GLO_JUNCTION + 0.4);
926
glRotated(getShapeRotations(s2)[i], 0, 0, 1);
927
for (double t = 0; t < getShapeLengths(s2)[i]; t += 6) {
928
const double lengthSolid = MIN2(6.0, getShapeLengths(s2)[i] - t);
929
glBegin(GL_QUADS);
930
glVertex2d(-mw, -t);
931
glVertex2d(-mw, -t - lengthSolid);
932
glVertex2d(-mw2, -t - lengthSolid);
933
glVertex2d(-mw2, -t);
934
glEnd();
935
if (cl || cr) {
936
const double lengthBroken = MIN2(3.0, getShapeLengths(s2)[i] - t);
937
glBegin(GL_QUADS);
938
glVertex2d(-mw3, -t);
939
glVertex2d(-mw3, -t - lengthBroken);
940
glVertex2d(-mw4, -t - lengthBroken);
941
glVertex2d(-mw4, -t);
942
glEnd();
943
}
944
}
945
GLHelper::popMatrix();
946
}
947
}
948
}
949
950
void
951
GUILane::drawDirectionIndicators(double exaggeration, bool spreadSuperposed, bool s2) const {
952
GLHelper::pushMatrix();
953
glTranslated(0, 0, GLO_EDGE);
954
int e = (int) getShape(s2).size() - 1;
955
const double widthFactor = spreadSuperposed ? 0.4 : 1;
956
const double w = MAX2(POSITION_EPS, myWidth * widthFactor);
957
const double w2 = MAX2(POSITION_EPS, myHalfLaneWidth * widthFactor);
958
const double w4 = MAX2(POSITION_EPS, myQuarterLaneWidth * widthFactor);
959
const double sideOffset = spreadSuperposed ? w * -0.5 : 0;
960
for (int i = 0; i < e; ++i) {
961
GLHelper::pushMatrix();
962
glTranslated(getShape(s2)[i].x(), getShape(s2)[i].y(), 0.1);
963
glRotated(getShapeRotations(s2)[i], 0, 0, 1);
964
for (double t = 0; t < getShapeLengths(s2)[i]; t += w) {
965
const double length = MIN2(w2, getShapeLengths(s2)[i] - t) * exaggeration;
966
glBegin(GL_TRIANGLES);
967
glVertex2d(sideOffset, -t - length);
968
glVertex2d(sideOffset - w4 * exaggeration, -t);
969
glVertex2d(sideOffset + w4 * exaggeration, -t);
970
glEnd();
971
}
972
GLHelper::popMatrix();
973
}
974
GLHelper::popMatrix();
975
}
976
977
978
void
979
GUILane::debugDrawFoeIntersections() const {
980
GLHelper::pushMatrix();
981
glTranslated(0, 0, 5);
982
glColor3d(1.0, 0.3, 0.3);
983
const double orthoLength = 0.5;
984
const MSLink* link = getLinkCont().front();
985
const std::vector<const MSLane*>& foeLanes = link->getFoeLanes();
986
const auto& conflicts = link->getConflicts();
987
if (foeLanes.size() == conflicts.size()) {
988
for (int i = 0; i < (int)foeLanes.size(); ++i) {
989
const MSLane* l = foeLanes[i];
990
Position pos = l->geometryPositionAtOffset(l->getLength() - conflicts[i].lengthBehindCrossing);
991
PositionVector ortho = l->getShape().getOrthogonal(pos, 10, true, orthoLength);
992
if (ortho.length() < orthoLength) {
993
ortho.extrapolate(orthoLength - ortho.length(), false, true);
994
}
995
GLHelper::drawLine(ortho);
996
//std::cout << "foe=" << l->getID() << " lanePos=" << l->getLength() - conflicts[i].lengthBehindCrossing << " pos=" << pos << "\n";
997
}
998
}
999
GLHelper::popMatrix();
1000
}
1001
1002
1003
// ------ inherited from GUIGlObject
1004
GUIGLObjectPopupMenu*
1005
GUILane::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {
1006
GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);
1007
buildPopupHeader(ret, app);
1008
buildCenterPopupEntry(ret);
1009
//
1010
GUIDesigns::buildFXMenuCommand(ret, TL("Copy edge name to clipboard"), nullptr, ret, MID_COPY_EDGE_NAME);
1011
buildNameCopyPopupEntry(ret);
1012
buildSelectionPopupEntry(ret);
1013
//
1014
buildShowParamsPopupEntry(ret, false);
1015
const PositionVector& baseShape = getShape(parent.getVisualisationSettings().secondaryShape);
1016
const double pos = interpolateGeometryPosToLanePos(baseShape.nearest_offset_to_point25D(parent.getPositionInformation()));
1017
const double height = baseShape.positionAtOffset(pos).z();
1018
GUIDesigns::buildFXMenuCommand(ret, (TL("pos: ") + toString(pos) + " " + TL("height: ") + toString(height)).c_str(), nullptr, nullptr, 0);
1019
if (getEdge().hasDistance()) {
1020
GUIDesigns::buildFXMenuCommand(ret, ("distance: " + toString(getEdge().getDistanceAt(pos))).c_str(), nullptr, nullptr, 0);
1021
}
1022
new FXMenuSeparator(ret);
1023
buildPositionCopyEntry(ret, app);
1024
new FXMenuSeparator(ret);
1025
if (myAmClosed) {
1026
if (myPermissionChanges.empty()) {
1027
GUIDesigns::buildFXMenuCommand(ret, TL("Reopen lane"), nullptr, &parent, MID_CLOSE_LANE);
1028
GUIDesigns::buildFXMenuCommand(ret, TL("Reopen edge"), nullptr, &parent, MID_CLOSE_EDGE);
1029
} else {
1030
GUIDesigns::buildFXMenuCommand(ret, TL("Reopen lane (override rerouter)"), nullptr, &parent, MID_CLOSE_LANE);
1031
GUIDesigns::buildFXMenuCommand(ret, TL("Reopen edge (override rerouter)"), nullptr, &parent, MID_CLOSE_EDGE);
1032
}
1033
} else {
1034
GUIDesigns::buildFXMenuCommand(ret, TL("Close lane"), nullptr, &parent, MID_CLOSE_LANE);
1035
GUIDesigns::buildFXMenuCommand(ret, TL("Close edge"), nullptr, &parent, MID_CLOSE_EDGE);
1036
}
1037
GUIDesigns::buildFXMenuCommand(ret, TL("Add rerouter"), nullptr, &parent, MID_ADD_REROUTER);
1038
new FXMenuSeparator(ret);
1039
// reachability menu
1040
FXMenuPane* reachableByClass = new FXMenuPane(ret);
1041
ret->insertMenuPaneChild(reachableByClass);
1042
new FXMenuCascade(ret, TL("Select reachable"), GUIIconSubSys::getIcon(GUIIcon::FLAG), reachableByClass);
1043
for (auto i : SumoVehicleClassStrings.getStrings()) {
1044
GUIDesigns::buildFXMenuCommand(reachableByClass, i.c_str(), VClassIcons::getVClassIcon(SumoVehicleClassStrings.get(i)), &parent, MID_REACHABILITY);
1045
}
1046
return ret;
1047
}
1048
1049
1050
GUIParameterTableWindow*
1051
GUILane::getParameterWindow(GUIMainWindow& app, GUISUMOAbstractView& view) {
1052
myCachedGUISettings = view.editVisualisationSettings();
1053
GUIParameterTableWindow* ret = new GUIParameterTableWindow(app, *this);
1054
// add items
1055
ret->mkItem(TL("allowed speed [m/s]"), false, getSpeedLimit());
1056
const std::map<SUMOVehicleClass, double>* restrictions = MSNet::getInstance()->getRestrictions(myEdge->getEdgeType());
1057
if (restrictions != nullptr) {
1058
for (const auto& elem : *restrictions) {
1059
ret->mkItem((std::string(" ") + TL("allowed speed [m/s]") + std::string(": ") + toString(elem.first)).c_str(), false, elem.second);
1060
}
1061
}
1062
ret->mkItem(TL("length [m]"), false, myLength);
1063
ret->mkItem(TL("width [m]"), false, myWidth);
1064
ret->mkItem(TL("street name"), false, myEdge->getStreetName());
1065
ret->mkItem(TL("stored travel time [s]"), true, new FunctionBinding<GUILane, double>(this, &GUILane::getStoredEdgeTravelTime));
1066
ret->mkItem(TL("loaded weight"), true, new FunctionBinding<GUILane, double>(this, &GUILane::getLoadedEdgeWeight));
1067
ret->mkItem(TL("routing speed [m/s]"), true, new FunctionBinding<MSEdge, double>(myEdge, &MSEdge::getRoutingSpeed));
1068
ret->mkItem(TL("lane friction coefficient [%]"), true, new FunctionBinding<GUILane, double>(this, &GUILane::getFrictionCoefficient));
1069
ret->mkItem(TL("time penalty [s]"), true, new FunctionBinding<MSEdge, double>(myEdge, &MSEdge::getTimePenalty));
1070
ret->mkItem(TL("brutto occupancy [%]"), true, new FunctionBinding<GUILane, double>(this, &GUILane::getBruttoOccupancy, 100.));
1071
ret->mkItem(TL("netto occupancy [%]"), true, new FunctionBinding<GUILane, double>(this, &GUILane::getNettoOccupancy, 100.));
1072
ret->mkItem(TL("pending insertions [#]"), true, new FunctionBinding<GUILane, double>(this, &GUILane::getPendingEmits));
1073
ret->mkItem(TL("edge type"), false, myEdge->getEdgeType());
1074
ret->mkItem(TL("type"), false, myLaneType);
1075
ret->mkItem(TL("priority"), false, myEdge->getPriority());
1076
ret->mkItem(TL("distance [km]"), false, myEdge->getDistance() / 1000);
1077
ret->mkItem(TL("allowed vehicle class"), false, StringUtils::wrapText(getVehicleClassNames(myPermissions), 60));
1078
ret->mkItem(TL("disallowed vehicle class"), false, StringUtils::wrapText(getVehicleClassNames(~myPermissions), 60));
1079
ret->mkItem(TL("permission code"), false, myPermissions);
1080
ret->mkItem(TL("color value"), true, new FunctionBinding<GUILane, double>(this, &GUILane::getColorValueForTracker));
1081
if (myBidiLane != nullptr) {
1082
ret->mkItem(TL("bidi-lane"), false, myBidiLane->getID());
1083
}
1084
// info for blocked departDriveWay
1085
for (auto item : getParametersMap()) {
1086
if (StringUtils::startsWith(item.first, "insertionBlocked:")) {
1087
const MSDriveWay* dw = MSDriveWay::retrieveDepartDriveWay(myEdge, item.second);
1088
if (dw != nullptr) {
1089
ret->mkItem(("blocking " + dw->getID()).c_str(), false, toString(MSRailSignal::getBlockingVehicles(dw)));
1090
ret->mkItem(("driveWays blocking " + dw->getID()).c_str(), false, toString(MSRailSignal::getBlockingDriveWays(dw)));
1091
}
1092
}
1093
}
1094
1095
for (const auto& kv : myEdge->getParametersMap()) {
1096
ret->mkItem(("edgeParam:" + kv.first).c_str(), false, kv.second);
1097
}
1098
ret->checkFont(myEdge->getStreetName());
1099
ret->closeBuilding();
1100
return ret;
1101
}
1102
1103
1104
Boundary
1105
GUILane::getCenteringBoundary() const {
1106
const PositionVector& shape = GUIGlobals::gSecondaryShape && myShape2.size() > 0 ? myShape2 : myShape;
1107
Boundary b;
1108
b.add(shape[0]);
1109
b.add(shape[-1]);
1110
b.grow(RENDERING_BUFFER);
1111
// ensure that vehicles and persons on the side are drawn even if the edge
1112
// is outside the view
1113
return b;
1114
}
1115
1116
1117
const PositionVector&
1118
GUILane::getShape(bool secondary) const {
1119
return secondary && myShape2.size() > 0 ? myShape2 : myShape;
1120
}
1121
1122
1123
const std::vector<double>&
1124
GUILane::getShapeRotations(bool secondary) const {
1125
return secondary && myShapeRotations2.size() > 0 ? myShapeRotations2 : myShapeRotations;
1126
}
1127
1128
1129
const std::vector<double>&
1130
GUILane::getShapeLengths(bool secondary) const {
1131
return secondary && myShapeLengths2.size() > 0 ? myShapeLengths2 : myShapeLengths;
1132
}
1133
1134
1135
std::vector<RGBColor>&
1136
GUILane::getShapeColors(bool secondary) const {
1137
return secondary && myShapeColors2.size() > 0 ? myShapeColors2 : myShapeColors;
1138
}
1139
1140
1141
double
1142
GUILane::firstWaitingTime() const {
1143
return myVehicles.size() == 0 ? 0 : myVehicles.back()->getWaitingSeconds();
1144
}
1145
1146
1147
double
1148
GUILane::getEdgeLaneNumber() const {
1149
return (double) myEdge->getLanes().size();
1150
}
1151
1152
1153
double
1154
GUILane::getStoredEdgeTravelTime() const {
1155
MSEdgeWeightsStorage& ews = MSNet::getInstance()->getWeightsStorage();
1156
if (!ews.knowsTravelTime(myEdge)) {
1157
return -1;
1158
} else {
1159
double value(0);
1160
ews.retrieveExistingTravelTime(myEdge, STEPS2TIME(MSNet::getInstance()->getCurrentTimeStep()), value);
1161
return value;
1162
}
1163
}
1164
1165
1166
double
1167
GUILane::getLoadedEdgeWeight() const {
1168
MSEdgeWeightsStorage& ews = MSNet::getInstance()->getWeightsStorage();
1169
if (!ews.knowsEffort(myEdge)) {
1170
return -1;
1171
} else {
1172
double value(-1);
1173
ews.retrieveExistingEffort(myEdge, STEPS2TIME(MSNet::getInstance()->getCurrentTimeStep()), value);
1174
return value;
1175
}
1176
}
1177
1178
1179
double
1180
GUILane::getColorValueWithFunctional(const GUIVisualizationSettings& s, int activeScheme) const {
1181
switch (activeScheme) {
1182
case 18: {
1183
return GeomHelper::naviDegree(getShape(s.secondaryShape).beginEndAngle()); // [0-360]
1184
}
1185
default:
1186
return getColorValue(s, activeScheme);
1187
}
1188
1189
}
1190
1191
1192
RGBColor
1193
GUILane::setColor(const GUIVisualizationSettings& s) const {
1194
// setting and retrieving the color does not work in OSGView so we return it explicitliy
1195
RGBColor col;
1196
if (MSGlobals::gUseMesoSim && static_cast<const GUIEdge*>(myEdge)->getMesoColor() != MESO_USE_LANE_COLOR) {
1197
col = static_cast<const GUIEdge*>(myEdge)->getMesoColor();
1198
} else {
1199
const GUIColorer& c = s.laneColorer;
1200
if (!setFunctionalColor(c, col) && !setMultiColor(s, c, col)) {
1201
col = c.getScheme().getColor(getColorValue(s, c.getActive()));
1202
}
1203
}
1204
GLHelper::setColor(col);
1205
return col;
1206
}
1207
1208
1209
bool
1210
GUILane::setFunctionalColor(const GUIColorer& c, RGBColor& col, int activeScheme) const {
1211
if (activeScheme < 0) {
1212
activeScheme = c.getActive();
1213
}
1214
switch (activeScheme) {
1215
case 0:
1216
if (myEdge->isCrossing()) {
1217
// determine priority to decide color
1218
const MSLink* const link = getLogicalPredecessorLane()->getLinkTo(this);
1219
if (link->havePriority() || link->getTLLogic() != nullptr) {
1220
col = RGBColor(230, 230, 230);
1221
} else {
1222
col = RGBColor(26, 26, 26);
1223
}
1224
GLHelper::setColor(col);
1225
return true;
1226
} else {
1227
return false;
1228
}
1229
case 18: {
1230
double hue = GeomHelper::naviDegree(myShape.beginEndAngle()); // [0-360]
1231
col = RGBColor::fromHSV(hue, 1., 1.);
1232
GLHelper::setColor(col);
1233
return true;
1234
}
1235
case 30: { // taz color
1236
col = c.getScheme().getColor(0);
1237
std::vector<RGBColor> tazColors;
1238
for (MSEdge* e : myEdge->getPredecessors()) {
1239
if (e->isTazConnector() && e->hasParameter("tazColor")) {
1240
tazColors.push_back(RGBColor::parseColor(e->getParameter("tazColor")));
1241
}
1242
}
1243
for (MSEdge* e : myEdge->getSuccessors()) {
1244
if (e->isTazConnector() && e->hasParameter("tazColor")) {
1245
tazColors.push_back(RGBColor::parseColor(e->getParameter("tazColor")));
1246
}
1247
}
1248
if (tazColors.size() > 0) {
1249
int randColor = RandHelper::rand((int)tazColors.size(), RGBColor::getColorRNG());
1250
col = tazColors[randColor];
1251
}
1252
GLHelper::setColor(col);
1253
return true;
1254
}
1255
default:
1256
return false;
1257
}
1258
}
1259
1260
1261
bool
1262
GUILane::setMultiColor(const GUIVisualizationSettings& s, const GUIColorer& c, RGBColor& col) const {
1263
const int activeScheme = c.getActive();
1264
auto& shapeColors = getShapeColors(s.secondaryShape);
1265
const PositionVector& shape = getShape(s.secondaryShape);
1266
shapeColors.clear();
1267
switch (activeScheme) {
1268
case 22: // color by height at segment start
1269
for (PositionVector::const_iterator ii = shape.begin(); ii != shape.end() - 1; ++ii) {
1270
shapeColors.push_back(c.getScheme().getColor(ii->z()));
1271
}
1272
// osg fallback (edge height at start)
1273
col = c.getScheme().getColor(getColorValue(s, 21));
1274
return true;
1275
case 24: // color by inclination at segment start
1276
for (int ii = 1; ii < (int)shape.size(); ++ii) {
1277
const double inc = (shape[ii].z() - shape[ii - 1].z()) / MAX2(POSITION_EPS, shape[ii].distanceTo2D(shape[ii - 1]));
1278
shapeColors.push_back(c.getScheme().getColor(inc));
1279
}
1280
col = c.getScheme().getColor(getColorValue(s, 23));
1281
return true;
1282
default:
1283
return false;
1284
}
1285
}
1286
1287
double
1288
GUILane::getColorValueForTracker() const {
1289
if (myCachedGUISettings != nullptr) {
1290
const GUIVisualizationSettings& s = *myCachedGUISettings;
1291
const GUIColorer& c = s.laneColorer;
1292
double val = getColorValueWithFunctional(s, c.getActive());
1293
if (val == GUIVisualizationSettings::MISSING_DATA) {
1294
// blowing up the dialog or plot isn't helpful. At least 0 may be understood as neutral
1295
return 0;
1296
} else {
1297
return val;
1298
}
1299
} else {
1300
return 0;
1301
}
1302
}
1303
1304
1305
double
1306
GUILane::getColorValue(const GUIVisualizationSettings& s, int activeScheme) const {
1307
switch (activeScheme) {
1308
case 0:
1309
switch (myPermissions) {
1310
case SVC_PEDESTRIAN:
1311
return 1;
1312
case SVC_BICYCLE:
1313
return 2;
1314
case 0:
1315
// forbidden road or green verge
1316
return myEdge->getPermissions() == 0 ? 10 : 3;
1317
case SVC_SHIP:
1318
return 4;
1319
case SVC_AUTHORITY:
1320
return 8;
1321
case SVC_AIRCRAFT:
1322
case SVC_DRONE:
1323
return 11;
1324
default:
1325
break;
1326
}
1327
if (myEdge->isTazConnector()) {
1328
return 9;
1329
} else if (isRailway(myPermissions)) {
1330
return 5;
1331
} else if ((myPermissions & SVC_PASSENGER) != 0) {
1332
if ((myPermissions & (SVC_RAIL_CLASSES & ~SVC_RAIL_FAST)) != 0 && (myPermissions & SVC_SHIP) == 0) {
1333
return 6;
1334
} else {
1335
return 0;
1336
}
1337
} else {
1338
if ((myPermissions & SVC_RAIL_CLASSES) != 0 && (myPermissions & SVC_SHIP) == 0) {
1339
return 6;
1340
} else {
1341
return 7;
1342
}
1343
}
1344
case 1:
1345
return isLaneOrEdgeSelected();
1346
case 2:
1347
return (double)myPermissions;
1348
case 3:
1349
return getSpeedLimit();
1350
case 4:
1351
return getBruttoOccupancy();
1352
case 5:
1353
return getNettoOccupancy();
1354
case 6:
1355
return firstWaitingTime();
1356
case 7:
1357
return getEdgeLaneNumber();
1358
case 8:
1359
return getEmissions<PollutantsInterface::CO2>() / myLength;
1360
case 9:
1361
return getEmissions<PollutantsInterface::CO>() / myLength;
1362
case 10:
1363
return getEmissions<PollutantsInterface::PM_X>() / myLength;
1364
case 11:
1365
return getEmissions<PollutantsInterface::NO_X>() / myLength;
1366
case 12:
1367
return getEmissions<PollutantsInterface::HC>() / myLength;
1368
case 13:
1369
return getEmissions<PollutantsInterface::FUEL>() / myLength;
1370
case 14:
1371
return getHarmonoise_NoiseEmissions();
1372
case 15: {
1373
return getStoredEdgeTravelTime();
1374
}
1375
case 16: {
1376
MSEdgeWeightsStorage& ews = MSNet::getInstance()->getWeightsStorage();
1377
if (!ews.knowsTravelTime(myEdge)) {
1378
return -1;
1379
} else {
1380
double value(0);
1381
ews.retrieveExistingTravelTime(myEdge, 0, value);
1382
return 100 * myLength / value / getSpeedLimit();
1383
}
1384
}
1385
case 17: {
1386
// geometrical length has no meaning for walkingAreas since it describes the outer boundary
1387
return myEdge->isWalkingArea() ? 1 : 1 / getLengthGeometryFactor(s.secondaryShape);
1388
}
1389
case 19: {
1390
return getLoadedEdgeWeight();
1391
}
1392
case 20: {
1393
return myEdge->getPriority();
1394
}
1395
case 21: {
1396
// color by z of first shape point
1397
return getShape(s.secondaryShape)[0].z();
1398
}
1399
case 23: {
1400
// color by incline
1401
return (getShape(s.secondaryShape)[-1].z() - getShape(s.secondaryShape)[0].z()) / getLength();
1402
}
1403
case 25: {
1404
// color by average speed
1405
return getMeanSpeed();
1406
}
1407
case 26: {
1408
// color by average relative speed
1409
return getMeanSpeed() / myMaxSpeed;
1410
}
1411
case 27: {
1412
// color by routing device assumed speed
1413
return myEdge->getRoutingSpeed();
1414
}
1415
case 28:
1416
return getEmissions<PollutantsInterface::ELEC>() / myLength;
1417
case 29:
1418
return getPendingEmits();
1419
case 31: {
1420
// by numerical edge param value
1421
if (myEdge->hasParameter(s.edgeParam)) {
1422
try {
1423
return StringUtils::toDouble(myEdge->getParameter(s.edgeParam, "0"));
1424
} catch (NumberFormatException&) {
1425
try {
1426
return StringUtils::toBool(myEdge->getParameter(s.edgeParam, "0"));
1427
} catch (BoolFormatException&) {
1428
return GUIVisualizationSettings::MISSING_DATA;
1429
}
1430
}
1431
} else {
1432
return GUIVisualizationSettings::MISSING_DATA;
1433
}
1434
}
1435
case 32: {
1436
// by numerical lane param value
1437
if (hasParameter(s.laneParam)) {
1438
try {
1439
return StringUtils::toDouble(getParameter(s.laneParam, "0"));
1440
} catch (NumberFormatException&) {
1441
try {
1442
return StringUtils::toBool(getParameter(s.laneParam, "0"));
1443
} catch (BoolFormatException&) {
1444
return GUIVisualizationSettings::MISSING_DATA;
1445
}
1446
}
1447
} else {
1448
return GUIVisualizationSettings::MISSING_DATA;
1449
}
1450
}
1451
case 33: {
1452
// by edge data value
1453
return GUINet::getGUIInstance()->getEdgeData(myEdge, s.edgeData);
1454
}
1455
case 34: {
1456
return myEdge->getDistance();
1457
}
1458
case 35: {
1459
return fabs(myEdge->getDistance());
1460
}
1461
case 36: {
1462
return myReachability;
1463
}
1464
case 37: {
1465
return myRNGIndex % MSGlobals::gNumSimThreads;
1466
}
1467
case 38: {
1468
if (myParkingAreas == nullptr) {
1469
// init
1470
myParkingAreas = new std::vector<MSParkingArea*>();
1471
for (auto& item : MSNet::getInstance()->getStoppingPlaces(SUMO_TAG_PARKING_AREA)) {
1472
if (&item.second->getLane().getEdge() == myEdge) {
1473
myParkingAreas->push_back(dynamic_cast<MSParkingArea*>(item.second));
1474
}
1475
}
1476
}
1477
int capacity = 0;
1478
for (MSParkingArea* pa : *myParkingAreas) {
1479
capacity += pa->getCapacity() - pa->getOccupancy();
1480
}
1481
return capacity;
1482
}
1483
case 39: {
1484
// by live edge data value
1485
return GUINet::getGUIInstance()->getMeanData(this, s.edgeDataID, s.edgeData);
1486
}
1487
}
1488
return 0;
1489
}
1490
1491
1492
double
1493
GUILane::getScaleValue(const GUIVisualizationSettings& s, int activeScheme, bool s2) const {
1494
switch (activeScheme) {
1495
case 0:
1496
return 0;
1497
case 1:
1498
return isLaneOrEdgeSelected();
1499
case 2:
1500
return getSpeedLimit();
1501
case 3:
1502
return getBruttoOccupancy();
1503
case 4:
1504
return getNettoOccupancy();
1505
case 5:
1506
return firstWaitingTime();
1507
case 6:
1508
return getEdgeLaneNumber();
1509
case 7:
1510
return getEmissions<PollutantsInterface::CO2>() / myLength;
1511
case 8:
1512
return getEmissions<PollutantsInterface::CO>() / myLength;
1513
case 9:
1514
return getEmissions<PollutantsInterface::PM_X>() / myLength;
1515
case 10:
1516
return getEmissions<PollutantsInterface::NO_X>() / myLength;
1517
case 11:
1518
return getEmissions<PollutantsInterface::HC>() / myLength;
1519
case 12:
1520
return getEmissions<PollutantsInterface::FUEL>() / myLength;
1521
case 13:
1522
return getHarmonoise_NoiseEmissions();
1523
case 14: {
1524
return getStoredEdgeTravelTime();
1525
}
1526
case 15: {
1527
MSEdgeWeightsStorage& ews = MSNet::getInstance()->getWeightsStorage();
1528
if (!ews.knowsTravelTime(myEdge)) {
1529
return -1;
1530
} else {
1531
double value(0);
1532
ews.retrieveExistingTravelTime(myEdge, 0, value);
1533
return 100 * myLength / value / getSpeedLimit();
1534
}
1535
}
1536
case 16: {
1537
return 1 / getLengthGeometryFactor(s2);
1538
}
1539
case 17: {
1540
return getLoadedEdgeWeight();
1541
}
1542
case 18: {
1543
return myEdge->getPriority();
1544
}
1545
case 19: {
1546
// scale by average speed
1547
return getMeanSpeed();
1548
}
1549
case 20: {
1550
// scale by average relative speed
1551
return getMeanSpeed() / myMaxSpeed;
1552
}
1553
case 21:
1554
return getEmissions<PollutantsInterface::ELEC>() / myLength;
1555
case 22:
1556
return MSNet::getInstance()->getInsertionControl().getPendingEmits(this);
1557
case 23:
1558
// by edge data value
1559
return GUINet::getGUIInstance()->getEdgeData(myEdge, s.edgeDataScaling);
1560
}
1561
return 0;
1562
}
1563
1564
1565
bool
1566
GUILane::drawAsRailway(const GUIVisualizationSettings& s) const {
1567
return isRailway(myPermissions) && ((myPermissions & SVC_BUS) == 0) && s.showRails;
1568
}
1569
1570
1571
bool
1572
GUILane::drawAsWaterway(const GUIVisualizationSettings& s) const {
1573
return isWaterway(myPermissions) && s.showRails; // reusing the showRails setting
1574
}
1575
1576
1577
#ifdef HAVE_OSG
1578
void
1579
GUILane::updateColor(const GUIVisualizationSettings& s) {
1580
if (myGeom == 0) {
1581
// not drawn
1582
return;
1583
}
1584
const RGBColor col = setColor(s);
1585
osg::Vec4ubArray* colors = dynamic_cast<osg::Vec4ubArray*>(myGeom->getColorArray());
1586
(*colors)[0].set(col.red(), col.green(), col.blue(), col.alpha());
1587
myGeom->setColorArray(colors);
1588
}
1589
#endif
1590
1591
1592
void
1593
GUILane::closeTraffic(bool rebuildAllowed) {
1594
MSGlobals::gCheckRoutes = false;
1595
if (myAmClosed) {
1596
myPermissionChanges.clear(); // reset rerouters
1597
resetPermissions(CHANGE_PERMISSIONS_GUI);
1598
} else {
1599
setPermissions(SVC_AUTHORITY, CHANGE_PERMISSIONS_GUI);
1600
}
1601
myAmClosed = !myAmClosed;
1602
if (rebuildAllowed) {
1603
getEdge().rebuildAllowedLanes();
1604
}
1605
}
1606
1607
1608
PositionVector
1609
GUILane::splitAtSegments(const PositionVector& shape) {
1610
assert(MSGlobals::gUseMesoSim);
1611
int no = MELoop::numSegmentsFor(myLength, OptionsCont::getOptions().getFloat("meso-edgelength"));
1612
const double slength = myLength / no;
1613
PositionVector result = shape;
1614
double offset = 0;
1615
for (int i = 0; i < no; ++i) {
1616
offset += slength;
1617
Position pos = shape.positionAtOffset(offset);
1618
int index = result.indexOfClosest(pos);
1619
if (pos.distanceTo(result[index]) > POSITION_EPS) {
1620
index = result.insertAtClosest(pos, false);
1621
}
1622
if (i != no - 1) {
1623
mySegmentStartIndex.push_back(index);
1624
}
1625
while ((int)myShapeSegments.size() < index) {
1626
myShapeSegments.push_back(i);
1627
}
1628
//std::cout << "splitAtSegments " << getID() << " no=" << no << " i=" << i << " offset=" << offset << " index=" << index << " segs=" << toString(myShapeSegments) << " resultSize=" << result.size() << " result=" << toString(result) << "\n";
1629
}
1630
while (myShapeSegments.size() < result.size()) {
1631
myShapeSegments.push_back(no - 1);
1632
}
1633
return result;
1634
}
1635
1636
bool
1637
GUILane::isSelected() const {
1638
return gSelected.isSelected(GLO_LANE, getGlID());
1639
}
1640
1641
bool
1642
GUILane::isLaneOrEdgeSelected() const {
1643
return isSelected() || gSelected.isSelected(GLO_EDGE, dynamic_cast<GUIEdge*>(myEdge)->getGlID());
1644
}
1645
1646
double
1647
GUILane::getPendingEmits() const {
1648
return MSNet::getInstance()->getInsertionControl().getPendingEmits(this);
1649
}
1650
1651
double
1652
GUILane::getClickPriority() const {
1653
if (MSGlobals::gUseMesoSim) {
1654
// do not select lanes in meso mode
1655
return INVALID_PRIORITY;
1656
}
1657
if (myEdge->isCrossing()) {
1658
return GLO_CROSSING;
1659
}
1660
return GLO_LANE;
1661
}
1662
1663
1664
/****************************************************************************/
1665
1666