Path: blob/main/src/netedit/elements/additional/GNELaneAreaDetector.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 GNELaneAreaDetector.cpp14/// @author Pablo Alvarez Lopez15/// @date Nov 201516///17//18/****************************************************************************/19#include <config.h>2021#include <netedit/GNENet.h>22#include <netedit/GNESegment.h>23#include <netedit/GNETagProperties.h>24#include <netedit/GNEUndoList.h>25#include <netedit/GNEViewNet.h>26#include <netedit/GNEViewParent.h>27#include <netedit/changes/GNEChange_Attribute.h>28#include <netedit/changes/GNEChange_Connection.h>29#include <netedit/elements/network/GNEConnection.h>30#include <netedit/frames/network/GNETLSEditorFrame.h>31#include <utils/gui/div/GLHelper.h>32#include <utils/gui/div/GUIGlobalViewObjectsHandler.h>33#include <utils/gui/globjects/GLIncludes.h>34#include <utils/xml/NamespaceIDs.h>3536#include "GNELaneAreaDetector.h"37#include "GNEAdditionalHandler.h"3839// ===========================================================================40// member method definitions41// ===========================================================================4243GNELaneAreaDetector::GNELaneAreaDetector(SumoXMLTag tag, GNENet* net) :44GNEDetector(net, tag) {45}464748GNELaneAreaDetector::GNELaneAreaDetector(const std::string& id, GNENet* net, const std::string& filename, GNELane* lane, double pos, double length, const SUMOTime freq,49const std::string& trafficLight, const std::string& outputFilename, const std::vector<std::string>& vehicleTypes, const std::vector<std::string>& nextEdges,50const std::string& detectPersons, const std::string& name, const SUMOTime timeThreshold, double speedThreshold, const double jamThreshold, const bool friendlyPos,51const bool show, const Parameterised::Map& parameters) :52GNEDetector(id, net, filename, SUMO_TAG_LANE_AREA_DETECTOR, pos, freq, lane, outputFilename, vehicleTypes, nextEdges,53detectPersons, name, friendlyPos, parameters),54myEndPositionOverLane(pos + length),55myTimeThreshold(timeThreshold),56mySpeedThreshold(speedThreshold),57myJamThreshold(jamThreshold),58myTrafficLight(trafficLight),59myShow(show) {60}616263GNELaneAreaDetector::GNELaneAreaDetector(const std::string& id, GNENet* net, const std::string& filename, std::vector<GNELane*> lanes, double pos, double endPos, const SUMOTime freq,64const std::string& trafficLight, const std::string& outputFilename, const std::vector<std::string>& vehicleTypes, const std::vector<std::string>& nextEdges,65const std::string& detectPersons, const std::string& name, const SUMOTime timeThreshold, double speedThreshold, const double jamThreshold, const bool friendlyPos, const bool show,66const Parameterised::Map& parameters) :67GNEDetector(id, net, filename, GNE_TAG_MULTI_LANE_AREA_DETECTOR, pos, freq, lanes, outputFilename, vehicleTypes, nextEdges,68detectPersons, name, friendlyPos, parameters),69myEndPositionOverLane(endPos),70myTimeThreshold(timeThreshold),71mySpeedThreshold(speedThreshold),72myJamThreshold(jamThreshold),73myTrafficLight(trafficLight),74myShow(show) {75}767778GNELaneAreaDetector::~GNELaneAreaDetector() {79}808182void83GNELaneAreaDetector::writeAdditional(OutputDevice& device) const {84device.openTag(SUMO_TAG_LANE_AREA_DETECTOR);85device.writeAttr(SUMO_ATTR_ID, getID());86// continue depending of E2 type87if (myTagProperty->getTag() == SUMO_TAG_LANE_AREA_DETECTOR) {88device.writeAttr(SUMO_ATTR_LANE, getParentLanes().front()->getID());89device.writeAttr(SUMO_ATTR_POSITION, myPositionOverLane);90device.writeAttr(SUMO_ATTR_LENGTH, toString(myEndPositionOverLane - myPositionOverLane));91} else {92device.writeAttr(SUMO_ATTR_LANES, getAttribute(SUMO_ATTR_LANES));93device.writeAttr(SUMO_ATTR_POSITION, myPositionOverLane);94device.writeAttr(SUMO_ATTR_ENDPOS, myEndPositionOverLane);95}96// write common detector parameters97writeDetectorValues(device);98// write specific attributes99if (myTrafficLight.size() > 0) {100device.writeAttr(SUMO_ATTR_TLID, myTrafficLight);101}102if (myTimeThreshold != myTagProperty->getDefaultTimeValue(SUMO_ATTR_HALTING_TIME_THRESHOLD)) {103device.writeAttr(SUMO_ATTR_HALTING_TIME_THRESHOLD, time2string(myTimeThreshold));104}105if (mySpeedThreshold != myTagProperty->getDefaultDoubleValue(SUMO_ATTR_HALTING_SPEED_THRESHOLD)) {106device.writeAttr(SUMO_ATTR_HALTING_SPEED_THRESHOLD, mySpeedThreshold);107}108if (myJamThreshold != myTagProperty->getDefaultDoubleValue(SUMO_ATTR_JAM_DIST_THRESHOLD)) {109device.writeAttr(SUMO_ATTR_JAM_DIST_THRESHOLD, myJamThreshold);110}111if (myShow != myTagProperty->getDefaultBoolValue(SUMO_ATTR_SHOW_DETECTOR)) {112device.writeAttr(SUMO_ATTR_SHOW_DETECTOR, myShow);113}114// write parameters (Always after children to avoid problems with additionals.xsd)115writeParams(device);116device.closeTag();117}118119120bool121GNELaneAreaDetector::isAdditionalValid() const {122if (getParentLanes().size() == 1) {123// with friendly position enabled position are "always fixed"124if (myFriendlyPosition) {125return true;126} else {127return (myPositionOverLane >= 0) && (myEndPositionOverLane <= getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength());128}129} else {130// first check if there is connection between all consecutive lanes131if (areLaneConnected(getParentLanes())) {132// with friendly position enabled position are "always fixed"133if (myFriendlyPosition) {134return true;135} else {136return (myPositionOverLane >= 0) &&137(myEndPositionOverLane >= 0) &&138(myPositionOverLane <= getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength()) &&139(myEndPositionOverLane <= getParentLanes().back()->getParentEdge()->getNBEdge()->getFinalLength());140}141} else {142return false;143}144}145}146147148std::string149GNELaneAreaDetector::getAdditionalProblem() const {150// declare variable for error position151std::string errorFirstLanePosition, separator, errorLastLanePosition;152if (getParentLanes().size() == 1) {153// check positions over lane154if (myPositionOverLane < 0) {155errorFirstLanePosition = (toString(SUMO_ATTR_POSITION) + " < 0");156}157if (myPositionOverLane > getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength()) {158errorFirstLanePosition = (toString(SUMO_ATTR_POSITION) + TL(" > lanes's length"));159}160} else {161// abort if lanes aren't consecutives162if (!areLaneConsecutives(getParentLanes())) {163return TL("lanes aren't consecutives");164}165// abort if lanes aren't connected166if (!areLaneConnected(getParentLanes())) {167return TL("lanes aren't connected");168}169// check positions over first lane170if (myPositionOverLane < 0) {171errorFirstLanePosition = (toString(SUMO_ATTR_POSITION) + " < 0");172}173if (myPositionOverLane > getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength()) {174errorFirstLanePosition = (toString(SUMO_ATTR_POSITION) + TL(" > lanes's length"));175}176// check positions over last lane177if (myEndPositionOverLane < 0) {178errorLastLanePosition = (toString(SUMO_ATTR_ENDPOS) + " < 0");179}180if (myEndPositionOverLane > getParentLanes().back()->getParentEdge()->getNBEdge()->getFinalLength()) {181errorLastLanePosition = (toString(SUMO_ATTR_ENDPOS) + TL(" > lanes's length"));182}183}184// check separator185if ((errorFirstLanePosition.size() > 0) && (errorLastLanePosition.size() > 0)) {186separator = TL(" and ");187}188// return error message189return errorFirstLanePosition + separator + errorLastLanePosition;190}191192193void194GNELaneAreaDetector::fixAdditionalProblem() {195if (getParentLanes().size() == 1) {196// obtain position and length197double newPositionOverLane = myPositionOverLane;198double newLength = (myEndPositionOverLane - myPositionOverLane);199// fix pos and length using fixE2DetectorPosition200GNEAdditionalHandler::fixLanePosition(newPositionOverLane, newLength, getParentLanes().at(0)->getParentEdge()->getNBEdge()->getFinalLength());201// set new position and length202setAttribute(SUMO_ATTR_POSITION, toString(newPositionOverLane), myNet->getViewNet()->getUndoList());203setAttribute(SUMO_ATTR_LENGTH, toString(newLength), myNet->getViewNet()->getUndoList());204} else {205if (!areLaneConsecutives(getParentLanes())) {206// build connections between all consecutive lanes207bool foundConnection = true;208int i = 0;209// iterate over all lanes, and stop if myE2valid is false210while (i < ((int)getParentLanes().size() - 1)) {211// change foundConnection to false212foundConnection = false;213// if a connection between "from" lane and "to" lane of connection is found, change myE2valid to true again214for (const auto& connection : getParentLanes().at(i)->getParentEdge()->getGNEConnections()) {215if ((connection->getLaneFrom() == getParentLanes().at(i)) && (connection->getLaneTo() == getParentLanes().at(i + 1))) {216foundConnection = true;217}218}219// if connection wasn't found220if (!foundConnection) {221// create new connection manually222NBEdge::Connection newCon(getParentLanes().at(i)->getIndex(), getParentLanes().at(i + 1)->getParentEdge()->getNBEdge(), getParentLanes().at(i + 1)->getIndex());223// allow to undo creation of new lane224myNet->getViewNet()->getUndoList()->add(new GNEChange_Connection(getParentLanes().at(i)->getParentEdge(), newCon, false, true), true);225}226// update lane iterator227i++;228}229} else {230// declare new positions231double newPositionOverLane = myPositionOverLane;232double newEndPositionOverLane = myEndPositionOverLane;233// fix pos and length checkAndFixDetectorPosition234GNEAdditionalHandler::fixMultiLanePosition(235newPositionOverLane, getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength(),236newEndPositionOverLane, getParentLanes().back()->getParentEdge()->getNBEdge()->getFinalLength());237// set new position and endPosition238setAttribute(SUMO_ATTR_POSITION, toString(newPositionOverLane), myNet->getViewNet()->getUndoList());239setAttribute(SUMO_ATTR_ENDPOS, toString(newEndPositionOverLane), myNet->getViewNet()->getUndoList());240}241}242}243244245void246GNELaneAreaDetector::updateGeometry() {247// check E2 detector248if (myTagProperty->getTag() == GNE_TAG_MULTI_LANE_AREA_DETECTOR) {249// compute path250computePathElement();251} else {252// Cut shape using as delimitators fixed start position and fixed end position253myAdditionalGeometry.updateGeometry(getParentLanes().front()->getLaneShape(), getStartGeometryPositionOverLane(), getEndGeometryPositionOverLane(), myMoveElementLateralOffset);254}255}256257258void259GNELaneAreaDetector::drawGL(const GUIVisualizationSettings& s) const {260// check drawing conditions261if ((myTagProperty->getTag() == SUMO_TAG_LANE_AREA_DETECTOR) &&262myNet->getViewNet()->getDataViewOptions().showAdditionals() &&263!myNet->getViewNet()->selectingDetectorsTLSMode()) {264// Obtain exaggeration of the draw265const double E2Exaggeration = getExaggeration(s);266// get detail level267const auto d = s.getDetailLevel(E2Exaggeration);268// draw geometry only if we'rent in drawForObjectUnderCursor mode269if (s.checkDrawAdditional(d, isAttributeCarrierSelected())) {270// draw E2271drawE2(s, d, E2Exaggeration);272// draw lock icon273GNEViewNetHelper::LockIcon::drawLockIcon(d, this, getType(), myAdditionalGeometry.getShape().getCentroid(), E2Exaggeration);274// Draw additional ID275drawAdditionalID(s);276// draw additional name277drawAdditionalName(s);278// draw dotted contour279myAdditionalContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);280}281// calculate contour and draw dotted geometry282myAdditionalContour.calculateContourExtrudedShape(s, d, this, myAdditionalGeometry.getShape(), getType(), s.detectorSettings.E2Width,283E2Exaggeration, true, true, 0, nullptr, getParentLanes().front()->getParentEdge());284}285}286287288void289GNELaneAreaDetector::computePathElement() {290// calculate path291myNet->getNetworkPathManager()->calculateConsecutivePathLanes(this, getParentLanes());292}293294295void296GNELaneAreaDetector::drawLanePartialGL(const GUIVisualizationSettings& s, const GNESegment* segment, const double offsetFront) const {297// check if E2 can be drawn298if (segment->getLane() && (myTagProperty->getTag() == GNE_TAG_MULTI_LANE_AREA_DETECTOR) &&299myNet->getViewNet()->getDataViewOptions().showAdditionals() && !myNet->getViewNet()->selectingDetectorsTLSMode()) {300const bool movingGeometryPoints = drawMovingGeometryPoints(false);301// Obtain exaggeration of the draw302const double E2Exaggeration = getExaggeration(s);303// get detail level304const auto d = s.getDetailLevel(E2Exaggeration);305// calculate startPos306const double geometryDepartPos = getAttributeDouble(SUMO_ATTR_POSITION);307// get endPos308const double geometryEndPos = getAttributeDouble(SUMO_ATTR_ENDPOS);309// declare path geometry310GUIGeometry E2Geometry;311// update pathGeometry depending of first and last segment312if (segment->isFirstSegment() && segment->isLastSegment()) {313E2Geometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),314geometryDepartPos,315Position::INVALID,316geometryEndPos,317Position::INVALID);318} else if (segment->isFirstSegment()) {319E2Geometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),320geometryDepartPos,321Position::INVALID,322-1,323Position::INVALID);324} else if (segment->isLastSegment()) {325E2Geometry.updateGeometry(segment->getLane()->getLaneGeometry().getShape(),326-1,327Position::INVALID,328geometryEndPos,329Position::INVALID);330} else {331E2Geometry = segment->getLane()->getLaneGeometry();332}333// draw geometry only if we'rent in drawForObjectUnderCursor mode334if (s.checkDrawAdditional(d, isAttributeCarrierSelected())) {335// draw E2 partial336drawE2PartialLane(s, d, segment, offsetFront, E2Geometry, E2Exaggeration, movingGeometryPoints);337// draw additional ID338drawName(getCenteringBoundary().getCenter(), s.scale, s.addName);339// draw dotted contour340if (movingGeometryPoints) {341// get mouse position342const Position mousePosition = myNet->getViewNet()->getPositionInformation();343// get snap radius344const double snap_radius = myNet->getViewNet()->getVisualisationSettings().neteditSizeSettings.additionalGeometryPointRadius;345if (segment->getFromContour() && E2Geometry.getShape().front().distanceSquaredTo2D(mousePosition) <= (snap_radius * snap_radius)) {346segment->getFromContour()->drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);347} else if (segment->getToContour() && E2Geometry.getShape().back().distanceSquaredTo2D(mousePosition) <= (snap_radius * snap_radius)) {348segment->getToContour()->drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);349}350} else {351segment->getContour()->drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);352}353}354// calculate contour and draw dotted geometry355segment->getContour()->calculateContourExtrudedShape(s, d, this, E2Geometry.getShape(), getType(), s.detectorSettings.E2Width,356E2Exaggeration, segment->isFirstSegment(), segment->isLastSegment(), 0, segment, segment->getLane()->getParentEdge());357// check if create from-to contours358if (segment->getFromContour()) {359segment->getFromContour()->calculateContourCircleShape(s, d, this, E2Geometry.getShape().front(),360s.neteditSizeSettings.additionalGeometryPointRadius, getType(), E2Exaggeration, segment->getLane()->getParentEdge());361} else if (segment->getToContour()) {362segment->getToContour()->calculateContourCircleShape(s, d, this, E2Geometry.getShape().back(),363s.neteditSizeSettings.additionalGeometryPointRadius, getType(), E2Exaggeration, segment->getLane()->getParentEdge());364}365// check if add this path element to redraw buffer366if (!gViewObjectsHandler.isPathElementMarkForRedraw(this) && segment->getContour()->checkDrawPathContour(s, d, this)) {367gViewObjectsHandler.addToRedrawPathElements(this);368}369}370}371372373void374GNELaneAreaDetector::drawJunctionPartialGL(const GUIVisualizationSettings& s, const GNESegment* segment, const double offsetFront) const {375// check if E2 can be drawn376if ((myTagProperty->getTag() == GNE_TAG_MULTI_LANE_AREA_DETECTOR) && segment->getPreviousLane() && segment->getNextLane() &&377myNet->getViewNet()->getDataViewOptions().showAdditionals() && !myNet->getViewNet()->selectingDetectorsTLSMode()) {378// Obtain exaggeration of the draw379const double E2Exaggeration = getExaggeration(s);380// get detail level381const auto d = s.getDetailLevel(E2Exaggeration);382// get flag for show only contour383const bool onlyContour = myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork() ? myNet->getViewNet()->getNetworkViewOptions().showConnections() : false;384// check if connection to next lane exist385const bool connectionExist = segment->getPreviousLane()->getLane2laneConnections().exist(segment->getNextLane());386// get geometry387const GUIGeometry& E2Geometry = connectionExist ? segment->getPreviousLane()->getLane2laneConnections().getLane2laneGeometry(segment->getNextLane()) :388GUIGeometry({segment->getPreviousLane()->getLaneShape().back(), segment->getNextLane()->getLaneShape().front()});389// draw geometry only if we'rent in drawForObjectUnderCursor mode390if (s.checkDrawAdditional(d, isAttributeCarrierSelected())) {391// draw E2 partial392drawE2PartialJunction(s, d, onlyContour, offsetFront, E2Geometry, E2Exaggeration);393// draw dotted contour394if (!drawMovingGeometryPoints(false)) {395segment->getContour()->drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);396}397}398// calculate contour399segment->getContour()->calculateContourExtrudedShape(s, d, this, E2Geometry.getShape(), getType(), s.detectorSettings.E2Width, E2Exaggeration,400false, false, 0, segment, segment->getJunction());401// check if add this path element to redraw buffer402if (!gViewObjectsHandler.isPathElementMarkForRedraw(this) && segment->getContour()->checkDrawPathContour(s, d, this)) {403gViewObjectsHandler.addToRedrawPathElements(this);404}405}406}407408409std::string410GNELaneAreaDetector::getAttribute(SumoXMLAttr key) const {411switch (key) {412case SUMO_ATTR_LANES:413return parseIDs(getParentLanes());414case SUMO_ATTR_STARTPOS:415return toString(myPositionOverLane);416case SUMO_ATTR_ENDPOS:417return toString(myEndPositionOverLane);418case SUMO_ATTR_TLID:419return myTrafficLight;420case SUMO_ATTR_LENGTH:421return toString(myEndPositionOverLane - myPositionOverLane);422case SUMO_ATTR_HALTING_TIME_THRESHOLD:423return time2string(myTimeThreshold);424case SUMO_ATTR_HALTING_SPEED_THRESHOLD:425return toString(mySpeedThreshold);426case SUMO_ATTR_JAM_DIST_THRESHOLD:427return toString(myJamThreshold);428case SUMO_ATTR_SHOW_DETECTOR:429return toString(myShow);430default:431return getDetectorAttribute(key);432}433}434435436double437GNELaneAreaDetector::getAttributeDouble(SumoXMLAttr key) const {438switch (key) {439case SUMO_ATTR_LENGTH:440return (myEndPositionOverLane - myPositionOverLane);441case SUMO_ATTR_ENDPOS:442return myEndPositionOverLane;443default:444return getDetectorAttributeDouble(key);445}446}447448449void450GNELaneAreaDetector::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {451switch (key) {452case SUMO_ATTR_LANES:453case SUMO_ATTR_ENDPOS:454case SUMO_ATTR_TLID:455case SUMO_ATTR_LENGTH:456case SUMO_ATTR_HALTING_TIME_THRESHOLD:457case SUMO_ATTR_HALTING_SPEED_THRESHOLD:458case SUMO_ATTR_JAM_DIST_THRESHOLD:459case SUMO_ATTR_SHOW_DETECTOR:460GNEChange_Attribute::changeAttribute(this, key, value, undoList);461break;462default:463setDetectorAttribute(key, value, undoList);464break;465}466}467468469bool470GNELaneAreaDetector::isValid(SumoXMLAttr key, const std::string& value) {471switch (key) {472case SUMO_ATTR_LANES:473if (value.empty()) {474return false;475} else {476return canParse<std::vector<GNELane*> >(myNet, value, true);477}478case SUMO_ATTR_ENDPOS:479return canParse<double>(value);480case SUMO_ATTR_TLID:481// temporal482return SUMOXMLDefinitions::isValidNetID(value);483case SUMO_ATTR_LENGTH:484return (canParse<double>(value) && (parse<double>(value) >= 0));485case SUMO_ATTR_HALTING_TIME_THRESHOLD:486return canParse<SUMOTime>(value) && (parse<SUMOTime>(value) >= 0);487case SUMO_ATTR_HALTING_SPEED_THRESHOLD:488return (canParse<double>(value) && (parse<double>(value) >= 0));489case SUMO_ATTR_JAM_DIST_THRESHOLD:490return (canParse<double>(value) && (parse<double>(value) >= 0));491case SUMO_ATTR_SHOW_DETECTOR:492return canParse<bool>(value);493default:494return isDetectorValid(key, value);495}496}497498// ===========================================================================499// private500// ===========================================================================501502void503GNELaneAreaDetector::drawE2(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,504const double exaggeration) const {505// declare color506RGBColor E2Color, textColor;507// set color508if (drawUsingSelectColor()) {509E2Color = s.colorSettings.selectedAdditionalColor;510textColor = E2Color.changedBrightness(-32);511} else if (areLaneConsecutives(getParentLanes())) {512E2Color = s.detectorSettings.E2Color;513textColor = RGBColor::BLACK;514}515// draw parent and child lines516drawParentChildLines(s, s.additionalSettings.connectionColor);517// push layer matrix518GLHelper::pushMatrix();519// translate to front520drawInLayer(GLO_E2DETECTOR);521// set color522GLHelper::setColor(E2Color);523// draw geometry524GUIGeometry::drawGeometry(d, myAdditionalGeometry, s.detectorSettings.E2Width * exaggeration);525// draw arrow526if (myAdditionalGeometry.getShape().size() > 1) {527glTranslated(0, 0, 0.1);528GLHelper::drawTriangleAtEnd(myAdditionalGeometry.getShape()[-2], myAdditionalGeometry.getShape()[-1], (double) 0.5, (double) 1, 0.5);529}530// draw E2 Logo531drawE2DetectorLogo(s, d, exaggeration, "E2", textColor);532// draw geometry points533drawLeftGeometryPoint(s, d, myAdditionalGeometry.getShape().front(), myAdditionalGeometry.getShapeRotations().front(), E2Color);534drawRightGeometryPoint(s, d, myAdditionalGeometry.getShape().back(), myAdditionalGeometry.getShapeRotations().back(), E2Color);535// pop layer matrix536GLHelper::popMatrix();537}538539540void541GNELaneAreaDetector::drawE2PartialLane(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,542const GNESegment* segment, const double offsetFront,543const GUIGeometry& geometry, const double exaggeration, const bool movingGeometryPoints) const {544// obtain color545const RGBColor E2Color = drawUsingSelectColor() ? s.colorSettings.selectedAdditionalColor : s.detectorSettings.E2Color;546// push layer matrix547GLHelper::pushMatrix();548// Start with the drawing of the area traslating matrix to origin549glTranslated(0, 0, getType() + offsetFront);550// Set color551GLHelper::setColor(E2Color);552// draw geometry553GUIGeometry::drawGeometry(d, geometry, s.detectorSettings.E2Width * exaggeration);554// check if draw moving geometry points555if (movingGeometryPoints) {556if (segment->isFirstSegment() && segment->isLastSegment()) {557drawLeftGeometryPoint(s, d, geometry.getShape().front(), geometry.getShapeRotations().front(), E2Color, true);558drawRightGeometryPoint(s, d, geometry.getShape().back(), geometry.getShapeRotations().back(), E2Color, true);559} else if (segment->isFirstSegment()) {560drawLeftGeometryPoint(s, d, geometry.getShape().front(), geometry.getShapeRotations().front(), E2Color, true);561} else if (segment->isLastSegment()) {562drawRightGeometryPoint(s, d, geometry.getShape().back(), geometry.getShapeRotations().back(), E2Color, true);563// draw arrow564if (geometry.getShape().size() > 1) {565glTranslated(0, 0, 0.1);566GLHelper::drawTriangleAtEnd(geometry.getShape()[-2], geometry.getShape()[-1], (double) 0.5, (double) 1, 0.5);567}568}569}570// Pop layer matrix571GLHelper::popMatrix();572// check if this is the label segment573if (segment->isLabelSegment()) {574// calculate middle point575const double middlePoint = (geometry.getShape().length2D() * 0.5);576// calculate position577const Position pos = geometry.getShape().positionAtOffset2D(middlePoint);578// calculate rotation579const double rot = s.getTextAngle((geometry.getShape().rotationDegreeAtOffset(middlePoint) * -1) + 90);580// Start pushing matrix581GLHelper::pushMatrix();582// Traslate to position583glTranslated(pos.x(), pos.y(), getType() + offsetFront + 0.1);584// rotate585glRotated(rot, 0, 0, 1);586// move587glTranslated(-1, 0, 0);588// scale text589glScaled(exaggeration, exaggeration, 1);590// draw E1 logo591GLHelper::drawText("E2 Multilane", Position(), .1, 1.5, RGBColor::BLACK);592// pop matrix593GLHelper::popMatrix();594}595596}597598599void600GNELaneAreaDetector::drawE2PartialJunction(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,601const bool onlyContour, const double offsetFront, const GUIGeometry& geometry,602const double exaggeration) const {603const bool invalid = geometry.getShape().length() == 2;604const double width = s.detectorSettings.E2Width * exaggeration * (invalid ? 0.5 : 1);605// Add a draw matrix606GLHelper::pushMatrix();607// Start with the drawing of the area traslating matrix to origin608glTranslated(0, 0, getType() + offsetFront);609// Set color of the base610if (drawUsingSelectColor()) {611GLHelper::setColor(s.colorSettings.selectedAdditionalColor);612} else if (invalid) {613GLHelper::setColor(RGBColor::RED);614} else {615GLHelper::setColor(s.detectorSettings.E2Color);616}617// check if draw only contour618if (onlyContour) {619GUIGeometry::drawContourGeometry(geometry, width);620} else {621GUIGeometry::drawGeometry(d, geometry, width);622}623// Pop last matrix624GLHelper::popMatrix();625}626627628void629GNELaneAreaDetector::setAttribute(SumoXMLAttr key, const std::string& value) {630switch (key) {631case SUMO_ATTR_LANES:632replaceAdditionalParentLanes(value);633break;634case SUMO_ATTR_ENDPOS:635myEndPositionOverLane = parse<double>(value);636// update geometry (except for template)637if (getParentLanes().size() > 0) {638updateGeometry();639}640break;641case SUMO_ATTR_TLID:642myTrafficLight = value;643break;644case SUMO_ATTR_LENGTH:645myEndPositionOverLane = (myPositionOverLane + parse<double>(value));646// update geometry (except for template)647if (getParentLanes().size() > 0) {648updateGeometry();649}650break;651case SUMO_ATTR_HALTING_TIME_THRESHOLD:652myTimeThreshold = TIME2STEPS(parse<double>(value));653break;654case SUMO_ATTR_HALTING_SPEED_THRESHOLD:655mySpeedThreshold = parse<double>(value);656break;657case SUMO_ATTR_JAM_DIST_THRESHOLD:658myJamThreshold = parse<double>(value);659break;660case SUMO_ATTR_SHOW_DETECTOR:661myShow = parse<bool>(value);662break;663default:664setDetectorAttribute(key, value);665break;666}667}668669670void671GNELaneAreaDetector::setMoveShape(const GNEMoveResult& moveResult) {672if ((moveResult.operationType == GNEMoveOperation::OperationType::SINGLE_LANE_MOVE_FIRST) ||673(moveResult.operationType == GNEMoveOperation::OperationType::MULTIPLE_LANES_MOVE_FIRST)) {674// change only start position675myPositionOverLane = moveResult.newFirstPos;676} else if ((moveResult.operationType == GNEMoveOperation::OperationType::SINGLE_LANE_MOVE_LAST) ||677(moveResult.operationType == GNEMoveOperation::OperationType::MULTIPLE_LANES_MOVE_LAST)) {678// change only end position679myEndPositionOverLane = moveResult.newFirstPos;680} else {681if (moveResult.operationType == GNEMoveOperation::OperationType::MULTIPLE_LANES_MOVE_BOTH_FIRST) {682const auto difference = moveResult.newFirstPos - myPositionOverLane;683// change start position684myPositionOverLane = moveResult.newFirstPos;685myEndPositionOverLane += difference;686} else if (moveResult.operationType == GNEMoveOperation::OperationType::MULTIPLE_LANES_MOVE_BOTH_LAST) {687const auto difference = moveResult.newFirstPos - myEndPositionOverLane;688// change end position689myPositionOverLane += difference;690myEndPositionOverLane = moveResult.newFirstPos;691}692// end position over lane693if (myPositionOverLane < 0) {694myPositionOverLane = 0;695} else if (myPositionOverLane > getParentLanes().front()->getLaneShapeLength()) {696myPositionOverLane = getParentLanes().front()->getLaneShapeLength();697}698// adjust position over lane699if (myEndPositionOverLane < 0) {700myEndPositionOverLane = 0;701} else if (myEndPositionOverLane > getParentLanes().back()->getLaneShapeLength()) {702myEndPositionOverLane = getParentLanes().back()->getLaneShapeLength();703}704}705// update geometry706updateGeometry();707}708709710void711GNELaneAreaDetector::commitMoveShape(const GNEMoveResult& moveResult, GNEUndoList* undoList) {712// begin change attribute713undoList->begin(this, "position of " + getTagStr());714// set attributes depending of operation type715if ((moveResult.operationType == GNEMoveOperation::OperationType::SINGLE_LANE_MOVE_FIRST) ||716(moveResult.operationType == GNEMoveOperation::OperationType::MULTIPLE_LANES_MOVE_FIRST)) {717// set only start position718setAttribute(SUMO_ATTR_POSITION, toString(moveResult.newFirstPos), undoList);719} else if ((moveResult.operationType == GNEMoveOperation::OperationType::SINGLE_LANE_MOVE_LAST) ||720(moveResult.operationType == GNEMoveOperation::OperationType::MULTIPLE_LANES_MOVE_LAST)) {721// set only end position722setAttribute(SUMO_ATTR_ENDPOS, toString(moveResult.newFirstPos), undoList);723} else {724double startPos = myPositionOverLane;725double endPos = myEndPositionOverLane;726// set positions727if (moveResult.operationType == GNEMoveOperation::OperationType::MULTIPLE_LANES_MOVE_BOTH_FIRST) {728const auto difference = moveResult.newFirstPos - myPositionOverLane;729// change start position730startPos = moveResult.newFirstPos;731endPos += difference;732} else if (moveResult.operationType == GNEMoveOperation::OperationType::MULTIPLE_LANES_MOVE_BOTH_LAST) {733const auto difference = moveResult.newFirstPos - myEndPositionOverLane;734// change end position735startPos += difference;736endPos = moveResult.newFirstPos;737}738// end position over lane739if (startPos < 0) {740startPos = 0;741} else if (startPos > getParentLanes().front()->getLaneShapeLength()) {742startPos = getParentLanes().front()->getLaneShapeLength();743}744// adjust position over lane745if (endPos < 0) {746endPos = 0;747} else if (endPos > getParentLanes().back()->getLaneShapeLength()) {748endPos = getParentLanes().back()->getLaneShapeLength();749}750// set only end position751setAttribute(SUMO_ATTR_POSITION, toString(startPos), undoList);752setAttribute(SUMO_ATTR_ENDPOS, toString(endPos), undoList);753}754// end change attribute755undoList->end();756}757758759double760GNELaneAreaDetector::getStartGeometryPositionOverLane() const {761// get lane final and shape length762const double laneLength = getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength();763// get startPosition764double fixedPos = myPositionOverLane;765// adjust fixedPos766if (fixedPos < 0) {767fixedPos += laneLength;768}769fixedPos *= getParentLanes().front()->getLengthGeometryFactor();770// return depending of fixedPos771if (fixedPos < 0) {772return 0;773} else if (fixedPos > (getParentLanes().front()->getLaneShapeLength() - POSITION_EPS)) {774return (getParentLanes().front()->getLaneShapeLength() - POSITION_EPS);775} else {776return fixedPos;777}778}779780781double782GNELaneAreaDetector::getEndGeometryPositionOverLane() const {783// get lane final and shape length784const double laneLength = getParentLanes().back()->getParentEdge()->getNBEdge()->getFinalLength();785// get endPosition786double fixedPos = myEndPositionOverLane;787// adjust fixedPos788if (fixedPos < 0) {789fixedPos += laneLength;790}791fixedPos *= getParentLanes().back()->getLengthGeometryFactor();792// return depending of fixedPos793if (fixedPos < POSITION_EPS) {794return POSITION_EPS;795} else if (fixedPos > getParentLanes().back()->getLaneShapeLength()) {796return getParentLanes().back()->getLaneShapeLength();797} else {798return fixedPos;799}800}801802/****************************************************************************/803804805