Path: blob/main/src/netedit/elements/additional/GNEDetector.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 GNEDetector.cpp14/// @author Pablo Alvarez Lopez15/// @date Nov 201516///17//18/****************************************************************************/19#include <config.h>2021#include <netedit/GNENet.h>22#include <netedit/GNETagProperties.h>23#include <netedit/GNESegment.h>24#include <netedit/GNEViewNet.h>25#include <netedit/GNEViewParent.h>26#include <netedit/changes/GNEChange_Attribute.h>27#include <netedit/elements/additional/GNEAdditionalHandler.h>28#include <netedit/frames/common/GNEMoveFrame.h>29#include <utils/gui/div/GLHelper.h>30#include <utils/gui/globjects/GLIncludes.h>3132#include "GNEDetector.h"3334// ===========================================================================35// member method definitions36// ===========================================================================3738GNEDetector::GNEDetector(GNENet* net, SumoXMLTag tag) :39GNEAdditional("", net, "", tag, "") {40}414243GNEDetector::GNEDetector(const std::string& id, GNENet* net, const std::string& filename, SumoXMLTag tag, const double pos, const SUMOTime period,44GNELane* lane, const std::string& outputFilename, const std::vector<std::string>& vehicleTypes, const std::vector<std::string>& nextEdges,45const std::string& detectPersons, const std::string& name, const bool friendlyPos, const Parameterised::Map& parameters) :46GNEAdditional(id, net, filename, tag, name),47Parameterised(parameters),48myPositionOverLane(pos),49myPeriod(period),50myOutputFilename(outputFilename),51myVehicleTypes(vehicleTypes),52myNextEdges(nextEdges),53myDetectPersons(detectPersons),54myFriendlyPosition(friendlyPos) {55// update output filename56if (outputFilename.empty()) {57myOutputFilename = id + ".xml";58}59// set parents60setParent<GNELane*>(lane);61}626364GNEDetector::GNEDetector(const std::string& id, GNENet* net, const std::string& filename, SumoXMLTag tag, const double pos, const SUMOTime period,65const std::vector<GNELane*>& lanes, const std::string& outputFilename, const std::vector<std::string>& vehicleTypes,66const std::vector<std::string>& nextEdges, const std::string& detectPersons, const std::string& name, const bool friendlyPos,67const Parameterised::Map& parameters) :68GNEAdditional(id, net, filename, tag, name),69Parameterised(parameters),70myPositionOverLane(pos),71myPeriod(period),72myOutputFilename(outputFilename),73myVehicleTypes(vehicleTypes),74myNextEdges(nextEdges),75myDetectPersons(detectPersons),76myFriendlyPosition(friendlyPos) {77// update output filename78if (outputFilename.empty()) {79myOutputFilename = id + ".xml";80}81// set parents82setParents<GNELane*>(lanes);83}848586GNEDetector::GNEDetector(GNEAdditional* additionalParent, SumoXMLTag tag, const double pos, const SUMOTime period, GNELane* lane,87const std::string& outputFilename, const std::string& name, const bool friendlyPos, const Parameterised::Map& parameters) :88GNEAdditional(additionalParent, tag, name),89Parameterised(parameters),90myPositionOverLane(pos),91myPeriod(period),92myOutputFilename(outputFilename),93myFriendlyPosition(friendlyPos) {94// set parents95setParent<GNELane*>(lane);96setParent<GNEAdditional*>(additionalParent);97}9899100GNEDetector::~GNEDetector() {}101102103GNEMoveOperation*104GNEDetector::getMoveOperation() {105// check modes and detector type106if (!myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork() || (myNet->getViewNet()->getEditModes().networkEditMode != NetworkEditMode::NETWORK_MOVE)) {107return nullptr;108} else if (myTagProperty->getTag() == SUMO_TAG_LANE_AREA_DETECTOR) {109return getMoveOperationSingleLane(myPositionOverLane, getAttributeDouble(SUMO_ATTR_ENDPOS));110} else if (myTagProperty->getTag() == GNE_TAG_MULTI_LANE_AREA_DETECTOR) {111return getMoveOperationMultiLane(myPositionOverLane, getAttributeDouble(SUMO_ATTR_ENDPOS));112} else {113// return move operation for detectors with single position placed over shape (E1, EntryExits..)114return new GNEMoveOperation(this, getParentLanes().front(), myPositionOverLane,115myNet->getViewNet()->getViewParent()->getMoveFrame()->getCommonMoveOptions()->getAllowChangeLane());116}117}118119120double121GNEDetector::getPositionOverLane() const {122return myPositionOverLane;123}124125126bool127GNEDetector::checkDrawMoveContour() const {128// get edit modes129const auto& editModes = myNet->getViewNet()->getEditModes();130// check if we're in move mode131if (!myNet->getViewNet()->isCurrentlyMovingElements() && editModes.isCurrentSupermodeNetwork() &&132!myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement() &&133(editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) && myNet->getViewNet()->checkOverLockedElement(this, mySelected)) {134// only move the first element135if (myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == this) {136// special case for multiple lane area detectors137if (myTagProperty->getTag() == GNE_TAG_MULTI_LANE_AREA_DETECTOR) {138auto segment = gViewObjectsHandler.getSelectedSegment(this);139if (segment && segment->getJunction()) {140return false;141} else if (segment && segment->getLane()) {142// ensure that is the first or the last lane143if (segment->getLaneIndex() == 0) {144return true;145} else if (segment->getLaneIndex() == ((int)getParentLanes().size() - 1)) {146return true;147}148} else {149// this is the start or end point150return true;151}152} else {153return true;154}155}156}157return false;158}159160161GNELane*162GNEDetector::getLane() const {163return getParentLanes().front();164}165166167Position168GNEDetector::getPositionInView() const {169return myAdditionalGeometry.getShape().getPolygonCenter();170}171172173void174GNEDetector::updateCenteringBoundary(const bool /*updateGrid*/) {175// nothing to update176}177178void179GNEDetector::splitEdgeGeometry(const double splitPosition, const GNENetworkElement* originalElement,180const GNENetworkElement* newElement, GNEUndoList* undoList) {181// only split geometry of E2 multilane detectors182if (myTagProperty->getTag() == GNE_TAG_MULTI_LANE_AREA_DETECTOR) {183// obtain new list of E2 lanes184std::string newE2Lanes = getNewListOfParents(originalElement, newElement);185// update E2 Lanes186if (newE2Lanes.size() > 0) {187setAttribute(SUMO_ATTR_LANES, newE2Lanes, undoList);188}189} else if (splitPosition < myPositionOverLane) {190// change lane191setAttribute(SUMO_ATTR_LANE, newElement->getID(), undoList);192// now adjust start position193setAttribute(SUMO_ATTR_POSITION, toString(myPositionOverLane - splitPosition), undoList);194}195}196197198double199GNEDetector::getGeometryPositionOverLane() const {200double fixedPos = myPositionOverLane;201const double len = getLane()->getParentEdge()->getNBEdge()->getFinalLength();202double length = 0;203GNEAdditionalHandler::fixLanePosition(fixedPos, length, len);204return (fixedPos * getLane()->getLengthGeometryFactor());205}206207208209std::string210GNEDetector::getParentName() const {211return getLane()->getID();212}213214215const Parameterised::Map&216GNEDetector::getACParametersMap() const {217return getParametersMap();218}219220221std::string222GNEDetector::getPopUpID() const {223return getTagStr() + ": " + getID();224}225226227std::string228GNEDetector::getHierarchyName() const {229return getTagStr();230}231232233std::string234GNEDetector::getDetectorAttribute(SumoXMLAttr key) const {235switch (key) {236case SUMO_ATTR_ID:237return getMicrosimID();238case SUMO_ATTR_LANE:239return getParentLanes().front()->getID();240case SUMO_ATTR_POSITION:241return toString(myPositionOverLane);242case SUMO_ATTR_PERIOD:243if (myPeriod == SUMOTime_MAX_PERIOD) {244return "";245} else {246return time2string(myPeriod);247}248case SUMO_ATTR_NAME:249return myAdditionalName;250case SUMO_ATTR_FILE:251return myOutputFilename;252case SUMO_ATTR_VTYPES:253return toString(myVehicleTypes);254case SUMO_ATTR_NEXT_EDGES:255return toString(myNextEdges);256case SUMO_ATTR_DETECT_PERSONS:257return toString(myDetectPersons);258case SUMO_ATTR_FRIENDLY_POS:259return toString(myFriendlyPosition);260case GNE_ATTR_SHIFTLANEINDEX:261return "";262default:263return getCommonAttribute(this, key);264}265}266267268double269GNEDetector::getDetectorAttributeDouble(SumoXMLAttr key) const {270switch (key) {271case SUMO_ATTR_POSITION:272return myPositionOverLane;273default:274throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");275}276}277278279void280GNEDetector::setDetectorAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {281switch (key) {282case SUMO_ATTR_ID:283case SUMO_ATTR_LANE:284case SUMO_ATTR_POSITION:285case SUMO_ATTR_PERIOD:286case SUMO_ATTR_NAME:287case SUMO_ATTR_FILE:288case SUMO_ATTR_VTYPES:289case SUMO_ATTR_NEXT_EDGES:290case SUMO_ATTR_DETECT_PERSONS:291case SUMO_ATTR_FRIENDLY_POS:292case GNE_ATTR_SHIFTLANEINDEX:293GNEChange_Attribute::changeAttribute(this, key, value, undoList);294break;295default:296setCommonAttribute(key, value, undoList);297break;298}299}300301302303bool304GNEDetector::isDetectorValid(SumoXMLAttr key, const std::string& value) {305switch (key) {306case SUMO_ATTR_ID:307return isValidDetectorID(value);308case SUMO_ATTR_LANE:309if (myNet->getAttributeCarriers()->retrieveLane(value, false) != nullptr) {310return true;311} else {312return false;313}314case SUMO_ATTR_POSITION:315return canParse<double>(value) && fabs(parse<double>(value)) < getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength();316case SUMO_ATTR_PERIOD:317if (value.empty()) {318return true;319} else {320return (canParse<double>(value) && (parse<double>(value) >= 0));321}322case SUMO_ATTR_NAME:323return SUMOXMLDefinitions::isValidAttribute(value);324case SUMO_ATTR_FILE:325return SUMOXMLDefinitions::isValidFilename(value);326case SUMO_ATTR_VTYPES:327if (value.empty()) {328return true;329} else {330return SUMOXMLDefinitions::isValidListOfTypeID(value);331}332case SUMO_ATTR_NEXT_EDGES:333if (value.empty()) {334return true;335} else {336return SUMOXMLDefinitions::isValidListOfNetIDs(value);337}338case SUMO_ATTR_DETECT_PERSONS:339if (value.empty()) {340return true;341} else {342return SUMOXMLDefinitions::PersonModeValues.hasString(value);343}344case SUMO_ATTR_FRIENDLY_POS:345return canParse<bool>(value);346default:347return isCommonValid(key, value);348}349}350351352void353GNEDetector::writeDetectorValues(OutputDevice& device) const {354if ((myPeriod > 0) && (myPeriod != SUMOTime_MAX_PERIOD)) {355device.writeAttr(SUMO_ATTR_PERIOD, time2string(myPeriod));356}357if (myAdditionalName.size() > 0) {358device.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(myAdditionalName));359}360if (myOutputFilename.size() > 0) {361device.writeAttr(SUMO_ATTR_FILE, myOutputFilename);362}363if (myVehicleTypes.size() > 0) {364device.writeAttr(SUMO_ATTR_VTYPES, myVehicleTypes);365}366if (myNextEdges.size() > 0) {367device.writeAttr(SUMO_ATTR_NEXT_EDGES, myNextEdges);368}369if ((myDetectPersons.size() > 0) && (myDetectPersons != SUMOXMLDefinitions::PersonModeValues.getString(PersonMode::NONE))) {370device.writeAttr(SUMO_ATTR_DETECT_PERSONS, myDetectPersons);371}372if (myFriendlyPosition) {373device.writeAttr(SUMO_ATTR_FRIENDLY_POS, myFriendlyPosition);374}375}376377378void379GNEDetector::setDetectorAttribute(SumoXMLAttr key, const std::string& value) {380switch (key) {381case SUMO_ATTR_ID:382// update microsimID383setAdditionalID(value);384break;385case SUMO_ATTR_LANE:386replaceAdditionalParentLanes(value);387break;388case SUMO_ATTR_POSITION:389myPositionOverLane = parse<double>(value);390break;391case SUMO_ATTR_PERIOD:392if (value.empty()) {393myPeriod = SUMOTime_MAX_PERIOD;394} else {395myPeriod = string2time(value);396}397break;398case SUMO_ATTR_FILE:399myOutputFilename = value;400break;401case SUMO_ATTR_NAME:402myAdditionalName = value;403break;404case SUMO_ATTR_VTYPES:405myVehicleTypes = parse<std::vector<std::string> >(value);406break;407case SUMO_ATTR_NEXT_EDGES:408myNextEdges = parse<std::vector<std::string> >(value);409break;410case SUMO_ATTR_DETECT_PERSONS:411myDetectPersons = value;412break;413case SUMO_ATTR_FRIENDLY_POS:414myFriendlyPosition = parse<bool>(value);415break;416case GNE_ATTR_SHIFTLANEINDEX:417shiftLaneIndex();418break;419default:420setCommonAttribute(this, key, value);421break;422}423}424425426void427GNEDetector::drawE1Shape(const GUIVisualizationSettings::Detail d, const double exaggeration,428const RGBColor& mainColor, const RGBColor& secondColor) const {429// push matrix430GLHelper::pushMatrix();431// set line width432glLineWidth(1.0);433// translate to center geometry434glTranslated(myAdditionalGeometry.getShape().front().x(), myAdditionalGeometry.getShape().front().y(), 0);435// rotate over lane436GUIGeometry::rotateOverLane(myAdditionalGeometry.getShapeRotations().front() + 90);437// scale438glScaled(exaggeration, exaggeration, 1);439// set main color440GLHelper::setColor(mainColor);441// begin draw square442glBegin(GL_QUADS);443// draw square444glVertex2d(-1.0, 2);445glVertex2d(-1.0, -2);446glVertex2d(1.0, -2);447glVertex2d(1.0, 2);448// end draw square449glEnd();450// move top451glTranslated(0, 0, .01);452// begin draw line453glBegin(GL_LINES);454// draw lines455glVertex2d(0, 2 - .1);456glVertex2d(0, -2 + .1);457// end draw line458glEnd();459// draw center only in draw in level 2460if (d <= GUIVisualizationSettings::Detail::AdditionalDetails) {461// set main color462GLHelper::setColor(secondColor);463// set polygon mode464glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);465// begin draw square466glBegin(GL_QUADS);467// draw square468glVertex2f(-1.0, 2);469glVertex2f(-1.0, -2);470glVertex2f(1.0, -2);471glVertex2f(1.0, 2);472// end draw square473glEnd();474// rotate 90 degrees475glRotated(90, 0, 0, -1);476//set polygon mode477glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);478// begin draw line479glBegin(GL_LINES);480// draw line481glVertex2d(0, 1.7);482glVertex2d(0, -1.7);483// end draw line484glEnd();485//arrow486glTranslated(2, 0, 0);487GLHelper::setColor(mainColor);488GLHelper::drawTriangleAtEnd(Position(0, 0), Position(0.5, 0), (double) 0.5, (double) 1);489}490// pop matrix491GLHelper::popMatrix();492}493494495void496GNEDetector::drawE1DetectorLogo(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,497const double exaggeration, const std::string& logo, const RGBColor& textColor) const {498// only draw in level 2499if (d <= GUIVisualizationSettings::Detail::Text) {500// calculate position501const Position pos = myAdditionalGeometry.getShape().front();502// calculate rotation503const double rot = s.getTextAngle(myAdditionalGeometry.getShapeRotations().front() + 90);504// Start pushing matrix505GLHelper::pushMatrix();506// Traslate to position507glTranslated(pos.x(), pos.y(), 0.1);508// scale text509glScaled(exaggeration, exaggeration, 1);510// draw E1 logo511GLHelper::drawText(logo + " ", Position(), .1, 1.5, textColor, rot);512// pop matrix513GLHelper::popMatrix();514}515}516517518void519GNEDetector::drawE2DetectorLogo(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,520const double exaggeration, const std::string& logo, const RGBColor& textColor) const {521// only draw in level 2522if (d <= GUIVisualizationSettings::Detail::Text) {523// calculate middle point524const double middlePoint = (myAdditionalGeometry.getShape().length2D() * 0.5);525// calculate position526const Position pos = myAdditionalGeometry.getShape().positionAtOffset2D(middlePoint);527// calculate rotation528const double rot = s.getTextAngle(myAdditionalGeometry.getShape().rotationDegreeAtOffset(middlePoint) + 90);529// Start pushing matrix530GLHelper::pushMatrix();531// Traslate to position532glTranslated(pos.x(), pos.y(), 0.1);533// scale text534glScaled(exaggeration, exaggeration, 1);535// draw E1 logo536GLHelper::drawText(logo, Position(), .1, 1.5, textColor, rot);537// pop matrix538GLHelper::popMatrix();539}540}541542/****************************************************************************/543544545