Mercurial > hg > soundsoftware-site
comparison .svn/pristine/74/74c1617ddcbb99d4a4a61efc78116f68f0818d6b.svn-base @ 1295:622f24f53b42 redmine-2.3
Update to Redmine SVN revision 11972 on 2.3-stable branch
author | Chris Cannam |
---|---|
date | Fri, 14 Jun 2013 09:02:21 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
1294:3e4c3460b6ca | 1295:622f24f53b42 |
---|---|
1 require 'SVG/Graph/Plot' | |
2 | |
3 module SVG | |
4 module Graph | |
5 # === For creating SVG plots of scalar temporal data | |
6 # | |
7 # = Synopsis | |
8 # | |
9 # require 'SVG/Graph/TimeSeriess' | |
10 # | |
11 # # Data sets are x,y pairs | |
12 # data1 = ["6/17/72", 11, "1/11/72", 7, "4/13/04 17:31", 11, | |
13 # "9/11/01", 9, "9/1/85", 2, "9/1/88", 1, "1/15/95", 13] | |
14 # data2 = ["8/1/73", 18, "3/1/77", 15, "10/1/98", 4, | |
15 # "5/1/02", 14, "3/1/95", 6, "8/1/91", 12, "12/1/87", 6, | |
16 # "5/1/84", 17, "10/1/80", 12] | |
17 # | |
18 # graph = SVG::Graph::TimeSeries.new( { | |
19 # :width => 640, | |
20 # :height => 480, | |
21 # :graph_title => title, | |
22 # :show_graph_title => true, | |
23 # :no_css => true, | |
24 # :key => true, | |
25 # :scale_x_integers => true, | |
26 # :scale_y_integers => true, | |
27 # :min_x_value => 0, | |
28 # :min_y_value => 0, | |
29 # :show_data_labels => true, | |
30 # :show_x_guidelines => true, | |
31 # :show_x_title => true, | |
32 # :x_title => "Time", | |
33 # :show_y_title => true, | |
34 # :y_title => "Ice Cream Cones", | |
35 # :y_title_text_direction => :bt, | |
36 # :stagger_x_labels => true, | |
37 # :x_label_format => "%m/%d/%y", | |
38 # }) | |
39 # | |
40 # graph.add_data({ | |
41 # :data => projection | |
42 # :title => 'Projected', | |
43 # }) | |
44 # | |
45 # graph.add_data({ | |
46 # :data => actual, | |
47 # :title => 'Actual', | |
48 # }) | |
49 # | |
50 # print graph.burn() | |
51 # | |
52 # = Description | |
53 # | |
54 # Produces a graph of temporal scalar data. | |
55 # | |
56 # = Examples | |
57 # | |
58 # http://www.germane-software/repositories/public/SVG/test/timeseries.rb | |
59 # | |
60 # = Notes | |
61 # | |
62 # The default stylesheet handles upto 10 data sets, if you | |
63 # use more you must create your own stylesheet and add the | |
64 # additional settings for the extra data sets. You will know | |
65 # if you go over 10 data sets as they will have no style and | |
66 # be in black. | |
67 # | |
68 # Unlike the other types of charts, data sets must contain x,y pairs: | |
69 # | |
70 # [ "12:30", 2 ] # A data set with 1 point: ("12:30",2) | |
71 # [ "01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and | |
72 # # ("14:20",6) | |
73 # | |
74 # Note that multiple data sets within the same chart can differ in length, | |
75 # and that the data in the datasets needn't be in order; they will be ordered | |
76 # by the plot along the X-axis. | |
77 # | |
78 # The dates must be parseable by ParseDate, but otherwise can be | |
79 # any order of magnitude (seconds within the hour, or years) | |
80 # | |
81 # = See also | |
82 # | |
83 # * SVG::Graph::Graph | |
84 # * SVG::Graph::BarHorizontal | |
85 # * SVG::Graph::Bar | |
86 # * SVG::Graph::Line | |
87 # * SVG::Graph::Pie | |
88 # * SVG::Graph::Plot | |
89 # | |
90 # == Author | |
91 # | |
92 # Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom> | |
93 # | |
94 # Copyright 2004 Sean E. Russell | |
95 # This software is available under the Ruby license[LICENSE.txt] | |
96 # | |
97 class TimeSeries < Plot | |
98 # In addition to the defaults set by Graph::initialize and | |
99 # Plot::set_defaults, sets: | |
100 # [x_label_format] '%Y-%m-%d %H:%M:%S' | |
101 # [popup_format] '%Y-%m-%d %H:%M:%S' | |
102 def set_defaults | |
103 super | |
104 init_with( | |
105 #:max_time_span => '', | |
106 :x_label_format => '%Y-%m-%d %H:%M:%S', | |
107 :popup_format => '%Y-%m-%d %H:%M:%S' | |
108 ) | |
109 end | |
110 | |
111 # The format string use do format the X axis labels. | |
112 # See Time::strformat | |
113 attr_accessor :x_label_format | |
114 # Use this to set the spacing between dates on the axis. The value | |
115 # must be of the form | |
116 # "\d+ ?(days|weeks|months|years|hours|minutes|seconds)?" | |
117 # | |
118 # EG: | |
119 # | |
120 # graph.timescale_divisions = "2 weeks" | |
121 # | |
122 # will cause the chart to try to divide the X axis up into segments of | |
123 # two week periods. | |
124 attr_accessor :timescale_divisions | |
125 # The formatting used for the popups. See x_label_format | |
126 attr_accessor :popup_format | |
127 | |
128 # Add data to the plot. | |
129 # | |
130 # d1 = [ "12:30", 2 ] # A data set with 1 point: ("12:30",2) | |
131 # d2 = [ "01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and | |
132 # # ("14:20",6) | |
133 # graph.add_data( | |
134 # :data => d1, | |
135 # :title => 'One' | |
136 # ) | |
137 # graph.add_data( | |
138 # :data => d2, | |
139 # :title => 'Two' | |
140 # ) | |
141 # | |
142 # Note that the data must be in time,value pairs, and that the date format | |
143 # may be any date that is parseable by ParseDate. | |
144 def add_data data | |
145 @data = [] unless @data | |
146 | |
147 raise "No data provided by #{@data.inspect}" unless data[:data] and | |
148 data[:data].kind_of? Array | |
149 raise "Data supplied must be x,y pairs! "+ | |
150 "The data provided contained an odd set of "+ | |
151 "data points" unless data[:data].length % 2 == 0 | |
152 return if data[:data].length == 0 | |
153 | |
154 | |
155 x = [] | |
156 y = [] | |
157 data[:data].each_index {|i| | |
158 if i%2 == 0 | |
159 t = DateTime.parse( data[:data][i] ).to_time | |
160 x << t.to_i | |
161 else | |
162 y << data[:data][i] | |
163 end | |
164 } | |
165 sort( x, y ) | |
166 data[:data] = [x,y] | |
167 @data << data | |
168 end | |
169 | |
170 | |
171 protected | |
172 | |
173 def min_x_value=(value) | |
174 @min_x_value = DateTime.parse( value ).to_time | |
175 end | |
176 | |
177 | |
178 def format x, y | |
179 Time.at( x ).strftime( popup_format ) | |
180 end | |
181 | |
182 def get_x_labels | |
183 get_x_values.collect { |v| Time.at(v).strftime( x_label_format ) } | |
184 end | |
185 | |
186 private | |
187 def get_x_values | |
188 rv = [] | |
189 min, max, scale_division = x_range | |
190 if timescale_divisions | |
191 timescale_divisions =~ /(\d+) ?(day|week|month|year|hour|minute|second)?/ | |
192 division_units = $2 ? $2 : "day" | |
193 amount = $1.to_i | |
194 if amount | |
195 step = nil | |
196 case division_units | |
197 when "month" | |
198 cur = min | |
199 while cur < max | |
200 rv << cur | |
201 arr = Time.at( cur ).to_a | |
202 arr[4] += amount | |
203 if arr[4] > 12 | |
204 arr[5] += (arr[4] / 12).to_i | |
205 arr[4] = (arr[4] % 12) | |
206 end | |
207 cur = Time.local(*arr).to_i | |
208 end | |
209 when "year" | |
210 cur = min | |
211 while cur < max | |
212 rv << cur | |
213 arr = Time.at( cur ).to_a | |
214 arr[5] += amount | |
215 cur = Time.local(*arr).to_i | |
216 end | |
217 when "week" | |
218 step = 7 * 24 * 60 * 60 * amount | |
219 when "day" | |
220 step = 24 * 60 * 60 * amount | |
221 when "hour" | |
222 step = 60 * 60 * amount | |
223 when "minute" | |
224 step = 60 * amount | |
225 when "second" | |
226 step = amount | |
227 end | |
228 min.step( max, step ) {|v| rv << v} if step | |
229 | |
230 return rv | |
231 end | |
232 end | |
233 min.step( max, scale_division ) {|v| rv << v} | |
234 return rv | |
235 end | |
236 end | |
237 end | |
238 end |