Last active
April 21, 2016 15:21
-
-
Save maw246/7303963 to your computer and use it in GitHub Desktop.
Zoomable Treemaps Using CSV
This file contains hidden or 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
| thedad | name | value | |
|---|---|---|---|
| Homer Simpson | Bart | 20 | |
| Homer Simpson | Lisa | 14 | |
| Homer Simpson | Maggie | 6 | |
| Peter Griffin | Chris | 19 | |
| Peter Griffin | Meg | 12 | |
| Peter Griffin | Stewie | 16 | |
| Bart | Bart Junior A | 77 | |
| Bart | Bart Junior B | 32 |
This file contains hidden or 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> | |
| <meta charset="utf-8"> | |
| <title>Zoomable Treemaps from CSV</title> | |
| <style> | |
| @import url(./style.css); | |
| #chart { | |
| width: 960px; | |
| height: 500px; | |
| background: #bbb; | |
| } | |
| text { | |
| pointer-events: none; | |
| } | |
| .grandparent text { | |
| font-weight: bold; | |
| } | |
| rect { | |
| fill: none; | |
| stroke: #fff; | |
| } | |
| rect.parent, | |
| .grandparent rect { | |
| stroke-width: 2px; | |
| } | |
| .grandparent rect { | |
| fill: orange; | |
| } | |
| .grandparent:hover rect { | |
| fill: #ee9700; | |
| } | |
| .children rect.parent, | |
| .grandparent rect { | |
| cursor: pointer; | |
| } | |
| rect.parent { | |
| pointer-events: all; | |
| } | |
| .children:hover rect.child { | |
| fill: #aaa; | |
| } | |
| </style> | |
| <header> | |
| <aside>November 4, 2013</aside> | |
| <a href="../" rel="author">maw246</a> | |
| </header> | |
| <h1>Zoomable Treemaps from CSV</h1> | |
| <p id="chart"></p> | |
| <aside>Click any cell to zoom in, or the top orange label to zoom out.</aside> | |
| <p>Derived from <a href="http://bost.ocks.org/mike/treemap/">Mike Bostock's example</a>.</p> | |
| <p>Whereas Mr. Bostock's example uses <i>flare.json</i>, this example utilizes D3's CSV capabilities to process comma-separated data.</p> | |
| <footer> | |
| <aside>November 4, 2013</aside> | |
| <a href="../" rel="author">maw246</a> | |
| </footer> | |
| <!--<script src="http://d3js.org/d3.v2.js?2.9.1"></script>--> | |
| <script src="http://d3js.org/d3.v3.js?2.9.1"></script> | |
| <script> | |
| var margin = {top: 20, right: 0, bottom: 0, left: 0}, | |
| width = 960, | |
| height = 500 - margin.top - margin.bottom, | |
| //formatNumber = d3.format(",d"), | |
| formatNumber = function(val) {return val}, | |
| transitioning; | |
| var x = d3.scale.linear() | |
| .domain([0, width]) | |
| .range([0, width]); | |
| var y = d3.scale.linear() | |
| .domain([0, height]) | |
| .range([0, height]); | |
| var svg = d3.select("#chart").append("svg") | |
| .attr("width", width + margin.left + margin.right) | |
| .attr("height", height + margin.bottom + margin.top) | |
| .style("margin-left", -margin.left + "px") | |
| .style("margin.right", -margin.right + "px") | |
| .append("g") | |
| .attr("transform", "translate(" + margin.left + "," + margin.top + ")") | |
| .style("shape-rendering", "crispEdges"); | |
| var grandparent = svg.append("g") | |
| .attr("class", "grandparent"); | |
| grandparent.append("rect") | |
| .attr("y", -margin.top) | |
| .attr("width", width) | |
| .attr("height", margin.top); | |
| grandparent.append("text") | |
| .attr("x", 6) | |
| .attr("y", 6 - margin.top) | |
| .attr("dy", ".75em"); | |
| //d3.json("flare.json", function(root) { | |
| d3.csv("hier.csv", function(hier) { | |
| //d3.json(myRoot, function(root) { | |
| console.log("Before:"); | |
| console.log(hier); | |
| var root = {"key":"Fox Cartoons" | |
| , "values":d3.nest() | |
| .key(function (d) { return d.thedad; }) | |
| .entries(hier) | |
| } | |
| console.log("After:"); | |
| console.log(root); | |
| //var data = root; | |
| var treemap = d3.layout.treemap() | |
| //.children(function(d, depth) { return depth ? null : d.children; }) | |
| .children(function(d, depth) { return depth ? null : d.values; }) | |
| //.text(function(d) { return d.key; }) | |
| .value(function(d) { return d.value; }) | |
| .sort(function(a, b) { return a.value - b.value; }) | |
| .ratio(height / width * 0.5 * (1 + Math.sqrt(5))) | |
| .round(false); | |
| initialize(root); | |
| accumulate(root); | |
| layout(root); | |
| display(root); | |
| function initialize(root) { | |
| root.x = root.y = 0; | |
| root.dx = width; | |
| root.dy = height; | |
| root.depth = 0; | |
| } | |
| // Aggregate the values for internal nodes. This is normally done by the | |
| // treemap layout, but not here because of our custom implementation. | |
| function accumulate(d) { | |
| //console.log(d.children); | |
| return d.values | |
| ? d.value = d.values.reduce(function(p, v) { return p + accumulate(v); }, 0) | |
| : +d.value; | |
| } | |
| // Compute the treemap layout recursively such that each group of siblings | |
| // uses the same size (1×1) rather than the dimensions of the parent cell. | |
| // This optimizes the layout for the current zoom state. Note that a wrapper | |
| // object is created for the parent node for each group of siblings so that | |
| // the parent’s dimensions are not discarded as we recurse. Since each group | |
| // of sibling was laid out in 1×1, we must rescale to fit using absolute | |
| // coordinates. This lets us use a viewport to zoom. | |
| function layout(d) { | |
| if (d.values) { | |
| treemap.nodes({values: d.values}); | |
| d.values.forEach(function(c) { | |
| c.x = d.x + c.x * d.dx; | |
| c.y = d.y + c.y * d.dy; | |
| c.dx *= d.dx; | |
| c.dy *= d.dy; | |
| c.parent = d; | |
| layout(c); | |
| }); | |
| } | |
| } | |
| function display(d) { | |
| grandparent | |
| .datum(d.parent) | |
| .on("click", transition) | |
| .select("text") | |
| .text(name(d)); | |
| var g1 = svg.insert("g", ".grandparent") | |
| .datum(d) | |
| .attr("class", "depth"); | |
| var g = g1.selectAll("g") | |
| .data(d.values) | |
| .enter().append("g"); | |
| g.filter(function(d) { return d.values; }) | |
| .classed("children", true) | |
| .on("click", transition); | |
| g.selectAll(".child") | |
| .data(function(d) { return d.values || [d]; }) | |
| .enter().append("rect") | |
| .attr("class", "child") | |
| .call(rect); | |
| g.append("rect") | |
| .attr("class", "parent") | |
| .call(rect) | |
| .append("title") | |
| .text(function(d) { return formatNumber(+d.value); }); | |
| g.append("text") | |
| .attr("dy", ".75em") | |
| .text(function(d) { return name(d); }) | |
| .call(text); | |
| function transition(d) { | |
| if (transitioning || !d) return; | |
| transitioning = true; | |
| var g2 = display(d), | |
| t1 = g1.transition().duration(750), | |
| t2 = g2.transition().duration(750); | |
| // Update the domain only after entering new elements. | |
| x.domain([d.x, d.x + d.dx]); | |
| y.domain([d.y, d.y + d.dy]); | |
| // Enable anti-aliasing during the transition. | |
| svg.style("shape-rendering", null); | |
| // Draw child nodes on top of parent nodes. | |
| svg.selectAll(".depth").sort(function(a, b) { return a.depth - b.depth; }); | |
| // Fade-in entering text. | |
| g2.selectAll("text").style("fill-opacity", 0); | |
| // Transition to the new view. | |
| t1.selectAll("text").call(text).style("fill-opacity", 0); | |
| t2.selectAll("text").call(text).style("fill-opacity", 1); | |
| t1.selectAll("rect").call(rect); | |
| t2.selectAll("rect").call(rect); | |
| // Remove the old node when the transition is finished. | |
| t1.remove().each("end", function() { | |
| svg.style("shape-rendering", "crispEdges"); | |
| transitioning = false; | |
| }); | |
| } | |
| return g; | |
| } | |
| function text(text) { | |
| text.attr("x", function(d) { return x(d.x) + 6; }) | |
| .attr("y", function(d) { return y(d.y) + 6; }); | |
| } | |
| function rect(rect) { | |
| rect.attr("x", function(d) { return x(d.x); }) | |
| .attr("y", function(d) { return y(d.y); }) | |
| .attr("width", function(d) { return x(d.x + d.dx) - x(d.x); }) | |
| .attr("height", function(d) { return y(d.y + d.dy) - y(d.y); }); | |
| } | |
| /* | |
| function name(d) { | |
| return d.parent | |
| ? name(d.parent) + "." + d.name | |
| : d.name; | |
| } | |
| */ | |
| function name(d) { | |
| return d.parent | |
| ? name(d.parent) + "." + d.key | |
| : d.key; | |
| } | |
| }); | |
| </script> |
This file contains hidden or 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
| /* This is an exact copy of the style.css from Bostock's example. No changes except this comment. */ | |
| @import url(http://fonts.googleapis.com/css?family=PT+Serif|PT+Serif:b|PT+Serif:i|PT+Sans|PT+Sans:b); | |
| html { | |
| min-width: 1040px; | |
| } | |
| body { | |
| background: #fcfcfa; | |
| color: #333; | |
| font-family: "PT Serif", serif; | |
| margin: 1em auto 4em auto; | |
| position: relative; | |
| width: 960px; | |
| } | |
| header, | |
| footer, | |
| h1, | |
| h2, | |
| h3, | |
| h4, | |
| aside { | |
| color: #000; | |
| font-family: "PT Sans", sans-serif; | |
| } | |
| h1 { | |
| font-size: 64px; | |
| font-weight: 300; | |
| letter-spacing: -2px; | |
| margin: .3em 0 .1em 0; | |
| } | |
| h2 { | |
| margin-top: 2em; | |
| } | |
| h1, h2 { | |
| text-rendering: optimizeLegibility; | |
| } | |
| h2 a { | |
| color: #ccc; | |
| left: -20px; | |
| position: absolute; | |
| width: 740px; | |
| } | |
| footer { | |
| font-size: small; | |
| margin-top: 8em; | |
| } | |
| header aside { | |
| margin-top: 88px; | |
| } | |
| header aside, | |
| footer aside { | |
| color: #636363; | |
| text-align: right; | |
| } | |
| aside { | |
| font-size: small; | |
| right: 0; | |
| position: absolute; | |
| width: 180px; | |
| } | |
| .attribution { | |
| font-size: small; | |
| margin-bottom: 2em; | |
| } | |
| body > p, li > p { | |
| line-height: 1.5em; | |
| } | |
| body > p { | |
| width: 720px; | |
| } | |
| body > blockquote { | |
| width: 640px; | |
| } | |
| blockquote q { | |
| display: block; | |
| font-style: oblique; | |
| } | |
| li { | |
| width: 680px; | |
| } | |
| a { | |
| color: steelblue; | |
| } | |
| a:not(:hover) { | |
| text-decoration: none; | |
| } | |
| pre, code, textarea { | |
| font-family: "Menlo", monospace; | |
| } | |
| code { | |
| line-height: 1em; | |
| } | |
| textarea { | |
| font-size: 100%; | |
| } | |
| body > pre { | |
| border-left: solid 2px #ccc; | |
| padding-left: 18px; | |
| margin: 2em 0 2em -20px; | |
| } | |
| .html .value, | |
| .javascript .string, | |
| .javascript .regexp { | |
| color: #756bb1; | |
| } | |
| .html .tag, | |
| .css .tag, | |
| .javascript .keyword { | |
| color: #3182bd; | |
| } | |
| .comment { | |
| color: #636363; | |
| } | |
| .html .doctype, | |
| .javascript .number { | |
| color: #31a354; | |
| } | |
| .html .attribute, | |
| .css .attribute, | |
| .javascript .class, | |
| .javascript .special { | |
| color: #e6550d; | |
| } | |
| svg { | |
| font: 10px sans-serif; | |
| } | |
| .axis path, .axis line { | |
| fill: none; | |
| stroke: #000; | |
| shape-rendering: crispEdges; | |
| } | |
| sup, sub { | |
| line-height: 0; | |
| } | |
| q:before { | |
| content: "“"; | |
| } | |
| q:after { | |
| content: "â€"; | |
| } | |
| blockquote:before { | |
| position: absolute; | |
| left: 2em; | |
| } | |
| blockquote:after { | |
| position: absolute; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment