geo_pack 0.1.0

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