-
Notifications
You must be signed in to change notification settings - Fork 219
Request1 #234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Request1 #234
Changes from all commits
d39fc7c
185b9bb
f541232
713e233
168d951
312e666
6aeddc4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| <!doctype html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <link rel="stylesheet" href="style.css" /> | ||
| <link | ||
| rel="stylesheet" | ||
| href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" | ||
| /> | ||
|
|
||
| <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script> | ||
| <title>Weather App</title> | ||
| </head> | ||
| <body> | ||
| <div class="container-fluid"> | ||
| <div class="row"> | ||
| <div class="col-md-8 offset-md-2"> | ||
| <div class="page-header mt-4"> | ||
| <h1>Weather Project</h1> | ||
| </div> | ||
| <hr class="page-divider" /> | ||
|
|
||
| <form id="search-form" class="mb-4"> | ||
| <div class="form-row container-fluid"> | ||
| <div class="col-6 offset-md-1"> | ||
| <input | ||
| type="text" | ||
| id="search-query" | ||
| class="form-control" | ||
| placeholder="Enter the City Name Here" | ||
| /> | ||
| </div> | ||
| <div class="col-2"> | ||
| <button type="submit" class="btn btn-primary btn-block search"> | ||
| Search City | ||
| </button> | ||
| </div> | ||
|
|
||
| <div class="col-2"> | ||
| <button | ||
| type="submit" | ||
| class="btn btn-primary btn-block set-default" | ||
| > | ||
| Set as Default | ||
| </button> | ||
| </div> | ||
| </div> | ||
| </form> | ||
|
|
||
| <div class="container-fluid"> | ||
| <div class="weather-data offset-md-2"></div> | ||
| </div> | ||
| <div class="mt-5 col-4 offset-md-4 text-center"> | ||
| <button type="submit" class="btn btn-primary btn-block geo-locate"> | ||
| Weather at my Current Location | ||
| </button> | ||
| <div | ||
| id="spinner" | ||
| class="loader offset-md-5 mt-5" | ||
| style="display: none" | ||
| ></div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div class="col-12 d-flex justify-content-center mt-5"> | ||
| <div id="map" style="width: 600px; height: 450px"></div> | ||
| </div> | ||
|
|
||
| <script | ||
| src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDbcmkOz0m2H2Eo5eum8Cm61U4jWCrn6rg&callback=initMap&v=weekly&libraries=marker" | ||
| defer | ||
| ></script> | ||
|
|
||
| <script src="main.js" type="text/javascript"></script> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,300 @@ | ||||||
| let currentWeather = []; | ||||||
| let weatherForecast = []; | ||||||
|
|
||||||
| //Render on Page Reload, etc. | ||||||
| if (localStorage.defaultWeather) { | ||||||
| let savedWeather = localStorage.defaultWeather; | ||||||
|
|
||||||
| document.querySelector(".weather-data").innerHTML = | ||||||
| `<div class="text-left mb-5 default-city"><strong>DEFAULT CITY</strong></div>` + | ||||||
| savedWeather; | ||||||
| } | ||||||
|
|
||||||
| const renderWeatherData = () => { | ||||||
| const weather = currentWeather[0]; | ||||||
|
|
||||||
| //Daily Data Array | ||||||
| const dailyData = weatherForecast[0].map((_, i) => ({ | ||||||
| condition: weatherForecast[0][i], | ||||||
| temp: weatherForecast[1][i], | ||||||
| icon: weatherForecast[2][i], | ||||||
| day: weatherForecast[3][i], | ||||||
| })); | ||||||
|
|
||||||
| //Change Styling Based on Current Weather w/ Switch Statement | ||||||
| switch (weather.condition) { | ||||||
| case "Clouds": | ||||||
| document.body.style.backgroundColor = "#80808080"; | ||||||
| break; | ||||||
| case "Clear": | ||||||
| document.body.style.backgroundColor = "#d4ef3a80"; | ||||||
| break; | ||||||
| case "Mist": | ||||||
| document.body.style.backgroundColor = "#647eef80"; | ||||||
| break; | ||||||
| case "Rain": | ||||||
| document.body.style.backgroundColor = "#0e0e8080"; | ||||||
| break; | ||||||
| case "Thunderstorm": | ||||||
| document.body.style.backgroundColor = "#87070780"; | ||||||
| break; | ||||||
| case "Drizzle": | ||||||
| document.body.style.backgroundColor = "#89CFF080"; | ||||||
| break; | ||||||
| case "Haze": | ||||||
| document.body.style.backgroundColor = "#C4A48480"; | ||||||
| break; | ||||||
| default: | ||||||
| document.body.style.backgroundColor = "white"; | ||||||
| } | ||||||
|
|
||||||
| let currentTemplate = `<div class = "row current-weather mb-4"> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can be const |
||||||
| <div class = "current-conditions col-md-3 offset-md-2 text-center"><strong> | ||||||
| <div>${Math.round(weather.temp)}°F</div> | ||||||
| <div>${weather.city}</div> | ||||||
| <div>${weather.condition}</div></strong> | ||||||
|
|
||||||
| </div> | ||||||
| <div class="current-icon col-md-2" > | ||||||
| <img src="https://openweathermap.org/img/wn/${weather.icon}@2x.png" class="condition ${weather.condition}" alt="${weather.condition}"> | ||||||
| </div> | ||||||
|
|
||||||
| </div> | ||||||
|
|
||||||
| `; | ||||||
|
|
||||||
| const forecastHTML = dailyData | ||||||
| .map( | ||||||
| (item) => ` | ||||||
| <div class="col-md-2 text-center day"><strong> | ||||||
| <div>${item.condition}</div> | ||||||
| <div>${item.temp}</div> | ||||||
| <img src="https://openweathermap.org/img/wn/${item.icon}@2x.png" class="img-fluid" alt="${item.condition}"> | ||||||
| <div>${item.day}</div> | ||||||
| </strong> | ||||||
| </div> | ||||||
|
|
||||||
| `, | ||||||
| ) | ||||||
| .join(""); | ||||||
|
|
||||||
| document.querySelector(".weather-data").innerHTML = | ||||||
| currentTemplate + | ||||||
| `<div class="row forecast-data text-center">${forecastHTML}</div>`; | ||||||
| }; | ||||||
|
|
||||||
| //Event Listener for Geolocation | ||||||
| const spinner = document.getElementById("spinner"); | ||||||
|
|
||||||
| document.querySelector(".geo-locate").addEventListener("click", (e) => { | ||||||
| e.preventDefault(); | ||||||
|
|
||||||
| spinner.style.display = "block"; | ||||||
|
|
||||||
| //Geolocation with Error Handling | ||||||
|
|
||||||
| navigator.geolocation.getCurrentPosition( | ||||||
| (position) => { | ||||||
| lat = position.coords.latitude; | ||||||
| lon = position.coords.longitude; | ||||||
|
|
||||||
| fetchWeather(lat, lon); | ||||||
| }, | ||||||
| (error) => { | ||||||
| spinner.style.display = "none"; | ||||||
| alert( | ||||||
| "Either Location Access Denied or timed out. Refresh the Page and Try Again", | ||||||
| ); | ||||||
| }, | ||||||
| { timeout: 7000 }, | ||||||
| ); | ||||||
| }); | ||||||
|
|
||||||
| //Event Listener when clicking on map | ||||||
|
|
||||||
| //Event Listener for Map | ||||||
|
|
||||||
| let map; | ||||||
| let clickMarker; | ||||||
|
|
||||||
| function initMap() { | ||||||
| map = new google.maps.Map(document.getElementById("map"), { | ||||||
| center: { lat: 36.2371, lng: -79.9795 }, | ||||||
| zoom: 12, | ||||||
| }); | ||||||
|
|
||||||
| if (navigator.geolocation) { | ||||||
| navigator.geolocation.getCurrentPosition((position) => { | ||||||
| const initialPos = { | ||||||
| lat: position.coords.latitude, | ||||||
| lng: position.coords.longitude, | ||||||
| }; | ||||||
|
|
||||||
| updateWeatherByMap(initialPos.lat, initialPos.lng); | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| map.addListener("click", (e) => { | ||||||
| const lat = e.latLng.lat(); | ||||||
| const lon = e.latLng.lng(); | ||||||
|
|
||||||
| updateWeatherByMap(e.latLng.lat(), e.latLng.lng()); | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| //Map Helper Function | ||||||
| updateWeatherByMap = (lat, lon) => { | ||||||
| const pos = { lat, lng: lon }; | ||||||
|
|
||||||
| map.panTo(pos); | ||||||
|
|
||||||
| if (clickMarker) { | ||||||
| clickMarker.setMap(null); | ||||||
| } | ||||||
|
|
||||||
| clickMarker = new google.maps.Marker({ | ||||||
| position: pos, | ||||||
| map: map, | ||||||
| title: "Selected Location", | ||||||
| }); | ||||||
|
|
||||||
| fetchWeather(lat, lon); | ||||||
| }; | ||||||
|
|
||||||
| //Event Listener for Set Default | ||||||
| document.querySelector(".set-default").addEventListener("click", (e) => { | ||||||
| e.preventDefault(); | ||||||
|
|
||||||
| const weatherData = document.querySelector(".weather-data"); | ||||||
|
|
||||||
| const label = weatherData.querySelector(".default-city"); | ||||||
| if (label) { | ||||||
| label.remove(); | ||||||
| } | ||||||
|
|
||||||
| const field = weatherData.innerHTML; | ||||||
| localStorage.setItem("defaultWeather", field); | ||||||
|
|
||||||
| weatherData.innerHTML = | ||||||
| `<div class="text-left mb-5 default-city"><strong>DEFAULT CITY</strong></div>` + | ||||||
| localStorage.defaultWeather; | ||||||
| }); | ||||||
|
|
||||||
| //Event Listener for Search | ||||||
| document.querySelector(".search").addEventListener("click", (e) => { | ||||||
| e.preventDefault(); | ||||||
|
|
||||||
| const city = document.querySelector("#search-query").value; | ||||||
|
|
||||||
| fetchWeather(city); | ||||||
|
|
||||||
| document.querySelector("#search-query").value = ""; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if requests fails, this is a bad experience |
||||||
| }); | ||||||
|
|
||||||
| const addCurrentWeather = (data) => { | ||||||
| currentWeather = []; | ||||||
|
|
||||||
| const conditions = { | ||||||
| temp: (data.main.temp - 273.15) * (9 / 5) + 32, | ||||||
| city: data.name || null, | ||||||
| condition: data.weather[0].main || null, | ||||||
| icon: data.weather[0].icon || null, | ||||||
| }; | ||||||
|
|
||||||
| currentWeather.push(conditions); | ||||||
| }; | ||||||
|
|
||||||
| const addForecast = (data) => { | ||||||
| weatherForecast = []; | ||||||
|
|
||||||
| const weatherArray = []; | ||||||
| const tempArray = []; | ||||||
| const iconArray = []; | ||||||
| const dayArray = []; | ||||||
|
|
||||||
| let count = 0; | ||||||
| let subWeather = []; | ||||||
| let subTemp = []; | ||||||
| let subIcon = []; | ||||||
| let subDay = []; | ||||||
|
|
||||||
| for (let i = 0; i < data.list.length; i++) { | ||||||
| count += 1; | ||||||
|
|
||||||
| subWeather.push(data.list[i].weather[0].main); | ||||||
| subTemp.push( | ||||||
| `${Math.round((data.list[i].main.temp - 273.15) * (9 / 5) + 32)}°F`, | ||||||
| ); | ||||||
| subIcon.push(data.list[i].weather[0].icon); | ||||||
| const date = new Date(data.list[i].dt_txt); | ||||||
| subDay.push(date.toLocaleDateString("en-US", { weekday: "long" })); | ||||||
|
|
||||||
| if (count === 8) { | ||||||
| weatherArray.push(mode(subWeather)); | ||||||
| tempArray.push(mode(subTemp)); | ||||||
| iconArray.push(mode(subIcon)); | ||||||
| dayArray.push(mode(subDay)); | ||||||
|
|
||||||
| count = 0; | ||||||
| subWeather = []; | ||||||
| subTemp = []; | ||||||
| subIcon = []; | ||||||
| subDay = []; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| weatherForecast.push(weatherArray); | ||||||
| weatherForecast.push(tempArray); | ||||||
| weatherForecast.push(iconArray); | ||||||
| weatherForecast.push(dayArray); | ||||||
| }; | ||||||
|
|
||||||
| //Find the mode of each nested Array | ||||||
| const mode = (array) => { | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not clear what mode is referring to. |
||||||
| frequencyObject = {}; | ||||||
|
|
||||||
| array.forEach((number) => { | ||||||
| frequencyObject[number] = (frequencyObject[number] || 0) + 1; | ||||||
| }); | ||||||
|
|
||||||
| const mode = Object.entries(frequencyObject).sort(([, a], [, b]) => b - a); | ||||||
|
|
||||||
| return mode[0][0]; | ||||||
| }; | ||||||
|
|
||||||
| const fetchWeather = (...args) => { | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. its not good to pass args like this with 2 different types of arguments.
Suggested change
|
||||||
| let url; | ||||||
|
|
||||||
| if (args.length === 1) { | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. before this, need to trim the args. |
||||||
| url = `https://api.openweathermap.org/data/2.5/weather?q=${args[0]}&appid=d408f27efbf2eb54ef2bd39871d4fe8b`; | ||||||
| } else { | ||||||
| url = `https://api.openweathermap.org/data/2.5/weather?lat=${args[0]}&lon=${args[1]}&appid=d408f27efbf2eb54ef2bd39871d4fe8b`; | ||||||
| } | ||||||
|
|
||||||
| //GET Request by Default; Includes Error Handling | ||||||
| fetch(url) | ||||||
| .then((data) => { | ||||||
| if (!data.ok) throw new Error("City not found. Please re-enter."); | ||||||
| return data.json(); | ||||||
| }) | ||||||
| .then((data) => { | ||||||
| addCurrentWeather(data); | ||||||
|
|
||||||
| const urlForecast = `https://api.openweathermap.org/data/2.5/forecast?lat=${data.coord.lat}&lon=${data.coord.lon}&appid=d408f27efbf2eb54ef2bd39871d4fe8b`; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if data changed, your code will crash |
||||||
|
|
||||||
| return fetch(urlForecast); | ||||||
| }) | ||||||
| .then((res) => res.json()) | ||||||
| .then((forecastData) => { | ||||||
| addForecast(forecastData); | ||||||
|
|
||||||
| renderWeatherData(); | ||||||
| }) | ||||||
|
|
||||||
| .catch((error) => { | ||||||
| alert(error.message); | ||||||
| }) | ||||||
| .finally(() => { | ||||||
| spinner.style.display = "none"; | ||||||
| }); | ||||||
| }; | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be extracted to a method, no need to redefine it every time you call this function