Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netedit/elements/demand/GNERoute.cpp
185790 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 GNERoute.cpp
15
/// @author Pablo Alvarez Lopez
16
/// @date Jan 2019
17
///
18
// A class for visualizing routes in Netedit
19
/****************************************************************************/
20
21
#include <netedit/changes/GNEChange_Attribute.h>
22
#include <netedit/changes/GNEChange_DemandElement.h>
23
#include <netedit/frames/demand/GNEVehicleFrame.h>
24
#include <netedit/frames/GNETagSelector.h>
25
#include <netedit/GNENet.h>
26
#include <netedit/GNESegment.h>
27
#include <netedit/GNETagProperties.h>
28
#include <netedit/GNEUndoList.h>
29
#include <netedit/GNEViewParent.h>
30
#include <utils/gui/div/GLHelper.h>
31
#include <utils/gui/div/GUIDesigns.h>
32
#include <utils/gui/windows/GUIAppEnum.h>
33
34
#include "GNERoute.h"
35
#include "GNEVehicle.h"
36
37
// ===========================================================================
38
// FOX callback mapping
39
// ===========================================================================
40
41
FXDEFMAP(GNERoute::GNERoutePopupMenu) GNERoutePopupMenuMap[] = {
42
FXMAPFUNC(SEL_COMMAND, MID_GNE_ROUTE_APPLY_DISTANCE, GNERoute::GNERoutePopupMenu::onCmdApplyDistance),
43
};
44
45
// Object implementation
46
FXIMPLEMENT(GNERoute::GNERoutePopupMenu, GUIGLObjectPopupMenu, GNERoutePopupMenuMap, ARRAYNUMBER(GNERoutePopupMenuMap))
47
48
// ===========================================================================
49
// GNERoute::GNERoutePopupMenu - methods
50
// ===========================================================================
51
52
GNERoute::GNERoutePopupMenu::GNERoutePopupMenu(GUIMainWindow& app, GUISUMOAbstractView& parent, GUIGlObject* o) :
53
GUIGLObjectPopupMenu(app, parent, o) {
54
}
55
56
57
GNERoute::GNERoutePopupMenu::~GNERoutePopupMenu() {}
58
59
60
long
61
GNERoute::GNERoutePopupMenu::onCmdApplyDistance(FXObject*, FXSelector, void*) {
62
GNERoute* route = static_cast<GNERoute*>(myObject);
63
GNEUndoList* undoList = route->myNet->getUndoList();
64
undoList->begin(route, "apply distance along route");
65
double dist = (route->getParentEdges().size() > 0) ? route->getParentEdges().front()->getNBEdge()->getDistance() : 0;
66
for (GNEEdge* edge : route->getParentEdges()) {
67
GNEChange_Attribute::changeAttribute(edge, SUMO_ATTR_DISTANCE, toString(dist), edge->getAttribute(SUMO_ATTR_DISTANCE), undoList);
68
dist += edge->getNBEdge()->getFinalLength();
69
}
70
undoList->end();
71
return 1;
72
}
73
74
// ===========================================================================
75
// GNERoute - methods
76
// ===========================================================================
77
78
GNERoute::GNERoute(SumoXMLTag tag, GNENet* net) :
79
GNEDemandElement(net, tag) {
80
}
81
82
83
GNERoute::GNERoute(GNEAdditional* calibrator) :
84
GNEDemandElement(calibrator->getNet()->getAttributeCarriers()->generateDemandElementID(SUMO_TAG_ROUTE),
85
calibrator->getNet(), SUMO_TAG_ROUTE, calibrator->getFileBucket()) {
86
// set parent edge
87
if (calibrator->getParentEdges().size() > 0) {
88
setParents<GNEEdge*>({calibrator->getParentEdges().front()});
89
} else if (calibrator->getParentLanes().size() > 0) {
90
setParents<GNEEdge*>({calibrator->getParentLanes().front()->getParentEdge()});
91
} else {
92
throw InvalidArgument("Calibrator parent requieres at least one edge or one lane");
93
}
94
}
95
96
97
GNERoute::GNERoute(const std::string& id, const GNEDemandElement* originalRoute) :
98
GNEDemandElement(id, originalRoute->getNet(), originalRoute->getTagProperty()->getTag(), originalRoute->getFileBucket()),
99
Parameterised(originalRoute->getParameters()->getParametersMap()),
100
myRepeat(parse<int>(originalRoute->getAttribute(SUMO_ATTR_REPEAT))),
101
myCycleTime(string2time(originalRoute->getAttribute(SUMO_ATTR_REPEAT))),
102
myVClass(originalRoute->getVClass()) {
103
// set parents
104
setParents<GNEEdge*>(originalRoute->getParentEdges());
105
setAttribute(SUMO_ATTR_COLOR, originalRoute->getAttribute(SUMO_ATTR_COLOR));
106
}
107
108
109
GNERoute::GNERoute(GNEVehicle* vehicleParent, const GNEDemandElement* originalRoute) :
110
GNEDemandElement(vehicleParent, originalRoute->getTagProperty()->getTag()),
111
Parameterised(originalRoute->getParameters()->getParametersMap()),
112
myRepeat(parse<int>(originalRoute->getAttribute(SUMO_ATTR_REPEAT))),
113
myCycleTime(string2time(originalRoute->getAttribute(SUMO_ATTR_REPEAT))),
114
myVClass(originalRoute->getVClass()) {
115
// set parents
116
setParents<GNEEdge*>(originalRoute->getParentEdges());
117
setParent<GNEDemandElement*>(vehicleParent);
118
setAttribute(SUMO_ATTR_COLOR, originalRoute->getAttribute(SUMO_ATTR_COLOR));
119
}
120
121
122
GNERoute::GNERoute(const std::string& id, GNENet* net, FileBucket* fileBucket, SUMOVehicleClass vClass,
123
const std::vector<GNEEdge*>& edges, const RGBColor& color, const int repeat,
124
const SUMOTime cycleTime, const double probability, const Parameterised::Map& parameters) :
125
GNEDemandElement(id, net, SUMO_TAG_ROUTE, fileBucket),
126
Parameterised(parameters),
127
myColor(color),
128
myRepeat(repeat),
129
myCycleTime(cycleTime),
130
myProbability(probability),
131
myVClass(vClass) {
132
// set parents
133
setParents<GNEEdge*>(edges);
134
}
135
136
137
GNERoute::GNERoute(GNEDemandElement* vehicleParent, const std::vector<GNEEdge*>& edges, const RGBColor& color,
138
const int repeat, const SUMOTime cycleTime, const Parameterised::Map& parameters) :
139
GNEDemandElement(vehicleParent, GNE_TAG_ROUTE_EMBEDDED),
140
Parameterised(parameters),
141
myColor(color),
142
myRepeat(repeat),
143
myCycleTime(cycleTime),
144
myVClass(vehicleParent->getVClass()) {
145
// set parents
146
setParents<GNEEdge*>(edges);
147
setParent<GNEDemandElement*>(vehicleParent);
148
}
149
150
151
GNERoute::~GNERoute() {}
152
153
154
GNEMoveElement* GNERoute::getMoveElement() const {
155
return nullptr;
156
}
157
158
159
Parameterised*
160
GNERoute::getParameters() {
161
return this;
162
}
163
164
165
const Parameterised*
166
GNERoute::getParameters() const {
167
return this;
168
}
169
170
171
GUIGLObjectPopupMenu*
172
GNERoute::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {
173
// create popup
174
GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);
175
// build common options
176
buildPopUpMenuCommonOptions(ret, app, myNet->getViewNet(), myTagProperty->getTag(), mySelected);
177
// show option to open demand element dialog
178
if (myTagProperty->hasDialog()) {
179
GUIDesigns::buildFXMenuCommand(ret, "Open " + getTagStr() + " Dialog", getACIcon(), &parent, MID_OPEN_ADDITIONAL_DIALOG);
180
new FXMenuSeparator(ret);
181
}
182
GUIDesigns::buildFXMenuCommand(ret, "Cursor position in view: " + toString(getPositionInView().x()) + "," + toString(getPositionInView().y()), nullptr, nullptr, 0);
183
new FXMenuSeparator(ret);
184
GUIDesigns::buildFXMenuCommand(ret, "Apply distance along route", nullptr, ret, MID_GNE_ROUTE_APPLY_DISTANCE);
185
// route length
186
buildMenuCommandRouteLength(ret);
187
if (myNet->getViewNet()->getEditModes().isCurrentSupermodeDemand()) {
188
// add reverse
189
buildMenuAddReverse(ret);
190
}
191
return ret;
192
}
193
194
195
void
196
GNERoute::writeDemandElement(OutputDevice& device) const {
197
device.openTag(SUMO_TAG_ROUTE);
198
// write id only for non-embedded routes
199
if (myTagProperty->getTag() == SUMO_TAG_ROUTE) {
200
device.writeAttr(SUMO_ATTR_ID, getID());
201
}
202
device.writeAttr(SUMO_ATTR_EDGES, parseIDs(getParentEdges()));
203
if (myColor != myTagProperty->getDefaultColorValue(SUMO_ATTR_COLOR)) {
204
device.writeAttr(SUMO_ATTR_COLOR, toString(myColor));
205
}
206
if (myRepeat != myTagProperty->getDefaultDoubleValue(SUMO_ATTR_REPEAT)) {
207
device.writeAttr(SUMO_ATTR_REPEAT, toString(myRepeat));
208
}
209
if (myCycleTime != myTagProperty->getDefaultTimeValue(SUMO_ATTR_CYCLETIME)) {
210
device.writeAttr(SUMO_ATTR_CYCLETIME, time2string(myCycleTime));
211
}
212
if (myTagProperty->hasAttribute(SUMO_ATTR_PROB) && (myProbability != myTagProperty->getDefaultDoubleValue(SUMO_ATTR_PROB))) {
213
device.writeAttr(SUMO_ATTR_PROB, toString(myProbability));
214
}
215
// write probability if we have exactly one routeRef
216
std::vector<GNEDemandElement*> refs;
217
for (const auto& routeChild : getChildDemandElements()) {
218
if (routeChild->getTagProperty()->getTag() == GNE_TAG_ROUTEREF) {
219
refs.push_back(routeChild);
220
}
221
}
222
// write sorted stops
223
if (myTagProperty->getTag() == SUMO_TAG_ROUTE) {
224
// write stops
225
for (const auto& routeChild : getChildDemandElements()) {
226
if (routeChild->getTagProperty()->isVehicleStop()) {
227
routeChild->writeDemandElement(device);
228
}
229
}
230
}
231
// write parameters
232
writeParams(device);
233
// close tag
234
device.closeTag();
235
}
236
237
238
GNEDemandElement::Problem
239
GNERoute::isDemandElementValid() const {
240
// get sorted stops and check number
241
std::vector<GNEDemandElement*> stops;
242
for (const auto& routeChild : getChildDemandElements()) {
243
if (routeChild->getTagProperty()->isVehicleStop()) {
244
stops.push_back(routeChild);
245
}
246
}
247
// check stops
248
if (getInvalidStops().size() > 0) {
249
return Problem::STOP_DOWNSTREAM;
250
}
251
// check repeating
252
if (myRepeat > 0) {
253
// avoid repeat in routes with only one edge
254
if (getParentEdges().size() == 1) {
255
return Problem::REPEATEDROUTE_DISCONNECTED;
256
}
257
// check if front and last routes are connected
258
if (isRouteValid({getParentEdges().back(), getParentEdges().front()}).size() > 0) {
259
return Problem::REPEATEDROUTE_DISCONNECTED;
260
}
261
}
262
// check that exist a connection between every edge
263
if (isRouteValid(getParentEdges()).size() > 0) {
264
return Problem::INVALID_PATH;
265
} else {
266
return Problem::OK;
267
}
268
}
269
270
271
std::string
272
GNERoute::getDemandElementProblem() const {
273
// get sorted stops and check number
274
std::vector<GNEDemandElement*> stops;
275
for (const auto& routeChild : getChildDemandElements()) {
276
if (routeChild->getTagProperty()->isVehicleStop()) {
277
stops.push_back(routeChild);
278
}
279
}
280
const auto invalidStops = getInvalidStops();
281
if (invalidStops.size() > 0) {
282
return toString(invalidStops.size()) + " stops are outside of route (downstream)";
283
}
284
// check repeating
285
if (myRepeat > 0) {
286
// avoid repeat in routes with only one edge
287
if (getParentEdges().size() == 1) {
288
return TL("Cannot repeat in routes with only one edge");
289
}
290
// check if front and last routes is connected
291
if (isRouteValid({getParentEdges().back(), getParentEdges().front()}).size() > 0) {
292
return TL("Cannot repeat route; front and last edge aren't connected");
293
}
294
}
295
// return string with the problem obtained from isRouteValid
296
return isRouteValid(getParentEdges());
297
}
298
299
300
void
301
GNERoute::fixDemandElementProblem() {
302
// currently the only solution is removing Route
303
}
304
305
306
SUMOVehicleClass
307
GNERoute::getVClass() const {
308
if (myTagProperty->getTag() == GNE_TAG_ROUTE_EMBEDDED) {
309
return getParentDemandElements().at(0)->getVClass();
310
} else {
311
return myVClass;
312
}
313
}
314
315
316
const RGBColor&
317
GNERoute::getColor() const {
318
if (myColor != RGBColor::INVISIBLE) {
319
return myColor;
320
} else if ((getParentDemandElements().size() > 0) && (getParentDemandElements().front()->getColor() != RGBColor::INVISIBLE)) {
321
return getParentDemandElements().front()->getColor();
322
} else if ((getChildDemandElements().size() > 0) && (getChildDemandElements().front()->getColor() != RGBColor::INVISIBLE)) {
323
return getChildDemandElements().front()->getColor();
324
} else {
325
return RGBColor::YELLOW;
326
}
327
}
328
329
330
void
331
GNERoute::updateGeometry() {
332
// compute geometry
333
computePathElement();
334
// update child demand elements
335
for (const auto& demandElement : getChildDemandElements()) {
336
if (!demandElement->getTagProperty()->isVehicleStop()) {
337
demandElement->updateGeometry();
338
}
339
}
340
}
341
342
343
Position
344
GNERoute::getPositionInView() const {
345
return getFirstPathLane()->getPositionInView();
346
}
347
348
349
std::string
350
GNERoute::getParentName() const {
351
return getParentEdges().front()->getID();
352
}
353
354
355
double
356
GNERoute::getExaggeration(const GUIVisualizationSettings& s) const {
357
return s.vehicleSize.getExaggeration(s, this);
358
}
359
360
361
Boundary
362
GNERoute::getCenteringBoundary() const {
363
Boundary routeBoundary;
364
// return the combination of all parent edges's boundaries
365
for (const auto& i : getParentEdges()) {
366
routeBoundary.add(i->getCenteringBoundary());
367
}
368
// check if is valid
369
if (routeBoundary.isInitialised()) {
370
return routeBoundary;
371
} else {
372
return Boundary(-0.1, -0.1, 0.1, 0.1);
373
}
374
}
375
376
377
void
378
GNERoute::splitEdgeGeometry(const double /*splitPosition*/, const GNENetworkElement* originalElement, const GNENetworkElement* newElement, GNEUndoList* undoList) {
379
// obtain new list of route edges
380
std::string newRouteEdges = getNewListOfParents(originalElement, newElement);
381
// update route edges
382
if (newRouteEdges.size() > 0) {
383
setAttribute(SUMO_ATTR_EDGES, newRouteEdges, undoList);
384
}
385
}
386
387
388
void
389
GNERoute::drawGL(const GUIVisualizationSettings& /*s*/) const {
390
// Routes are drawn in drawLanePartialGL and drawJunctionPartialGL
391
}
392
393
394
void
395
GNERoute::computePathElement() {
396
// calculate path depending if is embedded
397
if (myTagProperty->getTag() == GNE_TAG_ROUTE_EMBEDDED) {
398
myNet->getDemandPathManager()->calculateConsecutivePathEdges(this, getVClass(), getParentEdges(),
399
(int)getParentDemandElements().at(0)->getAttributeDouble(SUMO_ATTR_DEPARTLANE),
400
(int)getParentDemandElements().at(0)->getAttributeDouble(SUMO_ATTR_ARRIVALLANE));
401
} else {
402
myNet->getDemandPathManager()->calculateConsecutivePathEdges(this, SVC_PASSENGER, getParentEdges());
403
}
404
// if path is empty, then calculate path again using SVC_IGNORING
405
if (!myNet->getDemandPathManager()->isPathValid(this)) {
406
myNet->getDemandPathManager()->calculateConsecutivePathEdges(this, SVC_IGNORING, getParentEdges());
407
}
408
}
409
410
411
void
412
GNERoute::drawLanePartialGL(const GUIVisualizationSettings& s, const GNESegment* segment, const double offsetFront) const {
413
// check conditions
414
if (segment->getLane() && myNet->getViewNet()->getNetworkViewOptions().showDemandElements() && myNet->getViewNet()->getDataViewOptions().showDemandElements() &&
415
myNet->getViewNet()->getDemandViewOptions().showNonInspectedDemandElements(this) &&
416
myNet->getDemandPathManager()->getPathDraw()->checkDrawPathGeometry(s, segment->getLane(), myTagProperty->getTag(), false) &&
417
checkCreatingVehicleOverRoute()) {
418
// get exaggeration
419
const double exaggeration = getExaggeration(s);
420
// get detail level
421
const auto d = s.getDetailLevel(exaggeration);
422
// get embedded route flag
423
const bool embedded = (myTagProperty->getTag() == GNE_TAG_ROUTE_EMBEDDED);
424
// get route width
425
const double routeWidth = embedded ? s.widthSettings.embeddedRouteWidth : s.widthSettings.routeWidth;
426
// calculate startPos
427
const double geometryDepartPos = embedded ? (getParentDemandElements().at(0)->getAttributeDouble(SUMO_ATTR_DEPARTPOS) + getParentDemandElements().at(0)->getParentDemandElements().at(0)->getAttributeDouble(SUMO_ATTR_LENGTH)) : -1;
428
// get endPos
429
const double geometryEndPos = embedded ? getParentDemandElements().at(0)->getAttributeDouble(SUMO_ATTR_ARRIVALPOS) : -1;
430
// declare path geometry
431
GUIGeometry routeGeometry;
432
// update pathGeometry depending of first and last segment
433
if (segment->isFirstSegment() && segment->isLastSegment()) {
434
routeGeometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),
435
geometryDepartPos,
436
Position::INVALID,
437
geometryEndPos,
438
Position::INVALID);
439
} else if (segment->isFirstSegment()) {
440
routeGeometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),
441
geometryDepartPos,
442
Position::INVALID,
443
-1,
444
Position::INVALID);
445
} else if (segment->isLastSegment()) {
446
routeGeometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),
447
-1,
448
Position::INVALID,
449
geometryEndPos,
450
Position::INVALID);
451
} else {
452
routeGeometry = segment->getLane()->getLaneGeometry();
453
}
454
// draw geometry only if we'rent in drawForObjectUnderCursor mode
455
if (s.checkDrawVehicle(d, isAttributeCarrierSelected())) {
456
// draw route partial lane
457
drawRoutePartialLane(s, d, segment, offsetFront, routeGeometry, exaggeration);
458
// draw name
459
if (myTagProperty->getTag() == SUMO_TAG_ROUTE) {
460
drawName(getCenteringBoundary().getCenter(), s.scale, s.addName);
461
}
462
// draw dotted contour
463
segment->getContour()->drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);
464
// show index over every edge
465
if (s.showRouteIndex && myNet->getViewNet()->getEditModes().isCurrentSupermodeDemand() &&
466
myNet->getViewNet()->getInspectedElements().isACInspected(this)) {
467
const double textSize = s.vehicleName.size / s.scale;
468
std::string label = toString(segment->getLaneIndex());
469
Position pos = segment->getLane()->getLaneShape().front() - Position(0, textSize * 1);
470
// use layer above all demand elements
471
GLHelper::drawTextSettings(s.vehicleName, label, pos, s.scale, s.angle, GLO_VEHICLELABELS);
472
}
473
}
474
// calculate contour
475
segment->getContour()->calculateContourExtrudedShape(s, d, this, routeGeometry.getShape(), getType(), routeWidth, exaggeration,
476
segment->isFirstSegment(), segment->isLastSegment(), 0, segment, segment->getLane()->getParentEdge());
477
// check if add this path element to redraw buffer
478
if (!gViewObjectsHandler.isPathElementMarkForRedraw(this) && segment->getContour()->checkDrawPathContour(s, d, this)) {
479
gViewObjectsHandler.addToRedrawPathElements(this);
480
}
481
}
482
}
483
484
485
void
486
GNERoute::drawJunctionPartialGL(const GUIVisualizationSettings& s, const GNESegment* segment, const double offsetFront) const {
487
// check conditions
488
if (myNet->getViewNet()->getNetworkViewOptions().showDemandElements() && myNet->getViewNet()->getDataViewOptions().showDemandElements() &&
489
myNet->getViewNet()->getDemandViewOptions().showNonInspectedDemandElements(this) &&
490
myNet->getDemandPathManager()->getPathDraw()->checkDrawPathGeometry(s, segment, myTagProperty->getTag(), false) &&
491
checkCreatingVehicleOverRoute()) {
492
// Obtain exaggeration of the draw
493
const double routeExaggeration = getExaggeration(s);
494
// get detail level
495
const auto d = s.getDetailLevel(routeExaggeration);
496
// get route width
497
const double routeWidth = (myTagProperty->getTag() == GNE_TAG_ROUTE_EMBEDDED) ? s.widthSettings.embeddedRouteWidth : s.widthSettings.routeWidth;
498
// check if connection to next lane exist
499
const bool connectionExist = segment->getPreviousLane()->getLane2laneConnections().exist(segment->getNextLane());
500
// get geometry
501
const GUIGeometry& routeGeometry = connectionExist ? segment->getPreviousLane()->getLane2laneConnections().getLane2laneGeometry(segment->getNextLane()) :
502
GUIGeometry({segment->getPreviousLane()->getLaneShape().back(), segment->getNextLane()->getLaneShape().front()});
503
// draw geometry only if we'rent in drawForObjectUnderCursor mode
504
if (s.checkDrawVehicle(d, isAttributeCarrierSelected())) {
505
// draw route partial
506
drawRoutePartialJunction(s, d, offsetFront, routeGeometry, routeExaggeration);
507
// draw dotted contour
508
segment->getContour()->drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);
509
}
510
// calculate contour
511
segment->getContour()->calculateContourExtrudedShape(s, d, this, routeGeometry.getShape(), getType(), routeWidth, routeExaggeration,
512
false, false, 0, segment, segment->getJunction());
513
// check if add this path element to redraw buffer
514
if (!gViewObjectsHandler.isPathElementMarkForRedraw(this) && segment->getContour()->checkDrawPathContour(s, d, this)) {
515
gViewObjectsHandler.addToRedrawPathElements(this);
516
}
517
}
518
}
519
520
521
GNELane*
522
GNERoute::getFirstPathLane() const {
523
if (myTagProperty->getTag() == SUMO_TAG_ROUTE) {
524
return getParentEdges().front()->getLaneByAllowedVClass(SVC_PASSENGER);
525
} else {
526
return getParentDemandElements().at(0)->getFirstPathLane();
527
}
528
}
529
530
531
GNELane*
532
GNERoute::getLastPathLane() const {
533
if (myTagProperty->getTag() == SUMO_TAG_ROUTE) {
534
return getParentEdges().back()->getLaneByAllowedVClass(SVC_PASSENGER);
535
} else {
536
return getParentDemandElements().at(0)->getLastPathLane();
537
}
538
}
539
540
541
std::string
542
GNERoute::getAttribute(SumoXMLAttr key) const {
543
switch (key) {
544
case SUMO_ATTR_ID:
545
return getMicrosimID();
546
case SUMO_ATTR_EDGES:
547
return parseIDs(getParentEdges());
548
case SUMO_ATTR_COLOR:
549
if (myColor != RGBColor::INVISIBLE) {
550
return toString(myColor);
551
} else {
552
return "";
553
}
554
case SUMO_ATTR_REPEAT:
555
return toString(myRepeat);
556
case SUMO_ATTR_CYCLETIME:
557
return time2string(myCycleTime);
558
case SUMO_ATTR_PROB:
559
return toString(myProbability);
560
default:
561
return getCommonAttribute(key);
562
}
563
}
564
565
566
double
567
GNERoute::getAttributeDouble(SumoXMLAttr key) const {
568
switch (key) {
569
case SUMO_ATTR_DEPARTPOS:
570
return 0;
571
case SUMO_ATTR_ARRIVALPOS:
572
return getParentEdges().back()->getChildLanes().front()->getLaneShape().length2D();
573
case SUMO_ATTR_PROB:
574
return myProbability;
575
default:
576
return getCommonAttributeDouble(key);
577
}
578
}
579
580
581
Position
582
GNERoute::getAttributePosition(SumoXMLAttr key) const {
583
switch (key) {
584
case SUMO_ATTR_DEPARTPOS:
585
return getParentEdges().front()->getChildLanes().front()->getLaneShape().front();
586
case SUMO_ATTR_ARRIVALPOS:
587
return getParentEdges().back()->getChildLanes().front()->getLaneShape().back();
588
default:
589
return getCommonAttributePosition(key);
590
}
591
}
592
593
594
void
595
GNERoute::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {
596
if (value == getAttribute(key)) {
597
return; //avoid needless changes, later logic relies on the fact that attributes have changed
598
}
599
switch (key) {
600
case SUMO_ATTR_ID:
601
case SUMO_ATTR_COLOR:
602
case SUMO_ATTR_REPEAT:
603
case SUMO_ATTR_CYCLETIME:
604
case SUMO_ATTR_PROB:
605
GNEChange_Attribute::changeAttribute(this, key, value, undoList);
606
break;
607
// special case due depart and arrival edge vehicles
608
case SUMO_ATTR_EDGES: {
609
// extract all vehicle childrens
610
std::vector<GNEDemandElement*> vehicles;
611
for (const auto& childDemandElement : getChildDemandElements()) {
612
if (childDemandElement->getTagProperty()->isVehicle()) {
613
vehicles.push_back(childDemandElement);
614
}
615
}
616
// check vehicles
617
if (vehicles.size() > 0) {
618
undoList->begin(this, "reset start and end edges");
619
for (const auto& vehicle : vehicles) {
620
GNEChange_Attribute::changeAttribute(vehicle, SUMO_ATTR_DEPARTEDGE, "", undoList);
621
GNEChange_Attribute::changeAttribute(vehicle, SUMO_ATTR_ARRIVALEDGE, "", undoList);
622
}
623
GNEChange_Attribute::changeAttribute(this, key, value, undoList);
624
undoList->end();
625
} else if (myTagProperty->getTag() == GNE_TAG_ROUTE_EMBEDDED) {
626
undoList->begin(this, "reset start and end edges");
627
GNEChange_Attribute::changeAttribute(getParentDemandElements().front(), SUMO_ATTR_DEPARTEDGE, "", undoList);
628
GNEChange_Attribute::changeAttribute(getParentDemandElements().front(), SUMO_ATTR_ARRIVALEDGE, "", undoList);
629
GNEChange_Attribute::changeAttribute(this, key, value, undoList);
630
undoList->end();
631
} else {
632
// just change edges
633
GNEChange_Attribute::changeAttribute(this, key, value, undoList);
634
}
635
break;
636
}
637
default:
638
setCommonAttribute(key, value, undoList);
639
break;
640
}
641
}
642
643
644
bool
645
GNERoute::isValid(SumoXMLAttr key, const std::string& value) {
646
switch (key) {
647
case SUMO_ATTR_ID:
648
return isValidDemandElementID(value);
649
case SUMO_ATTR_EDGES:
650
if (value.empty()) {
651
return false;
652
} else {
653
return canParse<std::vector<GNEEdge*> >(myNet, value, true);
654
}
655
case SUMO_ATTR_COLOR:
656
if (value.empty()) {
657
return true;
658
} else {
659
return canParse<RGBColor>(value);
660
}
661
case SUMO_ATTR_REPEAT:
662
return canParse<int>(value) && (parse<int>(value) >= 0);
663
case SUMO_ATTR_CYCLETIME:
664
if (canParse<SUMOTime>(value)) {
665
return (parse<SUMOTime>(value) >= 0);
666
} else {
667
return false;
668
}
669
case SUMO_ATTR_PROB:
670
if (canParse<double>(value)) {
671
return (parse<double>(value) >= 0);
672
} else {
673
return false;
674
}
675
default:
676
return isCommonAttributeValid(key, value);
677
}
678
}
679
680
681
std::string
682
GNERoute::getPopUpID() const {
683
return getTagStr();
684
}
685
686
687
std::string
688
GNERoute::getHierarchyName() const {
689
return getTagStr() + ": " + getAttribute(SUMO_ATTR_ID) ;
690
}
691
692
693
std::string
694
GNERoute::isRouteValid(const std::vector<GNEEdge*>& edges) {
695
if (edges.size() == 0) {
696
// routes cannot be empty
697
return ("list of route edges cannot be empty");
698
} else if (edges.size() == 1) {
699
// routes with a single edge are valid, then return an empty string
700
return ("");
701
} else {
702
// iterate over edges to check that compounds a chain
703
auto it = edges.begin();
704
while (it != edges.end() - 1) {
705
const GNEEdge* currentEdge = *it;
706
const GNEEdge* nextEdge = *(it + 1);
707
// same consecutive edges aren't allowed
708
if (currentEdge->getID() == nextEdge->getID()) {
709
return ("consecutive duplicated edges (" + currentEdge->getID() + ") aren't allowed in a route");
710
}
711
// obtain outgoing edges of currentEdge
712
const std::vector<GNEEdge*>& outgoingEdges = currentEdge->getToJunction()->getGNEOutgoingEdges();
713
// check if nextEdge is in outgoingEdges
714
if (std::find(outgoingEdges.begin(), outgoingEdges.end(), nextEdge) == outgoingEdges.end()) {
715
return ("Edges '" + currentEdge->getID() + "' and '" + nextEdge->getID() + "' aren't consecutives");
716
}
717
it++;
718
}
719
// all edges consecutives, then return an empty string
720
return ("");
721
}
722
}
723
724
GNEDemandElement*
725
GNERoute::copyRoute(const GNERoute* originalRoute) {
726
// get net and undoList
727
const auto net = originalRoute->getNet();
728
auto undoList = net->getViewNet()->getUndoList();
729
// generate new route ID
730
const std::string newRouteID = net->getAttributeCarriers()->generateDemandElementID(SUMO_TAG_ROUTE);
731
// create new route
732
GNERoute* newRoute = new GNERoute(newRouteID, originalRoute);
733
// add new route using undo-list
734
undoList->begin(originalRoute, TLF("copy % '%'", originalRoute->getTagStr(), newRouteID));
735
net->getViewNet()->getUndoList()->add(new GNEChange_DemandElement(newRoute, true), true);
736
undoList->end();
737
// return new route
738
return newRoute;
739
}
740
741
// ===========================================================================
742
// private
743
// ===========================================================================
744
745
void
746
GNERoute::drawRoutePartialLane(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,
747
const GNESegment* segment, const double offsetFront,
748
const GUIGeometry& geometry, const double exaggeration) const {
749
// get route width
750
const double routeWidth = (myTagProperty->getTag() == GNE_TAG_ROUTE_EMBEDDED) ? s.widthSettings.embeddedRouteWidth : s.widthSettings.routeWidth;
751
// push layer matrix
752
GLHelper::pushMatrix();
753
// Start with the drawing of the area traslating matrix to origin
754
glTranslated(0, 0, getType() + offsetFront);
755
// Set color
756
if (drawUsingSelectColor()) {
757
GLHelper::setColor(s.colorSettings.selectedRouteColor);
758
} else {
759
GLHelper::setColor(getColor());
760
}
761
// draw geometry
762
GUIGeometry::drawGeometry(d, geometry, routeWidth * exaggeration);
763
// check if we have to draw a red line to the next segment
764
if (segment->getNextLane()) {
765
// push draw matrix
766
GLHelper::pushMatrix();
767
// Set red color
768
GLHelper::setColor(RGBColor::RED);
769
// get firstPosition (last position of current lane shape)
770
const Position firstPosition = segment->getLane()->getLaneShape().back();
771
// get lastPosition (first position of next lane shape)
772
const Position arrivalPos = segment->getNextLane()->getLaneShape().front();
773
// draw box line
774
GLHelper::drawBoxLine(arrivalPos,
775
RAD2DEG(firstPosition.angleTo2D(arrivalPos)) - 90,
776
firstPosition.distanceTo2D(arrivalPos), .05);
777
// pop draw matrix
778
GLHelper::popMatrix();
779
}
780
// Pop layer matrix
781
GLHelper::popMatrix();
782
}
783
784
785
void
786
GNERoute::drawRoutePartialJunction(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,
787
const double offsetFront, const GUIGeometry& geometry, const double exaggeration) const {
788
const bool invalid = geometry.getShape().length() == 2;
789
// get route width
790
const double routeWidth = (myTagProperty->getTag() == GNE_TAG_ROUTE_EMBEDDED) ? s.widthSettings.embeddedRouteWidth : s.widthSettings.routeWidth;
791
// Add a draw matrix
792
GLHelper::pushMatrix();
793
// Start with the drawing of the area traslating matrix to origin
794
glTranslated(0, 0, getType() + offsetFront);
795
// Set color of the base
796
if (drawUsingSelectColor()) {
797
GLHelper::setColor(s.colorSettings.selectedAdditionalColor);
798
} else if (invalid) {
799
GLHelper::setColor(RGBColor::RED);
800
} else {
801
GLHelper::setColor(getColor());
802
}
803
// draw geometry
804
GUIGeometry::drawGeometry(d, geometry, routeWidth * exaggeration);
805
// Pop last matrix
806
GLHelper::popMatrix();
807
}
808
809
810
void
811
GNERoute::setAttribute(SumoXMLAttr key, const std::string& value) {
812
switch (key) {
813
case SUMO_ATTR_ID:
814
// update microsimID
815
setDemandElementID(value);
816
break;
817
case SUMO_ATTR_EDGES:
818
// replace parents
819
replaceParentEdges(value);
820
// compute route
821
computePathElement();
822
// update all parent and child demand elements
823
for (const auto& element : getParentDemandElements()) {
824
element->updateGeometry();
825
}
826
for (const auto& element : getChildDemandElements()) {
827
element->updateGeometry();
828
}
829
break;
830
case SUMO_ATTR_COLOR:
831
if (value.empty()) {
832
myColor = RGBColor::INVISIBLE;
833
} else {
834
myColor = parse<RGBColor>(value);
835
}
836
break;
837
case SUMO_ATTR_REPEAT:
838
if (value.empty()) {
839
myRepeat = myTagProperty->getDefaultIntValue(key);
840
} else {
841
myRepeat = parse<int>(value);
842
}
843
break;
844
case SUMO_ATTR_CYCLETIME:
845
if (value.empty()) {
846
myCycleTime = myTagProperty->getDefaultTimeValue(key);
847
} else {
848
myCycleTime = string2time(value);
849
}
850
break;
851
case SUMO_ATTR_PROB:
852
if (value.empty()) {
853
myProbability = myTagProperty->getDefaultDoubleValue(key);
854
} else {
855
myProbability = parse<double>(value);
856
}
857
break;
858
default:
859
setCommonAttribute(key, value);
860
break;
861
}
862
}
863
864
865
bool
866
GNERoute::checkCreatingVehicleOverRoute() const {
867
if (myTagProperty->getTag() != GNE_TAG_ROUTE_EMBEDDED) {
868
// this affect only to embedded routes
869
return true;
870
} else if (!myNet->getViewNet()->getEditModes().isCurrentSupermodeDemand()) {
871
// only in demand mode
872
return true;
873
} else if (myNet->getViewNet()->getEditModes().demandEditMode != DemandEditMode::DEMAND_VEHICLE) {
874
// only creating vehicles
875
return true;
876
} else {
877
// get current template AC
878
const auto templateAC = myNet->getViewParent()->getVehicleFrame()->getVehicleTagSelector()->getCurrentTemplateAC();
879
if (templateAC && templateAC->getTagProperty()->vehicleRoute()) {
880
// we're creating a vehicle over a route, then hidde all embedded routes
881
return false;
882
} else {
883
return true;
884
}
885
}
886
}
887
888
889
/****************************************************************************/
890
891