Path: blob/main/src/netedit/elements/additional/GNEParkingSpace.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 GNEParkingSpace.cpp14/// @author Pablo Alvarez Lopez15/// @date Feb 201816///17// A lane area vehicles can halt at (GNE version)18/****************************************************************************/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/changes/GNEChange_Attribute.h>26#include <utils/gui/div/GLHelper.h>27#include <utils/gui/globjects/GLIncludes.h>28#include <utils/gui/div/GUIGlobalViewObjectsHandler.h>2930#include "GNEParkingSpace.h"3132// ===========================================================================33// method definitions34// ===========================================================================3536GNEParkingSpace::GNEParkingSpace(GNENet* net) :37GNEAdditional("", net, "", SUMO_TAG_PARKING_SPACE, ""),38mySlope(0) {39}404142GNEParkingSpace::GNEParkingSpace(GNEAdditional* parkingAreaParent, const Position& pos,43const std::string& width, const std::string& length, const std::string& angle, double slope,44const std::string& name, const Parameterised::Map& parameters) :45GNEAdditional(parkingAreaParent, SUMO_TAG_PARKING_SPACE, name),46Parameterised(parameters),47myPosition(pos),48myWidth(width),49myLength(length),50myAngle(angle),51mySlope(slope) {52// set parents53setParent<GNEAdditional*>(parkingAreaParent);54// update centering boundary without updating grid55updateCenteringBoundary(false);56}575859GNEParkingSpace::~GNEParkingSpace() {}606162GNEMoveOperation*63GNEParkingSpace::getMoveOperation() {64if (myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork() &&65(myNet->getViewNet()->getEditModes().networkEditMode == NetworkEditMode::NETWORK_MOVE) &&66myNet->getViewNet()->getMouseButtonKeyPressed().shiftKeyPressed()) {67// get snap radius68const double snap_radius = myNet->getViewNet()->getVisualisationSettings().neteditSizeSettings.additionalGeometryPointRadius;69// get mouse position70const Position mousePosition = myNet->getViewNet()->getPositionInformation();71// check if we're editing width or height72if (myShapeLength.back().distanceSquaredTo2D(mousePosition) <= (snap_radius * snap_radius)) {73// edit length74return new GNEMoveOperation(this, myShapeLength, false, GNEMoveOperation::OperationType::LENGTH);75} else if (myShapeWidth.front().distanceSquaredTo2D(mousePosition) <= (snap_radius * snap_radius)) {76// edit width77return new GNEMoveOperation(this, myShapeWidth, true, GNEMoveOperation::OperationType::WIDTH);78} else if (myShapeWidth.back().distanceSquaredTo2D(mousePosition) <= (snap_radius * snap_radius)) {79// edit width80return new GNEMoveOperation(this, myShapeWidth, false, GNEMoveOperation::OperationType::WIDTH);81} else {82return nullptr;83}84} else {85// move entire space86return new GNEMoveOperation(this, myPosition);87}88}899091void92GNEParkingSpace::writeAdditional(OutputDevice& device) const {93device.openTag(getTagProperty()->getTag());94if (!myAdditionalName.empty()) {95device.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(myAdditionalName));96}97device.writeAttr(SUMO_ATTR_X, myPosition.x());98device.writeAttr(SUMO_ATTR_Y, myPosition.y());99if (myPosition.z() != 0) {100device.writeAttr(SUMO_ATTR_Z, myPosition.z());101}102if (myWidth.size() > 0) {103device.writeAttr(SUMO_ATTR_WIDTH, myWidth);104}105if (myLength.size() > 0) {106device.writeAttr(SUMO_ATTR_LENGTH, myLength);107}108if (myAngle.size() > 0) {109device.writeAttr(SUMO_ATTR_ANGLE, myAngle);110}111if (mySlope != myTagProperty->getDefaultDoubleValue(SUMO_ATTR_SLOPE)) {112device.writeAttr(SUMO_ATTR_SLOPE, mySlope);113}114// write parameters (Always after children to avoid problems with additionals.xsd)115writeParams(device);116device.closeTag();117}118119120bool GNEParkingSpace::isAdditionalValid() const {121return true;122}123124125std::string GNEParkingSpace::getAdditionalProblem() const {126return "";127}128129130void GNEParkingSpace::fixAdditionalProblem() {131// nothing to fix132}133134135bool136GNEParkingSpace::checkDrawMoveContour() const {137// get edit modes138const auto& editModes = myNet->getViewNet()->getEditModes();139// check if we're in move mode140if (!myNet->getViewNet()->isCurrentlyMovingElements() && editModes.isCurrentSupermodeNetwork() &&141!myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement() &&142(editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) && myNet->getViewNet()->checkOverLockedElement(this, mySelected)) {143// only move the first element144return myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == this;145} else {146return false;147}148}149150151void152GNEParkingSpace::updateGeometry() {153// get width an length154const double width = getAttributeDouble(SUMO_ATTR_WIDTH) <= 0 ? POSITION_EPS : getAttributeDouble(SUMO_ATTR_WIDTH);155const double length = getAttributeDouble(SUMO_ATTR_LENGTH) <= 0 ? POSITION_EPS : getAttributeDouble(SUMO_ATTR_LENGTH);156// calculate shape length157myShapeLength.clear();158myShapeLength.push_back(Position(0, 0));159myShapeLength.push_back(Position(0, length));160// rotate161myShapeLength.rotate2D(DEG2RAD(getAttributeDouble(SUMO_ATTR_ANGLE)));162// move163myShapeLength.add(myPosition);164// calculate shape width165PositionVector leftShape = myShapeLength;166leftShape.move2side(width * -0.5);167PositionVector rightShape = myShapeLength;168rightShape.move2side(width * 0.5);169myShapeWidth = {leftShape.getCentroid(), rightShape.getCentroid()};170// update centering boundary171updateCenteringBoundary(true);172}173174175Position176GNEParkingSpace::getPositionInView() const {177return myPosition;178}179180181void182GNEParkingSpace::updateCenteringBoundary(const bool updateGrid) {183// remove additional from grid184if (updateGrid) {185myNet->removeGLObjectFromGrid(this);186}187// first reset boundary188myAdditionalBoundary.reset();189// add position190myAdditionalBoundary.add(myPosition);191// grow width and length192myAdditionalBoundary.grow(myShapeLength.length2D());193myAdditionalBoundary.grow(myShapeWidth.length2D());194// grow195myAdditionalBoundary.grow(5);196// add additional into RTREE again197if (updateGrid) {198myNet->addGLObjectIntoGrid(this);199}200}201202203void204GNEParkingSpace::splitEdgeGeometry(const double /*splitPosition*/, const GNENetworkElement* /*originalElement*/, const GNENetworkElement* /*newElement*/, GNEUndoList* /*undoList*/) {205// geometry of this element cannot be splitted206}207208209std::string210GNEParkingSpace::getParentName() const {211return getParentAdditionals().at(0)->getID();212}213214215void216GNEParkingSpace::drawGL(const GUIVisualizationSettings& s) const {217// first check if additional has to be drawn218if (myNet->getViewNet()->getDataViewOptions().showAdditionals()) {219// draw boundaries220GLHelper::drawBoundary(s, getCenteringBoundary());221// get exaggeration222const double spaceExaggeration = getExaggeration(s);223// get witdh224const double parkingSpaceWidth = myShapeWidth.length2D() * 0.5 + (spaceExaggeration * 0.1);225// get detail level226const auto d = s.getDetailLevel(spaceExaggeration);227// check if draw moving geometry points228const bool movingGeometryPoints = drawMovingGeometryPoints(false);229// draw geometry only if we'rent in drawForObjectUnderCursor mode230if (s.checkDrawAdditional(d, isAttributeCarrierSelected())) {231// draw space232drawSpace(s, d, parkingSpaceWidth, movingGeometryPoints);233// draw parent and child lines234drawParentChildLines(s, s.additionalSettings.connectionColor);235// draw lock icon236GNEViewNetHelper::LockIcon::drawLockIcon(d, this, getType(), myShapeLength.getPolygonCenter(), spaceExaggeration);237// Draw additional ID238drawAdditionalID(s);239// draw additional name240drawAdditionalName(s);241// draw dotted contours242if (movingGeometryPoints) {243// get snap radius244const double snapRadius = myNet->getViewNet()->getVisualisationSettings().neteditSizeSettings.additionalGeometryPointRadius;245const double snapRadiusSquared = snapRadius * snapRadius;246// get mouse position247const Position mousePosition = myNet->getViewNet()->getPositionInformation();248// check if we're editing width or height249if (myShapeLength.back().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared) {250myMovingContourUp.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);251myMovingContourDown.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);252} else if ((myShapeWidth.front().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared) ||253(myShapeWidth.back().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared)) {254myMovingContourLeft.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);255myMovingContourRight.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);256}257} else {258myAdditionalContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);259}260261}262// calculate contour263calculateSpaceContour(s, d, parkingSpaceWidth, spaceExaggeration, movingGeometryPoints);264}265}266267268std::string269GNEParkingSpace::getAttribute(SumoXMLAttr key) const {270switch (key) {271case SUMO_ATTR_ID:272return getMicrosimID();273case SUMO_ATTR_POSITION:274return toString(myPosition);275case SUMO_ATTR_NAME:276return myAdditionalName;277case SUMO_ATTR_WIDTH:278return myWidth;279case SUMO_ATTR_LENGTH:280return myLength;281case SUMO_ATTR_ANGLE:282return myAngle;283case SUMO_ATTR_SLOPE:284return toString(mySlope);285case GNE_ATTR_PARENT:286if (isTemplate()) {287return "";288} else {289return getParentAdditionals().at(0)->getID();290}291default:292return getCommonAttribute(this, key);293}294}295296297double298GNEParkingSpace::getAttributeDouble(SumoXMLAttr key) const {299switch (key) {300case SUMO_ATTR_WIDTH:301return myWidth.empty() ? getParentAdditionals().front()->getAttributeDouble(SUMO_ATTR_WIDTH) : parse<double>(myWidth);302case SUMO_ATTR_LENGTH:303return myLength.empty() ? getParentAdditionals().front()->getAttributeDouble(SUMO_ATTR_LENGTH) : parse<double>(myLength);304case SUMO_ATTR_ANGLE:305return myAngle.empty() ? getParentAdditionals().front()->getAttributeDouble(SUMO_ATTR_ANGLE) : parse<double>(myAngle);306default:307throw InvalidArgument(getTagStr() + " doesn't have an attribute of type '" + toString(key) + "'");308}309}310311312const Parameterised::Map&313GNEParkingSpace::getACParametersMap() const {314return getParametersMap();315}316317318void319GNEParkingSpace::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {320if (value == getAttribute(key)) {321return; //avoid needless changes, later logic relies on the fact that attributes have changed322}323switch (key) {324case SUMO_ATTR_POSITION:325case SUMO_ATTR_NAME:326case SUMO_ATTR_WIDTH:327case SUMO_ATTR_LENGTH:328case SUMO_ATTR_ANGLE:329case SUMO_ATTR_SLOPE:330case GNE_ATTR_PARENT:331GNEChange_Attribute::changeAttribute(this, key, value, undoList);332break;333default:334setCommonAttribute(key, value, undoList);335break;336}337}338339340bool341GNEParkingSpace::isValid(SumoXMLAttr key, const std::string& value) {342switch (key) {343case SUMO_ATTR_POSITION:344return canParse<Position>(value);345case SUMO_ATTR_NAME:346return SUMOXMLDefinitions::isValidAttribute(value);347case SUMO_ATTR_WIDTH:348return value.empty() || (canParse<double>(value) && (parse<double>(value) > 0));349case SUMO_ATTR_LENGTH:350return value.empty() || (canParse<double>(value) && (parse<double>(value) > 0));351case SUMO_ATTR_ANGLE:352return value.empty() || canParse<double>(value);353case SUMO_ATTR_SLOPE:354return canParse<double>(value);355case GNE_ATTR_PARENT:356return (myNet->getAttributeCarriers()->retrieveAdditional(SUMO_TAG_PARKING_AREA, value, false) != nullptr);357default:358return isCommonValid(key, value);359}360}361362363std::string364GNEParkingSpace::getPopUpID() const {365return getTagStr();366}367368369std::string370GNEParkingSpace::getHierarchyName() const {371return getTagStr() + ": " + getAttribute(SUMO_ATTR_POSITION);372}373374// ===========================================================================375// private376// ===========================================================================377378void379GNEParkingSpace::drawSpace(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,380const double width, const bool movingGeometryPoints) const {381// get angle382const double angle = getAttributeDouble(SUMO_ATTR_ANGLE);383// get contour color384RGBColor contourColor = s.colorSettings.parkingSpaceColorContour;385if (drawUsingSelectColor()) {386contourColor = s.colorSettings.selectedAdditionalColor;387} else if (d <= GUIVisualizationSettings::Detail::AdditionalDetails) {388contourColor = s.colorSettings.parkingSpaceColorContour;389}390// push later matrix391GLHelper::pushMatrix();392// translate to front393drawInLayer(GLO_PARKING_SPACE);394// set contour color395GLHelper::setColor(contourColor);396// draw extern397GLHelper::drawBoxLines(myShapeLength, width);398// make a copy of myShapeLength and scale399PositionVector shapeLengthInner = myShapeLength;400shapeLengthInner.scaleAbsolute(-0.1);401// draw intern402if (d <= GUIVisualizationSettings::Detail::AdditionalDetails) {403// Traslate to front404glTranslated(0, 0, 0.1);405// set base color406GLHelper::setColor(drawUsingSelectColor() ? s.colorSettings.selectedAdditionalColor : s.colorSettings.parkingSpaceColor);407//draw intern408GLHelper::drawBoxLines(shapeLengthInner, width - 0.1);409}410// draw geometry points411if (movingGeometryPoints) {412if (myShapeLength.size() > 0) {413drawUpGeometryPoint(s, d, myShapeLength.back(), angle, RGBColor::ORANGE);414}415if (myShapeWidth.size() > 0) {416drawLeftGeometryPoint(s, d, myShapeWidth.back(), angle - 90, RGBColor::ORANGE);417drawRightGeometryPoint(s, d, myShapeWidth.front(), angle - 90, RGBColor::ORANGE);418}419}420// pop layer matrix421GLHelper::popMatrix();422}423424425void426GNEParkingSpace::calculateSpaceContour(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,427const double width, const double exaggeration, const bool movingGeometryPoints) const {428// check if we're calculating the contour or the moving geometry points429if (movingGeometryPoints) {430myMovingContourUp.calculateContourCircleShape(s, d, this, myShapeLength.back(), s.neteditSizeSettings.additionalGeometryPointRadius,431getType(), exaggeration, nullptr);432myMovingContourLeft.calculateContourCircleShape(s, d, this, myShapeWidth.front(), s.neteditSizeSettings.additionalGeometryPointRadius, getType(),433exaggeration, nullptr);434myMovingContourRight.calculateContourCircleShape(s, d, this, myShapeWidth.back(), s.neteditSizeSettings.additionalGeometryPointRadius, getType(),435exaggeration, nullptr);436} else {437myAdditionalContour.calculateContourExtrudedShape(s, d, this, myShapeLength, getType(), width, exaggeration, true, true, 0, nullptr, nullptr);438}439}440441442void443GNEParkingSpace::setAttribute(SumoXMLAttr key, const std::string& value) {444switch (key) {445case SUMO_ATTR_POSITION:446myPosition = parse<Position>(value);447// update geometry448updateGeometry();449break;450case SUMO_ATTR_NAME:451myAdditionalName = value;452break;453case SUMO_ATTR_WIDTH:454myWidth = value;455// update geometry (except for template)456if (getParentAdditionals().size() > 0) {457updateGeometry();458}459break;460case SUMO_ATTR_LENGTH:461myLength = value;462// update geometry (except for template)463if (getParentAdditionals().size() > 0) {464updateGeometry();465}466break;467case SUMO_ATTR_ANGLE:468myAngle = value;469// update geometry (except for template)470if (getParentAdditionals().size() > 0) {471updateGeometry();472}473break;474case SUMO_ATTR_SLOPE:475mySlope = parse<double>(value);476break;477case GNE_ATTR_PARENT:478replaceAdditionalParent(SUMO_TAG_PARKING_AREA, value, 0);479break;480default:481setCommonAttribute(this, key, value);482break;483}484}485486487void488GNEParkingSpace::setMoveShape(const GNEMoveResult& moveResult) {489// check what are being updated490if (moveResult.operationType == GNEMoveOperation::OperationType::LENGTH) {491myShapeLength[1] = moveResult.shapeToUpdate[1];492} else if (moveResult.operationType == GNEMoveOperation::OperationType::WIDTH) {493myShapeWidth = moveResult.shapeToUpdate;494} else {495myPosition = moveResult.shapeToUpdate.front();496// update geometry497updateGeometry();498}499}500501502void503GNEParkingSpace::commitMoveShape(const GNEMoveResult& moveResult, GNEUndoList* undoList) {504// check what are being updated505if (moveResult.operationType == GNEMoveOperation::OperationType::LENGTH) {506undoList->begin(this, "length of " + getTagStr());507setAttribute(SUMO_ATTR_LENGTH, toString(myShapeLength[0].distanceTo2D(moveResult.shapeToUpdate[1])), undoList);508undoList->end();509} else if (moveResult.operationType == GNEMoveOperation::OperationType::WIDTH) {510undoList->begin(this, "width of " + getTagStr());511setAttribute(SUMO_ATTR_WIDTH, toString(moveResult.shapeToUpdate.length2D()), undoList);512undoList->end();513} else {514undoList->begin(this, "position of " + getTagStr());515setAttribute(SUMO_ATTR_POSITION, toString(moveResult.shapeToUpdate.front()), undoList);516undoList->end();517}518}519520/****************************************************************************/521522523