Skip to content

Instantly share code, notes, and snippets.

@joshuarrrr
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joshuarrrr/056d40462dcd84901c63 to your computer and use it in GitHub Desktop.
Save joshuarrrr/056d40462dcd84901c63 to your computer and use it in GitHub Desktop.
Tracking FISA requests and National Security Letters
<figure class="xlrg FISAchart" id ="FISAchart">
<style>
.FISAchart {
font-family: "Theinhardt-Regular", "Helvetica Neue", Helvetica, Arial, sans-serif;
width: 620px;
}
.FISAchart h3 {
text-align: center;
margin: 0;
}
.FISAchart form.layout {
text-align: center;
margin-top: .5em;
}
.FISAchart form.layout input {
margin-left: 10px;
}
.FISAchart form.layout label {
margin-left: 5px;
}
.FISAchart .chart {
width: 100%;
}
.FISAchart .chart .axis path,
.FISAchart .chart .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.FISAchart .chart .y.axis .tick line {
stroke: lightgray;
}
.FISAchart .chart .x.axis path {
display: none;
}
.FISAchart .chart .x.axis .source {
text-decoration: underline;
font-size: .8em;
}
.FISAchart .chart .x.axis .source:hover {
fill: #03a6e3;
}
.FISAchart .chart .segments {
stroke: white;
z-index: 3;
}
.FISAchart .chart .hover-label {
font-size: .8em;
text-anchor: middle;
display: none;
}
.FISAchart .chart .selected .segments {
stroke: lightgray;
stroke-width: 5;
}
.FISAchart .chart .selected text, .chart .axis text {
display: block;
}
</style>
<h3>1 Jan&ndash;30 Jun, 2013</h3>
<div class="chart">
<img class="mobile-only" alt="" src="/img/internet-giants-disclose-fisa-surveillance-requests-for-customer-data---ieee-spectrum-(1)-1399586612539.png">
</div>
<form class="layout">
<label for="layout">View:</label>
<input type="radio" name="layout" value="min" id="min">
<label for="min">Minimum</label>
<input type="radio" name="layout" value="range" id="range" checked>
<label for="range">Range</label>
<input type="radio" name="layout" value="max" id="max">
<label for="max">Maximum</label>
</form>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var theData = [
{
"company":"Yahoo",
"source":"http://yahoo.tumblr.com/post/75496314481/more-transparency-for-u-s-national-security-requests",
"accounts": [
{"requestType": "FISA Requests (content)",
"affected" : 30999},
{"requestType": "FISA Requests (non-content)", "affected" : 999},
{"requestType": "National Security Letters", "affected" : 999}],
"increment":1000 },
{
"company":"Microsoft",
"source":"http://blogs.technet.com/b/microsoft_on_the_issues/archive/2014/02/03/providing-additional-transparency-on-us-government-requests-for-customer-data.aspx",
"accounts": [
{"requestType": "FISA Requests (content)", "affected":15999},
{"requestType": "FISA Requests (non-content)", "affected":999},
{"requestType": "National Security Letters", "affected":999}],
"increment":1000 },
{
"company":"Google",
"source":"http://googleblog.blogspot.com/2014/02/shedding-some-light-on-foreign.html",
"accounts": [
{"requestType": "FISA Requests (content)", "affected":9999},
{"requestType": "FISA Requests (non-content)", "affected":999},
{"requestType": "National Security Letters", "affected":999}],
"increment":1000 },
{
"company":"Facebook",
"source":"http://newsroom.fb.com/news/2014/02/facebook-releases-new-data-about-national-security-requests/",
"accounts": [
{"requestType": "FISA Requests (content)", "affected":5999},
{"requestType": "FISA Requests (non-content)", "affected":999},
{"requestType": "National Security Letters", "affected":999}],
"increment":1000 }
];
var chartSelection = d3.select(".chart");
var margin = {top: 20, right: 0, bottom: 40, left: 85},
width = parseInt(chartSelection.style("width")) - margin.left - margin.right,
height = 350 - margin.top - margin.bottom,
t;
chartSelection.html("");
var svgSelection = chartSelection.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
theData.forEach(function (d) {
var y0 = 0;
var y1 = 0;
d.accounts.forEach(function (request, index) {
request.y0 = y0;
request.y1 = y1;
request.increment = d.increment;
y1 += request.affected;
y0 += request.affected - (request.affected % d.increment);
});
});
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .15, .15)
.domain(theData.map(function (d) { return d.company; }));
var barWidth = x.rangeBand();
var barSpacing = (width - (barWidth * theData.length)) / (theData.length + 1);
var y = d3.scale.linear()
.domain([0,d3.max(theData, function (d) {
var total = d.accounts.reduce(function (a,b) {
return {"affected": (a.affected + b.affected)};
});
return total.affected;
})])
.range([height,0]);
var color = d3.scale.ordinal()
.range(["black","blue","red"])
.domain(theData[0].accounts.map(function (d) { return d.requestType; }));
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width,0);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(0,0)
.orient("bottom");
var yAxisGroup = svgSelection.append("g")
.attr("class", "y axis")
.call(yAxis);
yAxisGroup.selectAll(".tick text").text(function () { return d3.select(this).text().replace(","," "); });
yAxisGroup.append("text")
.attr("class", "y label")
.attr("text-anchor", "middle")
.attr("y", 0)
.attr("x", -0.5 * height)
.attr("dy", "-4.5em")
.attr("transform", "rotate(-90)")
.text("Users/accounts affected");
var xAxisGroup = svgSelection.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
xAxisGroup.selectAll(".tick").append("a")
.attr("xlink:href", function (d, i) { return theData[i].source; })
.attr("class","source")
.append("text")
.attr("text-anchor", "middle")
.attr("x",0)
.attr("y","2.2em")
.text("Source")
var bar = svgSelection.selectAll(".bar")
.data(theData)
.enter()
.append("g")
.attr("class","bar")
.attr("transform", function(d, i) {
return "translate(" + x(d.company) + ",0)";});
var path = bar.selectAll("path")
.data(function(d) { return d.accounts; })
.enter()
.append("path")
.attr("d", function(d) {
return makeSegments(d.affected, d.increment, d.y0, d.y1, "range");
})
.style("stroke-width", 1)
.style("fill", function(d) {
return color(d.requestType);})
.attr("class", function(d) {
return "segments " + d.requestType.split(" ").join("");
});
bar.append("text")
.attr("y", function (d) { return y(d.accounts[d.accounts.length-1].y1 + (2*d.increment)); })
.attr("x", barWidth / 2)
.attr("class", "hover-label")
.text(function (d) {
var addSep = d3.format(",");
var lastRange = d.accounts[d.accounts.length-1];
var min = lastRange.y0 + lastRange.affected - (lastRange.affected % d.increment);
var max = lastRange.y1 + lastRange.affected;
return addSep(min).replace(","," ") + "–" + addSep(max).replace(","," ");
});
bar
.on("mouseover", function(d) {
d3.select(this).classed("selected", true);})
.on("mouseout", function(d) {
d3.select(this).classed("selected", false);});
var legend = svgSelection.selectAll(".legend")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + (i * 20) + ")"; });
legend.append("rect")
.attr("x", (width / 2))
.attr("width", (width / 2))
.attr("height", 18)
.style("fill", "white");
legend.append("rect")
.attr("x", width - barSpacing - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - barSpacing - 18 - 6)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
d3.selectAll(".layout input")
.on("change", function () {
var layout = this.value;
path.transition().duration(1000)
.attr("d", function(d) {
return makeSegments(d.affected, d.increment, d.y0, d.y1, layout);
});
clearTimeout(t);
});
function makeSegments (records, increment, y0, y1, layout) {
var ydiff = y1-y0;
var path = "";
records = y0 + records;
while((y0 + increment) < records) {
path += "M 0 "+ y(y0) +" l "+ barWidth +" "+ -(height - y(ydiff)) +" l 0 "+ -(height - y(increment)) +" l "+ -barWidth +" "+ (height - y(ydiff)) +" z ";
y0 += increment;
}
if (layout === "min") {
path += "M 0 "+ y(y0) +" l "+ barWidth +" 0 l 0 0 l "+ -barWidth +" 0 z ";
}
if (layout === "range") {
path += "M 0 "+ y(y0) +" l "+ barWidth +" "+ -(height - y(ydiff)) +" l 0 "+ -(height - y(records % increment)) +" l "+ -barWidth +" "+ (height - y(ydiff + (records % increment))) +" z";
}
if (layout === "max"){
path += "M 0 "+ y(y0+ydiff) +" l "+ barWidth +" 0 l 0 "+ -(height - y(records % increment)) +" l "+ -barWidth +" 0 z ";
}
return path;
}
function timeout () {
t = setTimeout(function() {
var inputs = d3.selectAll(".layout input")[0];
var current, next;
inputs.forEach(function (input, i) {
if (input.checked === true) {
current = i;
}
});
if (current == inputs.length - 1) {
next = inputs[0];
}
else {
next = inputs[current + 1];
}
next.checked = true;
path.transition().duration(1500)
.attr("d", function(d) {
return makeSegments(d.affected, d.increment, d.y0, d.y1, next.value);
});
timeout();
}, 2500);
}
timeout();
</script>
</figure>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment