diff --git a/python/plugins/processing/algs/help/qgis.yaml b/python/plugins/processing/algs/help/qgis.yaml
index 0eb07954e2b..e2afbfd3174 100755
--- a/python/plugins/processing/algs/help/qgis.yaml
+++ b/python/plugins/processing/algs/help/qgis.yaml
@@ -402,12 +402,14 @@ qgis:polygoncentroids: >
NOTE: This algorithm is deprecated and the generic "centroids" algorithm (which works for line and multi geometry layers) should be used instead.
-
qgis:polygonfromlayerextent: >
This algorithm takes a vector layer and generates a new one with the minimum bounding box (rectangle with N-S orientation) that covers all the input features.
As an alternative, the output layer can contain not just a single bounding box, but one for each input feature, representing the bounding box of each of them.
+qgis:polygonfromrasterextent: >
+ This algorithm takes a raster layer and generates a vector layer containing a feature with the minimum bounding box that covers the raster layer's extent.
+
qgis:polygonize: >
This algorithm takes a lines layer and creates a polygon layer, with polygons generated from the lines in the input layer.
diff --git a/python/plugins/processing/algs/qgis/ExtentFromLayer.py b/python/plugins/processing/algs/qgis/ExtentFromLayer.py
index d0bdd4cbd4d..61a21426d53 100644
--- a/python/plugins/processing/algs/qgis/ExtentFromLayer.py
+++ b/python/plugins/processing/algs/qgis/ExtentFromLayer.py
@@ -49,7 +49,7 @@ pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
class ExtentFromLayer(QgisAlgorithm):
- INPUT_LAYER = 'INPUT_LAYER'
+ INPUT = 'INPUT'
BY_FEATURE = 'BY_FEATURE'
OUTPUT = 'OUTPUT'
@@ -67,7 +67,7 @@ class ExtentFromLayer(QgisAlgorithm):
super().__init__()
def initAlgorithm(self, config=None):
- self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT_LAYER, self.tr('Input layer')))
+ self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer')))
self.addParameter(QgsProcessingParameterBoolean(self.BY_FEATURE,
self.tr('Calculate extent for each feature separately'), False))
@@ -77,10 +77,10 @@ class ExtentFromLayer(QgisAlgorithm):
return 'polygonfromlayerextent'
def displayName(self):
- return self.tr('Polygon from layer extent')
+ return self.tr('Polygon from vector extent')
def processAlgorithm(self, parameters, context, feedback):
- source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
+ source = self.parameterAsSource(parameters, self.INPUT, context)
byFeature = self.parameterAsBool(parameters, self.BY_FEATURE, context)
fields = QgsFields()
diff --git a/python/plugins/processing/algs/qgis/ExtentFromRasterLayer.py b/python/plugins/processing/algs/qgis/ExtentFromRasterLayer.py
new file mode 100755
index 00000000000..63efd366aef
--- /dev/null
+++ b/python/plugins/processing/algs/qgis/ExtentFromRasterLayer.py
@@ -0,0 +1,127 @@
+# -*- coding: utf-8 -*-
+
+"""
+***************************************************************************
+ ExtentFromRasterLayer.py
+ ---------------------
+ Date : August 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. *
+* *
+***************************************************************************
+"""
+
+__author__ = 'Nyall Dawson'
+__date__ = 'August 2017'
+__copyright__ = '(C) 2017, Nyall Dawson'
+
+# This will get replaced with a git SHA1 when you do a git archive
+
+__revision__ = '$Format:%H$'
+
+import os
+
+from qgis.PyQt.QtGui import QIcon
+from qgis.PyQt.QtCore import QVariant
+
+from qgis.core import (QgsField,
+ QgsFeatureSink,
+ QgsPointXY,
+ QgsGeometry,
+ QgsFeature,
+ QgsWkbTypes,
+ QgsProcessing,
+ QgsProcessingParameterRasterLayer,
+ QgsProcessingParameterFeatureSink,
+ QgsFields)
+
+from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
+
+pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
+
+
+class ExtentFromRasterLayer(QgisAlgorithm):
+
+ INPUT = 'INPUT'
+ OUTPUT = 'OUTPUT'
+
+ def icon(self):
+ return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'layer_extent.png'))
+
+ def tags(self):
+ return self.tr('extent,envelope,bounds,bounding,boundary,layer').split(',')
+
+ def group(self):
+ return self.tr('Raster tools')
+
+ def __init__(self):
+ super().__init__()
+
+ def initAlgorithm(self, config=None):
+ self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, self.tr('Input layer')))
+ self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Extent'), type=QgsProcessing.TypeVectorPolygon))
+
+ def name(self):
+ return 'polygonfromrasterextent'
+
+ def displayName(self):
+ return self.tr('Polygon from raster extent')
+
+ def processAlgorithm(self, parameters, context, feedback):
+ raster = self.parameterAsRasterLayer(parameters, self.INPUT, context)
+
+ fields = QgsFields()
+ fields.append(QgsField('MINX', QVariant.Double))
+ fields.append(QgsField('MINY', QVariant.Double))
+ fields.append(QgsField('MAXX', QVariant.Double))
+ fields.append(QgsField('MAXY', QVariant.Double))
+ fields.append(QgsField('CNTX', QVariant.Double))
+ fields.append(QgsField('CNTY', QVariant.Double))
+ fields.append(QgsField('AREA', QVariant.Double))
+ fields.append(QgsField('PERIM', QVariant.Double))
+ fields.append(QgsField('HEIGHT', QVariant.Double))
+ fields.append(QgsField('WIDTH', QVariant.Double))
+
+ (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
+ fields, QgsWkbTypes.Polygon, raster.crs())
+
+ self.layerExtent(raster, sink, feedback)
+
+ return {self.OUTPUT: dest_id}
+
+ def layerExtent(self, raster, sink, feedback):
+ rect = raster.extent()
+ geometry = QgsGeometry.fromRect(rect)
+ minx = rect.xMinimum()
+ miny = rect.yMinimum()
+ maxx = rect.xMaximum()
+ maxy = rect.yMaximum()
+ height = rect.height()
+ width = rect.width()
+ cntx = minx + width / 2.0
+ cnty = miny + height / 2.0
+ area = width * height
+ perim = 2 * width + 2 * height
+
+ feat = QgsFeature()
+ feat.setGeometry(geometry)
+ attrs = [
+ minx,
+ miny,
+ maxx,
+ maxy,
+ cntx,
+ cnty,
+ area,
+ perim,
+ height,
+ width,
+ ]
+ feat.setAttributes(attrs)
+ sink.addFeature(feat, QgsFeatureSink.FastInsert)
diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
index 8e062e8f664..9a8539cd641 100644
--- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
+++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
@@ -70,6 +70,7 @@ from .Explode import Explode
from .ExportGeometryInfo import ExportGeometryInfo
from .ExtendLines import ExtendLines
from .ExtentFromLayer import ExtentFromLayer
+from .ExtentFromRasterLayer import ExtentFromRasterLayer
from .ExtractNodes import ExtractNodes
from .ExtractSpecificNodes import ExtractSpecificNodes
from .FieldPyculator import FieldsPyculator
@@ -222,6 +223,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
ExportGeometryInfo(),
ExtendLines(),
ExtentFromLayer(),
+ ExtentFromRasterLayer(),
ExtractNodes(),
ExtractSpecificNodes(),
FieldsCalculator(),
diff --git a/python/plugins/processing/gui/TestTools.py b/python/plugins/processing/gui/TestTools.py
index 1d8bf7cdd10..ca54882ef47 100644
--- a/python/plugins/processing/gui/TestTools.py
+++ b/python/plugins/processing/gui/TestTools.py
@@ -27,6 +27,7 @@ __copyright__ = '(C) 2013, Victor Olaya'
__revision__ = '$Format:%H$'
import os
+import posixpath
import re
import yaml
import hashlib
@@ -81,7 +82,8 @@ def extractSchemaPath(filepath):
if part == 'testdata' and not localpath:
localparts = parts
localparts.reverse()
- localpath = os.path.join(*localparts)
+ # we always want posix style paths here
+ localpath = posixpath.join(*localparts)
parts.append(part)
diff --git a/python/plugins/processing/tests/testdata/expected/raster_extent.gfs b/python/plugins/processing/tests/testdata/expected/raster_extent.gfs
new file mode 100755
index 00000000000..c8cd5ab33c1
--- /dev/null
+++ b/python/plugins/processing/tests/testdata/expected/raster_extent.gfs
@@ -0,0 +1,66 @@
+
+
+ raster_extent
+ raster_extent
+
+ 3
+ EPSG:4326
+
+ 1
+ 18.66630
+ 18.70360
+ 45.77670
+ 45.81170
+
+
+ MINX
+ MINX
+ Real
+
+
+ MINY
+ MINY
+ Real
+
+
+ MAXX
+ MAXX
+ Real
+
+
+ MAXY
+ MAXY
+ Real
+
+
+ CNTX
+ CNTX
+ Real
+
+
+ CNTY
+ CNTY
+ Real
+
+
+ AREA
+ AREA
+ Real
+
+
+ PERIM
+ PERIM
+ Real
+
+
+ HEIGHT
+ HEIGHT
+ Real
+
+
+ WIDTH
+ WIDTH
+ Real
+
+
+
diff --git a/python/plugins/processing/tests/testdata/expected/raster_extent.gml b/python/plugins/processing/tests/testdata/expected/raster_extent.gml
new file mode 100755
index 00000000000..1b84c0ed0ff
--- /dev/null
+++ b/python/plugins/processing/tests/testdata/expected/raster_extent.gml
@@ -0,0 +1,29 @@
+
+
+
+
+ 18.666297944245.7767014376
+ 18.703597944245.8117014376
+
+
+
+
+
+ 18.6662979442,45.7767014376 18.7035979442,45.7767014376 18.7035979442,45.8117014376 18.6662979442,45.8117014376 18.6662979442,45.7767014376
+ 18.6662979442
+ 45.7767014376
+ 18.7035979442
+ 45.8117014376
+ 18.6849479442
+ 45.7942014376
+ 0.00130549999999981
+ 0.14459999999999
+ 0.0349999999999966
+ 0.0372999999999983
+
+
+
diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
index 39ebd6d0a99..570ba2d95a3 100644
--- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
+++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
@@ -2845,7 +2845,7 @@ tests:
name: Standard polygon from layer extent
params:
BY_FEATURE: false
- INPUT_LAYER:
+ INPUT:
name: polys.gml
type: vector
results:
@@ -3228,3 +3228,14 @@ tests:
OUTPUT:
name: expected/execute_sql.gml
type: vector
+
+ - algorithm: qgis:polygonfromrasterextent
+ name: Polygon from raster extent
+ params:
+ INPUT:
+ name: dem.tif
+ type: raster
+ results:
+ OUTPUT:
+ name: expected/raster_extent.gml
+ type: vector