add option to cut off omicron spike
This commit is contained in:
parent
46bf87f1e0
commit
58b40eb8b4
|
@ -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>
|
||||||
|
|
|
@ -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();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue