// 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, colorColumn: 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, fillDefault: "black", stroke: "none", strokeWidth: "1px", borderRectFill: "lightgray", brushEnabled: false, brushIntervalX: Model.None, brushIntervalY: Model.None }); var xScale = d3.scale.linear(); var yScale = d3.scale.linear(); var rScale = d3.scale.sqrt(); var colorScale = d3.scale.category10(); 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("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", "colorColumn"], function (data, colorColumn){ if(colorColumn === Model.None){ my.fill = fillDefault; } else { colorScale .domain(data.map(function(d){ return d[colorColumn] })); my.fill = function (d){ return colorScale(d[colorColumn]) } } }); my.when(["borderRectFill"], function (borderRectFill){ borderRect.style("fill", borderRectFill); }) 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; }