Path: blob/main/src/netedit/elements/demand/GNEPerson.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 GNEPerson.cpp14/// @author Pablo Alvarez Lopez15/// @date May 201916///17// Representation of persons in netedit18/****************************************************************************/1920#include <microsim/devices/MSDevice_BTreceiver.h>21#include <netedit/changes/GNEChange_Attribute.h>22#include <netedit/elements/moving/GNEMoveElementPlanParent.h>23#include <netedit/GNENet.h>24#include <netedit/GNETagProperties.h>25#include <utils/gui/div/GLHelper.h>26#include <utils/gui/div/GUIBasePersonHelper.h>27#include <utils/gui/div/GUIDesigns.h>28#include <utils/gui/windows/GUIAppEnum.h>29#include <utils/xml/NamespaceIDs.h>3031#include "GNEPerson.h"32#include "GNERouteHandler.h"3334// ===========================================================================35// FOX callback mapping36// ===========================================================================3738FXDEFMAP(GNEPerson::GNEPersonPopupMenu) personPopupMenuMap[] = {39FXMAPFUNC(SEL_COMMAND, MID_GNE_PERSON_TRANSFORM, GNEPerson::GNEPersonPopupMenu::onCmdTransform),40};4142FXDEFMAP(GNEPerson::GNESelectedPersonsPopupMenu) selectedPersonsPopupMenuMap[] = {43FXMAPFUNC(SEL_COMMAND, MID_GNE_PERSON_TRANSFORM, GNEPerson::GNESelectedPersonsPopupMenu::onCmdTransform),44};4546// Object implementation47FXIMPLEMENT(GNEPerson::GNEPersonPopupMenu, GUIGLObjectPopupMenu, personPopupMenuMap, ARRAYNUMBER(personPopupMenuMap))48FXIMPLEMENT(GNEPerson::GNESelectedPersonsPopupMenu, GUIGLObjectPopupMenu, selectedPersonsPopupMenuMap, ARRAYNUMBER(selectedPersonsPopupMenuMap))4950// ===========================================================================51// GNEPerson::GNEPersonPopupMenu52// ===========================================================================5354GNEPerson::GNEPersonPopupMenu::GNEPersonPopupMenu(GNEPerson* person, GUIMainWindow& app, GUISUMOAbstractView& parent) :55GUIGLObjectPopupMenu(app, parent, person),56myPerson(person),57myTransformToPerson(nullptr),58myTransformToPersonFlow(nullptr) {59// build common options60person->buildPopUpMenuCommonOptions(this, app, person->myNet->getViewNet(), person->getTagProperty()->getTag(), person->isAttributeCarrierSelected());61// add transform functions only in demand mode62if (myPerson->getNet()->getViewNet()->getEditModes().isCurrentSupermodeDemand()) {63// create menu pane for transform operations64FXMenuPane* transformOperation = new FXMenuPane(this);65this->insertMenuPaneChild(transformOperation);66new FXMenuCascade(this, "transform to", nullptr, transformOperation);67// Create menu comands for all transformations68myTransformToPerson = GUIDesigns::buildFXMenuCommand(transformOperation, "Person", GUIIconSubSys::getIcon(GUIIcon::PERSON), this, MID_GNE_PERSON_TRANSFORM);69myTransformToPersonFlow = GUIDesigns::buildFXMenuCommand(transformOperation, "PersonFlow", GUIIconSubSys::getIcon(GUIIcon::PERSONFLOW), this, MID_GNE_PERSON_TRANSFORM);70// check what menu command has to be disabled71if (myPerson->getTagProperty()->getTag() == SUMO_TAG_PERSON) {72myTransformToPerson->disable();73} else if (myPerson->getTagProperty()->getTag() == SUMO_TAG_PERSONFLOW) {74myTransformToPersonFlow->disable();75}76}77}787980GNEPerson::GNEPersonPopupMenu::~GNEPersonPopupMenu() {}818283long84GNEPerson::GNEPersonPopupMenu::onCmdTransform(FXObject* obj, FXSelector, void*) {85if (obj == myTransformToPerson) {86GNERouteHandler::transformToPerson(myPerson);87} else if (obj == myTransformToPersonFlow) {88GNERouteHandler::transformToPersonFlow(myPerson);89}90return 1;91}929394// ===========================================================================95// GNEPerson::GNESelectedPersonsPopupMenu96// ===========================================================================9798GNEPerson::GNESelectedPersonsPopupMenu::GNESelectedPersonsPopupMenu(GNEPerson* person, const std::vector<GNEPerson*>& selectedPerson, GUIMainWindow& app, GUISUMOAbstractView& parent) :99GUIGLObjectPopupMenu(app, parent, person),100myPersonTag(person->getTagProperty()->getTag()),101mySelectedPersons(selectedPerson),102myTransformToPerson(nullptr),103myTransformToPersonFlow(nullptr) {104// build common options105person->buildPopUpMenuCommonOptions(this, app, person->myNet->getViewNet(), person->myTagProperty->getTag(), person->isAttributeCarrierSelected());106// add transform functions only in demand mode107if (person->getNet()->getViewNet()->getEditModes().isCurrentSupermodeDemand()) {108// create menu pane for transform operations109FXMenuPane* transformOperation = new FXMenuPane(this);110this->insertMenuPaneChild(transformOperation);111new FXMenuCascade(this, "transform to", nullptr, transformOperation);112// Create menu comands for all transformations113myTransformToPerson = GUIDesigns::buildFXMenuCommand(transformOperation, "Person", GUIIconSubSys::getIcon(GUIIcon::PERSON), this, MID_GNE_PERSON_TRANSFORM);114myTransformToPersonFlow = GUIDesigns::buildFXMenuCommand(transformOperation, "PersonFlow", GUIIconSubSys::getIcon(GUIIcon::PERSONFLOW), this, MID_GNE_PERSON_TRANSFORM);115}116}117118119GNEPerson::GNESelectedPersonsPopupMenu::~GNESelectedPersonsPopupMenu() {}120121122long123GNEPerson::GNESelectedPersonsPopupMenu::onCmdTransform(FXObject* obj, FXSelector, void*) {124// iterate over all selected persons125for (const auto& i : mySelectedPersons) {126if ((obj == myTransformToPerson) &&127(i->getTagProperty()->getTag() == myPersonTag)) {128GNERouteHandler::transformToPerson(i);129} else if ((obj == myTransformToPersonFlow) &&130(i->getTagProperty()->getTag() == myPersonTag)) {131GNERouteHandler::transformToPerson(i);132}133}134return 1;135}136137// ===========================================================================138// member method definitions139// ===========================================================================140#ifdef _MSC_VER141#pragma warning(push)142#pragma warning(disable: 4355) // mask warning about "this" in initializers143#endif144GNEPerson::GNEPerson(SumoXMLTag tag, GNENet* net) :145GNEDemandElement(net, tag),146GNEDemandElementFlow(this),147myMoveElementPlanParent(new GNEMoveElementPlanParent(this, departPos, departPosProcedure)) {148// enable set and persons per hour as default flow values149toggleAttribute(SUMO_ATTR_END, true);150toggleAttribute(SUMO_ATTR_PERSONSPERHOUR, true);151}152153154GNEPerson::GNEPerson(SumoXMLTag tag, GNENet* net, FileBucket* fileBucket, GNEDemandElement* pType,155const SUMOVehicleParameter& personparameters) :156GNEDemandElement(personparameters.id, net, tag, fileBucket),157GNEDemandElementFlow(this, personparameters),158myMoveElementPlanParent(new GNEMoveElementPlanParent(this, departPos, departPosProcedure)) {159// set parents160setParent<GNEDemandElement*>(pType);161// set manually vtypeID (needed for saving)162vtypeid = pType->getID();163}164#ifdef _MSC_VER165#pragma warning(pop)166#endif167168GNEPerson::~GNEPerson() {}169170171GNEMoveElement*172GNEPerson::getMoveElement() const {173return myMoveElementPlanParent;174}175176177Parameterised*178GNEPerson::getParameters() {179return this;180}181182183const Parameterised*184GNEPerson::getParameters() const {185return this;186}187188189void190GNEPerson::writeDemandElement(OutputDevice& device) const {191// attribute VType musn't be written if is DEFAULT_PEDTYPE_ID192if (getTypeParent()->getID() == DEFAULT_PEDTYPE_ID) {193// unset VType parameter194parametersSet &= ~VEHPARS_VTYPE_SET;195// write person attributes (VType will not be written)196write(device, OptionsCont::getOptions(), myTagProperty->getXMLTag());197// set VType parameter again198parametersSet |= VEHPARS_VTYPE_SET;199} else {200// write person attributes, including VType201write(device, OptionsCont::getOptions(), myTagProperty->getXMLTag(), getTypeParent()->getID());202}203// write parameters204writeParams(device);205// write child demand elements associated to this person (Rides, Walks...)206for (const auto& i : getChildDemandElements()) {207i->writeDemandElement(device);208}209// close person tag210device.closeTag();211}212213214GNEDemandElement::Problem215GNEPerson::isDemandElementValid() const {216if (getChildDemandElements().size() == 0) {217return Problem::NO_PLANS;218} else {219return Problem::OK;220}221}222223224std::string225GNEPerson::getDemandElementProblem() const {226if (getChildDemandElements().size() == 0) {227return "Person needs at least one plan";228} else {229return "";230}231}232233234void235GNEPerson::fixDemandElementProblem() {236// nothing to fix237}238239240SUMOVehicleClass241GNEPerson::getVClass() const {242return getParentDemandElements().front()->getVClass();243}244245246const RGBColor&247GNEPerson::getColor() const {248return color;249}250251252void253GNEPerson::updateGeometry() {254// only update geometry of childrens255for (const auto& demandElement : getChildDemandElements()) {256demandElement->updateGeometry();257}258}259260261Position262GNEPerson::getPositionInView() const {263return getAttributePosition(SUMO_ATTR_DEPARTPOS);264}265266267GUIGLObjectPopupMenu*268GNEPerson::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {269// return a GNEPersonPopupMenu270return new GNEPersonPopupMenu(this, app, parent);271}272273274std::string275GNEPerson::getParentName() const {276return getParentDemandElements().front()->getID();277}278279280double281GNEPerson::getExaggeration(const GUIVisualizationSettings& s) const {282return s.personSize.getExaggeration(s, this, 80);283}284285286Boundary287GNEPerson::getCenteringBoundary() const {288Boundary personBoundary;289if (getChildDemandElements().size() > 0) {290if (getChildDemandElements().front()->getTagProperty()->isPlanStopPerson()) {291// use boundary of stop center292return getChildDemandElements().front()->getCenteringBoundary();293} else {294personBoundary.add(getPositionInView());295}296} else {297personBoundary = Boundary(-0.1, -0.1, 0.1, 0.1);298}299personBoundary.grow(20);300return personBoundary;301}302303304void305GNEPerson::splitEdgeGeometry(const double /*splitPosition*/, const GNENetworkElement* /*originalElement*/, const GNENetworkElement* /*newElement*/, GNEUndoList* /*undoList*/) {306// geometry of this element cannot be splitted307}308309310void311GNEPerson::drawGL(const GUIVisualizationSettings& s) const {312bool drawPerson = true;313// check if person can be drawn314if (!myNet->getViewNet()->getNetworkViewOptions().showDemandElements()) {315drawPerson = false;316} else if (!myNet->getViewNet()->getDataViewOptions().showDemandElements()) {317drawPerson = false;318} else if (!myNet->getViewNet()->getDemandViewOptions().showNonInspectedDemandElements(this)) {319drawPerson = false;320} else if (getChildDemandElements().empty()) {321drawPerson = false;322}323// continue if person can be drawn324if (drawPerson) {325// obtain exaggeration (and add the special personExaggeration)326const double exaggeration = getExaggeration(s) + 10;327// obtain position328const Position personPosition = getAttributePosition(SUMO_ATTR_DEPARTPOS);329if (personPosition == Position::INVALID) {330return;331}332// get detail level333const auto d = s.getDetailLevel(exaggeration);334// draw geometry only if we'rent in drawForObjectUnderCursor mode335if (s.checkDrawPerson(d, isAttributeCarrierSelected())) {336// obtain width and length337const double length = getTypeParent()->getAttributeDouble(SUMO_ATTR_LENGTH);338const double width = getTypeParent()->getAttributeDouble(SUMO_ATTR_WIDTH);339// obtain img file340const std::string file = getTypeParent()->getAttribute(SUMO_ATTR_IMGFILE);341// push draw matrix342GLHelper::pushMatrix();343// Start with the drawing of the area traslating matrix to origin344drawInLayer(getType());345// translate and rotate346glTranslated(personPosition.x(), personPosition.y(), 0);347glRotated(90, 0, 0, 1);348// set person color349GLHelper::setColor(getDrawingColor(s));350// set scale351glScaled(exaggeration, exaggeration, 1);352// draw person depending of detail level353if (s.personQuality >= 2) {354GUIBasePersonHelper::drawAction_drawAsImage(0, length, width, file, SUMOVehicleShape::PEDESTRIAN, exaggeration);355} else if (s.personQuality == 1) {356GUIBasePersonHelper::drawAction_drawAsCenteredCircle(length / 2, width / 2, s.scale * exaggeration);357} else if (s.personQuality == 0) {358GUIBasePersonHelper::drawAction_drawAsTriangle(0, length, width);359}360// pop matrix361GLHelper::popMatrix();362// draw stack label363if (myStackedLabelNumber > 0) {364drawStackLabel(myStackedLabelNumber, "person", Position(personPosition.x() - 2.5, personPosition.y()), -90, 1.3, 5, getExaggeration(s));365} else if ((getChildDemandElements().front()->getTagProperty()->getTag() == GNE_TAG_STOPPERSON_BUSSTOP) ||366(getChildDemandElements().front()->getTagProperty()->getTag() == GNE_TAG_STOPPERSON_TRAINSTOP)) {367// declare counter for stacked persons over stops368int stackedCounter = 0;369// get stoppingPlace370const auto stoppingPlace = getChildDemandElements().front()->getParentAdditionals().front();371// get stacked persons372for (const auto& stopPerson : stoppingPlace->getChildDemandElements()) {373if ((stopPerson->getTagProperty()->getTag() == GNE_TAG_STOPPERSON_BUSSTOP) ||374(stopPerson->getTagProperty()->getTag() == GNE_TAG_STOPPERSON_TRAINSTOP)) {375// get person parent376const auto personParent = stopPerson->getParentDemandElements().front();377// check if the stop if the first person plan parent378if (stopPerson->getPreviousChildDemandElement(personParent) == nullptr) {379stackedCounter++;380}381}382}383// if we have more than two stacked elements, draw label384if (stackedCounter > 1) {385drawStackLabel(stackedCounter, "person", Position(personPosition.x() - 2.5, personPosition.y()), -90, 1.3, 5, getExaggeration(s));386}387}388// draw flow label389if (myTagProperty->isFlow()) {390drawFlowLabel(Position(personPosition.x() - 1, personPosition.y() - 0.25), -90, 1.8, 2, getExaggeration(s));391}392// draw lock icon393GNEViewNetHelper::LockIcon::drawLockIcon(d, this, getType(), personPosition, exaggeration, s.dottedContourSettings.segmentWidth);394// draw name395drawName(personPosition, s.scale, s.personName, s.angle);396if (s.personValue.show(this)) {397Position personValuePosition = personPosition + Position(0, 0.6 * s.personName.scaledSize(s.scale));398const double value = getColorValue(s, s.personColorer.getActive());399GLHelper::drawTextSettings(s.personValue, toString(value), personValuePosition, s.scale, s.angle, GLO_MAX - getType());400}401// draw dotted contour402myPersonContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);403}404// calculate contour405myPersonContour.calculateContourRectangleShape(s, d, this, personPosition, 0.1, 0.2, getType(), -1.1, 0, 0, exaggeration, nullptr);406}407}408409410void411GNEPerson::computePathElement() {412// compute all person plan children (because aren't computed in "computeDemandElements()")413for (const auto& demandElement : getChildDemandElements()) {414demandElement->computePathElement();415}416}417418419void420GNEPerson::drawLanePartialGL(const GUIVisualizationSettings& /*s*/, const GNESegment* /*segment*/, const double /*offsetFront*/) const {421// Stops don't use drawJunctionPartialGL422}423424425void426GNEPerson::drawJunctionPartialGL(const GUIVisualizationSettings& /*s*/, const GNESegment* /*segment*/, const double /*offsetFront*/) const {427// Stops don't use drawJunctionPartialGL428}429430431GNELane*432GNEPerson::getFirstPathLane() const {433// use path lane of first person plan434return getChildDemandElements().front()->getFirstPathLane();435}436437438GNELane*439GNEPerson::getLastPathLane() const {440// use path lane of first person plan441return getChildDemandElements().front()->getLastPathLane();442}443444445std::string446GNEPerson::getAttribute(SumoXMLAttr key) const {447// declare string error448std::string error;449switch (key) {450case SUMO_ATTR_ID:451return getMicrosimID();452case SUMO_ATTR_TYPE:453return getTypeParent()->getID();454case SUMO_ATTR_COLOR:455if (wasSet(VEHPARS_COLOR_SET)) {456return toString(color);457} else {458return myTagProperty->getDefaultStringValue(SUMO_ATTR_COLOR);459}460case SUMO_ATTR_DEPARTPOS:461if (wasSet(VEHPARS_DEPARTPOS_SET)) {462return getDepartPos();463} else {464return myTagProperty->getDefaultStringValue(SUMO_ATTR_DEPARTPOS);465}466default:467return getFlowAttribute(this, key);468}469}470471472double473GNEPerson::getAttributeDouble(SumoXMLAttr key) const {474switch (key) {475case SUMO_ATTR_DEPARTPOS:476if (departPosProcedure == DepartPosDefinition::GIVEN) {477return departPos;478} else {479return 0;480}481default:482return getFlowAttributeDouble(key);483}484}485486487Position488GNEPerson::getAttributePosition(SumoXMLAttr key) const {489switch (key) {490case SUMO_ATTR_DEPARTPOS: {491// first check number of child demand elements492if (getChildDemandElements().empty()) {493return Position();494}495// get person plan496const GNEDemandElement* personPlan = getChildDemandElements().front();497// first check if first person plan is a stop498if (personPlan->getTagProperty()->isPlanStopPerson()) {499// stop center500return personPlan->getPositionInView();501} else if (personPlan->getTagProperty()->planFromTAZ()) {502// TAZ503if (personPlan->getParentAdditionals().front()->getAttribute(SUMO_ATTR_CENTER).empty()) {504return personPlan->getParentAdditionals().front()->getAttributePosition(GNE_ATTR_TAZ_CENTROID);505} else {506return personPlan->getParentAdditionals().front()->getAttributePosition(SUMO_ATTR_CENTER);507}508} else if (personPlan->getTagProperty()->planFromJunction()) {509// juncrtion510return personPlan->getParentJunctions().front()->getPositionInView();511} else {512return personPlan->getAttributePosition(GNE_ATTR_PLAN_GEOMETRY_STARTPOS);513}514}515default:516return getCommonAttributePosition(key);517}518}519520521void522GNEPerson::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {523switch (key) {524case SUMO_ATTR_ID:525case SUMO_ATTR_TYPE:526case SUMO_ATTR_COLOR:527case SUMO_ATTR_DEPARTPOS:528GNEChange_Attribute::changeAttribute(this, key, value, undoList);529break;530default:531setFlowAttribute(this, key, value, undoList);532break;533}534}535536537bool538GNEPerson::isValid(SumoXMLAttr key, const std::string& value) {539// declare string error540std::string error;541switch (key) {542case SUMO_ATTR_ID:543return isValidDemandElementID(NamespaceIDs::persons, value);544case SUMO_ATTR_TYPE:545return (myNet->getAttributeCarriers()->retrieveDemandElements(NamespaceIDs::types, value, false) != nullptr);546case SUMO_ATTR_COLOR:547return canParse<RGBColor>(value);548case SUMO_ATTR_DEPARTPOS: {549double dummyDepartPos;550DepartPosDefinition dummyDepartPosProcedure;551parseDepartPos(value, toString(SUMO_TAG_PERSON), id, dummyDepartPos, dummyDepartPosProcedure, error);552// if error is empty, given value is valid553return error.empty();554}555default:556return isValidFlowAttribute(this, key, value);557}558}559560561void562GNEPerson::enableAttribute(SumoXMLAttr key, GNEUndoList* undoList) {563enableFlowAttribute(this, key, undoList);564}565566567void568GNEPerson::disableAttribute(SumoXMLAttr key, GNEUndoList* undoList) {569disableFlowAttribute(this, key, undoList);570}571572573bool574GNEPerson::isAttributeEnabled(SumoXMLAttr key) const {575return isFlowAttributeEnabled(key);576}577578579std::string580GNEPerson::getPopUpID() const {581return getTagStr();582}583584585std::string586GNEPerson::getHierarchyName() const {587return getTagStr() + ": " + getAttribute(SUMO_ATTR_ID);588}589590// ===========================================================================591// protected592// ===========================================================================593594RGBColor595GNEPerson::getDrawingColor(const GUIVisualizationSettings& s) const {596if (isAttributeCarrierSelected()) {597return s.colorSettings.selectedPersonColor;598} else {599return getColorByScheme(s.personColorer, this);600}601}602603// ===========================================================================604// private605// ===========================================================================606607GNEPerson::personPlanSegment::personPlanSegment(GNEDemandElement* _personPlan) :608personPlan(_personPlan),609edge(nullptr),610arrivalPos(-1) {611}612613614GNEPerson::personPlanSegment::personPlanSegment() :615personPlan(nullptr),616edge(nullptr),617arrivalPos(-1) {618}619620621void622GNEPerson::setAttribute(SumoXMLAttr key, const std::string& value) {623// declare string error624std::string error;625switch (key) {626case SUMO_ATTR_ID:627// update microsimID628setDemandElementID(value);629// update id630id = value;631break;632case SUMO_ATTR_TYPE:633if (getID().size() > 0) {634replaceDemandElementParent(NamespaceIDs::types, value, 0);635// set manually vtypeID (needed for saving)636vtypeid = value;637}638break;639case SUMO_ATTR_COLOR:640if (!value.empty() && (value != myTagProperty->getDefaultStringValue(key))) {641color = parse<RGBColor>(value);642// mark parameter as set643parametersSet |= VEHPARS_COLOR_SET;644} else {645// set default value646color = parse<RGBColor>(myTagProperty->getDefaultStringValue(key));647// unset parameter648parametersSet &= ~VEHPARS_COLOR_SET;649}650break;651case SUMO_ATTR_DEPARTPOS:652if (!value.empty() && (value != myTagProperty->getDefaultStringValue(key))) {653parseDepartPos(value, toString(SUMO_TAG_PERSON), id, departPos, departPosProcedure, error);654// mark parameter as set655parametersSet |= VEHPARS_DEPARTPOS_SET;656} else {657// set default value658parseDepartPos(myTagProperty->getDefaultStringValue(key), toString(SUMO_TAG_PERSON), id, departPos, departPosProcedure, error);659// unset parameter660parametersSet &= ~VEHPARS_DEPARTPOS_SET;661}662// compute person663updateGeometry();664break;665default:666setFlowAttribute(this, key, value);667break;668}669}670671672void673GNEPerson::toggleAttribute(SumoXMLAttr key, const bool value) {674// toggle flow attributes675toggleFlowAttribute(key, value);676}677678/****************************************************************************/679680681