diff --git a/python/core/geometry/qgsabstractgeometry.sip b/python/core/geometry/qgsabstractgeometry.sip
index e95056e365f..6c39275ce2b 100644
--- a/python/core/geometry/qgsabstractgeometry.sip
+++ b/python/core/geometry/qgsabstractgeometry.sip
@@ -514,6 +514,17 @@ Returns the centroid of the geometry
protected:
+ virtual QgsAbstractGeometry *createEmptyWithSameType() const = 0 /Factory/;
+%Docstring
+ Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
+ To create it, the geometry is default constructed and then the WKB is changed.
+.. seealso:: clone()
+.. versionadded:: 3.0
+.. note::
+
+ Not available in Python bindings
+ :rtype: QgsAbstractGeometry
+%End
virtual bool hasChildGeometries() const;
%Docstring
diff --git a/python/core/geometry/qgsgeometry.sip b/python/core/geometry/qgsgeometry.sip
index 7fada2032ed..3fdb1b27e0e 100644
--- a/python/core/geometry/qgsgeometry.sip
+++ b/python/core/geometry/qgsgeometry.sip
@@ -665,6 +665,21 @@ Returns true if WKB of the geometry is of WKBMulti* type
:rtype: QgsGeometry
%End
+ QgsGeometry snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const;
+%Docstring
+ Returns a new geometry with all points or vertices snapped to the closest point of the grid.
+
+ If the gridified geometry could not be calculated (or was totally collapsed) an empty geometry will be returned.
+ Note that snapping to grid may generate an invalid geometry in some corner cases.
+ It can also be thought as rounding the edges and it may be useful for removing errors.
+ \param hSpacing Horizontal spacing of the grid (x axis). 0 to disable.
+ \param vSpacing Vertical spacing of the grid (y axis). 0 to disable.
+ \param dSpacing Depth spacing of the grid (z axis). 0 (default) to disable.
+ \param mSpacing Custom dimension spacing of the grid (m axis). 0 (default) to disable.
+.. versionadded:: 3.0
+ :rtype: QgsGeometry
+%End
+
bool intersects( const QgsRectangle &r ) const;
%Docstring
Tests for intersection with a rectangle (uses GEOS)
diff --git a/python/plugins/processing/algs/help/qgis.yaml b/python/plugins/processing/algs/help/qgis.yaml
index 3e002333212..90eb4e7d0af 100644
--- a/python/plugins/processing/algs/help/qgis.yaml
+++ b/python/plugins/processing/algs/help/qgis.yaml
@@ -510,9 +510,6 @@ qgis:snapgeometries: >
Vertices will be inserted or removed as required to make the geometries match the reference geometries.
-qgis:snappointstogrid: >
- This algorithm modifies the position of points in a vector layer, so they fall in the coordinates of a grid.
-
qgis:splitwithlines: >
This algorithm splits the lines or polygons in one layer using the lines in another layer to define the breaking points. Intersection between geometries in both layers are considered as split points.
diff --git a/python/plugins/processing/algs/qgis/Gridify.py b/python/plugins/processing/algs/qgis/Gridify.py
deleted file mode 100644
index 3fcb351e943..00000000000
--- a/python/plugins/processing/algs/qgis/Gridify.py
+++ /dev/null
@@ -1,174 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
-***************************************************************************
- Gridify.py
- ---------------------
- Date : May 2010
- Copyright : (C) 2010 by Michael Minn
- Email : pyqgis at michaelminn 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. *
-* *
-***************************************************************************
-"""
-
-__author__ = 'Michael Minn'
-__date__ = 'May 2010'
-__copyright__ = '(C) 2010, Michael Minn'
-
-# This will get replaced with a git SHA1 when you do a git archive
-
-__revision__ = '$Format:%H$'
-
-from qgis.core import (QgsGeometry,
- QgsFeature,
- QgsFeatureSink,
- QgsPointXY,
- QgsWkbTypes,
- QgsApplication,
- QgsProcessingException,
- QgsProcessingParameterNumber)
-from processing.algs.qgis.QgisAlgorithm import QgisFeatureBasedAlgorithm
-
-
-class Gridify(QgisFeatureBasedAlgorithm):
-
- HSPACING = 'HSPACING'
- VSPACING = 'VSPACING'
-
- def group(self):
- return self.tr('Vector geometry')
-
- def __init__(self):
- super().__init__()
- self.h_spacing = None
- self.v_spacing = None
-
- def initParameters(self, config=None):
- self.addParameter(QgsProcessingParameterNumber(self.HSPACING,
- self.tr('Horizontal spacing'), type=QgsProcessingParameterNumber.Double, minValue=0.0, defaultValue=0.1))
- self.addParameter(QgsProcessingParameterNumber(self.VSPACING,
- self.tr('Vertical spacing'), type=QgsProcessingParameterNumber.Double, minValue=0.0, defaultValue=0.1))
-
- def name(self):
- return 'snappointstogrid'
-
- def displayName(self):
- return self.tr('Snap points to grid')
-
- def outputName(self):
- return self.tr('Snapped')
-
- def prepareAlgorithm(self, parameters, context, feedback):
- self.h_spacing = self.parameterAsDouble(parameters, self.HSPACING, context)
- self.v_spacing = self.parameterAsDouble(parameters, self.VSPACING, context)
- if self.h_spacing <= 0 or self.v_spacing <= 0:
- raise QgsProcessingException(
- self.tr('Invalid grid spacing: {0}/{1}').format(self.h_spacing, self.v_spacing))
-
- return True
-
- def processFeature(self, feature, feedback):
- if feature.hasGeometry():
- geom = feature.geometry()
- geomType = QgsWkbTypes.flatType(geom.wkbType())
- newGeom = None
-
- if geomType == QgsWkbTypes.Point:
- points = self._gridify([geom.asPoint()], self.h_spacing, self.v_spacing)
- newGeom = QgsGeometry.fromPoint(points[0])
- elif geomType == QgsWkbTypes.MultiPoint:
- points = self._gridify(geom.asMultiPoint(), self.h_spacing, self.v_spacing)
- newGeom = QgsGeometry.fromMultiPoint(points)
- elif geomType == QgsWkbTypes.LineString:
- points = self._gridify(geom.asPolyline(), self.h_spacing, self.v_spacing)
- if len(points) < 2:
- feedback.reportError(self.tr('Failed to gridify feature with FID {0}').format(feature.id()))
- newGeom = None
- else:
- newGeom = QgsGeometry.fromPolylineXY(points)
- elif geomType == QgsWkbTypes.MultiLineString:
- polyline = []
- for line in geom.asMultiPolyline():
- points = self._gridify(line, self.h_spacing, self.v_spacing)
- if len(points) > 1:
- polyline.append(points)
- if len(polyline) <= 0:
- feedback.reportError(self.tr('Failed to gridify feature with FID {0}').format(feature.id()))
- newGeom = None
- else:
- newGeom = QgsGeometry.fromMultiPolyline(polyline)
-
- elif geomType == QgsWkbTypes.Polygon:
- polygon = []
- for line in geom.asPolygon():
- points = self._gridify(line, self.h_spacing, self.v_spacing)
- if len(points) > 1:
- polygon.append(points)
- if len(polygon) <= 0:
- feedback.reportError(self.tr('Failed to gridify feature with FID {0}').format(feature.id()))
- newGeom = None
- else:
- newGeom = QgsGeometry.fromPolygon(polygon)
- elif geomType == QgsWkbTypes.MultiPolygon:
- multipolygon = []
- for polygon in geom.asMultiPolygon():
- newPolygon = []
- for line in polygon:
- points = self._gridify(line, self.h_spacing, self.v_spacing)
- if len(points) > 2:
- newPolygon.append(points)
-
- if len(newPolygon) > 0:
- multipolygon.append(newPolygon)
-
- if len(multipolygon) <= 0:
- feedback.reportError(self.tr('Failed to gridify feature with FID {0}').format(feature.id()))
- newGeom = None
- else:
- newGeom = QgsGeometry.fromMultiPolygon(multipolygon)
-
- if newGeom is not None:
- feature.setGeometry(newGeom)
- else:
- feature.clearGeometry()
- return feature
-
- def _gridify(self, points, hSpacing, vSpacing):
- nPoints = []
- for p in points:
- nPoints.append(QgsPointXY(round(p.x() / hSpacing, 0) * hSpacing,
- round(p.y() / vSpacing, 0) * vSpacing))
-
- i = 0
- # Delete overlapping points
- while i < len(nPoints) - 2:
- if nPoints[i] == nPoints[i + 1]:
- nPoints.pop(i + 1)
- else:
- i += 1
-
- i = 0
- # Delete line points that go out and return to the same place
- while i < len(nPoints) - 3:
- if nPoints[i] == nPoints[i + 2]:
- nPoints.pop(i + 1)
- nPoints.pop(i + 1)
-
- # Step back to catch arcs
- if i > 0:
- i -= 1
- else:
- i += 1
-
- i = 0
- # Delete overlapping start/end points
- while len(nPoints) > 1 and nPoints[0] == nPoints[len(nPoints) - 1]:
- nPoints.pop(len(nPoints) - 1)
-
- return nPoints
diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
index 796f6bb4243..ad56c02bbca 100644
--- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
+++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
@@ -73,7 +73,6 @@ from .FindProjection import FindProjection
from .FixedDistanceBuffer import FixedDistanceBuffer
from .GeometryConvert import GeometryConvert
from .GeometryByExpression import GeometryByExpression
-from .Gridify import Gridify
from .GridLine import GridLine
from .GridPolygon import GridPolygon
from .Heatmap import Heatmap
@@ -200,7 +199,6 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
FixedDistanceBuffer(),
GeometryByExpression(),
GeometryConvert(),
- Gridify(),
GridLine(),
GridPolygon(),
Heatmap(),
diff --git a/python/plugins/processing/tests/testdata/expected/gridify_lines.gfs b/python/plugins/processing/tests/testdata/expected/gridify_lines.gfs
deleted file mode 100644
index ae929a27136..00000000000
--- a/python/plugins/processing/tests/testdata/expected/gridify_lines.gfs
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
- gridify_lines
- gridify_lines
-
- 2
- EPSG:4326
-
- 7
- 2.00000
- 12.00000
- -4.00000
- 4.00000
-
-
-
diff --git a/python/plugins/processing/tests/testdata/expected/gridify_lines.gml b/python/plugins/processing/tests/testdata/expected/gridify_lines.gml
index 961702eaa01..60d3cacce68 100644
--- a/python/plugins/processing/tests/testdata/expected/gridify_lines.gml
+++ b/python/plugins/processing/tests/testdata/expected/gridify_lines.gml
@@ -1,23 +1,24 @@
- 2-4
- 124
+ -2-4
+ 126
-
+
- 6,2 8,2 8,4 12,4
+ 6,2 10,2 10,4 12,6
+ -2,-2 2,-2
@@ -27,6 +28,7 @@
+ 4,2 6,2
@@ -36,7 +38,7 @@
- 6,-4 10,0
+ 6,-4 10,2
diff --git a/python/plugins/processing/tests/testdata/expected/gridify_polys.gfs b/python/plugins/processing/tests/testdata/expected/gridify_polys.gfs
deleted file mode 100644
index cc44b1a9047..00000000000
--- a/python/plugins/processing/tests/testdata/expected/gridify_polys.gfs
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
- gridify_polys
- gridify_polys
-
- 3
- EPSG:4326
-
- 6
- 0.00000
- 10.00000
- -4.00000
- 6.00000
-
-
- name
- name
- String
- 5
-
-
- intval
- intval
- Integer
-
-
- floatval
- floatval
- Real
-
-
-
diff --git a/python/plugins/processing/tests/testdata/expected/gridify_polys.gml b/python/plugins/processing/tests/testdata/expected/gridify_polys.gml
index c255726ba4e..f6029dd6071 100644
--- a/python/plugins/processing/tests/testdata/expected/gridify_polys.gml
+++ b/python/plugins/processing/tests/testdata/expected/gridify_polys.gml
@@ -1,19 +1,19 @@
- -0-4
+ -2-4
106
- 0,0 0,4 4,4 4,2 2,2 2,0 0,0
+ -2,-2 -2,4 4,4 4,2 2,2 2,-2 -2,-2
aaaaa
33
44.123456
@@ -21,6 +21,7 @@
+ 6,6 6,4 4,4 6,6
Aaaaa
-33
0
@@ -28,14 +29,13 @@
- 2,4 2,6 4,6 4,4 2,4
bbaaa
0.123
- 6,0 10,0 10,-4 6,-4 6,0
+ 6,2 10,2 10,-4 6,-4 6,28,0 8,-2 10,-2 10,0 8,0
ASDF
0
@@ -48,7 +48,7 @@
- 4,2 6,0 6,-4 2,0 2,2 4,2
+ 4,2 6,2 6,-4 2,-2 2,2 4,2
elim
2
3.33
diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
index c776f6b03be..eba29dd6d8d 100755
--- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
+++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
@@ -2413,7 +2413,7 @@ tests:
name: expected/lines_to_polygon.gml
type: vector
- - algorithm: qgis:snappointstogrid
+ - algorithm: native:snappointstogrid
name: Gridify polys
params:
INPUT:
@@ -2426,7 +2426,7 @@ tests:
name: expected/gridify_polys.gml
type: vector
- - algorithm: qgis:snappointstogrid
+ - algorithm: native:snappointstogrid
name: Gridify lines
params:
INPUT:
diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt
index af9e869b7bc..117552450c7 100644
--- a/src/analysis/CMakeLists.txt
+++ b/src/analysis/CMakeLists.txt
@@ -53,6 +53,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmsaveselectedfeatures.cpp
processing/qgsalgorithmsimplify.cpp
processing/qgsalgorithmsmooth.cpp
+ processing/qgsalgorithmsnaptogrid.cpp
processing/qgsalgorithmsplitwithlines.cpp
processing/qgsalgorithmstringconcatenation.cpp
processing/qgsalgorithmsubdivide.cpp
diff --git a/src/analysis/processing/qgsalgorithmsnaptogrid.cpp b/src/analysis/processing/qgsalgorithmsnaptogrid.cpp
new file mode 100644
index 00000000000..d5517a6a36a
--- /dev/null
+++ b/src/analysis/processing/qgsalgorithmsnaptogrid.cpp
@@ -0,0 +1,105 @@
+/***************************************************************************
+ qgsalgorithmsnaptogrid.cpp
+ --------------------------
+ begin : October 2017
+ copyright : (C) 2017 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 "qgsalgorithmsnaptogrid.h"
+
+///@cond PRIVATE
+
+QString QgsSnapToGridAlgorithm::name() const
+{
+ return QStringLiteral( "snappointstogrid" );
+}
+
+QString QgsSnapToGridAlgorithm::displayName() const
+{
+ return QObject::tr( "Snap points to grid" );
+}
+
+QStringList QgsSnapToGridAlgorithm::tags() const
+{
+ return QObject::tr( "snapped,grid,simplify,round,precision" ).split( ',' );
+}
+
+QString QgsSnapToGridAlgorithm::group() const
+{
+ return QObject::tr( "Vector geometry" );
+}
+
+QString QgsSnapToGridAlgorithm::outputName() const
+{
+ return QObject::tr( "Snapped" );
+}
+
+QString QgsSnapToGridAlgorithm::shortHelpString() const
+{
+ return QObject::tr( "This algorithm modifies the coordinates of geometries in a vector layer, so that all points "
+ "or vertices are snapped to the closest point of the grid.\n\n"
+ "If the snapped geometry could not be calculated (or was totally collapsed) then the feature's"
+ "geometry will be cleared.\n\n"
+ "Note that snapping to grid may generate an invalid geometry in some corner cases.\n\n"
+ "Snapping can be performed on the X, Y, Z or M axis. A grid spacing of 0 for any axis will"
+ "disable snapping for that axis." );
+}
+
+QgsSnapToGridAlgorithm *QgsSnapToGridAlgorithm::createInstance() const
+{
+ return new QgsSnapToGridAlgorithm();
+}
+
+void QgsSnapToGridAlgorithm::initParameters( const QVariantMap & )
+{
+ addParameter( new QgsProcessingParameterNumber( QStringLiteral( "HSPACING" ),
+ QObject::tr( "X Grid Spacing" ), QgsProcessingParameterNumber::Double,
+ 1, false, 0 ) );
+ addParameter( new QgsProcessingParameterNumber( QStringLiteral( "VSPACING" ),
+ QObject::tr( "Y Grid Spacing" ), QgsProcessingParameterNumber::Double,
+ 1, false, 0 ) );
+ addParameter( new QgsProcessingParameterNumber( QStringLiteral( "ZSPACING" ),
+ QObject::tr( "Z Grid Spacing" ), QgsProcessingParameterNumber::Double,
+ 0, false, 0 ) );
+ addParameter( new QgsProcessingParameterNumber( QStringLiteral( "MSPACING" ),
+ QObject::tr( "M Grid Spacing" ), QgsProcessingParameterNumber::Double,
+ 0, false, 0 ) );
+}
+
+bool QgsSnapToGridAlgorithm::prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback * )
+{
+ mIntervalX = parameterAsDouble( parameters, QStringLiteral( "HSPACING" ), context );
+ mIntervalY = parameterAsDouble( parameters, QStringLiteral( "VSPACING" ), context );
+ mIntervalZ = parameterAsDouble( parameters, QStringLiteral( "ZSPACING" ), context );
+ mIntervalM = parameterAsDouble( parameters, QStringLiteral( "MSPACING" ), context );
+ return true;
+}
+
+QgsFeature QgsSnapToGridAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingFeedback *feedback )
+{
+ QgsFeature f = feature;
+ if ( f.hasGeometry() )
+ {
+ QgsGeometry outputGeometry = f.geometry().snappedToGrid( mIntervalX, mIntervalY, mIntervalZ, mIntervalM );
+ if ( !outputGeometry )
+ {
+ feedback->reportError( QObject::tr( "Error snapping geometry %1" ).arg( feature.id() ) );
+ }
+ f.setGeometry( outputGeometry );
+ }
+ return f;
+}
+
+///@endcond
+
+
diff --git a/src/analysis/processing/qgsalgorithmsnaptogrid.h b/src/analysis/processing/qgsalgorithmsnaptogrid.h
new file mode 100644
index 00000000000..02204b7c4ff
--- /dev/null
+++ b/src/analysis/processing/qgsalgorithmsnaptogrid.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ qgsalgorithmsnaptogrid.h
+ ---------------------
+ begin : October 2017
+ copyright : (C) 2017 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 QGSALGORITHMSNAPTOGRID_H
+#define QGSALGORITHMSNAPTOGRID_H
+
+#define SIP_NO_FILE
+
+#include "qgis.h"
+#include "qgsprocessingalgorithm.h"
+
+///@cond PRIVATE
+
+/**
+ * Native snap to grid algorithm.
+ */
+class QgsSnapToGridAlgorithm : public QgsProcessingFeatureBasedAlgorithm
+{
+
+ public:
+
+ QgsSnapToGridAlgorithm() = default;
+ QString name() const override;
+ QString displayName() const override;
+ virtual QStringList tags() const override;
+ QString group() const override;
+ QString shortHelpString() const override;
+ QgsSnapToGridAlgorithm *createInstance() const override SIP_FACTORY;
+ void initParameters( const QVariantMap &configuration = QVariantMap() ) override;
+
+ protected:
+ QString outputName() const override;
+ bool prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
+ QgsFeature processFeature( const QgsFeature &feature, QgsProcessingFeedback *feedback ) override;
+
+ private:
+ double mIntervalX = 0.0;
+ double mIntervalY = 0.0;
+ double mIntervalZ = 0.0;
+ double mIntervalM = 0.0;
+};
+
+///@endcond PRIVATE
+
+#endif // QGSALGORITHMSNAPTOGRID_H
+
+
diff --git a/src/analysis/processing/qgsnativealgorithms.cpp b/src/analysis/processing/qgsnativealgorithms.cpp
index d6a984121dd..4492388406c 100644
--- a/src/analysis/processing/qgsnativealgorithms.cpp
+++ b/src/analysis/processing/qgsnativealgorithms.cpp
@@ -48,6 +48,7 @@
#include "qgsalgorithmsaveselectedfeatures.h"
#include "qgsalgorithmsimplify.h"
#include "qgsalgorithmsmooth.h"
+#include "qgsalgorithmsnaptogrid.h"
#include "qgsalgorithmsplitwithlines.h"
#include "qgsalgorithmstringconcatenation.h"
#include "qgsalgorithmsubdivide.h"
@@ -122,6 +123,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsSelectByLocationAlgorithm() );
addAlgorithm( new QgsSimplifyAlgorithm() );
addAlgorithm( new QgsSmoothAlgorithm() );
+ addAlgorithm( new QgsSnapToGridAlgorithm() );
addAlgorithm( new QgsSplitWithLinesAlgorithm() );
addAlgorithm( new QgsStringConcatenationAlgorithm() );
addAlgorithm( new QgsSubdivideAlgorithm() );
diff --git a/src/core/geometry/qgsabstractgeometry.h b/src/core/geometry/qgsabstractgeometry.h
index c9828fa7178..c21d5ca7fe7 100644
--- a/src/core/geometry/qgsabstractgeometry.h
+++ b/src/core/geometry/qgsabstractgeometry.h
@@ -583,7 +583,6 @@ class CORE_EXPORT QgsAbstractGeometry
protected:
-#ifndef SIP_RUN
/**
* Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
* To create it, the geometry is default constructed and then the WKB is changed.
@@ -592,7 +591,6 @@ class CORE_EXPORT QgsAbstractGeometry
* \note Not available in Python bindings
*/
virtual QgsAbstractGeometry *createEmptyWithSameType() const = 0 SIP_FACTORY;
-#endif
/**
* Returns whether the geometry has any child geometries (false for point / curve, true otherwise)
diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp
index cc180a02ea6..bb3670b8df9 100644
--- a/src/core/geometry/qgsgeometry.cpp
+++ b/src/core/geometry/qgsgeometry.cpp
@@ -1073,6 +1073,15 @@ QgsGeometry QgsGeometry::orthogonalize( double tolerance, int maxIterations, dou
return engine.orthogonalize( tolerance, maxIterations, angleThreshold );
}
+QgsGeometry QgsGeometry::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
+{
+ if ( !d->geometry )
+ {
+ return QgsGeometry();
+ }
+ return QgsGeometry( d->geometry->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
+}
+
bool QgsGeometry::intersects( const QgsRectangle &r ) const
{
QgsGeometry g = fromRect( r );
diff --git a/src/core/geometry/qgsgeometry.h b/src/core/geometry/qgsgeometry.h
index 7eb801f179b..da39044051f 100644
--- a/src/core/geometry/qgsgeometry.h
+++ b/src/core/geometry/qgsgeometry.h
@@ -729,6 +729,20 @@ class CORE_EXPORT QgsGeometry
*/
QgsGeometry orthogonalize( double tolerance = 1.0E-8, int maxIterations = 1000, double angleThreshold = 15.0 ) const;
+ /**
+ * Returns a new geometry with all points or vertices snapped to the closest point of the grid.
+ *
+ * If the gridified geometry could not be calculated (or was totally collapsed) an empty geometry will be returned.
+ * Note that snapping to grid may generate an invalid geometry in some corner cases.
+ * It can also be thought as rounding the edges and it may be useful for removing errors.
+ * \param hSpacing Horizontal spacing of the grid (x axis). 0 to disable.
+ * \param vSpacing Vertical spacing of the grid (y axis). 0 to disable.
+ * \param dSpacing Depth spacing of the grid (z axis). 0 (default) to disable.
+ * \param mSpacing Custom dimension spacing of the grid (m axis). 0 (default) to disable.
+ * \since 3.0
+ */
+ QgsGeometry snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const;
+
//! Tests for intersection with a rectangle (uses GEOS)
bool intersects( const QgsRectangle &r ) const;
diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp
index 9d913763755..9fb1fe9151f 100644
--- a/tests/src/core/testqgsgeometry.cpp
+++ b/tests/src/core/testqgsgeometry.cpp
@@ -15820,8 +15820,6 @@ void TestQgsGeometry::snappedToGrid()
std::unique_ptr snapped { curve.constGet()->snappedToGrid( 1, 1 ) };
QCOMPARE( snapped->asWkt(), QStringLiteral( "CompoundCurve ((59 402, 68 415),CircularString (68 415, 27 505, 27 406))" ) );
}
-
-
}
QGSTEST_MAIN( TestQgsGeometry )