svg-graph 2.1.1 → 2.2.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.
@@ -249,17 +249,22 @@ module SVG
249
249
  next if cum_sum[i].nil?
250
250
  c = calc_coords(i, cum_sum[i], fieldwidth, fieldheight)
251
251
  if show_data_points
252
- @graph.add_element( "circle", {
253
- "cx" => c[:x].to_s,
254
- "cy" => c[:y].to_s,
255
- "r" => "2.5",
256
- "class" => "dataPoint#{line}"
257
- })
252
+ shape_selection_string = data[:description][i].to_s
253
+ if !data[:shape][i].nil?
254
+ shape_selection_string = data[:shape][i].to_s
255
+ end
256
+ DataPoint.new(c[:x], c[:y], line).shape(shape_selection_string).each{|s|
257
+ @graph.add_element( *s )
258
+ }
258
259
  end
259
260
 
260
261
  make_datapoint_text( c[:x], c[:y] - font_size/2, cum_sum[i] + minvalue)
261
262
  # number format shall not apply to popup (use .to_s conversion)
262
- add_popup(c[:x], c[:y], (cum_sum[i] + minvalue).to_s)
263
+ descr = ""
264
+ if !data[:description][i].to_s.empty?
265
+ descr = ", #{data[:description][i].to_s}"
266
+ end
267
+ add_popup(c[:x], c[:y], (cum_sum[i] + minvalue).to_s + descr, "", data[:url][i].to_s)
263
268
  end
264
269
  end
265
270
 
@@ -3,30 +3,30 @@ require_relative 'Graph'
3
3
  module SVG
4
4
  module Graph
5
5
  # === Create presentation quality SVG pie graphs easily
6
- #
6
+ #
7
7
  # == Synopsis
8
- #
8
+ #
9
9
  # require 'SVG/Graph/Pie'
10
- #
10
+ #
11
11
  # fields = %w(Jan Feb Mar)
12
12
  # data_sales_02 = [12, 45, 21]
13
- #
13
+ #
14
14
  # graph = SVG::Graph::Pie.new({
15
15
  # :height => 500,
16
16
  # :width => 300,
17
17
  # :fields => fields,
18
18
  # })
19
- #
19
+ #
20
20
  # graph.add_data({
21
21
  # :data => data_sales_02,
22
22
  # :title => 'Sales 2002',
23
23
  # })
24
- #
24
+ #
25
25
  # print "Content-type: image/svg+xml\r\n\r\n"
26
26
  # print graph.burn();
27
- #
27
+ #
28
28
  # == Description
29
- #
29
+ #
30
30
  # This object aims to allow you to easily create high quality
31
31
  # SVG pie graphs. You can either use the default style sheet
32
32
  # or supply your own. Either way there are many options which can
@@ -35,9 +35,9 @@ module SVG
35
35
  # title, subtitle etc.
36
36
  #
37
37
  # = Examples
38
- #
38
+ #
39
39
  # http://www.germane-software/repositories/public/SVG/test/single.rb
40
- #
40
+ #
41
41
  # == See also
42
42
  #
43
43
  # * SVG::Graph::Graph
@@ -73,8 +73,8 @@ module SVG
73
73
  def set_defaults
74
74
  init_with(
75
75
  :show_shadow => true,
76
- :shadow_offset => 10,
77
-
76
+ :shadow_offset => 10,
77
+
78
78
  :show_data_labels => false,
79
79
  :show_actual_values => false,
80
80
  :show_percent => true,
@@ -82,11 +82,11 @@ module SVG
82
82
  :show_key_data_labels => true,
83
83
  :show_key_actual_values => true,
84
84
  :show_key_percent => false,
85
-
85
+
86
86
  :expanded => false,
87
87
  :expand_greatest => false,
88
88
  :expand_gap => 10,
89
-
89
+
90
90
  :show_x_labels => false,
91
91
  :show_y_labels => false,
92
92
  :datapoint_font_size => 12
@@ -110,7 +110,7 @@ module SVG
110
110
  # graph.add_data( { :data => [3,5,8,13] } )
111
111
  #
112
112
  # nil values in the array will be replaced by 0
113
- #
113
+ #
114
114
  # graph.add_data( { :data => [3,nil,nil,2] } ) is equivalent to graph.add_data( { :data => [3,0,0,2] } )
115
115
  #
116
116
  def add_data arg
@@ -123,28 +123,28 @@ module SVG
123
123
  end
124
124
 
125
125
  # If true, displays a drop shadow for the chart
126
- attr_accessor :show_shadow
126
+ attr_accessor :show_shadow
127
127
  # Sets the offset of the shadow from the pie chart
128
128
  attr_accessor :shadow_offset
129
129
  # If true, display the data labels on the chart
130
- attr_accessor :show_data_labels
130
+ attr_accessor :show_data_labels
131
131
  # If true, display the actual field values in the data labels
132
- attr_accessor :show_actual_values
132
+ attr_accessor :show_actual_values
133
133
  # If true, display the percentage value of each pie wedge in the data
134
134
  # labels
135
135
  attr_accessor :show_percent
136
136
  # If true, display the labels in the key
137
- attr_accessor :show_key_data_labels
137
+ attr_accessor :show_key_data_labels
138
138
  # If true, display the actual value of the field in the key
139
- attr_accessor :show_key_actual_values
139
+ attr_accessor :show_key_actual_values
140
140
  # If true, display the percentage value of the wedges in the key
141
141
  attr_accessor :show_key_percent
142
142
  # If true, "explode" the pie (put space between the wedges)
143
- attr_accessor :expanded
143
+ attr_accessor :expanded
144
144
  # If true, expand the largest pie wedge
145
- attr_accessor :expand_greatest
145
+ attr_accessor :expand_greatest
146
146
  # The amount of space between expanded wedges
147
- attr_accessor :expand_gap
147
+ attr_accessor :expand_gap
148
148
  # The font size of the data point labels
149
149
  attr_accessor :datapoint_font_size
150
150
 
@@ -187,7 +187,8 @@ module SVG
187
187
  count += 1
188
188
  v = @data[count]
189
189
  perc = show_key_percent ? " "+(v * percent_scale).round.to_s+"%" : ""
190
- x + " [" + v.to_s + "]" + perc
190
+ val = show_key_actual_values ? " [" + v.to_s + "]" : ""
191
+ x + val + perc
191
192
  }
192
193
  end
193
194
 
@@ -218,21 +219,20 @@ module SVG
218
219
  max_value = max_value < x ? x : max_value
219
220
  total += x
220
221
  }
221
- percent_scale = 100.0 / total
222
222
 
223
223
  prev_percent = 0
224
224
  rad_mult = 3.6 * RADIANS
225
225
  @config[:fields].each_index { |count|
226
226
  value = @data[count].to_f
227
- percent = percent_scale * value
227
+ percent = total.zero? ? 0.0 : 100.0 * value / total
228
228
  radians = prev_percent * rad_mult
229
229
 
230
- if percent == 100.0
230
+ if percent.rationalize(0.001) == 100.0
231
231
  @foreground.add_element( "circle", {
232
232
  "cx" => radius.to_s,
233
233
  "cy" => radius.to_s,
234
234
  "r" => radius.to_s,
235
- "class" => "fill1"
235
+ "class" => "fill#{count+1}"
236
236
  })
237
237
 
238
238
  if show_shadow
@@ -343,7 +343,7 @@ module SVG
343
343
 
344
344
  def get_css
345
345
  return <<EOL
346
- .dataPointLabel{
346
+ .dataPointLabel, .dataPointLabelBackground, .dataPointPopup{
347
347
  fill: #000000;
348
348
  text-anchor:middle;
349
349
  font-size: #{datapoint_font_size}px;
@@ -351,78 +351,89 @@ module SVG
351
351
  font-weight: normal;
352
352
  }
353
353
 
354
+ .dataPointLabelBackground{
355
+ stroke: #ffffff;
356
+ stroke-width: 2;
357
+ }
358
+
359
+ .dataPointPopup{
360
+ fill: #000000;
361
+ visibility: hidden;
362
+ stroke-width: 2;
363
+ }
364
+
354
365
  /* key - MUST match fill styles */
355
366
  .key1,.fill1{
356
367
  fill: #ff0000;
357
368
  fill-opacity: 0.7;
358
369
  stroke: none;
359
- stroke-width: 1px;
370
+ stroke-width: 1px;
360
371
  }
361
372
  .key2,.fill2{
362
373
  fill: #0000ff;
363
374
  fill-opacity: 0.7;
364
375
  stroke: none;
365
- stroke-width: 1px;
376
+ stroke-width: 1px;
366
377
  }
367
378
  .key3,.fill3{
368
379
  fill-opacity: 0.7;
369
380
  fill: #00ff00;
370
381
  stroke: none;
371
- stroke-width: 1px;
382
+ stroke-width: 1px;
372
383
  }
373
384
  .key4,.fill4{
374
385
  fill-opacity: 0.7;
375
386
  fill: #ffcc00;
376
387
  stroke: none;
377
- stroke-width: 1px;
388
+ stroke-width: 1px;
378
389
  }
379
390
  .key5,.fill5{
380
391
  fill-opacity: 0.7;
381
392
  fill: #00ccff;
382
393
  stroke: none;
383
- stroke-width: 1px;
394
+ stroke-width: 1px;
384
395
  }
385
396
  .key6,.fill6{
386
397
  fill-opacity: 0.7;
387
398
  fill: #ff00ff;
388
399
  stroke: none;
389
- stroke-width: 1px;
400
+ stroke-width: 1px;
390
401
  }
391
402
  .key7,.fill7{
392
403
  fill-opacity: 0.7;
393
404
  fill: #00ff99;
394
405
  stroke: none;
395
- stroke-width: 1px;
406
+ stroke-width: 1px;
396
407
  }
397
408
  .key8,.fill8{
398
409
  fill-opacity: 0.7;
399
410
  fill: #ffff00;
400
411
  stroke: none;
401
- stroke-width: 1px;
412
+ stroke-width: 1px;
402
413
  }
403
414
  .key9,.fill9{
404
415
  fill-opacity: 0.7;
405
416
  fill: #cc6666;
406
417
  stroke: none;
407
- stroke-width: 1px;
418
+ stroke-width: 1px;
408
419
  }
409
420
  .key10,.fill10{
410
421
  fill-opacity: 0.7;
411
422
  fill: #663399;
412
423
  stroke: none;
413
- stroke-width: 1px;
424
+ stroke-width: 1px;
414
425
  }
415
426
  .key11,.fill11{
416
427
  fill-opacity: 0.7;
417
428
  fill: #339900;
418
429
  stroke: none;
419
- stroke-width: 1px;
430
+ stroke-width: 1px;
420
431
  }
421
432
  .key12,.fill12{
422
433
  fill-opacity: 0.7;
423
434
  fill: #9966FF;
424
435
  stroke: none;
425
- stroke-width: 1px;
436
+ stroke-width: 1px;
426
437
  }
427
438
  EOL
428
439
  end
@@ -4,48 +4,48 @@ require_relative 'DataPoint'
4
4
  module SVG
5
5
  module Graph
6
6
  # === For creating SVG plots of scalar data
7
- #
7
+ #
8
8
  # = Synopsis
9
- #
9
+ #
10
10
  # require 'SVG/Graph/Plot'
11
- #
11
+ #
12
12
  # # Data sets are x,y pairs
13
13
  # # Note that multiple data sets can differ in length, and that the
14
14
  # # data in the datasets needn't be in order; they will be ordered
15
15
  # # by the plot along the X-axis.
16
16
  # projection = [
17
17
  # 6, 11, 0, 5, 18, 7, 1, 11, 13, 9, 1, 2, 19, 0, 3, 13,
18
- # 7, 9
18
+ # 7, 9
19
19
  # ]
20
20
  # actual = [
21
- # 0, 18, 8, 15, 9, 4, 18, 14, 10, 2, 11, 6, 14, 12,
21
+ # 0, 18, 8, 15, 9, 4, 18, 14, 10, 2, 11, 6, 14, 12,
22
22
  # 15, 6, 4, 17, 2, 12
23
23
  # ]
24
- #
24
+ #
25
25
  # graph = SVG::Graph::Plot.new({
26
26
  # :height => 500,
27
27
  # :width => 300,
28
28
  # :key => true,
29
29
  # :scale_x_integers => true,
30
- # :scale_y_integerrs => true,
30
+ # :scale_y_integers => true,
31
31
  # })
32
- #
32
+ #
33
33
  # graph.add_data({
34
34
  # :data => projection
35
35
  # :title => 'Projected',
36
36
  # })
37
- #
37
+ #
38
38
  # graph.add_data({
39
39
  # :data => actual,
40
40
  # :title => 'Actual',
41
41
  # })
42
- #
42
+ #
43
43
  # print graph.burn()
44
- #
44
+ #
45
45
  # = Description
46
- #
46
+ #
47
47
  # Produces a graph of scalar data.
48
- #
48
+ #
49
49
  # This object aims to allow you to easily create high quality
50
50
  # SVG[http://www.w3c.org/tr/svg] scalar plots. You can either use the
51
51
  # default style sheet or supply your own. Either way there are many options
@@ -54,15 +54,15 @@ module SVG
54
54
  # subtitle etc.
55
55
  #
56
56
  # = Examples
57
- #
57
+ #
58
58
  # http://www.germane-software/repositories/public/SVG/test/plot.rb
59
- #
59
+ #
60
60
  # = Notes
61
- #
62
- # The default stylesheet handles upto 10 data sets, if you
61
+ #
62
+ # The default stylesheet handles upto 12 data sets, if you
63
63
  # use more you must create your own stylesheet and add the
64
64
  # additional settings for the extra data sets. You will know
65
- # if you go over 10 data sets as they will have no style and
65
+ # if you go over 12 data sets as they will have no style and
66
66
  # be in black.
67
67
  #
68
68
  # Unlike the other types of charts, data sets must contain x,y pairs:
@@ -72,9 +72,9 @@ module SVG
72
72
  # Additional possible notation
73
73
  # [ [1,2], 5,6] # A data set with 2 points: (1,2) and (5,6), mixed notation
74
74
  # [ [1,2], [5,6]] # A data set with 2 points: (1,2) and (5,6), nested array
75
- #
75
+ #
76
76
  # = See also
77
- #
77
+ #
78
78
  # * SVG::Graph::Graph
79
79
  # * SVG::Graph::BarHorizontal
80
80
  # * SVG::Graph::Bar
@@ -105,7 +105,7 @@ module SVG
105
105
  :show_lines => true,
106
106
  :round_popups => true,
107
107
  :scale_x_integers => false,
108
- :scale_y_integerrs => false,
108
+ :scale_y_integers => false,
109
109
  )
110
110
  end
111
111
 
@@ -124,20 +124,20 @@ module SVG
124
124
  # would cause the graph to attempt to generate labels stepped by 0.5; EG:
125
125
  # 0, 0.5, 1, 1.5, 2, ...
126
126
  # default is automatic such that there are 10 labels
127
- attr_accessor :scale_y_divisions
127
+ attr_accessor :scale_y_divisions
128
128
  # Make the X axis labels integers, default: false
129
- attr_accessor :scale_x_integers
129
+ attr_accessor :scale_x_integers
130
130
  # Make the Y axis labels integers, default: false
131
- attr_accessor :scale_y_integers
131
+ attr_accessor :scale_y_integers
132
132
  # Fill the area under the line, default: false
133
- attr_accessor :area_fill
133
+ attr_accessor :area_fill
134
134
  # Show a small circle on the graph where the line
135
135
  # goes from one point to the next. default: true
136
136
  attr_accessor :show_data_points
137
137
  # Set the minimum value of the X axis, if nil the minimum from data is chosen, default: nil
138
- attr_accessor :min_x_value
138
+ attr_accessor :min_x_value
139
139
  # Set the maximum value of the X axis, if nil the maximum from data is chosen, default: nil
140
- attr_accessor :max_x_value
140
+ attr_accessor :max_x_value
141
141
  # Set the minimum value of the Y axis, if nil the minimum from data is chosen, default: nil
142
142
  attr_accessor :min_y_value
143
143
  # Set the maximum value of the Y axis, if nil the maximum from data is chosen, default: nil
@@ -155,43 +155,45 @@ module SVG
155
155
  # data_set2 = [[1,2], 5,6]
156
156
  # or
157
157
  # data_set2 = [[1,2], [5,6]]
158
- #
158
+ #
159
159
  # graph.add_data({
160
160
  # :data => data_set1,
161
161
  # :title => 'single point'
162
- # })
162
+ # })
163
163
  # graph.add_data({
164
164
  # :data => data_set2,
165
165
  # :title => 'two points'
166
- # })
166
+ # })
167
+ # @param conf [Hash] with keys
168
+ # :data [Array] of x,y pairs, one pair for each point
169
+ # :title [String] mandatory name of data series for legend of graph
170
+ # :description [Array<String>] (optional) if given, description for each datapoint (shown in popups)
171
+ # :shape [Array<String>] (optional) if given, DataPoint shape is chosen based on this string instead of description
172
+ # :url [Array<String>] (optional) if given, link will be added to each datapoint
167
173
  def add_data(conf)
168
- @data ||= []
169
- raise "No data provided by #{conf.inspect}" unless conf[:data] and
170
- conf[:data].kind_of? Array
174
+ @data ||= []
175
+ raise "No data provided by #{conf.inspect}" unless conf[:data].is_a?(Array)
171
176
  # support array of arrays and flatten it
172
177
  conf[:data] = conf[:data].flatten
173
178
  # check that we have pairs of values
174
179
  raise "Data supplied must be x,y pairs! "+
175
180
  "The data provided contained an odd set of "+
176
181
  "data points" unless conf[:data].length % 2 == 0
177
-
182
+
178
183
  # remove nil values
179
184
  conf[:data] = conf[:data].compact
180
-
181
- return if conf[:data].length == 0
182
185
 
183
- conf[:description] ||= Array.new(conf[:data].size/2)
184
- if conf[:description].size != conf[:data].size/2
185
- raise "Description for popups does not have same size as provided data: #{conf[:description].size} vs #{conf[:data].size/2}"
186
- end
186
+ return if conf[:data].length.zero?
187
+
188
+ add_data_init_or_check_optional_keys(conf, conf[:data].size / 2)
187
189
 
188
190
  x = []
189
191
  y = []
190
192
  conf[:data].each_index {|i|
191
193
  (i%2 == 0 ? x : y) << conf[:data][i]
192
194
  }
193
- sort( x, y, conf[:description] )
194
- conf[:data] = [x,y]
195
+ sort(x, y, conf[:description], conf[:shape], conf[:url])
196
+ conf[:data] = [x, y]
195
197
  # at the end data looks like:
196
198
  # [
197
199
  # [all x values],
@@ -218,20 +220,25 @@ module SVG
218
220
  @border_right = label_right if label_right > @border_right
219
221
  end
220
222
 
221
-
222
223
  X = 0
223
224
  Y = 1
224
225
 
225
226
  def max_x_range
227
+ # needs to be computed fresh when called, to cover the use-case:
228
+ # add_data -> burn -> add_data -> burn
229
+ # when values would be cached, the graph is not updated for second burning
226
230
  max_value = @data.collect{|x| x[:data][X][-1] }.max
227
231
  max_value = max_value > max_x_value ? max_value : max_x_value if max_x_value
228
- max_value
232
+ return max_value
229
233
  end
230
234
 
231
235
  def min_x_range
236
+ # needs to be computed fresh when called, to cover the use-case:
237
+ # add_data -> burn -> add_data -> burn
238
+ # when values would be cached, the graph is not updated for second burning
232
239
  min_value = @data.collect{|x| x[:data][X][0] }.min
233
240
  min_value = min_value < min_x_value ? min_value : min_x_value if min_x_value
234
- min_value
241
+ return min_value
235
242
  end
236
243
 
237
244
  def x_label_range
@@ -246,11 +253,11 @@ module SVG
246
253
  end
247
254
  scale_range = max_value - min_value
248
255
 
249
- scale_division = scale_x_divisions || (scale_range / 10.0)
256
+ scale_division = scale_x_divisions || (scale_range / 9.0)
250
257
  @x_offset = 0
251
-
258
+
252
259
  if scale_x_integers
253
- scale_division = scale_division < 1 ? 1 : scale_division.round
260
+ scale_division = scale_division < 1 ? 1 : scale_division.ceil
254
261
  @x_offset = min_value.to_f - min_value.floor
255
262
  min_value = min_value.floor
256
263
  end
@@ -261,7 +268,7 @@ module SVG
261
268
  def get_x_values
262
269
  min_value, max_value, @x_scale_division = x_label_range
263
270
  rv = []
264
- min_value.step( max_value, @x_scale_division ) {|v| rv << v}
271
+ min_value.step( max_value + @x_scale_division , @x_scale_division ) {|v| rv << v}
265
272
  return rv
266
273
  end
267
274
  alias :get_x_labels :get_x_values
@@ -270,20 +277,22 @@ module SVG
270
277
  # exclude values which are outside max_x_range
271
278
  values = get_x_values
272
279
  @graph_width.to_f / (values.length - 1 ) # -1 is to use entire x-axis
273
- # otherwise there is always 1 division unused
280
+ # otherwise there is always 1 division unused
274
281
  end
275
282
 
276
-
277
283
  def max_y_range
278
284
  max_value = @data.collect{|x| x[:data][Y].max }.max
279
285
  max_value = max_value > max_y_value ? max_value : max_y_value if max_y_value
280
- max_value
286
+ return max_value
281
287
  end
282
288
 
283
289
  def min_y_range
290
+ # needs to be computed fresh when called, to cover the use-case:
291
+ # add_data -> burn -> add_data -> burn
292
+ # when values would be cached, the graph is not updated for second burning
284
293
  min_value = @data.collect{|x| x[:data][Y].min }.min
285
294
  min_value = min_value < min_y_value ? min_value : min_y_value if min_y_value
286
- min_value
295
+ return min_value
287
296
  end
288
297
 
289
298
  def y_label_range
@@ -298,11 +307,11 @@ module SVG
298
307
  end
299
308
  scale_range = max_value - min_value
300
309
 
301
- scale_division = scale_y_divisions || (scale_range / 10.0)
310
+ scale_division = scale_y_divisions || (scale_range / 9.0)
302
311
  @y_offset = 0
303
-
312
+
304
313
  if scale_y_integers
305
- scale_division = scale_division < 1 ? 1 : scale_division.round
314
+ scale_division = scale_division < 1 ? 1 : scale_division.ceil
306
315
  @y_offset = (min_value.to_f - min_value.floor).to_f
307
316
  min_value = min_value.floor
308
317
  end
@@ -314,11 +323,11 @@ module SVG
314
323
  min_value, max_value, @y_scale_division = y_label_range
315
324
  if max_value != min_value
316
325
  while (max_value - min_value) < @y_scale_division
317
- @y_scale_division /= 10.0
326
+ @y_scale_division /= 9.0
318
327
  end
319
328
  end
320
329
  rv = []
321
- min_value.step( max_value, @y_scale_division ) {|v| rv << v}
330
+ min_value.step( max_value + @y_scale_division, @y_scale_division ) {|v| rv << v}
322
331
  rv << rv[0] + 1 if rv.length == 1
323
332
  return rv
324
333
  end
@@ -333,25 +342,23 @@ module SVG
333
342
  else
334
343
  dx = (max - values[-1]).to_f / (values[-1] - values[-2])
335
344
  end
336
- @graph_height.to_f / values.length
345
+ @graph_height.to_f / (values.length - 1)
337
346
  end
338
-
347
+
339
348
  def calc_coords(x, y)
340
349
  coords = {:x => 0, :y => 0}
341
350
  # scale the coordinates, use float division / multiplication
342
351
  # otherwise the point will be place inaccurate
343
- coords[:x] = (x + @x_offset)/@x_scale_division.to_f * field_width
352
+ coords[:x] = (x + @x_offset)/@x_scale_division.to_f * field_width
344
353
  coords[:y] = @graph_height - (y + @y_offset)/@y_scale_division.to_f * field_height
345
354
  return coords
346
355
  end
347
356
 
348
357
  def draw_data
349
358
  line = 1
350
-
359
+
351
360
  x_min = min_x_range
352
- x_max = max_x_range
353
361
  y_min = min_y_range
354
- y_max = max_y_range
355
362
 
356
363
  for data in @data
357
364
  x_points = data[:data][X]
@@ -384,89 +391,93 @@ module SVG
384
391
  x_points.each_index { |idx|
385
392
  c = calc_coords(x_points[idx] - x_min, y_points[idx] - y_min)
386
393
  if show_data_points
387
- DataPoint.new(c[:x], c[:y], line).shape(data[:description][idx]).each{|s|
394
+ shape_selection_string = data[:description][idx].to_s
395
+ if !data[:shape][idx].nil?
396
+ shape_selection_string = data[:shape][idx].to_s
397
+ end
398
+ DataPoint.new(c[:x], c[:y], line).shape(shape_selection_string).each{|s|
388
399
  @graph.add_element( *s )
389
400
  }
390
401
  end
391
402
  make_datapoint_text( c[:x], c[:y]-6, y_points[idx] )
392
- add_popup(c[:x], c[:y], format( x_points[idx], y_points[idx], data[:description][idx]))
403
+ add_popup(c[:x], c[:y], format( x_points[idx], y_points[idx], data[:description][idx].to_s), "", data[:url][idx].to_s)
393
404
  }
394
405
  end
395
406
  line += 1
396
407
  end
397
408
  end
398
-
409
+
399
410
  # returns the formatted string which is added as popup information
400
411
  def format x, y, desc
401
412
  info = []
402
413
  info << (round_popups ? x.round : @number_format % x )
403
414
  info << (round_popups ? y.round : @number_format % y )
404
- info << desc
415
+ info << desc if !desc.empty?
405
416
  "(#{info.compact.join(', ')})"
406
417
  end
407
-
418
+
408
419
  def get_css
409
420
  return <<EOL
410
421
  /* default line styles */
411
422
  .line1{
412
423
  fill: none;
413
424
  stroke: #ff0000;
414
- stroke-width: 1px;
425
+ stroke-width: 1px;
415
426
  }
416
427
  .line2{
417
428
  fill: none;
418
429
  stroke: #0000ff;
419
- stroke-width: 1px;
430
+ stroke-width: 1px;
420
431
  }
421
432
  .line3{
422
433
  fill: none;
423
434
  stroke: #00ff00;
424
- stroke-width: 1px;
435
+ stroke-width: 1px;
425
436
  }
426
437
  .line4{
427
438
  fill: none;
428
439
  stroke: #ffcc00;
429
- stroke-width: 1px;
440
+ stroke-width: 1px;
430
441
  }
431
442
  .line5{
432
443
  fill: none;
433
444
  stroke: #00ccff;
434
- stroke-width: 1px;
445
+ stroke-width: 1px;
435
446
  }
436
447
  .line6{
437
448
  fill: none;
438
449
  stroke: #ff00ff;
439
- stroke-width: 1px;
450
+ stroke-width: 1px;
440
451
  }
441
452
  .line7{
442
453
  fill: none;
443
454
  stroke: #00ffff;
444
- stroke-width: 1px;
455
+ stroke-width: 1px;
445
456
  }
446
457
  .line8{
447
458
  fill: none;
448
459
  stroke: #ffff00;
449
- stroke-width: 1px;
460
+ stroke-width: 1px;
450
461
  }
451
462
  .line9{
452
463
  fill: none;
453
- stroke: #ccc6666;
454
- stroke-width: 1px;
464
+ stroke: #cc6666;
465
+ stroke-width: 1px;
455
466
  }
456
467
  .line10{
457
468
  fill: none;
458
469
  stroke: #663399;
459
- stroke-width: 1px;
470
+ stroke-width: 1px;
460
471
  }
461
472
  .line11{
462
473
  fill: none;
463
474
  stroke: #339900;
464
- stroke-width: 1px;
475
+ stroke-width: 1px;
465
476
  }
466
477
  .line12{
467
478
  fill: none;
468
479
  stroke: #9966FF;
469
- stroke-width: 1px;
480
+ stroke-width: 1px;
470
481
  }
471
482
  /* default fill styles */
472
483
  .fill1{
@@ -533,62 +544,62 @@ module SVG
533
544
  .key1,.dataPoint1{
534
545
  fill: #ff0000;
535
546
  stroke: none;
536
- stroke-width: 1px;
547
+ stroke-width: 1px;
537
548
  }
538
549
  .key2,.dataPoint2{
539
550
  fill: #0000ff;
540
551
  stroke: none;
541
- stroke-width: 1px;
552
+ stroke-width: 1px;
542
553
  }
543
554
  .key3,.dataPoint3{
544
555
  fill: #00ff00;
545
556
  stroke: none;
546
- stroke-width: 1px;
557
+ stroke-width: 1px;
547
558
  }
548
559
  .key4,.dataPoint4{
549
560
  fill: #ffcc00;
550
561
  stroke: none;
551
- stroke-width: 1px;
562
+ stroke-width: 1px;
552
563
  }
553
564
  .key5,.dataPoint5{
554
565
  fill: #00ccff;
555
566
  stroke: none;
556
- stroke-width: 1px;
567
+ stroke-width: 1px;
557
568
  }
558
569
  .key6,.dataPoint6{
559
570
  fill: #ff00ff;
560
571
  stroke: none;
561
- stroke-width: 1px;
572
+ stroke-width: 1px;
562
573
  }
563
574
  .key7,.dataPoint7{
564
575
  fill: #00ffff;
565
576
  stroke: none;
566
- stroke-width: 1px;
577
+ stroke-width: 1px;
567
578
  }
568
579
  .key8,.dataPoint8{
569
580
  fill: #ffff00;
570
581
  stroke: none;
571
- stroke-width: 1px;
582
+ stroke-width: 1px;
572
583
  }
573
584
  .key9,.dataPoint9{
574
585
  fill: #cc6666;
575
586
  stroke: none;
576
- stroke-width: 1px;
587
+ stroke-width: 1px;
577
588
  }
578
589
  .key10,.dataPoint10{
579
590
  fill: #663399;
580
591
  stroke: none;
581
- stroke-width: 1px;
592
+ stroke-width: 1px;
582
593
  }
583
594
  .key11,.dataPoint11{
584
595
  fill: #339900;
585
596
  stroke: none;
586
- stroke-width: 1px;
597
+ stroke-width: 1px;
587
598
  }
588
599
  .key12,.dataPoint12{
589
600
  fill: #9966FF;
590
601
  stroke: none;
591
- stroke-width: 1px;
602
+ stroke-width: 1px;
592
603
  }
593
604
  EOL
594
605
  end