diff --git a/public/data/calculators/bmi-lms.json b/public/data/calculators/bmi-lms.json new file mode 100644 index 0000000..dba58cf --- /dev/null +++ b/public/data/calculators/bmi-lms.json @@ -0,0 +1,380 @@ +{ + "version": "1.0", + "source": "CDC 2000 BMI-for-age LMS values, 24-240 months every 6 months as previously embedded in calculators.js", + "lms": { + "male": { + "24": { + "L": -1.982374, + "M": 16.5478, + "S": 0.080127 + }, + "30": { + "L": -1.642107, + "M": 16.2497, + "S": 0.075499 + }, + "36": { + "L": -1.419991, + "M": 16.0003, + "S": 0.072634 + }, + "42": { + "L": -1.438165, + "M": 15.7941, + "S": 0.071495 + }, + "48": { + "L": -1.714869, + "M": 15.6282, + "S": 0.071889 + }, + "54": { + "L": -2.155348, + "M": 15.5026, + "S": 0.073491 + }, + "60": { + "L": -2.615166, + "M": 15.4191, + "S": 0.075992 + }, + "66": { + "L": -2.981797, + "M": 15.3795, + "S": 0.079211 + }, + "72": { + "L": -3.211705, + "M": 15.3835, + "S": 0.083048 + }, + "78": { + "L": -3.314769, + "M": 15.429, + "S": 0.0874 + }, + "84": { + "L": -3.323189, + "M": 15.5129, + "S": 0.092131 + }, + "90": { + "L": -3.270455, + "M": 15.6317, + "S": 0.097082 + }, + "96": { + "L": -3.183058, + "M": 15.7823, + "S": 0.102091 + }, + "102": { + "L": -3.079383, + "M": 15.9617, + "S": 0.107013 + }, + "108": { + "L": -2.971148, + "M": 16.1671, + "S": 0.111721 + }, + "114": { + "L": -2.865311, + "M": 16.3961, + "S": 0.116113 + }, + "120": { + "L": -2.765648, + "M": 16.6461, + "S": 0.120112 + }, + "126": { + "L": -2.673903, + "M": 16.9151, + "S": 0.123664 + }, + "132": { + "L": -2.59056, + "M": 17.2009, + "S": 0.126735 + }, + "138": { + "L": -2.51532, + "M": 17.5014, + "S": 0.129309 + }, + "144": { + "L": -2.447426, + "M": 17.8146, + "S": 0.131389 + }, + "150": { + "L": -2.385858, + "M": 18.1387, + "S": 0.132991 + }, + "156": { + "L": -2.329457, + "M": 18.4718, + "S": 0.134141 + }, + "162": { + "L": -2.277017, + "M": 18.812, + "S": 0.13488 + }, + "168": { + "L": -2.227362, + "M": 19.1576, + "S": 0.135251 + }, + "174": { + "L": -2.179426, + "M": 19.5067, + "S": 0.135309 + }, + "180": { + "L": -2.132345, + "M": 19.8577, + "S": 0.13511 + }, + "186": { + "L": -2.085574, + "M": 20.2086, + "S": 0.134718 + }, + "192": { + "L": -2.039015, + "M": 20.5576, + "S": 0.134198 + }, + "198": { + "L": -1.99315, + "M": 20.9029, + "S": 0.13362 + }, + "204": { + "L": -1.949135, + "M": 21.2425, + "S": 0.133057 + }, + "210": { + "L": -1.908831, + "M": 21.5742, + "S": 0.132585 + }, + "216": { + "L": -1.87467, + "M": 21.8959, + "S": 0.132286 + }, + "222": { + "L": -1.849323, + "M": 22.2054, + "S": 0.132249 + }, + "228": { + "L": -1.835138, + "M": 22.5007, + "S": 0.132566 + }, + "234": { + "L": -1.833401, + "M": 22.7799, + "S": 0.133339 + }, + "240": { + "L": -1.843581, + "M": 23.0414, + "S": 0.134675 + } + }, + "female": { + "24": { + "L": -1.024497, + "M": 16.388, + "S": 0.085026 + }, + "30": { + "L": -1.534542, + "M": 16.0059, + "S": 0.080932 + }, + "36": { + "L": -2.096829, + "M": 15.6992, + "S": 0.078605 + }, + "42": { + "L": -2.618733, + "M": 15.4647, + "S": 0.077904 + }, + "48": { + "L": -3.018522, + "M": 15.2985, + "S": 0.078713 + }, + "54": { + "L": -3.2593, + "M": 15.1961, + "S": 0.080904 + }, + "60": { + "L": -3.350078, + "M": 15.1519, + "S": 0.0843 + }, + "66": { + "L": -3.325522, + "M": 15.1606, + "S": 0.08868 + }, + "72": { + "L": -3.225607, + "M": 15.2169, + "S": 0.093803 + }, + "78": { + "L": -3.084291, + "M": 15.3161, + "S": 0.099427 + }, + "84": { + "L": -2.926187, + "M": 15.4536, + "S": 0.105325 + }, + "90": { + "L": -2.76731, + "M": 15.6252, + "S": 0.111295 + }, + "96": { + "L": -2.617192, + "M": 15.827, + "S": 0.117159 + }, + "102": { + "L": -2.480952, + "M": 16.0552, + "S": 0.122771 + }, + "108": { + "L": -2.360921, + "M": 16.3061, + "S": 0.128014 + }, + "114": { + "L": -2.257782, + "M": 16.5763, + "S": 0.132797 + }, + "120": { + "L": -2.171296, + "M": 16.8623, + "S": 0.137057 + }, + "126": { + "L": -2.100749, + "M": 17.161, + "S": 0.140754 + }, + "132": { + "L": -2.045235, + "M": 17.4691, + "S": 0.143868 + }, + "138": { + "L": -2.003802, + "M": 17.7836, + "S": 0.146399 + }, + "144": { + "L": -1.975521, + "M": 18.1015, + "S": 0.148361 + }, + "150": { + "L": -1.95952, + "M": 18.42, + "S": 0.149783 + }, + "156": { + "L": -1.954978, + "M": 18.7364, + "S": 0.150705 + }, + "162": { + "L": -1.9611, + "M": 19.0481, + "S": 0.151176 + }, + "168": { + "L": -1.977074, + "M": 19.3526, + "S": 0.151256 + }, + "174": { + "L": -2.002014, + "M": 19.6475, + "S": 0.15101 + }, + "180": { + "L": -2.034893, + "M": 19.9306, + "S": 0.150512 + }, + "186": { + "L": -2.07446, + "M": 20.1998, + "S": 0.149843 + }, + "192": { + "L": -2.119157, + "M": 20.4533, + "S": 0.14909 + }, + "198": { + "L": -2.167045, + "M": 20.6891, + "S": 0.148349 + }, + "204": { + "L": -2.215738, + "M": 20.9058, + "S": 0.147723 + }, + "210": { + "L": -2.262382, + "M": 21.1016, + "S": 0.147323 + }, + "216": { + "L": -2.303688, + "M": 21.2753, + "S": 0.147269 + }, + "222": { + "L": -2.336038, + "M": 21.4255, + "S": 0.147689 + }, + "228": { + "L": -2.355678, + "M": 21.5508, + "S": 0.148724 + }, + "234": { + "L": -2.35898, + "M": 21.6501, + "S": 0.150521 + }, + "240": { + "L": -2.342797, + "M": 21.7219, + "S": 0.153241 + } + } + } +} diff --git a/public/js/calculators.js b/public/js/calculators.js index 465cd9a..5ff3d99 100644 --- a/public/js/calculators.js +++ b/public/js/calculators.js @@ -1,5 +1,6 @@ import { parseAgeMonths, formatParsedAge } from './calculators/age.js'; import { calculateAapBilirubinAssessment, classifyBhutaniRisk, loadAapBilirubinThresholds, loadBhutaniZones } from './calculators/bilirubin.js'; +import { calculateBMIAssessment, loadBmiLmsData } from './calculators/bmi.js'; import { loadEquipmentData, renderEquipmentResult } from './calculators/equipment.js'; import { initImageLightbox } from './calculators/lightbox.js'; import { loadResusMedsData, renderResusMeds } from './calculators/resus.js'; @@ -701,21 +702,7 @@ initImageLightbox(); // BMI PERCENTILE — CDC 2000 // ═══════════════════════════════════════════════════════════ - // CDC BMI-for-Age 24-240mo (every 3 months, from CDC bmiagerev.csv) - var bmiLMS = {male:{24:{L:-1.982374,M:16.5478,S:0.080127},30:{L:-1.642107,M:16.2497,S:0.075499},36:{L:-1.419991,M:16.0003,S:0.072634},42:{L:-1.438165,M:15.7941,S:0.071495},48:{L:-1.714869,M:15.6282,S:0.071889},54:{L:-2.155348,M:15.5026,S:0.073491},60:{L:-2.615166,M:15.4191,S:0.075992},66:{L:-2.981797,M:15.3795,S:0.079211},72:{L:-3.211705,M:15.3835,S:0.083048},78:{L:-3.314769,M:15.429,S:0.0874},84:{L:-3.323189,M:15.5129,S:0.092131},90:{L:-3.270455,M:15.6317,S:0.097082},96:{L:-3.183058,M:15.7823,S:0.102091},102:{L:-3.079383,M:15.9617,S:0.107013},108:{L:-2.971148,M:16.1671,S:0.111721},114:{L:-2.865311,M:16.3961,S:0.116113},120:{L:-2.765648,M:16.6461,S:0.120112},126:{L:-2.673903,M:16.9151,S:0.123664},132:{L:-2.59056,M:17.2009,S:0.126735},138:{L:-2.51532,M:17.5014,S:0.129309},144:{L:-2.447426,M:17.8146,S:0.131389},150:{L:-2.385858,M:18.1387,S:0.132991},156:{L:-2.329457,M:18.4718,S:0.134141},162:{L:-2.277017,M:18.812,S:0.13488},168:{L:-2.227362,M:19.1576,S:0.135251},174:{L:-2.179426,M:19.5067,S:0.135309},180:{L:-2.132345,M:19.8577,S:0.13511},186:{L:-2.085574,M:20.2086,S:0.134718},192:{L:-2.039015,M:20.5576,S:0.134198},198:{L:-1.99315,M:20.9029,S:0.13362},204:{L:-1.949135,M:21.2425,S:0.133057},210:{L:-1.908831,M:21.5742,S:0.132585},216:{L:-1.87467,M:21.8959,S:0.132286},222:{L:-1.849323,M:22.2054,S:0.132249},228:{L:-1.835138,M:22.5007,S:0.132566},234:{L:-1.833401,M:22.7799,S:0.133339},240:{L:-1.843581,M:23.0414,S:0.134675}},female:{24:{L:-1.024497,M:16.388,S:0.085026},30:{L:-1.534542,M:16.0059,S:0.080932},36:{L:-2.096829,M:15.6992,S:0.078605},42:{L:-2.618733,M:15.4647,S:0.077904},48:{L:-3.018522,M:15.2985,S:0.078713},54:{L:-3.2593,M:15.1961,S:0.080904},60:{L:-3.350078,M:15.1519,S:0.0843},66:{L:-3.325522,M:15.1606,S:0.08868},72:{L:-3.225607,M:15.2169,S:0.093803},78:{L:-3.084291,M:15.3161,S:0.099427},84:{L:-2.926187,M:15.4536,S:0.105325},90:{L:-2.76731,M:15.6252,S:0.111295},96:{L:-2.617192,M:15.827,S:0.117159},102:{L:-2.480952,M:16.0552,S:0.122771},108:{L:-2.360921,M:16.3061,S:0.128014},114:{L:-2.257782,M:16.5763,S:0.132797},120:{L:-2.171296,M:16.8623,S:0.137057},126:{L:-2.100749,M:17.161,S:0.140754},132:{L:-2.045235,M:17.4691,S:0.143868},138:{L:-2.003802,M:17.7836,S:0.146399},144:{L:-1.975521,M:18.1015,S:0.148361},150:{L:-1.95952,M:18.42,S:0.149783},156:{L:-1.954978,M:18.7364,S:0.150705},162:{L:-1.9611,M:19.0481,S:0.151176},168:{L:-1.977074,M:19.3526,S:0.151256},174:{L:-2.002014,M:19.6475,S:0.15101},180:{L:-2.034893,M:19.9306,S:0.150512},186:{L:-2.07446,M:20.1998,S:0.149843},192:{L:-2.119157,M:20.4533,S:0.14909},198:{L:-2.167045,M:20.6891,S:0.148349},204:{L:-2.215738,M:20.9058,S:0.147723},210:{L:-2.262382,M:21.1016,S:0.147323},216:{L:-2.303688,M:21.2753,S:0.147269},222:{L:-2.336038,M:21.4255,S:0.147689},228:{L:-2.355678,M:21.5508,S:0.148724},234:{L:-2.35898,M:21.6501,S:0.150521},240:{L:-2.342797,M:21.7219,S:0.153241}}}; - - function calcBMIPercentile(bmi, L, M, S) { - // Z-score from LMS: Z = ((BMI/M)^L - 1) / (L * S) - var z; - if (Math.abs(L) < 0.001) { - z = Math.log(bmi / M) / S; - } else { - z = (Math.pow(bmi / M, L) - 1) / (L * S); - } - // Convert Z to percentile using standard normal CDF approximation - var p = normalCDF(z); - return { z: z, percentile: Math.round(p * 10000) / 100 }; - } + // CDC BMI-for-age LMS values load from /data/calculators/bmi-lms.json. function normalCDF(z) { var a1=0.254829592, a2=-0.284496736, a3=1.421413741, a4=-1.453152027, a5=1.061405429, p=0.3275911; @@ -961,26 +948,10 @@ initImageLightbox(); return { target: mph, low: mph - 8.5, high: mph + 8.5 }; } - // Extended BMI classification per CDC 2022 - // Uses % of 95th percentile for severe obesity staging - function classifyBMI(percentile, bmi, lms) { - // Calculate 95th percentile BMI from LMS - var p95z = 1.645; // Z for 95th percentile - var bmi95; - if (Math.abs(lms.L) < 0.001) bmi95 = lms.M * Math.exp(lms.S * p95z); - else bmi95 = lms.M * Math.pow(1 + lms.L * lms.S * p95z, 1 / lms.L); - - var pctOf95 = (bmi / bmi95) * 100; - - if (percentile >= 95) { - if (pctOf95 >= 140) return { text: 'Class 3 Severe Obesity', color: '#7f1d1d', bg: '#fecaca', pctOf95: pctOf95, bmi95: bmi95 }; - if (pctOf95 >= 120) return { text: 'Class 2 Severe Obesity', color: '#dc2626', bg: '#fee2e2', pctOf95: pctOf95, bmi95: bmi95 }; - return { text: 'Obese (Class 1)', color: '#ef4444', bg: '#fee2e2', pctOf95: pctOf95, bmi95: bmi95 }; - } - if (percentile >= 85) return { text: 'Overweight', color: '#f97316', bg: '#ffedd5', pctOf95: pctOf95, bmi95: bmi95 }; - if (percentile >= 5) return { text: 'Healthy Weight', color: '#10b981', bg: '#d1fae5', pctOf95: pctOf95, bmi95: bmi95 }; - return { text: 'Underweight', color: '#f59e0b', bg: '#fef3c7', pctOf95: pctOf95, bmi95: bmi95 }; - } + var BMI_LMS = null; + loadBmiLmsData() + .then(function(data) { BMI_LMS = data; }) + .catch(function(err) { console.warn('[calculators] BMI LMS data unavailable:', err && err.message); }); document.getElementById('btn-calc-bmi').addEventListener('click', function() { var ageYr = parseFloat(document.getElementById('bmi-age-yr').value) || 0; @@ -992,16 +963,13 @@ initImageLightbox(); if (!age || !sex || !weight || !height) { showToast('Fill in all fields', 'error'); return; } if (age < 2 || age > 20) { showToast('Age must be 2-20 years', 'error'); return; } + if (!BMI_LMS) { showToast('BMI percentile data is still loading.', 'info'); return; } - var bmi = weight / Math.pow(height / 100, 2); - var ageMonths = Math.round(age * 12); - if (ageMonths < 24) ageMonths = 24; - if (ageMonths > 240) ageMonths = 240; - var lms = interpolateLMS(bmiLMS[sex], ageMonths); - if (!lms) { showToast('Invalid age/sex', 'error'); return; } - - var result = calcBMIPercentile(bmi, lms.L, lms.M, lms.S); - var cl = classifyBMI(result.percentile, bmi, lms); + var assessment = calculateBMIAssessment(BMI_LMS, sex, age, weight, height); + var bmi = assessment.bmi; + var ageMonths = assessment.ageMonths; + var result = { percentile: assessment.percentile, z: assessment.z }; + var cl = assessment.classification; var extendedInfo = ''; if (result.percentile >= 85) { @@ -1021,7 +989,7 @@ initImageLightbox(); '