Project

General

Profile

« Previous | Next » 

Revision 73b1bea9

Added by Benoit Parmentier over 11 years ago

splitting LST script-climatology calculation

View differences:

climate/research/oregon/modis-lst/LST_climatology.py
1
# -*- coding: utf-8 -*-
2
"""
3
Created on Fri Oct 26 23:48:57 2012
4

  
5
@author: benoitparmentier
6
"""
7

  
8
#!/usr/bin/python
9
#
10
# Early version of assorted Python code to download MODIS 11A1 (daily)
11
# data associated with specified dates and tiles, then interface with
12
# GRASS to calculate temporally averaged LST in a way that accounts for
13
# QC flags.
14
#
15
# TODO:
16
#  - functionalize to encapsulate high level procedural steps
17
#  - create a 'main' function to allow script to be run as a command
18
#    that can accept arguments?
19
#  - write code to set up and take down a temporary GRASS location/mapset
20
#  - calculate other aggregate stats? (stddev, ...)
21
#  - deal with more than just first two bits of QC flags?
22
#     e.g.: r.mapcalc 'qc_level2 = (qc>>2) & 0x03'   
23
#     0x03?? that means 0000_0011 and >>2 means shift twice on the right for 
24
#
25
# Created by Jim Regetz
26
# NCEAS on 16-May-2012
27
# Modified by Benoit Parmentier
28
#NCEAS on 18-October-2012
29
#NCEAS on 19-January-2013
30
#NCEAS on 16-May-2013
31
# 
32

  
33
import os, glob
34
import datetime, calendar
35
import ftplib
36
import grass.script as gs
37
import argparse
38
import shutil
39

  
40
#------------------
41
# helper functions
42
#------------------
43

  
44
#Added on January 16, 2012 by Benoit NCEAS
45
#Add other lists to calculate climatology?? later 
46
def list_raster_per_month(listmaps):
47
    """Determine starting and ending numeric day-of-year (doy)
48
    asscociated with the specified month and year.
49

  
50
    Arguments:
51
    maplist -- list of raster grass-digit integer year
52
    month -- integer month (1-12)
53
    year_range -- list of years
54

  
55
    Returns tuple of start and end doy for that month/year.
56
    """
57
    list_maps_month=list()
58
    nb_month=12
59
    for i in range(1,nb_month+1):
60
        #i=i+1
61
        #filename = listfiles_wd2[i] #list the files of interest
62
        #monthlist[:]=[] #empty the list
63
        monthlist=list()
64
        #monthlist2[:]=[] #empty the list
65
        j=0  #counter
66
        for mapname in listmaps:
67
            #tmp_str=LST[0]
68
            date_map=mapname.split('_')[1][1:8]
69
            #date = filename[filename.rfind('_MOD')-8:filename.rfind('_MOD')]
70
            d=datetime.datetime.strptime(date_map,'%Y%j') #This produces a date object from a string extracted from the file name.
71
            month=d.strftime('%m') #Extract the month of the current map
72
            if int(month)==i:
73
                #monthlist.append(listmaps[j])
74
                monthlist.append(mapname)
75
                #monthlist2.append(listmaps[j])
76
            j=j+1
77
        list_maps_month.append(monthlist)   #This is a list of a list containing a list for each month
78
        #list_files2.append(monthlist2)
79
    return(list_maps_month)    
80

  
81
def calc_clim(maplist, name, overwrite=False):
82
    """Generate some climatalogies in GRASS based on the input list of
83
    maps. As usual, current GRASS region settings apply. Produces the
84
    following output rasters:
85
      * nobs: count of number of (non-null) values over the input maps
86
      * mean: arithmetic mean of (non-null) values over the input maps
87

  
88
    Arguments:
89
    maplist -- list of names of GRASS maps to be aggregated
90
    name -- template (suffix) for GRASS output map names
91

  
92
    Returns list of names of the output maps created in GRASS.
93
    """
94
    denominator = '(%s)' % '+'.join(['if(!isnull(%s))' % m
95
        for m in maplist])
96
    gs.mapcalc('nobs_%s = %s' % (name, denominator), overwrite=overwrite)
97
    numerator = '(%s)' % '+'.join(['if(isnull(%s), 0, %s)' % (m, m)
98
        for m in maplist])
99
    gs.mapcalc('mean_%s = round(float(%s)/nobs_%s)' % (name, numerator, name),
100
        overwrite=overwrite)
101
    return ['%s_%s' % (s, name) for s in ['nobs', 'mean']]
102

  
103
def load_qc_adjusted_lst(hdf):
104
    """Load LST_Day_1km into GRASS from the specified hdf file, and
105
    nullify any cells for which QA flags indicate anything other than
106
    high quality.
107

  
108
    Argument:
109
    hdf -- local path to the 11A1 HDF file
110

  
111
    Returns the name of the QC-adjusted LST map created in GRASS.
112
    """
113
    lstname =  'LST_' + '_'.join(os.path.basename(hdf).split('.')[1:3])               
114
    # read in lst grid
115
    gs.run_command('r.in.gdal',
116
        input='HDF4_EOS:EOS_GRID:%s:MODIS_Grid_Daily_1km_LST:LST_Day_1km' % hdf,
117
        output=lstname)            # No new location created since it has already been done with r.in.gdal before!!!
118

  
119
    # read in qc grid
120
    gs.run_command('r.in.gdal',
121
        input = 'HDF4_EOS:EOS_GRID:%s:MODIS_Grid_Daily_1km_LST:QC_Day' % hdf,
122
        output = 'qc_this_day')
123
    gs.run_command('g.region', rast=lstname)
124
    # null out any LST cells for which QC is not high [00]  #THIS WHERE WE MIGHT NEED TO CHANGE...
125
    gs.mapcalc('${lst} = if((qc_this_day & 0x03)==0, ${lst}, null())',
126
        lst=lstname, overwrite=True)
127
    # clean up
128
    gs.run_command('g.remove', rast='qc_this_day')
129
    # return name of qc-adjusted LST raster in GRASS
130
    return lstname
131

  
132
def main():
133
    #--------------------------------------------
134
    # Download and Calculate monthly climatology for daily LST time series for a specific area
135
    #--------------------------------------------
136

  
137
    # TODO: set up a (temporary?) GRASS database to use for processing? code
138
    # currently assumes it's being run within an existing GRASS session
139
    # using an appropriately configured LOCATION...
140
    #
141
    # note the following trick to fix datum for modis sinu;
142
    # TODO: check that this works properly...compare to MRT output?
143
    # gs.run_command('g.proj', flags='c',
144
    #     proj4='+proj=sinu +a=6371007.181 +b=6371007.181 +ellps=sphere')
145
    ##    proj4='+proj=sinu +R=6371007.181 +nadgrids=@null +wktext')
146

  
147
    ########## START OF SCRIPT ##############
148
    #### Modified by Benoit on May 13, 2013 
149
    
150
    ### INPUT Parameters
151
    #Inputs from R?? there are 9 parameters
152
   #tiles = ['h11v08','h11v07','h12v07','h12v08','h10v07','h10v08'] #These tiles correspond to Venezuela.
153
    #tiles= ['h08v04','h09v04']    
154
    #start_year = 2001
155
    #end_year = 2010
156
    #start_month=1
157
    #end_month=12
158
    #hdfdir =  '/home/layers/commons/modis/MOD11A1_tiles' #destination file where hdf files are stored locally after download.
159
    #hdfdir =  '/home/parmentier/Data/IPLANT_project/MOD11A1_tiles' #destination file where hdf files are stored locally after download.
160
    #night=1    # if 1 then produce night climatology
161
    #out_suffix="_03192013"
162
    #download=1  # if 1 then download files
163
   
164
    #Passing arguments from the shell...using positional assignment
165
    parser = argparse.ArgumentParser()
166
    parser.add_argument("tiles", type=str, help="list of Modis tiles")
167
    parser.add_argument("start_year", type=int, help="start year")
168
    parser.add_argument("end_year", type=int, help="end year")
169
    parser.add_argument("start_month", type=int, help="start month")
170
    parser.add_argument("end_month", type=int, help="end month")
171
    parser.add_argument("hdfdir", type=str, help="destination/source directory for hdf file")
172
    parser.add_argument("night", type=int, help="night")
173
    parser.add_argument("download", type=int, help="out_suffix")
174
    parser.add_argument("out_suffix", type=str, help="out_suffix")
175

  
176
    myargs = parser.parse_args()
177

  
178
    tiles = myargs.tiles #These tiles correspond to Venezuela.
179
    start_year = myargs.start_year
180
    end_year = myargs.end_year 
181
    end_month = myargs.end_month #not used...to be removed
182
    start_month= myargs.start_month #not used...to be removed
183
    hdfdir =  myargs.hdfdir
184
    night= myargs.night    # if 1 then produce night climatology
185
    out_suffix= myargs.out_suffix #"_03192013"
186
    download=myargs.download# if 1 then download files
187
    
188
    tiles =tiles.split(",") #Create a list from string
189
    #need to add on the fly creation of folder for each tile!!
190
    
191
    ################## First step: download data ###################
192
    # Added tile loop 
193
    year_list=range(start_year,end_year+1) #list of year to loop through
194
   
195
    # modify loop to take into account "hdfs", list of hdf files needed in the next steps…  
196
    ################# Second step: compute climatology #################
197
    ## Process tile by tile...
198
    var_name = ['LST_Day_1km','LST_Night_1km']
199
    if night==1:
200
        lst_var = var_name[1]
201
    if night==0:
202
        lst_var = var_name[0]
203
        
204
    #start = time.clock()    
205
    for tile in tiles:        
206
        # Set up a temporary GRASS data base   
207
        tmp_location = 'tmp' + "_"+ tile + "_"+ str(os.getpid())        # Name of the temporary GRASS data base 
208
        orig_location = gs.gisenv()['LOCATION_NAME']   #Current location  of GRASS DATABASE at start up (config grassgrc?)
209
        orig_mapset = gs.gisenv()['MAPSET']   #Current mapset of GRASS DATABASE at start up (grassgrc config)
210
        gs.os.environ['GRASS_OVERWRITE'] = '1'         # Allow for overwrite?? GRASS data base  
211
            
212
        #path_file = hdfdir #change
213
        path_file = os.path.join(hdfdir,tile)
214
        
215
        os.chdir(path_file)    #set working directory
216
        os.getcwd()            #get current working directory
217
        listfiles_wd2 = glob.glob('*'+tile+'*'+'.hdf') #list the all the hdf modis LST files of interest
218
        name_of_file = listfiles_wd2[0]    
219
        #Create the name of the layer to extract from the hdf file used for definition of the database
220
        lst1day = 'HDF4_EOS:EOS_GRID:%s/%s:%s' % (
221
        path_file,
222
        name_of_file,
223
        'MODIS_Grid_Daily_1km_LST:'+ lst_var)
224

  
225
        #now import one image and set the location, mapset and database !!create name per tile
226
        gs.run_command('r.in.gdal', input=lst1day, output='LST_1day_'+tile,
227
               location=tmp_location)
228
    
229
        # Now that the new location has been created, switch to new location
230
        gs.run_command('g.gisenv', set='LOCATION_NAME=%s' % tmp_location)
231
        gs.run_command('g.gisenv', set='MAPSET=PERMANENT')
232
    
233
        # set GRASS the projection system and GRASS region to match the extent of the file
234
        gs.run_command('g.proj', flags='c',
235
        proj4='+proj=sinu +a=6371007.181 +b=6371007.181 +ellps=sphere') #This should be set in the parameters
236
        gs.run_command('g.region', rast='LST_1day_'+tile)
237
    
238
        #generate monthly pixelwise mean & count of high-quality daytime LST
239
        gs.os.environ['GRASS_OVERWRITE'] = '1'
240
    
241
        #Provide a list of file "hdfs" to process…this should be in a loop...
242
        #tile= 'h12v08'
243
        fileglob = 'MOD11A1.A*.%s*hdf' % (tile)
244
        pathglob = os.path.join(path_file, fileglob)
245
        files = glob.glob(pathglob)
246
        hdfs = files
247
        os.chdir(hdfdir)    #set working directory to the general path to modis files...
248
        
249
        ### LST values in images must be masked out if they have low quality, determined from QC flags stored in hdf files
250
        LST = [load_qc_adjusted_lst(hdf) for hdf in hdfs]
251
        #load qc takes about 8 minutes for about 350 files        
252
        ### load_qc is a list that contains the name of the new lst files in the GRASS format. The files are stored in the temporary GRASS database.
253
        list_maps_month=list_raster_per_month(LST)    
254
        list_maps_name=['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec']
255
        ##Now calculate clim per month, do a test for year1
256
        nb_month = 12
257
        for i in range(1,nb_month+1):
258
            clims = calc_clim(list_maps_month[i-1],lst_var+'_'+tile+'_'+list_maps_name[i-1]+'_'+str(i-1))
259
            for j in range(1, len(clims)+1):
260
                if j-1 ==0:
261
                    gs.run_command('r.out.gdal', input= clims[j-1], output=clims[j-1]+ out_suffix +'.tif', type='Float64')
262
                if j-1 ==1:
263
                    gs.mapcalc(' clim_rescaled = ('+ clims[j-1 ]+ ' * 0.02) -273.15')  
264
                    gs.run_command('r.out.gdal', input= 'clim_rescaled', output=clims[j-1]+ out_suffix+'.tif', type='Float64')
265
        #clims = calc_clim(LST, 'LST_%s_%d_%02d' % (tile, year, month))
266
        
267
        # clean up GRASS DATABASE: ok working
268
        
269
        gs.run_command('g.remove', rast=','.join(LST))
270
        ## get list of remaining files...
271
        maps_list = gs.mlist_strings(type = 'rast') #list all remaining maps in the location
272
        gs.run_command('g.remove', rast=','.join(maps_list)) #remove remaning maps
273
        gs.os.environ['GRASS_OVERWRITE'] = '0'
274
        
275
        #clean up temporary location and set the original GRASS database parameters back
276
        gs.run_command('g.gisenv', set='LOCATION_NAME=%s' % orig_location)
277
        gs.run_command('g.gisenv', set='MAPSET=%s' % orig_mapset)
278
        shutil.rmtree(os.path.join(gs.gisenv()['GISDBASE'], tmp_location))
279

  
280
    return None
281
    
282
#Need to add this to run
283
if __name__ == '__main__':
284
    main()
285

  
286

  

Also available in: Unified diff