infinite-agents-public/mapbox_test/shared/data-generator.js

438 lines
20 KiB
JavaScript

/**
* Unified Vaccine Data Generator
*
* Generates realistic, geographically accurate Point-based GeoJSON data
* for vaccine visualization globe demos. Uses country centroids and
* realistic metric generation.
*/
/**
* Country centroids and metadata (WHO regions, income levels)
* This is a curated list of major countries with accurate geographic centers
*/
const COUNTRY_DATA = [
// Africa (AFRO)
{ name: 'Nigeria', iso3: 'NGA', centroid: [8.6753, 9.0820], region: 'AFRO', income: 'lower-middle', population: 218541000 },
{ name: 'Ethiopia', iso3: 'ETH', centroid: [40.4897, 9.1450], region: 'AFRO', income: 'low', population: 123379000 },
{ name: 'Egypt', iso3: 'EGY', centroid: [30.8025, 26.8206], region: 'AFRO', income: 'lower-middle', population: 109262000 },
{ name: 'Democratic Republic of the Congo', iso3: 'COD', centroid: [21.7587, -4.0383], region: 'AFRO', income: 'low', population: 99010000 },
{ name: 'South Africa', iso3: 'ZAF', centroid: [22.9375, -30.5595], region: 'AFRO', income: 'upper-middle', population: 60041000 },
{ name: 'Tanzania', iso3: 'TZA', centroid: [34.8888, -6.3690], region: 'AFRO', income: 'lower-middle', population: 65497000 },
{ name: 'Kenya', iso3: 'KEN', centroid: [37.9062, -0.0236], region: 'AFRO', income: 'lower-middle', population: 54027000 },
{ name: 'Uganda', iso3: 'UGA', centroid: [32.2903, 1.3733], region: 'AFRO', income: 'low', population: 47249000 },
{ name: 'Algeria', iso3: 'DZA', centroid: [1.6596, 28.0339], region: 'AFRO', income: 'lower-middle', population: 44903000 },
{ name: 'Sudan', iso3: 'SDN', centroid: [30.2176, 12.8628], region: 'AFRO', income: 'low', population: 46874000 },
{ name: 'Morocco', iso3: 'MAR', centroid: [-7.0926, 31.7917], region: 'AFRO', income: 'lower-middle', population: 37457000 },
{ name: 'Ghana', iso3: 'GHA', centroid: [-1.0232, 7.9465], region: 'AFRO', income: 'lower-middle', population: 33476000 },
{ name: 'Mozambique', iso3: 'MOZ', centroid: [35.5296, -18.6657], region: 'AFRO', income: 'low', population: 32969000 },
{ name: 'Madagascar', iso3: 'MDG', centroid: [46.8691, -18.7669], region: 'AFRO', income: 'low', population: 29611000 },
{ name: 'Cameroon', iso3: 'CMR', centroid: [12.3547, 7.3697], region: 'AFRO', income: 'lower-middle', population: 27914000 },
// Eastern Mediterranean (EMRO)
{ name: 'Pakistan', iso3: 'PAK', centroid: [69.3451, 30.3753], region: 'EMRO', income: 'lower-middle', population: 235825000 },
{ name: 'Iran', iso3: 'IRN', centroid: [53.6880, 32.4279], region: 'EMRO', income: 'lower-middle', population: 88550000 },
{ name: 'Saudi Arabia', iso3: 'SAU', centroid: [45.0792, 23.8859], region: 'EMRO', income: 'high', population: 36409000 },
{ name: 'Yemen', iso3: 'YEM', centroid: [48.5164, 15.5527], region: 'EMRO', income: 'low', population: 33697000 },
{ name: 'Iraq', iso3: 'IRQ', centroid: [43.6793, 33.2232], region: 'EMRO', income: 'upper-middle', population: 44496000 },
{ name: 'Afghanistan', iso3: 'AFG', centroid: [67.7100, 33.9391], region: 'EMRO', income: 'low', population: 41129000 },
{ name: 'Morocco', iso3: 'MAR', centroid: [-7.0926, 31.7917], region: 'EMRO', income: 'lower-middle', population: 37457000 },
{ name: 'Somalia', iso3: 'SOM', centroid: [46.1996, 5.1521], region: 'EMRO', income: 'low', population: 17597000 },
// Europe (EURO)
{ name: 'Russia', iso3: 'RUS', centroid: [105.3188, 61.5240], region: 'EURO', income: 'upper-middle', population: 144713000 },
{ name: 'Germany', iso3: 'DEU', centroid: [10.4515, 51.1657], region: 'EURO', income: 'high', population: 83294000 },
{ name: 'United Kingdom', iso3: 'GBR', centroid: [-3.4360, 55.3781], region: 'EURO', income: 'high', population: 67736000 },
{ name: 'France', iso3: 'FRA', centroid: [2.2137, 46.2276], region: 'EURO', income: 'high', population: 64626000 },
{ name: 'Italy', iso3: 'ITA', centroid: [12.5674, 41.8719], region: 'EURO', income: 'high', population: 58983000 },
{ name: 'Spain', iso3: 'ESP', centroid: [-3.7492, 40.4637], region: 'EURO', income: 'high', population: 47778000 },
{ name: 'Ukraine', iso3: 'UKR', centroid: [31.1656, 48.3794], region: 'EURO', income: 'lower-middle', population: 43814000 },
{ name: 'Poland', iso3: 'POL', centroid: [19.1451, 51.9194], region: 'EURO', income: 'high', population: 38307000 },
{ name: 'Romania', iso3: 'ROU', centroid: [24.9668, 45.9432], region: 'EURO', income: 'upper-middle', population: 19064000 },
{ name: 'Netherlands', iso3: 'NLD', centroid: [5.2913, 52.1326], region: 'EURO', income: 'high', population: 17564000 },
// Americas (PAHO)
{ name: 'United States', iso3: 'USA', centroid: [-95.7129, 37.0902], region: 'PAHO', income: 'high', population: 339996000 },
{ name: 'Brazil', iso3: 'BRA', centroid: [-51.9253, -14.2350], region: 'PAHO', income: 'upper-middle', population: 216422000 },
{ name: 'Mexico', iso3: 'MEX', centroid: [-102.5528, 23.6345], region: 'PAHO', income: 'upper-middle', population: 128456000 },
{ name: 'Colombia', iso3: 'COL', centroid: [-74.2973, 4.5709], region: 'PAHO', income: 'upper-middle', population: 52085000 },
{ name: 'Argentina', iso3: 'ARG', centroid: [-63.6167, -38.4161], region: 'PAHO', income: 'upper-middle', population: 45510000 },
{ name: 'Canada', iso3: 'CAN', centroid: [-106.3468, 56.1304], region: 'PAHO', income: 'high', population: 38781000 },
{ name: 'Peru', iso3: 'PER', centroid: [-75.0152, -9.1900], region: 'PAHO', income: 'upper-middle', population: 34352000 },
{ name: 'Venezuela', iso3: 'VEN', centroid: [-66.5897, 6.4238], region: 'PAHO', income: 'upper-middle', population: 28302000 },
{ name: 'Chile', iso3: 'CHL', centroid: [-71.5430, -35.6751], region: 'PAHO', income: 'high', population: 19603000 },
{ name: 'Guatemala', iso3: 'GTM', centroid: [-90.2308, 15.7835], region: 'PAHO', income: 'upper-middle', population: 18092000 },
{ name: 'Haiti', iso3: 'HTI', centroid: [-72.2852, 18.9712], region: 'PAHO', income: 'low', population: 11584000 },
// South-East Asia (SEARO)
{ name: 'India', iso3: 'IND', centroid: [78.9629, 20.5937], region: 'SEARO', income: 'lower-middle', population: 1428627000 },
{ name: 'Indonesia', iso3: 'IDN', centroid: [113.9213, -0.7893], region: 'SEARO', income: 'lower-middle', population: 277534000 },
{ name: 'Bangladesh', iso3: 'BGD', centroid: [90.3563, 23.6850], region: 'SEARO', income: 'lower-middle', population: 172954000 },
{ name: 'Thailand', iso3: 'THA', centroid: [100.9925, 15.8700], region: 'SEARO', income: 'upper-middle', population: 71801000 },
{ name: 'Myanmar', iso3: 'MMR', centroid: [95.9560, 21.9162], region: 'SEARO', income: 'lower-middle', population: 54577000 },
{ name: 'Sri Lanka', iso3: 'LKA', centroid: [80.7718, 7.8731], region: 'SEARO', income: 'lower-middle', population: 22181000 },
{ name: 'Nepal', iso3: 'NPL', centroid: [84.1240, 28.3949], region: 'SEARO', income: 'lower-middle', population: 30547000 },
// Western Pacific (WPRO)
{ name: 'China', iso3: 'CHN', centroid: [104.1954, 35.8617], region: 'WPRO', income: 'upper-middle', population: 1425671000 },
{ name: 'Philippines', iso3: 'PHL', centroid: [121.7740, 12.8797], region: 'WPRO', income: 'lower-middle', population: 117337000 },
{ name: 'Japan', iso3: 'JPN', centroid: [138.2529, 36.2048], region: 'WPRO', income: 'high', population: 123294000 },
{ name: 'Vietnam', iso3: 'VNM', centroid: [108.2772, 14.0583], region: 'WPRO', income: 'lower-middle', population: 98859000 },
{ name: 'South Korea', iso3: 'KOR', centroid: [127.7669, 35.9078], region: 'WPRO', income: 'high', population: 51784000 },
{ name: 'Australia', iso3: 'AUS', centroid: [133.7751, -25.2744], region: 'WPRO', income: 'high', population: 26439000 },
{ name: 'Malaysia', iso3: 'MYS', centroid: [101.9758, 4.2105], region: 'WPRO', income: 'upper-middle', population: 34308000 },
{ name: 'Cambodia', iso3: 'KHM', centroid: [104.9910, 12.5657], region: 'WPRO', income: 'lower-middle', population: 16944000 },
{ name: 'Papua New Guinea', iso3: 'PNG', centroid: [143.9555, -6.3150], region: 'WPRO', income: 'lower-middle', population: 10329000 },
{ name: 'New Zealand', iso3: 'NZL', centroid: [174.8860, -40.9006], region: 'WPRO', income: 'high', population: 5228000 }
];
/**
* Vaccine Data Generator Class
*/
export class VaccineDataGenerator {
constructor(vaccineType) {
this.vaccineType = vaccineType;
this.countries = COUNTRY_DATA;
}
/**
* Generate complete GeoJSON FeatureCollection
* @returns {Object} GeoJSON FeatureCollection with Point features
*/
generateData() {
return {
type: 'FeatureCollection',
features: this.countries.map(country => this.generateFeature(country))
};
}
/**
* Generate a single GeoJSON feature for a country
* @param {Object} country - Country metadata
* @returns {Object} GeoJSON Feature
*/
generateFeature(country) {
return {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: country.centroid // [lng, lat]
},
properties: {
name: country.name,
iso3: country.iso3,
region: country.region,
income_level: country.income,
population: country.population,
...this.generateVaccineMetrics(country)
}
};
}
/**
* Generate vaccine-specific metrics based on type
* @param {Object} country - Country metadata
* @returns {Object} Vaccine-specific properties
*/
generateVaccineMetrics(country) {
const generators = {
polio: () => this.generatePolioMetrics(country),
measles: () => this.generateMeaslesMetrics(country),
smallpox: () => this.generateSmallpoxMetrics(country),
dtp3: () => this.generateDTP3Metrics(country),
hpv: () => this.generateHPVMetrics(country)
};
return generators[this.vaccineType]?.() || {};
}
/**
* Polio-specific metrics
*/
generatePolioMetrics(country) {
const baseCoverage1980 = this.getBaseCoverage(country, 1980);
const baseCoverage2020 = this.getBaseCoverage(country, 2020);
return {
coverage_1980: baseCoverage1980,
coverage_1985: this.interpolate(baseCoverage1980, baseCoverage2020, 0.125),
coverage_1990: this.interpolate(baseCoverage1980, baseCoverage2020, 0.25),
coverage_1995: this.interpolate(baseCoverage1980, baseCoverage2020, 0.375),
coverage_2000: this.interpolate(baseCoverage1980, baseCoverage2020, 0.5),
coverage_2005: this.interpolate(baseCoverage1980, baseCoverage2020, 0.625),
coverage_2010: this.interpolate(baseCoverage1980, baseCoverage2020, 0.75),
coverage_2015: this.interpolate(baseCoverage1980, baseCoverage2020, 0.875),
coverage_2020: baseCoverage2020,
polio_free_year: this.getPolioFreeYear(country),
endemic: this.isPolioEndemic(country)
};
}
/**
* Measles-specific metrics
*/
generateMeaslesMetrics(country) {
const dose1 = this.getMeaslesCoverage(country, 'dose1');
const dose2 = this.getMeaslesCoverage(country, 'dose2');
return {
coverage_dose1: dose1,
coverage_dose2: dose2,
cases_2023: this.calculateMeaslesCases(country, dose1),
deaths_2023: this.calculateMeaslesDeaths(country, dose1)
};
}
/**
* Smallpox-specific metrics (historical)
*/
generateSmallpoxMetrics(country) {
return {
endemic_1950: this.wasEndemicIn1950(country),
endemic_1960: this.wasEndemicIn1960(country),
endemic_1970: this.wasEndemicIn1970(country),
endemic_1980: false, // Eradicated by 1980
eradication_year: this.getEradicationYear(country),
last_case_year: this.getLastCaseYear(country),
vaccination_intensity: this.getVaccinationIntensity(country),
cases_peak_year: this.random(1950, 1970),
cases_peak: this.random(10000, 500000)
};
}
/**
* DTP3-specific metrics
*/
generateDTP3Metrics(country) {
const coverage2024 = this.getDTP3Coverage(country);
const coverage1974 = this.random(5, 30);
return {
dtp3_coverage_2024: coverage2024,
dtp3_coverage_1974: coverage1974,
zero_dose_children: this.calculateZeroDose(country, coverage2024),
under5_mortality_rate: this.calculateMortality(country, coverage2024),
infant_deaths_prevented: this.calculateLivesSaved(country, coverage2024, coverage1974),
population_under1: Math.round(country.population * 0.012) // ~1.2% birth rate
};
}
/**
* HPV-specific metrics
*/
generateHPVMetrics(country) {
const coverage = this.getHPVCoverage(country);
return {
hpv_coverage_2024: coverage,
vaccine_program_started: coverage > 0 ? this.random(2008, 2020) : null,
target_age: '9-14',
cervical_cancer_incidence: this.getCervicalCancerRate(country, coverage),
cervical_cancer_mortality: this.getCervicalCancerMortality(country, coverage),
lives_saved_projected: this.calculateHPVLivesSaved(country, coverage),
gender_policy: this.random(0, 100) > 30 ? 'girls-only' : 'girls-and-boys',
annual_deaths: this.calculateAnnualDeaths(country, coverage)
};
}
// ===== HELPER METHODS =====
/**
* Get base vaccine coverage based on income and region
*/
getBaseCoverage(country, year) {
let base = 50;
// Income level adjustments
if (country.income === 'high') base += 30;
else if (country.income === 'upper-middle') base += 20;
else if (country.income === 'lower-middle') base += 5;
else base -= 10;
// Year adjustments (coverage improved over time)
if (year >= 2010) base += 20;
else if (year >= 2000) base += 15;
else if (year >= 1990) base += 10;
else if (year >= 1980) base += 5;
// Regional variations
if (country.region === 'EURO' || country.region === 'PAHO') base += 10;
if (country.region === 'AFRO') base -= 5;
// Add randomness
base += this.random(-8, 8);
return Math.max(5, Math.min(99, Math.round(base)));
}
getDTP3Coverage(country) {
return this.getBaseCoverage(country, 2024);
}
getMeaslesCoverage(country, dose) {
const base = this.getBaseCoverage(country, 2023);
if (dose === 'dose2') {
return Math.round(base * 0.85); // Dose 2 typically lower
}
return base;
}
getHPVCoverage(country) {
// HPV coverage is generally lower and more variable
let coverage = this.getBaseCoverage(country, 2024) * 0.7;
// Some low-income countries have no program
if (country.income === 'low' && this.random(0, 100) > 40) {
coverage = 0;
}
return Math.round(coverage);
}
/**
* Calculate derived metrics
*/
calculateZeroDose(country, coverage) {
const birthCohort = Math.round(country.population * 0.012);
return Math.round(birthCohort * (100 - coverage) / 100);
}
calculateMortality(country, coverage) {
// Lower coverage = higher mortality
let baseMortality = 50;
if (country.income === 'high') baseMortality = 5;
else if (country.income === 'upper-middle') baseMortality = 15;
else if (country.income === 'lower-middle') baseMortality = 35;
// Coverage impact
const coverageImpact = (100 - coverage) * 0.5;
return Math.round(baseMortality + coverageImpact);
}
calculateLivesSaved(country, currentCoverage, historicalCoverage) {
const improvement = currentCoverage - historicalCoverage;
const birthCohort = Math.round(country.population * 0.012);
const mortalityRate = this.calculateMortality(country, currentCoverage) / 1000;
return Math.round(birthCohort * (improvement / 100) * mortalityRate * 50); // 50 years
}
calculateMeaslesCases(country, coverage) {
// Cases inversely related to coverage
const susceptible = (100 - coverage) / 100;
const birthCohort = Math.round(country.population * 0.015);
return Math.round(birthCohort * susceptible * this.random(0.1, 0.4));
}
calculateMeaslesDeaths(country, coverage) {
const cases = this.calculateMeaslesCases(country, coverage);
const cfr = country.income === 'low' ? 0.05 : country.income === 'lower-middle' ? 0.02 : 0.005;
return Math.round(cases * cfr);
}
getCervicalCancerRate(country, coverage) {
// Higher coverage = lower cancer rate
let baseRate = 25;
if (country.income === 'high') baseRate = 8;
else if (country.income === 'upper-middle') baseRate = 15;
const coverageImpact = (100 - coverage) * 0.15;
return Math.max(2, Math.round(baseRate + coverageImpact));
}
getCervicalCancerMortality(country, coverage) {
return Math.round(this.getCervicalCancerRate(country, coverage) * 0.55);
}
calculateHPVLivesSaved(country, coverage) {
const womenPopulation = country.population * 0.5;
const targetAge = womenPopulation * 0.15; // 15% in target age
const cancerRate = this.getCervicalCancerRate(country, 0) / 100000;
return Math.round(targetAge * cancerRate * (coverage / 100) * 0.87); // 87% effectiveness
}
calculateAnnualDeaths(country, coverage) {
const incidence = this.getCervicalCancerRate(country, coverage);
const womenPopulation = country.population * 0.5;
return Math.round((incidence / 100000) * womenPopulation * 0.55);
}
/**
* Historical data helpers
*/
getPolioFreeYear(country) {
if (this.isPolioEndemic(country)) return null;
// Americas: 1994, Western Pacific: 2000, Europe: 2002, Southeast Asia: 2014, Africa: 2020
const regionYears = {
'PAHO': 1994,
'WPRO': 2000,
'EURO': 2002,
'SEARO': 2014,
'AFRO': 2020,
'EMRO': null
};
return regionYears[country.region] || 2020;
}
isPolioEndemic(country) {
// Only Pakistan and Afghanistan remain endemic
return ['PAK', 'AFG'].includes(country.iso3);
}
wasEndemicIn1950(country) {
// Most countries were endemic in 1950
return country.income !== 'high' || this.random(0, 100) > 70;
}
wasEndemicIn1960(country) {
return this.wasEndemicIn1950(country) && country.income !== 'high';
}
wasEndemicIn1970(country) {
return country.income === 'low' || (country.income === 'lower-middle' && this.random(0, 100) > 50);
}
getEradicationYear(country) {
if (country.income === 'high') return this.random(1950, 1965);
if (country.income === 'upper-middle') return this.random(1960, 1975);
return this.random(1970, 1978);
}
getLastCaseYear(country) {
return this.getEradicationYear(country) - this.random(1, 3);
}
getVaccinationIntensity(country) {
if (country.income === 'high') return this.random(80, 100);
if (country.income === 'upper-middle') return this.random(60, 85);
if (country.income === 'lower-middle') return this.random(40, 70);
return this.random(20, 55);
}
/**
* Utility functions
*/
random(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
interpolate(start, end, factor) {
return Math.round(start + (end - start) * factor);
}
}
// Export factory function for easy use
export function generateVaccineData(vaccineType) {
const generator = new VaccineDataGenerator(vaccineType);
return generator.generateData();
}