/** * 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(); }