svg-graph 2.0.2.beta → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.txt +10 -8
- data/README.markdown +9 -0
- data/lib/SVG/Graph/Bar.rb +7 -12
- data/lib/SVG/Graph/BarBase.rb +7 -0
- data/lib/SVG/Graph/BarHorizontal.rb +7 -10
- data/lib/SVG/Graph/DataPoint.rb +35 -0
- data/lib/SVG/Graph/ErrBar.rb +53 -45
- data/lib/SVG/Graph/Graph.rb +14 -25
- data/lib/SVG/Graph/Line.rb +17 -27
- data/lib/SVG/Graph/Pie.rb +1 -2
- data/lib/SVG/Graph/Plot.rb +101 -64
- data/lib/SVG/Graph/Schedule.rb +1 -1
- data/lib/SVG/Graph/TimeSeries.rb +18 -17
- data/lib/svggraph.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7101e9e0d576c758450907b8f9d30bf2b8c787ee
|
4
|
+
data.tar.gz: c370f0c3b179587d0e4bf34d4862f4c1a0366dce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da39453fcbd961e3b8758da1b7a5ed49f9fa6142f598151de6625e7294ca3ffc2689aca19110841a6f598bf23305ab85daaf2f5b2d59a03f29b3204d96c2ecc2
|
7
|
+
data.tar.gz: 58a9de11f45ce90ffbf7600b4d82a0b26b27f4b6598e016221bc838fa074cd7aaaecad537aa7cd69fbcae33d03f4c6da432883b890775dfd9db24900c1c39f3e
|
data/History.txt
CHANGED
@@ -1,16 +1,18 @@
|
|
1
|
-
TODO
|
2
|
-
*
|
3
|
-
*
|
4
|
-
* fix sorting and connected lines for Plot
|
1
|
+
TODO / Backlog
|
2
|
+
* add unit tests for all types of graphs
|
3
|
+
* refactor various hardcoded constant pixel offsets in Graph class to use named constants or variables
|
5
4
|
|
6
5
|
|
7
|
-
=== 2.0.2
|
6
|
+
=== 2.0.2 / 2016-10-24 [lumean]
|
8
7
|
* fix axis-title positioning
|
9
8
|
* fix/add support for popups of values line, plot, bar graph
|
10
9
|
* Line,Plot,Pie graph support nil values (or pairs of nil for plot) for non-present datapoints
|
11
|
-
* changed text anchor of first datapoint label to avoid overlap with axis
|
12
|
-
|
13
|
-
*
|
10
|
+
* fix show datapoint labels: changed text anchor of first datapoint label to avoid overlap with axis
|
11
|
+
and text anchor of last datapoint label
|
12
|
+
* fix pie graph key overlapping the graph [thanks adamalbrecht for reporting]
|
13
|
+
* fix axis titles positioning for staggered axis (prevent overlap)
|
14
|
+
* fix axis labels and positioning when scale_integers is true and min value is not integer.
|
15
|
+
* added various comments in the code to be better understandable (for me)
|
14
16
|
|
15
17
|
=== 2.0.1 / 2016-08-09
|
16
18
|
* dropped support for ruby 1.8.7 [lumean]
|
data/README.markdown
CHANGED
@@ -78,18 +78,27 @@ File.open('bar.svg', 'w') {|f| f.write(g.burn_svg_only)}
|
|
78
78
|
|
79
79
|
### ErrBar
|
80
80
|
|
81
|
+
![example err_bar graph](https://cdn.rawgit.com/lumean/svg-graph2/master/examples/err_bar.svg)
|
82
|
+
|
81
83
|
### Line
|
82
84
|
|
83
85
|
![example line graph](https://cdn.rawgit.com/lumean/svg-graph2/master/examples/line.svg)
|
84
86
|
|
85
87
|
### Pie
|
86
88
|
|
89
|
+
![example pie graph](https://cdn.rawgit.com/lumean/svg-graph2/master/examples/pie.svg)
|
90
|
+
|
87
91
|
### Plot
|
88
92
|
|
93
|
+
![example plot graph](https://cdn.rawgit.com/lumean/svg-graph2/master/examples/plot.svg)
|
94
|
+
|
89
95
|
### Schedule
|
90
96
|
|
97
|
+
![example schedule graph](https://cdn.rawgit.com/lumean/svg-graph2/master/examples/schedule.svg)
|
98
|
+
|
91
99
|
### TimeSeries
|
92
100
|
|
101
|
+
![example timeseries graph](https://cdn.rawgit.com/lumean/svg-graph2/master/examples/timeseries.svg)
|
93
102
|
|
94
103
|
Also have a look at the original [SVG::Graph web page](http://www.germane-software.com/software/SVG/SVG::Graph/), but note that this repository has already added some additional functionality, not available with the original.
|
95
104
|
|
data/lib/SVG/Graph/Bar.rb
CHANGED
@@ -58,12 +58,6 @@ module SVG
|
|
58
58
|
class Bar < BarBase
|
59
59
|
include REXML
|
60
60
|
|
61
|
-
# See Graph::initialize and BarBase::set_defaults
|
62
|
-
def set_defaults
|
63
|
-
super
|
64
|
-
# self.top_align = self.top_font = 1
|
65
|
-
end
|
66
|
-
|
67
61
|
protected
|
68
62
|
|
69
63
|
def get_x_labels
|
@@ -74,9 +68,13 @@ module SVG
|
|
74
68
|
maxvalue = max_value
|
75
69
|
minvalue = min_value
|
76
70
|
range = maxvalue - minvalue
|
77
|
-
|
78
|
-
|
79
|
-
|
71
|
+
# add some padding on top of the graph
|
72
|
+
if range == 0
|
73
|
+
maxvalue += 10
|
74
|
+
else
|
75
|
+
maxvalue += range / 20.0
|
76
|
+
end
|
77
|
+
scale_range = maxvalue - minvalue
|
80
78
|
|
81
79
|
@y_scale_division = scale_divisions || (scale_range / 10.0)
|
82
80
|
|
@@ -100,14 +98,11 @@ module SVG
|
|
100
98
|
minvalue = min_value
|
101
99
|
fieldwidth = field_width
|
102
100
|
|
103
|
-
# unit_size = (@graph_height.to_f - font_size*2*top_font) /
|
104
|
-
# (get_y_labels.max - get_y_labels.min)
|
105
101
|
unit_size = field_height
|
106
102
|
bargap = bar_gap ? (fieldwidth < 10 ? fieldwidth / 2 : 10) : 0
|
107
103
|
|
108
104
|
bar_width = fieldwidth - bargap
|
109
105
|
bar_width /= @data.length if stack == :side
|
110
|
-
#x_mod = (@graph_width-bargap)/2 - (stack==:side ? bar_width/2 : 0)
|
111
106
|
|
112
107
|
bottom = @graph_height
|
113
108
|
|
data/lib/SVG/Graph/BarBase.rb
CHANGED
@@ -40,6 +40,13 @@ module SVG
|
|
40
40
|
attr_accessor :stack
|
41
41
|
protected
|
42
42
|
|
43
|
+
# space in px between x-labels, we override the Graph version because
|
44
|
+
# we need the extra space (i.e. don't subtract 1 from get_x_labels.length)
|
45
|
+
def field_width
|
46
|
+
# don't use -1 otherwise bar is out of bounds
|
47
|
+
@graph_width.to_f / ( get_x_labels.length )
|
48
|
+
end
|
49
|
+
|
43
50
|
def max_value
|
44
51
|
@data.collect{|x| x[:data].max}.max
|
45
52
|
end
|
@@ -86,9 +86,9 @@ module SVG
|
|
86
86
|
end
|
87
87
|
|
88
88
|
rv = []
|
89
|
-
if maxvalue%@x_scale_division != 0
|
90
|
-
|
91
|
-
end
|
89
|
+
#if maxvalue%@x_scale_division != 0
|
90
|
+
# maxvalue = maxvalue + @x_scale_division
|
91
|
+
#end
|
92
92
|
minvalue.step( maxvalue, @x_scale_division ) {|v| rv << v}
|
93
93
|
return rv
|
94
94
|
end
|
@@ -104,10 +104,7 @@ module SVG
|
|
104
104
|
def draw_data
|
105
105
|
minvalue = min_value
|
106
106
|
fieldheight = field_height
|
107
|
-
|
108
|
-
# unit_size = (@graph_width.to_f - font_size*2*right_font ) /
|
109
|
-
# (get_x_labels.max - get_x_labels.min )
|
110
|
-
unit_size = field_width
|
107
|
+
|
111
108
|
bargap = bar_gap ? (fieldheight < 10 ? fieldheight / 2 : 10) : 0
|
112
109
|
|
113
110
|
bar_height = fieldheight - bargap
|
@@ -118,7 +115,7 @@ module SVG
|
|
118
115
|
@config[:fields].each_index { |i|
|
119
116
|
dataset_count = 0
|
120
117
|
for dataset in @data
|
121
|
-
value = dataset[:data][i]
|
118
|
+
value = dataset[:data][i]
|
122
119
|
|
123
120
|
top = @graph_height - (fieldheight * field_count)
|
124
121
|
top += (bar_height * dataset_count) if stack == :side
|
@@ -127,8 +124,8 @@ module SVG
|
|
127
124
|
# +ve +ve value.abs - min minvalue.abs
|
128
125
|
# +ve -ve value.abs - 0 minvalue.abs
|
129
126
|
# -ve -ve value.abs - 0 minvalue.abs + value
|
130
|
-
length = (value.abs - (minvalue > 0 ? minvalue : 0)) *
|
131
|
-
left = (minvalue.abs + (value < 0 ? value : 0)) *
|
127
|
+
length = (value.abs - (minvalue > 0 ? minvalue : 0))/@x_scale_division.to_f * field_width
|
128
|
+
left = (minvalue.abs + (value < 0 ? value : 0))/@x_scale_division.to_f * field_width
|
132
129
|
|
133
130
|
@graph.add_element( "rect", {
|
134
131
|
"x" => left.to_s,
|
data/lib/SVG/Graph/DataPoint.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# Allows to customize datapoint shapes
|
1
2
|
class DataPoint
|
2
3
|
OVERLAY = "OVERLAY" unless defined?(OVERLAY)
|
3
4
|
DEFAULT_SHAPE = lambda{|x,y,line| ["circle", {
|
@@ -8,24 +9,58 @@ class DataPoint
|
|
8
9
|
}]
|
9
10
|
} unless defined? DEFAULT_SHAPE
|
10
11
|
CRITERIA = [] unless defined? CRITERIA
|
12
|
+
|
13
|
+
# matchers are class scope. Once configured, each DataPoint instance will have
|
14
|
+
# access to the same matchers
|
15
|
+
# @param matchers [Array] multiple arrays of the following form:
|
16
|
+
# [ regex ,
|
17
|
+
# lambda taking three arguments (x,y, line_number for css)
|
18
|
+
# -> return value of the lambda must be an array: [svg tag name, Hash with attributes for the svg tag, e.g. "points" and "class"]
|
19
|
+
# ]
|
20
|
+
# @example
|
21
|
+
# DataPoint.configure_shape_criteria(
|
22
|
+
# [/.*/, lambda{|x,y,line| ['polygon', {
|
23
|
+
# "points" => "#{x-1.5},#{y+2.5} #{x+1.5},#{y+2.5} #{x+1.5},#{y-2.5} #{x-1.5},#{y-2.5}",
|
24
|
+
# "class" => "dataPoint#{line}"
|
25
|
+
# }]
|
26
|
+
# }]
|
27
|
+
# )
|
11
28
|
def DataPoint.configure_shape_criteria(*matchers)
|
12
29
|
CRITERIA.push(*matchers)
|
13
30
|
end
|
31
|
+
|
32
|
+
#
|
14
33
|
def DataPoint.reset_shape_criteria
|
15
34
|
CRITERIA.clear
|
16
35
|
end
|
17
36
|
|
37
|
+
# creates a new DataPoint
|
38
|
+
# @param x [Numeric] x coordinates of the point
|
39
|
+
# @param y [Numeric] y coordinates of the point
|
40
|
+
# @param line [Fixnum] line index of the current dataset (e.g. when multiple times Graph.add_data()), can be used to reference to the correct css class
|
18
41
|
def initialize(x, y, line)
|
19
42
|
@x = x
|
20
43
|
@y = y
|
21
44
|
@line = line
|
22
45
|
end
|
46
|
+
|
47
|
+
# @return [Array<Array>] see example
|
48
|
+
# @example Return value
|
49
|
+
# # two dimensional array, the splatted (*) inner array can be used as argument to REXML::add_element
|
50
|
+
# [["svgtag", {"points" => "", "class"=> "dataPoint#{line}" } ], ["svgtag", {"points"=>"", "class"=> ""}], ...]
|
51
|
+
# @exmple Usage
|
52
|
+
# dp = DataPoint.new(x, y, line).shape(data[:description])
|
53
|
+
# # for each svg we insert it to the graph
|
54
|
+
# dp.each {|s| @graph.add_element( *s )}
|
55
|
+
#
|
23
56
|
def shape(description=nil)
|
57
|
+
# select all criteria with size 2, and collect rendered lambdas in an array
|
24
58
|
shapes = CRITERIA.select {|criteria|
|
25
59
|
criteria.size == 2
|
26
60
|
}.collect {|regexp, proc|
|
27
61
|
proc.call(@x, @y, @line) if description =~ regexp
|
28
62
|
}.compact
|
63
|
+
# if above did not render anything use the defalt shape
|
29
64
|
shapes = [DEFAULT_SHAPE.call(@x, @y, @line)] if shapes.empty?
|
30
65
|
|
31
66
|
overlays = CRITERIA.select { |criteria|
|
data/lib/SVG/Graph/ErrBar.rb
CHANGED
@@ -8,19 +8,29 @@ module SVG
|
|
8
8
|
#
|
9
9
|
# = Synopsis
|
10
10
|
#
|
11
|
-
# require 'SVG/Graph/
|
11
|
+
# require 'SVG/Graph/ErrBar'
|
12
12
|
#
|
13
|
-
# fields = %w(Jan Feb
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
13
|
+
# fields = %w(Jan Feb);
|
14
|
+
# myarr1_mean = 10
|
15
|
+
# myarr1_confidence = 1
|
16
|
+
#
|
17
|
+
# myarr2_mean = 20
|
18
|
+
# myarr2_confidence = 2
|
19
|
+
#
|
20
|
+
# data= [myarr1_mean, myarr2_mean]
|
21
|
+
#
|
22
|
+
# err_mesure = [myarr1_confidence, myarr2_confidence]
|
23
|
+
#
|
24
|
+
# graph = SVG::Graph::ErrBar.new(
|
17
25
|
# :height => 500,
|
18
|
-
# :width =>
|
19
|
-
# :fields => fields
|
26
|
+
# :width => 600,
|
27
|
+
# :fields => fields,
|
28
|
+
# :errorBars => err_mesure,
|
29
|
+
# :scale_integers => true,
|
20
30
|
# )
|
21
|
-
#
|
31
|
+
#
|
22
32
|
# graph.add_data(
|
23
|
-
# :data =>
|
33
|
+
# :data => data,
|
24
34
|
# :title => 'Sales 2002'
|
25
35
|
# )
|
26
36
|
#
|
@@ -58,19 +68,15 @@ module SVG
|
|
58
68
|
class ErrBar < BarBase
|
59
69
|
include REXML
|
60
70
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
def set_defaults
|
71
|
-
super
|
72
|
-
# self.top_align = self.top_font = 1
|
73
|
-
end
|
71
|
+
def initialize config
|
72
|
+
raise "fields was not supplied or is empty" unless config[:errorBars] &&
|
73
|
+
config[:errorBars].kind_of?(Array) &&
|
74
|
+
config[:errorBars].length > 0
|
75
|
+
super
|
76
|
+
end
|
77
|
+
# Array of confidence values for each item in :fields. A range from
|
78
|
+
# value[i]-errorBars[i] to value[i]+errorBars[i] is drawn into the graph.
|
79
|
+
attr_accessor :errorBars
|
74
80
|
|
75
81
|
protected
|
76
82
|
|
@@ -82,20 +88,24 @@ module SVG
|
|
82
88
|
maxvalue = max_value
|
83
89
|
minvalue = min_value
|
84
90
|
range = maxvalue - minvalue
|
91
|
+
# add some padding on top of the graph
|
92
|
+
if range == 0
|
93
|
+
maxvalue += 10
|
94
|
+
else
|
95
|
+
maxvalue += range / 20.0
|
96
|
+
end
|
97
|
+
scale_range = maxvalue - minvalue
|
85
98
|
|
86
|
-
|
87
|
-
scale_range = (maxvalue + top_pad) - minvalue
|
88
|
-
|
89
|
-
scale_division = scale_divisions || (scale_range / 10.0)
|
99
|
+
@y_scale_division = scale_divisions || (scale_range / 10.0)
|
90
100
|
|
91
101
|
if scale_integers
|
92
|
-
|
102
|
+
@y_scale_division = @y_scale_division < 1 ? 1 : @y_scale_division.round
|
93
103
|
end
|
94
104
|
|
95
105
|
rv = []
|
96
|
-
maxvalue = maxvalue
|
97
|
-
maxvalue : maxvalue +
|
98
|
-
minvalue.step( maxvalue,
|
106
|
+
maxvalue = maxvalue%@y_scale_division == 0 ?
|
107
|
+
maxvalue : maxvalue + @y_scale_division
|
108
|
+
minvalue.step( maxvalue, @y_scale_division ) {|v| rv << v}
|
99
109
|
return rv
|
100
110
|
end
|
101
111
|
|
@@ -107,13 +117,11 @@ module SVG
|
|
107
117
|
minvalue = min_value
|
108
118
|
fieldwidth = field_width
|
109
119
|
|
110
|
-
unit_size =
|
111
|
-
(get_y_labels.max - get_y_labels.min)
|
120
|
+
unit_size = field_height
|
112
121
|
bargap = bar_gap ? (fieldwidth < 10 ? fieldwidth / 2 : 10) : 0
|
113
122
|
|
114
123
|
bar_width = fieldwidth - (bargap *2)
|
115
124
|
bar_width /= @data.length if stack == :side
|
116
|
-
#x_mod = (@graph_width-bargap)/2 - (stack==:side ? bar_width/2 : 0)
|
117
125
|
|
118
126
|
bottom = @graph_height
|
119
127
|
|
@@ -128,7 +136,7 @@ module SVG
|
|
128
136
|
# +ve -ve value - 0
|
129
137
|
# -ve -ve value.abs - 0
|
130
138
|
|
131
|
-
value = dataset[:data][i]
|
139
|
+
value = dataset[:data][i].to_f/@y_scale_division
|
132
140
|
|
133
141
|
left = (fieldwidth * field_count)
|
134
142
|
left += bargap
|
@@ -147,13 +155,11 @@ module SVG
|
|
147
155
|
"class" => "fill#{dataset_count+1}"
|
148
156
|
})
|
149
157
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
bottomErr = top-threshold
|
156
|
-
withthErr = bar_width/4
|
158
|
+
threshold = @config[:errorBars][i].to_f/@y_scale_division * unit_size
|
159
|
+
middlePointErr = left+bar_width/2
|
160
|
+
upperErr = top+threshold
|
161
|
+
bottomErr = top-threshold
|
162
|
+
withthErr = bar_width/4
|
157
163
|
|
158
164
|
@graph.add_element( "line", {
|
159
165
|
"x1" => middlePointErr.to_s,
|
@@ -178,12 +184,14 @@ module SVG
|
|
178
184
|
"style" => "stroke:rgb(0,0,0);stroke-width:1"
|
179
185
|
})
|
180
186
|
|
181
|
-
make_datapoint_text(left + bar_width/2.0, top - 6,
|
187
|
+
make_datapoint_text(left + bar_width/2.0, top - 6, dataset[:data][i].to_s)
|
188
|
+
add_popup(left + bar_width/2.0, top , dataset[:data][i].to_s)
|
182
189
|
dataset_count += 1
|
183
190
|
end
|
184
191
|
field_count += 1
|
185
|
-
}
|
186
|
-
end
|
187
|
-
|
192
|
+
} # config[:fields].each_index
|
193
|
+
end # draw_data
|
194
|
+
|
195
|
+
end # ErrBar
|
188
196
|
end
|
189
197
|
end
|
data/lib/SVG/Graph/Graph.rb
CHANGED
@@ -198,11 +198,7 @@ module SVG
|
|
198
198
|
#
|
199
199
|
def burn
|
200
200
|
raise "No data available" unless @data.size > 0
|
201
|
-
|
202
|
-
# undocumented and not used in any sublass
|
203
|
-
# to be removed
|
204
|
-
#calculations if methods.include? 'calculations'
|
205
|
-
|
201
|
+
|
206
202
|
start_svg
|
207
203
|
calculate_graph_dimensions
|
208
204
|
@foreground = Element.new( "g" )
|
@@ -381,7 +377,8 @@ module SVG
|
|
381
377
|
# Customize popup radius
|
382
378
|
attr_accessor :popup_radius
|
383
379
|
# Number format values and Y axis representation like 1.2345667 represent as 1.23,
|
384
|
-
#
|
380
|
+
# Any valid format accepted by sprintf can be specified.
|
381
|
+
# If you don't want to change the format in any way you can use "%s". Defaults to "%.2f"
|
385
382
|
attr_accessor :number_format
|
386
383
|
|
387
384
|
|
@@ -402,9 +399,6 @@ module SVG
|
|
402
399
|
@popup_radius ||= 10
|
403
400
|
end
|
404
401
|
|
405
|
-
# unknown why needed
|
406
|
-
# attr_accessor :top_align, :top_font, :right_align, :right_font
|
407
|
-
|
408
402
|
# size of the square box in the legend which indicates the colors
|
409
403
|
KEY_BOX_SIZE = 12
|
410
404
|
|
@@ -431,7 +425,7 @@ module SVG
|
|
431
425
|
else
|
432
426
|
max_width = y_label_font_size + 3
|
433
427
|
end
|
434
|
-
max_width +=
|
428
|
+
max_width += 5 + y_label_font_size if stagger_y_labels
|
435
429
|
return max_width
|
436
430
|
end
|
437
431
|
|
@@ -501,8 +495,8 @@ module SVG
|
|
501
495
|
"onmouseout" =>
|
502
496
|
"document.getElementById(#{t.object_id}).setAttribute('visibility', 'hidden' )",
|
503
497
|
})
|
504
|
-
end # add_popups
|
505
|
-
end
|
498
|
+
end # if add_popups
|
499
|
+
end # add_popup
|
506
500
|
|
507
501
|
# returns the longest label from an array of labels as string
|
508
502
|
# each object in the array must support .to_s
|
@@ -543,7 +537,7 @@ module SVG
|
|
543
537
|
else
|
544
538
|
max_height = x_label_font_size + 3
|
545
539
|
end
|
546
|
-
max_height +=
|
540
|
+
max_height += 5 + x_label_font_size if stagger_x_labels
|
547
541
|
return max_height
|
548
542
|
end
|
549
543
|
|
@@ -598,9 +592,11 @@ module SVG
|
|
598
592
|
if( numeric?(value) )
|
599
593
|
textStr = @number_format % value
|
600
594
|
end
|
601
|
-
# change anchor is label overlaps axis
|
602
|
-
if x < textStr.length/2 * font_size
|
595
|
+
# change anchor is label overlaps axis, normally anchor is middle (that's why we compute length/2)
|
596
|
+
if x < textStr.length/2 * font_size
|
603
597
|
style << "text-anchor: start;"
|
598
|
+
elsif x > @graph_width - textStr.length/2 * font_size
|
599
|
+
style << "text-anchor: end;"
|
604
600
|
end
|
605
601
|
# white background for better readability
|
606
602
|
@foreground.add_element( "text", {
|
@@ -694,9 +690,9 @@ module SVG
|
|
694
690
|
|
695
691
|
# space in px between x-labels
|
696
692
|
def field_width
|
697
|
-
#
|
698
|
-
#
|
699
|
-
@graph_width.to_f / get_x_labels.length
|
693
|
+
# -1 is to use entire x-axis
|
694
|
+
# otherwise there is always 1 division unused
|
695
|
+
@graph_width.to_f / ( get_x_labels.length - 1 )
|
700
696
|
end
|
701
697
|
|
702
698
|
# space in px between the y-labels
|
@@ -880,14 +876,7 @@ module SVG
|
|
880
876
|
x_offset = @border_left + 20
|
881
877
|
y_offset = @border_top + @graph_height + 5
|
882
878
|
if show_x_labels
|
883
|
-
# max_x_label_height_px = (not rotate_x_labels) ?
|
884
|
-
# x_label_font_size :
|
885
|
-
# get_x_labels.max{|a,b|
|
886
|
-
# a.to_s.length<=>b.to_s.length
|
887
|
-
# }.to_s.length * x_label_font_size * 0.6
|
888
|
-
# x_label_font_size
|
889
879
|
y_offset += max_x_label_height_px
|
890
|
-
y_offset += max_x_label_height_px + 5 if stagger_x_labels
|
891
880
|
end
|
892
881
|
y_offset += x_title_font_size + 5 if show_x_title
|
893
882
|
end
|
data/lib/SVG/Graph/Line.rb
CHANGED
@@ -104,7 +104,6 @@ module SVG
|
|
104
104
|
:stacked => false,
|
105
105
|
:area_fill => false
|
106
106
|
)
|
107
|
-
# self.top_align = self.top_font = self.right_align = self.right_font = 1
|
108
107
|
end
|
109
108
|
|
110
109
|
protected
|
@@ -157,29 +156,26 @@ module SVG
|
|
157
156
|
def get_y_labels
|
158
157
|
maxvalue = max_value
|
159
158
|
minvalue = min_value
|
160
|
-
#
|
161
159
|
range = maxvalue - minvalue
|
160
|
+
# add some padding on top of the graph
|
162
161
|
if range == 0
|
163
|
-
|
162
|
+
maxvalue += 10
|
164
163
|
else
|
165
|
-
|
164
|
+
maxvalue += range / 20.0
|
166
165
|
end
|
167
|
-
scale_range =
|
166
|
+
scale_range = maxvalue - minvalue
|
168
167
|
|
169
168
|
@y_scale_division = scale_divisions || (scale_range / 10.0)
|
170
|
-
|
169
|
+
@y_offset = 0
|
170
|
+
|
171
171
|
if scale_integers
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
end
|
172
|
+
@y_scale_division = @y_scale_division < 1 ? 1 : @y_scale_division.round
|
173
|
+
@y_offset = (minvalue.to_f - minvalue.floor).to_f
|
174
|
+
minvalue = minvalue.floor
|
176
175
|
end
|
177
176
|
|
178
177
|
rv = []
|
179
|
-
|
180
|
-
if maxvalue%@y_scale_division != 0
|
181
|
-
maxvalue = maxvalue + @y_scale_division
|
182
|
-
end
|
178
|
+
|
183
179
|
minvalue.step( maxvalue, @y_scale_division ) {|v| rv << v}
|
184
180
|
return rv
|
185
181
|
end
|
@@ -187,20 +183,19 @@ module SVG
|
|
187
183
|
def calc_coords(field, value, width = field_width, height = field_height)
|
188
184
|
coords = {:x => 0, :y => 0}
|
189
185
|
coords[:x] = width * field
|
190
|
-
|
191
|
-
|
186
|
+
# make sure we do float division, otherwise coords get messed up
|
187
|
+
coords[:y] = @graph_height - (value + @y_offset)/@y_scale_division.to_f * height
|
192
188
|
return coords
|
193
189
|
end
|
194
190
|
|
195
191
|
def draw_data
|
196
192
|
minvalue = min_value
|
197
|
-
#fieldheight = (@graph_height.to_f - font_size*2*top_font) /
|
198
|
-
# (get_y_labels.max - get_y_labels.min)
|
199
193
|
fieldheight = field_height
|
200
194
|
fieldwidth = field_width
|
201
195
|
line = @data.length
|
202
|
-
|
203
|
-
prev_sum = Array.new(@config[:fields].length).fill(
|
196
|
+
# always zero for filling areas
|
197
|
+
prev_sum = Array.new(@config[:fields].length).fill(-@y_offset)
|
198
|
+
# cumulated sum (used for stacked graphs)
|
204
199
|
cum_sum = Array.new(@config[:fields].length).fill(nil)
|
205
200
|
|
206
201
|
for data in @data.reverse
|
@@ -234,7 +229,7 @@ module SVG
|
|
234
229
|
c = calc_coords(0, prev_sum[0], fieldwidth, fieldheight)
|
235
230
|
else
|
236
231
|
apath = "V#@graph_height"
|
237
|
-
c = calc_coords(0,
|
232
|
+
c = calc_coords(0, -@y_offset, fieldwidth, fieldheight)
|
238
233
|
end
|
239
234
|
|
240
235
|
@graph.add_element("path", {
|
@@ -255,18 +250,13 @@ module SVG
|
|
255
250
|
c = calc_coords(i, cum_sum[i], fieldwidth, fieldheight)
|
256
251
|
if show_data_points
|
257
252
|
@graph.add_element( "circle", {
|
258
|
-
# "cx" => (fieldwidth * i).to_s,
|
259
|
-
# "cy" => (@graph_height - cum_sum[i] * fieldheight).to_s,
|
260
253
|
"cx" => c[:x].to_s,
|
261
254
|
"cy" => c[:y].to_s,
|
262
255
|
"r" => "2.5",
|
263
256
|
"class" => "dataPoint#{line}"
|
264
257
|
})
|
265
258
|
end
|
266
|
-
|
267
|
-
#y = @graph_height - cum_sum[i] * fieldheight
|
268
|
-
#make_datapoint_text( x, y - font_size/2, cum_sum[i] + minvalue)
|
269
|
-
#add_popup(x, y, cum_sum[i] + minvalue)
|
259
|
+
|
270
260
|
make_datapoint_text( c[:x], c[:y] - font_size/2, cum_sum[i] + minvalue)
|
271
261
|
add_popup(c[:x], c[:y], cum_sum[i] + minvalue)
|
272
262
|
end
|
data/lib/SVG/Graph/Pie.rb
CHANGED
@@ -204,7 +204,6 @@ module SVG
|
|
204
204
|
diameter -= 10 if show_shadow
|
205
205
|
radius = diameter / 2.0
|
206
206
|
|
207
|
-
#xoff = (width - diameter) / 2
|
208
207
|
xoff = (@graph_width - diameter) / 2
|
209
208
|
yoff = (height - @border_bottom - diameter)
|
210
209
|
yoff -= 10 if show_shadow
|
@@ -224,7 +223,7 @@ module SVG
|
|
224
223
|
prev_percent = 0
|
225
224
|
rad_mult = 3.6 * RADIANS
|
226
225
|
@config[:fields].each_index { |count|
|
227
|
-
value = @data[count]
|
226
|
+
value = @data[count].to_f
|
228
227
|
percent = percent_scale * value
|
229
228
|
radians = prev_percent * rad_mult
|
230
229
|
|
data/lib/SVG/Graph/Plot.rb
CHANGED
@@ -67,8 +67,11 @@ module SVG
|
|
67
67
|
#
|
68
68
|
# Unlike the other types of charts, data sets must contain x,y pairs:
|
69
69
|
#
|
70
|
-
# [ 1,
|
71
|
-
# [ 1,2, 5,6] # A data set with 2 points: (1,2) and (5,6)
|
70
|
+
# [ 1,2 ] # A data set with 1 point: (1,2)
|
71
|
+
# [ 1,2, 5,6] # A data set with 2 points: (1,2) and (5,6)
|
72
|
+
# Additional possible notation
|
73
|
+
# [ [1,2], 5,6] # A data set with 2 points: (1,2) and (5,6), mixed notation
|
74
|
+
# [ [1,2], [5,6]] # A data set with 2 points: (1,2) and (5,6), nested array
|
72
75
|
#
|
73
76
|
# = See also
|
74
77
|
#
|
@@ -89,20 +92,21 @@ module SVG
|
|
89
92
|
class Plot < Graph
|
90
93
|
|
91
94
|
# In addition to the defaults set by Graph::initialize, sets
|
92
|
-
# [show_data_values] true
|
93
95
|
# [show_data_points] true
|
94
96
|
# [area_fill] false
|
95
|
-
# [stacked] false
|
97
|
+
# [stacked] false, will not have any effect if true
|
98
|
+
# [show_lines] true
|
99
|
+
# [round_popups] true
|
96
100
|
def set_defaults
|
97
101
|
init_with(
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
102
|
+
:show_data_points => true,
|
103
|
+
:area_fill => false,
|
104
|
+
:stacked => false,
|
105
|
+
:show_lines => true,
|
106
|
+
:round_popups => true,
|
107
|
+
:scale_x_integers => false,
|
108
|
+
:scale_y_integerrs => false,
|
109
|
+
)
|
106
110
|
end
|
107
111
|
|
108
112
|
# Determines the scaling for the X axis divisions.
|
@@ -111,6 +115,7 @@ module SVG
|
|
111
115
|
#
|
112
116
|
# would cause the graph to attempt to generate labels stepped by 2; EG:
|
113
117
|
# 0,2,4,6,8...
|
118
|
+
# default is automatic such that there are 10 labels
|
114
119
|
attr_accessor :scale_x_divisions
|
115
120
|
# Determines the scaling for the Y axis divisions.
|
116
121
|
#
|
@@ -118,33 +123,38 @@ module SVG
|
|
118
123
|
#
|
119
124
|
# would cause the graph to attempt to generate labels stepped by 0.5; EG:
|
120
125
|
# 0, 0.5, 1, 1.5, 2, ...
|
126
|
+
# default is automatic such that there are 10 labels
|
121
127
|
attr_accessor :scale_y_divisions
|
122
|
-
# Make the X axis labels integers
|
128
|
+
# Make the X axis labels integers, default: false
|
123
129
|
attr_accessor :scale_x_integers
|
124
|
-
# Make the Y axis labels integers
|
130
|
+
# Make the Y axis labels integers, default: false
|
125
131
|
attr_accessor :scale_y_integers
|
126
|
-
# Fill the area under the line
|
132
|
+
# Fill the area under the line, default: false
|
127
133
|
attr_accessor :area_fill
|
128
134
|
# Show a small circle on the graph where the line
|
129
|
-
# goes from one point to the next.
|
135
|
+
# goes from one point to the next. default: true
|
130
136
|
attr_accessor :show_data_points
|
131
|
-
# Set the minimum value of the X axis
|
137
|
+
# Set the minimum value of the X axis, if nil the minimum from data is chosen, default: nil
|
132
138
|
attr_accessor :min_x_value
|
133
|
-
# Set the maximum value of the X axis
|
139
|
+
# Set the maximum value of the X axis, if nil the maximum from data is chosen, default: nil
|
134
140
|
attr_accessor :max_x_value
|
135
|
-
# Set the minimum value of the Y axis
|
141
|
+
# Set the minimum value of the Y axis, if nil the minimum from data is chosen, default: nil
|
136
142
|
attr_accessor :min_y_value
|
137
|
-
# Set the maximum value of the Y axis
|
143
|
+
# Set the maximum value of the Y axis, if nil the maximum from data is chosen, default: nil
|
138
144
|
attr_accessor :max_y_value
|
139
|
-
# Show lines connecting data points
|
145
|
+
# Show lines connecting data points, default: true
|
140
146
|
attr_accessor :show_lines
|
141
|
-
# Round value of data points in popups to integer
|
147
|
+
# Round value of data points in popups to integer, default: true
|
142
148
|
attr_accessor :round_popups
|
143
149
|
|
144
150
|
|
145
151
|
# Adds data to the plot. The data must be in X,Y pairs; EG
|
146
152
|
# data_set1 = [ 1, 2 ] # A data set with 1 point: (1,2)
|
147
|
-
# data_set2 = [ 1,2, 5,6] # A data set with 2 points: (1,2) and (5,6)
|
153
|
+
# data_set2 = [ 1,2, 5,6] # A data set with 2 points: (1,2) and (5,6)
|
154
|
+
# It's also supported to supply nested array or a mix (flatten is applied to the array); EG
|
155
|
+
# data_set2 = [[1,2], 5,6]
|
156
|
+
# or
|
157
|
+
# data_set2 = [[1,2], [5,6]]
|
148
158
|
#
|
149
159
|
# graph.add_data({
|
150
160
|
# :data => data_set1,
|
@@ -156,13 +166,18 @@ module SVG
|
|
156
166
|
# })
|
157
167
|
def add_data(conf)
|
158
168
|
@data ||= []
|
159
|
-
# remove nil values
|
160
|
-
conf[:data] = conf[:data].compact
|
161
169
|
raise "No data provided by #{conf.inspect}" unless conf[:data] and
|
162
170
|
conf[:data].kind_of? Array
|
171
|
+
# support array of arrays and flatten it
|
172
|
+
conf[:data] = conf[:data].flatten
|
173
|
+
# check that we have pairs of values
|
163
174
|
raise "Data supplied must be x,y pairs! "+
|
164
175
|
"The data provided contained an odd set of "+
|
165
176
|
"data points" unless conf[:data].length % 2 == 0
|
177
|
+
|
178
|
+
# remove nil values
|
179
|
+
conf[:data] = conf[:data].compact
|
180
|
+
|
166
181
|
return if conf[:data].length == 0
|
167
182
|
|
168
183
|
conf[:description] ||= Array.new(conf[:data].size/2)
|
@@ -177,6 +192,11 @@ module SVG
|
|
177
192
|
}
|
178
193
|
sort( x, y, conf[:description] )
|
179
194
|
conf[:data] = [x,y]
|
195
|
+
# at the end data looks like:
|
196
|
+
# [
|
197
|
+
# [all x values],
|
198
|
+
# [all y values]
|
199
|
+
# ]
|
180
200
|
@data << conf
|
181
201
|
end
|
182
202
|
|
@@ -214,27 +234,34 @@ module SVG
|
|
214
234
|
min_value
|
215
235
|
end
|
216
236
|
|
217
|
-
def
|
237
|
+
def x_label_range
|
218
238
|
max_value = max_x_range
|
219
239
|
min_value = min_x_range
|
220
|
-
|
221
240
|
range = max_value - min_value
|
222
|
-
|
223
|
-
|
241
|
+
# add some padding on right
|
242
|
+
if range == 0
|
243
|
+
max_value += 10
|
244
|
+
else
|
245
|
+
max_value += range / 20.0
|
246
|
+
end
|
247
|
+
scale_range = max_value - min_value
|
224
248
|
|
225
249
|
scale_division = scale_x_divisions || (scale_range / 10.0)
|
226
|
-
|
250
|
+
@x_offset = 0
|
251
|
+
|
227
252
|
if scale_x_integers
|
228
253
|
scale_division = scale_division < 1 ? 1 : scale_division.round
|
254
|
+
@x_offset = min_value.to_f - min_value.floor
|
255
|
+
min_value = min_value.floor
|
229
256
|
end
|
230
257
|
|
231
258
|
[min_value, max_value, scale_division]
|
232
259
|
end
|
233
260
|
|
234
261
|
def get_x_values
|
235
|
-
min_value, max_value,
|
262
|
+
min_value, max_value, @x_scale_division = x_label_range
|
236
263
|
rv = []
|
237
|
-
min_value.step( max_value,
|
264
|
+
min_value.step( max_value, @x_scale_division ) {|v| rv << v}
|
238
265
|
return rv
|
239
266
|
end
|
240
267
|
alias :get_x_labels :get_x_values
|
@@ -242,11 +269,8 @@ module SVG
|
|
242
269
|
def field_width
|
243
270
|
# exclude values which are outside max_x_range
|
244
271
|
values = get_x_values
|
245
|
-
|
246
|
-
|
247
|
-
#(@graph_width.to_f - font_size*2*right_font) /
|
248
|
-
# (values.length + dx - right_align)
|
249
|
-
@graph_width.to_f / values.length
|
272
|
+
@graph_width.to_f / (values.length - 1 ) # -1 is to use entire x-axis
|
273
|
+
# otherwise there is always 1 division unused
|
250
274
|
end
|
251
275
|
|
252
276
|
|
@@ -262,32 +286,39 @@ module SVG
|
|
262
286
|
min_value
|
263
287
|
end
|
264
288
|
|
265
|
-
def
|
289
|
+
def y_label_range
|
266
290
|
max_value = max_y_range
|
267
291
|
min_value = min_y_range
|
268
|
-
|
269
292
|
range = max_value - min_value
|
270
|
-
|
271
|
-
|
293
|
+
# add some padding on top
|
294
|
+
if range == 0
|
295
|
+
max_value += 10
|
296
|
+
else
|
297
|
+
max_value += range / 20.0
|
298
|
+
end
|
299
|
+
scale_range = max_value - min_value
|
272
300
|
|
273
301
|
scale_division = scale_y_divisions || (scale_range / 10.0)
|
274
|
-
|
302
|
+
@y_offset = 0
|
303
|
+
|
275
304
|
if scale_y_integers
|
276
305
|
scale_division = scale_division < 1 ? 1 : scale_division.round
|
306
|
+
@y_offset = (min_value.to_f - min_value.floor).to_f
|
307
|
+
min_value = min_value.floor
|
277
308
|
end
|
278
309
|
|
279
310
|
return [min_value, max_value, scale_division]
|
280
311
|
end
|
281
312
|
|
282
313
|
def get_y_values
|
283
|
-
min_value, max_value,
|
314
|
+
min_value, max_value, @y_scale_division = y_label_range
|
284
315
|
if max_value != min_value
|
285
|
-
while (max_value - min_value) <
|
286
|
-
|
316
|
+
while (max_value - min_value) < @y_scale_division
|
317
|
+
@y_scale_division /= 10.0
|
287
318
|
end
|
288
319
|
end
|
289
320
|
rv = []
|
290
|
-
min_value.step( max_value,
|
321
|
+
min_value.step( max_value, @y_scale_division ) {|v| rv << v}
|
291
322
|
rv << rv[0] + 1 if rv.length == 1
|
292
323
|
return rv
|
293
324
|
end
|
@@ -302,18 +333,25 @@ module SVG
|
|
302
333
|
else
|
303
334
|
dx = (max - values[-1]).to_f / (values[-1] - values[-2])
|
304
335
|
end
|
305
|
-
#(@graph_height.to_f - font_size*2*top_font) /
|
306
|
-
# (values.length + dx - top_align)
|
307
336
|
@graph_height.to_f / values.length
|
308
337
|
end
|
338
|
+
|
339
|
+
def calc_coords(x, y)
|
340
|
+
coords = {:x => 0, :y => 0}
|
341
|
+
# scale the coordinates, use float division / multiplication
|
342
|
+
# otherwise the point will be place inaccurate
|
343
|
+
coords[:x] = (x + @x_offset)/@x_scale_division.to_f * field_width
|
344
|
+
coords[:y] = @graph_height - (y + @y_offset)/@y_scale_division.to_f * field_height
|
345
|
+
return coords
|
346
|
+
end
|
309
347
|
|
310
348
|
def draw_data
|
311
349
|
line = 1
|
312
350
|
|
313
|
-
x_min
|
314
|
-
|
315
|
-
|
316
|
-
|
351
|
+
x_min = min_x_range
|
352
|
+
x_max = max_x_range
|
353
|
+
y_min = min_y_range
|
354
|
+
y_max = max_y_range
|
317
355
|
|
318
356
|
for data in @data
|
319
357
|
x_points = data[:data][X]
|
@@ -323,10 +361,9 @@ module SVG
|
|
323
361
|
x_start = 0
|
324
362
|
y_start = 0
|
325
363
|
x_points.each_index { |idx|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
lpath << "#{x} #{y} "
|
364
|
+
c = calc_coords(x_points[idx] - x_min, y_points[idx] - y_min)
|
365
|
+
x_start, y_start = c[:x],c[:y] if idx == 0
|
366
|
+
lpath << "#{c[:x]} #{c[:y]} "
|
330
367
|
}
|
331
368
|
|
332
369
|
if area_fill
|
@@ -345,25 +382,25 @@ module SVG
|
|
345
382
|
|
346
383
|
if show_data_points || show_data_values || add_popups
|
347
384
|
x_points.each_index { |idx|
|
348
|
-
|
349
|
-
y = @graph_height - (y_points[idx] - y_min) * y_step
|
385
|
+
c = calc_coords(x_points[idx] - x_min, y_points[idx] - y_min)
|
350
386
|
if show_data_points
|
351
|
-
DataPoint.new(x, y, line).shape(data[:description][idx]).each{|s|
|
387
|
+
DataPoint.new(c[:x], c[:y], line).shape(data[:description][idx]).each{|s|
|
352
388
|
@graph.add_element( *s )
|
353
389
|
}
|
354
390
|
end
|
355
|
-
make_datapoint_text( x, y-6, y_points[idx] )
|
356
|
-
add_popup(x, y, format( x_points[idx], y_points[idx], data[:description][idx]))
|
391
|
+
make_datapoint_text( c[:x], c[:y]-6, y_points[idx] )
|
392
|
+
add_popup(c[:x], c[:y], format( x_points[idx], y_points[idx], data[:description][idx]))
|
357
393
|
}
|
358
394
|
end
|
359
395
|
line += 1
|
360
396
|
end
|
361
397
|
end
|
362
|
-
|
398
|
+
|
399
|
+
# returns the formatted string which is added as popup information
|
363
400
|
def format x, y, desc
|
364
401
|
info = []
|
365
|
-
info << (round_popups ?
|
366
|
-
info << (round_popups ?
|
402
|
+
info << (round_popups ? x.round : @number_format % x )
|
403
|
+
info << (round_popups ? y.round : @number_format % y )
|
367
404
|
info << desc
|
368
405
|
"(#{info.compact.join(', ')})"
|
369
406
|
end
|
data/lib/SVG/Graph/Schedule.rb
CHANGED
@@ -140,7 +140,7 @@ module SVG
|
|
140
140
|
# Note that the data must be in time,value pairs, and that the date format
|
141
141
|
# may be any date that is parseable by DateTime#parse, DateTime#strptime.
|
142
142
|
# The :template argument is optional. By default DateTime#parse will be used.
|
143
|
-
# Checkout the ruby doc for
|
143
|
+
# Checkout the ruby doc for valid template notation:
|
144
144
|
# http://ruby-doc.org/stdlib-2.3.1/libdoc/date/rdoc/Date.html#method-i-strftime
|
145
145
|
# http://ruby-doc.org/stdlib-2.3.1/libdoc/date/rdoc/DateTime.html#method-c-parse
|
146
146
|
# http://ruby-doc.org/stdlib-2.3.1/libdoc/date/rdoc/DateTime.html#method-c-strptime.
|
data/lib/SVG/Graph/TimeSeries.rb
CHANGED
@@ -109,12 +109,12 @@ module SVG
|
|
109
109
|
init_with(
|
110
110
|
#:max_time_span => '',
|
111
111
|
:x_label_format => '%Y-%m-%d %H:%M:%S',
|
112
|
-
:popup_format => '%Y-%m-%d %H:%M:%S'
|
112
|
+
:popup_format => '%Y-%m-%d %H:%M:%S',
|
113
113
|
)
|
114
114
|
end
|
115
115
|
|
116
|
-
# The format string
|
117
|
-
# See Time::strformat
|
116
|
+
# The format string used to format the X axis labels.
|
117
|
+
# See Time::strformat, default: '%Y-%m-%d %H:%M:%S'
|
118
118
|
attr_accessor :x_label_format
|
119
119
|
# Use this to set the spacing between dates on the axis. The value
|
120
120
|
# must be of the form
|
@@ -138,7 +138,7 @@ module SVG
|
|
138
138
|
# graph.add_data(
|
139
139
|
# :data => d1,
|
140
140
|
# :title => 'One',
|
141
|
-
# :template => '%H:%M'
|
141
|
+
# :template => '%H:%M' #template is optional
|
142
142
|
# )
|
143
143
|
# graph.add_data(
|
144
144
|
# :data => d2,
|
@@ -150,26 +150,24 @@ module SVG
|
|
150
150
|
# a format that is parseable by ParseDate, a Time object, or a number of seconds
|
151
151
|
# after the unix epoch.
|
152
152
|
def add_data data
|
153
|
-
@time_template = data[:template]
|
154
153
|
data[:data].each_index do |i|
|
155
|
-
data[:data][i] = parse_time(data[:data][i]).to_i if i % 2 == 0
|
154
|
+
data[:data][i] = parse_time(data[:data][i], data[:template]).to_i if i % 2 == 0 # only even indices are time, odd indices are values
|
156
155
|
end
|
157
|
-
@time_template = nil # reset for the next data series
|
158
156
|
super(data)
|
159
157
|
end
|
160
158
|
|
161
159
|
|
162
160
|
protected
|
163
161
|
|
164
|
-
|
162
|
+
# value must be Integer, Time, or parseable by DateTime#parse
|
165
163
|
def min_x_value=(value)
|
166
|
-
t = parse_time(value)
|
164
|
+
t = parse_time(value, nil)
|
167
165
|
@min_x_value = t.to_i
|
168
166
|
end
|
169
167
|
|
170
|
-
# value
|
168
|
+
# value must be Integer, Time, or parseable by DateTime#parse
|
171
169
|
def max_x_value=(value)
|
172
|
-
t = parse_time(value)
|
170
|
+
t = parse_time(value, nil)
|
173
171
|
@max_x_value = t.to_i
|
174
172
|
end
|
175
173
|
|
@@ -190,13 +188,13 @@ module SVG
|
|
190
188
|
# Accepts date time as a string, number of seconds since the epoch, or Time
|
191
189
|
# object and returns a Time object. Raises an error if not a valid date time
|
192
190
|
# representation.
|
193
|
-
def parse_time(time)
|
191
|
+
def parse_time(time, template)
|
194
192
|
case time
|
195
193
|
when Time
|
196
194
|
return time
|
197
195
|
when String
|
198
|
-
if
|
199
|
-
return DateTime.strptime(time,
|
196
|
+
if template.kind_of? String
|
197
|
+
return DateTime.strptime(time, template).to_time
|
200
198
|
else
|
201
199
|
return DateTime.parse(time).to_time
|
202
200
|
end
|
@@ -209,7 +207,7 @@ module SVG
|
|
209
207
|
|
210
208
|
def get_x_values
|
211
209
|
rv = []
|
212
|
-
min, max,
|
210
|
+
min, max, @x_scale_division = x_label_range
|
213
211
|
if timescale_divisions
|
214
212
|
timescale_divisions =~ /(\d+) ?(day|week|month|year|hour|minute|second)?/
|
215
213
|
division_units = $2 ? $2 : "day"
|
@@ -219,6 +217,7 @@ module SVG
|
|
219
217
|
case division_units
|
220
218
|
when "month"
|
221
219
|
cur = min
|
220
|
+
@x_scale_division = 365.25/12 * 24 * 60 * 60 * amount
|
222
221
|
while cur < max
|
223
222
|
rv << cur
|
224
223
|
arr = Time.at( cur ).to_a
|
@@ -231,6 +230,7 @@ module SVG
|
|
231
230
|
end
|
232
231
|
when "year"
|
233
232
|
cur = min
|
233
|
+
@x_scale_division = 365.25 * 24 * 60 * 60 * amount
|
234
234
|
while cur < max
|
235
235
|
rv << cur
|
236
236
|
arr = Time.at( cur ).to_a
|
@@ -248,12 +248,13 @@ module SVG
|
|
248
248
|
when "second"
|
249
249
|
step = amount
|
250
250
|
end
|
251
|
+
# only do this if division_units is not year or month. Those are done already above in the cases.
|
251
252
|
min.step( max, step ) {|v| rv << v} if step
|
252
|
-
|
253
|
+
@x_scale_division = step if step
|
253
254
|
return rv
|
254
255
|
end
|
255
256
|
end
|
256
|
-
min.step( max,
|
257
|
+
min.step( max, @x_scale_division ) {|v| rv << v}
|
257
258
|
return rv
|
258
259
|
end # get_x_values
|
259
260
|
|
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.2
|
4
|
+
version: 2.0.2
|
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: 2016-
|
15
|
+
date: 2016-10-26 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,9 +67,9 @@ 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: '0'
|
73
73
|
requirements: []
|
74
74
|
rubyforge_project:
|
75
75
|
rubygems_version: 2.6.7
|