Skip to content

Commit

Permalink
[processing] port raster layer statistics
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbruy committed Aug 1, 2017
1 parent fa0bb2e commit 1f276a1
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 77 deletions.
4 changes: 1 addition & 3 deletions python/plugins/processing/algs/help/qgis.yaml
Expand Up @@ -467,9 +467,7 @@ qgis:rasterlayerhistogram: >
The raster layer must have a single band.

qgis:rasterlayerstatistics: >
This algorithm computes basic statistics from the values in a raster layer.

The raster layer must have a single band.
This algorithm computes basic statistics from the values in a given band of the raster layer.

qgis:refactorfields: >
This algorithm allows editing the structure of the attributes table of a vector layer. Fields can be modified in their type and name, using a fields mapping.
Expand Down
3 changes: 2 additions & 1 deletion python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
Expand Up @@ -99,6 +99,7 @@
from .RandomPointsExtent import RandomPointsExtent
from .RandomPointsLayer import RandomPointsLayer
from .RandomPointsPolygons import RandomPointsPolygons
from .RasterLayerStatistics import RasterLayerStatistics
from .RegularPoints import RegularPoints
from .ReverseLineDirection import ReverseLineDirection
from .Ruggedness import Ruggedness
Expand Down Expand Up @@ -144,7 +145,6 @@
# from .HubDistanceLines import HubDistanceLines
# from .HubLines import HubLines
# from .GeometryConvert import GeometryConvert
# from .RasterLayerStatistics import RasterLayerStatistics
# from .StatisticsByCategories import StatisticsByCategories
# from .FieldsCalculator import FieldsCalculator
# from .FieldPyculator import FieldsPyculator
Expand Down Expand Up @@ -270,6 +270,7 @@ def getAlgs(self):
RandomPointsExtent(),
RandomPointsLayer(),
RandomPointsPolygons(),
RasterLayerStatistics(),
RegularPoints(),
ReverseLineDirection(),
Ruggedness(),
Expand Down
131 changes: 58 additions & 73 deletions python/plugins/processing/algs/qgis/RasterLayerStatistics.py
Expand Up @@ -16,7 +16,6 @@
* *
***************************************************************************
"""
from builtins import str

__author__ = 'Victor Olaya'
__date__ = 'January 2013'
Expand All @@ -26,30 +25,31 @@

__revision__ = '$Format:%H$'

import math
import codecs

from qgis.core import (QgsApplication,
QgsProcessingUtils)
from qgis.core import (QgsRectangle,
QgsRasterBandStats,
QgsProcessingParameterRasterLayer,
QgsProcessingParameterNumber,
QgsProcessingParameterFileDestination,
QgsProcessingOutputHtml,
QgsProcessingOutputNumber)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import ParameterRaster
from processing.core.outputs import OutputNumber
from processing.core.outputs import OutputHTML
from processing.tools import raster


class RasterLayerStatistics(QgisAlgorithm):

INPUT = 'INPUT'
BAND = 'BAND'
OUTPUT_HTML_FILE = 'OUTPUT_HTML_FILE'

MIN = 'MIN'
MAX = 'MAX'
RANGE = 'RANGE'
SUM = 'SUM'
MEAN = 'MEAN'
COUNT = 'COUNT'
NO_DATA_COUNT = 'NO_DATA_COUNT'
STD_DEV = 'STD_DEV'
OUTPUT_HTML_FILE = 'OUTPUT_HTML_FILE'
SUM_OF_SQUARES = 'SUM_OF_SQUARES'

def group(self):
return self.tr('Raster tools')
Expand All @@ -58,16 +58,22 @@ def __init__(self):
super().__init__()

def initAlgorithm(self, config=None):
self.addParameter(ParameterRaster(self.INPUT, self.tr('Input layer')))

self.addOutput(OutputHTML(self.OUTPUT_HTML_FILE, self.tr('Statistics')))
self.addOutput(OutputNumber(self.MIN, self.tr('Minimum value')))
self.addOutput(OutputNumber(self.MAX, self.tr('Maximum value')))
self.addOutput(OutputNumber(self.SUM, self.tr('Sum')))
self.addOutput(OutputNumber(self.MEAN, self.tr('Mean value')))
self.addOutput(OutputNumber(self.COUNT, self.tr('valid cells count')))
self.addOutput(OutputNumber(self.COUNT, self.tr('No-data cells count')))
self.addOutput(OutputNumber(self.STD_DEV, self.tr('Standard deviation')))
self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT,
self.tr('Input layer')))
self.addParameter(QgsProcessingParameterNumber(self.BAND,
self.tr('Band number'),
QgsProcessingParameterNumber.Integer,
1, False, 1, 999))
self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT_HTML_FILE, self.tr('Statistics'), self.tr('HTML files (*.html)'), None, True))
self.addOutput(QgsProcessingOutputHtml(self.OUTPUT_HTML_FILE, self.tr('Statistics')))

self.addOutput(QgsProcessingOutputNumber(self.MIN, self.tr('Minimum value')))
self.addOutput(QgsProcessingOutputNumber(self.MAX, self.tr('Maximum value')))
self.addOutput(QgsProcessingOutputNumber(self.RANGE, self.tr('Range')))
self.addOutput(QgsProcessingOutputNumber(self.SUM, self.tr('Sum')))
self.addOutput(QgsProcessingOutputNumber(self.MEAN, self.tr('Mean value')))
self.addOutput(QgsProcessingOutputNumber(self.STD_DEV, self.tr('Standard deviation')))
self.addOutput(QgsProcessingOutputNumber(self.SUM_OF_SQUARES, self.tr('Sum of the squares')))

def name(self):
return 'rasterlayerstatistics'
Expand All @@ -76,62 +82,41 @@ def displayName(self):
return self.tr('Raster layer statistics')

def processAlgorithm(self, parameters, context, feedback):
outputFile = self.getOutputValue(self.OUTPUT_HTML_FILE)
uri = self.getParameterValue(self.INPUT)
layer = QgsProcessingUtils.mapLayerFromString(uri, context)
values = raster.scanraster(layer, feedback)

n = 0
nodata = 0
mean = 0
M2 = 0
sum = 0
minvalue = None
maxvalue = None

for v in values:
if v is not None:
sum += v
n = n + 1
delta = v - mean
mean = mean + delta / n
M2 = M2 + delta * (v - mean)
if minvalue is None:
minvalue = v
maxvalue = v
else:
minvalue = min(v, minvalue)
maxvalue = max(v, maxvalue)
else:
nodata += 1

variance = M2 / (n - 1)
stddev = math.sqrt(variance)
layer = self.parameterAsRasterLayer(parameters, self.INPUT, context)
band = self.parameterAsInt(parameters, self.BAND, context)
outputFile = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context)

stat = layer.dataProvider().bandStatistics(band, QgsRasterBandStats.All, QgsRectangle(), 0)

data = []
data.append('Valid cells: ' + str(n))
data.append('No-data cells: ' + str(nodata))
data.append('Minimum value: ' + str(minvalue))
data.append('Maximum value: ' + str(maxvalue))
data.append('Sum: ' + str(sum))
data.append('Mean value: ' + str(mean))
data.append('Standard deviation: ' + str(stddev))

self.createHTML(outputFile, data)

self.setOutputValue(self.COUNT, n)
self.setOutputValue(self.NO_DATA_COUNT, nodata)
self.setOutputValue(self.MIN, minvalue)
self.setOutputValue(self.MAX, maxvalue)
self.setOutputValue(self.SUM, sum)
self.setOutputValue(self.MEAN, mean)
self.setOutputValue(self.STD_DEV, stddev)
data.append(self.tr('Analyzed file: {} (band {})').format(layer.source(), band))
data.append(self.tr('Minimum value: {}').format(stat.minimumValue))
data.append(self.tr('Maximum value: {}').format(stat.maximumValue))
data.append(self.tr('Range: {}').format(stat.range))
data.append(self.tr('Sum: {}').format(stat.sum))
data.append(self.tr('Mean value: {}').format(stat.mean))
data.append(self.tr('Standard deviation: {}').format(stat.stdDev))
data.append(self.tr('Sum of the squares: {}').format(stat.sumOfSquares))

results = {self.MIN: stat.minimumValue,
self.MAX: stat.maximumValue,
self.RANGE: stat.range,
self.SUM: stat.sum,
self.MEAN: stat.mean,
self.STD_DEV: stat.stdDev,
self.SUM_OF_SQUARES: stat.sumOfSquares}

if outputFile:
self.createHTML(outputFile, data)
results[self.OUTPUT_HTML_FILE] = outputFile

return results

def createHTML(self, outputFile, algData):
with codecs.open(outputFile, 'w', encoding='utf-8') as f:
f.write('<html><head>')
f.write('<html><head>\n')
f.write('<meta http-equiv="Content-Type" content="text/html; \
charset=utf-8" /></head><body>')
charset=utf-8" /></head><body>\n')
for s in algData:
f.write('<p>' + str(s) + '</p>')
f.write('</body></html>')
f.write('<p>' + str(s) + '</p>\n')
f.write('</body></html>\n')
@@ -0,0 +1,11 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body>
<p>Analyzed file: /home/alex/devel/qgis/python/plugins/processing/tests/testdata/dem.tif (band 1)</p>
<p>Minimum value: 85.0</p>
<p>Maximum value: 243.0</p>
<p>Range: 158.0</p>
<p>Sum: 19213301.982429504</p>
<p>Mean value: 147.17197994967066</p>
<p>Standard deviation: 43.9618116337985</p>
<p>Sum of the squares: 252304334.52061242</p>
</body></html>
20 changes: 20 additions & 0 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Expand Up @@ -2758,3 +2758,23 @@ tests:
OUTPUT:
hash: f09384c64f56286ec4146a7b9a679cea7c6711ec4c7d77eec054e364
type: rasterhash

- algorithm: qgis:rasterlayerstatistics
name: Raster layer statistics
params:
INPUT:
name: dem.tif
type: raster
BAND: 1
results:
OUTPUT_HTML_FILE:
name: raster_statistics.html
type: regex
rules:
- 'Minimum value: 85.0'
- 'Maximum value: 243.0'
- 'Range: 158.0'
- 'Sum: 19213301.982429504'
- 'Mean value: 147.17197994967066'
- 'Standard deviation: 43.9618116337985'
- 'Sum of the squares: 252304334.52061242'

0 comments on commit 1f276a1

Please sign in to comment.