Skip to main content

API Example Using R

# This data frame contains the coordinate pairs for the vertices of the polygon
# for an AOI. You can get an equivalent data frame from an sf polygon object
# using sf::st_coordinates(). Note that the first and last coordinates are
# identical in order to close the polygon.
aoi_vertex_coords <- data.frame(X = c(-106.416024,
                                    -106.157845,
                                    -106.146859,
                                    -106.328133,
                                    -106.388558,
                                    -106.454476,
                                    -106.416024),
                              Y = c(44.086577,
                                    44.228451,
                                    44.471990,
                                    44.417086,
                                    44.318914,
                                    44.193015,
                                    44.086577))

# This creates the coordinate strings formatted for use in a geoJSON using the
# data frame of coordinates. The expected format for a coordinate pair is
# "[-106.454476,44.193015]". This could be achieved with an apply() but using
# mutate() from the package dplyr is more readable.
aoi_vertex_coords <- dplyr::mutate(.data = aoi_vertex_coords,
                                 # This creates a new variable with the strings
                                 # for each row.
                                 coordinate_string = paste0("[", X, ",", Y, "]"))


# These are the pieces of the geoJSON string that go before and after the
# coordinates. In the postcoordinate string, you may define "mask" as true to
# ignore cropland, development, and open water or false to include those.
# Note that in order for this to work, the strings defining the geoJSON *must*
# use quotation marks internally, so the strings themselves need to be wrapped
# in aprostrophes, e.g. '{"type":"Feature"}' and not "{'type':'Feature'}".
geojson_precoordinate_string <-'{"type":"Feature",
                               "geometry": {"geodesic":false,
                                            "type":"Polygon",
                                            "coordinates":[['
geojson_postcoordinate_string <- ']]},
                                 "properties": {"mask":true,
                                                "year":null}
                                }'

# The components can be assembled into a single geoJSON string.
# The coordinate strings in the data frame need to be collapsed into a single
# string where each pair is separated by a comma.
aoi_geojson <- paste0(geojson_precoordinate_string,
                    paste(aoi_vertex_coords[["coordinate_string"]],
                          collapse = ","),
                    geojson_postcoordinate_string)

# Using httr::RETRY() will let R make multiple attempts to retrieve the data
# before giving up. Importantly, the verb argument must be "POST", the URL must
# point to the correct API endpoint for the data you want, config should at
# least specify that the desired return format is JSON, and the body argument
# needs to be the geoJSON string constructed for the AOI.
rap_json <- httr::RETRY(verb = "POST",
                      url = "https://us-central1-rap-data-365417.cloudfunctions.net/production16dayV3",
                      config = httr::content_type_json(),
                      body = aoi_geojson)

# httr::content() will convert the returned JSON into more conventional R
# objects: nested lists containing the various kinds of values in the JSON.
rap_obj <- httr::content(x = rap_json,
                       as = "parsed")

# The returned and parsed object will be a list. One of the values in the list
# will be another list named "properties" which contains yet another list of
# each of the rows in the returned dataset, including the variable names as the
# first vector in the list. In this case, the data are for 16-day production.
returned_data_raw_list <- rap_obj$properties$production16day


# There's no "correct" way way to convert the list into a data frame. One of the
# more straightforward is by turning each list of values in the list into a
# data frame then combining those with dplyr:bind_rows().
returned_data_raw_list <- lapply(X = returned_data_raw_list[2:length(returned_data_raw_list)],
                               variable_names = returned_data_raw_list[[1]],
                               FUN = function(X, variable_names){
                                 # The current values need the variable names,
                                 # so this makes sure that variable_names is a
                                 # vector before assigning them to the current
                                 # row's values which are called X within the
                                 # lapply().
                                 names(X) <- unlist(variable_names)
                                 # Make a wide-format data frame out of the
                                 # current values by binding them column-wise
                                 # which results in a single data frame with
                                 # correctly-named variables thanks to the
                                 # naming of X above.
                                 dplyr::bind_cols(X)
                               })
returned_data_raw_dataframe <- dplyr::bind_rows(returned_data_raw_list)

# The data returned will include some values that need to be adjusted before
# using, which can be accomplished with dplyr::mutate().
returned_data <- dplyr::mutate(.data = returned_data_raw_dataframe,
                             # Convert the date from a character string to a 
                             # value of class "date".
                             date = as.Date(date),
                             # Make sure the year is numeric because it may be
                             # a character string.
                             year = as.numeric(year),
                             # Make sure the day of the year is also numeric
                             # and adjust so that the highest value is 365 and
                             # not 366.
                             doy = as.numeric(doy) - 1,
                             # The rest of the variables should be numeric and
                             # rounded to 3 decimal places. Additionally, any
                             # values of -99 should be considered NA values.
                             # Using dplyr::across() here applies the
                             # replacement, conversion, and rounding to all
                             # variables after the first three, i.e., after
                             # date, year, and doy.
                             dplyr::across(.cols = -c(1:3),
                                           .fns = ~ replace(x = .x,
                                                            list = .x == -99,
                                                            values = NA) |>
                                             as.numeric(x = _) |>
                                             round(x = _,
                                                   digits = 3)))