d3_rails 2.7.4 → 2.7.5.v2
Sign up to get free protection for your applications and to get access to all the features.
- data/.DS_Store +0 -0
- data/README.md +16 -2
- data/lib/d3_rails/version.rb +1 -1
- data/vendor/.DS_Store +0 -0
- data/vendor/assets/javascripts/LICENSE +12 -13
- data/vendor/assets/javascripts/chart/box.js +297 -0
- data/vendor/assets/javascripts/chart/bullet.js +237 -0
- data/vendor/assets/javascripts/chart/chart.js +1 -0
- data/vendor/assets/javascripts/chart/horizon.js +203 -0
- data/vendor/assets/javascripts/chart/qq.js +245 -0
- data/vendor/assets/javascripts/d3.chart.js +0 -0
- data/vendor/assets/javascripts/d3.geo.js +0 -0
- data/vendor/assets/javascripts/d3.geom.js +0 -0
- data/vendor/assets/javascripts/d3.js +21 -18
- data/vendor/assets/javascripts/d3.layout.js +5 -4
- data/vendor/assets/javascripts/d3.time.js +0 -0
- data/vendor/assets/javascripts/d3_chart_module.js +1 -0
- data/vendor/assets/javascripts/d3_csv.js +0 -0
- data/vendor/assets/javascripts/d3_rails.js +1 -1
- metadata +18 -12
- data/vendor/assets/javascripts/package.json +0 -33
@@ -0,0 +1 @@
|
|
1
|
+
d3.chart = {};
|
@@ -0,0 +1,203 @@
|
|
1
|
+
// Implements a horizon layout, which is a variation of a single-series
|
2
|
+
// area chart where the area is folded into multiple bands. Color is used to
|
3
|
+
// encode band, allowing the size of the chart to be reduced significantly
|
4
|
+
// without impeding readability. This layout algorithm is based on the work of
|
5
|
+
// J. Heer, N. Kong and M. Agrawala in "Sizing the Horizon: The Effects of Chart
|
6
|
+
// Size and Layering on the Graphical Perception of Time Series Visualizations",
|
7
|
+
// CHI 2009. http://hci.stanford.edu/publications/2009/heer-horizon-chi09.pdf
|
8
|
+
d3.chart.horizon = function() {
|
9
|
+
var bands = 1, // between 1 and 5, typically
|
10
|
+
mode = "offset", // or mirror
|
11
|
+
interpolate = "linear", // or basis, monotone, step-before, etc.
|
12
|
+
x = d3_chart_horizonX,
|
13
|
+
y = d3_chart_horizonY,
|
14
|
+
w = 960,
|
15
|
+
h = 40,
|
16
|
+
duration = 0;
|
17
|
+
|
18
|
+
var color = d3.scale.linear()
|
19
|
+
.domain([-1, 0, 1])
|
20
|
+
.range(["#d62728", "#fff", "#1f77b4"]);
|
21
|
+
|
22
|
+
// For each small multiple…
|
23
|
+
function horizon(g) {
|
24
|
+
g.each(function(d, i) {
|
25
|
+
var g = d3.select(this),
|
26
|
+
n = 2 * bands + 1,
|
27
|
+
xMin = Infinity,
|
28
|
+
xMax = -Infinity,
|
29
|
+
yMax = -Infinity,
|
30
|
+
x0, // old x-scale
|
31
|
+
y0, // old y-scale
|
32
|
+
id; // unique id for paths
|
33
|
+
|
34
|
+
// Compute x- and y-values along with extents.
|
35
|
+
var data = d.map(function(d, i) {
|
36
|
+
var xv = x.call(this, d, i),
|
37
|
+
yv = y.call(this, d, i);
|
38
|
+
if (xv < xMin) xMin = xv;
|
39
|
+
if (xv > xMax) xMax = xv;
|
40
|
+
if (-yv > yMax) yMax = -yv;
|
41
|
+
if (yv > yMax) yMax = yv;
|
42
|
+
return [xv, yv];
|
43
|
+
});
|
44
|
+
|
45
|
+
// Compute the new x- and y-scales.
|
46
|
+
var x1 = d3.scale.linear().domain([xMin, xMax]).range([0, w]),
|
47
|
+
y1 = d3.scale.linear().domain([0, yMax]).range([0, h * bands]);
|
48
|
+
|
49
|
+
// Retrieve the old scales, if this is an update.
|
50
|
+
if (this.__chart__) {
|
51
|
+
x0 = this.__chart__.x;
|
52
|
+
y0 = this.__chart__.y;
|
53
|
+
id = this.__chart__.id;
|
54
|
+
} else {
|
55
|
+
x0 = d3.scale.linear().domain([0, Infinity]).range(x1.range());
|
56
|
+
y0 = d3.scale.linear().domain([0, Infinity]).range(y1.range());
|
57
|
+
id = ++d3_chart_horizonId;
|
58
|
+
}
|
59
|
+
|
60
|
+
// We'll use a defs to store the area path and the clip path.
|
61
|
+
var defs = g.selectAll("defs")
|
62
|
+
.data([data]);
|
63
|
+
|
64
|
+
var defsEnter = defs.enter().append("svg:defs");
|
65
|
+
|
66
|
+
// The clip path is a simple rect.
|
67
|
+
defsEnter.append("svg:clipPath")
|
68
|
+
.attr("id", "d3_chart_horizon_clip" + id)
|
69
|
+
.append("svg:rect")
|
70
|
+
.attr("width", w)
|
71
|
+
.attr("height", h);
|
72
|
+
|
73
|
+
defs.select("rect").transition()
|
74
|
+
.duration(duration)
|
75
|
+
.attr("width", w)
|
76
|
+
.attr("height", h);
|
77
|
+
|
78
|
+
// The area path is rendered with our resuable d3.svg.area.
|
79
|
+
defsEnter.append("svg:path")
|
80
|
+
.attr("id", "d3_chart_horizon_path" + id)
|
81
|
+
.attr("d", d3_chart_horizonArea
|
82
|
+
.interpolate(interpolate)
|
83
|
+
.x(function(d) { return x0(d[0]); })
|
84
|
+
.y0(h * bands)
|
85
|
+
.y1(function(d) { return h * bands - y0(d[1]); }))
|
86
|
+
.transition()
|
87
|
+
.duration(duration)
|
88
|
+
.attr("d", d3_chart_horizonArea
|
89
|
+
.x(function(d) { return x1(d[0]); })
|
90
|
+
.y1(function(d) { return h * bands - y1(d[1]); }));
|
91
|
+
|
92
|
+
defs.select("path").transition()
|
93
|
+
.duration(duration)
|
94
|
+
.attr("d", d3_chart_horizonArea);
|
95
|
+
|
96
|
+
// We'll use a container to clip all horizon layers at once.
|
97
|
+
g.selectAll("g")
|
98
|
+
.data([null])
|
99
|
+
.enter().append("svg:g")
|
100
|
+
.attr("clip-path", "url(#d3_chart_horizon_clip" + id + ")");
|
101
|
+
|
102
|
+
// Define the transform function based on the mode.
|
103
|
+
var transform = mode == "offset"
|
104
|
+
? function(d) { return "translate(0," + (d + (d < 0) - bands) * h + ")"; }
|
105
|
+
: function(d) { return (d < 0 ? "scale(1,-1)" : "") + "translate(0," + (d - bands) * h + ")"; };
|
106
|
+
|
107
|
+
// Instantiate each copy of the path with different transforms.
|
108
|
+
var u = g.select("g").selectAll("use")
|
109
|
+
.data(d3.range(-1, -bands - 1, -1).concat(d3.range(1, bands + 1)), Number);
|
110
|
+
|
111
|
+
// TODO don't fudge the enter transition
|
112
|
+
u.enter().append("svg:use")
|
113
|
+
.attr("xlink:href", "#d3_chart_horizon_path" + id)
|
114
|
+
.attr("transform", function(d) { return transform(d + (d > 0 ? 1 : -1)); })
|
115
|
+
.style("fill", color)
|
116
|
+
.transition()
|
117
|
+
.duration(duration)
|
118
|
+
.attr("transform", transform);
|
119
|
+
|
120
|
+
u.transition()
|
121
|
+
.duration(duration)
|
122
|
+
.attr("transform", transform)
|
123
|
+
.style("fill", color);
|
124
|
+
|
125
|
+
u.exit().transition()
|
126
|
+
.duration(duration)
|
127
|
+
.attr("transform", transform)
|
128
|
+
.remove();
|
129
|
+
|
130
|
+
// Stash the new scales.
|
131
|
+
this.__chart__ = {x: x1, y: y1, id: id};
|
132
|
+
});
|
133
|
+
d3.timer.flush();
|
134
|
+
}
|
135
|
+
|
136
|
+
horizon.duration = function(x) {
|
137
|
+
if (!arguments.length) return duration;
|
138
|
+
duration = +x;
|
139
|
+
return horizon;
|
140
|
+
};
|
141
|
+
|
142
|
+
horizon.bands = function(x) {
|
143
|
+
if (!arguments.length) return bands;
|
144
|
+
bands = +x;
|
145
|
+
color.domain([-bands, 0, bands]);
|
146
|
+
return horizon;
|
147
|
+
};
|
148
|
+
|
149
|
+
horizon.mode = function(x) {
|
150
|
+
if (!arguments.length) return mode;
|
151
|
+
mode = x + "";
|
152
|
+
return horizon;
|
153
|
+
};
|
154
|
+
|
155
|
+
horizon.colors = function(x) {
|
156
|
+
if (!arguments.length) return color.range();
|
157
|
+
color.range(x);
|
158
|
+
return horizon;
|
159
|
+
};
|
160
|
+
|
161
|
+
horizon.interpolate = function(x) {
|
162
|
+
if (!arguments.length) return interpolate;
|
163
|
+
interpolate = x + "";
|
164
|
+
return horizon;
|
165
|
+
};
|
166
|
+
|
167
|
+
horizon.x = function(z) {
|
168
|
+
if (!arguments.length) return x;
|
169
|
+
x = z;
|
170
|
+
return horizon;
|
171
|
+
};
|
172
|
+
|
173
|
+
horizon.y = function(z) {
|
174
|
+
if (!arguments.length) return y;
|
175
|
+
y = z;
|
176
|
+
return horizon;
|
177
|
+
};
|
178
|
+
|
179
|
+
horizon.width = function(x) {
|
180
|
+
if (!arguments.length) return w;
|
181
|
+
w = +x;
|
182
|
+
return horizon;
|
183
|
+
};
|
184
|
+
|
185
|
+
horizon.height = function(x) {
|
186
|
+
if (!arguments.length) return h;
|
187
|
+
h = +x;
|
188
|
+
return horizon;
|
189
|
+
};
|
190
|
+
|
191
|
+
return horizon;
|
192
|
+
};
|
193
|
+
|
194
|
+
var d3_chart_horizonArea = d3.svg.area(),
|
195
|
+
d3_chart_horizonId = 0;
|
196
|
+
|
197
|
+
function d3_chart_horizonX(d) {
|
198
|
+
return d[0];
|
199
|
+
}
|
200
|
+
|
201
|
+
function d3_chart_horizonY(d) {
|
202
|
+
return d[1];
|
203
|
+
}
|
@@ -0,0 +1,245 @@
|
|
1
|
+
// Based on http://vis.stanford.edu/protovis/ex/qqplot.html
|
2
|
+
d3.chart.qq = function() {
|
3
|
+
var width = 1,
|
4
|
+
height = 1,
|
5
|
+
duration = 0,
|
6
|
+
domain = null,
|
7
|
+
tickFormat = null,
|
8
|
+
n = 100,
|
9
|
+
x = d3_chart_qqX,
|
10
|
+
y = d3_chart_qqY;
|
11
|
+
|
12
|
+
// For each small multiple…
|
13
|
+
function qq(g) {
|
14
|
+
g.each(function(d, i) {
|
15
|
+
var g = d3.select(this),
|
16
|
+
qx = d3_chart_qqQuantiles(n, x.call(this, d, i)),
|
17
|
+
qy = d3_chart_qqQuantiles(n, y.call(this, d, i)),
|
18
|
+
xd = domain && domain.call(this, d, i) || [d3.min(qx), d3.max(qx)], // new x-domain
|
19
|
+
yd = domain && domain.call(this, d, i) || [d3.min(qy), d3.max(qy)], // new y-domain
|
20
|
+
x0, // old x-scale
|
21
|
+
y0; // old y-scale
|
22
|
+
|
23
|
+
// Compute the new x-scale.
|
24
|
+
var x1 = d3.scale.linear()
|
25
|
+
.domain(xd)
|
26
|
+
.range([0, width]);
|
27
|
+
|
28
|
+
// Compute the new y-scale.
|
29
|
+
var y1 = d3.scale.linear()
|
30
|
+
.domain(yd)
|
31
|
+
.range([height, 0]);
|
32
|
+
|
33
|
+
// Retrieve the old scales, if this is an update.
|
34
|
+
if (this.__chart__) {
|
35
|
+
x0 = this.__chart__.x;
|
36
|
+
y0 = this.__chart__.y;
|
37
|
+
} else {
|
38
|
+
x0 = d3.scale.linear().domain([0, Infinity]).range(x1.range());
|
39
|
+
y0 = d3.scale.linear().domain([0, Infinity]).range(y1.range());
|
40
|
+
}
|
41
|
+
|
42
|
+
// Stash the new scales.
|
43
|
+
this.__chart__ = {x: x1, y: y1};
|
44
|
+
|
45
|
+
// Update diagonal line.
|
46
|
+
var diagonal = g.selectAll("line.diagonal")
|
47
|
+
.data([null]);
|
48
|
+
|
49
|
+
diagonal.enter().append("svg:line")
|
50
|
+
.attr("class", "diagonal")
|
51
|
+
.attr("x1", x1(yd[0]))
|
52
|
+
.attr("y1", y1(xd[0]))
|
53
|
+
.attr("x2", x1(yd[1]))
|
54
|
+
.attr("y2", y1(xd[1]));
|
55
|
+
|
56
|
+
diagonal.transition()
|
57
|
+
.duration(duration)
|
58
|
+
.attr("x1", x1(yd[0]))
|
59
|
+
.attr("y1", y1(xd[0]))
|
60
|
+
.attr("x2", x1(yd[1]))
|
61
|
+
.attr("y2", y1(xd[1]));
|
62
|
+
|
63
|
+
// Update quantile plots.
|
64
|
+
var circle = g.selectAll("circle")
|
65
|
+
.data(d3.range(n).map(function(i) {
|
66
|
+
return {x: qx[i], y: qy[i]};
|
67
|
+
}));
|
68
|
+
|
69
|
+
circle.enter().append("svg:circle")
|
70
|
+
.attr("class", "quantile")
|
71
|
+
.attr("r", 4.5)
|
72
|
+
.attr("cx", function(d) { return x0(d.x); })
|
73
|
+
.attr("cy", function(d) { return y0(d.y); })
|
74
|
+
.style("opacity", 1e-6)
|
75
|
+
.transition()
|
76
|
+
.duration(duration)
|
77
|
+
.attr("cx", function(d) { return x1(d.x); })
|
78
|
+
.attr("cy", function(d) { return y1(d.y); })
|
79
|
+
.style("opacity", 1);
|
80
|
+
|
81
|
+
circle.transition()
|
82
|
+
.duration(duration)
|
83
|
+
.attr("cx", function(d) { return x1(d.x); })
|
84
|
+
.attr("cy", function(d) { return y1(d.y); })
|
85
|
+
.style("opacity", 1);
|
86
|
+
|
87
|
+
circle.exit().transition()
|
88
|
+
.duration(duration)
|
89
|
+
.attr("cx", function(d) { return x1(d.x); })
|
90
|
+
.attr("cy", function(d) { return y1(d.y); })
|
91
|
+
.style("opacity", 1e-6)
|
92
|
+
.remove();
|
93
|
+
|
94
|
+
var xformat = tickFormat || x1.tickFormat(4),
|
95
|
+
yformat = tickFormat || y1.tickFormat(4),
|
96
|
+
tx = function(d) { return "translate(" + x1(d) + "," + height + ")"; },
|
97
|
+
ty = function(d) { return "translate(0," + y1(d) + ")"; };
|
98
|
+
|
99
|
+
// Update x-ticks.
|
100
|
+
var xtick = g.selectAll("g.x.tick")
|
101
|
+
.data(x1.ticks(4), function(d) {
|
102
|
+
return this.textContent || xformat(d);
|
103
|
+
});
|
104
|
+
|
105
|
+
var xtickEnter = xtick.enter().append("svg:g")
|
106
|
+
.attr("class", "x tick")
|
107
|
+
.attr("transform", function(d) { return "translate(" + x0(d) + "," + height + ")"; })
|
108
|
+
.style("opacity", 1e-6);
|
109
|
+
|
110
|
+
xtickEnter.append("svg:line")
|
111
|
+
.attr("y1", 0)
|
112
|
+
.attr("y2", -6);
|
113
|
+
|
114
|
+
xtickEnter.append("svg:text")
|
115
|
+
.attr("text-anchor", "middle")
|
116
|
+
.attr("dy", "1em")
|
117
|
+
.text(xformat);
|
118
|
+
|
119
|
+
// Transition the entering ticks to the new scale, x1.
|
120
|
+
xtickEnter.transition()
|
121
|
+
.duration(duration)
|
122
|
+
.attr("transform", tx)
|
123
|
+
.style("opacity", 1);
|
124
|
+
|
125
|
+
// Transition the updating ticks to the new scale, x1.
|
126
|
+
xtick.transition()
|
127
|
+
.duration(duration)
|
128
|
+
.attr("transform", tx)
|
129
|
+
.style("opacity", 1);
|
130
|
+
|
131
|
+
// Transition the exiting ticks to the new scale, x1.
|
132
|
+
xtick.exit().transition()
|
133
|
+
.duration(duration)
|
134
|
+
.attr("transform", tx)
|
135
|
+
.style("opacity", 1e-6)
|
136
|
+
.remove();
|
137
|
+
|
138
|
+
// Update ticks.
|
139
|
+
var ytick = g.selectAll("g.y.tick")
|
140
|
+
.data(y1.ticks(4), function(d) {
|
141
|
+
return this.textContent || yformat(d);
|
142
|
+
});
|
143
|
+
|
144
|
+
var ytickEnter = ytick.enter().append("svg:g")
|
145
|
+
.attr("class", "y tick")
|
146
|
+
.attr("transform", function(d) { return "translate(0," + y0(d) + ")"; })
|
147
|
+
.style("opacity", 1e-6);
|
148
|
+
|
149
|
+
ytickEnter.append("svg:line")
|
150
|
+
.attr("x1", 0)
|
151
|
+
.attr("x2", 6);
|
152
|
+
|
153
|
+
ytickEnter.append("svg:text")
|
154
|
+
.attr("text-anchor", "end")
|
155
|
+
.attr("dx", "-.5em")
|
156
|
+
.attr("dy", ".3em")
|
157
|
+
.text(yformat);
|
158
|
+
|
159
|
+
// Transition the entering ticks to the new scale, y1.
|
160
|
+
ytickEnter.transition()
|
161
|
+
.duration(duration)
|
162
|
+
.attr("transform", ty)
|
163
|
+
.style("opacity", 1);
|
164
|
+
|
165
|
+
// Transition the updating ticks to the new scale, y1.
|
166
|
+
ytick.transition()
|
167
|
+
.duration(duration)
|
168
|
+
.attr("transform", ty)
|
169
|
+
.style("opacity", 1);
|
170
|
+
|
171
|
+
// Transition the exiting ticks to the new scale, y1.
|
172
|
+
ytick.exit().transition()
|
173
|
+
.duration(duration)
|
174
|
+
.attr("transform", ty)
|
175
|
+
.style("opacity", 1e-6)
|
176
|
+
.remove();
|
177
|
+
});
|
178
|
+
}
|
179
|
+
|
180
|
+
qq.width = function(x) {
|
181
|
+
if (!arguments.length) return width;
|
182
|
+
width = x;
|
183
|
+
return qq;
|
184
|
+
};
|
185
|
+
|
186
|
+
qq.height = function(x) {
|
187
|
+
if (!arguments.length) return height;
|
188
|
+
height = x;
|
189
|
+
return qq;
|
190
|
+
};
|
191
|
+
|
192
|
+
qq.duration = function(x) {
|
193
|
+
if (!arguments.length) return duration;
|
194
|
+
duration = x;
|
195
|
+
return qq;
|
196
|
+
};
|
197
|
+
|
198
|
+
qq.domain = function(x) {
|
199
|
+
if (!arguments.length) return domain;
|
200
|
+
domain = x == null ? x : d3.functor(x);
|
201
|
+
return qq;
|
202
|
+
};
|
203
|
+
|
204
|
+
qq.count = function(z) {
|
205
|
+
if (!arguments.length) return n;
|
206
|
+
n = z;
|
207
|
+
return qq;
|
208
|
+
};
|
209
|
+
|
210
|
+
qq.x = function(z) {
|
211
|
+
if (!arguments.length) return x;
|
212
|
+
x = z;
|
213
|
+
return qq;
|
214
|
+
};
|
215
|
+
|
216
|
+
qq.y = function(z) {
|
217
|
+
if (!arguments.length) return y;
|
218
|
+
y = z;
|
219
|
+
return qq;
|
220
|
+
};
|
221
|
+
|
222
|
+
qq.tickFormat = function(x) {
|
223
|
+
if (!arguments.length) return tickFormat;
|
224
|
+
tickFormat = x;
|
225
|
+
return qq;
|
226
|
+
};
|
227
|
+
|
228
|
+
return qq;
|
229
|
+
};
|
230
|
+
|
231
|
+
function d3_chart_qqQuantiles(n, values) {
|
232
|
+
var m = values.length - 1;
|
233
|
+
values = values.slice().sort(d3.ascending);
|
234
|
+
return d3.range(n).map(function(i) {
|
235
|
+
return values[~~(i * m / n)];
|
236
|
+
});
|
237
|
+
}
|
238
|
+
|
239
|
+
function d3_chart_qqX(d) {
|
240
|
+
return d.x;
|
241
|
+
}
|
242
|
+
|
243
|
+
function d3_chart_qqY(d) {
|
244
|
+
return d.y;
|
245
|
+
}
|