Path: blob/main/src/netedit/elements/demand/GNEDemandElementPlan.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 GNEDemandElementPlan.cpp14/// @author Pablo Alvarez Lopez15/// @date Sep 202316///17// An auxiliar, asbtract class for plan elements18/****************************************************************************/1920#include <netedit/changes/GNEChange_Attribute.h>21#include <netedit/GNENet.h>22#include <netedit/GNESegment.h>23#include <utils/gui/div/GLHelper.h>24#include <utils/gui/div/GUIDesigns.h>25#include <utils/xml/NamespaceIDs.h>2627#include "GNEDemandElementPlan.h"28#include "GNERoute.h"2930// ===========================================================================31// static definitions32// ===========================================================================3334const double GNEDemandElementPlan::myArrivalPositionDiameter = SUMO_const_halfLaneWidth;3536// ===========================================================================37// GNEDemandElement method definitions38// ===========================================================================3940GNEDemandElementPlan::GNEDemandElementPlan(GNEDemandElement* planElement, const double departPosition, const double arrivalPosition) :41myMoveElementPlan(new GNEMoveElementPlan(planElement, myDepartPosition)),42myDepartPosition(departPosition),43myArrivalPosition(arrivalPosition),44myPlanElement(planElement) {45}464748void49GNEDemandElementPlan::writeLocationAttributes(OutputDevice& device) const {50const auto tagProperty = myPlanElement->getTagProperty();51// write attributes depending of parent elements52if (tagProperty->planConsecutiveEdges()) {53device.writeAttr(SUMO_ATTR_EDGES, myPlanElement->parseIDs(myPlanElement->getParentEdges()));54} else if (tagProperty->planRoute()) {55device.writeAttr(SUMO_ATTR_ROUTE, myPlanElement->getParentDemandElements().at(1)->getID());56} else if (tagProperty->planEdge()) {57device.writeAttr(SUMO_ATTR_EDGE, myPlanElement->getParentEdges().front()->getID());58} else if (tagProperty->planBusStop()) {59device.writeAttr(SUMO_ATTR_BUS_STOP, myPlanElement->getParentStoppingPlaces().front()->getID());60} else if (tagProperty->planTrainStop()) {61device.writeAttr(SUMO_ATTR_TRAIN_STOP, myPlanElement->getParentStoppingPlaces().front()->getID());62} else if (tagProperty->planContainerStop()) {63device.writeAttr(SUMO_ATTR_CONTAINER_STOP, myPlanElement->getParentStoppingPlaces().front()->getID());64} else if (tagProperty->planChargingStation()) {65device.writeAttr(SUMO_ATTR_CHARGING_STATION, myPlanElement->getParentStoppingPlaces().front()->getID());66} else if (tagProperty->planParkingArea()) {67device.writeAttr(SUMO_ATTR_PARKING_AREA, myPlanElement->getParentStoppingPlaces().front()->getID());68} else {69// write from attribute (if this is the first element)70if (myPlanElement->getParentDemandElements().at(0)->getPreviousChildDemandElement(myPlanElement) == nullptr) {71// check if write edge or junction72if (tagProperty->planFromEdge()) {73device.writeAttr(SUMO_ATTR_FROM, myPlanElement->getParentEdges().front()->getID());74} else if (tagProperty->planFromTAZ()) {75device.writeAttr(SUMO_ATTR_FROM_TAZ, myPlanElement->getParentTAZs().front()->getID());76} else if (tagProperty->planFromJunction()) {77device.writeAttr(SUMO_ATTR_FROM_JUNCTION, myPlanElement->getParentJunctions().front()->getID());78}79// origin stopping places are transformed into an intial stop stage (see writeOriginStop)80}81// continue writting to attribute82if (tagProperty->planToEdge()) {83device.writeAttr(SUMO_ATTR_TO, myPlanElement->getParentEdges().back()->getID());84} else if (tagProperty->planToJunction()) {85device.writeAttr(SUMO_ATTR_TO_JUNCTION, myPlanElement->getParentJunctions().back()->getID());86} else if (tagProperty->planToTAZ()) {87device.writeAttr(SUMO_ATTR_TO_TAZ, myPlanElement->getParentTAZs().back()->getID());88} else if (tagProperty->planToBusStop()) {89device.writeAttr(SUMO_ATTR_BUS_STOP, myPlanElement->getParentStoppingPlaces().back()->getID());90} else if (tagProperty->planToTrainStop()) {91device.writeAttr(SUMO_ATTR_TRAIN_STOP, myPlanElement->getParentStoppingPlaces().back()->getID());92} else if (tagProperty->planToContainerStop()) {93device.writeAttr(SUMO_ATTR_CONTAINER_STOP, myPlanElement->getParentStoppingPlaces().back()->getID());94} else if (tagProperty->planToChargingStation()) {95device.writeAttr(SUMO_ATTR_CHARGING_STATION, myPlanElement->getParentStoppingPlaces().back()->getID());96} else if (tagProperty->planToParkingArea()) {97device.writeAttr(SUMO_ATTR_PARKING_AREA, myPlanElement->getParentStoppingPlaces().back()->getID());98}99}100// check if write depart position101if (tagProperty->hasAttribute(SUMO_ATTR_DEPARTPOS) && (myDepartPosition > 0)) {102device.writeAttr(SUMO_ATTR_DEPARTPOS, myDepartPosition);103}104// check if write arrival position105if (tagProperty->hasAttribute(SUMO_ATTR_ARRIVALPOS) && (myArrivalPosition > 0)) {106device.writeAttr(SUMO_ATTR_ARRIVALPOS, myArrivalPosition);107}108// check if write end position109if (tagProperty->hasAttribute(SUMO_ATTR_ENDPOS)) {110device.writeAttr(SUMO_ATTR_ENDPOS, myArrivalPosition);111}112}113114115void116GNEDemandElementPlan::writeOriginStop(OutputDevice& device) const {117const auto tagProperty = myPlanElement->getTagProperty();118// write an extra stop element with duration 0 over a stopping place (if this is the first element)119if (tagProperty->planFromStoppingPlace()120&& myPlanElement->getParentDemandElements().at(0)->getPreviousChildDemandElement(myPlanElement) == nullptr) {121device.openTag(SUMO_TAG_STOP);122const std::string stopID = myPlanElement->getParentStoppingPlaces().front()->getID();123if (tagProperty->planFromBusStop()) {124device.writeAttr(SUMO_ATTR_BUS_STOP, stopID);125} else if (tagProperty->planFromTrainStop()) {126device.writeAttr(SUMO_ATTR_TRAIN_STOP, stopID);127} else if (tagProperty->planFromContainerStop()) {128device.writeAttr(SUMO_ATTR_CONTAINER_STOP, stopID);129} else if (tagProperty->planFromChargingStation()) {130device.writeAttr(SUMO_ATTR_CHARGING_STATION, stopID);131} else if (tagProperty->planFromParkingArea()) {132device.writeAttr(SUMO_ATTR_PARKING_AREA, stopID);133}134device.writeAttr(SUMO_ATTR_DURATION, 0);135device.closeTag();136}137}138139140GUIGLObjectPopupMenu*141GNEDemandElementPlan::getPlanPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {142// create popup143GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, myPlanElement);144// build common options145myPlanElement->buildPopUpMenuCommonOptions(ret, app, myPlanElement->myNet->getViewNet(), myPlanElement->getTagProperty()->getTag(), myPlanElement->isAttributeCarrierSelected());146GUIDesigns::buildFXMenuCommand(ret, ("Cursor position in view: " + toString(getPlanPositionInView().x()) + "," + toString(getPlanPositionInView().y())).c_str(), nullptr, nullptr, 0);147return ret;148}149150151GNELane*152GNEDemandElementPlan::getFirstPlanPathLane() const {153// get tag property154const auto tagProperty = myPlanElement->getTagProperty();155// get vclass156auto vClass = myPlanElement->getVClass();157// continue depending of parents158if (tagProperty->planRoute()) {159// route160return myPlanElement->getParentDemandElements().at(1)->getParentEdges().front()->getLaneByAllowedVClass(vClass);161} else if (tagProperty->planConsecutiveEdges() || tagProperty->planFromEdge() || tagProperty->planEdge()) {162// edges163return myPlanElement->getParentEdges().front()->getLaneByAllowedVClass(vClass);164} else if (tagProperty->planStoppingPlace() || tagProperty->planFromStoppingPlace() || tagProperty->planToStoppingPlace()) {165// additional166return myPlanElement->getParentStoppingPlaces().front()->getParentLanes().front();167} else {168// in other cases (TAZ, junctions, etc.) return null169return nullptr;170}171}172173174GNELane*175GNEDemandElementPlan::getLastPlanPathLane() const {176// get tag property177const auto tagProperty = myPlanElement->getTagProperty();178// get vclass179auto vClass = myPlanElement->getVClass();180// check parents181if (tagProperty->planRoute()) {182// route183return myPlanElement->getParentDemandElements().at(1)->getParentEdges().back()->getLaneByAllowedVClass(vClass);184} else if (tagProperty->planConsecutiveEdges() || tagProperty->planToEdge() || tagProperty->planEdge()) {185// edges186return myPlanElement->getParentEdges().back()->getLaneByAllowedVClass(vClass);187} else if (tagProperty->planStoppingPlace() || tagProperty->planFromStoppingPlace() || tagProperty->planToStoppingPlace()) {188// additional189return myPlanElement->getParentStoppingPlaces().back()->getParentLanes().front()->getParentEdge()->getLaneByAllowedVClass(vClass);190} else {191// in other cases (TAZ, junctions, etc.) return null192return nullptr;193}194}195196197void198GNEDemandElementPlan::computePlanPathElement() {199// get tag property200const auto tagProperty = myPlanElement->getTagProperty();201// get vClass202auto vClass = myPlanElement->getVClass();203// get path manager204auto pathManager = myPlanElement->getNet()->getDemandPathManager();205// continue depending of parents206if (tagProperty->planRoute()) {207// calculate consecutive path using route edges208pathManager->calculateConsecutivePathEdges(myPlanElement, vClass, myPlanElement->getParentDemandElements().at(1)->getParentEdges());209} else if (tagProperty->planConsecutiveEdges()) {210// calculate consecutive path using edges211pathManager->calculateConsecutivePathEdges(myPlanElement, vClass, myPlanElement->getParentEdges());212} else if (myPlanElement->myTagProperty->planFromJunction() && myPlanElement->myTagProperty->planToJunction()) {213// calculate path using junctions214pathManager->calculatePath(myPlanElement, vClass, myPlanElement->getParentJunctions().front(), myPlanElement->getParentJunctions().back());215} else if (myPlanElement->myTagProperty->planFromJunction()) {216// declare last lane217GNELane* lastLane = nullptr;218if (myPlanElement->myTagProperty->planStoppingPlace()) {219lastLane = myPlanElement->getParentStoppingPlaces().back()->getParentLanes().back();220} else if (myPlanElement->myTagProperty->planToStoppingPlace()) {221lastLane = myPlanElement->getParentStoppingPlaces().back()->getParentLanes().front();222} else if (myPlanElement->myTagProperty->planToEdge()) {223lastLane = myPlanElement->getParentEdges().back()->getLaneByAllowedVClass(vClass);224}225// calculate path226if (lastLane) {227pathManager->calculatePath(myPlanElement, vClass, myPlanElement->getParentJunctions().front(), lastLane);228}229} else if (myPlanElement->myTagProperty->planToJunction()) {230// declare first lane231GNELane* firstLane = nullptr;232if (myPlanElement->myTagProperty->planStoppingPlace()) {233firstLane = myPlanElement->getParentStoppingPlaces().front()->getParentLanes().back();234} else if (myPlanElement->myTagProperty->planFromStoppingPlace()) {235firstLane = myPlanElement->getParentStoppingPlaces().front()->getParentLanes().front();236} else if (myPlanElement->myTagProperty->planFromEdge()) {237firstLane = myPlanElement->getParentEdges().front()->getLaneByAllowedVClass(vClass);238}239// calculate path240if (firstLane) {241pathManager->calculatePath(myPlanElement, vClass, firstLane, myPlanElement->getParentJunctions().back());242}243} else {244// declare first edge245GNELane* firstLane = nullptr;246if (myPlanElement->myTagProperty->planFromEdge()) {247firstLane = myPlanElement->getParentEdges().front()->getLaneByAllowedVClass(vClass);248} else if (myPlanElement->myTagProperty->planStoppingPlace()) {249firstLane = myPlanElement->getParentStoppingPlaces().front()->getParentLanes().back();250} else if (myPlanElement->myTagProperty->planFromStoppingPlace()) {251firstLane = myPlanElement->getParentStoppingPlaces().front()->getParentLanes().front();252}253// declare last lane254GNELane* lastLane = nullptr;255if (myPlanElement->myTagProperty->planToEdge()) {256lastLane = myPlanElement->getParentEdges().back()->getLaneByAllowedVClass(vClass);257} else if (myPlanElement->myTagProperty->planStoppingPlace()) {258lastLane = myPlanElement->getParentStoppingPlaces().back()->getParentLanes().back();259} else if (myPlanElement->myTagProperty->planToStoppingPlace()) {260lastLane = myPlanElement->getParentStoppingPlaces().back()->getParentLanes().front();261}262if (firstLane && lastLane) {263pathManager->calculatePath(myPlanElement, vClass, firstLane, lastLane);264} else if (firstLane) {265pathManager->calculateConsecutivePathLanes(myPlanElement, {firstLane});266} else if (lastLane) {267pathManager->calculateConsecutivePathLanes(myPlanElement, {lastLane});268}269}270// update geometry271updatePlanGeometry();272}273274275void276GNEDemandElementPlan::updatePlanGeometry() {277// get tag property278const auto tagProperty = myPlanElement->getTagProperty();279// check if plan start or end in a TAZ (becase in this case has to be inserted in RTREE280if (tagProperty->planFromTAZ() || tagProperty->planToTAZ()) {281// declare first and last positions282Position firstPos = Position::INVALID;283Position lastPos = Position::INVALID;284// set first position285if (tagProperty->planFromEdge()) {286// from junction287firstPos = myPlanElement->getFirstPathLane()->getLaneShape().back();288} else if (tagProperty->planFromJunction()) {289// from junction290firstPos = myPlanElement->getParentJunctions().front()->getPositionInView();291} else if (tagProperty->planFromStoppingPlace()) {292// end of stoppingPlace lane shape293firstPos = myPlanElement->getParentStoppingPlaces().front()->getParentLanes().front()->getLaneShape().back();294} else if (tagProperty->planFromTAZ()) {295// from TAZ296if (myPlanElement->getParentTAZs().front()->getAttribute(SUMO_ATTR_CENTER).empty()) {297firstPos = myPlanElement->getParentTAZs().front()->getAttributePosition(GNE_ATTR_TAZ_CENTROID);298} else {299firstPos = myPlanElement->getParentTAZs().front()->getAttributePosition(SUMO_ATTR_CENTER);300}301}302// set last position303if (tagProperty->planToEdge()) {304// from junction305lastPos = myPlanElement->getLastPathLane()->getLaneShape().back();306} else if (tagProperty->planToJunction()) {307// from junction308lastPos = myPlanElement->getParentJunctions().back()->getPositionInView();309} else if (tagProperty->planToStoppingPlace()) {310// end of stoppingPlace lane shape311lastPos = myPlanElement->getParentStoppingPlaces().back()->getParentLanes().front()->getLaneShape().front();312} else if (tagProperty->planToTAZ()) {313// from TAZ314if (myPlanElement->getParentTAZs().back()->getAttribute(SUMO_ATTR_CENTER).empty()) {315lastPos = myPlanElement->getParentTAZs().back()->getAttributePosition(GNE_ATTR_TAZ_CENTROID);316} else {317lastPos = myPlanElement->getParentTAZs().back()->getAttributePosition(SUMO_ATTR_CENTER);318}319}320// use both position to calculate a line321if ((firstPos != Position::INVALID) && (lastPos != Position::INVALID)) {322myPlanElement->myDemandElementGeometry.updateGeometry({firstPos, lastPos});323} else {324myPlanElement->myDemandElementGeometry.clearGeometry();325}326}327// update centering boundary328updatePlanCenteringBoundary(true);329// update child demand elements330for (const auto& demandElement : myPlanElement->getChildDemandElements()) {331demandElement->updateGeometry();332}333}334335336Boundary337GNEDemandElementPlan::getPlanCenteringBoundary() const {338return myPlanBoundary;339}340341342void343GNEDemandElementPlan::updatePlanCenteringBoundary(const bool updateGrid) {344// remove additional from grid345if (myPlanBoundary.isInitialised() && updateGrid) {346myPlanElement->getNet()->removeGLObjectFromGrid(myPlanElement);347}348myPlanBoundary = myPlanElement->getDemandElementGeometry().getShape().getBoxBoundary();349// if this element is over route, add their boundary350if (myPlanElement->getParentDemandElements().size() > 1) {351myPlanBoundary.add(myPlanElement->getParentDemandElements().at(1)->getCenteringBoundary());352}353// add the combination of all parent edges's boundaries354for (const auto& edge : myPlanElement->getParentEdges()) {355myPlanBoundary.add(edge->getCenteringBoundary());356}357// add the combination of all parent edges's boundaries358for (const auto& junction : myPlanElement->getParentJunctions()) {359myPlanBoundary.add(junction->getCenteringBoundary());360}361// add the combination of all parent additional's boundaries (stoppingPlaces and TAZs)362for (const auto& additional : myPlanElement->getParentAdditionals()) {363if (additional->getTagProperty()->getTag() == SUMO_TAG_TAZ) {364if (additional->getAttribute(SUMO_ATTR_CENTER).empty()) {365myPlanBoundary.add(additional->getAttributePosition(GNE_ATTR_TAZ_CENTROID));366} else {367myPlanBoundary.add(additional->getAttributePosition(SUMO_ATTR_CENTER));368}369} else {370myPlanBoundary.add(additional->getCenteringBoundary());371}372}373// check if is valid374if (myPlanBoundary.isInitialised()) {375myPlanBoundary.grow(5);376}377// add additional into RTREE again378if (myPlanBoundary.isInitialised() && updateGrid) {379myPlanElement->getNet()->addGLObjectIntoGrid(myPlanElement);380}381}382383384Position385GNEDemandElementPlan::getPlanPositionInView() const {386// get tag property387const auto tagProperty = myPlanElement->getTagProperty();388// continue depending of parents389if (tagProperty->planRoute()) {390// route391return myPlanElement->getParentDemandElements().at(1)->getPositionInView();392} else if (tagProperty->isPlanStop()) {393return myPlanElement->getDemandElementGeometry().getShape().front();394} else if (tagProperty->planFromEdge() || tagProperty->planConsecutiveEdges() || tagProperty->planEdge()) {395// first edge396return myPlanElement->getParentEdges().front()->getPositionInView();397} else if (tagProperty->planFromJunction()) {398// first junction399return myPlanElement->getParentJunctions().front()->getPositionInView();400} else if (tagProperty->planStoppingPlace() || tagProperty->planFromStoppingPlace()) {401// first additional402return myPlanElement->getParentStoppingPlaces().front()->getPositionInView();403} else if (tagProperty->planFromTAZ()) {404if (myPlanElement->getParentTAZs().front()->getAttribute(SUMO_ATTR_CENTER).empty()) {405return myPlanElement->getParentTAZs().front()->getAttributePosition(GNE_ATTR_TAZ_CENTROID);406} else {407return myPlanElement->getParentTAZs().front()->getAttributePosition(SUMO_ATTR_CENTER);408}409} else {410// return parent position411return Position(0, 0);412}413}414415416std::string417GNEDemandElementPlan::getPlanAttribute(SumoXMLAttr key) const {418// continue depending of key419switch (key) {420// Common plan attributes421case SUMO_ATTR_ID:422case GNE_ATTR_PARENT:423return myPlanElement->getParentDemandElements().at(0)->getID();424case SUMO_ATTR_DEPARTPOS:425if (myDepartPosition < 0) {426return "";427} else {428return toString(myDepartPosition);429}430case SUMO_ATTR_ENDPOS:431case SUMO_ATTR_ARRIVALPOS:432if (myArrivalPosition < 0) {433return "";434} else {435return toString(myArrivalPosition);436}437// route438case SUMO_ATTR_ROUTE:439return myPlanElement->getParentDemandElements().at(1)->getID();440// edges441case SUMO_ATTR_EDGE:442case SUMO_ATTR_EDGES:443return myPlanElement->parseIDs(myPlanElement->getParentEdges());444// stoppingPlaces (single and back)445case SUMO_ATTR_BUS_STOP:446case SUMO_ATTR_TRAIN_STOP:447case SUMO_ATTR_CONTAINER_STOP:448case SUMO_ATTR_CHARGING_STATION:449case SUMO_ATTR_PARKING_AREA:450return myPlanElement->getParentStoppingPlaces().back()->getID();451// from elements452case SUMO_ATTR_FROM:453return myPlanElement->getParentEdges().front()->getID();454case SUMO_ATTR_FROM_JUNCTION:455return myPlanElement->getParentJunctions().front()->getID();456case SUMO_ATTR_FROM_TAZ:457return myPlanElement->getParentTAZs().front()->getID();458case GNE_ATTR_FROM_BUSSTOP:459case GNE_ATTR_FROM_TRAINSTOP:460case GNE_ATTR_FROM_CONTAINERSTOP:461case GNE_ATTR_FROM_CHARGINGSTATION:462case GNE_ATTR_FROM_PARKINGAREA:463return myPlanElement->getParentStoppingPlaces().front()->getID();464// to elements465case SUMO_ATTR_TO:466return myPlanElement->getParentEdges().back()->getID();467case SUMO_ATTR_TO_JUNCTION:468return myPlanElement->getParentJunctions().back()->getID();469case SUMO_ATTR_TO_TAZ:470return myPlanElement->getParentTAZs().back()->getID();471default:472return myPlanElement->getCommonAttribute(key);473}474}475476477double478GNEDemandElementPlan::getPlanAttributeDouble(SumoXMLAttr key) const {479// get tag property480const auto tagProperty = myPlanElement->getTagProperty();481// declare plan parent482const auto planParent = myPlanElement->getParentDemandElements().at(0);483// continue depending of key484switch (key) {485case GNE_ATTR_PLAN_GEOMETRY_STARTPOS: {486if (tagProperty->planStoppingPlace()) {487// use startpos of stoppingPlace parent (stops)488const auto factor = myPlanElement->getParentStoppingPlaces().front()->getParentLanes().front()->getLengthGeometryFactor();489return myPlanElement->getParentStoppingPlaces().front()->getAttributeDouble(SUMO_ATTR_STARTPOS) * factor;490} else if (tagProperty->planFromStoppingPlace()) {491// use end position of stoppingPlace parent (for plans that starts in stoppingPlaces)492const auto factor = myPlanElement->getParentStoppingPlaces().front()->getParentLanes().front()->getLengthGeometryFactor();493return myPlanElement->getParentStoppingPlaces().front()->getAttributeDouble(SUMO_ATTR_ENDPOS) * factor;494} else if (tagProperty->planFromTAZ()) {495return 0;496} else if (tagProperty->planFromJunction()) {497return -1;498} else {499// get previous plan element500const auto previousPlan = planParent->getPreviousChildDemandElement(myPlanElement);501// continue depending of previous plan502if (previousPlan) {503// use previous plan end position (normally the arrival position)504const auto posOverLane = previousPlan->getAttributeDouble(GNE_ATTR_PLAN_GEOMETRY_ENDPOS);505// if posOverLane is -1, means that previousPlan ends in the end of lane.506if (posOverLane == -1) {507// INVALID_DOUBLE will put the startPositio at the end of line508return INVALID_DOUBLE;509} else {510return previousPlan->getAttributeDouble(GNE_ATTR_PLAN_GEOMETRY_ENDPOS);511}512} else {513// use depart position defined in parent (person or container)514return planParent->getAttributeDouble(SUMO_ATTR_DEPARTPOS);515}516}517}518case GNE_ATTR_PLAN_GEOMETRY_ENDPOS:519// continue depending of parents520if (tagProperty->planStoppingPlace()) {521// use end position of the stoppingPlace (stops)522const auto factor = myPlanElement->getParentStoppingPlaces().back()->getParentLanes().front()->getLengthGeometryFactor();523return myPlanElement->getParentStoppingPlaces().back()->getAttributeDouble(SUMO_ATTR_ENDPOS) * factor;524} else if (tagProperty->planToStoppingPlace()) {525// use start position of the stoppingPlace (for elements that ends in stoppingPlaces)526const auto factor = myPlanElement->getParentStoppingPlaces().back()->getParentLanes().front()->getLengthGeometryFactor();527return myPlanElement->getParentStoppingPlaces().back()->getAttributeDouble(SUMO_ATTR_STARTPOS) * factor;528} else if (tagProperty->planToJunction() || tagProperty->planToTAZ()) {529// junctions and TAZs return always -1530return -1;531} else if ((tagProperty->isPlanStopPerson() || tagProperty->isPlanStopContainer()) && tagProperty->planEdge()) {532// elements that ends in stop always uses the end (arrival) position of the stops over edges533return myArrivalPosition;534} else {535// check if next plan is a stop over edge536const auto nextPlan = planParent->getNextChildDemandElement(myPlanElement);537if (nextPlan && (nextPlan->getTagProperty()->isPlanStopPerson() ||538nextPlan->getTagProperty()->isPlanStopContainer()) &&539nextPlan->getTagProperty()->planEdge()) {540// if next plan is an stop over stoppingPlaces, use ends of stoppingPlace541return nextPlan->getAttributeDouble(GNE_ATTR_PLAN_GEOMETRY_ENDPOS);542} else {543// use arrival position544return myArrivalPosition;545}546}547case SUMO_ATTR_DEPARTPOS:548return myDepartPosition;549case SUMO_ATTR_ENDPOS:550case SUMO_ATTR_ARRIVALPOS:551return myArrivalPosition;552default:553throw InvalidArgument(myPlanElement->getTagStr() + " doesn't have a doubleattribute of type '" + toString(key) + "'");554}555}556557558Position559GNEDemandElementPlan::getPlanAttributePosition(SumoXMLAttr key) const {560// get tag property561const auto tagProperty = myPlanElement->getTagProperty();562// declare plan parent563const auto planParent = myPlanElement->getParentDemandElements().at(0);564// continue depending of key565switch (key) {566case GNE_ATTR_PLAN_GEOMETRY_STARTPOS: {567// get previous plan568const auto previousPlan = planParent->getPreviousChildDemandElement(myPlanElement);569if (previousPlan && previousPlan->getTagProperty()->isPlanStop() && previousPlan->getTagProperty()->planStoppingPlace()) {570return previousPlan->getParentStoppingPlaces().front()->getAdditionalGeometry().getShape().back();571}572// continue depending of from element573if (tagProperty->planStoppingPlace()) {574return myPlanElement->getParentStoppingPlaces().front()->getAdditionalGeometry().getShape().front();575} else if (tagProperty->planFromStoppingPlace()) {576return myPlanElement->getParentStoppingPlaces().front()->getAdditionalGeometry().getShape().back();577} else if (tagProperty->planFromJunction()) {578// junction view position579return myPlanElement->getParentJunctions().front()->getPositionInView();580} else if (tagProperty->planFromTAZ()) {581if (myPlanElement->getParentTAZs().front()->getAttribute(SUMO_ATTR_CENTER).empty()) {582return myPlanElement->getParentTAZs().front()->getAttributePosition(GNE_ATTR_TAZ_CENTROID);583} else {584return myPlanElement->getParentTAZs().front()->getAttributePosition(SUMO_ATTR_CENTER);585}586} else if (tagProperty->planConsecutiveEdges() || tagProperty->planRoute() || tagProperty->planFromEdge()) {587// get first path lane588const auto firstLane = myPlanElement->getFirstPathLane();589// check if first lane exists590if (firstLane == nullptr) {591return Position::INVALID;592}593// declare lane position594double lanePosition = 0;595// continue depending of conditions596if (previousPlan) {597// use previous geometry end position598lanePosition = previousPlan->getAttributeDouble(GNE_ATTR_PLAN_GEOMETRY_ENDPOS);599} else {600// use departPos defined in planParent601lanePosition = planParent->getAttributeDouble(SUMO_ATTR_DEPARTPOS);602}603// get lane shape604const auto& laneShape = firstLane->getLaneShape();605// continue depending of lane position606if (lanePosition <= 0) {607return laneShape.front();608} else if (lanePosition >= laneShape.length2D()) {609return laneShape.back();610} else {611return laneShape.positionAtOffset2D(lanePosition);612}613} else {614return Position::INVALID;615}616}617case GNE_ATTR_PLAN_GEOMETRY_ENDPOS: {618// check parents619if (tagProperty->planToJunction()) {620// junctions621return myPlanElement->getParentJunctions().back()->getPositionInView();622} else if (tagProperty->planStoppingPlace()) {623// get additional back shape (stops)624return myPlanElement->getParentStoppingPlaces().back()->getAdditionalGeometry().getShape().back();625} else if (tagProperty->planToStoppingPlace()) {626// get additional front shape627return myPlanElement->getParentStoppingPlaces().back()->getAdditionalGeometry().getShape().front();628} else if (tagProperty->planToTAZ()) {629// taz630if (myPlanElement->getParentTAZs().back()->getAttribute(SUMO_ATTR_CENTER).empty()) {631return myPlanElement->getParentTAZs().back()->getAttributePosition(GNE_ATTR_TAZ_CENTROID);632} else {633return myPlanElement->getParentTAZs().back()->getAttributePosition(SUMO_ATTR_CENTER);634}635} else if (tagProperty->planConsecutiveEdges() || tagProperty->planRoute() || tagProperty->planFromEdge()) {636// get next plan637const auto nextPlan = planParent->getNextChildDemandElement(myPlanElement);638// if next plan exist, then use their first lane (needed to maintain connectivity with rides)639const auto lastLane = nextPlan ? nextPlan->getFirstPathLane() : myPlanElement->getLastPathLane();640// check if last lane exists641if (lastLane == nullptr) {642return Position::INVALID;643}644// get lane shape645const auto& laneShape = lastLane->getLaneShape();646// continue depending of arrival position647if (nextPlan && nextPlan->getTagProperty()->isPlanStop()) {648return nextPlan->getAttributePosition(GNE_ATTR_PLAN_GEOMETRY_ENDPOS);649} else if (myArrivalPosition == 0) {650return laneShape.front();651} else if ((myArrivalPosition == -1) || (myArrivalPosition >= laneShape.length2D())) {652return laneShape.back();653} else {654return laneShape.positionAtOffset2D(myArrivalPosition);655}656} else {657return Position::INVALID;658}659}660default:661throw InvalidArgument(myPlanElement->getTagStr() + " doesn't have a position attribute of type '" + toString(key) + "'");662}663}664665666void667GNEDemandElementPlan::setPlanAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {668// continue depending of key669switch (key) {670// common attributes671case SUMO_ATTR_DEPARTPOS:672case SUMO_ATTR_ARRIVALPOS:673case SUMO_ATTR_ENDPOS:674case GNE_ATTR_PARENT:675GNEChange_Attribute::changeAttribute(myPlanElement, key, value, undoList);676break;677default:678myPlanElement->setCommonAttribute(key, value, undoList);679break;680}681}682683684bool685GNEDemandElementPlan::isPlanValid(SumoXMLAttr key, const std::string& value) {686// continue depending of key687switch (key) {688// common attributes689case GNE_ATTR_PARENT:690return false;691case SUMO_ATTR_DEPARTPOS:692case SUMO_ATTR_ARRIVALPOS:693if (value.empty()) {694return true;695} else if (GNEAttributeCarrier::canParse<double>(value)) {696return GNEAttributeCarrier::parse<double>(value) >= 0;697} else {698return false;699}700case SUMO_ATTR_ENDPOS:701return GNEAttributeCarrier::canParse<double>(value);702default:703return myPlanElement->isCommonAttributeValid(key, value);704}705}706707708bool709GNEDemandElementPlan::isPlanAttributeEnabled(SumoXMLAttr key) const {710switch (key) {711// edges712case SUMO_ATTR_EDGES:713// edge714case SUMO_ATTR_EDGE:715// route716case SUMO_ATTR_ROUTE:717// from718case SUMO_ATTR_FROM:719case SUMO_ATTR_FROM_JUNCTION:720case SUMO_ATTR_FROM_TAZ:721case GNE_ATTR_FROM_BUSSTOP:722case GNE_ATTR_FROM_TRAINSTOP:723case GNE_ATTR_FROM_CONTAINERSTOP:724case GNE_ATTR_FROM_CHARGINGSTATION:725case GNE_ATTR_FROM_PARKINGAREA:726// to727case SUMO_ATTR_TO:728case SUMO_ATTR_TO_JUNCTION:729case SUMO_ATTR_TO_TAZ:730case SUMO_ATTR_BUS_STOP:731case SUMO_ATTR_TRAIN_STOP:732case SUMO_ATTR_CONTAINER_STOP:733case SUMO_ATTR_CHARGING_STATION:734case SUMO_ATTR_PARKING_AREA:735// depart pos (temporal, probably will be removed)736case SUMO_ATTR_DEPARTPOS:737return false;738default:739return true;740}741}742743744void745GNEDemandElementPlan::setPlanAttribute(SumoXMLAttr key, const std::string& value) {746bool recompute = false;747switch (key) {748// from-to attributes (needed if we're replacing junction by geometry points and similar operations)749case SUMO_ATTR_FROM:750myPlanElement->replaceFirstParentEdge(value);751recompute = true;752break;753case SUMO_ATTR_TO:754myPlanElement->replaceLastParentEdge(value);755recompute = true;756break;757// Common plan attributes758case GNE_ATTR_PARENT:759replacePlanParent(value);760break;761case SUMO_ATTR_DEPARTPOS:762if (value.empty()) {763myDepartPosition = -1;764} else {765myDepartPosition = GNEAttributeCarrier::parse<double>(value);766}767recompute = true;768break;769case SUMO_ATTR_ENDPOS:770case SUMO_ATTR_ARRIVALPOS:771if (value.empty()) {772myArrivalPosition = -1;773} else {774myArrivalPosition = GNEAttributeCarrier::parse<double>(value);775}776recompute = true;777break;778default:779myPlanElement->setCommonAttribute(key, value);780break;781}782// check if compute geometry and path783if (recompute && !myPlanElement->isTemplate()) {784myPlanElement->updateGeometry();785myPlanElement->computePathElement();786}787}788789790std::string791GNEDemandElementPlan::getPlanHierarchyName() const {792// get tag property793const auto tagProperty = myPlanElement->getTagProperty();794// declare result795std::string result;796// clear result797int index = 0;798while (tagProperty->getTagStr().at(index) != ':') {799result.push_back(tagProperty->getTagStr().at(index));800index++;801}802result += ": ";803// continue depending of attributes804if (tagProperty->planConsecutiveEdges()) {805// edges806return result + myPlanElement->getParentEdges().front()->getID() + " ... " + myPlanElement->getParentEdges().back()->getID();807} else if (tagProperty->planRoute()) {808// route809return result + myPlanElement->getParentDemandElements().at(1)->getID();810} else if (tagProperty->planEdge()) {811// edge812return result + myPlanElement->getParentEdges().front()->getID();813} else if (tagProperty->planStoppingPlace()) {814// stoppingPlace815return myPlanElement->getParentStoppingPlaces().front()->getID();816} else {817// stoppingPlace818if (tagProperty->planFromStoppingPlace()) {819result += myPlanElement->getParentStoppingPlaces().front()->getID();820}821// TAZ822if (tagProperty->planFromTAZ()) {823result += myPlanElement->getParentTAZs().front()->getID();824}825// junction826if (tagProperty->planFromJunction()) {827result += myPlanElement->getParentJunctions().front()->getID();828}829// edge830if (tagProperty->planFromEdge()) {831result += myPlanElement->getParentEdges().front()->getID();832}833// arrow834result += " -> ";835// stoppingPlace836if (tagProperty->planToStoppingPlace()) {837result += myPlanElement->getParentStoppingPlaces().back()->getID();838}839// TAZ840if (tagProperty->planToTAZ()) {841result += myPlanElement->getParentTAZs().back()->getID();842}843// junction844if (tagProperty->planToJunction()) {845result += myPlanElement->getParentJunctions().back()->getID();846}847// edge848if (tagProperty->planToEdge()) {849result += myPlanElement->getParentEdges().back()->getID();850}851return result;852}853}854855856bool857GNEDemandElementPlan::checkDrawPersonPlan() const {858const auto viewNet = myPlanElement->getNet()->getViewNet();859const auto& inspectedElements = viewNet->getInspectedElements();860// check conditions861if (viewNet->getEditModes().isCurrentSupermodeNetwork() &&862viewNet->getNetworkViewOptions().showDemandElements() &&863viewNet->getDemandViewOptions().showAllPersonPlans()) {864// show all person plans in network mode865return true;866} else if (viewNet->getEditModes().isCurrentSupermodeDemand() &&867viewNet->getDemandViewOptions().showAllPersonPlans()) {868// show all person plans869return true;870} else if (viewNet->getEditModes().isCurrentSupermodeDemand() && myPlanElement->isAttributeCarrierSelected()) {871// show selected872return true;873} else if (inspectedElements.isACInspected(myPlanElement->getParentDemandElements().front())) {874// person parent is inspected875return true;876} else if (viewNet->getDemandViewOptions().getLockedPerson() == myPlanElement->getParentDemandElements().front()) {877// person parent is locked878return true;879} else {880// check if parent881if (inspectedElements.getFirstAC() && inspectedElements.getFirstAC()->getTagProperty()->isPlanPerson() &&882(inspectedElements.getFirstAC()->getAttribute(GNE_ATTR_PARENT) == myPlanElement->getAttribute(GNE_ATTR_PARENT))) {883// common person parent884return true;885} else {886// all conditions are false887return false;888}889}890}891892893bool894GNEDemandElementPlan::checkDrawContainerPlan() const {895const auto viewNet = myPlanElement->getNet()->getViewNet();896const auto& inspectedElements = viewNet->getInspectedElements();897// check conditions898if (viewNet->getEditModes().isCurrentSupermodeNetwork() &&899viewNet->getNetworkViewOptions().showDemandElements() &&900viewNet->getDemandViewOptions().showAllContainerPlans()) {901// show all container plans in network mode902return true;903} else if (viewNet->getEditModes().isCurrentSupermodeDemand() &&904viewNet->getDemandViewOptions().showAllContainerPlans()) {905// show all container plans906return true;907} else if (viewNet->getEditModes().isCurrentSupermodeDemand() && myPlanElement->isAttributeCarrierSelected()) {908// show selected909return true;910} else if (inspectedElements.isACInspected(myPlanElement->getParentDemandElements().front())) {911// container parent is inspected912return true;913} else if (viewNet->getDemandViewOptions().getLockedContainer() == myPlanElement->getParentDemandElements().front()) {914// container parent is locked915return true;916} else {917// check if parent is inspected918if (inspectedElements.getFirstAC() && inspectedElements.getFirstAC()->getTagProperty()->isPlanContainer() &&919(inspectedElements.getFirstAC()->getAttribute(GNE_ATTR_PARENT) == myPlanElement->getAttribute(GNE_ATTR_PARENT))) {920// common container parent921return true;922} else {923// all conditions are false924return false;925}926}927}928929930void931GNEDemandElementPlan::drawPlanGL(const bool drawPlan, const GUIVisualizationSettings& s, const RGBColor& planColor, const RGBColor& planSelectedColor) const {932const auto viewNet = myPlanElement->getNet()->getViewNet();933const auto& inspectedElements = viewNet->getInspectedElements();934// get plan parent935const GNEDemandElement* planParent = myPlanElement->getParentDemandElements().front();936// get tag property937const auto tagProperty = myPlanElement->getTagProperty();938// get plan geometry939auto& planGeometry = myPlanElement->myDemandElementGeometry;940// draw relations between TAZs941if (drawPlan && (planGeometry.getShape().size() > 0)) {942// draw boundary943if (s.drawBoundaries) {944GLHelper::drawBoundary(s, getPlanCenteringBoundary());945}946// get detail level947const auto d = s.getDetailLevel(1);948// check if draw with double width949const bool drawHalfWidth = ((inspectedElements.getFirstAC() != myPlanElement) && (inspectedElements.getFirstAC() != planParent) && !gViewObjectsHandler.isObjectSelected(myPlanElement));950// calculate path width951double pathWidth = s.widthSettings.walkWidth;952if (tagProperty->isPlanRide()) {953pathWidth = s.widthSettings.rideWidth;954} else if (tagProperty->isPlanPersonTrip()) {955pathWidth = s.widthSettings.personTripWidth;956}957// draw geometry only if we'rent in drawForObjectUnderCursor mode958if ((tagProperty->isPlanPerson() && s.checkDrawPerson(d, myPlanElement->isAttributeCarrierSelected())) ||959(tagProperty->isPlanContainer() && s.checkDrawContainer(d, myPlanElement->isAttributeCarrierSelected()))) {960// push matrix961GLHelper::pushMatrix();962// translate to front963myPlanElement->drawInLayer(GLO_TAZ + 1);964// set color965GLHelper::setColor(myPlanElement->drawUsingSelectColor() ? planSelectedColor : planColor);966// draw line967GUIGeometry::drawGeometry(d, planGeometry, pathWidth * (drawHalfWidth ? 1 : 2));968if (drawHalfWidth) {969GLHelper::drawTriangleAtEnd(planGeometry.getShape().front(), planGeometry.getShape().back(), 0.5, 0.5, 0.5);970} else {971GLHelper::drawTriangleAtEnd(planGeometry.getShape().front(), planGeometry.getShape().back(), 1, 1, 1);972}973// pop matrix974GLHelper::popMatrix();975// draw dotted contour976myPlanContour.drawDottedContours(s, d, myPlanElement, s.dottedContourSettings.segmentWidth, true);977}978// calculate contour and draw dotted geometry979myPlanContour.calculateContourExtrudedShape(s, d, myPlanElement, planGeometry.getShape(), myPlanElement->getType(), pathWidth * 2,9801, true, true, 0, nullptr, nullptr);981// calculate contour for end982myPlanContourEnd.calculateContourCircleShape(s, d, myPlanElement, planGeometry.getShape().back(), 1, myPlanElement->getType(), 1, nullptr);983}984// check if draw plan parent985if (planParent->getPreviousChildDemandElement(myPlanElement) == nullptr) {986planParent->drawGL(s);987}988}989990991void992GNEDemandElementPlan::drawPlanLanePartial(const bool drawPlan, const GUIVisualizationSettings& s, const GNESegment* segment,993const double offsetFront, const double planWidth, const RGBColor& planColor, const RGBColor& planSelectedColor) const {994const auto viewNet = myPlanElement->getNet()->getViewNet();995const auto& inspectedElements = viewNet->getInspectedElements();996// get tag property997const auto tagProperty = myPlanElement->getTagProperty();998// get plan parent999const GNEDemandElement* planParent = myPlanElement->getParentDemandElements().front();1000// check if draw plan element can be drawn1001if (drawPlan && segment->getLane() && myPlanElement->getNet()->getDemandPathManager()->getPathDraw()->checkDrawPathGeometry(s, segment->getLane(), tagProperty->getTag(), true)) {1002// draw boundary1003if (tagProperty->isPlacedInRTree() && s.drawBoundaries) {1004GLHelper::drawBoundary(s, getPlanCenteringBoundary());1005}1006// get detail level1007const auto d = s.getDetailLevel(1);1008// declare path geometry1009GUIGeometry planGeometry;1010// update pathGeometry depending of first and last segment1011if (segment->isFirstSegment() && segment->isLastSegment()) {1012if (tagProperty->planFromTAZ()) {1013planGeometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),1014getPlanAttributeDouble(GNE_ATTR_PLAN_GEOMETRY_STARTPOS),1015Position::INVALID,1016getPlanAttributeDouble(GNE_ATTR_PLAN_GEOMETRY_ENDPOS),1017getPlanAttributePosition(GNE_ATTR_PLAN_GEOMETRY_ENDPOS));1018} else if (tagProperty->planToTAZ()) {1019planGeometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),1020getPlanAttributeDouble(GNE_ATTR_PLAN_GEOMETRY_STARTPOS),1021getPlanAttributePosition(GNE_ATTR_PLAN_GEOMETRY_STARTPOS),1022getPlanAttributeDouble(GNE_ATTR_PLAN_GEOMETRY_ENDPOS),1023Position::INVALID);1024} else {1025planGeometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),1026getPlanAttributeDouble(GNE_ATTR_PLAN_GEOMETRY_STARTPOS),1027getPlanAttributePosition(GNE_ATTR_PLAN_GEOMETRY_STARTPOS),1028getPlanAttributeDouble(GNE_ATTR_PLAN_GEOMETRY_ENDPOS),1029getPlanAttributePosition(GNE_ATTR_PLAN_GEOMETRY_ENDPOS));1030}1031} else if (segment->isFirstSegment()) {1032planGeometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),1033getPlanAttributeDouble(GNE_ATTR_PLAN_GEOMETRY_STARTPOS),1034getPlanAttributePosition(GNE_ATTR_PLAN_GEOMETRY_STARTPOS),1035-1,1036Position::INVALID);1037} else if (segment->isLastSegment()) {1038planGeometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),1039-1,1040Position::INVALID,1041getPlanAttributeDouble(GNE_ATTR_PLAN_GEOMETRY_ENDPOS),1042getPlanAttributePosition(GNE_ATTR_PLAN_GEOMETRY_ENDPOS));1043} else {1044planGeometry = segment->getLane()->getLaneGeometry();1045}1046// calculate path width double1047const double drawingWidth = s.addSize.getExaggeration(s, segment->getLane()) * planWidth * 2;1048// check if draw with double width1049const bool drawHalfWidth = ((inspectedElements.getFirstAC() != myPlanElement) && (inspectedElements.getFirstAC() != planParent) && !gViewObjectsHandler.isObjectSelected(myPlanElement));1050// get end pos radius1051const double endPosRadius = getEndPosRadius(s, segment, drawHalfWidth);1052// draw geometry only if we'rent in drawForObjectUnderCursor mode1053if ((tagProperty->isPlanPerson() && s.checkDrawPerson(d, myPlanElement->isAttributeCarrierSelected())) ||1054(tagProperty->isPlanContainer() && s.checkDrawContainer(d, myPlanElement->isAttributeCarrierSelected()))) {1055// Add a draw matrix1056GLHelper::pushMatrix();1057// Start with the drawing of the area traslating matrix to origin1058myPlanElement->drawInLayer(myPlanElement->getType(), offsetFront);1059// Set color1060GLHelper::setColor(myPlanElement->drawUsingSelectColor() ? planSelectedColor : planColor);1061// draw geometry depending of drawWithDoubleWidth1062GUIGeometry::drawGeometry(d, planGeometry, drawingWidth * (drawHalfWidth ? 0.5 : 1));1063// draw red arrows1064drawFromArrow(s, segment->getLane(), segment);1065drawToArrow(s, segment->getLane(), segment);1066// Pop last matrix1067GLHelper::popMatrix();1068// Draw name if isn't being drawn for selecting1069myPlanElement->drawName(myPlanElement->getCenteringBoundary().getCenter(), s.scale, s.addName);1070// draw dotted contour1071segment->getContour()->drawDottedContours(s, d, myPlanElement, s.dottedContourSettings.segmentWidth, true);1072// draw TAZ Center dotted contour1073myPlanContourEnd.drawDottedContours(s, d, myPlanElement, s.dottedContourSettings.segmentWidth, true);1074}1075// declare trim geometry to draw1076const auto& shape = (segment->isFirstSegment() || segment->isLastSegment()) ? planGeometry.getShape() : segment->getLane()->getLaneShape();1077// calculate contour and draw dotted geometry (always with double width)1078if (segment->isFirstSegment()) {1079segment->getContour()->calculateContourExtrudedShape(s, d, myPlanElement, shape, myPlanElement->getType(), drawingWidth, 1, true, false,10800, segment, segment->getLane()->getParentEdge());1081} else if (segment->isLastSegment()) {1082segment->getContour()->calculateContourExtrudedShape(s, d, myPlanElement, shape, myPlanElement->getType(), drawingWidth, 1, false, false,10830, segment, segment->getLane()->getParentEdge());1084// calculate contour for end1085myPlanContourEnd.calculateContourCircleShape(s, d, myPlanElement, getPlanAttributePosition(GNE_ATTR_PLAN_GEOMETRY_ENDPOS), 2 * endPosRadius,1086myPlanElement->getType(), 1, segment->getLane());1087} else {1088segment->getContour()->calculateContourExtrudedShape(s, d, myPlanElement, shape, myPlanElement->getType(), drawingWidth, 1, false, false, 0,1089segment, segment->getLane()->getParentEdge());1090}1091// check if add this path element to redraw buffer1092if (!gViewObjectsHandler.isPathElementMarkForRedraw(myPlanElement) && segment->getContour()->checkDrawPathContour(s, d, myPlanElement)) {1093gViewObjectsHandler.addToRedrawPathElements(myPlanElement);1094}1095}1096// check if draw plan parent1097if (planParent->getPreviousChildDemandElement(myPlanElement) == nullptr) {1098planParent->drawGL(s);1099}1100}110111021103void1104GNEDemandElementPlan::drawPlanJunctionPartial(const bool drawPlan, const GUIVisualizationSettings& s, const GNESegment* segment,1105const double offsetFront, const double planWidth, const RGBColor& planColor, const RGBColor& planSelectedColor) const {1106const auto viewNet = myPlanElement->getNet()->getViewNet();1107const auto& inspectedElements = viewNet->getInspectedElements();1108// get tag property1109const auto tagProperty = myPlanElement->getTagProperty();1110// get plan parent1111const GNEDemandElement* planParent = myPlanElement->getParentDemandElements().front();1112// check if draw plan elements can be drawn1113if (drawPlan && myPlanElement->getNet()->getDemandPathManager()->getPathDraw()->checkDrawPathGeometry(s, segment, tagProperty->getTag(), false)) {1114// draw boundary1115if (tagProperty->isPlacedInRTree() && s.drawBoundaries) {1116GLHelper::drawBoundary(s, getPlanCenteringBoundary());1117}1118// get detail level1119const auto d = s.getDetailLevel(1);1120// calculate path width double1121const double pathWidthDouble = s.addSize.getExaggeration(s, segment->getLane()) * planWidth * 2;1122// check if draw with double width1123const bool drawWithDoubleWidth = ((inspectedElements.getFirstAC() == myPlanElement) || (inspectedElements.getFirstAC() == planParent) || gViewObjectsHandler.isObjectSelected(myPlanElement));1124// draw geometry only if we'rent in drawForObjectUnderCursor mode1125if (!s.drawForViewObjectsHandler) {1126// push a draw matrix1127GLHelper::pushMatrix();1128// Start with the drawing of the area traslating matrix to origin1129myPlanElement->drawInLayer(myPlanElement->getType(), offsetFront);1130// Set plan color1131GLHelper::setColor(myPlanElement->drawUsingSelectColor() ? planSelectedColor : planColor);1132// check if draw lane2lane connection or a red line1133if (segment->getPreviousLane() && segment->getNextLane()) {1134if (segment->getPreviousLane()->getLane2laneConnections().exist(segment->getNextLane())) {1135// obtain lane2lane geometry1136const GUIGeometry& lane2laneGeometry = segment->getPreviousLane()->getLane2laneConnections().getLane2laneGeometry(segment->getNextLane());1137// draw lane2lane1138GUIGeometry::drawGeometry(d, lane2laneGeometry, pathWidthDouble * (drawWithDoubleWidth ? 1 : 0.5));1139} else {1140// Set invalid plan color1141GLHelper::setColor(RGBColor::RED);1142// draw line between end of first shape and first position of second shape1143GLHelper::drawBoxLines({segment->getPreviousLane()->getLaneShape().back(), segment->getNextLane()->getLaneShape().front()}, (0.5 * pathWidthDouble * (drawWithDoubleWidth ? 1 : 0.5)));1144}1145} else if (segment->getPreviousLane()) {1146// draw line between center of junction and last lane shape1147GLHelper::drawBoxLines({segment->getPreviousLane()->getLaneShape().back(), myPlanElement->getParentJunctions().back()->getPositionInView()}, pathWidthDouble * (drawWithDoubleWidth ? 1 : 0.5));1148} else if (segment->getNextLane()) {1149// draw line between center of junction and first lane shape1150GLHelper::drawBoxLines({myPlanElement->getParentJunctions().front()->getPositionInView(), segment->getNextLane()->getLaneShape().front()}, pathWidthDouble * (drawWithDoubleWidth ? 1 : 0.5));1151}1152// Pop last matrix1153GLHelper::popMatrix();1154// draw lock icon1155GNEViewNetHelper::LockIcon::drawLockIcon(d, myPlanElement, myPlanElement->getType(), myPlanElement->getPositionInView(), 0.5);1156// draw dotted contour1157segment->getContour()->drawDottedContours(s, d, myPlanElement, s.dottedContourSettings.segmentWidth, true);1158}1159// check if shape dotted contour has to be drawn1160if (segment->getPreviousLane() && segment->getNextLane()) {1161if (segment->getPreviousLane()->getLane2laneConnections().exist(segment->getNextLane())) {1162// get shape1163const auto& shape = segment->getPreviousLane()->getLane2laneConnections().getLane2laneGeometry(segment->getNextLane()).getShape();1164// calculate contour and draw dotted geometry (always with double width)1165segment->getContour()->calculateContourExtrudedShape(s, d, myPlanElement, shape, myPlanElement->getType(), pathWidthDouble, 1, false, false, 0, segment, segment->getJunction());1166}1167} else if (segment->getPreviousLane()) {1168segment->getContour()->calculateContourExtrudedShape(s, d, myPlanElement, {segment->getPreviousLane()->getLaneShape().back(), myPlanElement->getParentJunctions().back()->getPositionInView()},1169myPlanElement->getType(), pathWidthDouble, 1, false, true, 0, segment, segment->getJunction());1170} else if (segment->getNextLane()) {1171segment->getContour()->calculateContourExtrudedShape(s, d, myPlanElement, {myPlanElement->getParentJunctions().front()->getPositionInView(), segment->getNextLane()->getLaneShape().front()},1172myPlanElement->getType(), pathWidthDouble, 1, true, false, 0, segment, segment->getJunction());1173}1174// check if add this path element to redraw buffer1175if (!gViewObjectsHandler.isPathElementMarkForRedraw(myPlanElement) && segment->getContour()->checkDrawPathContour(s, d, myPlanElement)) {1176gViewObjectsHandler.addToRedrawPathElements(myPlanElement);1177}1178}1179// check if draw plan parent1180if (planParent->getPreviousChildDemandElement(myPlanElement) == nullptr) {1181planParent->drawGL(s);1182}1183}118411851186GNEDemandElement::Problem1187GNEDemandElementPlan::isPlanPersonValid() const {1188// get previous plan1189const auto previousPlan = myPlanElement->getParentDemandElements().at(0)->getPreviousChildDemandElement(myPlanElement);1190if (previousPlan) {1191// get previous lane1192const auto previousLastLane = previousPlan->getLastPathLane();1193// get first lane1194const auto firstLane = myPlanElement->getFirstPathLane();1195// compare edges1196if (previousLastLane && firstLane && (previousLastLane->getParentEdge() != firstLane->getParentEdge())) {1197return GNEDemandElement::Problem::DISCONNECTED_PLAN;1198}1199// in the future, check more elements1200}1201// get next child1202const auto nextPlan = myPlanElement->getParentDemandElements().at(0)->getNextChildDemandElement(myPlanElement);1203if (nextPlan) {1204// get previous lane1205const auto nextFirstLane = nextPlan->getFirstPathLane();1206// get first lane1207const auto lastLane = myPlanElement->getLastPathLane();1208// compare edges1209if (nextFirstLane && lastLane && (nextFirstLane->getParentEdge() != lastLane->getParentEdge())) {1210return GNEDemandElement::Problem::DISCONNECTED_PLAN;1211}1212// in the future, check more elements1213}1214// all ok, then return true1215return GNEDemandElement::Problem::OK;1216}121712181219std::string1220GNEDemandElementPlan::getPersonPlanProblem() const {1221// get previous plan1222const auto previousPlan = myPlanElement->getParentDemandElements().at(0)->getPreviousChildDemandElement(myPlanElement);1223if (previousPlan) {1224// get previous lane1225const auto previousLastLane = previousPlan->getLastPathLane();1226// get first lane1227const auto firstLane = myPlanElement->getLastPathLane();1228// compare edges1229if (previousLastLane && firstLane && (previousLastLane->getParentEdge() != firstLane->getParentEdge())) {1230return TLF("Edge '%' is not consecutive with edge '%'", previousLastLane->getParentEdge()->getID(), firstLane->getParentEdge()->getID());1231}1232// in the future, check more elements1233}1234// get next child1235const auto nextPlan = myPlanElement->getParentDemandElements().at(0)->getNextChildDemandElement(myPlanElement);1236if (nextPlan) {1237// get previous lane1238const auto nextFirstLane = nextPlan->getFirstPathLane();1239// get first lane1240const auto lastLane = myPlanElement->getLastPathLane();1241// compare edges1242if (nextFirstLane && lastLane && (nextFirstLane->getParentEdge() != lastLane->getParentEdge())) {1243return TLF("Edge '%' is not consecutive with edge '%'", nextFirstLane->getParentEdge()->getID(), lastLane->getParentEdge()->getID());1244}1245// in the future, check more elements1246}1247// undefined problem1248return "undefined problem";1249}125012511252double1253GNEDemandElementPlan::getEndPosRadius(const GUIVisualizationSettings& s, const GNESegment* segment, const bool drawHalfWidth) const {1254// check if myPlanElement is the last segment1255if (segment->isLastSegment()) {1256// calculate circle width1257const double circleRadius = (drawHalfWidth ? myArrivalPositionDiameter * 0.5 : myArrivalPositionDiameter);1258return circleRadius * MIN2((double)0.5, s.laneWidthExaggeration);1259} else {1260return -1;1261}1262}126312641265void1266GNEDemandElementPlan::drawFromArrow(const GUIVisualizationSettings& s, const GNELane* lane, const GNESegment* segment) const {1267// draw ifcurrent amd next segment is placed over lanes1268if (segment->getNextLane()) {1269// get firstPosition (last position of current lane shape)1270const Position from = lane->getLaneShape().back();1271// get lastPosition (first position of next lane shape)1272const Position to = segment->getNextLane()->getLaneShape().front();1273// push draw matrix1274GLHelper::pushMatrix();1275// move front1276glTranslated(0, 0, 4);1277// draw child line1278GUIGeometry::drawChildLine(s, from, to, RGBColor::RED, myPlanElement->isAttributeCarrierSelected(), .05);1279// pop draw matrix1280GLHelper::popMatrix();1281}1282}128312841285void1286GNEDemandElementPlan::drawToArrow(const GUIVisualizationSettings& s, const GNELane* lane, const GNESegment* segment) const {1287// draw the line if previos segment and current segment is placed over lanes1288if (segment->getPreviousLane()) {1289// get firstPosition (last position of current lane shape)1290const Position from = lane->getLaneShape().front();1291// get lastPosition (first position of next lane shape)1292const Position to = segment->getPreviousLane()->getLaneShape().back();1293// push draw matrix1294GLHelper::pushMatrix();1295// move front1296glTranslated(0, 0, 4);1297// draw child line1298GUIGeometry::drawChildLine(s, from, to, RGBColor::RED, myPlanElement->isAttributeCarrierSelected(), .05);1299// pop draw matrix1300GLHelper::popMatrix();1301}1302}130313041305void1306GNEDemandElementPlan::drawEndPosition(const GUIVisualizationSettings& /* s */, const GUIVisualizationSettings::Detail d, const double endPosRadius) const {1307// check if myPlanElement is the last segment1308if (endPosRadius > 0) {1309const Position geometryEndPos = getPlanAttributePosition(GNE_ATTR_PLAN_GEOMETRY_ENDPOS);1310// push draw matrix1311GLHelper::pushMatrix();1312// translate to pos and move to1313glTranslated(geometryEndPos.x(), geometryEndPos.y(), 4);1314// resolution of drawn circle depending of the zoom (To improve smothness)1315GLHelper::drawFilledCircleDetailled(d, endPosRadius);1316// pop draw matrix1317GLHelper::popMatrix();1318}1319}132013211322void1323GNEDemandElementPlan::replacePlanParent(const std::string& newParentID) {1324if (myPlanElement->myTagProperty->isPlanPerson()) {1325myPlanElement->replaceDemandElementParent(NamespaceIDs::persons, newParentID, 0);1326} else {1327myPlanElement->replaceDemandElementParent(NamespaceIDs::containers, newParentID, 0);1328}1329}13301331/****************************************************************************/133213331334