geo_pack 0.1.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.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ #geo_pack#
2
+
3
+ Geo related services.
4
+
5
+ ##Usage##
6
+
7
+ First create an instance passing the configuration file path to the constructor:
8
+
9
+ geo_pack = GeoPack.new("config.yml")
10
+
11
+ The configuration file contains this structure:
12
+
13
+ urbanmapping:
14
+ neighborhoods_apikey: jwncnt4vgs8xarc7pvap2ra7
15
+ secret: RRGxwfEn
16
+ zillow:
17
+ zwsid: X1-ZW3mlz1cqh8sej_1y8f9
18
+
19
+ Now you can transform an address into a normalized address with its coordinates, neighborhood with its coordinates/bounding box, city and state like this:
20
+
21
+ geo_pack.get_location("2114 bigelow Avenue , seattle")
22
+ =>
23
+ {
24
+ :state => { :name => "WA"},
25
+ :city => { :name => "Seattle"},
26
+ :neighborhood => { :name=>"Queen Anne", :geom=>[[47.617989, -122.378464], [47.656792, -122.34182]]},
27
+ :address => { :name => "2114 Bigelow Ave N, Seattle, WA 98109, USA", :geom => [47.637936, -122.34813]}
28
+ }
29
+
30
+ The address passed to get_location is strong enough to support weakly specified addresses. For example we can search just by zip code:
31
+
32
+ geo_pack.get_location("90210")
33
+ =>
34
+ {
35
+ :state=>{:name=>"CA"},
36
+ :city=>{:name=>"Beverly Hills"},
37
+ :neighborhood=>{:geom=>[[34.088879, -118.43866], [34.129559, -118.373741]], :name=>"Beverly Crest"},
38
+ :address=>{:geom=>[34.1030032, -118.4104684], :name=>"Beverly Hills, CA 90210, USA"}
39
+ }
40
+
41
+ If the address could not be found an empty hash will be returned:
42
+
43
+ geo_pack.get_location("supercalifragilisticexpialidocious address")
44
+ =>
45
+ {}
46
+
47
+ The neighborhood search, which uses an extra stack of api services, can be excluded if you don't need it and want faster results:
48
+
49
+ geo_pack.get_location("90210", :exclude => :neighborhood)
50
+ =>
51
+ {
52
+ :state=>{:name=>"CA"},
53
+ :city=>{:name=>"Beverly Hills"},
54
+ :address=>{:geom=>[34.1030032, -118.4104684], :name=>"Beverly Hills, CA 90210, USA"}
55
+ }
56
+
57
+ Zillow lookups are also supported:
58
+
59
+ geo_pack.get_property_details("2114 Bigelow, seattle")
60
+ =>
61
+ {
62
+ :error=>false,
63
+ :data=> {
64
+ :year_built=>1920,
65
+ :housing_type=>"house",
66
+ :square_footage=>"3470"
67
+ },
68
+ :message=>"Request successfully processed"
69
+ }
70
+
71
+ In case of error you'll get:
72
+
73
+ geo_pack.get_property_details("Somewhere")
74
+ =>
75
+ {
76
+ :error=>true,
77
+ :data=>{},
78
+ :message=>"Error: no exact match found for input address"
79
+ }
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "geo_pack"
8
+ gem.summary = %Q{Geo related services"}
9
+ gem.description = %Q{Geo related services"}
10
+ gem.homepage = "http://github.com/efficiency20/geo_pack"
11
+ gem.authors = ["Efficiency 2.0"]
12
+ gem.add_dependency "geokit", "= 1.5.0"
13
+ gem.add_dependency "geoplanet", "= 0.2.3"
14
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/**/test_*.rb'
26
+ test.verbose = true
27
+ end
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
+ end
40
+ end
41
+
42
+ task :test => :check_dependencies
43
+
44
+ task :default => :test
45
+
46
+ require 'rake/rdoctask'
47
+ Rake::RDocTask.new do |rdoc|
48
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "geo_pack #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/lib/geo_pack.rb ADDED
@@ -0,0 +1,73 @@
1
+ require 'geokit'
2
+ require 'geoplanet'
3
+ require 'geo_pack/urban_mapping'
4
+ require 'geo_pack/geo_names'
5
+ require 'geo_pack/zillower'
6
+
7
+ class GeoPack
8
+ Geokit::Geocoders::provider_order = [:google, :us, :yahoo, :geonames]
9
+ GeoPlanet.appid = 'aaaaaaaaaaaaaaaaaaaanobodyatyahoochecksthisaaaaaaaaaaaaaaaaaaaaa'
10
+
11
+ def initialize(config_file_path)
12
+ config = YAML.load_file(config_file_path)
13
+ @urban_mapping = UrbanMapping.new(config["urbanmapping"]["neighborhoods_apikey"], config["urbanmapping"]["secret"])
14
+ @geo_names = GeoNames.new
15
+ @zillower = Zillower.new(config["zillow"]["zwsid"])
16
+ end
17
+
18
+ def get_location(address, options = {})
19
+ location = {}
20
+ location.merge!(get_address_city_and_state(address, options))
21
+ return location if options[:exclude] == :neighborhood || location.empty?
22
+ location.merge!(get_neighborhood(location[:address][:geom]))
23
+ location
24
+ end
25
+
26
+ def get_property_details(address)
27
+ geokit_result = GeoKit::Geocoders::MultiGeocoder.geocode(address)
28
+ @zillower.lookup_address(:address => geokit_result.street_address, :citystatezip => "#{geokit_result.city}, #{geokit_result.state}, #{geokit_result.zip}")
29
+ end
30
+
31
+ private
32
+ def get_address_city_and_state(address, options)
33
+ location = {}
34
+ geokit_result = GeoKit::Geocoders::MultiGeocoder.geocode(address)
35
+ if geokit_result.success?
36
+ location[:address] = { :name => geokit_result.full_address, :geom => [geokit_result.lat, geokit_result.lng]}
37
+ location[:city] = { :name => geokit_result.city}
38
+ location[:state] = { :name => geokit_result.state}
39
+ end
40
+ location
41
+ end
42
+
43
+ def get_neighborhood(geom)
44
+ location = {}
45
+ lat, lng = geom
46
+ urban_mapping_result = @urban_mapping.query(:method => "getNeighborhoodsByLatLng", :lat => lat, :lng => lng)
47
+
48
+ unless urban_mapping_result.empty?
49
+ geom_or_bounding_box = get_bounding_box(urban_mapping_result) || geom
50
+ location[:neighborhood] = {:name => urban_mapping_result.first["name"], :geom => geom_or_bounding_box}
51
+ else
52
+ geo_names_result = @geo_names.query(:method => 'findNearbyPlaceName', :lat => lat, :lng => lng)
53
+ unless geo_names_result.empty?
54
+ geom_or_bounding_box = get_bounding_box(geo_names_result) || geom
55
+ location[:neighborhood] = {:name => geo_names_result.first["name"], :geom => geom_or_bounding_box}
56
+ end
57
+ end
58
+
59
+ location
60
+ end
61
+
62
+ def get_bounding_box(api_result)
63
+ name = api_result.first["name"]
64
+ city = api_result.first["city"]
65
+ state = api_result.first["state"]
66
+
67
+ if geo_planet_response = GeoPlanet::Place.search("#{name}, #{city}, #{state}, US")
68
+ geo_planet_response.first.bounding_box
69
+ else
70
+ nil
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,116 @@
1
+ #geonames rest api
2
+
3
+ require 'rubygems'
4
+ require 'httparty'
5
+ require 'digest/md5'
6
+
7
+
8
+ #$ curl 'http://ws.geonames.org/findNearbyPlaceNameJSON?lat=40.871992&lng=-124.087883&style=full'
9
+ #{"geonames":[{"adminCode2":"023","countryName":"United States","adminCode1":"CA","fclName":"city, village,...","elevation":7,"countryCode":"US","lng":-124.0828396,"adminName2":"Humboldt County","adminName3":"","fcodeName":"populated place","distance":"0.7419","adminName4":"","timezone":{"dstOffset":-7,"gmtOffset":-8,"timeZoneId":"America/Los_Angeles"},"fcl":"P","name":"Arcata","fcode":"PPL","geonameId":5558953,"lat":40.8665166,"population":17199,"adminName1":"California"}]}
10
+ #geoname = um.query_geonames(:method => 'findNearbyPlaceName',:lat => 40.871992, :lng => -124.087883, :style => 'full' )
11
+
12
+
13
+ class GeoPack
14
+ class GeoNames
15
+ include HTTParty
16
+ format :json
17
+
18
+ GEONAMES_API = {
19
+ 'findNearbyPlaceName' => [:lat, :lng],
20
+ 'postalCodeSearch' => [:postalcode]
21
+ }
22
+
23
+ def method_missing(method, *args)
24
+ if GEONAMES_API.has_key?( method )
25
+ query(method, *args)
26
+ else
27
+ super
28
+ end
29
+ end
30
+
31
+ def self.query(options= {})
32
+ geonames = self.new(options[:apikey])
33
+ return geonames.query(options)
34
+ end
35
+
36
+
37
+ def query(options={})
38
+ check_requirements(options)
39
+ options.merge!({:style => 'full'}) unless options.has_key? :style
40
+ options = munge_key_names(options)
41
+ method = options.delete(:method)
42
+ results = munge_results( GeoNames.get("http://ws.geonames.org/" + method + "JSON", :query => options) )
43
+
44
+ if results.kind_of?(Hash) && results.has_key?('status')
45
+ #throw exception here because there was an error
46
+ return[]
47
+ else
48
+ return results
49
+ end
50
+ end
51
+
52
+ #so we can use prettier options than geonames provides
53
+ def munge_key_names(options={})
54
+ options_mapping = {
55
+ 'latitude' => 'lat',
56
+ 'longitude' => 'lng',
57
+ 'long' => 'lng',
58
+ 'zip' => 'postalcode',
59
+ 'zipcode' => 'postalcode',
60
+ 'zip_code' => 'postalcode',
61
+ 'postalCode' => 'postalcode'}
62
+
63
+
64
+ options.map do |key,value|
65
+ if options_mapping.has_key? key.to_s
66
+ options[options_mapping[key.to_s]] = options.delete key
67
+ end
68
+ end
69
+
70
+ options[:countryBias] = 'US' if options[:method] == 'postalCodeSearch'
71
+
72
+ return options
73
+ end
74
+
75
+ def munge_results(results)
76
+ results = results["geonames"] if results.kind_of?(Hash) && results.has_key?("geonames")
77
+ results = results["postalCodes"] if results.kind_of?(Hash) && results.has_key?("postalCodes")
78
+ return [] if results.nil?
79
+
80
+ return munge_single_result(results) if results.kind_of?(Hash)
81
+
82
+ results_array = []
83
+ results.each {|result| results_array<<munge_single_result(result)}
84
+
85
+ return results_array
86
+ end
87
+
88
+ def munge_single_result(results)
89
+ mapping = {
90
+ 'latitude' => 'lat',
91
+ 'longitude' => 'lng',
92
+ 'long' => 'lng',
93
+ 'zip' => 'postalcode',
94
+ 'zipcode' => 'postalcode',
95
+ 'neighborhoodId' => 'neighborhood_id',
96
+ 'adminCode1' => 'state',
97
+ 'name' => 'city',
98
+ 'geonameId' => 'id',
99
+ 'placeName' => 'name'}
100
+
101
+ results.map do |key,value|
102
+ if mapping.has_key? key.to_s
103
+ results[mapping[key]] = results[ key ]
104
+ end
105
+ end
106
+
107
+ return results
108
+ end
109
+
110
+ def check_requirements(options)
111
+ #raise( 'apikey required for geonames' ) if @apikey.nil?
112
+ raise( 'method required for geonames' ) if options[:method].nil?
113
+ raise( "method #{options[:method]} geonames" ) unless GEONAMES_API.has_key?( options[:method] )
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,261 @@
1
+ #!/usr/bin/ruby
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'xmlsimple'
5
+ require 'cgi'
6
+ require 'geo_pack/rillow_helper'
7
+
8
+ # This api is created by Leo Chan on 10/29/2007.
9
+ # There is no license requirement. Use it or copy any part of the code that you want to use.
10
+ # Use it at your own risk. I am not liable for anything. Just want to share what I wrote. Hopefully you will find it useful.
11
+ # Rillow is a simple ruby wrapper to zillow webservice api: http://www.zillow.com/howto/api/APIOverview.htm
12
+ # It does the web service call and gets the result back for you. You don't have to know about the url request, url encoding or
13
+ # parsing the result. The result object is in hash/array format. So no need to parsse xml.
14
+ # You will need to register with zillow to get the Zillow Web Service Identifier first.
15
+ # You will need to pass the Zillow Web Service Identifier to the constructor.
16
+ # This wrapper depends on xmlsimple.
17
+ #
18
+ # Example:
19
+ # rillow = Rillow.new('your-zillow-service identifier')
20
+ # result = rillow.get_search_results(:address => '2114 Bigelow Ave', :citystatezip => 'Seattle WA')
21
+ # result.to_hash
22
+ # result.find_attribute 'valudationRange'
23
+ class GeoPack
24
+ class Rillow
25
+ attr_accessor :webservice_url, :zwsid
26
+
27
+ VALID_METHODS = {
28
+ :get_search_results => 'GetSearchResults',
29
+ :get_zestimate => 'GetZestimate',
30
+ :get_chart => 'GetChart',
31
+ :get_region_chart => 'GetRegionChart',
32
+ :get_demographics => 'GetDemographics',
33
+ :get_comps => "GetComps",
34
+ :get_region_children => 'GetRegionChildren',
35
+ :get_ratesummary => 'GetRateSummary',
36
+ :get_monthly_payments => 'GetMonthlyPayments',
37
+ :get_deep_search_results => 'GetDeepSearchResults',
38
+ :get_property_details => 'GetDeepSearchResults',
39
+ :get_deep_comps => 'GetDeepComps',
40
+ :get_region_postings => 'GetRegionPostings',
41
+ :get_updated_property_details => 'GetUpdatedPropertyDetails'
42
+ }
43
+
44
+
45
+ # rillow = Rillow.new('your-zillow-service identifier')
46
+ def initialize(my_zwsid, my_webservice_url= 'http://www.zillow.com/webservice/')
47
+ @zwsid= my_zwsid
48
+
49
+ @webservice_url = my_webservice_url
50
+ end
51
+
52
+ def method_missing(method, *args)
53
+ super(method, args) unless VALID_METHODS.has_key? method
54
+ fetch_from_zillow(VALID_METHODS[method], args)
55
+ end
56
+
57
+ #The get_search_results finds a property for a specified address.
58
+ #The content returned contains the address for the property or properties as well as the Zillow Property ID (ZPID) and current Zestimate.
59
+ #It also includes the date the Zestimate was computed, a valuation range and the Zestimate ranking for the property within its ZIP code.
60
+ #If no exact address match for a property is found, a list of closely matching properties is returned.
61
+ #See: http://www.zillow.com/howto/api/GetSearchResults.htm
62
+ # parameter:
63
+ # address street address
64
+ # citystatezip city&state or zip code
65
+ #Example:
66
+ # rillow = Rillow.new('your-zillow-service identifier')
67
+ # result = rillow.get_search_results(:address => '2114 Bigelow Ave', :citystatezip => 'Seattle, WA')
68
+ # result.to_hash
69
+
70
+ #For a specified Zillow property identifier (zpid), the get_zestimate returns:
71
+ #1. The most recent property Zestimate
72
+ #2. The date the Zestimate was computed
73
+ #3. The valuation range
74
+ #4. The Zestimate ranking within the property's ZIP code.
75
+ #5. The full property address and geographic location (latitude/longitude) and a set of identifiers that uniquely represent the region (ZIP code, city, county & state) in which the property exists.
76
+ #The GetZestimate API will only surface properties for which a Zestimate exists.
77
+ #If a request is made for a property that has no Zestimate, an error code is returned.
78
+ #See: http://www.zillow.com/howto/api/GetZestimate.htm
79
+ #parameter:
80
+ # zpid zillow property id
81
+ #Example:
82
+ # rillow = Rillow.new('your-zillow-service identifier')
83
+ # result = rillow.get_zestimate(:zpid => '48749425')
84
+ # result.to_hash
85
+
86
+
87
+ #The get_chart api generates a URL for an image file that displays historical Zestimates for a specific property.
88
+ #The API accepts as input the Zillow Property ID as well as a chart type: either percentage or dollar value change.
89
+ #Optionally, the API accepts width and height parameters that constrain the size of the image.
90
+ #The historical data can be for the past 1 year, 5 years or 10 years.
91
+ #See: http://www.zillow.com/howto/api/GetChart.htm
92
+ #parameters:
93
+ #require:
94
+ # zpid: The Zillow Property ID for the property; the parameter type is an integer
95
+ # unit_type: A string value that specifies whether to show the percent change, parameter value of "percent,"
96
+ # or dollar change, parameter value of "dollar"
97
+ #options:
98
+ # :width => width of the generated graph. The value must be between 200 and 600, inclusive
99
+ # :height=> height of the generated graph. The value must be between 100 and 300, inclusive.
100
+ # :chart_duration => The duration of past data that needs to be shown in the chart. Valid values are "1year",
101
+ # "5years" and "10years". If unspecified, the value defaults to "1year"
102
+ #Example:
103
+ # rillow = Rillow.new('your-zillow-service identifier')
104
+ # result = rillow.get_chart(:zpid => '48749425',:unit_type => 'percent',:width=>300, :height=>150, :chart_duration=>'5years')
105
+ # result.to_hash
106
+
107
+ #The get_region_chart generates a URL for an image file that displays the historical Zestimates for a specific geographic region.
108
+ #The API accepts as input the name of the region as well as a chart type: either percentage or dollar value change. #
109
+ #Optionally, the API accepts width and height parameters that constrain the size of the image.
110
+ #The historical data can be for the past 1 year, 5 years or 10 years.
111
+ #see: http://www.zillow.com/howto/api/GetRegionChart.htm
112
+ #parameters:
113
+ #require:
114
+ # unit_type: A string value that specifies whether to show the percent change, parameter value of "percent,"
115
+ # or dollar change, parameter value of "dollar"
116
+ #options:
117
+ # :city=> name of the city
118
+ # :state=> The two-letter abbreviation for a state
119
+ # :zip=> The 5-digit ZIP code
120
+ # :width=> width of the generated graph. The value must be between 200 and 600, inclusive
121
+ # :height=> height of the generated graph. The value must be between 100 and 300, inclusive.
122
+ # :chart_duration => The duration of past data that needs to be shown in the chart. Valid values are "1year",
123
+ # "5years" and "10years". If unspecified, the value defaults to "1year"
124
+ #Example:
125
+ # rillow = Rillow.new('your-zillow-service identifier')
126
+ # result = rillow.get_region_chart(:unit_type =>'percent',:city=>'seattle',:state=>'WA',:width=>300, :height=>150, :chart_duration=>'5years')
127
+ # result.to_hash
128
+
129
+ #For a specified region, the GetDemographics API returns a set of demographic data which includes:
130
+ # * A URL linking to the corresponding demographics page at Zillow.com
131
+ # * Census Information (i.e. total population, median household income, recent homeowners, etc)
132
+ # * Age Distributions
133
+ # * Who Lives Here (if available for the region)
134
+ # * What's Unique About the People (if available for the region)
135
+ # A region can be specified either through its respective Region ID or by providing one to three parameters:
136
+ # state, city, neighborhood. The neighborhood parameter can be omitted if demographic data on a city is desired.
137
+ # The state and city parameter are always required.
138
+ # see: http://www.zillow.com/howto/api/GetDemographics.htm
139
+ # parameters:
140
+ # :rid => region id
141
+ # :city=> name of the city
142
+ # :state=> The two-letter abbreviation for a state
143
+ # :neighborhood=> The neighborhood of the region to retrieve data
144
+ #Example:
145
+ # rillow = Rillow.new('your-zillow-service identifier')
146
+ # result = rillow.get_demographics(:city=>'seattle',:state=>'WA',:neighborhood=>'Ballard')
147
+ # result.to_hash
148
+
149
+ # For a specified region, the get_region_children API returns a list of subregions with the following information:
150
+ # * Subregion Type
151
+ # * Region IDs
152
+ # * Region Names
153
+ # * Latitudes and Longitudes
154
+ #A region can be specified at various levels of the region hierarchy.
155
+ #An optional childtype parameter can also be specified to return subregions of a specific type.
156
+ #Allowable region types include:
157
+ #country, state, county, and city. Country and county are optional parameters unless they are the region to be specified.
158
+ #Possible childtype parameters include: state, county, city, zipcode, and neighborhood.
159
+ #Any childtype parameter can be specified as long as the childtype parameter is a subregion type
160
+ #(i.e.. you cannot retrieve the subregion counties of a city).
161
+ #The only exception is that only subregion state can be specified for a country (otherwise it returns too many results).
162
+ #
163
+ #Childtype parameter is optional and defaults to types dependent on the specified region type:
164
+ #country defaults to return subregions of type state, state -> county, county -> city, city -> zipcode.
165
+ #see: http://www.zillow.com/howto/api/GetRegionChildren.htm
166
+ #parameters:
167
+ # :city=> name of the city. The city of the region to retrieve subregions from.
168
+ # :state=> The two-letter abbreviation for a state. The state of the region to retrieve subregions from.
169
+ # :country=> The country of the region to retrieve subregions from.
170
+ # :rid=> The regionId of the region to retrieve subregions from.
171
+ # :childtype=> The type of subregions to retrieve (available types: state, county, city, zipcode, and neighborhood)
172
+ # Example:
173
+ # rillow = Rillow.new('your-zillow-service identifier')
174
+ # result = rillow.get_region_children(:city=>'seattle',:state=>'WA',:country=>'united states',:childtype=>'neighborhood')
175
+ # result.to_hash
176
+
177
+ #The get_comps returns a list of comparable recent sales for a specified property.
178
+ #The result set returned contains the address, Zillow property identifier, and Zestimate for the comparable properties and
179
+ #the principal property for which the comparables are being retrieved.
180
+ #see: http://www.zillow.com/howto/api/GetComps.htm
181
+ #parameters:
182
+ #zpid The Zillow Property ID for the property for which to obtain information; the parameter type is an integer
183
+ #count The number of comparable recent sales to obtain
184
+ # Examples:
185
+ # rillow = Rillow.new('your-zillow-service identifier')
186
+ # result = rillow.get_comps('48749425',5)
187
+ # result.to_hash
188
+
189
+ #The get_deep_search_results finds a property for a specified address
190
+ #(or, if no exact match for a property is found, a list of closely matching properties is returned).
191
+ #The result set returned contains the full address(s), zpid and Zestimate data that is provided by the get_search_results API.
192
+ #Moreover, this API call also gives rich property data like lot size, year built, bath/beds, last sale details etc.
193
+ #see: http://www.zillow.com/howto/api/GetDeepSearchResults.htm
194
+ # parameter:
195
+ # address street address
196
+ # citystatezip city&state or zip code
197
+ #Example:
198
+ # rillow = Rillow.new('your-zillow-service identifier')
199
+ # result = rillow.get_deep_search_results('2114 Bigelow Ave','Seattle, WA')
200
+ # result.to_hash
201
+
202
+ #The get_deep_comps api returns a list of comparable recent sales for a specified property.
203
+ #The result set returned contains the address, Zillow property identifier,
204
+ #and Zestimate for the comparable properties and the principal property for which the comparables are being retrieved.
205
+ #This API call also returns rich property data for the comparables.
206
+ #see: http://www.zillow.com/howto/api/GetDeepComps.htm
207
+ #parameters:
208
+ #zpid The Zillow Property ID for the property for which to obtain information; the parameter type is an integer
209
+ #count The number of comparable recent sales to obtain
210
+ # Examples:
211
+ # rillow = Rillow.new('your-zillow-service identifier')
212
+ # result = rillow.get_deep_comps('48749425',5)
213
+ # result.to_hash
214
+
215
+ # Get monthly mortgage payments.
216
+ # price is required.
217
+ # optional parameters:
218
+ # :down => down payment, as an integer, representing the percent. If ommitted, 20% is assumed.
219
+ # :dollarsdown => down payment in dollars
220
+ # :zip => location of property, used to calculate tax and insurance estimate, if known.
221
+ # see: http://www.zillow.com/howto/api/GetMonthlyPayments.htm
222
+ # Example:
223
+ # rillow = Rillow.new('your-zillow-service identifier')
224
+ # result = rillow.get_monthlypayments(350000, {:down => 15, :zip => '33432'})
225
+ # result.to_hash
226
+
227
+ # Get current mortgage rates.
228
+ # No params.
229
+ # see: http://www.zillow.com/howto/api/GetRateSummary.htm
230
+ # Example:
231
+ # rillow = Rillow.new('your-zillow-service identifier')
232
+ # result = rillow.get_ratesummary
233
+ # result.to_hash
234
+
235
+ private
236
+ def fetch_from_zillow(method,options)
237
+ options = options[0] if options.kind_of? Array
238
+ options.merge!({'zws-id', zwsid})
239
+ fetch_result [webservice_url, method, '.htm?', to_arguments(options)].join
240
+ end
241
+
242
+ def to_arguments(options)
243
+ options.map {|k,v| "#{clean_param_key(k)}=#{CGI.escape(v.to_s)}" }.join("&")
244
+ end
245
+
246
+ def clean_param_key(param_key)
247
+ return 'chartDuration' if param_key == :chart_duration
248
+ param_key.to_s.gsub('_','-')
249
+ end
250
+
251
+ def fetch_result(url_s)
252
+ url = URI.parse(url_s)
253
+ res = Net::HTTP.get_response(url)
254
+ doc = XmlSimple.xml_in res.body
255
+ class<<doc
256
+ include RillowHelper
257
+ end
258
+ return doc
259
+ end
260
+ end
261
+ end
@@ -0,0 +1,34 @@
1
+ class GeoPack
2
+ module RillowHelper
3
+ # The find_attribute helper method is to provide a easy eay to find a attribute within the hash and array
4
+ # Examples:
5
+ # rillow = Rillow.new ('your-zillow-service identifier')
6
+ # result = rillow.get_search_results('2114 Bigelow Ave','Seattle, WA')
7
+ # valuationRange = result.find_attribute 'valuationRange'
8
+ def find_attribute(key,obj=nil)
9
+ if obj==nil then
10
+ obj=self
11
+ end
12
+ if obj.is_a? Hash then
13
+ obj.each { |k,v|
14
+ if k==key then
15
+ return v
16
+ else
17
+ result = find_attribute(key,v)
18
+ if result != nil then
19
+ return result
20
+ end
21
+ end
22
+ }
23
+ elsif obj.is_a? Array then
24
+ obj.each {|o|
25
+ result = find_attribute(key,o)
26
+ if result != nil then
27
+ return result
28
+ end
29
+ }
30
+ end
31
+ return nil
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,88 @@
1
+ #urbanmapping rest api
2
+
3
+ require 'rubygems'
4
+ require 'httparty'
5
+ require 'digest/md5'
6
+
7
+ class GeoPack
8
+ class UrbanMapping
9
+ include HTTParty
10
+ format :json
11
+
12
+ URBAN_MAPPING_API = {
13
+ 'getNeighborhoodsByLatLng' => [:lat, :lng],
14
+ 'getNearestNeighborhood' => [:lat, :lng],
15
+ 'getNeighborhoodsByExtent' => [:swlat, :swlng, :nelat, :nelng],
16
+ 'getNeighborhoodsByAddress'=> [:street, :city, :state, :country],
17
+ 'getNeighborhoodsByCityStateCountry' => [:city, :state, :country],
18
+ 'getNeighborhoodsByPostalCode' => [:postalcode],
19
+ 'getNeighborhoodsByName' => [:name],
20
+ 'getNeighborhoodDetail' => [:neighborhoodId],
21
+ 'getNeighborhoodRelationships' => [:neighborhoodId]
22
+ }
23
+
24
+ def initialize(apikey, secret)
25
+ @apikey = apikey
26
+ @secret = secret
27
+ end
28
+
29
+ def method_missing(method, *args)
30
+ if URBAN_MAPPING_API.has_key?( method )
31
+ query(method, *args)
32
+ else
33
+ super
34
+ end
35
+ end
36
+
37
+ def self.query(options= {})
38
+ um = self.new(options[:apikey])
39
+ return um.query(options)
40
+ end
41
+
42
+
43
+ def query(options={})
44
+ check_requirements(options)
45
+
46
+ method = options.delete(:method)
47
+ options.merge!({:apikey => @apikey})
48
+ options.merge!({:sig => generate_sig})
49
+
50
+ UrbanMapping.get("http://api1.urbanmapping.com/neighborhoods/rest/" + method, :query => options)
51
+ end
52
+
53
+ #so we can use prettier options than urbanmapping provides
54
+ def munge_key_names(options={})
55
+ options_mapping = {
56
+ :latitude => :lat,
57
+ :longitude => :lng,
58
+ :long => :lng,
59
+ :zip => :postalcode,
60
+ :zipcode => :postalcode,
61
+ :neighborhood_id => :neighborhoodId}
62
+
63
+ options.map do |key,value|
64
+ if options_mapping.has_key? key
65
+ options[options_mapping[key]] = options.delete key
66
+ end
67
+ end
68
+
69
+ return options
70
+ end
71
+
72
+ def check_requirements(options)
73
+ raise( 'apikey required for urbanmapping' ) if @apikey.nil?
74
+ raise( 'method required for urbanmapping' ) if options[:method].nil?
75
+ raise( "method #{options[:method]} urbanmapping" ) unless URBAN_MAPPING_API.has_key?( options[:method] )
76
+ #missing_params = []
77
+ #URBAN_MAPPING_API[options[:method]].each {|param| missing_params<<param unless options.has_key?(param) }
78
+ #raise( "#{options[:method]} requires #{missing_params.join(", ")} parameters" ) unless missing_params.empty?
79
+ end
80
+
81
+ def generate_sig
82
+ md5_input_string = @apikey + @secret + Time.now.to_i.to_s
83
+ d = Digest::MD5.new
84
+
85
+ return d.hexdigest(md5_input_string)
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,111 @@
1
+ require 'geo_pack/rillow'
2
+
3
+ class GeoPack
4
+ class Zillower
5
+ def initialize(zwsid)
6
+ @zwsid = zwsid
7
+ end
8
+
9
+ def lookup_address(options)
10
+ rillow = Rillow.new(@zwsid)
11
+ @base_result = rillow.get_property_details options
12
+ @message = @base_result['message'].first['text'].first
13
+ zpid = @base_result.find_attribute('zpid')
14
+ @updated_result = rillow.get_updated_property_details({:zpid => zpid}) if zpid
15
+ result = {
16
+ :message => @message,
17
+ :error => failure?,
18
+ :data => {}
19
+ }
20
+
21
+ unless result[:error]
22
+ {
23
+ :rooms => get_zillow_rooms,
24
+ :year_built => get_zillow_year,
25
+ :housing_type => get_zillow_housing_type,
26
+ :square_footage => get_zillow_square_footage
27
+ }.inject(result[:data]){|h, (k,v)| h[k] = v if v; h }
28
+ end
29
+
30
+ result
31
+ end
32
+
33
+ protected
34
+ def failure?
35
+ !!(@message =~ /error/i)
36
+ end
37
+
38
+ def get_zillow_rooms
39
+ rooms = @updated_result.find_attribute('numRooms') || @base_result.find_attribute('numRooms')
40
+ rooms.first if rooms.kind_of? Array
41
+ end
42
+
43
+ def get_zillow_square_footage
44
+ finishedSqFt = @updated_result.find_attribute('finishedSqFt') || @base_result.find_attribute('finishedSqFt')
45
+ finishedSqFt.first if finishedSqFt.kind_of? Array
46
+ end
47
+
48
+ def get_zillow_year
49
+ year_built = @updated_result.find_attribute('yearBuilt') || @base_result.find_attribute('yearBuilt')
50
+ year_built = year_built.first if year_built.kind_of? Array
51
+
52
+ #remember to remove this once the methods from the block comment below is moved to the client
53
+ map_year_to_range(year_built)
54
+ end
55
+
56
+ ####################
57
+ # This should be moved to the geo_pack client
58
+ def home_year_built
59
+ [
60
+ ['before 1940', 1920],
61
+ ['1940 to 1949', 1945],
62
+ ['1950 to 1959', 1955],
63
+ ['1960 to 1969', 1965],
64
+ ['1970 to 1979', 1975],
65
+ ['1980 to 1984', 1982],
66
+ ['1985 to 1989', 1987],
67
+ ['1990 to 1994', 1992],
68
+ ['1995 to 1999', 1997],
69
+ ['2000 or newer', 2002]
70
+ ]
71
+ end
72
+
73
+ def map_year_to_range(year_built)
74
+ option_year_built = nil
75
+ if year_built
76
+ home_year_built.each do |option, value|
77
+ option.match(/^(\d{4}) (to|or) (\d*)/)
78
+ end_date = $3.to_i == 0 ? 2100 : $3.to_i
79
+ if ($1.to_i..end_date).include? year_built.to_i
80
+ option_year_built = value
81
+ end
82
+ end
83
+ if option_year_built.nil?
84
+ option_year_built = year_built
85
+ end
86
+ end
87
+ option_year_built
88
+ end
89
+ ##################################
90
+
91
+ def get_zillow_housing_type
92
+ unit_type = @updated_result.find_attribute('useCode') || @base_result.find_attribute('useCode')
93
+ case unit_type.first
94
+ when 'Single family' : 'house'
95
+ when 'SingleFamily' : 'house'
96
+ when 'Unknown' : nil
97
+ when 'Duplex' : 'small_apartment'
98
+ when 'Triplex' : 'small_apartment'
99
+ when 'Quadruplex' : 'small_apartment'
100
+ when 'Condominium' : nil
101
+ when 'Cooperative' : nil
102
+ when 'Mobile' : 'mobile'
103
+ when 'MultiFamily2To4' : 'small_apartment'
104
+ when 'MultiFamily5Plus' : 'large_apartment'
105
+ when 'Timeshare' : nil
106
+ when 'Miscellaneous' : nil
107
+ when 'VacantResidentialLand' : nil
108
+ end if unit_type.kind_of? Array
109
+ end
110
+ end
111
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'geo_pack'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,5 @@
1
+ urbanmapping:
2
+ neighborhoods_apikey: jwncnvgs8xarct47pvap2ra7
3
+ secret: RRwfEnGx
4
+ zillow:
5
+ zwsid: X1-ZWz1cqh83mlsej_1y8f9
@@ -0,0 +1,59 @@
1
+ require 'helper'
2
+
3
+ #This is not stubbed on purpose, this are integration tests. We want it to break when any api is updated
4
+ class TestGeoPack < Test::Unit::TestCase
5
+ context "GeoPack" do
6
+ setup do
7
+ @geo_pack = GeoPack.new("#{File.dirname(__FILE__)}/test_config.yml")
8
+ end
9
+
10
+ should "get a complete location from a user provided address" do
11
+ location = @geo_pack.get_location("2114 bigelow Avenue , seattle")
12
+ assert_equal({
13
+ :state => { :name => "WA"},
14
+ :city => { :name => "Seattle"},
15
+ :neighborhood => { :name=>"Queen Anne", :geom=>[[47.617989, -122.378464], [47.656792, -122.34182]]},
16
+ :address => { :name => "2114 Bigelow Ave N, Seattle, WA 98109, USA", :geom => [47.637936, -122.34813]}
17
+ }, location)
18
+ end
19
+
20
+ should "get an empty location from an invalid address" do
21
+ location = @geo_pack.get_location("Foruncotolundo")
22
+ assert_equal({}, location)
23
+ end
24
+
25
+ should "be possible to exclude neighborhood search" do
26
+ location = @geo_pack.get_location("2114 bigelow Avenue , seattle", :exclude => :neighborhood)
27
+ assert_equal({
28
+ :state => { :name => "WA"},
29
+ :city => { :name => "Seattle"},
30
+ :address => { :name => "2114 Bigelow Ave N, Seattle, WA 98109, USA", :geom => [47.637936, -122.34813]}
31
+ }, location)
32
+ end
33
+
34
+ should "fallback to geo_names when urbanmapping fails finding a neighborhood" do
35
+ location = @geo_pack.get_location("Aetna Road and Cottage Creek Road")
36
+ assert_equal({
37
+ :state=>{:name=>"KS"},
38
+ :city=>{:name=>"Lake City"},
39
+ :neighborhood=> {:name=>"Aetna", :geom=>[[37.064449, -98.975754], [37.08263, -98.952972]]},
40
+ :address=> {:name=>"Aetna Rd & Cottage Creek Rd, Lake City, KS 67071, USA", :geom=>[37.0858864, -98.964822]}
41
+ }, location)
42
+ end
43
+
44
+ should "return zillow data from existent address" do
45
+ zillow_response = @geo_pack.get_property_details("2114 Bigelow, seattle, wA")
46
+ assert_equal({
47
+ :housing_type => "house",
48
+ :square_footage => "3470",
49
+ :year_built => 1920
50
+ }, zillow_response[:data])
51
+ assert !zillow_response[:error]
52
+ end
53
+
54
+ should "return zillow error from a non existent address" do
55
+ zillow_response = @geo_pack.get_property_details("asfsafdadsf ")
56
+ assert zillow_response[:error]
57
+ end
58
+ end
59
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: geo_pack
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Efficiency 2.0
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-02-28 00:00:00 -02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: geokit
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 5
30
+ - 0
31
+ version: 1.5.0
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: geoplanet
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ - 2
44
+ - 3
45
+ version: 0.2.3
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: thoughtbot-shoulda
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ type: :development
59
+ version_requirements: *id003
60
+ description: Geo related services"
61
+ email:
62
+ executables: []
63
+
64
+ extensions: []
65
+
66
+ extra_rdoc_files:
67
+ - README.md
68
+ files:
69
+ - .document
70
+ - .gitignore
71
+ - README.md
72
+ - Rakefile
73
+ - VERSION
74
+ - lib/geo_pack.rb
75
+ - lib/geo_pack/geo_names.rb
76
+ - lib/geo_pack/rillow.rb
77
+ - lib/geo_pack/rillow_helper.rb
78
+ - lib/geo_pack/urban_mapping.rb
79
+ - lib/geo_pack/zillower.rb
80
+ - test/helper.rb
81
+ - test/test_config.yml
82
+ - test/test_geo_pack.rb
83
+ has_rdoc: true
84
+ homepage: http://github.com/efficiency20/geo_pack
85
+ licenses: []
86
+
87
+ post_install_message:
88
+ rdoc_options:
89
+ - --charset=UTF-8
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ segments:
97
+ - 0
98
+ version: "0"
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ segments:
104
+ - 0
105
+ version: "0"
106
+ requirements: []
107
+
108
+ rubyforge_project:
109
+ rubygems_version: 1.3.6
110
+ signing_key:
111
+ specification_version: 3
112
+ summary: Geo related services"
113
+ test_files:
114
+ - test/helper.rb
115
+ - test/test_geo_pack.rb