svg-graph 2.0.2 → 2.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.txt +7 -3
- data/lib/SVG/Graph/Bar.rb +8 -7
- data/lib/SVG/Graph/BarHorizontal.rb +19 -20
- data/lib/SVG/Graph/ErrBar.rb +22 -21
- data/lib/SVG/Graph/Graph.rb +202 -125
- data/lib/SVG/Graph/Line.rb +53 -52
- data/lib/svggraph.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0cd53b929ddc82314e5a2f5905ee19e6945f4a7c
|
4
|
+
data.tar.gz: 95968148c4ec92b65c88f8dc1b0796fa95a645b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d08b2f7ec8271044779fb858cb79262a1d8e69a86b76181c7dc4fa0b8c02aec6c5a2e248ab2b90d07774456dbc9e77754a50a62e944d313426678fc216af9dc2
|
7
|
+
data.tar.gz: c4ec0861d565c824cc5aed3ce3d4eef79ce85093a730e630fd4255b8797559825f6fb1dfd2e68ff47a267704bffc28eba45579e81089dd997ea695e3f4686500
|
data/History.txt
CHANGED
@@ -2,6 +2,12 @@ TODO / Backlog
|
|
2
2
|
* add unit tests for all types of graphs
|
3
3
|
* refactor various hardcoded constant pixel offsets in Graph class to use named constants or variables
|
4
4
|
|
5
|
+
=== 2.1.0 / 2017-02-12 [lumean]
|
6
|
+
* change behavior for number format, Strings axis labels will never be formatted
|
7
|
+
previously, when containing numbers they were formatted with the number_format template
|
8
|
+
* added options to change x-axis and y-axis position :x_axis_position and :y_axis_position
|
9
|
+
* x_guidelines and y_guidelines can now be drawn independently of show_x_labels and show_y_labels,
|
10
|
+
before they were only drawn if show_x_labels resp show_y_labels was true
|
5
11
|
|
6
12
|
=== 2.0.2 / 2016-10-24 [lumean]
|
7
13
|
* fix axis-title positioning
|
@@ -24,7 +30,7 @@ TODO / Backlog
|
|
24
30
|
* all ruby files in the test directory now run on ruby >2.0 and write output to files [lumean]
|
25
31
|
* changed from autoload to static loading due to issues with unit-testing when the gem is not installed [lumean]
|
26
32
|
* begin transition to minitest for tests [lumean]
|
27
|
-
* merged fix for 100% pie graphs from https://github.com/endersstocker/svg-graph
|
33
|
+
* merged fix for 100% pie graphs from https://github.com/endersstocker/svg-graph
|
28
34
|
* fix wrong margins for labels after merge of number formatting [lumean]
|
29
35
|
* Add new option for locations for the axis titles (at the end of the axis instead of below / left from it) [lumean]
|
30
36
|
* improved x-axis label location when x-axis marks are vertical and long [lumean]
|
@@ -46,5 +52,3 @@ TODO / Backlog
|
|
46
52
|
* Max values and improved Time data handling.
|
47
53
|
* Merge pull request #1 from thinkingbox/master
|
48
54
|
* Adding scattered plot support with :show_lines =>false Rounding values to integers in popups can be disabled with :round_popups => false Optional description gets shown in popup
|
49
|
-
|
50
|
-
|
data/lib/SVG/Graph/Bar.rb
CHANGED
@@ -103,29 +103,29 @@ module SVG
|
|
103
103
|
|
104
104
|
bar_width = fieldwidth - bargap
|
105
105
|
bar_width /= @data.length if stack == :side
|
106
|
-
|
106
|
+
|
107
107
|
bottom = @graph_height
|
108
108
|
|
109
109
|
field_count = 0
|
110
110
|
@config[:fields].each_index { |i|
|
111
111
|
dataset_count = 0
|
112
112
|
for dataset in @data
|
113
|
-
|
113
|
+
|
114
114
|
# cases (assume 0 = +ve):
|
115
115
|
# value min length
|
116
116
|
# +ve +ve value - min
|
117
117
|
# +ve -ve value - 0
|
118
118
|
# -ve -ve value.abs - 0
|
119
|
-
|
119
|
+
|
120
120
|
value = dataset[:data][i]/@y_scale_division
|
121
|
-
|
121
|
+
|
122
122
|
left = (fieldwidth * field_count)
|
123
|
-
|
123
|
+
|
124
124
|
length = (value.abs - (minvalue > 0 ? minvalue : 0)) * unit_size
|
125
125
|
# top is 0 if value is negative
|
126
126
|
top = bottom - (((value < 0 ? 0 : value) - minvalue) * unit_size)
|
127
127
|
left += bar_width * dataset_count if stack == :side
|
128
|
-
|
128
|
+
|
129
129
|
@graph.add_element( "rect", {
|
130
130
|
"x" => left.to_s,
|
131
131
|
"y" => top.to_s,
|
@@ -134,7 +134,8 @@ module SVG
|
|
134
134
|
"class" => "fill#{dataset_count+1}"
|
135
135
|
})
|
136
136
|
|
137
|
-
make_datapoint_text(left + bar_width/2.0, top - font_size/2, dataset[:data][i]
|
137
|
+
make_datapoint_text(left + bar_width/2.0, top - font_size/2, dataset[:data][i])
|
138
|
+
# number format shall not apply to popup (use .to_s conversion)
|
138
139
|
add_popup(left + bar_width/2.0, top , dataset[:data][i].to_s)
|
139
140
|
dataset_count += 1
|
140
141
|
end
|
@@ -4,43 +4,43 @@ require_relative 'BarBase'
|
|
4
4
|
module SVG
|
5
5
|
module Graph
|
6
6
|
# === Create presentation quality SVG horitonzal bar graphs easily
|
7
|
-
#
|
7
|
+
#
|
8
8
|
# = Synopsis
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# require 'SVG/Graph/BarHorizontal'
|
11
|
-
#
|
11
|
+
#
|
12
12
|
# fields = %w(Jan Feb Mar)
|
13
13
|
# data_sales_02 = [12, 45, 21]
|
14
|
-
#
|
14
|
+
#
|
15
15
|
# graph = SVG::Graph::BarHorizontal.new({
|
16
16
|
# :height => 500,
|
17
17
|
# :width => 300,
|
18
18
|
# :fields => fields,
|
19
19
|
# })
|
20
|
-
#
|
20
|
+
#
|
21
21
|
# graph.add_data({
|
22
22
|
# :data => data_sales_02,
|
23
23
|
# :title => 'Sales 2002',
|
24
24
|
# })
|
25
|
-
#
|
25
|
+
#
|
26
26
|
# print "Content-type: image/svg+xml\r\n\r\n"
|
27
27
|
# print graph.burn
|
28
|
-
#
|
28
|
+
#
|
29
29
|
# = Description
|
30
|
-
#
|
30
|
+
#
|
31
31
|
# This object aims to allow you to easily create high quality
|
32
32
|
# SVG horitonzal bar graphs. You can either use the default style sheet
|
33
33
|
# or supply your own. Either way there are many options which can
|
34
34
|
# be configured to give you control over how the graph is
|
35
35
|
# generated - with or without a key, data elements at each point,
|
36
36
|
# title, subtitle etc.
|
37
|
-
#
|
37
|
+
#
|
38
38
|
# = Examples
|
39
|
-
#
|
39
|
+
#
|
40
40
|
# * http://germane-software.com/repositories/public/SVG/test/test.rb
|
41
|
-
#
|
41
|
+
#
|
42
42
|
# = See also
|
43
|
-
#
|
43
|
+
#
|
44
44
|
# * SVG::Graph::Graph
|
45
45
|
# * SVG::Graph::Bar
|
46
46
|
# * SVG::Graph::Line
|
@@ -62,14 +62,14 @@ module SVG
|
|
62
62
|
# [show_y_guidelines] false
|
63
63
|
def set_defaults
|
64
64
|
super
|
65
|
-
init_with(
|
65
|
+
init_with(
|
66
66
|
:rotate_y_labels => true,
|
67
67
|
:show_x_guidelines => true,
|
68
68
|
:show_y_guidelines => false
|
69
69
|
)
|
70
70
|
# self.right_align = self.right_font = 1
|
71
71
|
end
|
72
|
-
|
72
|
+
|
73
73
|
protected
|
74
74
|
|
75
75
|
def get_x_labels
|
@@ -110,14 +110,14 @@ module SVG
|
|
110
110
|
bar_height = fieldheight - bargap
|
111
111
|
bar_height /= @data.length if stack == :side
|
112
112
|
y_mod = (bar_height / 2) + (font_size / 2)
|
113
|
-
|
113
|
+
|
114
114
|
field_count = 1
|
115
115
|
@config[:fields].each_index { |i|
|
116
116
|
dataset_count = 0
|
117
117
|
for dataset in @data
|
118
118
|
value = dataset[:data][i]
|
119
|
-
|
120
|
-
top = @graph_height - (fieldheight * field_count)
|
119
|
+
|
120
|
+
top = @graph_height - (fieldheight * field_count) + bargap
|
121
121
|
top += (bar_height * dataset_count) if stack == :side
|
122
122
|
# cases (assume 0 = +ve):
|
123
123
|
# value min length left
|
@@ -135,9 +135,8 @@ module SVG
|
|
135
135
|
"class" => "fill#{dataset_count+1}"
|
136
136
|
})
|
137
137
|
|
138
|
-
make_datapoint_text(
|
139
|
-
|
140
|
-
)
|
138
|
+
make_datapoint_text(left+length+5, top+y_mod, dataset[:data][i], "text-anchor: start; ")
|
139
|
+
# number format shall not apply to popup (use .to_s conversion)
|
141
140
|
add_popup(left+length, top+y_mod , dataset[:data][i].to_s)
|
142
141
|
dataset_count += 1
|
143
142
|
end
|
data/lib/SVG/Graph/ErrBar.rb
CHANGED
@@ -13,14 +13,14 @@ module SVG
|
|
13
13
|
# fields = %w(Jan Feb);
|
14
14
|
# myarr1_mean = 10
|
15
15
|
# myarr1_confidence = 1
|
16
|
-
#
|
16
|
+
#
|
17
17
|
# myarr2_mean = 20
|
18
18
|
# myarr2_confidence = 2
|
19
|
-
#
|
19
|
+
#
|
20
20
|
# data= [myarr1_mean, myarr2_mean]
|
21
|
-
#
|
21
|
+
#
|
22
22
|
# err_mesure = [myarr1_confidence, myarr2_confidence]
|
23
|
-
#
|
23
|
+
#
|
24
24
|
# graph = SVG::Graph::ErrBar.new(
|
25
25
|
# :height => 500,
|
26
26
|
# :width => 600,
|
@@ -28,7 +28,7 @@ module SVG
|
|
28
28
|
# :errorBars => err_mesure,
|
29
29
|
# :scale_integers => true,
|
30
30
|
# )
|
31
|
-
#
|
31
|
+
#
|
32
32
|
# graph.add_data(
|
33
33
|
# :data => data,
|
34
34
|
# :title => 'Sales 2002'
|
@@ -71,10 +71,10 @@ module SVG
|
|
71
71
|
def initialize config
|
72
72
|
raise "fields was not supplied or is empty" unless config[:errorBars] &&
|
73
73
|
config[:errorBars].kind_of?(Array) &&
|
74
|
-
config[:errorBars].length > 0
|
74
|
+
config[:errorBars].length > 0
|
75
75
|
super
|
76
|
-
end
|
77
|
-
# Array of confidence values for each item in :fields. A range from
|
76
|
+
end
|
77
|
+
# Array of confidence values for each item in :fields. A range from
|
78
78
|
# value[i]-errorBars[i] to value[i]+errorBars[i] is drawn into the graph.
|
79
79
|
attr_accessor :errorBars
|
80
80
|
|
@@ -103,7 +103,7 @@ module SVG
|
|
103
103
|
end
|
104
104
|
|
105
105
|
rv = []
|
106
|
-
maxvalue = maxvalue%@y_scale_division == 0 ?
|
106
|
+
maxvalue = maxvalue%@y_scale_division == 0 ?
|
107
107
|
maxvalue : maxvalue + @y_scale_division
|
108
108
|
minvalue.step( maxvalue, @y_scale_division ) {|v| rv << v}
|
109
109
|
return rv
|
@@ -122,31 +122,31 @@ module SVG
|
|
122
122
|
|
123
123
|
bar_width = fieldwidth - (bargap *2)
|
124
124
|
bar_width /= @data.length if stack == :side
|
125
|
-
|
125
|
+
|
126
126
|
bottom = @graph_height
|
127
127
|
|
128
128
|
field_count = 0
|
129
129
|
@config[:fields].each_index { |i|
|
130
130
|
dataset_count = 0
|
131
131
|
for dataset in @data
|
132
|
-
|
132
|
+
|
133
133
|
# cases (assume 0 = +ve):
|
134
134
|
# value min length
|
135
135
|
# +ve +ve value - min
|
136
136
|
# +ve -ve value - 0
|
137
137
|
# -ve -ve value.abs - 0
|
138
|
-
|
138
|
+
|
139
139
|
value = dataset[:data][i].to_f/@y_scale_division
|
140
|
-
|
140
|
+
|
141
141
|
left = (fieldwidth * field_count)
|
142
142
|
left += bargap
|
143
|
-
|
144
|
-
|
143
|
+
|
144
|
+
|
145
145
|
length = (value.abs - (minvalue > 0 ? minvalue : 0)) * unit_size
|
146
146
|
# top is 0 if value is negative
|
147
147
|
top = bottom - (((value < 0 ? 0 : value) - minvalue) * unit_size)
|
148
148
|
left += bar_width * dataset_count if stack == :side
|
149
|
-
|
149
|
+
|
150
150
|
@graph.add_element( "rect", {
|
151
151
|
"x" => left.to_s,
|
152
152
|
"y" => top.to_s,
|
@@ -160,7 +160,7 @@ module SVG
|
|
160
160
|
upperErr = top+threshold
|
161
161
|
bottomErr = top-threshold
|
162
162
|
withthErr = bar_width/4
|
163
|
-
|
163
|
+
|
164
164
|
@graph.add_element( "line", {
|
165
165
|
"x1" => middlePointErr.to_s,
|
166
166
|
"y1" => upperErr.to_s,
|
@@ -168,7 +168,7 @@ module SVG
|
|
168
168
|
"y2" => bottomErr.to_s,
|
169
169
|
"style" => "stroke:rgb(0,0,0);stroke-width:1"
|
170
170
|
})
|
171
|
-
|
171
|
+
|
172
172
|
@graph.add_element( "line", {
|
173
173
|
"x1" => (middlePointErr-withthErr).to_s,
|
174
174
|
"y1" => upperErr.to_s,
|
@@ -183,15 +183,16 @@ module SVG
|
|
183
183
|
"y2" => bottomErr.to_s,
|
184
184
|
"style" => "stroke:rgb(0,0,0);stroke-width:1"
|
185
185
|
})
|
186
|
-
|
187
|
-
make_datapoint_text(left + bar_width/2.0, top - 6, dataset[:data][i]
|
186
|
+
|
187
|
+
make_datapoint_text(left + bar_width/2.0, top - 6, dataset[:data][i])
|
188
|
+
# number format shall not apply to popup (use .to_s conversion)
|
188
189
|
add_popup(left + bar_width/2.0, top , dataset[:data][i].to_s)
|
189
190
|
dataset_count += 1
|
190
191
|
end
|
191
192
|
field_count += 1
|
192
193
|
} # config[:fields].each_index
|
193
194
|
end # draw_data
|
194
|
-
|
195
|
+
|
195
196
|
end # ErrBar
|
196
197
|
end
|
197
198
|
end
|
data/lib/SVG/Graph/Graph.rb
CHANGED
@@ -10,7 +10,7 @@ module SVG
|
|
10
10
|
module Graph
|
11
11
|
|
12
12
|
# === Base object for generating SVG Graphs
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# == Synopsis
|
15
15
|
#
|
16
16
|
# This class is only used as a superclass of specialized charts. Do not
|
@@ -28,9 +28,9 @@ module SVG
|
|
28
28
|
# * file:test/single.rb
|
29
29
|
# * file:test/test.rb
|
30
30
|
# * file:test/timeseries.rb
|
31
|
-
#
|
31
|
+
#
|
32
32
|
# == Description
|
33
|
-
#
|
33
|
+
#
|
34
34
|
# This package should be used as a base for creating SVG graphs.
|
35
35
|
#
|
36
36
|
# == Acknowledgements
|
@@ -39,7 +39,7 @@ module SVG
|
|
39
39
|
# port is based on.
|
40
40
|
#
|
41
41
|
# Stephen Morgan for creating the TT template and SVG.
|
42
|
-
#
|
42
|
+
#
|
43
43
|
# == See
|
44
44
|
#
|
45
45
|
# * SVG::Graph::BarHorizontal
|
@@ -63,6 +63,8 @@ module SVG
|
|
63
63
|
# instantiate this class directly; see the subclass for options.
|
64
64
|
# [width] 500
|
65
65
|
# [height] 300
|
66
|
+
# [x_axis_position] nil
|
67
|
+
# [y_axis_position] nil
|
66
68
|
# [show_x_guidelines] false
|
67
69
|
# [show_y_guidelines] true
|
68
70
|
# [show_data_values] true
|
@@ -98,7 +100,7 @@ module SVG
|
|
98
100
|
# [key_font_size] 10
|
99
101
|
# [no_css] false
|
100
102
|
# [add_popups] false
|
101
|
-
# [number_format] '%.2f'
|
103
|
+
# [number_format] '%.2f'
|
102
104
|
def initialize( config )
|
103
105
|
@config = config
|
104
106
|
@data = []
|
@@ -112,6 +114,9 @@ module SVG
|
|
112
114
|
:show_y_guidelines => true,
|
113
115
|
:show_data_values => true,
|
114
116
|
|
117
|
+
:x_axis_position => nil,
|
118
|
+
:y_axis_position => nil,
|
119
|
+
|
115
120
|
:min_scale_value => nil,
|
116
121
|
|
117
122
|
:show_x_labels => true,
|
@@ -138,7 +143,7 @@ module SVG
|
|
138
143
|
:graph_title => 'Graph Title',
|
139
144
|
:show_graph_subtitle => false,
|
140
145
|
:graph_subtitle => 'Graph Sub Title',
|
141
|
-
:key => true,
|
146
|
+
:key => true,
|
142
147
|
:key_position => :right, # bottom or right
|
143
148
|
|
144
149
|
:font_size =>12,
|
@@ -149,27 +154,27 @@ module SVG
|
|
149
154
|
:x_title_font_size =>14,
|
150
155
|
:y_title_font_size =>14,
|
151
156
|
:key_font_size =>10,
|
152
|
-
|
157
|
+
|
153
158
|
:no_css =>false,
|
154
159
|
:add_popups =>false,
|
155
|
-
:number_format => '%.2f'
|
160
|
+
:number_format => '%.2f'
|
156
161
|
})
|
157
162
|
set_defaults if self.respond_to? :set_defaults
|
158
163
|
init_with config
|
159
164
|
end
|
160
165
|
|
161
|
-
|
166
|
+
|
162
167
|
# This method allows you do add data to the graph object.
|
163
168
|
# It can be called several times to add more data sets in.
|
164
169
|
#
|
165
170
|
# data_sales_02 = [12, 45, 21];
|
166
|
-
#
|
171
|
+
#
|
167
172
|
# graph.add_data({
|
168
173
|
# :data => data_sales_02,
|
169
174
|
# :title => 'Sales 2002'
|
170
175
|
# })
|
171
176
|
def add_data conf
|
172
|
-
@data = [] unless (defined? @data and !@data.nil?)
|
177
|
+
@data = [] unless (defined? @data and !@data.nil?)
|
173
178
|
|
174
179
|
if conf[:data] and conf[:data].kind_of? Array
|
175
180
|
@data << conf
|
@@ -183,7 +188,7 @@ module SVG
|
|
183
188
|
# reuse it to create a new graph but with the same config options.
|
184
189
|
#
|
185
190
|
# graph.clear_data
|
186
|
-
def clear_data
|
191
|
+
def clear_data
|
187
192
|
@data = []
|
188
193
|
end
|
189
194
|
|
@@ -193,12 +198,12 @@ module SVG
|
|
193
198
|
#
|
194
199
|
# This method will croak unless at least one data set has
|
195
200
|
# been added to the graph object.
|
196
|
-
#
|
201
|
+
#
|
197
202
|
# print graph.burn
|
198
|
-
#
|
203
|
+
#
|
199
204
|
def burn
|
200
205
|
raise "No data available" unless @data.size > 0
|
201
|
-
|
206
|
+
|
202
207
|
start_svg
|
203
208
|
calculate_graph_dimensions
|
204
209
|
@foreground = Element.new( "g" )
|
@@ -223,14 +228,14 @@ module SVG
|
|
223
228
|
data << "<!-- Ruby Zlib not available for SVGZ -->";
|
224
229
|
end
|
225
230
|
end
|
226
|
-
|
231
|
+
|
227
232
|
return data
|
228
233
|
end
|
229
|
-
|
230
|
-
# Burns the graph but returns only the <svg> node as String without the
|
234
|
+
|
235
|
+
# Burns the graph but returns only the <svg> node as String without the
|
231
236
|
# Doctype and XML Declaration. This allows easy integration into
|
232
237
|
# existing xml documents.
|
233
|
-
#
|
238
|
+
#
|
234
239
|
# @return [String] the SVG node which represents the Graph
|
235
240
|
def burn_svg_only
|
236
241
|
# initialize all instance variables by burning the graph
|
@@ -260,6 +265,19 @@ module SVG
|
|
260
265
|
attr_accessor :style_sheet
|
261
266
|
# (Bool) Show the value of each element of data on the graph
|
262
267
|
attr_accessor :show_data_values
|
268
|
+
# By default (nil/undefined) the x-axis is at the bottom of the graph.
|
269
|
+
# With this property a custom position for the x-axis can be defined.
|
270
|
+
# Valid values are between :min_scale_value and maximum value of the
|
271
|
+
# data.
|
272
|
+
# Default: nil
|
273
|
+
attr_accessor :x_axis_position
|
274
|
+
# By default (nil/undefined) the y-axis is the left border of the graph.
|
275
|
+
# With this property a custom position for the y-axis can be defined.
|
276
|
+
# Valid values are any values in the range of x-values (in case of a
|
277
|
+
# Plot) or any of the :fields values (in case of Line/Bar Graphs, note
|
278
|
+
# the '==' operator is used to find at which value to draw the axis).
|
279
|
+
# Default: nil
|
280
|
+
attr_accessor :y_axis_position
|
263
281
|
# The point at which the Y axis starts, defaults to nil,
|
264
282
|
# if set to nil it will default to the minimum data value.
|
265
283
|
attr_accessor :min_scale_value
|
@@ -268,17 +286,17 @@ module SVG
|
|
268
286
|
attr_accessor :show_x_labels
|
269
287
|
# This puts the X labels at alternative levels so if they
|
270
288
|
# are long field names they will not overlap so easily.
|
271
|
-
# Default
|
289
|
+
# Default is false, to turn on set to true.
|
272
290
|
attr_accessor :stagger_x_labels
|
273
291
|
# This puts the Y labels at alternative levels so if they
|
274
292
|
# are long field names they will not overlap so easily.
|
275
|
-
# Default
|
293
|
+
# Default is false, to turn on set to true.
|
276
294
|
attr_accessor :stagger_y_labels
|
277
295
|
# This turns the X axis labels by 90 degrees.
|
278
|
-
# Default
|
296
|
+
# Default is false, to turn on set to true.
|
279
297
|
attr_accessor :rotate_x_labels
|
280
298
|
# This turns the Y axis labels by 90 degrees.
|
281
|
-
# Default
|
299
|
+
# Default is true, to turn on set to false.
|
282
300
|
attr_accessor :rotate_y_labels
|
283
301
|
# How many "steps" to use between displayed X axis labels,
|
284
302
|
# a step of one means display every label, a step of two results
|
@@ -286,7 +304,7 @@ module SVG
|
|
286
304
|
# a step of three results in every third label being displayed
|
287
305
|
# (label <gap> <gap> label <gap> <gap> label) and so on.
|
288
306
|
attr_accessor :step_x_labels
|
289
|
-
# Whether to (when taking "steps" between X axis labels) step from
|
307
|
+
# Whether to (when taking "steps" between X axis labels) step from
|
290
308
|
# the first label (i.e. always include the first label) or step from
|
291
309
|
# the X axis origin (i.e. start with a gap if step_x_labels is greater
|
292
310
|
# than one).
|
@@ -295,7 +313,7 @@ module SVG
|
|
295
313
|
# to true, set to false if you want to turn them off.
|
296
314
|
attr_accessor :show_y_labels
|
297
315
|
# Ensures only whole numbers are used as the scale divisions.
|
298
|
-
# Default
|
316
|
+
# Default is false, to turn on set to true. This has no effect if
|
299
317
|
# scale divisions are less than 1.
|
300
318
|
attr_accessor :scale_integers
|
301
319
|
# This defines the gap between markers on the Y axis,
|
@@ -315,7 +333,7 @@ module SVG
|
|
315
333
|
# Whether to show the title under the Y axis labels,
|
316
334
|
# default is false, set to true to show.
|
317
335
|
attr_accessor :show_y_title
|
318
|
-
# Aligns writing mode for Y axis label.
|
336
|
+
# Aligns writing mode for Y axis label.
|
319
337
|
# Defaults to :bt (Bottom to Top).
|
320
338
|
# Change to :tb (Top to Bottom) to reverse.
|
321
339
|
attr_accessor :y_title_text_direction
|
@@ -383,7 +401,7 @@ module SVG
|
|
383
401
|
|
384
402
|
|
385
403
|
protected
|
386
|
-
|
404
|
+
|
387
405
|
# implementation of quicksort
|
388
406
|
# used for Schedule and Plot
|
389
407
|
def sort( *arrys )
|
@@ -411,7 +429,7 @@ module SVG
|
|
411
429
|
# Check size of Y labels
|
412
430
|
@border_left += max_y_label_width_px
|
413
431
|
if (show_y_title && (y_title_location ==:middle))
|
414
|
-
@border_left += y_title_font_size + 5
|
432
|
+
@border_left += y_title_font_size + 5
|
415
433
|
end
|
416
434
|
end
|
417
435
|
|
@@ -439,12 +457,12 @@ module SVG
|
|
439
457
|
@border_right = 7
|
440
458
|
if key and key_position == :right
|
441
459
|
val = keys.max { |a,b| a.length <=> b.length }
|
442
|
-
@border_right += val.length * key_font_size * 0.6
|
460
|
+
@border_right += val.length * key_font_size * 0.6
|
443
461
|
@border_right += KEY_BOX_SIZE
|
444
462
|
@border_right += 10 # Some padding around the box
|
445
463
|
end
|
446
464
|
if (x_title_location == :end)
|
447
|
-
@border_right = [@border_right, x_title.length * x_title_font_size * 0.6].max
|
465
|
+
@border_right = [@border_right, x_title.length * x_title_font_size * 0.6].max
|
448
466
|
end
|
449
467
|
end
|
450
468
|
|
@@ -483,16 +501,16 @@ module SVG
|
|
483
501
|
(x+txt_width > width ? "text-anchor: end;" : "text-anchor: start;")
|
484
502
|
t.text = label.to_s
|
485
503
|
t.attributes["id"] = t.object_id.to_s
|
486
|
-
|
504
|
+
|
487
505
|
# add a circle to catch the mouseover
|
488
506
|
@foreground.add_element( "circle", {
|
489
507
|
"cx" => x.to_s,
|
490
508
|
"cy" => y.to_s,
|
491
509
|
"r" => "#{popup_radius}",
|
492
510
|
"style" => "opacity: 0",
|
493
|
-
"onmouseover" =>
|
511
|
+
"onmouseover" =>
|
494
512
|
"document.getElementById(#{t.object_id}).setAttribute('visibility', 'visible' )",
|
495
|
-
"onmouseout" =>
|
513
|
+
"onmouseout" =>
|
496
514
|
"document.getElementById(#{t.object_id}).setAttribute('visibility', 'hidden' )",
|
497
515
|
})
|
498
516
|
end # if add_popups
|
@@ -501,16 +519,16 @@ module SVG
|
|
501
519
|
# returns the longest label from an array of labels as string
|
502
520
|
# each object in the array must support .to_s
|
503
521
|
def get_longest_label(arry)
|
504
|
-
longest_label = arry.max{|a,b|
|
522
|
+
longest_label = arry.max{|a,b|
|
505
523
|
# respect number_format
|
506
|
-
a = @number_format % a if numeric?(a)
|
524
|
+
a = @number_format % a if numeric?(a)
|
507
525
|
b = @number_format % b if numeric?(b)
|
508
526
|
a.to_s.length <=> b.to_s.length
|
509
527
|
}
|
510
528
|
longest_label = @number_format % longest_label if numeric?(longest_label)
|
511
529
|
return longest_label
|
512
530
|
end
|
513
|
-
|
531
|
+
|
514
532
|
# Override this (and call super) to change the margin to the bottom
|
515
533
|
# of the plot area. Results in @border_bottom being set.
|
516
534
|
#
|
@@ -526,13 +544,13 @@ module SVG
|
|
526
544
|
@border_bottom += x_title_font_size + 5
|
527
545
|
end
|
528
546
|
end
|
529
|
-
|
547
|
+
|
530
548
|
# returns the maximum height of the labels respect the rotation or 0 if
|
531
549
|
# the labels are not shown
|
532
550
|
def max_x_label_height_px
|
533
551
|
return 0 if !show_x_labels
|
534
|
-
|
535
|
-
if rotate_x_labels
|
552
|
+
|
553
|
+
if rotate_x_labels
|
536
554
|
max_height = get_longest_label(get_x_labels).to_s.length * x_label_font_size * 0.6
|
537
555
|
else
|
538
556
|
max_height = x_label_font_size + 3
|
@@ -557,32 +575,92 @@ module SVG
|
|
557
575
|
"class" => "graphBackground"
|
558
576
|
})
|
559
577
|
|
560
|
-
|
578
|
+
draw_x_axis
|
579
|
+
draw_y_axis
|
580
|
+
|
581
|
+
draw_x_labels
|
582
|
+
draw_y_labels
|
583
|
+
end
|
584
|
+
|
585
|
+
# draws the x-axis; can be overridden by child classes
|
586
|
+
def draw_x_axis
|
587
|
+
# relative position on y-axis (hence @graph_height is our axis length)
|
588
|
+
relative_position = calculate_rel_position(get_y_labels, field_height, @x_axis_position, @graph_height)
|
589
|
+
# X-Axis
|
590
|
+
y_offset = (1 - relative_position) * @graph_height
|
561
591
|
@graph.add_element( "path", {
|
562
|
-
"d" => "M 0
|
592
|
+
"d" => "M 0 #{y_offset} h#@graph_width",
|
563
593
|
"class" => "axis",
|
564
|
-
"id" => "
|
594
|
+
"id" => "yAxis"
|
565
595
|
})
|
596
|
+
end
|
597
|
+
|
598
|
+
# draws the y-axis; can be overridden by child classes
|
599
|
+
def draw_y_axis
|
600
|
+
# relative position on x-axis (hence @graph_width is our axis length)
|
601
|
+
relative_position = calculate_rel_position(get_x_labels, field_width, @y_axis_position, @graph_width)
|
602
|
+
# Y-Axis
|
603
|
+
x_offset = relative_position * @graph_width
|
566
604
|
@graph.add_element( "path", {
|
567
|
-
"d" => "M 0 #@graph_height
|
605
|
+
"d" => "M #{x_offset} 0 v#@graph_height",
|
568
606
|
"class" => "axis",
|
569
|
-
"id" => "
|
607
|
+
"id" => "xAxis"
|
570
608
|
})
|
571
|
-
|
572
|
-
draw_x_labels
|
573
|
-
draw_y_labels
|
574
609
|
end
|
575
610
|
|
611
|
+
# calculates the relative position betewen 0 and 1 of a value on the axis
|
612
|
+
# can be multiplied with either @graph_height or @graph_width to get the
|
613
|
+
# absolute position in pixels.
|
614
|
+
# If labels are strings, checks if one of label matches with the value
|
615
|
+
# and returns this position.
|
616
|
+
# If labels are numeric, compute relative position between first and last value
|
617
|
+
# If nothing else applies or the value is nil, the relative position is 0
|
618
|
+
# @param labels [Array] the array of x or y labels, see {#get_x_labels} or {#get_y_labels}
|
619
|
+
# @param segment_px [Float] number of pixels per label, see {#field_width} or {#field_height}
|
620
|
+
# @param value [Numeric, String] the value for which the relative position is computed
|
621
|
+
# @param axis_length [Numeric] either @graph_width or @graph_height
|
622
|
+
# @return [Float] relative position between 0 and 1, returns 0
|
623
|
+
def calculate_rel_position(labels, segment_px, value, axis_length)
|
624
|
+
# default value, y-axis on the left side, or x-axis at bottom
|
625
|
+
# puts "calculate_rel_position:"
|
626
|
+
# p labels
|
627
|
+
# p segment_px
|
628
|
+
# p value
|
629
|
+
# p axis_length
|
630
|
+
relative_position = 0
|
631
|
+
if !value.nil? # only
|
632
|
+
if (labels[0].is_a? Numeric) && (labels[-1].is_a? Numeric) && (value.is_a? Numeric)
|
633
|
+
# labels are numeric, compute relative position between first and last value
|
634
|
+
range = labels[-1] - labels[0]
|
635
|
+
position = value - labels[0]
|
636
|
+
# compute how many segments long the offset is
|
637
|
+
relative_to_segemts = position/range * (labels.size - 1)
|
638
|
+
# convert from segments to relative position on the axis axis,
|
639
|
+
# the number of segments (i.e. relative_to_segemts >= 1)
|
640
|
+
relative_position = relative_to_segemts * segment_px / axis_length
|
641
|
+
elsif labels[0].is_a? String
|
642
|
+
# labels are strings, see if one of label matches with the position
|
643
|
+
# and place the axis there
|
644
|
+
index = labels.index(value)
|
645
|
+
if !index.nil? # index would be nil if label is not found
|
646
|
+
offset_px = segment_px * index
|
647
|
+
relative_position = offset_px/axis_length # between 0 and 1
|
648
|
+
end
|
649
|
+
end
|
650
|
+
end # value.nil?
|
651
|
+
return relative_position
|
652
|
+
end
|
576
653
|
|
577
654
|
# Where in the X area the label is drawn
|
578
655
|
# Centered in the field, should be width/2. Start, 0.
|
579
656
|
def x_label_offset( width )
|
580
657
|
0
|
581
658
|
end
|
582
|
-
|
659
|
+
|
583
660
|
# check if an object can be converted to float
|
584
661
|
def numeric?(object)
|
585
|
-
true if Float(object) rescue false
|
662
|
+
# true if Float(object) rescue false
|
663
|
+
object.is_a? Numeric
|
586
664
|
end
|
587
665
|
|
588
666
|
# adds the datapoint text to the graph only if the config option is set
|
@@ -604,7 +682,7 @@ module SVG
|
|
604
682
|
"y" => y.to_s,
|
605
683
|
"class" => "dataPointLabel",
|
606
684
|
"style" => "#{style} stroke: #fff; stroke-width: 2;"
|
607
|
-
}).text = textStr
|
685
|
+
}).text = textStr
|
608
686
|
# actual label
|
609
687
|
text = @foreground.add_element( "text", {
|
610
688
|
"x" => x.to_s,
|
@@ -615,60 +693,60 @@ module SVG
|
|
615
693
|
text.attributes["style"] = style if style.length > 0
|
616
694
|
end
|
617
695
|
end
|
618
|
-
|
619
696
|
|
620
|
-
|
697
|
+
|
698
|
+
# Draws the X axis labels. The x-axis (@graph_width) is diveded into
|
699
|
+
# {#get_x_labels.length} equal sections. The (center) x-coordinate for a
|
700
|
+
# label hence is label_index * width_of_section
|
621
701
|
def draw_x_labels
|
622
702
|
stagger = x_label_font_size + 5
|
623
|
-
|
624
|
-
|
703
|
+
label_width = field_width
|
704
|
+
count = 0
|
705
|
+
x_axis_already_drawn = false
|
706
|
+
for label in get_x_labels
|
707
|
+
if step_include_first_x_label == true then
|
708
|
+
step = count % step_x_labels
|
709
|
+
else
|
710
|
+
step = (count + 1) % step_x_labels
|
711
|
+
end
|
712
|
+
# only draw every n-th label as defined by step_x_labels
|
713
|
+
if step == 0 && show_x_labels then
|
714
|
+
textStr = label.to_s
|
715
|
+
if( numeric?(label) )
|
716
|
+
textStr = @number_format % label
|
717
|
+
end
|
718
|
+
text = @graph.add_element( "text" )
|
719
|
+
text.attributes["class"] = "xAxisLabels"
|
720
|
+
text.text = textStr
|
625
721
|
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
722
|
+
x = count * label_width + x_label_offset( label_width )
|
723
|
+
y = @graph_height + x_label_font_size + 3
|
724
|
+
#t = 0 - (font_size / 2)
|
725
|
+
|
726
|
+
if stagger_x_labels and count % 2 == 1
|
727
|
+
y += stagger
|
728
|
+
@graph.add_element( "path", {
|
729
|
+
"d" => "M#{x} #@graph_height v#{stagger}",
|
730
|
+
"class" => "staggerGuideLine"
|
731
|
+
})
|
632
732
|
end
|
633
733
|
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
text.attributes["
|
641
|
-
|
642
|
-
|
643
|
-
x = count * label_width + x_label_offset( label_width )
|
644
|
-
y = @graph_height + x_label_font_size + 3
|
645
|
-
#t = 0 - (font_size / 2)
|
646
|
-
|
647
|
-
if stagger_x_labels and count % 2 == 1
|
648
|
-
y += stagger
|
649
|
-
@graph.add_element( "path", {
|
650
|
-
"d" => "M#{x} #@graph_height v#{stagger}",
|
651
|
-
"class" => "staggerGuideLine"
|
652
|
-
})
|
653
|
-
end
|
654
|
-
|
655
|
-
text.attributes["x"] = x.to_s
|
656
|
-
text.attributes["y"] = y.to_s
|
657
|
-
if rotate_x_labels
|
658
|
-
text.attributes["transform"] =
|
659
|
-
"rotate( 90 #{x} #{y-x_label_font_size} )"+
|
660
|
-
" translate( 0 -#{x_label_font_size/4} )"
|
661
|
-
text.attributes["style"] = "text-anchor: start"
|
662
|
-
else
|
663
|
-
text.attributes["style"] = "text-anchor: middle"
|
664
|
-
end
|
734
|
+
text.attributes["x"] = x.to_s
|
735
|
+
text.attributes["y"] = y.to_s
|
736
|
+
if rotate_x_labels
|
737
|
+
text.attributes["transform"] =
|
738
|
+
"rotate( 90 #{x} #{y-x_label_font_size} )"+
|
739
|
+
" translate( 0 -#{x_label_font_size/4} )"
|
740
|
+
text.attributes["style"] = "text-anchor: start"
|
741
|
+
else
|
742
|
+
text.attributes["style"] = "text-anchor: middle"
|
665
743
|
end
|
744
|
+
end # if step == 0 && show_x_labels
|
666
745
|
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
end
|
746
|
+
draw_x_guidelines( label_width, count ) if show_x_guidelines
|
747
|
+
count += 1
|
748
|
+
end # for label in get_x_labels
|
749
|
+
end # draw_x_labels
|
672
750
|
|
673
751
|
|
674
752
|
# Where in the Y area the label is drawn
|
@@ -681,17 +759,17 @@ module SVG
|
|
681
759
|
# must return the array of labels for the x-axis
|
682
760
|
def get_x_labels
|
683
761
|
end
|
684
|
-
|
762
|
+
|
685
763
|
# override this method in child class
|
686
764
|
# must return the array of labels for the y-axis
|
687
765
|
# this method defines @y_scale_division
|
688
766
|
def get_y_labels
|
689
767
|
end
|
690
|
-
|
768
|
+
|
691
769
|
# space in px between x-labels
|
692
770
|
def field_width
|
693
771
|
# -1 is to use entire x-axis
|
694
|
-
# otherwise there is always 1 division unused
|
772
|
+
# otherwise there is always 1 division unused
|
695
773
|
@graph_width.to_f / ( get_x_labels.length - 1 )
|
696
774
|
end
|
697
775
|
|
@@ -704,21 +782,20 @@ module SVG
|
|
704
782
|
|
705
783
|
|
706
784
|
# Draws the Y axis labels, the Y-Axis (@graph_height) is divided equally into #get_y_labels.lenght sections
|
707
|
-
# So the y coordinate for an arbitrary value is calculated as follows:
|
785
|
+
# So the y coordinate for an arbitrary value is calculated as follows:
|
708
786
|
# y = @graph_height equals the min_value
|
709
|
-
# #normalize value of a single scale_division:
|
710
|
-
# count = value /(@y_scale_division)
|
711
|
-
# y = @graph_height - count * field_height
|
712
|
-
#
|
787
|
+
# #normalize value of a single scale_division:
|
788
|
+
# count = value /(@y_scale_division)
|
789
|
+
# y = @graph_height - count * field_height
|
790
|
+
#
|
713
791
|
def draw_y_labels
|
714
792
|
stagger = y_label_font_size + 5
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
for label in get_y_labels
|
793
|
+
label_height = field_height
|
794
|
+
count = 0
|
795
|
+
y_offset = @graph_height + y_label_offset( label_height )
|
796
|
+
y_offset += font_size/1.2 unless rotate_y_labels
|
797
|
+
for label in get_y_labels
|
798
|
+
if show_y_labels
|
722
799
|
y = y_offset - (label_height * count)
|
723
800
|
x = rotate_y_labels ? 0 : -3
|
724
801
|
|
@@ -748,14 +825,14 @@ module SVG
|
|
748
825
|
text.attributes["y"] = (y - (y_label_font_size/2)).to_s
|
749
826
|
text.attributes["style"] = "text-anchor: end"
|
750
827
|
end
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
end
|
755
|
-
end
|
828
|
+
end # if show_y_labels
|
829
|
+
draw_y_guidelines( label_height, count ) if show_y_guidelines
|
830
|
+
count += 1
|
831
|
+
end # for label in get_y_labels
|
832
|
+
end # draw_y_labels
|
756
833
|
|
757
834
|
|
758
|
-
# Draws the X axis guidelines
|
835
|
+
# Draws the X axis guidelines, parallel to the y-axis
|
759
836
|
def draw_x_guidelines( label_height, count )
|
760
837
|
if count != 0
|
761
838
|
@graph.add_element( "path", {
|
@@ -766,7 +843,7 @@ module SVG
|
|
766
843
|
end
|
767
844
|
|
768
845
|
|
769
|
-
# Draws the Y axis guidelines
|
846
|
+
# Draws the Y axis guidelines, parallel to the x-axis
|
770
847
|
def draw_y_guidelines( label_height, count )
|
771
848
|
if count != 0
|
772
849
|
@graph.add_element( "path", {
|
@@ -788,7 +865,7 @@ module SVG
|
|
788
865
|
end
|
789
866
|
|
790
867
|
if show_graph_subtitle
|
791
|
-
y_subtitle = show_graph_title ?
|
868
|
+
y_subtitle = show_graph_title ?
|
792
869
|
title_font_size + subtitle_font_size + 5 :
|
793
870
|
subtitle_font_size
|
794
871
|
@root.add_element("text", {
|
@@ -840,7 +917,7 @@ module SVG
|
|
840
917
|
end
|
841
918
|
end # draw_titles
|
842
919
|
|
843
|
-
def keys
|
920
|
+
def keys
|
844
921
|
i = 0
|
845
922
|
return @data.collect{ |d| i+=1; d[:title] || "Serie #{i}" }
|
846
923
|
end
|
@@ -893,7 +970,7 @@ module SVG
|
|
893
970
|
sort_multiple(arrys, lo, p-1)
|
894
971
|
sort_multiple(arrys, p+1, hi)
|
895
972
|
end
|
896
|
-
arrys
|
973
|
+
arrys
|
897
974
|
end
|
898
975
|
|
899
976
|
def partition( arrys, lo, hi )
|
@@ -955,11 +1032,11 @@ module SVG
|
|
955
1032
|
def add_defs defs
|
956
1033
|
end
|
957
1034
|
|
958
|
-
# Creates the XML document and adds the root svg element with
|
1035
|
+
# Creates the XML document and adds the root svg element with
|
959
1036
|
# the width, height and viewBox attributes already set.
|
960
|
-
# The element is stored as @root.
|
1037
|
+
# The element is stored as @root.
|
961
1038
|
#
|
962
|
-
# In addition a rectangle background of the same size as the
|
1039
|
+
# In addition a rectangle background of the same size as the
|
963
1040
|
# svg is added.
|
964
1041
|
#
|
965
1042
|
def start_svg
|
@@ -1096,7 +1173,7 @@ module SVG
|
|
1096
1173
|
.staggerGuideLine{
|
1097
1174
|
fill: none;
|
1098
1175
|
stroke: #000000;
|
1099
|
-
stroke-width: 0.5px;
|
1176
|
+
stroke-width: 0.5px;
|
1100
1177
|
}
|
1101
1178
|
|
1102
1179
|
#{get_css}
|
data/lib/SVG/Graph/Line.rb
CHANGED
@@ -3,47 +3,47 @@ require_relative 'Graph'
|
|
3
3
|
module SVG
|
4
4
|
module Graph
|
5
5
|
# === Create presentation quality SVG line graphs easily
|
6
|
-
#
|
6
|
+
#
|
7
7
|
# = Synopsis
|
8
|
-
#
|
8
|
+
#
|
9
9
|
# require 'SVG/Graph/Line'
|
10
|
-
#
|
10
|
+
#
|
11
11
|
# fields = %w(Jan Feb Mar);
|
12
12
|
# data_sales_02 = [12, 45, 21]
|
13
13
|
# data_sales_03 = [15, 30, 40]
|
14
|
-
#
|
14
|
+
#
|
15
15
|
# graph = SVG::Graph::Line.new({
|
16
16
|
# :height => 500,
|
17
17
|
# :width => 300,
|
18
18
|
# :fields => fields,
|
19
19
|
# })
|
20
|
-
#
|
20
|
+
#
|
21
21
|
# graph.add_data({
|
22
22
|
# :data => data_sales_02,
|
23
23
|
# :title => 'Sales 2002',
|
24
24
|
# })
|
25
|
-
#
|
25
|
+
#
|
26
26
|
# graph.add_data({
|
27
27
|
# :data => data_sales_03,
|
28
28
|
# :title => 'Sales 2003',
|
29
29
|
# })
|
30
|
-
#
|
30
|
+
#
|
31
31
|
# print "Content-type: image/svg+xml\r\n\r\n";
|
32
32
|
# print graph.burn();
|
33
|
-
#
|
33
|
+
#
|
34
34
|
# = Description
|
35
|
-
#
|
35
|
+
#
|
36
36
|
# This object aims to allow you to easily create high quality
|
37
37
|
# SVG line graphs. You can either use the default style sheet
|
38
38
|
# or supply your own. Either way there are many options which can
|
39
39
|
# be configured to give you control over how the graph is
|
40
40
|
# generated - with or without a key, data elements at each point,
|
41
41
|
# title, subtitle etc.
|
42
|
-
#
|
42
|
+
#
|
43
43
|
# = Examples
|
44
|
-
#
|
44
|
+
#
|
45
45
|
# http://www.germane-software/repositories/public/SVG/test/single.rb
|
46
|
-
#
|
46
|
+
#
|
47
47
|
# = Notes
|
48
48
|
# Only number of fileds datapoints will be drawn, additional data values
|
49
49
|
# are ignored. Nil values in data are skipped and
|
@@ -54,9 +54,9 @@ module SVG
|
|
54
54
|
# additional settings for the extra data sets. You will know
|
55
55
|
# if you go over 10 data sets as they will have no style and
|
56
56
|
# be in black.
|
57
|
-
#
|
57
|
+
#
|
58
58
|
# = See also
|
59
|
-
#
|
59
|
+
#
|
60
60
|
# * SVG::Graph::Graph
|
61
61
|
# * SVG::Graph::BarHorizontal
|
62
62
|
# * SVG::Graph::Bar
|
@@ -75,14 +75,14 @@ module SVG
|
|
75
75
|
# Show a small circle on the graph where the line
|
76
76
|
# goes from one point to the next.
|
77
77
|
attr_accessor :show_data_points
|
78
|
-
# Accumulates each data set. (i.e. Each point increased by sum of
|
78
|
+
# Accumulates each data set. (i.e. Each point increased by sum of
|
79
79
|
# all previous series at same point). Default is 0, set to '1' to show.
|
80
80
|
attr_accessor :stacked
|
81
81
|
# Fill in the area under the plot if true
|
82
82
|
attr_accessor :area_fill
|
83
83
|
|
84
84
|
# The constructor takes a hash reference, :fields (the names for each
|
85
|
-
# field on the X axis) MUST be set, all other values are defaulted to
|
85
|
+
# field on the X axis) MUST be set, all other values are defaulted to
|
86
86
|
# those shown above - with the exception of style_sheet which defaults
|
87
87
|
# to using the internal style sheet.
|
88
88
|
def initialize config
|
@@ -112,7 +112,7 @@ module SVG
|
|
112
112
|
max = 0
|
113
113
|
if stacked
|
114
114
|
sums = Array.new(@config[:fields].length).fill(0)
|
115
|
-
|
115
|
+
|
116
116
|
@data.each do |data|
|
117
117
|
sums.each_index do |i|
|
118
118
|
sums[i] += data[:data][i].to_f
|
@@ -125,7 +125,7 @@ module SVG
|
|
125
125
|
x[:data].compact.max
|
126
126
|
}.max
|
127
127
|
end
|
128
|
-
|
128
|
+
|
129
129
|
return max
|
130
130
|
end
|
131
131
|
|
@@ -167,7 +167,7 @@ module SVG
|
|
167
167
|
|
168
168
|
@y_scale_division = scale_divisions || (scale_range / 10.0)
|
169
169
|
@y_offset = 0
|
170
|
-
|
170
|
+
|
171
171
|
if scale_integers
|
172
172
|
@y_scale_division = @y_scale_division < 1 ? 1 : @y_scale_division.round
|
173
173
|
@y_offset = (minvalue.to_f - minvalue.floor).to_f
|
@@ -201,10 +201,10 @@ module SVG
|
|
201
201
|
for data in @data.reverse
|
202
202
|
lpath = ""
|
203
203
|
apath = ""
|
204
|
-
|
204
|
+
|
205
205
|
# reset cum_sum if we are not in a stacked graph
|
206
206
|
if not stacked then cum_sum.fill(nil) end
|
207
|
-
|
207
|
+
|
208
208
|
# only consider as many datapoints as we have fields
|
209
209
|
@config[:fields].each_index do |i|
|
210
210
|
next if data[:data][i].nil?
|
@@ -216,33 +216,33 @@ module SVG
|
|
216
216
|
c = calc_coords(i, cum_sum[i], fieldwidth, fieldheight)
|
217
217
|
lpath << "#{c[:x]} #{c[:y]} "
|
218
218
|
end
|
219
|
-
|
219
|
+
|
220
220
|
if area_fill
|
221
221
|
if stacked then
|
222
222
|
(prev_sum.length - 1).downto 0 do |i|
|
223
223
|
next if prev_sum[i].nil?
|
224
224
|
c = calc_coords(i, prev_sum[i], fieldwidth, fieldheight)
|
225
|
-
|
225
|
+
|
226
226
|
apath << "#{c[:x]} #{c[:y]} "
|
227
227
|
end
|
228
|
-
|
228
|
+
|
229
229
|
c = calc_coords(0, prev_sum[0], fieldwidth, fieldheight)
|
230
230
|
else
|
231
231
|
apath = "V#@graph_height"
|
232
232
|
c = calc_coords(0, -@y_offset, fieldwidth, fieldheight)
|
233
233
|
end
|
234
|
-
|
234
|
+
|
235
235
|
@graph.add_element("path", {
|
236
236
|
"d" => "M#{c[:x]} #{c[:y]} L" + lpath + apath + "Z",
|
237
237
|
"class" => "fill#{line}"
|
238
238
|
})
|
239
239
|
end
|
240
|
-
|
240
|
+
|
241
241
|
@graph.add_element("path", {
|
242
242
|
"d" => "M0 #@graph_height L" + lpath,
|
243
243
|
"class" => "line#{line}"
|
244
244
|
})
|
245
|
-
|
245
|
+
|
246
246
|
if show_data_points || show_data_values || add_popups
|
247
247
|
cum_sum.each_index do |i|
|
248
248
|
# skip datapoint if nil
|
@@ -258,7 +258,8 @@ module SVG
|
|
258
258
|
end
|
259
259
|
|
260
260
|
make_datapoint_text( c[:x], c[:y] - font_size/2, cum_sum[i] + minvalue)
|
261
|
-
|
261
|
+
# number format shall not apply to popup (use .to_s conversion)
|
262
|
+
add_popup(c[:x], c[:y], (cum_sum[i] + minvalue).to_s)
|
262
263
|
end
|
263
264
|
end
|
264
265
|
|
@@ -274,62 +275,62 @@ module SVG
|
|
274
275
|
.line1{
|
275
276
|
fill: none;
|
276
277
|
stroke: #ff0000;
|
277
|
-
stroke-width: 1px;
|
278
|
+
stroke-width: 1px;
|
278
279
|
}
|
279
280
|
.line2{
|
280
281
|
fill: none;
|
281
282
|
stroke: #0000ff;
|
282
|
-
stroke-width: 1px;
|
283
|
+
stroke-width: 1px;
|
283
284
|
}
|
284
285
|
.line3{
|
285
286
|
fill: none;
|
286
287
|
stroke: #00ff00;
|
287
|
-
stroke-width: 1px;
|
288
|
+
stroke-width: 1px;
|
288
289
|
}
|
289
290
|
.line4{
|
290
291
|
fill: none;
|
291
292
|
stroke: #ffcc00;
|
292
|
-
stroke-width: 1px;
|
293
|
+
stroke-width: 1px;
|
293
294
|
}
|
294
295
|
.line5{
|
295
296
|
fill: none;
|
296
297
|
stroke: #00ccff;
|
297
|
-
stroke-width: 1px;
|
298
|
+
stroke-width: 1px;
|
298
299
|
}
|
299
300
|
.line6{
|
300
301
|
fill: none;
|
301
302
|
stroke: #ff00ff;
|
302
|
-
stroke-width: 1px;
|
303
|
+
stroke-width: 1px;
|
303
304
|
}
|
304
305
|
.line7{
|
305
306
|
fill: none;
|
306
307
|
stroke: #00ffff;
|
307
|
-
stroke-width: 1px;
|
308
|
+
stroke-width: 1px;
|
308
309
|
}
|
309
310
|
.line8{
|
310
311
|
fill: none;
|
311
312
|
stroke: #ffff00;
|
312
|
-
stroke-width: 1px;
|
313
|
+
stroke-width: 1px;
|
313
314
|
}
|
314
315
|
.line9{
|
315
316
|
fill: none;
|
316
317
|
stroke: #ccc6666;
|
317
|
-
stroke-width: 1px;
|
318
|
+
stroke-width: 1px;
|
318
319
|
}
|
319
320
|
.line10{
|
320
321
|
fill: none;
|
321
322
|
stroke: #663399;
|
322
|
-
stroke-width: 1px;
|
323
|
+
stroke-width: 1px;
|
323
324
|
}
|
324
325
|
.line11{
|
325
326
|
fill: none;
|
326
327
|
stroke: #339900;
|
327
|
-
stroke-width: 1px;
|
328
|
+
stroke-width: 1px;
|
328
329
|
}
|
329
330
|
.line12{
|
330
331
|
fill: none;
|
331
332
|
stroke: #9966FF;
|
332
|
-
stroke-width: 1px;
|
333
|
+
stroke-width: 1px;
|
333
334
|
}
|
334
335
|
/* default fill styles */
|
335
336
|
.fill1{
|
@@ -396,62 +397,62 @@ module SVG
|
|
396
397
|
.key1,.dataPoint1{
|
397
398
|
fill: #ff0000;
|
398
399
|
stroke: none;
|
399
|
-
stroke-width: 1px;
|
400
|
+
stroke-width: 1px;
|
400
401
|
}
|
401
402
|
.key2,.dataPoint2{
|
402
403
|
fill: #0000ff;
|
403
404
|
stroke: none;
|
404
|
-
stroke-width: 1px;
|
405
|
+
stroke-width: 1px;
|
405
406
|
}
|
406
407
|
.key3,.dataPoint3{
|
407
408
|
fill: #00ff00;
|
408
409
|
stroke: none;
|
409
|
-
stroke-width: 1px;
|
410
|
+
stroke-width: 1px;
|
410
411
|
}
|
411
412
|
.key4,.dataPoint4{
|
412
413
|
fill: #ffcc00;
|
413
414
|
stroke: none;
|
414
|
-
stroke-width: 1px;
|
415
|
+
stroke-width: 1px;
|
415
416
|
}
|
416
417
|
.key5,.dataPoint5{
|
417
418
|
fill: #00ccff;
|
418
419
|
stroke: none;
|
419
|
-
stroke-width: 1px;
|
420
|
+
stroke-width: 1px;
|
420
421
|
}
|
421
422
|
.key6,.dataPoint6{
|
422
423
|
fill: #ff00ff;
|
423
424
|
stroke: none;
|
424
|
-
stroke-width: 1px;
|
425
|
+
stroke-width: 1px;
|
425
426
|
}
|
426
427
|
.key7,.dataPoint7{
|
427
428
|
fill: #00ffff;
|
428
429
|
stroke: none;
|
429
|
-
stroke-width: 1px;
|
430
|
+
stroke-width: 1px;
|
430
431
|
}
|
431
432
|
.key8,.dataPoint8{
|
432
433
|
fill: #ffff00;
|
433
434
|
stroke: none;
|
434
|
-
stroke-width: 1px;
|
435
|
+
stroke-width: 1px;
|
435
436
|
}
|
436
437
|
.key9,.dataPoint9{
|
437
438
|
fill: #cc6666;
|
438
439
|
stroke: none;
|
439
|
-
stroke-width: 1px;
|
440
|
+
stroke-width: 1px;
|
440
441
|
}
|
441
442
|
.key10,.dataPoint10{
|
442
443
|
fill: #663399;
|
443
444
|
stroke: none;
|
444
|
-
stroke-width: 1px;
|
445
|
+
stroke-width: 1px;
|
445
446
|
}
|
446
447
|
.key11,.dataPoint11{
|
447
448
|
fill: #339900;
|
448
449
|
stroke: none;
|
449
|
-
stroke-width: 1px;
|
450
|
+
stroke-width: 1px;
|
450
451
|
}
|
451
452
|
.key12,.dataPoint12{
|
452
453
|
fill: #9966FF;
|
453
454
|
stroke: none;
|
454
|
-
stroke-width: 1px;
|
455
|
+
stroke-width: 1px;
|
455
456
|
}
|
456
457
|
EOL
|
457
458
|
end
|
data/lib/svggraph.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: svg-graph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.1.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Russell
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date:
|
15
|
+
date: 2017-02-13 00:00:00.000000000 Z
|
16
16
|
dependencies: []
|
17
17
|
description: "Gem version of SVG:::Graph. SVG:::Graph is a pure Ruby library for generating
|
18
18
|
charts, \nwhich are a type of graph where the values of one axis are not scalar.
|
@@ -67,12 +67,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
67
|
version: '0'
|
68
68
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
69
|
requirements:
|
70
|
-
- - "
|
70
|
+
- - ">"
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version:
|
72
|
+
version: 1.3.1
|
73
73
|
requirements: []
|
74
74
|
rubyforge_project:
|
75
|
-
rubygems_version: 2.6.
|
75
|
+
rubygems_version: 2.6.10
|
76
76
|
signing_key:
|
77
77
|
specification_version: 4
|
78
78
|
summary: SVG:::Graph is a pure Ruby library for generating charts, which are a type
|