module Geocoder::Store::ActiveRecord::ClassMethods

def add_exclude_condition(conditions, exclude)


The given conditions MUST be an array.
Adds a condition to exclude a given object by ID.
#
def add_exclude_condition(conditions, exclude)
  if exclude
    conditions[0] << " AND #{full_column_name(:id)} != ?"
    conditions << exclude.id
  end
  conditions
end

def approx_distance_from_sql(latitude, longitude, options)

def approx_distance_from_sql(latitude, longitude, options)
  lat_attr = geocoder_options[:latitude]
  lon_attr = geocoder_options[:longitude]
  dx = Geocoder::Calculations.longitude_degree_distance(30, options[:units] || :mi)
  dy = Geocoder::Calculations.latitude_degree_distance(options[:units] || :mi)
  # sin of 45 degrees = average x or y component of vector
  factor = Math.sin(Math::PI / 4)
  "(#{dy} * ABS(#{full_column_name(lat_attr)} - #{latitude}) * #{factor}) + " +
    "(#{dx} * ABS(#{full_column_name(lon_attr)} - #{longitude}) * #{factor})"
end

def approx_near_scope_options(latitude, longitude, radius, options)


only exist for interface consistency--not intended for production!
Distance and bearing calculations are *extremely inaccurate*. They

objects outside the given radius).
rather than a circle, so results are very approximate (will include
functions, like SQLite. Approach is to find objects within a square
Scope options hash for use with a database without trigonometric
#
def approx_near_scope_options(latitude, longitude, radius, options)
  lat_attr = geocoder_options[:latitude]
  lon_attr = geocoder_options[:longitude]
  unless options.include?(:bearing)
    options[:bearing] = (options[:method] || \
                         geocoder_options[:method] || \
                         Geocoder::Configuration.distances)
  end
  if options[:bearing]
    bearing = "CASE " +
      "WHEN (#{full_column_name(lat_attr)} >= #{latitude} AND #{full_column_name(lon_attr)} >= #{longitude}) THEN  45.0 " +
      "WHEN (#{full_column_name(lat_attr)} <  #{latitude} AND #{full_column_name(lon_attr)} >= #{longitude}) THEN 135.0 " +
      "WHEN (#{full_column_name(lat_attr)} <  #{latitude} AND #{full_column_name(lon_attr)} <  #{longitude}) THEN 225.0 " +
      "WHEN (#{full_column_name(lat_attr)} >= #{latitude} AND #{full_column_name(lon_attr)} <  #{longitude}) THEN 315.0 " +
    "END"
  else
    bearing = false
  end
  options[:units] ||= (geocoder_options[:units] || Geocoder::Configuration.units)
  distance = approx_distance_from_sql(latitude, longitude, options)
  b = Geocoder::Calculations.bounding_box([latitude, longitude], radius, options)
  conditions = [
    "#{full_column_name(lat_attr)} BETWEEN ? AND ? AND #{full_column_name(lon_attr)} BETWEEN ? AND ?"] +
    [b[0], b[2], b[1], b[3]
  ]
  default_near_scope_options(latitude, longitude, radius, options).merge(
    :select => "#{options[:select] || full_column_name("*")}, " +
      "#{distance} AS distance" +
      (bearing ? ", #{bearing} AS bearing" : ""),
    :conditions => add_exclude_condition(conditions, options[:exclude])
  )
end

def default_near_scope_options(latitude, longitude, radius, options)


Options used for any near-like scope.
#
def default_near_scope_options(latitude, longitude, radius, options)
  {
    :order  => options[:order] || "distance",
    :limit  => options[:limit],
    :offset => options[:offset]
  }
end

def distance_from_sql(location, *args)

def distance_from_sql(location, *args)
  latitude, longitude = Geocoder::Calculations.extract_coordinates(location)
  if Geocoder::Calculations.coordinates_present?(latitude, longitude)
    distance_from_sql_options(latitude, longitude, *args)
  end
end

def distance_from_sql_options(latitude, longitude, options = {})

def distance_from_sql_options(latitude, longitude, options = {})
  if using_sqlite?
    approx_distance_from_sql(latitude, longitude, options)
  else
    full_distance_from_sql(latitude, longitude, options)
  end
end

def false_condition


Value which can be passed to where() to produce no results.
#
def false_condition
  using_sqlite? ? 0 : "false"
end

def full_column_name(column)


Prepend table name if column name doesn't already contain one.
#
def full_column_name(column)
  column = column.to_s
  column.include?(".") ? column : [table_name, column].join(".")
end

def full_distance_from_sql(latitude, longitude, options)

def full_distance_from_sql(latitude, longitude, options)
  lat_attr = geocoder_options[:latitude]
  lon_attr = geocoder_options[:longitude]
  earth = Geocoder::Calculations.earth_radius(options[:units] || :mi)
  "#{earth} * 2 * ASIN(SQRT(" +
    "POWER(SIN((#{latitude} - #{full_column_name(lat_attr)}) * PI() / 180 / 2), 2) + " +
    "COS(#{latitude} * PI() / 180) * COS(#{full_column_name(lat_attr)} * PI() / 180) * " +
    "POWER(SIN((#{longitude} - #{full_column_name(lon_attr)}) * PI() / 180 / 2), 2) ))"
end

def full_near_scope_options(latitude, longitude, radius, options)


http://www.beginningspatial.com/calculating_bearing_one_point_another
Bearing calculation based on:

ATAN2(), DEGREES(), and RADIANS().
SQRT(), PI(), and trigonometric functions SIN(), COS(), ASIN(),
Scope options hash for use with a database that supports POWER(),
#
def full_near_scope_options(latitude, longitude, radius, options)
  lat_attr = geocoder_options[:latitude]
  lon_attr = geocoder_options[:longitude]
  options[:bearing] ||= (options[:method] ||
                         geocoder_options[:method] ||
                         Geocoder::Configuration.distances)
  bearing = case options[:bearing]
  when :linear
    "CAST(" +
      "DEGREES(ATAN2( " +
        "RADIANS(#{full_column_name(lon_attr)} - #{longitude}), " +
        "RADIANS(#{full_column_name(lat_attr)} - #{latitude})" +
      ")) + 360 " +
    "AS decimal) % 360"
  when :spherical
    "CAST(" +
      "DEGREES(ATAN2( " +
        "SIN(RADIANS(#{full_column_name(lon_attr)} - #{longitude})) * " +
        "COS(RADIANS(#{full_column_name(lat_attr)})), (" +
          "COS(RADIANS(#{latitude})) * SIN(RADIANS(#{full_column_name(lat_attr)}))" +
        ") - (" +
          "SIN(RADIANS(#{latitude})) * COS(RADIANS(#{full_column_name(lat_attr)})) * " +
          "COS(RADIANS(#{full_column_name(lon_attr)} - #{longitude}))" +
        ")" +
      ")) + 360 " +
    "AS decimal) % 360"
  end
  options[:units] ||= (geocoder_options[:units] || Geocoder::Configuration.units)
  distance = full_distance_from_sql(latitude, longitude, options)
  conditions = ["#{distance} <= ?", radius]
  default_near_scope_options(latitude, longitude, radius, options).merge(
    :select => "#{options[:select] || full_column_name("*")}, " +
      "#{distance} AS distance" +
      (bearing ? ", #{bearing} AS bearing" : ""),
    :conditions => add_exclude_condition(conditions, options[:exclude])
  )
end

def near_scope_options(latitude, longitude, radius = 20, options = {})


* +:exclude+ - an object to exclude (used by the +nearbys+ method)
* +:order+ - column(s) for ORDER BY SQL clause; default is distance
* +:select+ - string with the SELECT SQL fragment (e.g. “id, name”)
See Geocoder::Configuration to know how configure default method.
set to false for no bearing calculation.
between the given point and each found nearby point;
the method to be used for calculating the bearing (direction)
* +:bearing+ - :linear or :spherical.
See Geocoder::Configuration to know how configure default units.
is added to each found nearby object.
for interpreting radius as well as the +distance+ attribute which
* +:units+ - :mi or :km; to be used.

Options hash may include:
records within a radius (in kilometers) of the given point.
Get options hash suitable for passing to ActiveRecord.find to get
#
def near_scope_options(latitude, longitude, radius = 20, options = {})
  if using_sqlite?
    approx_near_scope_options(latitude, longitude, radius, options)
  else
    full_near_scope_options(latitude, longitude, radius, options)
  end
end

def using_sqlite?

def using_sqlite?
  connection.adapter_name.match /sqlite/i
end