Skip to content

Commit

Permalink
add treemap (#344)
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewhn authored and mistercrunch committed Apr 14, 2016
1 parent 5cadd67 commit 52ebdc5
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 1 deletion.
Binary file added caravel/assets/images/viz_thumbnails/treemap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion caravel/assets/javascripts/modules/caravel.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ var sourceMap = {
sunburst: 'sunburst.js',
table: 'table.js',
word_cloud: 'word_cloud.js',
world_map: 'world_map.js'
world_map: 'world_map.js',
treemap: 'treemap.js'
};

var color = function () {
Expand Down
15 changes: 15 additions & 0 deletions caravel/assets/visualizations/treemap.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.node {
border: solid 1px white;
font: 10px sans-serif;
line-height: 12px;
overflow: hidden;
position: absolute;
text-indent: 2px;
padding: 0px; /* form div giving top 1px */
box-sizing: content-box; /* otherwise inheriting border-box */
}

.treemap-container {
position: relative;
margin: auto;
}
104 changes: 104 additions & 0 deletions caravel/assets/visualizations/treemap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// JS
var d3 = window.d3 || require('d3');
var px = window.px || require('../javascripts/modules/caravel.js');

// CSS
require('./treemap.css');

/* Modified from https://bl.ocks.org/mbostock/4063582 */
function treemap(slice) {

var div = d3.select(slice.selector);

var _draw = function (data, eltWidth, eltHeight, includeTitle) {

var margin = { top: 0, right: 0, bottom: 0, left: 0 },
headerHeight = includeTitle ? 30 : 0,
width = eltWidth - margin.left - margin.right,
height = eltHeight - headerHeight - margin.top - margin.bottom;

var treemap = d3.layout.treemap()
.size([width, height])
.value(function (d) { return d.value; });

var root = div.append("div")
.classed("treemap-container", true);

var header = root.append("div")
.style("width", (width + margin.left + margin.right) + "px")
.style("height", headerHeight + "px");

var container = root.append("div")
.style("position", "relative")
.style("width", (width + margin.left + margin.right) + "px")
.style("height", (height + margin.top + margin.bottom) + "px")
.style("left", margin.left + "px")
.style("top", margin.top + "px");

var position = function (selection) {
selection.style("left", function (d) { return d.x + "px"; })
.style("top", function (d) { return d.y + "px"; })
.style("width", function (d) { return Math.max(0, d.dx - 1) + "px"; })
.style("height", function (d) { return Math.max(0, d.dy - 1) + "px"; });
};

container.datum(data).selectAll(".node")
.data(treemap.nodes)
.enter().append("div")
.attr("class", "node")
.call(position)
.style("background", function (d) {
return d.children ? px.color.category21(d.name) : null;
})
.style("color", function (d) {
// detect if our background is dark and we need a
// light text color or vice-versa
var bg = d.parent ? px.color.category21(d.parent.name) : null;
if (bg) {
return d3.hsl(bg).l < 0.35 ? '#d3d3d3' : '#111111';
}
})
.text(function (d) { return d.children ? null : d.name; });

if (includeTitle) {
// title to help with multiple metrics (if any)
header.append("span")
.style("font-size", "18px")
.style("font-weight", "bold")
.text(data.name);
}

};

var render = function () {

d3.json(slice.jsonEndpoint(), function (error, json) {

if (error !== null) {
slice.error(error.responseText);
return '';
}

div.selectAll("*").remove();
var width = slice.width();
// facet muliple metrics (no sense in combining)
var height = slice.height() / json.data.length;
var includeTitles = json.data.length > 1;
for (var i = 0, l = json.data.length; i < l; i ++) {
_draw(json.data[i], width, height, includeTitles);
}

slice.done(json);

});

};

return {
render: render,
resize: render
};
}

module.exports = treemap;

12 changes: 12 additions & 0 deletions caravel/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,18 @@ def load_world_bank_health_n_pop():
whisker_options="Tukey",
viz_type='box_plot',
groupby=["region"],)),
Slice(
slice_name="Treemap",
viz_type='treemap',
datasource_type='table',
table=tbl,
params=get_slice_json(
defaults,
since="1960-01-01",
until="now",
viz_type='treemap',
metrics=["sum__SP_POP_TOTL"],
groupby=["region", "country_code"],)),
]
for slc in slices:
merge_slice(slc)
Expand Down
32 changes: 32 additions & 0 deletions caravel/viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,37 @@ def get_data(self):
return df.to_dict(orient="records")


class TreemapViz(BaseViz):

"""Tree map visualisation for hierarchical data."""

viz_type = "treemap"
verbose_name = "Treemap"
credits = '<a href="https://d3js.org">d3.js</a>'
is_timeseries = False

def get_df(self, query_obj=None):
df = super(TreemapViz, self).get_df(query_obj)
df = df.set_index(self.form_data.get("groupby"))
return df

def _nest(self, metric, df):
nlevels = df.index.nlevels
if nlevels == 1:
result = [{"name": n, "value": v}
for n, v in zip(df.index, df[metric])]
else:
result = [{"name": l, "children": self._nest(metric, df.loc[l])}
for l in df.index.levels[0]]
return result

def get_data(self):
df = self.get_df()
chart_data = [{"name": metric, "children": self._nest(metric, df)}
for metric in df.columns]
return chart_data


class NVD3Viz(BaseViz):

"""Base class for all nvd3 vizs"""
Expand Down Expand Up @@ -1505,6 +1536,7 @@ def get_data(self):
ParallelCoordinatesViz,
HeatmapViz,
BoxPlotViz,
TreemapViz,
]

viz_types = OrderedDict([(v.viz_type, v) for v in viz_types_list])

0 comments on commit 52ebdc5

Please sign in to comment.