{"id":7138,"date":"2026-01-08T07:38:28","date_gmt":"2026-01-08T07:38:28","guid":{"rendered":"https:\/\/nap.livingindus.org.pk\/?page_id=7138"},"modified":"2026-01-08T10:15:38","modified_gmt":"2026-01-08T10:15:38","slug":"pakistan-model","status":"publish","type":"page","link":"https:\/\/nap.livingindus.org.pk\/?page_id=7138","title":{"rendered":"NAP City Model"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"7138\" class=\"elementor elementor-7138\">\n\t\t\t\t<div class=\"elementor-element elementor-element-4d909ac4 e-flex e-con-boxed e-con e-parent\" data-id=\"4d909ac4\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-7581d278 elementor-widget elementor-widget-text-editor\" data-id=\"7581d278\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<div id=\"content\" class=\"site-content\">\n<div class=\"inner-wrap\">\n<div id=\"primary\" class=\"content-area\">\n<p style=\"text-align: center;\"><\/p>\n\n<article id=\"post-45\" class=\"post-45 page type-page status-publish hentry\">\n<div class=\"entry-content\">\n<div class=\"elementor elementor-45\" data-elementor-type=\"wp-page\" data-elementor-id=\"45\">\n<div class=\"elementor-element elementor-element-a7f1d82 e-flex e-con-boxed e-con e-parent e-lazyloaded\" data-id=\"a7f1d82\" data-element_type=\"container\">\n<div class=\"e-con-inner\">\n<div class=\"elementor-element elementor-element-cc17656 elementor-widget elementor-widget-shortcode\" data-id=\"cc17656\" data-element_type=\"widget\" data-widget_type=\"shortcode.default\">\n<div class=\"elementor-shortcode\"><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/article>&nbsp;\n\n<\/div>\n<\/div>\n<\/div>\n<footer id=\"colophon\" class=\"site-footer\" role=\"contentinfo\">\n<div class=\"inner-wrap\"><aside class=\"footer-widgets widgets widget-columns-3\" role=\"complementary\" aria-label=\"Footer\">\n<div class=\"widget-column footer-widget-1\"><\/div>\n<\/aside><\/div>\n<\/footer>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-5996bcc e-flex e-con-boxed e-con e-parent\" data-id=\"5996bcc\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-7a28309 elementor-widget elementor-widget-shortcode\" data-id=\"7a28309\" data-element_type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\">\n<div class=\"napcity-wrap\">\n  <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js@4.4.1\/dist\/chart.umd.min.js\"><\/script>\n  <link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.css\" \/>\n  <script src=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.js\"><\/script>\n\n  <style>\n    .napcity{max-width:1480px;margin:18px auto;font-family:Arial,Helvetica,sans-serif;color:#101418}\n    .napcity h2{margin:0 0 6px 0;color:#004f7c}\n    .napcity-top{display:flex;flex-wrap:wrap;gap:10px;align-items:center;justify-content:space-between}\n    .napcity-controls{display:flex;flex-wrap:wrap;gap:8px;align-items:center}\n    .napcity-controls select,.napcity-controls input{padding:7px 10px;border:1px solid #cde2f3;border-radius:10px;background:#fff}\n    .napcity-btn{padding:8px 12px;border:0;border-radius:10px;background:#0073aa;color:#fff;cursor:pointer}\n    .napcity-btn:hover{background:#005f86}\n    .napcity-btn.secondary{background:#4b5563}\n    .napcity-btn.secondary:hover{background:#374151}\n    .napcity-row{display:flex;flex-wrap:wrap;gap:14px;margin-top:12px}\n    .napcity-panel{flex:1;min-width:320px;background:#f9fcff;border:1px solid #cde2f3;border-radius:14px;box-shadow:0 4px 10px rgba(0,0,0,0.06);padding:14px}\n    .napcity-kpi{display:flex;flex-wrap:wrap;gap:10px}\n    .napcity-kpi .k{flex:1;min-width:200px;background:#fff;border:1px solid #e5f0fb;border-radius:14px;padding:12px}\n    .napcity-kpi .k .t{font-size:.82em;color:#335}\n    .napcity-kpi .k .v{font-size:1.35em;font-weight:800;color:#0b2a3a;margin-top:6px}\n    .napcity-kpi .k .s{font-size:.82em;color:#333;margin-top:6px;line-height:1.35}\n    .napcity-health{font-size:.82em;color:#223;margin-top:10px;padding:10px;border:1px dashed #cde2f3;border-radius:12px;background:#fbfdff}\n    .napcity-map{height:360px;border-radius:14px;overflow:hidden;border:1px solid #cde2f3}\n    .napcity-section{margin-top:14px}\n    .napcity-section h3{margin:0 0 8px 0;color:#004f7c}\n    .napcity-chart{background:#fff;border:1px solid #e5f0fb;border-radius:14px;padding:12px}\n    .napcity-note{font-size:.84em;color:#334;margin-top:8px;line-height:1.45}\n    .napcity-ai{background:#f2f8ff;border:1px solid #cde2f3;border-radius:14px;padding:14px;box-shadow:0 3px 10px rgba(0,0,0,0.06)}\n    .napcity-ai h3{margin:0 0 8px 0;color:#004f7c}\n    .napcity-ai pre{white-space:pre-wrap;margin:0;font-family:inherit;font-size:.92em;line-height:1.45;color:#0f172a}\n    .napcity-meta{font-size:.78em;color:#667;margin-top:8px}\n    .napcity-badge{display:inline-block;padding:4px 10px;border-radius:999px;font-size:.75em;background:#e6f3ff;color:#0b4a72;border:1px solid #cde2f3}\n    .napcity-warn{color:#8a2c0d}\n    .napcity-ok{color:#0a5a2b}\n  <\/style>\n\n  <div class=\"napcity\" id=\"napcityRoot\">\n    <div class=\"napcity-top\">\n      <div>\n        <h2>City Climate Intelligence Dashboard<\/h2>\n        <div class=\"napcity-note\">\n          City-level climate and air-quality signals to support prioritisation of adaptation actions. Designed for senior management.\n        <\/div>\n      <\/div>\n\n      <div class=\"napcity-controls\">\n        <select id=\"napcityLocation\"><\/select>\n        <select id=\"napcityCompare\"><\/select>\n        <button class=\"napcity-btn\" id=\"napcityRefresh\">Refresh<\/button>\n        <button class=\"napcity-btn secondary\" id=\"napcityExport\">Download CSV<\/button>\n      <\/div>\n    <\/div>\n\n    <div class=\"napcity-health\" id=\"napcityHealth\">System check: loading\u2026<\/div>\n\n    <div class=\"napcity-row\">\n      <div class=\"napcity-panel\">\n        <div class=\"napcity-badge\" id=\"napcityCityBadge\">Selected city<\/div>\n        <div style=\"height:10px\"><\/div>\n        <div class=\"napcity-map\" id=\"napcityMap\"><\/div>\n        <div class=\"napcity-note\">\n          This map anchors the dashboard to the selected city coordinates. It supports place-based discussion and comparison across cities.\n        <\/div>\n      <\/div>\n\n      <div class=\"napcity-panel\">\n        <div class=\"napcity-section\">\n          <h3>Key operational signals (next 14 days)<\/h3>\n          <div class=\"napcity-kpi\" id=\"napcityKpi\"><\/div>\n          <div class=\"napcity-note\">\n            These signals support short-cycle decisions. They inform heat preparedness, drainage readiness, and pollution health advisories.\n          <\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n\n    <div class=\"napcity-row\">\n      <div class=\"napcity-panel\">\n        <div class=\"napcity-ai\">\n          <h3>AI Management Brief<\/h3>\n          <div class=\"napcity-note\">\n            This brief is generated automatically using only the loaded city facts. It supports decision framing and coordination.\n          <\/div>\n          <div style=\"height:10px\"><\/div>\n          <button class=\"napcity-btn\" id=\"napcityAiBtn\">Refresh AI Brief<\/button>\n          <div style=\"height:10px\"><\/div>\n          <pre id=\"napcityAiText\">Preparing AI brief\u2026<\/pre>\n          <div class=\"napcity-meta\" id=\"napcityAiMeta\"><\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"napcity-panel\">\n        <div class=\"napcity-section\">\n          <h3>Historical climate context (last 12 months)<\/h3>\n\n          <div class=\"napcity-note\">\n            Historical context helps interpret whether current conditions are typical for the season or unusually intense.\n            It supports planning for heat-health actions and water-demand management.\n          <\/div>\n          <div style=\"height:10px\"><\/div>\n\n          <div class=\"napcity-chart\"><canvas id=\"napcityHistTemp\"><\/canvas><\/div>\n          <div class=\"napcity-note\">\n            <b>What this chart is:<\/b> Monthly average temperature for the city based on point observations.\n            <br><b>Why it matters:<\/b> Extended warm months increase heat stress, energy demand, and health risks for vulnerable groups.\n          <\/div>\n\n          <div style=\"height:12px\"><\/div>\n\n          <div class=\"napcity-chart\"><canvas id=\"napcityHistRain\"><\/canvas><\/div>\n          <div class=\"napcity-note\">\n            <b>What this chart is:<\/b> Monthly total precipitation for the city based on point observations.\n            <br><b>Why it matters:<\/b> Higher rainfall months require drainage readiness, desilting operations, and local flood management preparedness.\n          <\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n\n    <div class=\"napcity-row\">\n      <div class=\"napcity-panel\">\n        <div class=\"napcity-section\">\n          <h3>Weather forecast (next 14 days)<\/h3>\n\n          <div class=\"napcity-chart\"><canvas id=\"napcityFcTemp\"><\/canvas><\/div>\n          <div class=\"napcity-note\">\n            <b>What this chart is:<\/b> Daily max and min temperature forecast.\n            <br><b>Why it matters:<\/b> High night-time minima reduce recovery from heat and increase health risk in dense settlements.\n          <\/div>\n\n          <div style=\"height:12px\"><\/div>\n\n          <div class=\"napcity-chart\"><canvas id=\"napcityFcRain\"><\/canvas><\/div>\n          <div class=\"napcity-note\">\n            <b>What this chart is:<\/b> Daily forecast precipitation totals.\n            <br><b>Why it matters:<\/b> Clustered wet days can overwhelm storm-water systems and increase urban flooding and service disruption risk.\n          <\/div>\n\n          <div style=\"height:12px\"><\/div>\n\n          <div class=\"napcity-chart\"><canvas id=\"napcityFcWind\"><\/canvas><\/div>\n          <div class=\"napcity-note\">\n            <b>What this chart is:<\/b> Daily maximum wind speed forecast.\n            <br><b>Why it matters:<\/b> Strong winds increase dust exposure and can increase damage risk to weak structures and informal housing.\n          <\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"napcity-panel\">\n        <div class=\"napcity-section\">\n          <h3>Air-quality outlook (next 5 days)<\/h3>\n\n          <div class=\"napcity-chart\"><canvas id=\"napcityAirPm\"><\/canvas><\/div>\n          <div class=\"napcity-note\">\n            <b>What this chart is:<\/b> PM2.5 and PM10 particulate concentration outlook.\n            <br><b>Why it matters:<\/b> Peaks drive respiratory risk. This supports targeted advisories for schools, hospitals, and outdoor workers.\n          <\/div>\n\n          <div style=\"height:12px\"><\/div>\n\n          <div class=\"napcity-chart\"><canvas id=\"napcityAirAqi\"><\/canvas><\/div>\n          <div class=\"napcity-note\">\n            <b>What this chart is:<\/b> AQI outlook summarising pollution stress.\n            <br><b>Why it matters:<\/b> AQI supports clear public messaging thresholds and city-level decision triggers for health protection.\n          <\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n\n  <\/div>\n<\/div>\n\n<script>\n(function(){\n  const REST = {\n    health:   \"https:\/\/nap.livingindus.org.pk\/?rest_route=\/napdv2\/v1\/health\",\n    nasa:     \"https:\/\/nap.livingindus.org.pk\/?rest_route=\/napdv2\/v1\/nasa\",\n    forecast: \"https:\/\/nap.livingindus.org.pk\/?rest_route=\/napdv2\/v1\/forecast\",\n    air:      \"https:\/\/nap.livingindus.org.pk\/?rest_route=\/napdv2\/v1\/air\",\n    ai:       \"https:\/\/nap.livingindus.org.pk\/?rest_route=\/napdv2\/v1\/ai\",\n    nonce:    \"3e7549f316\"\n  };\n\n  const CITIES = [\n    {name:\"Islamabad\", lat:33.6844, lon:73.0479},\n    {name:\"Lahore\",    lat:31.5204, lon:74.3587},\n    {name:\"Karachi\",   lat:24.8607, lon:67.0011},\n    {name:\"Peshawar\",  lat:34.0151, lon:71.5249},\n    {name:\"Quetta\",    lat:30.1798, lon:66.9750},\n    {name:\"Multan\",    lat:30.1575, lon:71.5249},\n    {name:\"Gilgit\",    lat:35.9208, lon:74.3146},\n    {name:\"Hyderabad\", lat:25.3960, lon:68.3578},\n    {name:\"Faisalabad\",lat:31.4504, lon:73.1350},\n    {name:\"Sialkot\",   lat:32.4945, lon:74.5229},\n    {name:\"Gujranwala\",lat:32.1877, lon:74.1945},\n    {name:\"Sukkur\",    lat:27.7052, lon:68.8574},\n    {name:\"Mardan\",    lat:34.1986, lon:72.0404},\n    {name:\"Charsadda\", lat:34.1460, lon:71.7320},\n    {name:\"Karak\",     lat:33.1167, lon:71.0833}\n  ];\n\n  const elHealth = document.getElementById('napcityHealth');\n  const elLoc = document.getElementById('napcityLocation');\n  const elCmp = document.getElementById('napcityCompare');\n  const elKpi = document.getElementById('napcityKpi');\n  const elBadge = document.getElementById('napcityCityBadge');\n\n  const btnRefresh = document.getElementById('napcityRefresh');\n  const btnExport  = document.getElementById('napcityExport');\n\n  const btnAi   = document.getElementById('napcityAiBtn');\n  const elAiTxt = document.getElementById('napcityAiText');\n  const elAiMet = document.getElementById('napcityAiMeta');\n\n  let map, markerA, markerB;\n  let charts = {};\n\n  function destroyChart(k){ if(charts[k]) { charts[k].destroy(); charts[k]=null; } }\n\n  function haversineKm(aLat,aLon,bLat,bLon){\n    const R=6371, dLat=(bLat-aLat)*Math.PI\/180, dLon=(bLon-aLon)*Math.PI\/180;\n    const s1=Math.sin(dLat\/2), s2=Math.sin(dLon\/2);\n    const q = s1*s1 + Math.cos(aLat*Math.PI\/180)*Math.cos(bLat*Math.PI\/180)*s2*s2;\n    return 2*R*Math.asin(Math.sqrt(q));\n  }\n\n  function nearestCity(lat, lon){\n    let best = CITIES[0], bestD=1e9;\n    for (const c of CITIES){\n      const d = haversineKm(lat,lon,c.lat,c.lon);\n      if (d < bestD){ bestD=d; best=c; }\n    }\n    return best;\n  }\n\n  async function getJson(url){\n    const res = await fetch(url, { method:\"GET\", headers: {\"X-WP-Nonce\": REST.nonce} });\n    const raw = await res.text();\n    let j=null; try{ j=JSON.parse(raw); }catch(e){}\n    if(!res.ok) throw new Error(\"HTTP \" + res.status + \": \" + (j?.message || raw));\n    return j;\n  }\n\n  async function postJson(url, body){\n    const res = await fetch(url, {\n      method:\"POST\",\n      headers: {\"Content-Type\":\"application\/json\",\"X-WP-Nonce\": REST.nonce},\n      body: JSON.stringify(body)\n    });\n    const raw = await res.text();\n    let j=null; try{ j=JSON.parse(raw); }catch(e){}\n    if(!res.ok) throw new Error(\"HTTP \" + res.status + \": \" + (j?.message || raw));\n    return j;\n  }\n\n  function optHtml(c){ return `<option value=\"${c.lat},${c.lon}|${c.name}\">${c.name}<\/option>`; }\n\n  function setDropdowns(){\n    elLoc.innerHTML = CITIES.map(optHtml).join(\"\");\n    elCmp.innerHTML = `<option value=\"\">Comparison city: Off<\/option>` +\n      CITIES.map(c => `<option value=\"${c.lat},${c.lon}|${c.name}\">Compare with ${c.name}<\/option>`).join(\"\");\n  }\n\n  function fmt(x, d=1){\n    const n = Number(x);\n    if (!isFinite(n)) return \"\u2014\";\n    return n.toLocaleString(undefined, {maximumFractionDigits:d});\n  }\n\n  function kpiCard(title, value, sub){\n    const d = document.createElement('div');\n    d.className = 'k';\n    d.innerHTML = `<div class=\"t\">${title}<\/div><div class=\"v\">${value}<\/div><div class=\"s\">${sub||\"\"}<\/div>`;\n    return d;\n  }\n\n  function mapInit(){\n    if (map) return;\n    map = L.map('napcityMap', {scrollWheelZoom:false});\n    L.tileLayer('https:\/\/{s}.tile.openstreetmap.org\/{z}\/{x}\/{y}.png', {\n      maxZoom: 18,\n      attribution: '&copy; OpenStreetMap contributors'\n    }).addTo(map);\n  }\n\n  function mapSet(a, b){\n    mapInit();\n    if (markerA) markerA.remove();\n    if (markerB) markerB.remove();\n\n    markerA = L.marker([a.lat, a.lon]).addTo(map).bindPopup(a.name);\n    if (b) markerB = L.marker([b.lat, b.lon]).addTo(map).bindPopup(b.name);\n\n    const group = L.featureGroup([markerA].concat(markerB?[markerB]:[]));\n    map.fitBounds(group.getBounds().pad(0.25));\n  }\n\n  function extractForecastDaily(fc){\n    const d = fc?.daily || {};\n    const time = d.time || [];\n    return {\n      time,\n      tmax: d.temperature_2m_max || [],\n      tmin: d.temperature_2m_min || [],\n      rain: d.precipitation_sum || [],\n      wind: d.wind_speed_10m_max || [],\n      uv:   d.uv_index_max || []\n    };\n  }\n\n  function extractAirHourly(air){\n    const h = air?.hourly || {};\n    return {\n      time: h.time || [],\n      pm25: h.pm2_5 || [],\n      pm10: h.pm10 || [],\n      usaqi: h.us_aqi || [],\n      euaqi: h.european_aqi || []\n    };\n  }\n\n  function nasaMonthly(nasaJson, key){\n    const p = nasaJson?.properties?.parameter || {};\n    const series = p[key] || {};\n    const keys = Object.keys(series).sort(); \/\/ YYYYMMDD\n    const byMonth = {};\n    for (const k of keys){\n      const v = Number(series[k]);\n      if (!isFinite(v)) continue;\n      const m = k.substring(0,6); \/\/ YYYYMM\n      if (!byMonth[m]) byMonth[m] = [];\n      byMonth[m].push(v);\n    }\n    const months = Object.keys(byMonth).sort();\n    return {\n      months,\n      values: months.map(m => {\n        const arr = byMonth[m];\n        if (!arr.length) return null;\n        if (key === 'PRECTOTCORR') return arr.reduce((a,b)=>a+b,0); \/\/ monthly total mm\n        return arr.reduce((a,b)=>a+b,0)\/arr.length; \/\/ monthly avg\n      })\n    };\n  }\n\n  function maxOf(arr){ return Math.max.apply(null, arr.map(Number).filter(v=>isFinite(v))); }\n  function sumOf(arr){ return arr.map(Number).filter(v=>isFinite(v)).reduce((a,b)=>a+b,0); }\n  function avgOf(arr){ const v=arr.map(Number).filter(x=>isFinite(x)); return v.length? v.reduce((a,b)=>a+b,0)\/v.length : null; }\n\n  function renderCharts(aName, aFc, aAir, aNasa, bName=null, bFc=null, bAir=null, bNasa=null){\n    Object.keys(charts).forEach(destroyChart);\n\n    const A = extractForecastDaily(aFc);\n    const B = bFc ? extractForecastDaily(bFc) : null;\n\n    charts.fcTemp = new Chart(document.getElementById('napcityFcTemp'), {\n      type:'line',\n      data:{\n        labels: A.time,\n        datasets: [\n          {label: `${aName} max`, data: A.tmax},\n          {label: `${aName} min`, data: A.tmin},\n          ...(B ? [{label:`${bName} max`, data:B.tmax},{label:`${bName} min`, data:B.tmin}] : [])\n        ]\n      },\n      options:{responsive:true, plugins:{legend:{display:true}}}\n    });\n\n    charts.fcRain = new Chart(document.getElementById('napcityFcRain'), {\n      type:'bar',\n      data:{\n        labels: A.time,\n        datasets: [\n          {label:`${aName} rainfall (mm)`, data:A.rain},\n          ...(B ? [{label:`${bName} rainfall (mm)`, data:B.rain}] : [])\n        ]\n      },\n      options:{responsive:true, plugins:{legend:{display:true}}}\n    });\n\n    charts.fcWind = new Chart(document.getElementById('napcityFcWind'), {\n      type:'line',\n      data:{\n        labels: A.time,\n        datasets: [\n          {label:`${aName} max wind (km\/h)`, data:A.wind},\n          ...(B ? [{label:`${bName} max wind (km\/h)`, data:B.wind}] : [])\n        ]\n      },\n      options:{responsive:true, plugins:{legend:{display:true}}}\n    });\n\n    const AirA = extractAirHourly(aAir);\n    const AirB = bAir ? extractAirHourly(bAir) : null;\n\n    function sample(time, arr, step){\n      const t=[], v=[];\n      for(let i=0;i<time.length;i+=step){ t.push(time[i]); v.push(arr[i]); }\n      return {t,v};\n    }\n    const sApm25 = sample(AirA.time, AirA.pm25, 3);\n    const sApm10 = sample(AirA.time, AirA.pm10, 3);\n    const sAus   = sample(AirA.time, AirA.usaqi, 3);\n\n    let sBpm25=null, sBpm10=null, sBus=null;\n    if (AirB){\n      sBpm25 = sample(AirB.time, AirB.pm25, 3);\n      sBpm10 = sample(AirB.time, AirB.pm10, 3);\n      sBus   = sample(AirB.time, AirB.usaqi, 3);\n    }\n\n    charts.airPm = new Chart(document.getElementById('napcityAirPm'), {\n      type:'line',\n      data:{\n        labels: sApm25.t,\n        datasets:[\n          {label:`${aName} PM2.5 (\u00b5g\/m\u00b3)`, data:sApm25.v},\n          {label:`${aName} PM10 (\u00b5g\/m\u00b3)`, data:sApm10.v},\n          ...(sBpm25 ? [\n            {label:`${bName} PM2.5 (\u00b5g\/m\u00b3)`, data:sBpm25.v},\n            {label:`${bName} PM10 (\u00b5g\/m\u00b3)`, data:sBpm10.v}\n          ] : [])\n        ]\n      },\n      options:{responsive:true, plugins:{legend:{display:true}}}\n    });\n\n    charts.airAqi = new Chart(document.getElementById('napcityAirAqi'), {\n      type:'line',\n      data:{\n        labels: sAus.t,\n        datasets:[\n          {label:`${aName} US AQI`, data:sAus.v},\n          ...(sBus ? [{label:`${bName} US AQI`, data:sBus.v}] : [])\n        ]\n      },\n      options:{responsive:true, plugins:{legend:{display:true}}}\n    });\n\n    const tA = nasaMonthly(aNasa, 'T2M');\n    const rA = nasaMonthly(aNasa, 'PRECTOTCORR');\n\n    const tB = bNasa ? nasaMonthly(bNasa, 'T2M') : null;\n    const rB = bNasa ? nasaMonthly(bNasa, 'PRECTOTCORR') : null;\n\n    charts.histTemp = new Chart(document.getElementById('napcityHistTemp'), {\n      type:'line',\n      data:{\n        labels: tA.months,\n        datasets:[\n          {label:`${aName} monthly avg temp (\u00b0C)`, data:tA.values},\n          ...(tB ? [{label:`${bName} monthly avg temp (\u00b0C)`, data:tB.values}] : [])\n        ]\n      },\n      options:{responsive:true, plugins:{legend:{display:true}}}\n    });\n\n    charts.histRain = new Chart(document.getElementById('napcityHistRain'), {\n      type:'bar',\n      data:{\n        labels: rA.months,\n        datasets:[\n          {label:`${aName} monthly rainfall total (mm)`, data:rA.values},\n          ...(rB ? [{label:`${bName} monthly rainfall total (mm)`, data:rB.values}] : [])\n        ]\n      },\n      options:{responsive:true, plugins:{legend:{display:true}}}\n    });\n  }\n\n  function buildFacts(cityName, aFc, aAir, aNasa){\n    const A = extractForecastDaily(aFc);\n    const AirA = extractAirHourly(aAir);\n\n    return {\n      city: cityName,\n      forecast_14d: {\n        max_temperature_c: isFinite(maxOf(A.tmax)) ? Number(maxOf(A.tmax).toFixed(1)) : null,\n        total_precip_mm: isFinite(sumOf(A.rain)) ? Number(sumOf(A.rain).toFixed(1)) : null,\n        max_wind_kmh: isFinite(maxOf(A.wind)) ? Number(maxOf(A.wind).toFixed(1)) : null,\n        max_uv_index: isFinite(maxOf(A.uv)) ? Number(maxOf(A.uv).toFixed(1)) : null\n      },\n      air_quality: {\n        avg_pm2_5_ugm3: isFinite(avgOf(AirA.pm25)) ? Number(avgOf(AirA.pm25).toFixed(1)) : null,\n        avg_pm10_ugm3: isFinite(avgOf(AirA.pm10)) ? Number(avgOf(AirA.pm10).toFixed(1)) : null,\n        max_us_aqi: isFinite(maxOf(AirA.usaqi)) ? Number(maxOf(AirA.usaqi).toFixed(0)) : null\n      },\n      last_12_months: (function(){\n        const histT = nasaMonthly(aNasa,'T2M');\n        const histR = nasaMonthly(aNasa,'PRECTOTCORR');\n        return {\n          months: histT.months,\n          monthly_avg_temp_c: histT.values.map(v => isFinite(v)? Number(v.toFixed(1)) : null),\n          monthly_total_rain_mm: histR.values.map(v => isFinite(v)? Number(v.toFixed(1)) : null)\n        };\n      })()\n    };\n  }\n\n  function renderKpis(aName, aFc, aAir, bName=null, bFc=null, bAir=null){\n    elKpi.innerHTML = \"\";\n\n    const A = extractForecastDaily(aFc);\n    const AirA = extractAirHourly(aAir);\n\n    elKpi.appendChild(kpiCard(\"Max temperature\", fmt(maxOf(A.tmax),1) + \" \u00b0C\", \"Heat readiness and heat-health measures.\"));\n    elKpi.appendChild(kpiCard(\"Total rainfall (14 days)\", fmt(sumOf(A.rain),1) + \" mm\", \"Drainage readiness and local flood management.\"));\n    elKpi.appendChild(kpiCard(\"Peak wind\", fmt(maxOf(A.wind),1) + \" km\/h\", \"Dust risk and safety advisories for vulnerable settlements.\"));\n    elKpi.appendChild(kpiCard(\"Peak air-quality index\", fmt(maxOf(AirA.usaqi),0), \"Public messaging thresholds and health protection planning.\"));\n\n    if (bFc && bAir){\n      const B = extractForecastDaily(bFc);\n      const AirB = extractAirHourly(bAir);\n      elKpi.appendChild(kpiCard(\"Comparison: Max temperature\", fmt(maxOf(B.tmax),1) + \" \u00b0C\", bName));\n      elKpi.appendChild(kpiCard(\"Comparison: Total rainfall\", fmt(sumOf(B.rain),1) + \" mm\", bName));\n      elKpi.appendChild(kpiCard(\"Comparison: Peak wind\", fmt(maxOf(B.wind),1) + \" km\/h\", bName));\n      elKpi.appendChild(kpiCard(\"Comparison: Peak AQI\", fmt(maxOf(AirB.usaqi),0), bName));\n    }\n  }\n\n  async function loadBundle(lat, lon){\n    const nasa = await getJson(REST.nasa + \"&lat=\" + encodeURIComponent(lat) + \"&lon=\" + encodeURIComponent(lon));\n    const fc   = await getJson(REST.forecast + \"&lat=\" + encodeURIComponent(lat) + \"&lon=\" + encodeURIComponent(lon) + \"&days=14\");\n    const air  = await getJson(REST.air + \"&lat=\" + encodeURIComponent(lat) + \"&lon=\" + encodeURIComponent(lon) + \"&days=5\");\n    return {nasa, fc, air};\n  }\n\n  async function generateAiBrief(cityName, facts, compareFacts){\n    elAiTxt.textContent = \"Preparing AI brief\u2026\";\n    elAiMet.textContent = \"\";\n    try{\n      const resp = await postJson(REST.ai, { city: cityName, facts, compare: compareFacts || null });\n      elAiTxt.textContent = resp?.text || \"AI returned no text.\";\n      elAiMet.textContent = \"AI generated \u2022 \" + (resp.model || \"\") + \" \u2022 \" + (resp.time || \"\");\n    } catch(e){\n      elAiTxt.textContent = \"AI brief not available. Please check AI configuration.\";\n      elAiMet.textContent = e.message;\n    }\n  }\n\n  async function loadAll(){\n    \/\/ Health\n    try{\n      const h = await getJson(REST.health);\n      const ok = !!h.ok;\n      const aiEnabled = !!h.ai?.enabled;\n      const keySet = !!h.ai?.key_set;\n      elHealth.innerHTML =\n        \"<b>System check<\/b><br>\" +\n        \"Online: <span class='\" + (ok?\"napcity-ok\":\"napcity-warn\") + \"'>\" + String(ok) + \"<\/span><br>\" +\n        \"Server time: \" + (h.time || \"\") + \"<br>\" +\n        \"AI enabled: \" + String(aiEnabled) + \"<br>\" +\n        \"AI key configured: \" + String(keySet);\n    } catch(e){\n      elHealth.innerHTML = \"<b>System check<\/b><br><span class='napcity-warn'>Service check failed<\/span><br>\" + e.message;\n    }\n\n    const [aCoord, aName] = elLoc.value.split(\"|\");\n    const [aLat, aLon] = aCoord.split(\",\").map(Number);\n\n    const cmpVal = elCmp.value;\n    const hasCmp = !!cmpVal;\n\n    let bLat=null,bLon=null,bName=null;\n    if (hasCmp){\n      const [bCoord, bn] = cmpVal.split(\"|\");\n      [bLat,bLon] = bCoord.split(\",\").map(Number);\n      bName = bn;\n    }\n\n    elBadge.textContent = hasCmp ? (aName + \" compared with \" + bName) : aName;\n    mapSet({name:aName, lat:aLat, lon:aLon}, hasCmp?{name:bName, lat:bLat, lon:bLon}:null);\n\n    \/\/ Load data\n    let A=null, B=null;\n    try{\n      A = await loadBundle(aLat,aLon);\n      if (hasCmp) B = await loadBundle(bLat,bLon);\n    } catch(e){\n      elHealth.innerHTML += \"<br><span class='napcity-warn'>Data load issue:<\/span> \" + e.message;\n      return;\n    }\n\n    renderKpis(aName, A.fc, A.air, bName, B?.fc, B?.air);\n    renderCharts(aName, A.fc, A.air, A.nasa, bName, B?.fc, B?.air, B?.nasa);\n\n    \/\/ Export CSV (daily forecast for selected city)\n    btnExport.onclick = function(){\n      const d = extractForecastDaily(A.fc);\n      const rows = [[\"date\",\"temp_max_c\",\"temp_min_c\",\"rain_mm\",\"wind_max_kmh\",\"uv_index_max\"]];\n      for (let i=0;i<d.time.length;i++){\n        rows.push([d.time[i], d.tmax[i], d.tmin[i], d.rain[i], d.wind[i], d.uv[i]]);\n      }\n      const csv = rows.map(r => r.map(x => (x===null||x===undefined)?\"\":String(x)).join(\",\")).join(\"\\n\");\n      const blob = new Blob([csv], {type:\"text\/csv;charset=utf-8\"});\n      const url = URL.createObjectURL(blob);\n      const a = document.createElement(\"a\");\n      a.href = url;\n      a.download = aName.replace(\/\\s+\/g,\"_\").toLowerCase() + \"_forecast.csv\";\n      document.body.appendChild(a);\n      a.click();\n      a.remove();\n      URL.revokeObjectURL(url);\n    };\n\n    \/\/ AI facts + automatic generation (always)\n    const factsA = buildFacts(aName, A.fc, A.air, A.nasa);\n    const factsB = (hasCmp && B) ? buildFacts(bName, B.fc, B.air, B.nasa) : null;\n\n    \/\/ Auto-generate on every load\n    await generateAiBrief(aName, factsA, factsB);\n\n    \/\/ Manual refresh button\n    btnAi.onclick = async function(){\n      btnAi.disabled = true;\n      try{\n        await generateAiBrief(aName, factsA, factsB);\n      } finally {\n        btnAi.disabled = false;\n      }\n    };\n  }\n\n  function setDefaultCityByDevice(){\n    const fallback = CITIES.find(c=>c.name===\"Islamabad\") || CITIES[0];\n\n    function selectCity(c){\n      const v = `${c.lat},${c.lon}|${c.name}`;\n      elLoc.value = v;\n      loadAll();\n    }\n\n    if (!navigator.geolocation){\n      selectCity(fallback);\n      return;\n    }\n\n    navigator.geolocation.getCurrentPosition(\n      pos => {\n        const lat = pos.coords.latitude;\n        const lon = pos.coords.longitude;\n        selectCity(nearestCity(lat,lon));\n      },\n      () => selectCity(fallback),\n      {enableHighAccuracy:false, timeout:4000, maximumAge: 600000}\n    );\n  }\n\n  \/\/ init\n  setDropdowns();\n  elLoc.onchange = loadAll;\n  elCmp.onchange = loadAll;\n  btnRefresh.onclick = loadAll;\n\n  setDefaultCityByDevice();\n\n})();\n<\/script>\n\n<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>&nbsp;<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"class_list":["post-7138","page","type-page","status-publish","hentry"],"acf":[],"featured_media_urls":[],"_links":{"self":[{"href":"https:\/\/nap.livingindus.org.pk\/index.php?rest_route=\/wp\/v2\/pages\/7138","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nap.livingindus.org.pk\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/nap.livingindus.org.pk\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/nap.livingindus.org.pk\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nap.livingindus.org.pk\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=7138"}],"version-history":[{"count":11,"href":"https:\/\/nap.livingindus.org.pk\/index.php?rest_route=\/wp\/v2\/pages\/7138\/revisions"}],"predecessor-version":[{"id":7150,"href":"https:\/\/nap.livingindus.org.pk\/index.php?rest_route=\/wp\/v2\/pages\/7138\/revisions\/7150"}],"wp:attachment":[{"href":"https:\/\/nap.livingindus.org.pk\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=7138"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}