geocodio-ocd 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/geocodio/address.rb +114 -0
- data/lib/geocodio/address_set.rb +48 -0
- data/lib/geocodio/canadian/electoral_district_base.rb +21 -0
- data/lib/geocodio/canadian/electoral_districts.rb +23 -0
- data/lib/geocodio/canadian/federal_electoral_district.rb +12 -0
- data/lib/geocodio/canadian/provincial_electoral_district.rb +6 -0
- data/lib/geocodio/canadian.rb +14 -0
- data/lib/geocodio/client/error.rb +15 -0
- data/lib/geocodio/client/response.rb +13 -0
- data/lib/geocodio/client.rb +155 -0
- data/lib/geocodio/congressional_district.rb +30 -0
- data/lib/geocodio/legislator.rb +69 -0
- data/lib/geocodio/school_district.rb +15 -0
- data/lib/geocodio/state_legislative_district.rb +16 -0
- data/lib/geocodio/timezone.rb +16 -0
- data/lib/geocodio/utils.rb +43 -0
- data/lib/geocodio/version.rb +13 -0
- data/lib/geocodio.rb +9 -0
- data/spec/address_set_spec.rb +31 -0
- data/spec/address_spec.rb +235 -0
- data/spec/canadian_spec.rb +77 -0
- data/spec/client_spec.rb +196 -0
- data/spec/congressional_district_spec.rb +45 -0
- data/spec/legislator_spec.rb +116 -0
- data/spec/school_district_spec.rb +27 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/state_legislative_district_spec.rb +43 -0
- data/spec/timezone_spec.rb +23 -0
- data/spec/vcr_cassettes/alaska_geocode_with_fields.yml +68 -0
- data/spec/vcr_cassettes/batch_geocode.yml +83 -0
- data/spec/vcr_cassettes/batch_geocode_with_bad_address.yml +61 -0
- data/spec/vcr_cassettes/batch_geocode_with_fields.yml +121 -0
- data/spec/vcr_cassettes/batch_reverse.yml +181 -0
- data/spec/vcr_cassettes/batch_reverse_with_fields.yml +384 -0
- data/spec/vcr_cassettes/canadian.yml +101 -0
- data/spec/vcr_cassettes/geocode.yml +61 -0
- data/spec/vcr_cassettes/geocode_bad_address.yml +53 -0
- data/spec/vcr_cassettes/geocode_with_fields.yml +71 -0
- data/spec/vcr_cassettes/geocode_with_fields_legacy.yml +91 -0
- data/spec/vcr_cassettes/geocode_with_postdirectional.yml +61 -0
- data/spec/vcr_cassettes/invalid_key.yml +59 -0
- data/spec/vcr_cassettes/parse.yml +51 -0
- data/spec/vcr_cassettes/reverse.yml +97 -0
- data/spec/vcr_cassettes/reverse_with_fields.yml +163 -0
- data/spec/vcr_cassettes/reverse_with_fields_no_house_info.yml +138 -0
- metadata +189 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c9199f7869e69da03015cf1880392b7850d05485fcb121e3ce019bdf7a21add9
|
4
|
+
data.tar.gz: dd5b48ccd480bf03f505a9fe49553966c9f1fe09f2ff443db50f27eef3db1e3c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c411d98acf8ffd2a498fcbec82670f5995285b638db230027eace6ebec0403e769d5f16f93834b31a9f82afcad63ab36170fb65282f8bbb6453a232ecc502807
|
7
|
+
data.tar.gz: d165d5e4169ff699f4c1ac099ac17cdca76580a307034224eb1e1698563e3876f64394d437b3a0599848f86b7b341e72ff69b85a6e61c9df270140b49e4153c1
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'geocodio/congressional_district'
|
2
|
+
require 'geocodio/school_district'
|
3
|
+
require 'geocodio/state_legislative_district'
|
4
|
+
require 'geocodio/timezone'
|
5
|
+
require 'geocodio/canadian/electoral_districts'
|
6
|
+
|
7
|
+
module Geocodio
|
8
|
+
class Address
|
9
|
+
include Geocodio::Canadian
|
10
|
+
attr_reader :number, :predirectional, :street, :suffix, :postdirectional,
|
11
|
+
:formatted_street, :city, :state, :zip, :county
|
12
|
+
|
13
|
+
attr_reader :latitude, :longitude
|
14
|
+
alias :lat :latitude
|
15
|
+
alias :lng :longitude
|
16
|
+
|
17
|
+
attr_reader :congressional_districts, :house_districts, :senate_districts,
|
18
|
+
:unified_school_district, :elementary_school_district,
|
19
|
+
:secondary_school_district
|
20
|
+
|
21
|
+
attr_reader :timezone
|
22
|
+
|
23
|
+
# How accurate geocod.io deemed this result to be given the original query.
|
24
|
+
#
|
25
|
+
# @return [Float] a number between 0 and 1
|
26
|
+
attr_reader :accuracy, :accuracy_type, :source
|
27
|
+
|
28
|
+
def initialize(payload = {})
|
29
|
+
set_attributes(payload['address_components']) if payload['address_components']
|
30
|
+
set_coordinates(payload['location']) if payload['location']
|
31
|
+
set_additional_fields(payload['fields']) if payload['fields']
|
32
|
+
|
33
|
+
@source = payload['source']
|
34
|
+
@accuracy = payload['accuracy']
|
35
|
+
@accuracy_type = payload['accuracy_type']
|
36
|
+
@formatted_address = payload['formatted_address']
|
37
|
+
end
|
38
|
+
|
39
|
+
# Formats the address in the standard way.
|
40
|
+
#
|
41
|
+
# @return [String] a formatted address
|
42
|
+
def to_s
|
43
|
+
@formatted_address
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def set_attributes(attributes)
|
49
|
+
@number = attributes['number']
|
50
|
+
@predirectional = attributes['predirectional']
|
51
|
+
@street = attributes['street']
|
52
|
+
@suffix = attributes['suffix']
|
53
|
+
@postdirectional = attributes['postdirectional']
|
54
|
+
@formatted_street = attributes['formatted_street']
|
55
|
+
@city = attributes['city']
|
56
|
+
@state = attributes['state']
|
57
|
+
@zip = attributes['zip']
|
58
|
+
@county = attributes['county']
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_coordinates(coordinates)
|
62
|
+
@latitude = coordinates['lat']
|
63
|
+
@longitude = coordinates['lng']
|
64
|
+
end
|
65
|
+
|
66
|
+
def set_additional_fields(fields)
|
67
|
+
set_congressional_districts(fields['congressional_districts']) if fields['congressional_districts']
|
68
|
+
set_legislative_districts(fields['state_legislative_districts']) if fields['state_legislative_districts']
|
69
|
+
set_school_districts(fields['school_districts']) if fields['school_districts']
|
70
|
+
set_timezone(fields['timezone']) if fields['timezone']
|
71
|
+
|
72
|
+
riding, provincial_riding = fields.values_at('riding', 'provincial_riding')
|
73
|
+
if riding || provincial_riding
|
74
|
+
set_canadian_fields(riding, provincial_riding)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def set_congressional_districts(districts)
|
79
|
+
return if districts.empty?
|
80
|
+
|
81
|
+
@congressional_districts = districts.map { |district| CongressionalDistrict.new(district) }
|
82
|
+
end
|
83
|
+
|
84
|
+
def set_legislative_districts(districts)
|
85
|
+
return if districts.empty?
|
86
|
+
@house_districts, @senate_districts = districts.values_at('house', 'senate').map do |district_entries|
|
87
|
+
[*district_entries].map{|entry| StateLegislativeDistrict.new(entry)}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def set_school_districts(schools)
|
92
|
+
return if schools.empty?
|
93
|
+
|
94
|
+
if schools['unified']
|
95
|
+
@unified_school_district = SchoolDistrict.new(schools['unified'])
|
96
|
+
else
|
97
|
+
@elementary_school_district = SchoolDistrict.new(schools['elementary']) if schools['elementary']
|
98
|
+
@secondary_school_district = SchoolDistrict.new(schools['secondary']) if schools['secondary']
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def set_timezone(timezone)
|
103
|
+
return if timezone.empty?
|
104
|
+
|
105
|
+
@timezone = Timezone.new(timezone)
|
106
|
+
end
|
107
|
+
|
108
|
+
def <=>(address)
|
109
|
+
return -1 if self.accuracy < address.accuracy
|
110
|
+
return 0 if self.accuracy == address.accuracy
|
111
|
+
return 1 if self.accuracy > address.accuracy
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# AddressSet is a collection of Geocodio::Address objects that get returned from
|
2
|
+
# a query to geocod.io. Because one query can return multiple results, each with
|
3
|
+
# an accuracy, a collection to manage them is needed. Most of the time, the user
|
4
|
+
# should only need to use the #best method.
|
5
|
+
module Geocodio
|
6
|
+
class AddressSet
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
# Returns the query that retrieved this result set.
|
10
|
+
#
|
11
|
+
# @return [String] the original query
|
12
|
+
attr_reader :query
|
13
|
+
|
14
|
+
def initialize(query, *addresses, input: nil)
|
15
|
+
@query = query
|
16
|
+
@addresses = addresses
|
17
|
+
@formatted_input = input
|
18
|
+
end
|
19
|
+
|
20
|
+
def each(&block)
|
21
|
+
@addresses.each(&block)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the result that geocod.io deemed the most accurate for the query.
|
25
|
+
#
|
26
|
+
# @return [Geocodio::Address] the most accurate address
|
27
|
+
def best
|
28
|
+
best = @addresses.find { |address| address.to_s == @formatted_input }
|
29
|
+
return best if best
|
30
|
+
|
31
|
+
max_by(&:accuracy)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the number of addresses contained in this result set.
|
35
|
+
#
|
36
|
+
# @return [Integer] the number of addresses
|
37
|
+
def size
|
38
|
+
@addresses.size
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns whether or not there are any addresses in this result set.
|
42
|
+
#
|
43
|
+
# @return [Boolean] if there were any results returned by Geocodio
|
44
|
+
def empty?
|
45
|
+
@addresses.empty?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Geocodio
|
2
|
+
module Canadian
|
3
|
+
class ElectoralDistrictBase
|
4
|
+
attr_accessor :ocd_id
|
5
|
+
attr_accessor :name_french
|
6
|
+
attr_accessor :name_english
|
7
|
+
attr_accessor :source
|
8
|
+
|
9
|
+
def initialize(payload = {})
|
10
|
+
@ocd_id = payload['ocd_id']
|
11
|
+
@name_english = payload['name_english']
|
12
|
+
@name_french = payload['name_french']
|
13
|
+
@source = payload['source']
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
@name_english
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'geocodio/canadian/electoral_district_base'
|
2
|
+
require 'geocodio/canadian/federal_electoral_district'
|
3
|
+
require 'geocodio/canadian/provincial_electoral_district'
|
4
|
+
|
5
|
+
module Geocodio
|
6
|
+
module Canadian
|
7
|
+
class ElectoralDistricts
|
8
|
+
attr_accessor :federal_electoral_district
|
9
|
+
attr_accessor :provincial_electoral_district
|
10
|
+
|
11
|
+
def initialize(riding, provincial_riding)
|
12
|
+
|
13
|
+
if riding && riding.is_a?(Hash) && riding.size > 0
|
14
|
+
@federal_electoral_district = FederalElectoralDistrict.new(riding)
|
15
|
+
end
|
16
|
+
|
17
|
+
if provincial_riding && provincial_riding.is_a?(Hash) && provincial_riding.size > 0
|
18
|
+
@provincial_electoral_district = ProvincialElectoralDistrict.new(provincial_riding)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Geocodio
|
2
|
+
class Client
|
3
|
+
class Error < RuntimeError
|
4
|
+
attr_reader :response
|
5
|
+
|
6
|
+
def initialize(response)
|
7
|
+
@response, @json = response, JSON.parse(response.body)
|
8
|
+
end
|
9
|
+
|
10
|
+
def body() @json end
|
11
|
+
def message() body['error'] end
|
12
|
+
alias :error :message
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
require 'geocodio/client/error'
|
6
|
+
require 'geocodio/client/response'
|
7
|
+
require 'geocodio/utils'
|
8
|
+
|
9
|
+
module Geocodio
|
10
|
+
class Client
|
11
|
+
include Geocodio::Utils
|
12
|
+
|
13
|
+
CONTENT_TYPE = 'application/json'
|
14
|
+
METHODS = {
|
15
|
+
:get => Net::HTTP::Get,
|
16
|
+
:post => Net::HTTP::Post,
|
17
|
+
:put => Net::HTTP::Put,
|
18
|
+
:delete => Net::HTTP::Delete
|
19
|
+
}
|
20
|
+
HOST = 'api.geocod.io'
|
21
|
+
BASE_PATH = '/v1.7'
|
22
|
+
PORT = 80
|
23
|
+
|
24
|
+
def initialize(api_key = ENV['GEOCODIO_API_KEY'])
|
25
|
+
@api_key = api_key
|
26
|
+
end
|
27
|
+
|
28
|
+
# Geocodes one or more addresses. If one address is specified, a GET request
|
29
|
+
# is submitted to http://api.geocod.io/v1/geocode. Multiple addresses will
|
30
|
+
# instead submit a POST request.
|
31
|
+
#
|
32
|
+
# @param [Array<String>] addresses one or more String addresses
|
33
|
+
# @param [Hash] options an options hash
|
34
|
+
# @option options [Array] :fields a list of option fields to request (possible: "cd" or "cd113", "stateleg", "school", "timezone")
|
35
|
+
# @return [Geocodio::Address, Array<Geocodio::AddressSet>] One or more Address Sets
|
36
|
+
def geocode(addresses, options = {})
|
37
|
+
if addresses.size < 1
|
38
|
+
raise ArgumentError, 'You must provide at least one address to geocode.'
|
39
|
+
elsif addresses.size == 1
|
40
|
+
geocode_single(addresses.first, options)
|
41
|
+
else
|
42
|
+
geocode_batch(addresses, options)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Reverse geocodes one or more pairs of coordinates. Coordinate pairs may be
|
47
|
+
# specified either as a comma-separated "latitude,longitude" string, or as
|
48
|
+
# a Hash with :lat/:latitude and :lng/:longitude keys. If one pair of
|
49
|
+
# coordinates is specified, a GET request is submitted to
|
50
|
+
# http://api.geocod.io/v1/reverse. Multiple pairs of coordinates will
|
51
|
+
# instead submit a POST request.
|
52
|
+
#
|
53
|
+
# @param [Array<String>, Array<Hash>] coordinates one or more pairs of coordinates
|
54
|
+
# @param [Hash] options an options hash
|
55
|
+
# @option options [Array] :fields a list of option fields to request (possible: "cd" or "cd113", "stateleg", "school", "timezone")
|
56
|
+
# @return [Geocodio::Address, Array<Geocodio::AddressSet>] One or more Address Sets
|
57
|
+
def reverse_geocode(coordinates, options = {})
|
58
|
+
if coordinates.size < 1
|
59
|
+
raise ArgumentError, 'You must provide coordinates to reverse geocode.'
|
60
|
+
elsif coordinates.size == 1
|
61
|
+
reverse_geocode_single(coordinates.first, options)
|
62
|
+
else
|
63
|
+
reverse_geocode_batch(coordinates, options)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
alias :reverse :reverse_geocode
|
67
|
+
|
68
|
+
# Sends a GET request to http://api.geocod.io/v1/parse to correctly dissect
|
69
|
+
# an address into individual parts. As this endpoint does not do any
|
70
|
+
# geocoding, parts missing from the passed address will be missing from the
|
71
|
+
# result.
|
72
|
+
#
|
73
|
+
# @param address [String] the full or partial address to parse
|
74
|
+
# @return [Geocodio::Address] a parsed and formatted Address
|
75
|
+
def parse(address, options = {})
|
76
|
+
params, options = normalize_params_and_options(options)
|
77
|
+
params[:q] = address
|
78
|
+
|
79
|
+
Address.new get('/parse', params, options).body
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
METHODS.each do |method, _|
|
85
|
+
define_method(method) do |path, params = {}, options = {}|
|
86
|
+
request method, path, options.merge(params: params)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def geocode_single(address, options = {})
|
91
|
+
params, options = normalize_params_and_options(options)
|
92
|
+
params[:q] = address
|
93
|
+
|
94
|
+
response = get '/geocode', params, options
|
95
|
+
addresses, input = parse_results(response)
|
96
|
+
|
97
|
+
AddressSet.new(address, *addresses, input: input)
|
98
|
+
end
|
99
|
+
|
100
|
+
def reverse_geocode_single(pair, options = {})
|
101
|
+
params, options = normalize_params_and_options(options)
|
102
|
+
pair = normalize_coordinates(pair)
|
103
|
+
params[:q] = pair
|
104
|
+
|
105
|
+
response = get '/reverse', params, options
|
106
|
+
addresses, input = parse_results(response)
|
107
|
+
|
108
|
+
AddressSet.new(pair, *addresses, input: input)
|
109
|
+
end
|
110
|
+
|
111
|
+
def geocode_batch(addresses, options = {})
|
112
|
+
params, options = normalize_params_and_options(options)
|
113
|
+
options[:body] = addresses
|
114
|
+
|
115
|
+
response = post '/geocode', params, options
|
116
|
+
|
117
|
+
parse_nested_results(response)
|
118
|
+
end
|
119
|
+
|
120
|
+
def reverse_geocode_batch(pairs, options = {})
|
121
|
+
params, options = normalize_params_and_options(options)
|
122
|
+
options[:body] = pairs.map { |pair| normalize_coordinates(pair) }
|
123
|
+
|
124
|
+
response = post '/reverse', params, options
|
125
|
+
|
126
|
+
parse_nested_results(response)
|
127
|
+
end
|
128
|
+
|
129
|
+
def request(method, path, options)
|
130
|
+
path += "?api_key=#{@api_key}"
|
131
|
+
|
132
|
+
if params = options[:params] and !params.empty?
|
133
|
+
q = params.map { |k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }
|
134
|
+
path += "&#{q.join('&')}"
|
135
|
+
end
|
136
|
+
|
137
|
+
req = METHODS[method].new(BASE_PATH + path, 'Accept' => CONTENT_TYPE)
|
138
|
+
|
139
|
+
if options.key?(:body)
|
140
|
+
req['Content-Type'] = CONTENT_TYPE
|
141
|
+
req.body = options[:body] ? JSON.dump(options[:body]) : ''
|
142
|
+
end
|
143
|
+
http = Net::HTTP.new HOST, PORT
|
144
|
+
http.read_timeout = options[:timeout] if options[:timeout]
|
145
|
+
res = http.start { http.request(req) }
|
146
|
+
|
147
|
+
case res
|
148
|
+
when Net::HTTPSuccess
|
149
|
+
return Response.new(res)
|
150
|
+
else
|
151
|
+
raise Error, res
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'geocodio/legislator'
|
2
|
+
|
3
|
+
module Geocodio
|
4
|
+
class CongressionalDistrict
|
5
|
+
attr_reader :name
|
6
|
+
attr_reader :district_number
|
7
|
+
attr_reader :congress_number
|
8
|
+
attr_reader :proportion
|
9
|
+
attr_reader :current_legislators
|
10
|
+
attr_accessor :ocd_id
|
11
|
+
|
12
|
+
def initialize(payload = {})
|
13
|
+
@name = payload['name']
|
14
|
+
@district_number = payload['district_number'].to_i
|
15
|
+
@congress_number = payload['congress_number'].to_i
|
16
|
+
@congress_years = payload['congress_years']
|
17
|
+
@proportion = payload['proportion'].to_i
|
18
|
+
@ocd_id = payload['ocd_id']
|
19
|
+
|
20
|
+
@current_legislators = [*payload['current_legislators']].map do |legislator|
|
21
|
+
Legislator.new(legislator)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def congress_years
|
26
|
+
first, last = @congress_years.split('-').map(&:to_i)
|
27
|
+
first..last
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Geocodio
|
2
|
+
class Legislator
|
3
|
+
attr_reader :type
|
4
|
+
attr_reader :name
|
5
|
+
attr_reader :birthday
|
6
|
+
attr_reader :gender
|
7
|
+
attr_reader :party
|
8
|
+
attr_reader :url
|
9
|
+
attr_reader :address
|
10
|
+
attr_reader :phone
|
11
|
+
attr_reader :contact_form
|
12
|
+
attr_reader :rss_url
|
13
|
+
attr_reader :twitter
|
14
|
+
attr_reader :facebook
|
15
|
+
attr_reader :youtube
|
16
|
+
attr_reader :youtube_id
|
17
|
+
attr_reader :bioguide_id
|
18
|
+
attr_reader :thomas_id
|
19
|
+
attr_reader :opensecrets_id
|
20
|
+
attr_reader :lis_id
|
21
|
+
attr_reader :cspan_id
|
22
|
+
attr_reader :govtrack_id
|
23
|
+
attr_reader :votesmart_id
|
24
|
+
attr_reader :ballotpedia_id
|
25
|
+
attr_reader :washington_post_id
|
26
|
+
attr_reader :icpsr_id
|
27
|
+
attr_reader :wikipedia_id
|
28
|
+
|
29
|
+
def initialize(payload = {})
|
30
|
+
@type = payload['type']
|
31
|
+
|
32
|
+
if payload['bio']
|
33
|
+
@name = "#{payload['bio']['first_name']} #{payload['bio']['last_name']}"
|
34
|
+
@birthday = Date.new(*payload['bio']['birthday'].split('-').map(&:to_i))
|
35
|
+
@gender = payload['bio']['gender']
|
36
|
+
@party = payload['bio']['party']
|
37
|
+
end
|
38
|
+
|
39
|
+
if payload['contact']
|
40
|
+
@url = payload['contact']['url']
|
41
|
+
@address = payload['contact']['address']
|
42
|
+
@phone = payload['contact']['phone']
|
43
|
+
@contact_form = payload['contact']['contact_form']
|
44
|
+
end
|
45
|
+
|
46
|
+
if payload['social']
|
47
|
+
@rss_url = payload['social']['rss_url']
|
48
|
+
@twitter = payload['social']['twitter']
|
49
|
+
@facebook = payload['social']['facebook']
|
50
|
+
@youtube = payload['social']['youtube']
|
51
|
+
@youtube_id = payload['social']['youtube_id']
|
52
|
+
end
|
53
|
+
|
54
|
+
if payload['references']
|
55
|
+
@bioguide_id = payload['references']['bioguide_id']
|
56
|
+
@thomas_id = payload['references']['thomas_id']
|
57
|
+
@opensecrets_id = payload['references']['opensecrets_id']
|
58
|
+
@lis_id = payload['references']['lis_id']
|
59
|
+
@cspan_id = payload['references']['cspan_id']
|
60
|
+
@govtrack_id = payload['references']['govtrack_id']
|
61
|
+
@votesmart_id = payload['references']['votesmart_id']
|
62
|
+
@ballotpedia_id = payload['references']['ballotpedia_id']
|
63
|
+
@washington_post_id = payload['references']['washington_post_id']
|
64
|
+
@icpsr_id = payload['references']['icpsr_id']
|
65
|
+
@wikipedia_id = payload['references']['wikipedia_id']
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Geocodio
|
2
|
+
class SchoolDistrict
|
3
|
+
attr_reader :name
|
4
|
+
attr_reader :lea_code
|
5
|
+
attr_reader :grade_low
|
6
|
+
attr_reader :grade_high
|
7
|
+
|
8
|
+
def initialize(payload = {})
|
9
|
+
@name = payload['name']
|
10
|
+
@lea_code = payload['lea_code']
|
11
|
+
@grade_low = payload['grade_low']
|
12
|
+
@grade_high = payload['grade_high']
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Geocodio
|
2
|
+
class StateLegislativeDistrict
|
3
|
+
attr_accessor :name
|
4
|
+
attr_accessor :district_number
|
5
|
+
attr_accessor :proportion
|
6
|
+
attr_accessor :ocd_id
|
7
|
+
|
8
|
+
def initialize(payload = {})
|
9
|
+
@name = payload['name']
|
10
|
+
@district_number = payload['district_number'].to_i
|
11
|
+
@district_number = payload['district_number'] if @district_number == 0
|
12
|
+
@proportion = payload['proportion']
|
13
|
+
@ocd_id = payload['ocd_id']
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Geocodio
|
2
|
+
class Timezone
|
3
|
+
attr_reader :name
|
4
|
+
attr_reader :utc_offset
|
5
|
+
|
6
|
+
def initialize(payload = {})
|
7
|
+
@name = payload['name']
|
8
|
+
@utc_offset = payload['utc_offset']
|
9
|
+
@observes_dst = payload['observes_dst']
|
10
|
+
end
|
11
|
+
|
12
|
+
def observes_dst?
|
13
|
+
!!@observes_dst
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Geocodio
|
2
|
+
module Utils
|
3
|
+
def parse_results(response)
|
4
|
+
results = response.body['results']
|
5
|
+
input = response.body['input']['formatted_address'] if response.body['input']
|
6
|
+
[results.map { |result| Address.new(result) }, input]
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse_nested_results(response)
|
10
|
+
results = response.body['results']
|
11
|
+
|
12
|
+
results = results.map do |result_set|
|
13
|
+
addresses = Array(result_set['response']['results'])
|
14
|
+
addresses.map! { |result| Address.new(result) }
|
15
|
+
|
16
|
+
query = result_set['query']
|
17
|
+
input = result_set['response']['input']['formatted_address'] if result_set['response']['input']
|
18
|
+
|
19
|
+
AddressSet.new(query, *addresses, input: input)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def normalize_coordinates(coordinates)
|
24
|
+
return coordinates unless coordinates.is_a?(Hash)
|
25
|
+
coordinates.sort.map { |p| p[1] }.join(',')
|
26
|
+
end
|
27
|
+
|
28
|
+
def normalize_params_and_options(hash)
|
29
|
+
hash = Hash[hash.map { |k, v| [k.to_sym, v] }]
|
30
|
+
|
31
|
+
# The only supported parameter is fields
|
32
|
+
params = hash.select { |k, _| [:fields].include?(k) }
|
33
|
+
|
34
|
+
# Normalize this particular parameter to be a comma-separated string
|
35
|
+
params[:fields] = params[:fields].join(',') if params[:fields]
|
36
|
+
|
37
|
+
# The only supported option is `timeout`
|
38
|
+
options = hash.select { |k, _| [:timeout].include?(k) }
|
39
|
+
|
40
|
+
[params, options]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|