Skip to content
Permalink
Browse files

Move 'coordinate capture' functionality to map canvas

Instead of requiring users to enable the core c++ plugin 'Coordinate Capture'
in order to copy coordinates for a map point, instead add this functionality
to a map canvas right click context menu.

Now, when compatible map tools are activated (pan, zoom, select by rect), right clicking
on the canvas shows a context menu with a "Copy Coordinate" submenu. The
submenu shows options for copying the coordinate in the map CRS, WGS84 or
a custom preset CRS.

Also adds API to allow 3rd party QgsMapTool subclasses to implement their own
context menus which include this Copy Coordinate action.
  • Loading branch information
nyalldawson committed May 26, 2020
1 parent c03ac22 commit ea326827ae5dea1cab86a315e8a4293ae168db36
@@ -20,6 +20,7 @@




class QgsMapCanvas : QGraphicsView
{
%Docstring
@@ -52,6 +52,7 @@ implemented as map tools.
Transient,
EditTool,
AllowZoomRect,
ShowContextMenu,
};
typedef QFlags<QgsMapTool::Flag> Flags;

@@ -191,6 +192,24 @@ Gets search radius in map units for given canvas. Used by identify, tip etc.
The values is calculated from searchRadiusMM().

.. versionadded:: 2.3
%End

virtual void populateContextMenu( QMenu *menu );
%Docstring
Allows the tool to populate and customize the given ``menu``,
prior to showing it in response to a right-mouse button click.

``menu`` will be initially populated with a set of default, generic actions.
Any new actions added to the menu should be correctly parented to ``menu``.

The default implementation does nothing.

.. note::

The context menu is only shown when the ShowContextMenu flag
is present in flags().

.. versionadded:: 3.14
%End

signals:
@@ -34,6 +34,7 @@ constructor


virtual Flags flags() const;

virtual void canvasPressEvent( QgsMapMouseEvent *e );

virtual void canvasMoveEvent( QgsMapMouseEvent *e );
@@ -28,6 +28,7 @@ constructor
~QgsMapToolZoom();

virtual Flags flags() const;

virtual void canvasMoveEvent( QgsMapMouseEvent *e );

virtual void canvasPressEvent( QgsMapMouseEvent *e );
@@ -122,6 +122,22 @@ void QgsMapToolSelect::deactivate()
QgsMapTool::deactivate();
}

QgsMapTool::Flags QgsMapToolSelect::flags() const
{
switch ( mSelectionHandler->selectionMode() )
{
case QgsMapToolSelectionHandler::SelectPolygon:
break;

case QgsMapToolSelectionHandler::SelectSimple:
case QgsMapToolSelectionHandler::SelectFreehand:
case QgsMapToolSelectionHandler::SelectRadius:
return QgsMapTool::flags() | QgsMapTool::ShowContextMenu;
}

return QgsMapTool::flags();
}

void QgsMapToolSelect::selectFeatures( Qt::KeyboardModifiers modifiers )
{
if ( mSelectionHandler->selectionMode() == QgsMapToolSelectionHandler::SelectSimple &&
@@ -50,6 +50,7 @@ class APP_EXPORT QgsMapToolSelect : public QgsMapTool
void keyPressEvent( QKeyEvent *e ) override;
void keyReleaseEvent( QKeyEvent *e ) override;
void deactivate() override;
Flags flags() const override;

signals:

@@ -37,6 +37,8 @@ email : sherman at mrcc.com
#include <QStringList>
#include <QWheelEvent>
#include <QWindow>
#include <QMenu>
#include <QClipboard>

#include "qgis.h"
#include "qgssettings.h"
@@ -80,6 +82,7 @@ email : sherman at mrcc.com
#include "qgsvectorlayertemporalproperties.h"
#include "qgstemporalcontroller.h"
#include "qgsruntimeprofiler.h"
#include "qgsprojectionselectiondialog.h"

/**
* \ingroup gui
@@ -114,6 +117,7 @@ class QgsMapCanvas::CanvasProperties
QgsMapCanvas::QgsMapCanvas( QWidget *parent )
: QGraphicsView( parent )
, mCanvasProperties( new CanvasProperties )
, mMenu( new QMenu( this ) )
, mExpressionContextScope( tr( "Map Canvas" ) )
{
mScene = new QGraphicsScene();
@@ -795,6 +799,85 @@ void QgsMapCanvas::clearTemporalCache()
}
}

void QgsMapCanvas::showContextMenu( QgsMapMouseEvent *event )
{
const QgsPointXY mapPoint = event->originalMapPoint();

mMenu->clear();

QMenu *copyCoordinateMenu = new QMenu( tr( "Copy Coordinate" ), mMenu );
copyCoordinateMenu->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditCopy.svg" ) ) );

auto addCoordinateFormat = [ = ]( const QString identifier, const QgsCoordinateReferenceSystem & crs )
{
QgsCoordinateTransform ct( mSettings.destinationCrs(), crs, mSettings.transformContext() );
try
{
const QgsPointXY transformedPoint = ct.transform( mapPoint );

const int displayPrecision = crs.mapUnits() == QgsUnitTypes::DistanceDegrees ? 5 : 3;
QAction *copyCoordinateAction = new QAction( QStringLiteral( "%1, %2 (%3)" ).arg(
QString::number( transformedPoint.x(), 'f', displayPrecision ),
QString::number( transformedPoint.y(), 'f', displayPrecision ),
identifier ), mMenu );

connect( copyCoordinateAction, &QAction::triggered, this, [displayPrecision, transformedPoint]
{
QClipboard *clipboard = QApplication::clipboard();

const QString coordinates = QString::number( transformedPoint.x(), 'f', displayPrecision ) + ',' + QString::number( transformedPoint.y(), 'f', displayPrecision );

//if we are on x11 system put text into selection ready for middle button pasting
if ( clipboard->supportsSelection() )
{
clipboard->setText( coordinates, QClipboard::Selection );
}
clipboard->setText( coordinates, QClipboard::Clipboard );

} );
copyCoordinateMenu->addAction( copyCoordinateAction );
}
catch ( QgsCsException & )
{

}
};

addCoordinateFormat( tr( "%1 - Map CRS" ).arg( mSettings.destinationCrs().userFriendlyIdentifier( QgsCoordinateReferenceSystem::ShortString ) ), mSettings.destinationCrs() );
if ( mSettings.destinationCrs() != QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) )
addCoordinateFormat( tr( "WGS84" ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );

QgsSettings settings;
const QString customCrsString = settings.value( QStringLiteral( "qgis/custom_coordinate_crs" ) ).toString();
if ( !customCrsString.isEmpty() )
{
QgsCoordinateReferenceSystem customCrs( customCrsString );
if ( customCrs != mSettings.destinationCrs() && customCrs != QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) )
{
addCoordinateFormat( tr( "%1 - Custom CRS" ).arg( customCrs.userFriendlyIdentifier( QgsCoordinateReferenceSystem::ShortString ) ), customCrs );
}
}
copyCoordinateMenu->addSeparator();
QAction *setCustomCrsAction = new QAction( QStringLiteral( "Set Custom CRS…" ), mMenu );
connect( setCustomCrsAction, &QAction::triggered, this, [ = ]
{
QgsProjectionSelectionDialog selector( this );
selector.setCrs( QgsCoordinateReferenceSystem( customCrsString ) );
if ( selector.exec() )
{
QgsSettings().setValue( QStringLiteral( "qgis/custom_coordinate_crs" ), selector.crs().authid().isEmpty() ? selector.crs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) : selector.crs().authid() );
}
} );
copyCoordinateMenu->addAction( setCustomCrsAction );

mMenu->addMenu( copyCoordinateMenu );

if ( mMapTool )
mMapTool->populateContextMenu( mMenu );

mMenu->exec( event->globalPos() );
}

void QgsMapCanvas::setTemporalRange( const QgsDateTimeRange &dateTimeRange )
{
if ( temporalRange() == dateTimeRange )
@@ -1601,6 +1684,12 @@ void QgsMapCanvas::mousePressEvent( QMouseEvent *e )
beginZoomRect( e->pos() );
return;
}
else if ( mMapTool->flags() & QgsMapTool::ShowContextMenu && e->button() == Qt::RightButton )
{
std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
showContextMenu( me.get() );
return;
}
else
{
std::unique_ptr<QgsMapMouseEvent> me( new QgsMapMouseEvent( this, e ) );
@@ -1616,9 +1705,7 @@ void QgsMapCanvas::mousePressEvent( QMouseEvent *e )

mCanvasProperties->mouseButtonDown = true;
mCanvasProperties->rubberStartPoint = e->pos();

} // mousePressEvent

}

void QgsMapCanvas::mouseReleaseEvent( QMouseEvent *e )
{
@@ -1679,7 +1766,7 @@ void QgsMapCanvas::mouseReleaseEvent( QMouseEvent *e )
if ( mCanvasProperties->panSelectorDown )
return;

} // mouseReleaseEvent
}

void QgsMapCanvas::resizeEvent( QResizeEvent *e )
{
@@ -71,6 +71,9 @@ class QgsReferencedRectangle;

class QgsTemporalController;

class QMenu;
class QgsMapMouseEvent;


/**
* \ingroup gui
@@ -1106,6 +1109,9 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
//! previous tool if current is for zooming/panning
QgsMapTool *mLastNonZoomMapTool = nullptr;

//! Context menu
QMenu *mMenu = nullptr;

//! recently used extent
QList <QgsRectangle> mLastExtent;
int mLastExtentIndex = -1;
@@ -1239,6 +1245,8 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
*/
void clearTemporalCache();

void showContextMenu( QgsMapMouseEvent *event );

friend class TestQgsMapCanvas;

}; // class QgsMapCanvas
@@ -227,3 +227,8 @@ double QgsMapTool::searchRadiusMU( QgsMapCanvas *canvas )
QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
return searchRadiusMU( context );
}

void QgsMapTool::populateContextMenu( QMenu * )
{

}
@@ -39,6 +39,7 @@ class QPoint;
class QAction;
class QAbstractButton;
class QgsMapMouseEvent;
class QMenu;

#ifdef SIP_RUN
% ModuleHeaderCode
@@ -92,6 +93,7 @@ class GUI_EXPORT QgsMapTool : public QObject
tool automatically restored. */
EditTool = 1 << 2, //!< Map tool is an edit tool, which can only be used when layer is editable
AllowZoomRect = 1 << 3, //!< Allow zooming by rectangle (by holding shift and dragging) while the tool is active
ShowContextMenu = 1 << 4, //!< Show a context menu when right-clicking with the tool (since QGIS 3.14). See populateContextMenu().
};
Q_DECLARE_FLAGS( Flags, Flag )

@@ -191,6 +193,22 @@ class GUI_EXPORT QgsMapTool : public QObject
* \since QGIS 2.3 */
static double searchRadiusMU( QgsMapCanvas *canvas );

/**
* Allows the tool to populate and customize the given \a menu,
* prior to showing it in response to a right-mouse button click.
*
* \a menu will be initially populated with a set of default, generic actions.
* Any new actions added to the menu should be correctly parented to \a menu.
*
* The default implementation does nothing.
*
* \note The context menu is only shown when the ShowContextMenu flag
* is present in flags().
*
* \since QGIS 3.14
*/
virtual void populateContextMenu( QMenu *menu );

signals:
//! emit a message
void messageEmitted( const QString &message, Qgis::MessageLevel = Qgis::Info );
@@ -15,7 +15,6 @@

#include <QBitmap>
#include <QCursor>

#include "qgsmaptoolpan.h"
#include "qgsmapcanvas.h"
#include "qgsmaptopixel.h"
@@ -50,6 +49,11 @@ void QgsMapToolPan::deactivate()
QgsMapTool::deactivate();
}

QgsMapTool::Flags QgsMapToolPan::flags() const
{
return QgsMapTool::Transient | QgsMapTool::AllowZoomRect | QgsMapTool::ShowContextMenu;
}

void QgsMapToolPan::canvasPressEvent( QgsMapMouseEvent *e )
{
if ( e->button() == Qt::LeftButton )
@@ -41,7 +41,7 @@ class GUI_EXPORT QgsMapToolPan : public QgsMapTool
void activate() override;
void deactivate() override;

Flags flags() const override { return QgsMapTool::Transient | QgsMapTool::AllowZoomRect; }
Flags flags() const override;
void canvasPressEvent( QgsMapMouseEvent *e ) override;
void canvasMoveEvent( QgsMapMouseEvent *e ) override;
void canvasReleaseEvent( QgsMapMouseEvent *e ) override;
@@ -47,6 +47,10 @@ QgsMapToolZoom::~QgsMapToolZoom()
delete mRubberBand;
}

QgsMapTool::Flags QgsMapToolZoom::flags() const
{
return QgsMapTool::Transient | QgsMapTool::ShowContextMenu;
}

void QgsMapToolZoom::canvasMoveEvent( QgsMapMouseEvent *e )
{
@@ -36,7 +36,7 @@ class GUI_EXPORT QgsMapToolZoom : public QgsMapTool
QgsMapToolZoom( QgsMapCanvas *canvas, bool zoomOut );
~QgsMapToolZoom() override;

Flags flags() const override { return QgsMapTool::Transient; }
Flags flags() const override;
void canvasMoveEvent( QgsMapMouseEvent *e ) override;
void canvasPressEvent( QgsMapMouseEvent *e ) override;
void canvasReleaseEvent( QgsMapMouseEvent *e ) override;

0 comments on commit ea32682

Please sign in to comment.
You can’t perform that action at this time.