Skip to content

Commit c373c8b

Browse files
authoredAug 1, 2017
Merge pull request #4948 from alexbruy/processing-raster
[processing] restore and improve some raster algorithms
2 parents 0328b7a + 79adad7 commit c373c8b

18 files changed

+224
-215
lines changed
 

‎python/core/raster/qgsrasterfilewriter.sip

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,15 @@ class QgsRasterFileWriter
156156
:rtype: list of str
157157
%End
158158

159+
static QString driverForExtension( const QString &extension );
160+
%Docstring
161+
Returns the GDAL driver name for a specified file ``extension``. E.g. the
162+
driver name for the ".tif" extension is "GTiff".
163+
If no suitable drivers are found then an empty string is returned.
164+
.. versionadded:: 3.0
165+
:rtype: str
166+
%End
167+
159168
};
160169

161170
/************************************************************************

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -467,9 +467,7 @@ qgis:rasterlayerhistogram: >
467467
The raster layer must have a single band.
468468

469469
qgis:rasterlayerstatistics: >
470-
This algorithm computes basic statistics from the values in a raster layer.
471-
472-
The raster layer must have a single band.
470+
This algorithm computes basic statistics from the values in a given band of the raster layer.
473471

474472
qgis:refactorfields: >
475473
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.

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030
from qgis.PyQt.QtGui import QIcon
3131

3232
from qgis.analysis import QgsAspectFilter
33-
from qgis.core import (QgsProcessingParameterRasterLayer,
33+
from qgis.core import (QgsRasterFileWriter,
34+
QgsProcessingParameterRasterLayer,
3435
QgsProcessingParameterNumber,
3536
QgsProcessingParameterRasterDestination)
3637
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
37-
from processing.tools import raster
3838
from processing.tools.dataobjects import exportRasterLayer
3939

4040
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
@@ -75,7 +75,7 @@ def processAlgorithm(self, parameters, context, feedback):
7575

7676
outputFile = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)
7777

78-
outputFormat = raster.formatShortNameFromFileName(outputFile)
78+
outputFormat = QgsRasterFileWriter.driverForExtension(os.path.splitext(outputFile)[1])
7979

8080
aspect = QgsAspectFilter(inputFile, outputFile, outputFormat)
8181
aspect.setZFactor(zFactor)

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

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,27 @@
2525

2626
__revision__ = '$Format:%H$'
2727

28-
from osgeo import gdal
29-
30-
from qgis.core import (QgsProcessingParameterRasterLayer,
28+
import os
29+
import math
30+
import struct
31+
32+
from qgis.core import (Qgis,
33+
QgsRasterBlock,
34+
QgsRasterFileWriter,
35+
QgsProcessingParameterExtent,
3136
QgsProcessingParameterNumber,
37+
QgsProcessingParameterCrs,
3238
QgsProcessingParameterRasterDestination)
3339
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
34-
from processing.tools.raster import RasterWriter
3540

3641

3742
class CreateConstantRaster(QgisAlgorithm):
3843

39-
INPUT = 'INPUT'
40-
OUTPUT = 'OUTPUT'
44+
EXTENT = 'EXTENT'
45+
TARGET_CRS = 'TARGET_CRS'
46+
PIXEL_SIZE = 'PIXEL_SIZE'
4147
NUMBER = 'NUMBER'
48+
OUTPUT = 'OUTPUT'
4249

4350
def group(self):
4451
return self.tr('Raster tools')
@@ -47,10 +54,18 @@ def __init__(self):
4754
super().__init__()
4855

4956
def initAlgorithm(self, config=None):
50-
self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT,
51-
self.tr('Reference layer')))
57+
self.addParameter(QgsProcessingParameterExtent(self.EXTENT,
58+
self.tr('Desired extent')))
59+
self.addParameter(QgsProcessingParameterCrs(self.TARGET_CRS,
60+
self.tr('Target CRS'),
61+
'ProjectCrs'))
62+
self.addParameter(QgsProcessingParameterNumber(self.PIXEL_SIZE,
63+
self.tr('Pixel size'),
64+
QgsProcessingParameterNumber.Double,
65+
0.1, False, 0.01, 999))
5266
self.addParameter(QgsProcessingParameterNumber(self.NUMBER,
53-
self.tr('Constant value'), QgsProcessingParameterNumber.Double,
67+
self.tr('Constant value'),
68+
QgsProcessingParameterNumber.Double,
5469
defaultValue=1))
5570
self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Constant')))
5671

@@ -61,28 +76,35 @@ def displayName(self):
6176
return self.tr('Create constant raster layer')
6277

6378
def processAlgorithm(self, parameters, context, feedback):
64-
layer = self.parameterAsRasterLayer(parameters, self.INPUT, context)
79+
extent = self.parameterAsExtent(parameters, self.EXTENT, context)
80+
crs = self.parameterAsCrs(parameters, self.TARGET_CRS, context)
6581
value = self.parameterAsDouble(parameters, self.NUMBER, context)
82+
pixelSize = self.parameterAsDouble(parameters, self.PIXEL_SIZE, context)
6683

6784
outputFile = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)
85+
outputFormat = QgsRasterFileWriter.driverForExtension(os.path.splitext(outputFile)[1])
86+
87+
rows = max([math.ceil(extent.height() / pixelSize) + 1, 1.0])
88+
cols = max([math.ceil(extent.width() / pixelSize) + 1, 1.0])
89+
90+
writer = QgsRasterFileWriter(outputFile)
91+
writer.setOutputProviderKey('gdal')
92+
writer.setOutputFormat(outputFormat)
93+
provider = writer.createOneBandRaster(Qgis.Float32, cols, rows, extent, crs)
94+
provider.setNoDataValue(1, -9999)
95+
96+
data = [value] * cols
97+
block = QgsRasterBlock(Qgis.Float32, cols, 1)
98+
block.setData(struct.pack('{}f'.format(len(data)), *data))
99+
100+
total = 100.0 / rows if rows else 0
101+
for i in range(rows):
102+
if feedback.isCanceled():
103+
break
104+
105+
provider.writeBlock(block, 1, 0, i)
106+
feedback.setProgress(int(i * rows))
68107

69-
raster = gdal.Open(layer.source(), gdal.GA_ReadOnly)
70-
geoTransform = raster.GetGeoTransform()
71-
72-
cellsize = (layer.extent().xMaximum() - layer.extent().xMinimum()) \
73-
/ layer.width()
74-
75-
w = RasterWriter(outputFile,
76-
layer.extent().xMinimum(),
77-
layer.extent().yMinimum(),
78-
layer.extent().xMaximum(),
79-
layer.extent().yMaximum(),
80-
cellsize,
81-
1,
82-
layer.crs(),
83-
geoTransform
84-
)
85-
w.matrix.fill(value)
86-
w.close()
108+
provider.setEditable(False)
87109

88110
return {self.OUTPUT: outputFile}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from qgis.PyQt.QtGui import QIcon
3232

3333
from qgis.core import (QgsFeatureRequest,
34+
QgsRasterFileWriter,
3435
QgsProcessing,
3536
QgsProcessingException,
3637
QgsProcessingParameterFeatureSource,
@@ -43,7 +44,6 @@
4344
from qgis.analysis import QgsKernelDensityEstimation
4445

4546
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
46-
from processing.tools import raster
4747

4848
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
4949

@@ -175,7 +175,7 @@ def processAlgorithm(self, parameters, context, feedback):
175175
decay = self.parameterAsDouble(parameters, self.DECAY, context)
176176
output_values = self.parameterAsEnum(parameters, self.OUTPUT_VALUE, context)
177177
outputFile = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)
178-
output_format = raster.formatShortNameFromFileName(outputFile)
178+
output_format = QgsRasterFileWriter.driverForExtension(os.path.splitext(outputFile)[1])
179179
weight_field = self.parameterAsString(parameters, self.WEIGHT_FIELD, context)
180180
radius_field = self.parameterAsString(parameters, self.RADIUS_FIELD, context)
181181

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030
from qgis.PyQt.QtGui import QIcon
3131

3232
from qgis.analysis import QgsHillshadeFilter
33-
from qgis.core import (QgsProcessingParameterRasterLayer,
33+
from qgis.core import (QgsRasterFileWriter,
34+
QgsProcessingParameterRasterLayer,
3435
QgsProcessingParameterNumber,
3536
QgsProcessingParameterRasterDestination)
3637
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
37-
from processing.tools import raster
3838
from processing.tools.dataobjects import exportRasterLayer
3939

4040
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
@@ -84,8 +84,7 @@ def processAlgorithm(self, parameters, context, feedback):
8484
vAngle = self.parameterAsDouble(parameters, self.V_ANGLE, context)
8585

8686
outputFile = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)
87-
88-
outputFormat = raster.formatShortNameFromFileName(outputFile)
87+
outputFormat = QgsRasterFileWriter.driverForExtension(os.path.splitext(outputFile)[1])
8988

9089
hillshade = QgsHillshadeFilter(inputFile, outputFile, outputFormat, azimuth, vAngle)
9190
hillshade.setZFactor(zFactor)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
from .RandomPointsExtent import RandomPointsExtent
100100
from .RandomPointsLayer import RandomPointsLayer
101101
from .RandomPointsPolygons import RandomPointsPolygons
102+
from .RasterLayerStatistics import RasterLayerStatistics
102103
from .RegularPoints import RegularPoints
103104
from .ReverseLineDirection import ReverseLineDirection
104105
from .Ruggedness import Ruggedness
@@ -144,7 +145,6 @@
144145
# from .HubDistanceLines import HubDistanceLines
145146
# from .HubLines import HubLines
146147
# from .GeometryConvert import GeometryConvert
147-
# from .RasterLayerStatistics import RasterLayerStatistics
148148
# from .StatisticsByCategories import StatisticsByCategories
149149
# from .FieldsCalculator import FieldsCalculator
150150
# from .FieldPyculator import FieldsPyculator
@@ -270,6 +270,7 @@ def getAlgs(self):
270270
RandomPointsExtent(),
271271
RandomPointsLayer(),
272272
RandomPointsPolygons(),
273+
RasterLayerStatistics(),
273274
RegularPoints(),
274275
ReverseLineDirection(),
275276
Ruggedness(),

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

Lines changed: 58 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
* *
1717
***************************************************************************
1818
"""
19-
from builtins import str
2019

2120
__author__ = 'Victor Olaya'
2221
__date__ = 'January 2013'
@@ -26,30 +25,31 @@
2625

2726
__revision__ = '$Format:%H$'
2827

29-
import math
3028
import codecs
3129

32-
from qgis.core import (QgsApplication,
33-
QgsProcessingUtils)
30+
from qgis.core import (QgsRectangle,
31+
QgsRasterBandStats,
32+
QgsProcessingParameterRasterLayer,
33+
QgsProcessingParameterNumber,
34+
QgsProcessingParameterFileDestination,
35+
QgsProcessingOutputHtml,
36+
QgsProcessingOutputNumber)
3437
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
35-
from processing.core.parameters import ParameterRaster
36-
from processing.core.outputs import OutputNumber
37-
from processing.core.outputs import OutputHTML
38-
from processing.tools import raster
3938

4039

4140
class RasterLayerStatistics(QgisAlgorithm):
4241

4342
INPUT = 'INPUT'
43+
BAND = 'BAND'
44+
OUTPUT_HTML_FILE = 'OUTPUT_HTML_FILE'
4445

4546
MIN = 'MIN'
4647
MAX = 'MAX'
48+
RANGE = 'RANGE'
4749
SUM = 'SUM'
4850
MEAN = 'MEAN'
49-
COUNT = 'COUNT'
50-
NO_DATA_COUNT = 'NO_DATA_COUNT'
5151
STD_DEV = 'STD_DEV'
52-
OUTPUT_HTML_FILE = 'OUTPUT_HTML_FILE'
52+
SUM_OF_SQUARES = 'SUM_OF_SQUARES'
5353

5454
def group(self):
5555
return self.tr('Raster tools')
@@ -58,16 +58,22 @@ def __init__(self):
5858
super().__init__()
5959

6060
def initAlgorithm(self, config=None):
61-
self.addParameter(ParameterRaster(self.INPUT, self.tr('Input layer')))
62-
63-
self.addOutput(OutputHTML(self.OUTPUT_HTML_FILE, self.tr('Statistics')))
64-
self.addOutput(OutputNumber(self.MIN, self.tr('Minimum value')))
65-
self.addOutput(OutputNumber(self.MAX, self.tr('Maximum value')))
66-
self.addOutput(OutputNumber(self.SUM, self.tr('Sum')))
67-
self.addOutput(OutputNumber(self.MEAN, self.tr('Mean value')))
68-
self.addOutput(OutputNumber(self.COUNT, self.tr('valid cells count')))
69-
self.addOutput(OutputNumber(self.COUNT, self.tr('No-data cells count')))
70-
self.addOutput(OutputNumber(self.STD_DEV, self.tr('Standard deviation')))
61+
self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT,
62+
self.tr('Input layer')))
63+
self.addParameter(QgsProcessingParameterNumber(self.BAND,
64+
self.tr('Band number'),
65+
QgsProcessingParameterNumber.Integer,
66+
1, False, 1, 999))
67+
self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT_HTML_FILE, self.tr('Statistics'), self.tr('HTML files (*.html)'), None, True))
68+
self.addOutput(QgsProcessingOutputHtml(self.OUTPUT_HTML_FILE, self.tr('Statistics')))
69+
70+
self.addOutput(QgsProcessingOutputNumber(self.MIN, self.tr('Minimum value')))
71+
self.addOutput(QgsProcessingOutputNumber(self.MAX, self.tr('Maximum value')))
72+
self.addOutput(QgsProcessingOutputNumber(self.RANGE, self.tr('Range')))
73+
self.addOutput(QgsProcessingOutputNumber(self.SUM, self.tr('Sum')))
74+
self.addOutput(QgsProcessingOutputNumber(self.MEAN, self.tr('Mean value')))
75+
self.addOutput(QgsProcessingOutputNumber(self.STD_DEV, self.tr('Standard deviation')))
76+
self.addOutput(QgsProcessingOutputNumber(self.SUM_OF_SQUARES, self.tr('Sum of the squares')))
7177

7278
def name(self):
7379
return 'rasterlayerstatistics'
@@ -76,62 +82,41 @@ def displayName(self):
7682
return self.tr('Raster layer statistics')
7783

7884
def processAlgorithm(self, parameters, context, feedback):
79-
outputFile = self.getOutputValue(self.OUTPUT_HTML_FILE)
80-
uri = self.getParameterValue(self.INPUT)
81-
layer = QgsProcessingUtils.mapLayerFromString(uri, context)
82-
values = raster.scanraster(layer, feedback)
83-
84-
n = 0
85-
nodata = 0
86-
mean = 0
87-
M2 = 0
88-
sum = 0
89-
minvalue = None
90-
maxvalue = None
91-
92-
for v in values:
93-
if v is not None:
94-
sum += v
95-
n = n + 1
96-
delta = v - mean
97-
mean = mean + delta / n
98-
M2 = M2 + delta * (v - mean)
99-
if minvalue is None:
100-
minvalue = v
101-
maxvalue = v
102-
else:
103-
minvalue = min(v, minvalue)
104-
maxvalue = max(v, maxvalue)
105-
else:
106-
nodata += 1
107-
108-
variance = M2 / (n - 1)
109-
stddev = math.sqrt(variance)
85+
layer = self.parameterAsRasterLayer(parameters, self.INPUT, context)
86+
band = self.parameterAsInt(parameters, self.BAND, context)
87+
outputFile = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context)
88+
89+
stat = layer.dataProvider().bandStatistics(band, QgsRasterBandStats.All, QgsRectangle(), 0)
11090

11191
data = []
112-
data.append('Valid cells: ' + str(n))
113-
data.append('No-data cells: ' + str(nodata))
114-
data.append('Minimum value: ' + str(minvalue))
115-
data.append('Maximum value: ' + str(maxvalue))
116-
data.append('Sum: ' + str(sum))
117-
data.append('Mean value: ' + str(mean))
118-
data.append('Standard deviation: ' + str(stddev))
119-
120-
self.createHTML(outputFile, data)
121-
122-
self.setOutputValue(self.COUNT, n)
123-
self.setOutputValue(self.NO_DATA_COUNT, nodata)
124-
self.setOutputValue(self.MIN, minvalue)
125-
self.setOutputValue(self.MAX, maxvalue)
126-
self.setOutputValue(self.SUM, sum)
127-
self.setOutputValue(self.MEAN, mean)
128-
self.setOutputValue(self.STD_DEV, stddev)
92+
data.append(self.tr('Analyzed file: {} (band {})').format(layer.source(), band))
93+
data.append(self.tr('Minimum value: {}').format(stat.minimumValue))
94+
data.append(self.tr('Maximum value: {}').format(stat.maximumValue))
95+
data.append(self.tr('Range: {}').format(stat.range))
96+
data.append(self.tr('Sum: {}').format(stat.sum))
97+
data.append(self.tr('Mean value: {}').format(stat.mean))
98+
data.append(self.tr('Standard deviation: {}').format(stat.stdDev))
99+
data.append(self.tr('Sum of the squares: {}').format(stat.sumOfSquares))
100+
101+
results = {self.MIN: stat.minimumValue,
102+
self.MAX: stat.maximumValue,
103+
self.RANGE: stat.range,
104+
self.SUM: stat.sum,
105+
self.MEAN: stat.mean,
106+
self.STD_DEV: stat.stdDev,
107+
self.SUM_OF_SQUARES: stat.sumOfSquares}
108+
109+
if outputFile:
110+
self.createHTML(outputFile, data)
111+
results[self.OUTPUT_HTML_FILE] = outputFile
112+
113+
return results
129114

130115
def createHTML(self, outputFile, algData):
131116
with codecs.open(outputFile, 'w', encoding='utf-8') as f:
132-
f.write('<html><head>')
117+
f.write('<html><head>\n')
133118
f.write('<meta http-equiv="Content-Type" content="text/html; \
134-
charset=utf-8" /></head><body>')
119+
charset=utf-8" /></head><body>\n')
135120
for s in algData:
136-
f.write('<p>' + str(s) + '</p>')
137-
f.write('</body></html>')
121+
f.write('<p>' + str(s) + '</p>\n')
122+
f.write('</body></html>\n')

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030
from qgis.PyQt.QtGui import QIcon
3131

3232
from qgis.analysis import QgsRuggednessFilter
33-
from qgis.core import (QgsProcessingParameterRasterLayer,
33+
from qgis.core import (QgsRasterFileWriter,
34+
QgsProcessingParameterRasterLayer,
3435
QgsProcessingParameterNumber,
3536
QgsProcessingParameterRasterDestination)
3637
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
37-
from processing.tools import raster
3838
from processing.tools.dataobjects import exportRasterLayer
3939

4040
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
@@ -75,8 +75,7 @@ def processAlgorithm(self, parameters, context, feedback):
7575
zFactor = self.parameterAsDouble(parameters, self.Z_FACTOR, context)
7676

7777
outputFile = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)
78-
79-
outputFormat = raster.formatShortNameFromFileName(outputFile)
78+
outputFormat = QgsRasterFileWriter.driverForExtension(os.path.splitext(outputFile)[1])
8079

8180
ruggedness = QgsRuggednessFilter(inputFile, outputFile, outputFormat)
8281
ruggedness.setZFactor(zFactor)

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030
from qgis.PyQt.QtGui import QIcon
3131

3232
from qgis.analysis import QgsSlopeFilter
33-
from qgis.core import (QgsProcessingParameterRasterLayer,
33+
from qgis.core import (QgsRasterFileWriter,
34+
QgsProcessingParameterRasterLayer,
3435
QgsProcessingParameterNumber,
3536
QgsProcessingParameterRasterDestination)
3637
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
37-
from processing.tools import raster
3838
from processing.tools.dataobjects import exportRasterLayer
3939

4040

@@ -75,8 +75,7 @@ def processAlgorithm(self, parameters, context, feedback):
7575
zFactor = self.parameterAsDouble(parameters, self.Z_FACTOR, context)
7676

7777
outputFile = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)
78-
79-
outputFormat = raster.formatShortNameFromFileName(outputFile)
78+
outputFormat = QgsRasterFileWriter.driverForExtension(os.path.splitext(outputFile)[1])
8079

8180
slope = QgsSlopeFilter(inputFile, outputFile, outputFormat)
8281
slope.setZFactor(zFactor)
Binary file not shown.

‎python/plugins/processing/tests/testdata/expected/constant_raster.tif.aux.xml

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<html><head>
2+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body>
3+
<p>Analyzed file: /home/alex/devel/qgis/python/plugins/processing/tests/testdata/dem.tif (band 1)</p>
4+
<p>Minimum value: 85.0</p>
5+
<p>Maximum value: 243.0</p>
6+
<p>Range: 158.0</p>
7+
<p>Sum: 19213301.982429504</p>
8+
<p>Mean value: 147.17197994967066</p>
9+
<p>Standard deviation: 43.9618116337985</p>
10+
<p>Sum of the squares: 252304334.52061242</p>
11+
</body></html>

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

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,16 +1223,15 @@ tests:
12231223
- algorithm: qgis:createconstantrasterlayer
12241224
name: Create constant raster
12251225
params:
1226-
INPUT:
1227-
name: raster.tif
1228-
type: raster
1226+
EXTENT: 270736.0673250682,270899.8544675339,4458899.000550019,4459029.574521748
1227+
TARGET_CRS: EPSG:23030
1228+
PIXEL_SIZE: 10.0
12291229
NUMBER: 3.0
12301230
results:
12311231
OUTPUT:
1232-
hash: 4cb3e82e8512cdbb75d9c39a10adc818dd6842c5dc6361fbc43dd9aa
1232+
hash: e453e9e36ce314d5197963ac27872a0cc3dfe43764ed586a334c66f0
12331233
type: rasterhash
12341234

1235-
12361235
# Case 1: Keep all fields
12371236
- algorithm: qgis:lineintersections
12381237
name: Line Intersection Keep All Fields from Both
@@ -2759,3 +2758,23 @@ tests:
27592758
OUTPUT:
27602759
hash: f09384c64f56286ec4146a7b9a679cea7c6711ec4c7d77eec054e364
27612760
type: rasterhash
2761+
2762+
- algorithm: qgis:rasterlayerstatistics
2763+
name: Raster layer statistics
2764+
params:
2765+
INPUT:
2766+
name: dem.tif
2767+
type: raster
2768+
BAND: 1
2769+
results:
2770+
OUTPUT_HTML_FILE:
2771+
name: raster_statistics.html
2772+
type: regex
2773+
rules:
2774+
- 'Minimum value: 85.0'
2775+
- 'Maximum value: 243.0'
2776+
- 'Range: 158.0'
2777+
- 'Sum: 19213301.982429504'
2778+
- 'Mean value: 147.17197994967066'
2779+
- 'Standard deviation: 43.9618116337985'
2780+
- 'Sum of the squares: 252304334.52061242'

‎python/plugins/processing/tools/raster.py

Lines changed: 0 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -38,39 +38,6 @@
3838
from qgis.core import QgsProcessingException
3939

4040

41-
RASTER_EXTENSION_MAP = None
42-
43-
44-
def initGdalData():
45-
global RASTER_EXTENSION_MAP
46-
47-
if RASTER_EXTENSION_MAP is not None:
48-
return
49-
50-
if gdal.GetDriverCount() == 0:
51-
gdal.AllRegister()
52-
53-
RASTER_EXTENSION_MAP = dict()
54-
for i in range(gdal.GetDriverCount()):
55-
driver = gdal.GetDriver(i)
56-
if driver is None:
57-
continue
58-
md = driver.GetMetadata()
59-
if gdal.DCAP_CREATE in md and md[gdal.DCAP_CREATE].lower() == 'yes':
60-
ext = md[gdal.DMD_EXTENSION] if gdal.DMD_EXTENSION in md else None
61-
if ext is not None and ext != '':
62-
RASTER_EXTENSION_MAP[driver.ShortName] = ext
63-
64-
65-
def formatShortNameFromFileName(fileName):
66-
initGdalData()
67-
ext = os.path.splitext(fileName)[1][1:]
68-
for k, v in RASTER_EXTENSION_MAP.items():
69-
if ext == v:
70-
return k
71-
return 'GTiff'
72-
73-
7441
def scanraster(layer, feedback):
7542
filename = str(layer.source())
7643
dataset = gdal.Open(filename, gdal.GA_ReadOnly)
@@ -111,48 +78,3 @@ def mapToPixel(mX, mY, geoTransform):
11178

11279
def pixelToMap(pX, pY, geoTransform):
11380
return gdal.ApplyGeoTransform(geoTransform, pX + 0.5, pY + 0.5)
114-
115-
116-
class RasterWriter(object):
117-
118-
NODATA = -99999.0
119-
120-
def __init__(self, fileName, minx, miny, maxx, maxy, cellsize,
121-
nbands, crs, geotransform=None):
122-
self.fileName = fileName
123-
self.nx = int((maxx - minx) / float(cellsize))
124-
self.ny = int((maxy - miny) / float(cellsize))
125-
self.nbands = nbands
126-
self.matrix = numpy.empty(shape=(self.ny, self.nx), dtype=numpy.float32)
127-
self.matrix.fill(self.NODATA)
128-
self.cellsize = cellsize
129-
self.crs = crs
130-
self.minx = minx
131-
self.maxy = maxy
132-
self.geotransform = geotransform
133-
134-
def setValue(self, value, x, y, band=0):
135-
try:
136-
self.matrix[y, x] = value
137-
except IndexError:
138-
pass
139-
140-
def getValue(self, x, y, band=0):
141-
try:
142-
return self.matrix[y, x]
143-
except IndexError:
144-
return self.NODATA
145-
146-
def close(self):
147-
fmt = 'GTiff'
148-
driver = gdal.GetDriverByName(fmt)
149-
dst_ds = driver.Create(self.fileName, self.nx, self.ny, 1,
150-
gdal.GDT_Float32)
151-
dst_ds.SetProjection(str(self.crs.toWkt()))
152-
if self.geotransform is None:
153-
dst_ds.SetGeoTransform([self.minx, self.cellsize, 0,
154-
self.maxy, self.cellsize, 0])
155-
else:
156-
dst_ds.SetGeoTransform(self.geotransform)
157-
dst_ds.GetRasterBand(1).WriteArray(self.matrix)
158-
dst_ds = None

‎src/core/raster/qgsrasterfilewriter.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
#include <QTextStream>
3030
#include <QMessageBox>
3131

32+
#include <gdal.h>
33+
#include <cpl_string.h>
34+
3235
QgsRasterDataProvider *QgsRasterFileWriter::createOneBandRaster( Qgis::DataType dataType, int width, int height, const QgsRectangle &extent, const QgsCoordinateReferenceSystem &crs )
3336
{
3437
if ( mTiledMode )
@@ -991,3 +994,37 @@ QString QgsRasterFileWriter::vrtFileName()
991994
QFileInfo outputInfo( mOutputUrl );
992995
return QStringLiteral( "%1.vrt" ).arg( outputInfo.fileName() );
993996
}
997+
998+
QString QgsRasterFileWriter::driverForExtension( const QString &extension )
999+
{
1000+
QString ext = extension.trimmed();
1001+
if ( ext.isEmpty() )
1002+
return QString();
1003+
1004+
if ( ext.startsWith( '.' ) )
1005+
ext.remove( 0, 1 );
1006+
1007+
GDALAllRegister();
1008+
int const drvCount = GDALGetDriverCount();
1009+
1010+
for ( int i = 0; i < drvCount; ++i )
1011+
{
1012+
GDALDriverH drv = GDALGetDriver( i );
1013+
if ( drv )
1014+
{
1015+
char **driverMetadata = GDALGetMetadata( drv, nullptr );
1016+
if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) && CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, false ) )
1017+
{
1018+
QString drvName = GDALGetDriverShortName( drv );
1019+
QStringList driverExtensions = QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) ).split( ' ' );
1020+
1021+
Q_FOREACH ( const QString &driver, driverExtensions )
1022+
{
1023+
if ( driver.compare( ext, Qt::CaseInsensitive ) == 0 )
1024+
return drvName;
1025+
}
1026+
}
1027+
}
1028+
}
1029+
return QString();
1030+
}

‎src/core/raster/qgsrasterfilewriter.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,14 @@ class CORE_EXPORT QgsRasterFileWriter
130130
void setPyramidsConfigOptions( const QStringList &list ) { mPyramidsConfigOptions = list; }
131131
QStringList pyramidsConfigOptions() const { return mPyramidsConfigOptions; }
132132

133+
/**
134+
* Returns the GDAL driver name for a specified file \a extension. E.g. the
135+
* driver name for the ".tif" extension is "GTiff".
136+
* If no suitable drivers are found then an empty string is returned.
137+
* \since QGIS 3.0
138+
*/
139+
static QString driverForExtension( const QString &extension );
140+
133141
private:
134142
QgsRasterFileWriter(); //forbidden
135143
WriterError writeDataRaster( const QgsRasterPipe *pipe, QgsRasterIterator *iter, int nCols, int nRows, const QgsRectangle &outputExtent,

‎tests/src/python/test_qgsrasterfilewriter.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ def testWrite(self):
9797

9898
assert allOk, "Raster file writer test failed"
9999

100+
def testDriverForExtension(self):
101+
self.assertEqual(QgsRasterFileWriter.driverForExtension('tif'), 'GTiff')
102+
self.assertEqual(QgsRasterFileWriter.driverForExtension('TIF'), 'GTiff')
103+
self.assertEqual(QgsRasterFileWriter.driverForExtension('tIf'), 'GTiff')
104+
self.assertEqual(QgsRasterFileWriter.driverForExtension('.tif'), 'GTiff')
105+
self.assertEqual(QgsRasterFileWriter.driverForExtension('img'), 'HFA')
106+
self.assertEqual(QgsRasterFileWriter.driverForExtension('.vrt'), 'VRT')
107+
self.assertEqual(QgsRasterFileWriter.driverForExtension('not a format'), '')
108+
self.assertEqual(QgsRasterFileWriter.driverForExtension(''), '')
109+
100110

101111
if __name__ == '__main__':
102112
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.