diff --git a/python/gui/auto_generated/qgssnaptogridcanvasitem.sip.in b/python/gui/auto_generated/qgssnaptogridcanvasitem.sip.in new file mode 100644 index 00000000000..98f4390c765 --- /dev/null +++ b/python/gui/auto_generated/qgssnaptogridcanvasitem.sip.in @@ -0,0 +1,91 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgssnaptogridcanvasitem.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + +class QgsSnapToGridCanvasItem : QObject, QgsMapCanvasItem +{ +%Docstring + +Shows a grid on the map canvas given a spatial resolution. + +.. versionadded:: 3.4 +%End + +%TypeHeaderCode +#include "qgssnaptogridcanvasitem.h" +%End + public: + + QgsSnapToGridCanvasItem( QgsMapCanvas *mapCanvas ); +%Docstring +Will automatically be added to the ``mapCanvas``. +%End + + virtual void paint( QPainter *painter ); + + + QgsPointXY point() const; +%Docstring +A point that will be highlighted on the map canvas. +The point needs to be in map coordinates. The closest point on the +grid will be highlighted. +%End + + void setPoint( const QgsPointXY &point ); +%Docstring +A point that will be highlighted on the map canvas. +The point needs to be in map coordinates. The closest point on the +grid will be highlighted. +%End + + double precision() const; +%Docstring +The resolution of the grid in map units. +If a crs has been specified it will be in CRS units. +%End + + void setPrecision( double precision ); +%Docstring +The resolution of the grid in map units. +If a crs has been specified it will be in CRS units. +%End + + QgsCoordinateReferenceSystem crs() const; +%Docstring +The CRS in which the grid should be calculated. +By default will be an invalid QgsCoordinateReferenceSystem and +as such equal to the CRS of the map canvas. +%End + + void setCrs( const QgsCoordinateReferenceSystem &crs ); +%Docstring +The CRS in which the grid should be calculated. +By default will be an invalid QgsCoordinateReferenceSystem and +as such equal to the CRS of the map canvas. +%End + + bool enabled() const; +%Docstring +Enable this item. It will be hidden if disabled. +%End + + void setEnabled( bool enabled ); +%Docstring +Enable this item. It will be hidden if disabled. +%End + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgssnaptogridcanvasitem.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/gui/gui_auto.sip b/python/gui/gui_auto.sip index 321bd6351eb..006f54edb43 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -192,6 +192,7 @@ %Include auto_generated/qgssearchquerybuilder.sip %Include auto_generated/qgsshortcutsmanager.sip %Include auto_generated/qgsslider.sip +%Include auto_generated/qgssnaptogridcanvasitem.sip %Include auto_generated/qgsstatusbar.sip %Include auto_generated/qgssublayersdialog.sip %Include auto_generated/qgssubstitutionlistwidget.sip diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 22b5920db3e..97ce4ac2475 100755 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -355,6 +355,7 @@ SET(QGIS_GUI_SRCS qgsshortcutsmanager.cpp qgsslider.cpp qgssnapindicator.cpp + qgssnaptogridcanvasitem.cpp qgssublayersdialog.cpp qgssubstitutionlistwidget.cpp qgssqlcomposerdialog.cpp @@ -524,6 +525,7 @@ SET(QGIS_GUI_MOC_HDRS qgssearchquerybuilder.h qgsshortcutsmanager.h qgsslider.h + qgssnaptogridcanvasitem.h qgssqlcomposerdialog.h qgsstatusbar.h qgssublayersdialog.h diff --git a/src/gui/qgssnaptogridcanvasitem.cpp b/src/gui/qgssnaptogridcanvasitem.cpp new file mode 100644 index 00000000000..51961211aa1 --- /dev/null +++ b/src/gui/qgssnaptogridcanvasitem.cpp @@ -0,0 +1,142 @@ +#include "qgssnaptogridcanvasitem.h" +#include "qgsmapcanvas.h" + +QgsSnapToGridCanvasItem::QgsSnapToGridCanvasItem( QgsMapCanvas *mapCanvas ) + : QgsMapCanvasItem( mapCanvas ) + , mGridPen( QPen( QColor( 127, 127, 127, 150 ), 1 ) ) + , mCurrentPointPen( QPen( QColor( 200, 200, 200, 150 ), 3 ) ) +{ + updateMapCanvasCrs(); + connect( mMapCanvas, &QgsMapCanvas::extentsChanged, this, &QgsSnapToGridCanvasItem::updateZoomFactor ); + connect( mMapCanvas, &QgsMapCanvas::destinationCrsChanged, this, &QgsSnapToGridCanvasItem::updateMapCanvasCrs ); +} + +void QgsSnapToGridCanvasItem::paint( QPainter *painter ) +{ + painter->save(); + QgsRectangle mapRect = mMapCanvas->extent(); + if ( rect() != mapRect ) + setRect( mapRect ); + + painter->setRenderHints( QPainter::Antialiasing ); + painter->setCompositionMode( QPainter::CompositionMode_Difference ); + + if ( mEnabled && mAvailableByZoomFactor ) + { + const QgsRectangle layerExtent = mTransform.transformBoundingBox( mapRect, QgsCoordinateTransform::ReverseTransform ); + const QgsPointXY layerPt = mTransform.transform( mPoint, QgsCoordinateTransform::ReverseTransform ); + + const double gridXMin = std::ceil( layerExtent.xMinimum() / mPrecision ) * mPrecision; + const double gridXMax = std::ceil( layerExtent.xMaximum() / mPrecision ) * mPrecision; + const double gridYMin = std::ceil( layerExtent.yMinimum() / mPrecision ) * mPrecision; + const double gridYMax = std::ceil( layerExtent.yMaximum() / mPrecision ) * mPrecision; + + for ( int x = gridXMin ; x < gridXMax; x += mPrecision ) + { + for ( int y = gridYMin ; y < gridYMax; y += mPrecision ) + { + const QgsPointXY pt = mTransform.transform( x, y ); + const QPointF canvasPt = toCanvasCoordinates( pt ); + + if ( qgsDoubleNear( layerPt.x(), x, mPrecision / 3 ) && qgsDoubleNear( layerPt.y(), y, mPrecision / 3 ) ) + { + painter->setPen( mCurrentPointPen ); + } + else + { + painter->setPen( mGridPen ); + } + painter->drawLine( canvasPt.x() - 3, canvasPt.y(), canvasPt.x() + 3, canvasPt.y() ); + painter->drawLine( canvasPt.x(), canvasPt.y() - 3, canvasPt.x(), canvasPt.y() + 3 ); + } + } + } + + painter->restore(); +} + +QgsPointXY QgsSnapToGridCanvasItem::point() const +{ + return mPoint; +} + +void QgsSnapToGridCanvasItem::setPoint( const QgsPointXY &point ) +{ + mPoint = point; + update(); +} + +double QgsSnapToGridCanvasItem::precision() const +{ + return mPrecision; +} + +void QgsSnapToGridCanvasItem::setPrecision( double precision ) +{ + mPrecision = precision; + updateZoomFactor(); +} + +QgsCoordinateReferenceSystem QgsSnapToGridCanvasItem::crs() const +{ + return mTransform.sourceCrs(); +} + +void QgsSnapToGridCanvasItem::setCrs( const QgsCoordinateReferenceSystem &crs ) +{ + mTransform.setSourceCrs( crs ); + updateZoomFactor(); +} + +bool QgsSnapToGridCanvasItem::enabled() const +{ + return mEnabled; +} + +void QgsSnapToGridCanvasItem::setEnabled( bool enabled ) +{ + mEnabled = enabled; + update(); +} + +void QgsSnapToGridCanvasItem::updateMapCanvasCrs() +{ + mTransform.setContext( mMapCanvas->mapSettings().transformContext() ); + mTransform.setDestinationCrs( mMapCanvas->mapSettings().destinationCrs() ); + update(); +} + + + +void QgsSnapToGridCanvasItem::updateZoomFactor() +{ + if ( !isVisible() ) + return; + + try + { + const int threshold = 5; + + const QgsPointXY centerPoint = mMapCanvas->extent().center(); + const QPointF canvasCenter = toCanvasCoordinates( centerPoint ); + + const QgsPointXY pt1 = mMapCanvas->mapSettings().mapToPixel().toMapCoordinates( canvasCenter.x() - threshold, canvasCenter.y() - threshold ); + const QgsPointXY pt2 = mMapCanvas->mapSettings().mapToPixel().toMapCoordinates( canvasCenter.x() + threshold, canvasCenter.y() + threshold ); + + const QgsPointXY layerPt1 = mTransform.transform( pt1, QgsCoordinateTransform::ReverseTransform ); + const QgsPointXY layerPt2 = mTransform.transform( pt2, QgsCoordinateTransform::ReverseTransform ); + + const double dist = layerPt1.distance( layerPt2 ); + + if ( dist < mPrecision ) + mAvailableByZoomFactor = true; + else + mAvailableByZoomFactor = false; + } + catch ( QgsCsException &e ) + { + // transform errors? + // you've probably got worse problems than the grid with your digitizing operations in the current projection. + mAvailableByZoomFactor = false; + } +} diff --git a/src/gui/qgssnaptogridcanvasitem.h b/src/gui/qgssnaptogridcanvasitem.h new file mode 100644 index 00000000000..2eef0c255d4 --- /dev/null +++ b/src/gui/qgssnaptogridcanvasitem.h @@ -0,0 +1,98 @@ +#ifndef QGSSNAPTOGRIDCANVASITEM_H +#define QGSSNAPTOGRIDCANVASITEM_H + +#include +#include + +#include "qgscoordinatereferencesystem.h" +#include "qgsmapcanvasitem.h" +#include "qgscoordinatetransform.h" + +/** + * \ingroup gui + * + * Shows a grid on the map canvas given a spatial resolution. + * + * \since QGIS 3.4 + */ +class GUI_EXPORT QgsSnapToGridCanvasItem : public QObject, public QgsMapCanvasItem +{ + Q_OBJECT + + public: + + /** + * Will automatically be added to the \a mapCanvas. + */ + QgsSnapToGridCanvasItem( QgsMapCanvas *mapCanvas ); + + void paint( QPainter *painter ) override; + + /** + * A point that will be highlighted on the map canvas. + * The point needs to be in map coordinates. The closest point on the + * grid will be highlighted. + */ + QgsPointXY point() const; + + /** + * A point that will be highlighted on the map canvas. + * The point needs to be in map coordinates. The closest point on the + * grid will be highlighted. + */ + void setPoint( const QgsPointXY &point ); + + /** + * The resolution of the grid in map units. + * If a crs has been specified it will be in CRS units. + */ + double precision() const; + + /** + * The resolution of the grid in map units. + * If a crs has been specified it will be in CRS units. + */ + void setPrecision( double precision ); + + /** + * The CRS in which the grid should be calculated. + * By default will be an invalid QgsCoordinateReferenceSystem and + * as such equal to the CRS of the map canvas. + */ + QgsCoordinateReferenceSystem crs() const; + + /** + * The CRS in which the grid should be calculated. + * By default will be an invalid QgsCoordinateReferenceSystem and + * as such equal to the CRS of the map canvas. + */ + void setCrs( const QgsCoordinateReferenceSystem &crs ); + + /** + * Enable this item. It will be hidden if disabled. + */ + bool enabled() const; + + /** + * Enable this item. It will be hidden if disabled. + */ + void setEnabled( bool enabled ); + + private slots: + void updateMapCanvasCrs(); + + void updateZoomFactor(); + + private: + QPen mGridPen; + QPen mCurrentPointPen; + + bool mEnabled = true; + bool mAvailableByZoomFactor = false; + + double mPrecision = 0.0; + QgsCoordinateTransform mTransform; + QgsPointXY mPoint; +}; + +#endif // QGSSNAPTOGRIDCANVASITEM_H