Zoomable radial chart with color scales and nesting. Based on Zoomable Sunburst.
forked from sathomas's block: Zoomable radial chart with color scales
license: mit |
Zoomable radial chart with color scales and nesting. Based on Zoomable Sunburst.
forked from sathomas's block: Zoomable radial chart with color scales
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset='utf-8'> | |
<title>Zoomable radial chart with color scales</title> | |
<link href='http://fonts.googleapis.com/css?family=Varela' rel='stylesheet' | |
type='text/css'> | |
<style> | |
body { font-family: Varela,sans-serif; } | |
</style> | |
</head> | |
<body> | |
<script src='http://d3js.org/d3.v3.min.js'></script> | |
<script> | |
d3.csv("https://gist.githubusercontent.com/sephcoster/c64c316b597895f0a644e4cbc5ed941c/raw/0be456471e8cbc17da626f134ac6536d9be8d00d/r1-2017-dod.json", function(data) { | |
buildChart(data); | |
}); | |
function buildChart(dataset){ | |
// Define the dimensions of the visualization. | |
var margin = {top: 30, right: 10, bottom: 20, left: 10}, | |
width = 636 - margin.left - margin.right, | |
height = 476 - margin.top - margin.bottom, | |
radius = Math.min(width, height) / 2; | |
// Create the SVG container for the visualization and | |
// define its dimensions. Within that container, add a | |
// group element (`<g>`) that can be transformed via | |
// a translation to account for the margins and to | |
// center the visualization in the container. | |
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 + width / 2) + "," + | |
(margin.top + height / 2) + ")"); | |
// Define the scales that will translate data values | |
// into visualization properties. The "x" scale | |
// will represent angular position within the | |
// visualization, so it ranges lnearly from 0 to | |
// 2π. The "y" scale will reprent area, so it | |
// ranges from 0 to the full radius of the | |
// visualization. Since area varies as the square | |
// of the radius, this scale takes the square | |
// root of the input domain before mapping to | |
// the output range. | |
var x = d3.scale.linear() | |
.range([0, 2 * Math.PI]); | |
var y = d3.scale.sqrt() | |
.range([0, radius]); | |
// Define the function that creates a partition | |
// layout from the dataset. Because we're using | |
// `d3.nest` to construct the input dataset, the | |
// children array will be stored in the `values` | |
// property unless the node is a leaf node. In | |
// that case the `values` property will hold | |
// the data value itself. | |
var partition = d3.layout.partition() | |
.children(function(d) { | |
return Array.isArray(d.values) ? | |
d.values : null; | |
}) | |
.value(function(d) { | |
return d.values; | |
}); | |
// Define a function that returns the color | |
// for a data point. The input parameter | |
// should be a data point as defined/created | |
// by the partition layout. | |
var color = function(d) { | |
// This function builds the total | |
// color palette incrementally so | |
// we don't have to iterate through | |
// the entire data structure. | |
// We're going to need a color scale. | |
// Normally we'll distribute the colors | |
// in the scale to child nodes. | |
var colors; | |
// The root node is special since | |
// we have to seed it with our | |
// desired palette. | |
if (!d.parent) { | |
// Create a categorical color | |
// scale to use both for the | |
// root node's immediate | |
// children. We're using the | |
// 10-color predefined scale, | |
// so set the domain to be | |
// [0, ... 9] to ensure that | |
// we can predictably generate | |
// correct individual colors. | |
colors = d3.scale.category10() | |
.domain(d3.range(0,10)); | |
// White for the root node | |
// itself. | |
d.color = "#fff"; | |
} else if (d.children) { | |
// Since this isn't the root node, | |
// we construct the scale from the | |
// node's assigned color. Our scale | |
// will range from darker than the | |
// node's color to brigher than the | |
// node's color. | |
var startColor = d3.hcl(d.color) | |
.darker(), | |
endColor = d3.hcl(d.color) | |
.brighter(); | |
// Create the scale | |
colors = d3.scale.linear() | |
.interpolate(d3.interpolateHcl) | |
.range([ | |
startColor.toString(), | |
endColor.toString() | |
]) | |
.domain([0,d.children.length+1]); | |
} | |
if (d.children) { | |
// Now distribute those colors to | |
// the child nodes. We want to do | |
// it in sorted order, so we'll | |
// have to calculate that. Because | |
// JavaScript sorts arrays in place, | |
// we use a mapped version. | |
d.children.map(function(child, i) { | |
return {value: child.value, idx: i}; | |
}).sort(function(a,b) { | |
return b.value - a.value | |
}).forEach(function(child, i) { | |
d.children[child.idx].color = colors(i); | |
}); | |
} | |
return d.color; | |
}; | |
// Define the function that constructs the | |
// path for an arc corresponding to a data | |
// value. | |
var arc = d3.svg.arc() | |
.startAngle(function(d) { | |
return Math.max(0, | |
Math.min(2 * Math.PI, x(d.x))); | |
}) | |
.endAngle(function(d) { | |
return Math.max(0, | |
Math.min(2 * Math.PI, x(d.x + d.dx))); | |
}) | |
.innerRadius(function(d) { | |
return Math.max(0, y(d.y)); | |
}) | |
.outerRadius(function(d) { | |
return Math.max(0, y(d.y + d.dy)); | |
}); | |
// Extract the hierachy from the raw data | |
// Using `d3.nest` operations. The data's | |
// hierarchy is region -> state -> county. | |
// At the county level, we're only interested | |
// in a count of the data points. | |
var hierarchy = { | |
key: "Account Title", | |
values: d3.nest() | |
.key(function(d) { return d.organization; }) | |
.key(function(d) { return d["Budget Activity Title"]; }) | |
.key(function(d) { return d["Program Element / Budget Line Item (BLI) Title"]; }) | |
.rollup(function(leaves) { | |
return leaves.length; | |
}) | |
.entries(dataset) | |
}; | |
// Construct the visualization. | |
var path = svg.selectAll("path") | |
.data(partition.nodes(hierarchy)) | |
.enter().append("path") | |
.attr("d", arc) | |
.attr("stroke", "#fff") | |
.attr("fill-rule", "evenodd") | |
.attr("fill", color) | |
.on("click", click) | |
.on("mouseover", mouseover) | |
.on("mouseout", mouseout); | |
// Add a container for the tooltip. | |
var tooltip = svg.append("text") | |
.attr("font-size", 12) | |
.attr("fill", "#000") | |
.attr("fill-opacity", 0) | |
.attr("text-anchor", "middle") | |
.attr("transform", "translate(" + 0 + "," + (12 + height/2) +")") | |
.style("pointer-events", "none"); | |
// Add the title. | |
svg.append("text") | |
.attr("font-size", 16) | |
.attr("fill", "#000") | |
.attr("text-anchor", "middle") | |
.attr("transform", "translate(" + 0 + "," + (-10 -height/2) +")") | |
.text("Tornado Sightings in 2013 (www.noaa.gov)"); | |
// Handle clicks on data points. All | |
// we need to do is start the transition | |
// that updates the paths of the arcs. | |
function click(d) { | |
path.transition() | |
.duration(750) | |
.attrTween("d", arcTween(d)); | |
// Hide the tooltip since the | |
// path "underneath" the cursor | |
// will likely have changed. | |
mouseout(); | |
}; | |
// Handle mouse moving over a data point | |
// by enabling the tooltip. | |
function mouseover(d) { | |
tooltip.text(d.key + ": " + | |
d.value + " sighting" + | |
(d.value > 1 ? "s" : "")) | |
.transition() | |
.attr("fill-opacity", 1); | |
}; | |
// Handle mouse leaving a data point | |
// by disabling the tooltip. | |
function mouseout() { | |
tooltip.transition() | |
.attr("fill-opacity", 0); | |
}; | |
// Function to interpolate values for | |
// the visualization elements during | |
// a transition. | |
function arcTween(d) { | |
var xd = d3.interpolate(x.domain(), | |
[d.x, d.x + d.dx]), | |
yd = d3.interpolate(y.domain(), | |
[d.y, 1]), | |
yr = d3.interpolate(y.range(), | |
[d.y ? 20 : 0, radius]); | |
return function(d, i) { | |
return i ? | |
function(t) { | |
return arc(d); | |
} : | |
function(t) { | |
x.domain(xd(t)); | |
y.domain(yd(t)).range(yr(t)); | |
return arc(d); | |
}; | |
}; | |
} | |
} | |
</script> |
http://jsdatav.is/img/thumbnails/radial.png |