runGdal.R 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. #' Process MODIS HDF with GDAL
  2. #'
  3. #' @description
  4. #' Downloads MODIS grid data from archive (FTP or local) and processes the
  5. #' files.
  6. #'
  7. #' @param product \code{character}, see \code{\link{getProduct}}.
  8. #' @param collection \code{character} or \code{integer}, see
  9. #' \code{\link{getCollection}}.
  10. #' @param begin \code{character}. Begin date of MODIS time series, see
  11. #' \code{\link{transDate}} for formatting.
  12. #' @param end Same for end date.
  13. #' @param extent Extent information, defaults to \code{'global'}. See
  14. #' \code{\link{getTile}}.
  15. #' @param tileH \code{numeric} or \code{character}. Horizontal tile number,
  16. #' see \code{\link{getTile}}.
  17. #' @param tileV \code{numeric} or \code{character}. Vertical tile number(s),
  18. #' see \code{tileH}.
  19. #' @param SDSstring \code{character}, see \code{\link{getSds}}.
  20. #' @param job \code{character}. Name of the current job for the creation of the
  21. #' output folder. If not specified, it is created in 'PRODUCT.COLLECTION_DATETIME'.
  22. #' @param checkIntegrity \code{logical}, see \code{\link{getHdf}}.
  23. #' @param forceDownload \code{logical}, see \code{\link{getHdf}}.
  24. #' @param overwrite \code{logical}, defaults to \code{FALSE}. Determines
  25. #' whether or not to overwrite existing SDS output files.
  26. #' @param ... Additional arguments passed to \code{MODIS:::combineOptions()} (eg
  27. #' 'wait'), see also \code{\link{MODISoptions}}.
  28. #'
  29. #' @return
  30. #' A \code{list} of the same length as 'product'. Each product slot holds a
  31. #' sub-\code{list} of processed dates which, for each time step, include the
  32. #' corresponding output files as \code{character} objects.
  33. #'
  34. #' @details
  35. #' \describe{
  36. #' \tabular{rll}{
  37. #' \tab \code{outProj}\tab CRS/ prj4 or EPSG code of output, any format supported by gdal see examples.\cr \tab \tab Default is 'asIn' (no warping). See \code{?MODISoptions}.\cr
  38. #' \tab \code{pixelSize}\tab Numeric single value. Output pixel size in target reference system unit.\cr \tab \tab Default is 'asIn'. See \code{?MODISoptions}.\cr
  39. #' \tab \code{resamplingType}\tab Character. Default is 'near', can be one of: 'bilinear', 'cubic', 'cubicspline', 'lanczos'.\cr \tab \tab See \code{?MODISoptions}.\cr
  40. #' \tab \code{blockSize}\tab integer. Default \code{NULL} that means the stripe size is set by GDAL.\cr \tab \tab Basically it is the "-co BLOCKYSIZE=" parameter. See: http://www.gdal.org/frmt_gtiff.html\cr
  41. #' \tab \code{compression}\tab logical. Default is \code{TRUE}, compress data with the lossless LZW compression with "predictor=2".\cr \tab \tab See: \url{http://www.gdal.org/frmt_gtiff.html}\cr
  42. #' \tab \code{dataFormat}\tab Data output format, see \code{getOption("MODIS_gdalOutDriver")} column 'name'.\cr
  43. #' \tab \code{localArcPath}\tab Character. See \code{?MODISoptions}. Local path to look for and/or to download MODIS files.\cr
  44. #' \tab \code{outDirPath}\tab Character. See \code{?MODISoptions}. Root directory where to write \code{job} folder.\cr
  45. #' }
  46. #' }
  47. #'
  48. #' \code{\link{runGdal}} uses a lot of \strong{MODIS} package functions, see in
  49. #' section Arguments and Methods the respective '?function' for details and
  50. #' inputs.\cr
  51. #' If \code{extent} is a \code{Raster*} object, the output has exactly the same
  52. #' extent, pixel size, and projection.\cr
  53. #' If \code{extent} is a \strong{sp} object (i.e., polygon shapefile), the
  54. #' output has exactly the same extent and projection.\cr
  55. #' If \code{tileH} and \code{tileV} are used (instead of \code{extent}) to
  56. #' define the area of interest, and \code{outProj} and \code{pixelSize} are
  57. #' \code{'asIn'}, the result is only converted from multilayer-HDF to
  58. #' \code{dataFormat}, default "GeoTiff" (\code{\link{MODISoptions}}).\cr
  59. #'
  60. #' @author
  61. #' Matteo Mattiuzzi, Florian Detsch
  62. #'
  63. #' @seealso
  64. #' \code{\link{getHdf}}, \code{\link{runMrt}}.
  65. #'
  66. #' @note
  67. #' You need to have a GDAL installed on your system!\cr
  68. #' \url{http://www.gdal.org/gdal_utilities.html}\cr\cr
  69. #' On Unix-alkes, install 'gdal-bin' (i.e. Ubuntu: 'sudo apt-get install gdal-bin')\cr
  70. #' On Windows, you need to install GDAL through OSGeo4W
  71. #' (\url{http://trac.osgeo.org/osgeo4w/}) or FWTools
  72. #' (\url{http://fwtools.maptools.org/}) since the standard GDAL does not support
  73. #' HDF4 format.
  74. #'
  75. #' @examples
  76. #' \dontrun{
  77. #' # LST in Austria
  78. #' runGdal( product="MOD11A1", extent="austria", begin="2010001", end="2010005", SDSstring="101")
  79. #'
  80. #' # LST with interactiv area selection
  81. #' runGdal( product="MOD11A1", begin="2010001", end="2010005", SDSstring="101")
  82. #'
  83. #' ### outProj examples
  84. #' # LST of Austria warped to UTM 34N (the three different possibilites to specify "outProj")
  85. #' # to find am EPSG or prj4 you may use: prj <- make_EPSG() See
  86. #' runGdal( job="LSTaustria", product="MOD11A1", extent="Austria", begin="2010001", end="2010005",
  87. #' SDSstring="101", outProj="EPSG:32634")
  88. #'
  89. #' runGdal( job="LSTaustria", product="MOD11A1", extent="Austria", begin="2010001", end="2010005",
  90. #' SDSstring="101", outProj="32634")
  91. #'
  92. #' runGdal( job="LSTaustria", product="MOD11A1", extent="Austria", begin="2010001", end="2010005",
  93. #' SDSstring="101", outProj="+proj=utm +zone=34 +ellps=WGS84 +datum=WGS84 +units=m +no_defs")
  94. #'
  95. #' ### resamplingType examples
  96. #' runGdal( job="LSTaustria", product="MOD11A1", extent="Austria", begin="2010001", end="2010005",
  97. #' SDSstring="1", resamplingType="lanczos", outProj="32634", pixelSize=100)
  98. #'
  99. #' ### processing entire tiles and keeping Sinusoidal projection
  100. #' # This corresponds to a format conversion (eos-hdf04 to Geotiff) and
  101. #' # layer extraction (multi-layer to single layer)
  102. #' runGdal( job="LSTaustria", product="MOD11A1", tileH=18:19,tileV=4, begin="2010001", end="2010005",
  103. #' SDSstring="1", outProj="asIn")
  104. #'
  105. #' }
  106. #'
  107. #' @export runGdal
  108. #' @name runGdal
  109. runGdal <- function(product, collection=NULL,
  110. begin=NULL, end=NULL,
  111. extent=NULL, tileH=NULL, tileV=NULL,
  112. SDSstring=NULL, job=NULL, checkIntegrity=TRUE,
  113. forceDownload=TRUE, overwrite = FALSE, ...)
  114. {
  115. opts <- combineOptions(...)
  116. if(!opts$gdalOk) {
  117. stop("GDAL not installed or configured, read in '?MODISoptions' for help")
  118. }
  119. # absolutely needed
  120. product <- getProduct(product, quiet=TRUE)
  121. # optional and if missing it is added here:
  122. product$CCC <- getCollection(product,collection=collection, quiet = opts$quiet)
  123. tLimits <- transDate(begin=begin,end=end)
  124. dataFormat <- toupper(opts$dataFormat)
  125. if (dataFormat == 'RAW BINARY')
  126. {
  127. stop('in argument dataFormat=\'raw binary\', format not supported by GDAL (it is MRT specific) type: \'options("MODIS_gdalOutDriver")\' (column \'name\') to list available inputs')
  128. }
  129. if(dataFormat == 'HDF-EOS')
  130. {
  131. dataFormat <- "HDF4IMAGE"
  132. } else if(dataFormat == 'GEOTIFF')
  133. {
  134. dataFormat <- "GTIFF"
  135. }
  136. if(is.null(opts$gdalOutDriver))
  137. {
  138. opts$gdalOutDriver <- gdalWriteDriver()
  139. options("MODIS_gdalOutDriver"=opts$gdalOutDriver) # save for current session
  140. }
  141. if(dataFormat %in% toupper(opts$gdalOutDriver$name))
  142. {
  143. dataFormat <- grep(opts$gdalOutDriver$name, pattern=paste("^",dataFormat,"$",sep=""),ignore.case = TRUE,value=TRUE)
  144. of <- paste0(" -of ",dataFormat)
  145. extension <- getExtension(dataFormat)
  146. } else
  147. {
  148. stop('in argument dataFormat=\'',opts$dataFormat,'\', format not supported by GDAL type: \'gdalWriteDriver()\' (column \'name\') to list available inputs')
  149. }
  150. #### settings with messages
  151. # output pixel size in output proj units (default is "asIn", but there are 2 chances of changing this argument: pixelSize, and if extent comes from a Raster* object.
  152. if (!inherits(extent, "MODISextent")) {
  153. extent = if (product$TYPE[1] == "Tile" |
  154. (all(!is.null(extent) | !is.null(tileH) & !is.null(tileV)) &
  155. product$TYPE[1]=="CMG")) {
  156. getTile(x = extent, tileH = tileH, tileV = tileV)
  157. } else {
  158. NULL
  159. }
  160. }
  161. ### GDAL command line arguments -----
  162. ## obligatory arguments
  163. t_srs <- OutProj(product, extent, opts) # outProj
  164. tr <- PixelSize(extent, opts) # pixelSize
  165. rt <- ResamplingType(opts) # resamplingType
  166. s_srs <- InProj(product) # inProj
  167. te <- TargetExtent(extent, # targetExtent
  168. outProj = strsplit(t_srs, "'|\"")[[1]][2])
  169. ## non-obligatory arguments (GTiff blocksize and compression, see
  170. ## http://www.gdal.org/frmt_gtiff.html)
  171. bs <- BlockSize(opts) # blockSize
  172. cp <- OutputCompression(opts) # compression
  173. q <- QuietOutput(opts) # quiet
  174. lst_product <- vector("list", length(product$PRODUCT))
  175. for (z in seq_along(product$PRODUCT)) {
  176. # z=1
  177. todo <- paste(product$PRODUCT[[z]], product$CCC[[product$PRODUCT[z]]], sep = ".")
  178. if(z==1)
  179. {
  180. if (is.null(job))
  181. {
  182. job <- paste0(todo[1],"_",format(Sys.time(), "%Y%m%d%H%M%S"))
  183. cat("Output directory = ",paste0(normalizePath(opts$outDirPath,"/",mustWork=FALSE),"/",job)," (no 'job' name specified, generated (date/time based))\n")
  184. } else
  185. {
  186. cat("Output Directory = ",paste0(normalizePath(opts$outDirPath,"/",mustWork=FALSE),"/",job),"\n")
  187. }
  188. cat("########################\n")
  189. outDir <- file.path(opts$outDirPath,job,fsep="/")
  190. outDir <- gsub("//", "/", outDir)
  191. dir.create(outDir,showWarnings=FALSE,recursive=TRUE)
  192. }
  193. lst_todo <- vector("list", length(todo))
  194. for (u in seq_along(todo)) {
  195. # u=1
  196. ftpdirs <- list()
  197. if (length(unlist(product$SOURCE)) > 1) {
  198. server <- unlist(product$SOURCE)[which(unlist(product$SOURCE) == opts$MODISserverOrder[1])]
  199. } else {
  200. server <- unlist(product$SOURCE)
  201. }
  202. ftpdirs[[1]] <- as.Date(getStruc(product = strsplit(todo[u], "\\.")[[1]][1],
  203. collection = strsplit(todo[u], "\\.")[[1]][2],
  204. begin = tLimits$begin, end = tLimits$end,
  205. server = server)$dates)
  206. prodname <- strsplit(todo[u],"\\.")[[1]][1]
  207. coll <- strsplit(todo[u],"\\.")[[1]][2]
  208. avDates <- ftpdirs[[1]]
  209. avDates <- avDates[avDates!=FALSE]
  210. avDates <- avDates[!is.na(avDates)]
  211. sel <- as.Date(avDates)
  212. us <- sel >= tLimits$begin & sel <= tLimits$end
  213. if (sum(us,na.rm=TRUE)>0)
  214. {
  215. avDates <- avDates[us]
  216. lst_ofile <- vector("list", length(avDates))
  217. for (l in seq_along(avDates)) {
  218. # l=1
  219. files <- unlist(
  220. getHdf(product = prodname, collection = coll
  221. , begin = avDates[l], end = avDates[l]
  222. , extent = extent, checkIntegrity = checkIntegrity
  223. , stubbornness = opts$stubbornness
  224. , MODISserverOrder = opts$MODISserverOrder
  225. , forceDownload = forceDownload, wait = opts$wait)
  226. )
  227. files <- files[basename(files)!="NA"] # is not a true NA so it need to be like that na not !is.na()
  228. if(length(files)>0)
  229. {
  230. w <- getOption("warn")
  231. options("warn"= -1)
  232. SDS <- list()
  233. for (y in seq_along(files))
  234. { # get all SDS names for one chunk
  235. SDS[[y]] <- getSds(HdfName=files[y], SDSstring=SDSstring, method="GDAL")
  236. }
  237. options("warn"= w)
  238. if (!exists("NAS"))
  239. {
  240. NAS <- getNa(SDS[[1]]$SDS4gdal)
  241. }
  242. ## loop over sds
  243. ofiles <- character(length(SDS[[1]]$SDSnames))
  244. for (i in seq_along(SDS[[1]]$SDSnames)) {
  245. # i=1
  246. outname <- paste0(paste0(strsplit(basename(files[1]),"\\.")[[1]][1:2],collapse="."),
  247. ".", gsub(SDS[[1]]$SDSnames[i],pattern=" ",replacement="_"), extension)
  248. gdalSDS <- sapply(SDS,function(x){x$SDS4gdal[i]}) # get names of layer 'o' of all files (SDS)
  249. naID <- which(SDS[[1]]$SDSnames[i] == names(NAS))
  250. if(length(naID)>0)
  251. {
  252. srcnodata <- paste0(" -srcnodata ",NAS[[naID]])
  253. dstnodata <- paste0(" -dstnodata ",NAS[[naID]])
  254. } else
  255. {
  256. srcnodata <- NULL
  257. dstnodata <- NULL
  258. }
  259. if(length(grep(todo,pattern="M.D13C2\\.005"))>0)
  260. {
  261. if(i==1)
  262. {
  263. cat("\n###############\nM.D13C2.005 is likely to have a problem in metadata extent information, it is corrected on the fly\n###############\n")
  264. }
  265. ranpat <- makeRandomString(length=21)
  266. randomName <- paste0(outDir,"/deleteMe_",ranpat,".tif")
  267. on.exit(unlink(list.files(path=outDir,pattern=ranpat,full.names=TRUE),recursive=TRUE))
  268. for(ix in seq_along(gdalSDS))
  269. {
  270. cmd1 <- paste0(opts$gdalPath,"gdal_translate -a_nodata ",NAS[[naID]]," '",gdalSDS[ix],"' '",randomName[ix],"'")
  271. cmd2 <- paste0(opts$gdalPath,"gdal_edit.py -a_ullr -180 90 180 -90 '",randomName[ix],"'")
  272. if (.Platform$OS=="unix")
  273. {
  274. system(cmd1,intern=TRUE)
  275. system(cmd2,intern=TRUE)
  276. } else
  277. {
  278. shell(cmd1,intern=TRUE)
  279. shell(cmd2,intern=TRUE)
  280. }
  281. }
  282. gdalSDS <- randomName
  283. }
  284. # unix
  285. if (.Platform$OS=="unix") {
  286. ifile <- paste0(gdalSDS,collapse="' '")
  287. ofile <- paste0(outDir, '/', outname)
  288. if (!file.exists(ofile) | overwrite) {
  289. cmd <- paste0(opts$gdalPath,
  290. "gdalwarp",
  291. s_srs,
  292. t_srs,
  293. of,
  294. te,
  295. tr,
  296. cp,
  297. bs,
  298. rt,
  299. q,
  300. srcnodata,
  301. dstnodata,
  302. " -overwrite",
  303. " -multi",
  304. " \'", ifile,"\'",
  305. " ",
  306. ofile)
  307. cmd <- gsub(x=cmd,pattern="\"",replacement="'")
  308. system(cmd)
  309. }
  310. # windows
  311. } else {
  312. cmd <- paste0(opts$gdalPath,"gdalwarp")
  313. ofile <- paste0(outDir, '/', outname)
  314. ifile <- paste0(gdalSDS,collapse='" "')
  315. # GDAL < 1.8.0 doesn't support ' -overwrite'
  316. if (!file.exists(ofile) | overwrite) {
  317. if(file.exists(ofile))
  318. invisible(file.remove(ofile))
  319. shell(
  320. paste0(cmd,
  321. s_srs,
  322. t_srs,
  323. of,
  324. te,
  325. tr,
  326. cp,
  327. bs,
  328. rt,
  329. q,
  330. srcnodata,
  331. dstnodata,
  332. ' -multi',
  333. ' \"', ifile,'\"',
  334. ' \"', ofile,'\"')
  335. )
  336. }
  337. }
  338. if(length(grep(todo, pattern = "M.D13C2\\.005")) > 0) {
  339. unlink(list.files(path = outDir, pattern = ranpat,
  340. full.names = TRUE), recursive = TRUE)
  341. }
  342. ofiles[i] <- ofile
  343. }
  344. lst_ofile[[l]] <- ofiles
  345. } else {
  346. warning(paste0("No file found for date: ",avDates[l]))
  347. lst_ofile[[l]] <- NULL
  348. }
  349. }
  350. names(lst_ofile) <- avDates
  351. lst_todo[[u]] <- lst_ofile
  352. } else {
  353. lst_todo[[u]] <- NULL
  354. }
  355. }
  356. lst_product[[z]] <- lst_todo[[1]]
  357. }
  358. names(lst_product) <- paste(product$PRODUCT, product$CCC, sep = ".")
  359. return(lst_product)
  360. }