|
107 | 107 | wco2: {label:"weak CO₂", ymax:50, key:"wco2"}, |
108 | 108 | sco2: {label:"strong CO₂",ymax:22, key:"sco2"}, |
109 | 109 | }; |
110 | | -const state={xco2:420, sza:30, vza:0, psurf:1013.25, surface:"dry_grass", obs:false}; const ORDER=["aband","wco2","sco2"]; |
| 110 | +const state={xco2:420, sza:30, vza:0, psurf:1013.25, surface:"dry_grass", obs:false, albL2:false}; const ORDER=["aband","wco2","sco2"]; |
111 | 111 | const cache={}; |
112 | 112 | async function getJSON(name){ if(!cache[name]) cache[name]=fetch(`${DATA}/${name}`).then(r=>{ if(!r.ok) throw new Error(name); return r.json(); }); return cache[name]; } |
113 | 113 | let ALB=null, SND=null; |
|
120 | 120 | for (const k of ["dry_grass","desert","conifer","snow","ocean"]){ |
121 | 121 | if (!ALB[k]) continue; |
122 | 122 | const b=document.createElement("button"); b.className="chip"+(state.surface===k?" on":""); b.textContent=names[k]||k; |
123 | | - b.onclick=()=>{ state.surface=k; state.obs=false; syncChips(); readouts(); draw(); }; box.appendChild(b); |
| 123 | + b.onclick=()=>{ state.surface=k; state.albL2=false; syncChips(); readouts(); draw(); }; box.appendChild(b); |
124 | 124 | } |
125 | 125 | } |
126 | 126 | function syncChips(){ |
127 | | - document.querySelectorAll("#surfaces .chip").forEach(b=>b.classList.toggle("on", !state.obs && (b.textContent===({ocean:"water (no glint)",desert:"desert",snow:"snow",conifer:"conifer forest",dry_grass:"dry grass"})[state.surface]))); |
| 127 | + document.querySelectorAll("#surfaces .chip").forEach(b=>b.classList.toggle("on", !state.albL2 && (b.textContent===({ocean:"water (no glint)",desert:"desert",snow:"snow",conifer:"conifer forest",dry_grass:"dry grass"})[state.surface]))); |
128 | 128 | document.getElementById("obsChip").classList.toggle("on", state.obs); |
129 | 129 | document.getElementById("keyObs").style.display = state.obs ? "" : "none"; |
130 | 130 | document.getElementById("roFitWrap").style.display = state.obs ? "" : "none"; |
131 | 131 | } |
132 | 132 | function bandAlbedo(band){ |
133 | | - if (state.obs && SND) return SND.meta.albedo_l2[band]; |
| 133 | + if (state.albL2 && SND) return SND.meta.albedo_l2[band]; |
134 | 134 | if (ALB && ALB.band_means && ALB.band_means[state.surface]) return ALB.band_means[state.surface][band]; |
135 | 135 | return 0.3; |
136 | 136 | } |
|
229 | 229 | function readouts(){ |
230 | 230 | const c=cur.wco2||cur.aband; if (!c) return; |
231 | 231 | document.getElementById("roM").textContent=c.m.toFixed(2)+" (1/cos SZA + 1/cos VZA)"; |
232 | | - document.getElementById("roA").textContent=ORDER.map(b=>cur[b]?cur[b].a.toFixed(2):"—").join(" / ")+(state.obs?" (L2 retrieved)":"")+" [A-band / weak / strong]"; |
| 232 | + document.getElementById("roA").textContent=ORDER.map(b=>cur[b]?cur[b].a.toFixed(2):"—").join(" / ")+(state.albL2?" (L2 retrieved)":" ("+state.surface.replace("_"," ")+")")+" [A-band / weak / strong]"; |
233 | 233 | document.getElementById("roC").textContent=ORDER.map(b=>cur[b]?Math.max(...cur[b].Lc).toFixed(0):"—").join(" / ")+" W m⁻² µm⁻¹ sr⁻¹"; |
234 | 234 | document.getElementById("xOut").textContent=state.xco2.toFixed(1)+" ppm"; document.getElementById("sOut").textContent=state.sza.toFixed(1)+"°"; document.getElementById("pOut").textContent=state.psurf.toFixed(0)+" hPa"; |
235 | | - document.getElementById("roP").textContent=state.psurf.toFixed(0)+" hPa (O₂ and CO₂ columns scale with it)"+(state.obs?" — L2 retrieved":""); |
| 235 | + document.getElementById("roP").textContent=state.psurf.toFixed(0)+" hPa (O₂ and CO₂ columns scale with it)"+(state.obs && Math.abs(state.psurf-SND.meta.surface_pressure_hpa)<0.6?" — L2 retrieved":""); |
236 | 236 | const t=c.tau.meta; |
237 | | - document.getElementById("note").textContent = `${t.line_list}; ${t.atmosphere.split(";")[0]}. Line-by-line model L = E₀·cos(SZA)·a/π·exp(−m·τ) at ${t.spacing_nm} nm, then convolved with the OCO-2 ILS (FWHM ${cur.aband?cur.aband.ils.fwhm_nm.toFixed(3):"—"} / ${cur.wco2?cur.wco2.ils.fwhm_nm.toFixed(3):"—"} / ${cur.sco2?cur.sco2.ils.fwhm_nm.toFixed(3):"—"} nm); solar continuum TSIS-1 HSRS × Toon lines; fixed y-axes 0–300 / 0–50 / 0–22 (snow at high sun clips in the A-band).` + (state.obs && SND ? ` Sounding ${SND.meta.sounding_id} (${SND.meta.surface}, ${SND.meta.time_utc.slice(0,10)}), shown ×${(1/(SND.meta.polarization_factor||0.5)).toFixed(0)} because OCO-2 measures one polarization; XCO₂ and albedos set to the L2 retrieval.` : ""); |
| 237 | + document.getElementById("note").textContent = `${t.line_list}; ${t.atmosphere.split(";")[0]}. Line-by-line model L = E₀·cos(SZA)·a/π·exp(−m·τ) at ${t.spacing_nm} nm, then convolved with the OCO-2 ILS (FWHM ${cur.aband?cur.aband.ils.fwhm_nm.toFixed(3):"—"} / ${cur.wco2?cur.wco2.ils.fwhm_nm.toFixed(3):"—"} / ${cur.sco2?cur.sco2.ils.fwhm_nm.toFixed(3):"—"} nm); solar continuum TSIS-1 HSRS × Toon lines; fixed y-axes 0–300 / 0–50 / 0–22 (snow at high sun clips in the A-band).` + (state.obs && SND ? ` Sounding ${SND.meta.sounding_id} (${SND.meta.surface}, ${SND.meta.time_utc.slice(0,10)}), shown ×${(1/(SND.meta.polarization_factor||0.5)).toFixed(0)} because OCO-2 measures one polarization; sliders start at the L2 retrieval — move them and watch the residual grow.` : ""); |
238 | 238 | } |
239 | 239 | /* ---- controls ---- */ |
240 | | -document.getElementById("xco2").addEventListener("input", e=>{ state.xco2=+e.target.value; if (state.obs){ state.obs=false; syncChips(); } draw(); }); |
241 | | -document.getElementById("sza").addEventListener("input", e=>{ state.sza=+e.target.value; if (state.obs){ state.obs=false; syncChips(); } draw(); }); |
242 | | -document.getElementById("psurf").addEventListener("input", e=>{ state.psurf=+e.target.value; if (state.obs){ state.obs=false; syncChips(); } draw(); }); |
| 240 | +document.getElementById("xco2").addEventListener("input", e=>{ state.xco2=+e.target.value; draw(); }); // overlay stays on: move the sliders to see the fit degrade |
| 241 | +document.getElementById("sza").addEventListener("input", e=>{ state.sza=+e.target.value; draw(); }); // overlay stays on: move the sliders to see the fit degrade |
| 242 | +document.getElementById("psurf").addEventListener("input", e=>{ state.psurf=+e.target.value; draw(); }); // overlay stays on: move the sliders to see the fit degrade |
243 | 243 | document.getElementById("obsChip").addEventListener("click", ()=>{ if(!SND) return; state.obs=!state.obs; |
244 | | - if (state.obs){ state.xco2=SND.meta.xco2_l2_ppm; state.sza=SND.meta.sza; state.vza=SND.meta.vza; state.psurf=SND.meta.surface_pressure_hpa; document.getElementById("xco2").value=state.xco2; document.getElementById("sza").value=state.sza; document.getElementById("psurf").value=Math.round(state.psurf); } else state.vza=0; |
| 244 | + if (state.obs){ state.albL2=true; state.xco2=SND.meta.xco2_l2_ppm; state.sza=SND.meta.sza; state.vza=SND.meta.vza; state.psurf=SND.meta.surface_pressure_hpa; document.getElementById("xco2").value=state.xco2; document.getElementById("sza").value=state.sza; document.getElementById("psurf").value=Math.round(state.psurf); } else { state.albL2=false; state.vza=0; } |
245 | 245 | syncChips(); draw(); }); |
246 | 246 | cv.addEventListener("mousemove", e=>{ const r=cv.getBoundingClientRect(); hoverX=e.clientX-r.left; if (cur.aband) render(cv.getContext("2d"), cv.clientWidth, 1); }); |
247 | 247 | cv.addEventListener("mouseleave", ()=>{ hoverX=null; if (cur.aband) render(cv.getContext("2d"), cv.clientWidth, 1); }); |
|
0 commit comments