Navigation Menu

Skip to content

Commit

Permalink
Merge pull request #5090 from nyalldawson/raster_layer_extent
Browse files Browse the repository at this point in the history
[FEATURE] New algorithm for creating vector layer from raster layer's extent
  • Loading branch information
nyalldawson committed Aug 30, 2017
2 parents 6fe6394 + bb5a499 commit 9fa12ac
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 7 deletions.
4 changes: 3 additions & 1 deletion python/plugins/processing/algs/help/qgis.yaml
Expand Up @@ -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.

Expand Down
8 changes: 4 additions & 4 deletions python/plugins/processing/algs/qgis/ExtentFromLayer.py
Expand Up @@ -49,7 +49,7 @@

class ExtentFromLayer(QgisAlgorithm):

INPUT_LAYER = 'INPUT_LAYER'
INPUT = 'INPUT'
BY_FEATURE = 'BY_FEATURE'

OUTPUT = 'OUTPUT'
Expand All @@ -67,7 +67,7 @@ def __init__(self):
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))

Expand All @@ -77,10 +77,10 @@ def name(self):
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()
Expand Down
127 changes: 127 additions & 0 deletions 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)
2 changes: 2 additions & 0 deletions python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
Expand Up @@ -70,6 +70,7 @@
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
Expand Down Expand Up @@ -222,6 +223,7 @@ def getAlgs(self):
ExportGeometryInfo(),
ExtendLines(),
ExtentFromLayer(),
ExtentFromRasterLayer(),
ExtractNodes(),
ExtractSpecificNodes(),
FieldsCalculator(),
Expand Down
4 changes: 3 additions & 1 deletion python/plugins/processing/gui/TestTools.py
Expand Up @@ -27,6 +27,7 @@
__revision__ = '$Format:%H$'

import os
import posixpath
import re
import yaml
import hashlib
Expand Down Expand Up @@ -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)

Expand Down
@@ -0,0 +1,66 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>raster_extent</Name>
<ElementPath>raster_extent</ElementPath>
<!--POLYGON-->
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>1</FeatureCount>
<ExtentXMin>18.66630</ExtentXMin>
<ExtentXMax>18.70360</ExtentXMax>
<ExtentYMin>45.77670</ExtentYMin>
<ExtentYMax>45.81170</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>MINX</Name>
<ElementPath>MINX</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>MINY</Name>
<ElementPath>MINY</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>MAXX</Name>
<ElementPath>MAXX</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>MAXY</Name>
<ElementPath>MAXY</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>CNTX</Name>
<ElementPath>CNTX</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>CNTY</Name>
<ElementPath>CNTY</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>AREA</Name>
<ElementPath>AREA</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>PERIM</Name>
<ElementPath>PERIM</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>HEIGHT</Name>
<ElementPath>HEIGHT</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>WIDTH</Name>
<ElementPath>WIDTH</ElementPath>
<Type>Real</Type>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=""
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>18.6662979442</gml:X><gml:Y>45.7767014376</gml:Y></gml:coord>
<gml:coord><gml:X>18.7035979442</gml:X><gml:Y>45.8117014376</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:raster_extent fid="raster_extent.0">
<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>
<ogr:MINX>18.6662979442</ogr:MINX>
<ogr:MINY>45.7767014376</ogr:MINY>
<ogr:MAXX>18.7035979442</ogr:MAXX>
<ogr:MAXY>45.8117014376</ogr:MAXY>
<ogr:CNTX>18.6849479442</ogr:CNTX>
<ogr:CNTY>45.7942014376</ogr:CNTY>
<ogr:AREA>0.00130549999999981</ogr:AREA>
<ogr:PERIM>0.14459999999999</ogr:PERIM>
<ogr:HEIGHT>0.0349999999999966</ogr:HEIGHT>
<ogr:WIDTH>0.0372999999999983</ogr:WIDTH>
</ogr:raster_extent>
</gml:featureMember>
</ogr:FeatureCollection>
Expand Up @@ -2845,7 +2845,7 @@ tests:
name: Standard polygon from layer extent
params:
BY_FEATURE: false
INPUT_LAYER:
INPUT:
name: polys.gml
type: vector
results:
Expand Down Expand Up @@ -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

0 comments on commit 9fa12ac

Please sign in to comment.