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