Path: blob/main/src/netedit/elements/additional/GNEMultiEntryExitDetector.cpp
169684 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 GNEMultiEntryExitDetector.cpp14/// @author Pablo Alvarez Lopez15/// @date Nov 201516///17// multi entry-exit (E3) detector18/****************************************************************************/19#include <config.h>2021#include <netedit/GNENet.h>22#include <netedit/GNETagProperties.h>23#include <netedit/GNEUndoList.h>24#include <netedit/GNEViewNet.h>25#include <netedit/GNEViewParent.h>26#include <netedit/changes/GNEChange_Attribute.h>27#include <netedit/frames/network/GNETLSEditorFrame.h>2829#include "GNEMultiEntryExitDetector.h"3031// ===========================================================================32// member method definitions33// ===========================================================================3435GNEMultiEntryExitDetector::GNEMultiEntryExitDetector(GNENet* net) :36GNEAdditional("", net, "", SUMO_TAG_ENTRY_EXIT_DETECTOR, "") {37}383940GNEMultiEntryExitDetector::GNEMultiEntryExitDetector(const std::string& id, GNENet* net, const std::string& filename, const Position pos, const SUMOTime freq,41const std::string& outputFilename, const std::vector<std::string>& vehicleTypes, const std::vector<std::string>& nextEdges, const std::string& detectPersons,42const std::string& name, const SUMOTime timeThreshold, const double speedThreshold, const bool openEntry, const bool expectedArrival, const Parameterised::Map& parameters) :43GNEAdditional(id, net, filename, SUMO_TAG_ENTRY_EXIT_DETECTOR, name),44Parameterised(parameters),45myPosition(pos),46myPeriod(freq),47myOutputFilename(outputFilename),48myVehicleTypes(vehicleTypes),49myNextEdges(nextEdges),50myDetectPersons(detectPersons),51myTimeThreshold(timeThreshold),52mySpeedThreshold(speedThreshold),53myOpenEntry(openEntry),54myExpectedArrival(expectedArrival) {55// update centering boundary without updating grid56updateCenteringBoundary(false);57// set default output filename58if (outputFilename.empty()) {59myOutputFilename = id + ".xml";60}61}626364GNEMultiEntryExitDetector::~GNEMultiEntryExitDetector() {}656667GNEMoveOperation*68GNEMultiEntryExitDetector::getMoveOperation() {69// return move operation for additional placed in view70return new GNEMoveOperation(this, myPosition);71}727374void75GNEMultiEntryExitDetector::writeAdditional(OutputDevice& device) const {76bool entry = false;77bool exit = false;78// first check if E3 has at least one entry and one exit79for (const auto& additionalChild : getChildAdditionals()) {80if (additionalChild->getTagProperty()->getTag() == SUMO_TAG_DET_ENTRY) {81entry = true;82} else if (additionalChild->getTagProperty()->getTag() == SUMO_TAG_DET_EXIT) {83exit = true;84}85}86// check entry/exits87if (entry && exit) {88device.openTag(getTagProperty()->getTag());89device.writeAttr(SUMO_ATTR_ID, getID());90if (!myAdditionalName.empty()) {91device.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(myAdditionalName));92}93device.writeAttr(SUMO_ATTR_POSITION, myPosition);94if (getAttribute(SUMO_ATTR_PERIOD).size() > 0) {95device.writeAttr(SUMO_ATTR_PERIOD, time2string(myPeriod));96}97if (myOutputFilename.size() > 0) {98device.writeAttr(SUMO_ATTR_FILE, myOutputFilename);99}100if (myVehicleTypes.size() > 0) {101device.writeAttr(SUMO_ATTR_VTYPES, myVehicleTypes);102}103if (myTimeThreshold != myTagProperty->getDefaultTimeValue(SUMO_ATTR_HALTING_TIME_THRESHOLD)) {104device.writeAttr(SUMO_ATTR_HALTING_TIME_THRESHOLD, time2string(myTimeThreshold));105}106if (mySpeedThreshold != myTagProperty->getDefaultDoubleValue(SUMO_ATTR_HALTING_SPEED_THRESHOLD)) {107device.writeAttr(SUMO_ATTR_HALTING_SPEED_THRESHOLD, mySpeedThreshold);108}109if (myExpectedArrival != myTagProperty->getDefaultBoolValue(SUMO_ATTR_EXPECT_ARRIVAL)) {110device.writeAttr(SUMO_ATTR_EXPECT_ARRIVAL, myExpectedArrival);111}112if (myOpenEntry != myTagProperty->getDefaultBoolValue(SUMO_ATTR_OPEN_ENTRY)) {113device.writeAttr(SUMO_ATTR_OPEN_ENTRY, myOpenEntry);114}115// write all entry/exits116for (const auto& access : getChildAdditionals()) {117access->writeAdditional(device);118}119// write parameters (Always after children to avoid problems with additionals.xsd)120writeParams(device);121device.closeTag();122} else {123WRITE_WARNING("E3 '" + getID() + TL("' needs at least one entry and one exit"));124}125}126127128bool129GNEMultiEntryExitDetector::isAdditionalValid() const {130return true;131}132133134std::string135GNEMultiEntryExitDetector::getAdditionalProblem() const {136return "";137}138139140void141GNEMultiEntryExitDetector::fixAdditionalProblem() {142// nothing to fix143}144145146bool147GNEMultiEntryExitDetector::checkDrawMoveContour() const {148// get edit modes149const auto& editModes = myNet->getViewNet()->getEditModes();150// check if we're in move mode151if (!myNet->getViewNet()->isCurrentlyMovingElements() && editModes.isCurrentSupermodeNetwork() &&152!myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement() &&153(editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) && myNet->getViewNet()->checkOverLockedElement(this, mySelected)) {154// only move the first element155return myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == this;156} else {157return false;158}159}160161162void163GNEMultiEntryExitDetector::updateGeometry() {164// update additional geometry165myAdditionalGeometry.updateSinglePosGeometry(myPosition, 0);166}167168169Position170GNEMultiEntryExitDetector::getPositionInView() const {171return myPosition;172}173174175void176GNEMultiEntryExitDetector::updateCenteringBoundary(const bool updateGrid) {177// remove additional from grid178if (updateGrid) {179myNet->removeGLObjectFromGrid(this);180}181// now update geometry182updateGeometry();183// add shape boundary184myAdditionalBoundary = myAdditionalGeometry.getShape().getBoxBoundary();185// grow186myAdditionalBoundary.grow(5);187// add additional into RTREE again188if (updateGrid) {189myNet->addGLObjectIntoGrid(this);190}191}192193194void195GNEMultiEntryExitDetector::splitEdgeGeometry(const double /*splitPosition*/, const GNENetworkElement* /*originalElement*/, const GNENetworkElement* /*newElement*/, GNEUndoList* /*undoList*/) {196// geometry of this element cannot be splitted197}198199200std::string201GNEMultiEntryExitDetector::getParentName() const {202return myNet->getMicrosimID();203}204205206void207GNEMultiEntryExitDetector::drawGL(const GUIVisualizationSettings& s) const {208// first check if additional has to be drawn209if (myNet->getViewNet()->getDataViewOptions().showAdditionals() &&210!myNet->getViewNet()->selectingDetectorsTLSMode()) {211// draw parent and child lines212drawParentChildLines(s, s.additionalSettings.connectionColor);213// draw E3214drawSquaredAdditional(s, myPosition, s.detectorSettings.E3Size, GUITexture::E3, GUITexture::E3_SELECTED);215}216}217218219std::string220GNEMultiEntryExitDetector::getAttribute(SumoXMLAttr key) const {221switch (key) {222case SUMO_ATTR_ID:223return getMicrosimID();224case SUMO_ATTR_POSITION:225return toString(myPosition);226case SUMO_ATTR_PERIOD:227if (myPeriod == SUMOTime_MAX_PERIOD) {228return "";229} else {230return time2string(myPeriod);231}232case SUMO_ATTR_NAME:233return myAdditionalName;234case SUMO_ATTR_FILE:235return myOutputFilename;236case SUMO_ATTR_VTYPES:237return toString(myVehicleTypes);238case SUMO_ATTR_NEXT_EDGES:239return toString(myNextEdges);240case SUMO_ATTR_DETECT_PERSONS:241return toString(myDetectPersons);242case SUMO_ATTR_HALTING_TIME_THRESHOLD:243return time2string(myTimeThreshold);244case SUMO_ATTR_HALTING_SPEED_THRESHOLD:245return toString(mySpeedThreshold);246case SUMO_ATTR_OPEN_ENTRY:247return toString(myOpenEntry);248case SUMO_ATTR_EXPECT_ARRIVAL:249return toString(myExpectedArrival);250default:251return getCommonAttribute(this, key);252}253}254255256double257GNEMultiEntryExitDetector::getAttributeDouble(SumoXMLAttr key) const {258throw InvalidArgument(getTagStr() + " doesn't have a double attribute of type '" + toString(key) + "'");259}260261262const Parameterised::Map&263GNEMultiEntryExitDetector::getACParametersMap() const {264return getParametersMap();265}266267268void269GNEMultiEntryExitDetector::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {270if (value == getAttribute(key)) {271return; //avoid needless changes, later logic relies on the fact that attributes have changed272}273switch (key) {274case SUMO_ATTR_ID:275case SUMO_ATTR_PERIOD:276case SUMO_ATTR_POSITION:277case SUMO_ATTR_NAME:278case SUMO_ATTR_FILE:279case SUMO_ATTR_VTYPES:280case SUMO_ATTR_NEXT_EDGES:281case SUMO_ATTR_DETECT_PERSONS:282case SUMO_ATTR_HALTING_TIME_THRESHOLD:283case SUMO_ATTR_HALTING_SPEED_THRESHOLD:284case SUMO_ATTR_OPEN_ENTRY:285case SUMO_ATTR_EXPECT_ARRIVAL:286GNEChange_Attribute::changeAttribute(this, key, value, undoList);287break;288default:289setCommonAttribute(key, value, undoList);290break;291}292}293294295bool296GNEMultiEntryExitDetector::isValid(SumoXMLAttr key, const std::string& value) {297switch (key) {298case SUMO_ATTR_ID:299return isValidDetectorID(value);300case SUMO_ATTR_POSITION:301return canParse<Position>(value);302case SUMO_ATTR_PERIOD:303if (value.empty()) {304return true;305} else {306return (canParse<double>(value) && (parse<double>(value) >= 0));307}308case SUMO_ATTR_NAME:309return SUMOXMLDefinitions::isValidAttribute(value);310case SUMO_ATTR_FILE:311return SUMOXMLDefinitions::isValidFilename(value);312case SUMO_ATTR_VTYPES:313if (value.empty()) {314return true;315} else {316return SUMOXMLDefinitions::isValidListOfTypeID(value);317}318case SUMO_ATTR_NEXT_EDGES:319if (value.empty()) {320return true;321} else {322return SUMOXMLDefinitions::isValidListOfNetIDs(value);323}324case SUMO_ATTR_DETECT_PERSONS:325if (value.empty()) {326return true;327} else {328return SUMOXMLDefinitions::PersonModeValues.hasString(value);329}330case SUMO_ATTR_HALTING_TIME_THRESHOLD:331case SUMO_ATTR_HALTING_SPEED_THRESHOLD:332return canParse<double>(value) && (parse<double>(value) >= 0);333case SUMO_ATTR_OPEN_ENTRY:334case SUMO_ATTR_EXPECT_ARRIVAL:335return canParse<bool>(value);336default:337return isCommonValid(key, value);338}339}340341342bool343GNEMultiEntryExitDetector::checkChildAdditionalRestriction() const {344int numEntrys = 0;345int numExits = 0;346// iterate over additional chidls and obtain number of entrys and exits347for (auto i : getChildAdditionals()) {348if (i->getTagProperty()->getTag() == SUMO_TAG_DET_ENTRY) {349numEntrys++;350} else if (i->getTagProperty()->getTag() == SUMO_TAG_DET_EXIT) {351numExits++;352}353}354// write warnings355if (numEntrys == 0) {356WRITE_WARNING(TL("An entry-exit detector needs at least one entry detector"));357}358if (numExits == 0) {359WRITE_WARNING(TL("An entry-exit detector needs at least one exit detector"));360}361// return false depending of number of Entrys and Exits362return ((numEntrys != 0) && (numExits != 0));363}364365366std::string367GNEMultiEntryExitDetector::getPopUpID() const {368return getTagStr() + ":" + getID();369}370371372std::string373GNEMultiEntryExitDetector::getHierarchyName() const {374return getTagStr();375}376377// ===========================================================================378// private379// ===========================================================================380381void382GNEMultiEntryExitDetector::setAttribute(SumoXMLAttr key, const std::string& value) {383switch (key) {384case SUMO_ATTR_ID:385// update microsimID386setAdditionalID(value);387break;388case SUMO_ATTR_POSITION:389myPosition = parse<Position>(value);390// update boundary (except for template)391if (getID().size() > 0) {392updateCenteringBoundary(true);393}394break;395case SUMO_ATTR_PERIOD:396if (value.empty()) {397myPeriod = SUMOTime_MAX_PERIOD;398} else {399myPeriod = string2time(value);400}401break;402case SUMO_ATTR_NAME:403myAdditionalName = value;404break;405case SUMO_ATTR_FILE:406myOutputFilename = value;407break;408case SUMO_ATTR_VTYPES:409myVehicleTypes = parse<std::vector<std::string> >(value);410break;411case SUMO_ATTR_NEXT_EDGES:412myNextEdges = parse<std::vector<std::string> >(value);413break;414case SUMO_ATTR_DETECT_PERSONS:415myDetectPersons = value;416break;417case SUMO_ATTR_HALTING_TIME_THRESHOLD:418myTimeThreshold = parse<SUMOTime>(value);419break;420case SUMO_ATTR_HALTING_SPEED_THRESHOLD:421mySpeedThreshold = parse<double>(value);422break;423case SUMO_ATTR_OPEN_ENTRY:424myOpenEntry = parse<bool>(value);425break;426case SUMO_ATTR_EXPECT_ARRIVAL:427myExpectedArrival = parse<bool>(value);428break;429default:430setCommonAttribute(this, key, value);431break;432}433}434435436void437GNEMultiEntryExitDetector::setMoveShape(const GNEMoveResult& moveResult) {438// update position439myPosition = moveResult.shapeToUpdate.front();440// update geometry441updateGeometry();442}443444445void446GNEMultiEntryExitDetector::commitMoveShape(const GNEMoveResult& moveResult, GNEUndoList* undoList) {447undoList->begin(this, "position of " + getTagStr());448GNEChange_Attribute::changeAttribute(this, SUMO_ATTR_POSITION, toString(moveResult.shapeToUpdate.front()), undoList);449undoList->end();450}451452453/****************************************************************************/454455456