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!