├── .Rbuildignore ├── .gitignore ├── .travis.yml ├── DESCRIPTION ├── NAMESPACE ├── R ├── getSceneinfo.R ├── pixelToPolygon.R ├── selectScenes.R ├── subsetScenes.R ├── subsetScenesPlot.R ├── tsChips.R └── tsChipsRGB.R ├── README.md ├── inst └── extdata │ └── MDD.tar.gz ├── man ├── getSceneinfo.Rd ├── pixelToPolygon.Rd ├── selectScenes.Rd ├── subsetScenes.Rd ├── subsetScenesPlot.Rd ├── tsChips.Rd └── tsChipsRGB.Rd └── timeSyncR.Rproj /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | 5 | temp 6 | animation.gif -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: r 2 | sudo: required 3 | r_github_packages: 4 | - dutri001/bfastSpatial 5 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: timeSyncR 2 | Type: Package 3 | Title: Tools to aid visualization and interpretation of Landsat time series in R for calibration/validation of change detection methods. 4 | Version: 1.0 5 | Date: 2014-08-20 6 | Author: Ben DeVries 7 | Maintainer: Ben DeVries 8 | Description: Tools for visualization of Landsat time series data. Loosely based on the 9 | TimeSync method from Cohen et al. (2010), Remote Sensing of Environment. 10 | Depends: 11 | R (>= 3.0.0), 12 | raster, 13 | zoo, 14 | RColorBrewer, 15 | stringr, 16 | zoo 17 | License: What license is it under? 18 | Encoding: UTF-8 19 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2 (4.1.0): do not edit by hand 2 | 3 | export(getSceneinfo) 4 | export(pixelToPolygon) 5 | export(selectScenes) 6 | export(subsetScenes) 7 | export(subsetScenesPlot) 8 | export(tsChips) 9 | export(tsChipsRGB) 10 | import(RColorBrewer) 11 | import(raster) 12 | import(sp) 13 | import(stringr) 14 | import(zoo) 15 | -------------------------------------------------------------------------------- /R/getSceneinfo.R: -------------------------------------------------------------------------------- 1 | 2 | #' @title Retrieve Landsat info from filenames 3 | #' 4 | #' @description Parses through typical Landsat filenames and retrieves information on sensor and acquisition date. Vectorized over \code{sourcefile}. 5 | #' 6 | #' @param sourcefile Character. Filename of a landsat layer or dataset. 7 | #' @param ... Additional arguments to pass to \code{\link{write.csv}}. 8 | #' 9 | #' @author Ben DeVries and Loic Dutrieux 10 | #' 11 | #' @return a \code{data.frame} with parsed scene information from Landsat scene names 12 | #' 13 | #' @import stringr 14 | #' 15 | #' @examples 16 | #' getSceneinfo(c('ndvi.LC82300702014234.tar.gz', 'ndvi.LT52300702008234.tif')) 17 | #' 18 | #' \dontrun{ 19 | #' # see package 'bfastSpatial' for more info: 20 | #' library(devtools) 21 | #' install_github('dutri001/bfastSpatial') 22 | #' library(bfastSpatial) 23 | #' ?getSceneinfo 24 | #' } 25 | #' 26 | #' @export 27 | 28 | 29 | getSceneinfo <- function(sourcefile, ...) 30 | { 31 | # for the sake of convenience, sourcefile can be either a character vector of scene names (or subfolders) or the original .tar.gz or .tar files 32 | # this will check which it is and format accordingly 33 | if(!all(grepl(pattern='(LT4|LT5|LE7|LC8)\\d{13}', x=sourcefile))) 34 | warning('Some of the characters provided do not contain recognized Landsat5/7/8 scene ID') 35 | 36 | 37 | sourcefile <- str_extract(sourcefile, '(LT4|LT5|LE7|LC8)\\d{13}') 38 | 39 | 40 | # dates in LS naming system are formatted with year and julian day as one number - "%Y%j" (e.g. 2001036 = 2001-02-05) 41 | # reformat date as "%Y-%m-%d" (ie. yyyy-mm-dd) 42 | dates <- as.Date(substr(sourcefile, 10, 16), format="%Y%j") 43 | 44 | # identify the sensor 45 | sensor <- as.character(mapply(sourcefile, dates, FUN=function(x,y){ 46 | sen <- substr(x, 1, 3) 47 | if(is.na(sen)) NA 48 | else if(sen == "LE7" & y <= "2003-03-31") 49 | "ETM+ SLC-on" 50 | else if(sen == "LE7" & y > "2003-03-31") 51 | "ETM+ SLC-off" 52 | else if(sen == "LT5" | sen == "LT4") 53 | "TM" 54 | else if(sen == "LC8") 55 | "OLI" 56 | })) 57 | # extract path, row 58 | path <- as.numeric(substr(sourcefile, 4, 6)) 59 | row <- as.numeric(substr(sourcefile, 7, 9)) 60 | 61 | # throw all attributes into a data.frame 62 | info <- data.frame(sensor = sensor, path = path, row = row, date = dates) 63 | sourcefile[is.na(sourcefile)] <- 'Not recognised' 64 | row.names(info) <- sourcefile 65 | 66 | # optional: print to .csv for future reference 67 | if(hasArg(file)) 68 | write.csv(info, ...) 69 | 70 | return(info) 71 | } -------------------------------------------------------------------------------- /R/pixelToPolygon.R: -------------------------------------------------------------------------------- 1 | #' @title Convert pixel to polygon 2 | #' 3 | #' @description Convert a pixel to a polygon object by giving the raster object and cell number or xy coordinates vector 4 | #' 5 | #' @param x Raster* object 6 | #' @param cell Cell number (numeric) or vector of length 2 representing the x and y coordinate of the desired pixel 7 | #' 8 | #' @return Object of class \code{SpatialPolygons} encompassing the outline of the pixel located at \code{cell}. 9 | #' 10 | #' @export 11 | 12 | pixelToPolygon <- function(x, cell, plot = FALSE) { 13 | 14 | # check if cell is a number or an xy vector 15 | if(length(cell) == 1) { 16 | cell <- as.vector(xyFromCell(x, cell)) 17 | } else if(length(cell) > 2) { 18 | warning("taking the first 2 elemtns of cell.\n") 19 | cell <- cell[1:2] 20 | } 21 | 22 | # define extent 23 | # note that cellFromXY() retrieves the centre of the pixel -- we need the top-left for xmin,ymin 24 | shift <- res(x)[1] / 2 25 | e <- extent(c( 26 | cell[1] - shift, 27 | cell[1] + shift, 28 | cell[2] - shift, 29 | cell[2] + shift)) 30 | 31 | # convert to polygons and set projection 32 | e <- as(e, "SpatialPolygons") 33 | projection(e) <- projection(x) 34 | 35 | return(e) 36 | } -------------------------------------------------------------------------------- /R/selectScenes.R: -------------------------------------------------------------------------------- 1 | #' @title Select scenes from directory 2 | #' 3 | #' @description Select scenes (filenames) from a directory wth extents overlapping that of a given spatial object 4 | #' 5 | #' @param x The object whose extent will be checked. It can be a raster, spatial object, or an extent object 6 | #' @param targ Either a character vector of image filenames or an object of type list containing images to be tested 7 | #' @param padding Numeric. Additional area surrounding the extent to be included. Units depend on the projection of x 8 | #' @param verbose Logical. Send status reports to the console? 9 | #' 10 | #' @return Either a character vector of filenames with overlapping extents if \code{is.character(targ)}, or a list of raster objects whose extents overlap with \code{x} if \code{targ} is a spatial object. 11 | #' 12 | #' @author Ben DeVries 13 | #' 14 | #' @import raster 15 | #' @import sp 16 | #' @export 17 | 18 | selectScenes <- function(x, targ, padding=NULL, verbose=TRUE) 19 | { 20 | 21 | # set padding (if not already done) 22 | if(!is.numeric(padding)){ 23 | padding <- 0 24 | } 25 | 26 | # adjust extent of the input object 27 | e <- extent(x) 28 | e <- extent(c(xmin(e) - padding, 29 | xmax(e) + padding, 30 | ymin(e) - padding, 31 | ymax(e) + padding)) 32 | 33 | 34 | # check if targ is a list of rasters or a vector of filenames 35 | if(is.character(targ)){ 36 | # in this case, return a character vector of files to be used 37 | # this can be passed to the subset function (wraps around this fn) 38 | 39 | # define a logical vector to correspond to targ 40 | fits <- logical() 41 | for(i in 1:length(targ)){ 42 | b <- raster(targ[i]) 43 | 44 | if(projection(b) != projection(x)){ 45 | warning(targ[i], " projection does not match x. ", targ[i], " skipped.\n") 46 | } 47 | else{ 48 | if(is.null(intersect(e, extent(b)))){ 49 | fits[i] <- FALSE 50 | if(verbose){ 51 | cat(targ[i], " does not overlap with x extent.\n") 52 | } 53 | } else { 54 | fits[i] <- TRUE 55 | if(verbose){ 56 | cat(targ[i], " overlaps with x extent. Added to list.\n") 57 | } 58 | } 59 | } 60 | } 61 | # names of files to use 62 | keepscenes <- targ[which(fits)] 63 | } 64 | else if (is.list(targ)){ 65 | # in this case, return another list with only rasters that fit 66 | # this list can be passed to subset fn (wrapped around this fn) for further processing 67 | keepscenes <- lapply(targ, FUN=function(x){ 68 | if(is.null(intersect(e, extent(x)))) 69 | NULL 70 | else{ 71 | x 72 | } 73 | }) 74 | } 75 | else{ 76 | # if the wrong object type for targ is supplied 77 | stop("targ must be either a character vector (filenames) or a list of raster layers.") 78 | } 79 | 80 | # if keepscenes is a list, remove all NULL elements 81 | if(is.list(keepscenes)){ 82 | keepscenes <- keepscenes[-which(unlist(lapply(keepscenes, is.null)))] 83 | } 84 | 85 | return(keepscenes) 86 | } -------------------------------------------------------------------------------- /R/subsetScenes.R: -------------------------------------------------------------------------------- 1 | #' @title Subset Scenes 2 | #' 3 | #' @description Subset scenes from a list or directory based on a given spatial object. Takes results of \code{selectScenes()} and subsets based on intersecting extents. 4 | #' 5 | #' @param x A spatial object (polygon, or otherwise) to base the subset extent on 6 | #' @param targ A character vector of filenames, or a list of raster layers to be subsetted 7 | #' @param filename Character. Optional: If results are to be written to file, then a character vector of \code{length = length(targ)} should be provided. Not supported yet (leave blank, or it will return an error) 8 | #' @param verbose Logical. Print status messages to the console? 9 | #' 10 | #' @return A list of avialable rasters cropped to the extent of the input object + padding. Side effect: cropped rasters written to file if a filename vector is supplied (not supported yet). 11 | #' 12 | #' @author Ben DeVries 13 | #' 14 | #' @import raster 15 | #' @import sp 16 | #' @export 17 | 18 | subsetScenes <- function(x, targ, padding=NULL, filename = "", verbose=TRUE, ...){ 19 | 20 | # set padding (if not already done) 21 | if(!is.numeric(padding)){ 22 | padding <- 0 23 | } 24 | 25 | # check if filenames correspond with targ 26 | if(filename != "" & length(filename) != length(targ)){ 27 | stop("filename should be of same length as targ") 28 | } 29 | 30 | # narrow down targ vector/list based on extent overlaps 31 | targ <- selectScenes(x, targ, padding=padding, verbose=verbose) 32 | 33 | # adjust extent of the input object 34 | e <- extent(x) 35 | e <- extent(c(xmin(e) - padding, 36 | xmax(e) + padding, 37 | ymin(e) - padding, 38 | ymax(e) + padding)) 39 | 40 | # read from file and subset these rasters and (optionally) write to output file 41 | if(is.character(targ)){ 42 | # loop through files and subset and write separately 43 | b <- list() 44 | for(i in 1:length(targ)){ 45 | b[[i]] <- brick(targ[i]) 46 | # TODO: detect whether input files are single or multi-layered 47 | # if single, b <- raster(); else b <- brick() 48 | if(filename != ""){ 49 | b[[i]] <- crop(b[[i]], e, filename=filename[i], ...) 50 | } else { 51 | b[[i]] <- crop(b[[i]], e) 52 | } 53 | } 54 | } else if(is.list(targ)){ 55 | b <- targ 56 | # then, same as above: 57 | for(i in 1:length(b)){ 58 | if(filename != ""){ 59 | b[[i]] <- crop(b[[i]], e, filename=filename[i], ...) 60 | } else { 61 | b[[i]] <- crop(b[[i]], e) 62 | } 63 | # TODO: detect if resulting image is only NA's or defined background value (e.g. 0) 64 | # if yes, reject it 65 | } 66 | } else { 67 | stop("targ must be either a character vector (filenames) or a list of raster layers.") 68 | } 69 | 70 | # return a list of cropped raster layers 71 | return(b) 72 | } -------------------------------------------------------------------------------- /R/subsetScenesPlot.R: -------------------------------------------------------------------------------- 1 | #' @title Plot subsetted scenes in sequence 2 | #' 3 | #' @description Plot subsetted scenes in sequence. Same as \code{\link{subsetScenes}}, except that it plots the subsets afterwards. 4 | #' 5 | #' @param ... Arguments to be passed to \code{\link{subsetScenes}} 6 | #' @param bands Numeric. Bands to display if an RGB composite (ignored if \code{length(bands)} is less than 3) 7 | #' @param stretch Character. Stretch to apply to composite (see \code{\link{plotRGB}} for more info) 8 | #' @param textcol Character. Colour of text to display on image. 9 | #' 10 | #' @return list of raster subsets (see \code{\link{subsetScenes}}) 11 | #' 12 | #' @import raster 13 | #' @import sp 14 | #' @export 15 | 16 | subsetScenesPlot <- function (..., bands = NULL, stretch = NULL, textcol = "white") 17 | { 18 | op <- par(mfrow = c(1, 1)) 19 | scenes <- subsetScenes(...) 20 | fl <- sapply(scenes, FUN = function(x) names(x)[1]) 21 | 22 | if(!is.null(scenes)){ 23 | for (i in 1:length(scenes)) { 24 | print(fl[i]) 25 | if (nlayers(scenes[[i]]) >= 3) { 26 | if (is.null(bands)) { 27 | plotRGB(scenes[[i]], 1, 2, 3, stretch = stretch) 28 | } 29 | else { 30 | plotRGB(scenes[[i]], bands[1], bands[2], bands[3], 31 | stretch = stretch) 32 | } 33 | e <- extent(scenes[[i]]) 34 | text(x = xmin(e) + xmax(e)/2, y = ymin(e) + 100*res(scenes[[i]])[1], labels = fl[i], col = textcol) 35 | } 36 | else if (nlayers(scenes[[i]]) == 2) { 37 | plot(raster(scenes[[i]], 1), main = fl[i]) 38 | } 39 | else { 40 | plot(scenes[[i]], main = fl[i]) 41 | } 42 | x <- list(...)[[1]] 43 | if (class(x) %in% c("SpatialPolygons", "SpatialPolygonsDataFrame", "SpatialPoints", "SpatialPointsDataFrame")) { 44 | plot(x, add = TRUE) 45 | } 46 | if(i != length(scenes)) 47 | readline("Press any key to continue to next screen: \n") 48 | par(op) 49 | } 50 | } else { 51 | cat("No scenes to display.") 52 | } 53 | 54 | return(scenes) 55 | } -------------------------------------------------------------------------------- /R/tsChips.R: -------------------------------------------------------------------------------- 1 | #' @title Plot TS Chips 2 | #' 3 | #' @description Plot image TS chips based on a location, area buffer, and time buffer 4 | #' 5 | #' @param x RasterBrick. Image time series brick 6 | #' @param loc Location. Can be a vector of length 2 representing the x,y coordinates, or a SpatialPolygons or SpatialPoints object of \code{nrow = 1} (the first row will be taken if \code{nrow(loc) > 1}), or an extent object (which will be extended if a buffer > 0 is given; see below) 7 | #' @param buff Numeric. Number of pixels to buffer the location in all directions. A higher buffer will essentially zoom out. 8 | #' @param start Date. OptionaL: earliest date ("yyyy-dd-mm") to display. 9 | #' @param end Date. Optional: latest date ("yyyy-dd-mm") to display. 10 | #' @param percNA Numeric. Maximum allowable \% NA in the cropped image chips 11 | #' @param cols Character. Name of colour map to use (see display.brewer.all()) or a character vector with two or more colour names or hexadecimal values (as strings) between which to interpolate. 12 | #' @param nbks Numeric. Number of breaks in the colour map 13 | #' @param nc/nr Numeric. Number of columns and rows to plot, respectively. If the number of layers is greater than \code{nc*nr}, a screen prompt will lead to the next series of plots. These cannot exceed 4. 14 | #' @param plot Logical. Plot pixel time series? 15 | #' @param plotlab Character. y-axis label for the pixel time series plot. 16 | #' @param exportChips Logical. Export processed chips to workspace as a rasterBrick? 17 | #' @param exportZoo Logical. Export pixel time series as a zoo object? 18 | #' @param show Logical. Plot the chips? Can be set to \code{FALSE} if you just want to export the chips as rasterBrick with or without the ggplot object. 19 | #' 20 | #' @return \code{NULL} if \code{exportChips = FALSE} and \code{exportZoo = FALSE}, with the side effect of time series chips being plotted. If \code{exportChips = TRUE}, an object of type \code{RasterBrick}. if \code{exportZoo = TRUE}, an object of type \code{zoo}. 21 | #' 22 | #' @author Ben DeVries 23 | #' 24 | #' @import raster 25 | #' @import zoo 26 | #' @import RColorBrewer 27 | #' @export 28 | #' 29 | #' @references 30 | #' Cohen, W. B., Yang, Z., Kennedy, R. (2010). Detecting trends in forest disturbance and recovery using yearly Landsat time series: 2. TimeSync - Tools for calibration and validation. Remote Sensing of Environment, 114(12), 2911-2924. 31 | #' 32 | #' @examples 33 | #' \dontrun{ 34 | #' library(bfastSpatial) 35 | #' data(tura) 36 | #' 37 | #' tsChips(tura, loc = c(820796, 831198)) 38 | #' tsChips(tura, loc = c(820796, 831198), buff = 50) # zoom out 39 | #' tsChips(tura, loc = c(820796, 831198), buff = 50, percNA = 80) # allow more NA's in field of view 40 | #' tsChips(tura, loc = c(820796, 831198), start = "2007-01-01", end = "2012-01-01", plot = TRUE, plotlab = 'NDVI') # restrict dates and produce pixel time series plot afterwards 41 | #' 42 | #' # alternative colour scales 43 | #' library(RColorBrewer) 44 | #' display.brewer.all() 45 | #' tsChips(tura, loc = c(820796, 831198), start = "1999-01-01", cols = "Spectral") 46 | #' tsChips(tura, loc = c(820796, 831198), start = "1999-01-01", cols = "Greens") 47 | #' 48 | #' tsChips(tura, loc = c(820796, 831198), start = "1999-01-01", cols = c("red", "yellow", "blue")) 49 | #' tsChips(tura, loc = c(820796, 831198), start = "1999-01-01", cols = c("#DEEBF7", "#3182BD")) 50 | #' 51 | #' # export image chips as a raster brick 52 | #' chips <- tsChips(tura, loc = c(820796, 831198), start = "1999-01-01", percNA = 0, exportChips = TRUE) 53 | #' chips2 <- tsChips(tura, loc = c(820796, 831198), start = "1999-01-01", percNA = 100, exportChips = TRUE) 54 | #' nlayers(chips) 55 | #' nlayers(chips2) 56 | #' 57 | #' # export centre pixel time series as a zoo object 58 | #' z <- tsChips(tura, loc = c(820796, 831198), start = "1999-01-01", exportZoo = TRUE) 59 | #' plot(z) 60 | #' 61 | #' # draw a custom SpatialPoygons object and plot around that 62 | #' plot(tura, 42) 63 | #' pol <- drawPoly(sp = TRUE) # click 'Finish' in plot window when done 64 | #' projection(pol) <- projection(tura) 65 | #' plot(tura, 42); plot(pol, add=TRUE) 66 | #' tsChips(tura, loc = pol, start = "1999-01-01", plot = TRUE, plotlab = 'NDVI') 67 | #' } 68 | 69 | 70 | tsChips <- function(x, loc, start = NULL, end = NULL, buff = 17, percNA = 20, cols = "PiYG", nbks = 35, nc = 3, nr = 3, plot = FALSE, plotlab = 'data', exportChips = FALSE, exportZoo = FALSE, show = TRUE) { 71 | 72 | # get sceneinfo 73 | s <- getSceneinfo(names(x)) 74 | 75 | # reformat buffer using image resolution 76 | buff <- buff * res(x)[1] 77 | 78 | # check location format and make a buffered extent object 79 | if(class(loc) == "numeric" & length(loc) != 2){ 80 | stop("loc should be either a numeric vector of length 2 or a spatial object (polygon, points or extent).") 81 | } else if(class(loc) == "numeric"){ 82 | e <- extent(c(loc[1] - buff, loc[1] + buff, loc[2] - buff, loc[2] + buff)) 83 | } else if(class(loc) %in% c("SpatialPolygons", "SpatialPolygonsDataFrame", "SpatialPoints", "SpatialPointsDataFrame")){ 84 | if(length(loc) > 1){ 85 | warning("only taking the 1st feature of loc") 86 | loc <- loc[1, ] 87 | } 88 | e <- extent(loc) 89 | e <- extent(c(xmin(e) - buff, xmax(e) + buff, ymin(e) - buff, ymax(e) + buff)) 90 | } else if(class(loc) == "extent"){ 91 | e <- loc 92 | e <- extent(c(xmin(e) - buff, xmax(e) + buff, ymin(e) - buff, ymax(e) + buff)) 93 | } 94 | 95 | # crop input brick 96 | xe <- crop(x, e) 97 | se <- getSceneinfo(names(xe)) 98 | 99 | # start and end dates 100 | if(!is.null(start)){ 101 | start <- as.Date(start) 102 | xe <- raster::subset(xe, subset = which(se$date >= start)) 103 | se <- getSceneinfo(names(xe)) 104 | } else { 105 | start <- as.Date(min(se$date)) # to be used in ggplot later 106 | } 107 | 108 | if(!is.null(end)){ 109 | end <- as.Date(end) 110 | xe <- raster::subset(xe, subset = which(se$date <= end)) 111 | se <- getSceneinfo(names(xe)) 112 | } else { 113 | end <- as.Date(max(se$date)) # to be used in ggplot later 114 | } 115 | 116 | # reorder scenes 117 | xe <- raster::subset(xe, subset = order(se$date)) 118 | se <- getSceneinfo(names(xe)) 119 | 120 | # filter out scenes with too many NA's 121 | if(percNA > 100) 122 | percNA <- 100 123 | nas <- sapply(freq(xe), FUN=function(x) as.numeric(x[is.na(x[, 1]), 2] / ncell(xe) * 100)) 124 | nas[which(sapply(nas, length) == 0)] <- 0 125 | nas <- unlist(nas) 126 | if(percNA == 0){ 127 | xe <- raster::subset(xe, subset = which(nas == percNA)) 128 | } else { 129 | xe <- raster::subset(xe, subset = which(nas < percNA)) 130 | } 131 | 132 | # final sceneinfo data.frame 133 | se <- getSceneinfo(names(xe)) 134 | 135 | # colour map 136 | if(length(cols) == 1){ 137 | require(RColorBrewer) 138 | cols <- colorRampPalette(brewer.pal(9, cols))(nbks) 139 | } else { 140 | cols <- colorRampPalette(cols)(nbks) 141 | } 142 | 143 | # breaks defined based on extreme values 144 | minbk <- minValue(xe) 145 | if(!any(!is.na(minbk))) 146 | stop("No non-NA values in the defined image chips.") 147 | minbk <- min(minbk) 148 | maxbk <- maxValue(xe) 149 | if(!any(!is.na(maxbk))) 150 | stop("No non-NA values in the defined image chips.") 151 | maxbk <- max(maxbk) 152 | breaks <- seq(minbk, maxbk, length = nbks) 153 | 154 | # add polygon or point to plot if given 155 | if(class(loc) %in% c("SpatialPolygons", "SpatialPolygonsDataFrame", "SpatialPoints", "SpatialPointsDataFrame")){ 156 | addfun <- function() plot(loc, extent = e, add=TRUE) 157 | } else { 158 | addfun <- function() NULL 159 | } 160 | 161 | # plots 162 | op <- par(mfrow = c(nr, nc)) 163 | pps <- nc * nr 164 | nscreens <- ceiling(nlayers(xe) / pps) 165 | for(i in seq(1, nlayers(xe), by = pps)){ 166 | if((nlayers(xe) - i) < pps){ 167 | xes <- raster::subset(xe, subset = c(i:nlayers(xe))) 168 | par(op) 169 | plot(xes, breaks = breaks, col = cols, main = getSceneinfo(names(xes))$date, legend=FALSE, nc = nc, nr = nr, addfun = addfun) 170 | } else { 171 | xes <- raster::subset(xe, subset = c(i:(i + pps - 1))) 172 | plot(xes, breaks = breaks, col = cols, main = getSceneinfo(names(xes))$date, legend=FALSE, nc = nc, nr = nr, addfun = addfun) 173 | readline("Press any key to continue to next screen: \n") 174 | } 175 | } 176 | 177 | # prepare zoo objects 178 | if(plot | exportZoo){ 179 | if(is.numeric(loc)){ 180 | z <- x[cellFromXY(x, loc)][1, ] 181 | } else if(class(loc) %in% c("SpatialPolygons", "SpatialPolygonsDataFrame")) { 182 | z <- apply(extract(x, loc)[[1]], 2, mean) 183 | } else if(class(loc) == 'Extent') { 184 | loc <- c(mean(xmin(e), xmax(e)), mean(ymin(e), ymax(e))) 185 | z <- x[cellFromXY(x, loc)][1, ] 186 | } else { 187 | z <- x$R[cellFromXY(x$R, as.vector(coordinates(loc)))][1, ] 188 | } 189 | z <- zoo(z, s$date) 190 | z <- na.omit(z) 191 | z <- window(z, start = start, end = end) 192 | } 193 | 194 | # plot pixel time series 195 | if(plot) { 196 | readline("Press any key to view time series plots: \n") 197 | par(mfrow = c(1, 1)) 198 | plot(z, xlab = 'Time', type = 'b', pch = '*', ylab = plotlab) 199 | } 200 | 201 | # decide what to return 202 | if(exportChips & exportZoo){ 203 | return(list(tsChips = xe, zoo = z)) 204 | } else if(exportChips & !exportZoo) { 205 | return(xe) 206 | } else if(!exportChips & exportZoo) { 207 | return(z) 208 | } else { 209 | return(NULL) 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /R/tsChipsRGB.R: -------------------------------------------------------------------------------- 1 | #' @title Plot TS Chips (RGB version) 2 | #' 3 | #' @description Plot image TS chips from 3 raster bricks (as RGB composites) based on a location, area buffer, and time buffer 4 | #' 5 | #' @param xr/xg/xb RasterBrick. Image time series bricks representing the red, green and blue channel, respectively 6 | #' @param loc Location. Can be a vector of length 2 representing the x,y coordinates, or a SpatialPolygons or SpatialPoints object of \code{nrow = 1} (the first row will be taken if \code{nrow(loc) > 1}), or an extent object (which will be extended if a buffer > 0 is given; see below) 7 | #' @param buff Numeric. Number of pixels to buffer the location in all directions. A higher buffer will essentially zoom out. 8 | #' @param start Date. OptionaL: earliest date ("yyyy-dd-mm") to display. 9 | #' @param end Date. Optional: latest date ("yyyy-dd-mm") to display. 10 | #' @param percNA Numeric. Maximum allowable \% NA in the cropped image chips 11 | #' @param nc/nr Numeric. Number of columns and rows to plot, respectively. If the number of layers is greater than \code{nc*nr}, a screen prompt will lead to the next series of plots. These cannot exceed 4. 12 | #' @param plot Logical. Plot individual band time series? 13 | #' @param exportChips Logical. Export processed chips to workspace as a list of rasterBricks (R, G, B)? 14 | #' @param exportZoo Logical. Export pixel time series as \code{zoo} objects? 15 | #' @param textcol Character. Colour of text showing image date (can also be hexadecimal) 16 | #' @param show Logical. Show image chips? Set to \code{FALSE} if you just want to export them to rasterBricks and/or export the \code{ggplot} object without viewing the chips. 17 | #' @param plotlabs Character. Vector of length 3 indicating the labels for each of the zoo plots (if \code{plot=TRUE}) 18 | #' @param ... Arguments to be passed to \code{\link{plotRGB}} 19 | #' 20 | #' @return \code{NULL} if \code{exportZoo = FALSE} and \code{exportChips = FALSE} or a list of \code{zoo} time series objects if \code{exportZoo = TRUE}, or a list of subsetted and cropped rasterBricks if \code{exportChips = TRUE}, with the side effect of time series chips being plotted in all cases. If both \code{exportChips} and \code{exportZoo} are \code{TRUE}, a list consisting both lists will be returned. 21 | #' 22 | #' @details see \link{http://bendv.github.io/timeSyncR/} for a short tutorial on \code{tsChipsRGB} 23 | #' 24 | #' @author Ben DeVries 25 | #' 26 | #' @import zoo 27 | #' @import raster 28 | #' @export 29 | #' 30 | #' @references 31 | #' Cohen, W. B., Yang, Z., Kennedy, R. (2010). Detecting trends in forest disturbance and recovery using yearly Landsat time series: 2. TimeSync - Tools for calibration and validation. Remote Sensing of Environment, 114(12), 2911-2924. 32 | #' 33 | 34 | tsChipsRGB <- function(xr, xg, xb, loc, start = NULL, end = NULL, buff = 17, percNA = 20, nc = 3, nr = 3, plot = FALSE, exportChips = FALSE, exportZoo = FALSE, textcol = "white", plotlabs = c('red', 'green', 'blue'), ...) { 35 | 36 | # check that all bricks have the same number of layers and are comparable 37 | if(!compareRaster(xr, xg, xb)) 38 | stop("Input RGB rasterBricks do not compare") 39 | if(length(unique(nlayers(xr), nlayers(xg), nlayers(xb))) > 1) 40 | stop("Input RGB rasterBricks have different number of layers") 41 | 42 | x <- list(R = xr, G = xg, B = xb) 43 | 44 | # get sceneinfo 45 | s <- getSceneinfo(names(x$R)) 46 | 47 | # set z-dimensions of each brick 48 | x <- lapply(x, FUN=function(x) setZ(x, s$date)) 49 | 50 | # reformat buffer using image resolution 51 | buff <- buff * res(x$R)[1] 52 | 53 | # check location format and make a buffered extent object 54 | if(class(loc) == "numeric" & length(loc) != 2){ 55 | stop("loc should be either a numeric vector of length 2 or a spatial object (polygon, points or extent).") 56 | } else if(class(loc) == "numeric"){ 57 | e <- extent(c(loc[1] - buff, loc[1] + buff, loc[2] - buff, loc[2] + buff)) 58 | } else if(class(loc) %in% c("SpatialPolygons", "SpatialPolygonsDataFrame", "SpatialPoints", "SpatialPointsDataFrame")){ 59 | if(length(loc) > 1){ 60 | warning("only taking the 1st feature of loc") 61 | loc <- loc[1, ] 62 | } 63 | e <- extent(loc) 64 | e <- extent(c(xmin(e) - buff, xmax(e) + buff, ymin(e) - buff, ymax(e) + buff)) 65 | } else if(class(loc) == "extent"){ 66 | e <- loc 67 | e <- extent(c(xmin(e) - buff, xmax(e) + buff, ymin(e) - buff, ymax(e) + buff)) 68 | } 69 | 70 | # crop input bricks 71 | xe <- lapply(x, FUN=function(x) crop(x, e)) 72 | 73 | # start and end dates 74 | if(!is.null(start)){ 75 | start <- as.Date(start) 76 | xe <- lapply(xe, FUN=function(x) raster::subset(x, subset = which(getZ(x) >= start))) 77 | xe <- lapply(xe, FUN=function(x) setZ(x, getSceneinfo(names(x))$date)) 78 | } else { 79 | start <- as.Date(min(getZ(xe[[1]]))) # to be used in ggplot later 80 | } 81 | 82 | if(!is.null(end)){ 83 | end <- as.Date(end) 84 | xe <- lapply(xe, FUN=function(x) raster::subset(x, subset = which(getZ(x) <= end))) 85 | xe <- lapply(xe, FUN=function(x) setZ(x, getSceneinfo(names(x))$date)) 86 | } else { 87 | end <- as.Date(max(getZ(xe[[1]]))) # to be used in ggplot later 88 | } 89 | 90 | # reorder scenes 91 | xe <- lapply(xe, FUN=function(x) raster::subset(x, subset = order(getZ(x)))) 92 | 93 | # filter out scenes with too many NA's 94 | # done on 1st band, assuming mask has been applied uniformly 95 | if(percNA < 100){ 96 | nas <- freq(xe[[1]], value = NA) / ncell(xe[[1]]) * 100 97 | for(i in 1:length(xe)){ 98 | if(percNA == 0){ 99 | xe[[i]] <- raster::subset(xe[[i]], subset = which(nas == percNA)) 100 | } else { 101 | xe[[i]] <- raster::subset(xe[[i]], subset = which(nas < percNA)) 102 | } 103 | } 104 | } 105 | 106 | # function to add spatial data (if present) 107 | if(class(loc) %in% c("SpatialPolygons", "SpatialPolygonsDataFrame", "SpatialPoints", "SpatialPointsDataFrame")){ 108 | addfun <- function() plot(loc, add=TRUE) 109 | } else { 110 | addfun <- function() NULL 111 | } 112 | 113 | # plots on separate screens if needed 114 | op <- par(mfrow = c(nr, nc)) 115 | pps <- nc * nr 116 | nscreens <- ceiling(nlayers(xe[[1]]) / pps) 117 | 118 | for(i in seq(1, nlayers(xe[[1]]), by = pps)){ 119 | if((nlayers(xe[[1]]) - i) <= pps){ 120 | xes <- lapply(xe, FUN=function(y) raster::subset(y, subset = c(i:nlayers(y)))) 121 | for(j in 1:nlayers(xes[[1]])){ 122 | b <- brick(raster(xes[[1]], j), raster(xes[[2]], j), raster(xes[[3]], j)) 123 | err <- try({ 124 | plotRGB(b, 1, 2, 3, addfun=addfun, ...) 125 | text(x = (xmin(e) + xmax(e))/2, y = ymin(e) + 2*res(xr)[1], labels = getZ(xes[[1]])[j], col = textcol) 126 | }, silent = TRUE) 127 | if(class(err) == "try-error") 128 | plot.new() 129 | } 130 | par(op) 131 | } else { 132 | xes <- lapply(xe, FUN=function(y) raster::subset(y, subset = c(i:(i + pps - 1)))) 133 | for(j in 1:nlayers(xes[[1]])){ 134 | b <- brick(raster(xes[[1]], j), raster(xes[[2]], j), raster(xes[[3]], j)) 135 | err <- try({ 136 | plotRGB(b, 1, 2, 3, addfun=addfun, ...) 137 | text(x = (xmin(e) + xmax(e))/2, y = ymin(e) + 2*res(xr)[1], labels = getZ(xes[[1]])[j], col = textcol) 138 | }, silent = TRUE) 139 | #if(class(err) == "try-error") 140 | # plot.new() 141 | } 142 | readline("Press any key to see next screen:\n") 143 | } 144 | } 145 | 146 | 147 | # prepare zoo objects 148 | if(plot | exportZoo){ 149 | if(is.numeric(loc)){ 150 | z <- list(R = x$R[cellFromXY(x$R, loc)][1, ], 151 | G = x$G[cellFromXY(x$G, loc)][1, ], 152 | B = x$B[cellFromXY(x$B, loc)][1, ]) 153 | } else if(class(loc) %in% c("SpatialPolygons", "SpatialPolygonsDataFrame")) { 154 | z <- list(R = apply(extract(x$R, loc)[[1]], 2, mean), 155 | G = apply(extract(x$G, loc)[[1]], 2, mean), 156 | B = apply(extract(x$B, loc)[[1]], 2, mean)) 157 | } else if(class(loc) == 'Extent') { 158 | loc <- c(mean(xmin(e), xmax(e)), mean(ymin(e), ymax(e))) 159 | z <- list(R = x$R[cellFromXY(x$R, loc)][1, ], 160 | G = x$G[cellFromXY(x$G, loc)][1, ], 161 | B = x$B[cellFromXY(x$B, loc)][1, ]) 162 | } else { 163 | z <- list(R = x$R[cellFromXY(x$R, as.vector(coordinates(loc)))][1, ], 164 | G = x$G[cellFromXY(x$G, as.vector(coordinates(loc)))][1, ], 165 | B = x$B[cellFromXY(x$B, as.vector(coordinates(loc)))][1, ]) 166 | } 167 | z <- lapply(z, FUN=function(x) zoo(x, s$date)) 168 | z <- lapply(z, na.omit) 169 | z <- lapply(z, FUN=function(x) window(x, start = start, end = end)) 170 | } 171 | 172 | # plot 173 | if(plot) { 174 | readline("Press any key to view time series plots: \n") 175 | lo <- matrix(c(1:3), nr=3, nc=1) 176 | layout(lo) 177 | op <- par(mar = c(0, 5, 0, 5), oma = c(3, 3, 3, 3)) 178 | plot(z$R, xlab = '', xaxt = 'n', type = 'b', pch = '*', ylab = plotlabs[1], col = 'red') 179 | plot(z$G, xlab = '', xaxt = 'n', yaxt = 'n', type = 'b', pch = '*', ylab = plotlabs[2], col = 'dark green') 180 | axis(4) 181 | plot(z$B, xlab = '', xaxt = 'n', type = 'b', pch = '*', ylab = plotlabs[3], col = 'blue') 182 | datelab <- seq.Date(as.Date(start), as.Date(end), by = '1 year') 183 | axis(1, at = datelab, labels = format(datelab, format = '%Y')) 184 | par(op) 185 | } 186 | 187 | # decide what to return 188 | if(exportChips & exportZoo){ 189 | return(list(tsChips = xe, zoo = z)) 190 | } else if(exportChips & !exportZoo) { 191 | return(xe) 192 | } else if(!exportChips & exportZoo) { 193 | return(z) 194 | } else { 195 | return(NULL) 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # News 2 | 3 | #### April 10, 2015 4 | - ```ggplot``` option in ```tsChipsRGB()``` replaced by ```plot```, which plots zoo objects instead of using ggplot2 5 | - ability to export RGB chips as a list of raster bricks added (```exportChips```) 6 | - ability to export centre pixel time series as a list of zoo objects added (```exportZoo```) 7 | - ```tsChips()``` updated. See ```?tsChips``` for more info. 8 | - [tutorial](http://bendv.github.io/timeSyncR) updated 9 | 10 | # timeSyncR 11 | 12 | #### Summary 13 | Tools to aid visualization and interpretation of Landsat time series in R for calibration/validation of change detection methods. This package is (loosely) based on the TimeSync method (Cohen et al., 2010). 14 | 15 | #### Installation 16 | ```R 17 | library(devtools) 18 | install_github('bendv/timeSyncR') 19 | library(timeSyncR) 20 | ``` 21 | 22 | #### Implementation 23 | Check out this [short tutorial](http://bendv.github.io/timeSyncR). This is very much a work in progress... 24 | 25 | #### References 26 | Cohen, W. B., Yang, Z., & Kennedy, R. (2010). Detecting trends in forest disturbance and recovery using yearly Landsat time series: 2. TimeSync - Tools for calibration and validation. Remote Sensing of Environment, 114(12), 2911–2924. doi:10.1016/j.rse.2010.07.010 27 | -------------------------------------------------------------------------------- /inst/extdata/MDD.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendv/timeSyncR/14bfc1e3aec929a0b29e2fc102f29fe75d926c8f/inst/extdata/MDD.tar.gz -------------------------------------------------------------------------------- /man/getSceneinfo.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.0): do not edit by hand 2 | % Please edit documentation in R/getSceneinfo.R 3 | \name{getSceneinfo} 4 | \alias{getSceneinfo} 5 | \title{Retrieve Landsat info from filenames} 6 | \usage{ 7 | getSceneinfo(sourcefile, ...) 8 | } 9 | \arguments{ 10 | \item{sourcefile}{Character. Filename of a landsat layer or dataset.} 11 | 12 | \item{...}{Additional arguments to pass to \code{\link{write.csv}}.} 13 | } 14 | \value{ 15 | a \code{data.frame} with parsed scene information from Landsat scene names 16 | } 17 | \description{ 18 | Parses through typical Landsat filenames and retrieves information on sensor and acquisition date. Vectorized over \code{sourcefile}. 19 | } 20 | \examples{ 21 | getSceneinfo(c('ndvi.LC82300702014234.tar.gz', 'ndvi.LT52300702008234.tif')) 22 | 23 | \dontrun{ 24 | # see package 'bfastSpatial' for more info: 25 | library(devtools) 26 | install_github('dutri001/bfastSpatial') 27 | library(bfastSpatial) 28 | ?getSceneinfo 29 | } 30 | } 31 | \author{ 32 | Ben DeVries and Loic Dutrieux 33 | } 34 | 35 | -------------------------------------------------------------------------------- /man/pixelToPolygon.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.0): do not edit by hand 2 | % Please edit documentation in R/pixelToPolygon.R 3 | \name{pixelToPolygon} 4 | \alias{pixelToPolygon} 5 | \title{Convert pixel to polygon} 6 | \usage{ 7 | pixelToPolygon(x, cell, plot = FALSE) 8 | } 9 | \arguments{ 10 | \item{x}{Raster* object} 11 | 12 | \item{cell}{Cell number (numeric) or vector of length 2 representing the x and y coordinate of the desired pixel} 13 | } 14 | \value{ 15 | Object of class \code{SpatialPolygons} encompassing the outline of the pixel located at \code{cell}. 16 | } 17 | \description{ 18 | Convert a pixel to a polygon object by giving the raster object and cell number or xy coordinates vector 19 | } 20 | 21 | -------------------------------------------------------------------------------- /man/selectScenes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.0): do not edit by hand 2 | % Please edit documentation in R/selectScenes.R 3 | \name{selectScenes} 4 | \alias{selectScenes} 5 | \title{Select scenes from directory} 6 | \usage{ 7 | selectScenes(x, targ, padding = NULL, verbose = TRUE) 8 | } 9 | \arguments{ 10 | \item{x}{The object whose extent will be checked. It can be a raster, spatial object, or an extent object} 11 | 12 | \item{targ}{Either a character vector of image filenames or an object of type list containing images to be tested} 13 | 14 | \item{padding}{Numeric. Additional area surrounding the extent to be included. Units depend on the projection of x} 15 | 16 | \item{verbose}{Logical. Send status reports to the console?} 17 | } 18 | \value{ 19 | Either a character vector of filenames with overlapping extents if \code{is.character(targ)}, or a list of raster objects whose extents overlap with \code{x} if \code{targ} is a spatial object. 20 | } 21 | \description{ 22 | Select scenes (filenames) from a directory wth extents overlapping that of a given spatial object 23 | } 24 | \author{ 25 | Ben DeVries 26 | } 27 | 28 | -------------------------------------------------------------------------------- /man/subsetScenes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.0): do not edit by hand 2 | % Please edit documentation in R/subsetScenes.R 3 | \name{subsetScenes} 4 | \alias{subsetScenes} 5 | \title{Subset Scenes} 6 | \usage{ 7 | subsetScenes(x, targ, padding = NULL, filename = "", verbose = TRUE, ...) 8 | } 9 | \arguments{ 10 | \item{x}{A spatial object (polygon, or otherwise) to base the subset extent on} 11 | 12 | \item{targ}{A character vector of filenames, or a list of raster layers to be subsetted} 13 | 14 | \item{filename}{Character. Optional: If results are to be written to file, then a character vector of \code{length = length(targ)} should be provided. Not supported yet (leave blank, or it will return an error)} 15 | 16 | \item{verbose}{Logical. Print status messages to the console?} 17 | } 18 | \value{ 19 | A list of avialable rasters cropped to the extent of the input object + padding. Side effect: cropped rasters written to file if a filename vector is supplied (not supported yet). 20 | } 21 | \description{ 22 | Subset scenes from a list or directory based on a given spatial object. Takes results of \code{selectScenes()} and subsets based on intersecting extents. 23 | } 24 | \author{ 25 | Ben DeVries 26 | } 27 | 28 | -------------------------------------------------------------------------------- /man/subsetScenesPlot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.0): do not edit by hand 2 | % Please edit documentation in R/subsetScenesPlot.R 3 | \name{subsetScenesPlot} 4 | \alias{subsetScenesPlot} 5 | \title{Plot subsetted scenes in sequence} 6 | \usage{ 7 | subsetScenesPlot(..., bands = NULL, stretch = NULL, textcol = "white") 8 | } 9 | \arguments{ 10 | \item{...}{Arguments to be passed to \code{\link{subsetScenes}}} 11 | 12 | \item{bands}{Numeric. Bands to display if an RGB composite (ignored if \code{length(bands)} is less than 3)} 13 | 14 | \item{stretch}{Character. Stretch to apply to composite (see \code{\link{plotRGB}} for more info)} 15 | 16 | \item{textcol}{Character. Colour of text to display on image.} 17 | } 18 | \value{ 19 | list of raster subsets (see \code{\link{subsetScenes}}) 20 | } 21 | \description{ 22 | Plot subsetted scenes in sequence. Same as \code{\link{subsetScenes}}, except that it plots the subsets afterwards. 23 | } 24 | 25 | -------------------------------------------------------------------------------- /man/tsChips.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.0): do not edit by hand 2 | % Please edit documentation in R/tsChips.R 3 | \name{tsChips} 4 | \alias{tsChips} 5 | \title{Plot TS Chips} 6 | \usage{ 7 | tsChips(x, loc, start = NULL, end = NULL, buff = 17, percNA = 20, 8 | cols = "PiYG", nbks = 35, nc = 3, nr = 3, plot = FALSE, 9 | plotlab = "data", exportChips = FALSE, exportZoo = FALSE, show = TRUE) 10 | } 11 | \arguments{ 12 | \item{x}{RasterBrick. Image time series brick} 13 | 14 | \item{loc}{Location. Can be a vector of length 2 representing the x,y coordinates, or a SpatialPolygons or SpatialPoints object of \code{nrow = 1} (the first row will be taken if \code{nrow(loc) > 1}), or an extent object (which will be extended if a buffer > 0 is given; see below)} 15 | 16 | \item{start}{Date. OptionaL: earliest date ("yyyy-dd-mm") to display.} 17 | 18 | \item{end}{Date. Optional: latest date ("yyyy-dd-mm") to display.} 19 | 20 | \item{buff}{Numeric. Number of pixels to buffer the location in all directions. A higher buffer will essentially zoom out.} 21 | 22 | \item{percNA}{Numeric. Maximum allowable \% NA in the cropped image chips} 23 | 24 | \item{cols}{Character. Name of colour map to use (see display.brewer.all()) or a character vector with two or more colour names or hexadecimal values (as strings) between which to interpolate.} 25 | 26 | \item{nbks}{Numeric. Number of breaks in the colour map} 27 | 28 | \item{plot}{Logical. Plot pixel time series?} 29 | 30 | \item{plotlab}{Character. y-axis label for the pixel time series plot.} 31 | 32 | \item{exportChips}{Logical. Export processed chips to workspace as a rasterBrick?} 33 | 34 | \item{exportZoo}{Logical. Export pixel time series as a zoo object?} 35 | 36 | \item{show}{Logical. Plot the chips? Can be set to \code{FALSE} if you just want to export the chips as rasterBrick with or without the ggplot object.} 37 | 38 | \item{nc/nr}{Numeric. Number of columns and rows to plot, respectively. If the number of layers is greater than \code{nc*nr}, a screen prompt will lead to the next series of plots. These cannot exceed 4.} 39 | } 40 | \value{ 41 | \code{NULL} if \code{exportChips = FALSE} and \code{exportZoo = FALSE}, with the side effect of time series chips being plotted. If \code{exportChips = TRUE}, an object of type \code{RasterBrick}. if \code{exportZoo = TRUE}, an object of type \code{zoo}. 42 | } 43 | \description{ 44 | Plot image TS chips based on a location, area buffer, and time buffer 45 | } 46 | \examples{ 47 | \dontrun{ 48 | library(bfastSpatial) 49 | data(tura) 50 | 51 | tsChips(tura, loc = c(820796, 831198)) 52 | tsChips(tura, loc = c(820796, 831198), buff = 50) # zoom out 53 | tsChips(tura, loc = c(820796, 831198), buff = 50, percNA = 80) # allow more NA's in field of view 54 | tsChips(tura, loc = c(820796, 831198), start = "2007-01-01", end = "2012-01-01", plot = TRUE, plotlab = 'NDVI') # restrict dates and produce pixel time series plot afterwards 55 | 56 | # alternative colour scales 57 | library(RColorBrewer) 58 | display.brewer.all() 59 | tsChips(tura, loc = c(820796, 831198), start = "1999-01-01", cols = "Spectral") 60 | tsChips(tura, loc = c(820796, 831198), start = "1999-01-01", cols = "Greens") 61 | 62 | tsChips(tura, loc = c(820796, 831198), start = "1999-01-01", cols = c("red", "yellow", "blue")) 63 | tsChips(tura, loc = c(820796, 831198), start = "1999-01-01", cols = c("#DEEBF7", "#3182BD")) 64 | 65 | # export image chips as a raster brick 66 | chips <- tsChips(tura, loc = c(820796, 831198), start = "1999-01-01", percNA = 0, exportChips = TRUE) 67 | chips2 <- tsChips(tura, loc = c(820796, 831198), start = "1999-01-01", percNA = 100, exportChips = TRUE) 68 | nlayers(chips) 69 | nlayers(chips2) 70 | 71 | # export centre pixel time series as a zoo object 72 | z <- tsChips(tura, loc = c(820796, 831198), start = "1999-01-01", exportZoo = TRUE) 73 | plot(z) 74 | 75 | # draw a custom SpatialPoygons object and plot around that 76 | plot(tura, 42) 77 | pol <- drawPoly(sp = TRUE) # click 'Finish' in plot window when done 78 | projection(pol) <- projection(tura) 79 | plot(tura, 42); plot(pol, add=TRUE) 80 | tsChips(tura, loc = pol, start = "1999-01-01", plot = TRUE, plotlab = 'NDVI') 81 | } 82 | } 83 | \author{ 84 | Ben DeVries 85 | } 86 | \references{ 87 | Cohen, W. B., Yang, Z., Kennedy, R. (2010). Detecting trends in forest disturbance and recovery using yearly Landsat time series: 2. TimeSync - Tools for calibration and validation. Remote Sensing of Environment, 114(12), 2911-2924. 88 | } 89 | 90 | -------------------------------------------------------------------------------- /man/tsChipsRGB.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.0): do not edit by hand 2 | % Please edit documentation in R/tsChipsRGB.R 3 | \name{tsChipsRGB} 4 | \alias{tsChipsRGB} 5 | \title{Plot TS Chips (RGB version)} 6 | \usage{ 7 | tsChipsRGB(xr, xg, xb, loc, start = NULL, end = NULL, buff = 17, 8 | percNA = 20, nc = 3, nr = 3, plot = FALSE, exportChips = FALSE, 9 | exportZoo = FALSE, textcol = "white", plotlabs = c("red", "green", 10 | "blue"), ...) 11 | } 12 | \arguments{ 13 | \item{loc}{Location. Can be a vector of length 2 representing the x,y coordinates, or a SpatialPolygons or SpatialPoints object of \code{nrow = 1} (the first row will be taken if \code{nrow(loc) > 1}), or an extent object (which will be extended if a buffer > 0 is given; see below)} 14 | 15 | \item{start}{Date. OptionaL: earliest date ("yyyy-dd-mm") to display.} 16 | 17 | \item{end}{Date. Optional: latest date ("yyyy-dd-mm") to display.} 18 | 19 | \item{buff}{Numeric. Number of pixels to buffer the location in all directions. A higher buffer will essentially zoom out.} 20 | 21 | \item{percNA}{Numeric. Maximum allowable \% NA in the cropped image chips} 22 | 23 | \item{plot}{Logical. Plot individual band time series?} 24 | 25 | \item{exportChips}{Logical. Export processed chips to workspace as a list of rasterBricks (R, G, B)?} 26 | 27 | \item{exportZoo}{Logical. Export pixel time series as \code{zoo} objects?} 28 | 29 | \item{textcol}{Character. Colour of text showing image date (can also be hexadecimal)} 30 | 31 | \item{plotlabs}{Character. Vector of length 3 indicating the labels for each of the zoo plots (if \code{plot=TRUE})} 32 | 33 | \item{...}{Arguments to be passed to \code{\link{plotRGB}}} 34 | 35 | \item{xr/xg/xb}{RasterBrick. Image time series bricks representing the red, green and blue channel, respectively} 36 | 37 | \item{nc/nr}{Numeric. Number of columns and rows to plot, respectively. If the number of layers is greater than \code{nc*nr}, a screen prompt will lead to the next series of plots. These cannot exceed 4.} 38 | 39 | \item{show}{Logical. Show image chips? Set to \code{FALSE} if you just want to export them to rasterBricks and/or export the \code{ggplot} object without viewing the chips.} 40 | } 41 | \value{ 42 | \code{NULL} if \code{exportZoo = FALSE} and \code{exportChips = FALSE} or a list of \code{zoo} time series objects if \code{exportZoo = TRUE}, or a list of subsetted and cropped rasterBricks if \code{exportChips = TRUE}, with the side effect of time series chips being plotted in all cases. If both \code{exportChips} and \code{exportZoo} are \code{TRUE}, a list consisting both lists will be returned. 43 | } 44 | \description{ 45 | Plot image TS chips from 3 raster bricks (as RGB composites) based on a location, area buffer, and time buffer 46 | } 47 | \details{ 48 | see \link{http://bendv.github.io/timeSyncR/} for a short tutorial on \code{tsChipsRGB} 49 | } 50 | \author{ 51 | Ben DeVries 52 | } 53 | \references{ 54 | Cohen, W. B., Yang, Z., Kennedy, R. (2010). Detecting trends in forest disturbance and recovery using yearly Landsat time series: 2. TimeSync - Tools for calibration and validation. Remote Sensing of Environment, 114(12), 2911-2924. 55 | } 56 | 57 | -------------------------------------------------------------------------------- /timeSyncR.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: knitr 13 | LaTeX: pdfLaTeX 14 | 15 | BuildType: Package 16 | PackageInstallArgs: --no-multiarch --with-keep.source 17 | --------------------------------------------------------------------------------