@andyreagan/hedotools 7.0.0 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -1
- package/bundle.sh +36 -16
- package/dist/hedotools-bundle.js +4972 -0
- package/dist/hedotools.js +2091 -0
- package/package.json +11 -2
|
@@ -0,0 +1,2091 @@
|
|
|
1
|
+
// namespace it
|
|
2
|
+
var hedotools = hedotools || {};
|
|
3
|
+
|
|
4
|
+
// hedonometer.org/maps.html needs this in hedotools.map.js
|
|
5
|
+
var classColor = d3.scaleQuantize()
|
|
6
|
+
.range([0,1,2,3,4,5,6])
|
|
7
|
+
.domain([50,1]);
|
|
8
|
+
|
|
9
|
+
// begin with some helper functions
|
|
10
|
+
// http://stackoverflow.com/a/1026087/3780153
|
|
11
|
+
function capitaliseFirstLetter(string)
|
|
12
|
+
{
|
|
13
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// this works really well, but it's deadly slow (working max 5 elements)
|
|
17
|
+
// and it's coupled to jquery
|
|
18
|
+
// http://stackoverflow.com/a/5047712/3780153
|
|
19
|
+
String.prototype.width = function(font) {
|
|
20
|
+
var f = font || '12px arial',
|
|
21
|
+
o = $('<div>' + this + '</div>')
|
|
22
|
+
.css({'position': 'absolute', 'float': 'left', 'white-space': 'nowrap', 'visibility': 'hidden', 'font': f})
|
|
23
|
+
.appendTo($('body')),
|
|
24
|
+
w = o.width();
|
|
25
|
+
o.remove();
|
|
26
|
+
return w;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
String.prototype.safe = function() {
|
|
32
|
+
var tmp = this.split("/")
|
|
33
|
+
tmp[tmp.length-1] = escape(tmp[tmp.length-1])
|
|
34
|
+
return tmp.join("/");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// yup
|
|
38
|
+
// http://stackoverflow.com/questions/3883342/add-commas-to-a-number-in-jquery
|
|
39
|
+
function commaSeparateNumber(val){
|
|
40
|
+
while (/(\d+)(\d{3})/.test(val.toString())){
|
|
41
|
+
val = val.toString().replace(/(\d+)(\d{3})/, '$1'+','+'$2');
|
|
42
|
+
}
|
|
43
|
+
return val;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function splitWidth(s,w) {
|
|
47
|
+
// s is the string
|
|
48
|
+
// w is the width that we want to split it to
|
|
49
|
+
var t = s.split(" ");
|
|
50
|
+
var n = [t[0]];
|
|
51
|
+
var i = 1;
|
|
52
|
+
var j = 0;
|
|
53
|
+
while (i<t.length) {
|
|
54
|
+
if ((n[j]+t[i]).width() < w) {
|
|
55
|
+
n[j] += " "+t[i]
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
j++;
|
|
59
|
+
n.push(t[i]);
|
|
60
|
+
}
|
|
61
|
+
i++;
|
|
62
|
+
}
|
|
63
|
+
return n;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// look away
|
|
67
|
+
var intStr0 = ["zero","one","two","three","four","five","six","seven","eight","nine","then"];
|
|
68
|
+
var intStr = intStr0.slice(1,100);
|
|
69
|
+
|
|
70
|
+
/*
|
|
71
|
+
hedotools.urllib
|
|
72
|
+
=========
|
|
73
|
+
|
|
74
|
+
a simple hedotools plugin to manage pushing and pulling the visualization state to the brower url
|
|
75
|
+
|
|
76
|
+
tests
|
|
77
|
+
-----
|
|
78
|
+
no test suite, I've tested in in Chrome v35 for reasonable use cases
|
|
79
|
+
|
|
80
|
+
example
|
|
81
|
+
-------
|
|
82
|
+
also no simple example, but you can see it in use here:
|
|
83
|
+
http://www.uvm.edu/storylab/share/papers/dodds2014a/books.html
|
|
84
|
+
|
|
85
|
+
documentation
|
|
86
|
+
-------------
|
|
87
|
+
slightly more documentation in the README
|
|
88
|
+
|
|
89
|
+
new:
|
|
90
|
+
|
|
91
|
+
decoder returns { current, cached } values
|
|
92
|
+
the current will be blank if there is nothing in the url
|
|
93
|
+
but the cached remains
|
|
94
|
+
I like this feature
|
|
95
|
+
|
|
96
|
+
*/
|
|
97
|
+
hedotools.urllib = {
|
|
98
|
+
encoder: function() {
|
|
99
|
+
var varname = "tmp";
|
|
100
|
+
var varval = [];
|
|
101
|
+
var show = true;
|
|
102
|
+
//var that = this;
|
|
103
|
+
|
|
104
|
+
function urllib(d) {
|
|
105
|
+
// nothing yet
|
|
106
|
+
//console.log(this);
|
|
107
|
+
//console.log(that);
|
|
108
|
+
return {current: varval,};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function parseurl() {
|
|
112
|
+
GET = {};
|
|
113
|
+
query = window.location.search.substring(1).split("&");
|
|
114
|
+
// break down the url
|
|
115
|
+
for (var i = 0, max = query.length; i < max; i++)
|
|
116
|
+
{
|
|
117
|
+
if (query[i] === "") // check for trailing & with no param
|
|
118
|
+
continue;
|
|
119
|
+
var param = query[i].split("=");
|
|
120
|
+
GET[decodeURIComponent(param[0])] = decodeURIComponent(param[1] || "");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
baseUrl = window.location.origin+window.location.pathname;
|
|
124
|
+
var tmpStr = ""
|
|
125
|
+
if (typeof varval == 'string' || varval instanceof String)
|
|
126
|
+
{ tmpStr+=varval; }
|
|
127
|
+
else
|
|
128
|
+
{
|
|
129
|
+
tmpStr += "["+varval[0]
|
|
130
|
+
for (var i=1; i<varval.length; i++) { tmpStr += ","+varval[i]; }
|
|
131
|
+
tmpStr+="]"
|
|
132
|
+
}
|
|
133
|
+
GET[varname] = tmpStr;
|
|
134
|
+
|
|
135
|
+
var urlString = ""
|
|
136
|
+
for (var key in GET) {
|
|
137
|
+
if (GET.hasOwnProperty(key)) {
|
|
138
|
+
if (varname === key) {
|
|
139
|
+
// console.log("found that variable");
|
|
140
|
+
// console.log(show);
|
|
141
|
+
if (show) {
|
|
142
|
+
urlString += key+"="+GET[key]+"&";
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else { urlString += key+"="+GET[key]+"&"; }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
urlString = urlString.substring(0,urlString.length-1);
|
|
150
|
+
|
|
151
|
+
// only add to url if there is stuff
|
|
152
|
+
if (urlString.length > 0) {
|
|
153
|
+
newDataUrl = baseUrl+"?"+urlString
|
|
154
|
+
}
|
|
155
|
+
else { newDataUrl = baseUrl; }
|
|
156
|
+
|
|
157
|
+
window.history.replaceState("object or string", "title",newDataUrl);
|
|
158
|
+
|
|
159
|
+
return urllib;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
urllib.varname = function(_) {
|
|
163
|
+
if (!arguments.length) return varname;
|
|
164
|
+
varname = _;
|
|
165
|
+
return urllib;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
urllib.destroy = function() {
|
|
169
|
+
show = false;
|
|
170
|
+
parseurl();
|
|
171
|
+
show = true;
|
|
172
|
+
// return urllib;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
urllib.varval = function(_) {
|
|
176
|
+
if (!arguments.length) return varval;
|
|
177
|
+
varval = _;
|
|
178
|
+
return parseurl();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return urllib;
|
|
182
|
+
},
|
|
183
|
+
decoder: function() {
|
|
184
|
+
var varname = "tmp";
|
|
185
|
+
var varresult = [];
|
|
186
|
+
var defvalue = [];
|
|
187
|
+
|
|
188
|
+
function urllib(d) {
|
|
189
|
+
parseurl();
|
|
190
|
+
return {current: varresult,
|
|
191
|
+
cached: defvalue};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function parseurl() {
|
|
195
|
+
GET = {};
|
|
196
|
+
query = window.location.search.substring(1).split("&");
|
|
197
|
+
for (var i = 0, max = query.length; i < max; i++) {
|
|
198
|
+
if (query[i] === "") // check for trailing & with no param
|
|
199
|
+
continue;
|
|
200
|
+
var param = query[i].split("=");
|
|
201
|
+
GET[decodeURIComponent(param[0])] = decodeURIComponent(param[1] || "");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (varname in GET) {
|
|
205
|
+
if (GET[varname].length > 0 && GET[varname][0] === "[") {
|
|
206
|
+
if (GET[varname][GET[varname].length-1] === "]") {
|
|
207
|
+
var tmpArray = GET[varname].substring(1, GET[varname].length - 1).split(',');
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
var tmpArray = GET[varname].substring(1, GET[varname].length).split(',');
|
|
211
|
+
}
|
|
212
|
+
varresult = tmpArray;
|
|
213
|
+
defvalue = tmpArray;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
varresult = GET[varname];
|
|
217
|
+
defvalue = GET[varname];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
// if there is nothing in the url...we'll let the value
|
|
222
|
+
// live. this next line would kill the value
|
|
223
|
+
varresult = ""
|
|
224
|
+
}
|
|
225
|
+
return urllib;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
urllib.varname = function(_) {
|
|
229
|
+
if (!arguments.length) return varname;
|
|
230
|
+
varname = _;
|
|
231
|
+
return parseurl();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
urllib.varresult = function(_) {
|
|
235
|
+
if (!arguments.length) return varresult;
|
|
236
|
+
varresult = _;
|
|
237
|
+
defvalue = _;
|
|
238
|
+
return urllib;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return urllib;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
hedotools.barchartoncall = function() {
|
|
257
|
+
var test = function(d,i) {
|
|
258
|
+
// console.log(i);
|
|
259
|
+
i = indices[i];
|
|
260
|
+
if (stateSelType) {
|
|
261
|
+
shiftComp = i;
|
|
262
|
+
d3.select(".complabel").text(allData[i].name);
|
|
263
|
+
compencoder.varval(allData[i].name);
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
shiftRef = i;
|
|
267
|
+
d3.select(".reflabel").text(allData[i].name);
|
|
268
|
+
refencoder.varval(allData[i].name);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (shiftRef !== shiftComp) {
|
|
272
|
+
hedotools.shifter.shift(allData[shiftRef].freq,allData[shiftComp].freq,lens,words);
|
|
273
|
+
var happysad = hedotools.shifter._compH() > hedotools.shifter._refH() ? "happier" : "less happy";
|
|
274
|
+
hedotools.shifter.setfigure(d3.select('#shift01')).setText(["Why "+allData[shiftComp].name+" is "+happysad+" than "+allData[shiftRef].name+":"]).plot();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
var opublic = { test: test,
|
|
278
|
+
};
|
|
279
|
+
return opublic;
|
|
280
|
+
}();
|
|
281
|
+
|
|
282
|
+
// make the plot
|
|
283
|
+
hedotools.barchart = function() {
|
|
284
|
+
var figure;
|
|
285
|
+
|
|
286
|
+
var setfigure = function(_) {
|
|
287
|
+
// console.log("setting figure");
|
|
288
|
+
figure = _;
|
|
289
|
+
return hedotools.barchart;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
var xlabeltext = "Happiness difference from US as a whole";
|
|
293
|
+
var _xlabeltext = function(_) {
|
|
294
|
+
if (!arguments.length) return xlabeltext;
|
|
295
|
+
xlabeltext = _;
|
|
296
|
+
return hedotools.barchart;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
var data;
|
|
300
|
+
var datanames;
|
|
301
|
+
var geodata;
|
|
302
|
+
|
|
303
|
+
var setdata = function(a,b) {
|
|
304
|
+
data = a;
|
|
305
|
+
geodata = b;
|
|
306
|
+
datanames = Array(geodata.length);
|
|
307
|
+
for (var i=0; i<geodata.length; i++) {
|
|
308
|
+
datanames[i] = geodata[i].properties.name;
|
|
309
|
+
}
|
|
310
|
+
return hedotools.barchart;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
var _data = function(_) {
|
|
314
|
+
if (!arguments.length) return data;
|
|
315
|
+
data = _;
|
|
316
|
+
return hedotools.barchart;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
var _datanames = function(_) {
|
|
320
|
+
if (!arguments.length) return datanames;
|
|
321
|
+
datanames = _;
|
|
322
|
+
return hedotools.barchart;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
var figheight = 730;
|
|
326
|
+
var _figheight = function(_) {
|
|
327
|
+
if (!arguments.length) return figheight;
|
|
328
|
+
figheight = _;
|
|
329
|
+
return hedotools.barchart;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
var manualTicks = [];
|
|
333
|
+
var _manualTicks = function(_) {
|
|
334
|
+
if (!arguments.length) return manualTicks;
|
|
335
|
+
manualTicks = _;
|
|
336
|
+
return hedotools.barchart;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
var sortedStates;
|
|
340
|
+
var getSorted = function(_) {
|
|
341
|
+
if (!arguments.length) return sortedStates.map(function(d) { return d[2]; });
|
|
342
|
+
if (_) {
|
|
343
|
+
return sortedStates.map(function(d,i) { return (i+1)+". "+d[2]; });
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
return sortedStates.map(function(d) { return d[2]; });
|
|
347
|
+
}
|
|
348
|
+
return hedotools.barchart;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
var plot = function() {
|
|
353
|
+
/* plot the bar chart
|
|
354
|
+
|
|
355
|
+
-take a d3 selection, and draw the bar chart SVG on it
|
|
356
|
+
-requires the magnitude for each state, and the geojson
|
|
357
|
+
with the names
|
|
358
|
+
|
|
359
|
+
*/
|
|
360
|
+
var margin = {top: 0, right: 0, bottom: 0, left: 0};
|
|
361
|
+
var axeslabelmargin = {top: 0, right: 0, bottom: 50, left: 0};
|
|
362
|
+
var figwidth = parseInt(figure.style('width')) - margin.left - margin.right;
|
|
363
|
+
// aspectRatio = 1.9,
|
|
364
|
+
// figheight = parseInt(d3.select('#barChart').style('width'))*aspectRatio - margin.top - margin.bottom,
|
|
365
|
+
var width = figwidth-axeslabelmargin.left-axeslabelmargin.right;
|
|
366
|
+
var height = figheight-axeslabelmargin.top-axeslabelmargin.bottom;
|
|
367
|
+
var figcenter = width/2;
|
|
368
|
+
var leftOffsetStatic = axeslabelmargin.left;
|
|
369
|
+
|
|
370
|
+
// do the sorting
|
|
371
|
+
var indices = Array(data.length);
|
|
372
|
+
for (var i = 0; i < data.length; i++) { indices[i] = i; }
|
|
373
|
+
// sort by abs magnitude
|
|
374
|
+
// indices.sort(function(a,b) { return Math.abs(data[a]) < Math.abs(data[b]) ? 1 : Math.abs(data[a]) > Math.abs(data[b]) ? -1 : 0; });
|
|
375
|
+
// sort by magnitude, parity preserving
|
|
376
|
+
indices.sort(function(a,b) { return data[a] < data[b] ? 1 : data[a] > data[b] ? -1 : 0; });
|
|
377
|
+
sortedStates = Array(data.length);
|
|
378
|
+
for (var i = 0; i < data.length; i++) { sortedStates[i] = [i,indices[i],datanames[indices[i]],data[indices[i]]]; }
|
|
379
|
+
// console.log(sortedStates);
|
|
380
|
+
|
|
381
|
+
// remove an old figure if it exists
|
|
382
|
+
figure.select(".canvas").remove();
|
|
383
|
+
|
|
384
|
+
var canvas = figure.append("svg")
|
|
385
|
+
.attr("width",figwidth)
|
|
386
|
+
.attr("height",figheight)
|
|
387
|
+
.attr("class","canvas")
|
|
388
|
+
.attr("id","barchartsvg");
|
|
389
|
+
|
|
390
|
+
// x scale, maps all the data to
|
|
391
|
+
var absDataMax = d3.max([d3.max(data),-d3.min(data)]);
|
|
392
|
+
var x = d3.scaleLinear()
|
|
393
|
+
.domain([-absDataMax,absDataMax])
|
|
394
|
+
.range([5,width-10]);
|
|
395
|
+
|
|
396
|
+
// linear scale function
|
|
397
|
+
var y = d3.scaleLinear()
|
|
398
|
+
.domain([data.length,1])
|
|
399
|
+
.range([height-20, 5]);
|
|
400
|
+
|
|
401
|
+
// // zoom object for the axes
|
|
402
|
+
// var zoom = d3.behavior.zoom()
|
|
403
|
+
// .y(y) // pass linear scale function
|
|
404
|
+
// // .translate([10,10])
|
|
405
|
+
// .scaleExtent([1,1])
|
|
406
|
+
// .on("zoom",zoomed);
|
|
407
|
+
|
|
408
|
+
// create the axes themselves
|
|
409
|
+
var axes = canvas.append("g")
|
|
410
|
+
.attr("transform", "translate(" + (axeslabelmargin.left) + "," +
|
|
411
|
+
(axeslabelmargin.top) + ")")
|
|
412
|
+
.attr("width", width)
|
|
413
|
+
.attr("height", height)
|
|
414
|
+
.attr("class", "main");
|
|
415
|
+
// .call(zoom);
|
|
416
|
+
|
|
417
|
+
// create the axes background
|
|
418
|
+
// var bgrect = axes.append("svg:rect")
|
|
419
|
+
// .attr("width", width)
|
|
420
|
+
// .attr("height", height)
|
|
421
|
+
// .attr("class", "bg")
|
|
422
|
+
// .style({'stroke-width':'2','stroke':'rgb(0,0,0)'})
|
|
423
|
+
// .attr("fill", "#FCFCFC");
|
|
424
|
+
|
|
425
|
+
// create the x axes
|
|
426
|
+
var bgrect = axes.append("svg:line")
|
|
427
|
+
.attr("x1", width)
|
|
428
|
+
.attr("y1", height)
|
|
429
|
+
.attr("x2", axeslabelmargin.left)
|
|
430
|
+
.attr("y2", height)
|
|
431
|
+
//.attr("class", "bg")
|
|
432
|
+
.style('stroke-width','1').style('stroke','rgb(10,10,10)');
|
|
433
|
+
//.attr("fill", "#FCFCFC");
|
|
434
|
+
|
|
435
|
+
// axes creation functions
|
|
436
|
+
var create_xAxis = function() {
|
|
437
|
+
return d3.axisBottom(x)
|
|
438
|
+
.ticks(4); }
|
|
439
|
+
|
|
440
|
+
// // axis creation function
|
|
441
|
+
// var create_yAxis = function() {
|
|
442
|
+
// return d3.svg.axis()
|
|
443
|
+
// .scale(y) //linear scale function
|
|
444
|
+
// .orient("left"); }
|
|
445
|
+
|
|
446
|
+
// // draw the axes
|
|
447
|
+
// var yAxis = create_yAxis()
|
|
448
|
+
// .tickSizeInner(6)
|
|
449
|
+
// .tickSizeOuter(0);
|
|
450
|
+
|
|
451
|
+
// axes.append("g")
|
|
452
|
+
// .attr("class", "y axis ")
|
|
453
|
+
// .attr("font-size", "14.0px")
|
|
454
|
+
// .attr("transform", "translate(0,0)")
|
|
455
|
+
// .call(yAxis);
|
|
456
|
+
|
|
457
|
+
var xAxis;
|
|
458
|
+
if (manualTicks.length > 0) {
|
|
459
|
+
xAxis = create_xAxis()
|
|
460
|
+
.tickSizeInner(6)
|
|
461
|
+
.tickSizeOuter(0)
|
|
462
|
+
.tickValues(manualTicks);
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
xAxis = create_xAxis()
|
|
466
|
+
.tickSizeInner(6)
|
|
467
|
+
.tickSizeOuter(0);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
axes.append("g")
|
|
471
|
+
.attr("class", "x axis ")
|
|
472
|
+
.attr("font-size", "14.0px")
|
|
473
|
+
.attr("transform", "translate(0," + (height) + ")")
|
|
474
|
+
.call(xAxis);
|
|
475
|
+
|
|
476
|
+
d3.selectAll(".tick line").style('stroke','black');
|
|
477
|
+
|
|
478
|
+
// create the clip boundary
|
|
479
|
+
// var clip = axes.append("svg:clipPath")
|
|
480
|
+
// .attr("id","clip")
|
|
481
|
+
// .append("svg:rect")
|
|
482
|
+
// .attr("x",0)
|
|
483
|
+
// .attr("y",0)
|
|
484
|
+
// .attr("width",width)
|
|
485
|
+
// .attr("height",height);
|
|
486
|
+
|
|
487
|
+
// // now something else
|
|
488
|
+
// var unclipped_axes = axes;
|
|
489
|
+
|
|
490
|
+
// axes = axes.append("g")
|
|
491
|
+
// .attr("clip-path","url(#clip)");
|
|
492
|
+
|
|
493
|
+
// var ylabel = canvas.append("text")
|
|
494
|
+
// .text("State Rank")
|
|
495
|
+
// .attr("class","axes-text")
|
|
496
|
+
// .attr("x",(figwidth-width)/4)
|
|
497
|
+
// .attr("y",figheight/2+30)
|
|
498
|
+
// .attr("font-size", "16.0px")
|
|
499
|
+
// .attr("fill", "#000000")
|
|
500
|
+
// .attr("transform", "rotate(-90.0," + (figwidth-width)/4 + "," + (figheight/2+30) + ")");
|
|
501
|
+
|
|
502
|
+
var xlabel = canvas.append("text")
|
|
503
|
+
// .text("Happiness")
|
|
504
|
+
.text(xlabeltext)
|
|
505
|
+
.attr("class","axes-text")
|
|
506
|
+
.attr("x",width/2+(figwidth-width)/2)
|
|
507
|
+
.attr("y",3*(figheight-height)/4+height)
|
|
508
|
+
.attr("font-size", "16.0px")
|
|
509
|
+
.attr("fill", "#000000")
|
|
510
|
+
.attr("style", "text-anchor: middle;");
|
|
511
|
+
|
|
512
|
+
axes.selectAll("rect.staterect")
|
|
513
|
+
.data(sortedStates)
|
|
514
|
+
.enter()
|
|
515
|
+
.append("rect")
|
|
516
|
+
.attr("class", function(d,i) { return d[2]+" staterect"+" q"+classColor(i+1)+"-8"; })
|
|
517
|
+
.attr("x", function(d,i) { if (d[3]>0) { return figcenter; } else { return x(d[3]); } })
|
|
518
|
+
.attr("y", function(d,i) { return y(i+1); })
|
|
519
|
+
.style('opacity','1.0').style('stroke-width','1.0').style('stroke','rgb(100,100,100)')
|
|
520
|
+
.attr("height",function(d,i) { return 11; } )
|
|
521
|
+
.attr("width",function(d,i) { if (d[3]>0) {return d3.max([x(d[3])-figcenter,0]);} else {return d3.max([figcenter-x(d[3]),0]); } } )
|
|
522
|
+
.on('mouseover', function(event,d){
|
|
523
|
+
var rectSelection = d3.select(this).style('opacity','1.0').style('stroke','black').style('stroke-width','1.0');
|
|
524
|
+
hedotools.barchartoncall.test(d,d[0]);
|
|
525
|
+
})
|
|
526
|
+
.on('mouseout', function(event,d){
|
|
527
|
+
var rectSelection = d3.select(this).style('opacity','1.0').style('stroke','rgb(100,100,100)').style('stroke-width','1.0');
|
|
528
|
+
// var rectSelection = d3.select(this).style({opacity:'0.7'});
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
axes.selectAll("text.statetext")
|
|
532
|
+
.data(sortedStates)
|
|
533
|
+
.enter()
|
|
534
|
+
.append("text")
|
|
535
|
+
.attr("class", function(d,i) { return d[2]+" statetext"; })
|
|
536
|
+
.attr("x", function(d,i) { if (d[3]>0) { return figcenter-6; } else { return figcenter+6; } })
|
|
537
|
+
.style("text-anchor", function(d,i) { if (d[3]>0) { return "end";} else { return "start";}})
|
|
538
|
+
.attr("y",function(d,i) { return y(i+1)+11; } )
|
|
539
|
+
.text(function(d,i) { return (i+1)+". "+d[2]; })
|
|
540
|
+
.on('mouseover', function(event,d){
|
|
541
|
+
hedotools.barchartoncall.test(d,d[0]);
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
// d3.select(window).on("resize.shiftplot",resizeshift);
|
|
545
|
+
|
|
546
|
+
// function resizeshift() {
|
|
547
|
+
// figwidth = parseInt(d3.select("#shift01").style('width')) - margin.left - margin.right,
|
|
548
|
+
// width = .775*figwidth
|
|
549
|
+
// figcenter = width/2;
|
|
550
|
+
|
|
551
|
+
// canvas.attr("width",figwidth);
|
|
552
|
+
|
|
553
|
+
// x.range([(sortedWords[0].length+3)*9, width-(sortedWords[0].length+3)*9]);
|
|
554
|
+
// topScale.range([width*.1,width*.9]);
|
|
555
|
+
|
|
556
|
+
// bgrect.attr("width",width);
|
|
557
|
+
// //axes.attr("transform", "translate(" + (0.125 * figwidth) + "," +
|
|
558
|
+
// // ((1 - 0.125 - 0.775) * figheight) + ")");
|
|
559
|
+
|
|
560
|
+
// // mainline.attr("d",line);
|
|
561
|
+
|
|
562
|
+
// // fix the x axis
|
|
563
|
+
// canvas.select(".x.axis").call(xAxis);
|
|
564
|
+
|
|
565
|
+
// clip.attr("width",width);
|
|
566
|
+
|
|
567
|
+
// // get the x label
|
|
568
|
+
// xlabel.attr("x",(leftOffsetStatic+width/2));
|
|
569
|
+
|
|
570
|
+
// // the andy reagan credit
|
|
571
|
+
// credit.attr("x",width-7);
|
|
572
|
+
|
|
573
|
+
// // line separating summary
|
|
574
|
+
// sepline.attr("x2",width);
|
|
575
|
+
|
|
576
|
+
// // all of the lower shift text
|
|
577
|
+
// axes.selectAll("text.shifttext").attr("x",function(d,i) { if (d>0) {return x(d)+2;} else {return x(d)-2; } } );
|
|
578
|
+
// }
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
var opublic = { setfigure: setfigure,
|
|
582
|
+
setdata: setdata,
|
|
583
|
+
_data: _data,
|
|
584
|
+
_manualTicks: _manualTicks,
|
|
585
|
+
_datanames: _datanames,
|
|
586
|
+
_figheight: _figheight,
|
|
587
|
+
_xlabeltext: _xlabeltext,
|
|
588
|
+
getSorted: getSorted,
|
|
589
|
+
plot: plot, };
|
|
590
|
+
|
|
591
|
+
return opublic;
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
// on call as a module
|
|
603
|
+
// in the test function, can set the function that gets called
|
|
604
|
+
// when the lens is moved
|
|
605
|
+
// full flexibility
|
|
606
|
+
hedotools.lensoncall = function() {
|
|
607
|
+
var test = function(extent1) {
|
|
608
|
+
console.log("set on load (works for maps.html)");
|
|
609
|
+
// reset
|
|
610
|
+
for (var j=0; j<allData.length; j++) {
|
|
611
|
+
for (var i=0; i<allData[j].rawFreq.length; i++) {
|
|
612
|
+
var include = true;
|
|
613
|
+
// check if in removed word list
|
|
614
|
+
for (var k=0; k<ignoreWords.length; k++) {
|
|
615
|
+
if (ignoreWords[k] == words[i]) {
|
|
616
|
+
include = false;
|
|
617
|
+
//console.log("ignored "+ignoreWords[k]);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
// check if underneath lens cover
|
|
621
|
+
if (lens[i] >= extent1[0] && lens[i] <= extent1[1]) {
|
|
622
|
+
include = false;
|
|
623
|
+
}
|
|
624
|
+
// include it, or set to 0
|
|
625
|
+
if (include) {
|
|
626
|
+
allData[j].freq[i] = allData[j].rawFreq[i];
|
|
627
|
+
}
|
|
628
|
+
else { allData[j].freq[i] = 0; }
|
|
629
|
+
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
hedotools.computeHapps.go();
|
|
633
|
+
hedotools.map.setfigure(d3.select('#map01')).setdata(geoJson).plot();
|
|
634
|
+
if (shiftRef !== shiftComp) {
|
|
635
|
+
hedotools.shifter.shift(allData[shiftRef].freq,allData[shiftComp].freq,lens,words);
|
|
636
|
+
var happysad = hedotools.shifter._compH() > hedotools.shifter._refH() ? "happier" : "less happy";
|
|
637
|
+
hedotools.shifter.setfigure(d3.select('#shift01')).setText(["Why "+allData[shiftComp].name+" is "+happysad+" than "+allData[shiftRef].name+":"]).plot();
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
var opublic = { test: test, };
|
|
641
|
+
return opublic;
|
|
642
|
+
}();
|
|
643
|
+
|
|
644
|
+
hedotools.lens = function() {
|
|
645
|
+
|
|
646
|
+
// for now, keep track of which page we're in
|
|
647
|
+
// since they're all a bit different
|
|
648
|
+
var page = "geo";
|
|
649
|
+
|
|
650
|
+
var encoder = hedotools.urllib.encoder().varname("lens"); //.varval(lensExtent);
|
|
651
|
+
var decoder = hedotools.urllib.decoder().varname("lens").varresult([4,6]); //.varval(lensExtent);
|
|
652
|
+
|
|
653
|
+
var figure;
|
|
654
|
+
var lens;
|
|
655
|
+
var margin = {top: 0, right: 55, bottom: 0, left: 0};
|
|
656
|
+
var figwidth;
|
|
657
|
+
var figheight = 100 - margin.top - margin.bottom;
|
|
658
|
+
var width;
|
|
659
|
+
var height = .875*figheight-5;
|
|
660
|
+
var leftOffsetStatic;
|
|
661
|
+
|
|
662
|
+
var grabwidth = function() {
|
|
663
|
+
figwidth = parseInt(figure.style('width')) - margin.left - margin.right;
|
|
664
|
+
width = .875*figwidth-5;
|
|
665
|
+
leftOffsetStatic = 0.125*figwidth;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
var setfigure = function(_) {
|
|
669
|
+
console.log("setting figure");
|
|
670
|
+
figure = _;
|
|
671
|
+
grabwidth();
|
|
672
|
+
return hedotools.lens;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
var setdata = function(_) {
|
|
676
|
+
lens = _;
|
|
677
|
+
return hedotools.lens;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
lensExtent = decoder().cached;
|
|
681
|
+
|
|
682
|
+
var plot = function () {
|
|
683
|
+
|
|
684
|
+
if (figwidth > 10) {
|
|
685
|
+
|
|
686
|
+
// remove an old figure if it exists
|
|
687
|
+
figure.selectAll(".canvas").remove();
|
|
688
|
+
|
|
689
|
+
var canvas = figure.append("svg")
|
|
690
|
+
.attr("width",figwidth)
|
|
691
|
+
.attr("height",figheight)
|
|
692
|
+
.attr("id","lenssvg")
|
|
693
|
+
.attr("class","canvas");
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
// create the x and y axis
|
|
697
|
+
var x = d3.scaleLinear()
|
|
698
|
+
.domain([1.00,9.00])
|
|
699
|
+
// .domain(d3.extent(lens))
|
|
700
|
+
.range([0,width]);
|
|
701
|
+
|
|
702
|
+
// use d3.layout http://bl.ocks.org/mbostock/3048450
|
|
703
|
+
var data = d3.bin()
|
|
704
|
+
.domain(x.domain())
|
|
705
|
+
.thresholds(x.ticks(65))
|
|
706
|
+
(lens);
|
|
707
|
+
|
|
708
|
+
// linear scale function
|
|
709
|
+
var y = d3.scaleLinear()
|
|
710
|
+
.domain([0,d3.max(data,function(d) { return d.length; } )])
|
|
711
|
+
.range([height, 0]);
|
|
712
|
+
|
|
713
|
+
// create the axes themselves
|
|
714
|
+
var axes = canvas.append("g")
|
|
715
|
+
.attr("transform", "translate(" + (0.125 * figwidth) + "," +
|
|
716
|
+
((1 - 0.125 - 0.875) * figheight) + ")")
|
|
717
|
+
.attr("width", width)
|
|
718
|
+
.attr("height", height)
|
|
719
|
+
.attr("class", "main");
|
|
720
|
+
|
|
721
|
+
// create the axes background
|
|
722
|
+
var bgrect = axes.append("svg:rect")
|
|
723
|
+
.attr("width", width)
|
|
724
|
+
.attr("height", height)
|
|
725
|
+
.attr("class", "bg")
|
|
726
|
+
.style('stroke-width','2').style('stroke','rgb(0,0,0)')
|
|
727
|
+
.attr("fill", "#FFFFF0");
|
|
728
|
+
|
|
729
|
+
// axes creation functions
|
|
730
|
+
var create_xAxis = function() {
|
|
731
|
+
return d3.axisBottom(x)
|
|
732
|
+
.ticks(9); }
|
|
733
|
+
|
|
734
|
+
// axis creation function
|
|
735
|
+
var create_yAxis = function() {
|
|
736
|
+
return d3.axisLeft(y)
|
|
737
|
+
.ticks(3); }
|
|
738
|
+
|
|
739
|
+
// draw the axes
|
|
740
|
+
var yAxis = create_yAxis()
|
|
741
|
+
.tickSizeInner(6)
|
|
742
|
+
.tickSizeOuter(0);
|
|
743
|
+
|
|
744
|
+
axes.append("g")
|
|
745
|
+
.attr("class", "top")
|
|
746
|
+
.attr("transform", "translate(0,0)")
|
|
747
|
+
.attr("font-size", "12.0px")
|
|
748
|
+
.call(yAxis);
|
|
749
|
+
|
|
750
|
+
var xAxis = create_xAxis()
|
|
751
|
+
.tickSizeInner(6)
|
|
752
|
+
.tickSizeOuter(0);
|
|
753
|
+
|
|
754
|
+
axes.append("g")
|
|
755
|
+
.attr("class", "x axis ")
|
|
756
|
+
.attr("font-size", "12.0px")
|
|
757
|
+
.attr("transform", "translate(0," + (height) + ")")
|
|
758
|
+
.call(xAxis);
|
|
759
|
+
|
|
760
|
+
d3.selectAll(".tick line").style('stroke','black');
|
|
761
|
+
|
|
762
|
+
// create the clip boundary
|
|
763
|
+
var clip = axes.append("svg:clipPath")
|
|
764
|
+
.attr("id","clip")
|
|
765
|
+
.append("svg:rect")
|
|
766
|
+
.attr("x",0)
|
|
767
|
+
.attr("y",80)
|
|
768
|
+
.attr("width",width)
|
|
769
|
+
.attr("height",height-80);
|
|
770
|
+
|
|
771
|
+
var unclipped_axes = axes;
|
|
772
|
+
|
|
773
|
+
//axes = axes.append("g")
|
|
774
|
+
//.attr("clip-path","url(#clip)");
|
|
775
|
+
|
|
776
|
+
canvas.append("text")
|
|
777
|
+
.text("Num Words")
|
|
778
|
+
.attr("class","axes-text")
|
|
779
|
+
.attr("x",(figwidth-width)/4)
|
|
780
|
+
.attr("y",figheight/2+30)
|
|
781
|
+
.attr("font-size", "12.0px")
|
|
782
|
+
.attr("fill", "#000000")
|
|
783
|
+
.attr("transform", "rotate(-90.0," + (figwidth-width)/4 + "," + (figheight/2+30) + ")");
|
|
784
|
+
|
|
785
|
+
// var xlabel = canvas.append("text")
|
|
786
|
+
// .text("Word score")
|
|
787
|
+
// .attr("class","axes-text")
|
|
788
|
+
// .attr("x",width/2+(figwidth-width)/2)
|
|
789
|
+
// .attr("y",figheight)
|
|
790
|
+
// .attr("font-size", "12.0px")
|
|
791
|
+
// .attr("fill", "#000000")
|
|
792
|
+
// .attr("style", "text-anchor: middle;");
|
|
793
|
+
|
|
794
|
+
var lensMean = d3.mean(lens);
|
|
795
|
+
|
|
796
|
+
var bar = axes.selectAll(".distrect")
|
|
797
|
+
.data(data)
|
|
798
|
+
.enter()
|
|
799
|
+
.append("g")
|
|
800
|
+
.attr("class","distrect")
|
|
801
|
+
.attr("fill",function(d,i) { if (d.x0 > lensMean) {return "#D3D3D3";} else { return "#D3D3D3";}})
|
|
802
|
+
.attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; });
|
|
803
|
+
|
|
804
|
+
var mainrect = bar.append("rect")
|
|
805
|
+
.attr("x", 1)
|
|
806
|
+
.attr("width", x(data[0].x1 - data[0].x0 + 1)-2 )
|
|
807
|
+
.attr("height", function(d) { return height - y(d.length); });
|
|
808
|
+
|
|
809
|
+
var line = d3.line()
|
|
810
|
+
.x(function(d,i) { return x(d.x0); })
|
|
811
|
+
.y(function(d) { return y(d.length); })
|
|
812
|
+
.curve(d3.curveLinear);
|
|
813
|
+
|
|
814
|
+
var mainline = axes.append("path")
|
|
815
|
+
.datum(data)
|
|
816
|
+
.attr("class", "line")
|
|
817
|
+
.attr("d", line)
|
|
818
|
+
.attr("stroke","black")
|
|
819
|
+
.attr("stroke-width",3)
|
|
820
|
+
.attr("fill","none");
|
|
821
|
+
|
|
822
|
+
//console.log(x(d3.min(lens)));
|
|
823
|
+
|
|
824
|
+
var brushX = d3.scaleLinear()
|
|
825
|
+
.domain([1,9])
|
|
826
|
+
// .domain(d3.extent(lens))
|
|
827
|
+
.range([figwidth*.125,width+figwidth*.125]);
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
function brushended(event) {
|
|
832
|
+
if (!event.sourceEvent) return;
|
|
833
|
+
if (!event.selection) return;
|
|
834
|
+
// selection is in pixels; invert through brushX to data space
|
|
835
|
+
var extent0 = event.selection.map(brushX.invert),
|
|
836
|
+
extent1 = extent0; // should round it to bins
|
|
837
|
+
|
|
838
|
+
onredrawfunction();
|
|
839
|
+
|
|
840
|
+
// window.stopVals = extent1;
|
|
841
|
+
// console.log(extent1);
|
|
842
|
+
if ((extent1[0] !== lensExtent[0]) || (extent1[1] !== lensExtent[1]))
|
|
843
|
+
{
|
|
844
|
+
|
|
845
|
+
lensExtent = [Math.round(extent1[0]*4)/4,Math.round(extent1[1]*4)/4];
|
|
846
|
+
hedotools.lensoncall.test(extent1);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
d3.select(this).transition()
|
|
850
|
+
.call(brush.move, lensExtent.map(brushX));
|
|
851
|
+
|
|
852
|
+
encoder.varval(lensExtent);
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
var brush = d3.brushX()
|
|
856
|
+
.extent([[brushX.range()[0], 0], [brushX.range()[1], height]])
|
|
857
|
+
.on("end",brushended);
|
|
858
|
+
|
|
859
|
+
var gBrush = canvas.append("g")
|
|
860
|
+
.attr("class","lensbrush")
|
|
861
|
+
.call(brush);
|
|
862
|
+
gBrush.call(brush.move, lensExtent.map(brushX));
|
|
863
|
+
|
|
864
|
+
gBrush.selectAll("rect")
|
|
865
|
+
.attr("height",height)
|
|
866
|
+
.attr("y",0)
|
|
867
|
+
.style('stroke-width','2').style('stroke','rgb(100,100,100)').style('opacity',0.95)
|
|
868
|
+
.attr("fill", "#FCFCFC");
|
|
869
|
+
|
|
870
|
+
//console.log(lensExtent);
|
|
871
|
+
|
|
872
|
+
function resizelens() {
|
|
873
|
+
figwidth = parseInt(d3.select("#lens01").style('width')) - margin.left - margin.right,
|
|
874
|
+
width = .775*figwidth;
|
|
875
|
+
|
|
876
|
+
canvas.attr("width",figwidth);
|
|
877
|
+
|
|
878
|
+
x.range([0,width]);
|
|
879
|
+
bgrect.attr("width",width);
|
|
880
|
+
//axes.attr("transform", "translate(" + (0.125 * figwidth) + "," +
|
|
881
|
+
// ((1 - 0.125 - 0.775) * figheight) + ")");
|
|
882
|
+
|
|
883
|
+
mainline.attr("d",line);
|
|
884
|
+
|
|
885
|
+
//create_xAxis.scale(x);
|
|
886
|
+
//xAxisHandle.call(xAxis);
|
|
887
|
+
canvas.select(".x.axis").call(xAxis);
|
|
888
|
+
|
|
889
|
+
canvas.selectAll(".distrect").attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; });
|
|
890
|
+
|
|
891
|
+
// xlabel.attr("x",(leftOffsetStatic+width/2));
|
|
892
|
+
|
|
893
|
+
d3.selectAll(".tick line").style('stroke','black');
|
|
894
|
+
|
|
895
|
+
// //brushX.range([figwidth*.125,width+figwidth*.125]);
|
|
896
|
+
brushX.range([leftOffsetStatic,leftOffsetStatic+width]);
|
|
897
|
+
brush.extent([[brushX.range()[0], 0], [brushX.range()[1], height]]);
|
|
898
|
+
d3.select(".lensbrush") //.transition()
|
|
899
|
+
.call(brush.move, lensExtent.map(brushX));
|
|
900
|
+
//brushing();
|
|
901
|
+
//brush.event();
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
d3.select(window).on("resize.selectlens",resizelens);
|
|
905
|
+
|
|
906
|
+
// var buttongroup = figure.append("div").attr({"class":"btn-group-vertical",});
|
|
907
|
+
//buttongroup.html('<button type="button" class="btn btn-default">Button</button><button type="button" class="btn btn-default">Button</button><div class="btn-group"><button id="btnGroupVerticalDrop1" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">Dropdown<span class="caret"></span> </button> <ul class="dropdown-menu" role="menu" aria-labelledby="btnGroupVerticalDrop1"> <li><a href="#">Dropdown link</a></li> <li><a href="#">Dropdown link</a></li> </ul></div> <button type="button" class="btn btn-default">Button</button>'
|
|
908
|
+
|
|
909
|
+
figure.selectAll("div.btn-group-vertical").remove();
|
|
910
|
+
var buttongroup = figure.append("div").attr("class","btn-group-vertical pull-right")
|
|
911
|
+
// var defaults = [[4,6],[3,7],[3,9],[1,7],[5,5]];
|
|
912
|
+
var defaults = [[4,6],[3,7],[5,5]];
|
|
913
|
+
// var defaultnames = ["Default","Wide","Sad","Happy","None"];
|
|
914
|
+
var defaultnames = ["Default","Wide","None"];
|
|
915
|
+
buttongroup.selectAll("button").data(defaults).enter()
|
|
916
|
+
.append("button")
|
|
917
|
+
.attr("type","button")
|
|
918
|
+
.attr("class", function(d,i) { return "btn btn-default btn-xs "+defaultnames[i]; })
|
|
919
|
+
.html(function(d,i) { return defaultnames[i]; })
|
|
920
|
+
.on("click",function(event,d) {
|
|
921
|
+
figure.selectAll("button").attr("class","btn btn-default btn-xs");
|
|
922
|
+
d3.select(this).attr("class","btn btn-primary btn-xs");
|
|
923
|
+
d3.select(".lensbrush") //.transition()
|
|
924
|
+
.call(brush.move, d.map(brushX));
|
|
925
|
+
});
|
|
926
|
+
// initially check if any are matched
|
|
927
|
+
console.log(lensExtent);
|
|
928
|
+
for (var i=0; i<defaults.length; i++) {
|
|
929
|
+
if (defaults[i][0] === parseFloat(lensExtent[0]) && defaults[i][1] === parseFloat(lensExtent[1])) {
|
|
930
|
+
// make it active
|
|
931
|
+
buttongroup.select("button."+defaultnames[i]).attr("class","btn btn-primary btn-xs");
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
}; // if figwidth > 10
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
var onredrawfunction = function() {
|
|
939
|
+
console.log("I got called");
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
var opublic = { setfigure: setfigure,
|
|
943
|
+
setdata: setdata,
|
|
944
|
+
plot: plot,
|
|
945
|
+
onredrawfunction: onredrawfunction,
|
|
946
|
+
};
|
|
947
|
+
|
|
948
|
+
return opublic;
|
|
949
|
+
};
|
|
950
|
+
hedotools.map = function() {
|
|
951
|
+
|
|
952
|
+
var figure;
|
|
953
|
+
|
|
954
|
+
var setfigure = function(_) {
|
|
955
|
+
console.log("setting figure");
|
|
956
|
+
figure = _;
|
|
957
|
+
return hedotools.map;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
var classColor = d3.scaleQuantize()
|
|
961
|
+
.range([0,1,2,3,4,5,6])
|
|
962
|
+
.domain([50,1]);
|
|
963
|
+
|
|
964
|
+
var geoJson;
|
|
965
|
+
|
|
966
|
+
var setdata = function(_) {
|
|
967
|
+
geoJson = _;
|
|
968
|
+
return hedotools.map;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
var plot = function() {
|
|
972
|
+
/*
|
|
973
|
+
plot the state map!
|
|
974
|
+
|
|
975
|
+
drawMap(figure,geoJson);
|
|
976
|
+
-figure is a d3 selection
|
|
977
|
+
-geoJson is the loaded us-states file
|
|
978
|
+
-stateHapps is the loaded csv (state,val)
|
|
979
|
+
*/
|
|
980
|
+
|
|
981
|
+
//Width and height
|
|
982
|
+
var w = parseInt(figure.style('width'));
|
|
983
|
+
var h = w*650/900;
|
|
984
|
+
|
|
985
|
+
// remove an old figure if it exists
|
|
986
|
+
figure.select(".canvas").remove();
|
|
987
|
+
|
|
988
|
+
//Create SVG element
|
|
989
|
+
var canvas = figure
|
|
990
|
+
.append("svg")
|
|
991
|
+
.attr("class", "map canvas")
|
|
992
|
+
.attr("id", "mapsvg")
|
|
993
|
+
.attr("width", w)
|
|
994
|
+
.attr("height", h);
|
|
995
|
+
|
|
996
|
+
var selarray = [false,true],
|
|
997
|
+
selstrings = ["Reference","Comparison"],
|
|
998
|
+
selstringslen = selstrings.map(function(d) { return d.width(); }),
|
|
999
|
+
initialpadding = 5,
|
|
1000
|
+
boxpadding = 5,
|
|
1001
|
+
fullselboxwidth = selarray.length*boxpadding*2-boxpadding+initialpadding+d3.sum(selstringslen);
|
|
1002
|
+
|
|
1003
|
+
var legendscale = d3.scaleLinear()
|
|
1004
|
+
.domain([340,730])
|
|
1005
|
+
.range([0,1]);
|
|
1006
|
+
|
|
1007
|
+
function makeSelector() {
|
|
1008
|
+
|
|
1009
|
+
canvas.append("text")
|
|
1010
|
+
.attr("x", (w-70-fullselboxwidth-56))
|
|
1011
|
+
.attr("y", 54)
|
|
1012
|
+
.attr("fill", "grey")
|
|
1013
|
+
.text("Selecting ");
|
|
1014
|
+
|
|
1015
|
+
var selgroup = canvas.append("g")
|
|
1016
|
+
.attr("class", "selgroup")
|
|
1017
|
+
.attr("transform", "translate("+(w-70-fullselboxwidth)+","+40+")");
|
|
1018
|
+
|
|
1019
|
+
selgroup.append("rect")
|
|
1020
|
+
.attr("class", "selbox")
|
|
1021
|
+
.attr("x", 0)
|
|
1022
|
+
.attr("y", 0)
|
|
1023
|
+
.attr("rx", 3)
|
|
1024
|
+
.attr("ry", 3)
|
|
1025
|
+
.attr("width", fullselboxwidth)
|
|
1026
|
+
.attr("height", 19)
|
|
1027
|
+
.attr("fill", "#F8F8F8")
|
|
1028
|
+
.attr('stroke-width', '0.5')
|
|
1029
|
+
.attr('stroke', 'rgb(0,0,0)');
|
|
1030
|
+
|
|
1031
|
+
selgroup.selectAll("rect.colorclick")
|
|
1032
|
+
.data(selarray)
|
|
1033
|
+
.enter()
|
|
1034
|
+
.append("rect")
|
|
1035
|
+
.attr("class", function(d,i) { return "colorclick "+intStr[i]; })
|
|
1036
|
+
.attr("x", function(d,i) { if (i === 0) { return 0; }
|
|
1037
|
+
else { return d3.sum(selstringslen.slice(0,i))+i*boxpadding+(i-1)*boxpadding+initialpadding; } })
|
|
1038
|
+
.attr("y", 0)
|
|
1039
|
+
.attr("rx", 3)
|
|
1040
|
+
.attr("ry", 3)
|
|
1041
|
+
.attr("width", function(d,i) { if (i === 0) { return selstringslen[i]+initialpadding+boxpadding; } else { return selstringslen[i]+boxpadding*2; }})
|
|
1042
|
+
.attr("height", 19)
|
|
1043
|
+
.attr("fill", "#F8F8F8") //http://www.w3schools.com/html/html_colors.asp
|
|
1044
|
+
.attr('stroke-width', '0.5')
|
|
1045
|
+
.attr('stroke', 'rgb(0,0,0)');
|
|
1046
|
+
|
|
1047
|
+
selgroup.selectAll("text")
|
|
1048
|
+
.data(selstrings)
|
|
1049
|
+
.enter()
|
|
1050
|
+
.append("text")
|
|
1051
|
+
.attr("x", function(d,i) {
|
|
1052
|
+
// start at 2
|
|
1053
|
+
if (i==0) { return initialpadding; }
|
|
1054
|
+
// then use 2+width+10+width+10+width...
|
|
1055
|
+
// for default padding of 5 on L/R
|
|
1056
|
+
else { return d3.sum(selstringslen.slice(0,i))+initialpadding+i*boxpadding*2; } })
|
|
1057
|
+
.attr("y", 14)
|
|
1058
|
+
.attr("class", function(d,i) { return "seltext "+intStr[i]; })
|
|
1059
|
+
.text(function(d,i) { return d; });
|
|
1060
|
+
|
|
1061
|
+
selgroup.selectAll("rect.selclick")
|
|
1062
|
+
.data(selarray)
|
|
1063
|
+
.enter()
|
|
1064
|
+
.append("rect")
|
|
1065
|
+
.attr("class", "selrect")
|
|
1066
|
+
.attr("x", function(d,i) { if (i === 0) { return 0; }
|
|
1067
|
+
else { return d3.sum(selstringslen.slice(0,i))+i*boxpadding+(i-1)*boxpadding+initialpadding; } })
|
|
1068
|
+
.attr("y", 0)
|
|
1069
|
+
.attr("width", function(d,i) { if (i === 0) { return selstringslen[i]+initialpadding+boxpadding; } else { return selstringslen[i]+boxpadding*2; }})
|
|
1070
|
+
.attr("height", 19)
|
|
1071
|
+
.attr("fill", "white") //http://www.w3schools.com/html/html_colors.asp
|
|
1072
|
+
.attr("opacity", "0.0")
|
|
1073
|
+
.on("mousedown", function(event,d) {
|
|
1074
|
+
var i = selarray.indexOf(d);
|
|
1075
|
+
if (stateSelType !== d) {
|
|
1076
|
+
stateSelType = d;
|
|
1077
|
+
activeHover = true;
|
|
1078
|
+
d3.selectAll("text.seltext").attr("fill","black")
|
|
1079
|
+
d3.select("text.seltext."+intStr[i]).attr("fill","white")
|
|
1080
|
+
d3.selectAll("rect.colorclick").attr("fill","#F8F8F8").attr("stroke","rgb(0,0,0)")
|
|
1081
|
+
d3.select("rect.colorclick."+intStr[i]).attr("fill","#428bca").attr("stroke","#428bca");
|
|
1082
|
+
d3.select(".selbutton.one").attr("class","btn btn-default btn-xs pull-right selbutton one");
|
|
1083
|
+
d3.select(".selbutton.two").attr("class","btn btn-default btn-xs pull-right selbutton two");
|
|
1084
|
+
d3.select(".selbutton."+intStr[i]).attr("class","btn btn-primary btn-xs pull-right selbutton "+intStr[i]);
|
|
1085
|
+
d3.selectAll(".state").attr("stroke-width",0.7);
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
selgroup.selectAll("line")
|
|
1090
|
+
.data(selstrings.slice(0,selstrings.length-1))
|
|
1091
|
+
.enter()
|
|
1092
|
+
.append("line")
|
|
1093
|
+
.attr("stroke","grey")
|
|
1094
|
+
.attr("stroke-width","2")
|
|
1095
|
+
.attr("x1", function(d,i) {
|
|
1096
|
+
return d3.sum(selstringslen.slice(0,i+1))+i*boxpadding+(i+1)*boxpadding+initialpadding;
|
|
1097
|
+
})
|
|
1098
|
+
.attr("x2", function(d,i) {
|
|
1099
|
+
return d3.sum(selstringslen.slice(0,i+1))+i*boxpadding+(i+1)*boxpadding+initialpadding;
|
|
1100
|
+
})
|
|
1101
|
+
.attr("y1", 0)
|
|
1102
|
+
.attr("y2", 19);
|
|
1103
|
+
|
|
1104
|
+
if (stateSelType) {
|
|
1105
|
+
var i = 1;
|
|
1106
|
+
}
|
|
1107
|
+
else {
|
|
1108
|
+
var i = 0;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
d3.selectAll("text.seltext").attr("fill","black")
|
|
1112
|
+
d3.select("text.seltext."+intStr[i]).attr("fill","white")
|
|
1113
|
+
d3.selectAll("rect.colorclick").attr("fill","#F8F8F8").attr("stroke","rgb(0,0,0)")
|
|
1114
|
+
d3.select("rect.colorclick."+intStr[i]).attr("fill","#428bca").attr("stroke","#428bca");
|
|
1115
|
+
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
function makeLegend(legendwidth,legendheight,textsize) {
|
|
1119
|
+
|
|
1120
|
+
var legendarray = [0,1,2,3,4,5,6],
|
|
1121
|
+
legendstringslen = [legendwidth,legendwidth,legendwidth,legendwidth,legendwidth,legendwidth,legendwidth,],
|
|
1122
|
+
initialpadding = 0,
|
|
1123
|
+
boxpadding = 0.25,
|
|
1124
|
+
fulllegendboxwidth = legendarray.length*boxpadding*2-boxpadding+initialpadding+d3.sum(legendstringslen);
|
|
1125
|
+
|
|
1126
|
+
var legendgroup = canvas.append("g")
|
|
1127
|
+
.attr("class", "legendgroup")
|
|
1128
|
+
.attr("transform", "translate("+(w-50-fulllegendboxwidth)+","+(h-legendheight-legendheight-2)+")");
|
|
1129
|
+
|
|
1130
|
+
legendgroup.selectAll("rect.legendrect")
|
|
1131
|
+
.data(legendarray)
|
|
1132
|
+
.enter()
|
|
1133
|
+
.append("rect")
|
|
1134
|
+
.attr("class", function(d,i) { return "q"+i+"-8"; })
|
|
1135
|
+
.attr("x", function(d,i) { if (i === 0) { return 0; }
|
|
1136
|
+
else { return d3.sum(legendstringslen.slice(0,i))+i*boxpadding+(i-1)*boxpadding+initialpadding; } })
|
|
1137
|
+
.attr("y", 0)
|
|
1138
|
+
// "rx": 3,
|
|
1139
|
+
// "ry": 3,
|
|
1140
|
+
.attr("width", function(d,i) { return legendstringslen[i]; })
|
|
1141
|
+
.attr("height", legendheight)
|
|
1142
|
+
.attr('stroke-width', '1')
|
|
1143
|
+
.attr('stroke', 'rgb(0,0,0)');
|
|
1144
|
+
|
|
1145
|
+
legendgroup.selectAll("text.legendtext")
|
|
1146
|
+
.data(["less happy","happier"])
|
|
1147
|
+
.enter()
|
|
1148
|
+
.append("text")
|
|
1149
|
+
.attr("x", function(d,i) {
|
|
1150
|
+
if (i==0) { return 0; }
|
|
1151
|
+
else { return fulllegendboxwidth-d.width(textsize+"px arial"); } })
|
|
1152
|
+
.attr("y", legendheight+legendheight)
|
|
1153
|
+
.attr("class", function(d,i) { return "legendtext"; })
|
|
1154
|
+
.attr("font-size", textsize+"px")
|
|
1155
|
+
.text(function(d,i) { return d; });
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
var scaleFactor = legendscale(w);
|
|
1159
|
+
|
|
1160
|
+
makeLegend((20+10*scaleFactor),(8+5*scaleFactor),(9+3*scaleFactor));
|
|
1161
|
+
|
|
1162
|
+
//Define map projection
|
|
1163
|
+
var projection = d3.geoAlbersUsa()
|
|
1164
|
+
.translate([w/2, h/2])
|
|
1165
|
+
.scale(w*1.3);
|
|
1166
|
+
//.scale(1000);
|
|
1167
|
+
|
|
1168
|
+
//Define path generator
|
|
1169
|
+
var path = d3.geoPath()
|
|
1170
|
+
.projection(projection);
|
|
1171
|
+
|
|
1172
|
+
var numColors = 20,
|
|
1173
|
+
hueRange = [240,60], // in degrees
|
|
1174
|
+
// see http://hslpicker.com/#ffd900
|
|
1175
|
+
saturation = 1, // full
|
|
1176
|
+
lightness = 0.5; // half
|
|
1177
|
+
var colors = Array(numColors);
|
|
1178
|
+
var colorStrings = Array(numColors);
|
|
1179
|
+
for (i = 0; i<numColors; i++) {
|
|
1180
|
+
colors[i] = hslToRgb((hueRange[0]+(hueRange[1]-hueRange[0])/(numColors-1)*i)/360, saturation, lightness);
|
|
1181
|
+
colorStrings[i] = "rgb(" + colors[i][0] + "," + colors[i][1] + "," + colors[i][2] + ")"
|
|
1182
|
+
}
|
|
1183
|
+
// console.log(colors);
|
|
1184
|
+
// console.log(colorStrings);
|
|
1185
|
+
|
|
1186
|
+
//Define quantize scale to sort data values into buckets of color
|
|
1187
|
+
color = d3.scaleQuantize()
|
|
1188
|
+
//.range(["rgb(237,248,233)","rgb(186,228,179)","rgb(116,196,118)","rgb(49,163,84)","rgb(0,109,44)"]);
|
|
1189
|
+
.range(colorStrings)
|
|
1190
|
+
.domain([
|
|
1191
|
+
d3.min(allData, function(d) { return d.avhapps; }),
|
|
1192
|
+
d3.max(allData, function(d) { return d.avhapps; })
|
|
1193
|
+
]);
|
|
1194
|
+
|
|
1195
|
+
//Colors taken from colorbrewer.js, included in the D3 download
|
|
1196
|
+
|
|
1197
|
+
// do the sorting
|
|
1198
|
+
indices = Array(allData.length-1);
|
|
1199
|
+
for (var i = 0; i < allData.length-1; i++) { indices[i] = i; }
|
|
1200
|
+
indices.sort(function(a,b) { return Math.abs(allData[a].avhapps) < Math.abs(allData[b].avhapps) ? 1 : Math.abs(allData[a].avhapps) > Math.abs(allData[b].avhapps) ? -1 : 0; });
|
|
1201
|
+
sortedStates = Array(allData.length-1);
|
|
1202
|
+
for (var i = 0; i < allData.length-1; i++) { sortedStates[i] = [i,indices[i],allStateNames[indices[i]]]; }
|
|
1203
|
+
// console.log(sortedStates);
|
|
1204
|
+
sortedStateList = Array(allData.length);
|
|
1205
|
+
for (var i = 0; i < allData.length; i++) { sortedStateList[indices[i]] = i+1; }
|
|
1206
|
+
|
|
1207
|
+
stateFeatures = topojson.feature(geoJson,geoJson.objects.states).features;
|
|
1208
|
+
|
|
1209
|
+
//Bind data and create one path per GeoJSON feature
|
|
1210
|
+
var states = canvas.selectAll("path")
|
|
1211
|
+
.data(stateFeatures);
|
|
1212
|
+
|
|
1213
|
+
states.enter()
|
|
1214
|
+
.append("path")
|
|
1215
|
+
.attr("d", function(d,i) { return path(d.geometry); } )
|
|
1216
|
+
.attr("id", function(d,i) { return d.properties.name; } )
|
|
1217
|
+
.attr("class",function(d,i) { return "state map "+d.properties.name[0]+d.properties.name.split(" ")[d.properties.name.split(" ").length-1]+" "+"q"+classColor(sortedStateList[i])+"-8"; } )
|
|
1218
|
+
.on("mousedown",state_clicked)
|
|
1219
|
+
.on("mouseover",state_hover)
|
|
1220
|
+
.on("mouseout",state_unhover);
|
|
1221
|
+
|
|
1222
|
+
states.exit().remove();
|
|
1223
|
+
|
|
1224
|
+
states
|
|
1225
|
+
.attr("stroke","black")
|
|
1226
|
+
.attr("stroke-width",".7");
|
|
1227
|
+
|
|
1228
|
+
function state_clicked(event,d) { var i = stateFeatures.indexOf(d);
|
|
1229
|
+
// next line verifies that the data and json line up
|
|
1230
|
+
// console.log(d.properties.name); console.log(allData[i].name);
|
|
1231
|
+
|
|
1232
|
+
if (activeHover) {
|
|
1233
|
+
// stop hovering
|
|
1234
|
+
activeHover = false;
|
|
1235
|
+
// remove the color
|
|
1236
|
+
d3.selectAll(".state").style("fill",null);
|
|
1237
|
+
if (stateSelType) {
|
|
1238
|
+
// select the comparison
|
|
1239
|
+
d3.selectAll(".state."+allData[i].name[0]+allData[i].name.split(" ")[allData[i].name.split(" ").length-1])
|
|
1240
|
+
.attr("stroke-width",3);
|
|
1241
|
+
}
|
|
1242
|
+
else {
|
|
1243
|
+
// toggle the reference
|
|
1244
|
+
d3.selectAll(".state."+allData[i].name[0]+allData[i].name.split(" ")[allData[i].name.split(" ").length-1])
|
|
1245
|
+
.attr("stroke-width",3);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
else {
|
|
1249
|
+
activeHover = true;
|
|
1250
|
+
d3.selectAll(".state").attr("stroke-width",0.7);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
//.text("Average Happiness h").append("tspan").attr("baseline-shift","sub").text("avg");
|
|
1254
|
+
|
|
1255
|
+
|
|
1256
|
+
|
|
1257
|
+
// if (shiftRef !== i) {
|
|
1258
|
+
// //console.log("reference "+allData[i].name);
|
|
1259
|
+
// shiftRef = i;
|
|
1260
|
+
// d3.selectAll(".state.map").attr("stroke-width",".7");
|
|
1261
|
+
// d3.selectAll(".state.list").attr("stroke","none");
|
|
1262
|
+
// d3.selectAll(".state."+allData[i].name[0]+allData[i].name.split(" ")[allData[i].name.split(" ").length-1])
|
|
1263
|
+
// .attr("stroke-width",3);
|
|
1264
|
+
// }
|
|
1265
|
+
// else {
|
|
1266
|
+
// //console.log("reference everything");
|
|
1267
|
+
// shiftRef = 51;
|
|
1268
|
+
// d3.selectAll(".state.map").attr("stroke-width","0.7");
|
|
1269
|
+
// d3.selectAll(".state.list").attr("stroke","none");
|
|
1270
|
+
// //.attr("stroke-width",3);
|
|
1271
|
+
// }
|
|
1272
|
+
|
|
1273
|
+
// if (shiftRef !== shiftComp) {
|
|
1274
|
+
// shiftObj = shift(allData[shiftRef].freq,allData[shiftComp].freq,lens,words);
|
|
1275
|
+
// plotShift(d3.select('#shift01'),shiftObj.sortedMag.slice(0,200),
|
|
1276
|
+
// shiftObj.sortedType.slice(0,200),
|
|
1277
|
+
// shiftObj.sortedWords.slice(0,200),
|
|
1278
|
+
// shiftObj.sumTypes,
|
|
1279
|
+
// shiftObj.refH,
|
|
1280
|
+
// shiftObj.compH);
|
|
1281
|
+
// }
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
function state_hover(event,d) { var i = stateFeatures.indexOf(d);
|
|
1285
|
+
var bbox = this.getBBox();
|
|
1286
|
+
var x = Math.floor(bbox.x + bbox.width/2.0);
|
|
1287
|
+
var y = Math.floor(bbox.y + bbox.height/2.0);
|
|
1288
|
+
// console.log(x);
|
|
1289
|
+
// console.log(y);
|
|
1290
|
+
|
|
1291
|
+
var wordsstring = "Words Used: "+commaSeparateNumber(d3.sum(allData[i].freq)),// +"/"+commaSeparateNumber(d3.sum(allData[i].rawFreq)),
|
|
1292
|
+
wordsstring2 = "Total Words: "+commaSeparateNumber(d3.sum(allData[i].rawFreq)),
|
|
1293
|
+
USwordsstring = "US Words Used: "+commaSeparateNumber(d3.sum(allData[51].freq)),// +"/"+commaSeparateNumber(d3.sum(allData[i].rawFreq)),
|
|
1294
|
+
USwordsstring2 = "US Total Words: "+commaSeparateNumber(d3.sum(allData[51].rawFreq)),
|
|
1295
|
+
happsstring = "Average Happiness: "+allData[i].avhapps.toFixed(2)
|
|
1296
|
+
//hoverboxheight = 115,
|
|
1297
|
+
hoverboxheight = 125+51,
|
|
1298
|
+
hoverboxwidth = d3.max([wordsstring.width('13px arial'),happsstring.width('15px arial'),wordsstring2.width('13px arial'),USwordsstring.width('13px arial'),USwordsstring2.width('13px arial')])+20,
|
|
1299
|
+
hoverboxxoffset = 60;
|
|
1300
|
+
var hoverboxyoffset = 30;
|
|
1301
|
+
|
|
1302
|
+
// if it would wrap it over, move it to the left side
|
|
1303
|
+
if ((x+hoverboxwidth+hoverboxxoffset)>w) {
|
|
1304
|
+
hoverboxxoffset = -hoverboxxoffset-hoverboxwidth;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
// if it would wrap it over, move it to the left side
|
|
1308
|
+
if ((y-hoverboxheight/2-hoverboxyoffset)<0) {
|
|
1309
|
+
hoverboxyoffset = -30;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
var hovergroup = canvas.append("g")
|
|
1313
|
+
.attr("class", "hoverinfogroup")
|
|
1314
|
+
.attr("transform", "translate("+(x+hoverboxxoffset)+","+(y-hoverboxheight/2-hoverboxyoffset)+")");
|
|
1315
|
+
|
|
1316
|
+
var hoverbox = hovergroup.append("rect")
|
|
1317
|
+
.attr("class", "hoverinfobox")
|
|
1318
|
+
.attr("x", 0)
|
|
1319
|
+
.attr("y", 0)
|
|
1320
|
+
.attr("width", hoverboxwidth)
|
|
1321
|
+
.attr("height", hoverboxheight)
|
|
1322
|
+
.attr("fill", "white")
|
|
1323
|
+
.attr("stroke", "black");
|
|
1324
|
+
|
|
1325
|
+
hovergroup.append("text")
|
|
1326
|
+
.attr("class", "hoverinfotext")
|
|
1327
|
+
.attr("x", 10)
|
|
1328
|
+
.attr("y", 15)
|
|
1329
|
+
.attr("font-size", 15)
|
|
1330
|
+
.text(allData[i].name);
|
|
1331
|
+
|
|
1332
|
+
hovergroup.append("line")
|
|
1333
|
+
.attr("class", "hoverinfotext")
|
|
1334
|
+
.attr("x", 10)
|
|
1335
|
+
.attr("y", 15)
|
|
1336
|
+
.attr("font-size", 15)
|
|
1337
|
+
.text(allData[i].name);
|
|
1338
|
+
|
|
1339
|
+
hovergroup.append("text")
|
|
1340
|
+
.attr("class", "hoverinfotext")
|
|
1341
|
+
.attr("x", 10)
|
|
1342
|
+
//"y": 55,
|
|
1343
|
+
.attr("y", 38)
|
|
1344
|
+
.attr("font-size", 17)
|
|
1345
|
+
.text("Rank:"); // +"/51");
|
|
1346
|
+
|
|
1347
|
+
hovergroup.append("text")
|
|
1348
|
+
.attr("class", "hoverinfotext")
|
|
1349
|
+
.attr("x", 59)
|
|
1350
|
+
.attr("y", 55)
|
|
1351
|
+
.attr("font-size", 40)
|
|
1352
|
+
.text(sortedStateList[i]); // +"/51");
|
|
1353
|
+
|
|
1354
|
+
hovergroup.append("text")
|
|
1355
|
+
.attr("class", "hoverinfotext")
|
|
1356
|
+
.attr("x", 105)
|
|
1357
|
+
.attr("y", 56)
|
|
1358
|
+
.attr("font-size", 20)
|
|
1359
|
+
.text("out of 51");
|
|
1360
|
+
|
|
1361
|
+
hovergroup.append("text")
|
|
1362
|
+
.attr("class", "hoverinfotext")
|
|
1363
|
+
.attr("x", 10)
|
|
1364
|
+
//"y": 73,
|
|
1365
|
+
.attr("y", 79)
|
|
1366
|
+
.attr("font-size", 15)
|
|
1367
|
+
.text(happsstring);
|
|
1368
|
+
|
|
1369
|
+
hovergroup.append("text")
|
|
1370
|
+
.attr("class", "hoverinfotext")
|
|
1371
|
+
.attr("x", 10)
|
|
1372
|
+
//"y": 89,
|
|
1373
|
+
.attr("y", 97)
|
|
1374
|
+
.attr("font-size", 13)
|
|
1375
|
+
.text(wordsstring);
|
|
1376
|
+
|
|
1377
|
+
hovergroup.append("text")
|
|
1378
|
+
.attr("class", "hoverinfotext")
|
|
1379
|
+
.attr("x", 10)
|
|
1380
|
+
//"y": 106,
|
|
1381
|
+
.attr("y", 114)
|
|
1382
|
+
.attr("font-size", 13)
|
|
1383
|
+
.text(wordsstring2);
|
|
1384
|
+
|
|
1385
|
+
hovergroup.append("text")
|
|
1386
|
+
.attr("class", "hoverinfotext")
|
|
1387
|
+
.attr("x", 10)
|
|
1388
|
+
//"y": 106,
|
|
1389
|
+
.attr("y", 131)
|
|
1390
|
+
.attr("font-size", 13)
|
|
1391
|
+
.text("US Average Happiness: "+allData[51].avhapps.toFixed(2));
|
|
1392
|
+
|
|
1393
|
+
hovergroup.append("text")
|
|
1394
|
+
.attr("class", "hoverinfotext")
|
|
1395
|
+
.attr("x", 10)
|
|
1396
|
+
//"y": 89,
|
|
1397
|
+
.attr("y", 97+51)
|
|
1398
|
+
.attr("font-size", 13)
|
|
1399
|
+
.text(USwordsstring);
|
|
1400
|
+
|
|
1401
|
+
hovergroup.append("text")
|
|
1402
|
+
.attr("class", "hoverinfotext")
|
|
1403
|
+
.attr("x", 10)
|
|
1404
|
+
//"y": 106,
|
|
1405
|
+
.attr("y", 114+51)
|
|
1406
|
+
.attr("font-size", 13)
|
|
1407
|
+
.text(USwordsstring2);
|
|
1408
|
+
|
|
1409
|
+
if (activeHover) {
|
|
1410
|
+
if (stateSelType) {
|
|
1411
|
+
shiftComp = i;
|
|
1412
|
+
d3.select(".complabel").text(allData[i].name);
|
|
1413
|
+
compencoder.varval(allData[i].name);
|
|
1414
|
+
}
|
|
1415
|
+
else {
|
|
1416
|
+
shiftRef = i;
|
|
1417
|
+
d3.select(".reflabel").text(allData[i].name);
|
|
1418
|
+
refencoder.varval(allData[i].name);
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
// next line verifies that the data and json line up
|
|
1422
|
+
// console.log(d.properties.name); console.log(allData[i].name.split(" ")[allData[i].name.split(" ").length-1]);
|
|
1423
|
+
d3.selectAll(".state."+allData[i].name[0]+allData[i].name.split(" ")[allData[i].name.split(" ").length-1]).style("fill","#428bca");
|
|
1424
|
+
|
|
1425
|
+
if (shiftRef !== shiftComp) {
|
|
1426
|
+
hedotools.shifter.shift(allData[shiftRef].freq,allData[shiftComp].freq,lens,words);
|
|
1427
|
+
var happysad = hedotools.shifter._compH() > hedotools.shifter._refH() ? "happier" : "less happy";
|
|
1428
|
+
hedotools.shifter.setfigure(d3.select('#shift01')).setText(["Why "+allData[shiftComp].name+" is "+happysad+" than "+allData[shiftRef].name+":"]).plot();
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
function state_unhover(event,d) {
|
|
1434
|
+
|
|
1435
|
+
d3.select(".hoverinfogroup").remove();
|
|
1436
|
+
|
|
1437
|
+
if (activeHover) {
|
|
1438
|
+
// next line verifies that the data and json line up
|
|
1439
|
+
// console.log(d.properties.name); console.log(allData[i].name.split(" ")[allData[i].name.split(" ").length-1]);
|
|
1440
|
+
// shiftComp = i;
|
|
1441
|
+
//console.log(".state.list."+allData[i].name[0]+allData[i].name.split(" ")[allData[i].name.split(" ").length-1]);
|
|
1442
|
+
//d3.selectAll(".state.list."+allData[i].name[0]+allData[i].name.split(" ")[allData[i].name.split(" ").length-1])
|
|
1443
|
+
//.style("fill",null);
|
|
1444
|
+
d3.select(this)
|
|
1445
|
+
.style("fill",null);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
function resizemap() {
|
|
1450
|
+
w = parseInt(figure.style('width'));
|
|
1451
|
+
h = w*650/900;
|
|
1452
|
+
projection.translate([w/2, h/2]).scale(w*1.3);
|
|
1453
|
+
canvas.selectAll("path").attr("d",path);
|
|
1454
|
+
canvas.attr("width",w).attr("height",h);
|
|
1455
|
+
};
|
|
1456
|
+
|
|
1457
|
+
d3.select(window).on("resize.map",resizemap);
|
|
1458
|
+
|
|
1459
|
+
};
|
|
1460
|
+
|
|
1461
|
+
|
|
1462
|
+
/*
|
|
1463
|
+
* Converts an HSL color value to RGB. Conversion formula
|
|
1464
|
+
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
|
1465
|
+
* Assumes h, s, and l are contained in the set [0, 1] and
|
|
1466
|
+
* returns r, g, and b in the set [0, 255].
|
|
1467
|
+
*
|
|
1468
|
+
* @param Number h The hue
|
|
1469
|
+
* @param Number s The saturation
|
|
1470
|
+
* @param Number l The lightness
|
|
1471
|
+
* @return Array The RGB representation
|
|
1472
|
+
*/
|
|
1473
|
+
function hslToRgb(h, s, l){
|
|
1474
|
+
var r, g, b;
|
|
1475
|
+
|
|
1476
|
+
if(s == 0){
|
|
1477
|
+
r = g = b = l; // achromatic
|
|
1478
|
+
}else{
|
|
1479
|
+
function hue2rgb(p, q, t){
|
|
1480
|
+
if(t < 0) t += 1;
|
|
1481
|
+
if(t > 1) t -= 1;
|
|
1482
|
+
if(t < 1/6) return p + (q - p) * 6 * t;
|
|
1483
|
+
if(t < 1/2) return q;
|
|
1484
|
+
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
|
1485
|
+
return p;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
1489
|
+
var p = 2 * l - q;
|
|
1490
|
+
r = hue2rgb(p, q, h + 1/3);
|
|
1491
|
+
g = hue2rgb(p, q, h);
|
|
1492
|
+
b = hue2rgb(p, q, h - 1/3);
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
var opublic = { setfigure: setfigure,
|
|
1499
|
+
setdata: setdata,
|
|
1500
|
+
plot: plot, };
|
|
1501
|
+
|
|
1502
|
+
return opublic;
|
|
1503
|
+
|
|
1504
|
+
};
|
|
1505
|
+
|
|
1506
|
+
|
|
1507
|
+
|
|
1508
|
+
|
|
1509
|
+
|
|
1510
|
+
|
|
1511
|
+
hedotools.sankeyoncall = function() {
|
|
1512
|
+
var test = function(i,data) {
|
|
1513
|
+
console.log("set in module");
|
|
1514
|
+
|
|
1515
|
+
console.log(allDataOld);
|
|
1516
|
+
|
|
1517
|
+
hedotools.shifter.shift(allDataOld[data[i].index].freq,allData[data[i].index].freq,lens,words);
|
|
1518
|
+
var happysad = hedotools.shifter._compH() > hedotools.shifter._refH() ? "happier" : "less happy";
|
|
1519
|
+
hedotools.shifter.setfigure(d3.select('#shift01')).setText(["Why "+data[i].name+" has become "+happysad+":"]).plot();
|
|
1520
|
+
|
|
1521
|
+
|
|
1522
|
+
}
|
|
1523
|
+
var opublic = { test: test, };
|
|
1524
|
+
return opublic;
|
|
1525
|
+
}();
|
|
1526
|
+
|
|
1527
|
+
hedotools.sankey = function() {
|
|
1528
|
+
|
|
1529
|
+
var popuptimer;
|
|
1530
|
+
|
|
1531
|
+
var figure;
|
|
1532
|
+
|
|
1533
|
+
var setfigure = function(_) {
|
|
1534
|
+
console.log("setting figure");
|
|
1535
|
+
figure = _;
|
|
1536
|
+
// grabwidth();
|
|
1537
|
+
return hedotools.sankey;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
var oldlist;
|
|
1541
|
+
var newlist;
|
|
1542
|
+
var stateNames;
|
|
1543
|
+
|
|
1544
|
+
var oldindices;
|
|
1545
|
+
var newindices;
|
|
1546
|
+
var data;
|
|
1547
|
+
|
|
1548
|
+
var setdata = function(a,b,c) {
|
|
1549
|
+
oldlist = a;
|
|
1550
|
+
newlist = b;
|
|
1551
|
+
stateNames = c;
|
|
1552
|
+
if ( stateNames[50] === "District of Columbia" ) {
|
|
1553
|
+
stateNames[50] = "DC";
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
// do the sorting
|
|
1557
|
+
oldindices = Array(oldlist.length);
|
|
1558
|
+
for (var i = 0; i < oldlist.length; i++) { oldindices[i] = i; }
|
|
1559
|
+
|
|
1560
|
+
// sort by abs magnitude
|
|
1561
|
+
// oldindices.sort(function(a,b) { return Math.abs(data[a]) < Math.abs(data[b]) ? 1 : Math.abs(data[a]) > Math.abs(data[b]) ? -1 : 0; });
|
|
1562
|
+
|
|
1563
|
+
// sort by magnitude, parity preserving
|
|
1564
|
+
oldindices.sort(function(a,b) { return oldlist[a] < oldlist[b] ? 1 : oldlist[a] > oldlist[b] ? -1 : 0; });
|
|
1565
|
+
|
|
1566
|
+
// do the sorting on new data
|
|
1567
|
+
newindices = Array(newlist.length);
|
|
1568
|
+
for (var i = 0; i < newlist.length; i++) { newindices[i] = i; }
|
|
1569
|
+
|
|
1570
|
+
newindices.sort(function(a,b) { return newlist[a] < newlist[b] ? 1 : newlist[a] > newlist[b] ? -1 : 0; });
|
|
1571
|
+
|
|
1572
|
+
data = Array(oldlist.length);
|
|
1573
|
+
for (var i=0; i<data.length; i++) {
|
|
1574
|
+
data[i] = {
|
|
1575
|
+
"name": stateNames[i],
|
|
1576
|
+
"index": i,
|
|
1577
|
+
"oldindex": oldindices.indexOf(i),
|
|
1578
|
+
"newindex": newindices.indexOf(i),
|
|
1579
|
+
"change": newlist[i]-oldlist[i],
|
|
1580
|
+
"oldhapps": oldlist[i],
|
|
1581
|
+
"newhapps": newlist[i],
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// console.log(data);
|
|
1586
|
+
// tmpglob = data;
|
|
1587
|
+
|
|
1588
|
+
return hedotools.sankey;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
// initialize everything so other function in this module have access
|
|
1592
|
+
var margin;
|
|
1593
|
+
var axeslabelmargin;
|
|
1594
|
+
var figwidth;
|
|
1595
|
+
var aspectRatio;
|
|
1596
|
+
var figheight;
|
|
1597
|
+
var width;
|
|
1598
|
+
var height;
|
|
1599
|
+
var figcenter;
|
|
1600
|
+
var leftOffsetStatic;
|
|
1601
|
+
|
|
1602
|
+
var canvas;
|
|
1603
|
+
var x;
|
|
1604
|
+
var y;
|
|
1605
|
+
var axes;
|
|
1606
|
+
|
|
1607
|
+
var oldstateselection;
|
|
1608
|
+
var newstateselction;
|
|
1609
|
+
var path;
|
|
1610
|
+
var sankeydata;
|
|
1611
|
+
var pathwidth;
|
|
1612
|
+
var pathselection;
|
|
1613
|
+
|
|
1614
|
+
var listlabels;
|
|
1615
|
+
var extraSideWidth = [0,0];
|
|
1616
|
+
|
|
1617
|
+
var useTip = false;
|
|
1618
|
+
var tip;
|
|
1619
|
+
|
|
1620
|
+
var minwidth = 450;
|
|
1621
|
+
|
|
1622
|
+
// make the plot
|
|
1623
|
+
var plot = function() {
|
|
1624
|
+
margin = {top: 0, right: 0, bottom: 0, left: 0};
|
|
1625
|
+
axeslabelmargin = {top: 0, right: 90+extraSideWidth[0], bottom: 0, left: 90+extraSideWidth[1]};
|
|
1626
|
+
figwidth = parseInt(figure.style('width')) - margin.left - margin.right;
|
|
1627
|
+
if (figwidth<minwidth) {
|
|
1628
|
+
console.log("width is too small...");
|
|
1629
|
+
d3.selectAll(".reftimelabel,.comptimelabel,.reftimelabelbottom,.comptimelabelbottom").remove();
|
|
1630
|
+
figure.append("text").text("Unfortunately, this visualization will look terrible on your device. If you're on a phone, try rotating and refreshing, or looking from a desktop. Thanks :)");
|
|
1631
|
+
return hedotools.sankey;
|
|
1632
|
+
}
|
|
1633
|
+
aspectRatio = 1.8+3.4*(oldlist.length-51)/(304-51);
|
|
1634
|
+
figheight = parseInt(figure.style('width'))*aspectRatio - margin.top - margin.bottom;
|
|
1635
|
+
// console.log("figheight is "+figheight);
|
|
1636
|
+
// figheight = 4576; // for the city sankey this seems good
|
|
1637
|
+
width = figwidth-axeslabelmargin.left-axeslabelmargin.right;
|
|
1638
|
+
height = figheight-axeslabelmargin.top-axeslabelmargin.bottom;
|
|
1639
|
+
figcenter = width/2;
|
|
1640
|
+
leftOffsetStatic = axeslabelmargin.left;
|
|
1641
|
+
|
|
1642
|
+
var hovergroup = figure.append("div").attr("class", "hoverinfogroup")
|
|
1643
|
+
.style("position", "absolute")
|
|
1644
|
+
.style("top", "100px")
|
|
1645
|
+
.style("left", "100px")
|
|
1646
|
+
.style("visibility", "hidden");
|
|
1647
|
+
|
|
1648
|
+
function hidehover() {
|
|
1649
|
+
console.log("hiding hover");
|
|
1650
|
+
d3.selectAll("path").transition().duration(500).style("opacity","1.0");
|
|
1651
|
+
if (useTip) {
|
|
1652
|
+
hovergroup.style("visibility", "hidden");
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
// remove an old figure if it exists
|
|
1657
|
+
figure.select(".canvas").remove();
|
|
1658
|
+
|
|
1659
|
+
canvas = figure.append("svg")
|
|
1660
|
+
.attr("width",figwidth)
|
|
1661
|
+
.attr("height",figheight)
|
|
1662
|
+
.attr("id","sankeysvg")
|
|
1663
|
+
.attr("class","canvas")
|
|
1664
|
+
|
|
1665
|
+
// x scale, maps all the data to
|
|
1666
|
+
x = d3.scaleLinear()
|
|
1667
|
+
.domain([0,1])
|
|
1668
|
+
.range([5,width-10]);
|
|
1669
|
+
|
|
1670
|
+
// linear scale function
|
|
1671
|
+
y = d3.scaleLinear()
|
|
1672
|
+
.domain([newlist.length,1])
|
|
1673
|
+
.range([height-20, 5]);
|
|
1674
|
+
|
|
1675
|
+
// create the axes themselves
|
|
1676
|
+
axes = canvas.append("g")
|
|
1677
|
+
.attr("transform", "translate(" + (axeslabelmargin.left) + "," +
|
|
1678
|
+
(axeslabelmargin.top) + ")")
|
|
1679
|
+
.attr("width", width)
|
|
1680
|
+
.attr("height", height)
|
|
1681
|
+
.attr("class", "main");
|
|
1682
|
+
|
|
1683
|
+
// if (useTip) {
|
|
1684
|
+
// console.log("setting tip");
|
|
1685
|
+
// tip = d3.tip().attr('class', 'd3-tip').html(function(d) { return d; });
|
|
1686
|
+
// axes.call(tip);
|
|
1687
|
+
// }
|
|
1688
|
+
|
|
1689
|
+
oldstateselection = axes.selectAll("text.statetext.old")
|
|
1690
|
+
.data(data)
|
|
1691
|
+
.enter()
|
|
1692
|
+
.append("text")
|
|
1693
|
+
.attr("class", function(d,i) { return d.name+" statetext"; })
|
|
1694
|
+
.attr("x",20)
|
|
1695
|
+
.style("text-anchor", "end")
|
|
1696
|
+
.attr("y",function(d,i) { return y(d.oldindex+1)+11; } )
|
|
1697
|
+
.text(function(d,i) { return (d.oldindex+1)+". "+d.name; });
|
|
1698
|
+
|
|
1699
|
+
newstateselection = axes.selectAll("text.statetext.new")
|
|
1700
|
+
.data(data)
|
|
1701
|
+
.enter()
|
|
1702
|
+
.append("text")
|
|
1703
|
+
.attr("class", function(d,i) { return d.name+" statetext"; })
|
|
1704
|
+
.attr("x",width-20)
|
|
1705
|
+
.style("text-anchor", "start")
|
|
1706
|
+
.attr("y",function(d,i) { return y(d.newindex+1)+11; } )
|
|
1707
|
+
.text(function(d,i) { return (d.newindex+1)+". "+d.name; });
|
|
1708
|
+
|
|
1709
|
+
// create an instance of the sankey to make paths
|
|
1710
|
+
// horizontal sankey-style link path: this is the v3 d3.sankey().link()
|
|
1711
|
+
// generator inlined. Nodes are positioned manually above, so we only need
|
|
1712
|
+
// the path shape (a horizontal cubic Bezier), not the sankey layout.
|
|
1713
|
+
path = function(d) {
|
|
1714
|
+
var x0 = d.source.x + d.source.dx,
|
|
1715
|
+
x1 = d.target.x,
|
|
1716
|
+
xi = d3.interpolateNumber(x0, x1),
|
|
1717
|
+
x2 = xi(0.5),
|
|
1718
|
+
x3 = xi(0.5),
|
|
1719
|
+
y0 = d.source.y + d.sy + d.dy / 2,
|
|
1720
|
+
y1 = d.target.y + d.ty + d.dy / 2;
|
|
1721
|
+
return "M" + x0 + "," + y0
|
|
1722
|
+
+ "C" + x2 + "," + y0
|
|
1723
|
+
+ " " + x3 + "," + y1
|
|
1724
|
+
+ " " + x1 + "," + y1;
|
|
1725
|
+
};
|
|
1726
|
+
|
|
1727
|
+
// create the sankey data thingy
|
|
1728
|
+
sankeydata = Array(oldlist.length);
|
|
1729
|
+
for (var i=0; i<data.length; i++) {
|
|
1730
|
+
sankeydata[i] = {
|
|
1731
|
+
"source": {
|
|
1732
|
+
"x": 20,
|
|
1733
|
+
"dx": 2,
|
|
1734
|
+
"y": y(data[i].oldindex+1)-8,
|
|
1735
|
+
},
|
|
1736
|
+
"target": {
|
|
1737
|
+
"x": width-22,
|
|
1738
|
+
"dx": 2,
|
|
1739
|
+
"y": y(data[i].newindex+1)-8,
|
|
1740
|
+
},
|
|
1741
|
+
"name": data[i].name,
|
|
1742
|
+
"oldhapps": data[i].oldhapps,
|
|
1743
|
+
"newhapps": data[i].newhapps,
|
|
1744
|
+
"oldindex": data[i].oldindex,
|
|
1745
|
+
"newindex": data[i].newindex,
|
|
1746
|
+
"sy": 10,
|
|
1747
|
+
"ty": 10,
|
|
1748
|
+
"dy": 10,
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
pathwidth = d3.scaleLinear()
|
|
1753
|
+
.domain(d3.extent(data.map(function(d) { return Math.abs(d.change); })))
|
|
1754
|
+
.range([2,13]);
|
|
1755
|
+
|
|
1756
|
+
pathselection = axes.selectAll("path.sankey").data(sankeydata)
|
|
1757
|
+
.enter()
|
|
1758
|
+
.append("path")
|
|
1759
|
+
.attr("d", path)
|
|
1760
|
+
.attr("fill", "none")
|
|
1761
|
+
.attr("class", function(d,i) { return "r"+classColor(data[i].oldindex)+"-8"; })
|
|
1762
|
+
.attr("stroke-width", function(d,i) { return pathwidth(Math.abs(data[i].change)); })
|
|
1763
|
+
.on("mouseover", function(event,d) { var i = sankeydata.indexOf(d);
|
|
1764
|
+
// console.log(i);
|
|
1765
|
+
// console.log(data[i]);
|
|
1766
|
+
// var rectSelection = d3.select(this)
|
|
1767
|
+
// .style({'opacity':'0.7',
|
|
1768
|
+
// // 'stroke-width':'1.0',
|
|
1769
|
+
// });
|
|
1770
|
+
|
|
1771
|
+
var thispath = this;
|
|
1772
|
+
|
|
1773
|
+
hedotools.sankeyoncall.test(i,data);
|
|
1774
|
+
|
|
1775
|
+
d3.selectAll("path").transition().duration(750).style("opacity","0.1");
|
|
1776
|
+
d3.select(this).transition().duration(5).style("opacity","1.0");
|
|
1777
|
+
|
|
1778
|
+
if (useTip) {
|
|
1779
|
+
|
|
1780
|
+
// var bbox = this.getBBox();
|
|
1781
|
+
// var x = Math.floor(bbox.x + bbox.width/2.0);
|
|
1782
|
+
// var y = Math.floor(bbox.y + bbox.height/2.0);
|
|
1783
|
+
|
|
1784
|
+
var hoverboxheight = 90;
|
|
1785
|
+
var hoverboxwidth = 200;
|
|
1786
|
+
var hoverboxyoffset = 0;
|
|
1787
|
+
var hoverboxxoffset = 0;
|
|
1788
|
+
|
|
1789
|
+
var x = d3.pointer(event, thispath)[0];
|
|
1790
|
+
var y = d3.pointer(event, thispath)[1];
|
|
1791
|
+
|
|
1792
|
+
var hoverboxheightguess = 190;
|
|
1793
|
+
if (refcity.length > 0) {
|
|
1794
|
+
hoverboxheightguess = 270;
|
|
1795
|
+
}
|
|
1796
|
+
if ((y+hoverboxheightguess)>height) { y-=(y+hoverboxheightguess-height); }
|
|
1797
|
+
|
|
1798
|
+
// tip.show;
|
|
1799
|
+
// console.log(d);
|
|
1800
|
+
|
|
1801
|
+
hovergroup.style("position", "absolute")
|
|
1802
|
+
.style("top", y+"px")
|
|
1803
|
+
.style("left", x+"px")
|
|
1804
|
+
.style("visibility", "visible");
|
|
1805
|
+
|
|
1806
|
+
hovergroup.selectAll("p,h3,button,br").remove();
|
|
1807
|
+
|
|
1808
|
+
hovergroup.append("h3")
|
|
1809
|
+
.attr("class","cityname")
|
|
1810
|
+
.text(d.name);
|
|
1811
|
+
|
|
1812
|
+
hovergroup.append("p")
|
|
1813
|
+
.attr("class","refhapps")
|
|
1814
|
+
.text(reftimeseldecoder().cached+" Happiness: "+parseFloat(d.oldhapps).toFixed(2));
|
|
1815
|
+
|
|
1816
|
+
hovergroup.append("p")
|
|
1817
|
+
.attr("class","refrank")
|
|
1818
|
+
.text(reftimeseldecoder().cached+" Rank: "+(d.oldindex+1));
|
|
1819
|
+
|
|
1820
|
+
hovergroup.append("p")
|
|
1821
|
+
.attr("class","comphapps")
|
|
1822
|
+
.text(comptimeseldecoder().cached+" Happiness: "+parseFloat(d.newhapps).toFixed(2));
|
|
1823
|
+
|
|
1824
|
+
hovergroup.append("p")
|
|
1825
|
+
.attr("class","comprank")
|
|
1826
|
+
.text(comptimeseldecoder().cached+" Rank: "+(d.newindex+1));
|
|
1827
|
+
|
|
1828
|
+
var popupshift = function(refyear,refname,compyear,compname) {
|
|
1829
|
+
refshifttimeencoder.varval(refyear);
|
|
1830
|
+
refshiftcityencoder.varval(refname);
|
|
1831
|
+
compshifttimeencoder.varval(compyear);
|
|
1832
|
+
compshiftcityencoder.varval(compname);
|
|
1833
|
+
// write a function to call on the load
|
|
1834
|
+
drawShift = function() {
|
|
1835
|
+
hedotools.shifter._refF(refF);
|
|
1836
|
+
hedotools.shifter._compF(compF);
|
|
1837
|
+
hedotools.shifter.stop();
|
|
1838
|
+
hedotools.shifter.shifter();
|
|
1839
|
+
hedotools.shifter.setText(["Why "+compname+" in "+compyear+" is "+( ( hedotools.shifter._compH() > hedotools.shifter._refH() ) ? "happier" : "less happy" )+" than "+refname+" in "+refyear+":"]).plot();
|
|
1840
|
+
$('#myModal').modal('show');
|
|
1841
|
+
}
|
|
1842
|
+
// load both of the files
|
|
1843
|
+
var csvLoadsRemaining = 2;
|
|
1844
|
+
// var reffile = "http://hedonometer.org/data/cities/word-vectors/"+reftimeseldecoder().cached+"/"+d.name+".csv";
|
|
1845
|
+
// if (parseInt(reftimeseldecoder().cached) < 2014) reffile+=".new"
|
|
1846
|
+
// var compfile = "http://hedonometer.org/data/cities/word-vectors/"+comptimeseldecoder().cached+"/"+d.name+".csv";
|
|
1847
|
+
// if (parseInt(comptimeseldecoder().cached) < 2014) compfile+=".new"
|
|
1848
|
+
var reffile = "http://hedonometer.org/data/cities/word-vectors/"+refyear+"/"+refname+".csv";
|
|
1849
|
+
if (parseInt(refyear) < 2014) reffile+=".new"
|
|
1850
|
+
var compfile = "http://hedonometer.org/data/cities/word-vectors/"+compyear+"/"+compname+".csv";
|
|
1851
|
+
if (parseInt(compyear) < 2014) compfile+=".new"
|
|
1852
|
+
console.log(reffile);
|
|
1853
|
+
console.log(compfile);
|
|
1854
|
+
var refF;
|
|
1855
|
+
var compF;
|
|
1856
|
+
d3.text(reffile).then(function(text) {
|
|
1857
|
+
refF = text.split(",");
|
|
1858
|
+
console.log(refF);
|
|
1859
|
+
if (!--csvLoadsRemaining) drawShift();
|
|
1860
|
+
});
|
|
1861
|
+
d3.text(compfile).then(function(text) {
|
|
1862
|
+
compF = text.split(",");
|
|
1863
|
+
console.log(compF);
|
|
1864
|
+
if (!--csvLoadsRemaining) drawShift();
|
|
1865
|
+
});
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
hovergroup.append("button")
|
|
1869
|
+
.attr("class","btn btn-sm btn-primary")
|
|
1870
|
+
.text("Shift city vs previous year")
|
|
1871
|
+
.on("click", function() {
|
|
1872
|
+
console.log(d);
|
|
1873
|
+
console.log(i);
|
|
1874
|
+
popupshift(reftimeseldecoder().cached,d.name,comptimeseldecoder().cached,d.name);
|
|
1875
|
+
});
|
|
1876
|
+
|
|
1877
|
+
hovergroup.append("br");
|
|
1878
|
+
hovergroup.append("br");
|
|
1879
|
+
|
|
1880
|
+
hovergroup.append("button")
|
|
1881
|
+
.attr("class","btn btn-sm btn-primary")
|
|
1882
|
+
.text("Shift city in "+reftimeseldecoder().cached+" vs sum "+reftimeseldecoder().cached)
|
|
1883
|
+
.on("click", function() {
|
|
1884
|
+
console.log(d);
|
|
1885
|
+
console.log(i);
|
|
1886
|
+
popupshift(reftimeseldecoder().cached,"US",reftimeseldecoder().cached,d.name);
|
|
1887
|
+
});
|
|
1888
|
+
|
|
1889
|
+
hovergroup.append("br");
|
|
1890
|
+
hovergroup.append("br");
|
|
1891
|
+
|
|
1892
|
+
hovergroup.append("button")
|
|
1893
|
+
.attr("class","btn btn-sm btn-primary")
|
|
1894
|
+
.text("Shift city in "+comptimeseldecoder().cached+" vs sum "+comptimeseldecoder().cached)
|
|
1895
|
+
.on("click", function() {
|
|
1896
|
+
console.log(d);
|
|
1897
|
+
console.log(i);
|
|
1898
|
+
popupshift(comptimeseldecoder().cached,"US",comptimeseldecoder().cached,d.name);
|
|
1899
|
+
});
|
|
1900
|
+
|
|
1901
|
+
hovergroup.append("br");
|
|
1902
|
+
hovergroup.append("br");
|
|
1903
|
+
|
|
1904
|
+
|
|
1905
|
+
hovergroup.append("button")
|
|
1906
|
+
.attr("class","btn btn-xs btn-primary")
|
|
1907
|
+
.text("Select as reference for city-city comparison")
|
|
1908
|
+
.on("click", function() {
|
|
1909
|
+
console.log(d);
|
|
1910
|
+
console.log(i);
|
|
1911
|
+
refcity = d.name;
|
|
1912
|
+
});
|
|
1913
|
+
|
|
1914
|
+
if (refcity.length > 0) {
|
|
1915
|
+
hovergroup.append("br");
|
|
1916
|
+
hovergroup.append("br");
|
|
1917
|
+
hovergroup.append("button")
|
|
1918
|
+
.attr("class","btn btn-xs btn-primary")
|
|
1919
|
+
.text("Compare against "+refcity+" in "+comptimeseldecoder().cached)
|
|
1920
|
+
.on("click", function() {
|
|
1921
|
+
console.log(d);
|
|
1922
|
+
console.log(i);
|
|
1923
|
+
popupshift(comptimeseldecoder().cached,refcity,comptimeseldecoder().cached,d.name);
|
|
1924
|
+
});
|
|
1925
|
+
hovergroup.append("br");
|
|
1926
|
+
hovergroup.append("br");
|
|
1927
|
+
hovergroup.append("button")
|
|
1928
|
+
.attr("class","btn btn-xs btn-primary")
|
|
1929
|
+
.text("Compare against "+refcity+" in "+reftimeseldecoder().cached)
|
|
1930
|
+
.on("click", function() {
|
|
1931
|
+
console.log(d);
|
|
1932
|
+
console.log(i);
|
|
1933
|
+
popupshift(reftimeseldecoder().cached,refcity,reftimeseldecoder().cached,d.name);
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
clearTimeout(popuptimer);
|
|
1939
|
+
popuptimer = setTimeout(hidehover,3000);
|
|
1940
|
+
})
|
|
1941
|
+
.on("mouseout", function(event,d) {
|
|
1942
|
+
var timeout = 500;
|
|
1943
|
+
if (useTip) {
|
|
1944
|
+
// hovergroup.style({
|
|
1945
|
+
// "visibility": "hidden",
|
|
1946
|
+
// });
|
|
1947
|
+
|
|
1948
|
+
timeout = 3000;
|
|
1949
|
+
clearTimeout(popuptimer);
|
|
1950
|
+
|
|
1951
|
+
popuptimer = setTimeout(hidehover,timeout);
|
|
1952
|
+
}
|
|
1953
|
+
clearTimeout(popuptimer);
|
|
1954
|
+
popuptimer = setTimeout(hidehover,timeout);
|
|
1955
|
+
var rectSelection = d3.select(this)
|
|
1956
|
+
.style('opacity', '1.0')
|
|
1957
|
+
});
|
|
1958
|
+
|
|
1959
|
+
return hedotools.sankey;
|
|
1960
|
+
};
|
|
1961
|
+
|
|
1962
|
+
var replot = function() {
|
|
1963
|
+
// assuming that the data has been updated
|
|
1964
|
+
// console.log(oldstateselection);
|
|
1965
|
+
// console.log(newstateselection);
|
|
1966
|
+
|
|
1967
|
+
console.log(data);
|
|
1968
|
+
|
|
1969
|
+
oldstateselection.data(data)
|
|
1970
|
+
.transition()
|
|
1971
|
+
.duration(3000)
|
|
1972
|
+
.text(function(d,i) { return (d.oldindex+1)+". "+d.name; })
|
|
1973
|
+
.attr("y",function(d,i) { return y(d.oldindex+1)+11; } );
|
|
1974
|
+
|
|
1975
|
+
newstateselection.data(data)
|
|
1976
|
+
.transition()
|
|
1977
|
+
.duration(3000)
|
|
1978
|
+
.text(function(d,i) { return (d.newindex+1)+". "+d.name; })
|
|
1979
|
+
.attr("y",function(d,i) { return y(d.newindex+1)+11; } );
|
|
1980
|
+
|
|
1981
|
+
// create the sankey data thingy
|
|
1982
|
+
for (var i=0; i<data.length; i++) {
|
|
1983
|
+
sankeydata[i] = {
|
|
1984
|
+
"source": {
|
|
1985
|
+
"x": 20,
|
|
1986
|
+
"dx": 2,
|
|
1987
|
+
"y": y(data[i].oldindex+1)-8,
|
|
1988
|
+
},
|
|
1989
|
+
"target": {
|
|
1990
|
+
"x": width-22,
|
|
1991
|
+
"dx": 2,
|
|
1992
|
+
"y": y(data[i].newindex+1)-8,
|
|
1993
|
+
},
|
|
1994
|
+
"name": data[i].name,
|
|
1995
|
+
"oldhapps": data[i].oldhapps,
|
|
1996
|
+
"newhapps": data[i].newhapps,
|
|
1997
|
+
"oldindex": data[i].oldindex,
|
|
1998
|
+
"newindex": data[i].newindex,
|
|
1999
|
+
"sy": 10,
|
|
2000
|
+
"ty": 10,
|
|
2001
|
+
"dy": 10,
|
|
2002
|
+
};
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
// update the width function
|
|
2006
|
+
pathwidth.domain(d3.extent(data.map(function(d) { return Math.abs(d.change); })));
|
|
2007
|
+
|
|
2008
|
+
pathselection.data(sankeydata)
|
|
2009
|
+
.transition()
|
|
2010
|
+
.duration(3000)
|
|
2011
|
+
.attr("d", path)
|
|
2012
|
+
// don't update this
|
|
2013
|
+
// because the transition is applied by the css at the end
|
|
2014
|
+
// and it messes up the whole effect
|
|
2015
|
+
.attr("stroke-width", function(d,i) { return pathwidth(Math.abs(data[i].change)); });
|
|
2016
|
+
|
|
2017
|
+
return hedotools.sankey;
|
|
2018
|
+
};
|
|
2019
|
+
|
|
2020
|
+
// need functions to access updated properties
|
|
2021
|
+
var GETdata = function() {
|
|
2022
|
+
return data;
|
|
2023
|
+
};
|
|
2024
|
+
|
|
2025
|
+
var GETnewindices = function() {
|
|
2026
|
+
return newindices;
|
|
2027
|
+
};
|
|
2028
|
+
|
|
2029
|
+
var setTitles = function(titles) {
|
|
2030
|
+
listlabels = titles;
|
|
2031
|
+
return hedotools.sankey;
|
|
2032
|
+
};
|
|
2033
|
+
|
|
2034
|
+
var setSideWidth = function(listTwoByOne) {
|
|
2035
|
+
extraSideWidth = listTwoByOne;
|
|
2036
|
+
return hedotools.sankey;
|
|
2037
|
+
};
|
|
2038
|
+
|
|
2039
|
+
var setTipOn = function() {
|
|
2040
|
+
useTip = true;
|
|
2041
|
+
return hedotools.sankey;
|
|
2042
|
+
};
|
|
2043
|
+
|
|
2044
|
+
var opublic = {
|
|
2045
|
+
plot: plot,
|
|
2046
|
+
setfigure: setfigure,
|
|
2047
|
+
setdata: setdata,
|
|
2048
|
+
data: GETdata,
|
|
2049
|
+
newindices: GETnewindices,
|
|
2050
|
+
replot: replot,
|
|
2051
|
+
setTitles: setTitles,
|
|
2052
|
+
setSideWidth: setSideWidth,
|
|
2053
|
+
setTipOn: setTipOn,
|
|
2054
|
+
};
|
|
2055
|
+
|
|
2056
|
+
return opublic;
|
|
2057
|
+
};
|
|
2058
|
+
|
|
2059
|
+
|
|
2060
|
+
|
|
2061
|
+
|
|
2062
|
+
|
|
2063
|
+
|
|
2064
|
+
|
|
2065
|
+
// hedotools.shifter — the wordshift graph.
|
|
2066
|
+
//
|
|
2067
|
+
// As of v4.5 this is no longer implemented here; it lives in its own package,
|
|
2068
|
+
// @andyreagan/d3-shifterator (extracted from this repo's old shifter.v4.js).
|
|
2069
|
+
// We instantiate that package's factory once and expose it as the shared
|
|
2070
|
+
// `hedotools.shifter` singleton that the dashboard modules (barchart, lens,
|
|
2071
|
+
// map, sankey) drive via .shift(refF, compF, lens, words) and
|
|
2072
|
+
// .setfigure(...).setText(...).plot().
|
|
2073
|
+
//
|
|
2074
|
+
// Load order on the page (and in bundle.sh): D3 v4, then the d3-shifterator
|
|
2075
|
+
// UMD bundle (which defines the global `shifterator`), then this file.
|
|
2076
|
+
//
|
|
2077
|
+
// One compatibility shim: the dashboard modules call
|
|
2078
|
+
// setfigure(d3.select('#shift01'))
|
|
2079
|
+
// passing a d3 SELECTION (the old hedotools.shifter API). d3-shifterator's
|
|
2080
|
+
// setfigure expects a selector string or node (it runs d3.select() on its
|
|
2081
|
+
// argument). Normalize a selection down to its node so both styles work.
|
|
2082
|
+
hedotools.shifter = (function () {
|
|
2083
|
+
var instance = shifterator.shifterator();
|
|
2084
|
+
var setfigure = instance.setfigure;
|
|
2085
|
+
instance.setfigure = function (_) {
|
|
2086
|
+
var arg = (_ && typeof _.node === "function") ? _.node() : _;
|
|
2087
|
+
setfigure.call(instance, arg);
|
|
2088
|
+
return instance;
|
|
2089
|
+
};
|
|
2090
|
+
return instance;
|
|
2091
|
+
})();
|