getCollection.R 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. #' Get Available Collections of MODIS Product(s)
  2. #'
  3. #' @description
  4. #' Checks and retrieves available MODIS collection(s) for a given product.
  5. #'
  6. #' @param product \code{character}. MODIS grid product to check for existing
  7. #' collections, see \code{\link{getProduct}}.
  8. #' @param collection \code{character} or \code{integer}. If provided, the
  9. #' function only checks if the specified collection exists and returns the
  10. #' collection number formatted based on the \code{as} parameter or \code{FALSE}
  11. #' if it doesn't exists. The check is performed on
  12. #' \href{https://lpdaac.usgs.gov/}{LP DAAC} as the exclusive source for several
  13. #' (but by far not all) products.
  14. #' @param newest \code{logical}. If \code{TRUE} (default), return only the
  15. #' newest collection, else return all available collections.
  16. #' @param forceCheck \code{logical}, defaults to \code{FALSE}. If \code{TRUE},
  17. #' connect to the 'LP DAAC' FTP server and get available collections, of which
  18. #' an updated version is permanently stored in
  19. #' \code{MODIS:::combineOptions()$auxPath}.
  20. #' @param as \code{character}, defaults to \code{'character'} which returns the
  21. #' typical 3-digit collection number (i.e., \code{"005"}). \code{as = 'numeric'}
  22. #' returns the result as \code{numeric} (i.e., \code{5}).
  23. #' @param quiet \code{logical}, defaults to \code{TRUE}.
  24. #' @param ... Additional arguments passed to \code{MODIS:::combineOptions}.
  25. #'
  26. #' @return
  27. #' A 3-digit \code{character} or \code{numeric} object (depending on 'as') or,
  28. #' if \code{length(product) > 1}, a \code{list} of such objects with each slot
  29. #' corresponding to the collection available for a certain product.
  30. #' Additionally, a text file in a hidden folder located in
  31. #' \code{getOption("MODIS_localArcPath")} as database for future calls. If
  32. #' 'collection' is provided, only the (formatted) collection (or \code{FALSE} if
  33. #' it could not be found) is returned.
  34. #'
  35. #' @author
  36. #' Matteo Mattiuzzi, Florian Detsch
  37. #'
  38. #' @seealso
  39. #' \code{\link{getProduct}}.
  40. #'
  41. #' @examples
  42. #' \dontrun{
  43. #'
  44. #' # update or get collections for MOD11C3 and MYD11C3
  45. #' getCollection(product="M.D11C3")
  46. #' getCollection(product="M.D11C3",newest=FALSE)
  47. #'
  48. #' getCollection(product="M.D11C3",collection=3)
  49. #' getCollection(product="M.D11C3",collection=41)
  50. #' getCollection(product="M.D11C3",collection="041")
  51. #' getCollection(product="M.D11C3",forceCheck=TRUE)
  52. #' }
  53. #'
  54. #' @export getCollection
  55. #' @name getCollection
  56. getCollection <- function(product,collection=NULL,newest=TRUE,forceCheck=FALSE,as="character",quiet=TRUE, ...)
  57. {
  58. opts <- combineOptions(...)
  59. ####
  60. # checks for product
  61. if (missing(product))
  62. {
  63. stop("Please provide a valid product")
  64. }
  65. productN <- getProduct(x = if (is.character(product)) {
  66. sapply(product, function(i) skipDuplicateProducts(i, quiet = quiet))
  67. } else product, quiet = TRUE)
  68. if (is.null(productN))
  69. {
  70. stop("Unknown product")
  71. }
  72. ## if 'collections' dataset does not exist in opts$auxPath, copy it from
  73. ## 'inst/external', then import data
  74. dir_aux <- opts$auxPath
  75. if (!dir.exists(dir_aux)) dir.create(dir_aux)
  76. fls_col <- file.path(dir_aux, "collections.RData")
  77. if (!file.exists(fls_col))
  78. invisible(
  79. file.copy(system.file("external", "collections.RData", package = "MODIS"),
  80. fls_col)
  81. )
  82. load(fls_col)
  83. if (forceCheck | sum(!productN$PRODUCT %in% colnames(MODIScollection))>0)
  84. {
  85. sturheit <- stubborn(level=opts$stubbornness)
  86. load(system.file("external", "MODIS_FTPinfo.RData", package = "MODIS"))
  87. for (i in seq_along(productN$PRODUCT))
  88. {
  89. ## retrieve ftp server address based on product source information
  90. server <- unlist(productN$SOURCE[[i]])
  91. ftp_id <- sapply(MODIS_FTPinfo, function(i) i$name %in% server)
  92. ftp_id <- which(ftp_id)[1]
  93. ftp <- file.path(MODIS_FTPinfo[[ftp_id]]$basepath, productN$PF1[i], "/")
  94. cat("Updating collection from", server[1], "for product:"
  95. , productN$PRODUCT[i], "\n")
  96. if(exists("dirs"))
  97. {
  98. suppressWarnings(rm(dirs))
  99. }
  100. for (g in 1:sturheit)
  101. {
  102. try(dirs <- filesUrl(ftp))
  103. if(exists("dirs"))
  104. {
  105. if(all(dirs != FALSE))
  106. {
  107. break
  108. }
  109. }
  110. }
  111. if (!exists("dirs"))
  112. {
  113. cat("FTP is not available, using stored information from previous calls (this should be mostly fine)\n")
  114. } else
  115. {
  116. ## choose relevant folders and remove empty ones
  117. dirs = grep(paste0(productN$PRODUCT[i], "\\.[[:digit:]]{3}"), dirs
  118. , value = TRUE)
  119. ids = sapply(file.path(ftp, dirs, "/"), function(ftpdir) {
  120. cnt = RCurl::getURL(ftpdir, dirlistonly = TRUE)
  121. dts = regmatches(cnt, regexpr("[[:digit:]]{4}\\.[[:digit:]]{2}\\.[[:digit:]]{2}", cnt))
  122. return(length(dts) > 0)
  123. })
  124. dirs = dirs[ids]
  125. ## information about products and collections
  126. ls_prod_col <- sapply(dirs, function(x) {strsplit(x, "\\.")})
  127. prod <- sapply(ls_prod_col, "[[", 1)
  128. coll <- sapply(ls_prod_col, "[[", 2)
  129. mtr <- cbind(prod,coll)
  130. mtr <- tapply(INDEX=mtr[,1],X=mtr[,2],function(x){x})
  131. maxrow <- max(nrow(MODIScollection),sapply(mtr,function(x)length(x)))
  132. basemtr <- matrix(NA,ncol=nrow(mtr), nrow = maxrow)
  133. colnames(basemtr) <- names(mtr)
  134. for(u in 1:ncol(basemtr))
  135. {
  136. basemtr[1:length(mtr[[u]]),u] <- mtr[[u]]
  137. }
  138. tmp = as.integer(basemtr)
  139. tmp[tmp >= 10 & !is.na(tmp)] = tmp[tmp >= 10 & !is.na(tmp)] / 10
  140. basemtr = matrix(as.integer(basemtr[order(tmp), ])
  141. , ncol = nrow(mtr), nrow = maxrow)
  142. colnames(basemtr) = names(mtr)
  143. ## if new collections are available,
  144. ## add additional rows to 'MODIScollection'
  145. if (nrow(MODIScollection) < maxrow & nrow(MODIScollection) > 0)
  146. {
  147. new_rows <- matrix(data = NA, nrow = maxrow-nrow(MODIScollection),
  148. ncol = ncol(MODIScollection))
  149. new_rows <- data.frame(new_rows)
  150. names(new_rows) <- names(MODIScollection)
  151. MODIScollection <- rbind(MODIScollection, new_rows)
  152. }
  153. if (ncol(MODIScollection)==0)
  154. { # relevant only for time
  155. MODIScollection <- data.frame(basemtr) # create new
  156. } else
  157. { # or update the available one
  158. MODIScollection[, colnames(basemtr)] = basemtr
  159. }
  160. }
  161. }
  162. }
  163. #write.table(MODIScollection,file.path(opts$auxPath,"collections",fsep="/"))
  164. ind <- which(colnames(MODIScollection)%in%productN$PRODUCT)
  165. if(length(ind)==1)
  166. {
  167. res <- list(MODIScollection[,ind])
  168. names(res) <- colnames(MODIScollection)[ind]
  169. } else if (length(ind)>1)
  170. {
  171. res <- as.list(MODIScollection[,ind])
  172. } else
  173. {
  174. stop("No data available, check product input?") # should not happen getProduct() should catch that before
  175. }
  176. res <- lapply(res, function(x){as.numeric(as.character(x[!is.na(x)]))})
  177. if (!is.null(collection))
  178. { # if collection is provided...return formatted collection or 'FALSE'
  179. isOk <- lapply(res,function(x)
  180. {
  181. if (as.numeric(collection) %in% x)
  182. {
  183. as.numeric(collection)
  184. } else
  185. {
  186. FALSE
  187. }
  188. })
  189. if (sum(isOk==FALSE)==length(isOk))
  190. {
  191. cat("Product(s) not available in collection '",collection,"'. Try 'getCollection('",productN$request,"',newest=FALSE,forceCheck=TRUE)'\n",sep="")
  192. return(invisible(isOk))
  193. } else if (sum(isOk==FALSE)>0 & sum(isOk==FALSE)<length(isOk))
  194. {
  195. cat("Not all the products in your input are available in collection '", collection,"'. Try 'getCollection('", productN$request, "', newest=FALSE, forceCheck=TRUE)'\n", sep="")
  196. }
  197. res <- isOk[isOk!=FALSE]
  198. } else if (newest)
  199. {
  200. if (!quiet) {
  201. cat("No collection specified, getting the newest for", productN$PRODUCT, "\n")
  202. }
  203. res <- lapply(res,function(x)
  204. { #select the newest
  205. x[order(sapply(x,function(c){
  206. s <- nchar(c)-1
  207. if (s==0)
  208. {
  209. c
  210. } else
  211. {
  212. c/as.numeric(paste(1,rep(0,s),sep=""))
  213. }}),decreasing=TRUE)][1]
  214. })
  215. }
  216. if (as=="character")
  217. {
  218. res <- lapply(res,function(x){sprintf("%03d",x)})
  219. }
  220. ## make changes permanent by saving updated 'collections' dataset in
  221. ## opts$auxPath
  222. save(MODIScollection, file = fls_col)
  223. return(res)
  224. }