Download index.html from usmar/redrawn: direct link, hf CLI and curl.
- Browser
- Download file 21.1 kB
-
https://huggingface.co/spaces/usmar/redrawn/resolve/main/index.html
- Command line
-
hf download hf://spaces/usmar/redrawn/index.html
-
curl -L -o index.html https://huggingface.co/spaces/usmar/redrawn/resolve/main/index.html
21.1 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>redrawn — does the map change the answer?</title> | |
| <style> | |
| :root { | |
| --surface: #fcfcfb; | |
| --panel: #ffffff; | |
| --ink: #14140f; | |
| --ink2: #52514e; | |
| --muted: #898781; | |
| --line: #e4e3dc; | |
| --blue: #2a78d6; | |
| --orange: #eb6834; | |
| --aqua: #1baf7a; | |
| --shadow: 0 1px 2px rgba(20,20,15,.05), 0 8px 24px rgba(20,20,15,.06); | |
| --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; | |
| } | |
| @media (prefers-color-scheme: dark) { | |
| :root { | |
| --surface: #14140f; --panel: #1c1c17; --ink: #f3f2ec; --ink2: #b8b6ad; | |
| --muted: #8a887f; --line: #2e2e26; | |
| --blue: #6ea8f0; --orange: #ff8a5c; --aqua: #3fd39c; | |
| --shadow: 0 1px 2px rgba(0,0,0,.3), 0 8px 24px rgba(0,0,0,.35); | |
| } | |
| } | |
| * { box-sizing: border-box; } | |
| body { | |
| margin: 0; background: var(--surface); color: var(--ink); | |
| font: 15px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; | |
| -webkit-font-smoothing: antialiased; | |
| } | |
| .wrap { max-width: 1240px; margin: 0 auto; padding: 34px 22px 70px; } | |
| header { margin-bottom: 26px; } | |
| h1 { | |
| margin: 0 0 6px; font-size: 30px; letter-spacing: -.022em; font-weight: 660; | |
| } | |
| h1 span { color: var(--muted); font-weight: 400; } | |
| .lede { margin: 0; max-width: 74ch; color: var(--ink2); font-size: 15.5px; } | |
| .lede b { color: var(--ink); font-weight: 620; } | |
| .layout { display: grid; grid-template-columns: minmax(0,1.32fr) minmax(0,1fr); gap: 20px; } | |
| @media (max-width: 940px) { .layout { grid-template-columns: 1fr; } } | |
| .card { | |
| background: var(--panel); border: 1px solid var(--line); border-radius: 12px; | |
| padding: 16px 18px; box-shadow: var(--shadow); | |
| } | |
| .card h2 { | |
| margin: 0 0 12px; font-size: 11.5px; font-weight: 660; letter-spacing: .075em; | |
| text-transform: uppercase; color: var(--muted); | |
| } | |
| .maps { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; } | |
| button.chip { | |
| font: inherit; font-size: 13px; padding: 6px 11px; border-radius: 999px; cursor: pointer; | |
| border: 1px solid var(--line); background: transparent; color: var(--ink2); | |
| transition: background .12s, color .12s, border-color .12s; | |
| } | |
| button.chip:hover { border-color: var(--blue); color: var(--ink); } | |
| button.chip[aria-pressed="true"] { | |
| background: var(--blue); border-color: var(--blue); color: #fff; font-weight: 560; | |
| } | |
| button.chip .k { opacity: .62; font-variant-numeric: tabular-nums; margin-left: 4px; } | |
| #map { width: 100%; height: auto; display: block; } | |
| #map path { vector-effect: none; } | |
| #map path.hi { stroke: var(--ink) ; } | |
| .readout { display: flex; align-items: baseline; gap: 12px; margin-bottom: 2px; } | |
| .rval { | |
| font: 640 44px/1 var(--mono); letter-spacing: -.02em; font-variant-numeric: tabular-nums; | |
| } | |
| .rlab { color: var(--muted); font-size: 13px; } | |
| .sub { color: var(--ink2); font-size: 13px; margin: 4px 0 0; } | |
| .dial { margin: 16px 0 6px; } | |
| .dial svg { width: 100%; height: 62px; display: block; } | |
| .grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 16px; margin-top: 4px; } | |
| .stat { border-top: 1px solid var(--line); padding-top: 8px; } | |
| .stat .v { font: 620 19px/1.2 var(--mono); font-variant-numeric: tabular-nums; } | |
| .stat .l { font-size: 11.5px; color: var(--muted); margin-top: 3px; } | |
| .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; } | |
| button.solid { | |
| font: inherit; font-size: 13.5px; font-weight: 560; padding: 8px 14px; border-radius: 8px; | |
| cursor: pointer; border: 1px solid var(--blue); background: var(--blue); color: #fff; | |
| } | |
| button.solid:hover { filter: brightness(1.07); } | |
| button.ghost { | |
| font: inherit; font-size: 13px; padding: 8px 12px; border-radius: 8px; cursor: pointer; | |
| border: 1px solid var(--line); background: transparent; color: var(--ink2); | |
| } | |
| button.ghost:hover { border-color: var(--muted); color: var(--ink); } | |
| label.slider { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink2); } | |
| input[type=range] { accent-color: var(--blue); width: 128px; } | |
| select { | |
| font: inherit; font-size: 13px; padding: 6px 8px; border-radius: 7px; | |
| border: 1px solid var(--line); background: var(--panel); color: var(--ink); | |
| } | |
| .kval { font: 600 13px var(--mono); font-variant-numeric: tabular-nums; min-width: 30px; } | |
| #strip { width: 100%; height: 96px; display: block; margin-top: 4px; } | |
| #scatter { width: 100%; height: 250px; display: block; } | |
| .foot { margin-top: 26px; color: var(--muted); font-size: 12.5px; max-width: 88ch; } | |
| .foot a { color: var(--blue); } | |
| .tag { | |
| display: inline-block; font-size: 11px; padding: 2px 7px; border-radius: 999px; | |
| border: 1px solid var(--line); color: var(--muted); margin-left: 6px; | |
| } | |
| .tag.ok { color: var(--aqua); border-color: color-mix(in srgb, var(--aqua) 40%, transparent); } | |
| #loading { padding: 40px 0; color: var(--muted); } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="wrap"> | |
| <header> | |
| <h1>redrawn <span>— does the map change the answer?</span></h1> | |
| <p class="lede"> | |
| Every number below comes from the same <b id="ncrash">…</b> New York City crashes. | |
| Nothing is added, removed or reweighted. The only thing that changes is | |
| <b>where the boundaries are drawn</b> — and the correlation an analyst would | |
| report moves from near zero to almost one. | |
| </p> | |
| </header> | |
| <div id="loading">Loading the city…</div> | |
| <div class="layout" id="app" hidden> | |
| <div> | |
| <div class="card"> | |
| <h2>The map</h2> | |
| <div class="maps" id="maps"></div> | |
| <svg id="map" preserveAspectRatio="xMidYMid meet" role="img" | |
| aria-label="Choropleth of New York City coloured by zone injury rate"></svg> | |
| <p class="sub" id="maphint">Shaded by each zone's injury rate. Hover a zone for its numbers.</p> | |
| </div> | |
| </div> | |
| <div> | |
| <div class="card"> | |
| <h2>What this map reports</h2> | |
| <div class="readout"> | |
| <span class="rval" id="r">—</span> | |
| <span class="rlab" id="rlab">correlation across zones</span> | |
| </div> | |
| <p class="sub" id="rdesc"></p> | |
| <div class="dial"><svg id="dial" viewBox="0 0 100 22" preserveAspectRatio="none"></svg></div> | |
| <div class="grid2"> | |
| <div class="stat"><div class="v" id="kzones">—</div><div class="l">zones</div></div> | |
| <div class="stat"><div class="v" id="rw">—</div><div class="l">weighted by crashes</div></div> | |
| <div class="stat"><div class="v" id="pointr">—</div><div class="l">between individual crashes</div></div> | |
| <div class="stat"><div class="v" id="contig">—</div><div class="l">every zone connected</div></div> | |
| </div> | |
| </div> | |
| <div class="card" style="margin-top:16px"> | |
| <h2>Zone rates <span id="pairlab" class="tag"></span></h2> | |
| <div class="controls" style="margin-bottom:10px"> | |
| <select id="pair"></select> | |
| </div> | |
| <svg id="scatter" role="img" aria-label="Scatter of zone-level rates"></svg> | |
| <p class="sub">One dot per zone, sized by crash count.</p> | |
| </div> | |
| <div class="card" style="margin-top:16px"> | |
| <h2>Draw your own</h2> | |
| <div class="controls"> | |
| <button class="solid" id="draw">Draw a new map</button> | |
| <label class="slider">zones | |
| <input type="range" id="k" min="5" max="400" value="71"> | |
| <span class="kval" id="kval">71</span> | |
| </label> | |
| <select id="family"> | |
| <option value="merge">balanced (merge)</option> | |
| <option value="grow">ragged (region grow)</option> | |
| </select> | |
| <button class="ghost" id="clear">Clear</button> | |
| </div> | |
| <svg id="strip" role="img" aria-label="Correlations from every map drawn so far"></svg> | |
| <p class="sub" id="striphint"> | |
| Each tick is one map you have drawn. Keep pressing — the answer wanders. | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| <p class="foot"> | |
| Crashes: NYPD Motor Vehicle Collisions, <span id="years">…</span>, via NYC Open Data. | |
| Boundaries: NYC Department of City Planning. Correlations are recomputed in your | |
| browser from the same per-tract counts the analysis uses — see | |
| <span class="mono">maup.js</span>. | |
| </p> | |
| </div> | |
| <script src="maup.js"></script> | |
| <script> | |
| (function () { | |
| "use strict"; | |
| var D = null, paths = [], nodes = [], current = null, currentKey = "cdta"; | |
| var drawn = [], pairKey = "speeding", seedCounter = 1; | |
| var $ = function (id) { return document.getElementById(id); }; | |
| var fmt = function (v, d) { return (v >= 0 ? "+" : "−") + Math.abs(v).toFixed(d === undefined ? 3 : d); }; | |
| /* Sequential ramp matching the README figures. */ | |
| var RAMP = [[238,243,250],[157,192,232],[42,120,214],[18,53,94]]; | |
| function ramp(t) { | |
| t = Math.max(0, Math.min(1, t)) * (RAMP.length - 1); | |
| var i = Math.min(RAMP.length - 2, Math.floor(t)), f = t - i; | |
| var a = RAMP[i], b = RAMP[i + 1]; | |
| return "rgb(" + Math.round(a[0] + (b[0] - a[0]) * f) + "," + | |
| Math.round(a[1] + (b[1] - a[1]) * f) + "," + | |
| Math.round(a[2] + (b[2] - a[2]) * f) + ")"; | |
| } | |
| function counts() { | |
| var p = D.meta.pairs[pairKey]; | |
| return { x: D.counts[p.x], y: D.counts[p.y], xn: p.x, yn: p.y }; | |
| } | |
| function buildMap() { | |
| var svg = $("map"); | |
| svg.setAttribute("viewBox", D.meta.viewBox.join(" ")); | |
| var frag = document.createDocumentFragment(); | |
| for (var i = 0; i < D.paths.length; i++) { | |
| var el = document.createElementNS("http://www.w3.org/2000/svg", "path"); | |
| el.setAttribute("d", D.paths[i]); | |
| el.dataset.i = i; | |
| frag.appendChild(el); | |
| nodes.push(el); | |
| } | |
| svg.appendChild(frag); | |
| // Stroke width is in user units; the viewBox is ~0.47 wide, so keep it tiny. | |
| var scale = D.meta.viewBox[2] / 900; | |
| var style = document.createElementNS("http://www.w3.org/2000/svg", "style"); | |
| style.textContent = "#map path{stroke-width:" + (scale * 0.35) + "}" + | |
| "#map path.hi{stroke-width:" + (scale * 2.2) + "}"; | |
| svg.appendChild(style); | |
| svg.addEventListener("mousemove", function (e) { | |
| var t = e.target; | |
| if (t.tagName !== "path") return; | |
| hover(+t.dataset.i); | |
| }); | |
| svg.addEventListener("mouseleave", function () { | |
| $("maphint").textContent = "Shaded by each zone's injury rate. Hover a zone for its numbers."; | |
| nodes.forEach(function (nd) { nd.classList.remove("hi"); }); | |
| }); | |
| } | |
| function hover(i) { | |
| var z = current[i], c = counts(); | |
| var zn = 0, zx = 0, zy = 0, members = 0; | |
| for (var j = 0; j < current.length; j++) { | |
| if (current[j] === z) { zn += D.n[j]; zx += c.x[j]; zy += c.y[j]; members++; } | |
| } | |
| nodes.forEach(function (nd, j) { nd.classList.toggle("hi", current[j] === z); }); | |
| $("maphint").innerHTML = "<b>Zone " + z + "</b> · " + members + " tracts · " + | |
| zn.toLocaleString() + " crashes · " + c.yn + " " + (100 * zy / zn).toFixed(1) + "% · " + | |
| c.xn + " " + (100 * zx / zn).toFixed(2) + "%"; | |
| } | |
| function paint() { | |
| var c = counts(); | |
| var z = MAUP.zoneRates(current, D.n, c.x, c.y); | |
| var lo = 0.15, hi = 0.40; | |
| var k = 0; | |
| for (var i = 0; i < current.length; i++) if (current[i] + 1 > k) k = current[i] + 1; | |
| var zn = new Float64Array(k), zy = new Float64Array(k); | |
| for (i = 0; i < current.length; i++) { zn[current[i]] += D.n[i]; zy[current[i]] += c.y[i]; } | |
| var colour = new Array(k); | |
| for (i = 0; i < k; i++) colour[i] = zn[i] > 0 ? ramp((zy[i] / zn[i] - lo) / (hi - lo)) : "transparent"; | |
| for (i = 0; i < nodes.length; i++) { | |
| var col = colour[current[i]]; | |
| nodes[i].setAttribute("fill", col); | |
| // Stroke each tract in its own fill colour. That closes the hairline | |
| // seams between tracts without drawing a grid over the map, so tracts | |
| // sharing a zone merge into one solid block and the zones are what the | |
| // eye actually sees. | |
| nodes[i].setAttribute("stroke", col); | |
| } | |
| return z; | |
| } | |
| function score(label) { | |
| var c = counts(); | |
| var z = paint(); | |
| var r = MAUP.pearson(z.x, z.y); | |
| var rw = MAUP.weightedPearson(z.x, z.y, z.n); | |
| $("r").textContent = fmt(r); | |
| $("r").style.color = r >= 0 ? "var(--blue)" : "var(--orange)"; | |
| $("kzones").textContent = z.k; | |
| $("rw").textContent = fmt(rw); | |
| $("pointr").textContent = fmt(D.meta.pointR[pairKey]); | |
| var ok = MAUP.isContiguous(current, D.adjStart, D.adj); | |
| $("contig").textContent = ok ? "yes" : "no"; | |
| $("rdesc").textContent = label; | |
| dial(r); | |
| scatter(z); | |
| return r; | |
| } | |
| function dial(r) { | |
| var svg = $("dial"), p = D.meta.pointR[pairKey]; | |
| var toX = function (v) { return (v + 1) / 2 * 100; }; | |
| svg.innerHTML = | |
| '<rect x="0" y="8" width="100" height="6" rx="3" fill="var(--line)"/>' + | |
| '<rect x="' + Math.min(toX(0), toX(r)) + '" y="8" width="' + Math.abs(toX(r) - toX(0)) + | |
| '" height="6" rx="3" fill="' + (r >= 0 ? "var(--blue)" : "var(--orange)") + '"/>' + | |
| '<line x1="' + toX(0) + '" y1="4" x2="' + toX(0) + '" y2="18" stroke="var(--muted)" stroke-width=".5"/>' + | |
| '<line x1="' + toX(p) + '" y1="3" x2="' + toX(p) + '" y2="19" stroke="var(--aqua)" stroke-width=".9"/>' + | |
| '<circle cx="' + toX(r) + '" cy="11" r="3.6" fill="var(--panel)" stroke="' + | |
| (r >= 0 ? "var(--blue)" : "var(--orange)") + '" stroke-width="1.6"/>' + | |
| '<text x="0" y="21.5" font-size="4" fill="var(--muted)">-1</text>' + | |
| '<text x="97" y="21.5" font-size="4" fill="var(--muted)">+1</text>' + | |
| '<text x="' + toX(p) + '" y="3.4" font-size="4" fill="var(--aqua)" text-anchor="middle">crash level</text>'; | |
| } | |
| function scatter(z) { | |
| var svg = $("scatter"), W = svg.clientWidth || 380, H = 250; | |
| svg.setAttribute("viewBox", "0 0 " + W + " " + H); | |
| var m = { l: 44, r: 10, t: 10, b: 32 }; | |
| var xs = z.x, ys = z.y; | |
| var xmax = Math.max.apply(null, xs) * 1.06 || 1; | |
| var ymin = Math.min.apply(null, ys) * 0.94, ymax = Math.max.apply(null, ys) * 1.04 || 1; | |
| var nmax = Math.max.apply(null, z.n); | |
| var X = function (v) { return m.l + v / xmax * (W - m.l - m.r); }; | |
| var Y = function (v) { return H - m.b - (v - ymin) / (ymax - ymin || 1) * (H - m.t - m.b); }; | |
| var c = counts(); | |
| var out = '<line x1="' + m.l + '" y1="' + (H - m.b) + '" x2="' + (W - m.r) + '" y2="' + (H - m.b) + | |
| '" stroke="var(--line)"/><line x1="' + m.l + '" y1="' + m.t + '" x2="' + m.l + '" y2="' + | |
| (H - m.b) + '" stroke="var(--line)"/>'; | |
| for (var i = 0; i < xs.length; i++) { | |
| var rad = 2 + 5 * Math.sqrt(z.n[i] / nmax); | |
| out += '<circle cx="' + X(xs[i]).toFixed(1) + '" cy="' + Y(ys[i]).toFixed(1) + '" r="' + | |
| rad.toFixed(1) + '" fill="var(--blue)" fill-opacity=".42" stroke="var(--blue)" stroke-opacity=".7" stroke-width=".7"/>'; | |
| } | |
| out += '<text x="' + ((W + m.l) / 2) + '" y="' + (H - 6) + '" font-size="11" fill="var(--muted)" text-anchor="middle">' + | |
| c.xn + ' rate</text>'; | |
| out += '<text transform="translate(12,' + ((H - m.b + m.t) / 2) + ') rotate(-90)" font-size="11" fill="var(--muted)" text-anchor="middle">' + | |
| c.yn + ' rate</text>'; | |
| out += '<text x="' + m.l + '" y="' + (H - m.b + 14) + '" font-size="10" fill="var(--muted)">0</text>'; | |
| out += '<text x="' + (W - m.r) + '" y="' + (H - m.b + 14) + '" font-size="10" fill="var(--muted)" text-anchor="end">' + | |
| (100 * xmax).toFixed(1) + '%</text>'; | |
| out += '<text x="' + (m.l - 6) + '" y="' + (m.t + 8) + '" font-size="10" fill="var(--muted)" text-anchor="end">' + | |
| (100 * ymax).toFixed(0) + '%</text>'; | |
| svg.innerHTML = out; | |
| } | |
| function strip() { | |
| var svg = $("strip"), W = svg.clientWidth || 520, H = 96; | |
| svg.setAttribute("viewBox", "0 0 " + W + " " + H); | |
| var m = 26; | |
| var X = function (v) { return m + (v + 1) / 2 * (W - 2 * m); }; | |
| var out = '<line x1="' + m + '" y1="62" x2="' + (W - m) + '" y2="62" stroke="var(--line)" stroke-width="2"/>'; | |
| [-1, -0.5, 0, 0.5, 1].forEach(function (t) { | |
| out += '<line x1="' + X(t) + '" y1="58" x2="' + X(t) + '" y2="66" stroke="var(--muted)" stroke-width=".8"/>'; | |
| out += '<text x="' + X(t) + '" y="79" font-size="10" fill="var(--muted)" text-anchor="middle">' + | |
| (t > 0 ? "+" : "") + t + '</text>'; | |
| }); | |
| drawn.forEach(function (d) { | |
| out += '<line x1="' + X(d.r) + '" y1="40" x2="' + X(d.r) + '" y2="60" stroke="var(--blue)" stroke-opacity=".5" stroke-width="1.4"/>'; | |
| }); | |
| Object.keys(D.partitions).forEach(function (key) { | |
| var p = D.partitions[key]; | |
| if (!p.official) return; | |
| var r = MAUP.partitionCorrelation(p.labels, D.n, counts().x, counts().y); | |
| out += '<line x1="' + X(r) + '" y1="18" x2="' + X(r) + '" y2="62" stroke="var(--orange)" stroke-width="1.6"/>'; | |
| out += '<text x="' + X(r) + '" y="14" font-size="9.5" fill="var(--orange)" text-anchor="middle">' + | |
| (p.k >= 1000 ? "tracts" : p.k) + '</text>'; | |
| }); | |
| var pr = D.meta.pointR[pairKey]; | |
| out += '<line x1="' + X(pr) + '" y1="30" x2="' + X(pr) + '" y2="70" stroke="var(--aqua)" stroke-width="2"/>'; | |
| svg.innerHTML = out; | |
| } | |
| function select(key) { | |
| currentKey = key; | |
| var p = D.partitions[key]; | |
| current = Int32Array.from(p.labels); | |
| document.querySelectorAll("#maps .chip").forEach(function (b) { | |
| b.setAttribute("aria-pressed", String(b.dataset.key === key)); | |
| }); | |
| score(p.official ? "An official map of New York City." : "A map generated by this study."); | |
| strip(); | |
| } | |
| function drawNew() { | |
| var k = +$("k").value; | |
| var rand = MAUP.mulberry32(0x9e37 + (seedCounter++) * 2654435761 % 2147483647); | |
| var t0 = performance.now(); | |
| current = $("family").value === "merge" | |
| ? MAUP.mergeToK(D.adjStart, D.adj, D.n, k, rand) | |
| : MAUP.growToK(D.adjStart, D.adj, k, rand); | |
| document.querySelectorAll("#maps .chip").forEach(function (b) { | |
| b.setAttribute("aria-pressed", "false"); | |
| }); | |
| var r = score("A map this page just invented — contiguous, " + k + " zones, " + | |
| "drawn without looking at the crash data."); | |
| drawn.push({ r: r, k: k }); | |
| $("striphint").innerHTML = drawn.length === 1 | |
| ? "One map drawn. Keep pressing." | |
| : drawn.length + " maps drawn · range " + fmt(Math.min.apply(null, drawn.map(function (d) { return d.r; }))) + | |
| " to " + fmt(Math.max.apply(null, drawn.map(function (d) { return d.r; }))) + | |
| " · " + (performance.now() - t0).toFixed(0) + " ms to build the last one"; | |
| strip(); | |
| } | |
| fetch("data.json").then(function (res) { return res.json(); }).then(function (data) { | |
| D = data; | |
| D.n = Float64Array.from(D.n); | |
| Object.keys(D.counts).forEach(function (key) { D.counts[key] = Float64Array.from(D.counts[key]); }); | |
| D.adjStart = Int32Array.from(D.adjStart); | |
| D.adj = Int32Array.from(D.adj); | |
| $("ncrash").textContent = D.meta.crashes.toLocaleString(); | |
| $("years").textContent = D.meta.years[0] + "–" + D.meta.years[1]; | |
| var maps = $("maps"); | |
| Object.keys(D.partitions).forEach(function (key) { | |
| var p = D.partitions[key]; | |
| var b = document.createElement("button"); | |
| b.className = "chip"; | |
| b.dataset.key = key; | |
| b.innerHTML = p.name.replace(/ \(K=\d+\)/, "") + '<span class="k">' + p.k + "</span>"; | |
| b.setAttribute("aria-pressed", "false"); | |
| b.onclick = function () { select(key); }; | |
| maps.appendChild(b); | |
| }); | |
| var sel = $("pair"); | |
| Object.keys(D.meta.pairs).forEach(function (key) { | |
| var o = document.createElement("option"); | |
| o.value = key; | |
| o.textContent = D.meta.pairs[key].x + " vs " + D.meta.pairs[key].y; | |
| sel.appendChild(o); | |
| }); | |
| sel.value = pairKey; | |
| sel.onchange = function () { | |
| pairKey = sel.value; | |
| $("pairlab").textContent = D.meta.pairs[pairKey].x; | |
| drawn = []; | |
| score($("rdesc").textContent); | |
| strip(); | |
| }; | |
| $("pairlab").textContent = D.meta.pairs[pairKey].x; | |
| $("k").oninput = function () { $("kval").textContent = $("k").value; }; | |
| $("draw").onclick = drawNew; | |
| $("clear").onclick = function () { | |
| drawn = []; | |
| $("striphint").textContent = "Each tick is one map you have drawn. Keep pressing — the answer wanders."; | |
| strip(); | |
| }; | |
| $("loading").hidden = true; | |
| $("app").hidden = false; | |
| buildMap(); | |
| // ?map=<key> opens on a chosen map, which is how the README screenshots | |
| // land on the official districts and the gerrymandered one. | |
| var wanted = new URLSearchParams(location.search).get("map"); | |
| select(D.partitions[wanted] ? wanted : "cdta"); | |
| window.addEventListener("resize", function () { score($("rdesc").textContent); strip(); }); | |
| }).catch(function (err) { | |
| $("loading").textContent = "Could not load data.json — run `make web` to export it. (" + err + ")"; | |
| }); | |
| })(); | |
| </script> | |
| </body> | |
| </html> | |