diff --git a/Server/server.py b/Server/server.py index 3e2b513..ab72409 100644 --- a/Server/server.py +++ b/Server/server.py @@ -63,7 +63,7 @@ def get_station_forecast(): } else: response = { - "status": 102, + "status": 103, "data": "Your area is not mapped for given interval. Mapping your area..." } diff --git a/index.js b/index.js index 3a3c79a..e75f614 100644 --- a/index.js +++ b/index.js @@ -6,49 +6,78 @@ document.addEventListener("DOMContentLoaded", async () => { document.getElementsByClassName("material-symbols-outlined"); }); +function showLoading() { + const loadingContainer = document.createElement("div"); + loadingContainer.classList.add("loading-container"); + + const spinner = document.createElement("div"); + spinner.classList.add("loading-spinner"); + + const message = document.createElement("div"); + message.classList.add("message"); + message.innerText = "Making request..."; + + loadingContainer.appendChild(spinner); + loadingContainer.appendChild(message); + + document.getElementById("plots").innerHTML = ""; + document.getElementById("plots").appendChild(loadingContainer); + document.getElementById("btn").setAttribute("disabled", true); +} + async function get_sdata() { try { - const position = await new Promise((resolve, reject) => { - navigator.geolocation.getCurrentPosition(resolve, reject); - }); + showLoading(); - var date = document.getElementById("date").value; - date = new Date(date).getTime(); + let status = 0; + while (status !== 101) { + const position = await new Promise((resolve, reject) => { + navigator.geolocation.getCurrentPosition(resolve, reject); + }); - const td = document.getElementById("td").value; - const pi = document.getElementById("pi").value; + var date = document.getElementById("date").value; + date = new Date(date).getTime(); - const coords = position.coords; - const apiUrl = "https://aqiapi.onrender.com/stationForecast"; + const td = document.getElementById("td").value; + const pi = document.getElementById("pi").value; - const params = new URLSearchParams(); + const coords = position.coords; + const apiUrl = "https://aqiapi.onrender.com/stationForecast"; - params.append("ts", date); - params.append("longitude", coords.longitude); - params.append("latitude", coords.latitude); - params.append("td", td); - params.append("pi", pi); - var data = await fetch(`${apiUrl}?${params.toString()}`); + const params = new URLSearchParams(); - const jsonData = await data.json(); - if (jsonData.status === 101) { - data = JSON.parse(jsonData.data); - Object.keys(data).forEach((attrib) => { - plot_df(JSON.parse(data[attrib]), attrib, pi); - }); - } else { - alert(jsonData.data); + params.append("ts", date); + params.append("longitude", coords.longitude); + params.append("latitude", coords.latitude); + params.append("td", td); + params.append("pi", pi); + var data = await fetch(`${apiUrl}?${params.toString()}`); + + const jsonData = await data.json(); + status = jsonData.status; + + if (status === 101) { + data = JSON.parse(jsonData.data); + document.getElementById("plots").innerHTML = ""; + Object.keys(data).forEach((attrib) => { + plot_df(JSON.parse(data[attrib]), attrib, pi); + }); + } else if (status === 103) { + document.querySelector(".message").innerText = "Training your model"; + } else if (status === 102) { + document.querySelector(".message").innerText = + "Downloading data and training your model"; + } + + await new Promise((resolve) => setTimeout(resolve, 3000)); } } catch (error) { console.error("Error:", error); + } finally { + document.getElementById("btn").removeAttribute("disabled"); } } -function getRandomColor() { - // Generate a random pastel color - const randomColorComponent = () => Math.floor(Math.random() * 200) + 100; - return `rgb(${randomColorComponent()}, ${randomColorComponent()}, ${randomColorComponent()})`; -} window.collapse = (ev) => { ev.parentElement.nextElementSibling.classList.toggle("collapse"); if (ev.innerText == "expand_more") { @@ -59,30 +88,41 @@ window.collapse = (ev) => { }; function plot_df(data, name, pi) { - //data = data.slice(-pi); - // Extract x and y values - const xValues = data.map((entry) => new Date(entry.ds)); - const yValues = data.map((entry) => entry.yhat); - - // Create a trace with a random pastel color - const trace = { - x: xValues, - y: yValues, - mode: "lines+markers", // Show both lines and markers + const trainingData = data.slice(0, data.length - pi); + const predictionData = data.slice(data.length - pi); + + const trainingTrace = { + x: trainingData.map((entry) => new Date(entry.ds)), + y: trainingData.map((entry) => entry.yhat), + mode: "lines+markers", type: "scatter", line: { - color: getRandomColor(), + color: "blue", width: 2, }, marker: { - color: getRandomColor(), + color: "blue", size: 8, }, - fill: "tonexty", // Fill the area below the line - fillcolor: getRandomColor(), // Random color for the filled area + name: "Training Period", + }; + + const predictionTrace = { + x: predictionData.map((entry) => new Date(entry.ds)), + y: predictionData.map((entry) => entry.yhat), + mode: "lines+markers", + type: "scatter", + line: { + color: "green", + width: 2, + }, + marker: { + color: "green", + size: 8, + }, + name: "Prediction Period", }; - // Create a layout const layout = { title: `${name} prediction for next ${pi} days`, xaxis: { @@ -91,10 +131,8 @@ function plot_df(data, name, pi) { yaxis: { title: `${name} Value`, }, - //autosize: true, }; - // Plot the chart const child = document.createElement("div"); child.id = name; const child_child = document.createElement("div"); @@ -105,5 +143,6 @@ function plot_df(data, name, pi) { `; child_child.appendChild(child); document.getElementById("plots").appendChild(child_child); - Plotly.newPlot(name, [trace], layout); + + Plotly.newPlot(name, [trainingTrace, predictionTrace], layout); } diff --git a/style.css b/style.css index 8b190fa..288b8b4 100644 --- a/style.css +++ b/style.css @@ -57,6 +57,19 @@ button:hover { background-color: rgb(25, 173, 123); } +button[disabled]::after { + display: none !important; +} + +button[disabled]:hover { + transition: none; + background-color: rgb(18, 220, 153); +} + +button[disabled] { + cursor: not-allowed; +} + #plots { margin: 20px 0; position: relative; @@ -121,4 +134,29 @@ button:hover { #user-input > div { margin-bottom: 10px; } -} \ No newline at end of file +} + +.loading-container { + text-align: center; +} + +.loading-spinner { + border: 8px solid #f3f3f3; + border-top: 8px solid #3498db; + border-radius: 50%; + width: 50px; + height: 50px; + animation: spin 1s linear infinite; + margin: 20px auto; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.message { + font-size: 18px; + font-weight: bold; + margin-top: 10px; +}