COVID19 Cases by Country
var latestDate = new moment();
var processed;
var myChart;
var toShow = [];
var lineColorCounter = 0;
var baselineColorCounter = 0;
var outbreak = true;
var baslineColors = [
'rgba(234, 20, 31, 0.25)',
'rgba(0, 168, 255, 0.25)',
'rgba(112, 173, 71, 1)',
'rgba(112, 173, 71, 1)',
'rgba(112, 173, 71, 1)',
'rgba(112, 173, 71, 1)',
'rgba(112, 173, 71, 1)',
'rgba(112, 173, 71, 1)'
];
function coreGenLables(t) {
var e = t.data.datasets,
i = t.options.legend || {},
n = i.labels && i.labels.usePointStyle;
return t._getSortedDatasetMetas().map((function(t) {
var i = t.controller.getStyle(n ? 0 : void 0);
return {
text: e[t.index].label,
fillStyle: i.backgroundColor,
hidden: !t.visible,
lineCap: i.borderCapStyle,
lineDash: i.borderDash,
lineDashOffset: i.borderDashOffset,
lineJoin: i.borderJoinStyle,
lineWidth: 8,
strokeStyle: i.borderColor,
pointStyle: i.pointStyle,
rotation: i.rotation,
datasetIndex: t.index
}
}), this)
}
function drawString(ctx, text, posX, posY, textColor, rotation, font, fontSize) {
var lines = text.split("\n");
if (!rotation) rotation = 0;
if (!font) font = "'serif'";
if (!fontSize) fontSize = 16;
if (!textColor) textColor = '#000000';
ctx.save();
ctx.font = fontSize + "px " + font;
ctx.fillStyle = textColor;
ctx.translate(posX, posY);
ctx.rotate(rotation * Math.PI / 180);
for (i = 0; i min = min max = max > indx ? max : indx);
return max;
}
function shiftKeys(a, n) {
var na = [];
a.forEach((current, indx) => {
na[indx + n] = current;
});
return na;
}
function refreshChart() {
toShow = [$("#countrylist").chosen().val()];
lineColorCounter = 0;
baselineColorCounter = 0;
myChart.data.datasets = createDatasetObject();
myChart.update({
duration: 800,
easing: 'easeInSine'
});
}
function rawToObjects(data) {
var fromDate = new moment([2020, 0, 22]);
latestDate = new moment(data['China'][data['China'].length - 1]['date'], "YYYY-MM-DD");
var r = {};
for (const key in data) {
if (!r.hasOwnProperty(key)) {
r[key] = {
positive: [],
death: [],
}
}
data[key].forEach(d => {
var dataDate = new moment(String(d["date"]), "YYYYMMDD");
var index = dataDate.diff(fromDate, 'days');
if (typeof d["confirmed"] == "number")
r[key]["positive"][index] = d["confirmed"];
if (typeof d["deaths"] == "number")
r[key]["death"][index] = d["deaths"];
});
};
for (var i in r) {
var e = r[i];
for (var j = 0; j < e["positive"].length; j++) {
if (e["positive"][j] === 0 && (j == 0 || typeof e["positive"][j - 1] == 'undefined')) {
delete e["positive"][j];
}
}
for (var j = 0; j < e["death"].length; j++) {
if (e["death"][j] === 0 && (j == 0 || typeof e["death"][j - 1] == 'undefined')) {
delete e["death"][j];
}
}
}
return r;
}
$(document).ready(function() {
$('head').append('');
$('head').append('');
$('#chartholder').append('');
$('#controlholder').append('');
$('#controlholder').append('');
$('#controlholder').css("display", "inline-block");
$('#chartholder').css("display", "inline-block");
$("#get_PNG").on('click', function(e) {
downloadCanvas($("#chartCanvas").get(0), "chart.png")
});
$('#controlholder').append('');
$("#get_csv").on('click', function(e) {
downloadCSV()
});
$("#countrylist").chosen().change(refreshChart);
fetch("https://pomber.github.io/covid19/timeseries.json")
.then(response => response.json())
.then(rawToObjects)
.then(processData)
});
var lineColors = [
'rgba(68, 114, 196, 1)',
'rgba(255, 192, 0, 1)',
'rgba(0, 63, 105, 1)',
'rgba(234, 20, 31, 1)',
'rgba(132, 60, 12, 1)',
'rgba(112, 173, 71, 1)',
'rgba(112, 173, 71, 1)',
'rgba(112, 173, 71, 1)',
'rgba(112, 173, 71, 1)',
'rgba(112, 173, 71, 1)',
'rgba(112, 173, 71, 1)',
'rgba(112, 173, 71, 1)'
];
function filterData(data) {
var filtered = {};
for (const key in data) {
var e = data[key];
if (e["death"][(e["death"].length - 1)] > 5) {
filtered[key] = {
width: 8,
death: e["death"],
positive: e["positive"],
dcalc: [],
mid: [],
hoped: [],
feared: []
};
}
}
return filtered;
}
function padDataBack(data) {
for (const key in data) {
var e = data[key];
var min = 20 - aMin(e["death"]);
e["death"] = shiftKeys(e["death"], min);
e["positive"] = shiftKeys(e["positive"], min);
}
}
function createDatasetObject() {
var set = [];
toShow.forEach(x => {
set.push(generateBase(x, "positive", " Reported"));
set.push(generateBase(x, "dcalc", " Calculated"));
set.push(generateBase(x, "death", " Deaths"));
set.push(generateBase(x, "feared", " Forecasted Feared"));
set.push(generateBase(x, "mid", " Forecasted Mid"));
set.push(generateBase(x, "hoped", " Forecasted Hoped"));
})
set.push({
label: 'Outbreak Threshold Risk',
fill: false,
borderWidth: processed['Outbreak Threshold Risk']["width"],
borderColor: 'rgba(0, 168, 255, 0.25)',
backgroundColor: 'rgba(0, 168, 255, 0.25)',
pointHitRadius: 0,
pointRadius: 0,
yAxisID: "yaxis",
xAxisID: "xaxis",
pointBackgroundColor: 'rgba(0,0,0,0)',
pointBorderColor: 'rgba(0,0,0,0)',
data: processed['Outbreak Threshold Risk']["points"],
})
return set;
}
var hoped = [90,80,95,90,80,70,90,85,70,60,65,55,60,50,55,45,50,40,45,];
var feared = [133,125,120,115,112,108,105,102,101,101,101,101,101,101,100,100,100,100,100,];
var mid = [100,100,100,100,100,90,80,95,90,80,70,90,85,70,60,65,55,60,50,];
function processData(data) {
var filt = filterData(data);
padDataBack(filt);
for (const key in filt) {
var e = filt[key];
var d = e["death"].length;
var t = d - 20;
e["dcalc"] = [];
for (var i = 0; i 0; i--) {
if(e["dcalc"][t - i - 1])
droc += (e["dcalc"][t - i] - e["dcalc"][t - i - 1])/e["dcalc"][t - i - 1];
}
droc = droc / 7;
e["mid"][t - 1] = e["dcalc"][t - 1];
e["hoped"][t - 1] = e["dcalc"][t - 1];
e["feared"][t - 1] = e["dcalc"][t - 1];
for (var i = 0; i < 20; i++) {
e["mid"][t + i] = Math.round(e["mid"][t - 1 + i] * (1+droc* (mid[i] / 100)));
e["hoped"][t + i] = Math.round(e["hoped"][t - 1 + i] * (1+droc* (hoped[i] / 100)));
e["feared"][t + i] = Math.round(e["feared"][t - 1 + i] * (1+droc* (feared[i] / 100)));
}
for (var i = 0; i < 250; i++) {
if (typeof e["mid"][i] == 'number' && e["mid"][i] < 1)
e["mid"][i] = 0;
if (typeof e["hoped"][i] == 'number' && e["hoped"][i] < 1)
e["hoped"][i] = 0;
if (typeof e["feared"][i] == 'number' && e["feared"][i] < 1)
e["feared"][i] = 0;
if (typeof e["positive"][i] == 'number' && e["positive"][i] < 1)
e["positive"][i] = 0;
if (typeof e["dcalc"][i] == 'number' && e["dcalc"][i] < 1)
e["dcalc"][i] = 0;
}
/*
//setup first 7 values
for (var i = d - 27; i < d - 20; i++) {
e["mid"][i] = e["dcalc"][i];
e["hoped"][i] = e["dcalc"][i];
e["feared"][i] = e["dcalc"][i];
}
for (var i = 0; i < 20; i++) {
var mavg = 0;
var havg = 0;
var favg = 0;
for (var j = 1; j < 7; j++) {
mavg += e["mid"][d - 20 + i - j] - e["mid"][d - 21 + i - j];
havg += e["hoped"][d - 20 + i - j] - e["hoped"][d - 21 + i - j];
favg += e["feared"][d - 20 + i - j] - e["feared"][d - 21 + i - j];
}
e["mid"][d - 20 + i] = e["mid"][d - 21 + i] + ((mid[i] / 100) * (mavg / 7));
e["hoped"][d - 20 + i] = e["hoped"][d - 21 + i] + ((hoped[i] / 100) * (havg / 7));
e["feared"][d - 20 + i] = e["feared"][d - 21 + i] + ((feared[i] / 100) * (favg / 7));
}
//remove seed values
for (var i = d - 27; i < d - 21; i++) {
delete e["mid"][i];
delete e["hoped"][i];
delete e["feared"][i];
}
*/
}
displayData(filt);
}
function trimArray(a) {
var m = a.length;
for (var i = 0; i 0; i--) {
if (a[i] === null) {
delete a[i];
} else {
break;
}
}
}
function displayData(data) {
toShow = ['China'];
processed = {};
var maxLength = 0;
for (const key in data) {
var e = data[key];
processed[key] = e;
e["positive"].forEach((c, i) => maxLength = i > maxLength ? i : maxLength);
}
fullCSV = "Location,Datapoint";
for (var i = 0; i < maxLength+1; i++) {
fullCSV += ","+i;
}
fullCSV += "\n";
for (const key in data) {
var e = data[key];
var sdcalc = key + ",calc,";
var spositive = key + ",posi,";
var sdeath = key + ",death,";
var sfeared = key + ",fear,";
var smid = key + ",mid,";
var shoped = key + ",hope,"
for (var i = 0; i {
var opt = $('').val(newKey).text(newKey);
if (toShow.includes(newKey)) {
opt.prop('selected', true);
}
$('#countrylist').append(opt);
});
$('#countrylist').trigger("chosen:updated");
processed["labels"] = [...Array(maxLength + 2).keys()];
processed['Outbreak Threshold Risk'] = {
points: Array.from(processed["labels"]).fill(800),
baseColor: true,
width: 70
};
var ctx = document.getElementById('chartCanvas').getContext('2d');
var yticks = [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000];
myChart = new Chart(ctx, {
type: 'line',
responsive: false,
data: {
labels: processed["labels"],
datasets: createDatasetObject()
},
options: {
aspectRatio: 1000 / 800,
title: {
display: true,
text: ["Confirmed (Tested) Cases vs. Calculated Actual Infected", "(Logarithmic Scale)", ""]
},
legend: {
position: 'bottom',
labels: {
boxWidth: 12,
fontSize: 14,
generateLabels: coreGenLables
},
},
chartArea: {
backgroundColor: 'rgba(235, 235, 235, 1)'
},
scales: {
xaxis: {
gridLines: {
display: false
},
scaleLabel: {
display: true,
labelString: "Days Since First Calculated Infection (Day 20 is First Confirmed Death)",
fontSize: 30
},
ticks: {
beginAtZero: true,
maxRotation: 0,
minRotation: 0,
autoSkip: false,
},
afterFit: function(s) {
s.ticks.forEach(t=>{if(t.value > 0 && t.value % 10 != 0) {t.label="";}});
},
},
yaxis: {
type: "logarithmic",
scaleLabel: {
display: true,
labelString: "Cumulative Total",
fontSize: 32
},
ticks: {
autoSkip: false,
min: yticks[0],
max: yticks[yticks.length - 1],
},
beforeBuildTicks: function(s) {
s.min = yticks[0];
s.max = yticks[yticks.length - 1];
},
afterBuildTicks: function(s) {
s.ticks = s.ticks.filter(t => yticks.includes(t.value));
}
}
}
}
});
$(".main-container").width("100%");
$("#page").width("100%");
$("#page").css("maxWidth", "100%");
}
Built with:
jQuery | Chosen | ChartJS | JSon transform of CSSEGISandData/COVID-19 | CSSEGISandData/COVID-19