library(httr2)
library(tidyverse)
library(repurrrsive)
library(ggplot2)
U.S. National Weather Service
Step by Step Process through the U.S. NWS API for a 7 Day Forcast
The U.S. National Weather Service is part of a federal agency that monitors climate (longterm weather averages), provides weather forecasts, warnings, and general information to the public. Their data is openly available for use through its API for developers.
Exploration Goal: Obtain 7-Day Forecast for New York
The NWS website states the grid forecast can be obtained with coordinates. The longitude and latitude points for New York are 40.730610, -73.935242 respectively.
·Load Libraries:
·Assigning Values:
<- "https://api.weather.gov/"
api_base_url <- "https://api.weather.gov/points/40.7128,-74.0060" ny_coords
·Requesting from the API:
<- request(ny_coords) %>%
ask_apireq_perform()
<-
api_responseresp_body_json(ask_api) %>%
glimpse()
List of 5
$ @context :List of 2
..$ : chr "https://geojson.org/geojson-ld/geojson-context.jsonld"
..$ :List of 17
.. ..$ @version : chr "1.1"
.. ..$ wx : chr "https://api.weather.gov/ontology#"
.. ..$ s : chr "https://schema.org/"
.. ..$ geo : chr "http://www.opengis.net/ont/geosparql#"
.. ..$ unit : chr "http://codes.wmo.int/common/unit/"
.. ..$ @vocab : chr "https://api.weather.gov/ontology#"
.. ..$ geometry :List of 2
.. ..$ city : chr "s:addressLocality"
.. ..$ state : chr "s:addressRegion"
.. ..$ distance :List of 2
.. ..$ bearing :List of 1
.. ..$ value :List of 1
.. ..$ unitCode :List of 2
.. ..$ forecastOffice :List of 1
.. ..$ forecastGridData:List of 1
.. ..$ publicZone :List of 1
.. ..$ county :List of 1
$ id : chr "https://api.weather.gov/points/40.7128,-74.006"
$ type : chr "Feature"
$ geometry :List of 2
..$ type : chr "Point"
..$ coordinates:List of 2
.. ..$ : num -74
.. ..$ : num 40.7
$ properties:List of 17
..$ @id : chr "https://api.weather.gov/points/40.7128,-74.006"
..$ @type : chr "wx:Point"
..$ cwa : chr "OKX"
..$ forecastOffice : chr "https://api.weather.gov/offices/OKX"
..$ gridId : chr "OKX"
..$ gridX : int 33
..$ gridY : int 35
..$ forecast : chr "https://api.weather.gov/gridpoints/OKX/33,35/forecast"
..$ forecastHourly : chr "https://api.weather.gov/gridpoints/OKX/33,35/forecast/hourly"
..$ forecastGridData : chr "https://api.weather.gov/gridpoints/OKX/33,35"
..$ observationStations: chr "https://api.weather.gov/gridpoints/OKX/33,35/stations"
..$ relativeLocation :List of 3
.. ..$ type : chr "Feature"
.. ..$ geometry :List of 2
.. ..$ properties:List of 4
..$ forecastZone : chr "https://api.weather.gov/zones/forecast/NYZ072"
..$ county : chr "https://api.weather.gov/zones/county/NYC061"
..$ fireWeatherZone : chr "https://api.weather.gov/zones/fire/NYZ212"
..$ timeZone : chr "America/New_York"
..$ radarStation : chr "KDIX"
The glimpse()
function shows all the groups of data for our chosen coordinates. Since our goal is to pull the weeks predicted weather, the information needed is stored in the endpoint named $forecast
.
#Assigning endpoint value
<- "https://api.weather.gov/gridpoints/OKX/33,35/forecast"
forecast
#Requesting from the API again
<-
ask_again request(forecast) %>%
req_perform()
<-
response_again resp_body_json(ask_again) %>%
glimpse()
List of 4
$ @context :List of 2
..$ : chr "https://geojson.org/geojson-ld/geojson-context.jsonld"
..$ :List of 5
.. ..$ @version: chr "1.1"
.. ..$ wx : chr "https://api.weather.gov/ontology#"
.. ..$ geo : chr "http://www.opengis.net/ont/geosparql#"
.. ..$ unit : chr "http://codes.wmo.int/common/unit/"
.. ..$ @vocab : chr "https://api.weather.gov/ontology#"
$ type : chr "Feature"
$ geometry :List of 2
..$ type : chr "Polygon"
..$ coordinates:List of 1
.. ..$ :List of 5
$ properties:List of 7
..$ units : chr "us"
..$ forecastGenerator: chr "BaselineForecastGenerator"
..$ generatedAt : chr "2024-11-29T03:33:06+00:00"
..$ updateTime : chr "2024-11-29T03:30:49+00:00"
..$ validTimes : chr "2024-11-28T21:00:00+00:00/P7DT16H"
..$ elevation :List of 2
.. ..$ unitCode: chr "wmoUnit:m"
.. ..$ value : num 2.13
..$ periods :List of 14
.. ..$ :List of 14
.. ..$ :List of 14
.. ..$ :List of 14
.. ..$ :List of 14
.. ..$ :List of 14
.. ..$ :List of 14
.. ..$ :List of 14
.. ..$ :List of 14
.. ..$ :List of 14
.. ..$ :List of 14
.. ..$ :List of 14
.. ..$ :List of 14
.. ..$ :List of 14
.. ..$ :List of 14
Now, we’ll unnest and rectangle the data.
·Converting to Tibble:
<- tibble(response_again) response_df
str(response_df) %>%
print()
I encountered many errors in unnesting because of the structure of the data. When using glimpse()
or print()
the data seeemd as though I would point to $properties
to unnest.
But in using view()
the data looked different. str()
shows whats inside in a very readable sort of way, but view()
is perfect for identifying/naming the items in code. This format was necessary to point to the appropriate items to unnest.
See below:
str(response_df$response_again[[4]]) %>%
glimpse()
(inserting screenshot of output to save you scrolling time)
·Selecting the Lists to Unnest
<- tibble(response_df$response_again[[4]]) forecast
Once the main list is selected, using the $
button allows for items of interest to be selected and by extension the appropriate characters are used.
<- tibble(forecast$`response_df$response_again[[4]]`[[7]])
forecast
<- forecast %>%
forecastunnest_wider(`forecast$\`response_df$response_again[[4]]\`[[7]]`) %>%
print()
## # A tibble: 14 × 14
## number name startTime endTime isDaytime temperature temperatureUnit
## <int> <chr> <chr> <chr> <lgl> <int> <chr>
## 1 1 Tonight 2024-11-… 2024-1… FALSE 37 F
## 2 2 Friday 2024-11-… 2024-1… TRUE 46 F
## 3 3 Friday Night 2024-11-… 2024-1… FALSE 31 F
## 4 4 Saturday 2024-11-… 2024-1… TRUE 40 F
## 5 5 Saturday Night 2024-11-… 2024-1… FALSE 31 F
## 6 6 Sunday 2024-12-… 2024-1… TRUE 38 F
## 7 7 Sunday Night 2024-12-… 2024-1… FALSE 31 F
## 8 8 Monday 2024-12-… 2024-1… TRUE 41 F
## 9 9 Monday Night 2024-12-… 2024-1… FALSE 32 F
## 10 10 Tuesday 2024-12-… 2024-1… TRUE 39 F
## 11 11 Tuesday Night 2024-12-… 2024-1… FALSE 31 F
## 12 12 Wednesday 2024-12-… 2024-1… TRUE 39 F
## 13 13 Wednesday Nig… 2024-12-… 2024-1… FALSE 32 F
## 14 14 Thursday 2024-12-… 2024-1… TRUE 40 F
## # ℹ 7 more variables: temperatureTrend <chr>,
## # probabilityOfPrecipitation <list>, windSpeed <chr>, windDirection <chr>,
## # icon <chr>, shortForecast <chr>, detailedForecast <chr>
·Isolating Columns of Interest
<- forecast %>%
forecastselect(number, name, startTime, temperature, temperatureUnit, shortForecast) %>%
print()
# A tibble: 14 × 6
number name startTime temperature temperatureUnit shortForecast
<int> <chr> <chr> <int> <chr> <chr>
1 1 Tonight 2024-11-28T… 37 F Mostly Clear
2 2 Friday 2024-11-29T… 46 F Sunny then C…
3 3 Friday Night 2024-11-29T… 31 F Mostly Clear
4 4 Saturday 2024-11-30T… 40 F Sunny
5 5 Saturday Night 2024-11-30T… 31 F Mostly Clear
6 6 Sunday 2024-12-01T… 38 F Mostly Sunny
7 7 Sunday Night 2024-12-01T… 31 F Mostly Clear
8 8 Monday 2024-12-02T… 41 F Mostly Sunny
9 9 Monday Night 2024-12-02T… 32 F Partly Cloudy
10 10 Tuesday 2024-12-03T… 39 F Mostly Sunny
11 11 Tuesday Night 2024-12-03T… 31 F Partly Cloudy
12 12 Wednesday 2024-12-04T… 39 F Mostly Sunny
13 13 Wednesday Night 2024-12-04T… 32 F Chance Rain …
14 14 Thursday 2024-12-05T… 40 F Chance Rain …
·Finalizing the Data Frame Clean
$startTime
is in a character datatype although the datapoints are technically a date and time span. While its completely possible to convert to reflect this, I will instead use the char type to select for “dates” and create a column that I can group into days of the week.
I indeed had to come back to this code to fix it! The API will respond very time this code is run with my key, the data will change to reflect the time and the new forecast. It is absolutely necessary to use the weekdays()
to auto populate day of the week depending on the ever changing $startTime
$startTime <- as.Date(forecast$startTime)
forecast
<- forecast %>%
forecast mutate(day= weekdays(startTime)) %>%
arrange(startTime) %>%
group_by(startTime)
·Creating a Forecast Visualization using ggplot
%>%
forecast ggplot(aes(day, temperature, fill= temperature))+
geom_line(linewidth = 50)+
facet_wrap(~day, scales = "free_x",
ncol = 7, axis.labels = "all_y", drop = TRUE)+
theme_minimal()+
theme(
panel.background = element_rect(fill = "#FFFFFF"),
panel.grid = element_line(colour ="#FFFFFF"))+
labs(title = "7 DAY FORECAST", caption = "")
·Addressing Issues
The plot is close to what I intend, but there are several issues here that need to be addressed:
The API request outputs current forecast, the day of the week is not specific enough.
The filled blocks should be color graded to better depict the high and low of the day
The day of the week appears on both the top and bottom
Y axis label doesn’t specify temperature unit
Weather forecast window has an in between where the beginning of the window has only once observation (night) and the end of the window only has one observation (day). using geom_line is not efficient because it requires two points within the grouping.
EX:
#1 and #3 can be fixed by changing the data type of $startTime
after its adjusted, the column can be used as the x in the ggplot base. Then the plot will show the day of the week and the date instead of being redundant. The column name will appear in the x axis now but we can remove that.
#4 can be solved by adding a ylab()
line to the ggplot code. #2 however becomes a little more complicated. geom_line()
does not support “fill”. Its possible to use geom_point()
in shape = 15
(square) and pair it with geom_line()
, then adjust the size and line widths to match in a way that seems like one large box. But even so the gradient wouldn’t work.
A possible fix pivoting wider so that the observations in $name
that predict “night” temperature are in their own column. This way it would be easier to plot a range. But, for now, the visualization gets the point across.
#5 The API returns a single weather data point when the forecast window moves. geom_tile()
was an easy fix, but the visualization is not as intended.
·ggPlot redo
·Useful References
Note: Using Quarto for HTML Documents
“HTML Basics”
https://quarto.org/docs/output-formats/html-basics.htmlNote: Adjusting Chunks
“Options: Chunk Options and package options”
https://yihui.org/knitr/options/#text-outputNote: Adjusting Facet themes
“The Art of Organization: Facets and Themes in ggplot2”
https://medium.com/number-around-us/the-art-of-organization-facets-and-themes-in-ggplot2-5c591bb3c54cNote: Starts with bigger picture and works inward to the minute details
“WebDev4R: The Ultimate Guide To Get Data Through APIs With {httr2} and R”
https://albert-rapp.de/posts/web_dev/07_httr2_ultimate_guide/07_httr2
FULL Text Control With ggplot & {marquee} | Step-By-Step Tutorial
https://www.youtube.com/watch?v=a8a_kLaYRlgHow to Get Data From APIs with R & {httr2} | Ultimate Step-By-Step Tutorial
https://www.youtube.com/watch?v=hmtE4QGIOuk