With limited R background and fairly new to tmap and jsonlite it was initially difficult to create an interactive chart. However, with much practice and researching for materials, I was able to create a simple interactive map that fetches data from API data sources.
Data Sources:
My proposed design was to show a simple dashboard that contains the Current & average temperature for the day & a map of bi-hourly weather forecast for the different regions of Singapore.
Preparation for Visualization
For this assignment, The libraries needed are tidyverse, shiny, rjson, jsonlite, Rcurl, tmap and lobstr
packages <- c('tidyverse', 'rjson','jsonlite','RCurl', 'sf', 'tmap', 'lobstr' ,"shiny")
for(p in packages){
if (!require(p,character.only = T)){
install.packages(p)
}
library(p,character.only = T)
}
## Loading required package: tidyverse
## ── Attaching packages ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.2 ✓ purrr 0.3.4
## ✓ tibble 3.0.4 ✓ dplyr 1.0.2
## ✓ tidyr 1.1.2 ✓ stringr 1.4.0
## ✓ readr 1.4.0 ✓ forcats 0.5.0
## ── Conflicts ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
## Loading required package: rjson
## Loading required package: jsonlite
##
## Attaching package: 'jsonlite'
## The following objects are masked from 'package:rjson':
##
## fromJSON, toJSON
## The following object is masked from 'package:purrr':
##
## flatten
## Loading required package: RCurl
##
## Attaching package: 'RCurl'
## The following object is masked from 'package:tidyr':
##
## complete
## Loading required package: sf
## Linking to GEOS 3.8.1, GDAL 3.1.1, PROJ 6.3.1
## Loading required package: tmap
## Loading required package: lobstr
## Loading required package: shiny
##
## Attaching package: 'shiny'
## The following object is masked from 'package:jsonlite':
##
## validate
We will first retrieve the singapore 2-hour weather forecast data from data.gov The data is retrieved using the URLencode function for the API.
url = "https://api.data.gov.sg/v1/environment/2-hour-weather-forecast"
url <- URLencode(url)
# view full url
url
## [1] "https://api.data.gov.sg/v1/environment/2-hour-weather-forecast"
Next, we will convert the data from JSON to a dataframe
# Convert JSON to data frame
weather_df <- fromJSON(getURL(url))
location_forecast <- weather_df$items$forecasts
forecast_start <- weather_df$items$valid_period$start
forecast_end <- weather_df$items$valid_period$end
query_time <- weather_df$items$update_timestamp
location_forecast
## [[1]]
## area forecast
## 1 Ang Mo Kio Partly Cloudy (Night)
## 2 Bedok Partly Cloudy (Night)
## 3 Bishan Partly Cloudy (Night)
## 4 Boon Lay Partly Cloudy (Night)
## 5 Bukit Batok Partly Cloudy (Night)
## 6 Bukit Merah Partly Cloudy (Night)
## 7 Bukit Panjang Partly Cloudy (Night)
## 8 Bukit Timah Partly Cloudy (Night)
## 9 Central Water Catchment Partly Cloudy (Night)
## 10 Changi Partly Cloudy (Night)
## 11 Choa Chu Kang Partly Cloudy (Night)
## 12 Clementi Partly Cloudy (Night)
## 13 City Partly Cloudy (Night)
## 14 Geylang Partly Cloudy (Night)
## 15 Hougang Partly Cloudy (Night)
## 16 Jalan Bahar Partly Cloudy (Night)
## 17 Jurong East Partly Cloudy (Night)
## 18 Jurong Island Partly Cloudy (Night)
## 19 Jurong West Partly Cloudy (Night)
## 20 Kallang Partly Cloudy (Night)
## 21 Lim Chu Kang Partly Cloudy (Night)
## 22 Mandai Partly Cloudy (Night)
## 23 Marine Parade Partly Cloudy (Night)
## 24 Novena Partly Cloudy (Night)
## 25 Pasir Ris Partly Cloudy (Night)
## 26 Paya Lebar Partly Cloudy (Night)
## 27 Pioneer Partly Cloudy (Night)
## 28 Pulau Tekong Partly Cloudy (Night)
## 29 Pulau Ubin Partly Cloudy (Night)
## 30 Punggol Partly Cloudy (Night)
## 31 Queenstown Partly Cloudy (Night)
## 32 Seletar Partly Cloudy (Night)
## 33 Sembawang Partly Cloudy (Night)
## 34 Sengkang Partly Cloudy (Night)
## 35 Sentosa Partly Cloudy (Night)
## 36 Serangoon Partly Cloudy (Night)
## 37 Southern Islands Partly Cloudy (Night)
## 38 Sungei Kadut Partly Cloudy (Night)
## 39 Tampines Partly Cloudy (Night)
## 40 Tanglin Partly Cloudy (Night)
## 41 Tengah Partly Cloudy (Night)
## 42 Toa Payoh Partly Cloudy (Night)
## 43 Tuas Partly Cloudy (Night)
## 44 Western Islands Partly Cloudy (Night)
## 45 Western Water Catchment Partly Cloudy (Night)
## 46 Woodlands Partly Cloudy (Night)
## 47 Yishun Partly Cloudy (Night)
Next, we will unnest the dataframe to attain the different areas’ coordinates (Longtitude & Latitude)
region <-as_tibble(weather_df$area_metadata$name)
coordinates <-weather_df$area_metadata$label_location
coordinates <- as_tibble(coordinates) %>% unnest()
## Warning: `cols` is now required when using unnest().
## Please use `cols = c()`
locations <- merge(region, coordinates, by ="row.names", all.x=TRUE)
locations$Row.names <-NULL
head(locations)
Next, we will convert the each coordinates into a geopoint.
locations_data <- locations %>%
st_as_sf(coords = c("longitude","latitude")) %>%
mutate( latitude= st_coordinates(.)[,1],
longitude= st_coordinates(.)[,2])
locations_sf <- st_as_sf(locations_data, coords = c("LONGITUDE", "LATITUDE"), crs = 3414) %>% rename(area = value)
locations_sf
## Simple feature collection with 47 features and 3 fields
## geometry type: POINT
## dimension: XY
## bbox: xmin: 103.635 ymin: 1.205926 xmax: 104.053 ymax: 1.445
## CRS: NA
## First 10 features:
## area latitude longitude geometry
## 1 Ang Mo Kio 103.839 1.375000 POINT (103.839 1.375)
## 2 Changi 103.987 1.357000 POINT (103.987 1.357)
## 3 Choa Chu Kang 103.745 1.377000 POINT (103.745 1.377)
## 4 Clementi 103.760 1.315000 POINT (103.76 1.315)
## 5 City 103.844 1.292000 POINT (103.844 1.292)
## 6 Geylang 103.884 1.318000 POINT (103.884 1.318)
## 7 Hougang 103.886 1.361218 POINT (103.886 1.361218)
## 8 Jalan Bahar 103.670 1.347000 POINT (103.67 1.347)
## 9 Jurong East 103.737 1.326000 POINT (103.737 1.326)
## 10 Jurong Island 103.699 1.266000 POINT (103.699 1.266)
Lastly, we will merge the locations data frame with the forecast data frame by the region name.
forecast <- merge(locations_sf, location_forecast, by ="area", all.x=TRUE)
forecast
## Simple feature collection with 47 features and 4 fields
## geometry type: POINT
## dimension: XY
## bbox: xmin: 103.635 ymin: 1.205926 xmax: 104.053 ymax: 1.445
## CRS: NA
## First 10 features:
## area latitude longitude forecast geometry
## 1 Ang Mo Kio 103.839 1.375000 Partly Cloudy (Night) POINT (103.839 1.375)
## 2 Bedok 103.924 1.321000 Partly Cloudy (Night) POINT (103.924 1.321)
## 3 Bishan 103.839 1.350772 Partly Cloudy (Night) POINT (103.839 1.350772)
## 4 Boon Lay 103.701 1.304000 Partly Cloudy (Night) POINT (103.701 1.304)
## 5 Bukit Batok 103.754 1.353000 Partly Cloudy (Night) POINT (103.754 1.353)
## 6 Bukit Merah 103.819 1.277000 Partly Cloudy (Night) POINT (103.819 1.277)
## 7 Bukit Panjang 103.772 1.362000 Partly Cloudy (Night) POINT (103.772 1.362)
## 8 Bukit Timah 103.791 1.325000 Partly Cloudy (Night) POINT (103.791 1.325)
## 9 Central Water Catchment 103.805 1.380000 Partly Cloudy (Night) POINT (103.805 1.38)
## 10 Changi 103.987 1.357000 Partly Cloudy (Night) POINT (103.987 1.357)
Let’s visualize the map.
tmap_mode("view") +
tm_shape(forecast)+
tm_dots("forecast",size = .1, popup.vars = c("Area" = "area", "Weather Forecast" = "forecast"))
## tmap mode set to interactive viewing
## Warning: Currect projection of shape forecast unknown. Long-lat (WGS84) is assumed.
We will now retrieve Singapore’s temperature data from openweathermap. This API requires authentication thus an API key is required.
weather = "api.openweathermap.org/data/2.5/weather?q=Singapore&units=metric&appid="
key = "2a0bc57119f75895c2418545d1195ed7"
full_url = paste0(weather,key)
full_url <- URLencode(full_url)
temp_df <-fromJSON(getURL(full_url))
curr_temp <- temp_df$main$temp
temp_min <- temp_df$main$temp_min
temp_max <- temp_df$main$temp_max
curr_hum <- temp_df$main$humidity
There are 2 main components of an Rshiny App: The UI & Server. The UI provides an visualization of the charts and widgets while the Server provides the backend logic to the UI.
The Rshiny app should include:
ui <- fluidPage(
tags$h2("IS428 Assignment 5"),
titlePanel(paste("Singapore Weather Forecast (", substr(query_time,0,10), ")")),br(),
fluidRow(
column(4,
h4("Current Temperature", align='center'),
h3(paste(curr_temp, "°C"), align='center'),
br()),
column(4,
h4("Temperature Range", align='center'),
h3(paste(temp_min, "°C to ", temp_max, "°C"), align='center'),
br()),
column(4,
h4("Current Humidity", align='center'),
h3(paste(curr_hum, "%"), align='center'),
br()),
h3("Singapore 2-Hour Weather Forecast",align='center'),
tmapOutput("my_tmap"),
h5(paste("Last updated: ",substr(query_time,12,19)),align='right'))
)
To generate the Map, the function renderTmap() is being used to plot the map. Once the map is formed, it can be shown on the UI with the tmapOutput() function.
server <- function(input,output,session){
output$my_tmap <- renderTmap({
tm <- tm_shape(forecast) + tm_dots("forecast",size = .2, popup.vars = c("Area" = "area", "Weather Forecast" = "forecast"))
})
}
shinyApp(ui, server)
With Rshiny, the visualization has become more interactive and intuitive allowing the user to view more information and interact with the map.
Based on the map, it can be seen that for 05/11/20 at around 8pm: