smart_chart 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ module SmartChart
2
+ class Bar < MultipleDataSetChart
3
+ include GridLines
4
+
5
+ private # ---------------------------------------------------------------
6
+
7
+ ##
8
+ # Specify the Google Chart type.
9
+ #
10
+ def type
11
+ "b" +
12
+ (orientation == :horizontal ? "h" : "v") +
13
+ (style == :stacked ? "s" : "g")
14
+ end
15
+
16
+ # zero line
17
+ def chp
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ module SmartChart
2
+ class Line < MultipleDataSetChart
3
+ include GridLines
4
+
5
+
6
+ private # ---------------------------------------------------------------
7
+
8
+ ##
9
+ # Specify the Google Chart type.
10
+ #
11
+ def type
12
+ "lc"
13
+ end
14
+
15
+ ##
16
+ # Array of all possible query string parameters.
17
+ #
18
+ def query_string_params
19
+ super + [:chg]
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,163 @@
1
+ module SmartChart
2
+ class Map < SingleDataSetChart
3
+
4
+ ##
5
+ # Array of valid ISO 3166-1-alpha-2 country codes.
6
+ # Source: http://code.google.com/apis/chart/isocodes.html
7
+ #
8
+ def self.country_codes
9
+ %w[
10
+ AF AX AL DZ AS AD AO AI AQ AG AR AM AW AU AT AZ BS BH BD BB BY BE
11
+ BZ BJ BM BT BO BA BW BV BR IO BN BG BF BI KH CM CA CV KY CF TD CL
12
+ CN CX CC CO KM CG CD CK CR CI HR CU CY CZ DK DJ DM DO EC EG SV GQ
13
+ ER EE ET FK FO FJ FI FR GF PF TF GA GM GE DE GH GI GR GL GD GP GU
14
+ GT GG GN GW GY HT HM VA HN HK HU IS IN ID IR IQ IE IM IL IT JM JP
15
+ JE JO KZ KE KI KP KR KW KG LA LV LB LS LR LY LI LT LU MO MK MG MW
16
+ MY MV ML MT MH MQ MR MU YT MX FM MD MC MN ME MS MA MZ MM NA NR NP
17
+ NL AN NC NZ NI NE NG NU NF MP NO OM PK PW PS PA PG PY PE PH PN PL
18
+ PT PR QA RE RO RU RW BL SH KN LC MF PM VC WS SM ST SA SN RS SC SL
19
+ SG SK SI SB SO ZA GS ES LK SD SR SJ SZ SE CH SY TW TJ TZ TH TL TG
20
+ TK TO TT TN TR TM TC TV UG UA AE GB US UM UY UZ VU VE VN VG VI WF
21
+ EH YE ZM ZW
22
+ ]
23
+ end
24
+
25
+ ##
26
+ # Array of valid US state codes.
27
+ # Source: http://code.google.com/apis/chart/statecodes.html
28
+ #
29
+ def self.us_state_codes
30
+ %w[
31
+ AK AL AR AZ CA CO CT DE FL GA HI IA ID IL IN KS KY LA MA MD ME MI
32
+ MN MO MS MT NC ND NE NH NJ NM NV NY OH OK OR PA RI SC SD TN TX UT
33
+ VA VT WA WI WV WY
34
+ ]
35
+ end
36
+
37
+ ##
38
+ # Array of valid map regions.
39
+ #
40
+ def self.regions
41
+ %w[
42
+ africa
43
+ asia
44
+ europe
45
+ middle_east
46
+ south_america
47
+ usa
48
+ world
49
+ ]
50
+ end
51
+
52
+ # region to be depicted (default "world", see Map.regions for choices)
53
+ attr_accessor :region
54
+
55
+ # color of countries or states with no data (default white)
56
+ attr_accessor :foreground
57
+
58
+
59
+ private # ---------------------------------------------------------------
60
+
61
+ ##
62
+ # Specify the Google Chart type.
63
+ #
64
+ def type
65
+ "t"
66
+ end
67
+
68
+ ##
69
+ # Array of all possible query string parameters.
70
+ #
71
+ def query_string_params
72
+ super + [
73
+ :chtm, # region
74
+ :chld, # countries/states
75
+ ]
76
+ end
77
+
78
+ ##
79
+ # Map region query string parameter.
80
+ #
81
+ def chtm
82
+ region || "world"
83
+ end
84
+
85
+ ##
86
+ # Countries or states to be indicated on the map.
87
+ #
88
+ def chld
89
+ labels.join
90
+ end
91
+
92
+ ##
93
+ # Chart color parameter: default ("foreground") plus gradient.
94
+ #
95
+ def chco
96
+ ([foreground || "FFFFFF"] + super.to_a).join(",")
97
+ end
98
+
99
+ ##
100
+ # Get the labels (auto-upcase).
101
+ #
102
+ def labels
103
+ super.map{ |i| i.to_s.upcase } unless super.nil?
104
+ end
105
+
106
+ ##
107
+ # Array of validations to be run on the chart.
108
+ #
109
+ def validations
110
+ super + [:map_region]
111
+ end
112
+
113
+ ##
114
+ # Validate the given map region against Google's available options.
115
+ #
116
+ def validate_map_region
117
+ unless region.nil? or Map.regions.include?(region.to_s)
118
+ raise DataFormatError,
119
+ "Map region must be one of: #{Map.regions.join(', ')}"
120
+ end
121
+ end
122
+
123
+ ##
124
+ # Make sure colors are valid hex codes.
125
+ #
126
+ def validate_colors
127
+ super
128
+ validate_color(foreground) if foreground
129
+ end
130
+
131
+ ##
132
+ # Make sure the given country codes are specified by ISO 3316.
133
+ #
134
+ def validate_labels
135
+ return if labels.nil?
136
+ # validate states
137
+ if region.to_s == "usa"
138
+ invalids = labels - Map.us_state_codes
139
+ unless invalids.size == 0
140
+ raise DataFormatError,
141
+ "Invalid state code(s): #{invalids.join(', ')}"
142
+ end
143
+ # validate countries
144
+ else
145
+ invalids = labels - Map.country_codes
146
+ unless invalids.size == 0
147
+ raise DataFormatError,
148
+ "Invalid country code(s): #{invalids.join(', ')}"
149
+ end
150
+ end
151
+ end
152
+
153
+ ##
154
+ # Make sure chart dimensions are within Google's 440x220 limit.
155
+ #
156
+ def validate_dimensions
157
+ if width > 440 or height > 220
158
+ raise DimensionsError,
159
+ "Map dimensions may not exceed 440x220 pixels"
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,13 @@
1
+ module SmartChart
2
+ class Meter < SingleDataSetChart
3
+
4
+ private # ---------------------------------------------------------------
5
+
6
+ ##
7
+ # Specify the Google Chart type.
8
+ #
9
+ def type
10
+ "gom"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,32 @@
1
+ module SmartChart
2
+ class Pie < SingleDataSetChart
3
+
4
+ # number of degrees to rotate start of first slice from 12 o'clock
5
+ attr_accessor :rotate
6
+
7
+
8
+ private # ---------------------------------------------------------------
9
+
10
+ ##
11
+ # Specify the Google Chart type.
12
+ #
13
+ def type
14
+ case style.to_s
15
+ when "concentric": "pc"
16
+ when "3d": "p3"
17
+ else "p"
18
+ end
19
+ end
20
+
21
+ ##
22
+ # Rotation. Google expects radians from 3 o'clock.
23
+ #
24
+ def chp
25
+ r = rotate || 0
26
+ s = SmartChart.decimal_string(
27
+ ((r - 90) % 360) * Math::PI / 180.0
28
+ )
29
+ s == "0" ? nil : s # omit parameter if zero
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,85 @@
1
+ module SmartChart
2
+ class QRCode < SingleDataSetChart
3
+
4
+ # output encoding
5
+ # :utf8 (default), :shift_jis, :iso88591
6
+ attr_accessor :encoding
7
+
8
+ # error correction level
9
+ # :l (default), :m, :q, or :h
10
+ attr_accessor :ec_level
11
+
12
+ # chart margin, in rows/columns
13
+ attr_accessor :margin
14
+
15
+ def self.ec_levels
16
+ %w[L M Q H]
17
+ end
18
+
19
+
20
+ private # ---------------------------------------------------------------
21
+
22
+ ##
23
+ # Specify the Google Chart type.
24
+ #
25
+ def type
26
+ "qr"
27
+ end
28
+
29
+ ##
30
+ # Array of all possible query string parameters.
31
+ #
32
+ def query_string_params
33
+ [:cht, :chs, :chl, :chld, :choe]
34
+ end
35
+
36
+ ##
37
+ # Raise an exception unless the provided data is given as a string and
38
+ # not too long.
39
+ #
40
+ def validate_data_format
41
+ unless data.is_a?(String)
42
+ raise DataFormatError, "Barcode data should be given as a string"
43
+ end
44
+ ecs = self.class.ec_levels
45
+ unless ec_level.nil? or ecs.include?(ec_level.to_s.upcase)
46
+ raise DataFormatError, "Error correction level must be #{ecs.join(', ')}"
47
+ end
48
+ #unless data.size <= 4296
49
+ # raise DataFormatError, "Barcode data can be at most 4296 characters"
50
+ #end
51
+ end
52
+
53
+ ##
54
+ # Label query string parameter (text to be encoded).
55
+ #
56
+ def chl
57
+ data
58
+ end
59
+
60
+ ##
61
+ # Error correction and margins.
62
+ #
63
+ def chld
64
+ # only return non-nil if non-default value given
65
+ return nil unless (
66
+ (ec_level and ![:l, "l", "L"].include?(ec_level)) or
67
+ (margin and margin != 4)
68
+ )
69
+ ec = ec_level || "L"
70
+ m = margin || 4
71
+ ec.to_s.upcase + (m != 4 ? "|#{m}" : "")
72
+ end
73
+
74
+ ##
75
+ # Output encoding query string parameter.
76
+ #
77
+ def choe
78
+ case encoding
79
+ when :shift_jis: "Shift_JIS"
80
+ when :iso88591: "ISO-8859-1"
81
+ else "UTF-8"
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,14 @@
1
+ module SmartChart
2
+ class Radar < MultipleDataSetChart
3
+ include GridLines
4
+
5
+ private # ---------------------------------------------------------------
6
+
7
+ ##
8
+ # Specify the Google Chart type.
9
+ #
10
+ def type
11
+ "r" + (style == :filled ? "s" : "")
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,21 @@
1
+ module SmartChart
2
+ class Scatter < MultipleDataSetChart
3
+ include GridLines
4
+
5
+ private # ---------------------------------------------------------------
6
+
7
+ ##
8
+ # Specify the Google Chart type.
9
+ #
10
+ def type
11
+ "s"
12
+ end
13
+
14
+ ##
15
+ # Array of all possible query string parameters.
16
+ #
17
+ def query_string_params
18
+ super + [:chg]
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ module SmartChart
2
+ class Venn < SingleDataSetChart
3
+
4
+ private # ---------------------------------------------------------------
5
+
6
+ ##
7
+ # Specify the Google Chart type.
8
+ #
9
+ def type
10
+ "v"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module SmartChart
2
+ class DataSet
3
+
4
+ attr_accessor :values, :style
5
+
6
+ def initialize(values, style)
7
+ self.values = values
8
+ self.style = style
9
+ end
10
+
11
+ def to_s
12
+ Encoder::Simple.new(values).to_s
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,226 @@
1
+ module SmartChart
2
+ module Encoder
3
+
4
+ ##
5
+ # Choose the best encoder, instantiate, and return.
6
+ #
7
+ def self.encode(data, min = nil, max = nil)
8
+ Simple.new(data, min, max)
9
+ end
10
+
11
+ ##
12
+ # The Base encoder defines a public interface for all Encoders. To create
13
+ # an actual Encoder, your subclass should implement _only_ these private
14
+ # methods:
15
+ #
16
+ # token # the single character encoding name
17
+ # digits # the "alphabet" of the encoding (array)
18
+ # missing # digit used for missing data
19
+ # delimiter # between data points
20
+ # separator # between data series
21
+ #
22
+ class Base
23
+
24
+ attr_accessor :data_sets, :min, :max
25
+
26
+ def initialize(data_sets, min = nil, max = nil)
27
+ self.data_sets = data_sets
28
+ self.min = min || data_sets.flatten.compact.min
29
+ self.max = max || data_sets.flatten.compact.max
30
+ end
31
+
32
+ ##
33
+ # The data, encoded as a string (eg: "s:e5Gf4").
34
+ #
35
+ def to_s
36
+ token + ":" + encode
37
+ end
38
+
39
+
40
+ private # -------------------------------------------------------------
41
+
42
+ ##
43
+ # The business end: map arrays of numbers onto a range of characters.
44
+ # See http://code.google.com/apis/chart/formats.html for specs.
45
+ #
46
+ def encode
47
+ encoded = []
48
+ data_sets.each_with_index do |set,i|
49
+ encoded[i] = []
50
+ set.each do |d|
51
+ if d.nil?
52
+ char = missing
53
+ else
54
+ if min == max # don't die when only one data point given
55
+ char = digits.last
56
+ else
57
+ n = (d - min).to_f / (max - min).to_f
58
+ n = (n * (digits.size - 1)).floor
59
+ char = digits[n]
60
+ end
61
+ end
62
+ encoded[i] << char
63
+ end
64
+ end
65
+ encoded.map{ |set| set.join(delimiter) }.join(separator)
66
+ end
67
+
68
+ ##
69
+ # The single-character name of the encoding, as specified in URLs.
70
+ # All encoders must implement this method.
71
+ #
72
+ def token
73
+ fail
74
+ end
75
+
76
+ ##
77
+ # An array of "digits": the encoding's alphabet, in order.
78
+ # All encoders must implement this method.
79
+ #
80
+ def digits
81
+ fail
82
+ end
83
+
84
+ ##
85
+ # String used for missing data point.
86
+ # All encoders must implement this method.
87
+ #
88
+ def missing
89
+ fail
90
+ end
91
+
92
+ ##
93
+ # Data series separator.
94
+ #
95
+ def separator
96
+ fail
97
+ end
98
+
99
+ ##
100
+ # Data point delimiter.
101
+ #
102
+ def delimiter
103
+ ""
104
+ end
105
+
106
+ ##
107
+ # Array of uppercase letters.
108
+ #
109
+ def uppers
110
+ chars = ["A"]
111
+ chars << chars.last.succ while chars.size < 26
112
+ chars
113
+ end
114
+
115
+ ##
116
+ # Array of lowercase letters.
117
+ #
118
+ def lowers
119
+ chars = ["a"]
120
+ chars << chars.last.succ while chars.size < 26
121
+ chars
122
+ end
123
+
124
+ ##
125
+ # Array of numerals.
126
+ #
127
+ def numerals
128
+ chars = ["0"]
129
+ chars << chars.last.succ while chars.size < 10
130
+ chars
131
+ end
132
+ end
133
+
134
+
135
+ ##
136
+ # Simple encoding (http://code.google.com/apis/chart/formats.html#simple).
137
+ #
138
+ class Simple < Base
139
+ private
140
+
141
+ def token
142
+ "s"
143
+ end
144
+
145
+ ##
146
+ # ABC...XYZabc...xyz0123456789
147
+ #
148
+ def digits
149
+ uppers + lowers + numerals
150
+ end
151
+
152
+ def missing
153
+ "_"
154
+ end
155
+
156
+ def separator
157
+ ","
158
+ end
159
+ end
160
+
161
+
162
+ ##
163
+ # Text encoding (http://code.google.com/apis/chart/formats.html#text).
164
+ #
165
+ class Text < Base
166
+ private
167
+
168
+ def token
169
+ "t"
170
+ end
171
+
172
+ ##
173
+ # ABC...XYZabc...xyz0123456789
174
+ #
175
+ def digits
176
+ (0..100).to_a.map{ |i| i.to_s }
177
+ end
178
+
179
+ def missing
180
+ "-1"
181
+ end
182
+
183
+ def separator
184
+ "|"
185
+ end
186
+
187
+ def delimiter
188
+ ","
189
+ end
190
+ end
191
+
192
+
193
+ ##
194
+ # Extended encoding (http://code.google.com/apis/chart/formats.html#extended).
195
+ #
196
+ class Extended < Base
197
+ private
198
+
199
+ def token
200
+ "e"
201
+ end
202
+
203
+ ##
204
+ # AA, AB, AC, ..., .8, .9, .-, ..
205
+ #
206
+ def digits
207
+ chars = uppers + lowers + numerals + %w[- .]
208
+ arr = []
209
+ chars.each do |c1|
210
+ chars.each do |c2|
211
+ arr << c1 + c2
212
+ end
213
+ end
214
+ arr
215
+ end
216
+
217
+ def missing
218
+ "__"
219
+ end
220
+
221
+ def separator
222
+ ","
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,43 @@
1
+ module SmartChart
2
+
3
+ class SmartChartError < StandardError #:nodoc:
4
+ end
5
+
6
+ class DataFormatError < SmartChartError #:nodoc:
7
+ end
8
+
9
+ class ColorFormatError < SmartChartError #:nodoc:
10
+ end
11
+
12
+ class NoAttributeError < SmartChartError #:nodoc:
13
+ def initialize(chart, param)
14
+ chart_type = chart.class.to_s.sub(/^SmartChart::/, "")
15
+ super("The #{chart_type} chart type does not accept the '#{param}' parameter")
16
+ end
17
+ end
18
+
19
+
20
+ # --- validations ---------------------------------------------------------
21
+
22
+ class ValidationError < SmartChartError #:nodoc:
23
+ end
24
+
25
+ class MissingRequiredAttributeError < ValidationError #:nodoc:
26
+ def initialize(chart, param)
27
+ chart_type = chart.class.to_s.sub(/^SmartChart::/, "")
28
+ super("The #{chart_type} chart type requires the '#{param}' parameter")
29
+ end
30
+ end
31
+
32
+ class DimensionsError < ValidationError #:nodoc:
33
+ def initialize(message = nil)
34
+ super(message || "Chart dimensions must result in at most 300,000 pixels")
35
+ end
36
+ end
37
+
38
+ class UrlLengthError < ValidationError #:nodoc:
39
+ def initialize(message = nil)
40
+ super(message || "URL too long (must not exceed #{SmartChart::URL_MAX_LENGTH} characters)")
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ module SmartChart
2
+ module AxisLines
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ attr_accessor :axis
7
+ end
8
+ end
9
+
10
+
11
+ private # ---------------------------------------------------------------
12
+
13
+ ##
14
+ # Axis type parameter.
15
+ #
16
+ def chxt
17
+ #return nil unless (axis.is_a?(Hash) and (axis[:sides].is_a?(Array))
18
+ "TODO"
19
+ end
20
+
21
+ ##
22
+ # Axis style parameter.
23
+ #
24
+ def chxs
25
+ #return nil unless (axis.is_a?(Hash) and (axis[:color] or axis[:style]))
26
+ "TODO"
27
+ end
28
+ end
29
+ end