block by renecnielsen 9771664

Slopegraph with tooltips

Full Screen

Slopegraph based on Dan Palmer’s Slope Graph and tooltips from D3-tip.

Used for a UN Global Pulse project for International Women’s Day & CSW 2014

index.html

<!DOCTYPE html>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
		<title>Slopegraph</title>
		<script type="text/javascript" src="//d3js.org/d3.v3.min.js"></script>
		<script src="//labratrevenge.com/d3-tip/javascripts/d3.tip.min.js"></script>
		<link rel="stylesheet" href="style.css" type="text/css">
		<link href='//fonts.googleapis.com/css?family=Open+Sans:300' rel='stylesheet' type='text/css'>
		<link href="//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 //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>

iwd.csv

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

style.css

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;
}