Skip to content
This repository has been archived by the owner on May 14, 2024. It is now read-only.

Commit

Permalink
Now fetches complete forecast for all states
Browse files Browse the repository at this point in the history
Now returns precipitation and precis as well as temperature

Updated documentation for clarity and description of what data is
returned

Fill in licence details

lint package and follow suggestions from "goodpractices" package

Add two tests for .validate_state()
  • Loading branch information
adamhsparks committed May 12, 2017
1 parent 25bc99d commit a49fd80
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 98 deletions.
9 changes: 5 additions & 4 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ Authors@R: c(person("Adam", "Sparks", role = c("aut", "cre"),
person("Keith", "Pembleton", role = "aut",
email = "[email protected]"))
Description: Fetches Fetch Australian Government Bureau of Meteorology Weather
data, currently Queensland only forecast and returns a tidy data frame of
next seven days weather forecast.
data and returns a tidy data frame of next six days weather forecast.
URL: https://github.com/ToowoombaTrio/BOMRang
BugReports: https://github.com/ToowoombaTrio/BOMRang/issues
License: MIT + file LICENSE
Expand All @@ -20,12 +19,14 @@ Imports:
foreign,
lubridate,
plyr,
stringr,
reshape2,
tibble,
tidyr,
xml2
Encoding: UTF-8
LazyData: true
Suggests: covr
Suggests: covr,
testthat
RoxygenNote: 6.0.1
NeedsCompilation: no
ByteCompile: TRUE
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
YEAR: 2017
COPYRIGHT HOLDER: Your name goes here
COPYRIGHT HOLDER: Toowoomba Trio
2 changes: 1 addition & 1 deletion R/data.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
#'
#' @source \url{ftp://ftp.bom.gov.au/anon/home/adfd/spatial/IDM00013.dbf}
#'
"AAC_codes"
"AAC_codes"
239 changes: 151 additions & 88 deletions R/get_BOM_forecast.R
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,40 @@
#' }
#'
#' @return
#' Data frame of a Australia BOM forecast for max temperature, min temperature
#' and corresponding locations with lat/lon values for the next six days.
#' Data frame of a Australia BOM forecast for the next six days in a data frame
#' with the following fields.
#'
#'\describe{
#' \item{aac}{AMOC Area Code, e.g. WA_MW008, a unique identifier for each location}
#' \item{date}{Date in YYYY-MM-DD format}
#' \item{max_temp}{Maximum forecasted temperature (degrees Celsius)}
#' \item{min_temp}{Minimum forecasted temperature (degrees Celsius)}
#' \item{lower_prcp_limit}{Lower forecasted precipitation limit (millimetres)}
#' \item{upper_prcp_limit}{Upper forecasted precipitation limit (millimetres)}
#' \item{precis}{Précis forecast (a short summary, less than 30 characters)}
#' \item{prob_prcp}{Probability of precipitation (percent)}
#' \item{location}{Named location for forecast}
#' \item{lon}{Longitude of named location (Decimal Degrees)}
#' \item{lat}{Latitude of named location (Decimal Degrees)}
#' \item{elev}{Elevation of named location (Metres)}
#' }
#'
#' @examples
#' \dontrun{
#' BOM_forecast <- get_forecast(state = "QLD")
#' }
#'
#' @author Adam H Sparks, \email{[email protected]} and Keith Pembleton \email{[email protected]}
#'
#' @references
#' Australian Bureau of Meteorology (BOM) Weather Data Services
#' \url{http://www.bom.gov.au/catalogue/data-feeds.shtml}
#'
#' @importFrom dplyr %>%
#'
#'
#' @export
get_forecast <- function(state) {
get_forecast <- function(state = NULL) {
.validate_state(state)

# ftp server
Expand All @@ -42,7 +63,7 @@ get_forecast <- function(state) {
# State/territory forecast files
NT <- "IDD10207.xml"
NSW <- "IDN11060.xml"
QLD <- "IDN11060.xml"
QLD <- "IDQ11295.xml"
SA <- "IDS10044.xml"
TAS <- "IDT16710.xml"
VIC <- "IDV10753.xml"
Expand All @@ -64,8 +85,7 @@ get_forecast <- function(state) {
xmlforecast <-
paste0(ftp_base, SA) # sa
}
else if (state == "TAS")
{
else if (state == "TAS") {
xmlforecast <-
paste0(ftp_base, TAS) # tas
}
Expand Down Expand Up @@ -96,94 +116,137 @@ get_forecast <- function(state) {
else if (state == "AUS") {
xml_list <-
list.files(tempdir(), pattern = ".xml$", full.names = TRUE)
tibble::as_tibble(plyr::ldply(.data = xml_list,
.fun = .parse_forecast,
.progress = "text"))
tibble::as_tibble(plyr::ldply(
.data = xml_list,
.fun = .parse_forecast,
.progress = "text"
))
}
}

.parse_forecast <- function(xmlforecast) {
type <-
precipitation_range <-
`parent-aac` <- LON <- LAT <- ELEVATION <- NULL

# Load BOM location data
utils::data("AAC_codes", package = "BOMRang")
AAC_codes <- AAC_codes

xmlforecast <- xml2::read_xml(xmlforecast)

# remove index=0 (today's "forecast"), it varies and we're not interested anyway
xml2::xml_find_all(xmlforecast, ".//*[@index='0']") %>%
xml2::xml_remove()

# extract locations from forecast
areas <- xml2::xml_find_all(xmlforecast, ".//*[@type='location']")
forecast_locations <-
dplyr::bind_rows(lapply(xml2::xml_attrs(areas), as.list))

# join locations with lat/lon values for mapping and interpolation
forecast_locations <- dplyr::left_join(forecast_locations,
AAC_codes,
by = c("aac" = "AAC",
"description" = "PT_NAME"))

# unlist and add the locations aac code
forecasts <-
lapply(xml2::xml_find_all(xmlforecast, ".//*[@type='location']"),
xml2::as_list)

forecasts <- plyr::llply(forecasts, unlist)
names(forecasts) <- forecast_locations$aac

# get all the <element> and <text> tags (the forecast)
eltext <- xml2::xml_find_all(xmlforecast, "//element | //text")

# extract and clean (if needed) (the labels for the forecast)
labs <- trimws(xml2::xml_attrs(eltext, "type"))

# use a loop to turn list of named character elements into a list of dataframes
# with the location aac code for each line of the data frame
y <- vector("list")
for (i in unique(names(forecasts))) {
x <- data.frame(
keyName = names(forecasts[[i]]),
value = forecasts[[i]],
row.names = NULL
)
z <- names(forecasts[i])
x <- data.frame(rep(as.character(z), nrow(x)), x)
y[[i]] <- x
}
# load BOM location data ---------------------------------------------------
utils::data("AAC_codes", package = "BOMRang")
AAC_codes <- AAC_codes

# load the XML forecast ----------------------------------------------------
xmlforecast <- xml2::read_xml(xmlforecast)

# remove today's "forecast" ------------------------------------------------
xml2::xml_find_all(xmlforecast, ".//*[@index='0']") %>%
xml2::xml_remove()

# extract locations from forecast ------------------------------------------
areas <- xml2::xml_find_all(xmlforecast, ".//*[@type='location']")
forecast_locations <-
dplyr::bind_rows(lapply(xml2::xml_attrs(areas), as.list)) %>%
dplyr::select(-type)

# join locations with lat/lon values ---------------------------------------
forecast_locations <- dplyr::left_join(forecast_locations,
AAC_codes,
by = c("aac" = "AAC",
"description" = "PT_NAME"))

# unlist and add the locations aac code ------------------------------------
forecasts <-
lapply(xml2::xml_find_all(xmlforecast, ".//*[@type='location']"),
xml2::as_list)

forecasts <- plyr::llply(forecasts, unlist)
names(forecasts) <- forecast_locations$aac

# get all the <element> and <text> tags (the forecast) ---------------------
eltext <- xml2::xml_find_all(xmlforecast, "//element | //text")

# extract and clean (if needed) (the labels for the forecast) --------------
labs <- trimws(xml2::xml_attrs(eltext, "type"))

# use a loop to turn list of named character elements into a list ----------
# of dataframes with the location aac code for each line of the data frame
y <- vector("list")
for (i in unique(names(forecasts))) {
x <- data.frame(
keyName = names(forecasts[[i]]),
value = forecasts[[i]],
row.names = NULL
)
z <- names(forecasts[i])
x <- data.frame(rep(as.character(z), nrow(x)), x)
y[[i]] <- x
}

# combine list into a single dataframe -
y <- data.table::rbindlist(y, fill = TRUE)

# add the forecast description to the dataframe ----------------------------
forecast <-
data.frame(y[, -2], labs) # drop keyName colum from "y"
names(forecast) <- c("aac", "value", "labs")

# add dates to forecast ----------------------------------------------------
forecast$date <- c(rep(seq(
lubridate::ymd(Sys.Date() + 1),
lubridate::ymd(Sys.Date() + 6),
by = "1 day"
),
each = 6))

# spread columns -----------------------------------------------------------
forecast <-
forecast %>%
reshape2::dcast(aac + date ~ labs, value.var = "value")

# split precipitation forecast values into lower/upper limits --------------

# format any values that are only zero to make next step easier
forecast$precipitation_range[which(forecast$precipitation_range == "0 mm")] <-
"0 mm to 0 mm"

# separate the precipitation column into two, upper/lower limit ------------
forecast <-
forecast %>%
tidyr::separate(
precipitation_range,
into = c("lower_prec_limit", "upper_prec_limit"),
sep = "to"
)

# remove unnecessary text (mm in prcp cols) --------------------------------
forecast <- lapply(forecast, function(x) {
gsub(" mm", "", x)
})

# rename columns -----------------------------------------------------------
forecast <- forecast[-5] # drop forecast_icon_code column

names(forecast) <-
c(
"aac",
"date",
"max_temp",
"min_temp",
"lower_prcp_limit",
"upper_prcp_limit",
"precis",
"prob_prcp"
)

# merge the forecast with the locations ------------------------------------

# convert forecast_locations$aac to factor for merging
forecast$aac <- as.character(forecast$aac)

# combind list into a single dataframe
y <- data.table::rbindlist(y, fill = TRUE)

# add the forecast description to the dataframe
forecast <- data.frame(y, labs, rep(NA, length(labs)))
names(forecast) <- c("aac", "keyName", "value", "labs", "element")

# add dates to the new object
forecast$date <- c(rep(seq(
lubridate::ymd(Sys.Date() + 1),
lubridate::ymd(Sys.Date() + 7),
by = "1 day"
),
each = 2))

# label for min/max temperature in a new col to use for sorting in next step
forecast$element <-
as.character(stringr::str_match(forecast$labs,
"air_temperature_[[:graph:]]{7}"))

# convert object to tibble and remove rows we don't need, e.g., precip
# keep only max and min temp
forecast <-
tibble::as_tibble(stats::na.omit(forecast[, c(1, 3, 5:6)]))

# convert forecast_locations$aac to factor for merging
forecast$aac <- as.character(forecast$aac)

# merge the forecast with the locations
forecast <-
dplyr::left_join(forecast, forecast_locations, by = "aac")
# return final forecast object ---------------------------------------------
forecast <-
dplyr::left_join(tibble::as_tibble(forecast),
forecast_locations, by = "aac") %>%
dplyr::select(-`parent-aac`) %>%
dplyr::rename(lon = LON, lat = LAT, elev = ELEVATION)
}

#' @noRd
Expand Down
6 changes: 6 additions & 0 deletions R/update_locations.R
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
#' update_locations()
#' }
#' @return Updated internal database of BOM forecast locations
#'
#' @references
#' Australian Bureau of Meteorology (BOM) Weather Data Services
#' \url{http://www.bom.gov.au/catalogue/data-feeds.shtml}
#'
#'
#' @author Adam H Sparks, \email{[email protected]}
#' @export
#'
Expand Down
1 change: 1 addition & 0 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ output: github_document

[![Travis-CI Build Status](https://travis-ci.org/ToowoombaTrio/BOMRang.svg?branch=master)](https://travis-ci.org/ToowoombaTrio/BOMRang)
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/ToowoombaTrio/BOMRang?branch=master&svg=true)](https://ci.appveyor.com/project/ToowoombaTrio/BOMRang)
[![Coverage Status](https://img.shields.io/codecov/c/github/ToowoombaTrio/BOMRang/master.svg)](https://codecov.io/github/ToowoombaTrio/BOMRang?branch=master)
[![Last-changedate](https://img.shields.io/badge/last%20change-`r gsub('-', '--', Sys.Date())`-brightgreen.svg)](https://github.com/toowoombatrio/BOMRang/commits/master)
[![minimal R version](https://img.shields.io/badge/R%3E%3D-`r as.character(getRversion())`-brightgreen.svg)](https://cran.r-project.org/)

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*BOMRang*: Fetch Australian Government Bureau of Meteorology Data
=================================================================

[![Travis-CI Build Status](https://travis-ci.org/ToowoombaTrio/BOMRang.svg?branch=master)](https://travis-ci.org/ToowoombaTrio/BOMRang) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/ToowoombaTrio/BOMRang?branch=master&svg=true)](https://ci.appveyor.com/project/ToowoombaTrio/BOMRang) [![Last-changedate](https://img.shields.io/badge/last%20change-2017--05--11-brightgreen.svg)](https://github.com/toowoombatrio/BOMRang/commits/master) [![minimal R version](https://img.shields.io/badge/R%3E%3D-3.4.0-brightgreen.svg)](https://cran.r-project.org/)
[![Travis-CI Build Status](https://travis-ci.org/ToowoombaTrio/BOMRang.svg?branch=master)](https://travis-ci.org/ToowoombaTrio/BOMRang) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/ToowoombaTrio/BOMRang?branch=master&svg=true)](https://ci.appveyor.com/project/ToowoombaTrio/BOMRang) [![Coverage Status](https://img.shields.io/codecov/c/github/ToowoombaTrio/BOMRang/master.svg)](https://codecov.io/github/ToowoombaTrio/BOMRang?branch=master) [![Last-changedate](https://img.shields.io/badge/last%20change-2017--05--12-brightgreen.svg)](https://github.com/toowoombatrio/BOMRang/commits/master) [![minimal R version](https://img.shields.io/badge/R%3E%3D-3.4.0-brightgreen.svg)](https://cran.r-project.org/)

Fetches Fetch Australian Government Bureau of Meteorology Weather forecasts and returns a tidy data frame in a [*Tibble*](http://tibble.tidyverse.org) of the current and next six days weather.

Expand Down
28 changes: 25 additions & 3 deletions man/get_forecast.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit a49fd80

Please sign in to comment.