module Geocoder::Store::ActiveRecord

def self.included(base)


Implementation of 'included' hook method.
#
def self.included(base)
  base.extend ClassMethods
  base.class_eval do
    # scope: geocoded objects
    scope :geocoded, lambda {
      where("#{table_name}.#{geocoder_options[:latitude]} IS NOT NULL " +
        "AND #{table_name}.#{geocoder_options[:longitude]} IS NOT NULL")
    }
    # scope: not-geocoded objects
    scope :not_geocoded, lambda {
      where("#{table_name}.#{geocoder_options[:latitude]} IS NULL " +
        "OR #{table_name}.#{geocoder_options[:longitude]} IS NULL")
    }
    # scope: not-reverse geocoded objects
    scope :not_reverse_geocoded, lambda {
      where("#{table_name}.#{geocoder_options[:fetched_address]} IS NULL")
    }
    ##
    # Find all objects within a radius of the given location.
    # Location may be either a string to geocode or an array of
    # coordinates (<tt>[lat,lon]</tt>). Also takes an options hash
    # (see Geocoder::Store::ActiveRecord::ClassMethods.near_scope_options
    # for details).
    #
    scope :near, lambda{ |location, *args|
      latitude, longitude = Geocoder::Calculations.extract_coordinates(location)
      if Geocoder::Calculations.coordinates_present?(latitude, longitude)
        options = near_scope_options(latitude, longitude, *args)
        select(options[:select]).where(options[:conditions]).
          order(options[:order])
      else
        # If no lat/lon given we don't want any results, but we still
        # need distance and bearing columns so you can add, for example:
        # .order("distance")
        select(select_clause(nil, null_value, null_value)).where(false_condition)
      end
    }
    ##
    # Find all objects within the area of a given bounding box.
    # Bounds must be an array of locations specifying the southwest
    # corner followed by the northeast corner of the box
    # (<tt>[[sw_lat, sw_lon], [ne_lat, ne_lon]]</tt>).
    #
    scope :within_bounding_box, lambda{ |*bounds|
      sw_lat, sw_lng, ne_lat, ne_lng = bounds.flatten if bounds
      if sw_lat && sw_lng && ne_lat && ne_lng
        where(Geocoder::Sql.within_bounding_box(
          sw_lat, sw_lng, ne_lat, ne_lng,
          full_column_name(geocoder_options[:latitude]),
          full_column_name(geocoder_options[:longitude])
        ))
      else
        select(select_clause(nil, null_value, null_value)).where(false_condition)
      end
    }
  end
end

def geocode


(or other as specified in +geocoded_by+). Returns coordinates (array).
Look up coordinates and assign to +latitude+ and +longitude+ attributes
#
def geocode
  do_lookup(false) do |o,rs|
    if r = rs.first
      unless r.latitude.nil? or r.longitude.nil?
        o.__send__  "#{self.class.geocoder_options[:latitude]}=",  r.latitude
        o.__send__  "#{self.class.geocoder_options[:longitude]}=", r.longitude
      end
      r.coordinates
    end
  end
end

def nearbys(radius = 20, options = {})


Returns nil if the object is not geocoded.
Takes the same options hash as the near class method (scope).
Get nearby geocoded objects.
#
def nearbys(radius = 20, options = {})
  return nil unless geocoded?
  options.merge!(:exclude => self) unless send(self.class.primary_key).nil?
  self.class.near(self, radius, options)
end

def reverse_geocode


in +reverse_geocoded_by+). Returns address (string).
Look up address and assign to +address+ attribute (or other as specified
#
def reverse_geocode
  do_lookup(true) do |o,rs|
    if r = rs.first
      unless r.address.nil?
        o.__send__ "#{self.class.geocoder_options[:fetched_address]}=", r.address
      end
      r.address
    end
  end
end