diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..0b5ad9507 Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index f8b15f4cb..9baa71f2e 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,19 @@ -# Weather App +Project: WeatherTheWeatherApp -Replace this readme with your own information about your project. +WeatherTheWeatherApp is a weather application that retrieves and displays real-time data for Stockholm. It fetches and displays the current location, temperature, weather condition, sunrise, and sunset times. It shows the weather forecast for today and the next four days, including daily min and max temperatures. The app visually changes images and background styles based on whether it’s clear, cloudy, or rainy, and whether it’s day or night. -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. +Challenges Encountered +- The initial challenge involved successfully retrieving data from the OpenWeatherMap API. Utilizing console.log helped me identifying the structure of the API response. +- Converting UNIX timestamps for sunrise and sunset times into a readable format was a challenge, but resolved this using a the function, convertUnixToTime. +- I found implementing some sort of logic to change the background and images based on weather conditions and the time of day very complex. For this, I use a switch statement and various condition checks to ensure the app responded accurately to different weather scenarios/time of day. -## The problem +If given more time, I would: -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? +- Create a helper function to streamline the code that handles image and background changes, as it now includes a lot of redundancy and repetetive code. +- Implement weather condition icons for each day in the forecast. +- Refine the styling to better align with the Figma design layout. +- Enhance responsiveness for a wider range of screen sizes. ## View it live -Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. +https://master--weathertheweatherapp.netlify.app \ No newline at end of file diff --git a/assets/.DS_Store b/assets/.DS_Store new file mode 100644 index 000000000..e7457303c Binary files /dev/null and b/assets/.DS_Store differ diff --git a/assets/design-2/Clouds.png b/assets/design-2/Clouds.png new file mode 100644 index 000000000..d805d8427 Binary files /dev/null and b/assets/design-2/Clouds.png differ diff --git a/assets/design-2/Moon.png b/assets/design-2/Moon.png new file mode 100644 index 000000000..e389df60f Binary files /dev/null and b/assets/design-2/Moon.png differ diff --git a/assets/design-2/broken-clouds.png b/assets/design-2/broken-clouds.png new file mode 100644 index 000000000..5b207ffca Binary files /dev/null and b/assets/design-2/broken-clouds.png differ diff --git a/assets/design-2/rain.png b/assets/design-2/rain.png new file mode 100644 index 000000000..ab462de29 Binary files /dev/null and b/assets/design-2/rain.png differ diff --git a/assets/design-2/sun-100.png b/assets/design-2/sun-100.png new file mode 100644 index 000000000..881309d6f Binary files /dev/null and b/assets/design-2/sun-100.png differ diff --git a/assets/design-2/sun.png b/assets/design-2/sun.png new file mode 100644 index 000000000..4e76f3ee9 Binary files /dev/null and b/assets/design-2/sun.png differ diff --git a/index.html b/index.html new file mode 100644 index 000000000..5007b8c16 --- /dev/null +++ b/index.html @@ -0,0 +1,48 @@ + + + + + + + + + + + + WeathertheweatherApp + + + +
+
+
+
+ sun + clouds-img + broken-clouds-img + rain-img + moon-img +

Temp

+

location

+

condition

+
+
+

sunrise

+

sunset

+
+
+ + +
+
    forecast
+
+
+ +
+ + + + + \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 000000000..9dedabdfe --- /dev/null +++ b/script.js @@ -0,0 +1,236 @@ + +//STEP 1: constructing the BASE URL +const BASE_URL = 'https://api.openweathermap.org/data/2.5/weather?q=Stockholm,Sweden&units=metric&appid=' +const API_KEY = '1bd3fe8b6571a9e92c6d24232e62bdc8' + +const URL = `${BASE_URL}${API_KEY}` + +//STEP 2: URL for Weather Forecast +const FORECAST_URL = 'https://api.openweathermap.org/data/2.5/forecast?q=Stockholm,Sweden&units=metric&appid=' + +const forecastURL = `${FORECAST_URL}${API_KEY}` + +// DOM Selectors +const weatherLocation = document.getElementById("location"); +const temperature = document.getElementById("temp"); +const weatherCondition = document.getElementById("condition"); +const sunriseDisplay = document.getElementById("sunriseID"); +const sunsetDisplay = document.getElementById("sunsetID"); +const forecast = document.getElementById("forecastID") +const locationCard = document.getElementById("locationCard") +//DOM Selectors - Images +const sunImg = document.getElementById("sun"); +const cloudsImg = document.getElementById("cloudsImg"); +const rainImg = document.getElementById("rainImg"); +const brokenCloudsImg = document.getElementById("brokenCloudsImg"); +const moonImg = document.getElementById("moonImg") + +//FUNCTIONS +//Function to handle errors +const handleError = (errorMessage) => { + console.error('Error fetching data:', errorMessage); + forecast.innerHTML = "Unable to retrieve weather data."; +}; + +// Function to convert UNIX timestamp to readable time format +const convertUnixToTime = (unixTimestamp) => { + const date = new Date(unixTimestamp * 1000); + //Round the number to a format without seconds + return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); +}; + +// Function to format timestamp to date string (YYYY-MM-DD) +const formatDate = (timestamp) => { + const date = new Date(timestamp * 1000); + return date.toISOString().split('T')[0]; // Get only the date part +}; + +// Function to get day of the week from date string +const getDayOfWeek = (dateString) => { + const date = new Date(dateString); + const daysOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + return daysOfWeek[date.getDay()]; +}; + +// Initialize an object to hold daily min and max temps +const dailyTemps = {}; + +//Fetching data from the IPA for current weather +fetch(URL) + .then(response => response.json()) + .then(data => { + console.log(data); + + const stockholm = data.name; + const stockholmTemp = data.main.temp; + const roundedTemp = Math.round(stockholmTemp); + const displayCondition = data.weather[0].description; + + //changing the innerHTML for city, temp and condition. + weatherLocation.innerText = `${stockholm}`; + temperature.innerHTML = + `${roundedTemp}°C`; + weatherCondition.innerText = `${displayCondition}`; + + //Get the sunrise and sunset + const sunriseTime = convertUnixToTime(data.sys.sunrise); + const sunsetTime = convertUnixToTime(data.sys.sunset); + + // Display sunrise and sunset times in innerText + sunriseDisplay.innerText = `Sunrise ${sunriseTime}`; + sunsetDisplay.innerText = `Sunset ${sunsetTime}`; + + // Create a new Date object + const date = new Date(); + + // Get the time offset for Stockholm (CET/CEST: Central European Time/ Central European Summer Time) + const options = { + timeZone: "Europe/Stockholm", + hour: "2-digit", + }; + + const stockholmTime = date.toLocaleTimeString("sv-SE", options); + + // Hide all images + sunImg.classList.add("hidden"); + cloudsImg.classList.add("hidden"); + rainImg.classList.add("hidden"); + brokenCloudsImg.classList.add("hidden"); + moonImg.classList.add("hidden"); + + // Change image & background if condition is sunny/cloudy and if the time is night/day + switch (true) { + case displayCondition.includes("clear"): + if (stockholmTime >= 6 && stockholmTime < 21) { + // Daytime clear weather + sunImg.classList.remove("hidden"); + sunImg.classList.add("visible"); + moonImg.classList.add("hidden"); + locationCard.classList.remove("location-card-night"); + locationCard.classList.add("location-card"); + } else { + // Nighttime clear weather + moonImg.classList.remove("hidden"); + moonImg.classList.add("visible"); + sunImg.classList.add("hidden"); + locationCard.classList.add("location-card-night"); + } + break; + + case displayCondition.includes("cloud"): + if (stockholmTime >= 6 && stockholmTime < 21) { + // Daytime cloudy weather + cloudsImg.classList.remove("hidden"); + cloudsImg.classList.add("visible"); + moonImg.classList.add("hidden"); + locationCard.classList.remove("location-card-night"); + locationCard.classList.add("location-card"); + } else { + // Nighttime cloudy weather + moonImg.classList.remove("hidden"); + moonImg.classList.add("visible"); + cloudsImg.classList.add("hidden"); + locationCard.classList.add("location-card-night"); + } + break; + + case displayCondition.includes("rain"): + if (stockholmTime >= 6 && stockholmTime < 21) { + // Daytime rainy weather + rainImg.classList.remove("hidden"); + rainImg.classList.add("visible"); + moonImg.classList.add("hidden"); + locationCard.classList.remove("location-card-night"); + locationCard.classList.add("location-card-rainy"); + } else { + // Nighttime rainy weather + moonImg.classList.remove("hidden"); + moonImg.classList.add("visible"); + rainImg.classList.add("hidden"); + locationCard.classList.add("location-card-night"); + } + break; + + case displayCondition.includes("broken"): + if (stockholmTime >= 6 && stockholmTime < 21) { + // Daytime broken clouds + brokenCloudsImg.classList.remove("hidden"); + brokenCloudsImg.classList.add("visible"); + moonImg.classList.add("hidden"); + locationCard.classList.remove("location-card-night"); + locationCard.classList.add("location-card"); + } else { + // Nighttime broken clouds + moonImg.classList.remove("hidden"); + moonImg.classList.add("visible"); + brokenCloudsImg.classList.add("hidden"); + locationCard.classList.add("location-card-night"); + } + break; + + default: + locationCard.classList.add("location-card"); + moonImg.classList.add("hidden"); + } + + }) + .catch(error => handleError(error)); + + +// Fetching data from the forecastURL +fetch(forecastURL) + .then(response => response.json()) + .then(data => { + console.log(data); + + // Iterate through the weather forecast + data.list.forEach((forecast) => { + const date = formatDate(forecast.dt); + const minTemp = forecast.main.temp_min; + const maxTemp = forecast.main.temp_max; + + // Initialize the entry for the date if it doesn't exist + if (!dailyTemps[date]) { + dailyTemps[date] = { min: minTemp, max: maxTemp }; + } else { + // Update the min and max temps for the day + dailyTemps[date].min = Math.min(dailyTemps[date].min, minTemp); + dailyTemps[date].max = Math.max(dailyTemps[date].max, maxTemp); + } + }); + + // Extract the next four days' temperatures and convert date to day name + const nextFourDays = Object.entries(dailyTemps) + //Getting the current day + the following 4 days + .slice(0, 5) + .map(([date, temps]) => ({ + date: getDayOfWeek(date), + min: temps.min, + max: temps.max, + })); + + // Render the forecast data in the browser + renderForecast(nextFourDays); + }) + .catch(error => handleError(error)); + + +// Function to render forecast data in the browser +const renderForecast = (forecastData) => { + forecast.innerHTML = ""; + let forecastContent = ""; + + forecastData.forEach(day => { + forecastContent += + `
  • + ${day.date} + ${Math.round(day.min)}° / ${Math.round(day.max)}°C +
  • `; + }); + + forecast.innerHTML = forecastContent; +}; + + + + diff --git a/style.css b/style.css new file mode 100644 index 000000000..5df1b47a9 --- /dev/null +++ b/style.css @@ -0,0 +1,206 @@ + body { + margin: 30px; + font-family: "Roboto", sans-serif; + font-weight: 300; + font-style: normal; + } + + .weather-card { + width: 414px; + height: 896px; + margin: 0 auto; + box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.3); + display: flex; + flex-direction: column; + background-color: white; + } + + .location-card { + background: linear-gradient(90deg, #8589FF 0%, #E8E9FF 100%); + width: 100%; + height: 50%; + margin: 0; + color: white; + position: relative; + } + + /* background for rainy */ + .location-card-rainy { + background: linear-gradient(90deg, #6264A2 0%, #9b9cc2 100%); + width: 100%; + height: 50%; + margin: 0; + color: white; + position: relative; + } + + /* background for night */ + .location-card-night { + background: linear-gradient(90deg, #222350 0%, #5a5c9b 100%); + width: 100%; + height: 50%; + margin: 0; + color: white; + position: relative; + } + + .temp-container { + position: relative; + } + + /* Hides images if they are not called */ + .hidden { + display: none; + } + + /*Shows images when they are called */ + .visible { + position: absolute; + top: 65px; + right: 45px; + width: 35%; + } + + .temp { + font-size: 120px; + display: inline-block; + position: relative; + margin-top: 140px; + margin-bottom: 0; + margin-left: 35px; + margin-bottom: 0; + padding: 0; + } + + .temp-unit { + font-size: 40px; + display: inline-block; + bottom: 100%; + left: 0; + transform: translateY(-50px); + } + + .location { + position: absolute; + top: 235px; + margin-left: 40px; + font-size: 34px; + margin-bottom: 0; + } + + .condition { + position: absolute; + top: 290px; + margin-left: 40px; + font-size: 22px; + margin-bottom: 0; + } + + .suntime-container { + display: flex; + flex-direction: row; + align-items: center; + margin-top: 30px; + padding-right: 40px; + position: relative; + } + + .sunrise, + .sunset { + margin-top: 40px; + margin-left: 40px; + font-size: 22px; + } + + + .forecast-card { + background-color: white; + width: 100%; + height: 50%; + display: grid; + } + + .forecast { + display: grid; + grid-template-columns: repeat(1, 1fr); + padding: 10px; + margin-top: 10px; + list-style-type: none; + } + + #forecastID li { + background-color: #ededed; + height: 24px; + padding: 20px; + border-radius: 30px; + text-align: center; + font-size: 20px; + line-height: 23.44px; + } + + .forecast-item { + display: flex; + justify-content: space-around; + padding: 10px 0; + border-bottom: 1px solid #ccc; + } + + /* Responsiveness */ + @media(max-width: 414px) { + body { + margin: 10px; + } + + .weather-card { + width: 95%; + height: 650px; + } + + .temp { + font-size: 80px; + margin-top: 70px; + } + + .temp-unit { + font-size: 30px; + transform: translateY(-35px); + } + + .visible { + top: 50px; + right: 30px; + width: 25%; + } + + .location { + top: 140px; + font-size: 22px; + } + + .condition { + top: 180px; + font-size: 17px; + } + + .suntime-container { + margin-top: 45px; + margin-left: 5px; + } + + .sunrise, + .sunset { + margin-top: 25px; + margin-left: 35px; + font-size: 13px; + } + + .forecast { + gap: 7px; + } + + #forecastID li { + height: 20px; + padding: 12px; + font-size: 15px; + } + } \ No newline at end of file