// Mapbox Globe Visualization 14: HPV Vaccine Impact on Cervical Cancer
// Demonstrates multi-layer correlation visualization with 3D extrusions,
// dual choropleth encoding, and gender health equity analysis
// Mapbox access token
mapboxgl.accessToken = 'pk.eyJ1IjoibGludXhpc2Nvb2wiLCJhIjoiY2w3ajM1MnliMDV4NDNvb2J5c3V5dzRxZyJ9.wJukH5hVSiO74GM_VSJR3Q';
// Initialize map with globe projection
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/dark-v11',
projection: 'globe',
center: [20, 15],
zoom: 1.6,
pitch: 0
});
// Auto-rotation state
let userInteracting = false;
let rotationActive = false;
// Track current styling metrics
let currentSizeMetric = 'coverage';
let currentColorMetric = 'cancer-rate';
let show3DExtrusion = false;
// Expression definitions for different metrics
const sizeExpressions = {
'coverage': [
'interpolate',
['linear'],
['get', 'hpv_coverage_2024'],
0, 4, // No program
20, 8,
40, 12,
60, 17,
80, 23,
95, 30 // Excellent coverage
],
'cancer-rate': [
'interpolate',
['linear'],
['get', 'cervical_cancer_incidence'],
2, 4, // Very low incidence
5, 7,
10, 11,
15, 16,
25, 22,
35, 27,
44, 32 // Very high incidence
],
'lives-saved': [
'interpolate',
['linear'],
['get', 'lives_saved_projected'],
0, 4,
100, 6,
500, 9,
1000, 12,
5000, 18,
10000, 24,
86000, 35 // India (largest potential)
],
'mortality': [
'interpolate',
['linear'],
['get', 'cervical_cancer_mortality'],
1.2, 4,
3, 8,
5, 12,
10, 18,
15, 24,
30.4, 32 // Eswatini (highest)
]
};
const colorExpressions = {
'coverage': [
'interpolate',
['linear'],
['get', 'hpv_coverage_2024'],
0, '#4a0e4e', // Dark purple - no program
15, '#6b1b6b',
30, '#8e2a8e',
50, '#b366ff', // Medium purple
70, '#d499ff',
90, '#ebccff' // Light purple - excellent coverage
],
'cancer-rate': [
'interpolate',
['linear'],
['get', 'cervical_cancer_incidence'],
2, '#66ffb3', // Green - very low incidence
5, '#99ff99',
10, '#ffff66', // Yellow - moderate
15, '#ffcc33',
20, '#ff9933', // Orange
30, '#ff6633',
40, '#ff3333', // Red - high incidence
44, '#cc0000' // Dark red - very high
],
'lives-saved': [
'interpolate',
['linear'],
['get', 'lives_saved_projected'],
0, '#0d4d4d', // Dark teal
500, '#1a7a7a',
2000, '#26a69a', // Teal
5000, '#4db6ac',
20000, '#80cbc4',
86000, '#b2dfdb' // Light teal - highest potential
],
'mortality': [
'interpolate',
['linear'],
['get', 'cervical_cancer_mortality'],
1.2, '#66ffb3', // Green - very low
3, '#99ff99',
6, '#ffff66', // Yellow
10, '#ffcc33',
15, '#ff6633', // Orange-red
22, '#ff3333',
30.4, '#cc0000' // Dark red - very high
]
};
// 3D extrusion height expression (cancer burden)
const extrusionHeightExpression = [
'interpolate',
['linear'],
['get', 'annual_deaths'],
0, 0,
100, 50000,
500, 150000,
1000, 250000,
5000, 500000,
10000, 800000,
77000, 1500000 // India (highest deaths)
];
map.on('load', () => {
// Configure globe atmosphere with purple-pink theme
map.setFog({
color: 'rgba(25, 15, 35, 0.9)',
'high-color': 'rgba(80, 50, 120, 0.5)',
'horizon-blend': 0.06,
'space-color': 'rgba(8, 5, 15, 1)',
'star-intensity': 0.8
});
// Add HPV vaccine data source
map.addSource('hpv-data', {
type: 'geojson',
data: hpvVaccineData
});
// Main circle layer - vaccine coverage and cancer correlation
map.addLayer({
id: 'hpv-circles',
type: 'circle',
source: 'hpv-data',
paint: {
// SIZE: Based on current size metric
'circle-radius': sizeExpressions[currentSizeMetric],
// COLOR: Based on current color metric
'circle-color': colorExpressions[currentColorMetric],
// OPACITY: Zoom-responsive
'circle-opacity': [
'interpolate',
['linear'],
['zoom'],
1, 0.8,
3, 0.85,
6, 0.92
],
// STROKE: Highlight countries without programs
'circle-stroke-color': [
'case',
['==', ['get', 'hpv_coverage_2024'], 0],
'#ff6eb4', // Pink for no program
'#ffffff' // White for countries with programs
],
'circle-stroke-width': [
'case',
['==', ['get', 'hpv_coverage_2024'], 0],
2.5, // Thicker stroke for no program (attention)
[
'interpolate',
['linear'],
['zoom'],
1, 0.5,
4, 1,
8, 1.5
]
]
}
});
// 3D extrusion layer for cancer burden (initially hidden)
map.addLayer({
id: 'cancer-burden-3d',
type: 'fill-extrusion',
source: 'hpv-data',
layout: {
'visibility': 'none'
},
paint: {
'fill-extrusion-color': [
'interpolate',
['linear'],
['get', 'cervical_cancer_incidence'],
2, '#4db6ac', // Teal - low incidence
10, '#ffeb3b', // Yellow
20, '#ff9800', // Orange
30, '#f44336', // Red
44, '#b71c1c' // Dark red - high incidence
],
'fill-extrusion-height': extrusionHeightExpression,
'fill-extrusion-base': 0,
'fill-extrusion-opacity': 0.85
}
});
// Labels for high-burden countries
map.addLayer({
id: 'country-labels',
type: 'symbol',
source: 'hpv-data',
filter: ['>=', ['get', 'annual_deaths'], 2000],
layout: {
'text-field': ['get', 'name'],
'text-size': [
'interpolate',
['linear'],
['get', 'annual_deaths'],
2000, 10,
10000, 12,
77000, 14
],
'text-offset': [0, 1.8],
'text-anchor': 'top',
'text-max-width': 8
},
paint: {
'text-color': '#b366ff',
'text-halo-color': '#0a0a0f',
'text-halo-width': 2,
'text-opacity': [
'interpolate',
['linear'],
['zoom'],
1, 0,
2.5, 0.5,
5, 0.85
]
},
minzoom: 2
});
// Interaction: Popup on hover
const popup = new mapboxgl.Popup({
closeButton: false,
closeOnClick: false,
offset: 15
});
map.on('mouseenter', 'hpv-circles', (e) => {
map.getCanvas().style.cursor = 'pointer';
const props = e.features[0].properties;
const coordinates = e.features[0].geometry.coordinates.slice();
// Determine coverage status
let coverageStatus = 'low';
if (props.hpv_coverage_2024 > 70) coverageStatus = 'high';
else if (props.hpv_coverage_2024 > 40) coverageStatus = 'medium';
// Determine cancer rate status (inverse)
let cancerStatus = 'high';
if (props.cervical_cancer_incidence < 10) cancerStatus = 'low';
else if (props.cervical_cancer_incidence < 20) cancerStatus = 'medium';
const programInfo = props.hpv_coverage_2024 > 0
? `Started: ${props.vaccine_program_started}
Target Age: ${props.target_age}
Policy: ${props.gender_policy === 'girls-and-boys' ? 'Girls & Boys' : 'Girls Only'}`
: 'No vaccination program';
const html = `