Skip to content


Merge branch 'processing-makevalid'
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbruy committed Feb 7, 2017
2 parents 11f0836 + d3003de commit 94a2639
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 10 deletions.
22 changes: 13 additions & 9 deletions python/plugins/processing/algs/help/qgis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ qgis:polygonstolines: >

qgis:spatialiteexecutesql: >
This algorithm performs a SQL database query on a Spatialite database.

qgis:postgisexecutesql: >
This algorithm performs a SQL database query on a PostGIS database connected to QGIS.

Expand Down Expand Up @@ -514,7 +514,7 @@ qgis:snappointstogrid: >

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.

Output will contain multi geometries for split features.

qgis:splitvectorlayer: >
Expand Down Expand Up @@ -571,16 +571,20 @@ qgis:zonalstatistics:

qgis:rastercalculator: >
This algorithm allows to perform algebraic operations using raster layers.

The resulting layer will have its values computed according to an expression. The expression can contain numerical values, operators and references to any of the layers in the current project. The following functions are also supported:

- sin(), cos(), tan(), atan2(), ln(), log10()

The extent and cellsize can be defined by the user. If the extent is not specified, the minimum extent that covers the input layers will be used. If the cell size is not specified, the minimum cell size of all input layers will be used.

The cell size is assumed to be the same in both X and Y axes.

Layers are referred by their name as displayed in the layer list and the number of the band to use (based on 1), using the pattern 'layer_name@band number'. For instance, the first band from a layer named DEM will be referred as DEM@1.

When using the calculator in the batch interface or from the console, the files to use have to be specified. The corresponding layers are referred using the base name of the file (without the full path). For instance, is using a layer at path/to/my/rasterfile.tif, the first band of that layer will be referred as rasterfile.tif@1.

qgis:fixgeometries: >
This algorithm attempts to create a valid representation of a given invalid geometry without losing any of the input vertices. Already-valid geometries are returned without further intervention. Always outputs multi-geometry layer.

NOTE: M values will be dropped from the output.
95 changes: 95 additions & 0 deletions python/plugins/processing/algs/qgis/
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-

Date : January 2017
Copyright : (C) 2017 by Alexander Bruy
Email : alexander dot bruy 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. *
* *

__author__ = 'Alexander Bruy'
__date__ = 'January 2017'
__copyright__ = '(C) 2017, Alexander Bruy'

# This will get replaced with a git SHA1 when you do a git archive

__revision__ = '$Format:%H$'

from qgis.core import QgsWkbTypes

from processing.core.GeoAlgorithm import GeoAlgorithm
from processing.core.parameters import ParameterVector
from processing.core.outputs import OutputVector
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from import dataobjects, vector

class FixGeometry(GeoAlgorithm):


def defineCharacteristics(self):, self.i18n_name = self.trAlgorithm('Fix geometries'), self.i18n_group = self.trAlgorithm('Vector geometry tools')
self.tags ='repair,invalid,geometry')

self.addParameter(ParameterVector(self.INPUT,'Input Layer'),
[dataobjects.TYPE_VECTOR_POLYGON, dataobjects.TYPE_VECTOR_LINE]))
self.addOutput(OutputVector(self.OUTPUT,'Layer with fixed geometries')))

def processAlgorithm(self, feedback):
layer = dataobjects.getObjectFromUri(

writer = self.getOutputFromName(

features = vector.features(layer)
if len(features) == 0:
raise GeoAlgorithmExecutionException('There are no features in the input layer'))

total = 100.0 / len(features)
for current, inputFeature in enumerate(features):
outputFeature = inputFeature
if inputFeature.geometry():
outputGeometry = inputFeature.geometry().makeValid()
if not outputGeometry:
'makeValid failed for feature {}'.format(

if outputGeometry.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(outputGeometry.geometry().wkbType()) == QgsWkbTypes.GeometryCollection:
tmpGeometries = outputGeometry.asGeometryCollection()
for g in tmpGeometries:
if g.type() == inputFeature.geometry().type():
feedback.setProgress(int(current * total))


feedback.setProgress(int(current * total))

del writer
4 changes: 3 additions & 1 deletion python/plugins/processing/algs/qgis/
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@
from .ServiceAreaFromLayer import ServiceAreaFromLayer
from .TruncateTable import TruncateTable
from .Polygonize import Polygonize
from .FixGeometry import FixGeometry

pluginPath = os.path.normpath(os.path.join(
os.path.split(os.path.dirname(__file__))[0], os.pardir))
Expand Down Expand Up @@ -252,7 +253,8 @@ def __init__(self):
RasterCalculator(), Heatmap(), Orthogonalize(),
ShortestPathPointToPoint(), ShortestPathPointToLayer(),
ShortestPathLayerToPoint(), ServiceAreaFromPoint(),
ServiceAreaFromLayer(), TruncateTable(), Polygonize()
ServiceAreaFromLayer(), TruncateTable(), Polygonize(),

if hasMatplotlib:
Expand Down
16 changes: 16 additions & 0 deletions python/plugins/processing/tests/testdata/expected/valid.gfs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
19 changes: 19 additions & 0 deletions python/plugins/processing/tests/testdata/expected/valid.gml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>

<ogr:valid fid="invalidgeometries.0">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:4326"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>122.186446688291,-8.60019485107161 122.188508041037,-8.60212999854704 122.186395514816,-8.60635505098748 122.183795482545,-8.60261750459786 122.186446688291,-8.60019485107161</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>122.176320389766,-8.60123623745388 122.179976685147,-8.59774244408966 122.183895970553,-8.59830234200473 122.176320389766,-8.60123623745388</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>122.186446688291,-8.60019485107161 122.184526741622,-8.59839245215742 122.183895970553,-8.59830234200473 122.190376814231,-8.59579241988638 122.188508041037,-8.59831120114895 122.186446688291,-8.60019485107161</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
11 changes: 11 additions & 0 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2300,6 +2300,17 @@ tests:
name: expected/zonal_statistics.gml
type: vector

- algorithm: qgis:fixgeometries
name: Fix geometries
name: invalidgeometries.gml
type: vector
name: expected/valid.gml
type: vector

- algorithm: qgis:polygonize
name: Polygonize
Expand Down

0 comments on commit 94a2639

Please sign in to comment.