# vogievetsky/IntroD3

### Subversion checkout URL

You can clone with HTTPS or Subversion.

authored
984 d3/d3.chart.js
 @@ -0,0 +1,984 @@ +(function(){d3.chart = {}; +// Inspired by http://informationandvisualization.de/blog/box-plot +d3.chart.box = function() { + var width = 1, + height = 1, + duration = 0, + domain = null, + value = Number, + whiskers = d3_chart_boxWhiskers, + quartiles = d3_chart_boxQuartiles, + tickFormat = null; + + // For each small multiple… + function box(g) { + g.each(function(d, i) { + d = d.map(value).sort(d3.ascending); + var g = d3.select(this), + n = d.length, + min = d[0], + max = d[n - 1]; + + // Compute quartiles. Must return exactly 3 elements. + var quartileData = d.quartiles = quartiles(d); + + // Compute whiskers. Must return exactly 2 elements, or null. + var whiskerIndices = whiskers && whiskers.call(this, d, i), + whiskerData = whiskerIndices && whiskerIndices.map(function(i) { return d[i]; }); + + // Compute outliers. If no whiskers are specified, all data are "outliers". + // We compute the outliers as indices, so that we can join across transitions! + var outlierIndices = whiskerIndices + ? d3.range(0, whiskerIndices[0]).concat(d3.range(whiskerIndices[1] + 1, n)) + : d3.range(n); + + // Compute the new x-scale. + var x1 = d3.scale.linear() + .domain(domain && domain.call(this, d, i) || [min, max]) + .range([height, 0]); + + // Retrieve the old x-scale, if this is an update. + var x0 = this.__chart__ || d3.scale.linear() + .domain([0, Infinity]) + .range(x1.range()); + + // Stash the new scale. + this.__chart__ = x1; + + // Note: the box, median, and box tick elements are fixed in number, + // so we only have to handle enter and update. In contrast, the outliers + // and other elements are variable, so we need to exit them! Variable + // elements also fade in and out. + + // Update center line: the vertical line spanning the whiskers. + var center = g.selectAll("line.center") + .data(whiskerData ? [whiskerData] : []); + + center.enter().insert("svg:line", "rect") + .attr("class", "center") + .attr("x1", width / 2) + .attr("y1", function(d) { return x0(d[0]); }) + .attr("x2", width / 2) + .attr("y2", function(d) { return x0(d[1]); }) + .style("opacity", 1e-6) + .transition() + .duration(duration) + .style("opacity", 1) + .attr("y1", function(d) { return x1(d[0]); }) + .attr("y2", function(d) { return x1(d[1]); }); + + center.transition() + .duration(duration) + .style("opacity", 1) + .attr("y1", function(d) { return x1(d[0]); }) + .attr("y2", function(d) { return x1(d[1]); }); + + center.exit().transition() + .duration(duration) + .style("opacity", 1e-6) + .attr("y1", function(d) { return x1(d[0]); }) + .attr("y2", function(d) { return x1(d[1]); }) + .remove(); + + // Update innerquartile box. + var box = g.selectAll("rect.box") + .data([quartileData]); + + box.enter().append("svg:rect") + .attr("class", "box") + .attr("x", 0) + .attr("y", function(d) { return x0(d[2]); }) + .attr("width", width) + .attr("height", function(d) { return x0(d[0]) - x0(d[2]); }) + .transition() + .duration(duration) + .attr("y", function(d) { return x1(d[2]); }) + .attr("height", function(d) { return x1(d[0]) - x1(d[2]); }); + + box.transition() + .duration(duration) + .attr("y", function(d) { return x1(d[2]); }) + .attr("height", function(d) { return x1(d[0]) - x1(d[2]); }); + + // Update median line. + var medianLine = g.selectAll("line.median") + .data([quartileData[1]]); + + medianLine.enter().append("svg:line") + .attr("class", "median") + .attr("x1", 0) + .attr("y1", x0) + .attr("x2", width) + .attr("y2", x0) + .transition() + .duration(duration) + .attr("y1", x1) + .attr("y2", x1); + + medianLine.transition() + .duration(duration) + .attr("y1", x1) + .attr("y2", x1); + + // Update whiskers. + var whisker = g.selectAll("line.whisker") + .data(whiskerData || []); + + whisker.enter().insert("svg:line", "circle, text") + .attr("class", "whisker") + .attr("x1", 0) + .attr("y1", x0) + .attr("x2", width) + .attr("y2", x0) + .style("opacity", 1e-6) + .transition() + .duration(duration) + .attr("y1", x1) + .attr("y2", x1) + .style("opacity", 1); + + whisker.transition() + .duration(duration) + .attr("y1", x1) + .attr("y2", x1) + .style("opacity", 1); + + whisker.exit().transition() + .duration(duration) + .attr("y1", x1) + .attr("y2", x1) + .style("opacity", 1e-6) + .remove(); + + // Update outliers. + var outlier = g.selectAll("circle.outlier") + .data(outlierIndices, Number); + + outlier.enter().insert("svg:circle", "text") + .attr("class", "outlier") + .attr("r", 5) + .attr("cx", width / 2) + .attr("cy", function(i) { return x0(d[i]); }) + .style("opacity", 1e-6) + .transition() + .duration(duration) + .attr("cy", function(i) { return x1(d[i]); }) + .style("opacity", 1); + + outlier.transition() + .duration(duration) + .attr("cy", function(i) { return x1(d[i]); }) + .style("opacity", 1); + + outlier.exit().transition() + .duration(duration) + .attr("cy", function(i) { return x1(d[i]); }) + .style("opacity", 1e-6) + .remove(); + + // Compute the tick format. + var format = tickFormat || x1.tickFormat(8); + + // Update box ticks. + var boxTick = g.selectAll("text.box") + .data(quartileData); + + boxTick.enter().append("svg:text") + .attr("class", "box") + .attr("dy", ".3em") + .attr("dx", function(d, i) { return i & 1 ? 6 : -6 }) + .attr("x", function(d, i) { return i & 1 ? width : 0 }) + .attr("y", x0) + .attr("text-anchor", function(d, i) { return i & 1 ? "start" : "end"; }) + .text(format) + .transition() + .duration(duration) + .attr("y", x1); + + boxTick.transition() + .duration(duration) + .text(format) + .attr("y", x1); + + // Update whisker ticks. These are handled separately from the box + // ticks because they may or may not exist, and we want don't want + // to join box ticks pre-transition with whisker ticks post-. + var whiskerTick = g.selectAll("text.whisker") + .data(whiskerData || []); + + whiskerTick.enter().append("svg:text") + .attr("class", "whisker") + .attr("dy", ".3em") + .attr("dx", 6) + .attr("x", width) + .attr("y", x0) + .text(format) + .style("opacity", 1e-6) + .transition() + .duration(duration) + .attr("y", x1) + .style("opacity", 1); + + whiskerTick.transition() + .duration(duration) + .text(format) + .attr("y", x1) + .style("opacity", 1); + + whiskerTick.exit().transition() + .duration(duration) + .attr("y", x1) + .style("opacity", 1e-6) + .remove(); + }); + d3.timer.flush(); + } + + box.width = function(x) { + if (!arguments.length) return width; + width = x; + return box; + }; + + box.height = function(x) { + if (!arguments.length) return height; + height = x; + return box; + }; + + box.tickFormat = function(x) { + if (!arguments.length) return tickFormat; + tickFormat = x; + return box; + }; + + box.duration = function(x) { + if (!arguments.length) return duration; + duration = x; + return box; + }; + + box.domain = function(x) { + if (!arguments.length) return domain; + domain = x == null ? x : d3.functor(x); + return box; + }; + + box.value = function(x) { + if (!arguments.length) return value; + value = x; + return box; + }; + + box.whiskers = function(x) { + if (!arguments.length) return whiskers; + whiskers = x; + return box; + }; + + box.quartiles = function(x) { + if (!arguments.length) return quartiles; + quartiles = x; + return box; + }; + + return box; +}; + +function d3_chart_boxWhiskers(d) { + return [0, d.length - 1]; +} + +function d3_chart_boxQuartiles(d) { + return [ + d3.quantile(d, .25), + d3.quantile(d, .5), + d3.quantile(d, .75) + ]; +} +// Chart design based on the recommendations of Stephen Few. Implementation +// based on the work of Clint Ivy, Jamie Love, and Jason Davies. +// http://projects.instantcognition.com/protovis/bulletchart/ +d3.chart.bullet = function() { + var orient = "left", // TODO top & bottom + reverse = false, + duration = 0, + ranges = d3_chart_bulletRanges, + markers = d3_chart_bulletMarkers, + measures = d3_chart_bulletMeasures, + width = 380, + height = 30, + tickFormat = null; + + // For each small multiple… + function bullet(g) { + g.each(function(d, i) { + var rangez = ranges.call(this, d, i).slice().sort(d3.descending), + markerz = markers.call(this, d, i).slice().sort(d3.descending), + measurez = measures.call(this, d, i).slice().sort(d3.descending), + g = d3.select(this); + + // Compute the new x-scale. + var x1 = d3.scale.linear() + .domain([0, Math.max(rangez[0], markerz[0], measurez[0])]) + .range(reverse ? [width, 0] : [0, width]); + + // Retrieve the old x-scale, if this is an update. + var x0 = this.__chart__ || d3.scale.linear() + .domain([0, Infinity]) + .range(x1.range()); + + // Stash the new scale. + this.__chart__ = x1; + + // Derive width-scales from the x-scales. + var w0 = d3_chart_bulletWidth(x0), + w1 = d3_chart_bulletWidth(x1); + + // Update the range rects. + var range = g.selectAll("rect.range") + .data(rangez); + + range.enter().append("svg:rect") + .attr("class", function(d, i) { return "range s" + i; }) + .attr("width", w0) + .attr("height", height) + .attr("x", reverse ? x0 : 0) + .transition() + .duration(duration) + .attr("width", w1) + .attr("x", reverse ? x1 : 0); + + range.transition() + .duration(duration) + .attr("x", reverse ? x1 : 0) + .attr("width", w1) + .attr("height", height); + + // Update the measure rects. + var measure = g.selectAll("rect.measure") + .data(measurez); + + measure.enter().append("svg:rect") + .attr("class", function(d, i) { return "measure s" + i; }) + .attr("width", w0) + .attr("height", height / 3) + .attr("x", reverse ? x0 : 0) + .attr("y", height / 3) + .transition() + .duration(duration) + .attr("width", w1) + .attr("x", reverse ? x1 : 0); + + measure.transition() + .duration(duration) + .attr("width", w1) + .attr("height", height / 3) + .attr("x", reverse ? x1 : 0) + .attr("y", height / 3); + + // Update the marker lines. + var marker = g.selectAll("line.marker") + .data(markerz); + + marker.enter().append("svg:line") + .attr("class", "marker") + .attr("x1", x0) + .attr("x2", x0) + .attr("y1", height / 6) + .attr("y2", height * 5 / 6) + .transition() + .duration(duration) + .attr("x1", x1) + .attr("x2", x1); + + marker.transition() + .duration(duration) + .attr("x1", x1) + .attr("x2", x1) + .attr("y1", height / 6) + .attr("y2", height * 5 / 6); + + // Compute the tick format. + var format = tickFormat || x1.tickFormat(8); + + // Update the tick groups. + var tick = g.selectAll("g.tick") + .data(x1.ticks(8), function(d) { + return this.textContent || format(d); + }); + + // Initialize the ticks with the old scale, x0. + var tickEnter = tick.enter().append("svg:g") + .attr("class", "tick") + .attr("transform", d3_chart_bulletTranslate(x0)) + .style("opacity", 1e-6); + + tickEnter.append("svg:line") + .attr("y1", height) + .attr("y2", height * 7 / 6); + + tickEnter.append("svg:text") + .attr("text-anchor", "middle") + .attr("dy", "1em") + .attr("y", height * 7 / 6) + .text(format); + + // Transition the entering ticks to the new scale, x1. + tickEnter.transition() + .duration(duration) + .attr("transform", d3_chart_bulletTranslate(x1)) + .style("opacity", 1); + + // Transition the updating ticks to the new scale, x1. + var tickUpdate = tick.transition() + .duration(duration) + .attr("transform", d3_chart_bulletTranslate(x1)) + .style("opacity", 1); + + tickUpdate.select("line") + .attr("y1", height) + .attr("y2", height * 7 / 6); + + tickUpdate.select("text") + .attr("y", height * 7 / 6); + + // Transition the exiting ticks to the new scale, x1. + tick.exit().transition() + .duration(duration) + .attr("transform", d3_chart_bulletTranslate(x1)) + .style("opacity", 1e-6) + .remove(); + }); + d3.timer.flush(); + } + + // left, right, top, bottom + bullet.orient = function(x) { + if (!arguments.length) return orient; + orient = x; + reverse = orient == "right" || orient == "bottom"; + return bullet; + }; + + // ranges (bad, satisfactory, good) + bullet.ranges = function(x) { + if (!arguments.length) return ranges; + ranges = x; + return bullet; + }; + + // markers (previous, goal) + bullet.markers = function(x) { + if (!arguments.length) return markers; + markers = x; + return bullet; + }; + + // measures (actual, forecast) + bullet.measures = function(x) { + if (!arguments.length) return measures; + measures = x; + return bullet; + }; + + bullet.width = function(x) { + if (!arguments.length) return width; + width = x; + return bullet; + }; + + bullet.height = function(x) { + if (!arguments.length) return height; + height = x; + return bullet; + }; + + bullet.tickFormat = function(x) { + if (!arguments.length) return tickFormat; + tickFormat = x; + return bullet; + }; + + bullet.duration = function(x) { + if (!arguments.length) return duration; + duration = x; + return bullet; + }; + + return bullet; +}; + +function d3_chart_bulletRanges(d) { + return d.ranges; +} + +function d3_chart_bulletMarkers(d) { + return d.markers; +} + +function d3_chart_bulletMeasures(d) { + return d.measures; +} + +function d3_chart_bulletTranslate(x) { + return function(d) { + return "translate(" + x(d) + ",0)"; + }; +} + +function d3_chart_bulletWidth(x) { + var x0 = x(0); + return function(d) { + return Math.abs(x(d) - x0); + }; +} +// Implements a horizon layout, which is a variation of a single-series +// area chart where the area is folded into multiple bands. Color is used to +// encode band, allowing the size of the chart to be reduced significantly +// without impeding readability. This layout algorithm is based on the work of +// J. Heer, N. Kong and M. Agrawala in "Sizing the Horizon: The Effects of Chart +// Size and Layering on the Graphical Perception of Time Series Visualizations", +// CHI 2009. http://hci.stanford.edu/publications/2009/heer-horizon-chi09.pdf +d3.chart.horizon = function() { + var bands = 1, // between 1 and 5, typically + mode = "offset", // or mirror + interpolate = "linear", // or basis, monotone, step-before, etc. + x = d3_chart_horizonX, + y = d3_chart_horizonY, + w = 960, + h = 40, + duration = 0; + + var color = d3.scale.linear() + .domain([-1, 0, 1]) + .range(["#d62728", "#fff", "#1f77b4"]); + + // For each small multiple… + function horizon(g) { + g.each(function(d, i) { + var g = d3.select(this), + n = 2 * bands + 1, + xMin = Infinity, + xMax = -Infinity, + yMax = -Infinity, + x0, // old x-scale + y0, // old y-scale + id; // unique id for paths + + // Compute x- and y-values along with extents. + var data = d.map(function(d, i) { + var xv = x.call(this, d, i), + yv = y.call(this, d, i); + if (xv < xMin) xMin = xv; + if (xv > xMax) xMax = xv; + if (-yv > yMax) yMax = -yv; + if (yv > yMax) yMax = yv; + return [xv, yv]; + }); + + // Compute the new x- and y-scales. + var x1 = d3.scale.linear().domain([xMin, xMax]).range([0, w]), + y1 = d3.scale.linear().domain([0, yMax]).range([0, h * bands]); + + // Retrieve the old scales, if this is an update. + if (this.__chart__) { + x0 = this.__chart__.x; + y0 = this.__chart__.y; + id = this.__chart__.id; + } else { + x0 = d3.scale.linear().domain([0, Infinity]).range(x1.range()); + y0 = d3.scale.linear().domain([0, Infinity]).range(y1.range()); + id = ++d3_chart_horizonId; + } + + // We'll use a defs to store the area path and the clip path. + var defs = g.selectAll("defs") + .data([data]); + + var defsEnter = defs.enter().append("svg:defs"); + + // The clip path is a simple rect. + defsEnter.append("svg:clipPath") + .attr("id", "d3_chart_horizon_clip" + id) + .append("svg:rect") + .attr("width", w) + .attr("height", h); + + defs.select("rect").transition() + .duration(duration) + .attr("width", w) + .attr("height", h); + + // The area path is rendered with our resuable d3.svg.area. + defsEnter.append("svg:path") + .attr("id", "d3_chart_horizon_path" + id) + .attr("d", d3_chart_horizonArea + .interpolate(interpolate) + .x(function(d) { return x0(d[0]); }) + .y0(h * bands) + .y1(function(d) { return h * bands - y0(d[1]); })) + .transition() + .duration(duration) + .attr("d", d3_chart_horizonArea + .x(function(d) { return x1(d[0]); }) + .y1(function(d) { return h * bands - y1(d[1]); })); + + defs.select("path").transition() + .duration(duration) + .attr("d", d3_chart_horizonArea); + + // We'll use a container to clip all horizon layers at once. + g.selectAll("g") + .data([null]) + .enter().append("svg:g") + .attr("clip-path", "url(#d3_chart_horizon_clip" + id + ")"); + + // Define the transform function based on the mode. + var transform = mode == "offset" + ? function(d) { return "translate(0," + (d + (d < 0) - bands) * h + ")"; } + : function(d) { return (d < 0 ? "scale(1,-1)" : "") + "translate(0," + (d - bands) * h + ")"; }; + + // Instantiate each copy of the path with different transforms. + var u = g.select("g").selectAll("use") + .data(d3.range(-1, -bands - 1, -1).concat(d3.range(1, bands + 1)), Number); + + // TODO don't fudge the enter transition + u.enter().append("svg:use") + .attr("xlink:href", "#d3_chart_horizon_path" + id) + .attr("transform", function(d) { return transform(d + (d > 0 ? 1 : -1)); }) + .style("fill", color) + .transition() + .duration(duration) + .attr("transform", transform); + + u.transition() + .duration(duration) + .attr("transform", transform) + .style("fill", color); + + u.exit().transition() + .duration(duration) + .attr("transform", transform) + .remove(); + + // Stash the new scales. + this.__chart__ = {x: x1, y: y1, id: id}; + }); + d3.timer.flush(); + } + + horizon.duration = function(x) { + if (!arguments.length) return duration; + duration = +x; + return horizon; + }; + + horizon.bands = function(x) { + if (!arguments.length) return bands; + bands = +x; + color.domain([-bands, 0, bands]); + return horizon; + }; + + horizon.mode = function(x) { + if (!arguments.length) return mode; + mode = x + ""; + return horizon; + }; + + horizon.colors = function(x) { + if (!arguments.length) return color.range(); + color.range(x); + return horizon; + }; + + horizon.interpolate = function(x) { + if (!arguments.length) return interpolate; + interpolate = x + ""; + return horizon; + }; + + horizon.x = function(z) { + if (!arguments.length) return x; + x = z; + return horizon; + }; + + horizon.y = function(z) { + if (!arguments.length) return y; + y = z; + return horizon; + }; + + horizon.width = function(x) { + if (!arguments.length) return w; + w = +x; + return horizon; + }; + + horizon.height = function(x) { + if (!arguments.length) return h; + h = +x; + return horizon; + }; + + return horizon; +}; + +var d3_chart_horizonArea = d3.svg.area(), + d3_chart_horizonId = 0; + +function d3_chart_horizonX(d) { + return d[0]; +} + +function d3_chart_horizonY(d) { + return d[1]; +} +// Based on http://vis.stanford.edu/protovis/ex/qqplot.html +d3.chart.qq = function() { + var width = 1, + height = 1, + duration = 0, + domain = null, + tickFormat = null, + n = 100, + x = d3_chart_qqX, + y = d3_chart_qqY; + + // For each small multiple… + function qq(g) { + g.each(function(d, i) { + var g = d3.select(this), + qx = d3_chart_qqQuantiles(n, x.call(this, d, i)), + qy = d3_chart_qqQuantiles(n, y.call(this, d, i)), + xd = domain && domain.call(this, d, i) || [d3.min(qx), d3.max(qx)], // new x-domain + yd = domain && domain.call(this, d, i) || [d3.min(qy), d3.max(qy)], // new y-domain + x0, // old x-scale + y0; // old y-scale + + // Compute the new x-scale. + var x1 = d3.scale.linear() + .domain(xd) + .range([0, width]); + + // Compute the new y-scale. + var y1 = d3.scale.linear() + .domain(yd) + .range([height, 0]); + + // Retrieve the old scales, if this is an update. + if (this.__chart__) { + x0 = this.__chart__.x; + y0 = this.__chart__.y; + } else { + x0 = d3.scale.linear().domain([0, Infinity]).range(x1.range()); + y0 = d3.scale.linear().domain([0, Infinity]).range(y1.range()); + } + + // Stash the new scales. + this.__chart__ = {x: x1, y: y1}; + + // Update diagonal line. + var diagonal = g.selectAll("line.diagonal") + .data([null]); + + diagonal.enter().append("svg:line") + .attr("class", "diagonal") + .attr("x1", x1(yd[0])) + .attr("y1", y1(xd[0])) + .attr("x2", x1(yd[1])) + .attr("y2", y1(xd[1])); + + diagonal.transition() + .duration(duration) + .attr("x1", x1(yd[0])) + .attr("y1", y1(xd[0])) + .attr("x2", x1(yd[1])) + .attr("y2", y1(xd[1])); + + // Update quantile plots. + var circle = g.selectAll("circle") + .data(d3.range(n).map(function(i) { + return {x: qx[i], y: qy[i]}; + })); + + circle.enter().append("svg:circle") + .attr("class", "quantile") + .attr("r", 4.5) + .attr("cx", function(d) { return x0(d.x); }) + .attr("cy", function(d) { return y0(d.y); }) + .style("opacity", 1e-6) + .transition() + .duration(duration) + .attr("cx", function(d) { return x1(d.x); }) + .attr("cy", function(d) { return y1(d.y); }) + .style("opacity", 1); + + circle.transition() + .duration(duration) + .attr("cx", function(d) { return x1(d.x); }) + .attr("cy", function(d) { return y1(d.y); }) + .style("opacity", 1); + + circle.exit().transition() + .duration(duration) + .attr("cx", function(d) { return x1(d.x); }) + .attr("cy", function(d) { return y1(d.y); }) + .style("opacity", 1e-6) + .remove(); + + var xformat = tickFormat || x1.tickFormat(4), + yformat = tickFormat || y1.tickFormat(4), + tx = function(d) { return "translate(" + x1(d) + "," + height + ")"; }, + ty = function(d) { return "translate(0," + y1(d) + ")"; }; + + // Update x-ticks. + var xtick = g.selectAll("g.x.tick") + .data(x1.ticks(4), function(d) { + return this.textContent || xformat(d); + }); + + var xtickEnter = xtick.enter().append("svg:g") + .attr("class", "x tick") + .attr("transform", function(d) { return "translate(" + x0(d) + "," + height + ")"; }) + .style("opacity", 1e-6); + + xtickEnter.append("svg:line") + .attr("y1", 0) + .attr("y2", -6); + + xtickEnter.append("svg:text") + .attr("text-anchor", "middle") + .attr("dy", "1em") + .text(xformat); + + // Transition the entering ticks to the new scale, x1. + xtickEnter.transition() + .duration(duration) + .attr("transform", tx) + .style("opacity", 1); + + // Transition the updating ticks to the new scale, x1. + xtick.transition() + .duration(duration) + .attr("transform", tx) + .style("opacity", 1); + + // Transition the exiting ticks to the new scale, x1. + xtick.exit().transition() + .duration(duration) + .attr("transform", tx) + .style("opacity", 1e-6) + .remove(); + + // Update ticks. + var ytick = g.selectAll("g.y.tick") + .data(y1.ticks(4), function(d) { + return this.textContent || yformat(d); + }); + + var ytickEnter = ytick.enter().append("svg:g") + .attr("class", "y tick") + .attr("transform", function(d) { return "translate(0," + y0(d) + ")"; }) + .style("opacity", 1e-6); + + ytickEnter.append("svg:line") + .attr("x1", 0) + .attr("x2", 6); + + ytickEnter.append("svg:text") + .attr("text-anchor", "end") + .attr("dx", "-.5em") + .attr("dy", ".3em") + .text(yformat); + + // Transition the entering ticks to the new scale, y1. + ytickEnter.transition() + .duration(duration) + .attr("transform", ty) + .style("opacity", 1); + + // Transition the updating ticks to the new scale, y1. + ytick.transition() + .duration(duration) + .attr("transform", ty) + .style("opacity", 1); + + // Transition the exiting ticks to the new scale, y1. + ytick.exit().transition() + .duration(duration) + .attr("transform", ty) + .style("opacity", 1e-6) + .remove(); + }); + } + + qq.width = function(x) { + if (!arguments.length) return width; + width = x; + return qq; + }; + + qq.height = function(x) { + if (!arguments.length) return height; + height = x; + return qq; + }; + + qq.duration = function(x) { + if (!arguments.length) return duration; + duration = x; + return qq; + }; + + qq.domain = function(x) { + if (!arguments.length) return domain; + domain = x == null ? x : d3.functor(x); + return qq; + }; + + qq.count = function(z) { + if (!arguments.length) return n; + n = z; + return qq; + }; + + qq.x = function(z) { + if (!arguments.length) return x; + x = z; + return qq; + }; + + qq.y = function(z) { + if (!arguments.length) return y; + y = z; + return qq; + }; + + qq.tickFormat = function(x) { + if (!arguments.length) return tickFormat; + tickFormat = x; + return qq; + }; + + return qq; +}; + +function d3_chart_qqQuantiles(n, values) { + var m = values.length - 1; + values = values.slice().sort(d3.ascending); + return d3.range(n).map(function(i) { + return values[~~(i * m / n)]; + }); +} + +function d3_chart_qqX(d) { + return d.x; +} + +function d3_chart_qqY(d) { + return d.y; +} +})();
1  d3/d3.chart.min.js
 @@ -0,0 +1 @@ +(function(){function a(a){return[0,a.length-1]}function b(a){return[d3.quantile(a,.25),d3.quantile(a,.5),d3.quantile(a,.75)]}function c(a){return a.ranges}function d(a){return a.markers}function e(a){return a.measures}function f(a){return function(b){return"translate("+a(b)+",0)"}}function g(a){var b=a(0);return function(c){return Math.abs(a(c)-b)}}function j(a){return a[0]}function k(a){return a[1]}function l(a,b){var c=b.length-1;return b=b.slice().sort(d3.ascending),d3.range(a).map(function(d){return b[~~(d*c/a)]})}function m(a){return a.x}function n(a){return a.y}d3.chart={},d3.chart.box=function(){function k(a){a.each(function(a,b){a=a.map(g).sort(d3.ascending);var k=d3.select(this),l=a.length,m=a[0],n=a[l-1],o=a.quartiles=i(a),p=h&&h.call(this,a,b),q=p&&p.map(function(b){return a[b]}),r=p?d3.range(0,p[0]).concat(d3.range(p[1]+1,l)):d3.range(l),s=d3.scale.linear().domain(f&&f.call(this,a,b)||[m,n]).range([d,0]),t=this.__chart__||d3.scale.linear().domain([0,Infinity]).range(s.range());this.__chart__=s;var u=k.selectAll("line.center").data(q?[q]:[]);u.enter().insert("svg:line","rect").attr("class","center").attr("x1",c/2).attr("y1",function(a){return t(a[0])}).attr("x2",c/2).attr("y2",function(a){return t(a[1])}).style("opacity",1e-6).transition().duration(e).style("opacity",1).attr("y1",function(a){return s(a[0])}).attr("y2",function(a){return s(a[1])}),u.transition().duration(e).style("opacity",1).attr("y1",function(a){return s(a[0])}).attr("y2",function(a){return s(a[1])}),u.exit().transition().duration(e).style("opacity",1e-6).attr("y1",function(a){return s(a[0])}).attr("y2",function(a){return s(a[1])}).remove();var v=k.selectAll("rect.box").data([o]);v.enter().append("svg:rect").attr("class","box").attr("x",0).attr("y",function(a){return t(a[2])}).attr("width",c).attr("height",function(a){return t(a[0])-t(a[2])}).transition().duration(e).attr("y",function(a){return s(a[2])}).attr("height",function(a){return s(a[0])-s(a[2])}),v.transition().duration(e).attr("y",function(a){return s(a[2])}).attr("height",function(a){return s(a[0])-s(a[2])});var w=k.selectAll("line.median").data([o[1]]);w.enter().append("svg:line").attr("class","median").attr("x1",0).attr("y1",t).attr("x2",c).attr("y2",t).transition().duration(e).attr("y1",s).attr("y2",s),w.transition().duration(e).attr("y1",s).attr("y2",s);var x=k.selectAll("line.whisker").data(q||[]);x.enter().insert("svg:line","circle, text").attr("class","whisker").attr("x1",0).attr("y1",t).attr("x2",c).attr("y2",t).style("opacity",1e-6).transition().duration(e).attr("y1",s).attr("y2",s).style("opacity",1),x.transition().duration(e).attr("y1",s).attr("y2",s).style("opacity",1),x.exit().transition().duration(e).attr("y1",s).attr("y2",s).style("opacity",1e-6).remove();var y=k.selectAll("circle.outlier").data(r,Number);y.enter().insert("svg:circle","text").attr("class","outlier").attr("r",5).attr("cx",c/2).attr("cy",function(b){return t(a[b])}).style("opacity",1e-6).transition().duration(e).attr("cy",function(b){return s(a[b])}).style("opacity",1),y.transition().duration(e).attr("cy",function(b){return s(a[b])}).style("opacity",1),y.exit().transition().duration(e).attr("cy",function(b){return s(a[b])}).style("opacity",1e-6).remove();var z=j||s.tickFormat(8),A=k.selectAll("text.box").data(o);A.enter().append("svg:text").attr("class","box").attr("dy",".3em").attr("dx",function(a,b){return b&1?6:-6}).attr("x",function(a,b){return b&1?c:0}).attr("y",t).attr("text-anchor",function(a,b){return b&1?"start":"end"}).text(z).transition().duration(e).attr("y",s),A.transition().duration(e).text(z).attr("y",s);var B=k.selectAll("text.whisker").data(q||[]);B.enter().append("svg:text").attr("class","whisker").attr("dy",".3em").attr("dx",6).attr("x",c).attr("y",t).text(z).style("opacity",1e-6).transition().duration(e).attr("y",s).style("opacity",1),B.transition().duration(e).text(z).attr("y",s).style("opacity",1),B.exit().transition().duration(e).attr("y",s).style("opacity",1e-6).remove()}),d3.timer.flush()}var c=1,d=1,e=0,f=null,g=Number,h=a,i=b,j=null;return k.width=function(a){return arguments.length?(c=a,k):c},k.height=function(a){return arguments.length?(d=a,k):d},k.tickFormat=function(a){return arguments.length?(j=a,k):j},k.duration=function(a){return arguments.length?(e=a,k):e},k.domain=function(a){return arguments.length?(f=a==null?a:d3.functor(a),k):f},k.value=function(a){return arguments.length?(g=a,k):g},k.whiskers=function(a){return arguments.length?(h=a,k):h},k.quartiles=function(a){return arguments.length?(i=a,k):i},k},d3.chart.bullet=function(){function o(a){a.each(function(a,c){var d=i.call(this,a,c).slice().sort(d3.descending),e=j.call(this,a,c).slice().sort(d3.descending),o=k.call(this,a,c).slice().sort(d3.descending),p=d3.select(this),q=d3.scale.linear().domain([0,Math.max(d[0],e[0],o[0])]).range(b?[l,0]:[0,l]),r=this.__chart__||d3.scale.linear().domain([0,Infinity]).range(q.range());this.__chart__=q;var s=g(r),t=g(q),u=p.selectAll("rect.range").data(d);u.enter().append("svg:rect").attr("class",function(a,b){return"range s"+b}).attr("width",s).attr("height",m).attr("x",b?r:0).transition().duration(h).attr("width",t).attr("x",b?q:0),u.transition().duration(h).attr("x",b?q:0).attr("width",t).attr("height",m);var v=p.selectAll("rect.measure").data(o);v.enter().append("svg:rect").attr("class",function(a,b){return"measure s"+b}).attr("width",s).attr("height",m/3).attr("x",b?r:0).attr("y",m/3).transition().duration(h).attr("width",t).attr("x",b?q:0),v.transition().duration(h).attr("width",t).attr("height",m/3).attr("x",b?q:0).attr("y",m/3);var w=p.selectAll("line.marker").data(e);w.enter().append("svg:line").attr("class","marker").attr("x1",r).attr("x2",r).attr("y1",m/6).attr("y2",m*5/6).transition().duration(h).attr("x1",q).attr("x2",q),w.transition().duration(h).attr("x1",q).attr("x2",q).attr("y1",m/6).attr("y2",m*5/6);var x=n||q.tickFormat(8),y=p.selectAll("g.tick").data(q.ticks(8),function(a){return this.textContent||x(a)}),z=y.enter().append("svg:g").attr("class","tick").attr("transform",f(r)).style("opacity",1e-6);z.append("svg:line").attr("y1",m).attr("y2",m*7/6),z.append("svg:text").attr("text-anchor","middle").attr("dy","1em").attr("y",m*7/6).text(x),z.transition().duration(h).attr("transform",f(q)).style("opacity",1);var A=y.transition().duration(h).attr("transform",f(q)).style("opacity",1);A.select("line").attr("y1",m).attr("y2",m*7/6),A.select("text").attr("y",m*7/6),y.exit().transition().duration(h).attr("transform",f(q)).style("opacity",1e-6).remove()}),d3.timer.flush()}var a="left",b=!1,h=0,i=c,j=d,k=e,l=380,m=30,n=null;return o.orient=function(c){return arguments.length?(a=c,b=a=="right"||a=="bottom",o):a},o.ranges=function(a){return arguments.length?(i=a,o):i},o.markers=function(a){return arguments.length?(j=a,o):j},o.measures=function(a){return arguments.length?(k=a,o):k},o.width=function(a){return arguments.length?(l=a,o):l},o.height=function(a){return arguments.length?(m=a,o):m},o.tickFormat=function(a){return arguments.length?(n=a,o):n},o.duration=function(a){return arguments.length?(h=a,o):h},o},d3.chart.horizon=function(){function n(j){j.each(function(j,k){var n=d3.select(this),o=2*a+1,p=Infinity,q=-Infinity,r=-Infinity,s,t,u,v=j.map(function(a,b){var c=d.call(this,a,b),f=e.call(this,a,b);return cq&&(q=c),-f>r&&(r=-f),f>r&&(r=f),[c,f]}),z=d3.scale.linear().domain([p,q]).range([0,f]),A=d3.scale.linear().domain([0,r]).range([0,g*a]);this.__chart__?(s=this.__chart__.x,t=this.__chart__.y,u=this.__chart__.id):(s=d3.scale.linear().domain([0,Infinity]).range(z.range()),t=d3.scale.linear().domain([0,Infinity]).range(A.range()),u=++i);var B=n.selectAll("defs").data([v]),C=B.enter().append("svg:defs");C.append("svg:clipPath").attr("id","d3_chart_horizon_clip"+u).append("svg:rect").attr("width",f).attr("height",g),B.select("rect").transition().duration(l).attr("width",f).attr("height",g),C.append("svg:path").attr("id","d3_chart_horizon_path"+u).attr("d",h.interpolate(c).x(function(a){return s(a[0])}).y0(g*a).y1(function(b){return g*a-t(b[1])})).transition().duration(l).attr("d",h.x(function(a){return z(a[0])}).y1(function(b){return g*a-A(b[1])})),B.select("path").transition().duration(l).attr("d",h),n.selectAll("g").data([null]).enter().append("svg:g").attr("clip-path","url(#d3_chart_horizon_clip"+u+")");var D=b=="offset"?function(b){return"translate(0,"+(b+(b<0)-a)*g+")"}:function(b){return(b<0?"scale(1,-1)":"")+"translate(0,"+(b-a)*g+")"},E=n.select("g").selectAll("use").data(d3.range(-1,-a-1,-1).concat(d3.range(1,a+1)),Number);E.enter().append("svg:use").attr("xlink:href","#d3_chart_horizon_path"+u).attr("transform",function(a){return D(a+(a>0?1:-1))}).style("fill",m).transition().duration(l).attr("transform",D),E.transition().duration(l).attr("transform",D).style("fill",m),E.exit().transition().duration(l).attr("transform",D).remove(),this.__chart__={x:z,y:A,id:u}}),d3.timer.flush()}var a=1,b="offset",c="linear",d=j,e=k,f=960,g=40,l=0,m=d3.scale.linear().domain([-1,0,1]).range(["#d62728","#fff","#1f77b4"]);return n.duration=function(a){return arguments.length?(l=+a,n):l},n.bands=function(b){return arguments.length?(a=+b,m.domain([-a,0,a]),n):a},n.mode=function(a){return arguments.length?(b=a+"",n):b},n.colors=function(a){return arguments.length?(m.range(a),n):m.range()},n.interpolate=function(a){return arguments.length?(c=a+"",n):c},n.x=function(a){return arguments.length?(d=a,n):d},n.y=function(a){return arguments.length?(e=a,n):e},n.width=function(a){return arguments.length?(f=+a,n):f},n.height=function(a){return arguments.length?(g=+a,n):g},n};var h=d3.svg.area(),i=0;d3.chart.qq=function(){function i(i){i.each(function(i,j){var k=d3.select(this),m=l(f,g.call(this,i,j)),n=l(f,h.call(this,i,j)),o=d&&d.call(this,i,j)||[d3.min(m),d3.max(m)],p=d&&d.call(this,i,j)||[d3.min(n),d3.max(n)],q,r,s=d3.scale.linear().domain(o).range([0,a]),t=d3.scale.linear().domain(p).range([b,0]);this.__chart__?(q=this.__chart__.x,r=this.__chart__.y):(q=d3.scale.linear().domain([0,Infinity]).range(s.range()),r=d3.scale.linear().domain([0,Infinity]).range(t.range())),this.__chart__={x:s,y:t};var u=k.selectAll("line.diagonal").data([null]);u.enter().append("svg:line").attr("class","diagonal").attr("x1",s(p[0])).attr("y1",t(o[0])).attr("x2",s(p[1])).attr("y2",t(o[1])),u.transition().duration(c).attr("x1",s(p[0])).attr("y1",t(o[0])).attr("x2",s(p[1])).attr("y2",t(o[1]));var v=k.selectAll("circle").data(d3.range(f).map(function(a){return{x:m[a],y:n[a]}}));v.enter().append("svg:circle").attr("class","quantile").attr("r",4.5).attr("cx",function(a){return q(a.x)}).attr("cy",function(a){return r(a.y)}).style("opacity",1e-6).transition().duration(c).attr("cx",function(a){return s(a.x)}).attr("cy",function(a){return t(a.y)}).style("opacity",1),v.transition().duration(c).attr("cx",function(a){return s(a.x)}).attr("cy",function(a){return t(a.y)}).style("opacity",1),v.exit().transition().duration(c).attr("cx",function(a){return s(a.x)}).attr("cy",function(a){return t(a.y)}).style("opacity",1e-6).remove();var w=e||s.tickFormat(4),z=e||t.tickFormat(4),A=function(a){return"translate("+s(a)+","+b+")"},B=function(a){return"translate(0,"+t(a)+")"},C=k.selectAll("g.x.tick").data(s.ticks(4),function(a){return this.textContent||w(a)}),D=C.enter().append("svg:g").attr("class","x tick").attr("transform",function(a){return"translate("+q(a)+","+b+")"}).style("opacity",1e-6);D.append("svg:line").attr("y1",0).attr("y2",-6),D.append("svg:text").attr("text-anchor","middle").attr("dy","1em").text(w),D.transition().duration(c).attr("transform",A).style("opacity",1),C.transition().duration(c).attr("transform",A).style("opacity",1),C.exit().transition().duration(c).attr("transform",A).style("opacity",1e-6).remove();var E=k.selectAll("g.y.tick").data(t.ticks(4),function(a){return this.textContent||z(a)}),F=E.enter().append("svg:g").attr("class","y tick").attr("transform",function(a){return"translate(0,"+r(a)+")"}).style("opacity",1e-6);F.append("svg:line").attr("x1",0).attr("x2",6),F.append("svg:text").attr("text-anchor","end").attr("dx","-.5em").attr("dy",".3em").text(z),F.transition().duration(c).attr("transform",B).style("opacity",1),E.transition().duration(c).attr("transform",B).style("opacity",1),E.exit().transition().duration(c).attr("transform",B).style("opacity",1e-6).remove()})}var a=1,b=1,c=0,d=null,e=null,f=100,g=m,h=n;return i.width=function(b){return arguments.length?(a=b,i):a},i.height=function(a){return arguments.length?(b=a,i):b},i.duration=function(a){return arguments.length?(c=a,i):c},i.domain=function(a){return arguments.length?(d=a==null?a:d3.functor(a),i):d},i.count=function(a){return arguments.length?(f=a,i):f},i.x=function(a){return arguments.length?(g=a,i):g},i.y=function(a){return arguments.length?(h=a,i):h},i.tickFormat=function(a){return arguments.length?(e=a,i):e},i}})();
92 d3/d3.csv.js
 @@ -0,0 +1,92 @@ +(function(){d3.csv = function(url, callback) { + d3.text(url, "text/csv", function(text) { + callback(text && d3.csv.parse(text)); + }); +}; +d3.csv.parse = function(text) { + var header; + return d3.csv.parseRows(text, function(row, i) { + if (i) { + var o = {}, j = -1, m = header.length; + while (++j < m) o[header[j]] = row[j]; + return o; + } else { + header = row; + return null; + } + }); +}; + +d3.csv.parseRows = function(text, f) { + var EOL = {}, // sentinel value for end-of-line + EOF = {}, // sentinel value for end-of-file + rows = [], // output rows + re = /\r\n|[,\r\n]/g, // field separator regex + n = 0, // the current line number + t, // the current token + eol; // is the current token followed by EOL? + + re.lastIndex = 0; // work-around bug in FF 3.6 + + /** @private Returns the next token. */ + function token() { + if (re.lastIndex >= text.length) return EOF; // special case: end of file + if (eol) { eol = false; return EOL; } // special case: end of line + + // special case: quotes + var j = re.lastIndex; + if (text.charCodeAt(j) === 34) { + var i = j; + while (i++ < text.length) { + if (text.charCodeAt(i) === 34) { + if (text.charCodeAt(i + 1) !== 34) break; + i++; + } + } + re.lastIndex = i + 2; + var c = text.charCodeAt(i + 1); + if (c === 13) { + eol = true; + if (text.charCodeAt(i + 2) === 10) re.lastIndex++; + } else if (c === 10) { + eol = true; + } + return text.substring(j + 1, i).replace(/""/g, "\""); + } + + // common case + var m = re.exec(text); + if (m) { + eol = m[0].charCodeAt(0) !== 44; + return text.substring(j, m.index); + } + re.lastIndex = text.length; + return text.substring(j); + } + + while ((t = token()) !== EOF) { + var a = []; + while ((t !== EOL) && (t !== EOF)) { + a.push(t); + t = token(); + } + if (f && !(a = f(a, n++))) continue; + rows.push(a); + } + + return rows; +}; +d3.csv.format = function(rows) { + return rows.map(d3_csv_formatRow).join("\n"); +}; + +function d3_csv_formatRow(row) { + return row.map(d3_csv_formatValue).join(","); +} + +function d3_csv_formatValue(text) { + return /[",\n]/.test(text) + ? "\"" + text.replace(/\"/g, "\"\"") + "\"" + : text; +} +})();
1  d3/d3.csv.min.js
 @@ -0,0 +1 @@ +(function(){function a(a){return a.map(b).join(",")}function b(a){return/[",\n]/.test(a)?'"'+a.replace(/\"/g,'""')+'"':a}d3.csv=function(a,b){d3.text(a,"text/csv",function(a){b(a&&d3.csv.parse(a))})},d3.csv.parse=function(a){var b;return d3.csv.parseRows(a,function(a,c){if(c){var d={},e=-1,f=b.length;while(++e=a.length)return d;if(i)return i=!1,c;var b=f.lastIndex;if(a.charCodeAt(b)===34){var e=b;while(e++
938 d3/d3.geo.js