Skip to content

Instantly share code, notes, and snippets.

@bbest
Last active January 7, 2020 14:47
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save bbest/2de0e25d4840c68f2db1 to your computer and use it in GitHub Desktop.
Save bbest/2de0e25d4840c68f2db1 to your computer and use it in GitHub Desktop.
Aster Plot in D3
license: mit
height: 300
scrolling: no
border: no

This aster plot displays pie slices as lengths extending outward to the edge (0 at inner to 100 at outer). Widths of the pie slices represent the weight of each pie, which gets used to arrive at a weighted mean of the length scores in the center.

Contributors

Jim Regetz @regetz developed the initial aster plot function in R (see aster-plot on github)

Parker Abercrombie @parkerabercrombie developed the initial D3 prototype varying 3 of the 4 arc elements starting with Mike Bostock's Donut Chart:

  • outerRadius
  • startAngle
  • endAngle

This is the only example pie chart I've encountered on the web (after much searching) which varied individual arc segments by all 3 (usually just startAngle and endAngle with fixed innerRadius and outerRadius, even if done with multiple rows as with the very cool Zoomable Sunburst). The outline of the pie chart is generated in addition to the outerRadius-varying segments. For more details, see the d3 Pie Layout documentation.

Tooltips on hover are rendered via the d3-tip library.

This effort was made possible through the NCEAS Open Science CodeFest Sep 2-4, 2014 in Santa Barbara, CA.

Ben Best @bbest adapted Parker's prototype to CSV data from the Ocean Health Index. For more details on the data prep, see ohi-aster on github.

id order score weight color label
FIS 1.1 59 0.5 #9E0041 Fisheries
MAR 1.3 24 0.5 #C32F4B Mariculture
AO 2 98 1 #E1514B Artisanal Fishing Opportunities
NP 3 60 1 #F47245 Natural Products
CS 4 74 1 #FB9F59 Carbon Storage
CP 5 70 1 #FEC574 Coastal Protection
TR 6 42 1 #FAE38C Tourism & Recreation
LIV 7.1 77 0.5 #EAF195 Livelihoods
ECO 7.3 88 0.5 #C7E89E Economies
ICO 8.1 60 0.5 #9CD6A4 Iconic Species
LSP 8.3 65 0.5 #6CC4A4 Lasting Special Places
CW 9 71 1 #4D9DB4 Clean Waters
HAB 10.1 88 0.5 #4776B4 Habitats
SPP 10.3 83 0.5 #5E4EA1 Species
var width = 500,
height = 500,
radius = Math.min(width, height) / 2,
innerRadius = 0.3 * radius;
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.width; });
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([0, 0])
.html(function(d) {
return d.data.label + ": <span style='color:orangered'>" + d.data.score + "</span>";
});
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(function (d) {
return (radius - innerRadius) * (d.data.score / 100.0) + innerRadius;
});
var outlineArc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(radius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.call(tip);
d3.csv('aster_data.csv', function(error, data) {
data.forEach(function(d) {
d.id = d.id;
d.order = +d.order;
d.color = d.color;
d.weight = +d.weight;
d.score = +d.score;
d.width = +d.weight;
d.label = d.label;
});
// for (var i = 0; i < data.score; i++) { console.log(data[i].id) }
var path = svg.selectAll(".solidArc")
.data(pie(data))
.enter().append("path")
.attr("fill", function(d) { return d.data.color; })
.attr("class", "solidArc")
.attr("stroke", "gray")
.attr("d", arc)
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
var outerPath = svg.selectAll(".outlineArc")
.data(pie(data))
.enter().append("path")
.attr("fill", "none")
.attr("stroke", "gray")
.attr("class", "outlineArc")
.attr("d", outlineArc);
// calculate the weighted mean score
var score =
data.reduce(function(a, b) {
//console.log('a:' + a + ', b.score: ' + b.score + ', b.weight: ' + b.weight);
return a + (b.score * b.weight);
}, 0) /
data.reduce(function(a, b) {
return a + b.weight;
}, 0);
svg.append("svg:text")
.attr("class", "aster-score")
.attr("dy", ".35em")
.attr("text-anchor", "middle") // text-align: right
.text(Math.round(score));
});
<!DOCTYPE html>
<meta charset="utf-8">
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<script src="draw.js" ></script>
</body>
</html>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: orange;
}
.solidArc:hover {
fill: orangered ;
}
.solidArc {
-moz-transition: all 0.3s;
-o-transition: all 0.3s;
-webkit-transition: all 0.3s;
transition: all 0.3s;
}
.x.axis path {
display: none;
}
.aster-score {
line-height: 1;
font-weight: bold;
font-size: 500%;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
@vwochnik
Copy link

I improved this version for a company I am working at. Check this out:
http://bl.ocks.org/vwochnik/401b8310515a50c495dd

@scottmcculloch
Copy link

scottmcculloch commented Mar 8, 2017

This is really close to what I'm looking for... I have a several questions (i.e., need some help) to determine if I can adapt this to our needs...

  • How would I reference the data in the script rather than in a separate .csv file (we'll be pulling data from a database)?

  • Is it possible to set the angles for the segments? (we will always have 3 segments of equal weight, and want to have one segment on "top" with each of the other two segments below and on either side -- when I test it 'as is' with 3 rows of data of equal weight, it's "upside down" from where I need it)

  • How would I get the svg to be generated within a specified div element? I've tried changing
    var svg = d3.select("body").append("svg")
    to
    var svg = d3.select("#graph").append("svg")
    and then adding a div id=graph to the html, but then nothing gets rendered at all.

  • Is it possible to have lines drawn around the circumference at set intervals? (e.g., in addition to the outerRadius circle being drawn, also draw at regular intervals from the center out - 10, 20, 30, 40, etc?)

Thanks,
Scott

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment