Wed, 24 Jun 2009 05:34:39 +0100
gchart.new_chart() => gchart.new()
0 | 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 | ||
2
107b9d00e4d4
gchart.new_chart() => gchart.new()
Matthew Wild <mwild1@gmail.com>
parents:
1
diff
changeset
|
18 | function new(type) |
0 | 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 | ||
1
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
111 | ---- Legend ---- |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
112 | function chart:set_legend(entries) |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
113 | self.legend = entries; |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
114 | end |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
115 | |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
116 | function writers:legend() |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
117 | if self.legend then |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
118 | return "chdl="..table.concat(self.legend, "|"); |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
119 | end |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
120 | end |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
121 | |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
122 | ---- Legend position ---- |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
123 | local position_map = { |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
124 | vertical = { bottom = "b", top = "t", left = "l", right = "r" }; |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
125 | horizontal = { bottom = "b", top = "t" }; |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
126 | }; |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
127 | |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
128 | function chart:set_legend_position(position, layout) |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
129 | self.legend_position = positionmap[layout or "vertical"][position or "right"] or position; |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
130 | end |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
131 | |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
132 | function writers:legend_position() |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
133 | if self.legend_position then |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
134 | return "chdlp="..self.legend_position; |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
135 | end |
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
136 | end |
0 | 137 | ---- Axes display ---- |
138 | local axismap = { bottom = "x", left = "y", top = "t", right = "r" }; | |
139 | function chart:add_axis(which, options) | |
140 | table.insert(self.axes, { type = axismap[which], options = options }); | |
141 | end | |
142 | ||
143 | function writers:axes() | |
144 | local axes, ranges = {}, {}; | |
145 | local labels, positions = {}, {}; | |
146 | local styles = {}; | |
147 | ||
148 | for index, axis in ipairs(self.axes) do | |
149 | index = index - 1; | |
150 | table.insert(axes, axis.type); | |
151 | if axis.options.range then | |
152 | local range = axis.options.range; | |
153 | table.insert(ranges, index..","..(range.min or 0)..","..(range.max or 100)..(range.interval and (","..range.interval) or "")); | |
154 | end | |
155 | if axis.options.labels then | |
156 | if axis.options.labels[1] then -- A list of strings | |
157 | table.insert(labels, index..":|"..table.concat(axis.options.labels, "|")); | |
158 | else -- Specifying positions too | |
159 | local label_list, position_list = {}, {}; | |
160 | for label, position in pairs(axis.options.labels) do | |
161 | table.insert(label_list, label); | |
162 | table.insert(position_list, position); | |
163 | end | |
164 | table.insert(labels, index..":|"..table.concat(label_list, "|")); | |
165 | table.insert(positions, index..","..table.concat(positions, ",")); | |
166 | end | |
167 | end | |
168 | if axis.options.style then | |
169 | table.insert(styles, index..","..axis.options.style); | |
170 | end | |
171 | if axis.options.ticklength then | |
172 | table.insert(ticklengths, index..","..axis.options.ticklength); | |
173 | end | |
174 | end | |
175 | ||
176 | local result = {}; | |
177 | ||
178 | if next(axes) then | |
179 | table.insert(result, "chxt="..urlencode(table.concat(axes, ","))); | |
180 | end | |
181 | if next(ranges) then | |
182 | table.insert(result, "chxr="..urlencode(table.concat(ranges, ","))); | |
183 | end | |
184 | if next(labels) then | |
185 | table.insert(result, "chxl="..urlencode(table.concat(labels, "|"))); | |
186 | end | |
187 | if next(positions) then | |
188 | table.insert(result, "chxp="..urlencode(table.concat(positions, ","))); | |
189 | end | |
190 | if next(styles) then | |
191 | table.insert(result, "chxs="..urlencode(table.concat(styles, "|"))); | |
192 | end | |
1
f930ba6a8923
Support for legends, and legend positioning
Matthew Wild <mwild1@gmail.com>
parents:
0
diff
changeset
|
193 | if next(ticklengths) then |
0 | 194 | table.insert(result, "chxtc="..urlencode(table.concat(ticklengths, "|"))); |
195 | end | |
196 | ||
197 | return table.concat(result, "&"); | |
198 | end | |
199 | ||
200 | ---- Data points ---- | |
201 | function chart:add_marker(marker) | |
202 | table.insert(self.markers, marker); | |
203 | end | |
204 | ||
205 | local marker_type_map = { flag = "f", text = "t", number = "N" }; | |
206 | function writers:markers() | |
207 | local result = { }; | |
208 | for _, marker in ipairs(self.markers) do | |
209 | table.insert(result, urlencode( | |
210 | (marker_type_map[marker.type] or "f") | |
211 | ..(marker.label or "Label").."," | |
212 | ..(marker.color or self.marker_color).."," | |
213 | ..(marker.series or 0).."," | |
214 | ..(marker.index or 0).."," | |
215 | ..(marker.size or 11).."," | |
216 | ..(marker.priority or 0))); | |
217 | end | |
218 | if next(result) then | |
219 | return "chm="..table.concat(result, "%7c"); | |
220 | end | |
221 | end | |
222 | ||
223 | ---- Colours and fill ---- | |
224 | function chart:set_color(color) | |
225 | self.color = color; | |
226 | end | |
227 | ||
228 | function chart:set_fill(fill_color) | |
229 | self.fill = fill_color; | |
230 | end | |
231 | ||
232 | function writers:color() | |
233 | if self.color then | |
234 | return "chco="..self.color; | |
235 | end | |
236 | end | |
237 | ||
238 | function chart:url() | |
239 | local url = self.base_url.."?cht="..self.type.."&"; | |
240 | ||
241 | local params = {}; | |
242 | for name, writer in pairs(writers) do | |
243 | local ret = writer(self); | |
244 | if ret then | |
245 | table.insert(params, tostring(ret)); | |
246 | end | |
247 | end | |
248 | ||
249 | return url..table.concat(params, "&"); | |
250 | end | |
251 |