Support for specifying a bottom for each point for line charts when
filling them, this means that an arbitrary bottom can be used
instead of just the x axis (based on patches patiently provided by
Roman V. Prikhodchenko).
New fillbetween plugin that can compute a bottom for a series from
another series, useful for filling areas between lines (see new
example percentiles.html for a use case).
More predictable handling of gaps for the stacking plugin, now all
undefined ranges are skipped.
Fixed problem with plugins adding options to the series objects.
Fixed a problem introduced in 0.6 with specifying a gradient with {
brightness: x, opacity: y }.
git-svn-id: https://flot.googlecode.com/svn/trunk@229 1e0a6537-2640-0410-bfb7-f154510ff394
pull/1/head
parent
7cbc14160e
commit
c3a34cd6e9
@ -0,0 +1,57 @@
|
|||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<title>Flot Examples</title>
|
||||||
|
<link href="layout.css" rel="stylesheet" type="text/css"></link>
|
||||||
|
<!--[if IE]><script language="javascript" type="text/javascript" src="../excanvas.min.js"></script><![endif]-->
|
||||||
|
<script language="javascript" type="text/javascript" src="../jquery.js"></script>
|
||||||
|
<script language="javascript" type="text/javascript" src="../jquery.flot.js"></script>
|
||||||
|
<script language="javascript" type="text/javascript" src="../jquery.flot.fillbetween.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Flot Examples</h1>
|
||||||
|
|
||||||
|
<div id="placeholder" style="width:600px;height:400px;"></div>
|
||||||
|
|
||||||
|
<p>Height in centimeters of individuals from the US (2003-2006) as function of
|
||||||
|
age in years (source: <a href="http://www.cdc.gov/nchs/data/nhsr/nhsr010.pdf">CDC</a>).
|
||||||
|
The 15%-85%, 25%-75% and 50% percentiles are indicated.</p>
|
||||||
|
|
||||||
|
<p>For each point of a filled curve, you can specify an arbitrary
|
||||||
|
bottom. As this example illustrates, this can be useful for
|
||||||
|
plotting percentiles. If you have the data sets available without
|
||||||
|
appropriate fill bottoms, you can use the fillbetween plugin to
|
||||||
|
compute the data point bottoms automatically.</p>
|
||||||
|
|
||||||
|
<script id="source" language="javascript" type="text/javascript">
|
||||||
|
$(function () {
|
||||||
|
var males = {'15%': [[2, 88.0], [3, 93.3], [4, 102.0], [5, 108.5], [6, 115.7], [7, 115.6], [8, 124.6], [9, 130.3], [10, 134.3], [11, 141.4], [12, 146.5], [13, 151.7], [14, 159.9], [15, 165.4], [16, 167.8], [17, 168.7], [18, 169.5], [19, 168.0]], '90%': [[2, 96.8], [3, 105.2], [4, 113.9], [5, 120.8], [6, 127.0], [7, 133.1], [8, 139.1], [9, 143.9], [10, 151.3], [11, 161.1], [12, 164.8], [13, 173.5], [14, 179.0], [15, 182.0], [16, 186.9], [17, 185.2], [18, 186.3], [19, 186.6]], '25%': [[2, 89.2], [3, 94.9], [4, 104.4], [5, 111.4], [6, 117.5], [7, 120.2], [8, 127.1], [9, 132.9], [10, 136.8], [11, 144.4], [12, 149.5], [13, 154.1], [14, 163.1], [15, 169.2], [16, 170.4], [17, 171.2], [18, 172.4], [19, 170.8]], '10%': [[2, 86.9], [3, 92.6], [4, 99.9], [5, 107.0], [6, 114.0], [7, 113.5], [8, 123.6], [9, 129.2], [10, 133.0], [11, 140.6], [12, 145.2], [13, 149.7], [14, 158.4], [15, 163.5], [16, 166.9], [17, 167.5], [18, 167.1], [19, 165.3]], 'mean': [[2, 91.9], [3, 98.5], [4, 107.1], [5, 114.4], [6, 120.6], [7, 124.7], [8, 131.1], [9, 136.8], [10, 142.3], [11, 150.0], [12, 154.7], [13, 161.9], [14, 168.7], [15, 173.6], [16, 175.9], [17, 176.6], [18, 176.8], [19, 176.7]], '75%': [[2, 94.5], [3, 102.1], [4, 110.8], [5, 117.9], [6, 124.0], [7, 129.3], [8, 134.6], [9, 141.4], [10, 147.0], [11, 156.1], [12, 160.3], [13, 168.3], [14, 174.7], [15, 178.0], [16, 180.2], [17, 181.7], [18, 181.3], [19, 182.5]], '85%': [[2, 96.2], [3, 103.8], [4, 111.8], [5, 119.6], [6, 125.6], [7, 131.5], [8, 138.0], [9, 143.3], [10, 149.3], [11, 159.8], [12, 162.5], [13, 171.3], [14, 177.5], [15, 180.2], [16, 183.8], [17, 183.4], [18, 183.5], [19, 185.5]], '50%': [[2, 91.9], [3, 98.2], [4, 106.8], [5, 114.6], [6, 120.8], [7, 125.2], [8, 130.3], [9, 137.1], [10, 141.5], [11, 149.4], [12, 153.9], [13, 162.2], [14, 169.0], [15, 174.8], [16, 176.0], [17, 176.8], [18, 176.4], [19, 177.4]]};
|
||||||
|
var females = {'15%': [[2, 84.8], [3, 93.7], [4, 100.6], [5, 105.8], [6, 113.3], [7, 119.3], [8, 124.3], [9, 131.4], [10, 136.9], [11, 143.8], [12, 149.4], [13, 151.2], [14, 152.3], [15, 155.9], [16, 154.7], [17, 157.0], [18, 156.1], [19, 155.4]], '90%': [[2, 95.6], [3, 104.1], [4, 111.9], [5, 119.6], [6, 127.6], [7, 133.1], [8, 138.7], [9, 147.1], [10, 152.8], [11, 161.3], [12, 166.6], [13, 167.9], [14, 169.3], [15, 170.1], [16, 172.4], [17, 169.2], [18, 171.1], [19, 172.4]], '25%': [[2, 87.2], [3, 95.9], [4, 101.9], [5, 107.4], [6, 114.8], [7, 121.4], [8, 126.8], [9, 133.4], [10, 138.6], [11, 146.2], [12, 152.0], [13, 153.8], [14, 155.7], [15, 158.4], [16, 157.0], [17, 158.5], [18, 158.4], [19, 158.1]], '10%': [[2, 84.0], [3, 91.9], [4, 99.2], [5, 105.2], [6, 112.7], [7, 118.0], [8, 123.3], [9, 130.2], [10, 135.0], [11, 141.1], [12, 148.3], [13, 150.0], [14, 150.7], [15, 154.3], [16, 153.6], [17, 155.6], [18, 154.7], [19, 153.1]], 'mean': [[2, 90.2], [3, 98.3], [4, 105.2], [5, 112.2], [6, 119.0], [7, 125.8], [8, 131.3], [9, 138.6], [10, 144.2], [11, 151.3], [12, 156.7], [13, 158.6], [14, 160.5], [15, 162.1], [16, 162.9], [17, 162.2], [18, 163.0], [19, 163.1]], '75%': [[2, 93.2], [3, 101.5], [4, 107.9], [5, 116.6], [6, 122.8], [7, 129.3], [8, 135.2], [9, 143.7], [10, 148.7], [11, 156.9], [12, 160.8], [13, 163.0], [14, 165.0], [15, 165.8], [16, 168.7], [17, 166.2], [18, 167.6], [19, 168.0]], '85%': [[2, 94.5], [3, 102.8], [4, 110.4], [5, 119.0], [6, 125.7], [7, 131.5], [8, 137.9], [9, 146.0], [10, 151.3], [11, 159.9], [12, 164.0], [13, 166.5], [14, 167.5], [15, 168.5], [16, 171.5], [17, 168.0], [18, 169.8], [19, 170.3]], '50%': [[2, 90.2], [3, 98.1], [4, 105.2], [5, 111.7], [6, 118.2], [7, 125.6], [8, 130.5], [9, 138.3], [10, 143.7], [11, 151.4], [12, 156.7], [13, 157.7], [14, 161.0], [15, 162.0], [16, 162.8], [17, 162.2], [18, 162.8], [19, 163.3]]};
|
||||||
|
|
||||||
|
var dataset = [
|
||||||
|
{ label: 'Female mean', data: females['mean'], lines: { show: true }, color: "rgb(255,50,50)" },
|
||||||
|
{ id: 'f15%', data: females['15%'], lines: { show: true, lineWidth: 0, fill: false }, color: "rgb(255,50,50)" },
|
||||||
|
{ id: 'f25%', data: females['25%'], lines: { show: true, lineWidth: 0, fill: 0.2 }, color: "rgb(255,50,50)", fillBetween: 'f15%' },
|
||||||
|
{ id: 'f50%', data: females['50%'], lines: { show: true, lineWidth: 0.5, fill: 0.4, shadowSize: 0 }, color: "rgb(255,50,50)", fillBetween: 'f25%' },
|
||||||
|
{ id: 'f75%', data: females['75%'], lines: { show: true, lineWidth: 0, fill: 0.4 }, color: "rgb(255,50,50)", fillBetween: 'f50%' },
|
||||||
|
{ id: 'f85%', data: females['85%'], lines: { show: true, lineWidth: 0, fill: 0.2 }, color: "rgb(255,50,50)", fillBetween: 'f75%' },
|
||||||
|
|
||||||
|
{ label: 'Male mean', data: males['mean'], lines: { show: true }, color: "rgb(50,50,255)" },
|
||||||
|
{ id: 'm15%', data: males['15%'], lines: { show: true, lineWidth: 0, fill: false }, color: "rgb(50,50,255)" },
|
||||||
|
{ id: 'm25%', data: males['25%'], lines: { show: true, lineWidth: 0, fill: 0.2 }, color: "rgb(50,50,255)", fillBetween: 'm15%' },
|
||||||
|
{ id: 'm50%', data: males['50%'], lines: { show: true, lineWidth: 0.5, fill: 0.4, shadowSize: 0 }, color: "rgb(50,50,255)", fillBetween: 'm25%' },
|
||||||
|
{ id: 'm75%', data: males['75%'], lines: { show: true, lineWidth: 0, fill: 0.4 }, color: "rgb(50,50,255)", fillBetween: 'm50%' },
|
||||||
|
{ id: 'm85%', data: males['85%'], lines: { show: true, lineWidth: 0, fill: 0.2 }, color: "rgb(50,50,255)", fillBetween: 'm75%' }
|
||||||
|
]
|
||||||
|
|
||||||
|
$.plot($("#placeholder"), dataset, {
|
||||||
|
xaxis: { tickDecimals: 0 },
|
||||||
|
yaxis: { tickFormatter: function (v) { return v + " cm"; } },
|
||||||
|
legend: { position: 'se' }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,183 @@
|
|||||||
|
/*
|
||||||
|
Flot plugin for computing bottoms for filled line and bar charts.
|
||||||
|
|
||||||
|
The case: you've got two series that you want to fill the area
|
||||||
|
between. In Flot terms, you need to use one as the fill bottom of the
|
||||||
|
other. You can specify the bottom of each data point as the third
|
||||||
|
coordinate manually, or you can use this plugin to compute it for you.
|
||||||
|
|
||||||
|
In order to name the other series, you need to give it an id, like this
|
||||||
|
|
||||||
|
var dataset = [
|
||||||
|
{ data: [ ... ], id: "foo" } , // use default bottom
|
||||||
|
{ data: [ ... ], fillBetween: "foo" }, // use first dataset as bottom
|
||||||
|
];
|
||||||
|
|
||||||
|
$.plot($("#placeholder"), dataset, { line: { show: true, fill: true }});
|
||||||
|
|
||||||
|
As a convenience, if the id given is a number that doesn't appear as
|
||||||
|
an id in the series, it is interpreted as the index in the array
|
||||||
|
instead (so fillBetween: 0 can also mean the first series).
|
||||||
|
|
||||||
|
Internally, the plugin modifies the datapoints in each series. For
|
||||||
|
line series, extra data points might be inserted through
|
||||||
|
interpolation. Note that at points where the bottom line is not
|
||||||
|
defined (due to a null point or start/end of line), the current line
|
||||||
|
will show a gap too. The algorithm comes from the jquery.flot.stack.js
|
||||||
|
plugin, possibly some code could be shared.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function ($) {
|
||||||
|
var options = {
|
||||||
|
series: { fillBetween: null } // or number
|
||||||
|
};
|
||||||
|
|
||||||
|
function init(plot) {
|
||||||
|
function findBottomSeries(s, allseries) {
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < allseries.length; ++i) {
|
||||||
|
if (allseries[i].id == s.fillBetween)
|
||||||
|
return allseries[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof s.fillBetween == "number") {
|
||||||
|
i = s.fillBetween;
|
||||||
|
|
||||||
|
if (i < 0 || i >= allseries.length)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return allseries[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function computeFillBottoms(plot, s, datapoints) {
|
||||||
|
if (s.fillBetween == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var other = findBottomSeries(s, plot.getData());
|
||||||
|
if (!other)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ps = datapoints.pointsize,
|
||||||
|
points = datapoints.points,
|
||||||
|
otherps = other.datapoints.pointsize,
|
||||||
|
otherpoints = other.datapoints.points,
|
||||||
|
newpoints = [],
|
||||||
|
px, py, intery, qx, qy, bottom,
|
||||||
|
withlines = s.lines.show,
|
||||||
|
withbottom = ps > 2 && datapoints.format[2].y,
|
||||||
|
withsteps = withlines && s.lines.steps,
|
||||||
|
fromgap = true,
|
||||||
|
i = 0, j = 0, l;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (i >= points.length)
|
||||||
|
break;
|
||||||
|
|
||||||
|
l = newpoints.length;
|
||||||
|
|
||||||
|
if (points[i] == null) {
|
||||||
|
// copy gaps
|
||||||
|
for (m = 0; m < ps; ++m)
|
||||||
|
newpoints.push(points[i + m]);
|
||||||
|
i += ps;
|
||||||
|
}
|
||||||
|
else if (j >= otherpoints.length) {
|
||||||
|
// for lines, we can't use the rest of the points
|
||||||
|
if (!withlines) {
|
||||||
|
for (m = 0; m < ps; ++m)
|
||||||
|
newpoints.push(points[i + m]);
|
||||||
|
}
|
||||||
|
i += ps;
|
||||||
|
}
|
||||||
|
else if (otherpoints[j] == null) {
|
||||||
|
// oops, got a gap
|
||||||
|
for (m = 0; m < ps; ++m)
|
||||||
|
newpoints.push(null);
|
||||||
|
fromgap = true;
|
||||||
|
j += otherps;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// cases where we actually got two points
|
||||||
|
px = points[i];
|
||||||
|
py = points[i + 1];
|
||||||
|
qx = otherpoints[j];
|
||||||
|
qy = otherpoints[j + 1];
|
||||||
|
bottom = 0;
|
||||||
|
|
||||||
|
if (px == qx) {
|
||||||
|
for (m = 0; m < ps; ++m)
|
||||||
|
newpoints.push(points[i + m]);
|
||||||
|
|
||||||
|
//newpoints[l + 1] += qy;
|
||||||
|
bottom = qy;
|
||||||
|
|
||||||
|
i += ps;
|
||||||
|
j += otherps;
|
||||||
|
}
|
||||||
|
else if (px > qx) {
|
||||||
|
// we got past point below, might need to
|
||||||
|
// insert interpolated extra point
|
||||||
|
if (withlines && i > 0 && points[i - ps] != null) {
|
||||||
|
intery = py + (points[i - ps + 1] - py) * (qx - px) / (points[i - ps] - px);
|
||||||
|
newpoints.push(qx);
|
||||||
|
newpoints.push(intery)
|
||||||
|
for (m = 2; m < ps; ++m)
|
||||||
|
newpoints.push(points[i + m]);
|
||||||
|
bottom = qy;
|
||||||
|
}
|
||||||
|
|
||||||
|
j += otherps;
|
||||||
|
}
|
||||||
|
else { // px < qx
|
||||||
|
if (fromgap && withlines) {
|
||||||
|
// if we come from a gap, we just skip this point
|
||||||
|
i += ps;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (m = 0; m < ps; ++m)
|
||||||
|
newpoints.push(points[i + m]);
|
||||||
|
|
||||||
|
// we might be able to interpolate a point below,
|
||||||
|
// this can give us a better y
|
||||||
|
if (withlines && j > 0 && otherpoints[j - otherps] != null)
|
||||||
|
bottom = qy + (otherpoints[j - otherps + 1] - qy) * (px - qx) / (otherpoints[j - otherps] - qx);
|
||||||
|
|
||||||
|
//newpoints[l + 1] += bottom;
|
||||||
|
|
||||||
|
i += ps;
|
||||||
|
}
|
||||||
|
|
||||||
|
fromgap = false;
|
||||||
|
|
||||||
|
if (l != newpoints.length && withbottom)
|
||||||
|
newpoints[l + 2] = bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
// maintain the line steps invariant
|
||||||
|
if (withsteps && l != newpoints.length && l > 0
|
||||||
|
&& newpoints[l] != null
|
||||||
|
&& newpoints[l] != newpoints[l - ps]
|
||||||
|
&& newpoints[l + 1] != newpoints[l - ps + 1]) {
|
||||||
|
for (m = 0; m < ps; ++m)
|
||||||
|
newpoints[l + ps + m] = newpoints[l + m];
|
||||||
|
newpoints[l + 1] = newpoints[l - ps + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
datapoints.points = newpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
plot.hooks.processDatapoints.push(computeFillBottoms);
|
||||||
|
}
|
||||||
|
|
||||||
|
$.plot.plugins.push({
|
||||||
|
init: init,
|
||||||
|
options: options,
|
||||||
|
name: 'fillbetween',
|
||||||
|
version: '1.0'
|
||||||
|
});
|
||||||
|
})(jQuery);
|
||||||
Loading…
Reference in New Issue