Skip to content

Commit

Permalink
Merge pull request #5 from ropensci/gganimate
Browse files Browse the repository at this point in the history
get animation working with new version of gganimate
  • Loading branch information
rmendels authored May 19, 2019
2 parents 49b38ff + 05bf1bc commit ba42520
Show file tree
Hide file tree
Showing 146 changed files with 4,478 additions and 11,669 deletions.
6 changes: 6 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
^README\.Rmd$
^.*\.Rproj$
^\.Rproj\.user$
.travis.yml
README.Rmd
README_cache
README_files
myPlot.png
^README_files$
^README_old\.md$
^README_old\.Rmd$
^cran-comments\.md$
^tests$
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
inst/doc
.Rproj.user
.Rhistory
.RData
Expand Down
35 changes: 20 additions & 15 deletions DESCRIPTION
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,35 +1,40 @@
Package: plotdap
Title: Easily visualize data from 'ERDDAP' Servers via the rerddap package
Version: 0.0.1
Authors@R: c(person("Carson", "Sievert", role = c("aut","cre"), email = "[email protected]"))
Description: Easily visualize 'tabledap' and 'griddap' objects obtained via the rerddap package.
Title: Easily Visualize Data from 'ERDDAP' Servers via the 'rerddap' Package
Version: 0.0.2
Date: 2019-04-29
Authors@R: c(
person("Carson", "Sievert", role = "aut"),
person("Roy", "Mendelssohn", role = c("aut", "ctb", "cre"), email = "[email protected]"))
Description: Easily visualize and animate 'tabledap' and 'griddap' objects obtained via the 'rerddap' package in a simple one-line command, using either base graphics or 'ggplot2' graphics. 'plotdap' handles extracting and reshaping the data, map projections and continental outlines. Optionally the data can be animated through time using the 'gganmiate' package.
License: MIT + file LICENSE
URL: https://github.com/ropensci/plotdap
BugReports: https://github.com/ropensci/plotdap/issues
Depends:
rerddap
R (>= 3.5.0),
rerddap
Imports:
magrittr,
tidyr,
dplyr,
gganimate,
ggplot2 (>= 3.1.0),
lazyeval,
ggplot2 (>= 2.2.1),
scales,
maps (>= 3.2.0),
lubridate,
magrittr,
mapdata,
maps (>= 3.2.0),
maptools,
plot3D,
rgdal,
rgeos,
raster,
scales,
sf,
rgeos,
rgdal
tidyr
Suggests:
testthat,
Cairo,
knitr,
rmarkdown
Remotes:
tidyverse/ggplot2
RoxygenNote: 6.0.1
RoxygenNote: 6.1.1
LazyData: true
Encoding: UTF-8
VignetteBuilder: knitr
Empty file modified LICENSE
100644 → 100755
Empty file.
2 changes: 2 additions & 0 deletions NAMESPACE
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export("%>%")
export(add_ggplot)
export(add_griddap)
export(add_tabledap)
export(bbox_set)
export(plotdap)
import(rerddap)
importFrom(dplyr,arrange)
Expand All @@ -17,6 +18,7 @@ importFrom(ggplot2,geom_sf)
importFrom(ggplot2,ggplot)
importFrom(ggplot2,ggtitle)
importFrom(ggplot2,guides)
importFrom(ggplot2,labs)
importFrom(ggplot2,scale_colour_gradientn)
importFrom(ggplot2,scale_fill_gradientn)
importFrom(ggplot2,theme_bw)
Expand Down
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# plotdap 0.0.2
First CRAN release

12 changes: 12 additions & 0 deletions R/QMwind.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#' QMwind Data
#'
#' pre-Download of QMwind in `add_griddap()` example so that example
#' can run within CRAN Time limits
#'
#' obtained using the `rerddap` command
#' wind <- griddap('erdQMwindmday',
#' time = c('2016-11-16', '2017-01-16'), latitude = c(30, 50),
#' longitude = c(210, 240),
#' fields = 'x_wind')
#' )
"QMwind"
36 changes: 36 additions & 0 deletions R/add_ggplot.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#' Add ggplot2 elements to a plotdap object
#'
#' \code{add_ggplot} allows for plotdap ggplot maps to be modified by
#' further ggplot2 settings
#' @param plot a plotdap object.
#' @param ... arguments passed along to \code{geom_sf()}
#' (if \code{method='ggplot2'}, otherwise ignored).
#' @return A plotdap object
#' @export
#' @rdname add_ggplot
#' @examples
#'
#' library(ggplot2)
#'
#' add_ggplot(
#' plotdap(
#' crs = "+proj=laea +y_0=0 +lon_0=155 +lat_0=-90 +ellps=WGS84 +no_defs",
#' mapColor = "black"
#' ),
#' theme_bw()
#' )
#'
#'
add_ggplot <- function(plot, ...) {
if (!is_plotdap(plot)) {
stop(
"The first argument to `add_ggplot()` must be a `plotdap()` object",
call. = FALSE
)
}
dots <- list(...)
for (i in seq_along(dots)) {
plot$ggplot <- plot$ggplot + dots[[i]]
}
plot
}
206 changes: 206 additions & 0 deletions R/add_griddap.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
#' Add rerddap::griddap() data to a plotdap map
#'
#' \code{add_griddap} adds the data from an 'rerddap::griddap() call to
#' a 'plotdap' map
#' @param plot a \link{plotdap} object.
#' @param grid a \link{griddap} object.
#' @param var a formula defining a variable, or function of variables to visualize.
#' @param fill either a character string of length 1 matching a name in \\link[rerddap]{colors}
#' or a vector of color codes. This defines the colorscale used to encode values
#' of \code{var}.
#' @param maxpixels integer > 0. Maximum number of cells to use for the plot.
#' If maxpixels < ncell(x), sampleRegular is used before plotting.
#' If gridded=TRUE maxpixels may be ignored to get a larger sample
#' @param time how to resolve multiple time frames. Choose one of the following:
#' \itemize{
#' \item A function to apply to each observation at a particular location
#' (\link{mean} is the default).
#' \item A character string (of length 1) matching a time value.
#' }
#' @param animate whether to animate over the \code{time} variable (if it exists).
#' Currently only implemented for \code{method='ggplot2'} and requires the
#' gganimate package.
#' @param cumulative - if animation should be cumulative -default FALSE
#' @param ... arguments passed along to \code{geom_sf()}
#' (if \code{method='ggplot2'}, otherwise ignored).
#' @return A plotdap object
#' @export
#' @rdname add_griddap
#' @examples
#'
#' # base plotting tends to be faster,
#' # but is less extensible plotdap("base")
#'
#' # actual datasets in data folder to meet execution timings
#'
#'\donttest{
#' murSST <- griddap(
#' 'jplMURSST41', latitude = c(35, 40), longitude = c(-125, -1205),
#' time = c('last', 'last'), fields = 'analysed_sst'
#' )
#'
#' QMwind <- griddap(
#' 'erdQMwindmday', time = c('2016-11-16', '2017-01-16'),
#' latitude = c(30, 50), longitude = c(210, 240),
#' fields = 'x_wind'
#' )
#'
#' p <- plotdap(crs = "+proj=robin")
#' add_griddap(p, murSST, ~analysed_sst)
#'
#' p <- plotdap(mapTitle = "Average wind over time")
#' add_griddap(p, QMwind, ~x_wind)
#'
#'}
#'
#
#' p <- plotdap("base", crs = "+proj=robin")
#' p <- add_griddap(p, murSST, ~analysed_sst)
#'
#' # layer tables on top of grids
#' require(magrittr)
#' p <- plotdap("base") %>%
#' add_griddap(murSST, ~analysed_sst) %>%
#' add_tabledap(sardines, ~subsample_count)
#'
#' # multiple time periods
#' p <- plotdap("base", mapTitle = "Average wind over time")
#' p <- add_griddap(p, QMwind, ~x_wind)
#'

add_griddap <- function(plot, grid, var, fill = "viridis",
maxpixels = 10000, time = mean, animate = FALSE,
cumulative = FALSE, ...) {
if (!is.grid(grid))
stop("The `grid` argument must be a `griddap()` object", call. = FALSE)
if (!lazyeval::is_formula(var))
stop("The `var` argument must be a formula", call. = FALSE)
if (!is.function(time) && !is.character(time))
stop("The `time` argument must be a function or a character string",
call. = FALSE)

# create raster object from filename;
# otherwise create a sensible raster from data
r <- get_raster(grid, var)

# checks for naming and numeric lat/lon
latlon_is_valid(r)
# adjust to ensure everthing is on standard lat/lon scale
r <- latlon_adjust(r)

# if necessary, reduce a RasterBrick to a RasterLayer
# http://gis.stackexchange.com/questions/82390/summarize-values-from-a-raster-brick-by-latitude-bands-in-r
if (body(time) == 'UseMethod("mean")') {
time <- function(x) mean(x, na.rm = TRUE)
}
if (raster::nlayers(r) > 1) {
if (is.function(time)) {
r <- raster::calc(r, time)
} else {
nm <- make.names(time)
if (!nm %in% names(r)) {
warning(
"The `time` argument doesn't match any of time values.\n",
sprintf(
"Valid options include: '%s'",
paste(unique(grid$data$time), collapse = "', '")
),
call. = FALSE
)
}
r <- r[[nm]]
}

if (raster::nlayers(r) > 1 && !animate) {
stop(
"The `time` argument hasn't reduced the raster down to a single layer.\n",
"Either set `animate=TRUE` or provide a suitable value to `time`.",
call. = FALSE
)
}
}

# simplify raster, if necessary
n <- raster::ncell(r)
if (n > maxpixels) {
message("grid object contains more than ", maxpixels, " pixels")
message("increase `maxpixels` for a finer resolution")
rnew <- raster::raster(
nrow = floor(raster::nrow(r) * sqrt(maxpixels / n)),
ncol = floor(raster::ncol(r) * sqrt(maxpixels / n)),
crs = raster::crs(r),
ext = raster::extent(r)
)
if (inherits(r, "RasterBrick")) {
for (i in seq_len(raster::nlayers(r))) {
r[[i]] <- raster::resample(r[[i]], rnew, method = 'bilinear')
}
} else {
r <- raster::resample(r, rnew, method = 'bilinear')
}
}

# assumes we apply sf::st_crs() to plot on initiation
if (inherits(plot$crs, "crs")) {
crs_string <- plot$crs$proj4string
if (!is.na(plot$crs$epsg)) {
epsg_string <- paste0("+init=epsg:", plot$crs$epsg)
crs_string <- paste(epsg_string, crs_string)
}
#r <- raster::projectRaster(r, crs = plot$crs$proj4string)
r <- raster::projectRaster(r, crs = crs_string)
}

# color scale
cols <- if (length(fill) == 1) rerddap::colors[[fill]] else fill

if (is_ggplotdap(plot)) {
# TODO: not the most efficient approach, but it will have to do for now
# https://twitter.com/hadleywickham/status/841763265344487424
s <- sf::st_as_sf(raster::rasterToPolygons(r))
vars <- setdiff(names(s), "geometry")
sg <- sf::st_as_sf(tidyr::gather_(s, "variable", "value", vars))
if (animate) {
try_gganimate()
plot$animate <- TRUE
plot$nper <- length(sg)
plot$ggplot <- plot$ggplot +
gganimate::transition_manual(variable, cumulative = cumulative) +
ggplot2::labs(title = "{current_frame}")
}

return(
add_ggplot(
plot,
geom_sf(data = sg,
mapping = aes_string(fill = "value", colour = "value"), ...),
scale_fill_gradientn(name = lazyeval::f_text(var), colors = cols),
scale_colour_gradientn(colors = cols),
guides(colour = FALSE)
)
)
}

if (animate) {
warning(
"Animations are currently only implemented for `method='ggplot2'`",
call. = FALSE
)
}

# TODO: more props!
grid <- structure(
r, props = list(
name = lazyeval::f_text(var),
values = raster::values(r),
color = cols
)
)

# Throw a warning if the grid extent overlaps with another grid?
plot$layers <- c(
plot$layers, list(grid)
)

plot
}
Loading

0 comments on commit ba42520

Please sign in to comment.