Module: Geocoder::Store::ActiveRecord::ClassMethods

Defined in:
lib/geocoder/stores/active_record.rb

Overview

Methods which will be class methods of the including class.

Instance Method Summary collapse

Instance Method Details

#add_exclude_condition(conditions, exclude) ⇒ Object

Adds a condition to exclude a given object by ID. Expects conditions as an array or string. Returns array.


230
231
232
233
234
235
236
237
# File 'lib/geocoder/stores/active_record.rb', line 230

def add_exclude_condition(conditions, exclude)
  conditions = [conditions] if conditions.is_a?(String)
  if exclude
    conditions[0] << " AND #{full_column_name(primary_key)} != ?"
    conditions << exclude.id
  end
  conditions
end

#bearing_sql(latitude, longitude, options = {}) ⇒ Object

SQL for calculating bearing based on the current database's capabilities (trig functions?).


188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/geocoder/stores/active_record.rb', line 188

def bearing_sql(latitude, longitude, options = {})
  if !options.include?(:bearing)
    options[:bearing] = Geocoder.config.distances
  end
  if options[:bearing]
    method_prefix = using_unextended_sqlite? ? "approx" : "full"
    Geocoder::Sql.send(
      method_prefix + "_bearing",
      latitude, longitude,
      full_column_name(options[:latitude] || geocoder_options[:latitude]),
      full_column_name(options[:longitude]|| geocoder_options[:longitude]),
      options
    )
  end
end

#distance_from_sql(location, *args) ⇒ Object


83
84
85
86
87
88
# File 'lib/geocoder/stores/active_record.rb', line 83

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

#distance_sql(latitude, longitude, options = {}) ⇒ Object

SQL for calculating distance based on the current database's capabilities (trig functions?).


173
174
175
176
177
178
179
180
181
182
# File 'lib/geocoder/stores/active_record.rb', line 173

def distance_sql(latitude, longitude, options = {})
  method_prefix = using_unextended_sqlite? ? "approx" : "full"
  Geocoder::Sql.send(
    method_prefix + "_distance",
    latitude, longitude,
    full_column_name(options[:latitude] || geocoder_options[:latitude]),
    full_column_name(options[:longitude]|| geocoder_options[:longitude]),
    options
  )
end

#false_conditionObject

Value which can be passed to where() to produce no results.


269
270
271
# File 'lib/geocoder/stores/active_record.rb', line 269

def false_condition
  using_unextended_sqlite? ? 0 : "false"
end

#full_column_name(column) ⇒ Object

Prepend table name if column name doesn't already contain one.


276
277
278
279
# File 'lib/geocoder/stores/active_record.rb', line 276

def full_column_name(column)
  column = column.to_s
  column.include?(".") ? column : [table_name, column].join(".")
end

#near_scope_options(latitude, longitude, radius = 20, options = {}) ⇒ Object

Get options hash suitable for passing to ActiveRecord.find to get records within a radius (in kilometers) of the given point. Options hash may include:

  • :units - :mi or :km; to be used. for interpreting radius as well as the distance attribute which is added to each found nearby object. Use Geocoder.configure to configure default units.

  • :bearing - :linear or :spherical. the method to be used for calculating the bearing (direction) between the given point and each found nearby point; set to false for no bearing calculation. Use Geocoder.configure to configure default calculation method.

  • :select - string with the SELECT SQL fragment (e.g. “id, name”)

  • :select_distance - whether to include the distance alias in the

    SELECT SQL fragment (e.g. <formula> AS distance)
    
  • :select_bearing - like :select_distance but for bearing.

  • :order - column(s) for ORDER BY SQL clause; default is distance;

    set to false or nil to omit the ORDER BY clause
    
  • :exclude - an object to exclude (used by the nearbys method)

  • :distance_column - used to set the column name of the calculated distance.

  • :bearing_column - used to set the column name of the calculated bearing.

  • :min_radius - the value to use as the minimum radius.

    ignored if database is sqlite.
    default is 0.0
    

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/geocoder/stores/active_record.rb', line 117

def near_scope_options(latitude, longitude, radius = 20, options = {})
  if options[:units]
    options[:units] = options[:units].to_sym
  end
  latitude_attribute = options[:latitude] || geocoder_options[:latitude]
  longitude_attribute = options[:longitude] || geocoder_options[:longitude]
  options[:units] ||= (geocoder_options[:units] || Geocoder.config.units)
  select_distance = options.fetch(:select_distance)  { true }
  options[:order] = "" if !select_distance && !options.include?(:order)
  select_bearing = options.fetch(:select_bearing) { true }
  bearing = bearing_sql(latitude, longitude, options)
  distance = distance_sql(latitude, longitude, options)
  distance_column = options.fetch(:distance_column) { 'distance' }
  bearing_column = options.fetch(:bearing_column)  { 'bearing' }

  # If radius is a DB column name, bounding box should include
  # all rows within the maximum radius appearing in that column.
  # Note: performance is dependent on variability of radii.
  bb_radius = radius.is_a?(Symbol) ? maximum(radius) : radius
  b = Geocoder::Calculations.bounding_box([latitude, longitude], bb_radius, options)
  args = b + [
    full_column_name(latitude_attribute),
    full_column_name(longitude_attribute)
  ]
  bounding_box_conditions = Geocoder::Sql.within_bounding_box(*args)

  if using_unextended_sqlite?
    conditions = bounding_box_conditions
  else
    min_radius = options.fetch(:min_radius, 0).to_f
    # if radius is a DB column name,
    # find rows between min_radius and value in column
    if radius.is_a?(Symbol)
      c = "BETWEEN ? AND #{radius}"
      a = [min_radius]
    else
      c = "BETWEEN ? AND ?"
      a = [min_radius, radius]
    end
    conditions = [bounding_box_conditions + " AND (#{distance}) " + c] + a
  end
  {
    :select => select_clause(options[:select],
                             select_distance ? distance : nil,
                             select_bearing ? bearing : nil,
                             distance_column,
                             bearing_column),
    :conditions => add_exclude_condition(conditions, options[:exclude]),
    :order => options.include?(:order) ? options[:order] : "#{distance_column} ASC"
  }
end

#null_valueObject

Use OID type when running in PosgreSQL


262
263
264
# File 'lib/geocoder/stores/active_record.rb', line 262

def null_value
  using_postgres? ? 'NULL::text' : 'NULL'
end

#select_clause(columns, distance = nil, bearing = nil, distance_column = 'distance', bearing_column = 'bearing') ⇒ Object

Generate the SELECT clause.


207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/geocoder/stores/active_record.rb', line 207

def select_clause(columns, distance = nil, bearing = nil, distance_column = 'distance', bearing_column = 'bearing')
  if columns == :id_only
    return full_column_name(primary_key)
  elsif columns == :geo_only
    clause = ""
  else
    clause = (columns || full_column_name("*"))
  end
  if distance
    clause += ", " unless clause.empty?
    clause += "#{distance} AS #{distance_column}"
  end
  if bearing
    clause += ", " unless clause.empty?
    clause += "#{bearing} AS #{bearing_column}"
  end
  clause
end

#using_postgres?Boolean

Returns:

  • (Boolean)

255
256
257
# File 'lib/geocoder/stores/active_record.rb', line 255

def using_postgres?
  connection.adapter_name.match(/postgres/i)
end

#using_sqlite?Boolean

Returns:

  • (Boolean)

243
244
245
# File 'lib/geocoder/stores/active_record.rb', line 243

def using_sqlite?
  !!connection.adapter_name.match(/sqlite/i)
end

#using_sqlite_with_extensions?Boolean

Returns:

  • (Boolean)

247
248
249
250
251
252
253
# File 'lib/geocoder/stores/active_record.rb', line 247

def using_sqlite_with_extensions?
  connection.adapter_name.match(/sqlite/i) &&
    defined?(::SqliteExt) &&
    %W(MOD POWER SQRT PI SIN COS ASIN ATAN2).all?{ |fn_name|
      connection.raw_connection.function_created?(fn_name)
    }
end

#using_unextended_sqlite?Boolean

Returns:

  • (Boolean)

239
240
241
# File 'lib/geocoder/stores/active_record.rb', line 239

def using_unextended_sqlite?
  using_sqlite? && !using_sqlite_with_extensions?
end