i.in.spotvgt.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. #!/usr/bin/env python
  2. ############################################################################
  3. #
  4. # MODULE: i.in.spot
  5. #
  6. # AUTHOR(S): Markus Neteler
  7. # Converted to Python by Glynn Clements
  8. #
  9. # PURPOSE: Import SPOT VEGETATION data into a GRASS raster map
  10. # SPOT Vegetation (1km, global) data:
  11. # http://free.vgt.vito.be/
  12. #
  13. # COPYRIGHT: (c) 2004-2011 GRASS Development Team
  14. #
  15. # This program is free software under the GNU General Public
  16. # License (>=v2). Read the file COPYING that comes with GRASS
  17. # for details.
  18. #
  19. #############################################################################
  20. #
  21. # REQUIREMENTS:
  22. # - gdal: http://www.gdal.org
  23. #
  24. # Notes:
  25. # * According to the faq (http://www.vgt.vito.be/faq/faq.html), SPOT vegetation
  26. # coordinates refer to the center of a pixel.
  27. # * GDAL coordinates refer to the corner of a pixel
  28. # -> correction of 0001/0001_LOG.TXT coordinates by 0.5 pixel
  29. #############################################################################
  30. #%module
  31. #% description: Imports SPOT VGT NDVI data into a raster map.
  32. #% keyword: imagery
  33. #% keyword: import
  34. #%end
  35. #%flag
  36. #% key: a
  37. #% description: Also import quality map (SM status map layer) and filter NDVI map
  38. #%end
  39. #%option G_OPT_F_INPUT
  40. #% description: Name of input SPOT VGT NDVI HDF file
  41. #%end
  42. #% option G_OPT_R_OUTPUT
  43. #% required : no
  44. #%end
  45. import os
  46. import atexit
  47. import string
  48. import grass.script as grass
  49. from grass.exceptions import CalledModuleError
  50. vrt = """<VRTDataset rasterXSize="$XSIZE" rasterYSize="$YSIZE">
  51. <SRS>GEOGCS[&quot;wgs84&quot;,DATUM[&quot;WGS_1984&quot;,SPHEROID[&quot;wgs84&quot;,6378137,298.257223563],TOWGS84[0.000,0.000,0.000]],PRIMEM[&quot;Greenwich&quot;,0],UNIT[&quot;degree&quot;,0.0174532925199433]]</SRS>
  52. <GeoTransform>$WESTCORNER, $RESOLUTION, 0.0000000000000000e+00, $NORTHCORNER, 0.0000000000000e+00, -$RESOLUTION</GeoTransform>
  53. <VRTRasterBand dataType="Byte" band="1">
  54. <NoDataValue>0.00000000000000E+00</NoDataValue>
  55. <ColorInterp>Gray</ColorInterp>
  56. <SimpleSource>
  57. <SourceFilename>$FILENAME</SourceFilename>
  58. <SourceBand>1</SourceBand>
  59. <SrcRect xOff="0" yOff="0" xSize="$XSIZE" ySize="$YSIZE"/>
  60. <DstRect xOff="0" yOff="0" xSize="$XSIZE" ySize="$YSIZE"/>
  61. </SimpleSource>
  62. </VRTRasterBand>
  63. </VRTDataset>"""
  64. #### a function for writing VRT files
  65. def create_VRT_file(projfile, vrtfile, infile):
  66. fh = file(projfile)
  67. kv = {}
  68. for l in fh:
  69. f = l.rstrip('\r\n').split()
  70. if f < 2:
  71. continue
  72. kv[f[0]] = f[1]
  73. fh.close()
  74. north_center = kv['CARTO_UPPER_LEFT_Y']
  75. south_center = kv['CARTO_LOWER_LEFT_Y']
  76. east_center = kv['CARTO_UPPER_RIGHT_X']
  77. west_center = kv['CARTO_UPPER_LEFT_X']
  78. map_proj_res = kv['MAP_PROJ_RESOLUTION']
  79. xsize = kv['IMAGE_UPPER_RIGHT_COL']
  80. ysize = kv['IMAGE_LOWER_RIGHT_ROW']
  81. resolution = float(map_proj_res)
  82. north_corner = float(north_center) + resolution / 2
  83. south_corner = float(south_center) - resolution / 2
  84. east_corner = float(east_center ) + resolution / 2
  85. west_corner = float(west_center ) - resolution / 2
  86. t = string.Template(vrt)
  87. s = t.substitute(NORTHCORNER = north_corner, WESTCORNER = west_corner,
  88. XSIZE = xsize, YSIZE = ysize, RESOLUTION = map_proj_res,
  89. FILENAME = infile)
  90. outf = file(vrtfile, 'w')
  91. outf.write(s)
  92. outf.close()
  93. def cleanup():
  94. #### clean up the mess
  95. grass.try_remove(vrtfile)
  96. grass.try_remove(tmpfile)
  97. def main():
  98. global vrtfile, tmpfile
  99. infile = options['input']
  100. rast = options['output']
  101. also = flags['a']
  102. #### check for gdalinfo (just to check if installation is complete)
  103. if not grass.find_program('gdalinfo', '--help'):
  104. grass.fatal(_("'gdalinfo' not found, install GDAL tools first (http://www.gdal.org)"))
  105. pid = str(os.getpid())
  106. tmpfile = grass.tempfile()
  107. ################### let's go
  108. spotdir = os.path.dirname(infile)
  109. spotname = grass.basename(infile, 'hdf')
  110. if rast:
  111. name = rast
  112. else:
  113. name = spotname
  114. if not grass.overwrite() and grass.find_file(name)['file']:
  115. grass.fatal(_("<%s> already exists. Aborting.") % name)
  116. # still a ZIP file? (is this portable?? see the r.in.srtm script for ideas)
  117. if infile.lower().endswith('.zip'):
  118. grass.fatal(_("Please extract %s before import.") % infile)
  119. try:
  120. p = grass.Popen(['file', '-ib', infile], stdout = grass.PIPE)
  121. s = p.communicate()[0]
  122. if s == "application/x-zip":
  123. grass.fatal(_("Please extract %s before import.") % infile)
  124. except:
  125. pass
  126. ### create VRT header for NDVI
  127. projfile = os.path.join(spotdir, "0001_LOG.TXT")
  128. vrtfile = tmpfile + '.vrt'
  129. # first process the NDVI:
  130. grass.try_remove(vrtfile)
  131. create_VRT_file(projfile, vrtfile, infile)
  132. ## let's import the NDVI map...
  133. grass.message(_("Importing SPOT VGT NDVI map..."))
  134. try:
  135. grass.run_command('r.in.gdal', input=vrtfile, output=name)
  136. except CalledModuleError:
  137. grass.fatal(_("An error occurred. Stop."))
  138. grass.message(_("Imported SPOT VEGETATION NDVI map <%s>.") % name)
  139. #################
  140. ## http://www.vgt.vito.be/faq/FAQS/faq19.html
  141. # What is the relation between the digital number and the real NDVI ?
  142. # Real NDVI =coefficient a * Digital Number + coefficient b
  143. # = a * DN +b
  144. #
  145. # Coefficient a = 0.004
  146. # Coefficient b = -0.1
  147. # clone current region
  148. # switch to a temporary region
  149. grass.use_temp_region()
  150. grass.run_command('g.region', raster = name, quiet = True)
  151. grass.message(_("Remapping digital numbers to NDVI..."))
  152. tmpname = "%s_%s" % (name, pid)
  153. grass.mapcalc("$tmpname = 0.004 * $name - 0.1", tmpname = tmpname, name = name)
  154. grass.run_command('g.remove', type = 'raster', name = name, quiet = True, flags = 'f')
  155. grass.run_command('g.rename', raster = (tmpname, name), quiet = True)
  156. # write cmd history:
  157. grass.raster_history(name)
  158. #apply color table:
  159. grass.run_command('r.colors', map = name, color = 'ndvi', quiet = True)
  160. ##########################
  161. # second, optionally process the SM quality map:
  162. #SM Status Map
  163. # http://nieuw.vgt.vito.be/faq/FAQS/faq22.html
  164. #Data about
  165. # Bit NR 7: Radiometric quality for B0 coded as 0 if bad and 1 if good
  166. # Bit NR 6: Radiometric quality for B2 coded as 0 if bad and 1 if good
  167. # Bit NR 5: Radiometric quality for B3 coded as 0 if bad and 1 if good
  168. # Bit NR 4: Radiometric quality for MIR coded as 0 if bad and 1 if good
  169. # Bit NR 3: land code 1 or water code 0
  170. # Bit NR 2: ice/snow code 1 , code 0 if there is no ice/snow
  171. # Bit NR 1: 0 0 1 1
  172. # Bit NR 0: 0 1 0 1
  173. # clear shadow uncertain cloud
  174. #
  175. #Note:
  176. # pos 7 6 5 4 3 2 1 0 (bit position)
  177. # 128 64 32 16 8 4 2 1 (values for 8 bit)
  178. #
  179. #
  180. # Bit 4-7 should be 1: their sum is 240
  181. # Bit 3 land code, should be 1, sum up to 248 along with higher bits
  182. # Bit 2 ice/snow code
  183. # Bit 0-1 should be 0
  184. #
  185. # A good map threshold: >= 248
  186. if also:
  187. grass.message(_("Importing SPOT VGT NDVI quality map..."))
  188. grass.try_remove(vrtfile)
  189. qname = spotname.replace('NDV','SM')
  190. qfile = os.path.join(spotdir, qname)
  191. create_VRT_file(projfile, vrtfile, qfile)
  192. ## let's import the SM quality map...
  193. smfile = name + '.sm'
  194. try:
  195. grass.run_command('r.in.gdal', input=vrtfile, output=smfile)
  196. except CalledModuleError:
  197. grass.fatal(_("An error occurred. Stop."))
  198. # some of the possible values:
  199. rules = [r + '\n' for r in [
  200. '8 50 50 50',
  201. '11 70 70 70',
  202. '12 90 90 90',
  203. '60 grey',
  204. '155 blue',
  205. '232 violet',
  206. '235 red',
  207. '236 brown',
  208. '248 orange',
  209. '251 yellow',
  210. '252 green'
  211. ]]
  212. grass.write_command('r.colors', map = smfile, rules = '-', stdin = rules)
  213. grass.message(_("Imported SPOT VEGETATION SM quality map <%s>.") % smfile)
  214. grass.message(_("Note: A snow map can be extracted by category 252 (d.rast %s cat=252)") % smfile)
  215. grass.message("")
  216. grass.message(_("Filtering NDVI map by Status Map quality layer..."))
  217. filtfile = "%s_filt" % name
  218. grass.mapcalc("$filtfile = if($smfile % 4 == 3 || ($smfile / 16) % 16 == 0, null(), $name)",
  219. filtfile = filtfile, smfile = smfile, name = name)
  220. grass.run_command('r.colors', map = filtfile, color = 'ndvi', quiet = True)
  221. grass.message(_("Filtered SPOT VEGETATION NDVI map <%s>.") % filtfile)
  222. # write cmd history:
  223. grass.raster_history(smfile)
  224. grass.raster_history(filtfile)
  225. grass.message(_("Done."))
  226. if __name__ == "__main__":
  227. options, flags = grass.parser()
  228. atexit.register(cleanup)
  229. main()