├── .Rbuildignore ├── .gitignore ├── CRAN-RELEASE ├── DESCRIPTION ├── NAMESPACE ├── NEWS.md ├── R ├── day_overlap.R ├── map_sites.R ├── moveCloud.R ├── moveReduce.R ├── sMoveRes.R ├── spaceDir.R ├── tMoveRes.R └── timeDir.R ├── README.md ├── cran-comments.md ├── inst └── extdata │ ├── 2013-07-16_ndvi.tif │ ├── 2013-08-01_ndvi.tif │ ├── 2013-08-17_ndvi.tif │ ├── baldEagle.csv │ ├── landCover.tif │ ├── longMove.csv │ ├── probabilities.tif │ ├── roi.cpg │ ├── roi.dbf │ ├── roi.prj │ ├── roi.qpj │ ├── roi.shp │ ├── roi.shx │ ├── sampleIndices.txt │ ├── shortMove.csv │ └── whiteStork.csv ├── man ├── day_overlap.Rd ├── map_sites.Rd ├── moveCloud.Rd ├── moveReduce.Rd ├── sMoveRes.Rd ├── spaceDir.Rd ├── tMoveRes.Rd └── timeDir.Rd └── rsMove.Rproj /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^CRAN-RELEASE$ 2 | ^.*\.Rproj$ 3 | ^\.Rproj\.user$ 4 | ^NEWS\.md$ 5 | ^cran-comments\.md$ 6 | ^data/ 7 | book/ 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | data 5 | -------------------------------------------------------------------------------- /CRAN-RELEASE: -------------------------------------------------------------------------------- 1 | This package was submitted to CRAN on 2020-06-02. 2 | Once it is accepted, delete this file and tag the release (commit 10a46b59d9). 3 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: rsMove 2 | Type: Package 3 | Title: Guidelines for the use of Remote Sensing in Movement Ecology 4 | Version: 0.2.8 5 | Date: 2020-06-01 6 | Authors@R: person("Ruben", "Remelgado", role = c("aut", "cre"), email="remelgado.ruben@gmail.com") 7 | URL: https://github.com/RRemelgado/rsMove/tree/master/ 8 | BugReports: https://github.com/RRemelgado/rsMove/issues/ 9 | Maintainer: Ruben Remelgado 10 | Description: Tools for the guided selection of satellite data and environmental predictors, the combination of remote sensing and animal movement data and the mapping of resource suitability. Based on the paper by Remelgado et al. (2015) . 11 | LazyData: TRUE 12 | Imports: terra, sp, ggplot2, grDevices, plyr, RCurl, dbscan 13 | RoxygenNote: 7.3.0 14 | License: GPL (>= 3) 15 | Suggests: knitr, rmarkdown, kableExtra, imager, devtools, lattice, igraph, randomForest, e1071 16 | VignetteBuilder: knitr 17 | Encoding: UTF-8 18 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(day_overlap) 4 | export(map_sites) 5 | export(moveCloud) 6 | export(moveReduce) 7 | export(sMoveRes) 8 | export(spaceDir) 9 | export(tMoveRes) 10 | export(timeDir) 11 | importFrom(RCurl,url.exists) 12 | importFrom(dbscan,dbscan) 13 | importFrom(ggplot2,aes) 14 | importFrom(ggplot2,coord_cartesian) 15 | importFrom(ggplot2,element_blank) 16 | importFrom(ggplot2,element_line) 17 | importFrom(ggplot2,geom_bar) 18 | importFrom(ggplot2,ggplot) 19 | importFrom(ggplot2,labs) 20 | importFrom(ggplot2,scale_fill_gradientn) 21 | importFrom(ggplot2,scale_y_continuous) 22 | importFrom(ggplot2,theme) 23 | importFrom(grDevices,colorRampPalette) 24 | importFrom(plyr,.) 25 | importFrom(plyr,ddply) 26 | importFrom(plyr,summarise) 27 | importFrom(sp,Polygon) 28 | importFrom(sp,Polygons) 29 | importFrom(sp,SpatialPolygons) 30 | importFrom(sp,SpatialPolygonsDataFrame) 31 | importFrom(stats,median) 32 | importFrom(stats,weighted.mean) 33 | importFrom(terra,app) 34 | importFrom(terra,cellFromXY) 35 | importFrom(terra,convHull) 36 | importFrom(terra,crds) 37 | importFrom(terra,crs) 38 | importFrom(terra,distance) 39 | importFrom(terra,ext) 40 | importFrom(terra,extend) 41 | importFrom(terra,extract) 42 | importFrom(terra,geom) 43 | importFrom(terra,rast) 44 | importFrom(terra,rasterize) 45 | importFrom(terra,vect) 46 | importFrom(utils,download.file) 47 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | ================================================================================= 2 | 3 | #### rsMove 0.2.8 4 | 5 | ================================================================================= 6 | 7 | ### Changes 8 | * Altered projection string of example, spatial data due to proj6 migration 9 | 10 | ================================================================================= 11 | 12 | #### rsMove 0.2.7 13 | 14 | ================================================================================= 15 | 16 | ### New 17 | * Added intime(), runmed2() and runmean2() c++ functions. 18 | * imgInt() allows for multi-band raster objects with observarion dates. 19 | * imgInt() can smooth the output using an user provided function. 20 | 21 | ### Changes: 22 | * imgInt() now depends on intime() and intime2() 23 | * ingInt() only accepts raster objects 24 | * segRaster() now depends on segMatrix() 25 | 26 | 27 | ================================================================================= 28 | 29 | #### rsMove 0.2.6 30 | 31 | ================================================================================= 32 | 33 | ### Changes 34 | * sMoveRes() and tMoveRes() optimized. 35 | 36 | ### Fixes: 37 | * The size legend in plotMove() is now consistent. 38 | ' moveReduce() provides correct values when preserve.revisits was FALSE. 39 | 40 | ================================================================================= 41 | 42 | #### rsMove 0.2.5 43 | 44 | ================================================================================= 45 | 46 | ### Changes 47 | * timeDir() returns a histogram ggplot object. 48 | * plausibilityTest() informs on the validation of each presence sample. 49 | * hotMove() returns a ggplot object with region polygons. 50 | * The output of hotMoveStats() was simplified. 51 | * moveReduce() allows the summary of movement data across temporal windows. 52 | 53 | ### Fixes: 54 | * checkOverlap() returned a warning when combining rasters and polygons. 55 | * Overall improvement of the detailed description of each function. 56 | * Simplification of variable names. 57 | 58 | ================================================================================= 59 | 60 | #### rsMove 0.2.4 61 | 62 | ================================================================================= 63 | 64 | #### New: 65 | * Added function checkOverlap() and plausibilityTest() 66 | 67 | #### Changes: 68 | * Simplification of variable names in all functions 69 | 70 | #### Fixes: 71 | * backSample(), labelSample() and imgInt() optimized 72 | * poly2sample now accepts RasterStack objects 73 | 74 | ================================================================================= 75 | 76 | #### rsMove 0.2.3 77 | 78 | ================================================================================= 79 | 80 | ##### New: 81 | * No new functions added. 82 | 83 | ##### Changes: 84 | * Inclusion of plotting routines within functions. 85 | * Functions that plot results now provide an editable ggplot object. 86 | 87 | ##### Fixes: 88 | * Signficant improvements to the description of functions. 89 | * Updated function keywords to clarify their usage. 90 | 91 | ================================================================================= 92 | 93 | #### rsMove 0.2.2 94 | 95 | ================================================================================= 96 | 97 | ##### New: 98 | * Introduced new functions 99 | * sMoveRes() 100 | * tMoveRes() 101 | * specVar() 102 | * getEnv() 103 | * proSat() 104 | * moveCloud() 105 | * satTime() 106 | * moveReduce() 107 | * plotMove() 108 | * imgInt(), moveSeg(), timeDir(), spaceDir(), linInt() now accept data frames as inputs. 109 | * hotMoveStats() now reports the temporal segment found within each region and their corresponding indices. 110 | * hotMoveStats() allows the analysis of temporal information without needing to run hotMove() first. 111 | * spaceDir() accepts categorical data. 112 | * spaceDir() and moveSeg() use an optional spatial buffer. 113 | * moveSeg(), timeDir(), spaceDir() and hotMoveStats() return plots. 114 | * dataQuery() requires an adjustable buffer specifying the size of search window before and after the target date. 115 | 116 | ##### Changes: 117 | * spaceDirSample() was renamed as spaceDir() to avoid confusion regarding its purpose. 118 | * timeDirSample() was renamed as timeDir() to avoid confusion regarding its purpose. 119 | * moveMovel() now requires data frames for presences and absences instead of shapefiles. 120 | * moveSeg(), spaceDir() and timeDir() no longer samples to single pixels internaly. 121 | 122 | ##### Fixes: 123 | * imgInt() allows that the interpolation is limited to the records in a point shapefile. 124 | * moseSeg() now recognizes categorical data. 125 | * BackSample() now assings background samples to the correct pixels. 126 | 127 | ================================================================================= 128 | 129 | #### rsMove 0.1.0 130 | 131 | ================================================================================= 132 | 133 | ### Initial release to CRAN (2017-07-15) with the following functions: 134 | * backSample() 135 | * dataQuery() 136 | * hotMove() 137 | * hotMoveStats() 138 | * imgInt() 139 | * labelSample() 140 | * modelApply() 141 | * moveModel() 142 | * moveSeg() 143 | * poly2sample() 144 | * rsComposite() 145 | * sampleMove() 146 | * segRaster() 147 | * spaceDirSample() 148 | * timeDirSample() 149 | -------------------------------------------------------------------------------- /R/day_overlap.R: -------------------------------------------------------------------------------- 1 | #' @title day_overlap 2 | #' 3 | #' @description Estimate the number of the day covered by timestamps. 4 | #' @param x Observation time. Object of class \emph{POSIXct}, \emph{POSIXlt}, or \emph{POSIXt}. 5 | #' @details {The function estimates how much of a day is covered 6 | #' by the timestamps in \emph{x}, which correspond to the timestamps 7 | #' in a movement dataset. Note that \emph{x} is not accepted if 8 | #' containing data from more than one day.} 9 | #' @return {A \emph{list} containing: 10 | #' \itemize{ 11 | #' \item{\emph{day.cover} - Percent of the day with recorded timestamps.} 12 | #' \item{\emph{day.overlap} - Percent of the day covered between the first and last timestamp.} 13 | #' } 14 | #' } 15 | #' @examples { 16 | #' 17 | #' # load samples 18 | #' multiMove <- read.csv(system.file('extdata', 'multiMove.csv', package="rsMove")) 19 | #' 20 | #' # data-time of species observations 21 | #' times = strptime(multiMove$timestamp,format="%Y-%m-%d %H:%M:%S") 22 | #' 23 | #' # subset to first unique day 24 | #' days = as.Date(times) 25 | #' times = times[which(days == unique(days)[1])] 26 | #' 27 | #' # extract regions 28 | #' day_overlap(date)]) 29 | #' 30 | #' } 31 | #' @export 32 | 33 | #-----------------------------------------------------------------------------# 34 | #-----------------------------------------------------------------------------# 35 | 36 | day_overlap = function(x) { 37 | 38 | #---------------------------------------------------------------------------# 39 | # 1. check if input is a valid vector of timestamps 40 | #---------------------------------------------------------------------------# 41 | 42 | if (!max(class(x) %in% c("POSIXlt","POSIXt","POSIXct"))) stop('"x" is not of a valid class') 43 | if (length(unique(format(x, "%Y-%m-%d"))) > 1) stop('"x" must refer to a single day') 44 | 45 | #---------------------------------------------------------------------------# 46 | # 2. estimate proportion of the day covered by timestamps 47 | #---------------------------------------------------------------------------# 48 | 49 | # convert first timestamp into number of seconds in the day 50 | t0 = as.numeric(format(x, "%H"))*3600 + 51 | as.numeric(format(x, "%M"))*60 + 52 | as.numeric(format(x, "%S")) 53 | 54 | # translate t0 to a hypothetical sequence of seconds (defines max. time cover) 55 | t1 = seq(min(t0), max(t0), 1) 56 | 57 | # build sequence for a hypothetical complete day 58 | t2 = seq(0, 86400, 1) 59 | 60 | # estimate percent overlap 61 | return( 62 | list( 63 | day.cover=length(intersect(t0,t2))/length(t2)*100, 64 | day.overlap=length(intersect(t1,t2))/length(t2)*100 65 | ) 66 | ) 67 | 68 | } 69 | -------------------------------------------------------------------------------- /R/map_sites.R: -------------------------------------------------------------------------------- 1 | #' @title map_sites 2 | #' 3 | #' @description Detection of geographic regions of samples using a pixel based approach. 4 | #' @param x Object of class \emph{SpatVector}. 5 | #' @param y Unique individual identifier for each entry in \emph{x}. 6 | #' @param z Timestamps of each element in \emph{x} given as a \emph{POSIXct} object. 7 | #' @param resolution Maximum distance between data points to identify clusters. 8 | #' @param min_size Minimum number of GPS data points per cluster. 9 | #' @importFrom sp Polygon Polygons SpatialPolygons SpatialPolygonsDataFrame 10 | #' @importFrom terra ext extend crs convHull crds 11 | #' @importFrom plyr ddply summarise . 12 | #' @importFrom dbscan dbscan 13 | #' @details {The function provides three outputs. First, it labels each 14 | #' entry in \emph{x} based on their spatial connectivity. Connections are 15 | #' based on \emph{resolution}, which defines the maximum distance allowed 16 | #' among members of a group of data points, below which the elements of 17 | #' that group are treated as a region and labeled with the same unique, 18 | #' non-zero, numeric identifier. Regions where the number of data points 19 | #' is below \emph{min_size} are labelled with a 0. Second, for each region, 20 | #' the function will return a polygon defined by the convex hull of the 21 | #' composing data points. 0-labeled regions are excluded, as well as those 22 | #' where the number of data points is less than 2. Third, for each region, 23 | #' and for each sequence of days with data points within the target region, 24 | #' we provide the following information and statistics: 25 | #' \itemize{ 26 | #' \item{\emph{region_id} - Region unique identifier.} 27 | #' \item{\emph{start.date} - Segment unique identifier.} 28 | #' \item{\emph{start.date} - First data date.} 29 | #' \item{\emph{end.date} - Last data date.} 30 | #' \item{\emph{day_cover_mean} - Mean percentage of the days in the segment with movement data} 31 | #' \item{\emph{day_cover_sd} - Standard deviation of the days in the segment with movement data.} 32 | #' \item{\emph{day_overlap_mean} - Mean percentage of the day across which movement data were recorded.} 33 | #' \item{\emph{day_overlap_sd} - Standard deviation of the percentage of the day across which movement data were recorded.} 34 | #' \item{\emph{nr_days} - Number of days with movement data.} 35 | #' \item{\emph{nr_individuals} - Number of individuals tracked.} 36 | #' \item{\emph{nr_records} - Number of movement data records.} 37 | #' \item{\emph{data_frequency_mean} - Mean frequency of GPS entries (in minutes).} 38 | #' \item{\emph{data_frequency_sd} - Standard deviation of the frequency of GPS entries (in minutes)} 39 | #' } 40 | #' Day segments are treated separately because they indicate that a given 41 | #' area was persistently occupied by one or more tracked individuals.} 42 | #' @return {A \emph{list} containing: 43 | #' \itemize{ 44 | #' \item{\emph{region.id} - Vector reporting on the region each element in \emph{x} belongs to.} 45 | #' \item{\emph{region.polygons} - Polygons for each temporal segment in each \emph{region.id.} 46 | #' \item{\emph{region.stats} - Statistics for each temporal segment.} 47 | #' } 48 | #' }} 49 | #' @examples { 50 | #' 51 | #' require(terra) 52 | #' 53 | #' # load samples 54 | #' multiMove <- read.csv(system.file('extdata', 'multiMove.csv', package="rsMove")) 55 | #' 56 | #' # convert samples to vector 57 | #' multiMove = vect(multiMove, geom=c("x","y"), crs="EPSG:4326")[1:10000,] 58 | #' 59 | #' # species identifier (only one individual, so set to 1) 60 | #' species_id = multiMove$id 61 | #' 62 | #' # data-time of species observations 63 | #' date = strptime(multiMove$timestamp,format="%Y-%m-%d %H:%M:%S", tz="GMT") 64 | #' 65 | #' # extract regions 66 | #' hm <- map_sites(multiMove, species_id, date, 0.1) 67 | #' 68 | #' } 69 | #' @export 70 | 71 | #-----------------------------------------------------------------------------# 72 | #-----------------------------------------------------------------------------# 73 | 74 | map_sites <- function(x, y, z, resolution, min_size=1) { 75 | 76 | #---------------------------------------------------------------------------# 77 | # 1. check inpur variables 78 | #---------------------------------------------------------------------------# 79 | 80 | if (is.null(crs(x))) 81 | stop('"x" is missing a valid projection') 82 | if (!class(y) %in% c("numeric", "character")) 83 | stop('"y" must be a "Date" vector') 84 | if (!sum(class(z) %in% c("POSIXct","POSIXlt","POSIXt")) > 0) 85 | stop('"z" is not of a valid class') 86 | if (length(unique(c(nrow(x),length(y),length(z)))) > 1) 87 | stop('lengths of "x", "y", "z" do not match') 88 | if (!is.numeric(resolution)) 89 | stop('"resolution" must be a numeric element') 90 | 91 | #---------------------------------------------------------------------------# 92 | # 2. label clusters of observations 93 | #---------------------------------------------------------------------------# 94 | 95 | regions <- dbscan(crds(x), 96 | eps=resolution, 97 | borderPoints=T, 98 | minPts=min_size)$cluster 99 | 100 | #---------------------------------------------------------------------------# 101 | # 3. derive statistics for each regions 102 | #---------------------------------------------------------------------------# 103 | 104 | tmp = list() 105 | region_shp = list() 106 | 107 | dates = as.Date(format(z, "%Y-%m-%d")) 108 | 109 | for (r in unique(regions[which(regions > 0)])) { 110 | 111 | # evaluate temporal composition 112 | ri <- which(regions == r) # region indices 113 | 114 | # find unique dates with data at region r 115 | region_dates = sort(unique(dates[ri])) 116 | 117 | # between-day interval 118 | day_diff = c(1,diff(region_dates)) 119 | 120 | # search for sequences of days 121 | segments = rle(day_diff)$lengths 122 | date_segments = vector("numeric", length(region_dates)) 123 | for (p in 1:length(segments)) { 124 | date_segments[(sum(segments[0:(p-1)])+1):sum(segments[1:p])] <- p} 125 | 126 | # characterize segments 127 | unique_segments = unique(date_segments) 128 | for (s in 1:length(unique_segments)) { 129 | 130 | si = ri[which(dates[ri] %in% region_dates[which(date_segments == s)])] 131 | 132 | # estimate running time difference 133 | day_freq = ddply(data.frame(date=dates[si], time=z[si]), 134 | .(date), summarise, 135 | start=min(time), end=max(time), 136 | day_cover=day_overlap(time)$day.cover, 137 | day_overlap=day_overlap(time)$day.overlap, 138 | data_frequency=as.numeric(mean(diff(time))/60)) 139 | 140 | # extract segment statistics 141 | tmp[[(length(tmp)+1)]] = data.frame( 142 | region_id=r, segment_id=s, 143 | start.date=min(day_freq$start), 144 | end.date=max(day_freq$end), 145 | day_cover_mean=mean(day_freq$day_cover), 146 | day_cover_sd=sd(day_freq$day_cover), 147 | day_overlap_mean=mean(day_freq$day_overlap), 148 | day_overlap_sd=sd(day_freq$day_overlap), 149 | nr_days=length(unique(z[si])), 150 | nr_individuals=length(unique(y[si])), 151 | nr_records=length(si), 152 | data_frequency_mean=mean(day_freq$data_frequency), 153 | data_frequency_sd=mean(day_freq$data_frequency) 154 | ) 155 | 156 | # build polygon for segment if sufficient data points exist 157 | if (length(si) > 2) { 158 | h = convHull(x[si,]) 159 | h$region_id = r 160 | h$segment_id = s 161 | region_shp[[(length(region_shp)+1)]] = h 162 | rm(h) 163 | } 164 | 165 | } 166 | 167 | } 168 | 169 | tmp = do.call(rbind, tmp) 170 | region_shp = do.call(rbind, region_shp) 171 | 172 | #---------------------------------------------------------------------------# 173 | # 4. compile outputs and report 174 | #---------------------------------------------------------------------------# 175 | 176 | return(list(region.id=regions, region.polygons=region_shp, region.stats=tmp)) 177 | 178 | 179 | 180 | } 181 | -------------------------------------------------------------------------------- /R/moveCloud.R: -------------------------------------------------------------------------------- 1 | #' @title moveCloud 2 | #' 3 | #' @description Extract Cloud Cover Fraction (CCF) data for a set of coordinate pairs. 4 | #' @param x Object of class \emph{SpatVector}. 5 | #' @param y Object of class \emph{Date} with observation dates of \emph{y}. 6 | #' @param start First data from when to download CCF data. 7 | #' @param end Last data from when to download CCF data. 8 | #' @param interval Daily interval of data download. 9 | #' @param data.path Output path for downloading data on cloud cover. 10 | #' @importFrom terra rast extract app crds 11 | #' @importFrom grDevices colorRampPalette 12 | #' @importFrom ggplot2 ggplot aes labs theme geom_bar coord_cartesian scale_y_continuous scale_fill_gradientn 13 | #' @importFrom utils download.file 14 | #' @importFrom RCurl url.exists 15 | #' @importFrom stats weighted.mean 16 | #' @details {The function extracts data on daily Cloud Cover Fractions (CCF) 17 | #' from NASA's Earth Observation (NEO). For a sequence of dates defined by 18 | #' \emph{start}, \emph{end}, and \emph{interval}, the function downloads the 19 | #' correspondent CCF data and stores them in \emph{data.path}. These data, 20 | #' which have a global coverage and a spatial resolution of 0.1 degrees, will 21 | #' only be downloaded if they do not already exist in \emph{data.path}. When 22 | #' checking for existing data, the function will follow a standard naming 23 | #' convention, so data acquired independently will likely be missed. After 24 | #' downloading the needed data, the function will extract the CCF fraction 25 | #' values for all dates at the coordinates in \emph{x}. This will be used to 26 | #' calculate the mean and standard deviation of the CCF at each date. In 27 | #' addition, the function will extract CCF data for each date in \emph{y} that 28 | #' falls between \emph{start} and \emph{end}.} 29 | #' @return {A \emph{list} containing: \itemize{ 30 | #' \item{\emph{x.stats} - CCF statistics at each entry in \emph{x}} 31 | #' \item{\emph{r.stats} - CCF statistics at each unique date between\emph{start} and \emph{end} summarized across the elements of \emph{x}} 32 | #' \item{\emph{x.plot} - Plot of the data in \emph{x.stats}}. 33 | #' \item{\emph{r.plot} - Plot of the data in \emph{r.stats}}.}} 34 | #' @references \url{https://cneos.jpl.nasa.gov/} 35 | #' @seealso \code{\link{sMoveRes}} \code{\link{tMoveRes}} 36 | #' @examples \dontrun{ 37 | #' 38 | #' require(terra) 39 | #' 40 | #' # read movement data 41 | #' longMove <- read.csv(system.file('extdata', 42 | #' 'longMove.csv', package="rsMove")) 43 | #' 44 | #' # convert observations to vector 45 | #' longMove = vect(longMove, geom=c("long","lat"), crs="EPSG:4326") 46 | #' 47 | #' # test function for 30 day buffer 48 | #' obs.dates <- as.Date(longMove$timestamp) 49 | #' c.cover <- moveCloud(shortMove, obs.dates) 50 | #' 51 | #' } 52 | #' @export 53 | 54 | #-----------------------------------------------------------------------------------------------------------------------# 55 | 56 | moveCloud <- function(x, y, start, end, interval, data.path) { 57 | 58 | #---------------------------------------------------------------------------------------------------------------------# 59 | # 1. check inpur variables 60 | #---------------------------------------------------------------------------------------------------------------------# 61 | 62 | if (!class(x)[1]%in%c('SpatVector')) stop('"x" is not of a valid class') 63 | x = as.data.frame(crds(project(x, "EPSG:4326"))) # reproject data if needed 64 | if (class(y)[1] != 'Date') stop('"start" is not of a valid class') 65 | 66 | if (class(start)[1] != 'Date') stop('"start" is not of a valid class') 67 | if (class(end)[1] != 'Date') stop('"end" is not of a valid class') 68 | if (!is.numeric(interval)) stop('"interval" is not of a valid class') 69 | 70 | if (!dir.exists(data.path)) { 71 | test = tryCatch(dir.create(data.path), error=function(e) return(TRUE)) 72 | if (is.logical(test)) stop('provided "data.path" not valid') 73 | } 74 | 75 | # data sources 76 | myd <- "https://neo.gsfc.nasa.gov/archive/geotiff.float/MYDAL2_D_CLD_FR/" # aqua 77 | mod <- "https://neo.gsfc.nasa.gov/archive/geotiff.float/MODAL2_D_CLD_FR//" # terra 78 | 79 | #---------------------------------------------------------------------------# 80 | # 2. download data on cloud cover 81 | #---------------------------------------------------------------------------# 82 | 83 | # target dates 84 | target_dates = seq(start, end, interval) 85 | 86 | # warn user of the volume of data required 87 | data.volume = 5.6*length(target_dates) 88 | message(paste0("warning: about to store ", data.volume, " Mb")) 89 | 90 | # download data 91 | dates = list() 92 | files = list() 93 | 94 | for (d in 1:length(target_dates)) { 95 | 96 | date = target_dates[d] 97 | 98 | # file to store 99 | ofile = file.path(data.path, paste0("MYDAL2-ccf_", 100 | paste0(strsplit(as.character(date), 101 | "[-]")[[1]], 102 | collapse=""), "_10km.tif")) 103 | 104 | if (!file.exists(ofile)) { 105 | 106 | # list images to be downloaded 107 | s1 = file.path(myd, paste0("MYDAL2_D_CLD_FR_", 108 | date, ".FLOAT.TIFF")) 109 | o1 = file.path(data.path, basename(s1)) 110 | if (!file.exists(s1)) c1 = tryCatch( 111 | download.file(s1, o1, mode="wb", quiet=T), 112 | error=function(e) return(TRUE)) 113 | 114 | s2 = file.path(mod, paste0("MODAL2_D_CLD_FR_", 115 | date, ".FLOAT.TIFF")) 116 | o2 = file.path(data.path, basename(s2)) 117 | if (!file.exists(s2)) c2 = tryCatch( 118 | download.file(s2, o2, mode="wb", quiet=T), 119 | error=function(e) return(TRUE)) 120 | 121 | if (!is.logical(c1) | !is.logical(c2)) { 122 | 123 | tmp = c(o1, o2) 124 | tmp = tmp[file.exists(tmp)] 125 | cloud.cover.img = rast(tmp) 126 | cloud.cover.img[cloud.cover.img > 1] = NA 127 | 128 | # average cloud cover measured with AQUA & TERRA 129 | cloud.cover.img = app(cloud.cover.img, mean, na.rm=T) 130 | files[[d]] = ofile 131 | writeRaster(cloud.cover.img, files[[d]]) 132 | dates[[d]] = date 133 | 134 | file.remove(tmp) 135 | 136 | } 137 | 138 | } else { 139 | files[[d]] = ofile 140 | dates[[d]] = date 141 | } 142 | 143 | } 144 | 145 | files = do.call("c", files) 146 | dates = do.call("c", dates) 147 | 148 | #---------------------------------------------------------------------------# 149 | # 3. extract cloud cover values for each GPS coordinate 150 | #---------------------------------------------------------------------------# 151 | 152 | # find GPS coordinates falling within the specified temporal window 153 | ind = which(y %in% dates) 154 | 155 | r.cloud.cover = extract(rast(files), x[ind,], ID=F) 156 | r.cloud.cover.data = data.frame(date=dates, 157 | mean=apply(r.cloud.cover, 2, mean, na.rm=T), 158 | sd=apply(r.cloud.cover, 2, sd, na.rm=T)) 159 | 160 | x.cloud.cover = rep(0,length(ind)) 161 | unique_dates = unique(y[ind]) 162 | for (d in 1:length(unique_dates)) { 163 | di = which(y[ind] == unique_dates[d]) 164 | ii = which(dates == unique_dates[d]) 165 | e = extract(rast(files[ii]), x[di,], ID=F)[[1]] 166 | x.cloud.cover[di] = e 167 | } 168 | 169 | x.cloud.cover.data = data.frame(index=ind, date=y[ind], 170 | cloud.cover=x.cloud.cover) 171 | 172 | #---------------------------------------------------------------------------# 173 | # 4. build plot 174 | #---------------------------------------------------------------------------# 175 | 176 | # table used to plot 177 | gdf = ddply(x.cloud.cover.data, .(date), summarise, 178 | cover=mean(cloud.cover), 179 | mean=mean(cloud.cover), 180 | sd=sd(cloud.cover)) 181 | 182 | p1 <- ggplot(gdf, aes(x=date, y=cover)) + 183 | theme_bw(base_size=6) + 184 | geom_bar(width=0.7, stat="identity", fill="grey60") + 185 | coord_cartesian(xlim=c(start,end), ylim=c(0,1)) + 186 | labs(x="Observation date", y="Cloud Cover Fraction") + 187 | scale_y_continuous(expand=c(0,0)) + 188 | theme(panel.grid=element_blank(), 189 | panel.background=element_blank(), 190 | panel.border=element_blank(), 191 | axis.line=element_line(linewidth=0.2, colour="grey5")) 192 | 193 | # plot cloud cover across the reference period 194 | p2 <- ggplot(r.cloud.cover.data, aes(x=date, y=mean)) + 195 | theme_bw(base_size=6) + 196 | geom_line(colour="grey10") + 197 | geom_ribbon(aes(ymin=mean-sd, ymax=mean+sd), alpha=0.3) + 198 | coord_cartesian(xlim=c(start,end), ylim=c(0,1)) + 199 | labs(x="Observation date", y="Cloud Cover Fraction") + 200 | scale_y_continuous(expand=c(0,0)) + 201 | theme(panel.grid=element_blank(), 202 | panel.background=element_blank(), 203 | panel.border=element_blank(), 204 | axis.line=element_line(linewidth=0.2, colour="grey5")) 205 | 206 | return(list(x.stats=x.cloud.cover.data, 207 | r.stats=r.cloud.cover.data, 208 | x.plot=p1, r.plot=p2)) 209 | 210 | } 211 | -------------------------------------------------------------------------------- /R/moveReduce.R: -------------------------------------------------------------------------------- 1 | #' @title moveReduce 2 | #' 3 | #' @description Pixel based summary of movement data that preserves periodic movements. 4 | #' @param x Object of class \emph{spatVector}. 5 | #' @param y Object of class \emph{spatRaster}. 6 | #' @param z Object of class \emph{Date} or \emph{POSIXct} with the observation time of each element in \emph{x}. 7 | #' @param preserve.revisits Logical. Should the function preserve revisit patterns? 8 | #' @importFrom terra rast crs cellFromXY rasterize geom vect 9 | #' @importFrom plyr ddply summarise 10 | #' @return A \emph{spatVector} object. 11 | #' @details {Translates (\emph{x}) into pixel coordinates within a reference 12 | #' raster (\emph{y}). The function identifies temporal segments corresponding 13 | #' to groups of consecutive observations within the same pixel. In this process, 14 | #' revisits to recorded pixels are preserved. Once the segments are identified, 15 | #' the function derives mean x and y coordinates for each of them and evaluates 16 | #' the time spent within each pixel. The function reports on the start and end 17 | #' timestamps and the elapsed time. If \emph{preserve.revisits} is FALSE, the 18 | #' function will then summarize the output on a pixel level summing the time 19 | #' spent at each pixel. Additionally, if \emph{derive.raster} is TRUE, the 20 | #' function will derive a \emph{RasterLayer} with the same configuration as 21 | #' \emph{y} depicting the the total amount of time spent per pixel. 22 | #' The output of the function consists of a \emph{spatVector} 23 | #' with the reduced sample set.} 24 | #' @examples { 25 | #' 26 | #' require(terra) 27 | #' 28 | #' # read raster data 29 | #' r <- (system.file('extdata', '2013-07-16_ndvi.tif', package="rsMove")) 30 | #' 31 | #' # read movement data 32 | #' shortMove <- read.csv(system.file('extdata', 'shortMove.csv', package="rsMove")) 33 | #' 34 | #' # convert observations to vector 35 | #' shortMove = vect(shortMove, geom=c("x","y"), crs="EPSG:32632") 36 | #' 37 | #' # observation time 38 | #' z <- as.POSIXct(strptime(paste0(shortMove$date, ' ', shortMove$time), 39 | #' format="%Y/%m/%d %H:%M:%S")) 40 | #' 41 | #' # reduce amount of samples 42 | #' move.reduce <- moveReduce(shortMove, r, z, derive.raster=TRUE) 43 | #' 44 | #' } 45 | #' @export 46 | 47 | #-----------------------------------------------------------------------------# 48 | 49 | moveReduce <- function(x, y, z, preserve.revisits=TRUE) { 50 | 51 | #---------------------------------------------------------------------------# 52 | # 1. check input variables 53 | #---------------------------------------------------------------------------# 54 | 55 | # samples 56 | if (!class(x)[1]%in%c('spatVector')) {stop('"x" is not of a valid class')} 57 | 58 | # sample dates 59 | if (!class(z)[1]%in%c('Date', 'POSIXct')) {stop('"z" is nof of a valid class')} 60 | if (length(z)!=length(x)) {stop('"x" and "z" have different lengths')} 61 | if (sum(is.na(z)) > 0) {stop('please filter missing values in "z"')} 62 | 63 | # environmental data 64 | if (!class(y)[1]%in%c('spatRaster')) stop('"y" is not a valid raster object') 65 | if (crs(x)==crs(y)) {stop('"x" and "edata" have different projections')} 66 | 67 | if (!is.logical(preserve.revisits)) stop('"preserve.revisits" not logical') 68 | 69 | #---------------------------------------------------------------------------# 70 | # 2. identify segments for each temporal window 71 | #---------------------------------------------------------------------------# 72 | 73 | # combine inputs 74 | samples = data.frame(geom(x)[,c("x","y")]) 75 | samples$date = as.POSIXct(z) 76 | 77 | rm(x, z) 78 | 79 | # convert x to single pixels 80 | samples = samples[order(samples$date),] 81 | samples$cell.id <- cellFromXY(y, samples[,c("x","y")]) 82 | 83 | # search for segments and return sample indices 84 | pd = rle(samples$cell.id)$lengths 85 | samples$seg.id = vector('numeric', nrow(samples)) 86 | for (p in 1:length(pd)) { 87 | samples$seg.id[(sum(pd[0:(p-1)])+1):sum(pd[1:p])] <- p} 88 | 89 | rm(pd) 90 | 91 | #---------------------------------------------------------------------------# 92 | # 3. derive samples 93 | #---------------------------------------------------------------------------# 94 | 95 | # summarise samples 96 | samples = ddply(samples, .(seg.id,cell.id), summarise, 97 | x=mean(x), y=mean(y), 98 | start.time=min(date), end.time=max(date), 99 | elapsed.time=difftime(max(date), min(date), units="h")) 100 | 101 | # if preserve.revisits is FALSE, reduce to unique pixels 102 | if (!preserve.revisits) { 103 | samples = ddply(samples, .(cell.id), summarise, 104 | x=mean(x), y=mean(y), start.time=min(start.time), 105 | end.time=max(end.time), elapsed.time=sum(elapsed.time)) 106 | } 107 | 108 | # convert reduce samples to vector before exporting 109 | return(vect(samples, geom=c("x","y"), crs=crs(y))) 110 | 111 | } 112 | -------------------------------------------------------------------------------- /R/sMoveRes.R: -------------------------------------------------------------------------------- 1 | #' @title sMoveRes 2 | #' 3 | #' @description Analysis of GPS data losses with the choice spatial resolutions. 4 | #' @param x Object of class \emph{spatVector}. 5 | #' @param y Numeric vector with target spatial resolutions. 6 | #' @importFrom terra ext crds extend 7 | #' @importFrom dbscan dbscan 8 | #' @importFrom grDevices colorRampPalette 9 | #' @importFrom ggplot2 ggplot aes theme geom_bar coord_cartesian element_blank element_line scale_y_continuous scale_fill_gradientn labs 10 | #' @return A \emph{list}. 11 | #' @details {The function simulates how many unique combinations of animal 12 | #' observations and environmental data would be preserved when choosing an 13 | #' environmental dataset with the resolution given in \emph{y}. It also 14 | #' estimates simulates how many unique pixel regions (i.e., groups of 15 | #' spatially connected pixels) would be preserved after accounting for 16 | #' pseudo-replication. Finally, For each spatial resolution, the function 17 | #' reports the 'pixel ratio' and the 'region ratio', i.e., the number of 18 | #' pixels and regions divided by the number of GPS records.} 19 | #' @return {The function returns a list with: 20 | #' \itemize{ 21 | #' \item{\emph{stats} - Summary statistics reporting on the number 22 | #' of unique samples and sample regions per spatial resolution.} 23 | #' \item{\emph{plot} - Plot representing the change in number of 24 | #' samples and sample regions per spatial resolution.}}} 25 | #' @seealso \code{\link{tMoveRes}} 26 | #' @examples { 27 | #' 28 | #' require(terra) 29 | #' 30 | #' # read movement data 31 | #' shortMove = read.csv(system.file('extdata', 32 | #' 'shortMove.csv', package="rsMove")) 33 | #' 34 | #' # convert observations to vector 35 | #' shortMove = vect(shortMove, geom=c("x","y"), crs="EPSG:32632") 36 | #' 37 | #' # test function for 5, 10 20 and 30 m 38 | #' a.res = sMoveRes(shortMove, c(5, 10, 20, 30)) 39 | #' 40 | #' } 41 | #' @export 42 | 43 | #-----------------------------------------------------------------------------# 44 | #-----------------------------------------------------------------------------# 45 | 46 | sMoveRes = function(x, y) { 47 | 48 | #---------------------------------------------------------------------------# 49 | # 1. check inpur variables 50 | #---------------------------------------------------------------------------# 51 | 52 | if (!class(x)[1]%in%c('SpatVector')) {stop('"x" is not of a valid class')} 53 | if (!is.numeric(y)) {stop('"y" is not numeric')} 54 | if (!is.vector(y)) {stop('"y" is not a vector')} 55 | 56 | #---------------------------------------------------------------------------# 57 | # 2. find unique sample regions 58 | #---------------------------------------------------------------------------# 59 | 60 | out = do.call(rbind, lapply(y, function(r) { 61 | 62 | # reference raster (extend to avoid missing samples along the borders) 63 | e = extend(rast(ext(x), res=r, crs=crs(x)), r) 64 | 65 | # cell positions of elements x and region clustering 66 | odf = data.frame(resolution=r, 67 | nr.pixels=length(unique(cellFromXY(e, crds(x)))), 68 | nr.regions=length(unique(dbscan(crds(x), 69 | eps=r,borderPoints=T, 70 | minPts=1)$cluster))) 71 | 72 | # output data frame with statistics 73 | return(odf) 74 | 75 | })) 76 | 77 | # calculate the ratio of pixes/regions per number of entries in x 78 | out$nr.observations = nrow(x) 79 | out$pixel.ratio = out$nr.pixels / out$nr.observations 80 | out$region.ratio = out$nr.regions / out$nr.observations 81 | 82 | #---------------------------------------------------------------------------# 83 | # 3. plot output 84 | #---------------------------------------------------------------------------# 85 | 86 | # make color palette 87 | # make color palette 88 | cr = colorRampPalette(c('#8c510a','#bf812d','#dfc27d', 89 | '#f6e8c3','#f5f5f5','#c7eae5', 90 | '#80cdc1','#35978f','#01665e')) 91 | 92 | # build plot object 93 | out$resolution = factor(out$resolution, levels=y) 94 | p = ggplot(out, aes(x=resolution, y=pixel.ratio, fill=region.ratio)) + 95 | theme_bw(base_size=6) + geom_bar(stat="identity", width=1) + 96 | scale_y_continuous(expand=c(0,0)) + coord_cartesian(ylim=c(0,1.0)) + 97 | scale_fill_gradientn(colors=cr(9), breaks=c(0.0, 0.5, 1.0), 98 | limits=c(0,1.0), name="Region ratio\n") + 99 | labs(x="\nResolution", y="Pixel ratio\n") + 100 | theme(panel.grid=element_blank(), 101 | panel.background=element_blank(), 102 | panel.border=element_blank(), 103 | axis.line=element_line(linewidth=0.2, colour="grey5")) 104 | 105 | # return data frame and plot 106 | return(list(stats=out, plot=p)) 107 | 108 | } 109 | 110 | -------------------------------------------------------------------------------- /R/spaceDir.R: -------------------------------------------------------------------------------- 1 | #' @title spaceDir 2 | #' 3 | #' @description Analysis of environmental change in space along a movement track. 4 | #' @param x \emph{SpatVector} object. 5 | #' @param y Object of class \emph{SpatRaster}. 6 | #' @param z Object of class \emph{Date} or \emph{POSIXct} with observation dates for each entry in \emph{x}. 7 | #' @param space.buffer Spatial buffer size expressed in meters. 8 | #' @param time.buffer Temporal buffer size expressed in days. 9 | #' @param fun List of functions to apply to each time step. 10 | #' @param min.count Minimum number of pixels required by \emph{stat.fun}. Default is 2. 11 | #' @importFrom terra extract distance 12 | #' @seealso \code{\link{timeDir}} 13 | #' @details {The function quantifies environmental changes along a spatial 14 | #' gradient defined by GPS tracking dataset. For each GPS observation, the 15 | #' function finds the nearest observations within a given \emph{buffer.size}, 16 | #' and uses those observations to apply a user-define list of functions 17 | #' (\emph{fun}) applied to thethe underlying pixel values in \emph{y}. In 18 | #' addition, the function will report on the linear distance traveled 19 | #' between endpoints (in meters) and the associated travel time (in minutes).} 20 | #' @return {A \emph{data.frame} with statistics at each GPS observation. 21 | #' In addition the user defined statistics, the table will contain several 22 | #' entries describing the data used to calculate those statistics: 23 | #' \itemize{ 24 | #' \item{\emph{distance_traveled} - Total distance traveled between all observations} 25 | #' \item{\emph{elapsed_time} - Time between the first and last observation} 26 | #' \item{\emph{nr_observations} - nr_observations} 27 | #' \item{\emph{pixel_value} - Pixel value at the main observation}}} 28 | #' 29 | #' @examples { 30 | #' 31 | #' require(terra) 32 | #' 33 | #' # read raster data 34 | #' r <- raster(system.file('extdata', '2013-07-16_ndvi.tif', package="rsMove")) 35 | #' 36 | #' shortMove <- read.csv(system.file('extdata', 37 | #' 'shortMove.csv', package="rsMove")) 38 | #' 39 | #' # convert observations to vector 40 | #' shortMove = vect(shortMove, geom=c("x","y"), crs="EPSG:32632") 41 | #' 42 | #' # observation time 43 | #' obs.time <- strptime(paste0(shortMove$date, ' ',shortMove$time), 44 | #' format="%Y/%m/%d %H:%M:%S") 45 | #' 46 | #' # construct target functions 47 | #' functions <- list( 48 | #' slope=function(i) lm(i~c(1:length(i)))$coefficients[2][[1]], 49 | #' mean=function(i) mean(i, na.rm=T), 50 | #' sd=function(i) sd(i, na.rm=T) 51 | #' ) 52 | #' s.sample <- spaceDir(shortMove, r, obs.time, 30, 1, fun=functions) 53 | #' 54 | #' } 55 | #' @export 56 | 57 | #-----------------------------------------------------------------------------# 58 | #-----------------------------------------------------------------------------# 59 | 60 | spaceDir <- function(x, y, z, space.buffer, time.buffer, fun, min.count=2) { 61 | 62 | #---------------------------------------------------------------------------# 63 | # 1. check variables 64 | #---------------------------------------------------------------------------# 65 | 66 | # samples 67 | if (!class(x)%in%c('SpatVector')) stop('"x" is not of a valid class') 68 | 69 | # sample dates 70 | if (!class(z)[1]%in%c('Date', 'POSIXct')) stop('"z" not of a valid class') 71 | if (length(z)!=nrow(x)) stop('"x" shorter than "z"') 72 | 73 | # raster 74 | if (!class(y)[1]=='SpatRaster') stop('"y" is not of a valid class') 75 | if (crs(x)!=crs(y)) stop('"x" and "y" have different projections') 76 | 77 | # check input metrics 78 | if (!is.list(fun)) stop('"fun" must be a list of functions') else { 79 | fun_type = sapply(fun, function(f) class(f)) 80 | if (sum(fun_type == "function") != length(fun)) { 81 | stop('one or more elements in "fun" not a function') 82 | } 83 | } 84 | 85 | # check min.count 86 | if (!is.numeric(min.count)) stop('"min.count" must be a numeric element') 87 | 88 | #---------------------------------------------------------------------------# 89 | # 2. evaluate space-time dynamics in movement data 90 | #---------------------------------------------------------------------------# 91 | 92 | output = do.call(rbind, lapply(1:nrow(x), function(i) { 93 | 94 | # target observations 95 | ind = which( 96 | (distance(x[i,], x) < buffer.size) & 97 | (abs(difftime(z[i],z, units="d")) < time.buffer)) 98 | 99 | if (length(ind) > min.count) { 100 | 101 | # extract pixel values 102 | pixel_values = extract(y, x[ind,], ID=F)[[1]] 103 | 104 | # evaluate extracted pixel values 105 | tmp = as.data.frame(lapply(fun, function(f) f(pixel_values)[[1]])) 106 | tmp$distance_travelled=sum(sapply(2:length(ind), function(p) distance(y[(p-1):p]))) 107 | tmp$elapsed_time=difftime(max(z[ind]), min(z[ind]), units="d") 108 | tmp$nr_observations=length(ind) 109 | tmp$pixel_value = extract(y, x[i,], mean, ID=F)[[1]] 110 | 111 | } else { 112 | 113 | # return empty data.frame 114 | tmp = as.data.frame(matrix(NA,1,length(names(fun))+3)) 115 | colnames(tmp) = c(names(fun), 116 | "distance_traveled", 117 | "elapsed_time", 118 | "nr_observations", 119 | "pixel_value") 120 | 121 | } 122 | 123 | return(tmp) 124 | 125 | })) 126 | 127 | # export 128 | return(output) 129 | 130 | } 131 | -------------------------------------------------------------------------------- /R/tMoveRes.R: -------------------------------------------------------------------------------- 1 | #' @title tMoveRes 2 | #' 3 | #' @description Analysis of GPS data losses with the choice temporal resolutions. 4 | #' @param x \emph{spatVector}. 5 | #' @param y \emph{Date} vector with observation dates or each entry in \emph{x}. 6 | #' @param time.res Vector of temporal resolutions (expressed in days). 7 | #' @param pixel.res Spatial resolution (unit depends on spatial projection). 8 | #' @importFrom ggplot2 ggplot aes theme geom_bar coord_cartesian element_blank element_line scale_y_continuous scale_fill_gradientn labs 9 | #' @importFrom terra rast ext extend cellFromXY crs 10 | #' @importFrom plyr ddply . summarise 11 | #' @importFrom dbscan dbscan 12 | #' @importFrom grDevices colorRampPalette 13 | #' @details {For each spatial resolution given by \emph{pixel.res}, and for 14 | #' each temporal resolutions given by \emph{time.res}, the function simulates 15 | #' the number of unique pixels and pixel regions preserved after accounting 16 | #' for pseudo-replication at each hypothetical time step, assuming that the 17 | #' GPS data until the next date with available environmental data is 18 | #' aggregated into unique pixels. Finally, For each temporal aggregation 19 | #' window, the function reports the 'pixel ratio' and the 'region ratio', 20 | #' i.e., the number of pixels and regions divided by the number of GPS records.} 21 | #' @return {A \emph{list} containing: 22 | #' \itemize{ 23 | #' \item{\emph{stats} - Summary statistics reporting on 24 | #' the number of temporal widows, unique samples and unique 25 | #' sample regions per temporal resolution.} 26 | #' \item{\emph{summary.plot} - Plot representing the change in number 27 | #' of unique pixels and pixel regions per temporal resolution.} 28 | #' \item{\emph{temporal.plot} - Plot representing the change in number 29 | #' of unique pixels and pixel regions per temporal resolution and time step.}}} 30 | #' @seealso \code{\link{sMoveRes}} 31 | #' @examples { 32 | #' 33 | #' require(terra) 34 | #' 35 | #' #' # read movement data 36 | #' longMove = read.csv(system.file('extdata', 37 | #' 'longMove.csv', package="rsMove")) 38 | #' 39 | #' # convert observations to vector 40 | #' longMove = vect(longMove, geom=c("long","lat"), crs="EPSG:4326") 41 | #' 42 | #' # test function for intervals of 1, 8 and 16 days (e.g. of MODIS data) 43 | #' obs.date = as.Date(longMove$timestamp) 44 | #' a.res = tMoveRes(longMove, obs.date, c(1,8,16), 0.1) 45 | #' 46 | #' } 47 | #' @export 48 | 49 | #-----------------------------------------------------------------------------# 50 | #-----------------------------------------------------------------------------# 51 | 52 | tMoveRes = function(x, y, time.res, pixel.res) { 53 | 54 | #---------------------------------------------------------------------------# 55 | # 1. check input variables 56 | #---------------------------------------------------------------------------# 57 | 58 | if (!class(x)[1]%in%c('SpatVector')) stop('"x" is not of a valid class') 59 | if (!class(y)[1]%in%c('Date')) stop('"y" is not of a valid class') 60 | if (length(pixel.res)>1) {stop('"pixel.res" has more than one element')} 61 | if (!is.numeric(time.res)) {stop('"time.res" is not numeric')} 62 | 63 | #---------------------------------------------------------------------------# 64 | # 2. determine pixel aggregations 65 | #---------------------------------------------------------------------------# 66 | 67 | st = min(y) # start time 68 | et = max(y) # end time 69 | 70 | # reference raster (extend to avoid missing samples along the borders) 71 | e = extend(rast(ext(x), res=pixel.res, crs=crs(x)), pixel.res) 72 | 73 | out = do.call(rbind, lapply(time.res, function(r) { 74 | 75 | nw = round(as.numeric((et - st)) / r + 1) # number of temporal windows 76 | 77 | tmp = do.call(rbind, lapply(1:nw, function(w) { 78 | 79 | # last date 80 | date = ((st+r)+(r*w))-1 81 | 82 | # GPS observations recorded within the target window 83 | loc = which(y >= (st+r*(w-1)) & y <= (((st+r)+(r*w))-1)) 84 | 85 | if (length(loc) > 0) { 86 | 87 | # cell positions of elements x and region clustering 88 | odf = data.frame(date=date, resolution=r, nr.observations=length(loc), 89 | nr.pixels=length(unique(cellFromXY(e, crds(x[loc,])))), 90 | nr.regions=length(unique(dbscan(crds(x[loc,]), 91 | eps=r,borderPoints=T, 92 | minPts=1)$cluster))) 93 | 94 | return(odf) 95 | } else { 96 | return(data.frame(date=date, resolution=r, 97 | nr.observations=0, nr.pixels=0, 98 | nr.regions=0)) 99 | } 100 | 101 | })) 102 | 103 | # estimate final count of pixels/regions 104 | return(tmp) 105 | 106 | })) 107 | 108 | # calculate the ratio of pixes/regions per number of entries in x 109 | i = which(out$nr.observations > 0) 110 | out$pixel.ratio = 0 111 | out$region.ratio = 0 112 | out$pixel.ratio[i] = out$nr.pixels[i] / out$nr.observations[i] 113 | out$region.ratio[i] = out$nr.regions[i] / out$nr.observations[i] 114 | 115 | #---------------------------------------------------------------------------# 116 | # 3. plot output 117 | #---------------------------------------------------------------------------# 118 | 119 | # make color palette 120 | cr = colorRampPalette(c('#8c510a','#bf812d','#dfc27d', 121 | '#f6e8c3','#f5f5f5','#c7eae5', 122 | '#80cdc1','#35978f','#01665e')) 123 | 124 | # build plot object 125 | out$resolution = factor(out$resolution, levels=sort(time.res)) 126 | 127 | gdf = ddply(out, .(resolution), summarise, 128 | pixel.ratio=sum(nr.pixels)/sum(nr.observations), 129 | region.ratio=sum(nr.regions)/sum(nr.observations)) 130 | 131 | p1 = ggplot(gdf, aes(x=resolution, y=pixel.ratio, fill=region.ratio)) + 132 | theme_bw(base_size=6) + geom_bar(stat="identity", width=1) + 133 | scale_y_continuous(expand=c(0,0)) + coord_cartesian(ylim=c(0,1.0)) + 134 | scale_fill_gradientn(colors=cr(9), breaks=c(0.0, 0.5, 1.0), 135 | limits=c(0,1.0), name="Region ratio\n") + 136 | labs(x="\nResolution (m)", y="Pixel ratio\n") + 137 | theme(panel.grid=element_blank(), 138 | panel.background=element_blank(), 139 | panel.border=element_blank(), 140 | axis.line=element_line(linewidth=0.2, colour="grey5")) 141 | 142 | p2 = ggplot(out, aes(x=date, y=pixel.ratio)) + 143 | theme_bw(base_size=6) + 144 | geom_bar(stat="identity", width=1, fill="grey60") + 145 | scale_y_continuous(expand=c(0,0)) + coord_cartesian(ylim=c(0,1.0)) + 146 | labs(x="\nDate", y="Pixel ratio\n") + 147 | facet_wrap(~resolution, nrow=1) + # , strip.position="right") + 148 | theme(panel.grid=element_blank(), 149 | panel.background=element_blank(), 150 | strip.background=element_blank(), 151 | axis.line=element_line(linewidth=0.2, colour="grey5")) 152 | 153 | # return data frame and plot 154 | return(list(stats=out, summary.plot=p1, temporal.plot=p2)) 155 | 156 | } 157 | -------------------------------------------------------------------------------- /R/timeDir.R: -------------------------------------------------------------------------------- 1 | #' @title timeDir 2 | #' 3 | #' @description Analysis of environmental change in time for a set of coordinate pairs. 4 | #' @param x Object of class \emph{SpatRaster}. 5 | #' @param x.dates Object of class \emph{Date} with observation dates of \emph{y}. 6 | #' @param y Object of class \emph{SpatVector}. 7 | #' @param y.dates Object of class \emph{Date} with observation dates of \emph{y}. 8 | #' @param temporal.buffer two element vector with temporal window size (expressed in days). 9 | #' @param fun List of statistical function to apply to the data. 10 | #' @param min.count Minimum number of samples required by \emph{stat.fun}. Default is 2. 11 | #' @importFrom terra extract geom 12 | #' @importFrom stats median 13 | #' @seealso \code{\link{spaceDir}} 14 | #' @details {This function quantifies environmental changes in time for each 15 | #' GPS entry along a movement track. First, for each point in \emph{y}, the 16 | #' function compares its observation date (\emph{y.dates}) against the dates 17 | #' with environmental data (\emph{x.dates}), and preserves those entries in 18 | #' \emph{x} that fall within the \emph{temporal.buffer}. The user can adjust 19 | #' this window to determine which images are the most important. For example, 20 | #' if one wishes to know how the landscape evolved up to the observation date 21 | #' of the target sample, \emph{temporal.buffer} can be define as, e.g., c(30,0) 22 | #' forcing the function to only consider pixels recorded within the previous 30 23 | #' days. After selecting adequate temporal information for each data point, a 24 | #' list of user-defined statistical metrics are calculated (i.e., \emph{fun}).} 25 | #' @return {A \emph{data.frame} with statistics at each GPS observation. 26 | #' In addition the user defined statistics, the table will contain several 27 | #' entries describing the data used to calculate those statistics: 28 | #' \itemize{ 29 | #' \item{\emph{distance_traveled} - Total distance traveled between all observations} 30 | #' \item{\emph{elapsed_time} - Time between the first and last observation} 31 | #' \item{\emph{nr_observations} - nr_observations} 32 | #' \item{\emph{pixel_value} - Pixel value at the main observation}}} 33 | #' @examples { 34 | #' 35 | #' require(terra) 36 | #' 37 | #' # read raster data 38 | #' file <- list.files(system.file('extdata', '', package="rsMove"), 'ndvi.tif', full.names=TRUE) 39 | #' r.stk <- rast(file) 40 | #' r.stk <- c(r.stk, r.stk, r.stk) # dummy files for the example 41 | #' 42 | #' # read movement data 43 | #' shortMove <- read.csv(system.file('extdata', 'shortMove.csv', package="rsMove")) 44 | #' 45 | #' # convert observations to vector 46 | #' shortMove = vect(shortMove, geom=c("x","y"), crs="EPSG:32632") 47 | #' 48 | #' # raster dates 49 | #' r.dates <- seq.Date(as.Date("2013-08-01"), as.Date("2013-08-09"), 1) 50 | #' 51 | #' # sample dates 52 | #' obs.dates <- as.Date(shortMove$date) 53 | #' 54 | #' # perform directional sampling 55 | #' functions <- list( 56 | #' slope=function(x,y) lm(y~x)$coefficients[2][[1]], 57 | #' mean=function(x,y) mean(y, na.rm=T), 58 | #' sd=function(x,y) sd(y, na.rm=T) 59 | #' ) 60 | #' time.env <- timeDir(r.stk, r.dates, shortMove, obs.dates, 3, fun=functions) 61 | #' 62 | #' } 63 | #' @export 64 | 65 | #-----------------------------------------------------------------------------# 66 | #-----------------------------------------------------------------------------# 67 | 68 | timeDir <- function(x, x.dates, y, y.dates, temporal.buffer, fun=NULL, min.count=2) { 69 | 70 | #---------------------------------------------------------------------------# 71 | # 1. check variables 72 | #---------------------------------------------------------------------------# 73 | 74 | # check raster data 75 | if (class(x)!=c('SpatRaster')) stop('"x" is not of a valid class') 76 | if (class(x.dates)!=c('Date')) stop('"x.dates" is not of a valid class') 77 | 78 | # check vector data 79 | if (class(y)!=c('SpatVector')) stop('"y" is not of a valid class') 80 | if (class(y.dates)!=c('Date')) stop('"y.dates" is not of a valid class') 81 | 82 | # time information 83 | if (!is.numeric(temporal.buffer)) stop('"temporal.buffer" us not numeric') 84 | 85 | # check input metrics 86 | if (!is.list(fun)) stop('"fun" must be a list of functions') else { 87 | fun_type = sapply(fun, function(f) class(f)) 88 | if (sum(fun_type == "function") != length(fun)) { 89 | stop('one or more elements in "fun" not a function') 90 | } 91 | } 92 | 93 | # check min.count 94 | if (!is.numeric(min.count)) stop('"min.count" must be a numeric element') 95 | 96 | #---------------------------------------------------------------------------# 97 | # 2. retrieve environmental data 98 | #---------------------------------------------------------------------------# 99 | 100 | # retrieve environmental variables 101 | ind <- which(x.dates%in%seq.Date(min(y.dates-temporal.buffer), 102 | max(y.dates+temporal.buffer), by=1)) 103 | env.data <- extract(x[[ind]], geom(y)[,c("x","y")]) 104 | x.dates = x.dates[ind] 105 | 106 | #---------------------------------------------------------------------------# 107 | # 3. evaluate changes for each sampling location 108 | #---------------------------------------------------------------------------# 109 | 110 | statistics = do.call(rbind, lapply(1:nrow(y), function(i) { 111 | 112 | # find observations within temporal window 113 | ind <- which( 114 | (x.dates >= (y.dates[i]-temporal.buffer)) & 115 | (x.dates <= (y.dates[i]+temporal.buffer)) & 116 | (!is.na(env.data[i,]))) 117 | 118 | if (length(ind) >= min.count) { 119 | tx <- as.numeric(x.dates[ind]) 120 | ty <- as.numeric(env.data[i,ind]) 121 | tmp = as.data.frame(lapply(fun, function(f) f(tx,ty))) 122 | tmp$start.date = min(x.dates[ind]) 123 | tmp$median.date = median(x.dates[ind]) 124 | tmp$end.date = max(x.dates[ind]) 125 | tmp$nr_steps = length(ind) 126 | return(tmp) 127 | } else { 128 | # return empty data.frame 129 | tmp = as.data.frame(matrix(NA,1,length(names(fun))+4)) 130 | colnames(tmp) = c(names(fun), 131 | "start.date", "median.date", 132 | "end.date", "nr_steps") 133 | return(tmp) 134 | 135 | } 136 | })) 137 | 138 | return(statistics) 139 | 140 | } 141 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # rsMove 3 | [![CRAN version](https://www.r-pkg.org/badges/version/rsMove)](https://CRAN.R-project.org/package=rsMove) 4 | [![CRAN downloads](https://cranlogs.r-pkg.org/badges/last-month/rsMove?color=brightgreen)](https://CRAN.R-project.org/package=rsMove) 5 | [![CRAN downloads](http://cranlogs.r-pkg.org/badges/grand-total/rsMove?color=brightgreen)](https://CRAN.R-project.org/package=rsMove) 6 | 7 | ### Why develop rsMove? 8 | 9 |

10 | In the scope of movement ecology, Global Positioning Systems (GPS) have evolved significantly offering a unique insight into animal behavior. But understanding this behavior is dependent on our ability to comprehend the underlying environmental conditions that guides it. In this context, remote sensing becomes a fundamental tool. It provides information on the spatial and temporal variability of the landscape and provides us the means to understand the impact of environmental change over animal behavior. However, linking remote sensing and animal movement can be troublesome due to the differences in the spatial and temporal scales at which they are acquired (Neuman et al, 2015). As a consequence, methods that are sensitive to the constraints imposed by remote sensing in the analysis of animal movement are required. rsMove answers to this issue providing tools to query and analyze movement data using remote sensing. 11 |

12 | 13 |
14 | 15 | ### Installation 16 | This gitHub is used as a basis for the improvement of *rsMove*. A stable release is available on CRAN and can installed with: 17 | 18 | ```R 19 | install.packages('rsMove') 20 | ``` 21 | 22 |
23 | 24 | ### Examples 25 |

26 | A paper on the package is currently available describing the general applicability of rsMove. Addtitionally, we developed a vignette on the use of rsMove to predict environmental resource suitability following the methodology described in this paper. 27 |

28 | 29 |
30 | 31 | ### Bug reports & contact 32 | 33 | For bug reports, please use our issue page. Feature requests and other contributions are also welcome. 34 | 35 |
36 | 37 | ### What else are we doing? 38 |

39 | The Department of Remote Sensing of the University of Würzburg has developed other R packages that might interest you: 40 |

41 | 42 | * RStoolbox 43 | * moveVis 44 | * fieldRS 45 | * CAWaR 46 | 47 |

48 | Click here for news on our department. 49 |

50 | 51 |
52 | 53 | ### Aknowledgements 54 |

55 | This initiative is part of the Opt4Environment project and was funded by the German Aerospace Center (DLR) on behalf of the Federal Ministry for Economic Affairs and Energy (BMWi) with the research grant 50 EE 1403. The movement data we used was provided by the Max Planck institute for Ornithology (MPIo). Click below to reach the involved parties. 56 |

57 | 58 |
59 |

60 |                                            61 |

62 | 63 |
64 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## Release summary 2 | Minor bug fixes and improvement of function descriptions. 3 | 4 | ## Test environments 5 | * local OS X install, R 3.5.0 6 | * ubuntu 12.04 (on travis-ci), R 3.4.3 7 | * win-builder (devel and release) 8 | 9 | ## R CMD check results 10 | 11 | * There were no ERRORs or WARNINGs. 12 | 13 | ## Reverse dependencies 14 | We checked the dependencies by running R CMD check. 15 | 16 | --- 17 | -------------------------------------------------------------------------------- /inst/extdata/2013-07-16_ndvi.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RRemelgado/rsMove/8589a5b3c71a46c9319df212376293700741cbca/inst/extdata/2013-07-16_ndvi.tif -------------------------------------------------------------------------------- /inst/extdata/2013-08-01_ndvi.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RRemelgado/rsMove/8589a5b3c71a46c9319df212376293700741cbca/inst/extdata/2013-08-01_ndvi.tif -------------------------------------------------------------------------------- /inst/extdata/2013-08-17_ndvi.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RRemelgado/rsMove/8589a5b3c71a46c9319df212376293700741cbca/inst/extdata/2013-08-17_ndvi.tif -------------------------------------------------------------------------------- /inst/extdata/landCover.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RRemelgado/rsMove/8589a5b3c71a46c9319df212376293700741cbca/inst/extdata/landCover.tif -------------------------------------------------------------------------------- /inst/extdata/probabilities.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RRemelgado/rsMove/8589a5b3c71a46c9319df212376293700741cbca/inst/extdata/probabilities.tif -------------------------------------------------------------------------------- /inst/extdata/roi.cpg: -------------------------------------------------------------------------------- 1 | UTF-8 -------------------------------------------------------------------------------- /inst/extdata/roi.dbf: -------------------------------------------------------------------------------- 1 | vAIdN 0 -------------------------------------------------------------------------------- /inst/extdata/roi.prj: -------------------------------------------------------------------------------- 1 | PROJCS["WGS_1984_UTM_Zone_32N",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["Meter",1]] -------------------------------------------------------------------------------- /inst/extdata/roi.qpj: -------------------------------------------------------------------------------- 1 | PROJCS["WGS 84 / UTM zone 32N",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32632"]] 2 | -------------------------------------------------------------------------------- /inst/extdata/roi.shp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RRemelgado/rsMove/8589a5b3c71a46c9319df212376293700741cbca/inst/extdata/roi.shp -------------------------------------------------------------------------------- /inst/extdata/roi.shx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RRemelgado/rsMove/8589a5b3c71a46c9319df212376293700741cbca/inst/extdata/roi.shx -------------------------------------------------------------------------------- /inst/extdata/sampleIndices.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 1 3 | 1 4 | 2 5 | 2 6 | 2 -------------------------------------------------------------------------------- /inst/extdata/shortMove.csv: -------------------------------------------------------------------------------- 1 | "x","y","date","time","x.1","y.1","optional" 2 | 494876.9903,5288666.339,"2013/08/04","02:00:23",494876.9903,5288666.339,1 3 | 494875.696,5288669.008,"2013/08/04","02:05:06",494875.696,5288669.008,1 4 | 494877.9706,5288664.56,"2013/08/04","02:10:06",494877.9706,5288664.56,1 5 | 494878.3588,5288662.782,"2013/08/04","02:15:06",494878.3588,5288662.782,1 6 | 494875.7241,5288666.896,"2013/08/04","02:20:06",494875.7241,5288666.896,1 7 | 494880.1287,5288663.892,"2013/08/04","02:25:06",494880.1287,5288663.892,1 8 | 494878.525,5288664.226,"2013/08/04","02:30:06",494878.525,5288664.226,1 9 | 494877.4297,5288663.116,"2013/08/04","02:35:06",494877.4297,5288663.116,1 10 | 494881.1067,5288659.445,"2013/08/04","02:40:06",494881.1067,5288659.445,1 11 | 494878.6034,5288659.67,"2013/08/04","02:45:06",494878.6034,5288659.67,1 12 | 494877.7207,5288661.56,"2013/08/04","02:50:06",494877.7207,5288661.56,1 13 | 494876.4658,5288666.451,"2013/08/04","02:55:06",494876.4658,5288666.451,1 14 | 494877.0366,5288667.784,"2013/08/04","03:00:06",494877.0366,5288667.784,1 15 | 494876.21,5288665.451,"2013/08/04","03:05:06",494876.21,5288665.451,1 16 | 494877.5666,5288665.339,"2013/08/04","03:10:06",494877.5666,5288665.339,1 17 | 494877.4025,5288666.228,"2013/08/04","03:15:06",494877.4025,5288666.228,1 18 | 494875.3208,5288668.564,"2013/08/04","03:20:06",494875.3208,5288668.564,1 19 | 494873.5354,5288666.787,"2013/08/04","03:25:06",494873.5354,5288666.787,1 20 | 494877.987,5288666.116,"2013/08/04","03:30:06",494877.987,5288666.116,1 21 | 494873.2168,5288671.011,"2013/08/04","03:35:06",494873.2168,5288671.011,1 22 | 494872.9611,5288670.011,"2013/08/04","03:40:06",494872.9611,5288670.011,1 23 | 494879.0052,5288664.782,"2013/08/04","03:45:06",494879.0052,5288664.782,1 24 | 494869.6159,5288684.351,"2013/08/04","03:50:06",494869.6159,5288684.351,1 25 | 494871.7153,5288668.234,"2013/08/04","03:55:06",494871.7153,5288668.234,1 26 | 494871.3521,5288672.902,"2013/08/04","04:00:07",494871.3521,5288672.902,1 27 | 494870.872,5288672.347,"2013/08/04","04:05:06",494870.872,5288672.347,1 28 | 494878.9915,5288666.227,"2013/08/04","04:10:06",494878.9915,5288666.227,1 29 | 494874.4142,5288668.898,"2013/08/04","04:15:06",494874.4142,5288668.898,1 30 | 494872.5834,5288658.23,"2013/08/04","04:20:06",494872.5834,5288658.23,1 31 | 494875.9244,5288664.562,"2013/08/04","04:25:20",494875.9244,5288664.562,1 32 | 494869.8013,5288665.234,"2013/08/04","04:30:07",494869.8013,5288665.234,1 33 | 494882.5088,5288668.557,"2013/08/04","04:35:06",494882.5088,5288668.557,1 34 | 494875.2491,5288663.674,"2013/08/04","04:40:07",494875.2491,5288663.674,1 35 | 494880.7979,5288666.225,"2013/08/04","04:45:06",494880.7979,5288666.225,1 36 | 494870.9289,5288668.901,"2013/08/04","04:50:07",494870.9289,5288668.901,1 37 | 494877.0852,5288663.45,"2013/08/04","04:55:06",494877.0852,5288663.45,1 38 | 494872.001,5288669.234,"2013/08/04","05:00:06",494872.001,5288669.234,1 39 | 494873.5034,5288656.006,"2013/08/04","05:05:06",494873.5034,5288656.006,1 40 | 494876.389,5288672.897,"2013/08/04","05:10:07",494876.389,5288672.897,1 41 | 494877.1677,5288663.45,"2013/08/04","05:15:06",494877.1677,5288663.45,1 42 | 494870.3812,5288659.899,"2013/08/04","05:20:06",494870.3812,5288659.899,1 43 | 494883.241,5288674.336,"2013/08/04","05:25:06",494883.241,5288674.336,1 44 | 494882.8884,5288665.445,"2013/08/04","05:30:06",494882.8884,5288665.445,1 45 | 494879.6056,5288665.67,"2013/08/04","05:35:06",494879.6056,5288665.67,1 46 | 494873.3301,5288663.453,"2013/08/04","05:40:06",494873.3301,5288663.453,1 47 | 494886.1429,5288658.663,"2013/08/04","05:45:06",494886.1429,5288658.663,1 48 | 494870.3377,5288661.455,"2013/08/04","05:50:06",494870.3377,5288661.455,1 49 | 494916.8328,5288629.072,"2013/08/04","06:00:07",494916.8328,5288629.072,1 50 | 494871.313,5288662.566,"2013/08/04","06:05:07",494871.313,5288662.566,1 51 | 494867.5652,5288670.905,"2013/08/04","06:10:07",494867.5652,5288670.905,1 52 | 494876.0815,5288664.229,"2013/08/04","06:15:07",494876.0815,5288664.229,1 53 | 494877.2497,5288663.005,"2013/08/04","06:20:07",494877.2497,5288663.005,1 54 | 494869.3831,5288658.455,"2013/08/04","06:25:07",494869.3831,5288658.455,1 55 | 494874.3713,5288662.785,"2013/08/04","06:30:07",494874.3713,5288662.785,1 56 | 494835.3808,5288663.042,"2013/08/04","06:35:07",494835.3808,5288663.042,1 57 | 494787.0067,5288713.988,"2013/08/04","06:40:08",494787.0067,5288713.988,1 58 | 494804.4405,5288696.968,"2013/08/04","06:45:07",494804.4405,5288696.968,1 59 | 494796.5252,5288705.088,"2013/08/04","06:50:14",494796.5252,5288705.088,1 60 | 494806.5444,5288702.856,"2013/08/04","06:55:23",494806.5444,5288702.856,1 61 | 494818.5441,5288694.177,"2013/08/04","07:00:07",494818.5441,5288694.177,1 62 | 494814.1341,5288682.622,"2013/08/04","07:05:07",494814.1341,5288682.622,1 63 | 494815.8896,5288684.51,"2013/08/04","07:10:07",494815.8896,5288684.51,1 64 | 494816.9619,5288684.953,"2013/08/04","07:15:07",494816.9619,5288684.953,1 65 | 494816.5907,5288680.619,"2013/08/04","07:20:07",494816.5907,5288680.619,1 66 | 494817.6686,5288687.398,"2013/08/04","07:25:07",494817.6686,5288687.398,1 67 | 494810.4505,5288678.846,"2013/08/04","07:30:06",494810.4505,5288678.846,1 68 | 494952.4856,5288307.065,"2013/08/04","07:35:06",494952.4856,5288307.065,1 69 | 494966.0338,5288328.17,"2013/08/04","07:40:07",494966.0338,5288328.17,1 70 | 494931.7416,5288372.212,"2013/08/04","07:45:07",494931.7416,5288372.212,1 71 | 494943.3089,5288425.439,"2013/08/04","07:50:07",494943.3089,5288425.439,1 72 | 494915.676,5288403.79,"2013/08/04","07:55:06",494915.676,5288403.79,1 73 | 494901.1028,5288367.793,"2013/08/04","08:00:07",494901.1028,5288367.793,1 74 | 494904.2282,5288384.573,"2013/08/04","08:05:23",494904.2282,5288384.573,1 75 | 494886.3581,5288426.6,"2013/08/04","08:10:06",494886.3581,5288426.6,1 76 | 494876.5668,5288381.93,"2013/08/04","08:15:06",494876.5668,5288381.93,1 77 | 494862.7519,5288364.604,"2013/08/04","08:20:06",494862.7519,5288364.604,1 78 | 494875.8933,5288425.609,"2013/08/04","08:25:06",494875.8933,5288425.609,1 79 | 494924.8655,5288437.791,"2013/08/04","08:30:06",494924.8655,5288437.791,1 80 | 494929.2401,5288451.68,"2013/08/04","08:35:07",494929.2401,5288451.68,1 81 | 495071.7455,5288379.871,"2013/08/04","08:40:07",495071.7455,5288379.871,1 82 | 495066.8666,5288380.765,"2013/08/04","08:45:07",495066.8666,5288380.765,1 83 | 495036.1726,5288373.122,"2013/08/04","08:50:07",495036.1726,5288373.122,1 84 | 495072.4543,5288375.981,"2013/08/04","08:55:07",495072.4543,5288375.981,1 85 | 495073.595,5288377.536,"2013/08/04","09:00:07",495073.595,5288377.536,1 86 | 495071.7665,5288386.984,"2013/08/04","09:05:07",495071.7665,5288386.984,1 87 | 495066.646,5288412.218,"2013/08/04","09:10:07",495066.646,5288412.218,1 88 | 495051.8106,5288401.783,"2013/08/04","09:15:07",495051.8106,5288401.783,1 89 | 495074.493,5288375.868,"2013/08/04","09:20:07",495074.493,5288375.868,1 90 | 495073.1151,5288377.425,"2013/08/04","09:25:07",495073.1151,5288377.425,1 91 | 494879.9807,5288666.003,"2013/08/04","09:30:07",494879.9807,5288666.003,1 92 | 494876.5672,5288662.45,"2013/08/04","09:35:06",494876.5672,5288662.45,1 93 | 494867.3276,5288664.903,"2013/08/04","09:40:07",494867.3276,5288664.903,1 94 | 494879.427,5288667.115,"2013/08/04","09:45:07",494879.427,5288667.115,1 95 | 494874.997,5288666.786,"2013/08/04","09:50:07",494874.997,5288666.786,1 96 | 494878.49,5288667.005,"2013/08/04","09:55:07",494878.49,5288667.005,1 97 | 494876.7035,5288664.117,"2013/08/04","10:00:07",494876.7035,5288664.117,1 98 | 494882.9839,5288671.78,"2013/08/04","10:05:07",494882.9839,5288671.78,1 99 | 494886.5589,5288662.997,"2013/08/04","10:10:07",494886.5589,5288662.997,1 100 | 494878.4888,5288665.671,"2013/08/04","10:15:23",494878.4888,5288665.671,1 101 | 494877.6403,5288663.894,"2013/08/04","10:20:07",494877.6403,5288663.894,1 102 | 494882.501,5288659.666,"2013/08/04","10:25:07",494882.501,5288659.666,1 103 | 494877.2616,5288668.006,"2013/08/04","10:30:07",494877.2616,5288668.006,1 104 | 494877.2442,5288665.228,"2013/08/04","10:30:17",494877.2442,5288665.228,1 105 | 494882.68,5288667.112,"2013/08/04","10:35:07",494882.68,5288667.112,1 106 | 494877.8247,5288669.006,"2013/08/04","10:40:06",494877.8247,5288669.006,1 107 | 494879.2728,5288670.783,"2013/08/04","10:45:07",494879.2728,5288670.783,1 108 | 494877.2238,5288667.562,"2013/08/04","10:50:06",494877.2238,5288667.562,1 109 | 494876.7597,5288668.34,"2013/08/04","10:55:07",494876.7597,5288668.34,1 110 | 494876.3314,5288667.118,"2013/08/04","11:00:07",494876.3314,5288667.118,1 111 | 494876.1218,5288667.341,"2013/08/04","11:05:06",494876.1218,5288667.341,1 112 | 494877.5238,5288667.784,"2013/08/04","11:10:06",494877.5238,5288667.784,1 113 | 494875.5017,5288669.675,"2013/08/04","11:15:06",494875.5017,5288669.675,1 114 | 494877.4663,5288662.116,"2013/08/04","11:20:07",494877.4663,5288662.116,1 115 | 494875.2683,5288668.453,"2013/08/04","11:25:07",494875.2683,5288668.453,1 116 | 494876.5023,5288665.34,"2013/08/04","11:30:06",494876.5023,5288665.34,1 117 | 494872.5215,5288664.565,"2013/08/04","11:35:06",494872.5215,5288664.565,1 118 | 494873.0018,5288673.678,"2013/08/04","11:40:07",494873.0018,5288673.678,1 119 | 494877.3952,5288675.008,"2013/08/04","11:45:07",494877.3952,5288675.008,1 120 | 494876.5106,5288666.34,"2013/08/04","11:50:07",494876.5106,5288666.34,1 121 | 494874.5561,5288668.342,"2013/08/04","11:55:07",494874.5561,5288668.342,1 122 | 494871.5828,5288671.012,"2013/08/04","12:00:07",494871.5828,5288671.012,1 123 | -------------------------------------------------------------------------------- /man/day_overlap.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/day_overlap.R 3 | \name{day_overlap} 4 | \alias{day_overlap} 5 | \title{day_overlap} 6 | \usage{ 7 | day_overlap(x) 8 | } 9 | \arguments{ 10 | \item{x}{Observation time. Object of class \emph{POSIXct}, \emph{POSIXlt}, or \emph{POSIXt}.} 11 | } 12 | \value{ 13 | {A \emph{list} containing: 14 | \itemize{ 15 | \item{\emph{day.cover} - Percent of the day with recorded timestamps.} 16 | \item{\emph{day.overlap} - Percent of the day covered between the first and last timestamp.} 17 | } 18 | } 19 | } 20 | \description{ 21 | Estimate the number of the day covered by timestamps. 22 | } 23 | \details{ 24 | {The function estimates how much of a day is covered 25 | by the timestamps in \emph{x}, which correspond to the timestamps 26 | in a movement dataset. Note that \emph{x} is not accepted if 27 | containing data from more than one day.} 28 | } 29 | \examples{ 30 | { 31 | 32 | # load samples 33 | multiMove <- read.csv(system.file('extdata', 'multiMove.csv', package="rsMove")) 34 | 35 | # data-time of species observations 36 | times = strptime(multiMove$timestamp,format="\%Y-\%m-\%d \%H:\%M:\%S") 37 | 38 | # subset to first unique day 39 | days = as.Date(times) 40 | times = times[which(days == unique(days)[1])] 41 | 42 | # extract regions 43 | day_overlap(date)]) 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /man/map_sites.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/map_sites.R 3 | \name{map_sites} 4 | \alias{map_sites} 5 | \title{map_sites} 6 | \usage{ 7 | map_sites(x, y, z, resolution, min_size = 1) 8 | } 9 | \arguments{ 10 | \item{x}{Object of class \emph{SpatVector}.} 11 | 12 | \item{y}{Unique individual identifier for each entry in \emph{x}.} 13 | 14 | \item{z}{Timestamps of each element in \emph{x} given as a \emph{POSIXct} object.} 15 | 16 | \item{resolution}{Maximum distance between data points to identify clusters.} 17 | 18 | \item{min_size}{Minimum number of GPS data points per cluster.} 19 | } 20 | \value{ 21 | {A \emph{list} containing: 22 | \itemize{ 23 | \item{\emph{region.id} - Vector reporting on the region each element in \emph{x} belongs to.} 24 | \item{\emph{region.polygons} - Polygons for each temporal segment in each \emph{region.id.} 25 | \item{\emph{region.stats} - Statistics for each temporal segment.} 26 | } 27 | }} 28 | } 29 | \description{ 30 | Detection of geographic regions of samples using a pixel based approach. 31 | } 32 | \details{ 33 | {The function provides three outputs. First, it labels each 34 | entry in \emph{x} based on their spatial connectivity. Connections are 35 | based on \emph{resolution}, which defines the maximum distance allowed 36 | among members of a group of data points, below which the elements of 37 | that group are treated as a region and labeled with the same unique, 38 | non-zero, numeric identifier. Regions where the number of data points 39 | is below \emph{min_size} are labelled with a 0. Second, for each region, 40 | the function will return a polygon defined by the convex hull of the 41 | composing data points. 0-labeled regions are excluded, as well as those 42 | where the number of data points is less than 2. Third, for each region, 43 | and for each sequence of days with data points within the target region, 44 | we provide the following information and statistics: 45 | \itemize{ 46 | \item{\emph{region_id} - Region unique identifier.} 47 | \item{\emph{start.date} - Segment unique identifier.} 48 | \item{\emph{start.date} - First data date.} 49 | \item{\emph{end.date} - Last data date.} 50 | \item{\emph{day_cover_mean} - Mean percentage of the days in the segment with movement data} 51 | \item{\emph{day_cover_sd} - Standard deviation of the days in the segment with movement data.} 52 | \item{\emph{day_overlap_mean} - Mean percentage of the day across which movement data were recorded.} 53 | \item{\emph{day_overlap_sd} - Standard deviation of the percentage of the day across which movement data were recorded.} 54 | \item{\emph{nr_days} - Number of days with movement data.} 55 | \item{\emph{nr_individuals} - Number of individuals tracked.} 56 | \item{\emph{nr_records} - Number of movement data records.} 57 | \item{\emph{data_frequency_mean} - Mean frequency of GPS entries (in minutes).} 58 | \item{\emph{data_frequency_sd} - Standard deviation of the frequency of GPS entries (in minutes)} 59 | } 60 | Day segments are treated separately because they indicate that a given 61 | area was persistently occupied by one or more tracked individuals.} 62 | } 63 | \examples{ 64 | { 65 | 66 | require(terra) 67 | 68 | # load samples 69 | multiMove <- read.csv(system.file('extdata', 'multiMove.csv', package="rsMove")) 70 | 71 | # convert samples to vector 72 | multiMove = vect(multiMove, geom=c("x","y"), crs="EPSG:4326")[1:10000,] 73 | 74 | # species identifier (only one individual, so set to 1) 75 | species_id = multiMove$id 76 | 77 | # data-time of species observations 78 | date = strptime(multiMove$timestamp,format="\%Y-\%m-\%d \%H:\%M:\%S", tz="GMT") 79 | 80 | # extract regions 81 | hm <- map_sites(multiMove, species_id, date, 0.1) 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /man/moveCloud.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/moveCloud.R 3 | \name{moveCloud} 4 | \alias{moveCloud} 5 | \title{moveCloud} 6 | \usage{ 7 | moveCloud(x, y, start, end, interval, data.path) 8 | } 9 | \arguments{ 10 | \item{x}{Object of class \emph{SpatVector}.} 11 | 12 | \item{y}{Object of class \emph{Date} with observation dates of \emph{y}.} 13 | 14 | \item{start}{First data from when to download CCF data.} 15 | 16 | \item{end}{Last data from when to download CCF data.} 17 | 18 | \item{interval}{Daily interval of data download.} 19 | 20 | \item{data.path}{Output path for downloading data on cloud cover.} 21 | } 22 | \value{ 23 | {A \emph{list} containing: \itemize{ 24 | \item{\emph{x.stats} - CCF statistics at each entry in \emph{x}} 25 | \item{\emph{r.stats} - CCF statistics at each unique date between\emph{start} and \emph{end} summarized across the elements of \emph{x}} 26 | \item{\emph{x.plot} - Plot of the data in \emph{x.stats}}. 27 | \item{\emph{r.plot} - Plot of the data in \emph{r.stats}}.}} 28 | } 29 | \description{ 30 | Extract Cloud Cover Fraction (CCF) data for a set of coordinate pairs. 31 | } 32 | \details{ 33 | {The function extracts data on daily Cloud Cover Fractions (CCF) 34 | from NASA's Earth Observation (NEO). For a sequence of dates defined by 35 | \emph{start}, \emph{end}, and \emph{interval}, the function downloads the 36 | correspondent CCF data and stores them in \emph{data.path}. These data, 37 | which have a global coverage and a spatial resolution of 0.1 degrees, will 38 | only be downloaded if they do not already exist in \emph{data.path}. When 39 | checking for existing data, the function will follow a standard naming 40 | convention, so data acquired independently will likely be missed. After 41 | downloading the needed data, the function will extract the CCF fraction 42 | values for all dates at the coordinates in \emph{x}. This will be used to 43 | calculate the mean and standard deviation of the CCF at each date. In 44 | addition, the function will extract CCF data for each date in \emph{y} that 45 | falls between \emph{start} and \emph{end}.} 46 | } 47 | \examples{ 48 | \dontrun{ 49 | 50 | require(terra) 51 | 52 | # read movement data 53 | longMove <- read.csv(system.file('extdata', 54 | 'longMove.csv', package="rsMove")) 55 | 56 | # convert observations to vector 57 | longMove = vect(longMove, geom=c("long","lat"), crs="EPSG:4326") 58 | 59 | # test function for 30 day buffer 60 | obs.dates <- as.Date(longMove$timestamp) 61 | c.cover <- moveCloud(shortMove, obs.dates) 62 | 63 | } 64 | } 65 | \references{ 66 | \url{https://cneos.jpl.nasa.gov/} 67 | } 68 | \seealso{ 69 | \code{\link{sMoveRes}} \code{\link{tMoveRes}} 70 | } 71 | -------------------------------------------------------------------------------- /man/moveReduce.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/moveReduce.R 3 | \name{moveReduce} 4 | \alias{moveReduce} 5 | \title{moveReduce} 6 | \usage{ 7 | moveReduce(x, y, z, preserve.revisits = TRUE) 8 | } 9 | \arguments{ 10 | \item{x}{Object of class \emph{spatVector}.} 11 | 12 | \item{y}{Object of class \emph{spatRaster}.} 13 | 14 | \item{z}{Object of class \emph{Date} or \emph{POSIXct} with the observation time of each element in \emph{x}.} 15 | 16 | \item{preserve.revisits}{Logical. Should the function preserve revisit patterns?} 17 | } 18 | \value{ 19 | A \emph{spatVector} object. 20 | } 21 | \description{ 22 | Pixel based summary of movement data that preserves periodic movements. 23 | } 24 | \details{ 25 | {Translates (\emph{x}) into pixel coordinates within a reference 26 | raster (\emph{y}). The function identifies temporal segments corresponding 27 | to groups of consecutive observations within the same pixel. In this process, 28 | revisits to recorded pixels are preserved. Once the segments are identified, 29 | the function derives mean x and y coordinates for each of them and evaluates 30 | the time spent within each pixel. The function reports on the start and end 31 | timestamps and the elapsed time. If \emph{preserve.revisits} is FALSE, the 32 | function will then summarize the output on a pixel level summing the time 33 | spent at each pixel. Additionally, if \emph{derive.raster} is TRUE, the 34 | function will derive a \emph{RasterLayer} with the same configuration as 35 | \emph{y} depicting the the total amount of time spent per pixel. 36 | The output of the function consists of a \emph{spatVector} 37 | with the reduced sample set.} 38 | } 39 | \examples{ 40 | { 41 | 42 | require(terra) 43 | 44 | # read raster data 45 | r <- (system.file('extdata', '2013-07-16_ndvi.tif', package="rsMove")) 46 | 47 | # read movement data 48 | shortMove <- read.csv(system.file('extdata', 'shortMove.csv', package="rsMove")) 49 | 50 | # convert observations to vector 51 | shortMove = vect(shortMove, geom=c("x","y"), crs="EPSG:32632") 52 | 53 | # observation time 54 | z <- as.POSIXct(strptime(paste0(shortMove$date, ' ', shortMove$time), 55 | format="\%Y/\%m/\%d \%H:\%M:\%S")) 56 | 57 | # reduce amount of samples 58 | move.reduce <- moveReduce(shortMove, r, z, derive.raster=TRUE) 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /man/sMoveRes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sMoveRes.R 3 | \name{sMoveRes} 4 | \alias{sMoveRes} 5 | \title{sMoveRes} 6 | \usage{ 7 | sMoveRes(x, y) 8 | } 9 | \arguments{ 10 | \item{x}{Object of class \emph{spatVector}.} 11 | 12 | \item{y}{Numeric vector with target spatial resolutions.} 13 | } 14 | \value{ 15 | A \emph{list}. 16 | 17 | {The function returns a list with: 18 | \itemize{ 19 | \item{\emph{stats} - Summary statistics reporting on the number 20 | of unique samples and sample regions per spatial resolution.} 21 | \item{\emph{plot} - Plot representing the change in number of 22 | samples and sample regions per spatial resolution.}}} 23 | } 24 | \description{ 25 | Analysis of GPS data losses with the choice spatial resolutions. 26 | } 27 | \details{ 28 | {The function simulates how many unique combinations of animal 29 | observations and environmental data would be preserved when choosing an 30 | environmental dataset with the resolution given in \emph{y}. It also 31 | estimates simulates how many unique pixel regions (i.e., groups of 32 | spatially connected pixels) would be preserved after accounting for 33 | pseudo-replication. Finally, For each spatial resolution, the function 34 | reports the 'pixel ratio' and the 'region ratio', i.e., the number of 35 | pixels and regions divided by the number of GPS records.} 36 | } 37 | \examples{ 38 | { 39 | 40 | require(terra) 41 | 42 | # read movement data 43 | shortMove = read.csv(system.file('extdata', 44 | 'shortMove.csv', package="rsMove")) 45 | 46 | # convert observations to vector 47 | shortMove = vect(shortMove, geom=c("x","y"), crs="EPSG:32632") 48 | 49 | # test function for 5, 10 20 and 30 m 50 | a.res = sMoveRes(shortMove, c(5, 10, 20, 30)) 51 | 52 | } 53 | } 54 | \seealso{ 55 | \code{\link{tMoveRes}} 56 | } 57 | -------------------------------------------------------------------------------- /man/spaceDir.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/spaceDir.R 3 | \name{spaceDir} 4 | \alias{spaceDir} 5 | \title{spaceDir} 6 | \usage{ 7 | spaceDir(x, y, z, space.buffer, time.buffer, fun, min.count = 2) 8 | } 9 | \arguments{ 10 | \item{x}{\emph{SpatVector} object.} 11 | 12 | \item{y}{Object of class \emph{SpatRaster}.} 13 | 14 | \item{z}{Object of class \emph{Date} or \emph{POSIXct} with observation dates for each entry in \emph{x}.} 15 | 16 | \item{space.buffer}{Spatial buffer size expressed in meters.} 17 | 18 | \item{time.buffer}{Temporal buffer size expressed in days.} 19 | 20 | \item{fun}{List of functions to apply to each time step.} 21 | 22 | \item{min.count}{Minimum number of pixels required by \emph{stat.fun}. Default is 2.} 23 | } 24 | \value{ 25 | {A \emph{data.frame} with statistics at each GPS observation. 26 | In addition the user defined statistics, the table will contain several 27 | entries describing the data used to calculate those statistics: 28 | \itemize{ 29 | \item{\emph{distance_traveled} - Total distance traveled between all observations} 30 | \item{\emph{elapsed_time} - Time between the first and last observation} 31 | \item{\emph{nr_observations} - nr_observations} 32 | \item{\emph{pixel_value} - Pixel value at the main observation}}} 33 | } 34 | \description{ 35 | Analysis of environmental change in space along a movement track. 36 | } 37 | \details{ 38 | {The function quantifies environmental changes along a spatial 39 | gradient defined by GPS tracking dataset. For each GPS observation, the 40 | function finds the nearest observations within a given \emph{buffer.size}, 41 | and uses those observations to apply a user-define list of functions 42 | (\emph{fun}) applied to thethe underlying pixel values in \emph{y}. In 43 | addition, the function will report on the linear distance traveled 44 | between endpoints (in meters) and the associated travel time (in minutes).} 45 | } 46 | \examples{ 47 | { 48 | 49 | require(terra) 50 | 51 | # read raster data 52 | r <- raster(system.file('extdata', '2013-07-16_ndvi.tif', package="rsMove")) 53 | 54 | shortMove <- read.csv(system.file('extdata', 55 | 'shortMove.csv', package="rsMove")) 56 | 57 | # convert observations to vector 58 | shortMove = vect(shortMove, geom=c("x","y"), crs="EPSG:32632") 59 | 60 | # observation time 61 | obs.time <- strptime(paste0(shortMove$date, ' ',shortMove$time), 62 | format="\%Y/\%m/\%d \%H:\%M:\%S") 63 | 64 | # construct target functions 65 | functions <- list( 66 | slope=function(i) lm(i~c(1:length(i)))$coefficients[2][[1]], 67 | mean=function(i) mean(i, na.rm=T), 68 | sd=function(i) sd(i, na.rm=T) 69 | ) 70 | s.sample <- spaceDir(shortMove, r, obs.time, 30, 1, fun=functions) 71 | 72 | } 73 | } 74 | \seealso{ 75 | \code{\link{timeDir}} 76 | } 77 | -------------------------------------------------------------------------------- /man/tMoveRes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tMoveRes.R 3 | \name{tMoveRes} 4 | \alias{tMoveRes} 5 | \title{tMoveRes} 6 | \usage{ 7 | tMoveRes(x, y, time.res, pixel.res) 8 | } 9 | \arguments{ 10 | \item{x}{\emph{spatVector}.} 11 | 12 | \item{y}{\emph{Date} vector with observation dates or each entry in \emph{x}.} 13 | 14 | \item{time.res}{Vector of temporal resolutions (expressed in days).} 15 | 16 | \item{pixel.res}{Spatial resolution (unit depends on spatial projection).} 17 | } 18 | \value{ 19 | {A \emph{list} containing: 20 | \itemize{ 21 | \item{\emph{stats} - Summary statistics reporting on 22 | the number of temporal widows, unique samples and unique 23 | sample regions per temporal resolution.} 24 | \item{\emph{summary.plot} - Plot representing the change in number 25 | of unique pixels and pixel regions per temporal resolution.} 26 | \item{\emph{temporal.plot} - Plot representing the change in number 27 | of unique pixels and pixel regions per temporal resolution and time step.}}} 28 | } 29 | \description{ 30 | Analysis of GPS data losses with the choice temporal resolutions. 31 | } 32 | \details{ 33 | {For each spatial resolution given by \emph{pixel.res}, and for 34 | each temporal resolutions given by \emph{time.res}, the function simulates 35 | the number of unique pixels and pixel regions preserved after accounting 36 | for pseudo-replication at each hypothetical time step, assuming that the 37 | GPS data until the next date with available environmental data is 38 | aggregated into unique pixels. Finally, For each temporal aggregation 39 | window, the function reports the 'pixel ratio' and the 'region ratio', 40 | i.e., the number of pixels and regions divided by the number of GPS records.} 41 | } 42 | \examples{ 43 | { 44 | 45 | require(terra) 46 | 47 | #' # read movement data 48 | longMove = read.csv(system.file('extdata', 49 | 'longMove.csv', package="rsMove")) 50 | 51 | # convert observations to vector 52 | longMove = vect(longMove, geom=c("long","lat"), crs="EPSG:4326") 53 | 54 | # test function for intervals of 1, 8 and 16 days (e.g. of MODIS data) 55 | obs.date = as.Date(longMove$timestamp) 56 | a.res = tMoveRes(longMove, obs.date, c(1,8,16), 0.1) 57 | 58 | } 59 | } 60 | \seealso{ 61 | \code{\link{sMoveRes}} 62 | } 63 | -------------------------------------------------------------------------------- /man/timeDir.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/timeDir.R 3 | \name{timeDir} 4 | \alias{timeDir} 5 | \title{timeDir} 6 | \usage{ 7 | timeDir(x, x.dates, y, y.dates, temporal.buffer, fun = NULL, min.count = 2) 8 | } 9 | \arguments{ 10 | \item{x}{Object of class \emph{SpatRaster}.} 11 | 12 | \item{x.dates}{Object of class \emph{Date} with observation dates of \emph{y}.} 13 | 14 | \item{y}{Object of class \emph{SpatVector}.} 15 | 16 | \item{y.dates}{Object of class \emph{Date} with observation dates of \emph{y}.} 17 | 18 | \item{temporal.buffer}{two element vector with temporal window size (expressed in days).} 19 | 20 | \item{fun}{List of statistical function to apply to the data.} 21 | 22 | \item{min.count}{Minimum number of samples required by \emph{stat.fun}. Default is 2.} 23 | } 24 | \value{ 25 | {A \emph{data.frame} with statistics at each GPS observation. 26 | In addition the user defined statistics, the table will contain several 27 | entries describing the data used to calculate those statistics: 28 | \itemize{ 29 | \item{\emph{distance_traveled} - Total distance traveled between all observations} 30 | \item{\emph{elapsed_time} - Time between the first and last observation} 31 | \item{\emph{nr_observations} - nr_observations} 32 | \item{\emph{pixel_value} - Pixel value at the main observation}}} 33 | } 34 | \description{ 35 | Analysis of environmental change in time for a set of coordinate pairs. 36 | } 37 | \details{ 38 | {This function quantifies environmental changes in time for each 39 | GPS entry along a movement track. First, for each point in \emph{y}, the 40 | function compares its observation date (\emph{y.dates}) against the dates 41 | with environmental data (\emph{x.dates}), and preserves those entries in 42 | \emph{x} that fall within the \emph{temporal.buffer}. The user can adjust 43 | this window to determine which images are the most important. For example, 44 | if one wishes to know how the landscape evolved up to the observation date 45 | of the target sample, \emph{temporal.buffer} can be define as, e.g., c(30,0) 46 | forcing the function to only consider pixels recorded within the previous 30 47 | days. After selecting adequate temporal information for each data point, a 48 | list of user-defined statistical metrics are calculated (i.e., \emph{fun}).} 49 | } 50 | \examples{ 51 | { 52 | 53 | require(terra) 54 | 55 | # read raster data 56 | file <- list.files(system.file('extdata', '', package="rsMove"), 'ndvi.tif', full.names=TRUE) 57 | r.stk <- rast(file) 58 | r.stk <- c(r.stk, r.stk, r.stk) # dummy files for the example 59 | 60 | # read movement data 61 | shortMove <- read.csv(system.file('extdata', 'shortMove.csv', package="rsMove")) 62 | 63 | # convert observations to vector 64 | shortMove = vect(shortMove, geom=c("x","y"), crs="EPSG:32632") 65 | 66 | # raster dates 67 | r.dates <- seq.Date(as.Date("2013-08-01"), as.Date("2013-08-09"), 1) 68 | 69 | # sample dates 70 | obs.dates <- as.Date(shortMove$date) 71 | 72 | # perform directional sampling 73 | functions <- list( 74 | slope=function(x,y) lm(y~x)$coefficients[2][[1]], 75 | mean=function(x,y) mean(y, na.rm=T), 76 | sd=function(x,y) sd(y, na.rm=T) 77 | ) 78 | time.env <- timeDir(r.stk, r.dates, shortMove, obs.dates, 3, fun=functions) 79 | 80 | } 81 | } 82 | \seealso{ 83 | \code{\link{spaceDir}} 84 | } 85 | -------------------------------------------------------------------------------- /rsMove.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 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 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace,vignette 22 | --------------------------------------------------------------------------------