@andyreagan/hedotools 6.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.
@@ -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
+ })();