Merge branch 'master' of github.com:khantey1998/nrml-dashboard
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
let trendChart;
|
||||
let influenzaSubtypeChart;
|
||||
let covidDistributedByAgeChart;
|
||||
let covidLineageFrequencyChart;
|
||||
let influenzaSubtypeFrequencyChart;
|
||||
let map;
|
||||
|
||||
/*
|
||||
@@ -30,7 +34,7 @@ function loadSummary() {
|
||||
<div class="d-flex justify-content-between">
|
||||
|
||||
<div>
|
||||
<h6 class="fw-bold">${item.code}</h6>
|
||||
<h6 class="fw-bold">${item.code} Cases</h6>
|
||||
<h3 class="mb-1">${item.current_total}</h3>
|
||||
<small class="text-muted">Last 7 days</small>
|
||||
</div>
|
||||
@@ -102,12 +106,15 @@ function loadTrend(periodType, startYear, startWeek, endYear, endWeek) {
|
||||
const colors = {
|
||||
SARI: '#2563eb',
|
||||
ILI: '#10b981',
|
||||
LBM: '#9333ea'
|
||||
LBM: '#9333ea',
|
||||
AFI: '#fc5741',
|
||||
NDS: '#d59d01',
|
||||
SEQ: '#9ca3af'
|
||||
};
|
||||
|
||||
const datasets = [];
|
||||
|
||||
const allowedPrograms = ['SARI', 'ILI', 'LBM'];
|
||||
const allowedPrograms = ['SARI', 'ILI', 'LBM', 'AFI', 'NDS', 'SEQ'];
|
||||
|
||||
Object.keys(data).forEach(code => {
|
||||
|
||||
@@ -145,7 +152,10 @@ function loadTrend(periodType, startYear, startWeek, endYear, endWeek) {
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: { position: 'bottom' }
|
||||
legend: { position: 'bottom' },
|
||||
datalabels: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
@@ -154,15 +164,499 @@ function loadTrend(periodType, startYear, startWeek, endYear, endWeek) {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: { stepSize: 1 }
|
||||
ticks: {
|
||||
stepSize: 1
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Number of Cases'
|
||||
},
|
||||
},
|
||||
x: { grid: { display: false } }
|
||||
x: {
|
||||
grid: {
|
||||
display: false
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Surveillance'
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function loadInfluenzaSubtypeDistribution(periodType, startYear, startWeek, endYear, endWeek) {
|
||||
|
||||
fetch(`/api/dashboard/influenza-subtype-distribution?period_type=${periodType}&start_year=${startYear}&start_week=${startWeek}&end_year=${endYear}&end_week=${endWeek}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
|
||||
let displayLabels = data.map(item => item.subtype);
|
||||
let dataset = data.map(item => item.total);
|
||||
|
||||
const colors = [
|
||||
'rgba(177,111,243,0.94)'
|
||||
];
|
||||
|
||||
|
||||
if (influenzaSubtypeChart) influenzaSubtypeChart.destroy();
|
||||
influenzaSubtypeChart = new Chart(document.getElementById('influenzaSubtypeDistribution'), {
|
||||
type: 'bar', // you can change to 'pie', 'doughnut', etc.
|
||||
data: {
|
||||
labels: displayLabels,
|
||||
datasets: [{
|
||||
data: dataset,
|
||||
backgroundColor: colors,
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
indexAxis: 'y',
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
position:'right'
|
||||
},
|
||||
datalabels: {
|
||||
color: '#ffffff',
|
||||
backgroundColor: 'rgba(68,76,68,0.31)',
|
||||
borderRadius: 6,
|
||||
z: 1000,
|
||||
padding: {
|
||||
top: 6,
|
||||
bottom: 6,
|
||||
left: 10,
|
||||
right: 10
|
||||
},
|
||||
font: {
|
||||
weight: 'bold',
|
||||
size: 12
|
||||
},
|
||||
formatter: (value) => value,
|
||||
anchor: 'end',
|
||||
align: 'end',
|
||||
offset: 10,
|
||||
clamp: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Number of Positive Influenza Subtypes'
|
||||
}
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Influenza Subtypes'
|
||||
},
|
||||
grid:{
|
||||
display: false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [ChartDataLabels]
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function loadCovidDistributedByAgeGroup(periodType, startYear, startWeek, endYear, endWeek) {
|
||||
|
||||
fetch(`/api/dashboard/covid-distributed-by-age-group?period_type=${periodType}&start_year=${startYear}&start_week=${startWeek}&end_year=${endYear}&end_week=${endWeek}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
|
||||
let displayLabels = data.map(item => item.age_group);
|
||||
let dataset = data.map(item => item.total);
|
||||
|
||||
const colors = [
|
||||
'#84cc16'
|
||||
];
|
||||
|
||||
if (covidDistributedByAgeChart) covidDistributedByAgeChart.destroy();
|
||||
covidDistributedByAgeChart = new Chart(document.getElementById('covidDistributedByAgeGroup'), {
|
||||
type: 'bar', // you can change to 'pie', 'doughnut', etc.
|
||||
data: {
|
||||
labels: displayLabels,
|
||||
datasets: [{
|
||||
label: 'Total Covid-19 Detected',
|
||||
data: dataset,
|
||||
backgroundColor: colors,
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
position:'bottom'
|
||||
},
|
||||
datalabels: {
|
||||
color: '#fff',
|
||||
backgroundColor: 'rgba(68,76,68,0.7)',
|
||||
borderRadius: 6,
|
||||
z: 1000,
|
||||
padding: {
|
||||
top: 6,
|
||||
bottom: 6,
|
||||
left: 10,
|
||||
right: 10
|
||||
},
|
||||
font: {
|
||||
weight: 'bold',
|
||||
size: 12
|
||||
},
|
||||
formatter: (value) => value,
|
||||
anchor: 'end',
|
||||
align: 'end',
|
||||
offset: 10,
|
||||
clamp: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Patient Age Group'
|
||||
},
|
||||
grid:{
|
||||
display: false
|
||||
}
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Number of Positive SARS-CoV-2'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function loadCovidLineageFrequency(periodType, startYear, startWeek, endYear, endWeek) {
|
||||
|
||||
fetch(`/api/dashboard/covid-lineage-frequency?period_type=${periodType}&start_year=${startYear}&start_week=${startWeek}&end_year=${endYear}&end_week=${endWeek}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
|
||||
// Extract unique weeks (X-axis)
|
||||
const weeks = [...new Set(data.map(item => item.week))].sort();
|
||||
|
||||
// Extract unique lineages
|
||||
const lineages = [...new Set(data.map(item => item.lineage))];
|
||||
|
||||
// Color palette
|
||||
const colors = [
|
||||
'#84cc16','#22c55e','#06b6d4','#3b82f6',
|
||||
'#6366f1','#a855f7','#ec4899','#ef4444',
|
||||
'#f97316','#eab308'
|
||||
];
|
||||
|
||||
// Build datasets
|
||||
const datasets = lineages.map((lineage, index) => {
|
||||
const lineageData = weeks.map(week => {
|
||||
const found = data.find(
|
||||
item => item.week === week && item.lineage === lineage
|
||||
);
|
||||
return found ? found.total : 0;
|
||||
});
|
||||
|
||||
return {
|
||||
label: lineage,
|
||||
data: lineageData,
|
||||
fill: true, // area fill
|
||||
tension: 0.4, // smooth curve
|
||||
borderColor: 'transparent', // hide the line
|
||||
borderWidth: 0,
|
||||
pointRadius: 0, // hide points
|
||||
backgroundColor: hexToRGBA(colors[index % colors.length], 0.3),
|
||||
stack: 'total'
|
||||
};
|
||||
});
|
||||
|
||||
// Destroy previous chart if exists
|
||||
if (covidLineageFrequencyChart) covidLineageFrequencyChart.destroy();
|
||||
|
||||
const ctx = document.getElementById('covidLineageFrequency').getContext('2d');
|
||||
|
||||
covidLineageFrequencyChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: weeks,
|
||||
datasets: datasets
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false // hide default legend
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
},
|
||||
datalabels: {
|
||||
display: false // hide labels
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Week'
|
||||
},
|
||||
grid:{
|
||||
display: false
|
||||
}
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Relative Frequency'
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// -------------------------
|
||||
// Custom right-side scrollable legend
|
||||
// -------------------------
|
||||
const legendContainer = document.getElementById('legendContainer');
|
||||
legendContainer.innerHTML = ''; // clear old legend
|
||||
|
||||
datasets.forEach((dataset, index) => {
|
||||
const item = document.createElement('div');
|
||||
item.style.display = 'flex';
|
||||
item.style.alignItems = 'center';
|
||||
item.style.marginBottom = '4px';
|
||||
item.style.fontSize = '11px';
|
||||
item.style.cursor = 'pointer';
|
||||
item.innerHTML = `
|
||||
<span style="width:15px;height:15px;background:${dataset.backgroundColor};display:inline-block;margin-right:8px;"></span>
|
||||
${dataset.label}
|
||||
`;
|
||||
|
||||
item.addEventListener('click', () => {
|
||||
const meta = covidLineageFrequencyChart.getDatasetMeta(index);
|
||||
|
||||
// If the clicked dataset is already the only visible one, show all
|
||||
const allHidden = datasets.every((d, i) => covidLineageFrequencyChart.getDatasetMeta(i).hidden || i === index);
|
||||
if (!allHidden) {
|
||||
// Hide all datasets
|
||||
datasets.forEach((d, i) => {
|
||||
covidLineageFrequencyChart.getDatasetMeta(i).hidden = true;
|
||||
});
|
||||
// Show only clicked
|
||||
meta.hidden = false;
|
||||
} else {
|
||||
// Show all datasets
|
||||
datasets.forEach((d, i) => {
|
||||
covidLineageFrequencyChart.getDatasetMeta(i).hidden = false;
|
||||
});
|
||||
}
|
||||
|
||||
covidLineageFrequencyChart.update();
|
||||
|
||||
// Update legend opacity
|
||||
Array.from(legendContainer.children).forEach((child, i) => {
|
||||
const metaItem = covidLineageFrequencyChart.getDatasetMeta(i);
|
||||
child.style.opacity = metaItem.hidden ? 0.5 : 1;
|
||||
});
|
||||
});
|
||||
|
||||
legendContainer.appendChild(item);
|
||||
});
|
||||
|
||||
// Scrollable CSS (in case legend is long)
|
||||
legendContainer.style.maxHeight = '375px';
|
||||
legendContainer.style.overflowY = 'auto';
|
||||
legendContainer.style.padding = '8px';
|
||||
legendContainer.style.borderRadius = '0px';
|
||||
});
|
||||
}
|
||||
|
||||
function loadInfluenzaSubtypeFrequency(periodType, startYear, startWeek, endYear, endWeek) {
|
||||
|
||||
fetch(`/api/dashboard/influenza-relative-frequency?period_type=${periodType}&start_year=${startYear}&start_week=${startWeek}&end_year=${endYear}&end_week=${endWeek}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
|
||||
// Extract unique weeks (X-axis)
|
||||
const weeks = [...new Set(data.map(item => item.week))].sort();
|
||||
|
||||
const colors = [
|
||||
'#84cc16','#22c55e','#06b6d4','#3b82f6',
|
||||
'#6366f1','#a855f7','#ec4899','#ef4444',
|
||||
'#f97316','#eab308'
|
||||
];
|
||||
|
||||
// Extract unique lineages
|
||||
const lineages = [...new Set(data.map(item => item.lineage))];
|
||||
// Build datasets
|
||||
const datasets = lineages.map((lineage, index) => {
|
||||
const lineageData = weeks.map(week => {
|
||||
const found = data.find(
|
||||
item => item.week === week && item.lineage === lineage
|
||||
);
|
||||
return found ? found.total : 0;
|
||||
});
|
||||
|
||||
return {
|
||||
label: lineage,
|
||||
data: lineageData,
|
||||
fill: true, // area fill
|
||||
tension: 0.4, // smooth curve
|
||||
borderColor: 'transparent', // hide the line
|
||||
borderWidth: 0,
|
||||
pointRadius: 0, // hide points
|
||||
backgroundColor: hexToRGBA(colors[index % colors.length], 0.3),
|
||||
stack: 'total'
|
||||
};
|
||||
});
|
||||
|
||||
// Destroy previous chart if exists
|
||||
if (influenzaSubtypeFrequencyChart) influenzaSubtypeFrequencyChart.destroy();
|
||||
|
||||
const ctx = document.getElementById('influenzaSubtypeFrequency').getContext('2d');
|
||||
|
||||
influenzaSubtypeFrequencyChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: weeks,
|
||||
datasets: datasets
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false // hide default legend
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
},
|
||||
datalabels: {
|
||||
display: false // hide labels
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Week'
|
||||
},
|
||||
grid:{
|
||||
display: false
|
||||
}
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Relative Frequency'
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const legendContainer = document.getElementById('legendContainerInfluenzaSubtypeFrequency');
|
||||
legendContainer.innerHTML = ''; // clear old legend
|
||||
|
||||
datasets.forEach((dataset, index) => {
|
||||
const item = document.createElement('div');
|
||||
item.style.display = 'flex';
|
||||
item.style.alignItems = 'center';
|
||||
item.style.marginBottom = '4px';
|
||||
item.style.fontSize = '11px';
|
||||
item.style.cursor = 'pointer';
|
||||
item.innerHTML = `
|
||||
<span style="width:15px;height:15px;background:${dataset.backgroundColor};display:inline-block;margin-right:8px;"></span>
|
||||
${dataset.label}
|
||||
`;
|
||||
|
||||
item.addEventListener('click', () => {
|
||||
const meta = influenzaSubtypeFrequencyChart.getDatasetMeta(index);
|
||||
|
||||
// If the clicked dataset is already the only visible one, show all
|
||||
const allHidden = datasets.every((d, i) => influenzaSubtypeFrequencyChart.getDatasetMeta(i).hidden || i === index);
|
||||
if (!allHidden) {
|
||||
// Hide all datasets
|
||||
datasets.forEach((d, i) => {
|
||||
influenzaSubtypeFrequencyChart.getDatasetMeta(i).hidden = true;
|
||||
});
|
||||
// Show only clicked
|
||||
meta.hidden = false;
|
||||
} else {
|
||||
// Show all datasets
|
||||
datasets.forEach((d, i) => {
|
||||
influenzaSubtypeFrequencyChart.getDatasetMeta(i).hidden = false;
|
||||
});
|
||||
}
|
||||
|
||||
influenzaSubtypeFrequencyChart.update();
|
||||
|
||||
// Update legend opacity
|
||||
Array.from(legendContainer.children).forEach((child, i) => {
|
||||
const metaItem = influenzaSubtypeFrequencyChart.getDatasetMeta(i);
|
||||
child.style.opacity = metaItem.hidden ? 0.5 : 1;
|
||||
});
|
||||
});
|
||||
|
||||
legendContainer.appendChild(item);
|
||||
});
|
||||
|
||||
// Scrollable CSS (in case legend is long)
|
||||
legendContainer.style.maxHeight = '375px';
|
||||
legendContainer.style.overflowY = 'auto';
|
||||
legendContainer.style.padding = '8px';
|
||||
legendContainer.style.borderRadius = '0px';
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// Helper to convert hex to rgba
|
||||
// -------------------------
|
||||
function hexToRGBA(hex, alpha) {
|
||||
const r = parseInt(hex.slice(1, 3), 16);
|
||||
const g = parseInt(hex.slice(3, 5), 16);
|
||||
const b = parseInt(hex.slice(5, 7), 16);
|
||||
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
||||
}
|
||||
|
||||
function updateAlerts() {
|
||||
|
||||
if (!window._summaryData || !window._provinceData) return;
|
||||
@@ -380,10 +874,14 @@ function renderAlerts(alerts) {
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
function getPositivityColor(p) {
|
||||
if (p > 20) return "#b91c1c";
|
||||
if (p > 10) return "#ef4444";
|
||||
if (p > 5) return "#f59e0b";
|
||||
if (p > 0) return "#84cc16";
|
||||
if (p == 'A/H1N1pdm') return "#2563eb";
|
||||
if (p == 'A/H3N2') return "#11de9d";
|
||||
if (p == 'B/Yam') return "#9333ea";
|
||||
if (p == 'B/Vic') return "#1021b9";
|
||||
if (p == 'A/H9N2') return "#b91081";
|
||||
if (p == 'A/H5N1') return "#ad850d";
|
||||
if (p == 'A/Unsubtypable') return "#047393";
|
||||
if (p == 'B/Unsubtypable') return "#890512";
|
||||
return "#9ca3af";
|
||||
}
|
||||
function normalizeProvince(name, validSet) {
|
||||
@@ -432,13 +930,15 @@ function addPositivityLegend() {
|
||||
div.innerHTML = `
|
||||
<div style="background:white;padding:10px 12px;border-radius:6px;
|
||||
box-shadow:0 2px 6px rgba(0,0,0,0.2);font-size:12px;">
|
||||
<div style="font-weight:600;margin-bottom:6px;">Positivity</div>
|
||||
|
||||
<div><span style="border:3px solid #b91c1c;width:12px;height:12px;display:inline-block;margin-right:6px;"></span>> 20%</div>
|
||||
<div><span style="border:3px solid #ef4444;width:12px;height:12px;display:inline-block;margin-right:6px;"></span>10–20%</div>
|
||||
<div><span style="border:3px solid #f59e0b;width:12px;height:12px;display:inline-block;margin-right:6px;"></span>5–10%</div>
|
||||
<div><span style="border:3px solid #84cc16;width:12px;height:12px;display:inline-block;margin-right:6px;"></span>0–5%</div>
|
||||
<div><span style="border:3px solid #9ca3af;width:12px;height:12px;display:inline-block;margin-right:6px;"></span>0%</div>
|
||||
<div style="font-weight:600;margin-bottom:6px;">Influenza Subtypes</div>
|
||||
<div><span style="background: #2563eb; border: 2px solid #aa9d9d; width:12px;height:12px;display:inline-block;margin-right:6px; border-radius: 50%;"></span>A/H1N1pdm</div>
|
||||
<div><span style="background: #11de9d; border: 2px solid #aa9d9d; width:12px;height:12px;display:inline-block;margin-right:6px; border-radius: 50%;"></span>A/H3N2</div>
|
||||
<div><span style="background: #b91081; border: 2px solid #aa9d9d; width:12px;height:12px;display:inline-block;margin-right:6px; border-radius: 50%;"></span>A/H9N2</div>
|
||||
<div><span style="background: #ad850d; border: 2px solid #aa9d9d; width:12px;height:12px;display:inline-block;margin-right:6px; border-radius: 50%;"></span>A/H5N1</div>
|
||||
<div><span style="background: #047393; border: 2px solid #aa9d9d; width:12px;height:12px;display:inline-block;margin-right:6px; border-radius: 50%;"></span>A/Unsubtypable</div>
|
||||
<div><span style="background: #9333ea; border: 2px solid #aa9d9d; width:12px;height:12px;display:inline-block;margin-right:6px; border-radius: 50%;"></span>B/Yam</div>
|
||||
<div><span style="background: #1021b9; border: 2px solid #aa9d9d; width:12px;height:12px;display:inline-block;margin-right:6px; border-radius: 50%;"></span>B/Vic</div>
|
||||
<div><span style="background: #890512; border: 2px solid #aa9d9d; width:12px;height:12px;display:inline-block;margin-right:6px; border-radius: 50%;"></span>B/Unsubtypable</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -490,52 +990,42 @@ function loadProvinceMap(startYear, startWeek, endYear, endWeek) {
|
||||
const center = layer.getBounds().getCenter();
|
||||
|
||||
const rows = data.filter(d => {
|
||||
if (![1, 2, 3].includes(d.surveillance_id)) return false;
|
||||
|
||||
const name = normalizeProvince(d.patient_province, validProvinces);
|
||||
return name === province;
|
||||
});
|
||||
|
||||
const offsets = { 1: -0.15, 2: 0, 3: 0.15 };
|
||||
if (!rows.length) return;
|
||||
|
||||
const colors = {
|
||||
1: '#2563eb',
|
||||
2: '#10b981',
|
||||
3: '#9333ea'
|
||||
};
|
||||
// 👉 group by pathogen_name
|
||||
const pathogens = [...new Set(rows.map(r => r.pathogen_name))];
|
||||
|
||||
const spacing = 1; //0.12; // adjust spacing between circles
|
||||
|
||||
rows.forEach(row => {
|
||||
|
||||
const percent = row.total
|
||||
? ((row.positive / row.total) * 100).toFixed(1)
|
||||
: 0;
|
||||
|
||||
const offset = offsets[row.surveillance_id] ?? 0;
|
||||
// 👉 dynamic offset based on pathogen index
|
||||
const index = pathogens.indexOf(row.pathogen_name);
|
||||
const offset = (index - (pathogens.length - 1) / 2) * spacing;
|
||||
|
||||
const lat = center.lat;
|
||||
const lng = center.lng + offset;
|
||||
|
||||
const programName =
|
||||
row.surveillance_id === 1 ? 'SARI' :
|
||||
row.surveillance_id === 2 ? 'ILI' : 'LBM';
|
||||
|
||||
L.circleMarker([lat, lng], {
|
||||
radius: getRadius(row.total),
|
||||
fillColor: colors[row.surveillance_id],
|
||||
color: getPositivityColor(percent),
|
||||
fillColor: getColorByPathogen(row.pathogen_name), // 👈 new function
|
||||
color: getPositivityColor(row.pathogen_nam),
|
||||
weight: 2,
|
||||
fillOpacity: 0.9
|
||||
})
|
||||
.bindTooltip(`
|
||||
<strong>${province}</strong><br>
|
||||
${programName}<br>
|
||||
Cases: ${row.total}<br>
|
||||
Positivity: ${percent}%
|
||||
`)
|
||||
<strong>${province}</strong><br>
|
||||
${row.pathogen_name}<br>
|
||||
Total Detected: ${row.total}<br>
|
||||
`)
|
||||
.addTo(map);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}).addTo(map);
|
||||
@@ -544,6 +1034,21 @@ function loadProvinceMap(startYear, startWeek, endYear, endWeek) {
|
||||
|
||||
}
|
||||
|
||||
function getColorByPathogen(name) {
|
||||
const colors = {
|
||||
"A/H1N1pdm": "#2563eb",
|
||||
"A/H3N2": "#11de9d",
|
||||
"B/Yam": "#9333ea",
|
||||
"B/Vic" : "#1021b9",
|
||||
"A/H9N2": "#b91081",
|
||||
"A/H5N1": "#ad850d",
|
||||
"A/Unsubtypable": "#047393",
|
||||
"B/Unsubtypable": "#890512"
|
||||
};
|
||||
|
||||
return colors[name] || "#000000"; // fallback color
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -558,8 +1063,58 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
new DashboardFilter((startYear, startWeek, endYear, endWeek) => {
|
||||
|
||||
loadTrend('week', startYear, startWeek, endYear, endWeek);
|
||||
loadInfluenzaSubtypeDistribution('week', startYear, startWeek, endYear, endWeek);
|
||||
loadCovidDistributedByAgeGroup('week', startYear, startWeek, endYear, endWeek);
|
||||
loadInfluenzaSubtypeFrequency('week', startYear, startWeek, endYear, endWeek);
|
||||
loadCovidLineageFrequency('week', startYear, startWeek, endYear, endWeek);
|
||||
loadProvinceMap(startYear, startWeek, endYear, endWeek);
|
||||
|
||||
const elements = document.querySelectorAll(".report-period");
|
||||
elements.forEach(el => {
|
||||
el.textContent = 'Week ' + startWeek + ' of '+startYear+' to ' + 'Week ' + endWeek + ' of ' + endYear
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
let currentSlide = 0;
|
||||
const slides = document.querySelectorAll('.slide');
|
||||
const nextBtn = document.querySelector('.next-btn');
|
||||
const prevBtn = document.querySelector('.prev-btn');
|
||||
|
||||
function showSlide(index) {
|
||||
slides.forEach((slide, i) => {
|
||||
slide.classList.remove('active', 'prev');
|
||||
|
||||
if (i === index) {
|
||||
slide.classList.add('active');
|
||||
} else if (i === index - 1) {
|
||||
slide.classList.add('prev');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function nextSlide() {
|
||||
currentSlide = (currentSlide + 1) % slides.length;
|
||||
showSlide(currentSlide);
|
||||
}
|
||||
|
||||
function prevSlide() {
|
||||
currentSlide = (currentSlide - 1 + slides.length) % slides.length;
|
||||
showSlide(currentSlide);
|
||||
}
|
||||
|
||||
// Button events
|
||||
nextBtn.addEventListener('click', nextSlide);
|
||||
prevBtn.addEventListener('click', prevSlide);
|
||||
|
||||
// Auto slide every 1 minute
|
||||
let slideInterval = 2 * (60 * 1000);
|
||||
setInterval(nextSlide, slideInterval);
|
||||
|
||||
// Init
|
||||
showSlide(currentSlide);
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user