geoptima 0.0.9 → 0.1.0
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/bin/csv_chart +244 -125
- data/bin/show_geoptima +1 -1
- data/examples/csv_chart.rb +244 -125
- data/examples/show_geoptima.rb +1 -1
- data/lib/geoptima/chart.rb +18 -0
- data/lib/geoptima/data.rb +1 -35
- data/lib/geoptima/daterange.rb +40 -0
- data/lib/geoptima/version.rb +1 -1
- metadata +5 -4
data/bin/csv_chart
CHANGED
@@ -8,8 +8,10 @@ require 'geoptima/chart'
|
|
8
8
|
require 'geoptima/version'
|
9
9
|
require 'geoptima/options'
|
10
10
|
require 'fileutils'
|
11
|
+
require 'geoptima/daterange'
|
11
12
|
|
12
|
-
Geoptima::assert_version("0.0
|
13
|
+
Geoptima::assert_version("0.1.0")
|
14
|
+
Geoptima::Chart.available? || puts("No charting libraries available") || exit(-1)
|
13
15
|
|
14
16
|
$export_dir = '.'
|
15
17
|
$diversity = 40.0
|
@@ -17,14 +19,22 @@ $diversity = 40.0
|
|
17
19
|
$files = Geoptima::Options.process_args do |option|
|
18
20
|
option.m {$merge_all = true}
|
19
21
|
option.a {$create_all = true}
|
22
|
+
option.t {$time_split = true}
|
20
23
|
option.D {$export_dir = ARGV.shift}
|
21
24
|
option.N {$merged_name = ARGV.shift}
|
22
25
|
option.S {$specfile = ARGV.shift}
|
23
26
|
option.P {$diversity = ARGV.shift.to_f}
|
27
|
+
option.T do
|
28
|
+
$time_range = Geoptima::DateRange.new(*(ARGV.shift.split(/[\,]+/).map do |t|
|
29
|
+
DateTime.parse t
|
30
|
+
end))
|
31
|
+
end
|
24
32
|
end
|
25
33
|
|
26
34
|
FileUtils.mkdir_p $export_dir
|
27
35
|
|
36
|
+
$create_all = true unless($specfile)
|
37
|
+
$merge_all = true if($time_split)
|
28
38
|
$help = true unless($files.length>0)
|
29
39
|
if $help
|
30
40
|
puts <<EOHELP
|
@@ -33,44 +43,46 @@ Usage: csv_chart <-dham> <-S specfile> <-N name> <-D dir> files...
|
|
33
43
|
-h print this help #{cw $help}
|
34
44
|
-a automatically create charts for all properties #{cw $create_all}
|
35
45
|
-m merge all files into single stats #{cw $merge_all}
|
46
|
+
-t merge and split by time (days) #{cw $time_split}
|
36
47
|
-N use specified name for merged dataset: #{$merged_name}
|
37
48
|
-D export charts to specified directory: #{$export_dir}
|
38
49
|
-S use chart specification in specified file: #{$specfile}
|
39
50
|
-P diversity threshold in percentage for automatic reports: #{$diversity}
|
51
|
+
-T set time-range filter: #{$time_range}
|
40
52
|
Files to import: #{$files.join(', ')}
|
41
53
|
EOHELP
|
42
54
|
exit
|
43
55
|
end
|
44
56
|
|
45
57
|
class Stats
|
46
|
-
attr_reader :
|
47
|
-
def initialize(
|
48
|
-
@file = file
|
58
|
+
attr_reader :name, :stats, :data, :numerical
|
59
|
+
def initialize(name)
|
49
60
|
@name = name
|
50
|
-
@
|
51
|
-
@
|
52
|
-
@
|
53
|
-
@numerical = fields.map{|h| true}
|
54
|
-
end
|
55
|
-
def add_header(h)
|
56
|
-
@headers << h
|
57
|
-
@stats << {}
|
58
|
-
@data << []
|
59
|
-
@numerical << true
|
60
|
-
@headers.length - 1
|
61
|
+
@stats = {}
|
62
|
+
@data = []
|
63
|
+
@numerical = true
|
61
64
|
end
|
62
|
-
def add(field
|
63
|
-
puts "\tAdding field '#{field}'
|
65
|
+
def add(field)
|
66
|
+
puts "\tAdding field '#{field}' for property #{name}" if($debug)
|
64
67
|
if field && field =~ /\w/
|
65
|
-
@numerical
|
66
|
-
puts "\tField[#{index}]: #{field}" if($debug)
|
67
|
-
stats = @stats[index]
|
68
|
+
@numerical &&= is_number?(field)
|
68
69
|
stats[field] ||= 0
|
69
70
|
stats[field] += 1
|
70
|
-
|
71
|
-
@data[index] << field
|
71
|
+
@data << field
|
72
72
|
end
|
73
73
|
end
|
74
|
+
def length
|
75
|
+
@stats.length
|
76
|
+
end
|
77
|
+
def numerical?
|
78
|
+
@numerical
|
79
|
+
end
|
80
|
+
def diversity
|
81
|
+
100.0 * @stats.length.to_f / @data.length.to_f
|
82
|
+
end
|
83
|
+
def diverse?
|
84
|
+
@stats.length>500 || diversity > $diversity
|
85
|
+
end
|
74
86
|
def is_number?(field)
|
75
87
|
is_integer?(field) || is_float?(field)
|
76
88
|
end
|
@@ -80,36 +92,104 @@ class Stats
|
|
80
92
|
def is_float?(field)
|
81
93
|
field.to_f.to_s == field
|
82
94
|
end
|
83
|
-
def
|
84
|
-
|
95
|
+
def to_s
|
96
|
+
"#{name}[#{length}]"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class StatsManager
|
101
|
+
attr_reader :name, :headers, :stats
|
102
|
+
def initialize(name)
|
103
|
+
@name = name
|
104
|
+
@headers = []
|
105
|
+
@stats = {}
|
106
|
+
end
|
107
|
+
def time_index
|
108
|
+
@time_index ||= @headers.index('Time') || @headers.index('Timestamp')
|
109
|
+
end
|
110
|
+
def time_stats
|
111
|
+
@time_stats ||= get_stats('Time') || get_stats('Timestamp')
|
112
|
+
end
|
113
|
+
def set_headers(headers)
|
114
|
+
@headers = []
|
115
|
+
headers.each {|h| add_header(h)}
|
116
|
+
$specs && $specs.add_stats(self,headers)
|
117
|
+
end
|
118
|
+
def add_header(h)
|
119
|
+
if @headers.index(h)
|
120
|
+
puts "Stats header already exists: #{h}"
|
121
|
+
else
|
122
|
+
@headers << h
|
123
|
+
@stats[h] ||= Stats.new(h)
|
124
|
+
end
|
125
|
+
@headers.index(h)
|
126
|
+
end
|
127
|
+
def get_stats(header)
|
128
|
+
stats[header] || stats[header.downcase]
|
129
|
+
end
|
130
|
+
def add_all(fields,headers)
|
131
|
+
fields.each_with_index do |field,index|
|
132
|
+
add(field,headers[index])
|
133
|
+
end
|
134
|
+
$specs && $specs.add_fields(self,fields)
|
85
135
|
end
|
86
|
-
def
|
87
|
-
|
136
|
+
def add(field,header)
|
137
|
+
puts "\tAdding field '#{field}' for property #{header}" if($debug)
|
138
|
+
add_header(header) unless(@stats[header])
|
139
|
+
@stats[header].add(field)
|
88
140
|
end
|
89
|
-
def
|
90
|
-
@stats
|
141
|
+
def length
|
142
|
+
@stats.length
|
91
143
|
end
|
92
|
-
def
|
93
|
-
|
144
|
+
def to_s
|
145
|
+
"Stats[#{length}]: #{@stats.inspect}"
|
94
146
|
end
|
95
147
|
end
|
96
148
|
|
97
|
-
$stats = {}
|
98
|
-
|
99
149
|
module Geoptima
|
100
150
|
class StatSpec
|
101
|
-
attr_reader :header, :index, :indices, :fields, :options, :proc
|
151
|
+
attr_reader :header, :index, :indices, :fields, :options, :proc, :groups
|
102
152
|
def initialize(header,*fields,&block)
|
103
153
|
@header = header
|
104
154
|
@fields = fields
|
105
155
|
@proc = block
|
156
|
+
@groups = {}
|
106
157
|
if @fields[-1].is_a?(Hash)
|
107
158
|
@options = @fields.pop
|
108
159
|
else
|
109
160
|
@options = {}
|
110
161
|
end
|
162
|
+
if @options[:group]
|
163
|
+
case @options[:group].to_s.intern
|
164
|
+
when :months
|
165
|
+
group_by {|t| t.strftime("%Y-%m")}
|
166
|
+
when :days
|
167
|
+
group_by {|t| t.strftime("%Y-%m-%d")}
|
168
|
+
else
|
169
|
+
group_by {|t| t.strftime("%Y-%m-%d %H")}
|
170
|
+
end
|
171
|
+
end
|
111
172
|
puts "Created StatSpec: #{self}"
|
112
173
|
end
|
174
|
+
def group_by(&block)
|
175
|
+
@group = block
|
176
|
+
end
|
177
|
+
def add(stats_manager,fields)
|
178
|
+
if @group
|
179
|
+
begin
|
180
|
+
time = DateTime.parse(fields[stats_manager.time_index])
|
181
|
+
if $time_range.nil? || $time_range.include?(time)
|
182
|
+
key = @group.call(time)
|
183
|
+
ghead = "#{header} #{key}"
|
184
|
+
@groups[key] = ghead
|
185
|
+
stats_manager.add(map(fields),ghead)
|
186
|
+
end
|
187
|
+
rescue ArgumentError
|
188
|
+
puts "Error: Unable to process time field[#{time}]: #{$!}"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
stats_manager.add(map(fields),header)
|
192
|
+
end
|
113
193
|
def mk_range(val)
|
114
194
|
if val =~ /\w/
|
115
195
|
div = options[:div].to_i
|
@@ -120,7 +200,7 @@ module Geoptima
|
|
120
200
|
val
|
121
201
|
end
|
122
202
|
end
|
123
|
-
def map(values)
|
203
|
+
def map(values,filter=nil)
|
124
204
|
if @indices
|
125
205
|
puts "StatSpec[#{self}]: #{options.inspect}" if($debug)
|
126
206
|
vals = @indices.map{|i| values[i]}
|
@@ -133,12 +213,14 @@ module Geoptima
|
|
133
213
|
val
|
134
214
|
end
|
135
215
|
end
|
136
|
-
def prepare_indices(
|
137
|
-
if
|
216
|
+
def prepare_indices(stats_manager,headers)
|
217
|
+
if headers.index(header)
|
138
218
|
puts "Header '#{header}' already exists, cannot create #{self}"
|
219
|
+
@index = nil
|
220
|
+
@indices = nil
|
139
221
|
else
|
140
|
-
@index =
|
141
|
-
@indices = @fields.map{|h|
|
222
|
+
@index = stats_manager.add_header(header)
|
223
|
+
@indices = @fields.map{|h| headers.index(h) || headers.index(h.downcase) }
|
142
224
|
puts "#{self}: Header[#{@index}], Indices[#{@indices.join(',')}]" if($debug)
|
143
225
|
if @indices.compact.length < @fields.length
|
144
226
|
puts "Unable to find some headers for #{self}, ignoring stats"
|
@@ -152,35 +234,60 @@ module Geoptima
|
|
152
234
|
end
|
153
235
|
class ChartSpec
|
154
236
|
attr_reader :chart_type, :header, :options
|
155
|
-
def initialize(
|
237
|
+
def initialize(header,options={})
|
156
238
|
@header = header
|
157
|
-
@chart_type = chart_type
|
239
|
+
@chart_type = options[:chart_type] || :histogram
|
158
240
|
@options = options
|
159
241
|
end
|
160
|
-
def process(
|
161
|
-
puts "Charting #{header} using
|
162
|
-
|
163
|
-
|
164
|
-
|
242
|
+
def process(stats_manager)
|
243
|
+
puts "Charting #{header} using headers: #{stats_manager.headers.inspect}"
|
244
|
+
stat_spec = $specs.stat_specs.find{|o| o.header == header}
|
245
|
+
stats = stats_manager.get_stats(header)
|
246
|
+
grouped_stats = {}
|
247
|
+
if stat_spec
|
248
|
+
stat_spec.groups.each do |name,header|
|
249
|
+
gs = stats_manager.get_stats(header)
|
250
|
+
grouped_stats[name] = gs
|
251
|
+
stats ||= gs
|
252
|
+
end
|
253
|
+
end
|
254
|
+
unless stats
|
165
255
|
puts "Cannot find statistics for '#{header}' - ignoring chart"
|
166
256
|
return
|
167
257
|
end
|
168
|
-
puts "Charting #{header}
|
169
|
-
puts "Charting #{header} with diversity #{stats.diversity
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
258
|
+
puts "Charting #{header} with options #{options.inspect} and stats: #{stats}"
|
259
|
+
puts "Charting #{header} with diversity #{stats.diversity}"
|
260
|
+
if grouped_stats.length > 0
|
261
|
+
title = options[:title]
|
262
|
+
title ||= "#{header} Distribution"
|
263
|
+
options.merge!( :title => title, :width => 1024 )
|
264
|
+
value_map = {}
|
265
|
+
groups = grouped_stats.keys.sort
|
266
|
+
groups.each_with_index do |name,index|
|
267
|
+
gs = grouped_stats[name]
|
268
|
+
hist = gs.stats
|
269
|
+
hist.keys.each do |k|
|
270
|
+
value_map[k] ||= [].fill(0,0...groups.length)
|
271
|
+
value_map[k][index] = hist[k]
|
272
|
+
end
|
273
|
+
end
|
274
|
+
legends = value_map.keys.sort
|
275
|
+
g = Geoptima::Chart.draw_grouped_chart legends, groups, value_map, options
|
175
276
|
else
|
176
|
-
|
277
|
+
hist = stats.stats
|
278
|
+
title = options[:title]
|
279
|
+
if options[:top]
|
280
|
+
keys = hist.keys.sort{|a,b| hist[b] <=> hist[a]}[0..(options[:top].to_i)]
|
281
|
+
title ||= "#{header} Top #{options[:top]}"
|
282
|
+
else
|
283
|
+
keys = hist.keys.sort{|a,b| a.to_i <=> b.to_i}
|
284
|
+
end
|
285
|
+
values = keys.map{|k| hist[k]}
|
286
|
+
title ||= "#{header} Distribution"
|
287
|
+
options.merge!( :title => title, :width => 1024 )
|
288
|
+
g = Geoptima::Chart.send "draw_#{chart_type}_chart", stats_manager.name, keys, values, options
|
177
289
|
end
|
178
|
-
|
179
|
-
title ||= "#{header} Distribution"
|
180
|
-
options.merge!( :title => title, :width => 1024 )
|
181
|
-
legend = $merge_all ? "ALL" : stats.name
|
182
|
-
g = Geoptima::Chart.send "draw_#{chart_type}_chart", stats.name, keys, values, options
|
183
|
-
g.write("#{$export_dir}/Chart_#{stats.name}_#{header}_distribution.png")
|
290
|
+
g.write("#{$export_dir}/Chart_#{stats_manager.name}_#{header}_#{chart_type}_distribution.png")
|
184
291
|
end
|
185
292
|
def to_s
|
186
293
|
"#{chart_type.upcase}-#{header}"
|
@@ -194,126 +301,138 @@ module Geoptima
|
|
194
301
|
instance_eval(File.open(specfile).read)
|
195
302
|
end
|
196
303
|
def category_chart(header,options={})
|
197
|
-
chart(
|
304
|
+
chart(header, options.merge(:chart_type => :category))
|
198
305
|
end
|
199
306
|
def histogram_chart(header,options={})
|
200
|
-
chart(
|
307
|
+
chart(header, options.merge(:chart_type => :histgram))
|
201
308
|
end
|
202
309
|
def line_chart(header,options={})
|
203
|
-
chart(
|
310
|
+
chart(header, options.merge(:chart_type => :line))
|
204
311
|
end
|
205
|
-
def chart(
|
206
|
-
@chart_specs << ChartSpec.new(
|
312
|
+
def chart(header,options={})
|
313
|
+
@chart_specs << ChartSpec.new(header,options)
|
207
314
|
end
|
208
315
|
def stats(header,*fields,&block)
|
209
316
|
@stat_specs << StatSpec.new(header,*fields,&block)
|
210
317
|
end
|
211
|
-
def add_stats(
|
318
|
+
def add_stats(stats_manager,headers)
|
212
319
|
stat_specs.each do |stat_spec|
|
213
|
-
stat_spec.prepare_indices(
|
320
|
+
stat_spec.prepare_indices(stats_manager,headers)
|
214
321
|
end
|
215
322
|
end
|
216
|
-
def add_fields(
|
217
|
-
|
218
|
-
|
219
|
-
stat_spec.map(fields),
|
220
|
-
stat_spec.index
|
221
|
-
)
|
323
|
+
def add_fields(stats_manager,fields)
|
324
|
+
stat_specs.each do |stat_spec|
|
325
|
+
stat_spec.add(stats_manager,fields)
|
222
326
|
end
|
223
327
|
end
|
224
328
|
def to_s
|
225
|
-
"
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
$specfile && $specs = Geoptima::StatsSpecs.new($specfile)
|
231
|
-
|
232
|
-
$files.each do |file|
|
233
|
-
lines = 0
|
234
|
-
filename = File.basename(file)
|
235
|
-
(names = filename.split(/[_\.]/)).pop
|
236
|
-
name = $merged_name || names.join('_')
|
237
|
-
puts "About to read file #{file}"
|
238
|
-
File.open(file).each do |line|
|
239
|
-
lines += 1
|
240
|
-
fields=line.chomp.split(/\t/)
|
241
|
-
if $stats[file]
|
242
|
-
puts "Processing line: #{line}" if($debug)
|
243
|
-
fields.each_with_index do |field,index|
|
244
|
-
$stats[file].add(field,index)
|
245
|
-
end
|
246
|
-
$specs && $specs.add_fields($stats[file],fields)
|
247
|
-
elsif($merge_all && $stats.length>0)
|
248
|
-
file = $stats.values[0].file
|
249
|
-
else
|
250
|
-
$stats[file] = Stats.new(filename,name,fields)
|
251
|
-
$specs && $specs.add_stats($stats[file])
|
329
|
+
"Stats[#{@stat_specs.join(', ')}] AND Charts[#{@chart_specs.join(', ')}]"
|
252
330
|
end
|
253
331
|
end
|
254
332
|
end
|
255
333
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
end
|
261
|
-
else
|
262
|
-
stats.headers.each_with_index do |header,index|
|
263
|
-
puts "Charting #{header} with diversity #{stats.diversity(index)}"
|
334
|
+
def create_all(name,stats_manager)
|
335
|
+
stats_manager.headers.each do |header|
|
336
|
+
stats = stats_manager.stats[header]
|
337
|
+
puts "Charting #{header} with diversity #{stats.diversity}"
|
264
338
|
case header
|
265
339
|
when 'signal.strength'
|
266
340
|
Geoptima::Chart.draw_line_chart(
|
267
|
-
|
268
|
-
|
269
|
-
stats.data
|
341
|
+
stats_manager.name,
|
342
|
+
stats_manager.time_stats.data,
|
343
|
+
stats.data.map{|f| v=f.to_i; (v>-130 && v<0) ? v : nil},
|
270
344
|
:title => 'Signal Strength',
|
271
345
|
:maximum_value => -30,
|
272
346
|
:minimum_value => -130,
|
273
347
|
:width => 1024
|
274
|
-
).write("#{$export_dir}/Chart_#{
|
348
|
+
).write("#{$export_dir}/Chart_#{stats_manager.name}_#{header}.png")
|
275
349
|
|
276
|
-
hist = stats.stats
|
350
|
+
hist = stats.stats
|
277
351
|
keys = hist.keys.sort{|a,b| a.to_i <=> b.to_i}
|
278
352
|
values = keys.map{|k| hist[k]}
|
279
353
|
Geoptima::Chart.draw_histogram_chart(
|
280
|
-
|
354
|
+
stats_manager.name, keys, values,
|
281
355
|
:title => 'Signal Strength Distribution',
|
282
356
|
:width => 1024
|
283
|
-
).write("#{$export_dir}/Chart_#{
|
357
|
+
).write("#{$export_dir}/Chart_#{stats_manager.name}_#{header}_distribution.png")
|
284
358
|
|
285
359
|
when 'Event'
|
286
|
-
hist = stats.stats
|
360
|
+
hist = stats.stats
|
287
361
|
keys = hist.keys.sort{|a,b| a.to_i <=> b.to_i}
|
288
362
|
values = keys.map{|k| hist[k]}
|
289
363
|
Geoptima::Chart.draw_category_chart(
|
290
|
-
|
364
|
+
stats_manager.name, keys, values,
|
291
365
|
:title => "#{header} Distribution",
|
292
366
|
:width => 1024
|
293
|
-
).write("#{$export_dir}/Chart_#{
|
367
|
+
).write("#{$export_dir}/Chart_#{stats_manager.name}_#{header}_distribution.png")
|
294
368
|
|
295
369
|
else
|
296
|
-
if stats.diverse?
|
370
|
+
if stats.diverse?
|
297
371
|
puts "Ignoring high diversity field #{header}"
|
298
372
|
else
|
299
|
-
puts "Charting field: #{header} with length #{stats.length
|
300
|
-
hist = stats.stats
|
373
|
+
puts "Charting field: #{header} with length #{stats.length} and diversity #{stats.diversity}"
|
374
|
+
hist = stats.stats
|
301
375
|
keys = hist.keys.sort{|a,b| a.to_i <=> b.to_i}
|
302
376
|
values = keys.map{|k| hist[k]}
|
303
|
-
args = [
|
377
|
+
args = [stats_manager.name, keys, values, {
|
304
378
|
:title => "#{header} Distribution",
|
305
379
|
:width => 1024}]
|
306
|
-
g = (stats.length
|
380
|
+
g = (stats.length > 50) ?
|
307
381
|
Geoptima::Chart.draw_line_chart(*args) :
|
308
|
-
(stats.length
|
382
|
+
(stats.length > 10 || stats.numerical?) ?
|
309
383
|
Geoptima::Chart.draw_histogram_chart(*args) :
|
310
|
-
(stats.length
|
384
|
+
(stats.length > 1) ?
|
311
385
|
Geoptima::Chart.draw_category_chart(*args) :
|
312
386
|
nil
|
313
|
-
g && g.write("#{$export_dir}/Chart_#{
|
387
|
+
g && g.write("#{$export_dir}/Chart_#{stats_manager.name}_#{header}_distribution.png")
|
314
388
|
end
|
315
389
|
end
|
316
390
|
end
|
391
|
+
end
|
392
|
+
|
393
|
+
#
|
394
|
+
# Now run the actual program, reading the specifation file and then the CSV files
|
395
|
+
#
|
396
|
+
|
397
|
+
$stats_managers = {}
|
398
|
+
|
399
|
+
$specfile && $specs = Geoptima::StatsSpecs.new($specfile)
|
400
|
+
|
401
|
+
$files.each do |file|
|
402
|
+
lines = 0
|
403
|
+
headers = nil
|
404
|
+
filename = File.basename(file)
|
405
|
+
(names = filename.split(/[_\.]/)).pop
|
406
|
+
name = $merge_all ? ($merged_name || 'All') : names.join('_')
|
407
|
+
$stats_managers[name] ||= StatsManager.new(name)
|
408
|
+
puts "About to read file #{file}"
|
409
|
+
File.open(file).each do |line|
|
410
|
+
lines += 1
|
411
|
+
fields=line.chomp.split(/\t/)
|
412
|
+
if headers
|
413
|
+
puts "Processing line: #{line}" if($debug)
|
414
|
+
$stats_managers[name].add_all(fields,headers)
|
415
|
+
else
|
416
|
+
headers = fields
|
417
|
+
if headers.length<2
|
418
|
+
puts "Too few headers, rejecting #{file}"
|
419
|
+
break
|
420
|
+
end
|
421
|
+
$stats_managers[name].set_headers(headers)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
# Finally output all charts specified
|
427
|
+
|
428
|
+
$stats_managers.each do |name,stats_manager|
|
429
|
+
if $specs
|
430
|
+
$specs.chart_specs.each do |chart_spec|
|
431
|
+
chart_spec.process(stats_manager)
|
432
|
+
end
|
433
|
+
end
|
434
|
+
if $create_all
|
435
|
+
create_all name, stats_manager
|
317
436
|
end
|
318
437
|
end
|
319
438
|
|
data/bin/show_geoptima
CHANGED
data/examples/csv_chart.rb
CHANGED
@@ -8,8 +8,10 @@ require 'geoptima/chart'
|
|
8
8
|
require 'geoptima/version'
|
9
9
|
require 'geoptima/options'
|
10
10
|
require 'fileutils'
|
11
|
+
require 'geoptima/daterange'
|
11
12
|
|
12
|
-
Geoptima::assert_version("0.0
|
13
|
+
Geoptima::assert_version("0.1.0")
|
14
|
+
Geoptima::Chart.available? || puts("No charting libraries available") || exit(-1)
|
13
15
|
|
14
16
|
$export_dir = '.'
|
15
17
|
$diversity = 40.0
|
@@ -17,14 +19,22 @@ $diversity = 40.0
|
|
17
19
|
$files = Geoptima::Options.process_args do |option|
|
18
20
|
option.m {$merge_all = true}
|
19
21
|
option.a {$create_all = true}
|
22
|
+
option.t {$time_split = true}
|
20
23
|
option.D {$export_dir = ARGV.shift}
|
21
24
|
option.N {$merged_name = ARGV.shift}
|
22
25
|
option.S {$specfile = ARGV.shift}
|
23
26
|
option.P {$diversity = ARGV.shift.to_f}
|
27
|
+
option.T do
|
28
|
+
$time_range = Geoptima::DateRange.new(*(ARGV.shift.split(/[\,]+/).map do |t|
|
29
|
+
DateTime.parse t
|
30
|
+
end))
|
31
|
+
end
|
24
32
|
end
|
25
33
|
|
26
34
|
FileUtils.mkdir_p $export_dir
|
27
35
|
|
36
|
+
$create_all = true unless($specfile)
|
37
|
+
$merge_all = true if($time_split)
|
28
38
|
$help = true unless($files.length>0)
|
29
39
|
if $help
|
30
40
|
puts <<EOHELP
|
@@ -33,44 +43,46 @@ Usage: csv_chart <-dham> <-S specfile> <-N name> <-D dir> files...
|
|
33
43
|
-h print this help #{cw $help}
|
34
44
|
-a automatically create charts for all properties #{cw $create_all}
|
35
45
|
-m merge all files into single stats #{cw $merge_all}
|
46
|
+
-t merge and split by time (days) #{cw $time_split}
|
36
47
|
-N use specified name for merged dataset: #{$merged_name}
|
37
48
|
-D export charts to specified directory: #{$export_dir}
|
38
49
|
-S use chart specification in specified file: #{$specfile}
|
39
50
|
-P diversity threshold in percentage for automatic reports: #{$diversity}
|
51
|
+
-T set time-range filter: #{$time_range}
|
40
52
|
Files to import: #{$files.join(', ')}
|
41
53
|
EOHELP
|
42
54
|
exit
|
43
55
|
end
|
44
56
|
|
45
57
|
class Stats
|
46
|
-
attr_reader :
|
47
|
-
def initialize(
|
48
|
-
@file = file
|
58
|
+
attr_reader :name, :stats, :data, :numerical
|
59
|
+
def initialize(name)
|
49
60
|
@name = name
|
50
|
-
@
|
51
|
-
@
|
52
|
-
@
|
53
|
-
@numerical = fields.map{|h| true}
|
54
|
-
end
|
55
|
-
def add_header(h)
|
56
|
-
@headers << h
|
57
|
-
@stats << {}
|
58
|
-
@data << []
|
59
|
-
@numerical << true
|
60
|
-
@headers.length - 1
|
61
|
+
@stats = {}
|
62
|
+
@data = []
|
63
|
+
@numerical = true
|
61
64
|
end
|
62
|
-
def add(field
|
63
|
-
puts "\tAdding field '#{field}'
|
65
|
+
def add(field)
|
66
|
+
puts "\tAdding field '#{field}' for property #{name}" if($debug)
|
64
67
|
if field && field =~ /\w/
|
65
|
-
@numerical
|
66
|
-
puts "\tField[#{index}]: #{field}" if($debug)
|
67
|
-
stats = @stats[index]
|
68
|
+
@numerical &&= is_number?(field)
|
68
69
|
stats[field] ||= 0
|
69
70
|
stats[field] += 1
|
70
|
-
|
71
|
-
@data[index] << field
|
71
|
+
@data << field
|
72
72
|
end
|
73
73
|
end
|
74
|
+
def length
|
75
|
+
@stats.length
|
76
|
+
end
|
77
|
+
def numerical?
|
78
|
+
@numerical
|
79
|
+
end
|
80
|
+
def diversity
|
81
|
+
100.0 * @stats.length.to_f / @data.length.to_f
|
82
|
+
end
|
83
|
+
def diverse?
|
84
|
+
@stats.length>500 || diversity > $diversity
|
85
|
+
end
|
74
86
|
def is_number?(field)
|
75
87
|
is_integer?(field) || is_float?(field)
|
76
88
|
end
|
@@ -80,36 +92,104 @@ class Stats
|
|
80
92
|
def is_float?(field)
|
81
93
|
field.to_f.to_s == field
|
82
94
|
end
|
83
|
-
def
|
84
|
-
|
95
|
+
def to_s
|
96
|
+
"#{name}[#{length}]"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class StatsManager
|
101
|
+
attr_reader :name, :headers, :stats
|
102
|
+
def initialize(name)
|
103
|
+
@name = name
|
104
|
+
@headers = []
|
105
|
+
@stats = {}
|
106
|
+
end
|
107
|
+
def time_index
|
108
|
+
@time_index ||= @headers.index('Time') || @headers.index('Timestamp')
|
109
|
+
end
|
110
|
+
def time_stats
|
111
|
+
@time_stats ||= get_stats('Time') || get_stats('Timestamp')
|
112
|
+
end
|
113
|
+
def set_headers(headers)
|
114
|
+
@headers = []
|
115
|
+
headers.each {|h| add_header(h)}
|
116
|
+
$specs && $specs.add_stats(self,headers)
|
117
|
+
end
|
118
|
+
def add_header(h)
|
119
|
+
if @headers.index(h)
|
120
|
+
puts "Stats header already exists: #{h}"
|
121
|
+
else
|
122
|
+
@headers << h
|
123
|
+
@stats[h] ||= Stats.new(h)
|
124
|
+
end
|
125
|
+
@headers.index(h)
|
126
|
+
end
|
127
|
+
def get_stats(header)
|
128
|
+
stats[header] || stats[header.downcase]
|
129
|
+
end
|
130
|
+
def add_all(fields,headers)
|
131
|
+
fields.each_with_index do |field,index|
|
132
|
+
add(field,headers[index])
|
133
|
+
end
|
134
|
+
$specs && $specs.add_fields(self,fields)
|
85
135
|
end
|
86
|
-
def
|
87
|
-
|
136
|
+
def add(field,header)
|
137
|
+
puts "\tAdding field '#{field}' for property #{header}" if($debug)
|
138
|
+
add_header(header) unless(@stats[header])
|
139
|
+
@stats[header].add(field)
|
88
140
|
end
|
89
|
-
def
|
90
|
-
@stats
|
141
|
+
def length
|
142
|
+
@stats.length
|
91
143
|
end
|
92
|
-
def
|
93
|
-
|
144
|
+
def to_s
|
145
|
+
"Stats[#{length}]: #{@stats.inspect}"
|
94
146
|
end
|
95
147
|
end
|
96
148
|
|
97
|
-
$stats = {}
|
98
|
-
|
99
149
|
module Geoptima
|
100
150
|
class StatSpec
|
101
|
-
attr_reader :header, :index, :indices, :fields, :options, :proc
|
151
|
+
attr_reader :header, :index, :indices, :fields, :options, :proc, :groups
|
102
152
|
def initialize(header,*fields,&block)
|
103
153
|
@header = header
|
104
154
|
@fields = fields
|
105
155
|
@proc = block
|
156
|
+
@groups = {}
|
106
157
|
if @fields[-1].is_a?(Hash)
|
107
158
|
@options = @fields.pop
|
108
159
|
else
|
109
160
|
@options = {}
|
110
161
|
end
|
162
|
+
if @options[:group]
|
163
|
+
case @options[:group].to_s.intern
|
164
|
+
when :months
|
165
|
+
group_by {|t| t.strftime("%Y-%m")}
|
166
|
+
when :days
|
167
|
+
group_by {|t| t.strftime("%Y-%m-%d")}
|
168
|
+
else
|
169
|
+
group_by {|t| t.strftime("%Y-%m-%d %H")}
|
170
|
+
end
|
171
|
+
end
|
111
172
|
puts "Created StatSpec: #{self}"
|
112
173
|
end
|
174
|
+
def group_by(&block)
|
175
|
+
@group = block
|
176
|
+
end
|
177
|
+
def add(stats_manager,fields)
|
178
|
+
if @group
|
179
|
+
begin
|
180
|
+
time = DateTime.parse(fields[stats_manager.time_index])
|
181
|
+
if $time_range.nil? || $time_range.include?(time)
|
182
|
+
key = @group.call(time)
|
183
|
+
ghead = "#{header} #{key}"
|
184
|
+
@groups[key] = ghead
|
185
|
+
stats_manager.add(map(fields),ghead)
|
186
|
+
end
|
187
|
+
rescue ArgumentError
|
188
|
+
puts "Error: Unable to process time field[#{time}]: #{$!}"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
stats_manager.add(map(fields),header)
|
192
|
+
end
|
113
193
|
def mk_range(val)
|
114
194
|
if val =~ /\w/
|
115
195
|
div = options[:div].to_i
|
@@ -120,7 +200,7 @@ module Geoptima
|
|
120
200
|
val
|
121
201
|
end
|
122
202
|
end
|
123
|
-
def map(values)
|
203
|
+
def map(values,filter=nil)
|
124
204
|
if @indices
|
125
205
|
puts "StatSpec[#{self}]: #{options.inspect}" if($debug)
|
126
206
|
vals = @indices.map{|i| values[i]}
|
@@ -133,12 +213,14 @@ module Geoptima
|
|
133
213
|
val
|
134
214
|
end
|
135
215
|
end
|
136
|
-
def prepare_indices(
|
137
|
-
if
|
216
|
+
def prepare_indices(stats_manager,headers)
|
217
|
+
if headers.index(header)
|
138
218
|
puts "Header '#{header}' already exists, cannot create #{self}"
|
219
|
+
@index = nil
|
220
|
+
@indices = nil
|
139
221
|
else
|
140
|
-
@index =
|
141
|
-
@indices = @fields.map{|h|
|
222
|
+
@index = stats_manager.add_header(header)
|
223
|
+
@indices = @fields.map{|h| headers.index(h) || headers.index(h.downcase) }
|
142
224
|
puts "#{self}: Header[#{@index}], Indices[#{@indices.join(',')}]" if($debug)
|
143
225
|
if @indices.compact.length < @fields.length
|
144
226
|
puts "Unable to find some headers for #{self}, ignoring stats"
|
@@ -152,35 +234,60 @@ module Geoptima
|
|
152
234
|
end
|
153
235
|
class ChartSpec
|
154
236
|
attr_reader :chart_type, :header, :options
|
155
|
-
def initialize(
|
237
|
+
def initialize(header,options={})
|
156
238
|
@header = header
|
157
|
-
@chart_type = chart_type
|
239
|
+
@chart_type = options[:chart_type] || :histogram
|
158
240
|
@options = options
|
159
241
|
end
|
160
|
-
def process(
|
161
|
-
puts "Charting #{header} using
|
162
|
-
|
163
|
-
|
164
|
-
|
242
|
+
def process(stats_manager)
|
243
|
+
puts "Charting #{header} using headers: #{stats_manager.headers.inspect}"
|
244
|
+
stat_spec = $specs.stat_specs.find{|o| o.header == header}
|
245
|
+
stats = stats_manager.get_stats(header)
|
246
|
+
grouped_stats = {}
|
247
|
+
if stat_spec
|
248
|
+
stat_spec.groups.each do |name,header|
|
249
|
+
gs = stats_manager.get_stats(header)
|
250
|
+
grouped_stats[name] = gs
|
251
|
+
stats ||= gs
|
252
|
+
end
|
253
|
+
end
|
254
|
+
unless stats
|
165
255
|
puts "Cannot find statistics for '#{header}' - ignoring chart"
|
166
256
|
return
|
167
257
|
end
|
168
|
-
puts "Charting #{header}
|
169
|
-
puts "Charting #{header} with diversity #{stats.diversity
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
258
|
+
puts "Charting #{header} with options #{options.inspect} and stats: #{stats}"
|
259
|
+
puts "Charting #{header} with diversity #{stats.diversity}"
|
260
|
+
if grouped_stats.length > 0
|
261
|
+
title = options[:title]
|
262
|
+
title ||= "#{header} Distribution"
|
263
|
+
options.merge!( :title => title, :width => 1024 )
|
264
|
+
value_map = {}
|
265
|
+
groups = grouped_stats.keys.sort
|
266
|
+
groups.each_with_index do |name,index|
|
267
|
+
gs = grouped_stats[name]
|
268
|
+
hist = gs.stats
|
269
|
+
hist.keys.each do |k|
|
270
|
+
value_map[k] ||= [].fill(0,0...groups.length)
|
271
|
+
value_map[k][index] = hist[k]
|
272
|
+
end
|
273
|
+
end
|
274
|
+
legends = value_map.keys.sort
|
275
|
+
g = Geoptima::Chart.draw_grouped_chart legends, groups, value_map, options
|
175
276
|
else
|
176
|
-
|
277
|
+
hist = stats.stats
|
278
|
+
title = options[:title]
|
279
|
+
if options[:top]
|
280
|
+
keys = hist.keys.sort{|a,b| hist[b] <=> hist[a]}[0..(options[:top].to_i)]
|
281
|
+
title ||= "#{header} Top #{options[:top]}"
|
282
|
+
else
|
283
|
+
keys = hist.keys.sort{|a,b| a.to_i <=> b.to_i}
|
284
|
+
end
|
285
|
+
values = keys.map{|k| hist[k]}
|
286
|
+
title ||= "#{header} Distribution"
|
287
|
+
options.merge!( :title => title, :width => 1024 )
|
288
|
+
g = Geoptima::Chart.send "draw_#{chart_type}_chart", stats_manager.name, keys, values, options
|
177
289
|
end
|
178
|
-
|
179
|
-
title ||= "#{header} Distribution"
|
180
|
-
options.merge!( :title => title, :width => 1024 )
|
181
|
-
legend = $merge_all ? "ALL" : stats.name
|
182
|
-
g = Geoptima::Chart.send "draw_#{chart_type}_chart", stats.name, keys, values, options
|
183
|
-
g.write("#{$export_dir}/Chart_#{stats.name}_#{header}_distribution.png")
|
290
|
+
g.write("#{$export_dir}/Chart_#{stats_manager.name}_#{header}_#{chart_type}_distribution.png")
|
184
291
|
end
|
185
292
|
def to_s
|
186
293
|
"#{chart_type.upcase}-#{header}"
|
@@ -194,126 +301,138 @@ module Geoptima
|
|
194
301
|
instance_eval(File.open(specfile).read)
|
195
302
|
end
|
196
303
|
def category_chart(header,options={})
|
197
|
-
chart(
|
304
|
+
chart(header, options.merge(:chart_type => :category))
|
198
305
|
end
|
199
306
|
def histogram_chart(header,options={})
|
200
|
-
chart(
|
307
|
+
chart(header, options.merge(:chart_type => :histgram))
|
201
308
|
end
|
202
309
|
def line_chart(header,options={})
|
203
|
-
chart(
|
310
|
+
chart(header, options.merge(:chart_type => :line))
|
204
311
|
end
|
205
|
-
def chart(
|
206
|
-
@chart_specs << ChartSpec.new(
|
312
|
+
def chart(header,options={})
|
313
|
+
@chart_specs << ChartSpec.new(header,options)
|
207
314
|
end
|
208
315
|
def stats(header,*fields,&block)
|
209
316
|
@stat_specs << StatSpec.new(header,*fields,&block)
|
210
317
|
end
|
211
|
-
def add_stats(
|
318
|
+
def add_stats(stats_manager,headers)
|
212
319
|
stat_specs.each do |stat_spec|
|
213
|
-
stat_spec.prepare_indices(
|
320
|
+
stat_spec.prepare_indices(stats_manager,headers)
|
214
321
|
end
|
215
322
|
end
|
216
|
-
def add_fields(
|
217
|
-
|
218
|
-
|
219
|
-
stat_spec.map(fields),
|
220
|
-
stat_spec.index
|
221
|
-
)
|
323
|
+
def add_fields(stats_manager,fields)
|
324
|
+
stat_specs.each do |stat_spec|
|
325
|
+
stat_spec.add(stats_manager,fields)
|
222
326
|
end
|
223
327
|
end
|
224
328
|
def to_s
|
225
|
-
"
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
$specfile && $specs = Geoptima::StatsSpecs.new($specfile)
|
231
|
-
|
232
|
-
$files.each do |file|
|
233
|
-
lines = 0
|
234
|
-
filename = File.basename(file)
|
235
|
-
(names = filename.split(/[_\.]/)).pop
|
236
|
-
name = $merged_name || names.join('_')
|
237
|
-
puts "About to read file #{file}"
|
238
|
-
File.open(file).each do |line|
|
239
|
-
lines += 1
|
240
|
-
fields=line.chomp.split(/\t/)
|
241
|
-
if $stats[file]
|
242
|
-
puts "Processing line: #{line}" if($debug)
|
243
|
-
fields.each_with_index do |field,index|
|
244
|
-
$stats[file].add(field,index)
|
245
|
-
end
|
246
|
-
$specs && $specs.add_fields($stats[file],fields)
|
247
|
-
elsif($merge_all && $stats.length>0)
|
248
|
-
file = $stats.values[0].file
|
249
|
-
else
|
250
|
-
$stats[file] = Stats.new(filename,name,fields)
|
251
|
-
$specs && $specs.add_stats($stats[file])
|
329
|
+
"Stats[#{@stat_specs.join(', ')}] AND Charts[#{@chart_specs.join(', ')}]"
|
252
330
|
end
|
253
331
|
end
|
254
332
|
end
|
255
333
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
end
|
261
|
-
else
|
262
|
-
stats.headers.each_with_index do |header,index|
|
263
|
-
puts "Charting #{header} with diversity #{stats.diversity(index)}"
|
334
|
+
def create_all(name,stats_manager)
|
335
|
+
stats_manager.headers.each do |header|
|
336
|
+
stats = stats_manager.stats[header]
|
337
|
+
puts "Charting #{header} with diversity #{stats.diversity}"
|
264
338
|
case header
|
265
339
|
when 'signal.strength'
|
266
340
|
Geoptima::Chart.draw_line_chart(
|
267
|
-
|
268
|
-
|
269
|
-
stats.data
|
341
|
+
stats_manager.name,
|
342
|
+
stats_manager.time_stats.data,
|
343
|
+
stats.data.map{|f| v=f.to_i; (v>-130 && v<0) ? v : nil},
|
270
344
|
:title => 'Signal Strength',
|
271
345
|
:maximum_value => -30,
|
272
346
|
:minimum_value => -130,
|
273
347
|
:width => 1024
|
274
|
-
).write("#{$export_dir}/Chart_#{
|
348
|
+
).write("#{$export_dir}/Chart_#{stats_manager.name}_#{header}.png")
|
275
349
|
|
276
|
-
hist = stats.stats
|
350
|
+
hist = stats.stats
|
277
351
|
keys = hist.keys.sort{|a,b| a.to_i <=> b.to_i}
|
278
352
|
values = keys.map{|k| hist[k]}
|
279
353
|
Geoptima::Chart.draw_histogram_chart(
|
280
|
-
|
354
|
+
stats_manager.name, keys, values,
|
281
355
|
:title => 'Signal Strength Distribution',
|
282
356
|
:width => 1024
|
283
|
-
).write("#{$export_dir}/Chart_#{
|
357
|
+
).write("#{$export_dir}/Chart_#{stats_manager.name}_#{header}_distribution.png")
|
284
358
|
|
285
359
|
when 'Event'
|
286
|
-
hist = stats.stats
|
360
|
+
hist = stats.stats
|
287
361
|
keys = hist.keys.sort{|a,b| a.to_i <=> b.to_i}
|
288
362
|
values = keys.map{|k| hist[k]}
|
289
363
|
Geoptima::Chart.draw_category_chart(
|
290
|
-
|
364
|
+
stats_manager.name, keys, values,
|
291
365
|
:title => "#{header} Distribution",
|
292
366
|
:width => 1024
|
293
|
-
).write("#{$export_dir}/Chart_#{
|
367
|
+
).write("#{$export_dir}/Chart_#{stats_manager.name}_#{header}_distribution.png")
|
294
368
|
|
295
369
|
else
|
296
|
-
if stats.diverse?
|
370
|
+
if stats.diverse?
|
297
371
|
puts "Ignoring high diversity field #{header}"
|
298
372
|
else
|
299
|
-
puts "Charting field: #{header} with length #{stats.length
|
300
|
-
hist = stats.stats
|
373
|
+
puts "Charting field: #{header} with length #{stats.length} and diversity #{stats.diversity}"
|
374
|
+
hist = stats.stats
|
301
375
|
keys = hist.keys.sort{|a,b| a.to_i <=> b.to_i}
|
302
376
|
values = keys.map{|k| hist[k]}
|
303
|
-
args = [
|
377
|
+
args = [stats_manager.name, keys, values, {
|
304
378
|
:title => "#{header} Distribution",
|
305
379
|
:width => 1024}]
|
306
|
-
g = (stats.length
|
380
|
+
g = (stats.length > 50) ?
|
307
381
|
Geoptima::Chart.draw_line_chart(*args) :
|
308
|
-
(stats.length
|
382
|
+
(stats.length > 10 || stats.numerical?) ?
|
309
383
|
Geoptima::Chart.draw_histogram_chart(*args) :
|
310
|
-
(stats.length
|
384
|
+
(stats.length > 1) ?
|
311
385
|
Geoptima::Chart.draw_category_chart(*args) :
|
312
386
|
nil
|
313
|
-
g && g.write("#{$export_dir}/Chart_#{
|
387
|
+
g && g.write("#{$export_dir}/Chart_#{stats_manager.name}_#{header}_distribution.png")
|
314
388
|
end
|
315
389
|
end
|
316
390
|
end
|
391
|
+
end
|
392
|
+
|
393
|
+
#
|
394
|
+
# Now run the actual program, reading the specifation file and then the CSV files
|
395
|
+
#
|
396
|
+
|
397
|
+
$stats_managers = {}
|
398
|
+
|
399
|
+
$specfile && $specs = Geoptima::StatsSpecs.new($specfile)
|
400
|
+
|
401
|
+
$files.each do |file|
|
402
|
+
lines = 0
|
403
|
+
headers = nil
|
404
|
+
filename = File.basename(file)
|
405
|
+
(names = filename.split(/[_\.]/)).pop
|
406
|
+
name = $merge_all ? ($merged_name || 'All') : names.join('_')
|
407
|
+
$stats_managers[name] ||= StatsManager.new(name)
|
408
|
+
puts "About to read file #{file}"
|
409
|
+
File.open(file).each do |line|
|
410
|
+
lines += 1
|
411
|
+
fields=line.chomp.split(/\t/)
|
412
|
+
if headers
|
413
|
+
puts "Processing line: #{line}" if($debug)
|
414
|
+
$stats_managers[name].add_all(fields,headers)
|
415
|
+
else
|
416
|
+
headers = fields
|
417
|
+
if headers.length<2
|
418
|
+
puts "Too few headers, rejecting #{file}"
|
419
|
+
break
|
420
|
+
end
|
421
|
+
$stats_managers[name].set_headers(headers)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
# Finally output all charts specified
|
427
|
+
|
428
|
+
$stats_managers.each do |name,stats_manager|
|
429
|
+
if $specs
|
430
|
+
$specs.chart_specs.each do |chart_spec|
|
431
|
+
chart_spec.process(stats_manager)
|
432
|
+
end
|
433
|
+
end
|
434
|
+
if $create_all
|
435
|
+
create_all name, stats_manager
|
317
436
|
end
|
318
437
|
end
|
319
438
|
|
data/examples/show_geoptima.rb
CHANGED
data/lib/geoptima/chart.rb
CHANGED
@@ -20,6 +20,12 @@ end
|
|
20
20
|
|
21
21
|
module Geoptima
|
22
22
|
class Chart
|
23
|
+
def self.libs
|
24
|
+
$chart_libs
|
25
|
+
end
|
26
|
+
def self.available?
|
27
|
+
$chart_libs.length>0
|
28
|
+
end
|
23
29
|
DEFAULT_OPTIONS = {:show_points => true, :show_lines => true, :title => nil, :width => 800, :margins => 20, :font_size => 14}
|
24
30
|
attr_reader :chart_type
|
25
31
|
attr_accessor :chart, :data, :options
|
@@ -55,6 +61,18 @@ module Geoptima
|
|
55
61
|
options[:filename] && g.write(options[:filename])
|
56
62
|
g
|
57
63
|
end
|
64
|
+
def self.draw_grouped_chart(legends,keys,values,options={})
|
65
|
+
puts "Creating a chart with legends #{legends.inspect} for #{keys.length} keys"
|
66
|
+
chart_type = (options[:chart_type]==:line) ? :line : :bar
|
67
|
+
g = make_chart(chart_type, options)
|
68
|
+
legends.each do |legend|
|
69
|
+
g.data(legend, values[legend])
|
70
|
+
end
|
71
|
+
g.minimum_value = 0
|
72
|
+
g.labels = keys.inject({}){|a,v| a[a.length] = v;a}
|
73
|
+
options[:filename] && g.write(options[:filename])
|
74
|
+
g
|
75
|
+
end
|
58
76
|
def self.draw_category_chart(legend,keys,values,options={})
|
59
77
|
puts "Creating category chart with keys: #{keys.join(',')}"
|
60
78
|
puts "Creating category chart with values: #{values.join(',')}"
|
data/lib/geoptima/data.rb
CHANGED
@@ -2,21 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'multi_json'
|
5
|
-
require '
|
6
|
-
if $use_dateperformance
|
7
|
-
begin
|
8
|
-
require 'date/performance'
|
9
|
-
require 'date/memoize'
|
10
|
-
class DateTime
|
11
|
-
def >(other) ; self - other > 0 ; end
|
12
|
-
def <(other) ; self - other < 0 ; end
|
13
|
-
def >=(other); self - other >= 0; end
|
14
|
-
def <=(other); self - other <= 0; end
|
15
|
-
end
|
16
|
-
rescue LoadError
|
17
|
-
puts "No date-performance gem installed, some features will run slower"
|
18
|
-
end
|
19
|
-
end
|
5
|
+
require 'geoptima/daterange'
|
20
6
|
|
21
7
|
#
|
22
8
|
# The Geoptima Module provides support for the Geoptima Client JSON file format
|
@@ -43,26 +29,6 @@ module Geoptima
|
|
43
29
|
end
|
44
30
|
end
|
45
31
|
|
46
|
-
class DateRange
|
47
|
-
attr_reader :min, :max, :range
|
48
|
-
def initialize(min,max)
|
49
|
-
@min = min
|
50
|
-
@max = max
|
51
|
-
@range = Range.new(min,max)
|
52
|
-
end
|
53
|
-
if ENV['RUBY_VERSION'] =~ /1\.8/
|
54
|
-
puts "Defining Range.include? to wrap for 1.8"
|
55
|
-
def include?(time)
|
56
|
-
@range.include?(time)
|
57
|
-
end
|
58
|
-
else
|
59
|
-
puts "Defining Range.include? to perform inequality tests for 1.9"
|
60
|
-
def include?(time)
|
61
|
-
(time >= min) && (time <= @max)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
32
|
# The Geoptima::Event class represents and individual record or event
|
67
33
|
class Event
|
68
34
|
KNOWN_HEADERS={
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
if $use_dateperformance
|
5
|
+
begin
|
6
|
+
require 'date/performance'
|
7
|
+
require 'date/memoize'
|
8
|
+
class DateTime
|
9
|
+
def >(other) ; self - other > 0 ; end
|
10
|
+
def <(other) ; self - other < 0 ; end
|
11
|
+
def >=(other); self - other >= 0; end
|
12
|
+
def <=(other); self - other <= 0; end
|
13
|
+
end
|
14
|
+
rescue LoadError
|
15
|
+
puts "No date-performance gem installed, some features will run slower"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module Geoptima
|
20
|
+
class DateRange
|
21
|
+
attr_reader :min, :max, :range
|
22
|
+
def initialize(min,max)
|
23
|
+
@min = min
|
24
|
+
@max = max
|
25
|
+
@range = Range.new(min,max)
|
26
|
+
end
|
27
|
+
if ENV['RUBY_VERSION'] =~ /1\.8/
|
28
|
+
puts "Defining Range.include? to wrap for 1.8"
|
29
|
+
def include?(time)
|
30
|
+
@range.include?(time)
|
31
|
+
end
|
32
|
+
else
|
33
|
+
puts "Defining Range.include? to perform inequality tests for 1.9"
|
34
|
+
def include?(time)
|
35
|
+
(time >= min) && (time <= @max)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
data/lib/geoptima/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: geoptima
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.9
|
10
|
+
version: 0.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Craig Taverner
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-03-
|
18
|
+
date: 2012-03-22 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: multi_json
|
@@ -78,6 +78,7 @@ files:
|
|
78
78
|
- lib/geoptima/data.rb
|
79
79
|
- lib/geoptima/chart.rb
|
80
80
|
- lib/geoptima/options.rb
|
81
|
+
- lib/geoptima/daterange.rb
|
81
82
|
- lib/geoptima.rb
|
82
83
|
- examples/show_geoptima_sos.rb
|
83
84
|
- examples/show_geoptima.rb
|