Skip to content

Commit 32ee716

Browse files
authored
Merge pull request #7711 from m-kuhn/mapToolSnapToGrid
Snap to grid for maptools [FEATURE]
2 parents 0cc9501 + d57c184 commit 32ee716

14 files changed

+537
-11
lines changed

python/gui/auto_generated/qgsmapmouseevent.sip.in

+9
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,15 @@ The unsnapped, real mouse cursor position in pixel coordinates.
117117
Alias to pos()
118118

119119
:return: Mouse position in pixel coordinates
120+
%End
121+
122+
void snapToGrid( double precision, const QgsCoordinateReferenceSystem &crs );
123+
%Docstring
124+
Snaps the mapPoint to a grid with the given ``precision``.
125+
The snapping will be done in the specified ``crs``. If this crs is
126+
different from the mapCanvas crs, it will be reprojected on the fly.
127+
128+
.. versionadded:: 3.4
120129
%End
121130

122131
};

python/gui/auto_generated/qgsmaptooladvanceddigitizing.sip.in

+16
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,22 @@ canvasMoveEvent is triggered and it's not hidden by the cad's
149149
construction mode.
150150

151151
:param e: Mouse events prepared by the cad system
152+
%End
153+
154+
bool snapToLayerGridEnabled() const;
155+
%Docstring
156+
Enables or disables snap to grid of mouse events.
157+
The snapping will occur in the layer's CRS.
158+
159+
.. versionadded:: 3.4
160+
%End
161+
162+
void setSnapToLayerGridEnabled( bool snapToLayerGridEnabled );
163+
%Docstring
164+
Enables or disables snap to grid of mouse events.
165+
The snapping will occur in the layer's CRS.
166+
167+
.. versionadded:: 3.4
152168
%End
153169

154170
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/gui/qgssnaptogridcanvasitem.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
class QgsSnapToGridCanvasItem : QObject, QgsMapCanvasItem
13+
{
14+
%Docstring
15+
16+
Shows a grid on the map canvas given a spatial resolution.
17+
18+
.. versionadded:: 3.4
19+
%End
20+
21+
%TypeHeaderCode
22+
#include "qgssnaptogridcanvasitem.h"
23+
%End
24+
public:
25+
26+
QgsSnapToGridCanvasItem( QgsMapCanvas *mapCanvas /TransferThis/ );
27+
%Docstring
28+
Will automatically be added to the ``mapCanvas``.
29+
%End
30+
31+
virtual void paint( QPainter *painter );
32+
33+
34+
QgsPointXY point() const;
35+
%Docstring
36+
A point that will be highlighted on the map canvas.
37+
The point needs to be in map coordinates. The closest point on the
38+
grid will be highlighted.
39+
%End
40+
41+
void setPoint( const QgsPointXY &point );
42+
%Docstring
43+
A point that will be highlighted on the map canvas.
44+
The point needs to be in map coordinates. The closest point on the
45+
grid will be highlighted.
46+
%End
47+
48+
double precision() const;
49+
%Docstring
50+
The resolution of the grid in map units.
51+
If a crs has been specified it will be in CRS units.
52+
%End
53+
54+
void setPrecision( double precision );
55+
%Docstring
56+
The resolution of the grid in map units.
57+
If a crs has been specified it will be in CRS units.
58+
%End
59+
60+
QgsCoordinateReferenceSystem crs() const;
61+
%Docstring
62+
The CRS in which the grid should be calculated.
63+
By default will be an invalid QgsCoordinateReferenceSystem and
64+
as such equal to the CRS of the map canvas.
65+
%End
66+
67+
void setCrs( const QgsCoordinateReferenceSystem &crs );
68+
%Docstring
69+
The CRS in which the grid should be calculated.
70+
By default will be an invalid QgsCoordinateReferenceSystem and
71+
as such equal to the CRS of the map canvas.
72+
%End
73+
74+
bool enabled() const;
75+
%Docstring
76+
Enable this item. It will be hidden if disabled.
77+
%End
78+
79+
void setEnabled( bool enabled );
80+
%Docstring
81+
Enable this item. It will be hidden if disabled.
82+
%End
83+
84+
};
85+
86+
/************************************************************************
87+
* This file has been generated automatically from *
88+
* *
89+
* src/gui/qgssnaptogridcanvasitem.h *
90+
* *
91+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
92+
************************************************************************/

python/gui/gui_auto.sip

+1
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@
193193
%Include auto_generated/qgssearchquerybuilder.sip
194194
%Include auto_generated/qgsshortcutsmanager.sip
195195
%Include auto_generated/qgsslider.sip
196+
%Include auto_generated/qgssnaptogridcanvasitem.sip
196197
%Include auto_generated/qgsstatusbar.sip
197198
%Include auto_generated/qgssublayersdialog.sip
198199
%Include auto_generated/qgssubstitutionlistwidget.sip

src/app/qgsmaptoolsplitfeatures.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ QgsMapToolSplitFeatures::QgsMapToolSplitFeatures( QgsMapCanvas *canvas )
2727
: QgsMapToolCapture( canvas, QgisApp::instance()->cadDockWidget(), QgsMapToolCapture::CaptureLine )
2828
{
2929
mToolName = tr( "Split features" );
30+
setSnapToLayerGridEnabled( false );
3031
}
3132

3233
void QgsMapToolSplitFeatures::cadCanvasReleaseEvent( QgsMapMouseEvent *e )

src/app/qgsmaptoolsplitparts.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ QgsMapToolSplitParts::QgsMapToolSplitParts( QgsMapCanvas *canvas )
2727
: QgsMapToolCapture( canvas, QgisApp::instance()->cadDockWidget(), QgsMapToolCapture::CaptureLine )
2828
{
2929
mToolName = tr( "Split parts" );
30+
setSnapToLayerGridEnabled( false );
3031
}
3132

3233
void QgsMapToolSplitParts::cadCanvasReleaseEvent( QgsMapMouseEvent *e )

src/core/qgsvectorlayereditutils.cpp

+11-11
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ bool QgsVectorLayerEditUtils::insertVertex( double x, double y, QgsFeatureId atF
4747

4848
geometry.insertVertex( x, y, beforeVertex );
4949

50-
mLayer->editBuffer()->changeGeometry( atFeatureId, geometry );
50+
mLayer->changeGeometry( atFeatureId, geometry );
5151
return true;
5252
}
5353

@@ -64,7 +64,7 @@ bool QgsVectorLayerEditUtils::insertVertex( const QgsPoint &point, QgsFeatureId
6464

6565
geometry.insertVertex( point, beforeVertex );
6666

67-
mLayer->editBuffer()->changeGeometry( atFeatureId, geometry );
67+
mLayer->changeGeometry( atFeatureId, geometry );
6868
return true;
6969
}
7070

@@ -87,7 +87,7 @@ bool QgsVectorLayerEditUtils::moveVertex( const QgsPoint &p, QgsFeatureId atFeat
8787

8888
geometry.moveVertex( p, atVertex );
8989

90-
mLayer->editBuffer()->changeGeometry( atFeatureId, geometry );
90+
mLayer->changeGeometry( atFeatureId, geometry );
9191
return true;
9292
}
9393

@@ -112,7 +112,7 @@ QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId f
112112
geometry.set( nullptr );
113113
}
114114

115-
mLayer->editBuffer()->changeGeometry( featureId, geometry );
115+
mLayer->changeGeometry( featureId, geometry );
116116
return !geometry.isNull() ? QgsVectorLayer::Success : QgsVectorLayer::EmptyGeometry;
117117
}
118118

@@ -159,7 +159,7 @@ QgsGeometry::OperationResult QgsVectorLayerEditUtils::addRing( QgsCurve *ring, c
159159
if ( addRingReturnCode == 0 )
160160
if ( addRingReturnCode == QgsGeometry::Success )
161161
{
162-
mLayer->editBuffer()->changeGeometry( f.id(), g );
162+
mLayer->changeGeometry( f.id(), g );
163163
if ( modifiedFeatureId )
164164
*modifiedFeatureId = f.id();
165165

@@ -212,7 +212,7 @@ QgsGeometry::OperationResult QgsVectorLayerEditUtils::addPart( const QgsPointSeq
212212
//convert back to single part if required by layer
213213
geometry.convertToSingleType();
214214
}
215-
mLayer->editBuffer()->changeGeometry( featureId, geometry );
215+
mLayer->changeGeometry( featureId, geometry );
216216
}
217217
return errorCode;
218218
}
@@ -247,7 +247,7 @@ QgsGeometry::OperationResult QgsVectorLayerEditUtils::addPart( QgsCurve *ring, Q
247247
//convert back to single part if required by layer
248248
geometry.convertToSingleType();
249249
}
250-
mLayer->editBuffer()->changeGeometry( featureId, geometry );
250+
mLayer->changeGeometry( featureId, geometry );
251251
}
252252
return errorCode;
253253
}
@@ -267,7 +267,7 @@ int QgsVectorLayerEditUtils::translateFeature( QgsFeatureId featureId, double dx
267267
int errorCode = geometry.translate( dx, dy );
268268
if ( errorCode == 0 )
269269
{
270-
mLayer->editBuffer()->changeGeometry( featureId, geometry );
270+
mLayer->changeGeometry( featureId, geometry );
271271
}
272272
return errorCode;
273273
}
@@ -348,13 +348,13 @@ QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitFeatures( const QVect
348348
if ( splitFunctionReturn == QgsGeometry::OperationResult::Success )
349349
{
350350
//change this geometry
351-
mLayer->editBuffer()->changeGeometry( feat.id(), featureGeom );
351+
mLayer->changeGeometry( feat.id(), featureGeom );
352352

353353
//insert new features
354354
for ( int i = 0; i < newGeometries.size(); ++i )
355355
{
356356
QgsFeature f = QgsVectorLayerUtils::createFeature( mLayer, newGeometries.at( i ), feat.attributes().toMap() );
357-
mLayer->editBuffer()->addFeature( f );
357+
mLayer->addFeature( f );
358358
}
359359

360360
if ( topologicalEditing )
@@ -470,7 +470,7 @@ QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitParts( const QVector<
470470

471471
if ( !addPartRet )
472472
{
473-
mLayer->editBuffer()->changeGeometry( feat.id(), featureGeom );
473+
mLayer->changeGeometry( feat.id(), featureGeom );
474474
}
475475

476476
if ( topologicalEditing )

src/gui/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ SET(QGIS_GUI_SRCS
358358
qgsshortcutsmanager.cpp
359359
qgsslider.cpp
360360
qgssnapindicator.cpp
361+
qgssnaptogridcanvasitem.cpp
361362
qgssublayersdialog.cpp
362363
qgssubstitutionlistwidget.cpp
363364
qgssqlcomposerdialog.cpp
@@ -527,6 +528,7 @@ SET(QGIS_GUI_MOC_HDRS
527528
qgssearchquerybuilder.h
528529
qgsshortcutsmanager.h
529530
qgsslider.h
531+
qgssnaptogridcanvasitem.h
530532
qgssqlcomposerdialog.h
531533
qgsstatusbar.h
532534
qgssublayersdialog.h

src/gui/qgsmapmouseevent.cpp

+24
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,30 @@ void QgsMapMouseEvent::setMapPoint( const QgsPointXY &point )
7171
mPixelPoint = mapToPixelCoordinates( point );
7272
}
7373

74+
void QgsMapMouseEvent::snapToGrid( double precision, const QgsCoordinateReferenceSystem &crs )
75+
{
76+
if ( precision <= 0 )
77+
return;
78+
79+
try
80+
{
81+
QgsCoordinateTransform ct( mMapCanvas->mapSettings().destinationCrs(), crs, mMapCanvas->mapSettings().transformContext() );
82+
83+
QgsPointXY pt = ct.transform( mMapPoint );
84+
85+
pt.setX( std::round( pt.x() / precision ) * precision );
86+
pt.setY( std::round( pt.y() / precision ) * precision );
87+
88+
pt = ct.transform( pt, QgsCoordinateTransform::ReverseTransform );
89+
90+
setMapPoint( pt );
91+
}
92+
catch ( QgsCsException &e )
93+
{
94+
Q_UNUSED( e )
95+
}
96+
}
97+
7498
QPoint QgsMapMouseEvent::mapToPixelCoordinates( const QgsPointXY &point )
7599
{
76100
double x = point.x(), y = point.y();

src/gui/qgsmapmouseevent.h

+9
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,15 @@ class GUI_EXPORT QgsMapMouseEvent : public QMouseEvent
126126
*/
127127
QPoint originalPixelPoint() const { return pos(); }
128128

129+
/**
130+
* Snaps the mapPoint to a grid with the given \a precision.
131+
* The snapping will be done in the specified \a crs. If this crs is
132+
* different from the mapCanvas crs, it will be reprojected on the fly.
133+
*
134+
* \since QGIS 3.4
135+
*/
136+
void snapToGrid( double precision, const QgsCoordinateReferenceSystem &crs );
137+
129138
private:
130139

131140
QPoint mapToPixelCoordinates( const QgsPointXY &point );

0 commit comments

Comments
 (0)