gchart.lua

changeset 0
757c17d808a8
child 1
f930ba6a8923
equal deleted inserted replaced
-1:000000000000 0:757c17d808a8
1
2 module(..., package.seeall);
3
4 local chart = {};
5 chart.__index = chart;
6
7 local writers = {}; -- Table of functions which build the URL
8
9 -- Defaults
10 chart.base_url = "http://chart.apis.google.com/chart";
11 chart.width, chart.height = 320, 200;
12 chart.marker_color = "4D89F9";
13
14 -- Helpers
15 local function urlencode(s) return s and (s:gsub("%W", function (c) return string.format("%%%02x", c:byte()); end)); end
16 local typemap = { line = "lc", sparkline = "ls", plot = "lxy", bar = "bhs" };
17
18 function new_chart(type)
19 local chart_obj = {
20 type = typemap[type] or type or "lc";
21 series = {};
22 axes = {};
23 markers = {};
24 };
25
26 return setmetatable(chart_obj, chart);
27 end
28
29 -- Library methods --
30
31 function set_base_url(url)
32 chart.base_url = url;
33 end
34
35 function set_default_size(width, height)
36 chart.width, chart.height = width or 320, height or 200;
37 end
38
39 ----- Chart methods -----
40
41 ---- Base URL ----
42 function chart:set_base_url(url)
43 self.base_url = url;
44 end
45
46 -- No writer for base URL
47
48 ---- Chart type ----
49 function chart:set_type(type)
50 self.type = typemap[type] or type;
51 end
52
53 -- No writer for type
54
55 ---- Data series ----
56 function chart:add_series(data)
57 table.insert(self.series, data);
58 end
59
60 local ee_string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.";
61 local ee_len = #ee_string;
62 local function to_extended_encoding(value)
63 value = tonumber(value);
64 if not value or value < 0 then return "__"; end
65 local div, rem = math.floor(value/ee_len)+1, math.floor(value % ee_len)+1;
66 return ee_string:sub(div, div)..ee_string:sub(rem, rem);
67 end
68
69 function writers:data()
70 local data = {};
71 for n, series in ipairs(self.series) do
72 local encoded = {};
73 for _, value in ipairs(series) do
74 if self.scale and value > 0 then
75 --value = value - (self.scale.min or 0);
76 --print(string.format("4096/(%d-%d)/(%d-%d) = %f", self.scale.max, self.scale.min, value, self.scale.min), value);
77 value = 4096/((self.scale.max-self.scale.min)/(value-self.scale.min));
78 end
79 table.insert(encoded, to_extended_encoding(value));
80 end
81 table.insert(data, table.concat(encoded));
82 end
83 return "chd=e:"..table.concat(data, ",");
84 end
85
86 ---- Scale ----
87 function chart:set_scale(min, max)
88 self.scale = { min = min, max = max };
89 end
90
91 ---- Size ----
92 function chart:set_size(width, height)
93 self.width, self.height = width, height;
94 end
95
96 function writers:size()
97 return "chs="..tostring(self.width).."x"..tostring(self.height);
98 end
99
100 ---- Title ----
101 function chart:set_title(title)
102 self.title = title;
103 end
104
105 function writers:title()
106 if self.title then
107 return "chtt="..urlencode(tostring(self.title):gsub("\n", "|"));
108 end
109 end
110
111 ---- Axes display ----
112 local axismap = { bottom = "x", left = "y", top = "t", right = "r" };
113 function chart:add_axis(which, options)
114 table.insert(self.axes, { type = axismap[which], options = options });
115 end
116
117 function writers:axes()
118 local axes, ranges = {}, {};
119 local labels, positions = {}, {};
120 local styles = {};
121
122 for index, axis in ipairs(self.axes) do
123 index = index - 1;
124 table.insert(axes, axis.type);
125 if axis.options.range then
126 local range = axis.options.range;
127 table.insert(ranges, index..","..(range.min or 0)..","..(range.max or 100)..(range.interval and (","..range.interval) or ""));
128 end
129 if axis.options.labels then
130 if axis.options.labels[1] then -- A list of strings
131 table.insert(labels, index..":|"..table.concat(axis.options.labels, "|"));
132 else -- Specifying positions too
133 local label_list, position_list = {}, {};
134 for label, position in pairs(axis.options.labels) do
135 table.insert(label_list, label);
136 table.insert(position_list, position);
137 end
138 table.insert(labels, index..":|"..table.concat(label_list, "|"));
139 table.insert(positions, index..","..table.concat(positions, ","));
140 end
141 end
142 if axis.options.style then
143 table.insert(styles, index..","..axis.options.style);
144 end
145 if axis.options.ticklength then
146 table.insert(ticklengths, index..","..axis.options.ticklength);
147 end
148 end
149
150 local result = {};
151
152 if next(axes) then
153 table.insert(result, "chxt="..urlencode(table.concat(axes, ",")));
154 end
155 if next(ranges) then
156 table.insert(result, "chxr="..urlencode(table.concat(ranges, ",")));
157 end
158 if next(labels) then
159 table.insert(result, "chxl="..urlencode(table.concat(labels, "|")));
160 end
161 if next(positions) then
162 table.insert(result, "chxp="..urlencode(table.concat(positions, ",")));
163 end
164 if next(styles) then
165 table.insert(result, "chxs="..urlencode(table.concat(styles, "|")));
166 end
167 if next(ticklengths)
168 table.insert(result, "chxtc="..urlencode(table.concat(ticklengths, "|")));
169 end
170
171 return table.concat(result, "&");
172 end
173
174 ---- Data points ----
175 function chart:add_marker(marker)
176 table.insert(self.markers, marker);
177 end
178
179 local marker_type_map = { flag = "f", text = "t", number = "N" };
180 function writers:markers()
181 local result = { };
182 for _, marker in ipairs(self.markers) do
183 table.insert(result, urlencode(
184 (marker_type_map[marker.type] or "f")
185 ..(marker.label or "Label")..","
186 ..(marker.color or self.marker_color)..","
187 ..(marker.series or 0)..","
188 ..(marker.index or 0)..","
189 ..(marker.size or 11)..","
190 ..(marker.priority or 0)));
191 end
192 if next(result) then
193 return "chm="..table.concat(result, "%7c");
194 end
195 end
196
197 ---- Colours and fill ----
198 function chart:set_color(color)
199 self.color = color;
200 end
201
202 function chart:set_fill(fill_color)
203 self.fill = fill_color;
204 end
205
206 function writers:color()
207 if self.color then
208 return "chco="..self.color;
209 end
210 end
211
212 function chart:url()
213 local url = self.base_url.."?cht="..self.type.."&";
214
215 local params = {};
216 for name, writer in pairs(writers) do
217 local ret = writer(self);
218 if ret then
219 table.insert(params, tostring(ret));
220 end
221 end
222
223 return url..table.concat(params, "&");
224 end
225

mercurial