/
index.html
575 lines (509 loc) · 24.7 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Coronavirus Data and Weather</title>
<link href="https://unpkg.com/material-components-web@v4.0.0/dist/material-components-web.min.css" rel="stylesheet">
<script src="https://unpkg.com/material-components-web@v4.0.0/dist/material-components-web.min.js"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="manifest" href="./site.webmanifest">
<style>
:root {
--mdc-theme-primary: navy;
}
body {
margin: 0;
background: #afddf2;
}
main {margin-left: 8px;}
.view {
display: none;
}
.template {
display: none;
}
h1#instructionsForLineChart {
display: none;
}
</style>
</head>
<body class="mdc-typography">
<!--we will put the cards in the container-->
<div class="container"></div>
<aside class="mdc-drawer mdc-drawer--modal">
<div class="mdc-drawer__content">
<nav class="mdc-list">
<a class="mdc-list-item mdc-list-item--activated" href="#home" aria-current="page">
<i class="material-icons mdc-list-item__graphic" aria-hidden="true">home</i>
<span class="mdc-list-item__text">Home</span>
</a>
<a class="mdc-list-item" href="#search" aria-current="page">
<i class="material-icons mdc-list-item__graphic" aria-hidden="true">search</i>
<span class="mdc-list-item__text">Search</span>
</a>
<a class="mdc-list-item" href="#table">
<i class="material-icons mdc-list-item__graphic" aria-hidden="true">table_chart</i>
<span class="mdc-list-item__text">Table</span>
</a>
<a class="mdc-list-item" href="#chart">
<i class="material-icons mdc-list-item__graphic" aria-hidden="true">show_chart</i>
<span class="mdc-list-item__text">Line Chart</span>
</a>
<a class="mdc-list-item" href="#history">
<i class="material-icons mdc-list-item__graphic" aria-hidden="true">history</i>
<span class="mdc-list-item__text">History</span>
</a>
<a class="mdc-list-item" href="#weather">
<i class="material-icons mdc-list-item__graphic" aria-hidden="true">wb_sunny</i>
<span class="mdc-list-item__text">Weather</span>
</a>
</nav>
</div>
</aside>
<div class="mdc-drawer-scrim"></div>
<header class="mdc-top-app-bar">
<div class="mdc-top-app-bar__row">
<section class="mdc-top-app-bar__section mdc-top-app-bar__section--align-start">
<button class="material-icons mdc-top-app-bar__navigation-icon mdc-icon-button">menu</button>
<span class="mdc-top-app-bar__title">Coronavirus Data And Weather</span>
</section>
</div>
</header>
<main class="mdc-top-app-bar--fixed-adjust">
<div class="view" id="home"><h1>Home</h1>
<p>This project visualizes coronavirus data and gives the user weather at their current location.
<br>This is a progressive web application. It uses Dexie.js and geolocation.
<br> The data comes from https://pomber.github.io/covid19/timeseries.json
and https://api.openweathermap.org/data/2.5/weather
<br>
</p>
<h1>To get the charts to show up, request the information you want from the search page.
You can access that through the three line icon on the top left.</h1>
</div>
<div class="view" id="search">
<h1>Search</h1>
<h3>Pick different countries to graph from the datalist below. Click the Add Country button to add the country to the list of countries that will be graphed.<br>
<br> You can choose as many countries as you want. <br>
You should also choose whether you want to display confirmed, death or recovered cases for each country you choose.
<br>The last case that you choose is the one that will be plotted.
<br>When your done, click the plot button. After plotting, please make sure to hit reset or add a country again.
<br>Feel free to reset when you want.</h3>
<br>
<label for="country">Click the textbox to the right (or below) and choose the countries you want to see from this list:</label>
<input list="countryList" name="country" id="theCountryList"/>
<datalist id="countryList">
</datalist>
<button id="submitCountry" type="submit" class="mdc-button foo-button mdc-button--raised">
<div class="mdc-button__ripple"></div>
<span class="mdc-button__label">Add Country</span>
</button>
<h2 id="listOfCountries">List of countries you are going to plot: </h2>
<label for="case">Click the textbox to the right (or below) and choose which case (confirmed, deaths, or recovered) you want to see from this list:</label>
<input list="whichCase" name="case" id="case"/>
<datalist id="whichCase">
<option value="confirmed">
<option value="deaths">
<option value="recovered">
</datalist>
<button id="submitCase" type="submit" class="mdc-button foo-button mdc-button--raised">
<div class="mdc-button__ripple"></div>
<span class="mdc-button__label">Confirm Case</span>
</button>
<h2 id="userChosenCase">You chose: </h2>
<br><br>
<button id="plotThings" class="mdc-button foo-button mdc-button--raised">
<div class="mdc-button__ripple"></div>
<span class="mdc-button__label">Plot</span>
</button>
<button id="buttonReset" class="mdc-button foo-button mdc-button--raised">
<div class="mdc-button__ripple"></div>
<span class="mdc-button__label">Reset</span>
</button>
<p id="safeToPlot"></p>
</div>
<div class="view" id="table">
<h1 id="tableHeader">Table Of Confirmed Cases</h1>
<h4 id="waitForDataTable">This page will populate when you choose countries and plot them from the search tab</h4>
<div id="table_div"></div>
</div>
<div class="view" id="chart">
<h1>Line Chart</h1>
<h4 id="waitForDataLine">This page will populate when you choose countries and plot them from the search tab</h4>
<h1 id="instructionsForLineChart">Mouse over the line(s) of the chart to see the data</h1>
<div id="curve_chart" style="width: 900px; height: 500px"></div>
</div>
<div class="view" id="history">
<h1>History</h1>
This is the history of the last 10 countries you have plotted (not added to the list to plot) so far. If you haven't plotted anything, go to the search tab for more details.
<div id="historyCardDisplay"></div>
<p id="historyDisplay"></p>
</div>
<div class="view" id="weather"><h1>Weather</h1>
<h4>This page shows the weather at your current location if you allow it, otherwise it will tell you it cannot retrieve the weather</h4>
<br>
<button id="getLocation" class="mdc-button foo-button mdc-button--raised">
<div class="mdc-button__ripple"></div>
<span class="mdc-button__label">Get Current Weather Near You</span>
</button>
<p id="weatherInfo"></p>
<!-- Card that will be used to display the weather -->
<div class="mdc-card template demo-card">
<div class="mdc-card__primary-action demo-card__primary-action" tabindex="0">
<div class="demo-card__primary">
<h2 class="demo-card__title mdc-typography mdc-typography--headline6">Our Changing Planet</h2>
<h3 class="demo-card__subtitle mdc-typography mdc-typography--subtitle2" id="firstSubheading">by Kurt Wagner</h3>
<h3 class="demo-card__subtitle mdc-typography mdc-typography--subtitle2" id="secondSubheading">by Kurt Wagner</h3>
</div>
<div class="demo-card__secondary mdc-typography mdc-typography--body2">Visit ten places on our planet that are undergoing the biggest changes today.</div>
</div>
</div>
<!-- Card that will be used to display the weather -->
</div>
</main>
<script src="https://unpkg.com/dexie@latest/dist/dexie.js"></script>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
if ('serviceWorker' in navigator) {
//wait for load
window.addEventListener('load', () => {
navigator.serviceWorker.register('./serviceWorker.js').then((registration) => {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, (err) => {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
//load google charts package
google.charts.load('current', {'packages':['corechart', 'table']});
let totalRecovered = 0;
let countryNames = [];
let specificData = {};
let userPlotted = false;
let stateNames = [];
//needed to check whether it is safe to plot or not
let userAddedCountry = false;
let userSpecifiedCase = false;
//-------------------------------------------------------------
//dexie database
const db = new Dexie('BigProject');
db.version(1).stores({
previousCountries: '++id, country, date, typeOfCase'
});
//-------------------------------------------------------------
getData = () => {
//first api
fetch("https://pomber.github.io/covid19/timeseries.json")
.then(response => response.json())
.then(data => {
specificData = data;
let dataList = document.querySelector("#countryList");
for(const country in data) {
//gets the names of every country
countryNames.push(country);
//for the option menu showing all of the different countries
let option = document.createElement("option");
option.textContent = country;
option.value = country;
dataList.appendChild(option);
}
});
}
//adds the country to a list that will be used to display later
//the users data is contained in whichCountry
let whichCountry = document.querySelector("#theCountryList");
//the selection of which country they want to plot
let userSelection = [];
document.querySelector("#submitCountry")
.addEventListener("click", (event) =>
{
//clears everything if the user had already plotted
if(userPlotted) {
userSelection = [];
realData = [];
specificRow = [];
chart.clearChart();
table.clearChart();
document.querySelector("p#safeToPlot").textContent = "";
document.querySelector("h2#userChosenCase").textContent = "You chose: ";
document.querySelector("h2#listOfCountries").textContent = "List of countries you are going to plot: ";
//document.querySelector("p#historyDisplay").textContent = "";
}
userAddedCountry = true;
userSelection.push(whichCountry.value);
let paragraph = document.querySelector("#listOfCountries");
if(userPlotted) {
userPlotted = false;
}
paragraph.textContent += whichCountry.value + ", ";
//the value where the user choses the country is set to blank
whichCountry.value = "";
});
//adds the case chosen by the user to a list that will be used to display later
//the users data is contained in userCase
//this will be cleared so I need a variable to store the value before clearing it
let userCase = document.querySelector("input#case");
//stores the user case value before clearing
let userChosenCase = userCase.value;
document.querySelector("button#submitCase")
.addEventListener("click", (event) => {
userSpecifiedCase = true;
userChosenCase = userCase.value;
let paragraph = document.querySelector("#userChosenCase");
paragraph.textContent = paragraph.textContent + " " + userCase.value;
//the value where the user choses the case is set to blank
userCase.value = "";
});
//let totalDataPoints = 0;
let maxDataPoints = 0;
//console.log(specificData["US"]);
//what will be plotted in the chart
let realData = [];
let count = 0;
//specific row of the chart or table
let specificRow = [];
let alreadyPushed = false;
let date = "";
//used to display on the history page
let plotNumber = 1;
document.querySelector("#plotThings")
.addEventListener("click", (event) =>
{
console.log(userSpecifiedCase);
console.log(userAddedCountry);
//both have to be true, otherwise it is unsafe to plot
if(!(userSpecifiedCase && userAddedCountry)) {
document.querySelector("p#safeToPlot").textContent = "It is not safe to plot at this time. Please make sure that you have specified at least one country and which case (confirmed, deaths, or recovered) you want to display (by clicking those buttons to the right of the textbox) before trying to plot.";
}
else {
//need to reset it again
userSpecifiedCase = false;
userAddedCountry = false;
userPlotted = true;
//populates the first row with the date and the country
specificRow.push("Date");
document.querySelector("h4#waitForDataTable").textContent = "";
document.querySelector("h4#waitForDataLine").textContent = "";
for(const myCountry of userSelection) {
specificRow.push(myCountry);
//adds it to the database
db.previousCountries.add({
country: myCountry,
date: new Date(),
typeOfCase: userChosenCase
});
}
db.previousCountries.toArray().then( (arr) => {
let count = 0;
let historyDisplay = document.querySelector("div#historyCardDisplay");
//will always update so that data is accurate
//RESETS THE CARD DISPLAY
historyDisplay.textContent = "";
//goes from most recent to least recent
//starts at arr.length - 1 because arr.length does not exist
for(let index = arr.length - 1; index >= 0; index--) {
//plots only 10 entries, since there could be more
if(count === 10) {
break;
}
else {
let clone = document.querySelector("div.template").cloneNode(true);
clone.classList.remove("template");
clone.querySelector("h2").textContent = "Country Name: " + arr[index].country;
clone.querySelector("h3#firstSubheading").textContent = "The case type you chose was: " + arr[index].typeOfCase;
clone.querySelector("h3#secondSubheading").textContent = "Date Plotted: " + arr[index].date;
clone.querySelector(".mdc-typography--body2").textContent = "ID Number: " + arr[index].id;
document.querySelector("div#historyCardDisplay").appendChild(clone);
}
count++;
}
});
realData.push(specificRow);
//The json contains the number of Coronavirus confirmed cases, deaths, and recovered cases for every country and every day since 2020-1-22:
//I just choosed the US because that's what I was testing before
for(let index = 0; index < specificData["US"].length; index++) {
//this is done so that there isn't one big row but multiple small rows for each day
specificRow = [];
for(const country of userSelection) {
//I only want to push the date once per row
if(alreadyPushed === false) {
date = specificData[country][index].date;
specificRow.push(date);
alreadyPushed = true;
}
let numCases = specificData[country][index][userChosenCase];
specificRow.push(numCases);
}
//pushes the row into the table of data I will use to plot the graph and the table
realData.push(specificRow);
alreadyPushed = false;
}
//console.log(realData);
let paragraph = document.querySelector("#listOfCountries");
paragraph.textContent += "When you add countries after plotting, the table and chart will reset.";
document.querySelector("#search").style.display = "none";
document.querySelector("#table").style.display = "block";
//shows the right tab in the hamburger
// document.querySelector("[href='#search']").classList.remove("mdc-list-item--activated");
// document.querySelector("[href='#table']").classList.add("mdc-list-item--activated");
document.querySelectorAll("a.mdc-list-item").forEach(item => {
item.classList.remove("mdc-list-item--activated");
});
document.querySelector("[href='#table']").classList.add("mdc-list-item--activated");
if(navigator.onLine === true) {
drawTable(realData);
drawChart(realData);
}
else {
//DELETES ALL SUB ELEMENTS
document.querySelector("div#table").textContent = "cannot create table offline";
document.querySelector("div#chart").textContent = "cannot create line chart offline";
}
}
});
document.querySelector("button#buttonReset")
.addEventListener("click", (event) => {
//clears everything if the user had already plotted
if(userPlotted) {
userSelection = [];
realData = [];
specificRow = [];
chart.clearChart();
table.clearChart();
userSpecifiedCase = false;
userAddedCountry = false;
document.querySelector("p#safeToPlot").textContent = "";
document.querySelector("h2#userChosenCase").textContent = "You chose: ";
document.querySelector("h2#listOfCountries").textContent = "List of countries you are going to plot: ";
//document.querySelector("p#historyDisplay").textContent = "";
}
//almost the same except I don't access the charts because those were not created
else {
userSelection = [];
realData = [];
specificRow = [];
userSpecifiedCase = false;
userAddedCountry = false;
document.querySelector("p#safeToPlot").textContent = "";
document.querySelector("h2#userChosenCase").textContent = "You chose: ";
document.querySelector("h2#listOfCountries").textContent = "List of countries you are going to plot: ";
}
});
//-------------------------------------------------------------
//creates the line chart
let chart;
drawChart = (dataArray) => {
//convert into a google charts table
//the first row needs to be what each column represents, otherwise the array would not work
let data = new google.visualization.arrayToDataTable(dataArray);
let myTitle = 'COVID-19 ' + userChosenCase + ' Cases';
let options = {
title: myTitle,
curveType: 'function',
legend: { position: 'bottom' },
};
chart = new google.visualization.LineChart(document.querySelector('#curve_chart'));
chart.draw(data, options);
document.querySelector("h1#instructionsForLineChart").style.display = "block";
}
//-------------------------------------------------------------
//-------------------------------------------------------------
//creates the table
let table;
drawTable = (dataArray) => {
//I am going to reverse the array because it doesn't seem to like my numbers when sorting
let firstPart = dataArray.slice(0, 1);
let reversed = dataArray.slice(1, dataArray.length).reverse();
for(let i = firstPart.length - 1; i >=0; i--) {
reversed.unshift(firstPart[i]);
}
let data = new google.visualization.arrayToDataTable(reversed);
table = new google.visualization.Table(document.getElementById('table_div'));
//gets the first row
//sort data descending based on the first column
table.draw(data, {width: '50%', height: '50%'});
document.querySelector("h1#tableHeader").textContent = "Table of " + userChosenCase + " Cases";
}
//-------------------------------------------------------------
google.charts.setOnLoadCallback(getData);
//---------------------------------------------------
//weather
const weatherInfo = document.querySelector("p#weatherInfo");
//handles logic with the weather screen
document.querySelector("button#getLocation").addEventListener("click", (event) => {
//cannot fetch while offline
if(navigator.onLine === false) {
weatherInfo.textContent = "Cannot retrieve data when offline. Please try again when your online.";
}
//user is online
else {
//the browser does not support geolocation
if (!navigator.geolocation) {
weatherInfo.textContent = 'Geolocation is not supported by your browser';
}
else {
weatherInfo.textContent = 'Locating…';
//there are two different callback functions, one is by default, the other one is optional where I want to handle the error
navigator.geolocation.getCurrentPosition(success, error) ;
}
}
});
//the getCurrentPosition function gives back access to the location data
success = (position) => {
let lat = position.coords.latitude;
let lng = position.coords.longitude;
//has my key and the right units, longitude and latitude
let weatherApi = "https://api.openweathermap.org/data/2.5/weather?appid=f4969555fb49e9a8e0c6d4f181a2a3e0&units=imperial" + "&lat=" + lat + "&lon=" + lng;;
fetch(weatherApi)
.then(response => response.json())
.then(data => {
weatherInfo.textContent = 'Success';
console.log(data);
let clone = document.querySelector("div.template").cloneNode(true);
clone.classList.remove("template");
clone.querySelector("h2").textContent = "Local Weather Report For " + data.name;
clone.querySelector("h3#firstSubheading").textContent = "Found at lat: " + lat + " and long: " + lng;
clone.querySelector("h3#secondSubheading").textContent = "The temperature currently in " + data.name + " is " + data.main.temp + " degrees fahrenheit, but it feels like " + data.main.feels_like + " degrees fahrenheit.";
clone.querySelector(".mdc-typography--body2").textContent = "Wind speeds are at " + data.wind.speed + " miles per hour " + "and cloudiness is at " + data.clouds.all + "%"
document.querySelector("div#weather").appendChild(clone);
})
}
error = (err) => {
weatherInfo.textContent = "Unable to retrieve your location because either you blocked access or you exited the dialog box";
}
//---------------------------------------------------
//-------------------------------------------------------------
//this has to do with the app shell logic itself
//mdc.ripple.MDCRipple.attachTo(document.querySelector('.foo-button'));
mdc.topAppBar.MDCTopAppBar.attachTo(document.querySelector('header.mdc-top-app-bar'));
const drawer = mdc.drawer.MDCDrawer.attachTo(document.querySelector('.mdc-drawer'));
// document.querySelector("div#curve_chart").style.display = "none";
const hideViews = () => {
document.querySelectorAll("div.view").forEach(item => {
item.style.display = "none";
})
};
document.querySelector(".mdc-top-app-bar__navigation-icon")
.addEventListener("click",
(e) => {drawer.open = true;}
);
document.querySelectorAll("aside.mdc-drawer a.mdc-list-item").forEach(item => {
item.addEventListener('click', event => {
hideViews();
document.querySelectorAll("a.mdc-list-item").forEach(item => {
item.classList.remove("mdc-list-item--activated");
});
//could also use event.toElement.id if the specific tag has an id
//target is an id selector with the hash symbol
let target = item.getAttribute("href");
document.querySelector(target).style.display = "block";
document.querySelector(target).classList.add("mdc-list-item--activated");
drawer.open = false;
})
})
document.querySelector("div#home").style.display = "block";
</script>
</body>
</html>