This is an example of integrating two visualizations using interactions on one of them. Based on mbostock's scatterplot and histogram demos, plus lgersman's selection window code: /lgersman/5311083. Forgive the horrid code -- I might clean this up later.
forked from mbostock's block: Scatterplot
xxxxxxxxxx
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.bar rect {
fill: steelblue;
shape-rendering: crispEdges;
}
.bar text {
fill: #fff;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
stroke: #000;
}
.inspectorlabel {
font-size: 14px;
}
rect.selectionWindow {
stroke: darkblue;
stroke-opacity: 0.8;
stroke-dasharray: 8px;
fill: transparent;
}
svg {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var attrToHistogram = 'petalLength';
var inspectorHeightWithMargin = 150;
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom - inspectorHeightWithMargin;
var inspectorHeight = inspectorHeightWithMargin - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var rawsvg = d3.select("body").append("svg");
var selectedDots = [];
var svg = rawsvg
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var inspectorSvg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", inspectorHeight + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var normalizeP = function (p) {
p[0] = p[0] - margin.left;
p[1] = p[1] - margin.top;
return p;
}
var setupDraggableWindow = function (data) {
rawsvg.on("mousedown", function () {
console.log('mousedown');
var p = d3.mouse(this);
p = normalizeP(p);
svg.append("rect")
.attr({
rx: 6,
ry: 6,
class: "selectionWindow",
x: p[0],
y: p[1],
width: 0,
height: 0
});
}).on("mousemove", function () {
var s = svg.select("rect.selectionWindow");
if (!s.empty()) {
var p = d3.mouse(this);
p = normalizeP(p);
var d = {
x: parseInt(s.attr("x"), 10),
y: parseInt(s.attr("y"), 10),
width: parseInt(s.attr("width"), 10),
height: parseInt(s.attr("height"), 10)
};
var move = {
x: p[0] - d.x,
y: p[1] - d.y
};
if( move.x < 1 || (move.x*2<d.width)) {
d.x = p[0];
d.width -= move.x;
} else {
d.width = move.x;
}
if( move.y < 1 || (move.y*2<d.height)) {
d.y = p[1];
d.height -= move.y;
} else {
d.height = move.y;
}
s.attr(d);
var newSelectedDots = [];
// Add remove selected items from some global.
d3.selectAll('circle.dot').each( function (da, i) {
var dataX = x(da.sepalWidth);
var dataY = y(da.sepalLength);
if (dataX >= d.x && dataX <= d.x + d.width && dataY >= d.y && dataY <= d.y + d.height) {
newSelectedDots.push(da);
}
});
renderInspectorHistogram(newSelectedDots);
}
}).on('mouseup', function () {
svg.selectAll( "rect.selectionWindow").remove();
renderInspectorHistogram([]);
});
};
var setupScatterPlot = function (data) {
x.domain(d3.extent(data, function(d) { return d.sepalWidth; })).nice();
y.domain(d3.extent(data, function(d) { return d.sepalLength; })).nice();
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Sepal Width (cm)");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Sepal Length (cm)")
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.sepalWidth); })
.attr("cy", function(d) { return y(d.sepalLength); })
.style("fill", function(d) { return color(d.species); });
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
}
function renderInspectorHistogram (inputData) {
var text = inputData ? attrToHistogram + ':' : 'Click and drag to select a region.';
if (inputData && inputData.length === 1) {
text = 'layout.histogram() seems to be broken with 1 element :(';
}
console.log('text: ', text);
inspectorSvg.selectAll(".inspectorlabel").remove();
inspectorSvg.append("text")
.attr("class", "inspectorlabel")
.attr("x", 10)
.attr("y", -10)
.text(text);
if (!inputData || inputData.length < 2) {
return;
}
var subsetData = inputData;
var values = subsetData.map(function (data) { return +data[attrToHistogram]; });
inspectorSvg.selectAll(".bar").remove();
inspectorSvg.selectAll(".axis").remove();
var formatCount = d3.format(",.0f");
var extent = d3.extent(values);
var xForHistogram = d3.scale.linear()
.domain(extent)
.range([0, width]);
var xForBarWidth = d3.scale.linear()
.domain([0, extent[1] - extent[0]])
.range([0, width]);
var x = d3.scale.linear()
.domain([0, 1])
.range([0, width]);
// Generate a histogram using twenty uniformly-spaced bins.
var data = d3.layout.histogram()
.bins(xForHistogram.ticks(20))
(values);
var y = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d.y; })])
.range([inspectorHeight, 0]);
var xAxis = d3.svg.axis()
.scale(xForHistogram)
.orient("bottom");
var bar = inspectorSvg.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(" + xForHistogram(d.x) + "," + y(d.y) + ")"; });
bar.append("rect")
.attr("x", 1)
.attr("width", xForBarWidth(data[0].dx) - 1)
.attr("height", function(d) { return inspectorHeight - y(d.y); });
bar.append("text")
.attr("dy", ".75em")
.attr("y", 6)
.attr("x", xForBarWidth(data[0].dx) / 2)
.attr("text-anchor", "middle")
.text(function(d) { return formatCount(d.y); });
inspectorSvg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + inspectorHeight + ")")
.call(xAxis);
}
d3.tsv("data.tsv", function(error, data) {
if (error) throw error;
data.forEach(function(d) {
d.sepalLength = +d.sepalLength;
d.sepalWidth = +d.sepalWidth;
});
setupScatterPlot(data);
setupDraggableWindow(data);
renderInspectorHistogram();
});
</script>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js