252 lines
9.9 KiB
HTML
252 lines
9.9 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Measles Diagnostic</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=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' />
|
|
<style>
|
|
body { margin: 0; padding: 0; }
|
|
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
|
|
#diagnostic {
|
|
position: fixed;
|
|
top: 10px;
|
|
right: 10px;
|
|
width: 400px;
|
|
max-height: 90vh;
|
|
overflow-y: auto;
|
|
background: rgba(0, 0, 0, 0.9);
|
|
color: #0f0;
|
|
padding: 20px;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 11px;
|
|
border: 2px solid #0f0;
|
|
z-index: 10000;
|
|
}
|
|
h3 { color: #0ff; margin: 10px 0 5px 0; font-size: 13px; }
|
|
.error { color: #f00; }
|
|
.warning { color: #ff0; }
|
|
.success { color: #0f0; }
|
|
.data-sample {
|
|
background: #111;
|
|
padding: 10px;
|
|
margin: 5px 0;
|
|
border-left: 3px solid #0f0;
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="map"></div>
|
|
<div id="diagnostic">
|
|
<h2 style="color: #0ff;">🔬 MEASLES DIAGNOSTIC</h2>
|
|
<div id="output">Initializing...</div>
|
|
</div>
|
|
|
|
<script type="module">
|
|
import { MAPBOX_CONFIG } from './shared/mapbox-config.js';
|
|
import { generateVaccineData } from './shared/data-generator.js';
|
|
import { LayerFactory, COLOR_SCALES } from './shared/layer-factory.js';
|
|
|
|
const output = document.getElementById('output');
|
|
let logBuffer = '';
|
|
|
|
function log(msg, type = 'normal') {
|
|
const className = type === 'error' ? 'error' : type === 'warning' ? 'warning' : type === 'success' ? 'success' : '';
|
|
logBuffer += `<div class="${className}">${msg}</div>`;
|
|
output.innerHTML = logBuffer;
|
|
console.log(msg);
|
|
}
|
|
|
|
// Generate data
|
|
log('Generating measles data...', 'normal');
|
|
const measlesData = generateVaccineData('measles');
|
|
log(`✅ Generated ${measlesData.features.length} features`, 'success');
|
|
|
|
// Sample first 3 countries
|
|
log('<h3>Sample Data (first 3 countries):</h3>');
|
|
measlesData.features.slice(0, 3).forEach((f, i) => {
|
|
const p = f.properties;
|
|
log(`<div class="data-sample">
|
|
<b>${i + 1}. ${p.name}</b><br>
|
|
Population: ${p.population?.toLocaleString() || 'N/A'}<br>
|
|
Coverage Dose 1: ${p.coverage_dose1}%<br>
|
|
Coverage Dose 2: ${p.coverage_dose2}%<br>
|
|
Cases 2023: ${p.cases_2023?.toLocaleString() || 'N/A'}<br>
|
|
Deaths 2023: ${p.deaths_2023}<br>
|
|
Coords: [${f.geometry.coordinates}]
|
|
</div>`);
|
|
});
|
|
|
|
// Check data statistics
|
|
const populations = measlesData.features.map(f => f.properties.population || 0);
|
|
const coverages = measlesData.features.map(f => f.properties.coverage_dose1 || 0);
|
|
const cases = measlesData.features.map(f => f.properties.cases_2023 || 0);
|
|
|
|
log('<h3>Data Statistics:</h3>');
|
|
log(`<div class="data-sample">
|
|
Population range: ${Math.min(...populations).toLocaleString()} - ${Math.max(...populations).toLocaleString()}<br>
|
|
Coverage range: ${Math.min(...coverages)}% - ${Math.max(...coverages)}%<br>
|
|
Cases range: ${Math.min(...cases).toLocaleString()} - ${Math.max(...cases).toLocaleString()}<br>
|
|
Countries with cases > 100: ${cases.filter(c => c > 100).length}<br>
|
|
Countries with cases > 1000: ${cases.filter(c => c > 1000).length}
|
|
</div>`);
|
|
|
|
// Initialize map
|
|
MAPBOX_CONFIG.applyToken();
|
|
log('✅ Token applied', 'success');
|
|
|
|
const map = new mapboxgl.Map({
|
|
container: 'map',
|
|
...MAPBOX_CONFIG.getMapOptions({
|
|
style: 'mapbox://styles/mapbox/dark-v11',
|
|
center: [15, 25],
|
|
zoom: 1.5
|
|
})
|
|
});
|
|
|
|
log('Map initialized, waiting for load...');
|
|
|
|
map.on('load', () => {
|
|
log('✅ Map loaded', 'success');
|
|
|
|
const factory = new LayerFactory(map);
|
|
factory.applyGlobeAtmosphere({ theme: 'dark' });
|
|
|
|
// Add source
|
|
map.addSource('measles-data', {
|
|
type: 'geojson',
|
|
data: measlesData
|
|
});
|
|
log('✅ Source added', 'success');
|
|
|
|
// Create coverage layer
|
|
log('<h3>Creating Coverage Layer:</h3>');
|
|
const coverageLayer = factory.createCircleLayer({
|
|
id: 'coverage-layer',
|
|
source: 'measles-data',
|
|
sizeProperty: 'population',
|
|
colorProperty: 'coverage_dose1',
|
|
colorScale: 'coverageReverse',
|
|
sizeRange: [4, 25],
|
|
opacityRange: [0.7, 0.85]
|
|
});
|
|
|
|
log(`Layer ID: ${coverageLayer.id}`);
|
|
log(`Layer Type: ${coverageLayer.type}`);
|
|
log(`Has size expression: ${!!coverageLayer.paint['circle-radius']}`);
|
|
log(`Has color expression: ${!!coverageLayer.paint['circle-color']}`);
|
|
|
|
try {
|
|
map.addLayer(coverageLayer);
|
|
log('✅ Coverage layer added', 'success');
|
|
} catch (e) {
|
|
log(`❌ ERROR adding coverage layer: ${e.message}`, 'error');
|
|
console.error('Coverage layer error:', e);
|
|
}
|
|
|
|
// Create outbreak layer
|
|
log('<h3>Creating Outbreak Layer:</h3>');
|
|
const outbreaksLayer = factory.createCircleLayer({
|
|
id: 'outbreaks-layer',
|
|
source: 'measles-data',
|
|
sizeProperty: 'cases_2023',
|
|
colorProperty: 'deaths_2023',
|
|
sizeRange: [6, 32],
|
|
opacityRange: [0.6, 0.8]
|
|
});
|
|
|
|
outbreaksLayer.paint['circle-color'] = 'rgba(239, 83, 80, 0.7)';
|
|
outbreaksLayer.paint['circle-stroke-color'] = '#ef5350';
|
|
outbreaksLayer.paint['circle-stroke-width'] = 2;
|
|
outbreaksLayer.filter = ['>', ['coalesce', ['get', 'cases_2023'], 0], 100];
|
|
|
|
log(`Outbreak filter: ${JSON.stringify(outbreaksLayer.filter)}`);
|
|
log(`Outbreak color: ${outbreaksLayer.paint['circle-color']}`);
|
|
|
|
try {
|
|
map.addLayer(outbreaksLayer);
|
|
log('✅ Outbreak layer added', 'success');
|
|
} catch (e) {
|
|
log(`❌ ERROR adding outbreak layer: ${e.message}`, 'error');
|
|
console.error('Outbreak layer error:', e);
|
|
}
|
|
|
|
// Wait a moment then query layers
|
|
setTimeout(() => {
|
|
log('<h3>Layer Check:</h3>');
|
|
const allLayers = map.getStyle().layers;
|
|
const ourLayers = allLayers.filter(l =>
|
|
l.id === 'coverage-layer' || l.id === 'outbreaks-layer'
|
|
);
|
|
|
|
ourLayers.forEach(layer => {
|
|
log(`<div class="data-sample">
|
|
<b>${layer.id}</b><br>
|
|
Type: ${layer.type}<br>
|
|
Source: ${layer.source}<br>
|
|
Visibility: ${layer.layout?.visibility || 'visible'}<br>
|
|
Has filter: ${layer.filter ? 'Yes' : 'No'}
|
|
</div>`);
|
|
});
|
|
|
|
// Query features
|
|
log('<h3>Features on Map:</h3>');
|
|
const coverageFeatures = map.querySourceFeatures('measles-data', {
|
|
sourceLayer: null,
|
|
filter: null
|
|
});
|
|
log(`Total features in source: ${coverageFeatures.length}`, 'success');
|
|
|
|
const renderedCoverage = map.queryRenderedFeatures({ layers: ['coverage-layer'] });
|
|
const renderedOutbreaks = map.queryRenderedFeatures({ layers: ['outbreaks-layer'] });
|
|
|
|
log(`Coverage layer rendering: ${renderedCoverage.length} circles`);
|
|
log(`Outbreak layer rendering: ${renderedOutbreaks.length} circles`);
|
|
|
|
if (renderedCoverage.length === 0) {
|
|
log('⚠️ WARNING: Coverage layer not rendering any features!', 'warning');
|
|
}
|
|
if (renderedOutbreaks.length === 0) {
|
|
log('⚠️ WARNING: Outbreak layer not rendering any features!', 'warning');
|
|
}
|
|
|
|
// Sample rendered feature
|
|
if (renderedCoverage.length > 0) {
|
|
const sample = renderedCoverage[0];
|
|
log(`<div class="data-sample">
|
|
<b>Sample Coverage Circle:</b><br>
|
|
${sample.properties.name}<br>
|
|
Population: ${sample.properties.population}<br>
|
|
Coverage: ${sample.properties.coverage_dose1}%
|
|
</div>`);
|
|
}
|
|
|
|
if (renderedOutbreaks.length > 0) {
|
|
const sample = renderedOutbreaks[0];
|
|
log(`<div class="data-sample">
|
|
<b>Sample Outbreak Circle:</b><br>
|
|
${sample.properties.name}<br>
|
|
Cases: ${sample.properties.cases_2023}<br>
|
|
Deaths: ${sample.properties.deaths_2023}
|
|
</div>`);
|
|
}
|
|
|
|
}, 2000);
|
|
});
|
|
|
|
map.on('error', (e) => {
|
|
log(`❌ Map Error: ${e.error.message}`, 'error');
|
|
});
|
|
|
|
// Expose map globally for debugging
|
|
window.debugMap = map;
|
|
log('<h3>Debug tip:</h3>');
|
|
log('Type "debugMap" in console to access map object');
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|