Skip to content

Commit 44e7f33

Browse files
committedAug 30, 2017
[FEATURE] New algorithm for creating vector layer from raster layer's extent
Allows creation of a new vector layer with a single feature containing a raster layer's extent. Previously this was only possible for vector layers.
1 parent 12e69d0 commit 44e7f33

File tree

6 files changed

+238
-1
lines changed

6 files changed

+238
-1
lines changed
 

‎python/plugins/processing/algs/help/qgis.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,12 +402,14 @@ qgis:polygoncentroids: >
402402

403403
NOTE: This algorithm is deprecated and the generic "centroids" algorithm (which works for line and multi geometry layers) should be used instead.
404404

405-
406405
qgis:polygonfromlayerextent: >
407406
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.
408407

409408
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.
410409

410+
qgis:polygonfromrasterextent: >
411+
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.
412+
411413
qgis:polygonize: >
412414
This algorithm takes a lines layer and creates a polygon layer, with polygons generated from the lines in the input layer.
413415

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
***************************************************************************
5+
ExtentFromRasterLayer.py
6+
---------------------
7+
Date : August 2017
8+
Copyright : (C) 2017 by Nyall Dawson
9+
Email : nyall dot dawson at gmail dot com
10+
***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************
18+
"""
19+
20+
__author__ = 'Nyall Dawson'
21+
__date__ = 'August 2017'
22+
__copyright__ = '(C) 2017, Nyall Dawson'
23+
24+
# This will get replaced with a git SHA1 when you do a git archive
25+
26+
__revision__ = '$Format:%H$'
27+
28+
import os
29+
30+
from qgis.PyQt.QtGui import QIcon
31+
from qgis.PyQt.QtCore import QVariant
32+
33+
from qgis.core import (QgsField,
34+
QgsFeatureSink,
35+
QgsPointXY,
36+
QgsGeometry,
37+
QgsFeature,
38+
QgsWkbTypes,
39+
QgsProcessing,
40+
QgsProcessingParameterRasterLayer,
41+
QgsProcessingParameterFeatureSink,
42+
QgsFields)
43+
44+
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
45+
46+
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
47+
48+
49+
class ExtentFromRasterLayer(QgisAlgorithm):
50+
51+
INPUT = 'INPUT'
52+
OUTPUT = 'OUTPUT'
53+
54+
def icon(self):
55+
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'layer_extent.png'))
56+
57+
def tags(self):
58+
return self.tr('extent,envelope,bounds,bounding,boundary,layer').split(',')
59+
60+
def group(self):
61+
return self.tr('Raster tools')
62+
63+
def __init__(self):
64+
super().__init__()
65+
66+
def initAlgorithm(self, config=None):
67+
self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, self.tr('Input layer')))
68+
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Extent'), type=QgsProcessing.TypeVectorPolygon))
69+
70+
def name(self):
71+
return 'polygonfromrasterextent'
72+
73+
def displayName(self):
74+
return self.tr('Polygon from raster extent')
75+
76+
def processAlgorithm(self, parameters, context, feedback):
77+
raster = self.parameterAsRasterLayer(parameters, self.INPUT, context)
78+
79+
fields = QgsFields()
80+
fields.append(QgsField('MINX', QVariant.Double))
81+
fields.append(QgsField('MINY', QVariant.Double))
82+
fields.append(QgsField('MAXX', QVariant.Double))
83+
fields.append(QgsField('MAXY', QVariant.Double))
84+
fields.append(QgsField('CNTX', QVariant.Double))
85+
fields.append(QgsField('CNTY', QVariant.Double))
86+
fields.append(QgsField('AREA', QVariant.Double))
87+
fields.append(QgsField('PERIM', QVariant.Double))
88+
fields.append(QgsField('HEIGHT', QVariant.Double))
89+
fields.append(QgsField('WIDTH', QVariant.Double))
90+
91+
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
92+
fields, QgsWkbTypes.Polygon, raster.crs())
93+
94+
self.layerExtent(raster, sink, feedback)
95+
96+
return {self.OUTPUT: dest_id}
97+
98+
def layerExtent(self, raster, sink, feedback):
99+
rect = raster.extent()
100+
geometry = QgsGeometry.fromRect(rect)
101+
minx = rect.xMinimum()
102+
miny = rect.yMinimum()
103+
maxx = rect.xMaximum()
104+
maxy = rect.yMaximum()
105+
height = rect.height()
106+
width = rect.width()
107+
cntx = minx + width / 2.0
108+
cnty = miny + height / 2.0
109+
area = width * height
110+
perim = 2 * width + 2 * height
111+
112+
feat = QgsFeature()
113+
feat.setGeometry(geometry)
114+
attrs = [
115+
minx,
116+
miny,
117+
maxx,
118+
maxy,
119+
cntx,
120+
cnty,
121+
area,
122+
perim,
123+
height,
124+
width,
125+
]
126+
feat.setAttributes(attrs)
127+
sink.addFeature(feat, QgsFeatureSink.FastInsert)

‎python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
from .ExportGeometryInfo import ExportGeometryInfo
7171
from .ExtendLines import ExtendLines
7272
from .ExtentFromLayer import ExtentFromLayer
73+
from .ExtentFromRasterLayer import ExtentFromRasterLayer
7374
from .ExtractNodes import ExtractNodes
7475
from .ExtractSpecificNodes import ExtractSpecificNodes
7576
from .FieldPyculator import FieldsPyculator
@@ -222,6 +223,7 @@ def getAlgs(self):
222223
ExportGeometryInfo(),
223224
ExtendLines(),
224225
ExtentFromLayer(),
226+
ExtentFromRasterLayer(),
225227
ExtractNodes(),
226228
ExtractSpecificNodes(),
227229
FieldsCalculator(),
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<GMLFeatureClassList>
2+
<GMLFeatureClass>
3+
<Name>raster_extent</Name>
4+
<ElementPath>raster_extent</ElementPath>
5+
<!--POLYGON-->
6+
<GeometryType>3</GeometryType>
7+
<SRSName>EPSG:4326</SRSName>
8+
<DatasetSpecificInfo>
9+
<FeatureCount>1</FeatureCount>
10+
<ExtentXMin>18.66630</ExtentXMin>
11+
<ExtentXMax>18.70360</ExtentXMax>
12+
<ExtentYMin>45.77670</ExtentYMin>
13+
<ExtentYMax>45.81170</ExtentYMax>
14+
</DatasetSpecificInfo>
15+
<PropertyDefn>
16+
<Name>MINX</Name>
17+
<ElementPath>MINX</ElementPath>
18+
<Type>Real</Type>
19+
</PropertyDefn>
20+
<PropertyDefn>
21+
<Name>MINY</Name>
22+
<ElementPath>MINY</ElementPath>
23+
<Type>Real</Type>
24+
</PropertyDefn>
25+
<PropertyDefn>
26+
<Name>MAXX</Name>
27+
<ElementPath>MAXX</ElementPath>
28+
<Type>Real</Type>
29+
</PropertyDefn>
30+
<PropertyDefn>
31+
<Name>MAXY</Name>
32+
<ElementPath>MAXY</ElementPath>
33+
<Type>Real</Type>
34+
</PropertyDefn>
35+
<PropertyDefn>
36+
<Name>CNTX</Name>
37+
<ElementPath>CNTX</ElementPath>
38+
<Type>Real</Type>
39+
</PropertyDefn>
40+
<PropertyDefn>
41+
<Name>CNTY</Name>
42+
<ElementPath>CNTY</ElementPath>
43+
<Type>Real</Type>
44+
</PropertyDefn>
45+
<PropertyDefn>
46+
<Name>AREA</Name>
47+
<ElementPath>AREA</ElementPath>
48+
<Type>Real</Type>
49+
</PropertyDefn>
50+
<PropertyDefn>
51+
<Name>PERIM</Name>
52+
<ElementPath>PERIM</ElementPath>
53+
<Type>Real</Type>
54+
</PropertyDefn>
55+
<PropertyDefn>
56+
<Name>HEIGHT</Name>
57+
<ElementPath>HEIGHT</ElementPath>
58+
<Type>Real</Type>
59+
</PropertyDefn>
60+
<PropertyDefn>
61+
<Name>WIDTH</Name>
62+
<ElementPath>WIDTH</ElementPath>
63+
<Type>Real</Type>
64+
</PropertyDefn>
65+
</GMLFeatureClass>
66+
</GMLFeatureClassList>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ogr:FeatureCollection
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation=""
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy>
8+
<gml:Box>
9+
<gml:coord><gml:X>18.6662979442</gml:X><gml:Y>45.7767014376</gml:Y></gml:coord>
10+
<gml:coord><gml:X>18.7035979442</gml:X><gml:Y>45.8117014376</gml:Y></gml:coord>
11+
</gml:Box>
12+
</gml:boundedBy>
13+
14+
<gml:featureMember>
15+
<ogr:raster_extent fid="raster_extent.0">
16+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>18.6662979442,45.7767014376 18.7035979442,45.7767014376 18.7035979442,45.8117014376 18.6662979442,45.8117014376 18.6662979442,45.7767014376</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
17+
<ogr:MINX>18.6662979442</ogr:MINX>
18+
<ogr:MINY>45.7767014376</ogr:MINY>
19+
<ogr:MAXX>18.7035979442</ogr:MAXX>
20+
<ogr:MAXY>45.8117014376</ogr:MAXY>
21+
<ogr:CNTX>18.6849479442</ogr:CNTX>
22+
<ogr:CNTY>45.7942014376</ogr:CNTY>
23+
<ogr:AREA>0.00130549999999981</ogr:AREA>
24+
<ogr:PERIM>0.14459999999999</ogr:PERIM>
25+
<ogr:HEIGHT>0.0349999999999966</ogr:HEIGHT>
26+
<ogr:WIDTH>0.0372999999999983</ogr:WIDTH>
27+
</ogr:raster_extent>
28+
</gml:featureMember>
29+
</ogr:FeatureCollection>

‎python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3228,3 +3228,14 @@ tests:
32283228
OUTPUT:
32293229
name: expected/execute_sql.gml
32303230
type: vector
3231+
3232+
- algorithm: qgis:polygonfromrasterextent
3233+
name: Polygon from raster extent
3234+
params:
3235+
INPUT:
3236+
name: dem.tif
3237+
type: raster
3238+
results:
3239+
OUTPUT:
3240+
name: expected\raster_extent.gml
3241+
type: vector

0 commit comments

Comments
 (0)
Please sign in to comment.