rchart 1.2.2 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,265 @@
1
+
2
+ module RchartHelper
3
+
4
+ # Set font Properties font_name,font_size
5
+ # font_name is
6
+ # * GeosansLight.ttf,
7
+ # * MankSans.ttf,
8
+ # * pf_arma_five.ttf,
9
+ # * Silkscreen.ttf,
10
+ # * tahoma.ttf
11
+ def set_font_properties(font_name, font_size)
12
+ @font_size = font_size
13
+ @font_name = "#{Rchart::FONT_PATH}/#{font_name}"
14
+ end
15
+
16
+ def validate_color(b, g, r)
17
+ r = 0 if ( r < 0 )
18
+ r = 255 if ( r > 255 )
19
+ g = 0 if ( g < 0 )
20
+ g = 255 if ( g > 255 )
21
+ b = 0 if ( b < 0 )
22
+ b = 255 if ( b > 255 )
23
+ return b, g, r
24
+ end
25
+ #color Helper
26
+ def allocate_color(picture,r,g,b,factor=0)
27
+ r = r + factor
28
+ g = g + factor
29
+ b = b + factor
30
+ b,g,r= validate_color(b,g,r)
31
+ image_color_allocate(picture,r,g,b)
32
+ end
33
+
34
+ #Use this function to set shadow properties.
35
+ def set_shadow_properties(x_distance=1,y_distance=1,r=60,g=60,b=60,alpha=50,blur=0)
36
+ @shadow_active = true
37
+ @shadow_x_distance = x_distance
38
+ @shadow_y_distance = y_distance
39
+ @shadow_r_color = r
40
+ @shadow_g_color = g
41
+ @shadow_b_color = b
42
+ @shadow_alpha = alpha
43
+ @shadow_blur = blur
44
+ end
45
+
46
+ # Use this function to deactivate the shadow options.
47
+ # Drawing shadows is time and CPU intensive.
48
+ def clear_shadow
49
+ @shadow_active = false
50
+ end
51
+
52
+
53
+ # This function allow you to customise the way lines are drawn in charts.
54
+ # This function only applies during chart drawing calls ( line charts,.. ).
55
+ # You can specify the width of the lines & if they are dotted.
56
+ def set_line_style(width=1,dot_size=0)
57
+ @line_width = width
58
+ @line_dot_size = dot_size
59
+ end
60
+
61
+ # Set currency symbol
62
+ def set_currency(currency)
63
+ @currency = currency
64
+ end
65
+ # This function allows you to merge an external PNG picture with your graph specifying the position and the transparency
66
+ def draw_from_png(file_name,x,y,alpha=100)
67
+ draw_from_picture(1,file_name,x,y,alpha)
68
+ end
69
+
70
+ #This function allows you to merge an external GIF picture with your graph specifying the position and the transparenc
71
+ #def draw_from_gif(file_name,x,y,alpha=100)
72
+ #self.draw_from_picture(2,file_name,x,y,alpha)
73
+ #end
74
+
75
+ # This function allows you to merge an external JPG picture with your graph specifying the position and the transparency.
76
+ def draw_from_jpg(file_name,x,y,alpha=100)
77
+ draw_from_picture(3,file_name,x,y,alpha)
78
+ end
79
+
80
+ # Generic loader function for external pictures accepts png format
81
+ def draw_from_picture(pic_type,file_name,x,y,alpha=100)
82
+ if ( File.exist?(file_name))
83
+ raster = image_create_from_png(file_name) if ( pic_type == 1 )
84
+ # raster = image_create_from_gif(file_name) if ( pic_type == 2 )
85
+ raster = image_create_from_jpeg(file_name) if ( pic_type == 3 )
86
+ infos = get_image_size(raster)
87
+ width = infos[0]
88
+ height = infos[1]
89
+ image_copy_merge(raster,@picture,x,y,0,0,width,height,alpha)
90
+ image_destroy(raster)
91
+ end
92
+ end
93
+
94
+
95
+ # Validate data contained in the description array Internal function
96
+ def validate_data_description(function_name,data_description,description_required=true)
97
+ if (data_description["position"].nil?)
98
+ @errors << "[Warning] #{function_name} - Y Labels are not set."
99
+ data_description["position"] = "name"
100
+ end
101
+
102
+ if (description_required)
103
+ if ((data_description["description"].nil?))
104
+ @errors << "[Warning] #{function_name} - Series descriptions are not set."
105
+ data_description["values"].each do |value|
106
+ if data_description["description"].nil?
107
+ data_description["description"]={value=> value}
108
+ else
109
+ data_description["description"]=data_description["description"].merge(value=>value)
110
+ end
111
+ end
112
+ end
113
+
114
+ data_desc_count = data_description["values"].is_a?(Array) ? data_description["values"].count : 1
115
+ if ((data_description["description"].count) < data_desc_count)
116
+ @errors << "[Warning] #{function_name} - Some series descriptions are not set."
117
+ data_description["values"].each do |value|
118
+ data_description["description"][value] = value if ( data_description["description"][value].nil?)
119
+ end
120
+ end
121
+ end
122
+ return data_description
123
+ end
124
+ #Validate data contained in the data array Internal function
125
+ def validate_data(function_name,data)
126
+ data_summary = {}
127
+ data.each do |v|
128
+ v.each do |key,val|
129
+
130
+ if (data_summary[key].nil?)
131
+ data_summary[key] = 1
132
+ else
133
+ data_summary[key] = data_summary[key]+1
134
+ end
135
+ end
136
+ end
137
+ if ( data_summary.max.last == 0 ) #TODO Check method
138
+ @errors << "[Warning] #{function_name} No data set."
139
+ end
140
+ data_summary.each do |k,v|
141
+ if v < data_summary.max.last
142
+ @errors << "#{function_name} Missing Data in serie #{key}"
143
+ end
144
+ end
145
+ return data
146
+ end
147
+ #Convert seconds to a time format string
148
+ def to_time(value)
149
+ hour = (value/3600).floor
150
+ minute = ((value - hour*3600)/60).floor
151
+ second =(value - hour*3600 - minute*60).floor
152
+
153
+ hour = "0.#{Hour}" if (hour.length == 1 )
154
+ minute = "0.#{minute}" if (minute.length == 1 )
155
+ second = "0.#{second}" if (second.length == 1 )
156
+
157
+ return ("#{hour}.:.#{minute}}.:.#{second}")
158
+ end
159
+
160
+ # Convert to metric system */
161
+ def to_metric(value)
162
+ go = (value/1000000000).floor
163
+ mo = ((value - go*1000000000)/1000000).floor
164
+ ko = ((value - go*1000000000 - mo*1000000)/1000).floor
165
+ o = (value - go*1000000000 - mo*1000000 - ko*1000).floor
166
+
167
+ return("#{go}..#{mo}.g") if (go != 0)
168
+ return("#{mo}...#{ko}.m") if (mo != 0)
169
+ return("#{ko}...#{o}).k") if (ko != 0)
170
+ return o
171
+ end
172
+
173
+ # Convert to curency
174
+ def to_currency(value)
175
+ go = (value/1000000000).floor
176
+ mo = ((value - go*1000000000)/1000000).floor
177
+ ko = ((value - go*1000000000 - mo*1000000)/1000).floor
178
+ o = (value - go*1000000000 - mo*1000000 - ko*1000).floor
179
+
180
+ o = "00.#{o}" if ( (o.length) == 1 )
181
+ o = "0.#{o}" if ( (o.length) == 2 )
182
+
183
+ result_string = o
184
+ result_string = "#{ko}...#{result_string}" if ( ko != 0 )
185
+ result_string = "#{mo}...#{result_string}" if ( mo != 0 )
186
+ result_string = "#{go}...#{result_string}" if ( go != 0 )
187
+
188
+ result_string = @currency.result_string
189
+ result_string
190
+ end
191
+ # Set date format for axis labels TODO
192
+ def set_date_format(format)
193
+ @date_format = format
194
+ end
195
+
196
+ def to_date(value)
197
+ #return(Time.parse(value))
198
+ end
199
+ # Check if a number is a full integer (for scaling)
200
+ def is_real_int(value)
201
+ value.ceil == value.floor
202
+ end
203
+ # round of particular decimal
204
+ def round_of(no,n=0)
205
+ (no * (10.0 ** n)).round * (10.0 ** (-n))
206
+ end
207
+
208
+ #convert degree to radian
209
+ def deg2rad(deg)
210
+ deg*Math::PI/180
211
+ end
212
+
213
+ def raise_fatal(message)
214
+ puts "[FATAL] "+message
215
+ return -1
216
+ end
217
+ # Print all error messages on the CLI or graphically
218
+ def print_errors(mode="cli")
219
+ return(0) if (@errors.count == 0)
220
+
221
+ if mode == "cli"
222
+ @errors.each do |value|
223
+ puts value
224
+ end
225
+ elsif ( mode == "gd" )
226
+ set_line_style(width=1)
227
+ max_width = 0
228
+ @errors.each do |value|
229
+ position = image_ftb_box(@error_font_size,0,@error_font_name,value)
230
+ text_width = position[2]-position[0]
231
+ max_width = text_width if ( text_width > max_width )
232
+ end
233
+ draw_filled_rounded_rectangle(@x_size-(max_width+20),@y_size-(20+((@error_font_size+4)*(@errors.count))),@x_size-10,@y_size-10,6,233,185,185)
234
+ draw_rounded_rectangle(@x_size-(max_width+20),@y_size-(20+((@error_font_size+4)*(@errors.count))),@x_size-10,@y_size-10,6,193,145,145)
235
+ c_text_color = allocate_color(@picture,133,85,85)
236
+ ypos = @y_size - (18 + ((@errors.count)-1) * (@error_font_size + 4))
237
+ @errors.each do |value|
238
+ image_ttf_text(@picture,@error_font_size,0,@x_size-(max_width+15),ypos,c_text_color,@error_font_name,value)
239
+ ypos = ypos + (@error_font_size + 4)
240
+ end
241
+ end
242
+ end
243
+
244
+ # resize image on passing png,jpeg,or gd image
245
+ # pass file_name/gd image,new_file_name,percentage,or resize width,resize height
246
+ def resize_image(file_name,resize_file_name="test",resized_width=0,resized_height=0,render_file_as="png")
247
+ image = Image.import(file_name)
248
+ resize_image = image.resize(resized_width, resized_height,true)
249
+
250
+ file=File.new(resize_file_name,"wb")
251
+ if render_file_as == "png"
252
+ file.write resize_image.png
253
+ elsif render_file_as == "jpeg"
254
+ file.write resize_image.jpeg
255
+ elsif render_file_as == "gd"
256
+ file.write resize_image.gd
257
+ elsif render_file_as == "gd2"
258
+ file.write resize_image.gd2
259
+ else
260
+ puts "Provide proper image"
261
+ end
262
+ file.close
263
+ end
264
+
265
+ end
@@ -1,7 +1,9 @@
1
+ require 'rubygems'
2
+
1
3
  class Rdata
2
4
  # This function create a new Rdata object.
3
5
  # This object will be used during all the steps of the data population.
4
- # Data will be extracted from this object using get_data and get_data_description
6
+ # Data will be extracted from this object using get_data and get_data_description
5
7
  def initialize
6
8
  @data = []
7
9
  @data_description = {}
@@ -36,7 +38,7 @@ class Rdata
36
38
  @data[id]= @data[id].merge(serie => value)
37
39
  end
38
40
  if description != ""
39
- @data[id]["name"] = description;
41
+ @data[id]["name"] = description
40
42
  elsif @data[id]["name"].nil?
41
43
  @data[id]["name"] = id
42
44
  end
@@ -85,9 +87,9 @@ class Rdata
85
87
  # Generate some data...
86
88
  # * chart_data.add_point([2,4,9,5,1,0],"Serie1")
87
89
  # * chart_data.add_point([(1,1,2,2,3,3],"Serie2")
88
- # This will mark both Serie1 & Serie2 as "graphable"
90
+ # This will mark both Serie1 & Serie2 as "graphable"
89
91
  # * chart_data.add_all_series
90
-
92
+
91
93
  def add_all_series
92
94
  @data_description["values"] = []
93
95
  if(!@data[0].nil?)
@@ -108,10 +110,10 @@ class Rdata
108
110
  # * chart_data.add_all_series
109
111
  # This will remove the "graphable" status of Serie2
110
112
  # * chart_data.remove_serie("Serie2")
111
-
113
+
112
114
  def remove_serie(serie_name="Serie1")
113
115
  if (!@data_description["values"].nil?)
114
- found = false;
116
+ found = false
115
117
  @data_description["values"].each do |v|
116
118
  @data_description["values"].delete(v) if (v == serie_name )
117
119
  end
@@ -126,7 +128,7 @@ class Rdata
126
128
  # * chart_data.add_serie("Serie2")
127
129
  # * chart_data.add_serie("Serie3")
128
130
  # Set Serie as abcisse label
129
- # * chart_data.set_abscise_label_serie("Serie1")
131
+ # * chart_data.set_abscise_label_serie("Serie1")
130
132
  def set_abscise_label_serie(serie_name = "name")
131
133
  @data_description["position"] = serie_name
132
134
  end
@@ -139,7 +141,7 @@ class Rdata
139
141
  # * chart_data.set_serie_name("January")
140
142
  # This will set the name of Serie2 to "February"
141
143
  # * chart_data.set_serie_name("February","Serie2")
142
-
144
+
143
145
  def set_serie_name(name,serie_name="Serie1")
144
146
  if @data_description["description"].nil?
145
147
  @data_description["description"]={serie_name => name}
@@ -158,7 +160,7 @@ class Rdata
158
160
  end
159
161
  end
160
162
  # This will give a name to the Y axis, writting it horizontally behind the chart
161
- # * chart_data.set_y_axis_name("Temperature")
163
+ # * chart_data.set_y_axis_name("Temperature")
162
164
  def set_y_axis_name(name="Y Axis")
163
165
  if @data_description["axis"].nil?
164
166
  @data_description["axis"]= {"y" => name}
@@ -187,11 +189,11 @@ class Rdata
187
189
  def set_x_axis_unit(unit="")
188
190
  @data_description["unit"]["x"] = unit
189
191
  end
190
-
192
+
191
193
  # Set the axis unit. This will be appended to the axis value
192
194
  # Give the "m/s" unit to the Y axis
193
195
  # * chart_data.set_x_axis_unit("m/s")
194
-
196
+
195
197
  def set_y_axis_unit(unit="")
196
198
  @data_description["unit"]["y"] = unit
197
199
  end
@@ -219,7 +221,7 @@ class Rdata
219
221
  # This function can be used to remove the description of a serie.
220
222
  # This description will be written on the graph when calling the drawLegend function.
221
223
  # Removing it's name using this function can be usefull to hide previously used series
222
-
224
+
223
225
  def remove_all_series
224
226
  @data_description["values"].each do |v|
225
227
  @data_description["values"] = []
@@ -230,10 +232,9 @@ class Rdata
230
232
  def get_data
231
233
  @data
232
234
  end
233
- # This function is used everytime you want to retrieve the Data description stored in the Rdata structure
235
+ # This function is used everytime you want to retrieve the Data description stored in the Rdata structure
234
236
  def get_data_description
235
237
  @data_description
236
238
  end
237
239
 
238
240
  end
239
-
@@ -0,0 +1,538 @@
1
+ module Scale
2
+ # Allow you to clear the scale : used if drawing multiple charts
3
+ # You'll need to call this function only if you're planning to draw a second chart in the rendered picture.
4
+ # Calling this function will clear the current scaling parameters thus you'll need to call again the draw_scale function before drawing any new chart.
5
+ def clear_scale
6
+ @vmin = nil
7
+ @vmax = nil
8
+ @v_x_min = nil
9
+ @v_x_max = nil
10
+ @divisions = 0
11
+ @x_divisions = 0
12
+ end
13
+
14
+ # Allow you to fix the scale, use this to bypass the automatic scaling
15
+ # You can use this function to skip the automatic scaling.
16
+ # vmin and vmax will be used to render the graph.
17
+ def set_fixed_scale(v_min,v_max,divisions=5,v_x_min=0,v_x_max=0,x_divisions=5)
18
+ @vmin = v_min.to_f
19
+ @vmax = v_max.to_f
20
+ @divisions = divisions.to_f
21
+
22
+ if (!v_x_min == 0 )
23
+ @v_x_min = v_x_min.to_f
24
+ @v_x_max = v_x_max.to_f
25
+ @x_divisions = x_divisions.to_f
26
+ end
27
+ end
28
+
29
+ # Wrapper to the draw_scale function allowing a second scale to be drawn
30
+ # It takes the same parameters of the draw_scale function.
31
+ # The scale values will be written on the right side of the graph area.
32
+ def draw_right_scale(data,data_description,scale_mode,r,g,b,draw_ticks=true,angle=0,decimals=1,with_margin=false,skip_labels=1)
33
+ self. draw_scale(data, data_description, scale_mode, r, g, b,draw_ticks,angle,decimals,with_margin,skip_labels,true)
34
+ end
35
+
36
+ # This function will draw both axis and write values on it. You can disable the labelling of the axis setting draw_ticks to false. angle can be used to rotate the vertical ticks labels.
37
+ # decimal specify the number of decimal values we want to keep. Setting draw_ticks to false will not draw vertical & horizontal ticks on the axis ( labels will also not be written ).
38
+ # There is four way of computing scales :
39
+ # * Getting Max & Min values per serie : scale_mode = Rchart::SCALE_NORMAL
40
+ # * Like the previous one but setting the min value to 0 : scale_mode = Rchart::SCALE_START0
41
+ # * Getting the series cumulative Max & Min values : scale_mode = Rchart::SCALE_ADDALL
42
+ # * Like the previous one but setting the min value to 0 : scale_mode = Rchart::SCALE_ADDALLSTART0
43
+ # This will depends on the kind of graph you are drawing, Drawing graphs were you want to fix the min value to 0 you must use the Rchart::SCALE_START0 option.
44
+ # You can display only one x label every xi labels using the skip_labels parameter.
45
+ # Keeping with_margin to false will make the chart use all the width of the graph area. For most graphs the rendering will be better. In some circumstances you'll have to set it to true ( introducing left & right margin ) : bar charts will require it.
46
+ def draw_scale(data,data_description,scale_mode,r,g,b,draw_ticks=true,angle=0,decimals=1,with_margin=false,skip_labels=1,right_scale=false)
47
+ # Validate the Data and DataDescription array
48
+ data = self.validate_data("draw_scale",data)
49
+ c_text_color = allocate_color(@picture,r,g,b)
50
+ self.draw_line(@g_area_x1,@g_area_y1,@g_area_x1,@g_area_y2,r,g,b)
51
+ self.draw_line(@g_area_x1,@g_area_y2,@g_area_x2,@g_area_y2,r,g,b)
52
+ scale =0
53
+ divisions =0
54
+ if(@vmin.nil? && @vmax.nil?)
55
+ if (!data_description["values"][0].nil?)
56
+ #My hack TODO for LINE GRAPH
57
+ if data_description["values"].is_a?(Array)
58
+ @vmin =data[0][data_description["values"][0]]
59
+ @vmax =data[0][data_description["values"][0]]
60
+ else
61
+ @vmin =data[0][data_description["values"][0]]
62
+ @vmax =data[0][data_description["values"]]
63
+ end
64
+
65
+ else
66
+ @vmin = 2147483647
67
+ @vmax = -2147483647
68
+ end
69
+ # /* Compute Min and Max values */
70
+ if(scale_mode == Rchart::SCALE_NORMAL || scale_mode == Rchart::SCALE_START0)
71
+ @vmin = 0 if (scale_mode == Rchart::SCALE_START0 )
72
+
73
+ data.each do |key|
74
+ data_description["values"].each do |col_name|
75
+ if(!key[col_name].nil?)
76
+ value = key[col_name]
77
+ if (value.is_a?(Numeric))
78
+ @vmax = value if ( @vmax < value)
79
+ @vmin = value if ( @vmin > value)
80
+ end
81
+ end
82
+ end
83
+ end
84
+ elsif ( scale_mode == Rchart::SCALE_ADDALL || scale_mode == Rchart::SCALE_ADDALLSTART0 ) # Experimental
85
+ @vmin = 0 if (scale_mode == Rchart::SCALE_ADDALLSTART0)
86
+ data.each do |key|
87
+ sum = 0
88
+ data_description["values"].each do|col_name|
89
+ if (!key[col_name].nil?)
90
+ value =key[col_name]
91
+ sum += value if ((value).is_a?(Numeric))
92
+ end
93
+ end
94
+ @vmax = sum if (@vmax < sum)
95
+ @vmin = sum if (@vmin > sum)
96
+ end
97
+
98
+ end
99
+
100
+ if(@vmax.is_a?(String))
101
+ @vmax = @vmax.gsub(/\.[0-9]+/,'')+1 if (@vmax > @vmax.gsub(/\.[0-9]+/,'') )
102
+ end
103
+ # If all values are the same */
104
+ if ( @vmax == @vmin )
105
+ if ( @vmax >= 0 )
106
+ @vmax = @vmax+1
107
+ else
108
+ @vmin = @vmin-1
109
+ end
110
+ end
111
+
112
+ data_range = @vmax - @vmin
113
+ data_range = 0.1 if (data_range.to_f == 0.0)
114
+
115
+ #Compute automatic scaling */
116
+ scale_ok = false
117
+ factor = 1
118
+ min_div_height = 25
119
+ max_divs = (@g_area_y2 - @g_area_y1)*1.0 / min_div_height
120
+
121
+ if (@vmin == 0 && @vmax == 0 )
122
+ @vmin = 0
123
+ @vmax = 2
124
+ scale = 1
125
+ divisions = 2
126
+ elsif (max_divs > 1)
127
+ while(!scale_ok)
128
+ scale1 = ( @vmax - @vmin )*1.0 / factor
129
+ scale2 = ( @vmax - @vmin )*1.0 /factor / 2
130
+ # scale4 = ( @vmax - @vmin )*1.0 / factor / 4
131
+ if ( scale1 > 1 && scale1 <= max_divs && !scale_ok)
132
+ scale_ok = true
133
+ divisions = (scale1).floor
134
+ scale = 1
135
+ end
136
+ if (scale2 > 1 && scale2 <= max_divs && !scale_ok)
137
+ scale_ok = true
138
+ divisions = (scale2).floor
139
+ scale = 2
140
+ end
141
+ if (!scale_ok)
142
+ factor = factor * 10 if ( scale2 > 1 )
143
+ factor = factor / 10 if ( scale2 < 1 )
144
+ end
145
+ end # while end
146
+ if ((((@vmax*1.0 / scale) / factor)).floor != ((@vmax*1.0 / scale) / factor))
147
+ grid_id = ( @vmax*1.0 / scale / factor).floor + 1
148
+ @vmax = grid_id * scale * factor
149
+ divisions = divisions+1
150
+ end
151
+
152
+ if (((@vmin*1.0 / scale) / factor).floor != ((@vmin*1.0 / scale) / factor))
153
+
154
+ grid_id = ( @vmin*1.0 / scale / factor).floor
155
+ @vmin = grid_id * scale * factor*1.0
156
+ divisions = divisions+1
157
+ end
158
+
159
+ else #/* Can occurs for small graphs */
160
+ scale = 1
161
+ end
162
+ divisions = 2 if ( divisions.nil? )
163
+
164
+ divisions = divisions-1 if (scale == 1 && divisions%2 == 1)
165
+
166
+ else
167
+ divisions = @divisions
168
+ end
169
+
170
+ @division_count = divisions
171
+ data_range = @vmax - @vmin
172
+ data_range = 0.1 if (data_range.to_f == 0.0 )
173
+ @division_height = ( @g_area_y2 - @g_area_y1 )*1.0 / divisions
174
+ @division_ratio = ( @g_area_y2 - @g_area_y1 )*1.0 /data_range
175
+ @g_area_x_offset = 0
176
+ if ( data.count > 1 )
177
+ if ( with_margin == false)
178
+ @division_width = ( @g_area_x2 - @g_area_x1 )*1.0 / ((data).count-1)
179
+ else
180
+ @division_width = ( @g_area_x2 - @g_area_x1 ) *1.0/ (data).count
181
+ @g_area_x_offset = @division_width*1.0 / 2
182
+ end
183
+ else
184
+ @division_width = (@g_area_x2 - @g_area_x1)*1.0
185
+ @g_area_x_offset = @division_width*1.0 / 2
186
+ end
187
+
188
+ @data_count = (data).count
189
+ return(0) if (draw_ticks == false )
190
+ ypos = @g_area_y2
191
+ xmin = nil
192
+ i =1
193
+
194
+ while(i<= divisions+1)
195
+ if (right_scale )
196
+ self.draw_line(@g_area_x2,ypos,@g_area_x2+5,ypos,r,g,b)
197
+ else
198
+ self.draw_line(@g_area_x1,ypos,@g_area_x1-5,ypos,r,g,b)
199
+ end
200
+ value = @vmin*1.0 + (i-1) * (( @vmax - @vmin ) / divisions)
201
+ value = (round_of(value * (10**decimals),2)) / (10**decimals)
202
+ value= value.round if value.floor == value.ceil
203
+ value = "#{value} #{data_description['unit']['y']}" if ( data_description["format"]["y"]== "number")
204
+ value = self.to_time(value) if ( data_description["format"]["y"] == "time" )
205
+ value = self.to_date(value) if ( data_description["format"]["y"] == "date" )
206
+ value = self.to_metric(value) if ( data_description["format"]["Y"] == "metric" )
207
+ value = self.to_currency(value) if ( data_description["format"]["Y"] == "currency" )
208
+ position = image_ftb_box(@font_size,0,@font_name,value)
209
+ text_width =position[2]-position[0]
210
+ if ( right_scale )
211
+ image_ttf_text(@picture,@font_size,0,@g_area_x2+10,ypos+(@font_size/2),c_text_color,@font_name,value)
212
+ xmin = @g_area_x2+15+text_width if (xmin.nil? || xmin < @g_area_x2+15+text_width )
213
+ else
214
+ image_ttf_text(@picture,@font_size,0,@g_area_x1-10-text_width,ypos+(@font_size/2),c_text_color,@font_name,value)
215
+ xmin = @g_area_x1-10-text_width if ( xmin.nil? || xmin > @g_area_x1-10-text_width)
216
+ end
217
+ ypos = ypos - @division_height
218
+ i = i+1
219
+ end
220
+ # Write the Y Axis caption if set */
221
+
222
+ if (!data_description["axis"].nil? && !data_description["axis"]["y"].nil? )
223
+ position = image_ftb_box(@font_size,90,@font_name,data_description["axis"]["y"])
224
+ text_height = (position[1]).abs+(position[3]).abs
225
+ text_top = ((@g_area_y2 - @g_area_y1) / 2) + @g_area_y1 + (text_height/2)
226
+
227
+ if (right_scale )
228
+ image_ttf_text(@picture,@font_size,90,xmin+@font_size,text_top,c_text_color,@font_name,data_description["axis"]["y"])
229
+ else
230
+ image_ttf_text(@picture,@font_size,90,xmin-@font_size,text_top,c_text_color,@font_name,data_description["axis"]["y"])
231
+ end
232
+ end
233
+ # Horizontal Axis */
234
+ xpos = @g_area_x1 + @g_area_x_offset
235
+ id = 1
236
+ ymax = nil
237
+ data.each do |key|
238
+ if ( id % skip_labels == 0 )
239
+ self.draw_line((xpos).floor,@g_area_y2,(xpos).floor,@g_area_y2+5,r,g,b)
240
+ value =key[data_description["position"]]
241
+ value = "#{value} #{data_description['unit']['x']}" if ( data_description["format"]["x"] == "number" )
242
+ value = self.to_time(value) if ( data_description["format"]["x"] == "time" )
243
+ value = self.to_date(value) if ( data_description["format"]["x"] == "date" )
244
+ value = self.to_metric(value) if ( data_description["format"]["x"] == "metric" )
245
+ value = self.to_currency(value) if ( data_description["format"]["x"] == "currency" )
246
+ position = image_ftb_box(@font_size,angle,@font_name,value.to_s)
247
+ text_width = (position[2]).abs+(position[0]).abs
248
+ text_height = (position[1]).abs+(position[3]).abs
249
+ if ( angle == 0 )
250
+ ypos = @g_area_y2+18
251
+ image_ttf_text(@picture,@font_size,angle,(xpos).floor-(text_width/2).floor,ypos,c_text_color,@font_name,value.to_s)
252
+ else
253
+ ypos = @g_area_y2+10+text_height
254
+ if ( angle <= 90 )
255
+ image_ttf_text(@picture,@font_size,angle,(xpos).floor-text_width+5,ypos,c_text_color,@font_name,value.to_s)
256
+ else
257
+ image_ttf_text(@picture,@font_size,angle,(xpos).floor+text_width+5,ypos,c_text_color,@font_name,value.to_s)
258
+ end
259
+ end
260
+ ymax = ypos if (ymax.nil? ||(!ymax.nil? && ymax < ypos))
261
+ end
262
+ xpos = xpos + @division_width
263
+ id = id+1
264
+ end #loop ended
265
+ #Write the X Axis caption if set */
266
+
267
+ if ((!data_description["axis"].nil? && !data_description["axis"]["x"].nil?) )
268
+ position = image_ftb_box(@font_size,90,@font_name,data_description["axis"]["x"])
269
+ text_width = (position[2]).abs+(position[0]).abs
270
+ text_left = ((@g_area_x2 - @g_area_x1) / 2) + @g_area_x1 + (text_width/2)
271
+ image_ttf_text(@picture,@font_size,0,text_left,ymax+@font_size+5,c_text_color,@font_name,data_description["axis"]["x"].to_s)
272
+ end
273
+
274
+ end
275
+
276
+ # This function is used by scatter charts.
277
+ # It will compute everything needed to draw the associated line and plot charts.
278
+ # You must specify the name of the two series that will be used as X and Y data. By default this function will compute the min & max values of both series, anyway you can override the automatic scaling by calling first the setFixedScale function.
279
+ def draw_xy_scale(data,data_description,y_serie_name,x_serie_name,r,g,b,with_margin=0,angle=0,decimals=1)
280
+
281
+ self.validate_data("draw_xy_scale",data)
282
+ c_text_color = allocate_color(@picture,r,g,b)
283
+ self.draw_line(@g_area_x1,@g_area_y1,@g_area_x1,@g_area_y2,r,g,b)
284
+ self.draw_line(@g_area_x1,@g_area_y2,@g_area_x2,@g_area_y2,r,g,b)
285
+
286
+ # Process Y scale */
287
+ if(@vmin.nil? && @vmax.nil?)
288
+ @vmin = data[0][y_serie_name]
289
+ @vmax = data[0][y_serie_name]
290
+ data.each do |key|
291
+ if !key[y_serie_name].nil?
292
+ value = key[y_serie_name]
293
+ if (value.is_a?(Numeric))
294
+ @vmax = value if ( @vmax < value)
295
+ @vmin = value if ( @vmin > value)
296
+ end
297
+ end
298
+ end
299
+
300
+ if(@vmax.is_a?(String))
301
+ @vmax = @vmax.gsub(/\.[0-9]+/,'')+1 if (@vmax > @vmax.gsub(/\.[0-9]+/,'') )
302
+ end
303
+ data_range = @vmax - @vmin
304
+ data_range = 0.1 if (data_range.to_f == 0.0 )
305
+
306
+ #Compute automatic scaling
307
+ scale_ok = false
308
+ factor = 1
309
+ min_div_height = 25
310
+ max_divs = (@g_area_y2 - @g_area_y1)*1.0 / min_div_height
311
+ if (@vmin == 0 && @vmax == 0 )
312
+ @vmin = 0
313
+ @vmax = 2
314
+ scale = 1
315
+ divisions = 2
316
+ elsif (max_divs > 1)
317
+ while(!scale_ok)
318
+ scale1 = ( @vmax - @vmin )*1.0 / factor
319
+ scale2 = ( @vmax - @vmin )*1.0 /factor / 2
320
+ scale4 = ( @vmax - @vmin )*1.0 / factor / 4
321
+
322
+ if ( scale1 > 1 && scale1 <= max_divs && !scale_ok)
323
+ scale_ok = true
324
+ divisions = (scale1).floor
325
+ scale = 1
326
+ end
327
+ if ( scale2 > 1 && scale2 <= max_divs && !scale_ok)
328
+ scale_ok = true
329
+ divisions = (scale2).floor
330
+ scale = 2
331
+ end
332
+ if (!scale_ok)
333
+ factor = factor * 10 if ( scale2 > 1 )
334
+ factor = factor / 10 if ( scale2 < 1 )
335
+ end
336
+ end
337
+ if ((((@vmax*1.0 / scale) / factor)).floor != ((@vmax*1.0 / scale) / factor))
338
+ grid_id = ( @vmax*1.0 / scale / factor).floor + 1
339
+ @vmax = grid_id * scale * factor
340
+ divisions = divisions+1
341
+ end
342
+
343
+ if (((@vmin*1.0 / scale) / factor).floor != ((@vmin*1.0 / scale) / factor))
344
+
345
+ grid_id = ( @vmin*1.0 / scale / factor).floor
346
+ @vmin = grid_id * scale * factor*1.0
347
+ divisions = divisions+1
348
+ end
349
+
350
+ else #/* Can occurs for small graphs */
351
+ scale = 1
352
+ end
353
+ divisions = 2 if ( divisions.nil? )
354
+
355
+ if ( is_real_int((@vmax-@vmin)/(divisions-1)))
356
+ divisions-=1
357
+ elsif ( is_real_int((@vmax-@vmin)/(divisions+1)))
358
+ divisions+=1
359
+ end
360
+ else
361
+ divisions =@divisions
362
+ end
363
+ @division_count = divisions
364
+
365
+ data_range = @vmax - @vmin
366
+ data_range = 0.1 if (data_range.to_f == 0.0 )
367
+ @division_height = ( @g_area_y2 - @g_area_y1 )*1.0 / divisions
368
+ @division_ratio = ( @g_area_y2 - @g_area_y1 )*1.0 /data_range
369
+ ypos = @g_area_y2
370
+ xmin = nil
371
+ i =1
372
+
373
+ while(i<= divisions+1)
374
+ self.draw_line(@g_area_x1,ypos,@g_area_x1-5,ypos,r,g,b)
375
+ value = @vmin*1.0 + (i-1) * (( @vmax - @vmin ) / divisions)
376
+ value = (round_of(value * (10**decimals),2)) / (10**decimals)
377
+ value= value.round if value.floor == value.ceil
378
+ value = "#{value} #{data_description['unit']['y']}" if ( data_description["format"]["y"]== "number")
379
+ value = self.to_time(value) if ( data_description["format"]["y"] == "time" )
380
+ value = self.to_date(value) if ( data_description["format"]["y"] == "date" )
381
+ value = self.to_metric(value) if ( data_description["format"]["Y"] == "metric" )
382
+ value = self.to_currency(value) if ( data_description["format"]["Y"] == "currency" )
383
+
384
+ position = image_ftb_box(@font_size,0,@font_name,value)
385
+ text_width =position[2]-position[0]
386
+ image_ttf_text(@picture,@font_size,0,@g_area_x1-10-text_width,ypos+(@font_size/2),c_text_color,@font_name,value)
387
+ xmin = @g_area_x1-10-text_width if ( xmin.nil? || xmin > @g_area_x1-10-text_width)
388
+ ypos = ypos - @division_height
389
+ i = i+1
390
+
391
+ end
392
+
393
+ # Process X scale */
394
+ if(@v_x_min.nil? && @v_x_max.nil?)
395
+
396
+ @v_x_min =data[0][x_serie_name]
397
+ @v_x_max =data[0][x_serie_name]
398
+ data.each do |key|
399
+
400
+ if !key[x_serie_name].nil?
401
+ value = key[x_serie_name]
402
+ if (value.is_a?(Numeric))
403
+
404
+ @v_x_max = value if ( @v_x_max < value)
405
+ @v_x_min = value if ( @v_x_min > value)
406
+ end
407
+ end
408
+ end
409
+
410
+ if (@v_x_max.is_a?(String))
411
+ @v_x_max = @v_x_max.gsub(/\.[0-9]+/,'')+1 if (@v_x_max > @v_x_max.gsub(/\.[0-9]+/,'') )
412
+ end
413
+
414
+ data_range = @vmax - @vmin
415
+ data_range = 0.1 if (data_range.to_f == 0.0 )
416
+
417
+ # Compute automatic scaling
418
+ scale_ok = false
419
+ factor = 1
420
+ min_div_width = 25
421
+ max_divs = (@g_area_x2 - @g_area_x1) / min_div_width
422
+
423
+ if ( @v_x_min == 0 && @v_x_max == 0 )
424
+ @v_x_min = 0
425
+ @v_x_max = 2
426
+ scale = 1
427
+ x_divisions = 2
428
+ elsif (max_divs > 1)
429
+
430
+ while(!scale_ok)
431
+ scale1 = ( @v_x_max - @v_x_min ) / factor
432
+ scale2 = ( @v_x_max - @v_x_min ) / factor / 2
433
+ scale4 = ( @v_x_max - @v_x_min ) / factor / 4
434
+ if ( scale1 > 1 && scale1 <= max_divs && !scale_ok)
435
+ scale_ok = true
436
+ x_divisions = (scale1).floor
437
+ scale = 1
438
+ end
439
+
440
+ if ( scale2 > 1 && scale2 <= max_divs && !scale_ok)
441
+ scale_ok = true
442
+ x_divisions = (scale2).floor
443
+
444
+ scale = 2
445
+ end
446
+ if (!scale_ok)
447
+ factor = factor * 10 if ( scale2 > 1 )
448
+ factor = factor / 10 if ( scale2 < 1 )
449
+ end
450
+ end
451
+
452
+ if ( (@v_x_max*1.0 / scale / factor).floor != @v_x_max / scale / factor)
453
+ grid_id = ( @v_x_max*1.0 / scale / factor).floor + 1
454
+ @v_x_max = grid_id * scale * factor
455
+ x_divisions+=1
456
+ end
457
+
458
+ if ( (@v_x_min*1.0 / scale / factor).floor != @v_x_min / scale / factor)
459
+ grid_id = floor( @v_x_min / scale / factor)
460
+ @v_x_min = grid_id * scale * factor
461
+ x_divisions+=1
462
+ end
463
+ else #/* Can occurs for small graphs */
464
+ scale = 1
465
+ end
466
+ x_divisions = 2 if ( x_divisions.nil? )
467
+
468
+ if ( is_real_int((@v_x_max-@v_x_min)/(x_divisions-1)))
469
+ x_divisions-=1
470
+ elsif ( is_real_int((@v_x_max-@v_x_min)/(x_divisions+1)))
471
+ x_divisions+=1
472
+ end
473
+ else
474
+
475
+ x_divisions = @x_divisions
476
+ end
477
+
478
+ @x_division_count = divisions
479
+ @data_count = divisions + 2
480
+
481
+ x_data_range = @v_x_max - @v_x_min
482
+ x_data_range = 0.1 if ( x_data_range == 0 )
483
+
484
+ @division_width = ( @g_area_x2 - @g_area_x1 ) / x_divisions
485
+ @x_division_ratio = ( @g_area_x2 - @g_area_x1 ) / x_data_range
486
+ xpos = @g_area_x1
487
+ ymax =nil
488
+ i=1
489
+
490
+ while(i<= x_divisions+1)
491
+ self.draw_line(xpos,@g_area_y2,xpos,@g_area_y2+5,r,g,b)
492
+ value = @v_x_min + (i-1) * (( @v_x_max - @v_x_min ) / x_divisions)
493
+ value = (round_of(value * (10**decimals),2)) / (10**decimals)
494
+ value= value.round if value.floor == value.ceil
495
+ value = "#{value}#{data_description['unit']['y']}" if ( data_description["format"]["y"]== "number")
496
+ value = self.to_time(value) if ( data_description["format"]["y"] == "time" )
497
+ value = self.to_date(value) if ( data_description["format"]["y"] == "date" )
498
+ value = self.to_metric(value) if ( data_description["format"]["Y"] == "metric" )
499
+ value = self.to_currency(value) if ( data_description["format"]["Y"] == "currency" )
500
+ position = image_ftb_box(@font_size,angle,@font_name,value)
501
+ text_width =position[2].abs+position[0].abs
502
+ text_height = position[1].abs+position[3].abs
503
+
504
+ if ( angle == 0 )
505
+ ypos = @g_area_y2+18
506
+ image_ttf_text(@picture,@font_size,angle,(xpos).floor-(text_width/2).floor,ypos,c_text_color,@font_name,value)
507
+ else
508
+
509
+ ypos = @g_area_y2+10+text_height
510
+ if ( angle <= 90 )
511
+ image_ttf_text(@picture,@font_size,angle,(xpos).floor-text_width+5,ypos,c_text_color,@font_name,value)
512
+ else
513
+ image_ttf_text(@picture,@font_size,angle,(xpos).floor+text_width+5,ypos,c_text_color,@font_name,value)
514
+ end
515
+
516
+ end
517
+
518
+ ymax = ypos if (ymax.nil? || ymax < ypos)
519
+ i=i+1
520
+ xpos = xpos + @division_width
521
+ end
522
+ #Write the Y Axis caption if set
523
+ if ((!data_description["axis"].nil? && !data_description["axis"]["y"].nil?) )
524
+ position = image_ftb_box(@font_size,90,@font_name,data_description["axis"]["y"])
525
+ text_height = (position[1]).abs+(position[3]).abs
526
+ text_top = ((@g_area_y2 - @g_area_y1) / 2) + @g_area_y1 + (text_width/2)
527
+ image_ttf_text(@picture,@font_size,90,xmin-@font_size,text_top,c_text_color,@font_name,data_description["axis"]["y"].to_s)
528
+ end
529
+ if ((!data_description["axis"].nil? && !data_description["axis"]["x"].nil?) )
530
+ position = image_ftb_box(@font_size,90,@font_name,data_description["axis"]["x"])
531
+ text_width = (position[2]).abs+(position[0]).abs
532
+ text_left = ((@g_area_x2 - @g_area_x1) / 2) + @g_area_x1 + (text_width/2)
533
+ image_ttf_text(@picture,@font_size,0,text_left,ymax+@font_size+5,c_text_color,@font_name,data_description["axis"]["x"].to_s)
534
+ end
535
+
536
+ end
537
+
538
+ end