-
Notifications
You must be signed in to change notification settings - Fork 0
/
open-project.html
931 lines (861 loc) · 59 KB
/
open-project.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Oil, Gas, and Coal Money: Creating a Split between Voters and their Representatives?</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<style>
path {
stroke: #555555;
stroke-linejoin: round;
stroke-linecap: round;
}
path:hover {
cursor: pointer;
}
h1,h2,h3,h4,h5,h6 {
text-align: center;
}
header {
border: 5px solid gray;
border-radius: 5px;
width: 890px;
}
#main-subheading {
color: lightgreen;
}
#container {
margin: 0 auto;
padding: 15px;
width: 900px;
}
body {
background-color: #dddddd;
}
.tooltip {
width: 200px;
background-color: #bbbbbb;
opacity: 0.8;
padding: 10px;
padding-top: 0px;
border: 1px solid gray;
border-radius: 4px;
z-index: 2;
}
.tooltip h3 {
border-bottom: 1px solid black;
}
#container div {
position: relative;
}
.icon {
display: inline-block;
width: 1.4em;
height: 1.4em;
color: white;
font-size: 0.714285714em;
font-weight: 700;
text-align: center;
line-height: 1.4em;
position: relative;
top: -0.2em;
border-radius: 0.7em;
margin-right: 1em;
}
.iconD {
background-color: #007dd6;
}
.iconR {
background-color: #b81800;
}
.text-container {
/* background-color: lightgreen;*/
padding: 20px;
margin-top: 15px;
margin-bottom: 15px;
border-top: 2px solid gray;
border-bottom: 2px solid gray;
/*
border: 5px solid gray;
border-radius: 5px;
*/
width: 850px;
}
</style>
</head>
<body>
<div id="container">
<header>
<h1 id="main-heading">Oil, Gas, and Coal Money: Creating a Split between Voters and their Representatives?</h1>
<h3 id="main-subheading">An Investigation into Dark Money, Public Opinion, and Voting Records</h3>
</header>
<div class="text-container">
<p>
Climate change, a topic which has become intensely political and partisan, is a clear example of human failure to address a terrifying long-term threat. It has been clear that our actions have been leading to global, rapid changes in climate patterns for a long time. However, some money-interested bad actors have done an incredible amount of damage by influencing elected lawmakers as well as sowing doubt about an established scientific consensus in the minds of the public. This project aims to quantify how that money, in this case spent by the Oil&Gas, Natural Gas Transmission, and Coal industries, coalition has shaped public and political opinion, and perhaps has led to a divergence of the two.
</p>
<p>
This project has used data from three seperate sources, looking at public opinion on climate change, campaign contribution data, and the voting records on climate-related bills of the 435 US Representatives. The sources for all of these data are:
</p>
<ul>
<li>
Public opinion: From Yale's <a href="http://climatecommunication.yale.edu/visualizations-data/ycom/">Program on Climate Change Communication, 2014 survey</a>. This is a comprehensive survey done on the American public on questions of perception of climate change, risk perception, and political perspective.
</li>
<li>
Campaign contribution data (yes, this is all full public): <a href="https://www.opensecrets.org/industries/summary.php?ind=E01&recipdetail=A&sortorder=U&mem=Y&cycle=2016">OpenSecrets.org</a>, 2016 data from 3 industries.
</li>
<li>
Voting records: From <a href="http://scorecard.lcv.org/members-of-congress">League of Conservation Voters (LCV)</a>. This site examines all the bills relevant to the environment and rates members of Congress with the percentage of those votes that they vote the pro-enviroment way.
</li>
</ul>
</div>
<div class="text-container">
<p>
First, here is the map from the LCV. Dark red is a very low climate rating and dark green is a high ranking (i.e. very pro-environment). Notice that this almost exactly mirrors the political map, with red being in Republican areas and green in Democratic areas.
</p>
</div>
<div id="scorecard-map"></div>
<div class="text-container">
<p>
Now, let's further examine the partisanship reflected in this highly-polarized map (notice there is lots of dark green and dark red... it appears that representatives are almost always voting 100% pro-environment or 0%). Here is a histogram, with both parties overlayed on top of each other on the same plot, of the 2016 ratings. <span style="color:blue;">Blue</span> is Democratic, <span style="color:red;">Red</span> is Republican. The partisanship is pretty clear, with, as suspected, each party saturated off to opposite sides of the plot.
</p>
</div>
<div id="scorecard-hist"></div>
<div class="text-container">
<p>
Now, let's take a look at public opinion. Perhaps it isn't as polarized as the representatives'? The Yale Program on Climate Change Communication gathers data on various questions, from which you can select, and display on the map.
</p>
</div>
<div id="opinion-map"></div>
<div id="opinion-options">Select survey question: </div>
<div class="text-container">
<p>
In contrast to the previous map, almost all of these maps are actually quite uniform, in the upper middle range (i.e. lots of light green). Notice that some maps are generally more green than others, indicating stronger public agreement with the corresponding survey question. For example, the question, "Should we fund research into renewable energy sources" reached an average of ~78% across all the districts. That's pretty high. Also notice that the question, "Should we regulate CO2 as a polutant" reached similarly high numbers. Some other interesting observations:
</p>
<ul>
<li>
While most people seem not to think that global warming is "already harming people in the US," most people do think that it will harm future generations in the US. That shows that people are aware it is a <strong>long term threat</strong>.
</li>
<li>
People also tend not to think that global warming will harm them personally, with especially low rates of agreement in Appalachia and the central United States. Note: almost all of these areas are in Trump country. Also, it is quite likely that low levels of worry about being personally effected make people more apathetic.
</li>
<li>
In spite of the previous point, notice how on average more than half (55%) of Americans say they are "worried about global warming".
</li>
<li>
The question about scientific consensus yeilds a particularly shocking result: well under half (42%) of the country thinks that scientists are in agreement on the issue. This is clearly a result of misinformation; it has been an unwaveringly clear consensus for decades. In his documentary, "An Inconvenient Truth," former Vice President Al Gore points to a study in which 900 credible academic papers on climate science were assessed for doubt on the issue. Out of those, exactly 0 showed any doubt. Thus it is clear that some kind of misinformation is creating the vast discrepancy with public perception.
</li>
</ul>
<p>
Next, let's go back to the political side of this. We'll examine how monetary contributions might be effecting the voting records of congressmen. To do this, here is a plot of money received from a selected industry (you can pick from Oil&Gas, Natural Gas transmission, and Coal) vs. voting score as calculated by the LCV. Each point represents one congressman, and <span style="color:blue;">Blue</span> is Democratic and <span style="color:red;">Red</span> is Republican.
</p>
</div>
<div id="lobbying-vs-scorecard"></div>
<div id="lobbying-vs-scorecard-options">Select industry: </div>
<div class="text-container">
<p>
While none of the 3 industries yields a plot with a clear linear fit (there's no reason it would have to be linear anyway), there is a clear trend in all of them. Notice that there are many congressmen receiving large sums from industry with low LCV scores. On the contrary, just about everyone getting a high LCV score is not receiving much industry money. It is difficult to prove cause an effect, but there is clearly some kind of an inverse relationship between industry money and pro-environment voting. Notice the partisan divide here as well.
</p>
<p>
Now, let's map this data to better visualize how it potentially fits in with the Yale and LCV data. Here's a map, where dark <span style="color:purple;">Purple</span> represents more money received:
</p>
</div>
<div id="money-map"></div>
<div id="money-map-options">Select industry: </div>
<div class="text-container">
<p>
It's pretty obvious from these three maps that Oil&Gas is the biggest industry, by far, out of these three, in terms of donations to politcal campaigns. Looking at the map, contributions are the largest along the spine of Appalachia, as well as from Texas straight up the Great Planes through to North Dakota. Even out by the Rockies the donations are the pretty high as well. The other 2 industries exhibit a similar pattern.
</p>
<p>
Now, let's take a deeper look into the money received by representatives' campaigns, with a histogram. The following histogram is seperated by industry (i.e. Oil&Gas, Natural Gas transmission, Coal), and within that by party (Dem and Rep). We can see that generally, Republicans are getting more money from these industries, with each histogram having a longer sweep out towards the high-bracket contributions than its Democratic counterpart. However, it is worth noting that the majority of contributions for both parties fit into the lowest monetary bracket.
</p>
</div>
<div id="lobbying-hist"></div>
<div class="text-container">
<p>
Lastly, let's take a look at the underlying question in all of this investigation: <strong>How do the industry money, political voting records, and public opinion relate to each other?</strong>
</p>
<p>
The following scatter plots industry contributions vs. the difference between LCV scores of congressmen and their constituents' response to survey questions. You can select the industry and survey question to update the plot. Naturally, some survey questions such as those focused on policy are more comparable to the voting record of the congressmen. The idea is that one would expect a representative to vote proportionally in accordance with the demographics of their constituency. In a perfectly fair world perhaps, if 60% of a rep's constituents felt one way on an issue and the other 40% felt the opposite way, the rep would vote 60% of the time in accordance with the beliefs of that 60%. Same here with climate; if most of a rep's constituency believes in concrete climate action measures such as taxing CO2 and subsidizing renewables, one would expect them to mostly vote in accordance with that view, rather than vote consistently anti-environment.
</p>
</div>
<div id="money-gap-plot"></div>
<div id="money-gap-plot-options"></div>
<div class="text-container">
Here, we actually do seem to see a correlation! Especially when you look at the category on funding research into renewables (which Oil, Gas, and Coal would naturally be opposed to to preserve their economic stature), we see that high industry dollars always coincides with steep differences between representative's voting and their constituents' sentiment. High industry money intake always means that (LCV score - popular sentiment percentage) is far below zero. Again, it is hard to prove cause and effect, but it is clear that there is a strong connection there. From this data, it does seem that receiving large sums of money from the Oil, Gas and Coal industries does, in fact, push reps further to the right on climate than their constituents.
</div>
</div>
<script>
var urls = {"districts": "cb_2015_us_cd114_20m.json",
"states": "cb_2016_us_state_20m.json",
"scorecard": "climate_scorecard.csv",
"opinion": "YPCCC_cd113_n28_2015Jan5.csv",
"lobbying": "lobbying_2016.csv"}
d3.json(urls.districts, function(e1, districts) {
d3.json(urls.states, function(e2, states) {
var distFts = topojson.feature(districts, districts.objects.cb_2015_us_cd114_20m).features;
var stateFts = topojson.feature(states, states.objects.cb_2016_us_state_20m).features;
// Maps a datafield STATEFP to the statename, and vice-versa.
var statefpList = [1,2,4,5,6,8,9,10,
12,13,15,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30,
31,32,33,34,35,36,37,38,39,40,
41,42,44,45,46,47,48,49,50,51,53,54,55,56];
var stateNamesList = ["Alabama", "Alaska", "Arizona", "Arkansas", "California",
"Colorado", "Connecticut", "Delaware", "Florida", "Georgia",
"Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas",
"Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts",
"Michigan", "Minnesota", "Mississippi", "Missouri", "Montana",
"Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico",
"New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma",
"Oregon", "Pennsylvania", "Rhode Island", "South Carolina",
"South Dakota", "Tennessee", "Texas", "Utah", "Vermont",
"Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"];
// STATEFP to index of the state, i.e. "Arizona" = index 2.
var fpToIdx = {};
for (var i = 0; i < 50; i++) {
fpToIdx[statefpList[i]] = i;
}
// Clean up SVG path data for districts
function cleanDistFts(df) {
// Splice out DC and PR, then reorder alphabetically and by district order.
df.splice(342,1);
df.splice(44,1);
var df1 = [];
for (var i = 0; i < 50; i++) {
df1.push([]);
}
for (var i = 0; i < df.length; i++) {
var state = +df[i].properties.STATEFP;
var district = +df[i].properties.CD114FP;
df1[fpToIdx[state]][Math.max(district-1,0)] = df[i];
}
var df2 = [];
for (var i = 0; i < df1.length; i++) {
for (var j = 0; j < df1[i].length; j++) {
df2.push(df1[i][j]);
}
}
return df2;
}
distFts = cleanDistFts(distFts);
// Clean up SVG path data for states
function cleanStateFts(sf) {
// Splice out DC and Puerto Rico, then reorder alphabetically.
for (var e of [46,9]) {
sf.splice(e,1);
}
var sf1 = [];
for (var i = 0; i < 50; i++) {
var state = +sf[i].properties.STATEFP;
sf1[fpToIdx[state]] = sf[i];
}
return sf1;
}
stateFts = cleanStateFts(stateFts);
// Default setup parameters for an SVG
var defaultSvgDims = {width: 900,
height: 600,
margin: {
top: 40,
bottom: 80,
left: 80,
right: 40
}};
function appendSvg(parentElt, svgDims=defaultSvgDims) {
var svg = parentElt.append("svg")
.attr("width", svgDims.width)
.attr("height", svgDims.height)
.style("margin", "0 auto")
.style("display", "block")
.style("overflow", "visible");
return svg;
}
// Generates a map and colors it according to mainData and colorFunc.
//
// Args ---
// pathData: an array of features to use as SVG path data. Most likely would be districts and states.
// mainData: data used to color the map.
// colorFunc: function that transforms from some property of the datapoint in mainData to a color.
function makeMap(parentElt,mainData,colorFunc,tooltipFunc,pathData=[distFts,stateFts],pathWidths=[0.3,0.6],svgDims=defaultSvgDims) {
var tooltip = parentElt.append("div")
.style("visibility","hidden")
.style("position","absolute")
.classed("tooltip", true);
var svg = appendSvg(parentElt,svgDims);
var wEff = svgDims.width-svgDims.margin.right-svgDims.margin.left;
var hEff = svgDims.height-svgDims.margin.top-svgDims.margin.bottom;
var scale = 4/3*(wEff);
var translate = [svgDims.margin.left + wEff/2,svgDims.margin.top + hEff/2];
var path = d3.geoPath()
.projection(d3.geoAlbersUsa()
.scale(scale)
.translate(translate));
for (var i = 0; i < pathData.length; i++) {
var g = svg.append("g")
.selectAll("path")
.data(pathData[i])
.enter()
.append("path")
.attr("class", "c"+i)
.attr("d", path)
.style("stroke-width", pathWidths[i]);
if (i == 0) {
g.data(mainData)
.style("fill", function(d) {return colorFunc(d);})
.on("mouseover", function(d) {
d3.select(this).transition()
.duration(300)
.style("opacity", 0.7);
var loc = d3.mouse(this);
if (tooltip.innerHTML !== "") tooltip = tooltipFunc(tooltip,d);
tooltip
.style("left", Math.floor(loc[0]-200/2)+"px")
.style("top", loc[1]+15+"px")
.style("visibility","visible");
})
.on("mousemove", function() {
var loc = d3.mouse(this);
tooltip
.style("left", Math.floor(loc[0]-200/2)+"px")
.style("top", loc[1]+15+"px")
.style("visibility","visible");
})
.on("mouseout", function() {
d3.select(this).transition()
.duration(300)
.style("opacity", 1.0);
tooltip.style("visibility","hidden");
tooltip.html("");
});
} else {
g.style("fill", "none");
}
}
return svg;
}
// Changes the tooltip for a map.
// Args ---
// svg: the svg of the map in question
// newFunc: the new tooltip function. Takes tooltip, d as args.
// tooltip: the tooltip (a div positioned absolute in the containing element of svg)
function changeTooltipFunc(svg,newFunc,tooltip) {
svg.selectAll("path.c0")
.on("mouseover", function(d) {
d3.select(this).transition()
.duration(300)
.style("opacity", 0.7);
var loc = d3.mouse(this);
if (tooltip.innerHTML !== "") tooltip = newFunc(tooltip,d);
tooltip
.style("left", Math.floor(loc[0]-200/2)+"px")
.style("top", loc[1]+15+"px")
.style("visibility","visible");
});
}
// Fills the div tooltip with a header and content, styled in CSS
// tooltip: a div
// header: the heading for the tooltip
// content: a list of strings, each will go on a new line in tooltip's body
function makeTooltip(tooltip,header,content) {
tooltip.append("h3").html(header);
for (l of content) {
tooltip.append("span").html(l);
tooltip.append("br");
}
return tooltip;
}
// Adds a key to the SVG
//
// Args ---
// svg: the SVG to place the key in
// color: color scale for the key
// xPos,yPos: the positions (x and y) of the key
// length: length of the key
// orientation : orientation of the key ("v" or "h")
// title: caption for the key
function makeKey(svg,color,xPos,yPos,length,orientation,title="") {
// Some of this is modified from a D3 example, called "Choropleth"
// https://bl.ocks.org/mbostock/4060606
var g = svg.append("g")
.attr("class", "key")
.attr("transform", "translate(" + xPos + "," + yPos + ")");
var keyRange = [0,length];
var keyScale = d3.scaleLinear()
.domain([color.domain()[0],color.domain()[color.domain().length-1]])
.rangeRound(keyRange);
var rects = g.selectAll("rect")
.data(color.range().map(function(d) {
d = color.invertExtent(d);
if (d[0] == null) d[0] = keyScale.domain()[0];
if (d[1] == null) d[1] = keyScale.domain()[1];
return d;
}))
.enter().append("rect");
if (orientation === "h") {
rects.attr("height", 8)
.attr("x", function(d) { return keyScale(d[0]); })
.attr("width", function(d) { return keyScale(d[1]) - keyScale(d[0]); })
.attr("fill", function(d) { return color(d[0]); });
} else {
rects.attr("width", 8)
.attr("y", function(d) { return keyScale(d[0]); })
.attr("height", function(d) { return keyScale(d[1]) - keyScale(d[0]); })
.attr("fill", function(d) { return color(d[0]); });
}
var ti_x = (keyScale.range()[0]+keyScale.range()[1])/2;
var ti_y = -6;
if (orientation === "v") {var temp = ti_x; ti_x = ti_y; ti_y = temp;}
g.append("text")
.attr("class", "caption")
.attr("fill", "#000")
.attr("x", ti_x)
.attr("y", ti_y)
.attr("text-anchor", "middle")
.attr("font-weight", "bold")
.text(title);
var ax = orientation === "h" ? d3.axisBottom(keyScale) : d3.axisRight(keyScale);
g.call(ax
.tickSize(13)
.ticks(color.domain().length,"s")
.tickValues(color.domain()));
}
// Makes a histogram.
// Args ---
// svg: the svg to make the histogram in
// data: the data to use
// xRange: the range for the histogram bins
// numBins: number of bins to use
// fillFunc: function, transforms from a datapoint to the fillcolor. Usually just a constant function.
// histFunc: function, transforms from an entry of data to the derived quantity to be histogrammed
// filterFunc: filters out data satisfying a specified criterion. Returns a boolean
// xLoc,yLoc,w,h: x location, y location, width, height of plot within svg
// title: title of svg
// xLabel: x axis title
// yLabel: y axis title
// opacity: opacity of the plot
// titleFontSize: the font size of the title
function makeHist(svg, data, xRange, numBins, fillFunc, histFunc, filterFunc, xLoc, yLoc, w, h, title, xLabel, yLabel, opacity=1.0, titleFontSize="10pt") {
// xRange = [-100,100]
var btBarMar = 0.15; //proportion of bar width
var aroundMar = 0.1; //proportion of plot height (for axes and title)
var binStep = (xRange[1]-xRange[0])/numBins;
var bins = [];
for (var i = 0; i < numBins; i++) {
bins.push(0);
}
if (histFunc == null) {
histFunc = function(d) {return d;};
}
// Increment each corresponding bin when you find a datapt in that range
for (var i = 0; i < data.length; i++) {
if (!filterFunc(data[i])) {
continue;
}
var binIdx = Math.floor((histFunc(data[i])-xRange[0])/binStep);
if (binIdx >= numBins) {
binIdx = numBins-1;
}
bins[binIdx] ++;
}
// Find the max bin size, to calculate the y-axis maximum.
var maxBinSize = bins[0];
var maxBinIdx = 0;
for (var i = 1; i < bins.length; i++) {
if (bins[i] > maxBinSize) {
maxBinSize = bins[i];
maxBinIdx = i;
}
}
// Then, find its order of magnitude, i.e. 25 would return 1
var oom = Math.floor(Math.log10(maxBinSize));
// Calculate y-axis max as the next multiple of 10^oom.
// i.e. if max bin size is 25, oom = 1 --> axis max is 30.
var maxH = Math.ceil(maxBinSize/Math.pow(10,oom))*Math.pow(10,oom);
//console.log("MAXH " + maxH);
var x = d3.scaleLinear()
.domain([0,bins.length])
.range([xLoc+aroundMar*w,xLoc+w-aroundMar*w]);
var y = d3.scaleLinear()
.domain([0,maxH])
.range([yLoc+h-aroundMar*h,yLoc+aroundMar*h]);
var group = svg.append("g");
if (title != null) {
// Add title
group.append("text")
.text(title)
.attr("x", xLoc+w/2)
.attr("y", yLoc+aroundMar*h*(0.7))
.attr("text-anchor", "middle")
.style("font-size", titleFontSize);
}
// Add rects, and bind to bins.
var rects = group.selectAll("rect")
.data(bins);
rects.enter()
.append("rect")
.attr("x", function(d,i) {return x(i)+btBarMar*w*(1-2*aroundMar)/numBins/2;})
.attr("y", function(d) {return y(d);})
.attr("width", w/numBins * (1-2*btBarMar))
.attr("height", function(d) {return yLoc+(h*(1-aroundMar))-y(d);})
.style("fill", function(d,i) {return fillFunc(i);})
.style("opacity", opacity);
// Make axes
if (xLabel != null) {
var realX = d3.scaleLinear()
.domain([xRange[0],xRange[1]])
.range([xLoc+aroundMar*w,xLoc+w-aroundMar*w]);
svg.append("g")
.attr("transform", `translate(0,${h*(1-aroundMar)+yLoc})`)
.call(d3.axisBottom(realX).ticks(numBins/3,"s"));
svg.append("text")
.attr("text-anchor", "middle")
.attr("transform", "translate("+ (xLoc+aroundMar*w + (w-w*aroundMar*2)/2 ) +","+(h - 1/2*h*aroundMar+yLoc+15)+")")
.text(xLabel);
}
if (yLabel != null) {
svg.append("g")
.attr("transform", `translate(${w*aroundMar+xLoc},0)`)
.call(d3.axisLeft(y).ticks(Math.min(maxBinSize,4)));
svg.append("text")
.attr("text-anchor", "middle")
.attr("transform", "translate("+ (xLoc+aroundMar*w/2-15) +","+(yLoc+aroundMar*h+(h-2*aroundMar*h)/2)+")rotate(-90)")
.text(yLabel);
}
}
// Makes a histogram that fills the full SVG. Just eliminates some parameters from makeHist.
function makeFullHist(svg, data, xRange, numBins, fillFunc, histFunc, filterFunc, title, xLabel, yLabel, opacity=1.0, titleFontSize="10pt") {
makeHist(svg, data, xRange, numBins, fillFunc, histFunc, filterFunc, 0, 0, 900, 600, title, xLabel, yLabel, opacity, titleFontSize);
}
// Load in the LCV data
d3.csv(urls.scorecard, function(e3,scorecard) {
var scorecardColorScale = d3.scaleThreshold()
.domain([0,10,20,30,40,50,60,70,80,90,100])
.range(d3.schemeRdYlGn[11]);
function scorecardColorFunc(d) {
var s = parseInt(d.score_2016);
return scorecardColorScale(s);
}
function scorecardTooltip(tooltip,d) {
var header = d.rep + " " + "<span class='icon icon" + d.party + "'>" + d.party + "</span>";
var content = ["District: " + d.district,
"2016 score: " + d.score_2016,
"Lifetime score: " + d.score_lifetime];
return makeTooltip(tooltip,header,content);
}
var map1 = makeMap(d3.select("#scorecard-map"), scorecard, scorecardColorFunc, scorecardTooltip);
makeKey(map1,scorecardColorScale,600,20,260,"h","Percentage Pro-environment Votes");
var scorecardHistSvg = appendSvg(d3.select("#scorecard-hist"));
makeFullHist(scorecardHistSvg,
scorecard,
[0,100],
20,
function(i) {return "red";},
function(d) {return parseInt(d.score_2016);},
function(d) {return d.party === "R";},
"Climate Scorecard of Members of Congress by Party",
"2016 score",
"Number of congressmen",
0.4);
makeFullHist(scorecardHistSvg,
scorecard,
[0,100],
20,
function(i) {return "blue";},
function(d) {return parseInt(d.score_2016);},
function(d) {return d.party === "D";},
null,
null,
null,
0.4);
d3.csv(urls.opinion, function(error,opinion) {
var opinionColorScale = d3.scaleThreshold()
.domain([0,10,20,30,40,50,60,70,80,90,100])
.range(d3.schemeRdYlGn[11]);
function opinionColorFunc(cat) {
var colorFunc = function(d) {
var s = d[cat];
return opinionColorScale(s);
}
return colorFunc;
}
var categories = {"happening": "Global warming is happening",
"human": "Global warming is caused mostly by human activities",
"consensus": "Most scientists think global warming is happening",
"worried": "Worried about global warming",
"personal": "Global warming will harm me personally",
"harmUS": "Global warming will harm people in the US",
"devharm": "Global warming will harm developing countries",
"futuregen": "Global warming will harm future generations",
"timing": "Global warming is already harming people in the US",
"taxdividend": "A carbon tax if refunded to every American household",
"CO2limits": "Set strict CO2 limits on existing coal-fired power plants",
"regulate": "Regulate CO2 as a pollutant",
"supportRPS": "Require utilities to produce 20% electricity from renewable sources",
"fundrenewables": "Fund research into renewable energy sources"};
// Calculate the averages for each category so as to only to it once.
var averages = {};
function calcAvg(cat) {
var total = 0;
var count = 0;
for (d of opinion) {
total += parseInt(d[cat]);
count ++;
}
console.log(total + ", " + count);
return total/count;
}
for (var cat in categories) {
averages[cat] = calcAvg(cat);
}
function opinionTooltip(cat) {
var opinionTooltip = function(tooltip,d) {
var statename = d.Statename;
var dist;
if (["Alaska","Delaware","Montana","North Dakota","South Dakota","Vermont","Wyoming"].includes(statename)) {
dist = "At Large";
} else {
dist = (""+d.cd113code);
dist = dist.substring(dist.length-2);
dist = parseInt(dist);
var ld = dist%10;
var ext = "th";
if (ld == 1) {
ext = "st";
} else if (ld == 2) {
ext = "nd";
} else if (ld == 3) {
ext = "rd";
}
dist = dist + ext + " District";
}
var header = statename + " " + dist;
var distToAvg = Math.floor(d[cat]-averages[cat]) + "";
if (distToAvg > 0) distToAvg = "+"+distToAvg;
var content = [categories[cat] + ": " + d[cat],
"(" + distToAvg + " from national avg.)"];
return makeTooltip(tooltip,header,content);
}
return opinionTooltip;
}
// Makes the opinion map, complete with the selects that manipulate it.
function makeOpinionMap(mapParentElt,optionsParentElt) {
var opMap = makeMap(mapParentElt, opinion, opinionColorFunc("happening"),opinionTooltip("happening"));
makeKey(opMap,opinionColorScale,600,20,260,"h","Percentage that Believes that: " + categories["happening"]);
function switchOpinionMap(cat) {
opMap.selectAll("path.c0").style("fill", opinionColorFunc(cat));
changeTooltipFunc(opMap,opinionTooltip(cat),mapParentElt.select(".tooltip"));
opMap.select(".caption").html("Percentage that Believes that: " + categories[cat]);
}
var opSel = optionsParentElt.append("select");
for (cat in categories) {
opSel.append("option").attr("value", cat).html(categories[cat]);
}
opSel.on("change", function() {
switchOpinionMap(opSel.property("value"));
});
}
var map2 = makeOpinionMap(d3.select("#opinion-map"), d3.select("#opinion-options"));
// Load in the industry data
d3.csv(urls.lobbying, function(error,lobbying) {
// Makes a scatter plot.
// Args---
// data: data to use
// xFunc: function, transforms from an entry of data to the value used for x
// yFunc: function, transforms from an entry of data to the value used for y
// parentElt: parentElement to attach svg to
// colorFunc: function, transforms from an entry of data to the fill color
// tooltipFunc: function, takes tooltip, d as args to populate tooltip based on d
// xLabel, yLabel: axis labels for x and y axes
function plot(data,xFunc,yFunc, parentElt, colorFunc, tooltipFunc, xLabel, yLabel, svgDims=defaultSvgDims) {
var tooltip = parentElt.append("div")
.style("visibility","hidden")
.style("position","absolute")
.classed("tooltip", true);
var svg = parentElt.append("svg")
.attr("width", svgDims.width)
.attr("height", svgDims.height)
.style("margin", "0 auto")
.style("display", "block")
.style("overflow", "visible");
var x = d3.scaleLinear()
.domain(d3.extent(data, function(d) {return xFunc(d);}))
.range([svgDims.margin.left,svgDims.width-svgDims.margin.right]);
var y = d3.scaleLinear()
.domain(d3.extent(data, function(d) {return yFunc(d);}))
.range([svgDims.height-svgDims.margin.bottom,svgDims.margin.top]);
var circles = svg.selectAll("circle");
circles.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {return x(xFunc(d));})
.attr("cy", function(d) {return y(yFunc(d));})
.attr("r", 6)
.attr("fill", function(d) {return colorFunc(d);})
.attr("opacity", 0.6)
.on("mouseover", function(d) {
d3.select(this).style("cursor","pointer");
tooltip = tooltipFunc(tooltip,d);
var loc = d3.mouse(this);
tooltip
.style("left", Math.floor(loc[0]-200/2)+"px")
.style("top", loc[1]+15+"px")
.style("visibility","visible");
})
.on("mouseout", function(d) {tooltip.style("visibility","hidden");tooltip.html("");});
// Make axes
svg.append("g")
.attr("transform", `translate(0,${svgDims.height-svgDims.margin.bottom})`)
.call(d3.axisBottom(x));
svg.append("g")
.attr("transform", `translate(${svgDims.margin.left},0)`)
.call(d3.axisLeft(y));
// Title axes
svg.append("text")
.attr("text-anchor", "middle")
.attr("transform", "translate("+ (svgDims.margin.left/2) +","+(svgDims.margin.top + (svgDims.height-svgDims.margin.top-svgDims.margin.bottom)/2)+")rotate(-90)")
.text(yLabel);
svg.append("text")
.attr("text-anchor", "middle")
.attr("transform", "translate("+ ((svgDims.margin.left+(svgDims.width-svgDims.margin.left-svgDims.margin.right)/2)) +","+(svgDims.height-svgDims.margin.bottom/2)+")")
.text(xLabel);
}
// Implements essentially the same as Python's zip function. Used for using the plot function,
// which only takes one data argument. Thus you must zip together all your data.
function zip(arrays) {
return arrays[0].map(function(_,i){
return arrays.map(function(array){return array[i]})
});
}
var l_s = zip([lobbying,scorecard]);
function plotLobbyingVsScorecard(cat) {
plot(l_s, function(d) {return parseInt(d[0][cat]);}, function(d) {return parseInt(d[1].score_2016);}, d3.select("#lobbying-vs-scorecard"), function(d) {return d[0].party === "R" ? "red" : "blue";}, function(d) {}, "Money received ($)", "2016 score");
}
plotLobbyingVsScorecard("oil_gas");
function addOptionsToLVsS() {
var sel = d3.select("#lobbying-vs-scorecard-options").append("select");
for (cat of ["oil_gas","pipelines","coal"]) {
sel.append("option").attr("value",cat).html(cat);
}
sel.on("change", function() {
d3.select("#lobbying-vs-scorecard").select("svg").remove();
var newCat = sel.property("value");
plotLobbyingVsScorecard(newCat);
});
}
addOptionsToLVsS();
function subDivide(dom, n) {
var x0 = dom[0];
var x1 = dom[1];
var step = (x1-x0)/n;
var sd = [x0];
for (var i = 0; i < n-1; i++) {
sd.push(sd[i]+step);
}
sd.push(x1);
return sd;
}
console.log(subDivide([0,10],10));
function lobbyingColorScale(cat) {
var lobbyingColorScale = d3.scaleQuantile()
.domain([0,10000,20000,30000,50000,75000,100000,200000,350000])
.range(d3.schemePurples[8]);
return lobbyingColorScale;
}
function lobbyingColorFunc(cat) {
var colorFunc = function(d) {
var s = d[cat];
return lobbyingColorScale(cat)(s);
}
return colorFunc;
}
function moneyTooltip(cat) {
var moneyTooltip = function(tooltip,d) {
var header = d.rep + " " + "<span class='icon icon" + d.party + "'>" + d.party + "</span>";
var content = ["District: " + d.district,
"Amount Recieved ($): " + d[cat]];
return makeTooltip(tooltip,header,content);
}
return moneyTooltip;
}
// Makes the map of money received
function makeMoneyMap(mapParentElt,optionsParentElt) {
var monMap = makeMap(mapParentElt, lobbying, lobbyingColorFunc("oil_gas"),moneyTooltip("oil_gas"));
makeKey(monMap,lobbyingColorScale("oil_gas"),600,20,260,"h","Amount received: oil_gas");
function switchMoneyMap(cat) {
monMap.selectAll("path.c0").style("fill", lobbyingColorFunc(cat));
changeTooltipFunc(monMap,moneyTooltip(cat),mapParentElt.select(".tooltip"));
monMap.select(".caption").html("Amount received: " + cat);
}
var monSel = optionsParentElt.append("select");
for (var cat of ["oil_gas","pipelines","coal"]) {
monSel.append("option").attr("value",cat).html(cat);
}
monSel.on("change", function() {
switchMoneyMap(monSel.property("value"));
});
}
makeMoneyMap(d3.select("#money-map"),d3.select("#money-map-options"));
// Makes the six-histogram composite of money received be industry and party
function makeHexHist() {
function makeTwoPartyHist(svg,xPos,yPos,w,h,cat) {
var xRange = d3.extent(lobbying,function(d) {return parseInt(d[cat]);});
console.log(xRange);
makeHist(svg,lobbying,xRange,20,function(i) {return "red";},function(d) {return parseInt(d[cat]);}, function(d) {return d.party === "R";},xPos,yPos,w,h/2,cat,"Money received: " + cat + " ($)", "Num. Rep. Congressmen");
makeHist(svg,lobbying,xRange,20,function(i) {return "blue";},function(d) {return parseInt(d[cat]);}, function(d) {return d.party === "D";},xPos,yPos+h/2,w,h/2,null,"Money received: " + cat + " ($)", "Num. Dem. Congressmen");
}
var svg = appendSvg(d3.select("#lobbying-hist"));
var x = 0;
for (cat of ["oil_gas","pipelines","coal"]) {
makeTwoPartyHist(svg,x,0,300,600,cat);
x += 300;
}
}
makeHexHist();
var l_o_s = zip([lobbying,opinion,scorecard]);
// Plots the gap in LCV score and opinion vs money in, for specified categories.
function plotGap(lobbyingCat, opinionCat) {
function gapTooltip(tooltip, d) {
var header = "District: " + d[0]["district"];
var content = [];
content.push("Rep: " + d[0].rep + " " + "<span class='icon icon" + d[0].party + "'>" + d[0].party + "</span>");
content.push("Constituent opinion: " + d[1][opinionCat] + "%");
content.push("Voting record: " + d[2]["score_2016"]);
content.push("Money received: " + "$"+ d[0][lobbyingCat])
return makeTooltip(tooltip,header,content);
}
plot(l_o_s,function(d) {return parseInt(d[0][lobbyingCat]);},function(d) {return parseInt(d[2]["score_2016"]) - parseInt(d[1][opinionCat]);}, d3.select("#money-gap-plot"), function(d) {return "purple";}, gapTooltip, "Money received: " + lobbyingCat + " ($)", "Difference between voting score and constituent positions");
}
plotGap("oil_gas","human");
function addOptionsToGapPlot() {
var div = d3.select("#money-gap-plot-options");
div.html("Select industry: ")
var selLob = div.append("select");
for (var cat of ["oil_gas","pipelines","coal"]) {
selLob.append("option").attr("value",cat).html(cat);
}
var t = document.createTextNode(" Select survey question: ");
document.getElementById("money-gap-plot-options").appendChild(t);
var selOp = div.append("select");
for (var cat in categories) {
selOp.append("option").attr("value",cat).html(categories[cat]);
}
selLob.on("change", function() {
d3.select("#money-gap-plot").select("svg").remove();
plotGap(selLob.property("value"),selOp.property("value"));
});
selOp.on("change", function() {
d3.select("#money-gap-plot").select("svg").remove();
plotGap(selLob.property("value"),selOp.property("value"));
});
}
addOptionsToGapPlot();
});
});
});
});
});
</script>
</body>
</html>