geoptima 0.0.6 → 0.0.7

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