570 lines
15 KiB
Markdown
570 lines
15 KiB
Markdown
# Vaccine & Infection Time Series Visualization Specification
|
|
|
|
## Overview
|
|
Generate progressive vaccine and infectious disease visualizations that combine Mapbox GL JS globe projections with time series data, interactive timelines, and embedded charts showing vaccination impact over time (2000-2023).
|
|
|
|
## Core Requirements
|
|
|
|
### Data Structure
|
|
Each visualization must include:
|
|
- **Geographic scope**: 60+ countries with accurate centroids
|
|
- **Temporal range**: 2000-2023 (24 years of data)
|
|
- **Vaccine metrics**: Coverage rates (dose 1, dose 2, booster if applicable)
|
|
- **Disease metrics**: Cases, deaths, incidence rates
|
|
- **Contextual data**: Income level, WHO region, population
|
|
|
|
### Time Series Features
|
|
1. **Timeline Slider**
|
|
- HTML5 range input (year 2000-2023)
|
|
- Auto-play functionality with play/pause button
|
|
- Year display showing current selected year
|
|
- Animation speed: 1 second per year
|
|
- Loop option to restart from 2000
|
|
|
|
2. **Map Filtering**
|
|
- Use `map.setFilter()` to show data for selected year
|
|
- Smooth transitions between years
|
|
- Color/size updates based on that year's metrics
|
|
|
|
3. **Hover Charts** (Chart.js integration)
|
|
- Display on country hover with 200ms delay
|
|
- Dual-axis line chart:
|
|
- Left axis: Vaccination coverage (%) - line chart
|
|
- Right axis: Cases/deaths - area or bar chart
|
|
- Time range: Full 2000-2023 series
|
|
- Interactive tooltips showing exact values
|
|
- Legend at bottom
|
|
- Responsive sizing (400x250px)
|
|
|
|
### Data Format Options
|
|
|
|
**Flattened Approach** (Recommended for Mapbox filtering):
|
|
```javascript
|
|
{
|
|
"type": "FeatureCollection",
|
|
"features": [
|
|
// One feature per country-year combination
|
|
{
|
|
"type": "Feature",
|
|
"geometry": { "type": "Point", "coordinates": [lng, lat] },
|
|
"properties": {
|
|
"name": "Nigeria",
|
|
"iso3": "NGA",
|
|
"year": 2015,
|
|
"region": "AFRO",
|
|
"income_level": "lower-middle",
|
|
"population": 182202000,
|
|
"coverage_dose1": 52,
|
|
"coverage_dose2": 35,
|
|
"cases": 45000,
|
|
"deaths": 850,
|
|
"incidence_per_100k": 24.7,
|
|
// Time series for chart (stored as JSON strings)
|
|
"years_array": "[2000,2001,...,2023]",
|
|
"coverage1_array": "[30,35,...,68]",
|
|
"coverage2_array": "[20,25,...,54]",
|
|
"cases_array": "[150000,140000,...,15000]",
|
|
"deaths_array": "[2500,2200,...,120]"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Vaccine Types to Explore
|
|
|
|
Each iteration should pick ONE vaccine/disease combination:
|
|
|
|
1. **Measles** (MCV1/MCV2)
|
|
- High global coverage (83% MCV1, 74% MCV2)
|
|
- Recent outbreaks despite vaccination
|
|
- 60 million deaths averted 2000-2023
|
|
|
|
2. **Polio** (OPV/IPV)
|
|
- Near-eradication story
|
|
- 99.9% reduction since 1988
|
|
- Last endemic countries: Afghanistan, Pakistan
|
|
|
|
3. **HPV** (Human Papillomavirus)
|
|
- Newer vaccine (2006+)
|
|
- Prevents cervical cancer
|
|
- Gender equity issues (girls vs boys)
|
|
- Wide coverage disparities (0-95%)
|
|
|
|
4. **DTP3** (Diphtheria, Tetanus, Pertussis)
|
|
- WHO zero-dose indicator
|
|
- Proxy for health system strength
|
|
- 84% global coverage (2023)
|
|
|
|
5. **COVID-19** (mRNA/Viral Vector)
|
|
- Rapid vaccine development (2020-2021)
|
|
- Unprecedented global campaign
|
|
- Equity challenges (COVAX)
|
|
|
|
6. **Rotavirus**
|
|
- Prevents severe diarrhea
|
|
- High impact in low-income countries
|
|
- 58 countries introduced 2006-2023
|
|
|
|
7. **Pneumococcal (PCV)**
|
|
- Prevents pneumonia
|
|
- Leading killer of children <5
|
|
- 154 countries introduced
|
|
|
|
8. **Hepatitis B (HepB)**
|
|
- Birth dose critical
|
|
- 84% global coverage
|
|
- Prevents liver cancer
|
|
|
|
### Visualization Layers
|
|
|
|
1. **Base Layer**: Globe with dark theme
|
|
- Atmosphere effects (medical or dark theme)
|
|
- Auto-rotation (pausable)
|
|
- Zoom range: 1.5 (globe) to 6 (continental)
|
|
|
|
2. **Coverage Layer**: Circle layer
|
|
- Size: Population
|
|
- Color: Vaccination coverage (coverageReverse scale)
|
|
- Opacity: Data availability (higher for complete data)
|
|
- Stroke: White, zoom-responsive
|
|
|
|
3. **Outbreak Layer**: Conditional circles (optional)
|
|
- Filter: Only show where cases > threshold
|
|
- Color: Red/orange (outbreak severity)
|
|
- Pulse animation for major outbreaks
|
|
|
|
### Chart.js Configuration
|
|
|
|
```javascript
|
|
{
|
|
type: 'line',
|
|
data: {
|
|
labels: years, // [2000, 2001, ..., 2023]
|
|
datasets: [
|
|
{
|
|
label: 'Vaccination Coverage (%)',
|
|
data: coverageData,
|
|
borderColor: 'rgb(75, 192, 192)',
|
|
backgroundColor: 'rgba(75, 192, 192, 0.1)',
|
|
yAxisID: 'y',
|
|
tension: 0.3,
|
|
fill: true
|
|
},
|
|
{
|
|
label: 'Cases',
|
|
data: casesData,
|
|
borderColor: 'rgb(255, 99, 132)',
|
|
backgroundColor: 'rgba(255, 99, 132, 0.3)',
|
|
yAxisID: 'y1',
|
|
type: 'bar',
|
|
opacity: 0.6
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: true,
|
|
interaction: {
|
|
mode: 'index',
|
|
intersect: false
|
|
},
|
|
scales: {
|
|
y: {
|
|
type: 'linear',
|
|
position: 'left',
|
|
title: { display: true, text: 'Coverage (%)' },
|
|
min: 0,
|
|
max: 100,
|
|
ticks: { color: '#9ca3af' }
|
|
},
|
|
y1: {
|
|
type: 'linear',
|
|
position: 'right',
|
|
title: { display: true, text: 'Cases' },
|
|
grid: { drawOnChartArea: false },
|
|
ticks: { color: '#9ca3af' }
|
|
}
|
|
},
|
|
plugins: {
|
|
title: {
|
|
display: true,
|
|
text: 'Vaccination Impact Over Time',
|
|
color: '#e5e7eb'
|
|
},
|
|
legend: {
|
|
position: 'bottom',
|
|
labels: { color: '#e5e7eb' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Timeline Control UI
|
|
|
|
```html
|
|
<div class="timeline-control">
|
|
<div class="header">
|
|
<h3>Timeline</h3>
|
|
<div class="year-display">
|
|
Year: <span id="current-year">2023</span>
|
|
</div>
|
|
</div>
|
|
|
|
<input id="year-slider"
|
|
type="range"
|
|
min="0"
|
|
max="23"
|
|
step="1"
|
|
value="23">
|
|
|
|
<div class="controls">
|
|
<button id="play-pause" class="btn">▶️ Play</button>
|
|
<button id="reset" class="btn">Reset</button>
|
|
<label>
|
|
<input type="checkbox" id="loop-checkbox" checked> Loop
|
|
</label>
|
|
</div>
|
|
|
|
<div class="stats">
|
|
<div class="stat">
|
|
<span class="label">Global Coverage:</span>
|
|
<span id="global-coverage">83%</span>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="label">Total Cases:</span>
|
|
<span id="total-cases">10.3M</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
### Styling Requirements
|
|
|
|
```css
|
|
.timeline-control {
|
|
position: absolute;
|
|
bottom: 30px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
background: rgba(10, 14, 26, 0.95);
|
|
padding: 20px 30px;
|
|
border-radius: 12px;
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
backdrop-filter: blur(10px);
|
|
min-width: 500px;
|
|
z-index: 1000;
|
|
}
|
|
|
|
.year-display {
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
color: #60a5fa;
|
|
text-align: center;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
#year-slider {
|
|
width: 100%;
|
|
height: 8px;
|
|
background: linear-gradient(90deg, #ef4444 0%, #f59e0b 50%, #10b981 100%);
|
|
border-radius: 5px;
|
|
outline: none;
|
|
-webkit-appearance: none;
|
|
}
|
|
|
|
#year-slider::-webkit-slider-thumb {
|
|
-webkit-appearance: none;
|
|
width: 20px;
|
|
height: 20px;
|
|
background: #60a5fa;
|
|
border-radius: 50%;
|
|
cursor: pointer;
|
|
box-shadow: 0 0 10px rgba(96, 165, 250, 0.5);
|
|
}
|
|
|
|
.controls {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-top: 15px;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.btn {
|
|
padding: 8px 16px;
|
|
background: rgba(96, 165, 250, 0.2);
|
|
border: 1px solid #60a5fa;
|
|
color: #60a5fa;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.btn:hover {
|
|
background: rgba(96, 165, 250, 0.3);
|
|
box-shadow: 0 0 15px rgba(96, 165, 250, 0.3);
|
|
}
|
|
|
|
/* Chart popup styling */
|
|
.measles-popup {
|
|
padding: 15px;
|
|
background: rgba(10, 14, 26, 0.98);
|
|
border-radius: 8px;
|
|
min-width: 420px;
|
|
}
|
|
|
|
.measles-popup h3 {
|
|
margin: 0 0 15px 0;
|
|
color: #e5e7eb;
|
|
font-size: 18px;
|
|
border-bottom: 2px solid rgba(96, 165, 250, 0.5);
|
|
padding-bottom: 10px;
|
|
}
|
|
|
|
.measles-popup canvas {
|
|
border-radius: 4px;
|
|
}
|
|
```
|
|
|
|
### Progressive Learning Path
|
|
|
|
**Iteration 1 - Foundation:**
|
|
- Basic timeline slider with manual control
|
|
- Static map showing current year data
|
|
- Simple hover popup with text data only
|
|
- Learn: Mapbox filtering, basic timeline UI
|
|
|
|
**Iteration 2 - Interactivity:**
|
|
- Auto-play functionality with play/pause
|
|
- Chart.js integration showing simple line chart
|
|
- Smooth transitions between years
|
|
- Learn: Chart.js basics, animation timing, DOM manipulation
|
|
|
|
**Iteration 3 - Advanced:**
|
|
- Dual-axis charts (coverage vs cases)
|
|
- Outbreak pulse animations
|
|
- Global statistics updating in real-time
|
|
- Advanced Chart.js options (themes, interactions)
|
|
- Optimized performance (chart caching, delayed popups)
|
|
- Learn: Complex Chart.js configurations, performance optimization
|
|
|
|
## Technical Requirements
|
|
|
|
### Dependencies
|
|
```html
|
|
<!-- Mapbox GL JS v3.0.1 -->
|
|
<script src='https://api.mapbox.com/mapbox-gl-js/v3.0.1/mapbox-gl.js'></script>
|
|
<link href='https://api.mapbox.com/mapbox-gl-js/v3.0.1/mapbox-gl.css' rel='stylesheet' />
|
|
|
|
<!-- Chart.js v4.4.1 -->
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
|
```
|
|
|
|
### Shared Architecture
|
|
Use existing shared modules:
|
|
```javascript
|
|
import { MAPBOX_CONFIG } from '../../shared/mapbox-config.js';
|
|
import { LayerFactory, COLOR_SCALES } from '../../shared/layer-factory.js';
|
|
```
|
|
|
|
### Data Generation Pattern
|
|
Create new data generator function:
|
|
```javascript
|
|
function generateTimeSeriesData(vaccineType, yearRange) {
|
|
const countries = [...]; // 60+ countries
|
|
const features = [];
|
|
|
|
for (const country of countries) {
|
|
for (let year = yearRange.start; year <= yearRange.end; year++) {
|
|
features.push({
|
|
type: "Feature",
|
|
geometry: { type: "Point", coordinates: country.centroid },
|
|
properties: {
|
|
name: country.name,
|
|
year: year,
|
|
coverage_dose1: generateCoverageForYear(country, year),
|
|
cases: generateCasesForYear(country, year),
|
|
// Time series arrays (same for all years of same country)
|
|
years_array: JSON.stringify([2000, ..., 2023]),
|
|
coverage1_array: JSON.stringify([...coverage values]),
|
|
cases_array: JSON.stringify([...case values])
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
return { type: "FeatureCollection", features };
|
|
}
|
|
```
|
|
|
|
### Performance Optimizations
|
|
|
|
1. **Chart Instance Management**
|
|
```javascript
|
|
let activeChart = null;
|
|
let chartCounter = 0;
|
|
|
|
function createPopupChart(feature) {
|
|
// Destroy previous chart
|
|
if (activeChart) {
|
|
activeChart.destroy();
|
|
}
|
|
|
|
const canvasId = `chart-${chartCounter++}`;
|
|
// ... create popup and chart
|
|
activeChart = new Chart(ctx, config);
|
|
}
|
|
```
|
|
|
|
2. **Popup Delay** (Prevent flickering)
|
|
```javascript
|
|
let popupTimeout;
|
|
|
|
map.on('mouseenter', 'layer', (e) => {
|
|
popupTimeout = setTimeout(() => {
|
|
createPopupChart(e.features[0]);
|
|
}, 200); // 200ms delay
|
|
});
|
|
|
|
map.on('mouseleave', 'layer', () => {
|
|
clearTimeout(popupTimeout);
|
|
});
|
|
```
|
|
|
|
3. **Data Filtering** (Performance)
|
|
```javascript
|
|
// Pre-filter features by year before adding to source
|
|
const currentYearFeatures = allFeatures.filter(f => f.properties.year === currentYear);
|
|
map.getSource('vaccine-data').setData({
|
|
type: "FeatureCollection",
|
|
features: currentYearFeatures
|
|
});
|
|
```
|
|
|
|
## Output Structure
|
|
|
|
```
|
|
vaccine_timeseries_[N]/
|
|
├── index.html # Main page with timeline UI
|
|
├── src/
|
|
│ ├── index.js # Map initialization, timeline logic
|
|
│ ├── chart-handler.js # Chart.js popup management
|
|
│ └── data/
|
|
│ └── timeseries-data.js # Time series GeoJSON data
|
|
├── README.md # Analysis and insights
|
|
└── CLAUDE.md # Technical documentation
|
|
```
|
|
|
|
## Naming Convention
|
|
|
|
**Folder**: `vaccine_timeseries_[number]_[vaccine]`
|
|
- Examples: `vaccine_timeseries_1_measles`, `vaccine_timeseries_2_polio`
|
|
|
|
**Title Pattern**: `[Vaccine Name] Vaccination Impact Timeline (2000-2023)`
|
|
|
|
## Quality Standards
|
|
|
|
### Data Quality
|
|
- ✅ Realistic trends (improving coverage over time, except COVID dip 2020-2021)
|
|
- ✅ Inverse correlation (higher coverage → fewer cases)
|
|
- ✅ Regional variations (AFRO lower, EURO higher)
|
|
- ✅ Income-based disparities
|
|
- ✅ All null values handled with coalesce
|
|
|
|
### Code Quality
|
|
- ✅ Shared module imports
|
|
- ✅ Chart instance cleanup
|
|
- ✅ Error handling for missing data
|
|
- ✅ Accessibility (ARIA labels on controls)
|
|
- ✅ Responsive design (timeline scales on mobile)
|
|
|
|
### User Experience
|
|
- ✅ Smooth animations (no jank)
|
|
- ✅ Clear visual feedback (year changes)
|
|
- ✅ Intuitive controls (play/pause, reset)
|
|
- ✅ Fast load time (<3s)
|
|
- ✅ Works offline after initial load
|
|
|
|
## Documentation Requirements
|
|
|
|
### README.md Must Include
|
|
1. **Key Findings**: 3-5 insights from the data
|
|
2. **Data Sources**: WHO, UNICEF, CDC references
|
|
3. **Temporal Trends**: Coverage and disease burden changes
|
|
4. **Regional Disparities**: AFRO vs EURO vs WPRO comparisons
|
|
5. **Policy Implications**: What the data shows about vaccine impact
|
|
|
|
### CLAUDE.md Must Include
|
|
1. **Setup Instructions**: How to run locally
|
|
2. **Timeline Controls**: How to use the slider
|
|
3. **Chart Interactions**: How to view country-level trends
|
|
4. **Data Structure**: Explanation of time series format
|
|
5. **Customization Guide**: How to add new vaccines or years
|
|
|
|
## Web Learning Strategy
|
|
|
|
Each iteration should learn from these progressively:
|
|
|
|
**Iteration 1**:
|
|
- Official Mapbox timeline example
|
|
- Basic Chart.js documentation
|
|
- WHO data portal overview
|
|
|
|
**Iteration 2**:
|
|
- Advanced Chart.js configurations (dual-axis)
|
|
- Stack Overflow popup chart patterns
|
|
- Timeline animation best practices
|
|
|
|
**Iteration 3**:
|
|
- Performance optimization techniques
|
|
- Advanced Mapbox expressions
|
|
- Data visualization best practices for health data
|
|
|
|
## Success Criteria
|
|
|
|
A successful visualization will:
|
|
1. ✅ Show clear correlation between vaccination and disease reduction
|
|
2. ✅ Enable exploration of any country's 24-year history
|
|
3. ✅ Provide smooth, intuitive timeline navigation
|
|
4. ✅ Display rich, readable charts on hover
|
|
5. ✅ Render correctly on all modern browsers
|
|
6. ✅ Tell a compelling story about vaccine impact
|
|
7. ✅ Use accurate, realistic data patterns
|
|
8. ✅ Perform smoothly (60fps animations)
|
|
|
|
## Vaccine-Specific Guidance
|
|
|
|
### Measles
|
|
- Coverage threshold: 95% for herd immunity
|
|
- Highlight 2019-2023 outbreak resurgence
|
|
- Show impact of COVID-19 disruptions (2020-2021 dip)
|
|
|
|
### Polio
|
|
- Focus on eradication progress (endemic → zero)
|
|
- Wild poliovirus vs vaccine-derived distinction
|
|
- Celebrate last case milestones
|
|
|
|
### HPV
|
|
- Recent introduction (most countries 2006+)
|
|
- Gender policy variations (girls-only vs both)
|
|
- Cancer prevention lag (10-20 years)
|
|
|
|
### DTP3
|
|
- Zero-dose children as equity indicator
|
|
- Consistent high coverage (plateau effect)
|
|
- Health system strength proxy
|
|
|
|
### COVID-19
|
|
- Unprecedented scale and speed
|
|
- Booster dose complexity (3rd, 4th doses)
|
|
- Equity challenges (high-income vs low-income)
|
|
|
|
---
|
|
|
|
**Version**: 1.0
|
|
**Created**: 2025-01-08
|
|
**Purpose**: Generate interactive vaccine impact visualizations with time series data and embedded analytics
|