Skip to content

Instantly share code, notes, and snippets.

@renecnielsen
Last active August 29, 2015 13:57
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 renecnielsen/9771664 to your computer and use it in GitHub Desktop.
Save renecnielsen/9771664 to your computer and use it in GitHub Desktop.
Slopegraph with tooltips
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Slopegraph</title>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.min.js"></script>
<link rel="stylesheet" href="style.css" type="text/css">
<link href='http://fonts.googleapis.com/css?family=Open+Sans:300' rel='stylesheet' type='text/css'>
<link href="http://fonts.googleapis.com/css?family=Libre+Baskerville:400,700" rel="stylesheet" type="text/css">
</head>
<body>
<div id="slopegraph"></div>
<script type="text/javascript">
//Based on http://thisiscave.co.uk/2013/08/04/d3-slope-graph.html
//Settings - width, height, margins
if ((window.innerHeight > 1000) & (window.innerWidth > 1600))
{
var WIDTH = 950,
HEIGHT = 0.85 * window.innerHeight,
MARGINS = [83, 0, 10, 225], //Top, right, bottom, left
AXISDISTANCE = 500; //Distance between the two y axes
}
else if (window.innerWidth > 1600)
{
var WIDTH = 950,
HEIGHT = 900,
MARGINS = [83, 0, 10, 225], //Top, right, bottom, left
AXISDISTANCE = 500; //Distance between the two y axes
}
else if ((window.innerWidth < window.innerHeight) & (window.innerWidth < 1025))
{
var WIDTH = 550,
HEIGHT = 800,
MARGINS = [83, 0, 10, 150], //Top, right, bottom, left
AXISDISTANCE = 250; //Distance between the two y axes
}
else if (window.innerWidth < 1025)
{
var WIDTH = 650,
HEIGHT = 800,
MARGINS = [83, 0, 10, 180], //Top, right, bottom, left
AXISDISTANCE = 300; //Distance between the two y axes
}
else
{
var WIDTH = 770,
HEIGHT = 1000,
MARGINS = [83, 0, 10, 210], //Top, right, bottom, left
AXISDISTANCE = 350; //Distance between the two y axes
}
//var WIDTH = 850,
// HEIGHT = 800,
// MARGINS = [83, 0, 10, 225], //Top, right, bottom, left
// AXISDISTANCE = 400; //Distance between the two y axes
// Real size for the chart
var CHARTWIDTH = WIDTH - MARGINS[1] - MARGINS[3],
CHARTHEIGHT = HEIGHT - MARGINS[0] - MARGINS[2];
//The svg element
var svg = d3.select("body").append("svg")
.attr({
"height": HEIGHT,
"width": WIDTH
});
//Visualisation group/container
var vis = svg.append("g")
.attr({
"id": "slopegraph",
"class": "col-md-8",
"transform": "translate(" + MARGINS[3] + "," + MARGINS[0] + ")"
});
//Popup
var tip = d3.tip()
.attr('id', "d3-tip")
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<h4>" + d.topic + "</h4>" + "The World: " + d.left + "%" + "<br/>" + "Women's Day & CSW: " + d.right + "%" + "<br/>" + "Difference: " + d3.round(d.right - d.left,2);
});
function fixedHide() {
tip.hide();
tip.attr("style", "position: absolute; opacity: 0; top: 0px; left: 0px;");
}
//Load the data
d3.csv("iwd.csv", function(d) {
//Tidy up the data depending on input csv
dataSlopeGraph = d.map(function(d) {
return {
"fill": d.Colour,
"left": +d["Global"],
"right": +d["IWD"],
"topic": d.Topic
};
//And sort it descending using the value on the right
}).sort(function(a, b) {
return a.right - b.right;
});
//The data
var dataSlopeGraph,
minSlopeValue = 0,
//maxSlopeValue = 48;
maxLeftSlopeValue = d3.max(d, function(d) { return +d["Global"];} ),
maxRightSlopeValue = d3.max(d, function(d) { return +d["IWD"];} );
if (maxLeftSlopeValue > maxRightSlopeValue) { var maxSlopeValue = (maxLeftSlopeValue + 1);}
else { var maxSlopeValue = (maxRightSlopeValue + 1);}
//Find max values of each side
//Y scale used for the two axes
var yScale = d3.scale.linear()
.range([CHARTHEIGHT, 0])
.domain([minSlopeValue, maxSlopeValue]);
//Left axis
var yAxisLeft = d3.svg.axis()
.scale(yScale)
.ticks(4)
.tickFormat(function(d) {
return d3.format(",.0f")(d) + "%";
})
.orient("left"),
//Right axis
yAxisRight = d3.svg.axis()
.scale(yScale)
.ticks(0)
.orient("left");
//Add the axis and gridlines
var tmpYAxis = vis.append("g")
.attr("class", "axis y")
.call(yAxisLeft);
//Gridlines
tmpYAxis.selectAll(".tick line")
.each(function(d) {
//Loop through all the ticks and extend them
//across the axis distance
var line = d3.select(this);
line.attr({
"x1": line.attr("x2") - -35,
"x2": AXISDISTANCE
});
});
//Move the tick labels in between the axes
tmpYAxis.selectAll(".tick text")
.attr("dx", "35px");
//Left axis title
tmpYAxis.append("text")
.text("All Post-2015 Tweets")
.style("text-anchor", "middle")
.attr({
"class": "title",
"dy": "-20px"
});
tmpYAxis.append("text")
.text("All tweets in the world about Post-2015")
.style("text-anchor", "middle")
.attr({
"class": "subtitle",
"dy": "-5px"
});
//Right axis, almost the same just without a gridline loop
tmpYAxis = vis.append("g")
.attr({
"class": "axis y",
"transform": "translate(" + AXISDISTANCE + ",0)"
})
.call(yAxisRight);
tmpYAxis.append("text")
.text("IWD & CSW")
.style("text-anchor", "middle")
.attr({
"class": "title",
"dy": "-20px"
});
tmpYAxis.append("text")
.text("Tweets also about International Women's Day or CSW")
.style("text-anchor", "middle")
.attr({
"class": "subtitle",
"dy": "-5px"
});
//This will return a line pathstring based on some data. The data
//that I pass in is formatted like so:
// [ [0,y-value], [AXISDISTANCE,y-value] ]
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) {
//Use the exact x value
return d[0];
})
.y(function(d) {
//Return a value based on the y scale created
return yScale(d[1]);
});
//Plot the actual data
//Create a group to store each slope. I also apply a mouseover/leave
//listener to it.
var topicData = vis.selectAll(".topic-data")
.data(dataSlopeGraph).enter()
.append("g")
.attr("class", "topic-data")
.on("mouseover", mouseover)
.on("mouseleave", mouseleave);
//Left circle
topicData.append("circle")
.attr({
"cy": function(d) {
return yScale(d.left);
},
"fill": function(d) {
return d.fill;
},
"r": 6
})
.style("opacity", "0.5");
//Right circle
topicData.append("circle")
.attr({
"cx": AXISDISTANCE,
"cy": function(d) {
return yScale(d.right);
},
"fill": function(d) {
return d.fill;
},
"r": 6
})
.style("opacity", "0.5");
//Text label
topicData.append("text")
.attr({
"class": "legend-item",
"dy": "0.3em",
"text-anchor": "end",
"x": -15,
"y": function(d) {
return yScale(d.left);
}
})
.text(function(d) {
return d.topic
})
.call(tip);
topicData.append("text")
.attr({
"class": "legend-item",
"dy": "0.3em",
"text-anchor": "start",
"x": AXISDISTANCE + 15,
"y": function(d) {
return yScale(d.right);
}
})
.text(function(d) {
return d.topic
})
.call(tip);
//Slope line
topicData.append("path")
.attr({
"class": "link",
"d": function(d) {
return line([
[0, d.left],
[AXISDISTANCE, d.right]
]);
}
})
.style("stroke", function(d) {
return d.fill;
})
.style("opacity", "0.5");
function mouseover(d, i) {
var topic = d3.select(topicData[0][i]);
topic.selectAll("circle").attr("r", 8);
topic.select(".link").style("stroke-width", "5px");
topic.selectAll("text").attr("font-weight", "700");
topic.selectAll("text").style("fill", "#000");
topic.selectAll("circle").on("mouseover",tip.show);
topic.selectAll("text").on("mouseover",tip.show);
topic.selectAll("circle").style("opacity", "1");
topic.select(".link").style("opacity", "1");
}
function mouseleave(d, i) {
var topic = d3.select(topicData[0][i]);
topic.selectAll("circle").attr("r", 6);
topic.select(".link").style("stroke-width", "");
topic.selectAll("text").attr("font-weight", "normal");
topic.selectAll("text").style("fill", "#96999b");
topic.selectAll("circle").on("mouseleave", fixedHide);
topic.selectAll("text").on("mouseleave", fixedHide);
topic.selectAll("circle").style("opacity", "0.5");
topic.select(".link").style("opacity", "0.5");
}
});
</script>
</body>
</html>
Topic IWD Global Colour
Political freedoms 1.40 6.51 #723390
Action taken on climate change 0.44 4.33 #fcb749
Equality between men and women 52.02 6.68 #c84699
An honest and responsive government 0.92 19.60 #2da9e1
Affordable and nutritious food 0.55 2.02 #b0d256
Freedom from discrimination 9.79 8.56 #dec0ca
Reliable energy at home 0.54 3.46 #a01c40
Protecting forests rivers and oceans 0.58 5.58 #71be45
Protection against crime and violence 21.35 3.76 #387195
Access to clean water and sanitation 0.37 2.32 #97d3c9
Phone and internet access 0.06 5.43 #7cb5d6
Better job opportunities 2.93 12.71 #e8168b
Better healthcare 0.16 2.56 #ca3a28
A good education 8.40 11.63 #47c0be
Support for people who can't work 0.24 1.01 #233884
Better transport and roads 0.24 3.84 #fbe792
html, body {
font-family: "Open Sans", serif;
font-size: 12px;
}
h1 {
text-align: center;
font-weight: 700;
font-size: 1.2em;
}
h2 {
font-weight: 400;
text-align: center;
}
h3 {
font-weight: 400;
}
.navbar-nav {
margin: 0 auto;
display: table;
table-layout: fixed;
float:none;
}
svg {
font-family: "Open Sans", serif;
}
line {
shape-rendering: crispEdges;
}
.title-main {
font-family: 'Open Sans', sans-serif;
font-size: 0.9em;
fill: #5d6263;
}
.axis {}
.axis .domain {
fill: none;
stroke: #96999b;
stroke-width: 1px;
}
.axis .tick {}
.axis .tick line {
stroke: #96999b;
stroke-dasharray: 5, 3;
stroke-width: 1px;
}
.axis .tick text {
fill: #96999b;
font-family: 'Open Sans', sans-serif;
font-size: 0.8em;
}
.axis .title {
font-family: "Libre Baskerville", serif;
font-size: 2.1em;
}
.axis .subtitle {
fill: #5d6263;
font-size: 0.9em;
}
.topic-data {}
.topic-data .link {
stroke-width: 3px;
}
.legend-item {
cursor: default;
}
.legend-item {
fill: #96999b;
font-size: 0.8em;
}
.d3-tip {
position: absolute;
line-height: 1;
font-weight: 200;
padding: 12px;
background: rgba(0,0,0,0.75);
color: #fff;
border-radius: 2px;
z-index: 100;
}
.d3-tip h4 {
font-family: "Libre Baskerville", serif;
font-size: 1.3em;
font-weight: 400;
margin-top: 5px;
margin-bottom: 5px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
width: 100%;
line-height: 0.75;
color: rgba(0, 0, 0,0.75);
content: "\25BC";
position: absolute;
text-align: center;
z-index: 100;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
/*height: 700px;*/
z-index: 100;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment