# MURSST
#' Test if an NCDF contains MURSST data.
#'
#' @export
#' @param x ncdf4 object or SPNCRefClass
#' @return logical
is_MURSST <- function(x){
ok <- FALSE
if (inherits(x, "SPNCRefClass")){
atts <- try(ncglobal_atts(x$NC))
} else if(inherits(x, "ncdf4")){
atts <- try(ncglobal_atts(x))
} else {
warning("input must be either SPNCRefClass or ncdf4 class object")
return(ok)
}
natts <- names(atts) <- tolower(names(atts))
if ('title' %in% natts)
ok <- mgrepl('MUR',atts[['title']], fixed = TRUE)
ok
}
#' A subclass of SPNCRefClass for Multi-scale Ultra-high Resolution Sea Surface Temperature \url{http://mur.jpl.nasa.gov/}
#'
#' @include SPNC.R
#' @export
MURSSTRefClass <- setRefClass("MURSSTRefClass",
contains = "SPNCRefClass",
methods = list(
init = function(...){
# this extent is from downloading an example via FTP and then reading
# with x = raster::raster(ncdf_file)
# then dput(as.vector(raster::extent(x)))
# this builds an empty raster identical to that read from file as far
# as extent and res goes.
e = c(-179.99500549324, 180.005000000076, -89.9949978636508, 89.9949978636508)
d = c(17999, 36000)
.self$rastertemplate = raster::raster(
ext = raster::extent(e),
ncol = d[2], nrow = d[1],
crs = "+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0")
}
)
)
#' Get the step size (resolution) as a guess - the space between the first
#' lon and lat values. Subclasses with unambiguous steps sizes, like L3SMI and
#' MURSST, should override this method.
#'
#' @name MURSSTClass_step
#' @return two element numeric vector of step size in x and y or NULL
#' These may be signed for descending values of lon and or lat
NULL
MURSSTRefClass$methods(
step = function(){
c(0.01, 0.01)
})
#' Craft subset indices into a ncdf array
#'
#' @name MURSSTRefClass_subset_bbox
#'
#' @seealso \url{https://stat.ethz.ch/pipermail/r-help/2011-March/272641.html}
#' @param bb numeric, four element bounding box [left, right, bottom, top]
#' @return a list of
#' \itemize{
#' \item{start indices in x and y}
#' \item{counts in x and y}
#' \item{bb vector of [left, right, bottom, top] pixel centers}
#' \item{ext extent vector [left, right, bottom, top] pixel outer edges}
#' }
NULL
MURSSTRefClass$methods(
subset_bbox = function(bb = NULL){
if (is.null(bb)) bb <- .self$get_extent()
nav_from_bb(.self$rastertemplate, bb, flip = 'y')
}) # subset_bbox
#' Compute extent given a bounding box
#'
#' @name MURSSTRefClass_get_extent
#' @param bb the bounding box needed, if missing the current one is used
#' @return a raster::Extent object
NULL
MURSSTRefClass$methods(
get_extent = function(bb){
s <- .self$step()
if (missing(bb)){
bb <- as.vector(raster::extent(.self$rastertemplate))
}
if (identical(bb, .self$BB)) return(raster::extent(.self$BB))
raster::extent(raster::crop(.self$rastertemplate, bb))
})
#' Compute indicies (start and count) for individual [lon,lat] points
#'
#' @name MURSSTRefClass_subset_points
#' @param x either a vector of longitude or a 2d matrix [lon,lat] or a data.frame
#' with lon,lat columns
#' @param y numeric vector, if x is a vector, otherwise NULL
#' @return a list of start/count elements, one per xy input. For inputs that
#' fall beyond the bounds of the data then NA is returned.
NULL
MURSSTRefClass$methods(
subset_points = function(x, y = NULL, layer = 1){
# first verify x and y
if (is.null(y)){
# this covers list(x=..., y=...) or data.frame(x=...,y=...)
if (is.list(x)){
if (!all(names(x) %in% c("x", "y"))){
stop("if y is NULL, then x must have elements names x and y")
} else {
y <- x$y
x <- x$x
}
} else if (is.matrix(x)){
if (!all(colnames(x) %in% c("x", "y"))){
stop("if x is a matrix, then x must have columns named x and y")
} else {
y <- x[,'y']
x <- x[,'x']
}
} else {
stop("if y is NULL, then x must be list, data.frame or matrix with x and y elements")
}
} # is y provided or NULL
stopifnot(length(x) == length(y))
# first we get extent, leading cell edges and indices
e <- .self$get_extent()
llon <- .self$lon("leading")
llat <- .self$lat("leading")
ix <- spnc::find_interval(x, llon)
iy <- spnc::find_interval(y, llat)
# now convert to list of (start = [ix,iy], count = [nx,ny])
# because we have points count is always 1
iz <- mapply(function(x, y, layer = 1){
list(start = c(x, y, layer), count = c(1,1,1))
},
ix,iy, layer = layer, SIMPLIFY = FALSE)
# now we have to flag any beyond extent as NA
# we could use the indices for the leading edge (where ix or iy < 1)
# but we would still need to dub around with the trailing edge
# so it is just as easy to use the input x and y instead of ix and iy
ixna <- (x < e[1]) | (x > e[2])
iyna <- (y < e[3]) | (y > e[4])
iz[ixna | iyna] <- NA
return(iz)
}) #subset_points
#' Get a raster
#'
#' @name MURSSTRefClass_get_raster
#' @param what character one or more variable names
#' @param bb a 4 element bounding box vector [left, right, bottom, top], defaults
#' to [-180, 180, -90, 90]
#' @param layer numeric vector either a 1-based indices or POSIXct timestamps.
#' If POSIXct then the layer at or just before the specified times are returned.
#' See \code{\link{findInterval}} for details.
#' @param crs character, the coordiante reference system to apply
#' @param flip logical if TRUE then flip the raster in the y direction
#' @param time_fmt if multiple time layers are returned, this controls the layer names
#' @return a \code{raster::brick} or \code{raster::layer} object or NULL
NULL
MURSSTRefClass$methods(
get_raster = function(what = 'analysed_sst', layer = 1, bb = .self$BB,
crs = "+proj=longlat +datum=WGS84",
flip = TRUE, time_fmt = "D%Y%j"){
R <- MURSST_get_raster(.self, what = what, layer = layer, bb = bb,
crs = crs, flip = flip, time_fmt = time_fmt)
return(R)
}) # MURSST_get_raster
#' Get points
#'
#' @name MURSSTRefClass_get_points
#' @param x numeric, vector of longitude points or, if y is NULL, a matrix [x,y]
#' or a data.frame or list with x,y elements
#' @param y numeric vector of lattitude points or NULL
#' @param what character one or more variable names or variable indices
#' @param layer numeric layer index
#' @return numeric vector of values
NULL
MURSSTRefClass$methods(
get_points = function(x, y = NULL, what = .self$VARS[1], layer = 1){
if (!all(what %in% .self$VARS))
stop("one or more requested variable(s) not in data:", paste(what, collapse = "\n"))
MURSST_get_points(.self, x=x, y=y, what=what, layer = layer)
}) # MURSST_get_points
##### Methods above
##### functions below
#' Get a raster
#'
#' @export
#' @param X MURSSTRefClass object
#' @param what character one or more variable names
#' @param bb a 4 element bounding box vector [left, right, bottom, top], defaults
#' to [-180, 180, -90, 90]
#' @param layer numeric vector either a 1-based indices or POSIXct timestamps.
#' If POSIXct then the layer at or just before the specified times are returned.
#' See \code{\link{findInterval}} for details.
#' @param crs character, the coordiante reference system to apply
#' @param flip logical if TRUE then flip the raster in the y direction
#' @param time_fmt if multiple time layers are returned, this controls the layer names
#' @return a \code{raster::brick} or \code{raster::layer} object or NULL
MURSST_get_raster <- function(X, what = 'analysed_sst', layer = 1, bb = X$BB,
crs = get_projection("longlat"), #"+proj=longlat +datum=WGS84",
flip = TRUE, time_fmt = "D%Y%j"){
stopifnot(inherits(X, "MURSSTRefClass"))
subnav <- X$subset_bbox(bb)
R <- raster::crop(X$rastertemplate, bb)
ext <- X$get_extent(bb = subnav[['bb']])
if (inherits(layer, "POSIXct") && (length(X$TIME) > 1) ){
layer <- findInterval(layer, X$TIME, all.inside = TRUE)
}
getOneVar <- function(vname, X, subnav, layer = 1,
crs = "+proj=longlat +datum=WGS84"){
ncdf4::ncvar_get(X, vname,
start = c(subnav[['start']], layer[1]),
count = c(subnav[['count']], 1) )
}
getOneLayer <- function(layer, X, subnav, what = names(X[['var']])[[1]],
crs = "+proj=longlat +datum=WGS84"){
ncdf4::ncvar_get(X, varid = what,
start = c(subnav[['start']], layer[1]),
count = c(subnav[['count']], 1) )
}
#R <- raster::raster(nrow = subnav[['count']][2],
# ncol = subnav[['count']][1], ext = ext, crs = crs)
if (length(what) > 1){
xx <- lapply(what, getOneVar, X$NC, subnav, crs = crs, layer = layer[1] )
for (w in names(X)) R <- raster::addLayer(R,
raster::raster(t(xx[[w]]), template = R))
names(R) <- what
} else {
xx <- lapply(layer, getOneLayer, X$NC, subnav, crs = crs, what = what[1])
for (r in xx) R <- raster::addLayer(R,
raster::raster(t(r), template = R))
if (length(X$TIME) > 1){
names(R) <- format(X$TIME[layer], time_fmt)
} else {
names(R) <- paste("layer", layer, sep = "_")
}
}
if (flip) R <- raster::flip(R,"y")
return(R)
}
#' Get points for MURSSTRefClass
#'
#' @export
#' @param NC MURSSTRefClass object
#' @param x numeric, vector of longitude points or, if y is NULL, a matrix [x,y]
#' or a data.frame or list with x,y elements
#' @param y numeric vector of lattitude points or NULL
#' @param what character one or more variable names or variable indices
#' @param layer the layer index
#' @return numeric vector of values
MURSST_get_points <- function(NC, x, y = NULL, what = NC$VARS[1], layer = 1){
stopifnot(inherits(NC, "MURSSTRefClass"))
p <- NC$subset_points(x,y=y, layer = layer)
sapply(p,
function(x, nc = NULL, what = 1){
if (!is.list(x) && is.na(x)){
r <- NA
} else {
r <- ncdf4::ncvar_get(nc, what,
start = x[['start']],
count = x[['count']] )
}
r
},
nc = NC$NC, what = what[1])
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.