| This Shiny app designed to display meteorological data by province in Spain. |
| #Key Features and functionalities |
It makes use of several R packages: shiny for the web application framework, httr for sending HTTP requests, jsonlite for parsing JSON data, and stringi for string manipulation. Here’s a breakdown of its components and functionality:
UI Components Title Panel: Displays the title of the app, “Meteorological Data by Province in Spain.” Select Input: A dropdown menu for users to select a province. The choices in this dropdown are dynamically loaded from an external API, which lists Spanish provinces. Calling the API https://www.el-tiempo.net/api
#Data Processing The server processes this data to: Correct character encoding issues, ensuring proper display of Spanish characters. Extract and calculate average temperatures, and generate a list or table of cities within the province with their respective weather details. #option to use library(stringr)
corregirCaracteres <- function(texto) {
texto <- gsub("á", "á", texto)
texto <- gsub("é", "é", texto)
texto <- gsub("Ã", "í", texto)
texto <- gsub("ó", "ó", texto)
texto <- gsub("ú", "ú", texto)
texto <- gsub("ñ", "ñ", texto)
texto <- gsub("Ã", "Á", texto)
texto <- gsub("É", "É", texto)
texto <- gsub("Ã", "Í", texto)
texto <- gsub("Ó", "Ó", texto)
texto <- gsub("Ú", "Ú", texto)
texto <- gsub("Ñ", "Ñ", texto)
texto <- gsub("ü", "ü", texto)
texto <- gsub("Â", "Â", texto)
return(texto)
}
Dynamic Content Loading: The dropdown menu for selecting a province is populated dynamically based on data fetched from an external API. This ensures that the list is up-to-date and comprehensive. Interactive Data Display: Upon selecting a province, users are presented with current weather data and forecasts relevant to that area. The display is updated in real-time, reflecting any changes in the user’s selection.
#HTML Output This section (respuesta) is meant to display the fetched weather data for the selected province, which could include forecasts and possibly other relevant information. Plot Output: Intended for showing graphical representations (grafico) of the weather data, such as temperature ranges in the selected province.
#DataFetching Data Fetching: Once a province is selected, the app constructs and sends a request to the same API to get detailed weather data for that province, including city-specific forecasts.
library(shiny)
library(httr)
library(jsonlite)
##
## Attaching package: 'jsonlite'
## The following object is masked from 'package:shiny':
##
## validate
library(stringi)
library(shinydashboard)
##
## Attaching package: 'shinydashboard'
## The following object is masked from 'package:graphics':
##
## box
#UI design , using shinydasboard features
ui <- dashboardPage(
dashboardHeader(title = "Datos Meteorológicos por Provincia", titleWidth = 250),
dashboardSidebar(
width = 200,
sidebarMenu(
menuItem("Inicio", tabName = "inicio", icon = icon("home")),
selectInput("provincia", "Selecciona una Provincia:", choices = c("No seleccionado" = ""))
)
),
dashboardBody(
tags$head(
tags$style(HTML("
.content-wrapper {
background-color: #f4f6f9;
}
.box {
border-radius: 5px;
box-shadow: 0 0 20px rgba(0,0,0,0.1);
}
.skin-blue .main-header .navbar {
background-color: #3c8dbc;
}
.skin-blue .main-header .logo {
background-color: #3c8dbc;
color: #ffffff;
}
.skin-blue .main-sidebar, .skin-blue .left-side {
background-color: #1e282c;
}
"))
),
tabItems(
tabItem(tabName = "inicio",
fluidRow(
box(title = "Gráfico.Temperaturas por Ciudad y media provicial", status = "primary", solidHeader = TRUE, collapsible = TRUE,
plotOutput("grafico", height = "400px")),
box(title = "Temperaturas por Ciudad y media provicial", status = "warning", solidHeader = TRUE, collapsible = TRUE,
htmlOutput("respuesta"))
)
)
)
),
skin = "blue"
)
#As the API has enconding problems, we force a dictionary so the content is in UTF-8, we try to force it with "enconding=" but no luck.
corregirCaracteres <- function(texto) {
texto <- gsub("á", "á", texto)
texto <- gsub("é", "é", texto)
texto <- gsub("Ã", "í", texto)
texto <- gsub("ó", "ó", texto)
texto <- gsub("ú", "ú", texto)
texto <- gsub("ñ", "ñ", texto)
texto <- gsub("Ã", "Á", texto)
texto <- gsub("É", "É", texto)
texto <- gsub("Ã", "Í", texto)
texto <- gsub("Ó", "Ó", texto)
texto <- gsub("Ú", "Ú", texto)
texto <- gsub("Ñ", "Ñ", texto)
texto <- gsub("ü", "ü", texto)
texto <- gsub("Â", "Â", texto)
return(texto)
}
#Server start
server <- function(input, output, session) {
#Calling the API
observe({
url_provincias <- "https://www.el-tiempo.net/api/json/v2/provincias"
response <- httr::GET(url_provincias)
raw_data <- httr::content(response, "text", encoding = "UTF-8")
lista_provincias <- jsonlite::fromJSON(raw_data, flatten = TRUE)
opciones_provincias <- setNames(lista_provincias$provincias$CODPROV, lista_provincias$provincias$NOMBRE_PROVINCIA)
updateSelectInput(session, "provincia", choices = c("Selecciona una provincia" = "", opciones_provincias))
})
#API response
output$respuesta <- renderUI({
req(input$provincia)
provincia_codificada <- URLencode(input$provincia, reserved = TRUE)
url <- paste0("https://www.el-tiempo.net/api/json/v2/provincias/", provincia_codificada)
print(url)
response <- httr::GET(url)
#enconding does not work for content
raw_content <- httr::content(response, "text", encoding = "UTF-8")
contenido_json <- jsonlite::fromJSON(raw_content, flatten = TRUE)
# Handling the special case when there is no city data.
if (length(contenido_json$ciudades) == 0) {
mensaje <- "No se encontraron datos de ciudades para esta provincia."
return(tags$p(mensaje))
}
#whe correct the enconding with out function 'corregirCaracteres'
titulo_corregido <- corregirCaracteres(contenido_json$title)
hoy_corregido <- corregirCaracteres(contenido_json$today$p)
manana_corregido <- corregirCaracteres(contenido_json$tomorrow$p)
# Create empty arrays to store the data of cities and their temperatures
ciudades_temperaturas <- list()
# Iterate over each city and extract its data
for (i in seq_along(contenido_json$ciudades$name)) {
ciudad <- corregirCaracteres(contenido_json$ciudades$name[i])
#print control
print(contenido_json$ciudades)
#reading API for temperatures
temp_max <- contenido_json$ciudades$temperatures.max[i]
temp_min <- contenido_json$ciudades$temperatures.min[i]
#making a list with the info, to present a table after
ciudad_temperaturas <- list(Ciudad = ciudad, Temperatura_Máxima = temp_max, Temperatura_Mínima = temp_min)
ciudades_temperaturas <- c(ciudades_temperaturas, list(ciudad_temperaturas))
}
# print control
print("ciudades_temperaturas:")
print(ciudades_temperaturas)
# Calculate the average maximum temperature and the average minimum temperature.
temp_max_promedio <- mean(as.numeric(contenido_json$ciudades$temperatures.max), na.rm = TRUE)
temp_min_promedio <- mean(as.numeric(contenido_json$ciudades$temperatures.min), na.rm = TRUE)
# Round the averages to 2 decimal places
temp_max_promedio <- round(temp_max_promedio, 2)
temp_min_promedio <- round(temp_min_promedio, 2)
# Add an additional row to display the averages
promedios <- list(Ciudad = "Promedio", Temperatura_Máxima = temp_max_promedio, Temperatura_Mínima = temp_min_promedio)
#print control
print(promedios)
#list averages
ciudades_temperaturas <- c(ciudades_temperaturas, list(promedios))
# Create a dataframe with the data of cities and their temperatures
data <- do.call(rbind, ciudades_temperaturas)
# Create the HTML table with the data
tabla_html <- datatable(data, options = list(paging = FALSE, searching = FALSE))
# Create the HTML with the title, forecasts, and the table of cities and temperatures
html <- tags$div(
tags$h2(titulo_corregido),
tags$h3("Hoy"),
tags$p(hoy_corregido),
tags$h3("Mañana"),
tags$p(manana_corregido),
tags$h3("Ciudades y Temperaturas"),
tabla_html
)
return(html)
})
#Graphic
output$grafico <- renderPlot({
req(input$provincia)
provincia_codificada <- URLencode(input$provincia, reserved = TRUE)
url <- paste0("https://www.el-tiempo.net/api/json/v2/provincias/", provincia_codificada)
response <- GET(url)
raw_content <- content(response, "text", encoding = "UTF-8")
contenido_json <- fromJSON(raw_content, flatten = TRUE)
#error management for cities with no data
if (length(contenido_json$ciudades) == 0) {
return(tags$p("No se encontraron datos de ciudades para esta provincia."))
}
#calling API
ciudades <- corregirCaracteres(contenido_json$ciudades$name)
#we need to be sure this values are numeric
temp_max <- as.numeric(contenido_json$ciudades$temperatures.max)
temp_min <- as.numeric(contenido_json$ciudades$temperatures.min)
# Calcular el promedio de las temperaturas máximas y mínimas
promedio_max <- mean(temp_max)
promedio_min <- mean(temp_min)
datos <- data.frame(Ciudad = factor(ciudades), TempMin = temp_min, TempMax = temp_max)
#graphic the data table info
library(ggplot2)
ggplot(datos, aes(x = Ciudad)) +
geom_errorbar(aes(ymin = TempMin, ymax = TempMax), width = 0.2, color = "gray") + # Ajuste de color para los barras de error
geom_hline(yintercept = promedio_max, linetype = "dashed", color = "blue", size = 1) +
geom_hline(yintercept = promedio_min, linetype = "dashed", color = "red", size = 1) +
geom_text(aes(x = 1, y = promedio_max, label = paste("Promedio Máx:", round(promedio_max, 2))), vjust = -2, color = "blue", hjust = -0.1) +
geom_text(aes(x = 1, y = promedio_min, label = paste("Promedio Mín:", round(promedio_min, 2))), vjust = 2, color = "red", hjust = -0.1) +
theme_minimal() + # Usa un tema minimalista para un aspecto más limpio
theme(axis.text.x = element_text(angle = 45, hjust = 1), # Ajuste de texto de eje X
plot.title = element_text(face = "bold", size = 14), # Ajuste del título del gráfico
legend.position = "bottom") + # Mueve la leyenda abajo
labs(x = "Ciudad",
y = "Temperatura (°C)",
title = "Rango de Temperaturas por Ciudad",
subtitle = "Incluye líneas para las temperaturas promedio máxima y mínima") # Añade un subtítulo para más contexto
})
}
#deploy
#shinyApp(ui, server)
#Data Visualization The app includes a feature to visually represent the weather data through a plot, offering insights into temperature variations across different cities within a province and highlighting average temperatures.
#DT #DataTable library to present the data in a interactive table
#Visualization The app creates a plot showing the temperature ranges (maximum and minimum temperatures) for cities in the selected province, alongside lines indicating the average maximum and minimum temperatures. Plotting: The app likely uses ggplot2 (implicitly through shiny) for generating plots, comparing temperature data across cities within a selected province.
#Link to the app
Accede a la Aplicación Meteorológica de España
#Summary Your app is designed to provide an interactive and informative experience for users interested in the meteorological conditions across Spain’s provinces. It leverages Shiny’s capability to integrate real-time data fetching, processing, and visualization within a user-friendly web interface. Thank you!