# -*- 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.fromPolyline(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