Path: blob/main/src/netedit/elements/demand/GNERoute.cpp
185790 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file GNERoute.cpp14/// @author Pablo Alvarez Lopez15/// @date Jan 201916///17// A class for visualizing routes in Netedit18/****************************************************************************/1920#include <netedit/changes/GNEChange_Attribute.h>21#include <netedit/changes/GNEChange_DemandElement.h>22#include <netedit/frames/demand/GNEVehicleFrame.h>23#include <netedit/frames/GNETagSelector.h>24#include <netedit/GNENet.h>25#include <netedit/GNESegment.h>26#include <netedit/GNETagProperties.h>27#include <netedit/GNEUndoList.h>28#include <netedit/GNEViewParent.h>29#include <utils/gui/div/GLHelper.h>30#include <utils/gui/div/GUIDesigns.h>31#include <utils/gui/windows/GUIAppEnum.h>3233#include "GNERoute.h"34#include "GNEVehicle.h"3536// ===========================================================================37// FOX callback mapping38// ===========================================================================3940FXDEFMAP(GNERoute::GNERoutePopupMenu) GNERoutePopupMenuMap[] = {41FXMAPFUNC(SEL_COMMAND, MID_GNE_ROUTE_APPLY_DISTANCE, GNERoute::GNERoutePopupMenu::onCmdApplyDistance),42};4344// Object implementation45FXIMPLEMENT(GNERoute::GNERoutePopupMenu, GUIGLObjectPopupMenu, GNERoutePopupMenuMap, ARRAYNUMBER(GNERoutePopupMenuMap))4647// ===========================================================================48// GNERoute::GNERoutePopupMenu - methods49// ===========================================================================5051GNERoute::GNERoutePopupMenu::GNERoutePopupMenu(GUIMainWindow& app, GUISUMOAbstractView& parent, GUIGlObject* o) :52GUIGLObjectPopupMenu(app, parent, o) {53}545556GNERoute::GNERoutePopupMenu::~GNERoutePopupMenu() {}575859long60GNERoute::GNERoutePopupMenu::onCmdApplyDistance(FXObject*, FXSelector, void*) {61GNERoute* route = static_cast<GNERoute*>(myObject);62GNEUndoList* undoList = route->myNet->getUndoList();63undoList->begin(route, "apply distance along route");64double dist = (route->getParentEdges().size() > 0) ? route->getParentEdges().front()->getNBEdge()->getDistance() : 0;65for (GNEEdge* edge : route->getParentEdges()) {66GNEChange_Attribute::changeAttribute(edge, SUMO_ATTR_DISTANCE, toString(dist), edge->getAttribute(SUMO_ATTR_DISTANCE), undoList);67dist += edge->getNBEdge()->getFinalLength();68}69undoList->end();70return 1;71}7273// ===========================================================================74// GNERoute - methods75// ===========================================================================7677GNERoute::GNERoute(SumoXMLTag tag, GNENet* net) :78GNEDemandElement(net, tag) {79}808182GNERoute::GNERoute(GNEAdditional* calibrator) :83GNEDemandElement(calibrator->getNet()->getAttributeCarriers()->generateDemandElementID(SUMO_TAG_ROUTE),84calibrator->getNet(), SUMO_TAG_ROUTE, calibrator->getFileBucket()) {85// set parent edge86if (calibrator->getParentEdges().size() > 0) {87setParents<GNEEdge*>({calibrator->getParentEdges().front()});88} else if (calibrator->getParentLanes().size() > 0) {89setParents<GNEEdge*>({calibrator->getParentLanes().front()->getParentEdge()});90} else {91throw InvalidArgument("Calibrator parent requieres at least one edge or one lane");92}93}949596GNERoute::GNERoute(const std::string& id, const GNEDemandElement* originalRoute) :97GNEDemandElement(id, originalRoute->getNet(), originalRoute->getTagProperty()->getTag(), originalRoute->getFileBucket()),98Parameterised(originalRoute->getParameters()->getParametersMap()),99myRepeat(parse<int>(originalRoute->getAttribute(SUMO_ATTR_REPEAT))),100myCycleTime(string2time(originalRoute->getAttribute(SUMO_ATTR_REPEAT))),101myVClass(originalRoute->getVClass()) {102// set parents103setParents<GNEEdge*>(originalRoute->getParentEdges());104setAttribute(SUMO_ATTR_COLOR, originalRoute->getAttribute(SUMO_ATTR_COLOR));105}106107108GNERoute::GNERoute(GNEVehicle* vehicleParent, const GNEDemandElement* originalRoute) :109GNEDemandElement(vehicleParent, originalRoute->getTagProperty()->getTag()),110Parameterised(originalRoute->getParameters()->getParametersMap()),111myRepeat(parse<int>(originalRoute->getAttribute(SUMO_ATTR_REPEAT))),112myCycleTime(string2time(originalRoute->getAttribute(SUMO_ATTR_REPEAT))),113myVClass(originalRoute->getVClass()) {114// set parents115setParents<GNEEdge*>(originalRoute->getParentEdges());116setParent<GNEDemandElement*>(vehicleParent);117setAttribute(SUMO_ATTR_COLOR, originalRoute->getAttribute(SUMO_ATTR_COLOR));118}119120121GNERoute::GNERoute(const std::string& id, GNENet* net, FileBucket* fileBucket, SUMOVehicleClass vClass,122const std::vector<GNEEdge*>& edges, const RGBColor& color, const int repeat,123const SUMOTime cycleTime, const double probability, const Parameterised::Map& parameters) :124GNEDemandElement(id, net, SUMO_TAG_ROUTE, fileBucket),125Parameterised(parameters),126myColor(color),127myRepeat(repeat),128myCycleTime(cycleTime),129myProbability(probability),130myVClass(vClass) {131// set parents132setParents<GNEEdge*>(edges);133}134135136GNERoute::GNERoute(GNEDemandElement* vehicleParent, const std::vector<GNEEdge*>& edges, const RGBColor& color,137const int repeat, const SUMOTime cycleTime, const Parameterised::Map& parameters) :138GNEDemandElement(vehicleParent, GNE_TAG_ROUTE_EMBEDDED),139Parameterised(parameters),140myColor(color),141myRepeat(repeat),142myCycleTime(cycleTime),143myVClass(vehicleParent->getVClass()) {144// set parents145setParents<GNEEdge*>(edges);146setParent<GNEDemandElement*>(vehicleParent);147}148149150GNERoute::~GNERoute() {}151152153GNEMoveElement* GNERoute::getMoveElement() const {154return nullptr;155}156157158Parameterised*159GNERoute::getParameters() {160return this;161}162163164const Parameterised*165GNERoute::getParameters() const {166return this;167}168169170GUIGLObjectPopupMenu*171GNERoute::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {172// create popup173GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);174// build common options175buildPopUpMenuCommonOptions(ret, app, myNet->getViewNet(), myTagProperty->getTag(), mySelected);176// show option to open demand element dialog177if (myTagProperty->hasDialog()) {178GUIDesigns::buildFXMenuCommand(ret, "Open " + getTagStr() + " Dialog", getACIcon(), &parent, MID_OPEN_ADDITIONAL_DIALOG);179new FXMenuSeparator(ret);180}181GUIDesigns::buildFXMenuCommand(ret, "Cursor position in view: " + toString(getPositionInView().x()) + "," + toString(getPositionInView().y()), nullptr, nullptr, 0);182new FXMenuSeparator(ret);183GUIDesigns::buildFXMenuCommand(ret, "Apply distance along route", nullptr, ret, MID_GNE_ROUTE_APPLY_DISTANCE);184// route length185buildMenuCommandRouteLength(ret);186if (myNet->getViewNet()->getEditModes().isCurrentSupermodeDemand()) {187// add reverse188buildMenuAddReverse(ret);189}190return ret;191}192193194void195GNERoute::writeDemandElement(OutputDevice& device) const {196device.openTag(SUMO_TAG_ROUTE);197// write id only for non-embedded routes198if (myTagProperty->getTag() == SUMO_TAG_ROUTE) {199device.writeAttr(SUMO_ATTR_ID, getID());200}201device.writeAttr(SUMO_ATTR_EDGES, parseIDs(getParentEdges()));202if (myColor != myTagProperty->getDefaultColorValue(SUMO_ATTR_COLOR)) {203device.writeAttr(SUMO_ATTR_COLOR, toString(myColor));204}205if (myRepeat != myTagProperty->getDefaultDoubleValue(SUMO_ATTR_REPEAT)) {206device.writeAttr(SUMO_ATTR_REPEAT, toString(myRepeat));207}208if (myCycleTime != myTagProperty->getDefaultTimeValue(SUMO_ATTR_CYCLETIME)) {209device.writeAttr(SUMO_ATTR_CYCLETIME, time2string(myCycleTime));210}211if (myTagProperty->hasAttribute(SUMO_ATTR_PROB) && (myProbability != myTagProperty->getDefaultDoubleValue(SUMO_ATTR_PROB))) {212device.writeAttr(SUMO_ATTR_PROB, toString(myProbability));213}214// write probability if we have exactly one routeRef215std::vector<GNEDemandElement*> refs;216for (const auto& routeChild : getChildDemandElements()) {217if (routeChild->getTagProperty()->getTag() == GNE_TAG_ROUTEREF) {218refs.push_back(routeChild);219}220}221// write sorted stops222if (myTagProperty->getTag() == SUMO_TAG_ROUTE) {223// write stops224for (const auto& routeChild : getChildDemandElements()) {225if (routeChild->getTagProperty()->isVehicleStop()) {226routeChild->writeDemandElement(device);227}228}229}230// write parameters231writeParams(device);232// close tag233device.closeTag();234}235236237GNEDemandElement::Problem238GNERoute::isDemandElementValid() const {239// get sorted stops and check number240std::vector<GNEDemandElement*> stops;241for (const auto& routeChild : getChildDemandElements()) {242if (routeChild->getTagProperty()->isVehicleStop()) {243stops.push_back(routeChild);244}245}246// check stops247if (getInvalidStops().size() > 0) {248return Problem::STOP_DOWNSTREAM;249}250// check repeating251if (myRepeat > 0) {252// avoid repeat in routes with only one edge253if (getParentEdges().size() == 1) {254return Problem::REPEATEDROUTE_DISCONNECTED;255}256// check if front and last routes are connected257if (isRouteValid({getParentEdges().back(), getParentEdges().front()}).size() > 0) {258return Problem::REPEATEDROUTE_DISCONNECTED;259}260}261// check that exist a connection between every edge262if (isRouteValid(getParentEdges()).size() > 0) {263return Problem::INVALID_PATH;264} else {265return Problem::OK;266}267}268269270std::string271GNERoute::getDemandElementProblem() const {272// get sorted stops and check number273std::vector<GNEDemandElement*> stops;274for (const auto& routeChild : getChildDemandElements()) {275if (routeChild->getTagProperty()->isVehicleStop()) {276stops.push_back(routeChild);277}278}279const auto invalidStops = getInvalidStops();280if (invalidStops.size() > 0) {281return toString(invalidStops.size()) + " stops are outside of route (downstream)";282}283// check repeating284if (myRepeat > 0) {285// avoid repeat in routes with only one edge286if (getParentEdges().size() == 1) {287return TL("Cannot repeat in routes with only one edge");288}289// check if front and last routes is connected290if (isRouteValid({getParentEdges().back(), getParentEdges().front()}).size() > 0) {291return TL("Cannot repeat route; front and last edge aren't connected");292}293}294// return string with the problem obtained from isRouteValid295return isRouteValid(getParentEdges());296}297298299void300GNERoute::fixDemandElementProblem() {301// currently the only solution is removing Route302}303304305SUMOVehicleClass306GNERoute::getVClass() const {307if (myTagProperty->getTag() == GNE_TAG_ROUTE_EMBEDDED) {308return getParentDemandElements().at(0)->getVClass();309} else {310return myVClass;311}312}313314315const RGBColor&316GNERoute::getColor() const {317if (myColor != RGBColor::INVISIBLE) {318return myColor;319} else if ((getParentDemandElements().size() > 0) && (getParentDemandElements().front()->getColor() != RGBColor::INVISIBLE)) {320return getParentDemandElements().front()->getColor();321} else if ((getChildDemandElements().size() > 0) && (getChildDemandElements().front()->getColor() != RGBColor::INVISIBLE)) {322return getChildDemandElements().front()->getColor();323} else {324return RGBColor::YELLOW;325}326}327328329void330GNERoute::updateGeometry() {331// compute geometry332computePathElement();333// update child demand elements334for (const auto& demandElement : getChildDemandElements()) {335if (!demandElement->getTagProperty()->isVehicleStop()) {336demandElement->updateGeometry();337}338}339}340341342Position343GNERoute::getPositionInView() const {344return getFirstPathLane()->getPositionInView();345}346347348std::string349GNERoute::getParentName() const {350return getParentEdges().front()->getID();351}352353354double355GNERoute::getExaggeration(const GUIVisualizationSettings& s) const {356return s.vehicleSize.getExaggeration(s, this);357}358359360Boundary361GNERoute::getCenteringBoundary() const {362Boundary routeBoundary;363// return the combination of all parent edges's boundaries364for (const auto& i : getParentEdges()) {365routeBoundary.add(i->getCenteringBoundary());366}367// check if is valid368if (routeBoundary.isInitialised()) {369return routeBoundary;370} else {371return Boundary(-0.1, -0.1, 0.1, 0.1);372}373}374375376void377GNERoute::splitEdgeGeometry(const double /*splitPosition*/, const GNENetworkElement* originalElement, const GNENetworkElement* newElement, GNEUndoList* undoList) {378// obtain new list of route edges379std::string newRouteEdges = getNewListOfParents(originalElement, newElement);380// update route edges381if (newRouteEdges.size() > 0) {382setAttribute(SUMO_ATTR_EDGES, newRouteEdges, undoList);383}384}385386387void388GNERoute::drawGL(const GUIVisualizationSettings& /*s*/) const {389// Routes are drawn in drawLanePartialGL and drawJunctionPartialGL390}391392393void394GNERoute::computePathElement() {395// calculate path depending if is embedded396if (myTagProperty->getTag() == GNE_TAG_ROUTE_EMBEDDED) {397myNet->getDemandPathManager()->calculateConsecutivePathEdges(this, getVClass(), getParentEdges(),398(int)getParentDemandElements().at(0)->getAttributeDouble(SUMO_ATTR_DEPARTLANE),399(int)getParentDemandElements().at(0)->getAttributeDouble(SUMO_ATTR_ARRIVALLANE));400} else {401myNet->getDemandPathManager()->calculateConsecutivePathEdges(this, SVC_PASSENGER, getParentEdges());402}403// if path is empty, then calculate path again using SVC_IGNORING404if (!myNet->getDemandPathManager()->isPathValid(this)) {405myNet->getDemandPathManager()->calculateConsecutivePathEdges(this, SVC_IGNORING, getParentEdges());406}407}408409410void411GNERoute::drawLanePartialGL(const GUIVisualizationSettings& s, const GNESegment* segment, const double offsetFront) const {412// check conditions413if (segment->getLane() && myNet->getViewNet()->getNetworkViewOptions().showDemandElements() && myNet->getViewNet()->getDataViewOptions().showDemandElements() &&414myNet->getViewNet()->getDemandViewOptions().showNonInspectedDemandElements(this) &&415myNet->getDemandPathManager()->getPathDraw()->checkDrawPathGeometry(s, segment->getLane(), myTagProperty->getTag(), false) &&416checkCreatingVehicleOverRoute()) {417// get exaggeration418const double exaggeration = getExaggeration(s);419// get detail level420const auto d = s.getDetailLevel(exaggeration);421// get embedded route flag422const bool embedded = (myTagProperty->getTag() == GNE_TAG_ROUTE_EMBEDDED);423// get route width424const double routeWidth = embedded ? s.widthSettings.embeddedRouteWidth : s.widthSettings.routeWidth;425// calculate startPos426const double geometryDepartPos = embedded ? (getParentDemandElements().at(0)->getAttributeDouble(SUMO_ATTR_DEPARTPOS) + getParentDemandElements().at(0)->getParentDemandElements().at(0)->getAttributeDouble(SUMO_ATTR_LENGTH)) : -1;427// get endPos428const double geometryEndPos = embedded ? getParentDemandElements().at(0)->getAttributeDouble(SUMO_ATTR_ARRIVALPOS) : -1;429// declare path geometry430GUIGeometry routeGeometry;431// update pathGeometry depending of first and last segment432if (segment->isFirstSegment() && segment->isLastSegment()) {433routeGeometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),434geometryDepartPos,435Position::INVALID,436geometryEndPos,437Position::INVALID);438} else if (segment->isFirstSegment()) {439routeGeometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),440geometryDepartPos,441Position::INVALID,442-1,443Position::INVALID);444} else if (segment->isLastSegment()) {445routeGeometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),446-1,447Position::INVALID,448geometryEndPos,449Position::INVALID);450} else {451routeGeometry = segment->getLane()->getLaneGeometry();452}453// draw geometry only if we'rent in drawForObjectUnderCursor mode454if (s.checkDrawVehicle(d, isAttributeCarrierSelected())) {455// draw route partial lane456drawRoutePartialLane(s, d, segment, offsetFront, routeGeometry, exaggeration);457// draw name458if (myTagProperty->getTag() == SUMO_TAG_ROUTE) {459drawName(getCenteringBoundary().getCenter(), s.scale, s.addName);460}461// draw dotted contour462segment->getContour()->drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);463// show index over every edge464if (s.showRouteIndex && myNet->getViewNet()->getEditModes().isCurrentSupermodeDemand() &&465myNet->getViewNet()->getInspectedElements().isACInspected(this)) {466const double textSize = s.vehicleName.size / s.scale;467std::string label = toString(segment->getLaneIndex());468Position pos = segment->getLane()->getLaneShape().front() - Position(0, textSize * 1);469// use layer above all demand elements470GLHelper::drawTextSettings(s.vehicleName, label, pos, s.scale, s.angle, GLO_VEHICLELABELS);471}472}473// calculate contour474segment->getContour()->calculateContourExtrudedShape(s, d, this, routeGeometry.getShape(), getType(), routeWidth, exaggeration,475segment->isFirstSegment(), segment->isLastSegment(), 0, segment, segment->getLane()->getParentEdge());476// check if add this path element to redraw buffer477if (!gViewObjectsHandler.isPathElementMarkForRedraw(this) && segment->getContour()->checkDrawPathContour(s, d, this)) {478gViewObjectsHandler.addToRedrawPathElements(this);479}480}481}482483484void485GNERoute::drawJunctionPartialGL(const GUIVisualizationSettings& s, const GNESegment* segment, const double offsetFront) const {486// check conditions487if (myNet->getViewNet()->getNetworkViewOptions().showDemandElements() && myNet->getViewNet()->getDataViewOptions().showDemandElements() &&488myNet->getViewNet()->getDemandViewOptions().showNonInspectedDemandElements(this) &&489myNet->getDemandPathManager()->getPathDraw()->checkDrawPathGeometry(s, segment, myTagProperty->getTag(), false) &&490checkCreatingVehicleOverRoute()) {491// Obtain exaggeration of the draw492const double routeExaggeration = getExaggeration(s);493// get detail level494const auto d = s.getDetailLevel(routeExaggeration);495// get route width496const double routeWidth = (myTagProperty->getTag() == GNE_TAG_ROUTE_EMBEDDED) ? s.widthSettings.embeddedRouteWidth : s.widthSettings.routeWidth;497// check if connection to next lane exist498const bool connectionExist = segment->getPreviousLane()->getLane2laneConnections().exist(segment->getNextLane());499// get geometry500const GUIGeometry& routeGeometry = connectionExist ? segment->getPreviousLane()->getLane2laneConnections().getLane2laneGeometry(segment->getNextLane()) :501GUIGeometry({segment->getPreviousLane()->getLaneShape().back(), segment->getNextLane()->getLaneShape().front()});502// draw geometry only if we'rent in drawForObjectUnderCursor mode503if (s.checkDrawVehicle(d, isAttributeCarrierSelected())) {504// draw route partial505drawRoutePartialJunction(s, d, offsetFront, routeGeometry, routeExaggeration);506// draw dotted contour507segment->getContour()->drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);508}509// calculate contour510segment->getContour()->calculateContourExtrudedShape(s, d, this, routeGeometry.getShape(), getType(), routeWidth, routeExaggeration,511false, false, 0, segment, segment->getJunction());512// check if add this path element to redraw buffer513if (!gViewObjectsHandler.isPathElementMarkForRedraw(this) && segment->getContour()->checkDrawPathContour(s, d, this)) {514gViewObjectsHandler.addToRedrawPathElements(this);515}516}517}518519520GNELane*521GNERoute::getFirstPathLane() const {522if (myTagProperty->getTag() == SUMO_TAG_ROUTE) {523return getParentEdges().front()->getLaneByAllowedVClass(SVC_PASSENGER);524} else {525return getParentDemandElements().at(0)->getFirstPathLane();526}527}528529530GNELane*531GNERoute::getLastPathLane() const {532if (myTagProperty->getTag() == SUMO_TAG_ROUTE) {533return getParentEdges().back()->getLaneByAllowedVClass(SVC_PASSENGER);534} else {535return getParentDemandElements().at(0)->getLastPathLane();536}537}538539540std::string541GNERoute::getAttribute(SumoXMLAttr key) const {542switch (key) {543case SUMO_ATTR_ID:544return getMicrosimID();545case SUMO_ATTR_EDGES:546return parseIDs(getParentEdges());547case SUMO_ATTR_COLOR:548if (myColor != RGBColor::INVISIBLE) {549return toString(myColor);550} else {551return "";552}553case SUMO_ATTR_REPEAT:554return toString(myRepeat);555case SUMO_ATTR_CYCLETIME:556return time2string(myCycleTime);557case SUMO_ATTR_PROB:558return toString(myProbability);559default:560return getCommonAttribute(key);561}562}563564565double566GNERoute::getAttributeDouble(SumoXMLAttr key) const {567switch (key) {568case SUMO_ATTR_DEPARTPOS:569return 0;570case SUMO_ATTR_ARRIVALPOS:571return getParentEdges().back()->getChildLanes().front()->getLaneShape().length2D();572case SUMO_ATTR_PROB:573return myProbability;574default:575return getCommonAttributeDouble(key);576}577}578579580Position581GNERoute::getAttributePosition(SumoXMLAttr key) const {582switch (key) {583case SUMO_ATTR_DEPARTPOS:584return getParentEdges().front()->getChildLanes().front()->getLaneShape().front();585case SUMO_ATTR_ARRIVALPOS:586return getParentEdges().back()->getChildLanes().front()->getLaneShape().back();587default:588return getCommonAttributePosition(key);589}590}591592593void594GNERoute::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {595if (value == getAttribute(key)) {596return; //avoid needless changes, later logic relies on the fact that attributes have changed597}598switch (key) {599case SUMO_ATTR_ID:600case SUMO_ATTR_COLOR:601case SUMO_ATTR_REPEAT:602case SUMO_ATTR_CYCLETIME:603case SUMO_ATTR_PROB:604GNEChange_Attribute::changeAttribute(this, key, value, undoList);605break;606// special case due depart and arrival edge vehicles607case SUMO_ATTR_EDGES: {608// extract all vehicle childrens609std::vector<GNEDemandElement*> vehicles;610for (const auto& childDemandElement : getChildDemandElements()) {611if (childDemandElement->getTagProperty()->isVehicle()) {612vehicles.push_back(childDemandElement);613}614}615// check vehicles616if (vehicles.size() > 0) {617undoList->begin(this, "reset start and end edges");618for (const auto& vehicle : vehicles) {619GNEChange_Attribute::changeAttribute(vehicle, SUMO_ATTR_DEPARTEDGE, "", undoList);620GNEChange_Attribute::changeAttribute(vehicle, SUMO_ATTR_ARRIVALEDGE, "", undoList);621}622GNEChange_Attribute::changeAttribute(this, key, value, undoList);623undoList->end();624} else if (myTagProperty->getTag() == GNE_TAG_ROUTE_EMBEDDED) {625undoList->begin(this, "reset start and end edges");626GNEChange_Attribute::changeAttribute(getParentDemandElements().front(), SUMO_ATTR_DEPARTEDGE, "", undoList);627GNEChange_Attribute::changeAttribute(getParentDemandElements().front(), SUMO_ATTR_ARRIVALEDGE, "", undoList);628GNEChange_Attribute::changeAttribute(this, key, value, undoList);629undoList->end();630} else {631// just change edges632GNEChange_Attribute::changeAttribute(this, key, value, undoList);633}634break;635}636default:637setCommonAttribute(key, value, undoList);638break;639}640}641642643bool644GNERoute::isValid(SumoXMLAttr key, const std::string& value) {645switch (key) {646case SUMO_ATTR_ID:647return isValidDemandElementID(value);648case SUMO_ATTR_EDGES:649if (value.empty()) {650return false;651} else {652return canParse<std::vector<GNEEdge*> >(myNet, value, true);653}654case SUMO_ATTR_COLOR:655if (value.empty()) {656return true;657} else {658return canParse<RGBColor>(value);659}660case SUMO_ATTR_REPEAT:661return canParse<int>(value) && (parse<int>(value) >= 0);662case SUMO_ATTR_CYCLETIME:663if (canParse<SUMOTime>(value)) {664return (parse<SUMOTime>(value) >= 0);665} else {666return false;667}668case SUMO_ATTR_PROB:669if (canParse<double>(value)) {670return (parse<double>(value) >= 0);671} else {672return false;673}674default:675return isCommonAttributeValid(key, value);676}677}678679680std::string681GNERoute::getPopUpID() const {682return getTagStr();683}684685686std::string687GNERoute::getHierarchyName() const {688return getTagStr() + ": " + getAttribute(SUMO_ATTR_ID) ;689}690691692std::string693GNERoute::isRouteValid(const std::vector<GNEEdge*>& edges) {694if (edges.size() == 0) {695// routes cannot be empty696return ("list of route edges cannot be empty");697} else if (edges.size() == 1) {698// routes with a single edge are valid, then return an empty string699return ("");700} else {701// iterate over edges to check that compounds a chain702auto it = edges.begin();703while (it != edges.end() - 1) {704const GNEEdge* currentEdge = *it;705const GNEEdge* nextEdge = *(it + 1);706// same consecutive edges aren't allowed707if (currentEdge->getID() == nextEdge->getID()) {708return ("consecutive duplicated edges (" + currentEdge->getID() + ") aren't allowed in a route");709}710// obtain outgoing edges of currentEdge711const std::vector<GNEEdge*>& outgoingEdges = currentEdge->getToJunction()->getGNEOutgoingEdges();712// check if nextEdge is in outgoingEdges713if (std::find(outgoingEdges.begin(), outgoingEdges.end(), nextEdge) == outgoingEdges.end()) {714return ("Edges '" + currentEdge->getID() + "' and '" + nextEdge->getID() + "' aren't consecutives");715}716it++;717}718// all edges consecutives, then return an empty string719return ("");720}721}722723GNEDemandElement*724GNERoute::copyRoute(const GNERoute* originalRoute) {725// get net and undoList726const auto net = originalRoute->getNet();727auto undoList = net->getViewNet()->getUndoList();728// generate new route ID729const std::string newRouteID = net->getAttributeCarriers()->generateDemandElementID(SUMO_TAG_ROUTE);730// create new route731GNERoute* newRoute = new GNERoute(newRouteID, originalRoute);732// add new route using undo-list733undoList->begin(originalRoute, TLF("copy % '%'", originalRoute->getTagStr(), newRouteID));734net->getViewNet()->getUndoList()->add(new GNEChange_DemandElement(newRoute, true), true);735undoList->end();736// return new route737return newRoute;738}739740// ===========================================================================741// private742// ===========================================================================743744void745GNERoute::drawRoutePartialLane(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,746const GNESegment* segment, const double offsetFront,747const GUIGeometry& geometry, const double exaggeration) const {748// get route width749const double routeWidth = (myTagProperty->getTag() == GNE_TAG_ROUTE_EMBEDDED) ? s.widthSettings.embeddedRouteWidth : s.widthSettings.routeWidth;750// push layer matrix751GLHelper::pushMatrix();752// Start with the drawing of the area traslating matrix to origin753glTranslated(0, 0, getType() + offsetFront);754// Set color755if (drawUsingSelectColor()) {756GLHelper::setColor(s.colorSettings.selectedRouteColor);757} else {758GLHelper::setColor(getColor());759}760// draw geometry761GUIGeometry::drawGeometry(d, geometry, routeWidth * exaggeration);762// check if we have to draw a red line to the next segment763if (segment->getNextLane()) {764// push draw matrix765GLHelper::pushMatrix();766// Set red color767GLHelper::setColor(RGBColor::RED);768// get firstPosition (last position of current lane shape)769const Position firstPosition = segment->getLane()->getLaneShape().back();770// get lastPosition (first position of next lane shape)771const Position arrivalPos = segment->getNextLane()->getLaneShape().front();772// draw box line773GLHelper::drawBoxLine(arrivalPos,774RAD2DEG(firstPosition.angleTo2D(arrivalPos)) - 90,775firstPosition.distanceTo2D(arrivalPos), .05);776// pop draw matrix777GLHelper::popMatrix();778}779// Pop layer matrix780GLHelper::popMatrix();781}782783784void785GNERoute::drawRoutePartialJunction(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,786const double offsetFront, const GUIGeometry& geometry, const double exaggeration) const {787const bool invalid = geometry.getShape().length() == 2;788// get route width789const double routeWidth = (myTagProperty->getTag() == GNE_TAG_ROUTE_EMBEDDED) ? s.widthSettings.embeddedRouteWidth : s.widthSettings.routeWidth;790// Add a draw matrix791GLHelper::pushMatrix();792// Start with the drawing of the area traslating matrix to origin793glTranslated(0, 0, getType() + offsetFront);794// Set color of the base795if (drawUsingSelectColor()) {796GLHelper::setColor(s.colorSettings.selectedAdditionalColor);797} else if (invalid) {798GLHelper::setColor(RGBColor::RED);799} else {800GLHelper::setColor(getColor());801}802// draw geometry803GUIGeometry::drawGeometry(d, geometry, routeWidth * exaggeration);804// Pop last matrix805GLHelper::popMatrix();806}807808809void810GNERoute::setAttribute(SumoXMLAttr key, const std::string& value) {811switch (key) {812case SUMO_ATTR_ID:813// update microsimID814setDemandElementID(value);815break;816case SUMO_ATTR_EDGES:817// replace parents818replaceParentEdges(value);819// compute route820computePathElement();821// update all parent and child demand elements822for (const auto& element : getParentDemandElements()) {823element->updateGeometry();824}825for (const auto& element : getChildDemandElements()) {826element->updateGeometry();827}828break;829case SUMO_ATTR_COLOR:830if (value.empty()) {831myColor = RGBColor::INVISIBLE;832} else {833myColor = parse<RGBColor>(value);834}835break;836case SUMO_ATTR_REPEAT:837if (value.empty()) {838myRepeat = myTagProperty->getDefaultIntValue(key);839} else {840myRepeat = parse<int>(value);841}842break;843case SUMO_ATTR_CYCLETIME:844if (value.empty()) {845myCycleTime = myTagProperty->getDefaultTimeValue(key);846} else {847myCycleTime = string2time(value);848}849break;850case SUMO_ATTR_PROB:851if (value.empty()) {852myProbability = myTagProperty->getDefaultDoubleValue(key);853} else {854myProbability = parse<double>(value);855}856break;857default:858setCommonAttribute(key, value);859break;860}861}862863864bool865GNERoute::checkCreatingVehicleOverRoute() const {866if (myTagProperty->getTag() != GNE_TAG_ROUTE_EMBEDDED) {867// this affect only to embedded routes868return true;869} else if (!myNet->getViewNet()->getEditModes().isCurrentSupermodeDemand()) {870// only in demand mode871return true;872} else if (myNet->getViewNet()->getEditModes().demandEditMode != DemandEditMode::DEMAND_VEHICLE) {873// only creating vehicles874return true;875} else {876// get current template AC877const auto templateAC = myNet->getViewParent()->getVehicleFrame()->getVehicleTagSelector()->getCurrentTemplateAC();878if (templateAC && templateAC->getTagProperty()->vehicleRoute()) {879// we're creating a vehicle over a route, then hidde all embedded routes880return false;881} else {882return true;883}884}885}886887888/****************************************************************************/889890891