add option to cut off omicron spike

This commit is contained in:
xenofem 2022-04-16 12:42:46 -04:00
parent 46bf87f1e0
commit 58b40eb8b4
2 changed files with 127 additions and 60 deletions

View file

@ -17,6 +17,7 @@
#controls { #controls {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center;
gap: 20px 20px; gap: 20px 20px;
} }
</style> </style>
@ -33,6 +34,10 @@
End date: End date:
<input id="endDate" type="date"> <input id="endDate" type="date">
</label> </label>
<label>
Cut off Omicron spike?
<input id="cutOmicron" type="checkbox">
</label>
</div> </div>
<div class="chart"> <div class="chart">
<canvas id="northCanvas"></canvas> <canvas id="northCanvas"></canvas>

View file

@ -1,7 +1,14 @@
const hash = window.location.hash.substring(1); const startInput = document.getElementById('startDate');
const [start, end] = (hash !== "") ? hash.split(":") : ["", ""]; const endInput = document.getElementById('endDate');
document.getElementById('startDate').value = start; const cutOmicronInput = document.getElementById('cutOmicron');
document.getElementById('endDate').value = end;
const hashParams = new URLSearchParams(window.location.hash.substring(1));
startInput.value = hashParams.get('start');
endInput.value = hashParams.get('end');
cutOmicronInput.checked = hashParams.get('cutOmicron') === 'true';
const OMICRON_START_DATE = '2021-11-01';
const OMICRON_END_DATE = '2022-02-31';
fetch('/data.json') fetch('/data.json')
.then(resp => resp.json()) .then(resp => resp.json())
@ -106,12 +113,34 @@ function extractWithErrorBars(data, region) {
return extracted; return extracted;
} }
function maxExcludingOmicron(data, start, end) {
let max = 0;
let secondMax = 0;
for (const row of data) {
if (
(start !== "" && row.x < start)
|| (end != "" && row.x > end)
|| (OMICRON_START_DATE < row.x && row.x < OMICRON_END_DATE)
) {
continue;
}
if (row.yMax > max) {
secondMax = max;
max = row.yMax;
}
}
// To exclude single weird outliers
return secondMax;
}
function plot(data) { function plot(data) {
const northData = extractWithErrorBars(data, 'Northern'); const northData = extractWithErrorBars(data, 'Northern');
const southData = extractWithErrorBars(data, 'Southern'); const southData = extractWithErrorBars(data, 'Southern');
const northCtx = document.getElementById('northCanvas'); const northCtx = document.getElementById('northCanvas');
const southCtx = document.getElementById('southCanvas'); const southCtx = document.getElementById('southCanvas');
let options = {
const getOptions = (region) => {
return {
aspectRatio: 1, aspectRatio: 1,
interaction: { interaction: {
intersect: false, intersect: false,
@ -119,20 +148,21 @@ function plot(data) {
scales: { scales: {
x: { x: {
type: 'time', type: 'time',
min: document.getElementById('startDate').value, min: startInput.value,
max: document.getElementById('endDate').value, max: endInput.value,
time: { time: {
tooltipFormat: 'MMM d, yyyy', tooltipFormat: 'MMM d, yyyy',
}, },
}, },
y: { y: {
min: 0, min: 0,
max: null,
}, },
}, },
plugins: { plugins: {
title: { title: {
display: true, display: true,
text: 'Wastewater COVID RNA Signal (copies/mL)', text: region + ' System Wastewater COVID RNA Signal (copies/mL)',
}, },
}, },
elements: { elements: {
@ -141,13 +171,33 @@ function plot(data) {
}, },
}, },
}; };
};
const northOptions = getOptions("North");
const southOptions = getOptions("South");
const updateYMax = () => {
if (
cutOmicronInput.checked
&& (startInput.value === "" || startInput.value < OMICRON_END_DATE)
&& (endInput.value === "" || endInput.value > OMICRON_START_DATE)
) {
northOptions.scales.y.max = maxExcludingOmicron(northData, startInput.value, endInput.value);
southOptions.scales.y.max = maxExcludingOmicron(southData, startInput.value, endInput.value);
} else {
northOptions.scales.y.max = null;
southOptions.scales.y.max = null;
}
};
updateYMax();
const northChart = new Chart(northCtx, { const northChart = new Chart(northCtx, {
data: { data: {
datasets: [ datasets: [
{ {
type: 'scatterWithErrorBars', type: 'scatterWithErrorBars',
label: 'North System', label: 'Measured value',
data: northData, data: northData,
backgroundColor: 'rgba(0,0,255,0.5)', backgroundColor: 'rgba(0,0,255,0.5)',
color: 'rgba(0,0,255,0.5)', color: 'rgba(0,0,255,0.5)',
@ -170,7 +220,7 @@ function plot(data) {
},*/ },*/
{ {
type: 'line', type: 'line',
label: 'North System 7-day average (low)', label: '7-day average (low)',
data: smooth(northData, 'yMin', 7, average), data: smooth(northData, 'yMin', 7, average),
pointRadius: 0, pointRadius: 0,
borderColor: 'rgba(0,255,0,0.6)', borderColor: 'rgba(0,255,0,0.6)',
@ -179,7 +229,7 @@ function plot(data) {
}, },
{ {
type: 'line', type: 'line',
label: 'North System 7-day average', label: '7-day average',
data: smooth(northData, 'y', 7, average), data: smooth(northData, 'y', 7, average),
pointRadius: 0, pointRadius: 0,
borderColor: 'rgba(0,128,0,0.9)', borderColor: 'rgba(0,128,0,0.9)',
@ -187,7 +237,7 @@ function plot(data) {
}, },
{ {
type: 'line', type: 'line',
label: 'North System 7-day average (high)', label: '7-day average (high)',
data: smooth(northData, 'yMax', 7, average), data: smooth(northData, 'yMax', 7, average),
pointRadius: 0, pointRadius: 0,
borderColor: 'rgba(0,255,0,0.6)', borderColor: 'rgba(0,255,0,0.6)',
@ -196,14 +246,14 @@ function plot(data) {
}, },
] ]
}, },
options, options: northOptions,
}); });
const southChart = new Chart(southCtx, { const southChart = new Chart(southCtx, {
data: { data: {
datasets: [ datasets: [
{ {
type: 'scatterWithErrorBars', type: 'scatterWithErrorBars',
label: 'South System', label: 'Measured value',
data: southData, data: southData,
backgroundColor: 'rgba(255,0,0,0.5)', backgroundColor: 'rgba(255,0,0,0.5)',
color: 'rgba(255,0,0,0.5)', color: 'rgba(255,0,0,0.5)',
@ -212,7 +262,7 @@ function plot(data) {
}, },
{ {
type: 'line', type: 'line',
label: 'South System 7-day average (low)', label: '7-day average (low)',
data: smooth(southData, 'yMin', 7, average), data: smooth(southData, 'yMin', 7, average),
pointRadius: 0, pointRadius: 0,
borderColor: 'rgba(255,127,0,0.6)', borderColor: 'rgba(255,127,0,0.6)',
@ -221,7 +271,7 @@ function plot(data) {
}, },
{ {
type: 'line', type: 'line',
label: 'South System 7-day average', label: '7-day average',
data: smooth(southData, 'y', 7, average), data: smooth(southData, 'y', 7, average),
pointRadius: 0, pointRadius: 0,
borderColor: 'rgba(127,63,0,0.9)', borderColor: 'rgba(127,63,0,0.9)',
@ -229,7 +279,7 @@ function plot(data) {
}, },
{ {
type: 'line', type: 'line',
label: 'South System 7-day average (high)', label: '7-day average (high)',
data: smooth(southData, 'yMax', 7, average), data: smooth(southData, 'yMax', 7, average),
pointRadius: 0, pointRadius: 0,
borderColor: 'rgba(255,127,0,0.6)', borderColor: 'rgba(255,127,0,0.6)',
@ -238,28 +288,40 @@ function plot(data) {
}, },
], ],
}, },
options, options: southOptions,
}); });
document.getElementById('startDate').addEventListener('input', (e) => {
options.scales.x.min = e.target.value;
northChart.update();
southChart.update();
updateHash();
});
document.getElementById('endDate').addEventListener('input', (e) => {
options.scales.x.max = e.target.value;
northChart.update();
southChart.update();
updateHash();
});
}
function updateHash() { const update = () => {
const start = document.getElementById('startDate').value; updateYMax();
const end = document.getElementById('endDate').value; northChart.update();
if (start !== "" || end !== "") { southChart.update();
window.location.hash = start + ":" + end;
} else { const params = new URLSearchParams();
window.location.hash = ""; const start = startInput.value;
if (start !== '') {
params.set('start', start);
} }
const end = endInput.value;
if (end !== '') {
params.set('end', end);
}
if (cutOmicronInput.checked) {
params.set('cutOmicron', 'true');
}
window.location.hash = params.toString();
};
startInput.addEventListener('input', (e) => {
northOptions.scales.x.min = e.target.value;
southOptions.scales.x.min = e.target.value;
update();
});
endInput.addEventListener('input', (e) => {
northOptions.scales.x.max = e.target.value;
southOptions.scales.x.max = e.target.value;
update();
});
cutOmicronInput.addEventListener('change', (e) => {
update();
});
} }