Path: blob/main/src/netedit/elements/additional/GNEAdditional.cpp
193871 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2026 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 GNEAdditional.cpp14/// @author Pablo Alvarez Lopez15/// @date Dec 201516///17// A abstract class for representation of additional elements18/****************************************************************************/19#include <config.h>2021#include <foreign/fontstash/fontstash.h>22#include <netedit/frames/common/GNEInspectorFrame.h>23#include <netedit/frames/common/GNEMoveFrame.h>24#include <netedit/frames/common/GNESelectorFrame.h>25#include <netedit/frames/data/GNETAZRelDataFrame.h>26#include <netedit/frames/demand/GNEContainerFrame.h>27#include <netedit/frames/demand/GNEContainerPlanFrame.h>28#include <netedit/frames/demand/GNEPersonFrame.h>29#include <netedit/frames/demand/GNEPersonPlanFrame.h>30#include <netedit/frames/demand/GNEVehicleFrame.h>31#include <netedit/frames/GNEAttributesEditor.h>32#include <netedit/frames/GNEPathCreator.h>33#include <netedit/frames/GNEPlanCreator.h>34#include <netedit/GNEApplicationWindow.h>35#include <netedit/GNENet.h>36#include <netedit/GNETagPropertiesDatabase.h>37#include <netedit/GNEViewParent.h>38#include <utils/gui/div/GLHelper.h>39#include <utils/gui/div/GUIDesigns.h>40#include <utils/gui/div/GUIParameterTableWindow.h>41#include <utils/gui/globjects/GUIGLObjectPopupMenu.h>4243#include "GNEAdditional.h"44#include "GNETAZ.h"4546// ===========================================================================47// member method definitions48// ===========================================================================4950GNEAdditional::GNEAdditional(GNENet* net, SumoXMLTag tag) :51GNEAttributeCarrier(tag, net),52GUIGlObject(myTagProperty->getGLType(), "", GUIIconSubSys::getIcon(myTagProperty->getGUIIcon())),53GNEPathElement(GNEPathElement::Options::ADDITIONAL_ELEMENT) {54}555657GNEAdditional::GNEAdditional(const std::string& id, GNENet* net, SumoXMLTag tag, FileBucket* fileBucket, const std::string& name) :58GNEAttributeCarrier(tag, net, fileBucket),59GUIGlObject(myTagProperty->getGLType(), id, GUIIconSubSys::getIcon(myTagProperty->getGUIIcon())),60GNEPathElement(GNEPathElement::Options::ADDITIONAL_ELEMENT),61myAdditionalName(name) {62}636465GNEAdditional::GNEAdditional(GNEAdditional* additionalParent, SumoXMLTag tag, const std::string& name) :66GNEAttributeCarrier(tag, additionalParent->getNet(), additionalParent->getFileBucket()),67GUIGlObject(myTagProperty->getGLType(), "", GUIIconSubSys::getIcon(myTagProperty->getGUIIcon())),68GNEPathElement(GNEPathElement::Options::ADDITIONAL_ELEMENT),69myAdditionalName(name) {70}717273GNEAdditional::~GNEAdditional() {}747576GNEHierarchicalElement*77GNEAdditional::getHierarchicalElement() {78return this;79}808182GUIGlObject*83GNEAdditional::getGUIGlObject() {84return this;85}868788const GUIGlObject*89GNEAdditional::getGUIGlObject() const {90return this;91}929394FileBucket*95GNEAdditional::getFileBucket() const {96if (myTagProperty->saveInParentFile()) {97if (isTemplate()) {98return nullptr;99} else {100return getParentAdditionals().front()->getFileBucket();101}102} else {103return myFileBucket;104}105}106107108const std::string109GNEAdditional::getOptionalName() const {110try {111return getAttribute(SUMO_ATTR_NAME);112} catch (InvalidArgument&) {113return "";114}115}116117118const GUIGeometry&119GNEAdditional::getAdditionalGeometry() const {120return myAdditionalGeometry;121}122123124void125GNEAdditional::setSpecialColor(const RGBColor* color) {126mySpecialColor = color;127}128129130void131GNEAdditional::resetAdditionalContour() {132myAdditionalContour.clearContour();133}134135136bool137GNEAdditional::isAdditionalValid() const {138return true;139}140141142std::string143GNEAdditional::getAdditionalProblem() const {144return "";145}146147148void149GNEAdditional::fixAdditionalProblem() {150throw InvalidArgument(getTagStr() + " cannot fix any problem");151}152153154void155GNEAdditional::openAdditionalDialog(FXWindow* /* restoringFocusWindow */) {156throw InvalidArgument(getTagStr() + " doesn't have an additional dialog");157}158159160double161GNEAdditional::getExaggeration(const GUIVisualizationSettings& s) const {162return s.addSize.getExaggeration(s, this);163}164165166Boundary167GNEAdditional::getCenteringBoundary() const {168if (myAdditionalBoundary.isInitialised()) {169return myAdditionalBoundary;170} else {171Boundary contourBoundary = myAdditionalContour.getContourBoundary();172if (contourBoundary.isInitialised()) {173contourBoundary.grow(5);174return contourBoundary;175} else if (myAdditionalGeometry.getShape().size() > 0) {176Boundary geometryBoundary = myAdditionalGeometry.getShape().getBoxBoundary();177geometryBoundary.grow(5);178return geometryBoundary;179} else if (getParentAdditionals().size() > 0) {180return getParentAdditionals().front()->getCenteringBoundary();181} else {182Boundary centerBoundary(0, 0, 0, 0);183centerBoundary.grow(5);184return centerBoundary;185}186}187}188189190bool191GNEAdditional::checkDrawFromContour() const {192// get modes and viewParent (for code legibility)193const auto& modes = myNet->getViewNet()->getEditModes();194const auto& viewParent = myNet->getViewParent();195const auto& inspectedElements = myNet->getViewNet()->getInspectedElements();196// continue depending of current status197if (inspectedElements.isInspectingSingleElement()) {198const auto inspectedAC = inspectedElements.getFirstAC();199// check conditions200if (inspectedAC->hasAttribute(SUMO_ATTR_FROM_TAZ)) {201return (inspectedAC->getAttribute(SUMO_ATTR_FROM_TAZ) == getID());202} else if ((inspectedAC->getTagProperty()->getTag() == SUMO_TAG_TAZREL)) {203return (inspectedAC->getAttribute(SUMO_ATTR_FROM) == getID());204}205} else if (modes.isCurrentSupermodeDemand()) {206// get current GNEPlanCreator207GNEPlanCreator* planCreator = nullptr;208if (modes.demandEditMode == DemandEditMode::DEMAND_PERSON) {209planCreator = viewParent->getPersonFrame()->getPlanCreator();210} else if (modes.demandEditMode == DemandEditMode::DEMAND_PERSONPLAN) {211planCreator = viewParent->getPersonPlanFrame()->getPlanCreator();212} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINER) {213planCreator = viewParent->getContainerFrame()->getPlanCreator();214} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINERPLAN) {215planCreator = viewParent->getContainerPlanFrame()->getPlanCreator();216}217// continue depending of planCreator218if (planCreator) {219// check if this is the from additional220const auto additionalID = getID();221if ((planCreator->getPlanParameteres().fromBusStop == additionalID) ||222(planCreator->getPlanParameteres().fromTrainStop == additionalID) ||223(planCreator->getPlanParameteres().fromContainerStop == additionalID) ||224(planCreator->getPlanParameteres().fromChargingStation == additionalID) ||225(planCreator->getPlanParameteres().fromParkingArea == additionalID) ||226(planCreator->getPlanParameteres().fromTAZ == additionalID)) {227return true;228}229} else if (modes.demandEditMode == DemandEditMode::DEMAND_VEHICLE) {230// get selected TAZs231const auto& selectedTAZs = viewParent->getVehicleFrame()->getPathCreator()->getSelectedTAZs();232// check if this is the first selected TAZ233if ((selectedTAZs.size() > 0) && (selectedTAZs.front() == this)) {234return true;235}236}237} else if (modes.isCurrentSupermodeData()) {238// get TAZRelDataFrame239const auto& TAZRelDataFrame = viewParent->getTAZRelDataFrame();240if (TAZRelDataFrame->shown() && (TAZRelDataFrame->getFirstTAZ() == this)) {241return true;242}243}244// nothing to draw245return false;246}247248249bool250GNEAdditional::checkDrawToContour() const {251// get modes and viewParent (for code legibility)252const auto& modes = myNet->getViewNet()->getEditModes();253const auto& viewParent = myNet->getViewParent();254const auto& inspectedElements = myNet->getViewNet()->getInspectedElements();255// check conditions256if (myNet->getViewParent()->getInspectorFrame()->getAttributesEditor()->isReparenting()) {257return false;258} else if (inspectedElements.isInspectingSingleElement()) {259const auto inspectedAC = inspectedElements.getFirstAC();260// check conditions261if (inspectedAC->hasAttribute(SUMO_ATTR_TO_TAZ)) {262return (inspectedAC->getAttribute(SUMO_ATTR_TO_TAZ) == getID());263} else if (inspectedAC->getTagProperty()->getTag() == SUMO_TAG_TAZREL) {264return (inspectedAC->getAttribute(SUMO_ATTR_TO) == getID());265} else if (inspectedAC->hasAttribute(GNE_ATTR_PARENT)) {266// check all parent tags267const auto& parentTags = inspectedAC->getTagProperty()->getXMLParentTags();268if (std::find(parentTags.begin(), parentTags.end(), myTagProperty->getTag()) != parentTags.end()) {269return (inspectedAC->getAttribute(GNE_ATTR_PARENT) == getID());270}271}272} else if (modes.isCurrentSupermodeDemand()) {273// get current GNEPlanCreator274GNEPlanCreator* planCreator = nullptr;275if (modes.demandEditMode == DemandEditMode::DEMAND_PERSON) {276planCreator = viewParent->getPersonFrame()->getPlanCreator();277} else if (modes.demandEditMode == DemandEditMode::DEMAND_PERSONPLAN) {278planCreator = viewParent->getPersonPlanFrame()->getPlanCreator();279} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINER) {280planCreator = viewParent->getContainerFrame()->getPlanCreator();281} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINERPLAN) {282planCreator = viewParent->getContainerPlanFrame()->getPlanCreator();283}284// continue depending of planCreator285if (planCreator) {286// check if this is the from additional287const auto additionalID = getID();288if ((planCreator->getPlanParameteres().toBusStop == additionalID) ||289(planCreator->getPlanParameteres().toTrainStop == additionalID) ||290(planCreator->getPlanParameteres().toContainerStop == additionalID) ||291(planCreator->getPlanParameteres().toChargingStation == additionalID) ||292(planCreator->getPlanParameteres().toParkingArea == additionalID) ||293(planCreator->getPlanParameteres().toTAZ == additionalID)) {294return true;295}296} else if (modes.demandEditMode == DemandEditMode::DEMAND_VEHICLE) {297// get selected TAZs298const auto& selectedTAZs = viewParent->getVehicleFrame()->getPathCreator()->getSelectedTAZs();299// check if this is the first selected TAZ300if ((selectedTAZs.size() > 1) && (selectedTAZs.back() == this)) {301return true;302}303}304} else if (modes.isCurrentSupermodeData()) {305// get TAZRelDataFrame306const auto& TAZRelDataFrame = viewParent->getTAZRelDataFrame();307if (TAZRelDataFrame->shown() && (TAZRelDataFrame->getSecondTAZ() == this)) {308return true;309}310}311// nothing to draw312return false;313}314315316bool317GNEAdditional::checkDrawRelatedContour() const {318const auto& neteditAttributesEditor = myNet->getViewParent()->getInspectorFrame()->getAttributesEditor();319if (neteditAttributesEditor->isReparenting()) {320return neteditAttributesEditor->checkNewParent(this);321}322// check opened popup323if (myNet->getViewNet()->getPopup()) {324return myNet->getViewNet()->getPopup()->getGLObject() == this;325}326return false;327}328329330bool331GNEAdditional::checkDrawOverContour() const {332const auto& modes = myNet->getViewNet()->getEditModes();333if (myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() != this) {334return false;335} else {336const auto& viewParent = myNet->getViewParent();337if (modes.isCurrentSupermodeDemand()) {338// get current plan selector339GNEPlanSelector* planSelector = nullptr;340if (modes.demandEditMode == DemandEditMode::DEMAND_PERSON) {341planSelector = viewParent->getPersonFrame()->getPlanSelector();342} else if (modes.demandEditMode == DemandEditMode::DEMAND_PERSONPLAN) {343planSelector = viewParent->getPersonPlanFrame()->getPlanSelector();344} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINER) {345planSelector = viewParent->getContainerFrame()->getPlanSelector();346} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINERPLAN) {347planSelector = viewParent->getContainerPlanFrame()->getPlanSelector();348}349// continue depending of plan selector350if (planSelector) {351if ((myTagProperty->isStoppingPlace() && planSelector->markStoppingPlaces()) ||352(myTagProperty->isTAZElement() && planSelector->markTAZs())) {353return true;354}355} else if (modes.demandEditMode == DemandEditMode::DEMAND_VEHICLE) {356// get current vehicle template357const auto& vehicleTemplate = viewParent->getVehicleFrame()->getVehicleTagSelector()->getCurrentTemplateAC();358// check if vehicle can be placed over from-to TAZs359if (vehicleTemplate && vehicleTemplate->getTagProperty()->vehicleTAZs()) {360return true;361}362}363} else if (modes.isCurrentSupermodeData()) {364// get TAZRelDataFrame365const auto& TAZRelDataFrame = viewParent->getTAZRelDataFrame();366if (TAZRelDataFrame->shown()) {367if (TAZRelDataFrame->getFirstTAZ() && TAZRelDataFrame->getSecondTAZ()) {368return false;369} else if (TAZRelDataFrame->getFirstTAZ() == this) {370return false;371} else if (TAZRelDataFrame->getSecondTAZ() == this) {372return false;373} else {374return true;375}376}377}378return false;379}380}381382383bool384GNEAdditional::checkDrawDeleteContour() const {385// get edit modes386const auto& editModes = myNet->getViewNet()->getEditModes();387// check if we're in delete mode388if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_DELETE)) {389return myNet->getViewNet()->checkOverLockedElement(this, mySelected);390} else {391return false;392}393}394395396bool397GNEAdditional::checkDrawDeleteContourSmall() const {398// get edit modes399const auto& editModes = myNet->getViewNet()->getEditModes();400// check if we're in delete mode and this additional has a parent401if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_DELETE) && (getParentAdditionals().size() > 0)) {402const auto additional = myNet->getViewNet()->getViewObjectsSelector().getAdditionalFront();403if (additional && (additional == myNet->getViewNet()->getViewObjectsSelector().getAttributeCarrierFront())) {404return (getParentAdditionals().front() == additional);405}406}407return false;408}409410411bool412GNEAdditional::checkDrawSelectContour() const {413// get edit modes414const auto& editModes = myNet->getViewNet()->getEditModes();415// check if we're in select mode416if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_SELECT)) {417return myNet->getViewNet()->checkOverLockedElement(this, mySelected);418} else {419return false;420}421}422423424GUIGLObjectPopupMenu*425GNEAdditional::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {426// create popup427GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);428// build common options429buildPopUpMenuCommonOptions(ret, app, myNet->getViewNet(), myTagProperty->getTag(), mySelected);430// show option to open additional dialog431if (myTagProperty->hasDialog()) {432GUIDesigns::buildFXMenuCommand(ret, TL("Open ") + getTagStr() + TL(" Dialog"), getACIcon(), &parent, MID_OPEN_ADDITIONAL_DIALOG);433new FXMenuSeparator(ret);434}435// Show position parameters436if (myTagProperty->hasAttribute(SUMO_ATTR_LANE) && (myAdditionalGeometry.getShape().size() > 1)) {437const GNELane* lane = myNet->getAttributeCarriers()->retrieveLane(getAttribute(SUMO_ATTR_LANE));438// Show menu command inner position439const double innerPos = myAdditionalGeometry.getShape().nearest_offset_to_point2D(parent.getPositionInformation());440GUIDesigns::buildFXMenuCommand(ret, TL("Cursor position over additional shape: ") + toString(innerPos), nullptr, nullptr, 0);441// If shape isn't empty, show menu command lane position442if (myAdditionalGeometry.getShape().size() > 0) {443const double lanePos = lane->getLaneShape().nearest_offset_to_point2D(myAdditionalGeometry.getShape().front());444GUIDesigns::buildFXMenuCommand(ret, TL("Cursor position over lane: ") + toString(innerPos + lanePos), nullptr, nullptr, 0);445}446} else if (myTagProperty->hasAttribute(SUMO_ATTR_EDGE) && (myAdditionalGeometry.getShape().size() > 1)) {447const GNEEdge* edge = myNet->getAttributeCarriers()->retrieveEdge(getAttribute(SUMO_ATTR_EDGE));448// Show menu command inner position449const double innerPos = myAdditionalGeometry.getShape().nearest_offset_to_point2D(parent.getPositionInformation());450GUIDesigns::buildFXMenuCommand(ret, TL("Cursor position over additional shape: ") + toString(innerPos), nullptr, nullptr, 0);451// If shape isn't empty, show menu command edge position452if (myAdditionalGeometry.getShape().size() > 0) {453const double edgePos = edge->getChildLanes().at(0)->getLaneShape().nearest_offset_to_point2D(myAdditionalGeometry.getShape().front());454GUIDesigns::buildFXMenuCommand(ret, TL("Mouse position over edge: ") + toString(innerPos + edgePos), nullptr, nullptr, 0);455}456} else {457const auto mousePos = myNet->getViewNet()->getPositionInformation();458GUIDesigns::buildFXMenuCommand(ret, TL("Cursor position in view: ") + toString(mousePos.x()) + "," + toString(mousePos.y()), nullptr, nullptr, 0);459}460return ret;461}462463464GUIParameterTableWindow*465GNEAdditional::getParameterWindow(GUIMainWindow& app, GUISUMOAbstractView&) {466// Create table467GUIParameterTableWindow* ret = new GUIParameterTableWindow(app, *this);468// Iterate over attributes469for (const auto& attributeProperty : myTagProperty->getAttributeProperties()) {470// Add attribute and set it dynamic if aren't unique471if (attributeProperty->isUnique()) {472ret->mkItem(attributeProperty->getAttrStr().c_str(), false, getAttribute(attributeProperty->getAttr()));473} else {474ret->mkItem(attributeProperty->getAttrStr().c_str(), true, getAttribute(attributeProperty->getAttr()));475}476}477// close building478ret->closeBuilding();479return ret;480}481482483const std::string&484GNEAdditional::getOptionalAdditionalName() const {485return myAdditionalName;486}487488489bool490GNEAdditional::isGLObjectLocked() const {491if (myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork()) {492return myNet->getViewNet()->getLockManager().isObjectLocked(getType(), isAttributeCarrierSelected());493} else {494return true;495}496}497498499void500GNEAdditional::markAsFrontElement() {501markForDrawingFront();502}503504505void506GNEAdditional::deleteGLObject() {507myNet->deleteAdditional(this, myNet->getUndoList());508}509510511void512GNEAdditional::selectGLObject() {513if (isAttributeCarrierSelected()) {514unselectAttributeCarrier();515} else {516selectAttributeCarrier();517}518// update information label519myNet->getViewParent()->getSelectorFrame()->getSelectionInformation()->updateInformationLabel();520}521522523void GNEAdditional::updateGLObject() {524updateGeometry();525}526527528void529GNEAdditional::computePathElement() {530// Nothing to compute531}532533534bool535GNEAdditional::isPathElementSelected() const {536return mySelected;537}538539540void541GNEAdditional::drawLanePartialGL(const GUIVisualizationSettings& /*s*/, const GNESegment* /*segment*/, const double /*offsetFront*/) const {542// Nothing to draw543}544545546void547GNEAdditional::drawJunctionPartialGL(const GUIVisualizationSettings& /*s*/, const GNESegment* /*segment*/, const double /*offsetFront*/) const {548// Nothing to draw549}550551// ---------------------------------------------------------------------------552// GNEAdditional - protected methods553// ---------------------------------------------------------------------------554555void556GNEAdditional::writeAdditionalAttributes(OutputDevice& device) const {557// ID (if defined)558if (myTagProperty->hasAttribute(SUMO_ATTR_ID)) {559device.writeAttr(SUMO_ATTR_ID, StringUtils::escapeXML(getID()));560}561// name562if (myAdditionalName.size() > 0) {563device.writeAttr(SUMO_ATTR_NAME, myAdditionalName);564}565}566567568bool569GNEAdditional::isValidAdditionalID(const std::string& value) const {570if (!isTemplate() && (value == getID())) {571return true;572} else if (SUMOXMLDefinitions::isValidAdditionalID(value)) {573return (myNet->getAttributeCarriers()->retrieveAdditional(myTagProperty->getTag(), value, false) == nullptr);574} else {575return false;576}577}578579580bool581GNEAdditional::isValidAdditionalID(const std::vector<SumoXMLTag>& tags, const std::string& value) const {582if (isTemplate() && value.empty()) {583return true;584} else if (!isTemplate() && (value == getID())) {585return true;586} else if (SUMOXMLDefinitions::isValidAdditionalID(value)) {587return (myNet->getAttributeCarriers()->retrieveAdditionals(tags, value, false) == nullptr);588} else {589return false;590}591}592593594bool595GNEAdditional::isValidDetectorID(const std::string& value) const {596if (isTemplate() && value.empty()) {597return true;598} else if (!isTemplate() && (value == getID())) {599return true;600} else if (SUMOXMLDefinitions::isValidDetectorID(value)) {601return (myNet->getAttributeCarriers()->retrieveAdditional(myTagProperty->getTag(), value, false) == nullptr);602} else {603return false;604}605}606607608bool609GNEAdditional::isValidDetectorID(const std::vector<SumoXMLTag>& tags, const std::string& value) const {610if (!isTemplate() && (value == getID())) {611return true;612} else if (SUMOXMLDefinitions::isValidDetectorID(value)) {613return (myNet->getAttributeCarriers()->retrieveAdditionals(tags, value, false) == nullptr);614} else {615return false;616}617}618619620void621GNEAdditional::setAdditionalID(const std::string& newID) {622// update ID623if (isTemplate()) {624setMicrosimID(newID);625} else if ((myTagProperty->getTag() == SUMO_TAG_VAPORIZER) || !myTagProperty->hasAttribute(SUMO_ATTR_ID)) {626setMicrosimID(newID);627} else {628myNet->getAttributeCarriers()->updateAdditionalID(this, newID);629}630// change IDs of certain children631for (const auto& additionalChild : getChildAdditionals()) {632// get tag633const auto tag = additionalChild->getTagProperty()->getTag();634if ((tag == SUMO_TAG_ACCESS) || (tag == SUMO_TAG_PARKING_SPACE) ||635(tag == SUMO_TAG_DET_ENTRY) || (tag == SUMO_TAG_DET_EXIT)) {636additionalChild->setAdditionalID(getID());637}638}639// enable save demand elements if this additional has children640if (getChildDemandElements().size() > 0) {641myNet->getSavingStatus()->requireSaveDemandElements();642}643// enable save data elements if this additional has children644if (getChildGenericDatas().size() > 0) {645myNet->getSavingStatus()->requireSaveDataElements();646}647}648649650void651GNEAdditional::drawAdditionalID(const GUIVisualizationSettings& s) const {652if (s.addName.show(this) && (myAdditionalGeometry.getShape().size() > 0)) {653// calculate middle point654const double middlePoint = (myAdditionalGeometry.getShape().length2D() * 0.5);655// calculate position656const Position pos = (myAdditionalGeometry.getShape().size() == 1) ? myAdditionalGeometry.getShape().front() : myAdditionalGeometry.getShape().positionAtOffset2D(middlePoint);657// calculate rotation658const double rot = (myAdditionalGeometry.getShape().size() == 1) ? myAdditionalGeometry.getShapeRotations().front() : myAdditionalGeometry.getShape().rotationDegreeAtOffset(middlePoint);659// draw additional ID660if (myTagProperty->hasAttribute(SUMO_ATTR_LANE)) {661GLHelper::drawText(getMicrosimID(), pos, GLO_MAX - getType(), s.addName.scaledSize(s.scale), s.addName.color, s.getTextAngle(rot - 90));662} else {663GLHelper::drawText(getMicrosimID(), pos, GLO_MAX - getType(), s.addName.scaledSize(s.scale), s.addName.color, 0);664}665}666}667668669void670GNEAdditional::drawAdditionalName(const GUIVisualizationSettings& s) const {671if (s.addFullName.show(this) && (myAdditionalGeometry.getShape().size() > 0) && (myAdditionalName != "")) {672// calculate middle point673const double middlePoint = (myAdditionalGeometry.getShape().length2D() * 0.5);674// calculate position675const Position pos = (myAdditionalGeometry.getShape().size() == 1) ? myAdditionalGeometry.getShape().front() : myAdditionalGeometry.getShape().positionAtOffset2D(middlePoint);676// calculate rotation677const double rot = (myAdditionalGeometry.getShape().size() == 1) ? myAdditionalGeometry.getShapeRotations().front() : myAdditionalGeometry.getShape().rotationDegreeAtOffset(middlePoint);678// draw additional name679if (myTagProperty->hasAttribute(SUMO_ATTR_LANE)) {680GLHelper::drawText(myAdditionalName, pos, GLO_MAX - getType(), s.addFullName.scaledSize(s.scale), s.addFullName.color, s.getTextAngle(rot - 90));681} else {682GLHelper::drawText(myAdditionalName, pos, GLO_MAX - getType(), s.addFullName.scaledSize(s.scale), s.addFullName.color, 0);683}684}685}686687688void689GNEAdditional::replaceAdditionalParentEdges(const std::string& value) {690GNEHierarchicalElement::updateParents(this, parse<GNEHierarchicalContainerParents<GNEEdge*> >(getNet(), value));691}692693694void695GNEAdditional::replaceAdditionalParentLanes(const std::string& value) {696GNEHierarchicalElement::updateParents(this, parse<GNEHierarchicalContainerParents<GNELane*> >(getNet(), value));697698}699700701void702GNEAdditional::replaceAdditionalChildEdges(const std::string& value) {703GNEHierarchicalElement::updateChildren(this, parse<GNEHierarchicalContainerParents<GNEEdge*> >(getNet(), value));704}705706707void708GNEAdditional::replaceAdditionalChildLanes(const std::string& value) {709GNEHierarchicalElement::updateChildren(this, parse<GNEHierarchicalContainerParents<GNELane*> >(getNet(), value));710711}712713714void715GNEAdditional::replaceAdditionalParent(SumoXMLTag tag, const std::string& value, const int parentIndex) {716std::vector<GNEAdditional*> newParentAdditionals;717// special case for calibrators and routeprobes718if (value.size() > 0) {719newParentAdditionals = getParentAdditionals();720if ((newParentAdditionals.size() == 0) && (parentIndex == 0)) {721newParentAdditionals.push_back(myNet->getAttributeCarriers()->retrieveAdditional(tag, value));722} else {723newParentAdditionals[parentIndex] = myNet->getAttributeCarriers()->retrieveAdditional(tag, value);724}725}726GNEHierarchicalElement::updateParents(this, newParentAdditionals);727}728729730void731GNEAdditional::replaceDemandElementParent(SumoXMLTag tag, const std::string& value, const int parentIndex) {732auto newDemandElement = myNet->getAttributeCarriers()->retrieveDemandElement(tag, value);733GNEHierarchicalElement::updateParent(this, parentIndex, newDemandElement);734}735736737void738GNEAdditional::shiftLaneIndex() {739const std::vector<GNELane*> newLanes = {getParentLanes().front()->getParentEdge()->getChildLanes().at(getParentLanes().front()->getIndex() + 1)};740GNEHierarchicalElement::updateParents(this, newLanes);741}742743744void745GNEAdditional::calculatePerpendicularLine(const double endLaneposition) {746if (getParentEdges().empty()) {747throw ProcessError(TL("Invalid number of edges"));748} else {749// get lanes750const GNELane* firstLane = getParentEdges().front()->getChildLanes().front();751const GNELane* lastLane = getParentEdges().front()->getChildLanes().back();752// get first and back lane shapes753PositionVector firstLaneShape = firstLane->getLaneShape();754PositionVector lastLaneShape = lastLane->getLaneShape();755// move shapes756firstLaneShape.move2side((firstLane->getParentEdge()->getNBEdge()->getLaneWidth(firstLane->getIndex()) * 0.5) + 1);757lastLaneShape.move2side(lastLane->getParentEdge()->getNBEdge()->getLaneWidth(lastLane->getIndex()) * -0.5);758// calculate lane postion759const double lanePosition = firstLaneShape.length2D() >= endLaneposition ? endLaneposition : firstLaneShape.length2D();760// update geometry761myAdditionalGeometry.updateGeometry({firstLaneShape.positionAtOffset2D(lanePosition), lastLaneShape.positionAtOffset2D(lanePosition)});762}763}764765766void767GNEAdditional::drawDemandElementChildren(const GUIVisualizationSettings& s) const {768// draw child demand elements769for (const auto& demandElement : getChildDemandElements()) {770if (!demandElement->getTagProperty()->isPlacedInRTree()) {771demandElement->drawGL(s);772}773}774}775776777std::string778GNEAdditional::getJuPedSimType(SumoXMLTag tag) {779// continue depending of tag780switch (tag) {781case GNE_TAG_JPS_WALKABLEAREA:782return "jupedsim.walkable_area";783case GNE_TAG_JPS_OBSTACLE:784return "jupedsim.obstacle";785default:786throw InvalidArgument("Invalid JuPedSim tag");787}788}789790791RGBColor792GNEAdditional::getJuPedSimColor(SumoXMLTag tag) {793// continue depending of tag794switch (tag) {795case GNE_TAG_JPS_WALKABLEAREA:796return RGBColor(179, 217, 255);797case GNE_TAG_JPS_OBSTACLE:798return RGBColor(255, 204, 204);799default:800throw InvalidArgument("Invalid JuPedSim tag");801}802}803804805bool806GNEAdditional::getJuPedSimFill(SumoXMLTag tag) {807// continue depending of tag808switch (tag) {809case GNE_TAG_JPS_WALKABLEAREA:810case GNE_TAG_JPS_OBSTACLE:811return true;812default:813throw InvalidArgument("Invalid JuPedSim tag");814}815}816817818double819GNEAdditional::getJuPedSimLayer(SumoXMLTag tag) {820// continue depending of tag821switch (tag) {822case GNE_TAG_JPS_WALKABLEAREA:823return 1;824case GNE_TAG_JPS_OBSTACLE:825return 2;826default:827throw InvalidArgument("Invalid JuPedSim tag");828}829}830831832void833GNEAdditional::calculateContourPolygons(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,834const double layer, const double exaggeration, const bool filledShape) const {835// calculate contour depending of contoured shape836if (filledShape) {837myAdditionalContour.calculateContourClosedShape(s, d, this, myAdditionalGeometry.getShape(), layer, 1, nullptr);838} else {839myAdditionalContour.calculateContourExtrudedShape(s, d, this, myAdditionalGeometry.getShape(), layer,840s.neteditSizeSettings.polylineWidth, exaggeration, true, true, 0, nullptr, nullptr);841}842// get edit modes843const auto& editModes = myNet->getViewNet()->getEditModes();844// check if draw geometry points845if (editModes.isCurrentSupermodeNetwork() && !myNet->getViewParent()->getMoveFrame()->getNetworkMoveOptions()->getMoveWholePolygons()) {846// check if we're in move mode847const bool moveMode = (editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE);848// get geometry point radius (size depends if we're in move mode)849const double geometryPointRaidus = s.neteditSizeSettings.polygonGeometryPointRadius * (moveMode ? 1 : 0.5);850// calculate contour geometry points851myAdditionalContour.calculateContourAllGeometryPoints(s, d, this, myAdditionalGeometry.getShape(), layer, geometryPointRaidus, exaggeration, moveMode);852}853}854855856GNELane*857GNEAdditional::getFirstPathLane() const {858return getParentLanes().front();859}860861862GNELane*863GNEAdditional::getLastPathLane() const {864return getParentLanes().back();865}866867868void869GNEAdditional::drawParentChildLines(const GUIVisualizationSettings& s, const RGBColor& color, const bool onlySymbols) const {870const auto& inspectedElements = myNet->getViewNet()->getInspectedElements();871// check if current additional is inspected, front or selected872const bool currentDrawEntire = inspectedElements.isACInspected(this) || myDrawInFront || isAttributeCarrierSelected();873// push layer matrix874GLHelper::pushMatrix();875// translate to parentChildLine layer876glTranslated(0, 0, GLO_PARENTCHILDLINE);877// iterate over parent additionals878for (const auto& parent : getParentAdditionals()) {879// get inspected flag880const bool parentInspected = inspectedElements.isACInspected(parent);881// draw parent lines882GUIGeometry::drawParentLine(s, getPositionInView(), parent->getPositionInView(),883(isAttributeCarrierSelected() || parent->isAttributeCarrierSelected()) ? s.additionalSettings.connectionColorSelected : color,884currentDrawEntire || parentInspected || parent->isAttributeCarrierSelected(), .05);885}886// special case for Parking area reroutes887if (getTagProperty()->getTag() == SUMO_TAG_REROUTER) {888// iterate over rerouter elements889for (const auto& rerouterInterval : getChildAdditionals()) {890for (const auto& rerouterElement : rerouterInterval->getChildAdditionals()) {891if (rerouterElement->getTagProperty()->getTag() == SUMO_TAG_PARKING_AREA_REROUTE) {892// get parking area893const auto parkingArea = rerouterElement->getParentAdditionals().at(1);894// get inspected flag895const bool parkingAreaInspected = inspectedElements.isACInspected(parkingArea);896// draw parent lines897GUIGeometry::drawParentLine(s, getPositionInView(), parkingArea->getPositionInView(),898(isAttributeCarrierSelected() || parkingArea->isAttributeCarrierSelected()) ? s.additionalSettings.connectionColorSelected : color,899currentDrawEntire || parkingAreaInspected || parkingArea->isAttributeCarrierSelected(), .05);900}901}902}903}904// iterate over child additionals905for (const auto& child : getChildAdditionals()) {906// get inspected flag907const bool childInspected = inspectedElements.isACInspected(child);908// special case for parking zone reroute909if (child->getTagProperty()->getTag() == SUMO_TAG_PARKING_AREA_REROUTE) {910// draw child line between parking area and rerouter911GUIGeometry::drawChildLine(s, getPositionInView(), child->getParentAdditionals().front()->getParentAdditionals().front()->getPositionInView(),912(isAttributeCarrierSelected() || child->isAttributeCarrierSelected()) ? s.additionalSettings.connectionColorSelected : color,913currentDrawEntire || childInspected || child->isAttributeCarrierSelected(), .05);914} else if (!onlySymbols || child->getTagProperty()->isSymbol()) {915// draw child line916GUIGeometry::drawChildLine(s, getPositionInView(), child->getPositionInView(),917(isAttributeCarrierSelected() || child->isAttributeCarrierSelected()) ? s.additionalSettings.connectionColorSelected : color,918currentDrawEntire || childInspected || child->isAttributeCarrierSelected(), .05);919}920}921// pop layer matrix922GLHelper::popMatrix();923}924925926void927GNEAdditional::drawUpGeometryPoint(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d, const Position& pos,928const double rot, const RGBColor& baseColor, const bool ignoreShift) const {929drawSemiCircleGeometryPoint(s, d, pos, rot, baseColor, -90, 90, ignoreShift);930}931932void933GNEAdditional::drawDownGeometryPoint(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d, const Position& pos,934const double rot, const RGBColor& baseColor, const bool ignoreShift) const {935drawSemiCircleGeometryPoint(s, d, pos, rot, baseColor, 90, 270, ignoreShift);936}937938void939GNEAdditional::drawLeftGeometryPoint(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d, const Position& pos,940const double rot, const RGBColor& baseColor, const bool ignoreShift) const {941drawSemiCircleGeometryPoint(s, d, pos, rot, baseColor, -90, 90, ignoreShift);942}943944945void946GNEAdditional::drawRightGeometryPoint(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d, const Position& pos,947const double rot, const RGBColor& baseColor, const bool ignoreShift) const {948drawSemiCircleGeometryPoint(s, d, pos, rot, baseColor, 270, 90, ignoreShift);949}950951952bool953GNEAdditional::areLaneConsecutives(const std::vector<GNELane*>& lanes) {954// declare lane iterator955int laneIt = 0;956// iterate over all lanes957while (laneIt < ((int)lanes.size() - 1)) {958// we assume that lanes aren't consecutive959bool consecutiveFound = false;960// get lanes961const auto lane = lanes.at(laneIt);962const auto nextLane = lanes.at(laneIt + 1);963// if there is a connection between "from" lane and "to" lane of connection, change connectionFound to true964for (const auto& outgoingEdge : lane->getParentEdge()->getToJunction()->getGNEOutgoingEdges()) {965for (const auto& outgoingLane : outgoingEdge->getChildLanes()) {966if (outgoingLane == nextLane) {967consecutiveFound = true;968}969}970}971// abort if consecutiveFound is false972if (!consecutiveFound) {973return false;974}975// update iterator976laneIt++;977}978// lanes are consecutive, then return true979return true;980}981982983bool984GNEAdditional::areLaneConnected(const std::vector<GNELane*>& lanes) {985// declare lane iterator986int laneIt = 0;987// iterate over all lanes, and stop if myE2valid is false988while (laneIt < ((int)lanes.size() - 1)) {989// we assume that E2 is invalid990bool connectionFound = false;991// get lanes992const auto lane = lanes.at(laneIt);993const auto nextLane = lanes.at(laneIt + 1);994// check if both lanes are sidewalks995if ((lane->getAttribute(SUMO_ATTR_ALLOW) == "pedestrian") && (nextLane->getAttribute(SUMO_ATTR_ALLOW) == "pedestrian")) {996connectionFound = true;997}998// if there is a connection between "from" lane and "to" lane of connection, change connectionFound to true999for (const auto& connection : lane->getParentEdge()->getNBEdge()->getConnections()) {1000if ((connection.toEdge == nextLane->getParentEdge()->getNBEdge()) &&1001(connection.fromLane == lane->getIndex()) &&1002(connection.toLane == nextLane->getIndex())) {1003connectionFound = true;1004}1005}1006// abort if connectionFound is false1007if (!connectionFound) {1008return false;1009}1010// update iterator1011laneIt++;1012}1013// there are connections between all lanes, then return true1014return true;1015}101610171018bool1019GNEAdditional::checkChildAdditionalRestriction() const {1020// throw exception because this function mus be implemented in child (see GNEE3Detector)1021throw ProcessError(StringUtils::format("Calling non-implemented function checkChildAdditionalRestriction during saving of %. It muss be reimplemented in child class", getTagStr()));1022}102310241025void1026GNEAdditional::drawSemiCircleGeometryPoint(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,1027const Position& pos, const double rot, const RGBColor& baseColor, const double fromAngle, const double toAngle,1028const bool /* ignoreShift */) const {1029// check if draw geometry point1030if (!s.drawForViewObjectsHandler && (d <= GUIVisualizationSettings::Detail::GeometryPoint)) {1031// push matrix1032GLHelper::pushMatrix();1033// translated to front1034glTranslated(0, 0, 0.1);1035// set color depending if check if mouse is over element1036GLHelper::setColor(baseColor.changedBrightness(-50));1037// translate and rotate1038glTranslated(pos.x(), pos.y(), 0.1);1039glRotated(rot, 0, 0, 1);1040// draw geometry point1041GLHelper::drawFilledCircleDetailled(d, s.neteditSizeSettings.additionalGeometryPointRadius, fromAngle, toAngle);1042// pop geometry point matrix1043GLHelper::popMatrix();1044}1045}10461047/****************************************************************************/104810491050