svg-graph 2.0.2 → 2.1.0.beta1
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.
- 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
|