lib/geokit/geocoders.rb in andre-geokit-1.2.6 vs lib/geokit/geocoders.rb in andre-geokit-1.3.1
- old
+ new
@@ -35,10 +35,14 @@
def url_escape(s)
s.gsub(/([^ a-zA-Z0-9_.-]+)/nu) do
'%' + $1.unpack('H2' * $1.size).join('%').upcase
end.tr(' ', '+')
end
+
+ def camelize(str)
+ str.split('_').map {|w| w.capitalize}.join
+ end
end
# Contains a range of geocoders:
#
# ### "regular" address geocoders
@@ -68,29 +72,41 @@
@@google = 'REPLACE_WITH_YOUR_GOOGLE_KEY'
@@geocoder_us = false
@@geocoder_ca = false
@@geonames = false
@@provider_order = [:google,:us]
+ @@ip_provider_order = [:geo_plugin,:ip]
@@logger=Logger.new(STDOUT)
@@logger.level=Logger::INFO
+ @@domain = nil
- [:yahoo, :google, :geocoder_us, :geocoder_ca, :geonames, :provider_order, :timeout,
- :proxy_addr, :proxy_port, :proxy_user, :proxy_pass,:logger].each do |sym|
- class_eval <<-EOS, __FILE__, __LINE__
- def self.#{sym}
- if defined?(#{sym.to_s.upcase})
- #{sym.to_s.upcase}
- else
- @@#{sym}
- end
+ def self.__define_accessors
+ class_variables.each do |v|
+ sym = v.delete("@").to_sym
+ unless self.respond_to? sym
+ module_eval <<-EOS, __FILE__, __LINE__
+ def self.#{sym}
+ value = if defined?(#{sym.to_s.upcase})
+ #{sym.to_s.upcase}
+ else
+ @@#{sym}
+ end
+ if value.is_a?(Hash)
+ value = (self.domain.nil? ? nil : value[self.domain]) || value.values.first
+ end
+ value
+ end
+
+ def self.#{sym}=(obj)
+ @@#{sym} = obj
+ end
+ EOS
end
-
- def self.#{sym}=(obj)
- @@#{sym} = obj
- end
- EOS
+ end
end
+
+ __define_accessors
# Error which is thrown in the event a geocoding error occurs.
class GeocodeError < StandardError; end
# -------------------------------------------------------------------------------------------
@@ -103,13 +119,12 @@
# Main method which calls the do_geocode template method which subclasses
# are responsible for implementing. Returns a populated GeoLoc or an
# empty one with a failed success code.
def self.geocode(address)
res = do_geocode(address)
- return res.success? ? res : GeoLoc.new
+ return res.nil? ? GeoLoc.new : res
end
-
# Main method which calls the do_reverse_geocode template method which subclasses
# are responsible for implementing. Returns a populated GeoLoc or an
# empty one with a failed success code.
def self.reverse_geocode(latlng)
res = do_reverse_geocode(latlng)
@@ -296,10 +311,12 @@
res.city=doc.elements['//City'].text if doc.elements['//City'] && doc.elements['//City'].text != nil
res.state=doc.elements['//State'].text if doc.elements['//State'] && doc.elements['//State'].text != nil
res.zip=doc.elements['//Zip'].text if doc.elements['//Zip'] && doc.elements['//Zip'].text != nil
res.street_address=doc.elements['//Address'].text if doc.elements['//Address'] && doc.elements['//Address'].text != nil
res.precision=doc.elements['//Result'].attributes['precision'] if doc.elements['//Result']
+ # set the accuracy as google does (added by Andruby)
+ res.accuracy=%w{unknown country state state city zip zip+4 street address building}.index(res.precision)
res.success=true
return res
else
logger.info "Yahoo was unable to geocode address: "+address
return GeoLoc.new
@@ -439,12 +456,12 @@
res.zip = doc.elements['.//PostalCodeNumber'].text if doc.elements['.//PostalCodeNumber']
res.street_address = doc.elements['.//ThoroughfareName'].text if doc.elements['.//ThoroughfareName']
# Translate accuracy into Yahoo-style token address, street, zip, zip+4, city, state, country
# For Google, 1=low accuracy, 8=high accuracy
address_details=doc.elements['.//*[local-name() = "AddressDetails"]']
- accuracy = address_details ? address_details.attributes['Accuracy'].to_i : 0
- res.precision=%w{unknown country state state city zip zip+4 street address building}[accuracy]
+ res.accuracy = address_details ? address_details.attributes['Accuracy'].to_i : 0
+ res.precision=%w{unknown country state state city zip zip+4 street address building}[res.accuracy]
res.success=true
return res
end
end
@@ -461,11 +478,11 @@
def self.do_geocode(ip)
return GeoLoc.new unless /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/.match(ip)
response = self.call_geocoder_service("http://www.geoplugin.net/xml.gp?ip=#{ip}")
return response.is_a?(Net::HTTPSuccess) ? parse_xml(response.body) : GeoLoc.new
rescue
- logger.error "Caught an error during GeloPluginGeocoder geocoding call: "+$!
+ logger.error "Caught an error during GeoPluginGeocoder geocoding call: "+$!
return GeoLoc.new
end
def self.parse_xml(xml)
xml = REXML::Document.new(xml)
@@ -474,11 +491,11 @@
geo.city = xml.elements['//geoplugin_city'].text
geo.state = xml.elements['//geoplugin_region'].text
geo.country_code = xml.elements['//geoplugin_countryCode'].text
geo.lat = xml.elements['//geoplugin_latitude'].text.to_f
geo.lng = xml.elements['//geoplugin_longitude'].text.to_f
- geo.success = !geo.city.empty?
+ geo.success = !!geo.city && !geo.city.empty?
return geo
end
end
# Provides geocoding based upon an IP address. The underlying web service is a hostip.info
@@ -527,29 +544,33 @@
# -------------------------------------------------------------------------------------------
# The Multi Geocoder
# -------------------------------------------------------------------------------------------
# Provides methods to geocode with a variety of geocoding service providers, plus failover
- # among providers in the order you configure.
+ # among providers in the order you configure. When 2nd parameter is set 'true', perform
+ # ip location lookup with 'address' as the ip address.
#
# Goal:
# - homogenize the results of multiple geocoders
#
# Limitations:
# - currently only provides the first result. Sometimes geocoders will return multiple results.
# - currently discards the "accuracy" component of the geocoding calls
- class MultiGeocoder < Geocoder
- private
+ class MultiGeocoder < Geocoder
+ private
# This method will call one or more geocoders in the order specified in the
# configuration until one of the geocoders work.
#
# The failover approach is crucial for production-grade apps, but is rarely used.
# 98% of your geocoding calls will be successful with the first call
def self.do_geocode(address)
- Geokit::Geocoders::provider_order.each do |provider|
+ geocode_ip = !!/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/.match(address)
+ provider_order = geocode_ip ? Geokit::Geocoders::ip_provider_order : Geokit::Geocoders::provider_order
+
+ provider_order.each do |provider|
begin
- klass = Geokit::Geocoders.const_get "#{provider.to_s.capitalize}Geocoder"
+ klass = Geokit::Geocoders.const_get "#{Geokit::Inflector::camelize(provider.to_s)}Geocoder"
res = klass.send :geocode, address
return res if res.success?
rescue
logger.error("Something has gone very wrong during geocoding, OR you have configured an invalid class name in Geokit::Geocoders::provider_order. Address: #{address}. Provider: #{provider}")
end