barton 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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