~ubuntu-branches/debian/sid/gdal/sid

« back to all changes in this revision

Viewing changes to swig/python/scripts/gdal_calc.py

  • Committer: Package Import Robot
  • Author(s): Francesco Paolo Lovergine
  • Date: 2012-05-07 15:04:42 UTC
  • mfrom: (5.5.16 experimental)
  • Revision ID: package-import@ubuntu.com-20120507150442-2eks97loeh6rq005
Tags: 1.9.0-1
* Ready for sid, starting transition.
* All symfiles updated to latest builds.
* Added dh_numpy call in debian/rules to depend on numpy ABI.
* Policy bumped to 3.9.3, no changes required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
#******************************************************************************
 
3
 
4
#  Project:  GDAL
 
5
#  Purpose:  Command line raster calculator for gdal supported files
 
6
#  Author:   Chris Yesson, chris.yesson@ioz.ac.uk
 
7
 
8
#******************************************************************************
 
9
#  Copyright (c) 2010, Chris Yesson <chris.yesson@ioz.ac.uk>
 
10
 
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:
 
17
 
18
#  The above copyright notice and this permission notice shall be included
 
19
#  in all copies or substantial portions of the Software.
 
20
 
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
#******************************************************************************
 
29
 
 
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.
 
32
 
 
33
# example 1 - add two files together
 
34
# gdal_calc.py -A input1.tif -B input2.tif --outfile=result.tif --calc="A+B"
 
35
 
 
36
# example 2 - average of two layers
 
37
# gdal_calc.py -A input.tif -B input2.tif --outfile=result.tif --calc="(A+B)/2"
 
38
 
 
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
################################################################
 
42
 
 
43
try:
 
44
    from osgeo import gdal
 
45
    from osgeo.gdalnumeric import *
 
46
except ImportError:
 
47
    import gdal
 
48
    from gdalnumeric import *
 
49
 
 
50
from optparse import OptionParser
 
51
import sys
 
52
import os
 
53
 
 
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"]
 
57
 
 
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}
 
60
 
 
61
#3.402823466E+38
 
62
 
 
63
################################################################
 
64
def doit(opts, args):
 
65
 
 
66
    if opts.debug:
 
67
        print("gdal_calc.py starting calculation %s" %(opts.calc))
 
68
 
 
69
    ################################################################
 
70
    # fetch details of input layers
 
71
    ################################################################
 
72
 
 
73
    # set up some lists to store data for each band
 
74
    myFiles=[]
 
75
    myBands=[]
 
76
    myAlphaList=[]
 
77
    myDataType=[]
 
78
    myDataTypeNum=[]
 
79
    myNDV=[]
 
80
    DimensionsCheck=None
 
81
 
 
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))
 
86
        if myF:
 
87
            myFiles.append(gdal.Open(myF, gdal.GA_ReadOnly))
 
88
            # check if we have asked for a specific band...
 
89
            if myBand:
 
90
                myBands.append(myBand)
 
91
            else:
 
92
                myBands.append(1)
 
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
 
98
            if DimensionsCheck:
 
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]))
 
102
                    return
 
103
            else:
 
104
                DimensionsCheck=[myFiles[i].RasterXSize, myFiles[i].RasterYSize]
 
105
 
 
106
            if opts.debug:
 
107
                print("file %s: %s, dimensions: %s, %s, type: %s" %(myI,myF,DimensionsCheck[0],DimensionsCheck[1],myDataType[i]))
 
108
 
 
109
    ################################################################
 
110
    # set up output file
 
111
    ################################################################
 
112
 
 
113
    # open output file exists
 
114
    if os.path.isfile(opts.outF) and not opts.overwrite:
 
115
        if opts.debug:
 
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")
 
120
            return
 
121
        myOutB=myOut.GetRasterBand(1)
 
122
        myOutNDV=myOutB.GetNoDataValue()
 
123
        myOutType=gdal.GetDataTypeName(myOutB.DataType)
 
124
 
 
125
    else:
 
126
        # remove existing file and regenerate
 
127
        if os.path.isfile(opts.outF):
 
128
            os.remove(opts.outF)
 
129
        # create a new file
 
130
        if opts.debug:
 
131
            print("Generating output file %s" %(opts.outF))
 
132
 
 
133
        # find data type to use
 
134
        if not opts.type:
 
135
            # use the largest type of the input files
 
136
            myOutType=gdal.GetDataTypeName(max(myDataTypeNum))
 
137
        else:
 
138
            myOutType=opts.type
 
139
 
 
140
        # create file
 
141
        myOutDrv = gdal.GetDriverByName(opts.format)
 
142
        myOut=myOutDrv.Create(opts.outF, DimensionsCheck[0], DimensionsCheck[1], 1, gdal.GetDataTypeByName(myOutType))
 
143
 
 
144
        # set output geo info based on first input layer
 
145
        myOut.SetGeoTransform(myFiles[0].GetGeoTransform())
 
146
        myOut.SetProjection(myFiles[0].GetProjection())
 
147
 
 
148
        myOutB=myOut.GetRasterBand(1)
 
149
        if opts.NoDataValue!=None:
 
150
            myOutNDV=opts.NoDataValue
 
151
        else:
 
152
            myOutNDV=DefaultNDVLookup[myOutType]
 
153
        myOutB.SetNoDataValue(myOutNDV)
 
154
        # write to band
 
155
        myOutB=None
 
156
        # refetch band
 
157
        myOutB=myOut.GetRasterBand(1)
 
158
 
 
159
    if opts.debug:
 
160
        print("output file: %s, dimensions: %s, %s, type: %s" %(opts.outF,myOut.RasterXSize,myOut.RasterYSize,gdal.GetDataTypeName(myOutB.DataType)))
 
161
 
 
162
    ################################################################
 
163
    # find block size to chop grids into bite-sized chunks 
 
164
    ################################################################
 
165
 
 
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]
 
175
 
 
176
    if opts.debug:
 
177
        print("using blocksize %s x %s" %(myBlockSize[0], myBlockSize[1]))
 
178
 
 
179
    # variables for displaying progress
 
180
    ProgressCt=-1
 
181
    ProgressMk=-1
 
182
    ProgressEnd=nXBlocks*nYBlocks
 
183
    
 
184
    ################################################################
 
185
    # start looping through blocks of data
 
186
    ################################################################
 
187
 
 
188
    # loop through X-lines
 
189
    for X in range(0,nXBlocks):
 
190
 
 
191
        # in the rare (impossible?) case that the blocks don't fit perfectly
 
192
        # change the block size of the final piece
 
193
        if X==nXBlocks-1:
 
194
             nXValid = DimensionsCheck[0] - X * myBlockSize[0]
 
195
             myBufSize = nXValid*nYValid
 
196
 
 
197
        # find X offset
 
198
        myX=X*myBlockSize[0]
 
199
 
 
200
        # reset buffer size for start of Y loop
 
201
        nYValid = myBlockSize[1]
 
202
        myBufSize = nXValid*nYValid
 
203
 
 
204
        # loop through Y lines
 
205
        for Y in range(0,nYBlocks):
 
206
            ProgressCt+=1
 
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=" ")')
 
212
                else:
 
213
                    exec('print 10*ProgressMk, "..",')
 
214
 
 
215
            # change the block size of the final piece
 
216
            if Y==nYBlocks-1:
 
217
                nYValid = DimensionsCheck[1] - Y * myBlockSize[1]
 
218
                myBufSize = nXValid*nYValid
 
219
 
 
220
            # find Y offset
 
221
            myY=Y*myBlockSize[1]
 
222
 
 
223
            # create empty buffer to mark where nodata occurs
 
224
            myNDVs=numpy.zeros(myBufSize)
 
225
            myNDVs.shape=(nYValid,nXValid)
 
226
 
 
227
            # fetch data for each input layer
 
228
            for i,Alpha in enumerate(myAlphaList):
 
229
 
 
230
                # populate lettered arrays with values
 
231
                myval=BandReadAsArray(myFiles[i].GetRasterBand(myBands[i]),
 
232
                                      xoff=myX, yoff=myY,
 
233
                                      win_xsize=nXValid, win_ysize=nYValid)
 
234
 
 
235
                # fill in nodata values
 
236
                myNDVs=1*numpy.logical_or(myNDVs==1, myval==myNDV[i])
 
237
 
 
238
                # create an array of values for this block
 
239
                exec("%s=myval" %Alpha)
 
240
                myval=None
 
241
 
 
242
 
 
243
            # try the calculation on the array blocks
 
244
            try:
 
245
                myResult = eval(opts.calc)
 
246
            except:
 
247
                print("evaluation of calculation %s failed" %(opts.calc))
 
248
                raise
 
249
 
 
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)
 
253
 
 
254
            # write data block to the output file
 
255
            BandWriteArray(myOutB, myResult, xoff=myX, yoff=myY)
 
256
 
 
257
    print("100 - Done")
 
258
    #print("Finished - Results written to %s" %opts.outF)
 
259
 
 
260
    return
 
261
 
 
262
################################################################
 
263
def main():
 
264
 
 
265
    usage = """
 
266
gdal_calc.py [-A <filename>] [--A_band] [-B...-Z filename]  [--calc <calculation>] [--format] [--outfile output_file] [--type data_type] [--NoDataValue] [--overwrite]
 
267
    """
 
268
 
 
269
    parser = OptionParser(usage)
 
270
 
 
271
    # define options
 
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))
 
277
 
 
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")
 
284
 
 
285
    (opts, args) = parser.parse_args()
 
286
 
 
287
    if len(sys.argv) == 1:
 
288
        print(usage)
 
289
    elif not opts.calc:
 
290
        print("No calculation provided.  Nothing to do!")
 
291
        print(usage)
 
292
    else:
 
293
        doit(opts, args)
 
294
 
 
295
if __name__ == "__main__":
 
296
    main()