|
| 1 | +var data; // loaded asynchronously |
| 2 | +var data_quantile; // computed after load |
| 3 | +var data_mean; |
| 4 | +var data_std; |
| 5 | +var data_min; |
| 6 | +var data_max; |
| 7 | + |
| 8 | +var county_codes; |
| 9 | +var m = Number.MAX_VALUE; |
| 10 | +var km_to_m = 1.0 / 1.609344; |
| 11 | +// removed 0 here and hacked the legend code so that we don't have white + white borders |
| 12 | +var legend_min = {1:m, 2:m, 3:m, 4:m, 5:m, 6:m, 7:m, 8:m}; |
| 13 | +var legend_max = {1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0}; |
| 14 | +/* |
| 15 | +0.00 - 0.04% |
| 16 | +0.04 - 0.09% |
| 17 | +0.09 - 0.13% |
| 18 | +0.13 - 0.18% |
| 19 | +0.18 - 0.24% |
| 20 | +0.24 - 0.32% |
| 21 | +0.32 - 0.46% |
| 22 | +0.46 - 21.51% |
| 23 | +*/ |
| 24 | +var legend_min = {1:0.0000, 2:0.0004, 3:0.0009, 4:0.0013, 5:0.0018, 6:0.0024, 7:0.0032, 8:0.0046}; |
| 25 | +var legend_max = {1:0.0004, 2:0.0009, 3:0.0013, 4:0.0018, 5:0.0024, 6:0.0032, 7:0.0046, 8:0.2151}; |
| 26 | + |
| 27 | + |
| 28 | +var percent = d3.format(".2%"); |
| 29 | +var percentx = function(x) { return d3.format(".2f")(100*x);} |
| 30 | +var fixed = d3.format(".0f"); |
| 31 | +var number = d3.format("n"); |
| 32 | +var fixedx = function(x) { return d3.format(".0f")(km_to_m*x);} |
| 33 | +var format = percent; |
| 34 | +var formatx = percentx; |
| 35 | + |
| 36 | +var path = d3.geo.path(); |
| 37 | + |
| 38 | +var svg = d3.select("#chart") |
| 39 | + .append("svg:svg"); |
| 40 | + |
| 41 | +var title = svg.append("svg:text") |
| 42 | + .attr("text-anchor", "start") |
| 43 | + .attr("dx", 10) |
| 44 | + .attr("dy", 18) |
| 45 | + .attr("class", "title") |
| 46 | + ; |
| 47 | + |
| 48 | +var counties = svg.append("svg:g") |
| 49 | + .attr("id", "counties") |
| 50 | + .attr("class", "Reds"); |
| 51 | + |
| 52 | +var states = svg.append("svg:g") |
| 53 | + .attr("id", "states"); |
| 54 | + |
| 55 | +var legend = svg.append("svg:g") |
| 56 | + .attr("id", "legend") |
| 57 | + .attr("class", "Reds"); |
| 58 | + |
| 59 | +d3.json("us-counties.json", function(json) { |
| 60 | + counties.selectAll("path") |
| 61 | + .data(json.features) |
| 62 | + .enter().append("svg:path") |
| 63 | + .attr("class", data ? quantize : null) |
| 64 | + .attr("d", path) |
| 65 | + .on("mouseover", show(true)) |
| 66 | + .on("mouseout", show(false)) |
| 67 | + ; |
| 68 | + |
| 69 | + make_legend(); |
| 70 | +}); |
| 71 | + |
| 72 | +d3.json("us-states.json", function(json) { |
| 73 | + states.selectAll("path") |
| 74 | + .data(json.features) |
| 75 | + .enter().append("svg:path") |
| 76 | + .attr("d", path); |
| 77 | +}); |
| 78 | + |
| 79 | +d3.json("county_codes.json", function(json) { |
| 80 | + county_codes = json; |
| 81 | +}); |
| 82 | + |
| 83 | +d3.json("data.json", function(json) { |
| 84 | + data = json; |
| 85 | + |
| 86 | + populate_stats(data); |
| 87 | + |
| 88 | + counties.selectAll("path") |
| 89 | + .attr("class", quantize) |
| 90 | + ; |
| 91 | + |
| 92 | + make_legend(); |
| 93 | + |
| 94 | +}); |
| 95 | + |
| 96 | +function make_legend() |
| 97 | +{ |
| 98 | + if (!data) |
| 99 | + return; |
| 100 | + |
| 101 | + // populate legend |
| 102 | + var mins = get_values(legend_min); |
| 103 | + legend.selectAll("path") |
| 104 | + .data(mins) |
| 105 | + .enter().append("svg:rect") |
| 106 | + .attr("width", 40) |
| 107 | + .attr("height", 20) |
| 108 | + .attr("y", function(d, i){ return 30 + i*21;}) |
| 109 | + .attr("x", 10) |
| 110 | + .attr("class", function(d, i){return "q" + (i+1) + "-9";}) |
| 111 | + ; |
| 112 | + |
| 113 | + var maxes = get_values(legend_max); |
| 114 | + legend.selectAll("text") |
| 115 | + .data(mins) |
| 116 | + .enter().append("svg:text") |
| 117 | + .attr("text-anchor", "start") // text-align |
| 118 | + .attr("x", 50) |
| 119 | + .attr("y", function(d, i){return 30 + i*21}) |
| 120 | + .attr("dx", 3) // padding-right |
| 121 | + .attr("dy", 12 + 4) // vertical-align: used font size (copied from css. must be a better way) |
| 122 | + .attr("class", "legend") |
| 123 | + .text(function (d, i){return formatx(d) + " - " + format(maxes[i]);}) |
| 124 | + ; |
| 125 | +} |
| 126 | + |
| 127 | +function show(b) |
| 128 | +{ |
| 129 | + return function(d, i) { |
| 130 | + var s = counties.selectAll("path").filter(function(g){return g.id == d.id;}); |
| 131 | + if (b) |
| 132 | + { |
| 133 | + title.text(county_codes[d.id] + ": " + format(data[d.id] !== undefined ? data[d.id] : 0)); |
| 134 | + s.attr("class", "highlight");//"q0-9" |
| 135 | + } |
| 136 | + else |
| 137 | + { |
| 138 | + title.text(""); |
| 139 | + s.attr("class", quantize); |
| 140 | + } |
| 141 | + } |
| 142 | +} |
| 143 | + |
| 144 | +function __quantize(f, min, max) |
| 145 | +{ |
| 146 | + // quantile scaling |
| 147 | + var q = data_quantile(f); |
| 148 | + |
| 149 | + // log scaling (works for county_pop data) |
| 150 | + var l = ~~(Math.log(f+1) * (9 / (Math.log(data_max) - Math.log(data_min+1)))); |
| 151 | + |
| 152 | + // original scaling (ish). |
| 153 | + var o = ~~(f * 9 / (data_mean + data_std)); |
| 154 | + |
| 155 | + // original with less head room |
| 156 | + var ol = ~~(f * 11 / (data_mean + data_std)); |
| 157 | + |
| 158 | + // original with more head room |
| 159 | + var om = ~~(f * 7 / (data_mean + data_std)); |
| 160 | + |
| 161 | + return Math.max(min, Math.min(max, q)); |
| 162 | +} |
| 163 | + |
| 164 | +function quantize(d) { |
| 165 | + // map data[d.id] to be between 0 and 8 |
| 166 | + // original code did: |
| 167 | + // values ranged between 1.2 and 30.1. Avg was 9, std is 3.65 |
| 168 | + var min = 1; |
| 169 | + var max = 8; |
| 170 | + var f = data[d.id]; |
| 171 | + if (f == undefined) |
| 172 | + f = 0; |
| 173 | + |
| 174 | + var q = __quantize(f, min, max); |
| 175 | + legend_min[q] = Math.min(legend_min[q], f); |
| 176 | + legend_max[q] = Math.max(legend_max[q], f); |
| 177 | + |
| 178 | + return "q" + q + "-9"; |
| 179 | +} |
| 180 | + |
| 181 | +var get_values = function(obj) |
| 182 | +{ |
| 183 | + var values = []; |
| 184 | + for (var key in obj) |
| 185 | + { |
| 186 | + if (obj.hasOwnProperty(key)) |
| 187 | + values.push(obj[key]); |
| 188 | + } |
| 189 | + return values; |
| 190 | +} |
| 191 | + |
| 192 | +var populate_stats = function(data) |
| 193 | +{ |
| 194 | + // need sorted values for quantile |
| 195 | + var values = get_values(data); |
| 196 | + data_quantile = d3.scale.quantile(); |
| 197 | + data_quantile.domain(values); |
| 198 | + data_quantile.range([1,2,3,4,5,6,7,8]); |
| 199 | + |
| 200 | + data_mean = d3.mean(values); |
| 201 | + data_std = std(values); |
| 202 | + data_max = d3.max(values); |
| 203 | + data_min = d3.min(values); |
| 204 | +} |
| 205 | + |
| 206 | +var std = function(l) |
| 207 | +{ |
| 208 | + var M = 0.0; |
| 209 | + var S = 0.0; |
| 210 | + var k = 1; |
| 211 | + for (var i = 0; i < l.length; i++) |
| 212 | + { |
| 213 | + var value = l[i]; |
| 214 | + var tmpM = M; |
| 215 | + M += (value - tmpM) / k; |
| 216 | + S += (value - tmpM) * (value - M); |
| 217 | + k++; |
| 218 | + } |
| 219 | + return Math.sqrt(S / (k-1)); |
| 220 | +} |
0 commit comments