diff --git a/viz/board.html b/viz/board.html index f5b18e6ba9..dd2eb394dd 100644 --- a/viz/board.html +++ b/viz/board.html @@ -123,28 +123,35 @@ function pathFor(g){let d="";(g.type==="Polygon"?[g.coordinates]:g.coordinates). // ---- lenses ---- const LENSES={ coverage:{label:"Coverage", kind:"cov"}, - pop:{label:"Population", field:"pop", scale:"sqrt", fmt:fmtN, hi:[67,214,196]}, - income:{label:"Median income", field:"income", scale:"lin", fmt:fmt$, hi:[122,208,140]}, - poverty:{label:"Poverty rate", field:"poverty", scale:"lin", fmt:fmtP, hi:[244,120,90]}, + pop:{label:"Population", field:"pop", fmt:fmtN, lo:[22,48,60], hi:[96,236,244]}, + income:{label:"Median income", field:"income", fmt:fmt$, lo:[28,54,42], hi:[150,246,142]}, + poverty:{label:"Poverty rate", field:"poverty", fmt:fmtP, lo:[48,40,36], hi:[252,150,96]}, }; const ORDER=["coverage","pop","income","poverty"]; -const BASE=[20,27,37]; // --dark-terr-ish -let cur="coverage", domains={}, maxOH=0; +const BASE=[26,34,46]; // --dark-terr-ish +let cur="income", domains={}, maxOH=0; Object.values(D).forEach(v=>{ if(v.oh>maxOH)maxOH=v.oh; }); +const ranks={}; // sorted value arrays, for percentile (rank) coloring for(const key of ORDER){ const L=LENSES[key]; if(!L.field)continue; - let lo=1e15,hi=-1e15; Object.values(D).forEach(v=>{const x=v[L.field]; if(x!=null){if(xhi)hi=x;}}); + let lo=1e15,hi=-1e15; const vals=[]; + Object.values(D).forEach(v=>{const x=v[L.field]; if(x!=null){vals.push(x); if(xhi)hi=x;}}); domains[key]=[lo,hi]; + ranks[key]=vals.sort((a,b)=>a-b); } +function rankT(key,x){ const a=ranks[key]; let lo=0,hi=a.length; + while(lo>1; if(a[m]1 ? lo/(a.length-1) : 0.5; } function mix(a,b,t){return `rgb(${a.map((x,i)=>Math.round(x+(b[i]-x)*t)).join(",")})`;} function colorFor(fips){ const v=D[fips]; const L=LENSES[cur]; - if(!v) return "#0d1219"; - if(L.kind==="cov") return v.oh>0 ? mix([60,44,20],[244,178,78],Math.sqrt(v.oh/maxOH)) : "#141b25"; - const x=v[L.field]; if(x==null) return "#141b25"; - const[lo,hi]=domains[cur]; let t=(x-lo)/(hi-lo||1); - if(L.scale==="sqrt") t=Math.sqrt(t); - return mix(BASE,L.hi,Math.max(0,Math.min(1,t))); + if(!v) return "#101720"; + if(L.kind==="cov"){ + if(!(v.oh>0)) return "#18212e"; + return mix([120,82,34],[250,201,105], 0.45+0.55*Math.sqrt(v.oh/maxOH)); + } + const x=v[L.field]; if(x==null) return "#18212e"; + return mix(L.lo, L.hi, 0.14+0.86*rankT(cur,x)); // percentile spread, per-lens ramp } // ---- render (single innerHTML string; event delegation for hover) ---- @@ -190,7 +197,7 @@ function renderLegend(){ }else{ const[lo,hi]=domains[cur]; el.innerHTML=` -
+
${L.fmt(lo)}${L.fmt(hi)}
no data
`; } @@ -223,6 +230,6 @@ document.getElementById("t-people").textContent="11,285"; document.getElementById("t-counties").textContent=Object.keys(D).length.toLocaleString(); document.getElementById("t-lit").textContent=nLit; -render(); renderLegend(); document.getElementById("stagefoot").textContent=foot.coverage; +render(); renderLegend(); document.getElementById("stagefoot").textContent=foot[cur]; requestAnimationFrame(()=>requestAnimationFrame(()=>document.getElementById("board").classList.remove("booting")));