From d83a7698e23c06ab14008dd59dc484fb00e5c389 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 15 Jun 2021 08:20:24 +1000 Subject: [PATCH] [feature] Add a "measure bearing" map tool This tool behaves similarly to the existing "measure angle" map tool, but requires the user only to click two points on the map and displays the bearing between these points. --- images/images.qrc | 1 + .../themes/default/mActionMeasureBearing.svg | 1 + src/app/CMakeLists.txt | 1 + src/app/maptools/qgsappmaptools.cpp | 2 + src/app/maptools/qgsappmaptools.h | 1 + src/app/qgisapp.cpp | 11 + src/app/qgsdisplayangle.cpp | 22 +- src/app/qgsdisplayangle.h | 26 ++- src/app/qgsmaptoolmeasureangle.cpp | 4 +- src/app/qgsmaptoolmeasurebearing.cpp | 188 ++++++++++++++++++ src/app/qgsmaptoolmeasurebearing.h | 72 +++++++ src/ui/qgisapp.ui | 17 ++ tests/src/app/CMakeLists.txt | 1 + tests/src/app/testqgsmeasurebearingtool.cpp | 160 +++++++++++++++ 14 files changed, 492 insertions(+), 15 deletions(-) create mode 100644 images/themes/default/mActionMeasureBearing.svg create mode 100644 src/app/qgsmaptoolmeasurebearing.cpp create mode 100644 src/app/qgsmaptoolmeasurebearing.h create mode 100644 tests/src/app/testqgsmeasurebearingtool.cpp diff --git a/images/images.qrc b/images/images.qrc index 63fe143b682..e3822e8de65 100644 --- a/images/images.qrc +++ b/images/images.qrc @@ -920,6 +920,7 @@ themes/default/mIconFolderOpenParams.svg themes/default/mIconFolderLinkParams.svg themes/default/mIconFolderHomeParams.svg + themes/default/mActionMeasureBearing.svg qgis_tips/symbol_levels.png diff --git a/images/themes/default/mActionMeasureBearing.svg b/images/themes/default/mActionMeasureBearing.svg new file mode 100644 index 00000000000..4d0a4f987bc --- /dev/null +++ b/images/themes/default/mActionMeasureBearing.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index a6e1a64fa3e..863812917dc 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -86,6 +86,7 @@ set(QGIS_APP_SRCS qgsmaptoolhtmlannotation.cpp qgsmaptoolidentifyaction.cpp qgsmaptoolmeasureangle.cpp + qgsmaptoolmeasurebearing.cpp qgsmaptoolmovefeature.cpp qgsmaptooloffsetcurve.cpp qgsmaptooloffsetpointsymbol.cpp diff --git a/src/app/maptools/qgsappmaptools.cpp b/src/app/maptools/qgsappmaptools.cpp index 6387bc9a2d6..44bbb86c260 100644 --- a/src/app/maptools/qgsappmaptools.cpp +++ b/src/app/maptools/qgsappmaptools.cpp @@ -29,6 +29,7 @@ #include "qgsmaptoolcircle3points.h" #include "qgsmaptoolcircle2tangentspoint.h" #include "qgsmaptoolmeasureangle.h" +#include "qgsmaptoolmeasurebearing.h" #include "qgsmaptoolformannotation.h" #include "qgsmaptoolsvgannotation.h" #include "qgsmaptoolcircularstringcurvepoint.h" @@ -120,6 +121,7 @@ QgsAppMapTools::QgsAppMapTools( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockW mTools.insert( Tool::MeasureDistance, new QgsMeasureTool( canvas, false /* area */ ) ); mTools.insert( Tool::MeasureArea, new QgsMeasureTool( canvas, true /* area */ ) ); mTools.insert( Tool::MeasureAngle, new QgsMapToolMeasureAngle( canvas ) ); + mTools.insert( Tool::MeasureBearing, new QgsMapToolMeasureBearing( canvas ) ); mTools.insert( Tool::TextAnnotation, new QgsMapToolTextAnnotation( canvas ) ); mTools.insert( Tool::FormAnnotation, new QgsMapToolFormAnnotation( canvas ) ); mTools.insert( Tool::HtmlAnnotation, new QgsMapToolHtmlAnnotation( canvas ) ); diff --git a/src/app/maptools/qgsappmaptools.h b/src/app/maptools/qgsappmaptools.h index abfe1c05c3c..eb6764dda4f 100644 --- a/src/app/maptools/qgsappmaptools.h +++ b/src/app/maptools/qgsappmaptools.h @@ -56,6 +56,7 @@ class QgsAppMapTools MeasureDistance, MeasureArea, MeasureAngle, + MeasureBearing, AddFeature, CircularStringCurvePoint, CircularStringRadius, diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index c54a415b6ec..d1eeeca5752 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -457,6 +457,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX(); #include "qgsmaptoolidentifyaction.h" #include "qgsmaptoolpinlabels.h" #include "qgsmaptoolmeasureangle.h" +#include "qgsmaptoolmeasurebearing.h" #include "qgsmaptoolrotatepointsymbols.h" #include "qgsmaptooldigitizefeature.h" #include "qgsmaptooloffsetpointsymbol.h" @@ -2643,6 +2644,7 @@ void QgisApp::createActions() connect( mActionMeasure, &QAction::triggered, this, &QgisApp::measure ); connect( mActionMeasureArea, &QAction::triggered, this, &QgisApp::measureArea ); connect( mActionMeasureAngle, &QAction::triggered, this, &QgisApp::measureAngle ); + connect( mActionMeasureBearing, &QAction::triggered, this, [ = ] { setMapTool( mMapTools->mapTool( QgsAppMapTools::MeasureBearing ) ); } ); connect( mActionZoomFullExtent, &QAction::triggered, this, &QgisApp::zoomFull ); connect( mActionZoomToLayer, &QAction::triggered, this, &QgisApp::zoomToLayerExtent ); connect( mActionZoomToLayers, &QAction::triggered, this, &QgisApp::zoomToLayerExtent ); @@ -2951,6 +2953,7 @@ void QgisApp::createActionGroups() mMapToolGroup->addAction( mActionMeasure ); mMapToolGroup->addAction( mActionMeasureArea ); mMapToolGroup->addAction( mActionMeasureAngle ); + mMapToolGroup->addAction( mActionMeasureBearing ); mMapToolGroup->addAction( mActionAddFeature ); mMapToolGroup->addAction( mActionCircularStringCurvePoint ); mMapToolGroup->addAction( mActionCircularStringRadius ); @@ -3396,6 +3399,7 @@ void QgisApp::createToolBars() bt->setPopupMode( QToolButton::MenuButtonPopup ); bt->addAction( mActionMeasure ); bt->addAction( mActionMeasureArea ); + bt->addAction( mActionMeasureBearing ); bt->addAction( mActionMeasureAngle ); QAction *defMeasureAction = mActionMeasure; @@ -3408,6 +3412,9 @@ void QgisApp::createToolBars() defMeasureAction = mActionMeasureArea; break; case 2: + defMeasureAction = mActionMeasureBearing; + break; + case 3: defMeasureAction = mActionMeasureAngle; break; } @@ -4078,6 +4085,7 @@ void QgisApp::setTheme( const QString &themeName ) mActionMeasure->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMeasure.svg" ) ) ); mActionMeasureArea->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMeasureArea.svg" ) ) ); mActionMeasureAngle->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMeasureAngle.svg" ) ) ); + mActionMeasureBearing->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMeasureBearing.svg" ) ) ); mActionMapTips->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMapTips.svg" ) ) ); mActionShowBookmarkManager->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionShowBookmarks.svg" ) ) ); mActionShowBookmarks->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionShowBookmarks.svg" ) ) ); @@ -4297,6 +4305,7 @@ void QgisApp::setupCanvasTools() mMapTools->mapTool( QgsAppMapTools::MeasureDistance )->setAction( mActionMeasure ); mMapTools->mapTool( QgsAppMapTools::MeasureArea )->setAction( mActionMeasureArea ); mMapTools->mapTool( QgsAppMapTools::MeasureAngle )->setAction( mActionMeasureAngle ); + mMapTools->mapTool( QgsAppMapTools::MeasureBearing )->setAction( mActionMeasureBearing ); mMapTools->mapTool( QgsAppMapTools::TextAnnotation )->setAction( mActionTextAnnotation ); mMapTools->mapTool( QgsAppMapTools::FormAnnotation )->setAction( mActionFormAnnotation ); mMapTools->mapTool( QgsAppMapTools::HtmlAnnotation )->setAction( mActionHtmlAnnotation ); @@ -12678,6 +12687,7 @@ void QgisApp::showOptionsDialog( QWidget *parent, const QString ¤tPage, in mMapTools->mapTool< QgsMeasureTool >( QgsAppMapTools::MeasureDistance )->updateSettings(); mMapTools->mapTool< QgsMeasureTool >( QgsAppMapTools::MeasureArea )->updateSettings(); mMapTools->mapTool< QgsMapToolMeasureAngle >( QgsAppMapTools::MeasureAngle )->updateSettings(); + mMapTools->mapTool< QgsMapToolMeasureBearing >( QgsAppMapTools::MeasureBearing )->updateSettings(); #ifdef HAVE_3D const QList< Qgs3DMapCanvasDockWidget * > canvases3D = findChildren< Qgs3DMapCanvasDockWidget * >(); @@ -14599,6 +14609,7 @@ void QgisApp::projectProperties( const QString ¤tPage ) mMapTools->mapTool< QgsMeasureTool >( QgsAppMapTools::MeasureDistance )->updateSettings(); mMapTools->mapTool< QgsMeasureTool >( QgsAppMapTools::MeasureArea )->updateSettings(); mMapTools->mapTool< QgsMapToolMeasureAngle >( QgsAppMapTools::MeasureAngle )->updateSettings(); + mMapTools->mapTool< QgsMapToolMeasureBearing >( QgsAppMapTools::MeasureBearing )->updateSettings(); // Set the window title. setTitleBarText_( *this ); diff --git a/src/app/qgsdisplayangle.cpp b/src/app/qgsdisplayangle.cpp index a455d40c4f7..089b09b5538 100644 --- a/src/app/qgsdisplayangle.cpp +++ b/src/app/qgsdisplayangle.cpp @@ -19,26 +19,36 @@ #include "qgsunittypes.h" #include "qgsmaptoolmeasureangle.h" #include "qgssettings.h" +#include "qgsprojectdisplaysettings.h" +#include "qgsproject.h" +#include "qgsbearingnumericformat.h" #include -QgsDisplayAngle::QgsDisplayAngle( QgsMapToolMeasureAngle *tool, Qt::WindowFlags f ) +QgsDisplayAngle::QgsDisplayAngle( QgsMapTool *tool, Qt::WindowFlags f ) : QDialog( tool->canvas()->topLevelWidget(), f ) , mTool( tool ) { setupUi( this ); } -void QgsDisplayAngle::setValueInRadians( double value ) +void QgsDisplayAngle::setAngleInRadians( double value ) { mValue = value; - updateUi(); -} -void QgsDisplayAngle::updateUi() -{ QgsSettings settings; QgsUnitTypes::AngleUnit unit = QgsUnitTypes::decodeAngleUnit( settings.value( QStringLiteral( "qgis/measure/angleunits" ), QgsUnitTypes::encodeUnit( QgsUnitTypes::AngleDegrees ) ).toString() ); int decimals = settings.value( QStringLiteral( "qgis/measure/decimalplaces" ), 3 ).toInt(); mAngleLineEdit->setText( QgsUnitTypes::formatAngle( mValue * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AngleRadians, unit ), decimals, unit ) ); } + +void QgsDisplayAngle::setBearingInRadians( double value ) +{ + mValue = value; + + const double degrees = value * 180.0 / M_PI; + + QgsNumericFormatContext context; + const QString valueAsText = QgsProject::instance()->displaySettings()->bearingFormat()->formatDouble( degrees, context ); + mAngleLineEdit->setText( valueAsText ); +} diff --git a/src/app/qgsdisplayangle.h b/src/app/qgsdisplayangle.h index e52f297b004..7ca10c5bce3 100644 --- a/src/app/qgsdisplayangle.h +++ b/src/app/qgsdisplayangle.h @@ -19,7 +19,7 @@ #include "ui_qgsdisplayanglebase.h" #include "qgis_app.h" -class QgsMapToolMeasureAngle; +class QgsMapTool; //! A class that displays results of angle measurements with the proper unit class APP_EXPORT QgsDisplayAngle: public QDialog, private Ui::QgsDisplayAngleBase @@ -27,23 +27,35 @@ class APP_EXPORT QgsDisplayAngle: public QDialog, private Ui::QgsDisplayAngleBas Q_OBJECT public: - QgsDisplayAngle( QgsMapToolMeasureAngle *tool = nullptr, Qt::WindowFlags f = Qt::WindowFlags() ); + QgsDisplayAngle( QgsMapTool *tool = nullptr, Qt::WindowFlags f = Qt::WindowFlags() ); /** * Sets the measured angle value (in radians). The value is going to * be converted to degrees / gon automatically if necessary. */ - void setValueInRadians( double value ); + void setAngleInRadians( double value ); + + /** + * Sets the measured bearing (in radians). + */ + void setBearingInRadians( double value ); + + /** + * Returns the current angular value (in radians) + */ + double value() const { return mValue; } + + /** + * Returns the current value, as a string. + */ + QString text() const { return mAngleLineEdit->text(); } private: //! pointer to tool which owns this dialog - QgsMapToolMeasureAngle *mTool = nullptr; + QgsMapTool *mTool = nullptr; //! The value we're showing double mValue = 0.0; - - //! Updates UI according to user settings. - void updateUi(); }; #endif // QGSDISPLAYANGLE_H diff --git a/src/app/qgsmaptoolmeasureangle.cpp b/src/app/qgsmaptoolmeasureangle.cpp index 6bd573a24db..44f9cf9aeff 100644 --- a/src/app/qgsmaptoolmeasureangle.cpp +++ b/src/app/qgsmaptoolmeasureangle.cpp @@ -80,7 +80,7 @@ void QgsMapToolMeasureAngle::canvasMoveEvent( QgsMapMouseEvent *e ) } } - mResultDisplay->setValueInRadians( resultAngle ); + mResultDisplay->setAngleInRadians( resultAngle ); } } @@ -210,7 +210,7 @@ void QgsMapToolMeasureAngle::updateSettings() } } - mResultDisplay->setValueInRadians( resultAngle ); + mResultDisplay->setAngleInRadians( resultAngle ); } void QgsMapToolMeasureAngle::configureDistanceArea() diff --git a/src/app/qgsmaptoolmeasurebearing.cpp b/src/app/qgsmaptoolmeasurebearing.cpp new file mode 100644 index 00000000000..ff40f5ff553 --- /dev/null +++ b/src/app/qgsmaptoolmeasurebearing.cpp @@ -0,0 +1,188 @@ +/*************************************************************************** + qgsmaptoolmeasurebearing.cpp + ------------------------ + begin : June 2021 + copyright : (C) 2021 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsmaptoolmeasurebearing.h" +#include "qgsdisplayangle.h" +#include "qgsdistancearea.h" +#include "qgslogger.h" +#include "qgsmapcanvas.h" +#include "qgsmaptopixel.h" +#include "qgsproject.h" +#include "qgsrubberband.h" +#include "qgssnappingutils.h" +#include "qgssettings.h" +#include "qgssnapindicator.h" +#include "qgsmapmouseevent.h" + +#include + +QgsMapToolMeasureBearing::QgsMapToolMeasureBearing( QgsMapCanvas *canvas ) + : QgsMapTool( canvas ) + , mSnapIndicator( new QgsSnapIndicator( canvas ) ) +{ + mToolName = tr( "Measure bearing" ); + + connect( canvas, &QgsMapCanvas::destinationCrsChanged, + this, &QgsMapToolMeasureBearing::updateSettings ); +} + +QgsMapToolMeasureBearing::~QgsMapToolMeasureBearing() +{ + stopMeasuring(); +} + +void QgsMapToolMeasureBearing::canvasMoveEvent( QgsMapMouseEvent *e ) +{ + QgsPointXY point = e->snapPoint(); + mSnapIndicator->setMatch( e->mapPointMatch() ); + + if ( !mRubberBand || mAnglePoints.empty() || mAnglePoints.size() >= 2 ) + { + return; + } + + mRubberBand->movePoint( point ); + if ( mAnglePoints.size() == 1 ) + { + if ( !mResultDisplay->isVisible() ) + { + mResultDisplay->move( e->pos() - QPoint( 100, 100 ) ); + mResultDisplay->show(); + } + + const double bearing = mDa.bearing( mAnglePoints.at( 0 ), point ); + mResultDisplay->setBearingInRadians( bearing ); + } +} + +void QgsMapToolMeasureBearing::canvasReleaseEvent( QgsMapMouseEvent *e ) +{ + // if we clicked the right button we cancel the operation, unless it's the "final" click + if ( e->button() == Qt::RightButton && mAnglePoints.size() != 1 ) + { + stopMeasuring(); + return; + } + + //add points until we have two + if ( mAnglePoints.size() == 2 ) + { + mAnglePoints.clear(); + } + + if ( mAnglePoints.empty() ) + { + if ( !mResultDisplay ) + { + mResultDisplay = new QgsDisplayAngle( this ); + mResultDisplay->setWindowFlags( mResultDisplay->windowFlags() | Qt::Tool ); + mResultDisplay->setWindowTitle( tr( "Bearing" ) ); + connect( mResultDisplay, &QDialog::rejected, this, &QgsMapToolMeasureBearing::stopMeasuring ); + } + configureDistanceArea(); + createRubberBand(); + } + + if ( mAnglePoints.size() < 2 ) + { + QgsPointXY newPoint = e->snapPoint(); + mAnglePoints.push_back( newPoint ); + mRubberBand->addPoint( newPoint ); + } +} + +void QgsMapToolMeasureBearing::keyPressEvent( QKeyEvent *e ) +{ + if ( e->key() == Qt::Key_Escape ) + { + stopMeasuring(); + } + else if ( ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete ) ) + { + if ( !mAnglePoints.empty() && mRubberBand ) + { + if ( mAnglePoints.size() == 1 ) + { + //removing first point, so restart everything + stopMeasuring(); + } + else + { + //remove second last point from line band, and last point from points band + mRubberBand->removePoint( -2, true ); + mAnglePoints.removeLast(); + } + } + + // Override default shortcut management in MapCanvas + e->ignore(); + } +} + +void QgsMapToolMeasureBearing::stopMeasuring() +{ + delete mRubberBand; + mRubberBand = nullptr; + delete mResultDisplay; + mResultDisplay = nullptr; + mAnglePoints.clear(); +} + +void QgsMapToolMeasureBearing::activate() +{ + QgsMapTool::activate(); +} + +void QgsMapToolMeasureBearing::deactivate() +{ + mSnapIndicator->setMatch( QgsPointLocator::Match() ); + + stopMeasuring(); + QgsMapTool::deactivate(); +} + +void QgsMapToolMeasureBearing::createRubberBand() +{ + delete mRubberBand; + mRubberBand = new QgsRubberBand( mCanvas, QgsWkbTypes::LineGeometry ); + + QgsSettings settings; + int myRed = settings.value( QStringLiteral( "qgis/default_measure_color_red" ), 180 ).toInt(); + int myGreen = settings.value( QStringLiteral( "qgis/default_measure_color_green" ), 180 ).toInt(); + int myBlue = settings.value( QStringLiteral( "qgis/default_measure_color_blue" ), 180 ).toInt(); + mRubberBand->setColor( QColor( myRed, myGreen, myBlue, 100 ) ); + mRubberBand->setWidth( 3 ); +} + +void QgsMapToolMeasureBearing::updateSettings() +{ + if ( mAnglePoints.size() != 2 ) + return; + + if ( !mResultDisplay ) + return; + + configureDistanceArea(); + + const double bearing = mDa.bearing( mAnglePoints.at( 0 ), mAnglePoints.at( 1 ) ); + mResultDisplay->setBearingInRadians( bearing ); +} + +void QgsMapToolMeasureBearing::configureDistanceArea() +{ + QString ellipsoidId = QgsProject::instance()->ellipsoid(); + mDa.setSourceCrs( mCanvas->mapSettings().destinationCrs(), QgsProject::instance()->transformContext() ); + mDa.setEllipsoid( ellipsoidId ); +} diff --git a/src/app/qgsmaptoolmeasurebearing.h b/src/app/qgsmaptoolmeasurebearing.h new file mode 100644 index 00000000000..c5983b4d72e --- /dev/null +++ b/src/app/qgsmaptoolmeasurebearing.h @@ -0,0 +1,72 @@ +/*************************************************************************** + qgsmaptoolmeasurebearing.h + ------------------------ + begin : June 2021 + copyright : (C) 2021 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSMAPTOOLMEASUREBEARING_H +#define QGSMAPTOOLMEASUREBEARING_H + +#include "qgsmaptool.h" +#include "qgspointxy.h" +#include "qgsdistancearea.h" +#include "qgis_app.h" + +class QgsDisplayAngle; +class QgsRubberBand; +class QgsSnapIndicator; + +//! Map tool to measure bearing between two points +class APP_EXPORT QgsMapToolMeasureBearing: public QgsMapTool +{ + Q_OBJECT + public: + QgsMapToolMeasureBearing( QgsMapCanvas *canvas ); + ~QgsMapToolMeasureBearing() override; + + Flags flags() const override { return QgsMapTool::AllowZoomRect; } + void canvasMoveEvent( QgsMapMouseEvent *e ) override; + void canvasReleaseEvent( QgsMapMouseEvent *e ) override; + void keyPressEvent( QKeyEvent *e ) override; + void activate() override; + void deactivate() override; + + private: + //! Points defining the angle (three for measuring) + QList mAnglePoints; + QgsRubberBand *mRubberBand = nullptr; + QgsDisplayAngle *mResultDisplay = nullptr; + + //! Creates a new rubber band and deletes the old one + void createRubberBand(); + + //! Tool for measuring + QgsDistanceArea mDa; + + std::unique_ptr mSnapIndicator; + + public slots: + //! Recalculate angle if projection state changed + void updateSettings(); + + private slots: + //! Deletes the rubber band and the dialog + void stopMeasuring(); + + //! Configures distance area objects with ellipsoid / output crs + void configureDistanceArea(); + + friend class TestQgsMeasureBearingTool; + +}; + +#endif // QGSMAPTOOLMEASUREBEARING_H diff --git a/src/ui/qgisapp.ui b/src/ui/qgisapp.ui index 60935d49f2a..4bab0c2ca1d 100644 --- a/src/ui/qgisapp.ui +++ b/src/ui/qgisapp.ui @@ -97,6 +97,7 @@ + @@ -3533,9 +3534,25 @@ Shows placeholders for labels which could not be placed, e.g. due to overlaps wi Toggles stream digitizing mode + + + true + + + + :/images/themes/default/mActionMeasureBearing.svg:/images/themes/default/mActionMeasureBearing.svg + + + Measure Bearing + + + Measure Bearing + + + diff --git a/tests/src/app/CMakeLists.txt b/tests/src/app/CMakeLists.txt index 644be83ed18..f4723cd4844 100644 --- a/tests/src/app/CMakeLists.txt +++ b/tests/src/app/CMakeLists.txt @@ -79,6 +79,7 @@ ADD_QGIS_TEST(maptoolregularpolygontest testqgsmaptoolregularpolygon.cpp) ADD_QGIS_TEST(maptoolsplitpartstest testqgsmaptoolsplitparts.cpp) ADD_QGIS_TEST(maptoolsplitfeaturestest testqgsmaptoolsplitfeatures.cpp) ADD_QGIS_TEST(measuretool testqgsmeasuretool.cpp) +ADD_QGIS_TEST(measurebearingtool testqgsmeasurebearingtool.cpp) ADD_QGIS_TEST(vertextool testqgsvertextool.cpp) ADD_QGIS_TEST(vectorlayersaveasdialogtest testqgsvectorlayersaveasdialog.cpp) ADD_QGIS_TEST(maptoolreverselinetest testqgsmaptoolreverseline.cpp) diff --git a/tests/src/app/testqgsmeasurebearingtool.cpp b/tests/src/app/testqgsmeasurebearingtool.cpp new file mode 100644 index 00000000000..57a775e742a --- /dev/null +++ b/tests/src/app/testqgsmeasurebearingtool.cpp @@ -0,0 +1,160 @@ +/*************************************************************************** + testqgsmeasurebearingtool.cpp + ---------------------- + Date : 2021-06-15 + Copyright : (C) 2021 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include "qgstest.h" +#include "qgisapp.h" +#include "qgsapplication.h" +#include "qgsvectorlayer.h" +#include "qgsfeature.h" +#include "qgsgeometry.h" +#include "qgsvectordataprovider.h" +#include "qgsmeasuretool.h" +#include "qgsmeasuredialog.h" +#include "qgsproject.h" +#include "qgsmapcanvas.h" +#include "qgsunittypes.h" +#include "qgsmaptoolmeasurebearing.h" +#include "testqgsmaptoolutils.h" +#include "qgsdisplayangle.h" + +/** + * \ingroup UnitTests + * This is a unit test for the measure bearing tool + */ +class TestQgsMeasureBearingTool : public QObject +{ + Q_OBJECT + public: + TestQgsMeasureBearingTool(); + + private slots: + void initTestCase();// will be called before the first testfunction is executed. + void cleanupTestCase();// will be called after the last testfunction was executed. + void init() {} // will be called before each testfunction is executed. + void cleanup() {} // will be called after every testfunction. + void testBearingCartesian(); + void testBearingEllipsoid(); + + private: + QgisApp *mQgisApp = nullptr; + QgsMapCanvas *mCanvas = nullptr; +}; + +TestQgsMeasureBearingTool::TestQgsMeasureBearingTool() = default; + +//runs before all tests +void TestQgsMeasureBearingTool::initTestCase() +{ + qDebug() << "TestQgisAppClipboard::initTestCase()"; + // init QGIS's paths - true means that all path will be inited from prefix + QgsApplication::init(); + QgsApplication::initQgis(); + + // Set up the QgsSettings environment + QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) ); + QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) ); + QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) ); + + mQgisApp = new QgisApp(); + mCanvas = new QgsMapCanvas(); + + mCanvas->setFrameStyle( QFrame::NoFrame ); + mCanvas->resize( 50, 50 ); + mCanvas->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) ); + mCanvas->setExtent( QgsRectangle( -11554312, -5082786, 15123372, 11957046 ) ); + mCanvas->show(); // to make the canvas resize + mCanvas->hide(); + + // enforce C locale because the tests expect it + // (decimal separators / thousand separators) + QLocale::setDefault( QLocale::c() ); +} + +//runs after all tests +void TestQgsMeasureBearingTool::cleanupTestCase() +{ + delete mCanvas; + QgsApplication::exitQgis(); +} + +void TestQgsMeasureBearingTool::testBearingCartesian() +{ + // set project CRS and set ellipsoid to none, so that Cartesian calculations are performed + QgsCoordinateReferenceSystem srs( QStringLiteral( "EPSG:3857" ) ); + QgsProject::instance()->setCrs( srs ); + QgsProject::instance()->setEllipsoid( QString() ); + + QgsMapToolMeasureBearing *mapTool = new QgsMapToolMeasureBearing( mCanvas ) ; + mCanvas->setMapTool( mapTool ); + + QVERIFY( !mapTool->mResultDisplay ); + + TestQgsMapToolUtils tools( mapTool ); + + tools.mouseMove( 8749058, 1916460 ); + tools.mouseClick( 8749058, 1916460, Qt::LeftButton ); + tools.mouseMove( 14498439, -2694154 ); + + QVERIFY( mapTool->mResultDisplay ); + QGSCOMPARENEAR( mapTool->mResultDisplay->value(), 2.1995926132, 0.001 ); + QCOMPARE( mapTool->mResultDisplay->text(), QStringLiteral( "126.027373\u00B0E" ) ); + + tools.mouseClick( 14498439, -2694154, Qt::LeftButton ); + QGSCOMPARENEAR( mapTool->mResultDisplay->value(), 2.1995926132, 0.001 ); + QCOMPARE( mapTool->mResultDisplay->text(), QStringLiteral( "126.027373\u00B0E" ) ); + + tools.mouseMove( 555496, 3291312 ); + tools.mouseClick( 555496, 3291312, Qt::LeftButton ); + tools.mouseMove( -611045, 5082786 ); + tools.mouseClick( -611045, 5082786, Qt::LeftButton ); + QGSCOMPARENEAR( mapTool->mResultDisplay->value(), -0.5880026035, 0.001 ); + QCOMPARE( mapTool->mResultDisplay->text(), QStringLiteral( "33.690068\u00B0W" ) ); +} + +void TestQgsMeasureBearingTool::testBearingEllipsoid() +{ + // set project CRS and ellipsoid + QgsCoordinateReferenceSystem srs( QStringLiteral( "EPSG:3857" ) ); + QgsProject::instance()->setCrs( srs ); + QgsProject::instance()->setEllipsoid( QStringLiteral( "EPSG:7030" ) ); + + QgsMapToolMeasureBearing *mapTool = new QgsMapToolMeasureBearing( mCanvas ) ; + mCanvas->setMapTool( mapTool ); + + QVERIFY( !mapTool->mResultDisplay ); + + TestQgsMapToolUtils tools( mapTool ); + + tools.mouseMove( 8749058, 1916460 ); + tools.mouseClick( 8749058, 1916460, Qt::LeftButton ); + tools.mouseMove( 14498439, -2694154 ); + + QVERIFY( mapTool->mResultDisplay ); + QGSCOMPARENEAR( mapTool->mResultDisplay->value(), 2.1679949043, 0.001 ); + QCOMPARE( mapTool->mResultDisplay->text(), QStringLiteral( "124.216958\u00B0E" ) ); + + tools.mouseClick( 14498439, -2694154, Qt::LeftButton ); + QGSCOMPARENEAR( mapTool->mResultDisplay->value(), 2.1679949043, 0.001 ); + QCOMPARE( mapTool->mResultDisplay->text(), QStringLiteral( "124.216958\u00B0E" ) ); + + tools.mouseMove( 555496, 3291312 ); + tools.mouseClick( 555496, 3291312, Qt::LeftButton ); + tools.mouseMove( -611045, 5082786 ); + tools.mouseClick( -611045, 5082786, Qt::LeftButton ); + QGSCOMPARENEAR( mapTool->mResultDisplay->value(), -0.5448498177, 0.001 ); + QCOMPARE( mapTool->mResultDisplay->text(), QStringLiteral( "31.217595\u00B0W" ) ); +} + +QGSTEST_MAIN( TestQgsMeasureBearingTool ) +#include "testqgsmeasurebearingtool.moc"