|
#!/usr/bin/env python3 |
|
# -*- coding: utf-8 -*- |
|
|
|
"""""" |
|
|
|
# MIT License |
|
|
|
# Copyright (c) 2022 Anton Bakker |
|
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy |
|
# of this software and associated documentation files (the "Software"), to deal |
|
# in the Software without restriction, including without limitation the rights |
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
# copies of the Software, and to permit persons to whom the Software is |
|
# furnished to do so, subject to the following conditions: |
|
|
|
# The above copyright notice and this permission notice shall be included in all |
|
# copies or substantial portions of the Software. |
|
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
# SOFTWARE. |
|
|
|
__author__ = "Anton Bakker" |
|
__copyright__ = "Copyright 2022, Anton Bakker" |
|
__license__ = "MIT" |
|
__version__ = "0.0.0" |
|
__maintainer__ = "Anton Bakker" |
|
__email__ = "[email protected]" |
|
__date__ = "2022-08-09" |
|
|
|
from qgis.PyQt.QtCore import QCoreApplication |
|
from qgis.core import ( |
|
QgsProcessingAlgorithm, |
|
QgsProcessingException, |
|
QgsProcessingParameterFile, |
|
QgsProcessingParameterFileDestination, |
|
) |
|
from qgis import processing |
|
from osgeo import ogr, gdal |
|
|
|
|
|
class PDOKGeomFixer(QgsProcessingAlgorithm): |
|
"""QGIS processing algorithm that forces counter-clockwise orientation of polygon geometries in Geopackage file""" |
|
|
|
def tr(self, string): |
|
""" |
|
Returns a translatable string with the self.tr() function. |
|
""" |
|
return QCoreApplication.translate("Processing", string) |
|
|
|
def createInstance(self): |
|
# Must return a new copy of your algorithm. |
|
return PDOKGeomFixer() |
|
|
|
def name(self): |
|
""" |
|
Returns the unique algorithm name. |
|
""" |
|
return "pdok-geom-orientation-fixer" |
|
|
|
def displayName(self): |
|
""" |
|
Returns the translated algorithm name. |
|
""" |
|
return self.tr("PDOK Geometry Orientation Fixer") |
|
|
|
def group(self): |
|
""" |
|
Returns the name of the group this algorithm belongs to. |
|
""" |
|
return self.tr("PDOK Tools") |
|
|
|
def groupId(self): |
|
""" |
|
Returns the unique ID of the group this algorithm belongs |
|
to. |
|
""" |
|
return "pdok-tools" |
|
|
|
def shortHelpString(self): |
|
""" |
|
Returns a localised short help string for the algorithm. |
|
""" |
|
return self.tr( |
|
"This processing tool forces the orientation of polygon geometries to counter-clockwise (CCW) in the ouput GeoPackage file. Non-polygon layers are copied over to the output GeoPackage file.\n\ |
|
Parameters:\n\n\ |
|
- Input GeoPackage file\n\ |
|
- Output GeoPackage file\ |
|
" |
|
) |
|
|
|
def initAlgorithm(self, config=None): |
|
""" |
|
Here we define the inputs and outputs of the algorithm. |
|
""" |
|
|
|
self.INPUT = "INPUT" # recommended name for the main input parameter |
|
self.OUTPUT = "OUTPUT" # recommended name for the main input parameter |
|
|
|
self.addParameter( |
|
QgsProcessingParameterFile( |
|
self.INPUT, self.tr("Input GeoPackage file"), fileFilter="gpkg(*.gpkg)" |
|
) |
|
) |
|
self.addParameter( |
|
QgsProcessingParameterFileDestination( |
|
self.OUTPUT, |
|
self.tr("Output GeoPackage file"), |
|
fileFilter="gpkg(*.gpkg)", |
|
) |
|
) |
|
|
|
def processAlgorithm(self, parameters, context, feedback): |
|
try: |
|
# read out parameters |
|
source_gpkg_path = self.parameterAsFile(parameters, self.INPUT, context) |
|
target_gpkg_path = self.parameterAsFile(parameters, self.OUTPUT, context) |
|
feedback.pushInfo(f"INFO: {source_gpkg_path}") |
|
|
|
# read out |
|
layer_list = [] |
|
source = ogr.Open(source_gpkg_path) |
|
for i in source: |
|
layer_list_item = {} |
|
layer_name = i.GetName() |
|
layer_list_item["name"] = layer_name |
|
layer = source.GetLayer(layer_name) |
|
# attributes = [] |
|
# ldefn = layer.GetLayerDefn() |
|
# for n in range(ldefn.GetFieldCount()): |
|
# fdefn = ldefn.GetFieldDefn(n) |
|
# attributes.append(fdefn.name) |
|
# gdef = ldefn.GetGeomFieldDefn(0) |
|
# geom_name = gdef.name |
|
# layer_list_item["attributes"] = attributes |
|
# layer_list_item["geom_name"] = geom_name |
|
|
|
feature = layer.GetNextFeature() |
|
geometry = feature.GetGeometryRef() |
|
geom_type = geometry.GetGeometryName() |
|
layer_list_item["geom_type"] = geom_type |
|
layer_list.append(layer_list_item) |
|
|
|
gdal.UseExceptions() |
|
gdal.SetConfigOption("CPL_DEBUG", "ON") |
|
|
|
first = True |
|
|
|
for layer in layer_list: |
|
options = ["-lco", "GEOMETRY_NAME=geom"] |
|
if not first: |
|
options.extend(["-update", "-append"]) |
|
layer_name = layer["name"] |
|
|
|
options.extend([layer_name]) |
|
gdoptions = gdal.VectorTranslateOptions( |
|
format="GPKG", |
|
options=options, |
|
) |
|
ds = gdal.VectorTranslate( |
|
destNameOrDestDS=target_gpkg_path, |
|
srcDS=source_gpkg_path, |
|
options=gdoptions, |
|
) |
|
del ds |
|
first = False |
|
|
|
ogr.UseExceptions() |
|
for layer in layer_list: |
|
if not "POLYGON" in layer["geom_type"]: |
|
next |
|
layer_name = layer["name"] |
|
driver = ogr.GetDriverByName("GPKG") |
|
ogr_ds = driver.Open(target_gpkg_path, 1) |
|
geom_name = "geom" |
|
sql = f"UPDATE {layer_name} SET {geom_name} = ST_ForcePolygonCCW({geom_name})" |
|
layer = ogr_ds.ExecuteSQL(sql) |
|
del layer |
|
|
|
return { |
|
"Input folder path": source_gpkg_path, |
|
"output_file": target_gpkg_path, |
|
"layer_list": layer_list, |
|
} # all processing algs must |
|
|
|
except Exception as e: |
|
raise QgsProcessingException( |
|
f"Unexpected error occured while running PDOKGeomFixer: {str(e)}" |
|
) |