453 lines
13 KiB
Markdown
453 lines
13 KiB
Markdown
# Critical Fixes Implementation Guide
|
|
|
|
## Overview
|
|
|
|
This guide explains how to use the new shared architecture to fix all Mapbox globe visualizations. Three critical infrastructure components have been created in the `shared/` directory:
|
|
|
|
1. **mapbox-config.js** - Centralized token management
|
|
2. **data-generator.js** - Unified, realistic data generation
|
|
3. **layer-factory.js** - Best-practice layer creation
|
|
|
|
## The Problem
|
|
|
|
The original demos (globe_10-13) failed because:
|
|
|
|
1. ❌ **Invalid tokens**: Placeholder strings like `'pk.eyJ1IjoieW91cnVzZXJuYW1lIiwiYSI6InlvdXJ0b2tlbiJ9.yourtokenstring'`
|
|
2. ❌ **Wrong layer types**: Using `fill` layers (requires Polygons) with Point data
|
|
3. ❌ **Inconsistent data**: Each demo generated data differently
|
|
4. ❌ **No validation**: No error messages when things went wrong
|
|
|
|
## The Solution
|
|
|
|
### Architecture Overview
|
|
|
|
```
|
|
mapbox_test/
|
|
├── shared/ # NEW: Shared infrastructure
|
|
│ ├── mapbox-config.js # Token management & validation
|
|
│ ├── data-generator.js # Unified data generation
|
|
│ └── layer-factory.js # Best-practice layers
|
|
│
|
|
├── mapbox_globe_10/ # Polio demo
|
|
│ ├── index.html
|
|
│ └── src/
|
|
│ └── index.js # NOW: Uses shared components
|
|
│
|
|
├── mapbox_globe_11/ # Measles demo
|
|
├── mapbox_globe_12/ # Smallpox demo
|
|
└── mapbox_globe_13/ # DTP3 demo
|
|
```
|
|
|
|
## How to Fix Each Demo
|
|
|
|
### Step 1: Update HTML (index.html)
|
|
|
|
**Before:**
|
|
```html
|
|
<script type="module" src="src/index.js"></script>
|
|
```
|
|
|
|
**After:**
|
|
```html
|
|
<!-- Load Mapbox config BEFORE data -->
|
|
<script type="module">
|
|
import { MAPBOX_CONFIG } from '../shared/mapbox-config.js';
|
|
MAPBOX_CONFIG.applyToken(); // Validates and applies token
|
|
</script>
|
|
|
|
<!-- Load data generator -->
|
|
<script type="module" src="../shared/data-generator.js"></script>
|
|
|
|
<!-- Load main app -->
|
|
<script type="module" src="src/index.js"></script>
|
|
```
|
|
|
|
### Step 2: Update JavaScript (src/index.js)
|
|
|
|
**Before (BROKEN):**
|
|
```javascript
|
|
// ❌ Invalid token
|
|
mapboxgl.accessToken = 'pk.eyJ1IjoieW91cnVzZXJuYW1lIi...';
|
|
|
|
// ❌ Wrong layer type for Point data
|
|
map.addLayer({
|
|
id: 'country-fills',
|
|
type: 'fill', // Requires Polygon geometries!
|
|
source: 'countries',
|
|
paint: { 'fill-color': '#4caf50' }
|
|
});
|
|
```
|
|
|
|
**After (WORKING):**
|
|
```javascript
|
|
import { MAPBOX_CONFIG } from '../shared/mapbox-config.js';
|
|
import { generateVaccineData } from '../shared/data-generator.js';
|
|
import { LayerFactory } from '../shared/layer-factory.js';
|
|
|
|
// ✅ Token already applied by HTML import
|
|
// ✅ Generate proper Point-based data
|
|
const vaccineData = generateVaccineData('polio');
|
|
|
|
// ✅ Initialize map with validated config
|
|
const map = new mapboxgl.Map({
|
|
container: 'map',
|
|
...MAPBOX_CONFIG.getMapOptions({
|
|
center: [20, 20],
|
|
zoom: 1.5
|
|
})
|
|
});
|
|
|
|
map.on('load', () => {
|
|
const factory = new LayerFactory(map);
|
|
|
|
// ✅ Apply beautiful atmosphere
|
|
factory.applyGlobeAtmosphere({ theme: 'medical' });
|
|
|
|
// ✅ Add data source
|
|
map.addSource('vaccine-data', {
|
|
type: 'geojson',
|
|
data: vaccineData
|
|
});
|
|
|
|
// ✅ Create proper circle layer
|
|
const layer = factory.createCircleLayer({
|
|
id: 'vaccine-circles',
|
|
source: 'vaccine-data',
|
|
sizeProperty: 'population',
|
|
colorProperty: 'coverage_2020', // For polio
|
|
colorScale: 'coverage'
|
|
});
|
|
|
|
map.addLayer(layer);
|
|
|
|
// ✅ Add hover effects
|
|
factory.setupHoverEffects('vaccine-circles');
|
|
|
|
// ✅ Add legend
|
|
factory.addLegend({
|
|
title: 'Polio Coverage 2020',
|
|
colorScale: 'coverage'
|
|
});
|
|
});
|
|
```
|
|
|
|
## Complete Example: Fixing globe_10 (Polio)
|
|
|
|
Here's a complete, working replacement for `mapbox_globe_10/src/index.js`:
|
|
|
|
```javascript
|
|
/**
|
|
* Polio Eradication Progress Visualization
|
|
* Using shared architecture for reliability
|
|
*/
|
|
|
|
import { MAPBOX_CONFIG } from '../../shared/mapbox-config.js';
|
|
import { generateVaccineData } from '../../shared/data-generator.js';
|
|
import { LayerFactory, COLOR_SCALES } from '../../shared/layer-factory.js';
|
|
|
|
// Generate polio data (automatically creates realistic Point geometries)
|
|
const polioData = generateVaccineData('polio');
|
|
|
|
// Initialize map
|
|
const map = new mapboxgl.Map({
|
|
container: 'map',
|
|
...MAPBOX_CONFIG.getMapOptions({
|
|
style: 'mapbox://styles/mapbox/dark-v11',
|
|
center: [20, 20],
|
|
zoom: 1.5
|
|
})
|
|
});
|
|
|
|
// Timeline state
|
|
let currentYear = 1980;
|
|
let isAnimating = false;
|
|
let animationInterval = null;
|
|
|
|
map.on('load', () => {
|
|
const factory = new LayerFactory(map);
|
|
|
|
// Apply medical-themed atmosphere
|
|
factory.applyGlobeAtmosphere({ theme: 'medical' });
|
|
|
|
// Add data source
|
|
map.addSource('polio-data', {
|
|
type: 'geojson',
|
|
data: polioData
|
|
});
|
|
|
|
// Create main circle layer
|
|
const layer = factory.createCircleLayer({
|
|
id: 'polio-circles',
|
|
source: 'polio-data',
|
|
sizeProperty: 'population',
|
|
colorProperty: 'coverage_1980', // Will update dynamically
|
|
colorScale: 'coverage'
|
|
});
|
|
|
|
map.addLayer(layer);
|
|
|
|
// Setup interactive hover
|
|
factory.setupHoverEffects('polio-circles', (feature) => {
|
|
const props = feature.properties;
|
|
const coverage = props[`coverage_${currentYear}`];
|
|
|
|
const popup = new mapboxgl.Popup({ offset: 15 })
|
|
.setLngLat(feature.geometry.coordinates)
|
|
.setHTML(factory.createPopupContent(feature, {
|
|
metrics: [
|
|
{
|
|
label: `Coverage ${currentYear}`,
|
|
property: `coverage_${currentYear}`,
|
|
format: (v) => `${v}%`
|
|
},
|
|
{
|
|
label: 'Polio Free Since',
|
|
property: 'polio_free_year',
|
|
format: (v) => v || 'Not certified'
|
|
},
|
|
{
|
|
label: 'Endemic Status',
|
|
property: 'endemic',
|
|
format: (v) => v ? '🔴 Still endemic' : '✅ Eradicated'
|
|
}
|
|
]
|
|
}))
|
|
.addTo(map);
|
|
});
|
|
|
|
// Add legend
|
|
factory.addLegend({
|
|
title: `Polio Coverage ${currentYear}`,
|
|
colorScale: 'coverage',
|
|
position: 'bottom-right'
|
|
});
|
|
|
|
// Initialize timeline
|
|
updateVisualization();
|
|
});
|
|
|
|
// Update map for current year
|
|
function updateVisualization() {
|
|
if (!map.isStyleLoaded()) return;
|
|
|
|
// Update layer color to use current year's data
|
|
const colorExpression = [
|
|
'interpolate',
|
|
['linear'],
|
|
['get', `coverage_${currentYear}`],
|
|
...COLOR_SCALES.coverage.stops.flatMap((stop, i) => [
|
|
stop,
|
|
COLOR_SCALES.coverage.colors[i]
|
|
])
|
|
];
|
|
|
|
map.setPaintProperty('polio-circles', 'circle-color', colorExpression);
|
|
|
|
// Update UI
|
|
document.getElementById('year-display').textContent = currentYear;
|
|
document.getElementById('year-slider').value = currentYear;
|
|
|
|
// Update statistics (example)
|
|
updateStatistics();
|
|
}
|
|
|
|
function updateStatistics() {
|
|
const features = map.querySourceFeatures('polio-data');
|
|
|
|
let totalCoverage = 0;
|
|
let certifiedCount = 0;
|
|
let endemicCount = 0;
|
|
|
|
features.forEach(feature => {
|
|
const props = feature.properties;
|
|
totalCoverage += props[`coverage_${currentYear}`] || 0;
|
|
|
|
if (props.polio_free_year && currentYear >= props.polio_free_year) {
|
|
certifiedCount++;
|
|
}
|
|
|
|
if (props.endemic && currentYear === 2020) {
|
|
endemicCount++;
|
|
}
|
|
});
|
|
|
|
const avgCoverage = (totalCoverage / features.length).toFixed(1);
|
|
|
|
document.getElementById('global-coverage').textContent = `${avgCoverage}%`;
|
|
document.getElementById('certified-countries').textContent = certifiedCount;
|
|
document.getElementById('endemic-countries').textContent = endemicCount;
|
|
}
|
|
|
|
// Timeline controls
|
|
document.getElementById('year-slider').addEventListener('input', (e) => {
|
|
currentYear = parseInt(e.target.value);
|
|
updateVisualization();
|
|
});
|
|
|
|
document.getElementById('play-btn').addEventListener('click', () => {
|
|
if (isAnimating) {
|
|
clearInterval(animationInterval);
|
|
isAnimating = false;
|
|
document.getElementById('play-btn').textContent = 'Play';
|
|
} else {
|
|
isAnimating = true;
|
|
document.getElementById('play-btn').textContent = 'Pause';
|
|
|
|
animationInterval = setInterval(() => {
|
|
currentYear++;
|
|
if (currentYear > 2020) currentYear = 1980;
|
|
updateVisualization();
|
|
}, 800);
|
|
}
|
|
});
|
|
|
|
document.getElementById('reset-btn').addEventListener('click', () => {
|
|
currentYear = 1980;
|
|
if (isAnimating) {
|
|
clearInterval(animationInterval);
|
|
isAnimating = false;
|
|
document.getElementById('play-btn').textContent = 'Play';
|
|
}
|
|
updateVisualization();
|
|
});
|
|
```
|
|
|
|
## Data Types for Each Demo
|
|
|
|
### Polio (globe_10)
|
|
```javascript
|
|
const polioData = generateVaccineData('polio');
|
|
// Properties: coverage_1980, coverage_1990, ..., coverage_2020, polio_free_year, endemic
|
|
```
|
|
|
|
### Measles (globe_11)
|
|
```javascript
|
|
const measlesData = generateVaccineData('measles');
|
|
// Properties: coverage_dose1, coverage_dose2, cases_2023, deaths_2023
|
|
```
|
|
|
|
### Smallpox (globe_12)
|
|
```javascript
|
|
const smallpoxData = generateVaccineData('smallpox');
|
|
// Properties: endemic_1950, endemic_1960, endemic_1970, eradication_year, vaccination_intensity
|
|
```
|
|
|
|
### DTP3 (globe_13)
|
|
```javascript
|
|
const dtp3Data = generateVaccineData('dtp3');
|
|
// Properties: dtp3_coverage_2024, zero_dose_children, under5_mortality_rate, infant_deaths_prevented
|
|
```
|
|
|
|
### HPV (globe_14 - already working!)
|
|
```javascript
|
|
const hpvData = generateVaccineData('hpv');
|
|
// Properties: hpv_coverage_2024, cervical_cancer_incidence, lives_saved_projected, annual_deaths
|
|
```
|
|
|
|
## Color Scales Available
|
|
|
|
The `LayerFactory` provides these pre-configured color scales:
|
|
|
|
1. **coverage** - Red → Green (0-100%)
|
|
2. **coverageReverse** - Green → Red (inverse)
|
|
3. **diverging** - Green ← Gray → Red
|
|
4. **purple** - Purple gradient (HPV theme)
|
|
5. **blueOrange** - Blue-Orange diverging
|
|
|
|
Example usage:
|
|
```javascript
|
|
const layer = factory.createCircleLayer({
|
|
id: 'my-layer',
|
|
source: 'my-source',
|
|
colorScale: 'purple' // Use purple theme
|
|
});
|
|
```
|
|
|
|
## Atmosphere Themes
|
|
|
|
Choose from pre-configured atmosphere themes:
|
|
|
|
- **default** - Light blue, professional
|
|
- **dark** - Deep space, dramatic
|
|
- **medical** - Clinical, serious
|
|
- **purple** - Health equity theme
|
|
|
|
```javascript
|
|
factory.applyGlobeAtmosphere({ theme: 'medical' });
|
|
```
|
|
|
|
## Validation & Debugging
|
|
|
|
The new architecture provides automatic validation:
|
|
|
|
### Token Validation
|
|
```javascript
|
|
MAPBOX_CONFIG.validateToken();
|
|
// Logs: ✅ Mapbox token validated successfully
|
|
// OR
|
|
// Logs: ❌ MAPBOX TOKEN ERROR: Invalid placeholder token detected!
|
|
```
|
|
|
|
### Data Validation
|
|
The data generator ensures:
|
|
- ✅ All features are Point geometries
|
|
- ✅ Coordinates are [lng, lat] format
|
|
- ✅ Realistic values based on income/region
|
|
- ✅ Proper property names
|
|
|
|
### Layer Validation
|
|
The layer factory ensures:
|
|
- ✅ Circle layers (work with Points)
|
|
- ✅ Zoom-responsive sizing
|
|
- ✅ Proper color expressions
|
|
- ✅ Performance optimizations
|
|
|
|
## Migration Checklist
|
|
|
|
For each demo (globe_10, 11, 12, 13):
|
|
|
|
- [ ] Update `index.html` to import shared config
|
|
- [ ] Replace `src/index.js` with new architecture
|
|
- [ ] Remove old data file (e.g., `src/data/data.js`)
|
|
- [ ] Update imports to use shared modules
|
|
- [ ] Test in browser:
|
|
- [ ] Globe renders
|
|
- [ ] Data appears as circles
|
|
- [ ] Hover works
|
|
- [ ] Timeline works (if applicable)
|
|
- [ ] Colors look correct
|
|
- [ ] No console errors
|
|
|
|
## Benefits of New Architecture
|
|
|
|
✅ **Reliability**: Valid token guaranteed
|
|
✅ **Consistency**: All demos use same patterns
|
|
✅ **Maintainability**: Fix bugs in one place
|
|
✅ **Performance**: Best-practice layers
|
|
✅ **Validation**: Automatic error detection
|
|
✅ **Scalability**: Easy to add new demos
|
|
|
|
## Next Steps
|
|
|
|
1. Use this architecture to fix globe_10 (Polio)
|
|
2. Verify it works perfectly
|
|
3. Apply same pattern to globe_11 (Measles)
|
|
4. Apply same pattern to globe_12 (Smallpox)
|
|
5. Apply same pattern to globe_13 (DTP3)
|
|
6. All 5 demos will work beautifully!
|
|
|
|
## Support
|
|
|
|
If you encounter issues:
|
|
|
|
1. **Check browser console** - validation messages appear there
|
|
2. **Verify token** - Run `MAPBOX_CONFIG.validateToken()` in console
|
|
3. **Check data** - Run `generateVaccineData('polio')` to see generated data
|
|
4. **Check layer** - Use Mapbox GL Inspector to see layers
|
|
|
|
---
|
|
|
|
**Created**: 2025
|
|
**Purpose**: Fix broken Mapbox globe visualizations with shared, validated architecture
|
|
**Status**: Ready for implementation
|