{
  "schemaVersion": 1,
  "slug": "uv-index-calculator",
  "category": "weather",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "8a30cc43706baaadc4eed6aff0f2e0cfd497879817f87994467af5d8bbd49cf3",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/uv-index-calculator.json",
    "src/engine.js"
  ],
  "runtimeSource": "/sources/engine.txt",
  "runtimeHash": "5c888d8898afbdee36267bb9977829e1e2ac568f6677c8ee87469c139864645c",
  "scope": "The compute function records implemented equations, constants, lookup tables and custom rules. Inputs record defaults and allowed values; outputs record precision and units. Defaults are examples, not recommendations. General background references are not evidence for this specific algorithm.",
  "publishedFormula": {
    "formula": "δ = 23.45π÷180×sin(2π(284+d)÷365); cos z = max(0,sinφ sinδ+cosφ cosδ cos(15πt÷180)); UVI = 12.5(cos z)^2.42×c×(1+0.0001a)",
    "vars": [
      [
        "φ",
        "Latitude (rad)"
      ],
      [
        "d",
        "Day of year"
      ],
      [
        "t",
        "Solar hour"
      ],
      [
        "a",
        "Altitude (m)"
      ],
      [
        "c",
        "Cloud factor"
      ],
      [
        "UVI",
        "UV index"
      ]
    ],
    "note": "Educational approximation using solar geometry and broad cloud/altitude factors. It omits ozone, aerosols, surface reflection and local weather; it is not a measurement or official forecast. Use a trusted local forecast for decisions."
  },
  "externalEvidence": null,
  "contract": {
    "compute": "const d = new Date(date + 'T00:00:00Z'); if(Number.isNaN(d.getTime())) return {uvIndex:null,category:null,solarElevation:null,clearSkyUV:null,dayOfYear:null,__error:'inputError'}; const doy = Math.floor((d - new Date(Date.UTC(d.getUTCFullYear(), 0, 0)))/86400000); const decl = 23.45*Math.sin(2*Math.PI*(284 + doy)/365)*Math.PI/180; const lat = latitude*Math.PI/180; const hourAngle = timeOfDay*15*Math.PI/180; let cosZ = Math.sin(lat)*Math.sin(decl) + Math.cos(lat)*Math.cos(decl)*Math.cos(hourAngle); cosZ = Math.max(0, cosZ); const noonZ = Math.sin(lat)*Math.sin(decl) + Math.cos(lat)*Math.cos(decl); const elevation = Math.asin(Math.max(-1, Math.min(1, noonZ)))*180/Math.PI; const clear = 12.5*Math.pow(cosZ, 2.42); const cloud = cloudCover === 'clear' ? 1 : cloudCover === 'partly' ? 0.89 : cloudCover === 'mostly' ? 0.73 : 0.31; const altFactor = 1 + altitude/1000*0.10; const uv = Math.max(0, clear*cloud*altFactor); return { uvIndex: uv, category: uv < 3 ? 'Low' : uv < 6 ? 'Moderate' : uv < 8 ? 'High' : uv < 11 ? 'Very high' : 'Extreme', solarElevation: elevation, clearSkyUV: clear, dayOfYear: doy };",
    "inputs": [
      {
        "name": "latitude",
        "type": "number",
        "default": 40,
        "min": -90,
        "max": 90,
        "step": 0.5,
        "slider": true
      },
      {
        "name": "date",
        "type": "date",
        "default": "@today",
        "step": 1
      },
      {
        "name": "cloudCover",
        "type": "select",
        "default": "clear",
        "options": [
          {
            "value": "clear"
          },
          {
            "value": "partly"
          },
          {
            "value": "mostly"
          },
          {
            "value": "overcast"
          }
        ]
      },
      {
        "name": "altitude",
        "type": "number",
        "default": 0,
        "min": -400,
        "max": 6000,
        "step": 50,
        "slider": true
      },
      {
        "name": "timeOfDay",
        "type": "number",
        "default": 0,
        "min": 0,
        "max": 12,
        "step": 0.5,
        "slider": true
      }
    ],
    "outputs": [
      {
        "name": "uvIndex",
        "precision": 1,
        "unit": ""
      },
      {
        "name": "category",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "solarElevation",
        "precision": 1,
        "unit": "degrees"
      },
      {
        "name": "clearSkyUV",
        "precision": 1,
        "unit": ""
      },
      {
        "name": "dayOfYear",
        "precision": 0,
        "unit": ""
      }
    ],
    "interpret": null
  }
}
