gerbilcharts 0.0.3
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.
- data/History.txt +11 -0
- data/License.txt +21 -0
- data/Manifest.txt +75 -0
- data/PostInstall.txt +7 -0
- data/README.txt +174 -0
- data/Rakefile +4 -0
- data/lib/gerbilcharts.rb +18 -0
- data/lib/gerbilcharts/charts.rb +16 -0
- data/lib/gerbilcharts/charts/area_chart.rb +36 -0
- data/lib/gerbilcharts/charts/bar_chart.rb +33 -0
- data/lib/gerbilcharts/charts/bar_chart_compact.rb +26 -0
- data/lib/gerbilcharts/charts/chart_base.rb +123 -0
- data/lib/gerbilcharts/charts/impulse_chart.rb +30 -0
- data/lib/gerbilcharts/charts/line_chart.rb +35 -0
- data/lib/gerbilcharts/charts/stacked_area_chart.rb +31 -0
- data/lib/gerbilcharts/models.rb +19 -0
- data/lib/gerbilcharts/models/bucketized_timeseries_graph_model.rb +138 -0
- data/lib/gerbilcharts/models/discrete_time_range.rb +63 -0
- data/lib/gerbilcharts/models/graph_model.rb +89 -0
- data/lib/gerbilcharts/models/graph_model_group.rb +240 -0
- data/lib/gerbilcharts/models/monotonous_graph_model.rb +192 -0
- data/lib/gerbilcharts/models/presets.rb +94 -0
- data/lib/gerbilcharts/models/raw_range.rb +68 -0
- data/lib/gerbilcharts/models/round_range.rb +104 -0
- data/lib/gerbilcharts/models/round_time_range.rb +105 -0
- data/lib/gerbilcharts/models/sampled_timeseries_graph_model.rb +80 -0
- data/lib/gerbilcharts/models/simple_timeseries_model_group.rb +68 -0
- data/lib/gerbilcharts/models/time_series_graph_model.rb +34 -0
- data/lib/gerbilcharts/public/brushmetal.css +197 -0
- data/lib/gerbilcharts/public/gerbil.js +327 -0
- data/lib/gerbilcharts/surfaces.rb +32 -0
- data/lib/gerbilcharts/surfaces/area_surface.rb +46 -0
- data/lib/gerbilcharts/surfaces/axis.rb +31 -0
- data/lib/gerbilcharts/surfaces/bar_surface.rb +62 -0
- data/lib/gerbilcharts/surfaces/basic_grid.rb +17 -0
- data/lib/gerbilcharts/surfaces/chart.rb +132 -0
- data/lib/gerbilcharts/surfaces/graph_element.rb +170 -0
- data/lib/gerbilcharts/surfaces/grid.rb +38 -0
- data/lib/gerbilcharts/surfaces/horizontal_axis.rb +32 -0
- data/lib/gerbilcharts/surfaces/horizontal_name_axis.rb +28 -0
- data/lib/gerbilcharts/surfaces/horizontal_time_axis.rb +25 -0
- data/lib/gerbilcharts/surfaces/impulse_surface.rb +47 -0
- data/lib/gerbilcharts/surfaces/legend.rb +59 -0
- data/lib/gerbilcharts/surfaces/line_surface.rb +53 -0
- data/lib/gerbilcharts/surfaces/mark_band.rb +17 -0
- data/lib/gerbilcharts/surfaces/panel.rb +17 -0
- data/lib/gerbilcharts/surfaces/pie_surface.rb +16 -0
- data/lib/gerbilcharts/surfaces/rect.rb +86 -0
- data/lib/gerbilcharts/surfaces/stacked_area_surface.rb +66 -0
- data/lib/gerbilcharts/surfaces/stacked_grid.rb +15 -0
- data/lib/gerbilcharts/surfaces/surface.rb +20 -0
- data/lib/gerbilcharts/surfaces/surface_background.rb +13 -0
- data/lib/gerbilcharts/surfaces/title_panel.rb +44 -0
- data/lib/gerbilcharts/surfaces/tracker.rb +62 -0
- data/lib/gerbilcharts/surfaces/vertical_axis.rb +46 -0
- data/lib/gerbilcharts/svgdc.rb +22 -0
- data/lib/gerbilcharts/svgdc/filters.rb +40 -0
- data/lib/gerbilcharts/svgdc/presentation_attributes.rb +50 -0
- data/lib/gerbilcharts/svgdc/svg_circle.rb +22 -0
- data/lib/gerbilcharts/svgdc/svg_custom_win.rb +36 -0
- data/lib/gerbilcharts/svgdc/svg_element.rb +87 -0
- data/lib/gerbilcharts/svgdc/svg_line.rb +26 -0
- data/lib/gerbilcharts/svgdc/svg_polygon.rb +34 -0
- data/lib/gerbilcharts/svgdc/svg_polyline.rb +27 -0
- data/lib/gerbilcharts/svgdc/svg_rect.rb +29 -0
- data/lib/gerbilcharts/svgdc/svg_shape.rb +10 -0
- data/lib/gerbilcharts/svgdc/svg_text.rb +21 -0
- data/lib/gerbilcharts/svgdc/svg_win.rb +52 -0
- data/lib/gerbilcharts/svgdc/svgdc.rb +335 -0
- data/lib/gerbilcharts/svgdc/transformations.rb +66 -0
- data/lib/gerbilcharts/version.rb +9 -0
- data/setup.rb +1585 -0
- data/test/test_Scratch.rb +21 -0
- data/test/test_charts.rb +119 -0
- data/test/test_gerbilcharts.rb +11 -0
- data/test/test_helper.rb +2 -0
- data/test/test_models.rb +118 -0
- data/test/test_noob.rb +81 -0
- data/test/test_ranges.rb +135 -0
- data/test/test_svgdc.rb +221 -0
- data/test/trafgen.rb +25 -0
- metadata +156 -0
@@ -0,0 +1,192 @@
|
|
1
|
+
module GerbilCharts::Models
|
2
|
+
|
3
|
+
# = MonotonousGraphModel
|
4
|
+
# simple graph model (x increases monotonously)
|
5
|
+
# All timeseries models are derived from this class
|
6
|
+
#
|
7
|
+
class MonotonousGraphModel < GraphModel
|
8
|
+
|
9
|
+
attr_reader :xarr # the x values
|
10
|
+
attr_reader :yarr # the y values
|
11
|
+
attr_reader :xrange,:yrange # raw ranges
|
12
|
+
attr_reader :rounderx, :roundery # rounded ranges according to presets
|
13
|
+
|
14
|
+
def initialize(name,opt={})
|
15
|
+
super(name)
|
16
|
+
@xrange = RawRange.new
|
17
|
+
@yrange = RawRange.new
|
18
|
+
@rounderx = RoundRange
|
19
|
+
@roundery = RoundRange
|
20
|
+
@xarr=[]
|
21
|
+
@yarr=[]
|
22
|
+
end
|
23
|
+
|
24
|
+
# add a tuple, apply a transformer lambda is supplied by the user
|
25
|
+
#
|
26
|
+
# A new datapoint must have a x_val greater than the previous one
|
27
|
+
def add(x_val,y_val)
|
28
|
+
|
29
|
+
|
30
|
+
if @xarr.length > 0 and @xarr.last > x_val
|
31
|
+
raise "Expecting monotonous series data"
|
32
|
+
end
|
33
|
+
|
34
|
+
@xrange.update x_val
|
35
|
+
|
36
|
+
@xarr<<x_val
|
37
|
+
|
38
|
+
if @transformer
|
39
|
+
trval = @transformer.xform(y_val)
|
40
|
+
@yarr << trval
|
41
|
+
@yrange.update trval
|
42
|
+
else
|
43
|
+
@yarr << y_val
|
44
|
+
@yrange.update y_val
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
# add an array of values at once (just a convenience method)
|
50
|
+
def add_tuples tarr
|
51
|
+
tarr.each do |t|
|
52
|
+
add t[0],t[1]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def clear
|
57
|
+
@xrange.reset
|
58
|
+
@yrange.reset
|
59
|
+
@xarr=[]
|
60
|
+
@yarr=[]
|
61
|
+
end
|
62
|
+
|
63
|
+
def count
|
64
|
+
@xarr.size
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def latest
|
69
|
+
yield @xarr.last, @yarr.last
|
70
|
+
end
|
71
|
+
|
72
|
+
def latest_x
|
73
|
+
return @xarr.last
|
74
|
+
end
|
75
|
+
|
76
|
+
def latest_val
|
77
|
+
return @yarr.last
|
78
|
+
end
|
79
|
+
|
80
|
+
def latest_x_dbtime
|
81
|
+
return @xarr.last<<32
|
82
|
+
end
|
83
|
+
|
84
|
+
def first
|
85
|
+
yield @xarr[0],@yarr[0]
|
86
|
+
end
|
87
|
+
|
88
|
+
def latest_formatted_val
|
89
|
+
return @yrange.format_value(@yarr.last)
|
90
|
+
end
|
91
|
+
|
92
|
+
def ranges
|
93
|
+
yield @xrange, @yrange if block_given?
|
94
|
+
return @xrange,@yrange
|
95
|
+
end
|
96
|
+
|
97
|
+
def round_ranges
|
98
|
+
if block_given?
|
99
|
+
yield @rounderx.new(@xrange), @roundery.new(@yrange)
|
100
|
+
else
|
101
|
+
return @rounderx.new(@xrange), @roundery.new(@yrange)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def round_given_x(rx)
|
106
|
+
return @rounderx.new(rx)
|
107
|
+
end
|
108
|
+
|
109
|
+
def round_given_y(ry)
|
110
|
+
return @roundery.new(ry)
|
111
|
+
end
|
112
|
+
|
113
|
+
def each_tuple
|
114
|
+
for i in (0 .. @xarr.length-1)
|
115
|
+
yield @xarr[i],@yarr[i]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# crop all tuples older than a cutoff window
|
120
|
+
#
|
121
|
+
# [+cutoffwindow+] older than a number of seconds
|
122
|
+
#
|
123
|
+
def crop_window(cutoffwindow)
|
124
|
+
xcutoff = latest_x - cutoffwindow
|
125
|
+
crop_at(xcutoff)
|
126
|
+
end
|
127
|
+
|
128
|
+
# crop all tuples less than an absolute cutoff
|
129
|
+
# [+cutoff+] Cutoff value
|
130
|
+
#
|
131
|
+
def crop_at(cutoff)
|
132
|
+
until @xarr.empty? or @xarr.first >= cutoff
|
133
|
+
@xarr.shift
|
134
|
+
@yarr.shift
|
135
|
+
end
|
136
|
+
recompute_ranges
|
137
|
+
end
|
138
|
+
|
139
|
+
# all data points newer than a cut off
|
140
|
+
def tuples_since(x_last)
|
141
|
+
istart = binarySearch(@xarr,x_last,0,@xarr.length)
|
142
|
+
for i in istart..@xarr.length-1
|
143
|
+
yield @xarr[i],@yarr[i]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# for test purposes
|
148
|
+
def randomizeLastValue
|
149
|
+
v=@yarr.last
|
150
|
+
@yarr[@yarr.size-1]=v * ( 0.5 + rand())
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
private
|
155
|
+
# recompute ranges
|
156
|
+
def recompute_ranges
|
157
|
+
@xrange.reset
|
158
|
+
@yrange.reset
|
159
|
+
|
160
|
+
each_tuple do |x,y|
|
161
|
+
@xrange.update x
|
162
|
+
@yrange.update y
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
# rudimentary binary search
|
169
|
+
def binarySearch(array, target, i, n)
|
170
|
+
case n
|
171
|
+
when 0
|
172
|
+
raise ArgumentError
|
173
|
+
when 1
|
174
|
+
if array[i] == target
|
175
|
+
return i
|
176
|
+
elsif i+1 < array.length and array[i] < target && array[i+1]>target
|
177
|
+
return i
|
178
|
+
else
|
179
|
+
raise ArgumentError
|
180
|
+
end
|
181
|
+
else
|
182
|
+
j = i + n / 2
|
183
|
+
if array[j] <= target
|
184
|
+
return binarySearch(array, target, j, n - n/2)
|
185
|
+
else
|
186
|
+
return binarySearch(array, target, i, n/2)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module GerbilCharts::Models
|
2
|
+
|
3
|
+
# Presets base class for all ranges
|
4
|
+
#
|
5
|
+
# Preset values for ranges. So we do not scale charts arbitrarily
|
6
|
+
# and the label values are neat and clean
|
7
|
+
#
|
8
|
+
class Presets
|
9
|
+
|
10
|
+
protected
|
11
|
+
PRESETS = [
|
12
|
+
[0,0], [1,0.2], [5,1], [10,1],[20,5],
|
13
|
+
[50,10],[100,25],[200,50], [500,100], [1000,200],
|
14
|
+
[2000,500], [5000,1000], [8000,2000], [10000,2000],
|
15
|
+
[20000,5000], [50000,10000], [100000,25000], [200000,50000],
|
16
|
+
[500000,100000],[800000,200000],
|
17
|
+
[1000000,200000],[2000000,500000], [3000000,500000], [4000000,1000000],
|
18
|
+
[5000000,1000000],[6000000, 1500000],
|
19
|
+
[10000000,2500000], [20000000,4000000], [50000000, 10000000],
|
20
|
+
[100000000,25000000], [500000000,100000000],
|
21
|
+
[1000000000,250000000]
|
22
|
+
]
|
23
|
+
|
24
|
+
TIMEPRESETS = [
|
25
|
+
[0,0], [1,0.2], [5,1], [10,1],[20,5],
|
26
|
+
[30,10],[60,10],[120,15], [300,60], [600,120],
|
27
|
+
[900,300], [1800,300],
|
28
|
+
[3600,900], [7200,1800],[10800,3600], [14400,3600], [21600,7200],
|
29
|
+
[43200, 10800], [86400,21600],
|
30
|
+
[172800, 43200], [259200,86400],
|
31
|
+
[604800, 86400], [1209600,172800],
|
32
|
+
[2419200, 604800],
|
33
|
+
[7257600, 2419200],
|
34
|
+
[14515200,7257600],
|
35
|
+
[29030400,7257600],
|
36
|
+
]
|
37
|
+
|
38
|
+
UNITPRESETS = [
|
39
|
+
[1e+12, "T"],
|
40
|
+
[1e+09, "G"],
|
41
|
+
[1e+06, "M"],
|
42
|
+
[1e+03, "K"],
|
43
|
+
[1, ""],
|
44
|
+
[1e-03, "m"],
|
45
|
+
[1e-06, "u"],
|
46
|
+
[1e-09, "n"],
|
47
|
+
[1e-12, "p"],
|
48
|
+
]
|
49
|
+
|
50
|
+
PRESET_VAL_POS=0
|
51
|
+
PRESET_LABEL_POS=1
|
52
|
+
|
53
|
+
|
54
|
+
# Formats a number with a units suffix
|
55
|
+
# So a number like 11899833 = 11.90 M
|
56
|
+
def format_suffix raw_value
|
57
|
+
return "0" if raw_value == 0
|
58
|
+
UNITPRESETS.each do |unit|
|
59
|
+
if raw_value >= unit[0]
|
60
|
+
retval = raw_value / unit[0]
|
61
|
+
sout=format("%.2f %s", retval, unit[1])
|
62
|
+
sout.gsub! ".00",""
|
63
|
+
return sout
|
64
|
+
end
|
65
|
+
end
|
66
|
+
return raw_value.to_s
|
67
|
+
end
|
68
|
+
|
69
|
+
# Formats a time value : based on available interval
|
70
|
+
# tvsec = seconds since Jan 1 1970
|
71
|
+
# interval = desired window in seconds
|
72
|
+
def format_timeval ( tvsec, interval)
|
73
|
+
t = Time.at(tvsec).getlocal
|
74
|
+
if interval < 60
|
75
|
+
return t.strftime("%M:%S")
|
76
|
+
elsif interval < 300
|
77
|
+
return t.strftime("%M:%S")
|
78
|
+
elsif interval < 3600
|
79
|
+
return t.strftime("%H:%M")
|
80
|
+
elsif interval < 86400
|
81
|
+
return t.strftime("%H:%M")
|
82
|
+
elsif interval < 259200
|
83
|
+
return t.strftime("%a-%H:%M")
|
84
|
+
elsif interval < 604800
|
85
|
+
return t.strtime("%b-%d")
|
86
|
+
elsif interval < 2419200
|
87
|
+
return t.strftime("%b-%d")
|
88
|
+
else
|
89
|
+
return t.strftime("%b")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module GerbilCharts::Models
|
2
|
+
|
3
|
+
# RawRange
|
4
|
+
# Maintains a range when values are added to a model
|
5
|
+
# This tracks the raw min/max values
|
6
|
+
class RawRange < Presets
|
7
|
+
|
8
|
+
attr_reader :rmax,:rmin
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@rmax,@rmin=nil,nil
|
12
|
+
end
|
13
|
+
|
14
|
+
# update - updates the range
|
15
|
+
# newval - new incoming value
|
16
|
+
#
|
17
|
+
def update(newval)
|
18
|
+
@rmax=newval if @rmax.nil? or newval > @rmax
|
19
|
+
@rmin=newval if @rmin.nil? or newval < @rmin
|
20
|
+
end
|
21
|
+
|
22
|
+
# update - updates the range (using another range)
|
23
|
+
# newval - new range
|
24
|
+
#
|
25
|
+
def update_r(range)
|
26
|
+
update(range.rmax)
|
27
|
+
update(range.rmin)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
# returns the min/max
|
32
|
+
#
|
33
|
+
def min_max
|
34
|
+
yield @rmin,@rmax if block_given?
|
35
|
+
return @rmin,@rmax
|
36
|
+
end
|
37
|
+
|
38
|
+
def reset
|
39
|
+
@rmin,@rmax=nil,nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
"Min = #{@rmin} Max=#{@rmax}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def delta
|
47
|
+
return @rmax-@rmin
|
48
|
+
end
|
49
|
+
|
50
|
+
# scales a value with respect to the range
|
51
|
+
# val = scale this value
|
52
|
+
def scale_factor(val)
|
53
|
+
f = (val-@rmin).to_f/(@rmax-@rmin).to_f
|
54
|
+
return f
|
55
|
+
end
|
56
|
+
|
57
|
+
def zeromin
|
58
|
+
@rmin=0
|
59
|
+
end
|
60
|
+
|
61
|
+
def format_value(val)
|
62
|
+
return format_suffix(val)
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module GerbilCharts::Models
|
2
|
+
|
3
|
+
# A rounded range from a raw range
|
4
|
+
# Example : RawRange (102,8991) = RoundRange (100,10K)
|
5
|
+
#
|
6
|
+
class RoundRange < RawRange
|
7
|
+
|
8
|
+
attr_reader :lmax
|
9
|
+
|
10
|
+
# given a raw range, we calculate a round range
|
11
|
+
def initialize(raw_range)
|
12
|
+
super()
|
13
|
+
@rmin=round_min(raw_range.rmin)
|
14
|
+
@rmax=round_max(raw_range.rmax)
|
15
|
+
@lmax=label_step
|
16
|
+
end
|
17
|
+
|
18
|
+
# provide labels
|
19
|
+
# yields two items (value,string label)
|
20
|
+
#
|
21
|
+
# Usage example:
|
22
|
+
# r.each_label do |v,s|
|
23
|
+
# p "Value = #{v} Label String = #{s}"
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
def each_label
|
27
|
+
|
28
|
+
raise "Range not aligned with presets (call round range first)" if not @lmax
|
29
|
+
raise "No data points in model" if @rmax == -1
|
30
|
+
return if @lmax == 0
|
31
|
+
|
32
|
+
if @rmin % @lmax != 0
|
33
|
+
v = (@rmin+@lmax) - (@rmin%@lmax)
|
34
|
+
else
|
35
|
+
v = @rmin
|
36
|
+
end
|
37
|
+
while (v<=@rmax) do
|
38
|
+
yield v, format_suffix(v)
|
39
|
+
v = v+@lmax
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# provide ticks (per label interval)
|
44
|
+
def each_tick(tpl)
|
45
|
+
|
46
|
+
raise "Range not aligned with presets (call round range first)" if not @lmax
|
47
|
+
|
48
|
+
lint = @lmax/tpl
|
49
|
+
if @rmin % lint != 0
|
50
|
+
v = (@rmin+lint) - (@rmin%lint)
|
51
|
+
else
|
52
|
+
v = @rmin
|
53
|
+
end
|
54
|
+
while (v<@rmax) do
|
55
|
+
yield v
|
56
|
+
v = v+lint
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
# round_max (ceiling)
|
62
|
+
def round_max(raw)
|
63
|
+
last_pre=1
|
64
|
+
if raw > PRESETS.last[0]
|
65
|
+
return raw
|
66
|
+
else
|
67
|
+
PRESETS.reverse_each do |pre|
|
68
|
+
break if pre[0] < raw
|
69
|
+
last_pre=pre[0]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
return last_pre
|
73
|
+
end
|
74
|
+
|
75
|
+
# round_min (floor)
|
76
|
+
def round_min(raw)
|
77
|
+
last_pre=0
|
78
|
+
PRESETS.each do |pre|
|
79
|
+
break if pre[0] > raw
|
80
|
+
last_pre=pre[0]
|
81
|
+
end
|
82
|
+
return last_pre
|
83
|
+
end
|
84
|
+
|
85
|
+
# labels
|
86
|
+
def label_step
|
87
|
+
del = @rmax-@rmin
|
88
|
+
|
89
|
+
last_pre=0.2
|
90
|
+
if del > PRESETS.last[0]
|
91
|
+
return del/10
|
92
|
+
else
|
93
|
+
PRESETS.reverse_each do |pre|
|
94
|
+
break if pre[0] < del
|
95
|
+
last_pre=pre[1]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
return last_pre
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|