lib/geokit/mappable.rb in darrell-geokit-1.2.4.1 vs lib/geokit/mappable.rb in darrell-geokit-1.4.1.1

- old
+ new

@@ -108,13 +108,13 @@ distance=from.distance_to(to,options) midpoint=from.endpoint(heading,distance/2,options) end # Geocodes a location using the multi geocoder. - def geocode(location) - res = Geocoders::MultiGeocoder.geocode(location) - return res if res.success + def geocode(location, options = {}) + res = Geocoders::MultiGeocoder.geocode(location, options) + return res if res.success? raise Geokit::Geocoders::GeocodeError end protected @@ -249,10 +249,18 @@ # is true if the lat and lng attributes are the same for both objects. def ==(other) other.is_a?(LatLng) ? self.lat == other.lat && self.lng == other.lng : false end + def hash + lat.hash + lng.hash + end + + def eql?(other) + self == other + end + # A *class* method to take anything which can be inferred as a point and generate # a LatLng from it. You should use this anything you're not sure what the input is, # and want to deal with it as a LatLng if at all possible. Can take: # 1) two arguments (lat,lng) # 2) a string in the format "37.1234,-129.1234" or "37.1234 -129.1234" @@ -268,11 +276,11 @@ thing.strip! if match=thing.match(/(\-?\d+\.?\d*)[, ] ?(\-?\d+\.?\d*)$/) return Geokit::LatLng.new(match[1],match[2]) else res = Geokit::Geocoders::MultiGeocoder.geocode(thing) - return res if res.success + return res if res.success? raise Geokit::Geocoders::GeocodeError end elsif thing.is_a?(Array) && thing.size==2 return Geokit::LatLng.new(thing[0],thing[1]) elsif thing.is_a?(LatLng) # will also be true for GeoLocs @@ -282,10 +290,34 @@ end raise ArgumentError.new("#{thing} (#{thing.class}) cannot be normalized to a LatLng. We tried interpreting it as an array, string, Mappable, etc., but no dice.") end + # Reverse geocodes a LatLng object using the MultiGeocoder (default), or optionally + # using a geocoder of your choosing. Returns a new Geokit::GeoLoc object + # + # ==== Options + # * :using - Specifies the geocoder to use for reverse geocoding. Defaults to + # MultiGeocoder. Can be either the geocoder class (or any class that + # implements do_reverse_geocode for that matter), or the name of + # the class without the "Geocoder" part (e.g. :google) + # + # ==== Examples + # LatLng.new(51.4578329, 7.0166848).reverse_geocode # => #<Geokit::GeoLoc:0x12dac20 @state...> + # LatLng.new(51.4578329, 7.0166848).reverse_geocode(:using => :google) # => #<Geokit::GeoLoc:0x12dac20 @state...> + # LatLng.new(51.4578329, 7.0166848).reverse_geocode(:using => Geokit::Geocoders::GoogleGeocoder) # => #<Geokit::GeoLoc:0x12dac20 @state...> + def reverse_geocode(options = { :using => Geokit::Geocoders::MultiGeocoder }) + if options[:using].is_a?(String) or options[:using].is_a?(Symbol) + provider = Geokit::Geocoders.const_get("#{Geokit::Inflector::camelize(options[:using].to_s)}Geocoder") + elsif options[:using].respond_to?(:do_reverse_geocode) + provider = options[:using] + else + raise ArgumentError.new("#{options[:using]} is not a valid geocoder.") + end + + provider.send(:reverse_geocode, self) + end end # This class encapsulates the result of a geocoding call. # It's primary purpose is to homogenize the results of multiple # geocoding providers. It also provides some additional functionality, such as @@ -312,13 +344,16 @@ # 100 Spear St, San Francisco, CA, 94101, US attr_accessor :street_address, :city, :state, :zip, :country_code, :full_address, :all # Attributes set upon return from geocoding. Success will be true for successful # geocode lookups. The provider will be set to the name of the providing geocoder. # Finally, precision is an indicator of the accuracy of the geocoding. - attr_accessor :success, :provider, :precision + attr_accessor :success, :provider, :precision, :suggested_bounds # Street number and street name are extracted from the street address attribute. attr_reader :street_number, :street_name + # accuracy is set for Yahoo and Google geocoders, it is a numeric value of the + # precision. see http://code.google.com/apis/maps/documentation/geocoding/#GeocodingAccuracy + attr_accessor :accuracy # Constructor expects a hash of symbols to correspond with attributes. def initialize(h={}) @all = [self] @@ -335,10 +370,14 @@ # Returns true if geocoded to the United States. def is_us? country_code == 'US' end + + def success? + success == true + end # full_address is provided by google but not by yahoo. It is intended that the google # geocoding method will provide the full address, whereas for yahoo it will be derived # from the parts of the address we do have. def full_address @@ -372,26 +411,26 @@ # Sets the street address after capitalizing each word within the street address. def street_address=(address) #@street_address = Geokit::Inflector::titleize(address) if address @street_address = address if address end - + # Returns a comma-delimited string consisting of the street address, city, state, # zip, and country code. Only includes those attributes that are non-blank. def to_geocodeable_s a=[street_address, city, state, zip, country_code].compact a.delete_if { |e| !e || e == '' } a.join(', ') end def to_yaml_properties - (instance_variables - ['@results']).sort + (instance_variables - ['@all']).sort end # Returns a string representation of the instance. def to_s - "Provider: #{provider}\n Street: #{street_address}\nCity: #{city}\nState: #{state}\nZip: #{zip}\nLatitude: #{lat}\nLongitude: #{lng}\nCountry: #{country_code}\nSuccess: #{success}" + "Provider: #{provider}\nStreet: #{street_address}\nCity: #{city}\nState: #{state}\nZip: #{zip}\nLatitude: #{lat}\nLongitude: #{lng}\nCountry: #{country_code}\nSuccess: #{success}" end end # Bounds represents a rectangular bounds, defined by the SW and NE corners class Bounds @@ -439,9 +478,19 @@ # Returns true if the candidate object is logically equal. Logical equivalence # is true if the lat and lng attributes are the same for both objects. def ==(other) other.is_a?(Bounds) ? self.sw == other.sw && self.ne == other.ne : false + end + + # Equivalent to Google Maps API's .toSpan() method on GLatLng's. + # + # Returns a LatLng object, whose coordinates represent the size of a rectangle + # defined by these bounds. + def to_span + lat_span = (@ne.lat - @sw.lat).abs + lng_span = (crosses_meridian? ? 360 + @ne.lng - @sw.lng : @ne.lng - @sw.lng).abs + Geokit::LatLng.new(lat_span, lng_span) end class <<self # returns an instance of bounds which completely encompases the given circle