Circular Heatmap with tooltips
Last active
February 4, 2017 03:12
-
-
Save arpitnarechania/b0bdc4f9a377ea9d8612677e12f65b82 to your computer and use it in GitHub Desktop.
Circular Heatmap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: MIT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Circular Heat Map</title> | |
<!-- JavaScript Libraries //--> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<!-- CSS Style //--> | |
<link href="style.css" rel="stylesheet" type="text/css"> | |
<script type="text/javascript" src="main.js"></script> | |
</head> | |
<body> | |
<div style="margin-left:50px;" id="chart"></div> | |
</body> | |
<script> | |
var inputData = | |
[{month:1,type:"Category 1",value:25}, | |
{month:2,type:"Category 1",value:15}, | |
{month:3,type:"Category 1",value:27}, | |
{month:4,type:"Category 1",value:10}, | |
{month:5,type:"Category 1",value:54}, | |
{month:6,type:"Category 1",value:23}, | |
{month:7,type:"Category 1",value:31}, | |
{month:8,type:"Category 1",value:17}, | |
{month:9,type:"Category 1",value:8}, | |
{month:10,type:"Category 1",value:12}, | |
{month:11,type:"Category 1",value:32}, | |
{month:12,type:"Category 1",value:35}, | |
{month:1,type:"Category 2",value:19}, | |
{month:2,type:"Category 2",value:24}, | |
{month:3,type:"Category 2",value:27}, | |
{month:4,type:"Category 2",value:12}, | |
{month:5,type:"Category 2",value:19}, | |
{month:6,type:"Category 2",value:30}, | |
{month:7,type:"Category 2",value:31}, | |
{month:8,type:"Category 2",value:25}, | |
{month:9,type:"Category 2",value:20}, | |
{month:10,type:"Category 2",value:5}, | |
{month:11,type:"Category 2",value:21}, | |
{month:12,type:"Category 2",value:10}, | |
{month:1,type:"Category 3",value:19}, | |
{month:2,type:"Category 3",value:3}, | |
{month:3,type:"Category 3",value:32}, | |
{month:4,type:"Category 3",value:23}, | |
{month:5,type:"Category 3",value:9}, | |
{month:6,type:"Category 3",value:17}, | |
{month:7,type:"Category 3",value:25}, | |
{month:8,type:"Category 3",value:29}, | |
{month:9,type:"Category 3",value:32}, | |
{month:10,type:"Category 3",value:33}, | |
{month:11,type:"Category 3",value:19}, | |
{month:12,type:"Category 3",value:24}, | |
{month:1,type:"Category 4",value:12}, | |
{month:2,type:"Category 4",value:43}, | |
{month:3,type:"Category 4",value:12}, | |
{month:4,type:"Category 4",value:23}, | |
{month:5,type:"Category 4",value:14}, | |
{month:6,type:"Category 4",value:19}, | |
{month:7,type:"Category 4",value:22}, | |
{month:8,type:"Category 4",value:39}, | |
{month:9,type:"Category 4",value:22}, | |
{month:10,type:"Category 4",value:26}, | |
{month:11,type:"Category 4",value:31}, | |
{month:12,type:"Category 4",value:25}, | |
]; | |
var radial_labels = ["Category 1","Category 2","Category 3","Category 4"]; | |
var segment_labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; | |
loadCircularHeatMap(inputData,"#chart",radial_labels, segment_labels); | |
</script> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function loadCircularHeatMap (dataset, dom_element_to_append_to,radial_labels,segment_labels) { | |
var margin = {top: 50, right: 50, bottom: 50, left: 50}; | |
var width = 600 - margin.left - margin.right; | |
var height = width; | |
var innerRadius = width/14; | |
var segmentHeight = (width - margin.top - margin.bottom - 2*innerRadius )/(2*radial_labels.length) | |
var chart = circularHeatChart() | |
.innerRadius(innerRadius) | |
.segmentHeight(segmentHeight) | |
.range(["white", "#01579b"]) | |
.radialLabels(radial_labels) | |
.segmentLabels(segment_labels); | |
chart.accessor(function(d) {return d.value;}) | |
var svg = d3.select(dom_element_to_append_to) | |
.selectAll('svg') | |
.data([dataset]) | |
.enter() | |
.append('svg') | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.append('g') | |
.attr("transform", | |
"translate(" + ( (width )/2 - (radial_labels.length*segmentHeight + innerRadius) ) + "," + margin.top + ")") | |
.call(chart); | |
var tooltip = d3.select(dom_element_to_append_to) | |
.append('div') | |
.attr('class', 'tooltip'); | |
tooltip.append('div') | |
.attr('class', 'month'); | |
tooltip.append('div') | |
.attr('class', 'value'); | |
tooltip.append('div') | |
.attr('class', 'type'); | |
svg.selectAll("path") | |
.on('mouseover', function(d) { | |
tooltip.select('.month').html("<b> Month: " + d.month + "</b>"); | |
tooltip.select('.type').html("<b> Type: " + d.type + "</b>"); | |
tooltip.select('.value').html("<b> Value: " + d.value + "</b>"); | |
tooltip.style('display', 'block'); | |
tooltip.style('opacity',2); | |
}) | |
.on('mousemove', function(d) { | |
tooltip.style('top', (d3.event.layerY + 10) + 'px') | |
.style('left', (d3.event.layerX - 25) + 'px'); | |
}) | |
.on('mouseout', function(d) { | |
tooltip.style('display', 'none'); | |
tooltip.style('opacity',0); | |
}); | |
} | |
function circularHeatChart() { | |
var margin = {top: 20, right: 20, bottom: 20, left: 20}, | |
innerRadius = 20, | |
numSegments = 12, | |
segmentHeight = 20, | |
domain = null, | |
range = ["white", "red"], | |
accessor = function(d) {return d;}, | |
radialLabels = segmentLabels = []; | |
function chart(selection) { | |
selection.each(function(data) { | |
var svg = d3.select(this); | |
var offset = innerRadius + Math.ceil(data.length / numSegments) * segmentHeight; | |
g = svg.append("g") | |
.classed("circular-heat", true) | |
.attr("transform", "translate(" + parseInt(margin.left + offset) + "," + parseInt(margin.top + offset) + ")"); | |
var autoDomain = false; | |
if (domain === null) { | |
domain = d3.extent(data, accessor); | |
autoDomain = true; | |
} | |
var color = d3.scale.linear().domain(domain).range(range); | |
if(autoDomain) | |
domain = null; | |
g.selectAll("path").data(data) | |
.enter().append("path") | |
.attr("d", d3.svg.arc().innerRadius(ir).outerRadius(or).startAngle(sa).endAngle(ea)) | |
.attr("stroke", function(d) {return "#4f5b69";}) | |
.attr("fill", function(d) {return color(accessor(d));}); | |
// Unique id so that the text path defs are unique - is there a better way to do this? | |
var id = d3.selectAll(".circular-heat")[0].length; | |
//Radial labels | |
var lsa = 0.01; //Label start angle | |
var labels = svg.append("g") | |
.classed("labels", true) | |
.classed("radial", true) | |
.attr("transform", "translate(" + parseInt(margin.left + offset) + "," + parseInt(margin.top + offset) + ")"); | |
labels.selectAll("def") | |
.data(radialLabels).enter() | |
.append("def") | |
.append("path") | |
.attr("id", function(d, i) {return "radial-label-path-"+id+"-"+i;}) | |
.attr("d", function(d, i) { | |
var r = innerRadius + ((i + 0.2) * segmentHeight); | |
return "m" + r * Math.sin(lsa) + " -" + r * Math.cos(lsa) + | |
" a" + r + " " + r + " 0 1 1 -1 0"; | |
}); | |
labels.selectAll("text") | |
.data(radialLabels).enter() | |
.append("text") | |
.append("textPath") | |
.attr("xlink:href", function(d, i) {return "#radial-label-path-"+id+"-"+i;}) | |
.style("font-size", "16px") | |
.text(function(d) {return d;}); | |
//Segment labels | |
var segmentLabelOffset = 2; | |
var r = innerRadius + Math.ceil(data.length / numSegments) * segmentHeight + segmentLabelOffset; | |
labels = svg.append("g") | |
.classed("labels", true) | |
.classed("segment", true) | |
.attr("transform", "translate(" + parseInt(margin.left + offset) + "," + parseInt(margin.top + offset) + ")"); | |
labels.append("def") | |
.append("path") | |
.attr("id", "segment-label-path-"+id) | |
.attr("d", "m0 -" + r + " a" + r + " " + r + " 0 1 1 -1 0"); | |
labels.selectAll("text") | |
.data(segmentLabels).enter() | |
.append("text") | |
.append("textPath") | |
.attr("xlink:href", "#segment-label-path-"+id) | |
.style("font-size", "16px") | |
.attr("startOffset", function(d, i) {return i * 100 / numSegments + "%";}) | |
.text(function(d) {return d;}); | |
}); | |
} | |
/* Arc functions */ | |
ir = function(d, i) { | |
return innerRadius + Math.floor(i/numSegments) * segmentHeight; | |
} | |
or = function(d, i) { | |
return innerRadius + segmentHeight + Math.floor(i/numSegments) * segmentHeight; | |
} | |
sa = function(d, i) { | |
return (i * 2 * Math.PI) / numSegments; | |
} | |
ea = function(d, i) { | |
return ((i + 1) * 2 * Math.PI) / numSegments; | |
} | |
/* Configuration getters/setters */ | |
chart.margin = function(_) { | |
if (!arguments.length) return margin; | |
margin = _; | |
return chart; | |
}; | |
chart.innerRadius = function(_) { | |
if (!arguments.length) return innerRadius; | |
innerRadius = _; | |
return chart; | |
}; | |
chart.numSegments = function(_) { | |
if (!arguments.length) return numSegments; | |
numSegments = _; | |
return chart; | |
}; | |
chart.segmentHeight = function(_) { | |
if (!arguments.length) return segmentHeight; | |
segmentHeight = _; | |
return chart; | |
}; | |
chart.domain = function(_) { | |
if (!arguments.length) return domain; | |
domain = _; | |
return chart; | |
}; | |
chart.range = function(_) { | |
if (!arguments.length) return range; | |
range = _; | |
return chart; | |
}; | |
chart.radialLabels = function(_) { | |
if (!arguments.length) return radialLabels; | |
if (_ == null) _ = []; | |
radialLabels = _; | |
return chart; | |
}; | |
chart.segmentLabels = function(_) { | |
if (!arguments.length) return segmentLabels; | |
if (_ == null) _ = []; | |
segmentLabels = _; | |
return chart; | |
}; | |
chart.accessor = function(_) { | |
if (!arguments.length) return accessor; | |
accessor = _; | |
return chart; | |
}; | |
return chart; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
path:hover{ | |
fill:#9b59b6; | |
} | |
.tooltip { | |
background: #eee; | |
box-shadow: 0 0 5px #999999; | |
color: #333; | |
font-size: 12px; | |
left: 130px; | |
padding: 10px; | |
position: absolute; | |
text-align: center; | |
top: 95px; | |
z-index: 10; | |
display: block; | |
opacity: 0; | |
} | |
.title{ | |
font-size:16px; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment