R/monitor_writeCurrentStatusGeoJSON.R

Defines functions monitor_writeCurrentStatusGeoJSON

Documented in monitor_writeCurrentStatusGeoJSON

#' @title Write current monitor data to geojson file
#'
#' @description
#' Writes a geoJSON file containing current monitor data. For details on what is
#' included, see \code{\link{monitor_getCurrentStatus}}.
#'
#' @param ws_monitor \emph{ws_monitor} object.
#' @param filename Filename where geojson file will be saved.
#' @param datetime Time to which data will be 'current' (integer or character
#'   representing YYYYMMDDHH or \code{POSIXct}. If not \code{POSIXct},
#'   interpreted as UTC time). So if \code{datetime} is 3 hours ago, a dataframe
#'   with the most current data from 3 hours ago will be returned.
#' @param properties Optional character vector of properties to include for each
#'   monitor in geoJSON. If NULL all are included. May include any ws_monitor
#'   metadata and additional columns generated in
#'   \code{\link{monitor_getCurrentStatus}}.
#' @param propertyNames Optional character vector supplying custom names for
#'   properties in geoJSON. If NULL or different length than \code{properties}
#'   defaults will be used.
#' @param metadataList List of top-level foreign members to include. May include
#'   nested lists as long as they can be converted into JSON using
#'   \code{jsonlite::toJSON()}. For more information on what can be included see
#'   \href{https://tools.ietf.org/html/rfc7946#section-6.1}{https://tools.ietf.org/html/rfc7946#section-6.1}.
#'
#' @return Invisibly returns geoJSON string.
#'
#' @keywords ws_monitor
#' @export
#' @import MazamaCoreUtils
#'
#' @examples
#' \donttest{
#' # Fail gracefully if any resources are not available
#' try({
#'
#' library(PWFSLSmoke)
#'
#' wa <-
#'   monitor_loadLatest() %>%
#'   monitor_subset(stateCodes = "WA")
#'
#' geojson_file <- tempfile(fileext = ".geojson")
#' wa_current_geojson <- monitor_writeCurrentStatusGeoJSON(wa, geojson_file)
#' wa_current_list <- jsonlite::fromJSON(wa_current_geojson)
#' wa_spdf <- rgdal::readOGR(dsn = geojson_file)
#' map("state", "washington")
#' points(wa_spdf)
#'
#' }, silent = FALSE)
#' }
#'

monitor_writeCurrentStatusGeoJSON <- function(
  ws_monitor,
  filename,
  datetime = lubridate::now(tzone = "UTC"),
  properties = NULL,
  propertyNames = NULL,
  metadataList = list()
) {

  logger.debug(" ----- monitor_writeCurrentStatusGeoJSON() ----- ")

  # ----- Validate parameters --------------------------------------------------

  if (monitor_isEmpty(ws_monitor))
    stop("ws_monitor object contains zero monitors.")


  # Load and format data -------------------------------------------------------

  result <- try({
    currentTbl <- monitor_getCurrentStatus(ws_monitor, datetime)
  }, silent = TRUE)
  if ("try-error" %in% class(result)) {
    err_msg <- geterrmessage()
    stop(paste0("error getting current data: ", err_msg))
  }

  ## NOTE:
  #  For highest values to be plotted last on Leaflet Maps we want monitors with
  #  NA's to come first and then everything in increasing order.

  if (!is.null(currentTbl$yesterday_pm25_24hr)) {
    myOrder <- rev(order(currentTbl$yesterday_pm25_24hr, decreasing = TRUE))
    currentTbl <- currentTbl[myOrder, ]
  }

  # Use monitorID as siteName if siteName is missing
  currentTbl$siteName <- ifelse(
    is.na(currentTbl$siteName),
    currentTbl$monitorID,
    currentTbl$siteName
  )


  # Get coords from meta and create SPDF ---------------------------------------

  # NOTE: To save space we always round to 5 decimal places (~1 meter)
  # NOTE: Get coords before subsetting meta, in case lon and lat aren't included
  coords <- cbind(round(currentTbl$longitude, 5), round(currentTbl$latitude, 5))

  # Subset meta to desired properties
  if (!is.null(properties)) {
    currentTbl <- currentTbl[, properties]
    if (!is.null(propertyNames) & length(propertyNames) == length(properties))
      result <- try(names(currentTbl) <- propertyNames)
    if ("try-error" %in% class(result)) {
      err_msg <- geterrmessage()
      warning(paste0("could not change propertyNames - using defaults: ", err_msg))
    }
  }

  # Create the SpatialPointsDataFrame
  # NOTE:  We use as.data.frame because use of dplyr:: functions adds the "tbl_df" class
  sites <- sp::SpatialPointsDataFrame(
    coords = coords,
    proj4string = sp::CRS("+proj=longlat +ellps=WGS84"),
    data = as.data.frame(currentTbl)
  )


  # Convert to geoJSON ---------------------------------------------------------

  ## NOTE: about rgdal::writeOGR
  #  * creates .geojson files where numeric properties are not quoted.
  #  * complains if filename already exists.
  #  * does not automatically append '.geojson'

  # filename <- paste0(opt$outputDir,'/',opt$dataSource,'_PM2.5_latest10.geojson')
  # logger.info('Using rgdal::writeOGR to create geojson file %s', filename)

  rgdal::writeOGR(sites, dsn = filename, layer = "currentTbl", driver = "GeoJSON")

  # Add top level metadata as Foreign Members:  https://tools.ietf.org/html/rfc7946#section-6.1
  # and rewrite the geojson file
  geojsonList <- as.list(
    jsonlite::fromJSON(
      filename,
      simplifyVector = TRUE,
      simplifyDataFrame = FALSE,
      simplifyMatrix = FALSE,
      flatten = FALSE
    )
  )

  geojsonList <- append(geojsonList, metadataList)
  geojsonText <- jsonlite::toJSON(
    geojsonList,
    auto_unbox = TRUE,
    null = "null",
    na = "null"
  )
  cat(geojsonText, file = filename)

  return(invisible(geojsonText))

}
MazamaScience/PWFSLSmoke documentation built on July 3, 2023, 11:03 a.m.