@andyreagan/hedotools 3.0.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,550 @@
1
+ hedotools.sankeyoncall = function() {
2
+ var test = function(i,data) {
3
+ console.log("set in module");
4
+
5
+ console.log(allDataOld);
6
+
7
+ hedotools.shifter.shift(allDataOld[data[i].index].freq,allData[data[i].index].freq,lens,words);
8
+ var happysad = hedotools.shifter._compH() > hedotools.shifter._refH() ? "happier" : "less happy";
9
+ hedotools.shifter.setfigure(d3.select('#shift01')).setText(["Why "+data[i].name+" has become "+happysad+":"]).plot();
10
+
11
+
12
+ }
13
+ var opublic = { test: test, };
14
+ return opublic;
15
+ }();
16
+
17
+ hedotools.sankey = function() {
18
+
19
+ var popuptimer;
20
+
21
+ var figure;
22
+
23
+ var setfigure = function(_) {
24
+ console.log("setting figure");
25
+ figure = _;
26
+ // grabwidth();
27
+ return hedotools.sankey;
28
+ }
29
+
30
+ var oldlist;
31
+ var newlist;
32
+ var stateNames;
33
+
34
+ var oldindices;
35
+ var newindices;
36
+ var data;
37
+
38
+ var setdata = function(a,b,c) {
39
+ oldlist = a;
40
+ newlist = b;
41
+ stateNames = c;
42
+ if ( stateNames[50] === "District of Columbia" ) {
43
+ stateNames[50] = "DC";
44
+ }
45
+
46
+ // do the sorting
47
+ oldindices = Array(oldlist.length);
48
+ for (var i = 0; i < oldlist.length; i++) { oldindices[i] = i; }
49
+
50
+ // sort by abs magnitude
51
+ // 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; });
52
+
53
+ // sort by magnitude, parity preserving
54
+ oldindices.sort(function(a,b) { return oldlist[a] < oldlist[b] ? 1 : oldlist[a] > oldlist[b] ? -1 : 0; });
55
+
56
+ // do the sorting on new data
57
+ newindices = Array(newlist.length);
58
+ for (var i = 0; i < newlist.length; i++) { newindices[i] = i; }
59
+
60
+ newindices.sort(function(a,b) { return newlist[a] < newlist[b] ? 1 : newlist[a] > newlist[b] ? -1 : 0; });
61
+
62
+ data = Array(oldlist.length);
63
+ for (var i=0; i<data.length; i++) {
64
+ data[i] = {
65
+ "name": stateNames[i],
66
+ "index": i,
67
+ "oldindex": oldindices.indexOf(i),
68
+ "newindex": newindices.indexOf(i),
69
+ "change": newlist[i]-oldlist[i],
70
+ "oldhapps": oldlist[i],
71
+ "newhapps": newlist[i],
72
+ };
73
+ }
74
+
75
+ // console.log(data);
76
+ // tmpglob = data;
77
+
78
+ return hedotools.sankey;
79
+ }
80
+
81
+ // initialize everything so other function in this module have access
82
+ var margin;
83
+ var axeslabelmargin;
84
+ var figwidth;
85
+ var aspectRatio;
86
+ var figheight;
87
+ var width;
88
+ var height;
89
+ var figcenter;
90
+ var leftOffsetStatic;
91
+
92
+ var canvas;
93
+ var x;
94
+ var y;
95
+ var axes;
96
+
97
+ var oldstateselection;
98
+ var newstateselction;
99
+ var path;
100
+ var sankeydata;
101
+ var pathwidth;
102
+ var pathselection;
103
+
104
+ var listlabels;
105
+ var extraSideWidth = [0,0];
106
+
107
+ var useTip = false;
108
+ var tip;
109
+
110
+ var minwidth = 450;
111
+
112
+ // make the plot
113
+ var plot = function() {
114
+ margin = {top: 0, right: 0, bottom: 0, left: 0};
115
+ axeslabelmargin = {top: 0, right: 90+extraSideWidth[0], bottom: 0, left: 90+extraSideWidth[1]};
116
+ figwidth = parseInt(figure.style('width')) - margin.left - margin.right;
117
+ if (figwidth<minwidth) {
118
+ console.log("width is too small...");
119
+ d3.selectAll(".reftimelabel,.comptimelabel,.reftimelabelbottom,.comptimelabelbottom").remove();
120
+ 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 :)");
121
+ return hedotools.sankey;
122
+ }
123
+ aspectRatio = 1.8+3.4*(oldlist.length-51)/(304-51);
124
+ figheight = parseInt(figure.style('width'))*aspectRatio - margin.top - margin.bottom;
125
+ // console.log("figheight is "+figheight);
126
+ // figheight = 4576; // for the city sankey this seems good
127
+ width = figwidth-axeslabelmargin.left-axeslabelmargin.right;
128
+ height = figheight-axeslabelmargin.top-axeslabelmargin.bottom;
129
+ figcenter = width/2;
130
+ leftOffsetStatic = axeslabelmargin.left;
131
+
132
+ var hovergroup = figure.append("div").attr({
133
+ "class": "hoverinfogroup",
134
+ // "transform": "translate("+(x+hoverboxxoffset+axeslabelmargin.left)+","+(d3.min([d3.max([0,y-hoverboxheight/2-hoverboxyoffset]),height-hoverboxheight]))+")",
135
+ })
136
+ .style({
137
+ "position": "absolute",
138
+ "top": "100px",
139
+ "left": "100px",
140
+ "visibility": "hidden",
141
+ });
142
+
143
+ function hidehover() {
144
+ console.log("hiding hover");
145
+ d3.selectAll("path").transition().duration(500).style("opacity","1.0");
146
+ if (useTip) {
147
+ hovergroup.style({
148
+ "visibility": "hidden",
149
+ });
150
+ }
151
+ }
152
+
153
+ // remove an old figure if it exists
154
+ figure.select(".canvas").remove();
155
+
156
+ canvas = figure.append("svg")
157
+ .attr("width",figwidth)
158
+ .attr("height",figheight)
159
+ .attr("id","sankeysvg")
160
+ .attr("class","canvas")
161
+
162
+ // x scale, maps all the data to
163
+ x = d3.scale.linear()
164
+ .domain([0,1])
165
+ .range([5,width-10]);
166
+
167
+ // linear scale function
168
+ y = d3.scale.linear()
169
+ .domain([newlist.length,1])
170
+ .range([height-20, 5]);
171
+
172
+ // create the axes themselves
173
+ axes = canvas.append("g")
174
+ .attr("transform", "translate(" + (axeslabelmargin.left) + "," +
175
+ (axeslabelmargin.top) + ")")
176
+ .attr("width", width)
177
+ .attr("height", height)
178
+ .attr("class", "main");
179
+
180
+ // if (useTip) {
181
+ // console.log("setting tip");
182
+ // tip = d3.tip().attr('class', 'd3-tip').html(function(d) { return d; });
183
+ // axes.call(tip);
184
+ // }
185
+
186
+ oldstateselection = axes.selectAll("text.statetext.old")
187
+ .data(data)
188
+ .enter()
189
+ .append("text")
190
+ .attr("class", function(d,i) { return d.name+" statetext"; })
191
+ .attr("x",20)
192
+ .style("text-anchor", "end")
193
+ .attr("y",function(d,i) { return y(d.oldindex+1)+11; } )
194
+ .text(function(d,i) { return (d.oldindex+1)+". "+d.name; });
195
+
196
+ newstateselection = axes.selectAll("text.statetext.new")
197
+ .data(data)
198
+ .enter()
199
+ .append("text")
200
+ .attr("class", function(d,i) { return d.name+" statetext"; })
201
+ .attr("x",width-20)
202
+ .style("text-anchor", "start")
203
+ .attr("y",function(d,i) { return y(d.newindex+1)+11; } )
204
+ .text(function(d,i) { return (d.newindex+1)+". "+d.name; });
205
+
206
+ // create an instance of the sankey to make paths
207
+ var sankey = d3.sankey();
208
+ path = sankey.link();
209
+
210
+ // create the sankey data thingy
211
+ sankeydata = Array(oldlist.length);
212
+ for (var i=0; i<data.length; i++) {
213
+ sankeydata[i] = {
214
+ "source": {
215
+ "x": 20,
216
+ "dx": 2,
217
+ "y": y(data[i].oldindex+1)-8,
218
+ },
219
+ "target": {
220
+ "x": width-22,
221
+ "dx": 2,
222
+ "y": y(data[i].newindex+1)-8,
223
+ },
224
+ "name": data[i].name,
225
+ "oldhapps": data[i].oldhapps,
226
+ "newhapps": data[i].newhapps,
227
+ "oldindex": data[i].oldindex,
228
+ "newindex": data[i].newindex,
229
+ "sy": 10,
230
+ "ty": 10,
231
+ "dy": 10,
232
+ };
233
+ }
234
+
235
+ pathwidth = d3.scale.linear()
236
+ .domain(d3.extent(data.map(function(d) { return Math.abs(d.change); })))
237
+ .range([2,13]);
238
+
239
+ pathselection = axes.selectAll("path.sankey").data(sankeydata)
240
+ .enter()
241
+ .append("path")
242
+ .attr({ "d": path,
243
+ "fill": "none",
244
+ "class": function(d,i) { return "r"+classColor(data[i].oldindex)+"-8"; },
245
+ "stroke-width": function(d,i) { return pathwidth(Math.abs(data[i].change)); } })
246
+ .on("mouseover", function(d,i) {
247
+ // console.log(i);
248
+ // console.log(data[i]);
249
+ // var rectSelection = d3.select(this)
250
+ // .style({'opacity':'0.7',
251
+ // // 'stroke-width':'1.0',
252
+ // });
253
+
254
+ var thispath = this;
255
+
256
+ hedotools.sankeyoncall.test(i,data);
257
+
258
+ d3.selectAll("path").transition().duration(750).style("opacity","0.1");
259
+ d3.select(this).transition().duration(5).style("opacity","1.0");
260
+
261
+ if (useTip) {
262
+
263
+ // var bbox = this.getBBox();
264
+ // var x = Math.floor(bbox.x + bbox.width/2.0);
265
+ // var y = Math.floor(bbox.y + bbox.height/2.0);
266
+
267
+ var hoverboxheight = 90;
268
+ var hoverboxwidth = 200;
269
+ var hoverboxyoffset = 0;
270
+ var hoverboxxoffset = 0;
271
+
272
+ var x = d3.mouse(thispath)[0];
273
+ var y = d3.mouse(thispath)[1];
274
+
275
+ var hoverboxheightguess = 190;
276
+ if (refcity.length > 0) {
277
+ hoverboxheightguess = 270;
278
+ }
279
+ if ((y+hoverboxheightguess)>height) { y-=(y+hoverboxheightguess-height); }
280
+
281
+ // tip.show;
282
+ // console.log(d);
283
+
284
+ hovergroup.style({
285
+ "position": "absolute",
286
+ "top": y+"px",
287
+ "left": x+"px",
288
+ "visibility": "visible",
289
+ });
290
+
291
+ hovergroup.selectAll("p,h3,button,br").remove();
292
+
293
+ hovergroup.append("h3")
294
+ .attr("class","cityname")
295
+ .text(d.name);
296
+
297
+ hovergroup.append("p")
298
+ .attr("class","refhapps")
299
+ .text(reftimeseldecoder().cached+" Happiness: "+parseFloat(d.oldhapps).toFixed(2));
300
+
301
+ hovergroup.append("p")
302
+ .attr("class","refrank")
303
+ .text(reftimeseldecoder().cached+" Rank: "+(d.oldindex+1));
304
+
305
+ hovergroup.append("p")
306
+ .attr("class","comphapps")
307
+ .text(comptimeseldecoder().cached+" Happiness: "+parseFloat(d.newhapps).toFixed(2));
308
+
309
+ hovergroup.append("p")
310
+ .attr("class","comprank")
311
+ .text(comptimeseldecoder().cached+" Rank: "+(d.newindex+1));
312
+
313
+ var popupshift = function(refyear,refname,compyear,compname) {
314
+ refshifttimeencoder.varval(refyear);
315
+ refshiftcityencoder.varval(refname);
316
+ compshifttimeencoder.varval(compyear);
317
+ compshiftcityencoder.varval(compname);
318
+ // write a function to call on the load
319
+ drawShift = function() {
320
+ hedotools.shifter._refF(refF);
321
+ hedotools.shifter._compF(compF);
322
+ hedotools.shifter.stop();
323
+ hedotools.shifter.shifter();
324
+ hedotools.shifter.setText(["Why "+compname+" in "+compyear+" is "+( ( hedotools.shifter._compH() > hedotools.shifter._refH() ) ? "happier" : "less happy" )+" than "+refname+" in "+refyear+":"]).plot();
325
+ $('#myModal').modal('show');
326
+ }
327
+ // load both of the files
328
+ var csvLoadsRemaining = 2;
329
+ // var reffile = "http://hedonometer.org/data/cities/word-vectors/"+reftimeseldecoder().cached+"/"+d.name+".csv";
330
+ // if (parseInt(reftimeseldecoder().cached) < 2014) reffile+=".new"
331
+ // var compfile = "http://hedonometer.org/data/cities/word-vectors/"+comptimeseldecoder().cached+"/"+d.name+".csv";
332
+ // if (parseInt(comptimeseldecoder().cached) < 2014) compfile+=".new"
333
+ var reffile = "http://hedonometer.org/data/cities/word-vectors/"+refyear+"/"+refname+".csv";
334
+ if (parseInt(refyear) < 2014) reffile+=".new"
335
+ var compfile = "http://hedonometer.org/data/cities/word-vectors/"+compyear+"/"+compname+".csv";
336
+ if (parseInt(compyear) < 2014) compfile+=".new"
337
+ console.log(reffile);
338
+ console.log(compfile);
339
+ var refF;
340
+ var compF;
341
+ d3.text(reffile,function(text) {
342
+ refF = text.split(",");
343
+ console.log(refF);
344
+ if (!--csvLoadsRemaining) drawShift();
345
+ });
346
+ d3.text(compfile,function(text) {
347
+ compF = text.split(",");
348
+ console.log(compF);
349
+ if (!--csvLoadsRemaining) drawShift();
350
+ });
351
+ }
352
+
353
+ hovergroup.append("button")
354
+ .attr("class","btn btn-sm btn-primary")
355
+ .text("Shift city vs previous year")
356
+ .on("click", function() {
357
+ console.log(d);
358
+ console.log(i);
359
+ popupshift(reftimeseldecoder().cached,d.name,comptimeseldecoder().cached,d.name);
360
+ });
361
+
362
+ hovergroup.append("br");
363
+ hovergroup.append("br");
364
+
365
+ hovergroup.append("button")
366
+ .attr("class","btn btn-sm btn-primary")
367
+ .text("Shift city in "+reftimeseldecoder().cached+" vs sum "+reftimeseldecoder().cached)
368
+ .on("click", function() {
369
+ console.log(d);
370
+ console.log(i);
371
+ popupshift(reftimeseldecoder().cached,"US",reftimeseldecoder().cached,d.name);
372
+ });
373
+
374
+ hovergroup.append("br");
375
+ hovergroup.append("br");
376
+
377
+ hovergroup.append("button")
378
+ .attr("class","btn btn-sm btn-primary")
379
+ .text("Shift city in "+comptimeseldecoder().cached+" vs sum "+comptimeseldecoder().cached)
380
+ .on("click", function() {
381
+ console.log(d);
382
+ console.log(i);
383
+ popupshift(comptimeseldecoder().cached,"US",comptimeseldecoder().cached,d.name);
384
+ });
385
+
386
+ hovergroup.append("br");
387
+ hovergroup.append("br");
388
+
389
+
390
+ hovergroup.append("button")
391
+ .attr("class","btn btn-xs btn-primary")
392
+ .text("Select as reference for city-city comparison")
393
+ .on("click", function() {
394
+ console.log(d);
395
+ console.log(i);
396
+ refcity = d.name;
397
+ });
398
+
399
+ if (refcity.length > 0) {
400
+ hovergroup.append("br");
401
+ hovergroup.append("br");
402
+ hovergroup.append("button")
403
+ .attr("class","btn btn-xs btn-primary")
404
+ .text("Compare against "+refcity+" in "+comptimeseldecoder().cached)
405
+ .on("click", function() {
406
+ console.log(d);
407
+ console.log(i);
408
+ popupshift(comptimeseldecoder().cached,refcity,comptimeseldecoder().cached,d.name);
409
+ });
410
+ hovergroup.append("br");
411
+ hovergroup.append("br");
412
+ hovergroup.append("button")
413
+ .attr("class","btn btn-xs btn-primary")
414
+ .text("Compare against "+refcity+" in "+reftimeseldecoder().cached)
415
+ .on("click", function() {
416
+ console.log(d);
417
+ console.log(i);
418
+ popupshift(reftimeseldecoder().cached,refcity,reftimeseldecoder().cached,d.name);
419
+ });
420
+ }
421
+ }
422
+
423
+ clearTimeout(popuptimer);
424
+ popuptimer = setTimeout(hidehover,3000);
425
+ })
426
+ .on("mouseout", function(d,i) {
427
+ var timeout = 500;
428
+ if (useTip) {
429
+ // hovergroup.style({
430
+ // "visibility": "hidden",
431
+ // });
432
+
433
+ timeout = 3000;
434
+ clearTimeout(popuptimer);
435
+
436
+ popuptimer = setTimeout(hidehover,timeout);
437
+ }
438
+ clearTimeout(popuptimer);
439
+ popuptimer = setTimeout(hidehover,timeout);
440
+ var rectSelection = d3.select(this)
441
+ .style({ 'opacity':'1.0', })
442
+ });
443
+
444
+ return hedotools.sankey;
445
+ };
446
+
447
+ var replot = function() {
448
+ // assuming that the data has been updated
449
+ // console.log(oldstateselection);
450
+ // console.log(newstateselection);
451
+
452
+ console.log(data);
453
+
454
+ oldstateselection.data(data)
455
+ .transition()
456
+ .duration(3000)
457
+ .text(function(d,i) { return (d.oldindex+1)+". "+d.name; })
458
+ .attr("y",function(d,i) { return y(d.oldindex+1)+11; } );
459
+
460
+ newstateselection.data(data)
461
+ .transition()
462
+ .duration(3000)
463
+ .text(function(d,i) { return (d.newindex+1)+". "+d.name; })
464
+ .attr("y",function(d,i) { return y(d.newindex+1)+11; } );
465
+
466
+ // create the sankey data thingy
467
+ for (var i=0; i<data.length; i++) {
468
+ sankeydata[i] = {
469
+ "source": {
470
+ "x": 20,
471
+ "dx": 2,
472
+ "y": y(data[i].oldindex+1)-8,
473
+ },
474
+ "target": {
475
+ "x": width-22,
476
+ "dx": 2,
477
+ "y": y(data[i].newindex+1)-8,
478
+ },
479
+ "name": data[i].name,
480
+ "oldhapps": data[i].oldhapps,
481
+ "newhapps": data[i].newhapps,
482
+ "oldindex": data[i].oldindex,
483
+ "newindex": data[i].newindex,
484
+ "sy": 10,
485
+ "ty": 10,
486
+ "dy": 10,
487
+ };
488
+ }
489
+
490
+ // update the width function
491
+ pathwidth.domain(d3.extent(data.map(function(d) { return Math.abs(d.change); })));
492
+
493
+ pathselection.data(sankeydata)
494
+ .transition()
495
+ .duration(3000)
496
+ .attr({ "d": path,
497
+ // don't update this
498
+ // because the transition is applied by the css at the end
499
+ // and it messes up the whole effect
500
+ // "class": function(d,i) { return "r"+classColor(data[i].oldindex)+"-8"; },
501
+ "stroke-width": function(d,i) { return pathwidth(Math.abs(data[i].change)); } });
502
+
503
+ return hedotools.sankey;
504
+ };
505
+
506
+ // need functions to access updated properties
507
+ var GETdata = function() {
508
+ return data;
509
+ };
510
+
511
+ var GETnewindices = function() {
512
+ return newindices;
513
+ };
514
+
515
+ var setTitles = function(titles) {
516
+ listlabels = titles;
517
+ return hedotools.sankey;
518
+ };
519
+
520
+ var setSideWidth = function(listTwoByOne) {
521
+ extraSideWidth = listTwoByOne;
522
+ return hedotools.sankey;
523
+ };
524
+
525
+ var setTipOn = function() {
526
+ useTip = true;
527
+ return hedotools.sankey;
528
+ };
529
+
530
+ var opublic = {
531
+ plot: plot,
532
+ setfigure: setfigure,
533
+ setdata: setdata,
534
+ data: GETdata,
535
+ newindices: GETnewindices,
536
+ replot: replot,
537
+ setTitles: setTitles,
538
+ setSideWidth: setSideWidth,
539
+ setTipOn: setTipOn,
540
+ };
541
+
542
+ return opublic;
543
+ };
544
+
545
+
546
+
547
+
548
+
549
+
550
+