dc
Version:
A multi-dimensional charting library built to work natively with crossfilter and rendered using d3.js
145 lines (130 loc) • 4.76 kB
HTML
<html lang="en">
<head>
<title>dc.js - Brushing on an Ordinal Bar Chart</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="../css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="../css/dc.css"/>
</head>
<body>
<div class="container">
<script type="text/javascript" src="header.js"></script>
<p>This example demonstrates brushing on an ordinal bar chart, by mapping the values to
integers and specifying a linear scale. The data is sorted by descending value in one
fake group, and then another fake group provides the integer/ordinal mapping.</p>
<div id="bar"></div>
<div id="first-letters"></div>
<script type="text/javascript" src="../js/promise-polyfill.js"></script>
<script type="text/javascript" src="../js/fetch.umd.js"></script>
<script type="text/javascript" src="../js/d3.js"></script>
<script type="text/javascript" src="../js/crossfilter.js"></script>
<script type="text/javascript" src="../js/dc.js"></script>
<style>
#bar .axis.x .tick text {
text-anchor: end;
transform: rotate(-60deg) translate(-10px,-12px);
}
#range.dc-chart .axis {
display: none;
}
#range svg {
display: block; /* default inline causes padding? */
}
</style>
<script type="text/javascript">
function sort_group(group, sort) {
return {
all: function() {
return group.all().sort(sort);
}
};
}
// index a group key -> i and i -> key
function ordinal_to_linear_group(group, sort) {
var _ord2int, _int2ord;
return {
all: function() {
var ret = group.all();
_ord2int = {};
_int2ord = [];
ret.forEach(function(d, i) {
_ord2int[d.key] = i;
_int2ord[i] = d.key;
});
return ret;
},
ord2int: function(o) {
if(!_ord2int)
this.all();
return _ord2int[o];
},
int2ord: function(i) {
if(!_int2ord)
this.all();
return _int2ord[i];
}
};
}
// phrases generated with https://www.fourmilab.ch/javascrypt/pass_phrase.html
var bar;
d3.json("wide-ordinal.json").then(function(wide) {
var short = wide.slice(0, 50);
// dummy crossfilter taking group data and regrouping it to the same thing
var cf = crossfilter(short),
dimension = cf.dimension(function(d) {return d.key;}),
group = dimension.group().reduceSum(function(d) {return d.value;});
group = ordinal_to_linear_group(sort_group(group, function(a, b) {
return d3.descending(a.value, b.value);
}));
bar = dc.barChart('#bar');
var linear_domain = [-0.5, short.length - 0.5];
bar
.width(1000).height(300)
.margins({left: 75, top: 0, right: 10, bottom: 140})
.x(d3.scaleLinear().domain(linear_domain))
.xUnits(dc.units.integers)
.keyAccessor(kv => group.ord2int(kv.key))
.centerBar(true)
.yAxisLabel('counts')
.elasticY(true)
.brushOn(true)
.dimension(dimension)
.group(group)
.title(kv => kv.key);
bar.xAxis()
.tickValues(d3.range(short.length))
.tickFormat(function(d) { return group.int2ord(d); });
bar.filterHandler(function(dimension, filters) {
if(!filters || !filters.length) {
dimension.filter(null);
return filters;
}
console.assert(filters.length === 1);
console.assert(filters[0].filterType === 'RangedFilter');
var inside = group.all().filter(function(kv) {
var i = group.ord2int(kv.key);
return filters[0][0] <= i && i < filters[0][1];
}).map(function(kv) { return kv.key; });
dimension.filterFunction(function(d) {
return inside.includes(d);
});
return filters;
});
// display a row chart of first letters, to test filtering
var letterDimension = cf.dimension(function(d) {
return d.key.split(' ').map(function(s) { return s[0]; });
}, true);
var letterGroup = letterDimension.group();
var row = dc.rowChart('#first-letters');
row
.width(1000)
.height(350)
.gap(1)
.dimension(letterDimension)
.group(letterGroup);
dc.renderAll();
});
</script>
</div>
</body>
</html>