IS428 Assignment 5

Flavia Ng

10/29/2020

1. Major Design and Challenges Faced

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:

  1. Singapore 2-Hour Weather Forecast API retrieved from Data.gov
  2. Singapore’s Temperature API retrieved from Openweathermap

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. Proposed Design

2. Data Visualization Preperation

Preparation for Visualization

2.1 Downloading of Packages

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

2.2 Retrieving Bi-hourly weather data

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"

2.3 Data Wrangling

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.

2.4 Retrieving the temperature data

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

2.5 Embedding in RShiny

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.

2.6 Designing & Building the UI

The Rshiny app should include:

  1. An equal sizing for the visulization of Temperatures & Humidity.
  2. An interactive Map of the Bi-hourly weather forecast
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'))
    )

2.7 RShiny Server

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"))
  })
}

2.8 Shiny App

shinyApp(ui, server)

3 Final Visualization

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:

  1. The temperature is cooling today(05/11/20) with the range of 27.78 °C to 30 °C.
  2. All regions in singapore are Partly cloudy for the next 2 hours till 10pm
  3. The humidity level is relatively high today at 70%. Actual App