2
#******************************************************************************
5
# Purpose: Command line raster calculator for gdal supported files
6
# Author: Chris Yesson, chris.yesson@ioz.ac.uk
8
#******************************************************************************
9
# Copyright (c) 2010, Chris Yesson <chris.yesson@ioz.ac.uk>
11
# Permission is hereby granted, free of charge, to any person obtaining a
12
# copy of this software and associated documentation files (the "Software"),
13
# to deal in the Software without restriction, including without limitation
14
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
# and/or sell copies of the Software, and to permit persons to whom the
16
# Software is furnished to do so, subject to the following conditions:
18
# The above copyright notice and this permission notice shall be included
19
# in all copies or substantial portions of the Software.
21
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
# DEALINGS IN THE SOFTWARE.
28
#******************************************************************************
30
################################################################
31
# Command line raster calculator for gdal supported files. Use any basic arithmetic supported by numpy arrays such as +-*\ along with logical operators such as >. Note that all files must be the same dimensions, but no projection checking is performed. Use gdal_calc.py --help for list of options.
33
# example 1 - add two files together
34
# gdal_calc.py -A input1.tif -B input2.tif --outfile=result.tif --calc="A+B"
36
# example 2 - average of two layers
37
# gdal_calc.py -A input.tif -B input2.tif --outfile=result.tif --calc="(A+B)/2"
39
# example 3 - set values of zero and below to null
40
# gdal_calc.py -A input.tif --outfile=result.tif --calc="A*(A>0)" --NoDataValue=0
41
################################################################
44
from osgeo import gdal
45
from osgeo.gdalnumeric import *
48
from gdalnumeric import *
50
from optparse import OptionParser
54
# create alphabetic list for storing input layers
55
AlphaList=["A","B","C","D","E","F","G","H","I","J","K","L","M",
56
"N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
58
# set up some default nodatavalues for each datatype
59
DefaultNDVLookup={'Byte':255, 'UInt16':65535, 'Int16':-32767, 'UInt32':4294967293, 'Int32':-2147483647, 'Float32':1.175494351E-38, 'Float64':1.7976931348623158E+308}
63
################################################################
67
print("gdal_calc.py starting calculation %s" %(opts.calc))
69
################################################################
70
# fetch details of input layers
71
################################################################
73
# set up some lists to store data for each band
82
# loop through input files - checking dimensions
83
for i,myI in enumerate(AlphaList[0:len(sys.argv)-1]):
84
myF = eval("opts.%s" %(myI))
85
myBand = eval("opts.%s_band" %(myI))
87
myFiles.append(gdal.Open(myF, gdal.GA_ReadOnly))
88
# check if we have asked for a specific band...
90
myBands.append(myBand)
93
myAlphaList.append(myI)
94
myDataType.append(gdal.GetDataTypeName(myFiles[i].GetRasterBand(myBands[i]).DataType))
95
myDataTypeNum.append(myFiles[i].GetRasterBand(myBands[i]).DataType)
96
myNDV.append(myFiles[i].GetRasterBand(myBands[i]).GetNoDataValue())
97
# check that the dimensions of each layer are the same
99
if DimensionsCheck!=[myFiles[i].RasterXSize, myFiles[i].RasterYSize]:
100
print("Error! Dimensions of file %s (%i, %i) are different from other files (%i, %i). Cannot proceed" % \
101
(myF,myFiles[i].RasterXSize, myFiles[i].RasterYSize,DimensionsCheck[0],DimensionsCheck[1]))
104
DimensionsCheck=[myFiles[i].RasterXSize, myFiles[i].RasterYSize]
107
print("file %s: %s, dimensions: %s, %s, type: %s" %(myI,myF,DimensionsCheck[0],DimensionsCheck[1],myDataType[i]))
109
################################################################
111
################################################################
113
# open output file exists
114
if os.path.isfile(opts.outF) and not opts.overwrite:
116
print("Output file %s exists - filling in results into file" %(opts.outF))
117
myOut=gdal.Open(opts.outF, gdal.GA_Update)
118
if [myOut.RasterXSize,myOut.RasterYSize] != DimensionsCheck:
119
print("Error! Output exists, but is the wrong size. Use the --overwrite option to automatically overwrite the existing file")
121
myOutB=myOut.GetRasterBand(1)
122
myOutNDV=myOutB.GetNoDataValue()
123
myOutType=gdal.GetDataTypeName(myOutB.DataType)
126
# remove existing file and regenerate
127
if os.path.isfile(opts.outF):
131
print("Generating output file %s" %(opts.outF))
133
# find data type to use
135
# use the largest type of the input files
136
myOutType=gdal.GetDataTypeName(max(myDataTypeNum))
141
myOutDrv = gdal.GetDriverByName(opts.format)
142
myOut=myOutDrv.Create(opts.outF, DimensionsCheck[0], DimensionsCheck[1], 1, gdal.GetDataTypeByName(myOutType))
144
# set output geo info based on first input layer
145
myOut.SetGeoTransform(myFiles[0].GetGeoTransform())
146
myOut.SetProjection(myFiles[0].GetProjection())
148
myOutB=myOut.GetRasterBand(1)
149
if opts.NoDataValue!=None:
150
myOutNDV=opts.NoDataValue
152
myOutNDV=DefaultNDVLookup[myOutType]
153
myOutB.SetNoDataValue(myOutNDV)
157
myOutB=myOut.GetRasterBand(1)
160
print("output file: %s, dimensions: %s, %s, type: %s" %(opts.outF,myOut.RasterXSize,myOut.RasterYSize,gdal.GetDataTypeName(myOutB.DataType)))
162
################################################################
163
# find block size to chop grids into bite-sized chunks
164
################################################################
166
# use the block size of the first layer to read efficiently
167
myBlockSize=myFiles[0].GetRasterBand(myBands[0]).GetBlockSize();
168
# store these numbers in variables that may change later
169
nXValid = myBlockSize[0]
170
nYValid = myBlockSize[1]
171
# find total x and y blocks to be read
172
nXBlocks = (int)((DimensionsCheck[0] + myBlockSize[0] - 1) / myBlockSize[0]);
173
nYBlocks = (int)((DimensionsCheck[1] + myBlockSize[1] - 1) / myBlockSize[1]);
174
myBufSize = myBlockSize[0]*myBlockSize[1]
177
print("using blocksize %s x %s" %(myBlockSize[0], myBlockSize[1]))
179
# variables for displaying progress
182
ProgressEnd=nXBlocks*nYBlocks
184
################################################################
185
# start looping through blocks of data
186
################################################################
188
# loop through X-lines
189
for X in range(0,nXBlocks):
191
# in the rare (impossible?) case that the blocks don't fit perfectly
192
# change the block size of the final piece
194
nXValid = DimensionsCheck[0] - X * myBlockSize[0]
195
myBufSize = nXValid*nYValid
200
# reset buffer size for start of Y loop
201
nYValid = myBlockSize[1]
202
myBufSize = nXValid*nYValid
204
# loop through Y lines
205
for Y in range(0,nYBlocks):
207
if 10*ProgressCt/ProgressEnd%10!=ProgressMk:
208
ProgressMk=10*ProgressCt/ProgressEnd%10
209
from sys import version_info
210
if version_info >= (3,0,0):
211
exec('print("%d.." % (10*ProgressMk), end=" ")')
213
exec('print 10*ProgressMk, "..",')
215
# change the block size of the final piece
217
nYValid = DimensionsCheck[1] - Y * myBlockSize[1]
218
myBufSize = nXValid*nYValid
223
# create empty buffer to mark where nodata occurs
224
myNDVs=numpy.zeros(myBufSize)
225
myNDVs.shape=(nYValid,nXValid)
227
# fetch data for each input layer
228
for i,Alpha in enumerate(myAlphaList):
230
# populate lettered arrays with values
231
myval=BandReadAsArray(myFiles[i].GetRasterBand(myBands[i]),
233
win_xsize=nXValid, win_ysize=nYValid)
235
# fill in nodata values
236
myNDVs=1*numpy.logical_or(myNDVs==1, myval==myNDV[i])
238
# create an array of values for this block
239
exec("%s=myval" %Alpha)
243
# try the calculation on the array blocks
245
myResult = eval(opts.calc)
247
print("evaluation of calculation %s failed" %(opts.calc))
250
# propogate nodata values
251
# (set nodata cells to zero then add nodata value to these cells)
252
myResult = ((1*(myNDVs==0))*myResult) + (myOutNDV*myNDVs)
254
# write data block to the output file
255
BandWriteArray(myOutB, myResult, xoff=myX, yoff=myY)
258
#print("Finished - Results written to %s" %opts.outF)
262
################################################################
266
gdal_calc.py [-A <filename>] [--A_band] [-B...-Z filename] [--calc <calculation>] [--format] [--outfile output_file] [--type data_type] [--NoDataValue] [--overwrite]
269
parser = OptionParser(usage)
272
parser.add_option("--calc", dest="calc", help="calculation in gdalnumeric syntax using +-/* or any numpy array functions (i.e. logical_and())")
273
# hack to limit the number of input file options close to required number
274
for myAlpha in AlphaList[0:len(sys.argv)-1]:
275
eval('parser.add_option("-%s", dest="%s", help="input gdal raster file, note you can use any letter A-Z")' %(myAlpha, myAlpha))
276
eval('parser.add_option("--%s_band", dest="%s_band", default=0, type=int, help="number of raster band for file %s")' %(myAlpha, myAlpha, myAlpha))
278
parser.add_option("--outfile", dest="outF", default='gdal_calc.tif', help="output file to generate or fill.")
279
parser.add_option("--NoDataValue", dest="NoDataValue", type=float, help="set output nodatavalue (Defaults to datatype specific values)")
280
parser.add_option("--type", dest="type", help="set datatype must be one of %s" %DefaultNDVLookup.keys())
281
parser.add_option("--format", dest="format", default="GTiff", help="GDAL format for output file (default 'GTiff')")
282
parser.add_option("--overwrite", dest="overwrite", action="store_true", help="overwrite output file if it already exists")
283
parser.add_option("--debug", dest="debug", action="store_true", help="print debugging information")
285
(opts, args) = parser.parse_args()
287
if len(sys.argv) == 1:
290
print("No calculation provided. Nothing to do!")
295
if __name__ == "__main__":