Statistics
| Revision:

root / hci / trunk / eneraptor-web-app / web-app / js / flot / jquery.flot.image.js @ 11

History | View | Annotate | Download (7.46 KB)

1
/*
2
Flot plugin for plotting images, e.g. useful for putting ticks on a
3
prerendered complex visualization.
4

5
The data syntax is [[image, x1, y1, x2, y2], ...] where (x1, y1) and
6
(x2, y2) are where you intend the two opposite corners of the image to
7
end up in the plot. Image must be a fully loaded Javascript image (you
8
can make one with new Image()). If the image is not complete, it's
9
skipped when plotting.
10

11
There are two helpers included for retrieving images. The easiest work
12
the way that you put in URLs instead of images in the data (like
13
["myimage.png", 0, 0, 10, 10]), then call $.plot.image.loadData(data,
14
options, callback) where data and options are the same as you pass in
15
to $.plot. This loads the images, replaces the URLs in the data with
16
the corresponding images and calls "callback" when all images are
17
loaded (or failed loading). In the callback, you can then call $.plot
18
with the data set. See the included example.
19

20
A more low-level helper, $.plot.image.load(urls, callback) is also
21
included. Given a list of URLs, it calls callback with an object
22
mapping from URL to Image object when all images are loaded or have
23
failed loading.
24

25
Options for the plugin are
26

27
  series: {
28
      images: {
29
          show: boolean
30
          anchor: "corner" or "center"
31
          alpha: [0,1]
32
      }
33
  }
34

35
which can be specified for a specific series
36

37
  $.plot($("#placeholder"), [{ data: [ ... ], images: { ... } ])
38

39
Note that because the data format is different from usual data points,
40
you can't use images with anything else in a specific data series.
41

42
Setting "anchor" to "center" causes the pixels in the image to be
43
anchored at the corner pixel centers inside of at the pixel corners,
44
effectively letting half a pixel stick out to each side in the plot.
45

46

47
A possible future direction could be support for tiling for large
48
images (like Google Maps).
49

50
*/
51

    
52
(function ($) {
53
    var options = {
54
        series: {
55
            images: {
56
                show: false,
57
                alpha: 1,
58
                anchor: "corner" // or "center"
59
            }
60
        }
61
    };
62

    
63
    $.plot.image = {};
64

    
65
    $.plot.image.loadDataImages = function (series, options, callback) {
66
        var urls = [], points = [];
67

    
68
        var defaultShow = options.series.images.show;
69
        
70
        $.each(series, function (i, s) {
71
            if (!(defaultShow || s.images.show))
72
                return;
73
            
74
            if (s.data)
75
                s = s.data;
76

    
77
            $.each(s, function (i, p) {
78
                if (typeof p[0] == "string") {
79
                    urls.push(p[0]);
80
                    points.push(p);
81
                }
82
            });
83
        });
84

    
85
        $.plot.image.load(urls, function (loadedImages) {
86
            $.each(points, function (i, p) {
87
                var url = p[0];
88
                if (loadedImages[url])
89
                    p[0] = loadedImages[url];
90
            });
91

    
92
            callback();
93
        });
94
    }
95
    
96
    $.plot.image.load = function (urls, callback) {
97
        var missing = urls.length, loaded = {};
98
        if (missing == 0)
99
            callback({});
100

    
101
        $.each(urls, function (i, url) {
102
            var handler = function () {
103
                --missing;
104
                
105
                loaded[url] = this;
106
                
107
                if (missing == 0)
108
                    callback(loaded);
109
            };
110

    
111
            $('<img />').load(handler).error(handler).attr('src', url);
112
        });
113
    }
114
    
115
    function draw(plot, ctx) {
116
        var plotOffset = plot.getPlotOffset();
117
        
118
        $.each(plot.getData(), function (i, series) {
119
            var points = series.datapoints.points,
120
                ps = series.datapoints.pointsize;
121
            
122
            for (var i = 0; i < points.length; i += ps) {
123
                var img = points[i],
124
                    x1 = points[i + 1], y1 = points[i + 2],
125
                    x2 = points[i + 3], y2 = points[i + 4],
126
                    xaxis = series.xaxis, yaxis = series.yaxis,
127
                    tmp;
128

    
129
                // actually we should check img.complete, but it
130
                // appears to be a somewhat unreliable indicator in
131
                // IE6 (false even after load event)
132
                if (!img || img.width <= 0 || img.height <= 0)
133
                    continue;
134

    
135
                if (x1 > x2) {
136
                    tmp = x2;
137
                    x2 = x1;
138
                    x1 = tmp;
139
                }
140
                if (y1 > y2) {
141
                    tmp = y2;
142
                    y2 = y1;
143
                    y1 = tmp;
144
                }
145
                
146
                // if the anchor is at the center of the pixel, expand the 
147
                // image by 1/2 pixel in each direction
148
                if (series.images.anchor == "center") {
149
                    tmp = 0.5 * (x2-x1) / (img.width - 1);
150
                    x1 -= tmp;
151
                    x2 += tmp;
152
                    tmp = 0.5 * (y2-y1) / (img.height - 1);
153
                    y1 -= tmp;
154
                    y2 += tmp;
155
                }
156
                
157
                // clip
158
                if (x1 == x2 || y1 == y2 ||
159
                    x1 >= xaxis.max || x2 <= xaxis.min ||
160
                    y1 >= yaxis.max || y2 <= yaxis.min)
161
                    continue;
162

    
163
                var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height;
164
                if (x1 < xaxis.min) {
165
                    sx1 += (sx2 - sx1) * (xaxis.min - x1) / (x2 - x1);
166
                    x1 = xaxis.min;
167
                }
168

    
169
                if (x2 > xaxis.max) {
170
                    sx2 += (sx2 - sx1) * (xaxis.max - x2) / (x2 - x1);
171
                    x2 = xaxis.max;
172
                }
173

    
174
                if (y1 < yaxis.min) {
175
                    sy2 += (sy1 - sy2) * (yaxis.min - y1) / (y2 - y1);
176
                    y1 = yaxis.min;
177
                }
178

    
179
                if (y2 > yaxis.max) {
180
                    sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1);
181
                    y2 = yaxis.max;
182
                }
183
                
184
                x1 = xaxis.p2c(x1);
185
                x2 = xaxis.p2c(x2);
186
                y1 = yaxis.p2c(y1);
187
                y2 = yaxis.p2c(y2);
188
                
189
                // the transformation may have swapped us
190
                if (x1 > x2) {
191
                    tmp = x2;
192
                    x2 = x1;
193
                    x1 = tmp;
194
                }
195
                if (y1 > y2) {
196
                    tmp = y2;
197
                    y2 = y1;
198
                    y1 = tmp;
199
                }
200

    
201
                tmp = ctx.globalAlpha;
202
                ctx.globalAlpha *= series.images.alpha;
203
                ctx.drawImage(img,
204
                              sx1, sy1, sx2 - sx1, sy2 - sy1,
205
                              x1 + plotOffset.left, y1 + plotOffset.top,
206
                              x2 - x1, y2 - y1);
207
                ctx.globalAlpha = tmp;
208
            }
209
        });
210
    }
211

    
212
    function processRawData(plot, series, data, datapoints) {
213
        if (!series.images.show)
214
            return;
215

    
216
        // format is Image, x1, y1, x2, y2 (opposite corners)
217
        datapoints.format = [
218
            { required: true },
219
            { x: true, number: true, required: true },
220
            { y: true, number: true, required: true },
221
            { x: true, number: true, required: true },
222
            { y: true, number: true, required: true }
223
        ];
224
    }
225
    
226
    function init(plot) {
227
        plot.hooks.processRawData.push(processRawData);
228
        plot.hooks.draw.push(draw);
229
    }
230
    
231
    $.plot.plugins.push({
232
        init: init,
233
        options: options,
234
        name: 'image',
235
        version: '1.1'
236
    });
237
})(jQuery);