geoptima 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/bin/csv_chart ADDED
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # useful if being run inside a source code checkout
4
+ $: << 'lib'
5
+ $: << '../lib'
6
+
7
+ require 'geoptima/chart'
8
+ require 'geoptima/options'
9
+
10
+ $files = Geoptima::Options.process_args
11
+
12
+ class Stats
13
+ attr_reader :file, :imei, :headers, :stats, :data
14
+ def initialize(file,imei,fields)
15
+ @file = file
16
+ @imei = imei
17
+ @headers = fields
18
+ @stats = fields.map{|h| {}}
19
+ @data = fields.map{|h| []}
20
+ @numerical = fields.map{|h| true}
21
+ end
22
+ def add(field,index)
23
+ if field =~ /\w/
24
+ @numerical[index] &&= is_number?(field)
25
+ puts "\tField[#{index}]: #{field}" if($debug)
26
+ stats = @stats[index]
27
+ stats[field] ||= 0
28
+ stats[field] += 1
29
+ puts "\tField[#{index}]: #{field} => #{stats[field]}" if($debug)
30
+ @data[index] << field
31
+ end
32
+ end
33
+ def is_number?(field)
34
+ is_integer?(field) || is_float?(field)
35
+ end
36
+ def is_integer?(field)
37
+ field.to_i.to_s == field
38
+ end
39
+ def is_float?(field)
40
+ field.to_f.to_s == field
41
+ end
42
+ def length(index)
43
+ @stats[index].length
44
+ end
45
+ def diversity(index)
46
+ 100.0 * @stats[index].length.to_f / @data[index].length.to_f
47
+ end
48
+ def diverse?(index)
49
+ @stats[index].length>500 || diversity(index) > 40.0
50
+ end
51
+ def numerical?(index)
52
+ @numerical[index]
53
+ end
54
+ end
55
+
56
+ $stats = {}
57
+
58
+ $files.each do |file|
59
+ lines = 0
60
+ imei = file.split(/[_\.]/)[0]
61
+ File.open(file).each do |line|
62
+ lines += 1
63
+ fields=line.chomp.split(/\t/)
64
+ if $stats[file]
65
+ puts "Processing line: #{line}" if($debug)
66
+ fields.each_with_index do |field,index|
67
+ $stats[file].add(field,index)
68
+ end
69
+ else
70
+ $stats[file] = Stats.new(file,imei,fields)
71
+ end
72
+ end
73
+ end
74
+
75
+ $stats.each do |file,stats|
76
+ stats.headers.each_with_index do |header,index|
77
+ puts "Charting #{header} with diversity #{stats.diversity(index)}"
78
+ case header
79
+ when 'signal.strength'
80
+ Geoptima::Chart.draw_line_chart(
81
+ stats.imei,
82
+ stats.data[0],
83
+ stats.data[index].map{|f| v=f.to_i; (v>-130 && v<0) ? v : nil},
84
+ :title => 'Signal Strength',
85
+ :maximum_value => -30,
86
+ :minimum_value => -130,
87
+ :width => 1024
88
+ ).write("Chart_#{stats.imei}_#{header}.png")
89
+
90
+ hist = stats.stats[index]
91
+ keys = hist.keys.sort{|a,b| a.to_i <=> b.to_i}
92
+ values = keys.map{|k| hist[k]}
93
+ Geoptima::Chart.draw_histogram_chart(
94
+ stats.imei, keys, values,
95
+ :title => 'Signal Strength Distribution',
96
+ :width => 1024
97
+ ).write("Chart_#{stats.imei}_#{header}_distribution.png")
98
+
99
+ when 'Event'
100
+ hist = stats.stats[index]
101
+ keys = hist.keys.sort{|a,b| a.to_i <=> b.to_i}
102
+ values = keys.map{|k| hist[k]}
103
+ Geoptima::Chart.draw_category_chart(
104
+ stats.imei, keys, values,
105
+ :title => "#{header} Distribution",
106
+ :width => 1024
107
+ ).write("Chart_#{stats.imei}_#{header}_distribution.png")
108
+
109
+ else
110
+ if stats.diverse?(index)
111
+ puts "Ingnoring high diversity field #{header}"
112
+ else
113
+ puts "Charting field: #{header} with length #{stats.length(index)} and diversity #{stats.diversity(index)}"
114
+ hist = stats.stats[index]
115
+ keys = hist.keys.sort{|a,b| a.to_i <=> b.to_i}
116
+ values = keys.map{|k| hist[k]}
117
+ args = [stats.imei, keys, values, {
118
+ :title => "#{header} Distribution",
119
+ :width => 1024}]
120
+ g = (stats.length(index) > 50) ?
121
+ Geoptima::Chart.draw_line_chart(*args) :
122
+ (stats.length(index) > 10 || stats.numerical?(index)) ?
123
+ Geoptima::Chart.draw_histogram_chart(*args) :
124
+ (stats.length(index) > 1) ?
125
+ Geoptima::Chart.draw_category_chart(*args) :
126
+ nil
127
+ g && g.write("Chart_#{stats.imei}_#{header}_distribution.png")
128
+ end
129
+ end
130
+ end
131
+ end
132
+
data/bin/csv_stats ADDED
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # useful if being run inside a source code checkout
4
+ $: << 'lib'
5
+ $: << '../lib'
6
+
7
+ require 'geoptima/options'
8
+
9
+ $show_all = false
10
+
11
+ $files = Geoptima::Options.process_args do |option|
12
+ option.a {$show_all = true}
13
+ option.o {$output_files = true}
14
+ option.u {$exclude_stats = true}
15
+ option.I {$include_props = ARGV.shift.split(/[\,\;]+/)}
16
+ option.O {$output_prefix = ARGV.shift}
17
+ end
18
+
19
+ $help = true if($files.length<0)
20
+ if $help
21
+ puts <<EOHELP
22
+ Usage: ./stats.rb <-dha> <-I include_props> files
23
+ -d debug mode #{cw $debug}
24
+ -h print this help #{cw $help}
25
+ -a show all values, not just discrete ones #{cw $show_all}
26
+ -o output the stats to files instead of the console #{cw $output_files}
27
+ -u exclude stats, showing only unique lists of values #{cw $exclude_stats}
28
+ -I only calculate statistics for named properties #{aw $include_props}
29
+ -O prefix output files with specific prefix #{aw $output_prefix}
30
+ EOHELP
31
+ exit 0
32
+ end
33
+
34
+ $files.each do |file|
35
+ lines = 0
36
+ headers = nil
37
+ file_stats = nil
38
+ File.open(file).each do |line|
39
+ lines += 1
40
+ fields=line.chomp.split(/[\t\,]/)
41
+ if headers
42
+ puts "Processing line: #{line}" if($debug)
43
+ lac,ci=nil
44
+ fields.each_with_index do |field,index|
45
+ if $include_props.to_s=='' || $include_props.index(headers[index])
46
+ puts "\tField[#{index}]: #{field}" if($debug)
47
+ stats = file_stats[index]
48
+ stats[field] ||= 0
49
+ stats[field] += 1
50
+ puts "\tField[#{index}]: #{field} => #{stats[field]}" if($debug)
51
+ lac=field if(headers[index]=='LAC' || headers[index]=='service.lac')
52
+ ci=field if(headers[index]=='CI' || headers[index]=='service.cell_id')
53
+ #puts "\tSet LAC=#{lac}, CI=#{ci} based on header #{headers[index]}" if($debug)
54
+ end
55
+ end
56
+ puts "\tSet LAC=#{lac}, CI=#{ci}" if($debug)
57
+ if lac && ci
58
+ index = headers.length - 1
59
+ puts "\tAdding statistics for LAC=#{lac}, CI=#{ci} using additional header '#{headers[index]}'" if($debug)
60
+ stats = file_stats[index]
61
+ field="#{lac}-#{ci}"
62
+ stats[field] ||= 0
63
+ stats[field] += 1
64
+ puts "\tField[#{index}]: #{field} => #{stats[field]}" if($debug)
65
+ end
66
+ if $debug
67
+ headers.each_with_index do |header,index|
68
+ stats = file_stats[index]
69
+ values = stats.keys
70
+ puts "\nFound #{values.length} unique values for field[#{index}] '#{header}'"
71
+ end
72
+ end
73
+ else
74
+ headers = fields
75
+ headers << 'lac-ci'
76
+ file_stats = fields.map{|h| {}}
77
+ file_stats << {}
78
+ end
79
+ end
80
+
81
+ if headers
82
+ found=[]
83
+ empty=[]
84
+ headers.each_with_index do |header,index|
85
+ stats=file_stats[index]
86
+ found << [header,stats] if(stats.keys.length>0)
87
+ end
88
+ found.each do |ff|
89
+ header,stats=*ff
90
+ output = STDOUT
91
+ filename = ([$output_prefix,header,$exclude_stats ? "Values" : "Stats"]).compact.join('_')+".txt"
92
+ if $output_files
93
+ output = File.open(filename,'w')
94
+ end
95
+ values = stats.keys
96
+ perc = 100.0 * values.length.to_f / lines.to_f
97
+ puts "Found #{values.length} unique values for field '#{header}'"
98
+ if !$show_all && (values.length > 500)
99
+ puts "\tNot printing more values more diverse than 500"
100
+ elsif (!$show_all && (perc > 75))
101
+ puts "\tNot printing more values more diverse than #{perc}%"
102
+ else
103
+ output.puts header
104
+ values.sort.each do |value|
105
+ value_text = (value.to_s.length < 1) ? '<empty>' : value
106
+ if $exclude_stats
107
+ output.puts "\t#{value_text}"
108
+ else
109
+ output.puts "\t#{value_text}\t#{stats[value]}"
110
+ end
111
+ end
112
+ end
113
+ if $output_files
114
+ puts "\tSaved to #{filename}"
115
+ output.close
116
+ else
117
+ puts
118
+ end
119
+ end
120
+ else
121
+ puts "No headers found in file #{file}"
122
+ end
123
+
124
+ end
125
+
126
+
data/bin/show_geoptima CHANGED
@@ -7,7 +7,7 @@ $: << '../lib'
7
7
  require 'date'
8
8
  require 'geoptima'
9
9
 
10
- Geoptima::assert_version("0.0.6")
10
+ Geoptima::assert_version("0.0.7")
11
11
 
12
12
  $debug=false
13
13
 
@@ -15,10 +15,6 @@ $event_names=[]
15
15
  $files = []
16
16
  $print_limit = 10000
17
17
 
18
- def cw(val)
19
- val.nil? ? '' : "(#{val})"
20
- end
21
-
22
18
  while arg=ARGV.shift do
23
19
  if arg =~ /^\-(\w+)/
24
20
  $1.split(//).each do |aa|
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # useful if being run inside a source code checkout
4
+ $: << 'lib'
5
+ $: << '../lib'
6
+
7
+ require 'date'
8
+ require 'geoptima'
9
+
10
+ $debug=false
11
+
12
+ $events={}
13
+ $files = []
14
+
15
+ $events={}
16
+
17
+ while arg=ARGV.shift:
18
+ if arg =~ /^\-(\w+)/
19
+ $1.split(//).each do |aa|
20
+ case aa
21
+ when 'd'
22
+ $debug=true
23
+ else
24
+ puts "Unrecognized option: -#{aa}"
25
+ end
26
+ end
27
+ else
28
+ if File.exist? arg
29
+ $files << arg
30
+ else
31
+ puts "No such file: #{arg}"
32
+ end
33
+ end
34
+ end
35
+
36
+ $datasets = Geoptima::Dataset.make_datasets($files, :locate => true)
37
+
38
+ puts "Found #{$datasets.length} IMEIs"
39
+ $datasets.keys.sort.each do |imei|
40
+ dataset = $datasets[imei]
41
+ events = dataset.sorted('mode')
42
+ puts "\nFor #{imei} found #{dataset.length} events:\n"
43
+ if events && events.length>0
44
+ puts "Time\tLat\tLon\t#{events.first.header[1..-1].join("\t")}"
45
+ events.each do |event|
46
+ puts "#{event.time}\t#{event.latitude}\t#{event.longitude}\t#{event.data[1..-1].join("\t")}"
47
+ end
48
+ end
49
+ end
50
+
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # useful if being run inside a source code checkout
4
+ $: << 'lib'
5
+ $: << '../lib'
6
+
7
+ require 'geoptima/chart'
8
+ require 'geoptima/options'
9
+
10
+ $files = Geoptima::Options.process_args
11
+
12
+ class Stats
13
+ attr_reader :file, :imei, :headers, :stats, :data
14
+ def initialize(file,imei,fields)
15
+ @file = file
16
+ @imei = imei
17
+ @headers = fields
18
+ @stats = fields.map{|h| {}}
19
+ @data = fields.map{|h| []}
20
+ @numerical = fields.map{|h| true}
21
+ end
22
+ def add(field,index)
23
+ if field =~ /\w/
24
+ @numerical[index] &&= is_number?(field)
25
+ puts "\tField[#{index}]: #{field}" if($debug)
26
+ stats = @stats[index]
27
+ stats[field] ||= 0
28
+ stats[field] += 1
29
+ puts "\tField[#{index}]: #{field} => #{stats[field]}" if($debug)
30
+ @data[index] << field
31
+ end
32
+ end
33
+ def is_number?(field)
34
+ is_integer?(field) || is_float?(field)
35
+ end
36
+ def is_integer?(field)
37
+ field.to_i.to_s == field
38
+ end
39
+ def is_float?(field)
40
+ field.to_f.to_s == field
41
+ end
42
+ def length(index)
43
+ @stats[index].length
44
+ end
45
+ def diversity(index)
46
+ 100.0 * @stats[index].length.to_f / @data[index].length.to_f
47
+ end
48
+ def diverse?(index)
49
+ @stats[index].length>500 || diversity(index) > 40.0
50
+ end
51
+ def numerical?(index)
52
+ @numerical[index]
53
+ end
54
+ end
55
+
56
+ $stats = {}
57
+
58
+ $files.each do |file|
59
+ lines = 0
60
+ imei = file.split(/[_\.]/)[0]
61
+ File.open(file).each do |line|
62
+ lines += 1
63
+ fields=line.chomp.split(/\t/)
64
+ if $stats[file]
65
+ puts "Processing line: #{line}" if($debug)
66
+ fields.each_with_index do |field,index|
67
+ $stats[file].add(field,index)
68
+ end
69
+ else
70
+ $stats[file] = Stats.new(file,imei,fields)
71
+ end
72
+ end
73
+ end
74
+
75
+ $stats.each do |file,stats|
76
+ stats.headers.each_with_index do |header,index|
77
+ puts "Charting #{header} with diversity #{stats.diversity(index)}"
78
+ case header
79
+ when 'signal.strength'
80
+ Geoptima::Chart.draw_line_chart(
81
+ stats.imei,
82
+ stats.data[0],
83
+ stats.data[index].map{|f| v=f.to_i; (v>-130 && v<0) ? v : nil},
84
+ :title => 'Signal Strength',
85
+ :maximum_value => -30,
86
+ :minimum_value => -130,
87
+ :width => 1024
88
+ ).write("Chart_#{stats.imei}_#{header}.png")
89
+
90
+ hist = stats.stats[index]
91
+ keys = hist.keys.sort{|a,b| a.to_i <=> b.to_i}
92
+ values = keys.map{|k| hist[k]}
93
+ Geoptima::Chart.draw_histogram_chart(
94
+ stats.imei, keys, values,
95
+ :title => 'Signal Strength Distribution',
96
+ :width => 1024
97
+ ).write("Chart_#{stats.imei}_#{header}_distribution.png")
98
+
99
+ when 'Event'
100
+ hist = stats.stats[index]
101
+ keys = hist.keys.sort{|a,b| a.to_i <=> b.to_i}
102
+ values = keys.map{|k| hist[k]}
103
+ Geoptima::Chart.draw_category_chart(
104
+ stats.imei, keys, values,
105
+ :title => "#{header} Distribution",
106
+ :width => 1024
107
+ ).write("Chart_#{stats.imei}_#{header}_distribution.png")
108
+
109
+ else
110
+ if stats.diverse?(index)
111
+ puts "Ingnoring high diversity field #{header}"
112
+ else
113
+ puts "Charting field: #{header} with length #{stats.length(index)} and diversity #{stats.diversity(index)}"
114
+ hist = stats.stats[index]
115
+ keys = hist.keys.sort{|a,b| a.to_i <=> b.to_i}
116
+ values = keys.map{|k| hist[k]}
117
+ args = [stats.imei, keys, values, {
118
+ :title => "#{header} Distribution",
119
+ :width => 1024}]
120
+ g = (stats.length(index) > 50) ?
121
+ Geoptima::Chart.draw_line_chart(*args) :
122
+ (stats.length(index) > 10 || stats.numerical?(index)) ?
123
+ Geoptima::Chart.draw_histogram_chart(*args) :
124
+ (stats.length(index) > 1) ?
125
+ Geoptima::Chart.draw_category_chart(*args) :
126
+ nil
127
+ g && g.write("Chart_#{stats.imei}_#{header}_distribution.png")
128
+ end
129
+ end
130
+ end
131
+ end
132
+
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # useful if being run inside a source code checkout
4
+ $: << 'lib'
5
+ $: << '../lib'
6
+
7
+ require 'geoptima/options'
8
+
9
+ $show_all = false
10
+
11
+ $files = Geoptima::Options.process_args do |option|
12
+ option.a {$show_all = true}
13
+ option.o {$output_files = true}
14
+ option.u {$exclude_stats = true}
15
+ option.I {$include_props = ARGV.shift.split(/[\,\;]+/)}
16
+ option.O {$output_prefix = ARGV.shift}
17
+ end
18
+
19
+ $help = true if($files.length<0)
20
+ if $help
21
+ puts <<EOHELP
22
+ Usage: ./stats.rb <-dha> <-I include_props> files
23
+ -d debug mode #{cw $debug}
24
+ -h print this help #{cw $help}
25
+ -a show all values, not just discrete ones #{cw $show_all}
26
+ -o output the stats to files instead of the console #{cw $output_files}
27
+ -u exclude stats, showing only unique lists of values #{cw $exclude_stats}
28
+ -I only calculate statistics for named properties #{aw $include_props}
29
+ -O prefix output files with specific prefix #{aw $output_prefix}
30
+ EOHELP
31
+ exit 0
32
+ end
33
+
34
+ $files.each do |file|
35
+ lines = 0
36
+ headers = nil
37
+ file_stats = nil
38
+ File.open(file).each do |line|
39
+ lines += 1
40
+ fields=line.chomp.split(/[\t\,]/)
41
+ if headers
42
+ puts "Processing line: #{line}" if($debug)
43
+ lac,ci=nil
44
+ fields.each_with_index do |field,index|
45
+ if $include_props.to_s=='' || $include_props.index(headers[index])
46
+ puts "\tField[#{index}]: #{field}" if($debug)
47
+ stats = file_stats[index]
48
+ stats[field] ||= 0
49
+ stats[field] += 1
50
+ puts "\tField[#{index}]: #{field} => #{stats[field]}" if($debug)
51
+ lac=field if(headers[index]=='LAC' || headers[index]=='service.lac')
52
+ ci=field if(headers[index]=='CI' || headers[index]=='service.cell_id')
53
+ #puts "\tSet LAC=#{lac}, CI=#{ci} based on header #{headers[index]}" if($debug)
54
+ end
55
+ end
56
+ puts "\tSet LAC=#{lac}, CI=#{ci}" if($debug)
57
+ if lac && ci
58
+ index = headers.length - 1
59
+ puts "\tAdding statistics for LAC=#{lac}, CI=#{ci} using additional header '#{headers[index]}'" if($debug)
60
+ stats = file_stats[index]
61
+ field="#{lac}-#{ci}"
62
+ stats[field] ||= 0
63
+ stats[field] += 1
64
+ puts "\tField[#{index}]: #{field} => #{stats[field]}" if($debug)
65
+ end
66
+ if $debug
67
+ headers.each_with_index do |header,index|
68
+ stats = file_stats[index]
69
+ values = stats.keys
70
+ puts "\nFound #{values.length} unique values for field[#{index}] '#{header}'"
71
+ end
72
+ end
73
+ else
74
+ headers = fields
75
+ headers << 'lac-ci'
76
+ file_stats = fields.map{|h| {}}
77
+ file_stats << {}
78
+ end
79
+ end
80
+
81
+ if headers
82
+ found=[]
83
+ empty=[]
84
+ headers.each_with_index do |header,index|
85
+ stats=file_stats[index]
86
+ found << [header,stats] if(stats.keys.length>0)
87
+ end
88
+ found.each do |ff|
89
+ header,stats=*ff
90
+ output = STDOUT
91
+ filename = ([$output_prefix,header,$exclude_stats ? "Values" : "Stats"]).compact.join('_')+".txt"
92
+ if $output_files
93
+ output = File.open(filename,'w')
94
+ end
95
+ values = stats.keys
96
+ perc = 100.0 * values.length.to_f / lines.to_f
97
+ puts "Found #{values.length} unique values for field '#{header}'"
98
+ if !$show_all && (values.length > 500)
99
+ puts "\tNot printing more values more diverse than 500"
100
+ elsif (!$show_all && (perc > 75))
101
+ puts "\tNot printing more values more diverse than #{perc}%"
102
+ else
103
+ output.puts header
104
+ values.sort.each do |value|
105
+ value_text = (value.to_s.length < 1) ? '<empty>' : value
106
+ if $exclude_stats
107
+ output.puts "\t#{value_text}"
108
+ else
109
+ output.puts "\t#{value_text}\t#{stats[value]}"
110
+ end
111
+ end
112
+ end
113
+ if $output_files
114
+ puts "\tSaved to #{filename}"
115
+ output.close
116
+ else
117
+ puts
118
+ end
119
+ end
120
+ else
121
+ puts "No headers found in file #{file}"
122
+ end
123
+
124
+ end
125
+
126
+
@@ -7,7 +7,7 @@ $: << '../lib'
7
7
  require 'date'
8
8
  require 'geoptima'
9
9
 
10
- Geoptima::assert_version("0.0.6")
10
+ Geoptima::assert_version("0.0.7")
11
11
 
12
12
  $debug=false
13
13
 
@@ -15,10 +15,6 @@ $event_names=[]
15
15
  $files = []
16
16
  $print_limit = 10000
17
17
 
18
- def cw(val)
19
- val.nil? ? '' : "(#{val})"
20
- end
21
-
22
18
  while arg=ARGV.shift do
23
19
  if arg =~ /^\-(\w+)/
24
20
  $1.split(//).each do |aa|
data/geoptima.gemspec CHANGED
@@ -25,7 +25,7 @@ EOF
25
25
  s.files = Dir.glob("{bin,lib,rdoc}/**/*").reject{|x| x=~/(tmp|target|test-data)/ || x=~/~$/} +
26
26
  Dir.glob("examples/*rb") + Dir.glob("examples/sample*json") +
27
27
  %w(README.rdoc CHANGELOG CONTRIBUTORS Gemfile geoptima.gemspec)
28
- s.executables = ['show_geoptima','geoptima_file_time']
28
+ s.executables = ['show_geoptima','geoptima_file_time','csv_chart','csv_stats']
29
29
 
30
30
  s.extra_rdoc_files = %w( README.rdoc )
31
31
  s.rdoc_options = ["--quiet", "--title", "Geoptima.rb", "--line-numbers", "--main", "README.rdoc", "--inline-source"]
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'rubygems'
4
+
5
+ $chart_libs = ['gruff'].map do |chart_lib|
6
+ begin
7
+ require chart_lib
8
+ chart_lib
9
+ rescue LoadError
10
+ puts "Failed to load charting library '#{chart_lib}': #{$!}"
11
+ end
12
+ end.compact
13
+
14
+ if $chart_libs.length > 0
15
+ puts "Loaded #{$chart_libs.length} charting libraries: #{$chart_libs.join(', ')}"
16
+ else
17
+ puts "Warning: No charting libraries loaded. Chart support disabled."
18
+ puts "Please consider installing appropriate chart gems like 'gruff'"
19
+ end
20
+
21
+ module Geoptima
22
+ class Chart
23
+ DEFAULT_OPTIONS = {:show_points => true, :show_lines => true, :title => nil, :width => 800, :margins => 20, :font_size => 14}
24
+ attr_reader :chart_type
25
+ attr_accessor :chart, :data, :options
26
+ def initialize(chart_type,options={})
27
+ @chart_type = chart_type
28
+ @options = DEFAULT_OPTIONS.merge(options)
29
+ end
30
+ def self.engine
31
+ @@engine ||= $chart_libs[0] || 'not available'
32
+ end
33
+ def self.engine=(name)
34
+ @@engine = $chart_libs.grep(/name/)[0] || engine
35
+ end
36
+ def self.line(options={})
37
+ make_chart(:line,options)
38
+ end
39
+ def self.draw_line_chart(legend,keys,values,options={})
40
+ g = make_chart(:line,{:show_points => false}.merge(options))
41
+ g.data(legend, values)
42
+ g.labels= {0=>keys[0], (keys.length-1)=>keys[-1]}
43
+ options[:maximum_value] && g.maximum_value = options[:maximum_value].to_i
44
+ options[:minimum_value] && g.minimum_value = options[:minimum_value].to_i
45
+ options[:filename] && g.write(options[:filename])
46
+ g
47
+ end
48
+ def self.draw_histogram_chart(legend,keys,values,options={})
49
+ puts "Creating a chart with legend #{legend} for #{keys.length} keys and #{values.length} values"
50
+ g = make_chart(:bar, options)
51
+ g.data(legend, values)
52
+ g.minimum_value = 0
53
+ g.labels = keys.inject({}){|a,v| a[a.length] = v;a}
54
+ options[:filename] && g.write(options[:filename])
55
+ g
56
+ end
57
+ def self.draw_category_chart(legend,keys,values,options={})
58
+ puts "Creating category chart with keys: #{keys.join(',')}"
59
+ puts "Creating category chart with values: #{values.join(',')}"
60
+ g = make_chart(:bar, options)
61
+ keys.each_with_index do |key,index|
62
+ puts "\t Adding category #{key} with value #{values[index]}"
63
+ g.data(key, values[index])
64
+ end
65
+ g.minimum_value = 0
66
+ options[:filename] && g.write(options[:filename])
67
+ g
68
+ end
69
+ def self.bar(options={})
70
+ make_chart(:bar,options)
71
+ end
72
+ def self.make_chart(chart_type,options={})
73
+ case engine
74
+ when 'gruff'
75
+ GruffChart.new(chart_type,options)
76
+ else
77
+ puts "Unsupported chart engine: #{@@engine}"
78
+ end
79
+ end
80
+ end
81
+ class GruffChart < Chart
82
+ def initialize(chart_type,options={})
83
+ super(chart_type,options)
84
+ end
85
+ def chart
86
+ unless @chart
87
+ case chart_type.to_s
88
+ when 'line'
89
+ @chart = Gruff::Line.new(options[:width])
90
+ @chart.hide_dots = !options[:show_points]
91
+ @chart.hide_lines = !options[:show_lines]
92
+ when 'bar'
93
+ @chart = Gruff::Bar.new(options[:width])
94
+ else
95
+ raise "Unsupported chart type: #{chart_type}"
96
+ end
97
+ @chart.title = options[:title]
98
+ @chart.margins = options[:margins]
99
+ @chart.legend_font_size = options[:font_size]
100
+ @chart.marker_font_size = options[:font_size]
101
+ @chart.title_font_size = options[:title_font_size] || 2 * options[:font_size]
102
+ end
103
+ @chart
104
+ end
105
+ def data(name,values)
106
+ chart.data(name,values)
107
+ end
108
+ def labels=(label_map)
109
+ chart.labels = label_map
110
+ end
111
+ def method_missing(symbol,*args,&block)
112
+ chart.send symbol, *args, &block
113
+ end
114
+ def write(filename)
115
+ chart.write(filename)
116
+ end
117
+ end
118
+ end
119
+
data/lib/geoptima/data.rb CHANGED
@@ -3,6 +3,20 @@
3
3
  require 'rubygems'
4
4
  require 'multi_json'
5
5
  require 'date'
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
6
20
 
7
21
  #
8
22
  # The Geoptima Module provides support for the Geoptima Client JSON file format
@@ -29,6 +43,26 @@ module Geoptima
29
43
  end
30
44
  end
31
45
 
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
+
32
66
  # The Geoptima::Event class represents and individual record or event
33
67
  class Event
34
68
  KNOWN_HEADERS={
@@ -67,7 +101,7 @@ module Geoptima
67
101
  time.new_offset(0)
68
102
  end
69
103
  def time_key
70
- utc.strftime("%Y-%m-%d %H:%M:%S.%3N")
104
+ utc.strftime("%Y-%m-%d %H:%M:%S.%3N").gsub(/\.(\d{3})\d+/,'.\1')
71
105
  end
72
106
  def [](key)
73
107
  @fields[key] || @fields[key.gsub(/#{name}\./,'')]
@@ -127,7 +161,7 @@ module Geoptima
127
161
  @fields[key] ||= subscriber[key] || subscriber[key.downcase]
128
162
  end
129
163
  def start
130
- @start ||= subscriber['start'] && DateTime.parse(subscriber['start'].gsub(/Asia\/Bangkok/,'GMT+7').gsub(/Mar 17 2044/,'Feb 14 2012'))
164
+ @start ||= subscriber['start'] && DateTime.parse(subscriber['start'].gsub(/Asia\/Bangkok/,'GMT+7'))#.gsub(/Mar 17 2044/,'Feb 14 2012'))
131
165
  end
132
166
  def valid?
133
167
  start && start > Data.min_start && start < Data.max_start
@@ -216,7 +250,7 @@ module Geoptima
216
250
  @imei = imei
217
251
  @data = []
218
252
  @options = options
219
- @time_range = options[:time_range] || Range.new(Config[:min_datetime],Config[:max_datetime])
253
+ @time_range = options[:time_range] || DateRange.new(Config[:min_datetime],Config[:max_datetime])
220
254
  @fields = {}
221
255
  end
222
256
 
@@ -347,17 +381,30 @@ module Geoptima
347
381
  @sorted ||= {}
348
382
  unless @sorted[nil]
349
383
  event_hash = {}
384
+ puts "Creating sorted maps for #{self}" if($debug)
350
385
  events_names.each do |name|
386
+ puts "Preparing maps for #{name}" if($debug)
351
387
  @data.each do |data|
388
+ puts "Processing #{(e=data.events[name]) && e.length} events for #{name}" if($debug)
352
389
  (events = data.events[name]) && events.each do |event|
390
+ # t = event.time.to_i
391
+ puts "\t\tTesting #{event.time} inside #{@time_range}" if($debug)
392
+ # if t < tmax
393
+ # if event.time > @time_range.min
394
+ # if (event.time >= @time_range.min) && (event.time < @time_range.max)
353
395
  if @time_range.include?(event.time)
396
+ # if @time_range.cover?(event.time)
397
+ puts "\t\t\tEvent at #{event.time} is inside #{@time_range}" if($debug)
354
398
  key = "#{event.time_key} #{name}"
355
399
  event_hash[key] = event
356
400
  end
357
401
  end
358
402
  end
403
+ puts "After adding #{name} events, maps are #{event_hash.length} long" if($debug)
359
404
  end
405
+ puts "Merging and sorting #{event_hash.keys.length} maps" if($debug)
360
406
  @sorted[nil] = event_hash.keys.sort.map{|k| event_hash[k]}
407
+ puts "Sorted #{@sorted[nil].length} events" if($debug)
361
408
  locate_events if(options[:locate])
362
409
  end
363
410
  @sorted
@@ -365,18 +412,21 @@ module Geoptima
365
412
 
366
413
  def locate_events
367
414
  prev_gps = nil
415
+ count = 0
416
+ puts "Locating #{sorted.length} events" if($debug)
368
417
  sorted.each do |event|
369
418
  if event.name === 'gps'
370
419
  event.locate(event)
371
420
  prev_gps = event
372
421
  elsif prev_gps
373
- event.locate_if_closer_than(prev_gps,60)
422
+ count += 1 if(event.locate_if_closer_than(prev_gps,60))
374
423
  end
375
424
  end
425
+ puts "Located #{count} / #{sorted.length} events" if($debug)
376
426
  end
377
427
 
378
428
  def to_s
379
- "IMEI:#{imei}, IMSI:#{imsis.join(',')}, Platform:#{platform}, Model:#{model}, OS:#{os}, Files:#{file_count}, Events:#{sorted.length}"
429
+ "IMEI:#{imei}, IMSI:#{imsis.join(',')}, Platform:#{platform}, Model:#{model}, OS:#{os}, Files:#{file_count}, Events:#{sorted && sorted.length}"
380
430
  end
381
431
 
382
432
  def self.make_datasets(files, options={})
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/ruby
2
+
3
+ def cw(val)
4
+ val.nil? ? '' : "(#{val})"
5
+ end
6
+
7
+ def aw(val)
8
+ val.nil? ? '' : "#{val.inspect}"
9
+ end
10
+
11
+ module Geoptima
12
+
13
+ class Options
14
+
15
+ attr_reader :args, :options, :debug
16
+
17
+ def initialize(debug=nil)
18
+ @debug = debug
19
+ @args = []
20
+ @options = {}
21
+ end
22
+ def add(*args,&block)
23
+ puts "Adding option processing for: #{args[0]}" if(debug)
24
+ @options[args[0].to_s] = block
25
+ end
26
+ def method_missing(symbol,*args,&block)
27
+ puts "Adding option processing for: #{symbol}" if(debug)
28
+ @options[symbol.to_s] = block
29
+ end
30
+ def process(a)
31
+ puts "Looking for match to option #{a}" if(debug)
32
+ @options.each do |opt,block|
33
+ puts "Comparing option #{a} to known option #{opt}" if(debug)
34
+ if opt === a
35
+ puts "Calling block for option #{a}: #{block.inspect}" if(debug)
36
+ block.call
37
+ return
38
+ end
39
+ end
40
+ puts "Unknown option: -#{a}"
41
+ end
42
+ def to_s
43
+ "Options[#{@options.keys.sort.join(', ')}]: #{args.join(', ')}"
44
+ end
45
+
46
+ def self.process_args(debug=nil)
47
+ options = Options.new(debug)
48
+ options.add('d') {$debug = true}
49
+ options.add('h') {$help = true}
50
+ puts "Processing options: #{options}" if(debug)
51
+ yield options if(block_given?)
52
+ while arg = ARGV.shift do
53
+ if arg =~ /^\-(\w+)/
54
+ $1.split(//).each do |a|
55
+ options.process a
56
+ end
57
+ else
58
+ options.args << arg
59
+ end
60
+ end
61
+ options.args
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
@@ -1,6 +1,6 @@
1
1
  module Geoptima
2
2
 
3
- VERSION = "0.0.6"
3
+ VERSION = "0.0.7"
4
4
 
5
5
  def self.version_as_int(ver)
6
6
  base = 1
data/lib/geoptima.rb CHANGED
@@ -1,3 +1,4 @@
1
1
  require 'geoptima/version.rb'
2
2
  require 'geoptima/data.rb'
3
+ require 'geoptima/options.rb'
3
4
 
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: 19
4
+ hash: 17
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 6
10
- version: 0.0.6
9
+ - 7
10
+ version: 0.0.7
11
11
  platform: ruby
12
12
  authors:
13
13
  - Craig Taverner
@@ -15,7 +15,8 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-03-13 00:00:00 Z
18
+ date: 2012-03-19 00:00:00 +01:00
19
+ default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: multi_json
@@ -62,27 +63,35 @@ email: craig@amanzi.com
62
63
  executables:
63
64
  - show_geoptima
64
65
  - geoptima_file_time
66
+ - csv_chart
67
+ - csv_stats
65
68
  extensions: []
66
69
 
67
70
  extra_rdoc_files:
68
71
  - README.rdoc
69
72
  files:
73
+ - bin/show_geoptima_sos
70
74
  - bin/show_geoptima
75
+ - bin/csv_chart
71
76
  - bin/geoptima_file_time
77
+ - bin/csv_stats
72
78
  - lib/geoptima/version.rb
73
79
  - lib/geoptima/data.rb
80
+ - lib/geoptima/chart.rb
81
+ - lib/geoptima/options.rb
74
82
  - lib/geoptima.rb
75
83
  - examples/show_geoptima_sos.rb
76
- - examples/export_layer.rb
77
- - examples/stats.rb
78
84
  - examples/show_geoptima.rb
85
+ - examples/csv_chart.rb
79
86
  - examples/geoptima_file_time.rb
87
+ - examples/csv_stats.rb
80
88
  - examples/sample_geoptima.json
81
89
  - README.rdoc
82
90
  - CHANGELOG
83
91
  - CONTRIBUTORS
84
92
  - Gemfile
85
93
  - geoptima.gemspec
94
+ has_rdoc: true
86
95
  homepage: http://github.com/craigtaverner/geoptima.rb
87
96
  licenses: []
88
97
 
@@ -120,7 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
129
  requirements: []
121
130
 
122
131
  rubyforge_project: geoptima
123
- rubygems_version: 1.7.2
132
+ rubygems_version: 1.6.2
124
133
  signing_key:
125
134
  specification_version: 3
126
135
  summary: Ruby access to Geoptima JSON files
@@ -1,59 +0,0 @@
1
- #!/usr/bin/env jruby
2
-
3
- # useful if being run inside a source code checkout
4
- $: << 'lib'
5
- $: << '../lib'
6
-
7
- require 'rubygems'
8
- require 'neo4j/spatial'
9
- require 'neo4j/spatial/cmd'
10
-
11
- $zoom = 1.0
12
- $args = Neo4j::Spatial::Cmd.args
13
-
14
- if $list === 'layers'
15
- layers = Neo4j::Spatial::Layer.list
16
- puts "Have #{layers.length} existing layers in the database:"
17
- layers.each {|l| puts "\t#{l} (#{l.type_name})"}
18
- puts
19
- exit 0
20
- end
21
-
22
- if $help || $args.length < 1
23
- puts <<-eos
24
-
25
- usage: ./export_layer.rb <-D storage_path> <-F format> <-E dir> <-Z zoom> <-W width> <-H height> <-l> <-h> layer <layers>
26
- -D Use specified database location
27
- -F Use specified export format (png, shp)
28
- -E Use specified export directory path (default '.')
29
- -Z Zoom in by specified factor (eg. 3.0)
30
- -W Image width (default 600)
31
- -H Image height (default 400)
32
- -l List existing database layers first
33
- -h Display this help and exit
34
- The layer(s) should be pre-existing layers (including dynamic layers) in the database.
35
- Supported formats are 'shp' for ESRI Shapefile and 'png' for images.
36
-
37
- For example:
38
- ./export_layer.rb -D db -E exports -F png croatia.osm highway highway-residential natural-water
39
-
40
- This will export four previously defined layers to png format files in the 'exports' directory.
41
-
42
- eos
43
- exit
44
- end
45
-
46
- if $format.to_s.downcase === 'shp'
47
- $exporter = Neo4j::Spatial::SHPExporter.new :dir => $export
48
- else
49
- $exporter = Neo4j::Spatial::ImageExporter.new :dir => $export, :zoom => $zoom, :width => $width, :height => $height
50
- end
51
-
52
- puts "Exporting #{$args.length} layers to #{$exporter.format}"
53
-
54
- $args.each do |layer|
55
- l = Neo4j::Spatial::Layer.find layer
56
- puts "Exporting #{l} (#{l.type_name}) - #{l.index.layer_bounding_box}"
57
- $exporter.export l.name
58
- puts "Finished exporting #{l} (#{l.type_name}) of #{l.index.count} entries"
59
- end
data/examples/stats.rb DELETED
@@ -1,41 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- $debug = false
4
- $lines = 0
5
-
6
- ARGF.each do |line|
7
- $lines += 1
8
- fields=line.chomp.split(/\t/)
9
- if $headers
10
- puts "Processing line: #{line}" if($debug)
11
- fields.each_with_index do |field,index|
12
- puts "\tField[#{index}]: #{field}" if($debug)
13
- stats = $stats[index]
14
- stats[field] ||= 0
15
- stats[field] += 1
16
- puts "\tField[#{index}]: #{field} => #{stats[field]}" if($debug)
17
- end
18
- else
19
- $headers = fields
20
- $stats = fields.map{|h| {}}
21
- end
22
- end
23
-
24
- $headers.each_with_index do |header,index|
25
- stats = $stats[index]
26
- values = stats.keys
27
- perc = 100.0 * values.length.to_f / $lines.to_f
28
- puts "\nFound #{values.length} unique values for field '#{header}'"
29
- if values.length > 500
30
- puts "\tNot printing more values more diverse than 500"
31
- elsif (perc > 75)
32
- puts "\tNot printing more values more diverse than #{perc}%"
33
- else
34
- puts header
35
- values.sort.each do |value|
36
- value_text = (value.to_s.length < 1) ? '<empty>' : value
37
- puts "\t#{value_text}\t#{stats[value]}"
38
- end
39
- end
40
- end
41
-