geocodio-ocd 1.7.0
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.
- 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
|