-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add functions to access GEFS ensemble forecasts. #106
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
c1e4cf0
Add functions to access GEFS ensemble forecasts.
potterzot bc6f2ee
Fixes to gefs pull request
potterzot 7735e56
add hard reference to ncdf functions to pass tests.
potterzot 243e76a
fix example that can give error if 1800UTC forecast is not up yet.
potterzot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
#' Get GEFS ensemble forecast data for a specific lat/lon. | ||
#' | ||
#' Fetches GEFS forecast data for every 6 hours out to 384 hours past selected date. GEFS | ||
#' is an ensemble of 21 models that can be summarized to estimate likelihoods of forecasts. | ||
#' | ||
#' @importFrom tidyr gather | ||
#' @export | ||
#' | ||
#' @param var the variable to get. Must be one of the variables listed in \code{gefs_variables()}. | ||
#' @param lat,lon the longitude. Will be converted to the nearest GEFS available | ||
#' longitude. If lon is a list of vlaues, it must be a sequential list, and | ||
#' data are returned for the number of longitudes in the list starting with | ||
#' the maximum value and incrementing through the indexed values for the | ||
#' length of the list. | ||
#' @param date A date/string formatted as YYYYMMDD. | ||
#' @param forecast_time a string indicating which time of day UTC the forecast is from. Options are "0000", "0600", "1200", "1800". | ||
#' @param ens_idx sequential list of ensembles to fetch. Default is all 21. Note that the ensembles are labelled 0-20, so ens_idx=1:3 will return ensembles 0, 1, and 2. | ||
#' @param time_idx sequential list of time increments to return. List is the index | ||
#' of times, which are in 6 hour increments. (e.g. c(1,2) fetches the 6 and 12 hour forecast.) | ||
#' @param dims (not implemented) indices for additional dimensions to be included between lat, lon, ens, and time. | ||
#' @param raw logical to indicate whether to return raw data matrix or reshaped data frame. | ||
#' @param ... additional parameters passed to \code{ncvar_get}. | ||
#' @return a list containing metadata and accompanying data frame of forecast | ||
#' values. If lat/lon are not specified, the $data is an unprocessed matrix. | ||
#' | ||
#' @references \url{https://www.ncdc.noaa.gov/data-access/model-data/model-datasets/global-ensemble-forecast-system-gefs} | ||
#' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a |
||
#' @author Nicholas Potter \email{potterzot@@gmail.com} | ||
#' @examples \dontrun{ | ||
#' | ||
#' #avialable latitudes and longitudes | ||
#' gefs_latitudes() | ||
#' gefs_longitudes() | ||
#' | ||
#' #get a list of all gefs variables | ||
#' gefs_variables() | ||
#' | ||
#' #All GEFS dimensions | ||
#' gefs_dimensions() | ||
#' | ||
#' #values for a specific dimension | ||
#' gefs_dimension_values("height_above_ground") | ||
#' | ||
#' #example location. | ||
#' lat = 46.28125 | ||
#' lon = -116.2188 | ||
#' | ||
#' #Get forecast for a certain variable. | ||
#' forecast = gefs("Total_precipitation_surface_6_Hour_Accumulation_ens", lat, lon) | ||
#' | ||
#' #Fetch a different date (available up to 10 days prior to today) | ||
#' forecast_yesterday_prec = gefs("Total_precipitation_surface_6_Hour_Accumulation_ens", | ||
#' lat, | ||
#' lon, | ||
#' date=format(as.Date(Sys.time()) - 1, "%Y%m%d")) | ||
#' | ||
#' #specific ensemble and times, for the 1800 forecast. | ||
#' # here ensembles 1-3 (ensembles are numbered starting with 0) | ||
#' # and time for 2 days from today at 1800 | ||
#' date=format(as.Date(Sys.time()) - 1, "%Y%m%d") | ||
#' var = "Temperature_height_above_ground_ens" | ||
#' gefs(var, lat, lon, date = date, forecast_time = "1800", ens_idx=2:4, time_idx=1:8) | ||
#' | ||
#' #One ensemble, all latitudes and longitudes (this is a big file) for the | ||
#' # next 3 days. | ||
#' gefs(var, ens=1, time=1:12) | ||
#' } | ||
#' | ||
gefs <- function(var, lat, lon, ...) { | ||
check4ncdf() | ||
gefs_GET(var, lat, lon, ...) | ||
} | ||
|
||
#' @rdname gefs | ||
gefs_CONNECT <- function(date = format(Sys.time(), "%Y%m%d"), | ||
forecast_time = c("0000", "0600", "1200", "1800")) { | ||
|
||
#forecast time | ||
forecast_time = match.arg(forecast_time) | ||
|
||
#url parts | ||
gefs_url_pre = 'http://motherlode.ucar.edu/thredds/dodsC/grib/NCEP/GEFS/Global_1p0deg_Ensemble/members/GEFS_Global_1p0deg_Ensemble_' | ||
gefs_url_suf = ".grib2" | ||
|
||
#final url | ||
gefs_url = paste0(gefs_url_pre, date, "_", forecast_time, gefs_url_suf) | ||
|
||
#open the connection | ||
#nc_open(gefs_url) #ncdf4 version | ||
ncdf::open.ncdf(gefs_url) | ||
} | ||
|
||
#' @rdname gefs | ||
gefs_GET <- function(var, lat, lon, | ||
date = format(Sys.time(), "%Y%m%d"), | ||
forecast_time = c("0000", "0600", "1200", "1800"), | ||
ens_idx = 1:21, | ||
time_idx = 1:65, | ||
dims = NULL, | ||
raw = FALSE, | ||
...) { | ||
|
||
#Sanity Checks | ||
if (missing(var)) stop("Need to specify the variable to get. A list of variables is available from gefs_variables().") | ||
|
||
#get a connection | ||
con = gefs_CONNECT(date, forecast_time) | ||
|
||
#Get a subset of data to speed up access | ||
v = con$var[[var]] # lon, lat, height_above_ground, ens (ensemble), time1 | ||
varsize = v$varsize | ||
ndims = v$ndims | ||
n_time = varsize[ndims] #time is always the last dimension | ||
dims = c() | ||
for (i in 1:length(v$dim)) { dims[i] = v$dim[[i]]$name } | ||
|
||
#get lat/lon indices | ||
lon_start_idx = if(!missing(lon)) which(v$dim[[1]]$vals==round(max(lon) %% 360, 0)) else 1 | ||
lat_start_idx = if(!missing(lat)) which(v$dim[[2]]$vals==round(max(lat), 0)) else 1 | ||
|
||
#indices of dimensions to read from data | ||
start = rep(1,ndims) #number of dims | ||
start[1] = lon_start_idx #first is always longitude | ||
start[2] = lat_start_idx #first is always latitude | ||
start[ndims - 1] = ens_idx[1] #first ensemble | ||
start[ndims] = time_idx[1] #first time | ||
|
||
count_n = rep(1,ndims) | ||
|
||
#if not specified, get all locations. | ||
count_n[1] = if(missing(lon)) -1 else length(lon) | ||
count_n[2] = if(missing(lat)) -1 else length(lat) | ||
|
||
#ensemble is always the 2nd to last dimension. Take either the variable | ||
#max or the last value indicated by the parameter ens | ||
count_n[ndims-1] = min(varsize[ndims-1], length(ens_idx)) | ||
|
||
#time is always the last dimension | ||
#take the minimum of the variable max or the value indicated by the parameter time | ||
count_n[ndims] = min(varsize[ndims], length(time_idx)) | ||
|
||
# actual data | ||
# Do not modify the data, so don't convert (- 273.15) * 1.8 + 32. #convert from K to F | ||
#d = ncvar_get(con, v, start = start, count = count_n, ...) #ncdf4 version | ||
d = ncdf::get.var.ncdf(con, v, start = start, count = count_n, ...) | ||
|
||
#create the data frame | ||
#For now, if lat/lon are not specified, just return a matrix. | ||
if (raw==FALSE) { | ||
d = gather(as.data.frame(d)) | ||
|
||
for (i in 1:length(count_n)) { | ||
dim_vals = v$dim[[i]]$vals | ||
if(count_n[i]==1) { | ||
d[dims[i]] = dim_vals[start[i]] | ||
} | ||
else { | ||
d[dims[i]] = dim_vals[0:(count_n[i]-1) + start[i]] | ||
} | ||
} | ||
names(d) = c("key", var, dims) | ||
d = d[,c(dims, var)] | ||
} | ||
|
||
fname = strsplit(con$filename, "_")[[1]] | ||
date = fname[7] | ||
forecast_time = strsplit(fname, ".grib2")[[8]] | ||
list(forecast_date = date, | ||
forecast_time = forecast_time, | ||
dimensions = dims, | ||
data = d) | ||
} | ||
|
||
######################## | ||
# helper functions | ||
|
||
#' @export | ||
#' | ||
#' @param con an ncdf4 connection. | ||
#' @rdname gefs | ||
gefs_latitudes <- function(con = NULL, ...) { | ||
gefs_dimension_values("lat", con) | ||
} | ||
|
||
#' @export | ||
#' @rdname gefs | ||
gefs_longitudes <- function(con = NULL, ...) { | ||
gefs_dimension_values("lon", con) | ||
} | ||
|
||
#' @export | ||
#' @rdname gefs | ||
gefs_variables <- function(con = NULL, ...) { | ||
if (is.null(con)) con = gefs_CONNECT(...) | ||
names(con$var) | ||
} | ||
|
||
#' @export | ||
#' @rdname gefs | ||
gefs_dimensions <- function(con = NULL, ...) { | ||
if (is.null(con)) con = gefs_CONNECT(...) | ||
names(con$dim) | ||
} | ||
|
||
#' @export | ||
#' | ||
#' @param dim (character) the dimension. | ||
#' @rdname gefs | ||
gefs_dimension_values <- function(dim, con = NULL, ...) { | ||
if (is.null(dim) || missing(dim)) stop("dim cannot be NULL or missing.") | ||
if (is.null(con)) con = gefs_CONNECT(...) | ||
con$dim[[dim]]$vals | ||
} | ||
|
||
#Check that ncdf is installed | ||
check4ncdf <- function() { | ||
if (!requireNamespace("ncdf", quietly = TRUE)) { | ||
stop("Please install ncdf", call. = FALSE) | ||
} else { | ||
invisible(TRUE) | ||
} | ||
} | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
% Generated by roxygen2 (4.1.1): do not edit by hand | ||
% Please edit documentation in R/gefs.R | ||
\name{gefs} | ||
\alias{gefs} | ||
\alias{gefs_CONNECT} | ||
\alias{gefs_GET} | ||
\alias{gefs_dimension_values} | ||
\alias{gefs_dimensions} | ||
\alias{gefs_latitudes} | ||
\alias{gefs_longitudes} | ||
\alias{gefs_variables} | ||
\title{Get GEFS ensemble forecast data for a specific lat/lon.} | ||
\usage{ | ||
gefs(var, lat, lon, ...) | ||
|
||
gefs_CONNECT(date = format(Sys.time(), "\%Y\%m\%d"), | ||
forecast_time = c("0000", "0600", "1200", "1800")) | ||
|
||
gefs_GET(var, lat, lon, date = format(Sys.time(), "\%Y\%m\%d"), | ||
forecast_time = c("0000", "0600", "1200", "1800"), ens_idx = 1:21, | ||
time_idx = 1:65, dims = NULL, raw = FALSE, ...) | ||
|
||
gefs_latitudes(con = NULL, ...) | ||
|
||
gefs_longitudes(con = NULL, ...) | ||
|
||
gefs_variables(con = NULL, ...) | ||
|
||
gefs_dimensions(con = NULL, ...) | ||
|
||
gefs_dimension_values(dim, con = NULL, ...) | ||
} | ||
\arguments{ | ||
\item{var}{the variable to get. Must be one of the variables listed in \code{gefs_variables()}.} | ||
|
||
\item{lat,lon}{the longitude. Will be converted to the nearest GEFS available | ||
longitude. If lon is a list of vlaues, it must be a sequential list, and | ||
data are returned for the number of longitudes in the list starting with | ||
the maximum value and incrementing through the indexed values for the | ||
length of the list.} | ||
|
||
\item{...}{additional parameters passed to \code{ncvar_get}.} | ||
|
||
\item{date}{A date/string formatted as YYYYMMDD.} | ||
|
||
\item{forecast_time}{a string indicating which time of day UTC the forecast is from. Options are "0000", "0600", "1200", "1800".} | ||
|
||
\item{ens_idx}{sequential list of ensembles to fetch. Default is all 21. Note that the ensembles are labelled 0-20, so ens_idx=1:3 will return ensembles 0, 1, and 2.} | ||
|
||
\item{time_idx}{sequential list of time increments to return. List is the index | ||
of times, which are in 6 hour increments. (e.g. c(1,2) fetches the 6 and 12 hour forecast.)} | ||
|
||
\item{dims}{(not implemented) indices for additional dimensions to be included between lat, lon, ens, and time.} | ||
|
||
\item{raw}{logical to indicate whether to return raw data matrix or reshaped data frame.} | ||
|
||
\item{con}{an ncdf4 connection.} | ||
|
||
\item{dim}{(character) the dimension.} | ||
} | ||
\value{ | ||
a list containing metadata and accompanying data frame of forecast | ||
values. If lat/lon are not specified, the $data is an unprocessed matrix. | ||
} | ||
\description{ | ||
Fetches GEFS forecast data for every 6 hours out to 384 hours past selected date. GEFS | ||
is an ensemble of 21 models that can be summarized to estimate likelihoods of forecasts. | ||
} | ||
\examples{ | ||
\dontrun{ | ||
|
||
#avialable latitudes and longitudes | ||
gefs_latitudes() | ||
gefs_longitudes() | ||
|
||
#get a list of all gefs variables | ||
gefs_variables() | ||
|
||
#All GEFS dimensions | ||
gefs_dimensions() | ||
|
||
#values for a specific dimension | ||
gefs_dimension_values("height_above_ground") | ||
|
||
#example location. | ||
lat = 46.28125 | ||
lon = -116.2188 | ||
|
||
#Get forecast for a certain variable. | ||
forecast = gefs("Total_precipitation_surface_6_Hour_Accumulation_ens", lat, lon) | ||
|
||
#Fetch a different date (available up to 10 days prior to today) | ||
forecast_yesterday_prec = gefs("Total_precipitation_surface_6_Hour_Accumulation_ens", | ||
lat, | ||
lon, | ||
date=format(as.Date(Sys.time()) - 1, "\%Y\%m\%d")) | ||
|
||
#specific ensemble and times, for the 1800 forecast. | ||
# here ensembles 1-3 (ensembles are numbered starting with 0) | ||
# and time for 2 days from today at 1800 | ||
date=format(as.Date(Sys.time()) - 1, "\%Y\%m\%d") | ||
var = "Temperature_height_above_ground_ens" | ||
gefs(var, lat, lon, date = date, forecast_time = "1800", ens_idx=2:4, time_idx=1:8) | ||
|
||
#One ensemble, all latitudes and longitudes (this is a big file) for the | ||
# next 3 days. | ||
gefs(var, ens=1, time=1:12) | ||
} | ||
} | ||
\author{ | ||
Nicholas Potter \email{[email protected]} | ||
} | ||
\references{ | ||
\url{https://www.ncdc.noaa.gov/data-access/model-data/model-datasets/global-ensemble-forecast-system-gefs} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you used
@export
tags in thegefs.R
file, but maybe you need to re-document/roxygenize as those exported functions should be in the NAMESPACE file