svg-graph 2.0.2.beta → 2.0.2
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 +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
|
+

|
82
|
+
|
81
83
|
### Line
|
82
84
|
|
83
85
|

|
84
86
|
|
85
87
|
### Pie
|
86
88
|
|
89
|
+

|
90
|
+
|
87
91
|
### Plot
|
88
92
|
|
93
|
+

|
94
|
+
|
89
95
|
### Schedule
|
90
96
|
|
97
|
+

|
98
|
+
|
91
99
|
### TimeSeries
|
92
100
|
|
101
|
+

|
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
|