Skip to content

Commit e811c54

Browse files
authoredApr 26, 2018
Merge pull request #6835 from wonder-sk/identify-selection-modes
[FEATURE] Identify: more selection modes (simple / polygon / freehand / radius)
2 parents 16a6a90 + f2a2eeb commit e811c54

30 files changed

+1268
-950
lines changed
 

‎images/images.qrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,10 @@
649649
<file>themes/default/mActionNewReport.svg</file>
650650
<file>themes/default/mIconReport.svg</file>
651651
<file>themes/default/downloading_svg.svg</file>
652+
<file>themes/default/mActionIdentifyByFreehand.svg</file>
653+
<file>themes/default/mActionIdentifyByPolygon.svg</file>
654+
<file>themes/default/mActionIdentifyByRadius.svg</file>
655+
<file>themes/default/mActionIdentifyByRectangle.svg</file>
652656
</qresource>
653657
<qresource prefix="/images/tips">
654658
<file alias="symbol_levels.png">qgis_tips/symbol_levels.png</file>
Lines changed: 63 additions & 0 deletions
Loading
Lines changed: 63 additions & 0 deletions
Loading
Lines changed: 68 additions & 0 deletions
Loading
Lines changed: 59 additions & 0 deletions
Loading

‎python/gui/qgsmaptoolidentify.sip.in

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,16 @@ this has been made private and two publics methods are offered
108108
:return: a list of IdentifyResult*
109109
%End
110110

111+
QList<QgsMapToolIdentify::IdentifyResult> identify( const QgsGeometry &geometry, IdentifyMode mode, LayerType layerType );
112+
%Docstring
113+
Performs identification based on a geometry (in map coordinates)
114+
%End
115+
QList<QgsMapToolIdentify::IdentifyResult> identify( const QgsGeometry &geometry, IdentifyMode mode, const QList<QgsMapLayer *> &layerList, LayerType layerType );
116+
%Docstring
117+
Performs identification based on a geometry (in map coordinates)
118+
%End
119+
120+
111121
QgsIdentifyMenu *identifyMenu();
112122
%Docstring
113123
return a pointer to the identify menu which will be used in layer selection mode

‎src/app/CMakeLists.txt

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,7 @@ SET(QGIS_APP_SRCS
9797
qgsmaptoolrotatelabel.cpp
9898
qgsmaptoolrotatepointsymbols.cpp
9999
qgsmaptoolselect.cpp
100-
qgsmaptoolselectrectangle.cpp
101-
qgsmaptoolselectfreehand.cpp
102-
qgsmaptoolselectpolygon.cpp
103-
qgsmaptoolselectradius.cpp
100+
qgsmaptoolselectionhandler.cpp
104101
qgsmaptoolselectutils.cpp
105102
qgsmaptoolsimplify.cpp
106103
qgsmaptoolsplitfeatures.cpp
@@ -310,11 +307,8 @@ SET (QGIS_APP_MOC_HDRS
310307
qgsmaptoolrotatefeature.h
311308
qgsmaptoolrotatelabel.h
312309
qgsmaptoolrotatepointsymbols.h
313-
qgsmaptoolselectfreehand.h
314310
qgsmaptoolselect.h
315-
qgsmaptoolselectpolygon.h
316-
qgsmaptoolselectradius.h
317-
qgsmaptoolselectrectangle.h
311+
qgsmaptoolselectionhandler.h
318312
qgsmaptoolsimplify.h
319313
qgsmaptoolsplitfeatures.h
320314
qgsmaptoolsplitparts.h

‎src/app/qgisapp.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -386,10 +386,6 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
386386
#include "qgsmaptooloffsetpointsymbol.h"
387387
#include "qgsmaptoolpan.h"
388388
#include "qgsmaptoolselect.h"
389-
#include "qgsmaptoolselectrectangle.h"
390-
#include "qgsmaptoolselectfreehand.h"
391-
#include "qgsmaptoolselectpolygon.h"
392-
#include "qgsmaptoolselectradius.h"
393389
#include "qgsmaptoolsvgannotation.h"
394390
#include "qgsmaptoolreshape.h"
395391
#include "qgsmaptoolrotatepointsymbols.h"
@@ -1317,7 +1313,6 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
13171313
// should come after fileNewBlank to ensure project is properly set up to receive any data source files
13181314
QgsApplication::setFileOpenEventReceiver( this );
13191315

1320-
13211316
#ifdef ANDROID
13221317
toggleFullScreen();
13231318
#endif
@@ -3413,14 +3408,18 @@ void QgisApp::createCanvasTools()
34133408
mMapTools.mSplitFeatures->setAction( mActionSplitFeatures );
34143409
mMapTools.mSplitParts = new QgsMapToolSplitParts( mMapCanvas );
34153410
mMapTools.mSplitParts->setAction( mActionSplitParts );
3416-
mMapTools.mSelectFeatures = new QgsMapToolSelectFeatures( mMapCanvas );
3411+
mMapTools.mSelectFeatures = new QgsMapToolSelect( mMapCanvas );
34173412
mMapTools.mSelectFeatures->setAction( mActionSelectFeatures );
3418-
mMapTools.mSelectPolygon = new QgsMapToolSelectPolygon( mMapCanvas );
3413+
mMapTools.mSelectFeatures->setSelectionMode( QgsMapToolSelectionHandler::SelectSimple );
3414+
mMapTools.mSelectPolygon = new QgsMapToolSelect( mMapCanvas );
34193415
mMapTools.mSelectPolygon->setAction( mActionSelectPolygon );
3420-
mMapTools.mSelectFreehand = new QgsMapToolSelectFreehand( mMapCanvas );
3416+
mMapTools.mSelectPolygon->setSelectionMode( QgsMapToolSelectionHandler::SelectPolygon );
3417+
mMapTools.mSelectFreehand = new QgsMapToolSelect( mMapCanvas );
34213418
mMapTools.mSelectFreehand->setAction( mActionSelectFreehand );
3422-
mMapTools.mSelectRadius = new QgsMapToolSelectRadius( mMapCanvas );
3419+
mMapTools.mSelectFreehand->setSelectionMode( QgsMapToolSelectionHandler::SelectFreehand );
3420+
mMapTools.mSelectRadius = new QgsMapToolSelect( mMapCanvas );
34233421
mMapTools.mSelectRadius->setAction( mActionSelectRadius );
3422+
mMapTools.mSelectRadius->setSelectionMode( QgsMapToolSelectionHandler::SelectRadius );
34243423
mMapTools.mAddRing = new QgsMapToolAddRing( mMapCanvas );
34253424
mMapTools.mAddRing->setAction( mActionAddRing );
34263425
mMapTools.mFillRing = new QgsMapToolFillRing( mMapCanvas );

‎src/app/qgisapp.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class QgsMapToolAddFeature;
7676
class QgsMapToolDigitizeFeature;
7777
class QgsMapToolAdvancedDigitizing;
7878
class QgsMapToolIdentifyAction;
79+
class QgsMapToolSelect;
7980
class QgsPluginLayer;
8081
class QgsPluginLayer;
8182
class QgsPluginManager;
@@ -1964,11 +1965,10 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
19641965
QgsMapTool *mReshapeFeatures = nullptr;
19651966
QgsMapTool *mSplitFeatures = nullptr;
19661967
QgsMapTool *mSplitParts = nullptr;
1967-
QgsMapTool *mSelect = nullptr;
1968-
QgsMapTool *mSelectFeatures = nullptr;
1969-
QgsMapTool *mSelectPolygon = nullptr;
1970-
QgsMapTool *mSelectFreehand = nullptr;
1971-
QgsMapTool *mSelectRadius = nullptr;
1968+
QgsMapToolSelect *mSelectFeatures = nullptr;
1969+
QgsMapToolSelect *mSelectPolygon = nullptr;
1970+
QgsMapToolSelect *mSelectFreehand = nullptr;
1971+
QgsMapToolSelect *mSelectRadius = nullptr;
19721972
QgsMapTool *mVertexAdd = nullptr;
19731973
QgsMapTool *mVertexMove = nullptr;
19741974
QgsMapTool *mVertexDelete = nullptr;

‎src/app/qgsidentifyresultsdialog.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "qgsfeaturestore.h"
2828
#include "qgsgeometry.h"
2929
#include "qgshighlight.h"
30+
#include "qgsmaptoolidentifyaction.h"
3031
#include "qgsidentifyresultsdialog.h"
3132
#include "qgslogger.h"
3233
#include "qgsmapcanvas.h"
@@ -408,6 +409,8 @@ QgsIdentifyResultsDialog::QgsIdentifyResultsDialog( QgsMapCanvas *canvas, QWidge
408409
connect( mOpenFormAction, &QAction::triggered, this, &QgsIdentifyResultsDialog::featureForm );
409410
connect( mClearResultsAction, &QAction::triggered, this, &QgsIdentifyResultsDialog::clear );
410411
connect( mHelpToolButton, &QAbstractButton::clicked, this, &QgsIdentifyResultsDialog::showHelp );
412+
413+
initSelectionModes();
411414
}
412415

413416
QgsIdentifyResultsDialog::~QgsIdentifyResultsDialog()
@@ -424,6 +427,31 @@ QgsIdentifyResultsDialog::~QgsIdentifyResultsDialog()
424427
mPlotCurves.clear();
425428
}
426429

430+
void QgsIdentifyResultsDialog::initSelectionModes()
431+
{
432+
mSelectModeButton = new QToolButton( mIdentifyToolbar );
433+
mSelectModeButton->setPopupMode( QToolButton::MenuButtonPopup );
434+
QList<QAction *> selectActions;
435+
selectActions << mActionSelectFeatures << mActionSelectPolygon
436+
<< mActionSelectFreehand << mActionSelectRadius;
437+
438+
QActionGroup *group = new QActionGroup( this );
439+
group->addAction( mActionSelectFeatures );
440+
group->addAction( mActionSelectPolygon );
441+
group->addAction( mActionSelectFreehand );
442+
group->addAction( mActionSelectRadius );
443+
444+
mSelectModeButton->addActions( selectActions );
445+
mSelectModeButton->setDefaultAction( mActionSelectFeatures );
446+
447+
mIdentifyToolbar->addWidget( mSelectModeButton );
448+
449+
connect( mActionSelectFeatures, &QAction::triggered, this, &QgsIdentifyResultsDialog::setSelectionMode );
450+
connect( mActionSelectPolygon, &QAction::triggered, this, &QgsIdentifyResultsDialog::setSelectionMode );
451+
connect( mActionSelectFreehand, &QAction::triggered, this, &QgsIdentifyResultsDialog::setSelectionMode );
452+
connect( mActionSelectRadius, &QAction::triggered, this, &QgsIdentifyResultsDialog::setSelectionMode );
453+
}
454+
427455
QTreeWidgetItem *QgsIdentifyResultsDialog::layerItem( QObject *object )
428456
{
429457
for ( int i = 0; i < lstResults->topLevelItemCount(); i++ )
@@ -1991,6 +2019,40 @@ void QgsIdentifyResultsDialog::showHelp()
19912019
QgsHelp::openHelp( QStringLiteral( "introduction/general_tools.html#identify" ) );
19922020
}
19932021

2022+
void QgsIdentifyResultsDialog::setSelectionMode()
2023+
{
2024+
QObject *obj = sender();
2025+
QgsMapToolSelectionHandler::SelectionMode oldMode = mSelectionMode;
2026+
if ( obj == mActionSelectFeatures )
2027+
{
2028+
mSelectModeButton->setDefaultAction( mActionSelectFeatures );
2029+
mSelectionMode = QgsMapToolSelectionHandler::SelectSimple;
2030+
}
2031+
else if ( obj == mActionSelectPolygon )
2032+
{
2033+
mSelectModeButton->setDefaultAction( mActionSelectPolygon );
2034+
mSelectionMode = QgsMapToolSelectionHandler::SelectPolygon;
2035+
}
2036+
else if ( obj == mActionSelectFreehand )
2037+
{
2038+
mSelectModeButton->setDefaultAction( mActionSelectFreehand );
2039+
mSelectionMode = QgsMapToolSelectionHandler::SelectFreehand;
2040+
}
2041+
else if ( obj == mActionSelectRadius )
2042+
{
2043+
mSelectModeButton->setDefaultAction( mActionSelectRadius );
2044+
mSelectionMode = QgsMapToolSelectionHandler::SelectRadius;
2045+
}
2046+
2047+
if ( oldMode != mSelectionMode )
2048+
emit selectionModeChanged();
2049+
}
2050+
2051+
QgsMapToolSelectionHandler::SelectionMode QgsIdentifyResultsDialog::selectionMode() const
2052+
{
2053+
return mSelectionMode;
2054+
}
2055+
19942056
void QgsIdentifyResultsDialog::setExpressionContextScope( const QgsExpressionContextScope &scope )
19952057
{
19962058
mExpressionContextScope = scope;

‎src/app/qgsidentifyresultsdialog.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "qgsmaptoolidentify.h"
2727
#include "qgswebview.h"
2828
#include "qgsexpressioncontext.h"
29+
#include "qgsmaptoolselectionhandler.h"
2930

3031
#include <QWidget>
3132
#include <QList>
@@ -167,6 +168,8 @@ class APP_EXPORT QgsIdentifyResultsDialog: public QDialog, private Ui::QgsIdenti
167168
*/
168169
QgsExpressionContextScope expressionContextScope() const;
169170

171+
QgsMapToolSelectionHandler::SelectionMode selectionMode() const;
172+
170173
signals:
171174
void selectedFeatureChanged( QgsVectorLayer *, QgsFeatureId featureId );
172175

@@ -177,6 +180,8 @@ class APP_EXPORT QgsIdentifyResultsDialog: public QDialog, private Ui::QgsIdenti
177180

178181
void activateLayer( QgsMapLayer * );
179182

183+
void selectionModeChanged();
184+
180185
public slots:
181186
//! Remove results
182187
void clear();
@@ -251,6 +256,7 @@ class APP_EXPORT QgsIdentifyResultsDialog: public QDialog, private Ui::QgsIdenti
251256
QList<QgsFeature> mFeatures;
252257
QMap< QString, QMap< QString, QVariant > > mWidgetCaches;
253258
QgsExpressionContextScope mExpressionContextScope;
259+
QToolButton *mSelectModeButton = nullptr;
254260

255261
QgsMapLayer *layer( QTreeWidgetItem *item );
256262
QgsVectorLayer *vectorLayer( QTreeWidgetItem *item );
@@ -259,6 +265,7 @@ class APP_EXPORT QgsIdentifyResultsDialog: public QDialog, private Ui::QgsIdenti
259265
QTreeWidgetItem *layerItem( QTreeWidgetItem *item );
260266
QTreeWidgetItem *layerItem( QObject *layer );
261267

268+
262269
void highlightLayer( QTreeWidgetItem *object );
263270
void layerProperties( QTreeWidgetItem *object );
264271
void disconnectLayer( QObject *object );
@@ -279,6 +286,12 @@ class APP_EXPORT QgsIdentifyResultsDialog: public QDialog, private Ui::QgsIdenti
279286
QVector<QgsIdentifyPlotCurve *> mPlotCurves;
280287

281288
void showHelp();
289+
290+
QgsMapToolSelectionHandler::SelectionMode mSelectionMode = QgsMapToolSelectionHandler::SelectSimple;
291+
292+
void setSelectionMode();
293+
294+
void initSelectionModes();
282295
};
283296

284297
class QgsIdentifyResultsDialogMapLayerAction : public QAction

‎src/app/qgsmaptoolidentifyaction.cpp

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "qgsmaptopixel.h"
2929
#include "qgsmessageviewer.h"
3030
#include "qgsmaptoolidentifyaction.h"
31+
#include "qgsmaptoolselectionhandler.h"
3132
#include "qgsrasterlayer.h"
3233
#include "qgscoordinatereferencesystem.h"
3334
#include "qgsvectordataprovider.h"
@@ -55,6 +56,9 @@ QgsMapToolIdentifyAction::QgsMapToolIdentifyAction( QgsMapCanvas *canvas )
5556
QgsMapLayerAction *attrTableAction = new QgsMapLayerAction( tr( "Show attribute table" ), mIdentifyMenu, QgsMapLayer::VectorLayer, QgsMapLayerAction::MultipleFeatures );
5657
connect( attrTableAction, &QgsMapLayerAction::triggeredForFeatures, this, &QgsMapToolIdentifyAction::showAttributeTable );
5758
identifyMenu()->addCustomAction( attrTableAction );
59+
mSelectionHandler = new QgsMapToolSelectionHandler( canvas, QgsMapToolSelectionHandler::SelectSimple );
60+
connect( mSelectionHandler, &QgsMapToolSelectionHandler::geometryChanged, this, &QgsMapToolIdentifyAction::identifyFromGeometry );
61+
5862
}
5963

6064
QgsMapToolIdentifyAction::~QgsMapToolIdentifyAction()
@@ -63,6 +67,7 @@ QgsMapToolIdentifyAction::~QgsMapToolIdentifyAction()
6367
{
6468
mResultsDialog->done( 0 );
6569
}
70+
delete mSelectionHandler;
6671
}
6772

6873
QgsIdentifyResultsDialog *QgsMapToolIdentifyAction::resultsDialog()
@@ -73,6 +78,10 @@ QgsIdentifyResultsDialog *QgsMapToolIdentifyAction::resultsDialog()
7378

7479
connect( mResultsDialog.data(), static_cast<void ( QgsIdentifyResultsDialog::* )( QgsRasterLayer * )>( &QgsIdentifyResultsDialog::formatChanged ), this, &QgsMapToolIdentify::formatChanged );
7580
connect( mResultsDialog.data(), &QgsIdentifyResultsDialog::copyToClipboard, this, &QgsMapToolIdentifyAction::handleCopyToClipboard );
81+
connect( mResultsDialog.data(), &QgsIdentifyResultsDialog::selectionModeChanged, this, [this]
82+
{
83+
mSelectionHandler->setSelectionMode( mResultsDialog->selectionMode() );
84+
} );
7685
}
7786

7887
return mResultsDialog;
@@ -98,34 +107,29 @@ void QgsMapToolIdentifyAction::showAttributeTable( QgsMapLayer *layer, const QLi
98107
tableDialog->show();
99108
}
100109

101-
void QgsMapToolIdentifyAction::canvasMoveEvent( QgsMapMouseEvent *e )
102-
{
103-
Q_UNUSED( e );
104-
}
105110

106-
void QgsMapToolIdentifyAction::canvasPressEvent( QgsMapMouseEvent *e )
107-
{
108-
Q_UNUSED( e );
109-
}
110-
111-
void QgsMapToolIdentifyAction::canvasReleaseEvent( QgsMapMouseEvent *e )
111+
void QgsMapToolIdentifyAction::identifyFromGeometry()
112112
{
113113
resultsDialog()->clear();
114114
connect( this, &QgsMapToolIdentifyAction::identifyProgress, QgisApp::instance(), &QgisApp::showProgress );
115115
connect( this, &QgsMapToolIdentifyAction::identifyMessage, QgisApp::instance(), &QgisApp::showStatusMessage );
116116

117-
setClickContextScope( toMapCoordinates( e->pos() ) );
117+
QgsGeometry geometry = mSelectionHandler->selectedGeometry();
118+
bool isSinglePoint = geometry.type() == QgsWkbTypes::PointGeometry;
119+
120+
if ( isSinglePoint )
121+
setClickContextScope( geometry.asPoint() );
118122

119123
identifyMenu()->setResultsIfExternalAction( false );
120124

121125
// enable the right click for extended menu so it behaves as a contextual menu
122126
// this would be removed when a true contextual menu is brought in QGIS
123-
bool extendedMenu = e->modifiers() == Qt::ShiftModifier || e->button() == Qt::RightButton;
127+
bool extendedMenu = isSinglePoint && mShowExtendedMenu;
124128
identifyMenu()->setExecWithSingleResult( extendedMenu );
125129
identifyMenu()->setShowFeatureActions( extendedMenu );
126130
IdentifyMode mode = extendedMenu ? LayerSelection : DefaultQgsSetting;
127131

128-
QList<IdentifyResult> results = QgsMapToolIdentify::identify( e->x(), e->y(), mode );
132+
QList<IdentifyResult> results = QgsMapToolIdentify::identify( geometry, mode, AllLayers );
129133

130134
disconnect( this, &QgsMapToolIdentifyAction::identifyProgress, QgisApp::instance(), &QgisApp::showProgress );
131135
disconnect( this, &QgsMapToolIdentifyAction::identifyMessage, QgisApp::instance(), &QgisApp::showStatusMessage );
@@ -156,6 +160,23 @@ void QgsMapToolIdentifyAction::canvasReleaseEvent( QgsMapMouseEvent *e )
156160
resultsDialog()->updateViewModes();
157161
}
158162

163+
void QgsMapToolIdentifyAction::canvasMoveEvent( QgsMapMouseEvent *e )
164+
{
165+
mSelectionHandler->canvasMoveEvent( e );
166+
}
167+
168+
void QgsMapToolIdentifyAction::canvasPressEvent( QgsMapMouseEvent *e )
169+
{
170+
mSelectionHandler->canvasPressEvent( e );
171+
}
172+
173+
void QgsMapToolIdentifyAction::canvasReleaseEvent( QgsMapMouseEvent *e )
174+
{
175+
mShowExtendedMenu = e->modifiers() == Qt::ShiftModifier || e->button() == Qt::RightButton;
176+
mSelectionHandler->canvasReleaseEvent( e );
177+
mShowExtendedMenu = false;
178+
}
179+
159180
void QgsMapToolIdentifyAction::handleChangedRasterResults( QList<IdentifyResult> &results )
160181
{
161182
// Add new result after raster format change
@@ -179,6 +200,7 @@ void QgsMapToolIdentifyAction::activate()
179200
void QgsMapToolIdentifyAction::deactivate()
180201
{
181202
resultsDialog()->deactivate();
203+
mSelectionHandler->deactivate();
182204
QgsMapToolIdentify::deactivate();
183205
}
184206

@@ -211,3 +233,12 @@ void QgsMapToolIdentifyAction::setClickContextScope( const QgsPointXY &point )
211233
mIdentifyMenu->setExpressionContextScope( clickScope );
212234
}
213235
}
236+
237+
238+
void QgsMapToolIdentifyAction::keyReleaseEvent( QKeyEvent *e )
239+
{
240+
if ( mSelectionHandler->keyReleaseEvent( e ) )
241+
return;
242+
243+
QgsMapTool::keyReleaseEvent( e );
244+
}

‎src/app/qgsmaptoolidentifyaction.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323
#include <QPointer>
2424
#include "qgis_app.h"
2525

26+
class QgisInterface;
2627
class QgsIdentifyResultsDialog;
2728
class QgsMapLayer;
29+
class QgsMapToolSelectionHandler;
2830
class QgsRasterLayer;
2931
class QgsVectorLayer;
3032
class QgsFeatureStore;
@@ -70,16 +72,23 @@ class APP_EXPORT QgsMapToolIdentifyAction : public QgsMapToolIdentify
7072
private slots:
7173
void showAttributeTable( QgsMapLayer *layer, const QList<QgsFeature> &featureList );
7274

75+
void identifyFromGeometry();
76+
7377
private:
7478
//! Pointer to the identify results dialog for name/value pairs
7579
QPointer<QgsIdentifyResultsDialog> mResultsDialog;
7680

81+
QgsMapToolSelectionHandler *mSelectionHandler = nullptr;
82+
bool mShowExtendedMenu = false;
83+
7784
QgsIdentifyResultsDialog *resultsDialog();
7885

7986
QgsUnitTypes::DistanceUnit displayDistanceUnits() const override;
8087
QgsUnitTypes::AreaUnit displayAreaUnits() const override;
8188
void setClickContextScope( const QgsPointXY &point );
8289

90+
void keyReleaseEvent( QKeyEvent *e ) override;
91+
8392
friend class TestQgsMapToolIdentifyAction;
8493
};
8594

‎src/app/qgsmaptoolselect.cpp

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,60 @@
3131
QgsMapToolSelect::QgsMapToolSelect( QgsMapCanvas *canvas )
3232
: QgsMapTool( canvas )
3333
{
34-
mToolName = tr( "Select" );
35-
mCursor = Qt::ArrowCursor;
36-
mFillColor = QColor( 254, 178, 76, 63 );
37-
mStrokeColor = QColor( 254, 58, 29, 100 );
34+
mToolName = tr( "Select features" );
35+
36+
mSelectionHandler = qgis::make_unique<QgsMapToolSelectionHandler>( canvas );
37+
connect( mSelectionHandler.get(), &QgsMapToolSelectionHandler::geometryChanged, this, &QgsMapToolSelect::selectFeatures );
38+
setSelectionMode( QgsMapToolSelectionHandler::SelectSimple );
39+
}
40+
41+
void QgsMapToolSelect::setSelectionMode( QgsMapToolSelectionHandler::SelectionMode selectionMode )
42+
{
43+
mSelectionHandler->setSelectionMode( selectionMode );
44+
if ( selectionMode == QgsMapToolSelectionHandler::SelectSimple )
45+
mCursor = QgsApplication::getThemeCursor( QgsApplication::Cursor::Select );
46+
else
47+
mCursor = Qt::ArrowCursor;
48+
}
49+
50+
void QgsMapToolSelect::canvasPressEvent( QgsMapMouseEvent *e )
51+
{
52+
mSelectionHandler->canvasPressEvent( e );
53+
}
54+
55+
void QgsMapToolSelect::canvasMoveEvent( QgsMapMouseEvent *e )
56+
{
57+
mSelectionHandler->canvasMoveEvent( e );
3858
}
3959

4060
void QgsMapToolSelect::canvasReleaseEvent( QgsMapMouseEvent *e )
4161
{
42-
QgsVectorLayer *vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( mCanvas );
43-
if ( !vlayer )
62+
mSelectionHandler->canvasReleaseEvent( e );
63+
}
64+
65+
void QgsMapToolSelect::keyReleaseEvent( QKeyEvent *e )
66+
{
67+
if ( mSelectionHandler->keyReleaseEvent( e ) )
4468
return;
4569

46-
QgsRubberBand rubberBand( mCanvas, QgsWkbTypes::PolygonGeometry );
47-
rubberBand.setFillColor( mFillColor );
48-
rubberBand.setStrokeColor( mStrokeColor );
49-
QRect selectRect( 0, 0, 0, 0 );
50-
QgsMapToolSelectUtils::expandSelectRectangle( selectRect, vlayer, e->pos() );
51-
QgsMapToolSelectUtils::setRubberBand( mCanvas, selectRect, &rubberBand );
52-
QgsGeometry selectGeom( rubberBand.asGeometry() );
53-
QgsMapToolSelectUtils::selectSingleFeature( mCanvas, selectGeom, e->modifiers() );
54-
rubberBand.reset( QgsWkbTypes::PolygonGeometry );
70+
QgsMapTool::keyReleaseEvent( e );
71+
}
72+
73+
void QgsMapToolSelect::deactivate()
74+
{
75+
mSelectionHandler->deactivate();
76+
QgsMapTool::deactivate();
77+
}
78+
79+
void QgsMapToolSelect::selectFeatures( Qt::KeyboardModifiers modifiers )
80+
{
81+
if ( mSelectionHandler->selectionMode() == QgsMapToolSelectionHandler::SelectSimple &&
82+
mSelectionHandler->selectedGeometry().type() == QgsWkbTypes::PointGeometry )
83+
{
84+
QgsVectorLayer *vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( mCanvas );
85+
QgsRectangle r = QgsMapToolSelectUtils::expandSelectRectangle( mSelectionHandler->selectedGeometry().asPoint(), mCanvas, vlayer );
86+
QgsMapToolSelectUtils::selectSingleFeature( mCanvas, QgsGeometry::fromRect( r ), modifiers );
87+
}
88+
else
89+
QgsMapToolSelectUtils::selectMultipleFeatures( mCanvas, mSelectionHandler->selectedGeometry(), modifiers );
5590
}

‎src/app/qgsmaptoolselect.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "qgsmaptool.h"
2020
#include "qgis_app.h"
21+
#include "qgsmaptoolselectionhandler.h"
2122

2223
class QgsMapCanvas;
2324
class QMouseEvent;
@@ -28,12 +29,19 @@ class APP_EXPORT QgsMapToolSelect : public QgsMapTool
2829
public:
2930
QgsMapToolSelect( QgsMapCanvas *canvas );
3031

31-
//! Overridden mouse release event
32+
void setSelectionMode( QgsMapToolSelectionHandler::SelectionMode selectionMode );
33+
34+
void canvasPressEvent( QgsMapMouseEvent *e ) override;
35+
void canvasMoveEvent( QgsMapMouseEvent *e ) override;
3236
void canvasReleaseEvent( QgsMapMouseEvent *e ) override;
37+
void keyReleaseEvent( QKeyEvent *e ) override;
38+
void deactivate() override;
39+
40+
private slots:
41+
void selectFeatures( Qt::KeyboardModifiers modifiers );
3342

3443
private:
35-
QColor mFillColor;
36-
QColor mStrokeColor;
44+
std::unique_ptr<QgsMapToolSelectionHandler> mSelectionHandler;
3745
};
3846

3947
#endif

‎src/app/qgsmaptoolselectfreehand.cpp

Lines changed: 0 additions & 97 deletions
This file was deleted.

‎src/app/qgsmaptoolselectfreehand.h

Lines changed: 0 additions & 54 deletions
This file was deleted.

‎src/app/qgsmaptoolselectionhandler.cpp

Lines changed: 453 additions & 0 deletions
Large diffs are not rendered by default.

‎src/app/qgsmaptoolselectionhandler.h

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/***************************************************************************
2+
qgsmaptoolselectionhandler.cpp
3+
---------------------
4+
begin : March 2018
5+
copyright : (C) 2018 by Viktor Sklencar
6+
email : vsklencar at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSMAPTOOLSELECTIONHANDLER_H
17+
#define QGSMAPTOOLSELECTIONHANDLER_H
18+
19+
#include <QObject>
20+
#include <QWidget>
21+
22+
#include "qgsgeometry.h"
23+
24+
class QHBoxLayout;
25+
class QKeyEvent;
26+
27+
class QgsDoubleSpinBox;
28+
class QgsMapCanvas;
29+
class QgsMapMouseEvent;
30+
class QgsRubberBand;
31+
class QgsSnapIndicator;
32+
33+
/// @cond private
34+
35+
/**
36+
* \ingroup gui
37+
* \class QgsDistanceWidget
38+
* \brief Spinner widget for radius selection.
39+
*
40+
* \since QGIS 3.2
41+
*/
42+
class QgsDistanceWidget : public QWidget
43+
{
44+
Q_OBJECT
45+
46+
public:
47+
48+
//! Constrructor
49+
explicit QgsDistanceWidget( const QString &label = QString(), QWidget *parent = nullptr );
50+
51+
//! distance setter
52+
void setDistance( double distance );
53+
//! distance getter
54+
double distance();
55+
56+
signals:
57+
//! distance changed signal
58+
void distanceChanged( double distance );
59+
//! distanceEditingFinished signal
60+
void distanceEditingFinished( double distance, const Qt::KeyboardModifiers &modifiers );
61+
//! distanceEditingCanceled signal
62+
void distanceEditingCanceled();
63+
64+
protected:
65+
virtual bool eventFilter( QObject *obj, QEvent *ev ) override;
66+
67+
private:
68+
QHBoxLayout *mLayout = nullptr;
69+
QgsDoubleSpinBox *mDistanceSpinBox = nullptr;
70+
};
71+
72+
/// @endcond
73+
74+
75+
/**
76+
* \ingroup gui
77+
* \brief Utility class for handling various methods to create geometry for selection in layers.
78+
* \since QGIS 3.2
79+
*/
80+
class QgsMapToolSelectionHandler : public QObject
81+
{
82+
Q_OBJECT
83+
84+
public:
85+
86+
//! Select features to identify by:
87+
enum SelectionMode
88+
{
89+
//! SelectSimple - single click or drawing a rectangle, default option
90+
SelectSimple,
91+
//! SelectPolygon - drawing a polygon
92+
SelectPolygon,
93+
//! SelectFreehand - free hand selection
94+
SelectFreehand,
95+
//! SelectRadius - a circle selection
96+
SelectRadius
97+
};
98+
Q_ENUM( SelectionMode )
99+
100+
//! constructor
101+
QgsMapToolSelectionHandler( QgsMapCanvas *canvas,
102+
QgsMapToolSelectionHandler::SelectionMode selectionMode = QgsMapToolSelectionHandler::SelectionMode::SelectSimple );
103+
104+
//! destructor
105+
~QgsMapToolSelectionHandler() override;
106+
107+
//! Returns most recently selected geometry (may be a point or a polygon)
108+
QgsGeometry selectedGeometry() const;
109+
110+
//! Sets the current selection mode
111+
SelectionMode selectionMode() const;
112+
//! Returns the current selection mode
113+
void setSelectionMode( SelectionMode mode );
114+
115+
//! Deactivates handler (when map tool gets deactivated)
116+
void deactivate();
117+
118+
//! Handles mouse move event from map tool
119+
void canvasMoveEvent( QgsMapMouseEvent *e );
120+
//! Handles mouse press event from map tool
121+
void canvasPressEvent( QgsMapMouseEvent *e );
122+
//! Handles mouse releasae event from map tool
123+
void canvasReleaseEvent( QgsMapMouseEvent *e );
124+
//! Handles escape press event - returns true if the even has been processed
125+
bool keyReleaseEvent( QKeyEvent *e );
126+
127+
signals:
128+
//! emitted when a new geometry has been picked (selectedGeometry())
129+
void geometryChanged( Qt::KeyboardModifiers modifiers = Qt::NoModifier );
130+
131+
private slots:
132+
//! update the rubber band from the input widget
133+
void updateRadiusRubberband( const double &radius );
134+
135+
/**
136+
* triggered when the user input widget has a new value
137+
* either programmatically (from the mouse event) or entered by the user
138+
*/
139+
void radiusValueEntered( const double &radius, const Qt::KeyboardModifiers &modifiers );
140+
141+
//! cancel selecting (between two click events)
142+
void cancel();
143+
144+
private:
145+
146+
void selectFeaturesMoveEvent( QgsMapMouseEvent *e );
147+
void selectFeaturesReleaseEvent( QgsMapMouseEvent *e );
148+
void selectFeaturesPressEvent( QgsMapMouseEvent *e );
149+
150+
void selectPolygonMoveEvent( QgsMapMouseEvent *e );
151+
void selectPolygonPressEvent( QgsMapMouseEvent *e );
152+
153+
void selectFreehandMoveEvent( QgsMapMouseEvent *e );
154+
void selectFreehandReleaseEvent( QgsMapMouseEvent *e );
155+
156+
void selectRadiusMoveEvent( QgsMapMouseEvent *e );
157+
void selectRadiusReleaseEvent( QgsMapMouseEvent *e );
158+
159+
void initRubberBand();
160+
161+
QgsPointXY toMapCoordinates( QPoint point );
162+
163+
void createDistanceWidget();
164+
void deleteDistanceWidget();
165+
166+
void updateRadiusFromEdge( QgsPointXY &radiusEdge );
167+
168+
void setSelectedGeometry( const QgsGeometry &geometry, Qt::KeyboardModifiers modifiers = Qt::NoModifier );
169+
170+
private:
171+
172+
QgsMapCanvas *mCanvas = nullptr;
173+
174+
//! the rubberband for selection visualization
175+
std::unique_ptr<QgsRubberBand> mSelectionRubberBand;
176+
177+
//! Stores the most recent geometry (in map coordinates)
178+
QgsGeometry mSelectionGeometry;
179+
180+
//! Whether a geometry is being picked right now
181+
bool mSelectionActive = false;
182+
183+
SelectionMode mSelectionMode = SelectSimple;
184+
185+
//! Snap indicator used in radius selection mode
186+
std::unique_ptr<QgsSnapIndicator> mSnapIndicator;
187+
188+
//! Initial point (in screen coordinates) when using simple selection
189+
QPoint mInitDragPos;
190+
191+
//! Center point for the radius when using radius selection
192+
QgsPointXY mRadiusCenter;
193+
194+
//! Shows current angle value and allows numerical editing
195+
QgsDistanceWidget *mDistanceWidget = nullptr;
196+
197+
QColor mFillColor = QColor( 254, 178, 76, 63 );
198+
QColor mStrokeColor = QColor( 254, 58, 29, 100 );
199+
};
200+
201+
#endif

‎src/app/qgsmaptoolselectpolygon.cpp

Lines changed: 0 additions & 76 deletions
This file was deleted.

‎src/app/qgsmaptoolselectpolygon.h

Lines changed: 0 additions & 50 deletions
This file was deleted.

‎src/app/qgsmaptoolselectradius.cpp

Lines changed: 0 additions & 282 deletions
This file was deleted.

‎src/app/qgsmaptoolselectradius.h

Lines changed: 0 additions & 124 deletions
This file was deleted.

‎src/app/qgsmaptoolselectrectangle.cpp

Lines changed: 0 additions & 117 deletions
This file was deleted.

‎src/app/qgsmaptoolselectrectangle.h

Lines changed: 0 additions & 62 deletions
This file was deleted.

‎src/app/qgsmaptoolselectutils.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,10 @@ void QgsMapToolSelectUtils::setRubberBand( QgsMapCanvas *canvas, QRect &selectRe
6565
}
6666
}
6767

68-
void QgsMapToolSelectUtils::expandSelectRectangle( QRect &selectRect,
69-
QgsVectorLayer *vlayer,
70-
QPoint point )
68+
QgsRectangle QgsMapToolSelectUtils::expandSelectRectangle( QgsPointXY mapPoint, QgsMapCanvas *canvas, QgsVectorLayer *vlayer )
7169
{
7270
int boxSize = 0;
73-
if ( vlayer->geometryType() != QgsWkbTypes::PolygonGeometry )
71+
if ( !vlayer || vlayer->geometryType() != QgsWkbTypes::PolygonGeometry )
7472
{
7573
//if point or line use an artificial bounding box of 10x10 pixels
7674
//to aid the user to click on a feature accurately
@@ -81,10 +79,12 @@ void QgsMapToolSelectUtils::expandSelectRectangle( QRect &selectRect,
8179
//otherwise just use the click point for polys
8280
boxSize = 1;
8381
}
84-
selectRect.setLeft( point.x() - boxSize );
85-
selectRect.setRight( point.x() + boxSize );
86-
selectRect.setTop( point.y() - boxSize );
87-
selectRect.setBottom( point.y() + boxSize );
82+
83+
const QgsMapToPixel *transform = canvas->getCoordinateTransform();
84+
QgsPointXY point = transform->transform( mapPoint );
85+
QgsPointXY ll = transform->toMapCoordinates( point.x() - boxSize, point.y() + boxSize );
86+
QgsPointXY ur = transform->toMapCoordinates( point.x() + boxSize, point.y() - boxSize );
87+
return QgsRectangle( ll, ur );
8888
}
8989

9090
void QgsMapToolSelectUtils::selectMultipleFeatures( QgsMapCanvas *canvas, const QgsGeometry &selectGeometry, const Qt::KeyboardModifiers &modifiers )
@@ -297,4 +297,3 @@ QgsFeatureIds QgsMapToolSelectUtils::getMatchingFeatures( QgsMapCanvas *canvas,
297297
return newSelectedFeatures;
298298
}
299299

300-

‎src/app/qgsmaptoolselectutils.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,13 @@ namespace QgsMapToolSelectUtils
9898
QgsVectorLayer *getCurrentVectorLayer( QgsMapCanvas *canvas );
9999

100100
/**
101-
Expands a rectangle to a minimum size for selection based on the vector layer type
102-
\param selectRect The QRect to expand
101+
Expands a point to a rectangle with minimum size for selection based on the vector layer type
102+
\param point The point to expand the rectangle around (in map coordinates)
103+
\param canvas The map canvas used to transform between canvas and map units
103104
\param vlayer The vector layer layer
104-
\param vlayer The point to expand the rectangle around
105+
\returns Expanded rectangle in map units
105106
*/
106-
void expandSelectRectangle( QRect &selectRect, QgsVectorLayer *vlayer, QPoint point );
107+
QgsRectangle expandSelectRectangle( QgsPointXY mapPoint, QgsMapCanvas *canvas, QgsVectorLayer *vlayer );
107108

108109
/**
109110
Sets a QgsRubberband to rectangle in map units using a rectangle defined in device coords

‎src/gui/qgsmaptoolidentify.cpp

Lines changed: 79 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "qgsfeaturestore.h"
2121
#include "qgsfields.h"
2222
#include "qgsgeometry.h"
23+
#include "qgsgeometryengine.h"
2324
#include "qgsidentifymenu.h"
2425
#include "qgslogger.h"
2526
#include "qgsmapcanvas.h"
@@ -89,10 +90,20 @@ QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( int x, i
8990
}
9091

9192
QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( int x, int y, IdentifyMode mode, const QList<QgsMapLayer *> &layerList, LayerType layerType )
93+
{
94+
return identify( QgsGeometry::fromPointXY( toMapCoordinates( QPoint( x, y ) ) ), mode, layerList, layerType );
95+
}
96+
97+
QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( const QgsGeometry &geometry, IdentifyMode mode, LayerType layerType )
98+
{
99+
return identify( geometry, mode, QList<QgsMapLayer *>(), layerType );
100+
}
101+
102+
QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( const QgsGeometry &geometry, IdentifyMode mode, const QList<QgsMapLayer *> &layerList, LayerType layerType )
92103
{
93104
QList<IdentifyResult> results;
94105

95-
mLastPoint = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y );
106+
mLastGeometry = geometry;
96107
mLastExtent = mCanvas->extent();
97108
mLastMapUnitsPerPixel = mCanvas->mapUnitsPerPixel();
98109

@@ -106,6 +117,8 @@ QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( int x, i
106117

107118
if ( mode == LayerSelection )
108119
{
120+
QPoint canvasPt = toCanvasCoordinates( geometry.asPoint() );
121+
int x = canvasPt.x(), y = canvasPt.y();
109122
QList<IdentifyResult> results = identify( x, y, TopDownAll, layerList, layerType );
110123
QPoint globalPos = mCanvas->mapToGlobal( QPoint( x + 5, y + 5 ) );
111124
return mIdentifyMenu->exec( results, globalPos );
@@ -122,7 +135,7 @@ QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( int x, i
122135

123136
QApplication::setOverrideCursor( Qt::WaitCursor );
124137

125-
identifyLayer( &results, layer, mLastPoint, mLastExtent, mLastMapUnitsPerPixel, layerType );
138+
identifyLayer( &results, layer, mLastGeometry, mLastExtent, mLastMapUnitsPerPixel, layerType );
126139
}
127140
else
128141
{
@@ -152,7 +165,7 @@ QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( int x, i
152165
if ( noIdentifyLayerIdList.contains( layer->id() ) )
153166
continue;
154167

155-
if ( identifyLayer( &results, layer, mLastPoint, mLastExtent, mLastMapUnitsPerPixel, layerType ) )
168+
if ( identifyLayer( &results, layer, mLastGeometry, mLastExtent, mLastMapUnitsPerPixel, layerType ) )
156169
{
157170
if ( mode == TopDownStopAtFirst )
158171
break;
@@ -178,23 +191,33 @@ void QgsMapToolIdentify::deactivate()
178191
QgsMapTool::deactivate();
179192
}
180193

181-
bool QgsMapToolIdentify::identifyLayer( QList<IdentifyResult> *results, QgsMapLayer *layer, const QgsPointXY &point, const QgsRectangle &viewExtent, double mapUnitsPerPixel, LayerType layerType )
194+
bool QgsMapToolIdentify::identifyLayer( QList<IdentifyResult> *results, QgsMapLayer *layer, const QgsPointXY &point, const QgsRectangle &viewExtent, double mapUnitsPerPixel, QgsMapToolIdentify::LayerType layerType )
195+
{
196+
return identifyLayer( results, layer, QgsGeometry::fromPointXY( point ), viewExtent, mapUnitsPerPixel, layerType );
197+
}
198+
199+
bool QgsMapToolIdentify::identifyLayer( QList<IdentifyResult> *results, QgsMapLayer *layer, const QgsGeometry &geometry, const QgsRectangle &viewExtent, double mapUnitsPerPixel, QgsMapToolIdentify::LayerType layerType )
182200
{
183201
if ( layer->type() == QgsMapLayer::RasterLayer && layerType.testFlag( RasterLayer ) )
184202
{
185-
return identifyRasterLayer( results, qobject_cast<QgsRasterLayer *>( layer ), point, viewExtent, mapUnitsPerPixel );
203+
return identifyRasterLayer( results, qobject_cast<QgsRasterLayer *>( layer ), geometry, viewExtent, mapUnitsPerPixel );
186204
}
187205
else if ( layer->type() == QgsMapLayer::VectorLayer && layerType.testFlag( VectorLayer ) )
188206
{
189-
return identifyVectorLayer( results, qobject_cast<QgsVectorLayer *>( layer ), point );
207+
return identifyVectorLayer( results, qobject_cast<QgsVectorLayer *>( layer ), geometry );
190208
}
191209
else
192210
{
193211
return false;
194212
}
195213
}
196214

197-
bool QgsMapToolIdentify::identifyVectorLayer( QList<IdentifyResult> *results, QgsVectorLayer *layer, const QgsPointXY &point )
215+
bool QgsMapToolIdentify::identifyVectorLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorLayer *layer, const QgsPointXY &point )
216+
{
217+
return identifyVectorLayer( results, layer, QgsGeometry::fromPointXY( point ) );
218+
}
219+
220+
bool QgsMapToolIdentify::identifyVectorLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorLayer *layer, const QgsGeometry &geometry )
198221
{
199222
if ( !layer || !layer->isSpatial() )
200223
return false;
@@ -209,33 +232,62 @@ bool QgsMapToolIdentify::identifyVectorLayer( QList<IdentifyResult> *results, Qg
209232

210233
QMap< QString, QString > commonDerivedAttributes;
211234

212-
commonDerivedAttributes.insert( tr( "(clicked coordinate X)" ), formatXCoordinate( point ) );
213-
commonDerivedAttributes.insert( tr( "(clicked coordinate Y)" ), formatYCoordinate( point ) );
235+
QgsGeometry selectionGeom = geometry;
236+
bool isPointOrRectangle;
237+
QgsPointXY point;
238+
bool isSingleClick = selectionGeom.type() == QgsWkbTypes::PointGeometry;
239+
if ( isSingleClick )
240+
{
241+
isPointOrRectangle = true;
242+
point = selectionGeom.asPoint();
243+
244+
commonDerivedAttributes.insert( tr( "(clicked coordinate X)" ), formatXCoordinate( point ) );
245+
commonDerivedAttributes.insert( tr( "(clicked coordinate Y)" ), formatYCoordinate( point ) );
246+
}
247+
else
248+
{
249+
// we have a polygon - maybe it is a rectangle - in such case we can avoid costly insterestion tests later
250+
isPointOrRectangle = QgsGeometry::fromRect( selectionGeom.boundingBox() ).isGeosEqual( selectionGeom );
251+
}
214252

215253
int featureCount = 0;
216254

217255
QgsFeatureList featureList;
256+
std::unique_ptr<QgsGeometryEngine> selectionGeomPrepared;
218257

219258
// toLayerCoordinates will throw an exception for an 'invalid' point.
220259
// For example, if you project a world map onto a globe using EPSG 2163
221260
// and then click somewhere off the globe, an exception will be thrown.
222261
try
223262
{
224-
// create the search rectangle
225-
double searchRadius = searchRadiusMU( mCanvas );
226-
227263
QgsRectangle r;
228-
r.setXMinimum( point.x() - searchRadius );
229-
r.setXMaximum( point.x() + searchRadius );
230-
r.setYMinimum( point.y() - searchRadius );
231-
r.setYMaximum( point.y() + searchRadius );
264+
if ( isSingleClick )
265+
{
266+
double sr = searchRadiusMU( mCanvas );
267+
r = toLayerCoordinates( layer, QgsRectangle( point.x() - sr, point.y() - sr, point.x() + sr, point.y() + sr ) );
268+
}
269+
else
270+
{
271+
r = toLayerCoordinates( layer, selectionGeom.boundingBox() );
272+
273+
if ( !isPointOrRectangle )
274+
{
275+
QgsCoordinateTransform ct( mCanvas->mapSettings().destinationCrs(), layer->crs(), mCanvas->mapSettings().transformContext() );
276+
if ( ct.isValid() )
277+
selectionGeom.transform( ct );
232278

233-
r = toLayerCoordinates( layer, r );
279+
// use prepared geometry for faster intersection test
280+
selectionGeomPrepared.reset( QgsGeometry::createGeometryEngine( selectionGeom.constGet() ) );
281+
}
282+
}
234283

235284
QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterRect( r ).setFlags( QgsFeatureRequest::ExactIntersect ) );
236285
QgsFeature f;
237286
while ( fit.nextFeature( f ) )
238-
featureList << QgsFeature( f );
287+
{
288+
if ( !selectionGeomPrepared || selectionGeomPrepared->intersects( f.geometry().constGet() ) )
289+
featureList << QgsFeature( f );
290+
}
239291
}
240292
catch ( QgsCsException &cse )
241293
{
@@ -268,7 +320,8 @@ bool QgsMapToolIdentify::identifyVectorLayer( QList<IdentifyResult> *results, Qg
268320

269321
featureCount++;
270322

271-
derivedAttributes.unite( featureDerivedAttributes( feature, layer, toLayerCoordinates( layer, point ) ) );
323+
if ( isSingleClick )
324+
derivedAttributes.unite( featureDerivedAttributes( feature, layer, toLayerCoordinates( layer, point ) ) );
272325

273326
derivedAttributes.insert( tr( "feature id" ), fid < 0 ? tr( "new feature" ) : FID_TO_STRING( fid ) );
274327

@@ -458,6 +511,12 @@ QMap< QString, QString > QgsMapToolIdentify::featureDerivedAttributes( const Qgs
458511
return derivedAttributes;
459512
}
460513

514+
bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, const QgsGeometry &geometry, const QgsRectangle &viewExtent, double mapUnitsPerPixel )
515+
{
516+
QgsPointXY point = geometry.asPoint(); // raster layers currently only support identification by point
517+
return identifyRasterLayer( results, layer, point, viewExtent, mapUnitsPerPixel );
518+
}
519+
461520
bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, QgsPointXY point, const QgsRectangle &viewExtent, double mapUnitsPerPixel )
462521
{
463522
QgsDebugMsg( "point = " + point.toString() );
@@ -697,7 +756,7 @@ QString QgsMapToolIdentify::formatArea( double area ) const
697756
void QgsMapToolIdentify::formatChanged( QgsRasterLayer *layer )
698757
{
699758
QList<IdentifyResult> results;
700-
if ( identifyRasterLayer( &results, layer, mLastPoint, mLastExtent, mLastMapUnitsPerPixel ) )
759+
if ( identifyRasterLayer( &results, layer, mLastGeometry, mLastExtent, mLastMapUnitsPerPixel ) )
701760
{
702761
emit changedRasterResults( results );
703762
}

‎src/gui/qgsmaptoolidentify.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ class GUI_EXPORT QgsMapToolIdentify : public QgsMapTool
123123
\returns a list of IdentifyResult*/
124124
QList<QgsMapToolIdentify::IdentifyResult> identify( int x, int y, IdentifyMode mode, LayerType layerType = AllLayers );
125125

126+
//! Performs identification based on a geometry (in map coordinates)
127+
QList<QgsMapToolIdentify::IdentifyResult> identify( const QgsGeometry &geometry, IdentifyMode mode, LayerType layerType );
128+
//! Performs identification based on a geometry (in map coordinates)
129+
QList<QgsMapToolIdentify::IdentifyResult> identify( const QgsGeometry &geometry, IdentifyMode mode, const QList<QgsMapLayer *> &layerList, LayerType layerType );
130+
131+
126132
/**
127133
* return a pointer to the identify menu which will be used in layer selection mode
128134
* this menu can also be customized
@@ -161,6 +167,10 @@ class GUI_EXPORT QgsMapToolIdentify : public QgsMapTool
161167

162168
private:
163169

170+
bool identifyLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsMapLayer *layer, const QgsGeometry &geometry, const QgsRectangle &viewExtent, double mapUnitsPerPixel, QgsMapToolIdentify::LayerType layerType = AllLayers );
171+
bool identifyRasterLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsRasterLayer *layer, const QgsGeometry &geometry, const QgsRectangle &viewExtent, double mapUnitsPerPixel );
172+
bool identifyVectorLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorLayer *layer, const QgsGeometry &geometry );
173+
164174
/**
165175
* Desired units for distance display.
166176
* \since QGIS 2.14
@@ -200,8 +210,8 @@ class GUI_EXPORT QgsMapToolIdentify : public QgsMapTool
200210
QString formatXCoordinate( const QgsPointXY &canvasPoint ) const;
201211
QString formatYCoordinate( const QgsPointXY &canvasPoint ) const;
202212

203-
// Last point in canvas CRS
204-
QgsPointXY mLastPoint;
213+
// Last geometry (point or polygon) in map CRS
214+
QgsGeometry mLastGeometry;
205215

206216
double mLastMapUnitsPerPixel;
207217

‎src/ui/qgsidentifyresultsbase.ui

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,45 @@
339339
<string>Print Selected HTML Response</string>
340340
</property>
341341
</action>
342+
<action name="mActionSelectFeatures">
343+
<property name="icon">
344+
<iconset resource="../../images/images.qrc">
345+
<normaloff>:/images/themes/default/mActionIdentifyByRectangle.svg</normaloff>:/images/themes/default/mActionIdentifyByRectangle.svg</iconset>
346+
</property>
347+
<property name="text">
348+
<string>Identify Feature(s)</string>
349+
</property>
350+
<property name="toolTip">
351+
<string>Identify Features by area or single click</string>
352+
</property>
353+
</action>
354+
<action name="mActionSelectPolygon">
355+
<property name="icon">
356+
<iconset resource="../../images/images.qrc">
357+
<normaloff>:/images/themes/default/mActionIdentifyByPolygon.svg</normaloff>:/images/themes/default/mActionIdentifyByPolygon.svg</iconset>
358+
</property>
359+
<property name="text">
360+
<string>Identify Features by Polygon</string>
361+
</property>
362+
</action>
363+
<action name="mActionSelectFreehand">
364+
<property name="icon">
365+
<iconset resource="../../images/images.qrc">
366+
<normaloff>:/images/themes/default/mActionIdentifyByFreehand.svg</normaloff>:/images/themes/default/mActionIdentifyByFreehand.svg</iconset>
367+
</property>
368+
<property name="text">
369+
<string>Identify Features by Freehand</string>
370+
</property>
371+
</action>
372+
<action name="mActionSelectRadius">
373+
<property name="icon">
374+
<iconset resource="../../images/images.qrc">
375+
<normaloff>:/images/themes/default/mActionIdentifyByRadius.svg</normaloff>:/images/themes/default/mActionIdentifyByRadius.svg</iconset>
376+
</property>
377+
<property name="text">
378+
<string>Identify Features by Radius</string>
379+
</property>
380+
</action>
342381
</widget>
343382
<layoutdefault spacing="6" margin="11"/>
344383
<customwidgets>

0 commit comments

Comments
 (0)
Please sign in to comment.