Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | espaco | 1 | /* Flot plugin for plotting images. |
| 2 | |||
| 3 | Copyright (c) 2007-2014 IOLA and Ole Laursen. |
||
| 4 | Licensed under the MIT license. |
||
| 5 | |||
| 6 | The data syntax is [ [ image, x1, y1, x2, y2 ], ... ] where (x1, y1) and |
||
| 7 | (x2, y2) are where you intend the two opposite corners of the image to end up |
||
| 8 | in the plot. Image must be a fully loaded Javascript image (you can make one |
||
| 9 | with new Image()). If the image is not complete, it's skipped when plotting. |
||
| 10 | |||
| 11 | There are two helpers included for retrieving images. The easiest work the way |
||
| 12 | that you put in URLs instead of images in the data, like this: |
||
| 13 | |||
| 14 | [ "myimage.png", 0, 0, 10, 10 ] |
||
| 15 | |||
| 16 | Then call $.plot.image.loadData( data, options, callback ) where data and |
||
| 17 | options are the same as you pass in to $.plot. This loads the images, replaces |
||
| 18 | the URLs in the data with the corresponding images and calls "callback" when |
||
| 19 | all images are loaded (or failed loading). In the callback, you can then call |
||
| 20 | $.plot with the data set. See the included example. |
||
| 21 | |||
| 22 | A more low-level helper, $.plot.image.load(urls, callback) is also included. |
||
| 23 | Given a list of URLs, it calls callback with an object mapping from URL to |
||
| 24 | Image object when all images are loaded or have failed loading. |
||
| 25 | |||
| 26 | The plugin supports these options: |
||
| 27 | |||
| 28 | series: { |
||
| 29 | images: { |
||
| 30 | show: boolean |
||
| 31 | anchor: "corner" or "center" |
||
| 32 | alpha: [ 0, 1 ] |
||
| 33 | } |
||
| 34 | } |
||
| 35 | |||
| 36 | They can be specified for a specific series: |
||
| 37 | |||
| 38 | $.plot( $("#placeholder"), [{ |
||
| 39 | data: [ ... ], |
||
| 40 | images: { ... } |
||
| 41 | ]) |
||
| 42 | |||
| 43 | Note that because the data format is different from usual data points, you |
||
| 44 | can't use images with anything else in a specific data series. |
||
| 45 | |||
| 46 | Setting "anchor" to "center" causes the pixels in the image to be anchored at |
||
| 47 | the corner pixel centers inside of at the pixel corners, effectively letting |
||
| 48 | half a pixel stick out to each side in the plot. |
||
| 49 | |||
| 50 | A possible future direction could be support for tiling for large images (like |
||
| 51 | Google Maps). |
||
| 52 | |||
| 53 | */ |
||
| 54 | |||
| 55 | (function ($) { |
||
| 56 | var options = { |
||
| 57 | series: { |
||
| 58 | images: { |
||
| 59 | show: false, |
||
| 60 | alpha: 1, |
||
| 61 | anchor: "corner" // or "center" |
||
| 62 | } |
||
| 63 | } |
||
| 64 | }; |
||
| 65 | |||
| 66 | $.plot.image = {}; |
||
| 67 | |||
| 68 | $.plot.image.loadDataImages = function (series, options, callback) { |
||
| 69 | var urls = [], points = []; |
||
| 70 | |||
| 71 | var defaultShow = options.series.images.show; |
||
| 72 | |||
| 73 | $.each(series, function (i, s) { |
||
| 74 | if (!(defaultShow || s.images.show)) |
||
| 75 | return; |
||
| 76 | |||
| 77 | if (s.data) |
||
| 78 | s = s.data; |
||
| 79 | |||
| 80 | $.each(s, function (i, p) { |
||
| 81 | if (typeof p[0] == "string") { |
||
| 82 | urls.push(p[0]); |
||
| 83 | points.push(p); |
||
| 84 | } |
||
| 85 | }); |
||
| 86 | }); |
||
| 87 | |||
| 88 | $.plot.image.load(urls, function (loadedImages) { |
||
| 89 | $.each(points, function (i, p) { |
||
| 90 | var url = p[0]; |
||
| 91 | if (loadedImages[url]) |
||
| 92 | p[0] = loadedImages[url]; |
||
| 93 | }); |
||
| 94 | |||
| 95 | callback(); |
||
| 96 | }); |
||
| 97 | } |
||
| 98 | |||
| 99 | $.plot.image.load = function (urls, callback) { |
||
| 100 | var missing = urls.length, loaded = {}; |
||
| 101 | if (missing == 0) |
||
| 102 | callback({}); |
||
| 103 | |||
| 104 | $.each(urls, function (i, url) { |
||
| 105 | var handler = function () { |
||
| 106 | --missing; |
||
| 107 | |||
| 108 | loaded[url] = this; |
||
| 109 | |||
| 110 | if (missing == 0) |
||
| 111 | callback(loaded); |
||
| 112 | }; |
||
| 113 | |||
| 114 | $('<img />').load(handler).error(handler).attr('src', url); |
||
| 115 | }); |
||
| 116 | }; |
||
| 117 | |||
| 118 | function drawSeries(plot, ctx, series) { |
||
| 119 | var plotOffset = plot.getPlotOffset(); |
||
| 120 | |||
| 121 | if (!series.images || !series.images.show) |
||
| 122 | return; |
||
| 123 | |||
| 124 | var points = series.datapoints.points, |
||
| 125 | ps = series.datapoints.pointsize; |
||
| 126 | |||
| 127 | for (var i = 0; i < points.length; i += ps) { |
||
| 128 | var img = points[i], |
||
| 129 | x1 = points[i + 1], y1 = points[i + 2], |
||
| 130 | x2 = points[i + 3], y2 = points[i + 4], |
||
| 131 | xaxis = series.xaxis, yaxis = series.yaxis, |
||
| 132 | tmp; |
||
| 133 | |||
| 134 | // actually we should check img.complete, but it |
||
| 135 | // appears to be a somewhat unreliable indicator in |
||
| 136 | // IE6 (false even after load event) |
||
| 137 | if (!img || img.width <= 0 || img.height <= 0) |
||
| 138 | continue; |
||
| 139 | |||
| 140 | if (x1 > x2) { |
||
| 141 | tmp = x2; |
||
| 142 | x2 = x1; |
||
| 143 | x1 = tmp; |
||
| 144 | } |
||
| 145 | if (y1 > y2) { |
||
| 146 | tmp = y2; |
||
| 147 | y2 = y1; |
||
| 148 | y1 = tmp; |
||
| 149 | } |
||
| 150 | |||
| 151 | // if the anchor is at the center of the pixel, expand the |
||
| 152 | // image by 1/2 pixel in each direction |
||
| 153 | if (series.images.anchor == "center") { |
||
| 154 | tmp = 0.5 * (x2-x1) / (img.width - 1); |
||
| 155 | x1 -= tmp; |
||
| 156 | x2 += tmp; |
||
| 157 | tmp = 0.5 * (y2-y1) / (img.height - 1); |
||
| 158 | y1 -= tmp; |
||
| 159 | y2 += tmp; |
||
| 160 | } |
||
| 161 | |||
| 162 | // clip |
||
| 163 | if (x1 == x2 || y1 == y2 || |
||
| 164 | x1 >= xaxis.max || x2 <= xaxis.min || |
||
| 165 | y1 >= yaxis.max || y2 <= yaxis.min) |
||
| 166 | continue; |
||
| 167 | |||
| 168 | var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height; |
||
| 169 | if (x1 < xaxis.min) { |
||
| 170 | sx1 += (sx2 - sx1) * (xaxis.min - x1) / (x2 - x1); |
||
| 171 | x1 = xaxis.min; |
||
| 172 | } |
||
| 173 | |||
| 174 | if (x2 > xaxis.max) { |
||
| 175 | sx2 += (sx2 - sx1) * (xaxis.max - x2) / (x2 - x1); |
||
| 176 | x2 = xaxis.max; |
||
| 177 | } |
||
| 178 | |||
| 179 | if (y1 < yaxis.min) { |
||
| 180 | sy2 += (sy1 - sy2) * (yaxis.min - y1) / (y2 - y1); |
||
| 181 | y1 = yaxis.min; |
||
| 182 | } |
||
| 183 | |||
| 184 | if (y2 > yaxis.max) { |
||
| 185 | sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1); |
||
| 186 | y2 = yaxis.max; |
||
| 187 | } |
||
| 188 | |||
| 189 | x1 = xaxis.p2c(x1); |
||
| 190 | x2 = xaxis.p2c(x2); |
||
| 191 | y1 = yaxis.p2c(y1); |
||
| 192 | y2 = yaxis.p2c(y2); |
||
| 193 | |||
| 194 | // the transformation may have swapped us |
||
| 195 | if (x1 > x2) { |
||
| 196 | tmp = x2; |
||
| 197 | x2 = x1; |
||
| 198 | x1 = tmp; |
||
| 199 | } |
||
| 200 | if (y1 > y2) { |
||
| 201 | tmp = y2; |
||
| 202 | y2 = y1; |
||
| 203 | y1 = tmp; |
||
| 204 | } |
||
| 205 | |||
| 206 | tmp = ctx.globalAlpha; |
||
| 207 | ctx.globalAlpha *= series.images.alpha; |
||
| 208 | ctx.drawImage(img, |
||
| 209 | sx1, sy1, sx2 - sx1, sy2 - sy1, |
||
| 210 | x1 + plotOffset.left, y1 + plotOffset.top, |
||
| 211 | x2 - x1, y2 - y1); |
||
| 212 | ctx.globalAlpha = tmp; |
||
| 213 | } |
||
| 214 | } |
||
| 215 | |||
| 216 | function processRawData(plot, series, data, datapoints) { |
||
| 217 | if (!series.images.show) |
||
| 218 | return; |
||
| 219 | |||
| 220 | // format is Image, x1, y1, x2, y2 (opposite corners) |
||
| 221 | datapoints.format = [ |
||
| 222 | { required: true }, |
||
| 223 | { x: true, number: true, required: true }, |
||
| 224 | { y: true, number: true, required: true }, |
||
| 225 | { x: true, number: true, required: true }, |
||
| 226 | { y: true, number: true, required: true } |
||
| 227 | ]; |
||
| 228 | } |
||
| 229 | |||
| 230 | function init(plot) { |
||
| 231 | plot.hooks.processRawData.push(processRawData); |
||
| 232 | plot.hooks.drawSeries.push(drawSeries); |
||
| 233 | } |
||
| 234 | |||
| 235 | $.plot.plugins.push({ |
||
| 236 | init: init, |
||
| 237 | options: options, |
||
| 238 | name: 'image', |
||
| 239 | version: '1.1' |
||
| 240 | }); |
||
| 241 | })(jQuery); |