Skip to content

Instantly share code, notes, and snippets.

@espinielli
Last active April 12, 2016 11:40
Show Gist options
  • Save espinielli/9418ce8105c0192e788270fa6fc05d31 to your computer and use it in GitHub Desktop.
Save espinielli/9418ce8105c0192e788270fa6fc05d31 to your computer and use it in GitHub Desktop.
Flights by entity (Jan - Feb 2016 vs Jan - Feb 2015)

TODOs:

  • position labels for negative bars on the right side, see this SO post
  • highlight relevant underlying bar when hovering on y-axis label, see commented out stroke styling on bars

Data about percent change of flights movement by entity (Jan - Feb 2016 vs Jan - Feb 2015) from PRU website.

Inspired by Mike's Bar Chart with Negative Values.

entity 2015 2016 prc_change
EUROCONTROL Area (MS) 1333376 1385518 3.9
Albcontrol 21552 19650 -8.8
ANA LUX 9018 9493 5.3
ANS CR 91808 99779 8.7
ARMATS 6683 5492 -17.8
Austro Control 114847 117349 2.2
Avinor 90098 90533 0.5
Belgocontrol 79681 82409 3.4
BULATSA 88170 93705 6.3
Croatia Control 55789 55021 -1.4
DCAC Cyprus 42178 41855 -0.8
DFS 378040 394274 4.3
DHMI 165509 178179 7.7
DSNA 376003 390573 3.9
EANS 26921 28239 4.9
ENAIRE 214366 231088 7.8
ENAV 190847 196003 2.7
Finavia 34349 35423 3.1
HCAA 69351 70310 1.4
HungaroControl (EC) 87610 95373 8.9
IAA 72986 80821 10.7
LFV 100722 104412 3.7
LGS 33087 33391 0.9
LPS 55693 59219 6.3
LVNL 80953 85356 5.4
MATS 13504 14626 8.3
M-NAV 12824 12192 -4.9
MoldATSA 5327 4878 -8.4
MUAC 236549 249839 5.6
NATS (Continental) 308461 325319 5.5
NAV Portugal (Continental) 72650 76896 5.8
NAVIAIR 91615 95617 4.4
Oro Navigacija 30646 31556 3.0
PANSA 92611 97729 5.5
ROMATSA 78951 81343 3.0
SAKAERONAVIGATSIA 17592 17862 1.5
Skyguide 159267 164158 3.1
Slovenia Control 28547 29100 1.9
SMATSA 62689 65824 5.0
UkSATSE 25840 22401 -13.3
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar--positive {
fill: steelblue;
}
.bar--negative {
fill: darkorange;
}
.xlabel, .axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.tick line{
opacity: 0.15;
}
#tooltip {
position: absolute;
width: auto;
height: auto;
padding: 2px 2px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
-webkit-box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.4);
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.4);
pointer-events: none;
background-color: #bbb;
stroke-width: 0.5px;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0 0 0 0;
padding: 2px 2px;
font-family: sans-serif;
font-size: 11px;
}
.hidden{
display: none;
}
</style>
<body>
<div id="tooltip" class="hidden">
<p id="info"></p>
</div>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 30, bottom: 40, left: 30},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var formatPercent = d3.format("+,%");
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.ordinal()
.rangeRoundBands([0, height], 0.1);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.innerTickSize(-height)
.tickPadding(8)
.tickFormat(formatPercent);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(0)
.tickPadding(6);
var svg = d3.select("body").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 + ")");
var tooltip = d3.select("#tooltip").classed("hidden", true);
var info = d3.select("#info");
svg.on("mousemove", function () {
// update tooltip position
tooltip.style("top", (event.pageY + 8) + "px").style("left", (event.pageX + 8) + "px");
return true;
});
d3.tsv("ert_flt_ansp.tsv", type, function(error, data) {
x.domain(d3.extent(data, function(d) { return d.value; })).nice();
y.domain(data.map(function(d) { return d.name; }));
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", function(d) { return "bar bar--" + (d.value < 0 ? "negative" : "positive"); })
.attr("x", function(d) { return x(Math.min(0, d.value)); })
.attr("y", function(d) { return y(d.name); })
.attr("width", function(d) { return Math.abs(x(d.value) - x(0)); })
.attr("height", y.rangeBand())
.on("mouseover", function (d, i) {
// d3.select(this).style({'stroke': '#F00'});
// http://stackoverflow.com/questions/17917072/#answer-17917341
tooltip.classed("hidden", false);
info.text(d.name + ": " + d.prc_change + "%");
})
.on("mouseout", function () {
// this.style.stroke = "none";
tooltip.classed("hidden", true);
});
svg.append("text") // text label for the x axis
.attr("class", "xlabel")
.attr("x", width / 2 )
.attr("y", height + margin.bottom - 4)
.style("text-anchor", "bottom")
.text("% change (Jan - Feb 2016 vs Jan - Feb 2015)");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + x(0) + ",0)")
.call(yAxis);
// TODO: position y label on the right for negative values
svg.selectAll(".y.axis g.tick text")
.data(data)
// .attr("x", width / 2 )
.on("mouseover", function (d) {
if (d.value < 0) {
d3.select(this).style({"cursor": "default"});
tooltip.classed("hidden", false);
info.text(d.name + ": " + d.prc_change + "%");
}
})
.on("mouseout", function () {
tooltip.classed("hidden", true);
})
;
});
function type(d) {
d.value = +d.prc_change/100;
d.name = d.entity;
return d;
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment