Focus + Context Scatter Plots using Chiasm.
Draws from
Focus + Context Scatter Plots using Chiasm.
Draws from
// A Chiasm plugin for loading DSV data sets. | |
function DataLoader (){ | |
var my = ChiasmComponent({ | |
path: Model.None | |
}); | |
my.when("path", function (path){ | |
if(path !== Model.None){ | |
d3.json(path + ".json", function(error, schema) { | |
var numericColumns = schema.columns.filter(function (column){ | |
return column.type === "number"; | |
}); | |
var type = function (d){ | |
numericColumns.forEach(function (column){ | |
d[column.name] = +d[column.name]; | |
}); | |
return d; | |
} | |
d3.csv(path + ".csv", type, function(error, data) { | |
my.data = data; | |
}); | |
}); | |
} | |
}); | |
return my; | |
} |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Focus + Context Scatter Plots</title> | |
<!-- Chiasm depends on Lodash, D3.js, and Model.js. --> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script> | |
<!-- A functional reactive model library. github.com/curran/model --> | |
<script src="http://curran.github.io/model/cdn/model-v0.2.4.js"></script> | |
<!-- Chiasm core and plugins. github.com/chiasm-project --> | |
<script src="http://chiasm-project.github.io/chiasm/chiasm-v0.2.0.js"></script> | |
<script src="http://chiasm-project.github.io/chiasm-component/chiasm-component-v0.2.1.js"></script> | |
<script src="http://chiasm-project.github.io/chiasm-layout/chiasm-layout-v0.2.2.js"></script> | |
<script src="http://chiasm-project.github.io/chiasm-links/chiasm-links-v0.2.1.js"></script> | |
<!-- Custom Chiasm components for this example. --> | |
<script src="dataLoader.js"></script> | |
<script src="scatterPlot.js"></script> | |
<!-- Make the Chiasm container fill the page and have a 20px black border. --> | |
<style> | |
body { | |
background-color: black; | |
} | |
#chiasm-container { | |
background-color: white; | |
position: fixed; | |
left: 20px; | |
right: 20px; | |
top: 20px; | |
bottom: 20px; | |
} | |
/* Style the brush. Draws from http://bl.ocks.org/mbostock/4343214 */ | |
.brush .extent { | |
stroke: gray; | |
fill-opacity: .125; | |
shape-rendering: crispEdges; | |
} | |
</style> | |
</head> | |
<body> | |
<!-- Chiasm component instances will be injected into this div. --> | |
<div id="chiasm-container"></div> | |
<script> | |
// Create a new Chiasm instance. | |
var chiasm = new Chiasm(); | |
// Register plugins that the configuration can access. | |
chiasm.plugins.layout = ChiasmLayout; | |
chiasm.plugins.links = ChiasmLinks; | |
chiasm.plugins.dataLoader = DataLoader; | |
chiasm.plugins.scatterPlot = ScatterPlot; | |
// Set the Chaism configuration. | |
chiasm.setConfig({ | |
"layout": { | |
"plugin": "layout", | |
"state": { | |
"containerSelector": "#chiasm-container", | |
"layout": { | |
"orientation": "horizontal", | |
"children": [ | |
"context", | |
"focus" | |
] | |
} | |
} | |
}, | |
"scatterPlotData": { | |
"plugin": "dataLoader", | |
"state": { | |
"path": "scatterPlotData" | |
} | |
}, | |
"focus": { | |
"plugin": "scatterPlot", | |
"state": { | |
"xColumn": "sepal_length", | |
"yColumn": "petal_length" | |
} | |
}, | |
"context": { | |
"plugin": "scatterPlot", | |
"state": { | |
"xColumn": "sepal_length", | |
"yColumn": "petal_length", | |
"brushEnabled": true, | |
"brushIntervalX": [5.3, 6.5], | |
"brushIntervalY": [3.2, 5.6] | |
} | |
}, | |
"links": { | |
"plugin": "links", | |
"state": { | |
"bindings": [ | |
"scatterPlotData.data -> focus.data", | |
"scatterPlotData.data -> context.data", | |
"context.brushIntervalX -> focus.xScaleDomain", | |
"context.brushIntervalY -> focus.yScaleDomain" | |
] | |
} | |
} | |
}); | |
</script> | |
</body> | |
</html> |
The MIT License (MIT) | |
Copyright (c) 2015 Curran Kelleher | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
SOFTWARE. | |
// This is an example Chaism plugin that uses D3 to make a scatter plot. | |
// Draws from this Scatter Plot example http://bl.ocks.org/curran/134ed87c99257e3f2e31 | |
function ScatterPlot() { | |
var my = ChiasmComponent({ | |
margin: { | |
left: 20, | |
top: 20, | |
right: 20, | |
bottom: 20 | |
}, | |
xColumn: Model.None, | |
yColumn: Model.None, | |
xScaleDomain: Model.None, | |
yScaleDomain: Model.None, | |
// "r" stands for radius. | |
rColumn: Model.None, | |
// The circle radius used if rColumn is not specified. | |
rDefault: 10, | |
// The range of the radius scale if rColumn is specified. | |
rMin: 0, | |
rMax: 10, | |
fill: "black", | |
stroke: "none", | |
strokeWidth: "1px", | |
brushEnabled: false, | |
brushIntervalX: Model.None, | |
brushIntervalY: Model.None | |
}); | |
var xScale = d3.scale.linear(); | |
var yScale = d3.scale.linear(); | |
var rScale = d3.scale.sqrt(); | |
var brush = d3.svg.brush() | |
.x(xScale) | |
.y(yScale) | |
.on("brush", onBrush); | |
var svg = d3.select(my.initSVG()); | |
var clipRect = svg | |
.append("clipPath") | |
.attr("id", "clip") | |
.append("rect"); | |
var g = svg.append("g"); | |
var borderRect = g.append("rect") | |
.style("fill", "lightgray") | |
.style("stroke", "black") | |
.style("stroke-width", 1); | |
var circlesG = g.append("g") | |
.style("clip-path", "url(#clip)"); | |
var brushG = g.append("g") | |
.attr("class", "brush"); | |
// Respond to changes in size and margin. | |
// Inspired by D3 margin convention from http://bl.ocks.org/mbostock/3019563 | |
my.when(["box", "margin"], function(box, margin){ | |
my.innerBox = { | |
width: box.width - margin.left - margin.right, | |
height: box.height - margin.top - margin.bottom | |
}; | |
g.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
}); | |
my.when(["innerBox"], function (innerBox){ | |
borderRect | |
.attr("width", innerBox.width) | |
.attr("height", innerBox.height); | |
clipRect | |
.attr("width", innerBox.width) | |
.attr("height", innerBox.height); | |
}); | |
my.when(["data", "innerBox", "xColumn", "xScaleDomain"], | |
function (data, innerBox, xColumn, xScaleDomain){ | |
if(xColumn !== Model.None){ | |
xScale.range([0, innerBox.width]); | |
if(xScaleDomain !== Model.None){ | |
xScale.domain(xScaleDomain); | |
} else { | |
xScale.domain(d3.extent(data, function (d){ return d[xColumn]; })); | |
} | |
my.x = function (d){ return xScale(d[xColumn]); }; | |
} | |
}); | |
my.when(["data", "innerBox", "yColumn", "yScaleDomain"], | |
function (data, innerBox, yColumn, yScaleDomain){ | |
if(yColumn !== Model.None){ | |
if(yScaleDomain !== Model.None){ | |
yScale.domain(yScaleDomain); | |
} else { | |
yScale.domain(d3.extent(data, function (d){ return d[yColumn]; })); | |
} | |
yScale.range([innerBox.height, 0]); | |
my.y = function (d){ return yScale(d[yColumn]); }; | |
} | |
}); | |
// Generate a function or constant for circle radius, | |
// depending on whether or not rColumn is defined. | |
my.when(["data", "rColumn", "rDefault", "rMin", "rMax"], | |
function (data, rColumn, rDefault, rMin, rMax){ | |
if(rColumn === Model.None){ | |
my.r = rDefault; | |
} else { | |
rScale | |
.domain(d3.extent(data, function (d){ return d[rColumn]; })) | |
.range([rMin, rMax]); | |
my.r = function (d){ return rScale(d[rColumn]); }; | |
} | |
}); | |
my.when([ "data", "x", "y", "r", "fill", "stroke", "strokeWidth" ], | |
function (data, x, y, r, fill, stroke, strokeWidth){ | |
// Render the circles of the scatter plot. | |
var circles = circlesG.selectAll("circle").data(data); | |
circles.enter().append("circle"); | |
circles.exit().remove(); | |
circles | |
.attr("cx", x) | |
.attr("cy", y) | |
.attr("r", r) | |
.attr("fill", fill) | |
.attr("stroke", stroke) | |
.attr("stroke-width", strokeWidth); | |
}); | |
my.when("brushEnabled", function (brushEnabled){ | |
brushG.remove(); | |
if(brushEnabled){ | |
g.node().appendChild(brushG.node()); | |
} | |
}); | |
function onBrush() { | |
if(brush.empty()){ | |
my.brushIntervalX = Model.None; | |
my.brushIntervalY = Model.None; | |
} else { | |
var e = brush.extent(); | |
my.brushIntervalX = [e[0][0], e[1][0]]; | |
my.brushIntervalY = [e[0][1], e[1][1]]; | |
} | |
} | |
my.when(["brushIntervalX", "brushIntervalY", "x", "y"], | |
function (brushIntervalX, brushIntervalY){ | |
if(brushIntervalX !== Model.None && brushIntervalY !== Model.None){ | |
brush.extent([ | |
[brushIntervalX[0], brushIntervalY[0]], | |
[brushIntervalX[1], brushIntervalY[1]] | |
]); | |
} | |
brushG.call(brush); | |
}); | |
return my; | |
} |
sepal_length | sepal_width | petal_length | petal_width | class | |
---|---|---|---|---|---|
5.0 | 3.4 | 1.5 | 0.2 | setosa | |
4.8 | 3.4 | 1.6 | 0.2 | setosa | |
5.1 | 3.5 | 1.4 | 0.3 | setosa | |
5.4 | 3.4 | 1.7 | 0.2 | setosa | |
5.0 | 3.4 | 1.6 | 0.4 | setosa | |
4.4 | 3.0 | 1.3 | 0.2 | setosa | |
5.0 | 3.5 | 1.3 | 0.3 | setosa | |
4.6 | 3.2 | 1.4 | 0.2 | setosa | |
6.9 | 3.1 | 4.9 | 1.5 | versicolor | |
5.9 | 3.0 | 4.2 | 1.5 | versicolor | |
5.8 | 2.7 | 4.1 | 1.0 | versicolor | |
6.1 | 2.8 | 4.0 | 1.3 | versicolor | |
5.7 | 2.6 | 3.5 | 1.0 | versicolor | |
5.5 | 2.4 | 3.8 | 1.1 | versicolor | |
5.6 | 3.0 | 4.1 | 1.3 | versicolor | |
5.8 | 2.6 | 4.0 | 1.2 | versicolor | |
5.1 | 2.5 | 3.0 | 1.1 | versicolor | |
5.8 | 2.7 | 5.1 | 1.9 | virginica | |
7.3 | 2.9 | 6.3 | 1.8 | virginica | |
6.8 | 3.0 | 5.5 | 2.1 | virginica | |
6.0 | 2.2 | 5.0 | 1.5 | virginica | |
5.6 | 2.8 | 4.9 | 2.0 | virginica | |
6.3 | 2.8 | 5.1 | 1.5 | virginica | |
6.9 | 3.1 | 5.4 | 2.1 | virginica | |
6.2 | 3.4 | 5.4 | 2.3 | virginica |
{ | |
"columns": [ | |
{ "name": "sepal_length", "type": "number" }, | |
{ "name": "sepal_width", "type": "number" }, | |
{ "name": "petal_length", "type": "number" }, | |
{ "name": "petal_width", "type": "number" }, | |
{ "name": "class", "type": "string" } | |
] | |
} |