From b49b3e6f0a4f71b54b35917474ba0cf3628baf25 Mon Sep 17 00:00:00 2001 From: David Celis Date: Fri, 28 Mar 2014 09:28:31 -0700 Subject: [PATCH] Add support for optional fields when geocoding This will require a major version bump, as the API has changed: parsing options hashes out of the splatted arrays was proving way too hairy for reverse geocoding, so both `geocode` and `reverse_geocode` now expect to be handed an array at all times and an optional hash of options. Signed-off-by: David Celis --- README.md | 40 +++- lib/geocodio/address.rb | 55 ++++-- lib/geocodio/client.rb | 49 +++-- lib/geocodio/congressional_district.rb | 19 ++ lib/geocodio/school_district.rb | 15 ++ lib/geocodio/state_legislative_district.rb | 11 ++ lib/geocodio/timezone.rb | 16 ++ spec/address_set_spec.rb | 2 +- spec/address_spec.rb | 38 +++- spec/client_spec.rb | 173 ++++++++++++------ spec/congressional_district_spec.rb | 28 +++ spec/school_district_spec.rb | 27 +++ spec/state_legislative_district_spec.rb | 19 ++ spec/timezone_spec.rb | 23 +++ .../batch_geocode_with_fields.yml | 78 ++++++++ .../batch_reverse_with_fields.yml | 96 ++++++++++ spec/vcr_cassettes/geocode_with_fields.yml | 53 ++++++ spec/vcr_cassettes/reverse_with_fields.yml | 58 ++++++ 18 files changed, 697 insertions(+), 103 deletions(-) create mode 100644 lib/geocodio/congressional_district.rb create mode 100644 lib/geocodio/school_district.rb create mode 100644 lib/geocodio/state_legislative_district.rb create mode 100644 lib/geocodio/timezone.rb create mode 100644 spec/congressional_district_spec.rb create mode 100644 spec/school_district_spec.rb create mode 100644 spec/state_legislative_district_spec.rb create mode 100644 spec/timezone_spec.rb create mode 100644 spec/vcr_cassettes/batch_geocode_with_fields.yml create mode 100644 spec/vcr_cassettes/batch_reverse_with_fields.yml create mode 100644 spec/vcr_cassettes/geocode_with_fields.yml create mode 100644 spec/vcr_cassettes/reverse_with_fields.yml diff --git a/README.md b/README.md index d7fdac7..e37fb9f 100644 --- a/README.md +++ b/README.md @@ -25,10 +25,10 @@ geocodio = Geocodio::Client.new ### Geocoding -The `Geocodio::Client#geocode` method is used to request coordinates and expanded information on one or more addresses. It is possible for a geocoding request to yield multiple results with varying degrees of accuracy, so the `geocode` method will always return one `Geocodio::AddressSet` for each query made: +The `Geocodio::Client#geocode` method is used to request coordinates and expanded information on one or more addresses. It accepts an array of addresses and an options hash. If more than one address is provided, `#geocode` will use Geocodio's batch endpoint behind the scenes. It is possible for a geocoding request to yield multiple results with varying degrees of accuracy, so the `geocode` method will always return one `Geocodio::AddressSet` for each query made: ```ruby -results = geocodio.geocode('1 Infinite Loop, Cupertino, CA 95014') +results = geocodio.geocode(['1 Infinite Loop, Cupertino, CA 95014']) # => # ``` @@ -54,10 +54,10 @@ puts address.longitude # or address.lng # => -122.03074 ``` -To perform a batch geocoding operation, simply pass multiple addresses to `Geocodio::Client#geocode`: +To perform a batch geocoding operation as mentioned earlier, simply add more addresses to the passed array: ```ruby -result_sets = geocodio.geocode('1 Infinite Loop, Cupertino, CA 95014', '54 West Colorado Boulevard, Pasadena, CA 91105') +result_sets = geocodio.geocode(['1 Infinite Loop, Cupertino, CA 95014', '54 West Colorado Boulevard, Pasadena, CA 91105']) # => [#, #] cupertino = result_sets.first.best @@ -69,17 +69,17 @@ cupertino = result_sets.first.best The interface to reverse geocoding is very similar to geocoding. Use the `Geocodio::Client#reverse_geocode` method (aliased to `Geocodio::Client#reverse`) with one or more pairs of coordinates: ```ruby -addresses = geocodio.reverse_geocode('37.331669,-122.03074') +addresses = geocodio.reverse_geocode(['37.331669,-122.03074']) # => # -address_sets = geocodio.reverse_geocode('37.331669,-122.03074', '34.145760590909,-118.15204363636') +address_sets = geocodio.reverse_geocode(['37.331669,-122.03074', '34.145760590909,-118.15204363636']) # => [#, #] ``` Coordinate pairs can also be specified as hashes: ```ruby -address_sets = geocodio.reverse_geocode({ lat: 37.331669, lng: -122.03074 }, { latitude: 34.145760590909, longitude: -118.15204363636 }) +address_sets = geocodio.reverse_geocode([{ lat: 37.331669, lng: -122.03074 }, { latitude: 34.145760590909, longitude: -118.15204363636 }]) # => [#, #] ``` @@ -92,6 +92,31 @@ address = geocodio.parse('1 Infinite Loop, Cupertino, CA 95014') Note that this endpoint performs no geocoding; it merely formats a single provided address according to geocod.io's standards. +### Additional fields + +Geocodio has added support for retrieving [additional fields][fields] when geocoding or reverse geocoding. To request these fields, pass an options hash to either `#geocode` or `#reverse_geocode`. Possible fields include `cd` or `cd113`, `stateleg`, `school`, and `timezone`: + +```ruby +address = geocodio.geocode(['54 West Colorado Boulevard Pasadena CA 91105'], fields: %w[cd stateleg school timezone]) + +address.congressional_district +# => # + +address.house_district +# => # + +address.senate_district +# => # + +address.unified_school_district # or .elementary_school_district and .secondary_school_district if not unified +# => # + +address.timezone +# => # +address.timezone.observes_dst? +# => true +``` + ## Contributing 1. Fork it ( http://github.com/davidcelis/geocodio/fork ) @@ -101,3 +126,4 @@ Note that this endpoint performs no geocoding; it merely formats a single provid 5. Create new Pull Request [geocod.io]: http://geocod.io/ +[fields]: http://geocod.io/docs/?ruby#toc_17 diff --git a/lib/geocodio/address.rb b/lib/geocodio/address.rb index 3fd245d..1742079 100644 --- a/lib/geocodio/address.rb +++ b/lib/geocodio/address.rb @@ -1,22 +1,26 @@ +require 'geocodio/congressional_district' +require 'geocodio/school_district' +require 'geocodio/state_legislative_district' +require 'geocodio/timezone' + module Geocodio class Address - attr_accessor :number - attr_accessor :predirectional - attr_accessor :street - attr_accessor :suffix - attr_accessor :city - attr_accessor :state - attr_accessor :zip - - attr_accessor :latitude - attr_accessor :longitude + attr_reader :number, :predirectional, :street, :suffix, :city, :state, :zip + + attr_reader :latitude, :longitude alias :lat :latitude alias :lng :longitude - # How accurage geocod.io deemed this result to be given the original query. + attr_reader :congressional_district, :house_district, :senate_district, + :unified_school_district, :elementary_school_district, + :secondary_school_district + + attr_reader :timezone + + # How accurate geocod.io deemed this result to be given the original query. # # @return [Float] a number between 0 and 1 - attr_accessor :accuracy + attr_reader :accuracy def initialize(payload = {}) if payload['address_components'] @@ -34,9 +38,32 @@ def initialize(payload = {}) @longitude = payload['location']['lng'] end - @accuracy = payload['accuracy'] - + @accuracy = payload['accuracy'] @formatted_address = payload['formatted_address'] + + return self unless fields = payload['fields'] + + if fields['congressional_district'] && !fields['congressional_district'].empty? + @congressional_district = CongressionalDistrict.new(fields['congressional_district']) + end + + if fields['state_legislative_districts'] && !fields['state_legislative_districts'].empty? + @house_district = StateLegislativeDistrict.new(fields['state_legislative_districts']['house']) + @senate_district = StateLegislativeDistrict.new(fields['state_legislative_districts']['senate']) + end + + if (schools = fields['school_districts']) && !schools.empty? + if schools['unified'] + @unified_school_district = SchoolDistrict.new(schools['unified']) + else + @elementary_school_district = SchoolDistrict.new(schools['elementary']) + @secondary_school_district = SchoolDistrict.new(schools['secondary']) + end + end + + if fields['timezone'] && !fields['timezone'].empty? + @timezone = Timezone.new(fields['timezone']) + end end # Formats the address in the standard way. diff --git a/lib/geocodio/client.rb b/lib/geocodio/client.rb index 3879228..b827977 100644 --- a/lib/geocodio/client.rb +++ b/lib/geocodio/client.rb @@ -29,17 +29,17 @@ def initialize(api_key = ENV['GEOCODIO_API_KEY']) # is submitted to http://api.geocod.io/v1/geocode. Multiple addresses will # instead submit a POST request. # - # @param addresses [Array] one or more String addresses + # @param [Array] addresses one or more String addresses + # @param [Hash] options an options hash + # @option options [Array] :fields a list of option fields to request (possible: "cd" or "cd113", "stateleg", "school", "timezone") # @return [Geocodio::Address, Array] One or more Address Sets - def geocode(*addresses) - addresses = addresses.first if addresses.first.is_a?(Array) - + def geocode(addresses, options = {}) if addresses.size < 1 raise ArgumentError, 'You must provide at least one address to geocode.' elsif addresses.size == 1 - geocode_single(addresses.first) + geocode_single(addresses.first, options) else - geocode_batch(addresses) + geocode_batch(addresses, options) end end @@ -50,17 +50,17 @@ def geocode(*addresses) # http://api.geocod.io/v1/reverse. Multiple pairs of coordinates will # instead submit a POST request. # - # @param coordinates [Array, Array] one or more pairs of coordinates + # @param [Array, Array] coordinates one or more pairs of coordinates + # @param [Hash] options an options hash + # @option options [Array] :fields a list of option fields to request (possible: "cd" or "cd113", "stateleg", "school", "timezone") # @return [Geocodio::Address, Array] One or more Address Sets - def reverse_geocode(*coordinates) - coordinates = coordinates.first if coordinates.first.is_a?(Array) - + def reverse_geocode(coordinates, options = {}) if coordinates.size < 1 raise ArgumentError, 'You must provide coordinates to reverse geocode.' elsif coordinates.size == 1 - reverse_geocode_single(coordinates.first) + reverse_geocode_single(coordinates.first, options) else - reverse_geocode_batch(coordinates) + reverse_geocode_batch(coordinates, options) end end alias :reverse :reverse_geocode @@ -84,31 +84,40 @@ def parse(address) end end - def geocode_single(address) - response = get '/geocode', q: address + def geocode_single(address, options = {}) + params = { q: address } + params[:fields] = options[:fields].join(',') if options[:fields] + + response = get '/geocode', params addresses = parse_results(response) AddressSet.new(address, *addresses) end - def reverse_geocode_single(pair) + def reverse_geocode_single(pair, options = {}) pair = normalize_coordinates(pair) + params = { q: pair } + params[:fields] = options[:fields].join(',') if options[:fields] - response = get '/reverse', q: pair + response = get '/reverse', params addresses = parse_results(response) AddressSet.new(pair, *addresses) end - def geocode_batch(addresses) - response = post '/geocode', {}, body: addresses + def geocode_batch(addresses, options = {}) + options[:fields] = options[:fields].join(',') if options[:fields] + + response = post '/geocode', options, body: addresses parse_nested_results(response) end - def reverse_geocode_batch(pairs) + def reverse_geocode_batch(pairs, options = {}) pairs.map! { |pair| normalize_coordinates(pair) } - response = post '/reverse', {}, body: pairs + options[:fields] = options[:fields].join(',') if options[:fields] + + response = post '/reverse', options, body: pairs parse_nested_results(response) end diff --git a/lib/geocodio/congressional_district.rb b/lib/geocodio/congressional_district.rb new file mode 100644 index 0000000..bd985b3 --- /dev/null +++ b/lib/geocodio/congressional_district.rb @@ -0,0 +1,19 @@ +module Geocodio + class CongressionalDistrict + attr_reader :name + attr_reader :district_number + attr_reader :congress_number + + def initialize(payload = {}) + @name = payload['name'] + @district_number = payload['district_number'].to_i + @congress_number = payload['congress_number'].to_i + @congress_years = payload['congress_years'] + end + + def congress_years + first, last = @congress_years.split('-').map(&:to_i) + first..last + end + end +end diff --git a/lib/geocodio/school_district.rb b/lib/geocodio/school_district.rb new file mode 100644 index 0000000..fd0484b --- /dev/null +++ b/lib/geocodio/school_district.rb @@ -0,0 +1,15 @@ +module Geocodio + class SchoolDistrict + attr_reader :name + attr_reader :lea_code + attr_reader :grade_low + attr_reader :grade_high + + def initialize(payload = {}) + @name = payload['name'] + @lea_code = payload['lea_code'] + @grade_low = payload['grade_low'] + @grade_high = payload['grade_high'] + end + end +end diff --git a/lib/geocodio/state_legislative_district.rb b/lib/geocodio/state_legislative_district.rb new file mode 100644 index 0000000..2ec36ba --- /dev/null +++ b/lib/geocodio/state_legislative_district.rb @@ -0,0 +1,11 @@ +module Geocodio + class StateLegislativeDistrict + attr_accessor :name + attr_accessor :district_number + + def initialize(payload = {}) + @name = payload['name'] + @district_number = payload['district_number'].to_i + end + end +end diff --git a/lib/geocodio/timezone.rb b/lib/geocodio/timezone.rb new file mode 100644 index 0000000..8bc1d86 --- /dev/null +++ b/lib/geocodio/timezone.rb @@ -0,0 +1,16 @@ +module Geocodio + class Timezone + attr_reader :name + attr_reader :utc_offset + + def initialize(payload = {}) + @name = payload['name'] + @utc_offset = payload['utc_offset'] + @observes_dst = payload['observes_dst'] + end + + def observes_dst? + !!@observes_dst + end + end +end diff --git a/spec/address_set_spec.rb b/spec/address_set_spec.rb index 38918db..ff99d7e 100644 --- a/spec/address_set_spec.rb +++ b/spec/address_set_spec.rb @@ -12,7 +12,7 @@ subject(:address_set) do VCR.use_cassette('batch_geocode') do - geocodio.geocode(*addresses).last + geocodio.geocode(addresses).last end end diff --git a/spec/address_spec.rb b/spec/address_spec.rb index 11e38e0..fcc020d 100644 --- a/spec/address_spec.rb +++ b/spec/address_spec.rb @@ -56,7 +56,7 @@ context 'when geocoded' do subject(:address) do VCR.use_cassette('geocode') do - geocodio.geocode('54 West Colorado Boulevard Pasadena CA 91105').best + geocodio.geocode(['54 West Colorado Boulevard Pasadena CA 91105']).best end end @@ -101,5 +101,41 @@ it 'has an accuracy' do expect(address.accuracy).to eq(1) end + + context 'with additional fields' do + subject(:address) do + VCR.use_cassette('geocode_with_fields') do + geocodio.geocode(['54 West Colorado Boulevard Pasadena CA 91105'], fields: %w[cd stateleg school timezone]).best + end + end + + it 'has a congressional district' do + expect(address.congressional_district).to be_a(Geocodio::CongressionalDistrict) + end + + it 'has a house district' do + expect(address.house_district).to be_a(Geocodio::StateLegislativeDistrict) + end + + it 'has a senate district' do + expect(address.senate_district).to be_a(Geocodio::StateLegislativeDistrict) + end + + it 'has a unified school district' do + expect(address.unified_school_district).to be_a(Geocodio::SchoolDistrict) + end + + it 'could have an elementary school district' do + expect(address.elementary_school_district).to be_nil + end + + it 'could have a secondary school district' do + expect(address.secondary_school_district).to be_nil + end + + it 'has a timezone' do + expect(address.timezone).to be_a(Geocodio::Timezone) + end + end end end diff --git a/spec/client_spec.rb b/spec/client_spec.rb index 5f7975c..b0b1b09 100644 --- a/spec/client_spec.rb +++ b/spec/client_spec.rb @@ -7,7 +7,7 @@ it 'requires an API key' do VCR.use_cassette('invalid_key') do - expect { geocodio.geocode(address) }.to raise_error(Geocodio::Client::Error) + expect { geocodio.geocode([address]) }.to raise_error(Geocodio::Client::Error) end end @@ -19,94 +19,147 @@ end end - it 'geocodes a single address' do - VCR.use_cassette('geocode') do - addresses = geocodio.geocode(address) + describe '#geocode' do + it 'geocodes a single address' do + VCR.use_cassette('geocode') do + addresses = geocodio.geocode([address]) - expect(addresses.size).to eq(2) - expect(addresses).to be_a(Geocodio::AddressSet) - end - end - - context 'reverse geocoding a single address' do - it 'uses a string' do - VCR.use_cassette('reverse') do - addresses = geocodio.reverse_geocode(coordinates) - - expect(addresses.size).to eq(3) + expect(addresses.size).to eq(2) expect(addresses).to be_a(Geocodio::AddressSet) end end - it 'uses a hash' do - VCR.use_cassette('reverse') do - lat, lng = coordinates.split(',') - addresses = geocodio.reverse_geocode(latitude: lat, longitude: lng) + it 'geocodes a single address with an options hash' do + VCR.use_cassette('geocode_with_fields') do + addresses = geocodio.geocode([address], fields: %w[cd stateleg school timezone]) - expect(addresses.size).to eq(3) + expect(addresses.size).to eq(2) expect(addresses).to be_a(Geocodio::AddressSet) end end - end - it 'geocodes multiple addresses' do - VCR.use_cassette('batch_geocode') do - addresses = [ - '1 Infinite Loop Cupertino CA 95014', - '54 West Colorado Boulevard Pasadena CA 91105', - '826 Howard Street San Francisco CA 94103' - ] + it 'geocodes multiple addresses' do + VCR.use_cassette('batch_geocode') do + addresses = [ + '1 Infinite Loop Cupertino CA 95014', + '54 West Colorado Boulevard Pasadena CA 91105', + '826 Howard Street San Francisco CA 94103' + ] - addresses = geocodio.geocode(*addresses) + addresses = geocodio.geocode(addresses) - expect(addresses.size).to eq(3) - addresses.each { |address| expect(address).to be_a(Geocodio::AddressSet) } + expect(addresses.size).to eq(3) + addresses.each { |address| expect(address).to be_a(Geocodio::AddressSet) } + end end - end - context 'reverse geocoding multiple addresses' do - it 'uses strings' do - VCR.use_cassette('batch_reverse') do - coordinate_pairs = [ - '37.331669,-122.03074', - '34.145760590909,-118.15204363636', - '37.7815,-122.404933' + it 'geocodes multiple addresses with an options hash' do + VCR.use_cassette('batch_geocode_with_fields') do + addresses = [ + '1 Infinite Loop Cupertino CA 95014', + '54 West Colorado Boulevard Pasadena CA 91105', + '826 Howard Street San Francisco CA 94103' ] - addresses = geocodio.reverse_geocode(*coordinate_pairs) + addresses = geocodio.geocode(addresses, fields: %w[cd stateleg school timezone]) expect(addresses.size).to eq(3) addresses.each { |address| expect(address).to be_a(Geocodio::AddressSet) } end end + end - it 'uses hashes' do - VCR.use_cassette('batch_reverse') do - coordinate_pairs = [ - { latitude: 37.331669, longitude: -122.03074 }, - { latitude: 34.145760590909, longitude: -118.15204363636 }, - { latitude: 37.7815, longitude: -122.404933 } - ] + describe '#reverse_geocode' do + context 'with a single address' do + it 'uses strings' do + VCR.use_cassette('reverse') do + addresses = geocodio.reverse_geocode([coordinates]) - addresses = geocodio.reverse_geocode(*coordinate_pairs) + expect(addresses.size).to eq(3) + expect(addresses).to be_a(Geocodio::AddressSet) + end + end - expect(addresses.size).to eq(3) - addresses.each { |address| expect(address).to be_a(Geocodio::AddressSet) } + it 'uses hashes' do + VCR.use_cassette('reverse') do + lat, lng = coordinates.split(',') + addresses = geocodio.reverse_geocode([{ latitude: lat, longitude: lng }]) + + expect(addresses.size).to eq(3) + expect(addresses).to be_a(Geocodio::AddressSet) + end + end + + it 'accepts an options hash' do + VCR.use_cassette('reverse_with_fields') do + lat, lng = coordinates.split(',') + addresses = geocodio.reverse_geocode([{ latitude: lat, longitude: lng} ], fields: %w[cd stateleg school timezone]) + + expect(addresses.size).to eq(3) + expect(addresses).to be_a(Geocodio::AddressSet) + end end end - it 'uses an arbitrary combination of strings and hashes' do - VCR.use_cassette('batch_reverse') do - coordinate_pairs = [ - { latitude: 37.331669, longitude: -122.03074 }, - '34.145760590909,-118.15204363636', - { latitude: 37.7815, longitude: -122.404933 } - ] + context 'reverse geocoding multiple addresses' do + it 'uses strings' do + VCR.use_cassette('batch_reverse') do + coordinate_pairs = [ + '37.331669,-122.03074', + '34.145760590909,-118.15204363636', + '37.7815,-122.404933' + ] - addresses = geocodio.reverse_geocode(*coordinate_pairs) + addresses = geocodio.reverse_geocode(coordinate_pairs) - expect(addresses.size).to eq(3) - addresses.each { |address| expect(address).to be_a(Geocodio::AddressSet) } + expect(addresses.size).to eq(3) + addresses.each { |address| expect(address).to be_a(Geocodio::AddressSet) } + end + end + + it 'uses hashes' do + VCR.use_cassette('batch_reverse') do + coordinate_pairs = [ + { latitude: 37.331669, longitude: -122.03074 }, + { latitude: 34.145760590909, longitude: -118.15204363636 }, + { latitude: 37.7815, longitude: -122.404933 } + ] + + addresses = geocodio.reverse_geocode(coordinate_pairs) + + expect(addresses.size).to eq(3) + addresses.each { |address| expect(address).to be_a(Geocodio::AddressSet) } + end + end + + it 'uses an arbitrary combination of strings and hashes' do + VCR.use_cassette('batch_reverse') do + coordinate_pairs = [ + { latitude: 37.331669, longitude: -122.03074 }, + '34.145760590909,-118.15204363636', + { latitude: 37.7815, longitude: -122.404933 } + ] + + addresses = geocodio.reverse_geocode(coordinate_pairs) + + expect(addresses.size).to eq(3) + addresses.each { |address| expect(address).to be_a(Geocodio::AddressSet) } + end + end + + it 'accepts an options hash' do + VCR.use_cassette('batch_reverse_with_fields') do + coordinate_pairs = [ + { latitude: 37.331669, longitude: -122.03074 }, + '34.145760590909,-118.15204363636', + { latitude: 37.7815, longitude: -122.404933 } + ] + + addresses = geocodio.reverse_geocode(coordinate_pairs, fields: %w[cd stateleg school timezone]) + + expect(addresses.size).to eq(3) + addresses.each { |address| expect(address).to be_a(Geocodio::AddressSet) } + end end end end diff --git a/spec/congressional_district_spec.rb b/spec/congressional_district_spec.rb new file mode 100644 index 0000000..97567e3 --- /dev/null +++ b/spec/congressional_district_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe Geocodio::CongressionalDistrict do + let(:geocodio) { Geocodio::Client.new } + + subject(:district) do + VCR.use_cassette('geocode_with_fields') do + geocodio.geocode(['54 West Colorado Boulevard Pasadena CA 91105'], fields: %w[cd stateleg school timezone]).best.congressional_district + end + end + + it 'has a name' do + expect(district.name).to eq("Congressional District 27") + end + + it 'has a district_number' do + expect(district.district_number).to eq(27) + end + + it 'has a congress_number' do + expect(district.congress_number).to eq(113) + end + + it 'has a congress_years' do + expect(district.congress_years).to eq(2013..2015) + end + +end diff --git a/spec/school_district_spec.rb b/spec/school_district_spec.rb new file mode 100644 index 0000000..9e81bba --- /dev/null +++ b/spec/school_district_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe Geocodio::SchoolDistrict do + let(:geocodio) { Geocodio::Client.new } + + subject(:district) do + VCR.use_cassette('geocode_with_fields') do + geocodio.geocode(['54 West Colorado Boulevard Pasadena CA 91105'], fields: %w[cd stateleg school timezone]).best.unified_school_district + end + end + + it 'has a name' do + expect(district.name).to eq('Pasadena Unified School District') + end + + it 'has a lea_code' do + expect(district.lea_code).to eq('29940') + end + + it 'has a grade_low' do + expect(district.grade_low).to eq('KG') + end + + it 'has a grade_high' do + expect(district.grade_high).to eq('12') + end +end diff --git a/spec/state_legislative_district_spec.rb b/spec/state_legislative_district_spec.rb new file mode 100644 index 0000000..8e5bb5c --- /dev/null +++ b/spec/state_legislative_district_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe Geocodio::StateLegislativeDistrict do + let(:geocodio) { Geocodio::Client.new } + + subject(:district) do + VCR.use_cassette('geocode_with_fields') do + geocodio.geocode(['54 West Colorado Boulevard Pasadena CA 91105'], fields: %w[cd stateleg school timezone]).best.house_district + end + end + + it 'has a name' do + expect(district.name).to eq("Assembly District 41") + end + + it 'has a district_number' do + expect(district.district_number).to eq(41) + end +end diff --git a/spec/timezone_spec.rb b/spec/timezone_spec.rb new file mode 100644 index 0000000..394a0b5 --- /dev/null +++ b/spec/timezone_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe Geocodio::Timezone do + let(:geocodio) { Geocodio::Client.new } + + subject(:timezone) do + VCR.use_cassette('geocode_with_fields') do + geocodio.geocode(['54 West Colorado Boulevard Pasadena CA 91105'], fields: %w[cd stateleg school timezone]).best.timezone + end + end + + it 'has a name' do + expect(timezone.name).to eq('PST') + end + + it 'has a utc_offset' do + expect(timezone.utc_offset).to eq(-8) + end + + it 'observes DST' do + expect(timezone).to be_observes_dst + end +end diff --git a/spec/vcr_cassettes/batch_geocode_with_fields.yml b/spec/vcr_cassettes/batch_geocode_with_fields.yml new file mode 100644 index 0000000..929dae4 --- /dev/null +++ b/spec/vcr_cassettes/batch_geocode_with_fields.yml @@ -0,0 +1,78 @@ +--- +http_interactions: +- request: + method: post + uri: http://api.geocod.io/v1/geocode?api_key=secret_api_key&fields=cd,stateleg,school,timezone + body: + encoding: UTF-8 + string: '["1 Infinite Loop Cupertino CA 95014","54 West Colorado Boulevard Pasadena + CA 91105","826 Howard Street San Francisco CA 94103"]' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + User-Agent: + - Ruby + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx/1.2.1 + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Cache-Control: + - no-cache + Date: + - Fri, 28 Mar 2014 16:00:59 GMT + Access-Control-Allow-Origin: + - "*" + body: + encoding: UTF-8 + string: '{"results":[{"query":"1 Infinite Loop Cupertino CA 95014","response":{"input":{"address_components":{"number":"1","street":"Infinite","suffix":"Loop","city":"Cupertino","state":"CA","zip":"95014"},"formatted_address":"1 + Infinite Loop, Cupertino, CA 95014"},"results":[{"address_components":{"number":"1","street":"Infinite","suffix":"Loop","city":"Cupertino","county":"Santa + Clara County","state":"CA","zip":"95014"},"formatted_address":"1 Infinite + Loop, Cupertino, CA 95014","location":{"lat":37.331669,"lng":-122.03074},"accuracy":1,"fields":{"congressional_district":{"name":"Congressional + District 17","district_number":17,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 15","district_number":15},"house":{"name":"Assembly District + 15","district_number":15}},"school_districts":{"secondary":{"name":"Fremont + Union High School District","lea_code":"14430","grade_low":"09","grade_high":"12"},"elementary":{"name":"Cupertino + Union Elementary School District","lea_code":"10290","grade_low":"KG","grade_high":"08"}},"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}}]}},{"query":"54 + West Colorado Boulevard Pasadena CA 91105","response":{"input":{"address_components":{"number":"54","predirectional":"W","street":"Colorado","suffix":"Blvd","city":"Pasadena","state":"CA","zip":"91105"},"formatted_address":"54 + W Colorado Blvd, Pasadena, CA 91105"},"results":[{"address_components":{"number":"54","predirectional":"W","street":"Colorado","suffix":"Blvd","city":"Pasadena","county":"Los + Angeles County","state":"CA","zip":"91105"},"formatted_address":"54 W Colorado + Blvd, Pasadena, CA 91105","location":{"lat":34.145760590909,"lng":-118.15204363636},"accuracy":1,"fields":{"congressional_district":{"name":"Congressional + District 27","district_number":27,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 25","district_number":25},"house":{"name":"Assembly District + 41","district_number":41}},"school_districts":{"unified":{"name":"Pasadena + Unified School District","lea_code":"29940","grade_low":"KG","grade_high":"12"}},"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}},{"address_components":{"number":"54","predirectional":"W","street":"Colorado","suffix":"Blvd","city":"Pasadena","county":"Los + Angeles County","state":"CA","zip":"91105"},"formatted_address":"54 W Colorado + Blvd, Pasadena, CA 91105","location":{"lat":34.1457634625,"lng":-118.15170725},"accuracy":0.8,"fields":{"congressional_district":{"name":"Congressional + District 27","district_number":27,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 25","district_number":25},"house":{"name":"Assembly District + 41","district_number":41}},"school_districts":{"unified":{"name":"Pasadena + Unified School District","lea_code":"29940","grade_low":"KG","grade_high":"12"}},"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}}]}},{"query":"826 + Howard Street San Francisco CA 94103","response":{"input":{"address_components":{"number":"826","street":"Howard","suffix":"St","city":"San + Francisco","state":"CA","zip":"94103"},"formatted_address":"826 Howard St, + San Francisco, CA 94103"},"results":[{"address_components":{"number":"826","street":"Howard","suffix":"St","city":"San + Francisco","county":"San Francisco County","state":"CA","zip":"94103"},"formatted_address":"826 + Howard St, San Francisco, CA 94103","location":{"lat":37.7815,"lng":-122.404933},"accuracy":1,"fields":{"congressional_district":{"name":"Congressional + District 12","district_number":12,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 11","district_number":11},"house":{"name":"Assembly District + 11","district_number":11}},"school_districts":[],"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}},{"address_components":{"number":"826","street":"Howard","suffix":"St","city":"San + Francisco","county":"San Francisco County","state":"CA","zip":"94103"},"formatted_address":"826 + Howard St, San Francisco, CA 94103","location":{"lat":37.781552539474,"lng":-122.40486656579},"accuracy":0.8,"fields":{"congressional_district":{"name":"Congressional + District 12","district_number":12,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 11","district_number":11},"house":{"name":"Assembly District + 11","district_number":11}},"school_districts":[],"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}}]}}]}' + http_version: + recorded_at: Fri, 28 Mar 2014 16:00:59 GMT +recorded_with: VCR 2.8.0 diff --git a/spec/vcr_cassettes/batch_reverse_with_fields.yml b/spec/vcr_cassettes/batch_reverse_with_fields.yml new file mode 100644 index 0000000..9f8cee3 --- /dev/null +++ b/spec/vcr_cassettes/batch_reverse_with_fields.yml @@ -0,0 +1,96 @@ +--- +http_interactions: +- request: + method: post + uri: http://api.geocod.io/v1/reverse?api_key=secret_api_key&fields=cd,stateleg,school,timezone + body: + encoding: UTF-8 + string: '["37.331669,-122.03074","34.145760590909,-118.15204363636","37.7815,-122.404933"]' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + User-Agent: + - Ruby + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx/1.2.1 + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Cache-Control: + - no-cache + Date: + - Fri, 28 Mar 2014 16:25:49 GMT + Access-Control-Allow-Origin: + - "*" + body: + encoding: UTF-8 + string: '{"results":[{"query":"37.331669,-122.03074","response":{"results":[{"address_components":{"number":"91","street":"Infinite","suffix":"Loop","city":"Cupertino","county":"Santa + Clara County","state":"CA","zip":"95014"},"formatted_address":"91 Infinite + Loop, Cupertino, CA 95014","location":{"lat":37.332709,"lng":-122.03071},"accuracy":1,"fields":{"congressional_district":{"name":"Congressional + District 17","district_number":17,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 15","district_number":15},"house":{"name":"Assembly District + 15","district_number":15}},"school_districts":{"secondary":{"name":"Fremont + Union High School District","lea_code":"14430","grade_low":"09","grade_high":"12"},"elementary":{"name":"Cupertino + Union Elementary School District","lea_code":"10290","grade_low":"KG","grade_high":"08"}},"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}},{"address_components":{"number":"19116","street":"Mariani","suffix":"Ave","city":"Cupertino","county":"Santa + Clara County","state":"CA","zip":"95014"},"formatted_address":"19116 Mariani + Ave, Cupertino, CA 95014","location":{"lat":37.330407,"lng":-122.030873},"accuracy":0.28,"fields":{"congressional_district":{"name":"Congressional + District 17","district_number":17,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 15","district_number":15},"house":{"name":"Assembly District + 15","district_number":15}},"school_districts":{"secondary":{"name":"Fremont + Union High School District","lea_code":"14430","grade_low":"09","grade_high":"12"},"elementary":{"name":"Cupertino + Union Elementary School District","lea_code":"10290","grade_low":"KG","grade_high":"08"}},"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}},{"address_components":{"number":"19118","street":"Mariani","suffix":"Ave","city":"Cupertino","county":"Santa + Clara County","state":"CA","zip":"95014"},"formatted_address":"19118 Mariani + Ave, Cupertino, CA 95014","location":{"lat":37.330489,"lng":-122.0305},"accuracy":0.28,"fields":{"congressional_district":{"name":"Congressional + District 17","district_number":17,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 15","district_number":15},"house":{"name":"Assembly District + 15","district_number":15}},"school_districts":{"secondary":{"name":"Fremont + Union High School District","lea_code":"14430","grade_low":"09","grade_high":"12"},"elementary":{"name":"Cupertino + Union Elementary School District","lea_code":"10290","grade_low":"KG","grade_high":"08"}},"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}}]}},{"query":"34.145760590909,-118.15204363636","response":{"results":[{"address_components":{"number":"43","predirectional":"W","street":"Colorado","suffix":"Blvd","city":"Pasadena","county":"Los + Angeles County","state":"CA","zip":"91105"},"formatted_address":"43 W Colorado + Blvd, Pasadena, CA 91105","location":{"lat":34.145766,"lng":-118.15141},"accuracy":1,"fields":{"congressional_district":{"name":"Congressional + District 27","district_number":27,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 25","district_number":25},"house":{"name":"Assembly District + 41","district_number":41}},"school_districts":{"unified":{"name":"Pasadena + Unified School District","lea_code":"29940","grade_low":"KG","grade_high":"12"}},"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}},{"address_components":{"number":"1","predirectional":"N","street":"De + Lacey","suffix":"Ave","city":"Pasadena","county":"Los Angeles County","state":"CA","zip":"91103"},"formatted_address":"1 + N De Lacey Ave, Pasadena, CA 91103","location":{"lat":34.145759,"lng":-118.15223},"accuracy":0,"fields":{"congressional_district":{"name":"Congressional + District 27","district_number":27,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 25","district_number":25},"house":{"name":"Assembly District + 41","district_number":41}},"school_districts":{"unified":{"name":"Pasadena + Unified School District","lea_code":"29940","grade_low":"KG","grade_high":"12"}},"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}},{"address_components":{"number":"1","predirectional":"S","street":"De + Lacey","suffix":"Ave","city":"Pasadena","county":"Los Angeles County","state":"CA","zip":"91105"},"formatted_address":"1 + S De Lacey Ave, Pasadena, CA 91105","location":{"lat":34.145759,"lng":-118.15223},"accuracy":0,"fields":{"congressional_district":{"name":"Congressional + District 27","district_number":27,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 25","district_number":25},"house":{"name":"Assembly District + 41","district_number":41}},"school_districts":{"unified":{"name":"Pasadena + Unified School District","lea_code":"29940","grade_low":"KG","grade_high":"12"}},"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}}]}},{"query":"37.7815,-122.404933","response":{"results":[{"address_components":{"number":"177","street":"5th","suffix":"St","city":"San + Francisco","county":"San Francisco County","state":"CA","zip":"94102"},"formatted_address":"177 + 5th St, San Francisco, CA 94102","location":{"lat":37.781925,"lng":-122.405466},"accuracy":0,"fields":{"congressional_district":{"name":"Congressional + District 12","district_number":12,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 11","district_number":11},"house":{"name":"Assembly District + 11","district_number":11}},"school_districts":[],"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}},{"address_components":{"number":"861","street":"Howard","suffix":"St","city":"San + Francisco","county":"San Francisco County","state":"CA","zip":"94103"},"formatted_address":"861 + Howard St, San Francisco, CA 94103","location":{"lat":37.782831,"lng":-122.40325},"accuracy":0,"fields":{"congressional_district":{"name":"Congressional + District 12","district_number":12,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 11","district_number":11},"house":{"name":"Assembly District + 11","district_number":11}},"school_districts":[],"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}},{"address_components":{"number":"900","street":"Howard","suffix":"St","city":"San + Francisco","county":"San Francisco County","state":"CA","zip":"94103"},"formatted_address":"900 + Howard St, San Francisco, CA 94103","location":{"lat":37.7815,"lng":-122.404933},"accuracy":0,"fields":{"congressional_district":{"name":"Congressional + District 12","district_number":12,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 11","district_number":11},"house":{"name":"Assembly District + 11","district_number":11}},"school_districts":[],"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}}]}}]}' + http_version: + recorded_at: Fri, 28 Mar 2014 16:25:49 GMT +recorded_with: VCR 2.8.0 diff --git a/spec/vcr_cassettes/geocode_with_fields.yml b/spec/vcr_cassettes/geocode_with_fields.yml new file mode 100644 index 0000000..3bfe844 --- /dev/null +++ b/spec/vcr_cassettes/geocode_with_fields.yml @@ -0,0 +1,53 @@ +--- +http_interactions: +- request: + method: get + uri: http://api.geocod.io/v1/geocode?api_key=secret_api_key&fields=cd,stateleg,school,timezone&q=54%20West%20Colorado%20Boulevard%20Pasadena%20CA%2091105 + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx/1.2.1 + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Cache-Control: + - no-cache + Date: + - Fri, 28 Mar 2014 16:00:06 GMT + Access-Control-Allow-Origin: + - "*" + body: + encoding: UTF-8 + string: '{"input":{"address_components":{"number":"54","predirectional":"W","street":"Colorado","suffix":"Blvd","city":"Pasadena","state":"CA","zip":"91105"},"formatted_address":"54 + W Colorado Blvd, Pasadena, CA 91105"},"results":[{"address_components":{"number":"54","predirectional":"W","street":"Colorado","suffix":"Blvd","city":"Pasadena","county":"Los + Angeles County","state":"CA","zip":"91105"},"formatted_address":"54 W Colorado + Blvd, Pasadena, CA 91105","location":{"lat":34.145760590909,"lng":-118.15204363636},"accuracy":1,"fields":{"congressional_district":{"name":"Congressional + District 27","district_number":27,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 25","district_number":25},"house":{"name":"Assembly District + 41","district_number":41}},"school_districts":{"unified":{"name":"Pasadena + Unified School District","lea_code":"29940","grade_low":"KG","grade_high":"12"}},"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}},{"address_components":{"number":"54","predirectional":"W","street":"Colorado","suffix":"Blvd","city":"Pasadena","county":"Los + Angeles County","state":"CA","zip":"91105"},"formatted_address":"54 W Colorado + Blvd, Pasadena, CA 91105","location":{"lat":34.1457634625,"lng":-118.15170725},"accuracy":0.8,"fields":{"congressional_district":{"name":"Congressional + District 27","district_number":27,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 25","district_number":25},"house":{"name":"Assembly District + 41","district_number":41}},"school_districts":{"unified":{"name":"Pasadena + Unified School District","lea_code":"29940","grade_low":"KG","grade_high":"12"}},"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}}]}' + http_version: + recorded_at: Fri, 28 Mar 2014 16:00:06 GMT +recorded_with: VCR 2.8.0 diff --git a/spec/vcr_cassettes/reverse_with_fields.yml b/spec/vcr_cassettes/reverse_with_fields.yml new file mode 100644 index 0000000..f38a436 --- /dev/null +++ b/spec/vcr_cassettes/reverse_with_fields.yml @@ -0,0 +1,58 @@ +--- +http_interactions: +- request: + method: get + uri: http://api.geocod.io/v1/reverse?api_key=secret_api_key&fields=cd,stateleg,school,timezone&q=34.145760590909,-118.15204363636 + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx/1.2.1 + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Cache-Control: + - no-cache + Date: + - Fri, 28 Mar 2014 16:27:32 GMT + Access-Control-Allow-Origin: + - "*" + body: + encoding: UTF-8 + string: '{"results":[{"address_components":{"number":"43","predirectional":"W","street":"Colorado","suffix":"Blvd","city":"Pasadena","county":"Los + Angeles County","state":"CA","zip":"91105"},"formatted_address":"43 W Colorado + Blvd, Pasadena, CA 91105","location":{"lat":34.145766,"lng":-118.15141},"accuracy":1,"fields":{"congressional_district":{"name":"Congressional + District 27","district_number":27,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 25","district_number":25},"house":{"name":"Assembly District + 41","district_number":41}},"school_districts":{"unified":{"name":"Pasadena + Unified School District","lea_code":"29940","grade_low":"KG","grade_high":"12"}},"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}},{"address_components":{"number":"1","predirectional":"N","street":"De + Lacey","suffix":"Ave","city":"Pasadena","county":"Los Angeles County","state":"CA","zip":"91103"},"formatted_address":"1 + N De Lacey Ave, Pasadena, CA 91103","location":{"lat":34.145759,"lng":-118.15223},"accuracy":0,"fields":{"congressional_district":{"name":"Congressional + District 27","district_number":27,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 25","district_number":25},"house":{"name":"Assembly District + 41","district_number":41}},"school_districts":{"unified":{"name":"Pasadena + Unified School District","lea_code":"29940","grade_low":"KG","grade_high":"12"}},"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}},{"address_components":{"number":"1","predirectional":"S","street":"De + Lacey","suffix":"Ave","city":"Pasadena","county":"Los Angeles County","state":"CA","zip":"91105"},"formatted_address":"1 + S De Lacey Ave, Pasadena, CA 91105","location":{"lat":34.145759,"lng":-118.15223},"accuracy":0,"fields":{"congressional_district":{"name":"Congressional + District 27","district_number":27,"congress_number":"113th","congress_years":"2013-2015"},"state_legislative_districts":{"senate":{"name":"State + Senate District 25","district_number":25},"house":{"name":"Assembly District + 41","district_number":41}},"school_districts":{"unified":{"name":"Pasadena + Unified School District","lea_code":"29940","grade_low":"KG","grade_high":"12"}},"timezone":{"name":"PST","utc_offset":-8,"observes_dst":true}}}]}' + http_version: + recorded_at: Fri, 28 Mar 2014 16:27:32 GMT +recorded_with: VCR 2.8.0