Inicio Apuntes FPApuntes DAW Aplicación del tiempo. Crea la tuya con la API de openweathermap

Aplicación del tiempo. Crea la tuya con la API de openweathermap

Predicción a 5 días vista

Publicado por entreunosyceros

Una vez más aquí. Sé que hace como dos meses que no publico nada en esta web (por falta de tiempo principalmente). Por eso es por lo que hoy vengo a dejar una pequeña aplicación que hace algún tiempo que tengo en el disco duro de este equipo, pero como he dicho, no pude publicar antes por falta de tiempo. Y como hoy me han recordado que en su día hice algunos trabajos en los que trabajaba con API’S de diferentes servicios, pues me he decidido a publicar el artículo que vamos a ver a continuación. En el vamos a desarrollar una aplicación del tiempo bastante básica, pero funcional. En ella usaremos la API de openweathermap para obtener los datos meteorológicos en tiempo real.

Hoy en día, el clima es un aspecto fundamental de nuestras vidas diarias, y contar con una herramienta que nos brinde información precisa sobre las condiciones meteorológicas es algo que muchos valoran. En mi familia tengo diversos casos de personas que se pasan el día consultando aplicaciones de este tipo. En este contexto, nace esta aplicación del tiempo, diseñada para ofrecer a los usuarios una experiencia intuitiva y útil para consultar el clima en su ciudad o cualquier otro lugar del mundo.

Lenguajes utilizados en esta aplicación del tiempo

  1. HTML: El lenguaje de marcado que utilizamos para estructurar la interfaz de usuario de nuestra aplicación.
  2. CSS: Para dar estilo y diseño a nuestra aplicación, asegurando una experiencia visual atractiva y coherente.
  3. JavaScript: El lenguaje de programación que utilizamos para agregar interactividad y funcionalidad a nuestra aplicación del tiempo.
  4. API de OpenWeatherMap: Esta API nos proporciona datos meteorológicos en tiempo real. En ella encontraremos la temperatura, la velocidad del viento y la humedad, para cualquier ubicación geográfica del mundo. Hay que decir que esta API, nos ofrece la posibilidad de trabajar con algunos datos que se pueden consultar en su página. También es verdad que esta API gratuita, tiene algunas limitaciones, pero para este ejemplo, vale perfectamente.

Estructura del proyecto

Aplicación del tiempo

Nuestro proyecto está organizado en tres archivos principales y otro de configuración desde donde tomaremos la API de openweathermap :

  1. HTML: Este archivo define la estructura básica de nuestra aplicación, incluyendo los elementos de entrada de datos y las secciones donde se mostrarán los resultados del clima.
  2. CSS: Aquí definimos los estilos visuales de nuestra aplicación, con los que nos aseguraremos una presentación atractiva y fácil de usar.
  3. JavaScript: En este archivo, manejamos la lógica y la interacción de la aplicación. Desde la recopilación de datos del usuario hasta la obtención de datos meteorológicos de la API y la presentación de la información en la interfaz.

Funcionalidades principales

Aplicación del tiempo funcionando
  1. Consulta del Clima por Ciudad: Los usuarios pueden ingresar el nombre de una ciudad en el campo de entrada y buscar el clima actual y la predicción para los próximos 5 días.
  2. Ubicación Actual: Con solo un clic, los usuarios pueden obtener el clima actual de su ubicación actual, utilizando la geolocalización del navegador.
  3. Presentación Visual del Clima: Utilizamos iconos y descripciones visuales para representar las condiciones meteorológicas de manera intuitiva y comprensible.
  4. Interfaz Responsiva: Nuestra aplicación está diseñada para adaptarse a diferentes tamaños de pantalla, garantizando una experiencia de usuario óptima en dispositivos móviles y de escritorio.

El código de la aplicación

Index.hml

En este código nos encontraremos:

  1. Estructura Básica: El código HTML define la estructura básica de la página web, incluyendo elementos como <head> para metadatos y <body> para el contenido visible.
  2. Inputs y Botones: Se crean campos de entrada de texto para que los usuarios escriban el nombre de la ciudad y botones para buscar el clima y obtener la ubicación actual.
  3. Secciones para Mostrar Datos: Se definen secciones donde se mostrarán los datos del clima, tanto el clima actual como la predicción a 5 días.
  4. Scripts JavaScript: Se enlazan los archivos JavaScript (script.js y config.js) para agregar interactividad y funcionalidad a la página.
<!DOCTYPE html>
<html lang="es">
  <head>
    <meta charset="utf-8">
    <title>Aplicación del tiempo | predicción a 5 días</title>
    <link rel="stylesheet" href="style.css">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  </head>
  <body>
    <h1>Consulta el clima en tu ciudad</h1>
    <div class="container">
      <div class="weather-input">
        <h3>Escribe el nombre de una ciudad</h3>
        <input class="city-input" type="text" placeholder="Ej. Madrid, ES">
        <button class="search-btn">Buscar</button>
        <div class="separador"></div>
        <button class="location-btn">Tu ubicación actual</button>
      </div>
      <div class="weather-data hidden">
        <div class="current-weather">
          <div class="details">
            <h2>________ | _______ </h2>
            <h6>Temperatura: __°C</h6>
            <h6>Viento: __ M/S</h6>
            <h6>Humedad: __%</h6>
          </div>
        </div>
        <div class="days-forecast">
          <h2>Predicción a 5 días</h2>
          <ul class="weather-cards">
            <li class="card">
              <h3> _______ </h3>
              <h6>Temperatura: __°C</h6>
              <h6>Viento: __ M/S</h6>
              <h6>Humedad: __%</h6>
            </li>
            <li class="card">
              <h3> _______ </h3>
              <h6>Temperatura: __°C</h6>
              <h6>Viento: __ M/S</h6>
              <h6>Humedad: __%</h6>
            </li>
            <li class="card">
              <h3> _______ </h3>
              <h6>Temperatura: __°C</h6>
              <h6>Viento: __ M/S</h6>
              <h6>Humedad: __%</h6>
            </li>
            <li class="card">
              <h3> _______ </h3>
              <h6>Temperatura: __°C</h6>
              <h6>Viento: __ M/S</h6>
              <h6>Humedad: __%</h6>
            </li>
            <li class="card">
              <h3> _______ </h3>
              <h6>Temperatura: __°C</h6>
              <h6>Viento: __ M/S</h6>
              <h6>Humedad: __%</h6>
            </li>
          </ul>
          <div class="footer">
            <h6>Datos obtenidos de: openweathermap.org</h6>
          </div>
        </div>
      </div>
    </div>
    
    <script type="module" src="script.js" defer></script>
    <script type="module" src="config.js"></script>
  </body>
</html>

Archivo style.css

En la hoja de estilos, veremos:

  1. Estilos generales: Se aplican estilos generales para resetear los márgenes y el relleno predeterminados, y se define la fuente utilizada en toda la página.
  2. Estilos para campos de entrada y botones: Se personalizan los estilos de los campos de entrada de texto y los botones para que se vean atractivos y sean fáciles de usar.
  3. Estilos para secciones y contenedores: Se definen estilos para las secciones de datos del clima y los contenedores para una presentación visual coherente.
  4. Media queries: Se añaden reglas de estilo específicas para adaptar la apariencia de la aplicación a diferentes tamaños de pantalla, asegurando una experiencia de usuario consistente en dispositivos móviles y de escritorio.
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: sans-serif, Arial, Helvetica;
}
body {
  background: #E3F2FD;
}
h1 {
  background: #5a00ad;
  font-size: 1.75rem;
  text-align: center;
  padding: 18px 0;
  color: #fff;
  border-bottom: 2px solid #000;
}
.container {
  display: flex;
  gap: 35px;
  padding: 30px;
}
.weather-input {
  width: 550px;
}
.weather-input input {
  height: 46px;
  width: 100%;
  outline: none;
  font-size: 1.07rem;
  padding: 0 17px;
  margin: 10px 0 20px 0;
  border-radius: 4px;
  border: 1px solid #ccc;
}
.weather-input input:focus {
  padding: 0 16px;
  border: 2px solid #5a00ad;
}
.weather-input .separador {
  height: 1px;
  width: 100%;
  margin: 25px 0;
  background: #BBBBBB;
  display: flex;
  align-items: center;
  justify-content: center;
}
.weather-input .separador::before{
  content: "o utiliza";
  color: #6C757D;
  font-size: 1.18rem;
  padding: 0 15px;
  margin-top: -4px;
  background: #E3F2FD;
}
.weather-input button {
  width: 100%;
  padding: 10px 0;
  cursor: pointer;
  outline: none;
  border: none;
  border-radius: 4px;
  font-size: 1rem;
  color: #fff;
  background: #5a00ad;
  transition: 0.2s ease;
}
.weather-input .search-btn:hover {
  background: #5a00ad;
}
.weather-input .location-btn {
  background: #0055a0;
}
.weather-input .location-btn:hover {
  background: rgb(0, 67, 134);
}
.weather-data {
  width: 100%;
}
.weather-data .current-weather {
  color: #fff;
  background: #5a00ad;
  border-radius: 5px;
  padding: 20px 70px 20px 20px;
  display: flex;
  justify-content: space-between;
  border:1px solid #000;
}
.current-weather h2 {
  font-weight: 700;
  font-size: 1.7rem;
}
.weather-data h6 {
  margin-top: 12px;
  font-size: 1rem;
  font-weight: 500;
}
.current-weather .icon {
  text-align: center;
}
.current-weather .icon img {
  max-width: 120px;
  margin-top: -15px;
}
.current-weather .icon h6 {
  margin-top: -10px;
  text-transform: capitalize;
}
.days-forecast h2 {
  margin: 20px 0;
  font-size: 1.5rem;
  text-align: center;
  background: #7343a0;
  color:#fff;
  padding: 1%;
}
.days-forecast .weather-cards {
  display: flex;
  gap: 20px;
}
.weather-cards .card {
  color: #fff;
  padding: 18px 16px;
  list-style: none;
  width: calc(100% / 5);
  background: #6825a7;
  border-radius: 5px;
  border:1px solid #000;
}
.weather-cards .card h3 {
  font-size: 1.3rem;
  font-weight: 600;
}
.weather-cards .card img {
  max-width: 70px;
  margin: 5px 0 -12px 0;
}
.footer{
  text-align: center;
  margin-bottom: 0em;
  font-weight: bold;
}

/*Ocultamos por defecto las tarjeta donde mostrar los datos*/
.hidden {
  display: none;
}

/* Media Querys para dispositivos móviles*/
@media (max-width: 1400px) {
  .weather-data .current-weather {
    padding: 20px;
  }
  .weather-cards {
    flex-wrap: wrap;
  }
  .weather-cards .card {
    width: calc(100% / 4 - 15px);
  }
}
@media (max-width: 1200px) {
  .weather-cards .card {
    width: calc(100% / 3 - 15px);
  }
}
@media (max-width: 950px) {
  .weather-input {
    width: 450px;
  }
  .weather-cards .card {
    width: calc(100% / 2 - 10px);
  }
}
@media (max-width: 750px) {
  h1 {
    font-size: 1.45rem;
    padding: 16px 0;
  }
  .container {
    flex-wrap: wrap;
    padding: 15px;
  }
  .weather-input {
    width: 100%;
  }
  .weather-data h2 {
    font-size: 1.35rem;
  }
}

Archivo script.js

Para manejar la lógica de la aplicación, encontraremos dentro de este archivo:

Importación de API Key: Se importa la API key desde el archivo config.js y se invierte la cadena para ofuscarla.

Funciones para crear y mostrar tarjetas de clima: Se definen funciones para crear tarjetas de clima para el clima actual y la predicción a 5 días. También se trabajará la lógica para mostrarlas en la interfaz de usuario.

Obtención de detalles del clima: Se implementa la lógica para obtener los detalles del clima. También se incluye la solicitud de datos de la API de OpenWeatherMap y el procesamiento de la respuesta para mostrarla en la interfaz.

Funciones para obtener ubicación y mostrar datos: Se definen funciones para obtener la ubicación actual del usuario y mostrar los datos del clima correspondientes.

Event listeners: Se añaden escuchadores de eventos para los botones de búsqueda y ubicación, así como para la entrada de texto, para activar las funciones correspondientes cuando los usuarios interactúan con la aplicación.

import obfuscatedKey from './config.js';

const reverseString = (str) => {
    return str.split('').reverse().join('');
};
const cityInput = document.querySelector(".city-input");
const searchButton = document.querySelector(".search-btn");
const locationButton = document.querySelector(".location-btn");
const currentWeatherDiv = document.querySelector(".current-weather");
const weatherCardsDiv = document.querySelector(".weather-cards");

// Obtener la API key 
const API_KEY = reverseString(obfuscatedKey);

//Función para crear la tarjeta del clima del día de hoy y la predicción a 5 días
const createWeatherCard = (cityName, weatherItem, index) => {
    const { dt_txt, main, wind, weather } = weatherItem;
    const date = new Date(dt_txt);
    const formattedDate = `${date.getDate()}/${date.getMonth() + 1}/${date.getFullYear()}`; // Cambiamos el formato de fecha devuelta por la API
    const temperature = (main.temp - 273.15).toFixed(0);



    const traducirDescripcionClima = (descripcion) => {
        switch (descripcion) { // Traducción de la descripción que devuelve la API
            case 'clear sky':
                return 'Cielo despejado';
            case 'few clouds':
                return 'Algunas nubes';
            case 'overcast clouds':
                return 'Nublado';
            case 'drizzle':
                return 'Llovizna';
            case 'broken clouds':
                return 'Algunas nubes';
            case 'scattered clouds':
                return 'Nubes dispersas';
            case 'rain':
                return 'Lluvia';
            case 'light rain':
                return 'Lluvia ligera';
            case 'moderate rain':
                return 'Lluvia moderada';
            case 'shower rain':
                return 'Lluvia intensa';
            case 'thunderstorm':
                return 'Tormenta';
            case 'snow':
                return 'Nieve';
            case 'light snow':
                return 'Nieve ligera';
            case 'mist':
                return 'Niebla';
            default:
                return descripcion; // Devuelve la descripción original si no se encuentra una traducción
        }
    };

    if (index === 0) { // Muestra los datos del clima para el día de hoy
        return `<div class="details">
                    <h2>Clima en ${cityName} a ${formattedDate}</h2>
                    <h6>Temperatura: ${temperature}°C</h6>
                    <h6>Viento: ${wind.speed} M/S</h6>
                    <h6>Humedad: ${main.humidity}%</h6>
                </div>
                <div class="icon">
                    <img src="https://openweathermap.org/img/wn/${weather[0].icon}@4x.png" alt="weather-icon">
                    <h6>${traducirDescripcionClima(weather[0].description)}</h6>
                </div>`;
    } else { // Muestra los datos para la predicción de los cinco días
        return `<li class="card"> 
                    <h3>${cityName} el ${formattedDate}</h3>
                    <img src="https://openweathermap.org/img/wn/${weather[0].icon}.png" alt="weather-icon">
                    <h6>Predicción: ${traducirDescripcionClima(weather[0].description)}</h6>
                    <h6>Temperatura: ${temperature}°C</h6>
                    <h6>Viento: ${wind.speed} M/S</h6>
                    <h6>Humedad: ${main.humidity}%</h6>
                </li>`;
    }
};

// Función para obtener detalles de la API
const getWeatherDetails = async (cityName, latitude, longitude) => {
    try {
        const WEATHER_API_URL = `https://api.openweathermap.org/data/2.5/forecast?lat=${latitude}&lon=${longitude}&appid=${API_KEY}`;
        const response = await fetch(WEATHER_API_URL);
        const data = await response.json();

        const uniqueForecastDays = [];
        const fiveDaysForecast = data.list.filter(forecast => {
            const forecastDate = new Date(forecast.dt_txt).getDate();
            if (!uniqueForecastDays.includes(forecastDate)) {
                return uniqueForecastDays.push(forecastDate);
            }
        });

        cityInput.value = "";
        currentWeatherDiv.innerHTML = "";
        weatherCardsDiv.innerHTML = "";

        fiveDaysForecast.forEach((weatherItem, index) => {
            const html = createWeatherCard(cityName, weatherItem, index);
            index === 0 ? currentWeatherDiv.insertAdjacentHTML("beforeend", html) : weatherCardsDiv.insertAdjacentHTML("beforeend", html);
        });
    } catch (error) {
        console.error("Error al obtener los detalles del clima. ", error);
        alert("Ocurrió un error al obtener el pronóstico del tiempo.");
        hideWeatherData(); // Oculta la sección de datos climáticos en caso de error
    }
};

const getCityCoordinates = async () => {
    const cityName = cityInput.value.trim();
    if (!cityName){
        hideWeatherData();
        alert("Por favor, escribe el nombre de una ciudad.");
        return; // Utiliza 'return' en lugar de 'exit' para salir de la función
    }
    try {
        const API_URL = `https://api.openweathermap.org/geo/1.0/direct?q=${cityName}&limit=1&appid=${API_KEY}`;
        const response = await fetch(API_URL);
        const data = await response.json();

        if (!data.length){
            hideWeatherData();
            alert(`No se encontraron coordenadas para ${cityName}`);
            return; // Utiliza 'return' para salir de la función
        }
        const { lat, lon, name } = data[0];
        getWeatherDetails(name, lat, lon);
        
    } catch (error) {
        console.error("Error al obtener las coordenadas de la ciudad:", error);
        alert("Ocurrió un error al obtener las coordenadas.");
        hideWeatherData(); // Oculta la sección de datos climáticos en caso de error
    }
};

// Función para obtener los datos del clima cuando el usuario utiliza su ubicación actual
const getUserCoordinates = () => {
    navigator.geolocation.getCurrentPosition(
        position => {
            const { latitude, longitude } = position.coords;
            const API_URL = `https://api.openweathermap.org/geo/1.0/reverse?lat=${latitude}&lon=${longitude}&limit=1&appid=${API_KEY}`;

            fetch(API_URL)
                .then(response => response.json())
                .then(data => {
                    const { name } = data[0];
                    getWeatherDetails(name, latitude, longitude);
                })
                .catch(error => {
                    console.error("Error al obtener el nombre de la ciudad. ", error);
                    alert("Ocurrió un error al obtener el nombre de la ciudad.");
                    hideWeatherData(); // Oculta la sección de datos climáticos en caso de error
                });
        },
        error => {
            if (error.code === error.PERMISSION_DENIED) {
                alert("Solicitud de geolocalización denegada. Restablece el permiso de ubicación para otorgar acceso nuevamente.");
                hideWeatherData(); // Oculta la sección de datos climáticos en caso de error
            } else {
                alert("Error de solicitud de geolocalización. Restablece el permiso de ubicación.");
                hideWeatherData(); // Oculta la sección de datos climáticos en caso de error
            }
        }
    );
};

locationButton.addEventListener("click", getUserCoordinates);
searchButton.addEventListener("click", getCityCoordinates);
cityInput.addEventListener("keyup", e => e.key === "Enter" && getCityCoordinates());

cityInput.addEventListener("keyup", function(event) {
    if (event.keyCode === 13) {
        showWeatherData(); // Mostrar la sección de datos climáticos al presionar Enter
    }
});

searchButton.addEventListener("click", function() {
    showWeatherData(); // Mostrar la sección de datos climáticos al hacer clic en el botón de búsqueda
});

locationButton.addEventListener("click", function() {
    showWeatherData(); // Mostrar la sección de datos climáticos al hacer clic en el botón de ubicación
});

const showWeatherData = () => {
    const cityName = cityInput.value.trim();
    if (!cityName) {
        hideWeatherData(); // Ocultar la sección de datos climáticos si no se proporciona un nombre de ciudad
        return; // Utiliza 'return' para salir de la función después de ocultar los datos climáticos
    }
    // Mostrar la sección de datos climáticos si se proporciona un nombre de ciudad
    document.querySelector('.weather-data').classList.remove('hidden');
};

const hideWeatherData = () => {
    document.querySelector('.weather-data').classList.add('hidden');
};

Archivo config.js

Este archivo solo contendrá la definición de la API KEY. Tengo que decir que por que me ha parecido gracioso, aquí la KEY debe estar guardada al revés. Dentro de este archivo veremos:

////API obtenida desde https://openweathermap.org

const API_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXX"; // AQUI VA LA APIKEY DADA LA VUELTA

// Exportar el objeto de configuración
export default API_KEY;

En resumen, nuestro proyecto de aplicación del tiempo es una herramienta valiosa que ofrece a los usuarios acceso rápido y fácil a la información meteorológica. Con una interfaz amigable y funcionalidades interesantes, esta aplicación ha sido un pequeño proyecto para trabajar con una API sencilla de manejar.

Si quieres ver esta aplicación funcionando, puedes utilizar el siguiente enlace. El código de esta aplicación también se puede ver en el repositorio en GitHub en el que he alojado este proyecto.

También te puede interesar ...

Deja un comentario

* Al utilizar este formulario, aceptas que este sitio web almacene y maneje tus datos.

Adblock Detectado!!

Ayúdanos deshabilitando la extensión AdBlocker de tu navegador para visitar esta web.
Si no sabes hacerlo en Chrome, consulta el siguiente enlace. Si utilizas Firefox, puedes consultar este otro enlace.
Esto mejorará tu experiencia en este sitio web.