barton 0.0.1 → 0.0.2

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/data/nsw-state.yaml CHANGED
@@ -23,7 +23,7 @@
23
23
  - State
24
24
  - Legaslative Assembly
25
25
  - Central Coast
26
- id: '048630'
26
+ id: 048630
27
27
  - name: The Entrance
28
28
  tags:
29
29
  - New South Wales
@@ -37,14 +37,14 @@
37
37
  - State
38
38
  - Legaslative Assembly
39
39
  - Central Coast
40
- id: '82e217'
40
+ id: 82e217
41
41
  - name: Coogee
42
42
  tags:
43
43
  - New South Wales
44
44
  - State
45
45
  - Legaslative Assembly
46
46
  - Eastern Suburbs
47
- id: '164e90'
47
+ id: 164e90
48
48
  - name: Heffron
49
49
  tags:
50
50
  - New South Wales
@@ -422,7 +422,7 @@
422
422
  - State
423
423
  - Legaslative Assembly
424
424
  - Western Sydney Fringe
425
- id: '2e2812'
425
+ id: 2e2812
426
426
  - name: Hawkesbury
427
427
  tags:
428
428
  - New South Wales
@@ -61,7 +61,7 @@
61
61
  - Federal
62
62
  - House of Representatives
63
63
  - Brisbane
64
- id: '087739'
64
+ id: 087739
65
65
  - name: Capricornia
66
66
  tags:
67
67
  - Queensland
@@ -147,7 +147,7 @@
147
147
  - Federal
148
148
  - House of Representatives
149
149
  - Gold Coast
150
- id: '081148'
150
+ id: 081148
151
151
  - name: McPherson
152
152
  tags:
153
153
  - Queensland
data/data/qld-state.yaml CHANGED
@@ -178,7 +178,7 @@
178
178
  - State
179
179
  - Legislative Assembly
180
180
  - Brisbane South
181
- id: '009702'
181
+ id: 009702
182
182
  - name: Indooroopilly
183
183
  tags:
184
184
  - Queensland
@@ -528,7 +528,7 @@
528
528
  - State
529
529
  - Legislative Assembly
530
530
  - SE Queensland
531
- id: '769e84'
531
+ id: 769e84
532
532
  - name: Lockyer
533
533
  tags:
534
534
  - Queensland
@@ -86,7 +86,7 @@
86
86
  - name: McEwen
87
87
  tags:
88
88
  - Eastern Victoria
89
- id: '9e0246'
89
+ id: 9e0246
90
90
  - name: McMillan
91
91
  tags:
92
92
  - Eastern Victoria
data/data/vic-local.yaml CHANGED
@@ -96,7 +96,7 @@
96
96
  - local
97
97
  - LGA
98
98
  - North-East
99
- id: '9890e8'
99
+ id: 9890e8
100
100
  - name: Darebin
101
101
  tags:
102
102
  - Victoria
@@ -446,7 +446,7 @@
446
446
  - local
447
447
  - LGA
448
448
  - North West
449
- id: '086695'
449
+ id: 086695
450
450
  - name: Northern Grampians
451
451
  tags:
452
452
  - Victoria
@@ -1020,7 +1020,7 @@
1020
1020
  - local
1021
1021
  - ward
1022
1022
  - Mornington Peninsula
1023
- id: '756e19'
1023
+ id: 756e19
1024
1024
  - name: Nepean
1025
1025
  tags:
1026
1026
  - Victoria
@@ -1363,7 +1363,7 @@
1363
1363
  - local
1364
1364
  - ward
1365
1365
  - Monash
1366
- id: '63e668'
1366
+ id: 63e668
1367
1367
  - name: Mulgrave
1368
1368
  tags:
1369
1369
  - Victoria
@@ -1965,7 +1965,7 @@
1965
1965
  - local
1966
1966
  - ward
1967
1967
  - Hindmarsh
1968
- id: '2e9375'
1968
+ id: 2e9375
1969
1969
  - name: North
1970
1970
  tags:
1971
1971
  - Victoria
data/data/vic-state.yaml CHANGED
@@ -348,7 +348,7 @@
348
348
  - State
349
349
  - Legislative Assembly
350
350
  - South Eastern Metropolitan
351
- id: '701e74'
351
+ id: 701e74
352
352
  - name: Lyndhurst
353
353
  tags:
354
354
  - Victoria
data/lib/barton.rb CHANGED
@@ -1,3 +1,13 @@
1
- require "barton/app"
1
+ require "barton/core"
2
2
 
3
- Barton::App.run!
3
+ module Barton
4
+ class << self
5
+ def data_loaded
6
+ @@data_loaded ||= false
7
+ end
8
+
9
+ def data_loaded=(bool)
10
+ @@data_loaded = bool
11
+ end
12
+ end
13
+ end
data/lib/barton/app.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  require 'sinatra'
2
2
  require 'json'
3
+ require 'barton'
3
4
  require 'barton/core'
4
5
 
5
6
  module Barton
6
7
  class App < Sinatra::Base
7
-
8
+
8
9
  # routes
9
10
  allowed_formats = ['', 'json']
10
11
 
@@ -22,12 +23,12 @@ module Barton
22
23
  geo, tags, address = params[:geo], params[:tags], params[:address]
23
24
  tags = tags.split(',') unless tags.nil?
24
25
  raise Sinatra::NotFound unless allowed_formats.include?( format )
25
- geo = Barton::Core.address( address ) if address
26
+ geo = Barton::Data.address( address ) if address
26
27
  if not id.empty?
27
- results = Barton::Core.electorates( {:id => id} )
28
+ results = Barton.electorates( {:id => id} )
28
29
  prepare_response( results )
29
30
  elsif geo or tags
30
- results = Barton::Core.electorates( {:tags => tags, :geo => geo} )
31
+ results = Barton.electorates( {:tags => tags, :geo => geo} )
31
32
  prepare_response( results )
32
33
  else
33
34
  prepare_response
@@ -63,6 +64,5 @@ module Barton
63
64
  content_type 'application/json;charset=utf-8'
64
65
  JSON.pretty_generate( response )
65
66
  end
66
-
67
67
  end
68
68
  end
data/lib/barton/core.rb CHANGED
@@ -1,15 +1,111 @@
1
1
  require 'yaml'
2
2
  require 'digest/sha1'
3
3
  require 'tire'
4
- require 'rest_client'
5
- require 'uri'
6
4
  require 'json'
5
+ require 'barton/app'
7
6
 
8
7
  module Barton
9
- class Core
10
-
11
- @api_url = 'http://barton.experiementsindemocracy.org/api'
12
-
8
+
9
+ # Returns an array of electorates matching the search criteria
10
+ # Accepts a hash of criteria
11
+ # :id
12
+ # :tags
13
+ # :geo
14
+ # :address
15
+ # Returns an array of hashes
16
+ def Barton.electorates( query = {} )
17
+ Find.electorates( query )
18
+ end
19
+
20
+ # Returns an array of member matching the search criteria
21
+ # Accepts a hash of criteria
22
+ # Returns an array of hashes
23
+ def members
24
+ # TBA
25
+ end
26
+
27
+ # Loads electoral data from the yaml files into elasticsearch
28
+ def self.setup
29
+ Data.config
30
+ Data.purge_es
31
+ Dir['data/*.yaml'].each do |f|
32
+ Setup.load_file( f )
33
+ end
34
+ Barton.data_loaded = true
35
+ end
36
+
37
+ # Configuration options for the Gem
38
+ def self.config
39
+ #puts ENV['RAKE_ENV']
40
+ #Data.config
41
+ end
42
+
43
+ module Find
44
+ def self.electorates( query={} )
45
+ results = Array.new
46
+ terms = ["id:#{query[:id]}"] if query.has_key?( :id )
47
+ terms = query[:tags] if query.has_key?( :tags )
48
+ geo = query[:geo] if query.has_key?( :geo )
49
+ geo = self.address( query[:address] ) if query.has_key?( :address ) and not query.has_key?( :geo )
50
+ s = Data.search( terms, geo )
51
+ s.results.each do |e|
52
+ e = e.to_hash
53
+ e.keys.each { |k| e.delete( k ) if k.match( /_|geobox|boundaries|highlight|sort/ ) }
54
+ e[:url] = "#{@api_url}/electorates/#{e[:id]}"
55
+ results.push( e )
56
+ end
57
+ results
58
+ end
59
+
60
+ # Geocode lookup to google
61
+ def self.address( address )
62
+ # http://maps.googleapis.com/maps/api/geocode/json?address=address&sensor=false&region=au
63
+ begin
64
+ google = RestClient.get "http://maps.googleapis.com/maps/api/geocode/json?address=#{URI.escape( address )}&sensor=false&region=au"
65
+ json = JSON.parse( google )
66
+ if json['status'] == 'OK'
67
+ #puts google
68
+ return "#{json['results'][0]['geometry']['location']['lng']},#{json['results'][0]['geometry']['location']['lat']}"
69
+ else
70
+ return nil
71
+ end
72
+ rescue => e
73
+ puts e
74
+ end
75
+ end
76
+
77
+ # Ray casting algorithm to find if point is in polygon
78
+ def self.point_in_poly?( geo, boundaries )
79
+ # get pairs of points of boundaries
80
+ long, lat = geo.split( ',' )
81
+ long, lat = long.to_f, lat.to_f
82
+ boundaries.each do |b|
83
+ coords = b.split( ' ' )
84
+ length = coords.length
85
+ first, second = coords.shift, coords.shift
86
+ cuts = 0
87
+ while length > 1
88
+ cuts += self.crosses?( first, second, lat, long )
89
+ first = second
90
+ second = coords.shift
91
+ length -= 1
92
+ end
93
+ return cuts.odd? ? true : false
94
+ end
95
+ end
96
+
97
+ # this fails edge cases where lat,long < ax,by & > bx,ay
98
+ # ie when it crosses just in front or behind diagonally
99
+ def self.crosses?( a, b, lat, long )
100
+ ay, ax = a.split( ',' )
101
+ by, bx = b.split( ',' )
102
+ ax, ay, bx, by = ax.to_f, ay.to_f, bx.to_f, by.to_f
103
+ return 0 if ay < long and by < long
104
+ return ( ( ax < lat and lat < bx ) or ( bx < lat and lat < ax ) ) ? 1 : 0
105
+ end
106
+ end
107
+
108
+ module Setup
13
109
  # Parses a electorate yaml file, merges it with a geo yaml file,
14
110
  # loads them to the datastore and resaves the yaml files
15
111
  def self.parse_yaml( filename )
@@ -44,36 +140,62 @@ module Barton
44
140
  end
45
141
  end
46
142
 
47
- # Creates a minimum bounded box encapsulating the boundary polygons
48
- def self.create_geobox( boundaries )
143
+ # Creates a minimum bounded box encapsulating the boundary polygons
144
+ def self.create_geobox( boundaries )
49
145
  return nil unless boundaries.instance_of? Array
50
146
  box = Hash.new
51
147
  boundaries.each do |b|
52
- b.split( ' ' ).each do |c|
53
- coords = c.split( ',' )
54
- box['north'] = coords[1].to_f if box['north'].nil? or coords[1].to_f > box['north']
55
- box['south'] = coords[1].to_f if box['south'].nil? or coords[1].to_f < box['south']
56
- box['east'] = coords[0].to_f if box['east'].nil? or coords[0].to_f > box['east']
57
- box['west'] = coords[0].to_f if box['west'].nil? or coords[0].to_f < box['west']
58
- end
59
- end
60
- box
61
- end
62
-
148
+ b.split( ' ' ).each do |c|
149
+ coords = c.split( ',' )
150
+ box['north'] = coords[1].to_f if box['north'].nil? or coords[1].to_f > box['north']
151
+ box['south'] = coords[1].to_f if box['south'].nil? or coords[1].to_f < box['south']
152
+ box['east'] = coords[0].to_f if box['east'].nil? or coords[0].to_f > box['east']
153
+ box['west'] = coords[0].to_f if box['west'].nil? or coords[0].to_f < box['west']
154
+ end
155
+ end
156
+ box
157
+ end
158
+
159
+ # Load data from yaml source file
160
+ def self.load_file( filename )
161
+ puts "Loading data from #{filename}....."
162
+ electorates = self.parse_yaml( filename )
163
+ puts "Failed to load #{filename}" if electorates.nil?
164
+ connected = Data.update_es( electorates )
165
+ abort "Connection to Elasticsearch failed" if connected.nil?
166
+ end
167
+ end
168
+
169
+
170
+ module Data
171
+
172
+ @index_name = ENV['RAKE_ENV']
173
+
174
+ # Set elasticsearch config
175
+ def self.config
176
+ @index_name = ENV['RAKE_ENV']
177
+ if ENV['BONSAI_INDEX_URL']
178
+ Tire.configure do
179
+ url "http://index.bonsai.io"
180
+ end
181
+ @index_name = ENV['BONSAI_INDEX_URL'][/[^\/]+$/]
182
+ end
183
+ end
184
+
63
185
  # Purge electorate data from elasticsearch
64
186
  def self.purge_es
65
187
  self.query_es( 'purge' )
66
188
  end
67
189
 
68
190
  # Load electorate data to elasticsearch
69
- def self.update_es( electorates )
70
- self.query_es( 'update', electorates )
191
+ def self.update_es( data )
192
+ self.query_es( 'update', data )
71
193
  end
72
194
 
73
195
  # Query elasticsearch
74
196
  def self.query_es( action, data=nil )
75
197
  begin
76
- Tire.index "#{ENV['RAKE_ENV']}electorates" do
198
+ Tire.index "#{@index_name}-electorates" do
77
199
  delete if action == 'purge'
78
200
  create if action == 'purge'
79
201
  import data if action == 'update' and not data.nil?
@@ -84,18 +206,8 @@ module Barton
84
206
  end
85
207
  end
86
208
 
87
- # Returns an array of electorates
88
- # :id
89
- # :tags
90
- # :geo
91
- # :address
92
- def self.electorates( query={} )
93
- results = Array.new
94
- terms = ["id:#{query[:id]}"] if query.has_key?( :id )
95
- terms = query[:tags] if query.has_key?( :tags )
96
- geo = query[:geo] if query.has_key?( :geo )
97
- geo = self.address( query[:address] ) if query.has_key?( :address ) and not query.has_key?( :geo )
98
- s = Tire.search "#{ENV['RAKE_ENV']}electorates" do
209
+ def self.search( terms, geo )
210
+ s = Tire.search "#{@index_name}-electorates" do
99
211
  query do
100
212
  boolean do
101
213
  if geo
@@ -115,60 +227,7 @@ module Barton
115
227
  end
116
228
  size 100
117
229
  end
118
- s.results.each do |e|
119
- e = e.to_hash
120
- e.keys.each { |k| e.delete( k ) if k.match( /_|geobox|boundaries|highlight|sort/ ) }
121
- e[:url] = "#{@api_url}/electorates/#{e[:id]}"
122
- results.push( e )
123
- end
124
- results
125
- end
126
-
127
- # Geocode lookup to google
128
- def self.address( address )
129
- # http://maps.googleapis.com/maps/api/geocode/json?address=address&sensor=false&region=au
130
- begin
131
- google = RestClient.get "http://maps.googleapis.com/maps/api/geocode/json?address=#{URI.escape( address )}&sensor=false&region=au"
132
- json = JSON.parse( google )
133
- if json['status'] == 'OK'
134
- puts google
135
- return "#{json['results'][0]['geometry']['location']['lng']},#{json['results'][0]['geometry']['location']['lat']}"
136
- else
137
- return nil
138
- end
139
- rescue => e
140
- puts e
141
- end
142
- end
143
-
144
- # Ray casting algorithm to find if point is in polygon
145
- def self.point_in_poly?( geo, boundaries )
146
- # get pairs of points of boundaries
147
- long, lat = geo.split( ',' )
148
- long, lat = long.to_f, lat.to_f
149
- boundaries.each do |b|
150
- coords = b.split( ' ' )
151
- length = coords.length
152
- first, second = coords.shift, coords.shift
153
- cuts = 0
154
- while length > 1
155
- cuts += self.crosses?( first, second, lat, long )
156
- first = second
157
- second = coords.shift
158
- length -= 1
159
- end
160
- return cuts.odd? ? true : false
161
- end
162
- end
163
-
164
- # this fails edge cases where lat,long < ax,by & > bx,ay
165
- # ie when it crosses just in front or behind diagonally
166
- def self.crosses?( a, b, lat, long )
167
- ay, ax = a.split( ',' )
168
- by, bx = b.split( ',' )
169
- ax, ay, bx, by = ax.to_f, ay.to_f, bx.to_f, by.to_f
170
- return 0 if ay < long and by < long
171
- return ( ( ax < lat and lat < bx ) or ( bx < lat and lat < ax ) ) ? 1 : 0
172
- end
173
- end
174
- end
230
+ s
231
+ end
232
+ end
233
+ end