GithubHelp home page GithubHelp logo

geokit / geokit-rails Goto Github PK

View Code? Open in Web Editor NEW
1.6K 36.0 243.0 389 KB

Official Geokit plugin for Rails/ActiveRecord. Provides location-based goodness for your Rails app. Requires the Geokit gem.

License: MIT License

Ruby 93.50% CSS 0.03% HTML 6.47%

geokit-rails's Introduction

Geokit Rails

Gem Version Build Status Coverage Status Code Climate

COMMUNICATION

  • If you need help, use Stack Overflow. (Tag 'geokit' and we'll be alerted)
  • If you found a bug, use GitHub issues.
  • If you have an idea, use GitHub issues.
  • If you'd like to ask a general question, use GitHub issues.
  • If you want to contribute, submit a pull request.

INSTALLATION

Geokit for Rails consists of a generic Gem (geokit) and a Rails plugin (geokit-rails).

Make sure you use a version >= 3.0 of Rails.

You just have to add the 'geokit-rails' gem to your Gemfile

gem 'geokit-rails'

Then tell bundler to update the gems :

$ bundle install

Generate the configuration initializer:

$ rails g geokit_rails:install

Now, if you wish to use the various geocoding services, you can add your keys to the new initializer.

If you want to use geokit-rails in a Rails 2 application, just use the good old plugin (geokit-rails).

FEATURE SUMMARY

Geokit provides key functionality for location-oriented Rails applications:

  • Distance calculations, for both flat and spherical environments. For example, given the location of two points on the earth, you can calculate the miles/Km between them.
  • ActiveRecord distance-based finders. For example, you can find all the points in your database within a 50-mile radius.
  • IP-based location lookup utilizing hostip.info. Provide an IP address, and get city name and latitude/longitude in return
  • A before_action helper to geocoder the user's location based on IP address, and retain the location in a cookie.
  • Geocoding from multiple providers. It provides a fail-over mechanism, in case your input fails to geocode in one service. Geocoding is provided by the Geokit gem, which you must have installed

The goal of this plugin is to provide the common functionality for location-oriented applications (geocoding, location lookup, distance calculation) in an easy-to-use package.

A NOTE ON TERMINOLOGY

Throughout the code and API, latitude and longitude are referred to as lat and lng. We've found over the long term the abbreviation saves lots of typing time.

LOCATION QUERIES

MAKING A MODEL MAPPABLE

To get started, just specify an ActiveRecord class as acts_as_mappable:

class Location < ActiveRecord::Base
  acts_as_mappable
end

There are some defaults you can override:

class Location < ActiveRecord::Base
  acts_as_mappable :default_units => :miles,
                   :default_formula => :sphere,
                   :distance_field_name => :distance,
                   :lat_column_name => :lat,
                   :lng_column_name => :lng
end

The optional parameters are units, formula, and distance_field_name. Values for units can be :miles, :kms (kilometers), or :nms (nautical miles), with :miles as the default. Values for formula can be :sphere or :flat with :sphere as the default. :sphere gives you Haversine calculations, while :flat gives the Pythagoreum Theory. These defaults persist through out the gem.

The plug-in creates a calculated distance field on AR instances that have been retrieved through a Geokit location query. By default, these fields are known as "distance" but this can be changed through the :distance_field_name key.

You can also define alternative column names for latitude and longitude using the :lat_column_name and :lng_column_name keys. The defaults are lat and lng respectively.

NEW SCOPES TO USE

Once you've specified acts_as_mappable, a few scopes are available :

  • within and beyond find records within or beyond a certain distance from the origin point.
  • in_range finds records within a certain distance range from the origin point.
  • in_bounds finds records within a rectangle on the map
  • closest and farthest find the closest or farthest record from the origin point
  • by_distance finds records ordered by distance from the origin point

All of these scopes take a hash of options where the first parameter is simply one of the possible options, without the name.

A few examples :

Location.within(5, :origin => @somewhere)
Location.in_range(2..5, :origin => @somewhere)
Location.in_bounds([@south_west_point, @north_east_point], :origin => @somewhere)

The options can be :

:origin as a two-element array of latitude/longitude:

Location.by_distance(:origin => [37.792,-122.393])

:origin as a geocodeable string:

Location.by_distance(:origin => '100 Spear st, San Francisco, CA')

:origin as an object which responds to lat and lng methods, or latitude and longitude methods, or whatever methods you have specified for lng_column_name and lat_column_name:

Location.geo_scope(:origin => my_store)
# my_store.lat and my_store.lng methods exist

:units or :formula can be used to override the default values in a specific query

Location.within(5, :units => :kms, :origin => @somewhere)
# it will get the records within 5 kilometers instead of 5 miles

:range as a native Ruby range

:bounds as an array of two elements : the south/west point and the north/east point.

@sw = Geokit::LatLng.new(32.91663,-96.982841)
@ne = Geokit::LatLng.new(32.96302,-96.919495)
@somewhere = Location.find(123456)
Location.within(:bounds => [@sw, @ne], :origin => @somewhere)

:bounds as a Geokit::Bounds object

@bounds = Geokit::Bounds.new([32.91663,-96.982841], [32.96302,-96.919495])
@somewhere = Location.find(123456)
Location.within(:bounds => [@sw, @ne], :origin => @somewhere)

When using a point of reference or bounds, you leverage the power of Geokit to build this objects. Basically, if Geokit can make a Geokit::Point or a Geokit::Bounds with what you give to it, you're good to go.

FIND BY SQL

Finally, if all that is desired is the raw SQL for distance calculations, you can use the following:

Location.distance_sql(origin, units = default_units, formula = default_formula)

Thereafter, you are free to use it in find_by_sql as you wish.

CHAINABILITY

You can then chain these scope with any other or use a "calling" method like first, all, count, โ€ฆ

Location.within(5, :origin => @somewhere).all
Location.within(5, :origin => @somewhere).count
Location.by_distance(:origin => [37.792,-122.393]).first

You can add order clauses in the chain as for any ActiveRecord query

Location.within(5, :origin => @somewhere).order('nbr_seats ASC')

You can even sort by distance (use the same name as specified in the model class)

Location.within(5, :origin => @somewhere).order('distance DESC, nbr_seats ASC')

Idem for the limit clause. In fact, closest and farthest are defined like this :

def closest(options = {})
  by_distance(options).limit(1)
end
def farthest(options = {})
  by_distance({:reverse => true}.merge(options)).limit(1)
end

Important caveat

In the current version of geokit-rails, it is not possible to add a where clause using the distance column. I've tried many different ways to do this and didn't get it working.

One would expect to build a query like this :

scoped  = Location.by_distance(:origin => @somewhere)
scoped  = scoped.where('distance <= 5')
results = scoped.all

This is not possible right now, it must be done in a single step like this :

scoped  = Location.within(5, :origin => @somewhere)
results = scoped.all

Every good idea that would help achieve this is very much welcome.

FINDING WITHIN A BOUNDING BOX

If you are displaying points on a map, you probably need to query for whatever falls within the rectangular bounds of the map:

Store.in_bounds([sw_point,ne_point]).all

If you want the query to return things that are located on the rectangular bounds, specify the inclusive option set to true:

Store.in_bounds([sw_point,ne_point], :inclusive => true).all

The input to bounds can be an array with the two points or a Bounds object. However you provide them, the order should always be the southwest corner, northeast corner of the rectangle. Typically, you will be getting the sw_point and ne_point from a map that is displayed on a web page.

If you need to calculate the bounding box from a point and radius, you can do that:

bounds = Geokit::Bounds.from_point_and_radius(home,5)
Store.in_bounds(bounds).all

What is following is from the previous geokit-rails plugin.

It has not been tested with Rails 3 nor with this version of the gem. Most of it should work, but it is not sure

USING INCLUDES

You can use includes along with your distance finders:

stores = Store.within(5, :origin=>home).includes([:reviews,:cities]).order('distance asc').all

However, ActiveRecord drops the calculated distance column when you use include. So, if you need to use the distance column, you'll have to re-calculate it post-query in Ruby:

stores.sort_by{|s| s.distance_to(home)}

In this case, you may want to just use the bounding box condition alone in your SQL (there's no use calculating the distance twice):

bounds=Geokit::Bounds.from_point_and_radius(home,5)
stores=Store.includes([:reviews,:cities]).in_bounds(bounds)
stores.sort_by{|s| s.distance_to(home)}

USING :through

You can also specify a model as mappable "through" another associated model. In other words, that associated model is the actual mappable model with "lat" and "lng" attributes, but this "through" model can still utilize all of the above find methods to search for records.

class Location < ActiveRecord::Base
  belongs_to :locatable, :polymorphic => true
  acts_as_mappable
end
class Company < ActiveRecord::Base
  has_one :location, :as => :locatable  # also works for belongs_to associations
  acts_as_mappable :through => :location
end

Then you can still call:

Company.within(distance, :origin => @somewhere)

You can also give :through a hash if your location is nested deep. For example, given:

class House
  acts_as_mappable
end
class Family
  belongs_to :house
end
class Person
  belongs_to :family
  acts_as_mappable :through => { :family => :house }
end

Remember that the notes above about USING INCLUDES apply to the results from this find, since an include is automatically used.

IP GEOCODING

You can obtain the location for an IP at any time using the geocoder as in the following example:

location = IpGeocoder.geocode('12.215.42.19')

where Location is a GeoLoc instance containing the latitude, longitude, city, state, and country code. Also, the success value is true.

If the IP cannot be geocoded, a GeoLoc instance is returned with a success value of false.

It should be noted that the IP address needs to be visible to the Rails application. In other words, you need to ensure that the requesting IP address is forwarded by any front-end servers that are out in front of the Rails app. Otherwise, the IP will always be that of the front-end server.

The Multi-Geocoder will also geocode IP addresses and provide failover among multiple IP geocoders. Just pass in an IP address for the parameter instead of a street address. Eg:

location = Geocoders::MultiGeocoder.geocode('12.215.42.19')

The MultiGeocoder class requires 2 configuration setting for the provider order. Ordering is done through Geokit::Geocoders::provider_order and Geokit::Geocoders::ip_provider_order, found in config/initializers/geokit_config.rb. If you don't already have a geokit_config.rb file, the plugin creates one when it is first installed.

IP GEOCODING HELPER

A class method called geocode_ip_address has been mixed into the ActionController::Base. This enables before_action style lookup of the IP address. Since it is a filter, it can accept any of the available filter options.

Usage is as below:

class LocationAwareController < ActionController::Base
  geocode_ip_address
end

A first-time lookup will result in the GeoLoc class being stored in the session as :geo_location as well as in a cookie called :geo_session. Subsequent lookups will use the session value if it exists or the cookie value if it doesn't exist. The last resort is to make a call to the web service. Clients are free to manage the cookie as they wish.

The intent of this feature is to be able to provide a good guess as to a new visitor's location.

INTEGRATED FIND AND GEOCODING

Geocoding has been integrated with the finders enabling you to pass a physical address or an IP address. This would look the following:

Location.farthest(:origin => '217.15.10.9')
Location.farthest(:origin => 'Irving, TX')

where the IP or physical address would be geocoded to a location and then the resulting latitude and longitude coordinates would be used in the find. This is not expected to be common usage, but it can be done nevertheless.

ADDRESS GEOCODING

Geocoding is provided by the Geokit gem, which is required for this plugin. See the top of this file for instructions on installing the Geokit gem.

Geokit can geocode addresses using multiple geocodeing web services. Geokit supports services like Google, Yahoo, and Geocoder.us, and more -- see the Geokit gem API for a complete list.

These geocoder services are made available through the following classes: GoogleGeocoder, YahooGeocoder, UsGeocoder, CaGeocoder, and GeonamesGeocoder. Further, an additional geocoder class called MultiGeocoder incorporates an ordered failover sequence to increase the probability of successful geocoding.

All classes are called using the following signature:

include Geokit::Geocoders
location = XxxGeocoder.geocode(address)

where you replace Xxx Geocoder with the appropriate class. A GeoLoc instance is the result of the call. This class has a "success" attribute which will be true if a successful geocoding occurred. If successful, the lat and lng properties will be populated.

Geocoders are named with the convention NameGeocoder. This naming convention enables Geocoder to auto-detect its sub-classes in order to create methods called name_geocoder(address) so that all geocoders can be called through the base class. This is done purely for convenience; the individual geocoder classes are expected to be used independently.

The MultiGeocoder class requires the configuration of a provider order which dictates what order to use the various geocoders. Ordering is done through Geokit::Geocoders::provider_order, found in config/initializers/geokit_config.rb.

If you don't already have a geokit_config.rb file, the plugin creates one when it is first installed.

Make sure your failover configuration matches the usage characteristics of your application -- for example, if you routinely get bogus input to geocode, your code will be much slower if you have to failover among multiple geocoders before determining that the input was in fact bogus.

The Geocoder.geocode method returns a GeoLoc object. Basic usage:

loc=Geocoder.geocode('100 Spear St, San Francisco, CA')
if loc.success
  puts loc.lat
  puts loc.lng
  puts loc.full_address
end

REVERSE GEOCODING

Currently, only the Google Geocoder supports reverse geocoding. Pass the lat/lng as a string, array or LatLng instance:

res=Geokit::Geocoders::GoogleGeocoder.reverse_geocode "37.791821,-122.394679"
# => #<Geokit::GeoLoc:0x558ed0 ...
res.full_address
# => "101-115 Main St, San Francisco, CA 94105, USA"

The address will usually appear as a range, as it does in the above example.

INTEGRATED FIND WITH ADDRESS GEOCODING

Just has you can pass an IP address directly into an ActiveRecord finder as the origin, you can also pass a physical address as the origin:

Location.closest(:origin => '100 Spear st, San Francisco, CA')

where the physical address would be geocoded to a location and then the resulting latitude and longitude coordinates would be used in the find.

Note that if the address fails to geocode, the find method will raise an ActiveRecord::GeocodeError you must be prepared to catch. Alternatively, You can geocoder the address beforehand, and pass the resulting lat/lng into the finder if successful.

Auto Geocoding

If your geocoding needs are simple, you can tell your model to automatically geocode itself on create:

class Store < ActiveRecord::Base
  acts_as_mappable :auto_geocode=>true
end

It takes two optional params:

class Store < ActiveRecord::Base
  acts_as_mappable :auto_geocode=>{:field=>:address, :error_message=>'Could not geocode address'}
end

. . . which is equivalent to:

class Store << ActiveRecord::Base
  acts_as_mappable
  before_validation :geocode_address, :on => :create

  private
  def geocode_address
    geo=Geokit::Geocoders::MultiGeocoder.geocode (address)
    errors.add(:address, "Could not Geocode address") if !geo.success
    self.lat, self.lng = geo.lat,geo.lng if geo.success
  end
end

If you need any more complicated geocoding behavior for your model, you should roll your own before_validate callback.

Distances, headings, endpoints, and midpoints

distance = home.distance_from(work, :units=>:miles)
heading  = home.heading_to(work) # result is in degrees, 0 is north
endpoint = home.endpoint(90,2)  # two miles due east
midpoint = home.midpoint_to(work)

Cool stuff you can do with bounds

bounds = Bounds.new(sw_point,ne_point)
bounds.contains?(home)
puts bounds.center

HOW TO . . .

A few quick examples to get you started ....

How to install the Geokit Rails plugin

(See the very top of this file)

How to find all stores within a 10-mile radius of a given lat/lng

  1. ensure your stores table has lat and lng columns with numeric or float datatypes to store your latitude/longitude

  2. use acts_as_mappable on your store model:

class Store < ActiveRecord::Base
   acts_as_mappable
   ...
end
  1. finders now have extra capabilities:
Store.find(:all, :origin =>[32.951613,-96.958444], :within=>10)

How to geocode an address

  1. configure your geocoder key(s) in config/initializers/geokit_config.rb

  2. also in geokit_config.rb, make sure that Geokit::Geocoders::provider_order reflects the geocoder(s). If you only want to use one geocoder, there should be only one symbol in the array. For example:

Geokit::Geocoders::provider_order=[:google]
  1. Test it out in script/console
include Geokit::Geocoders
res = MultiGeocoder.geocode('100 Spear St, San Francisco, CA')
puts res.lat
puts res.lng
puts res.full_address
... etc. The return type is GeoLoc, see the API for
all the methods you can call on it.

How to find all stores within 10 miles of a given address

  1. as above, ensure your table has the lat/lng columns, and you've applied acts_as_mappable to the Store model.

  2. configure and test out your geocoder, as above

  3. pass the address in under the :origin key

Store.find(:all, :origin=>'100 Spear st, San Francisco, CA', :within=>10)
  1. you can also use a zipcode, or anything else that's geocodable:
Store.find(:all, :origin=>'94117', :conditions=>'distance<10')

How to sort a query by distance from an origin

You now have access to a 'distance' column, and you can use it as you would any other column. For example:

Store.find(:all, :origin=>'94117', :order=>'distance')

How to sort elements of an array according to distance from a common point

Usually, you can do your sorting in the database as part of your find call. If you need to sort things post-query, you can do so:

stores = Store.all
stores.sort_by{|s| s.distance_to(home)}

Obviously, each of the items in the array must have a latitude/longitude so they can be sorted by distance.

Database indexes

MySQL can't create indexes on a calculated field such as those Geokit uses to calculate distance based on latitude/longitude values for a record. However, indexing the lat and lng columns does improve Geokit distance calculation performance since the lat and lng columns are used in a straight comparison for distance calculation. Assuming a Page model that is incorporating the Geokit plugin the migration would be as follows.

class AddIndexToPageLatAndLng < ActiveRecord::Migration

  def self.up
    add_index  :pages, [:lat, :lng]
  end

  def self.down
    remove_index  :pages, [:lat, :lng]
  end
end

Database Compatability

  • Geokit works with MySQL (tested with version 5.0.41), PostgreSQL (tested with version 8.2.6) and Microsoft SQL Server (tested with 2000).
  • Geokit is known to not work with Postgres versions under 8.1 -- it uses the least() function.

HIGH-LEVEL NOTES ON WHAT'S WHERE

acts_as_mappable.rb, as you'd expect, contains the ActsAsMappable module which gets mixed into your models to provide the location-based finder goodness.

ip_geocode_lookup.rb contains the before_action helper method which enables auto lookup of the requesting IP address.

The Geokit gem provides the building blocks of distance-based operations:

The Mappable module, which provides basic distance calculation methods, i.e., calculating the distance between two points.

The LatLng class is a simple container for latitude and longitude, but it's made more powerful by mixing in the above-mentioned Mappable module -- therefore, you can calculate easily the distance between two LatLng objects with distance = first.distance_to(other)

GeoLoc represents an address or location which has been geocoded. You can get the city, zipcode, street address, etc. from a GeoLoc object. GeoLoc extends LatLng, so you also get lat/lng AND the Mappable module goodness for free.

geokit-rails's People

Contributors

albanpeignier avatar brandoncc avatar brennandunn avatar denisahearn avatar dreamcat4 avatar forgotpw1 avatar glennpow avatar gustin avatar jensb avatar jlecour avatar jonnyandrew avatar josevalim avatar mischa avatar mnoack avatar monde avatar mquy avatar nikosmichas avatar nneal avatar olleolleolle avatar pex avatar pklingem avatar pzgz avatar richessler avatar roccogalluzzo avatar ryankopf avatar sethpollack avatar sionide21 avatar thomdixon avatar wspruijt avatar zeke avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

geokit-rails's Issues

"geocode" method failing for authenticated geocoder.us call

When using authenticated requests to geocoder.us the requests fail with 401 not authorized. Replacing line 166 of geocoders.rb with the code below solves the problem.

res = Net::HTTP.start(uri.host, uri.port) {|http|
http.request(req)
}

Is this mis-configuration or a problem with the proxy code?

connection.adapter_name for Postgresql

There may be a bogg in Geokit::ActsAsMappable::SingletonMethods in adapter method:

klass = Adapters.const_get(connection.adapter_name.camelcase)
should be (I think)
klass = Adapters.const_get(connection.adapter_name)

Otherwise I receive an exception: uninitialized constant Geokit::Adapters::Postgresql

Error with Rails 4 and auto_geocode param : undefined method 'before_validation_on_create'

When I use the "auto_geocode" params of the acts_as_mappable method, I get the following error :

./.rvm/gems/ruby-1.9.3-p194/gems/activerecord-4.0.0/lib/active_record/dynamic_matchers.rb:22:in 'method_missing' : undefined method 'before_validation_on_create' for #<Class:0x007fcc7a24dc38> (NoMethodError)
from ./.rvm/gems/ruby-1.9.3-p194/gems/geokit-rails-1.1.4/lib/geokit-rails/acts_as_mappable.rb:91:in `acts_as_mappable'

Where is config file?

The README refers to file assets/api_keys_template for a default configuration file. This seems to be missing. I see no source for the configuration file anywhere in the source. I was expecting a rake task or some such to generate the default configuration file.

Find Query

Is it possible to find Location within a country.

For example if I give input as 36.03,-115.23(this reverse gecodes to Las Vegas , US)

is it possible to find Locations within United States or for that matter within Las Vegas

Calling .last on ActiveRecord_Relation with by_distance generates bad sql

Not sure if this is a bug in geokit-rails or ActiveRecord but I have a database query that uses by_nearby to sort locations by proximity. This works great if I append .first, .second etc to the query call.

It also gives expected behaviour if I use query[-1] to select the last record.

However, if I call query.last, it generates bad sql (presumably it tries to optimise by reversing the sort order, but this fails due to some complex math).

Here is the generated SQL:

>>> puts Friend.friends_closest_first(@user, @user.current_geolocation).to_sql
=> SELECT "friends".* FROM "friends" INNER JOIN "friendships" ON "friendships"."friend_id" = "friends"."id" INNER JOIN "geolocations" ON "geolocations"."id" = "friends"."geolocation_id" WHERE (friendships.user_id = 1)  ORDER BY
          (ACOS(least(1,COS(-0.5838126347921033)*COS(-1.2332496494591931)*COS(RADIANS(geolocations.latitude))*COS(RADIANS(geolocations.longitude))+
          COS(-0.5838126347921033)*SIN(-1.2332496494591931)*COS(RADIANS(geolocations.latitude))*SIN(RADIANS(geolocations.longitude))+
          SIN(-0.5838126347921033)*SIN(RADIANS(geolocations.latitude))))*3963.19)
          asc

This works fine:

>>>Friend.friends_closest_first(@user, @user.current_geolocation).first
=> first_record
>>>Friend.friends_closest_first(@user, @user.current_geolocation)[-1]
=> last_record

This fails:

>>>Friend.friends_closest_first(@user, @user.current_geolocation).last
=> ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR:  syntax error at or near "DESC"
LINE 1: ...(friendships.user_id = 1)  ORDER BY (ACOS(least(1 DESC, COS(...
                                                             ^
: SELECT  "friends".* FROM "friends" INNER JOIN "friendships" ON "friendships"."friend_id" = "friends"."id" INNER JOIN "geolocations" ON "geolocations"."id" = "friends"."geolocation_id" WHERE (friendships.user_id = 1)  ORDER BY (ACOS(least(1 DESC, COS(-0.5838126347921033)*COS(-1.2332496494591931)*COS(RADIANS(geolocations.latitude))*COS(RADIANS(geolocations.longitude))+
          COS(-0.5838126347921033)*SIN(-1.2332496494591931)*COS(RADIANS(geolocations.latitude))*SIN(RADIANS(geolocations.longitude))+
          SIN(-0.5838126347921033)*SIN(RADIANS(geolocations.latitude))))*3963.19)
          DESC LIMIT 1

Not a problem as I said, I can use the array[-1] selector as a workaround, but perhaps it could be possible to fix in geokit-rails so that last returns the correct value?

-S

Updated Rails 3 docs?

For the newbie rails developers, it would be nice if there were docs for installing on rails 3 (assuming it works there...).

Add this to Configuration/Gemfile.rb: gem 'geokit'
make sure you have bundler: http://github.com/schacon/bundler
run bundle install in the command line

rake test failure

I'm trying to execute test, but it's failing with this message:
uninitialized constant MockPerson::MockFamily

DB=postgresql rake test
Here is an extract of backtrace

/var/lib/gems/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:105:in const_missing': uninitialized constant MockPerson::MockFamily (NameError) from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2199:incompute_type'
from /var/lib/gems/1.8/gems/activesupport-2.3.5/lib/active_support/core_ext/kernel/reporting.rb:11:in silence_warnings' from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2195:incompute_type'
from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/reflection.rb:156:in send' from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/reflection.rb:156:inklass'
from ./lib/geokit-rails/acts_as_mappable.rb:122:in end_of_reflection_chain' from ./lib/geokit-rails/acts_as_mappable.rb:64:inacts_as_mappable'
from ./test/../test/models/mock_person.rb:3
...

Recommended way to work with model with multiple geocoded associations

Just curious how you guys would handle a scenario like this:

Say you have a Trip model and a Trip has an origin_location and a destination_location. Hypothetically something like:

class Trip < ActiveRecord::Base 
  belongs_to :origin_location, :class_name => 'Location'
  belongs_to :destination_location, :class_name => 'Location'
  acts_as_mappable :through => :origin_location
  acts_as_mappable :through => :destination_location
end

class Location < ActiveRecord::Base
  acts_as_mappable
end

And I'm kind of thinking I want to make calls like:

Trip.destination_location_within(50, [lat, lng])
and
Trip.origin_location_within(50, [lat, lng])

Do you think that makes sense, or should this instead be modeled differently? Any insight / advice on how to handle this situation?

Thanks!

WARNING: geokit-rails requires the Geokit gem

Even though I have the geokit gem installed, I keep getting this message:

WARNING: geokit-rails requires the Geokit gem. You either don't have the gem installed,
or you haven't told Rails to require it. If you're using a recent version of Rails:
config.gem "geokit" # in config/environment.rb
and of course install the gem: sudo gem install geokit

Is it possible to make geokit-rails not print it when the geokit gem is installed.

Records have invalid ids when using all(:within => distance, :origin => location) on a has_many :through association

The problem is that geokit-rails sets the find's :select option to "*", and the has_many :through association uses a join...causing it to include multiple id columns. I've got a fix here:

http://github.com/myronmarston/geokit-rails/commit/a1caf6dcde7406988798685de73b2b87370539c5

It'd be nice to get my fix pulled into the official repo. How do you like to receive patches? I sent a pull request a while ago and never got a response.

NoMethodError: undefined method `geo_scope' for #<Class... on Rails4

Hi guys, I'm trying to use this gem but I've a big issue.

my gemfile:
gem "geokit"
gem 'geokit-rails', '~> 2.0.0' # otherwise 1.14 (maybe)

the model:
class MyModel < ActiveRecord::Base
acts_as_mappable :units => :kms,
:distance_field_name => :distance,
:lat_column_name => :lat,
:lng_column_name => :lng
end

in console:
MyModel.geo_scope(within: location.range, origin: [location.lat, location.lng])

this is the output:
NoMethodError: undefined method geo_scope' for #<Class:0x007f9eaf632dd8> from .../.rvm/gems/ruby-2.0.0-p0/gems/activerecord-4.0.0/lib/active_record/dynamic_matchers.rb:22:inmethod_missing'

How can I fix it?

Thank you

Install issues

Hi,
Thanks for your work. i followed the instructions adding the geokit gem and then installing geokit-rails. however, had trouble with the following step:

  1. The configuration file: Geokit for Rails uses a configuration file in config/initializers. You must add your own keys for the various geocoding services if you want to use geocoding. If you need to refer to the original template again, see the assets/api_keys_template file.

**Didnt see the assets/api_keys)template file. Are you referring to /app/assets folder? I found an example of the api_keys_template online but what should the final file be called?

  1. The gem dependency: Geokit for Rails depends on the Geokit gem. Tell Rails about this dependency in config/environment.rb, within the initializer block: config.gem "geokit"

***I am a little new to Rails and tried to follow this instruction but when i copied "config.gem "geokit"" to config/environment.rb i got an error. Can you provide an example with the initializer blck code that would wrap your entry?

Is there any deeper problem indicated by the fact that the initializer file wasnt created?

Thanks,
Tyrone

nil latitude and logitude

I noticed an issue with nil lat/long values:

For ex:

a = Blah.new(:latitude => nil, :longitude => nil)

b = Blah.new(:latitude => x, :longitude => y)

Blah.find_closest(:origin => [x, y])

This will return a not b. Is this the proper intention? Is there a method in place to ignore nils? My solution was to use a named scope to prefilter for nils for the time being.

Searching within Polygons

Searching on the basis of a rectangle can be rather artificial; most of all, it can be contextually misleading. Have you investigated the possibility of defining multiple points and searching within that area?

Many avenues can be thought of:
โ€ข searching multiple vectors (n-1 points of the polygon);
โ€ข working one's way through northern-most & southern-most points, as well as eastern-most and western-most points, then middling points;
โ€ข defining a central point (not necessarily center) and outlying points to alleviate the filtering...

Rails3 syntax

I'm trying to migrate to Rails3 syntax. The following query works:

@users = User.find(
  :all,
  :joins => "INNER JOIN profiles AS profiles ON users.id=profiles.user_id",
  :conditions => ["users.disabled = ? AND users.hidden = ?", false, false],
  :include => :profile,
  :origin => ll,
  :units => :miles, 
  :order => order_by
  ).paginate(:per_page => @per_page, :page => params[:page])

If I convert this query to Rails3 syntax using scopes, the query resembles:
@users = User.valid.joins(:profile).includes(:profile).order(order_by).paginate(:per_page => @per_page, :page => params[:page])

Question: how do I reference the :units and :origin in this syntax? I've tried a variation of the Rails3 syntax to use scopes and the .find method:

@users = User.valid.joins(:profile).includes(:profile).find(
  :all,
  :origin => ll,
  :units => :miles, 
  :order => order_by
  ).paginate(:per_page => @per_page, :page => params[:page])

but I get an error that reads "Unknown key(s): origin, units".

Is the plugin Rails3 compatible?

Reverse Geocode example is not working

I have tried the following example given in reverse geocode section,

res=Geokit::Geocoders::GoogleGeocoder.reverse_geocode "37.791821,-122.394679"
=> #<Geokit::GeoLoc:0x558ed0 ...
res.full_address
"101-115 Main St, San Francisco, CA 94105, USA"

But I am getting the following result,

Provider: 
Street: 
City: 
State: 
Zip: 
Latitude: 
Longitude: 
Country: 
Success: false

What could be wrong?

acts_as_mappable not getting correct database adapter_name

When I try and use the acts_as_mappable integration, I always get the error message 'jdbc' is not a supported adapter. Does this mean that this plugin will not work with jruby?

It looks like connection.adapter_name in acts_as_mappable fails if the name is 'jdbc' but hardcoding mySQL seems to work... I'm not sure if that is the correct solution. Any advice would be appreciated.

Cannot load geoip

I'm having this error after "bundle update":

/Users/../.rvm/gems/ruby-2.0.0-p247@master/gems/activesupport-4.0.1/lib/active_support/dependencies.rb:229:in `require': cannot load such file -- geoip (LoadError)

Before the bundle update it was working perfectly.

In the while, if I exclude geokit-rails from my gemfile, everything works fine....

geokit-rails plugin doesn't play nice with geokit 1.5.0

After I upgraded the geokit gem from version 1.4.1 to version 1.5.0, I get the following error when starting my Rails project:

.../config/initializers/geokit_config.rb:10:NoMethodError: undefined method `timeout=' for Geokit::Geocoders:Module

I'm using Rails 2.3.4 with commit 93a264b of the geokit-rails plugin.

session[:geo_location] returns nil

I added in the top of the controller:

geocode_ip_address

And then:

logger.info "======= GEOLOCATION ==========="
logger.info session[:geo_location]

It returns: nil in the logs

Missing gem warning

I have geokit-rails installed, along with the geokit gem as outlined in the README. However, when I run rake gems:install I get the following error:

WARNING: geokit-rails requires the Geokit gem.

I've also tried adding require 'geokit' to the top of my config/initializers/geokit_config.rb file, but to no avail. I can actually stop the warning if I add that line to the top of the plugin's init.rb file, but I'm not keen on poking into the plugin code to make it behave. Could you please address these warnings? Thank you!

Missing 'distance' field on returned instances

As documented:

The plug-in creates a calculated distance field on AR instances that have been retrieved throw a Geokit location query. By default, these fields are known as "distance" but this can be changed through the :distance_field_name key.

"distance" field on returned objects is not available.

NoMethodError: undefined method merge_conditions

Ruby -> ruby 1.9.2dev (2010-04-04 trunk 27215) [i686-linux]
Rails -> Rails 3.0.0.beta2

Statement producing the error:
Location.find(:all, :origin =>[37.792,-122.393], :within=>10)

Stack trace:
NoMethodError: undefined method merge_conditions' for #<Class:0x9692d24> from /usr/local/lib/ruby/gems/1.9.1/gems/activerecord-3.0.0.beta2/lib/active_record/base.rb:1154:inmethod_missing'
from /home/ahmad/geok/vendor/plugins/geokit-rails/lib/geokit-rails/acts_as_mappable.rb:349:in apply_bounds_conditions' from /home/ahmad/geok/vendor/plugins/geokit-rails/lib/geokit-rails/acts_as_mappable.rb:260:inprepare_for_find_or_count'
from /home/ahmad/geok/vendor/plugins/geokit-rails/lib/geokit-rails/acts_as_mappable.rb:152:in find' from (irb):1 from /usr/local/lib/ruby/gems/1.9.1/gems/railties-3.0.0.beta2/lib/rails/commands/console.rb:47:instart'
from /usr/local/lib/ruby/gems/1.9.1/gems/railties-3.0.0.beta2/lib/rails/commands/console.rb:8:in start' from /usr/local/lib/ruby/gems/1.9.1/gems/railties-3.0.0.beta2/lib/rails/commands.rb:34:in<top (required)>'
from ./script/rails:9:in require' from ./script/rails:9:in

'

gem

Is there a chance to build a gem? It would be handy

'distance' field was gone away

I'm using geokit-rails 2.0.1. When I run:

Location.within(5, origin: [37.792,-122.393]).order("distance asc")

An error occurred:

PG::UndefinedColumn: ERROR:  column "distance" does not exist

Why does the field distance go away?

Doc says "attribute on object" but method creates attribute on class

In acts_as_mappable.rb the Array#sort_by_distance_from method is documented like this:

This method creates a "distance" attribute on each object, calculates the

distance from the passed origin, and finally sorts the array by the

resulting distance.

In the implementation, a "distance" attribute is created on the class or classes of all objects in the array, which means that henceforth, all objects of that class will #respond_to?:distance. Not what I expected, and quite confusing since it never shows up in development mode (when the classes are reloaded between requests).

If this is the intended behaviour, it would be great if it was correctly documented. If it is not the intended behaviour, using singleton methods could be a solution.

before_validation_on_create removed in Rails 3

Rails 3 is giving me a "no method found" error when I enable auto_geocode.
The validation should be called like so:
before_validation :auto_geocode_address, :on => :create
because before_validation_on_create has been deprecated.
Although when I change the callback to that code WEBrick crashes so there's more to the bug.

Running Rails 3 Beta 2 with ruby 1.9.2

Mysql::Error: Unknown column 'distance'

I'm using Rails 3.2.15, ruby 1.8.7 with geokit-rails 2.0. I get the following error message on a by_distance query:

Mysql::Error: Unknown column 'distance' in 'order clause': SELECT tests.* FROM tests WHERE (course_type = 1 OR course_type = 2) AND ((sharemode = 1 OR sharemode = 3) AND event_date >= '2011-01-01 00:00:00' AND event_date <= '2014-01-01 00:00:00') ORDER BY (ACOS(least(1,COS(0.0)_COS(0.0)_COS(RADIANS(tests.latitude))_COS(RADIANS(tests.longitude))+ COS(0.0)_SIN(0.0)_COS(RADIANS(tests.latitude))_SIN(RADIANS(tests.longitude))+
SIN(0.0)_SIN(RADIANS(tests.latitude))))_3963.19) asc, country, distance ASC

Query:

@test_results = Test.app_mode(request.domain).by_distance(:origin => [0,0]).find(:all, :conditions => ["(sharemode = ? OR sharemode = ?) AND event_date >= ? AND event_date <= ?", 1, 3, @search_window_past, @search_window_future], :order => 'country, distance ASC')

Thanks,

Nick,

Geokit::ActsAsMappable::UnsupportedAdapter: `postgresql` is not a supported adapter.

Brief backtrace:

/gems/geokit-rails-2.0.1/lib/geokit-rails/acts_as_mappable.rb:101 in "rescue in adapter"
/gems/geokit-rails-2.0.1/lib/geokit-rails/acts_as_mappable.rb:95 in "adapter"
/gems/geokit-rails-2.0.1/lib/geokit-rails/acts_as_mappable.rb:322 in "sphere_distance_sql"
/gems/geokit-rails-2.0.1/lib/geokit-rails/acts_as_mappable.rb:193 in "distance_sql"

I'm not sure why this error happens, but sometimes connection.adapter_name returns postgresql instead of PostgreSQL, and so it causes connection.adapter_name.camelcase to return Postgresql. I will inspect more to find the root cause.

For now, we can monkey patch this problem by this:

# ./config/initializers/geokit_rails_patch.rb
require 'geokit-rails/adapters/postgresql'
module Geokit::Adapters
  class Postgresql < PostgreSQL
  end
end

Or dirtier ;)

# ./config/initializers/geokit_rails_patch.rb
require 'geokit-rails/adapters/postgresql'
Geokit::Adapters::Postgresql = Geokit::Adapters::PostgreSQL

Geokit is called twice for a query

Hi guys,

I am calling a simple query:

`@cars = Car.includes(:services).within(distance, origin: city).paginate(:page => params[:page], :per_page => 100)`

And when I take a look to the log, I see there following:

Geokit is using the domain: localhost
Multi geocoding. address: New York, NY, United States, args []
Google geocoding. address: New York, NY, United States, args []
Google geocoding. Result: %7B%0A.....
Geokit is using the domain: localhost
Multi geocoding. address: New York, NY, United States, args []
Google geocoding. address: New York, NY, United States, args []
Google geocoding. Result: %7B%0A.....

Why is this output twice for every call I make? I checked the code there shouldn't be any duplicity.

In Gemfile: gem 'geokit-rails'

Thanks,
Radek

No initializer created, defaults to miles despite specifying it in acts_as_mappable

I'm specifying kms as my unit as per the instructions, as below:

class Airport < ActiveRecord::Base
  attr_accessible :IATA_code, :ICAO_code, :name, :latitude, :longitude,
                  :city, :altitude_feet, :timezone_offset, :daylight_savings_time,
                  :openflight_airport_id

    acts_as_mappable :default_units => :kms,
                  :units => :kms,
                  :default_formula => :sphere,
                  :distance_field_name => :distance,
                  :lat_column_name => :latitude,
                  :lng_column_name => :longitude


end

But every distance_to calculation that I make on airport instances returns the distance in miles. There is no initialiser file, as installing the gem didn't put one in the right place.

ActiveRecord errors with Rails 4 and Postgres

I'm running Ruby 2.0.0p195, Rails 4.0.0, geokit 1.6.5, geokit-rails 2.0.0 (master), Postgres 9.2.4, pg gem 0.15.1.

I have a zip_codes table which has :latitude and :longitude columns both of PostgresQL type "float".

class ZipCode < ActiveRecord::Base
    acts_as_mappable :lat_column_name => :latitude, :lng_column_name => :longitude

and

class Item < ActiveRecord::Base
    belongs_to :zip_code
    acts_as_mappable :through => :zip_code

Without the acts_as_mappable, all the associations work fine. With them, the associations blow up. It seems to me that the Postgres ordered arguments ($1, $2, etc) are involved somehow. I get errors like this:

Item.all.map(&:zip_code)
#<ZipCode:0x007fb820efaaf8>
ActiveRecord::StatementInvalid: PG::Error: ERROR:  could not determine data type of parameter $1
: SELECT  "zip_codes".* FROM "zip_codes"  WHERE "zip_codes"."id" = $7  ORDER BY "zip_codes"."id" ASC LIMIT 1

it's as if the numbered arguments haven't been "reset" to $1, or something? I'm not entirely sure. Anyone else had this issue, or anything like it?

Error: syntax error near DESC

Been trying to fix some test errors on the Rails4 branch. Just compiling some info here.

ActsAsMappableTest#test_scoped_distance_column_in_select
ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR:  syntax error at or near "DESC"
LINE 1: ...ations"."company_id" = $1  ORDER BY (ACOS(least(1 DESC, COS(...

also occurs in:

ActsAsMappableTest#test_distance_column_in_select
ActsAsMappableTest#test_ip_geocoded_distance_column_in_select

Occuring on Postgres and MySQL

It appears that the problem is introduced when ActiveRecord splits strings with a comma to inject ASC/DESC here: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/query_methods.rb#L994

I'm not sure if it's truly an ActiveRecord error or if we are passing in a malformed order string.

The order passed in is:

"\n          (ACOS(least(1,COS(0.5745378329739577)*COS(-1.692244085410541)*COS(RADIANS(locations.lat))*COS(RADIANS(locations.lng))+\n          COS(0.5745378329739577)*SIN(-1.692244085410541)*COS(RADIANS(locations.lat))*SIN(RADIANS(locations.lng))+\n          SIN(0.5745378329739577)*SIN(RADIANS(locations.lat))))*3963.19)\n          asc"

@mnoack can I assume that all the tests on Rails4 branch are up to date?

Rails 3: ArgumentError (Unknown key(s): within, origin) / fix

I just started migrating my app to Rails 3 and for my old find syntax,
Court.all(:conditions => ["auctions_count > 0 AND id <> ?", @court.id], :origin => [@court.lat, @court.lng], :within => 150.0, :order => "auctions_count DESC", :limit => 5)
I get the following error, and provide an easy (and nasty) fix for it below.

ArgumentError (Unknown key(s): within, origin):
  activesupport (3.0.1) lib/active_support/core_ext/hash/keys.rb:43:in `assert_valid_keys'
  activerecord (3.0.1) lib/active_record/relation/spawn_methods.rb:89:in `apply_finder_options'
  activerecord (3.0.1) lib/active_record/relation/finder_methods.rb:143:in `all'
  activerecord (3.0.1) lib/active_record/base.rb:439:in `__send__'
  activerecord (3.0.1) lib/active_record/base.rb:439:in `all'
  (my controller)

It works when I monkey patch the following with an initializer.
module ActiveRecord
module SpawnMethods
VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, :extend,
:order, :select, :readonly, :group, :having, :from, :lock, :origin, :within ]
end
end

This works for me with

ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9]

rails 3.0.1

geokit gem 1.5.0

geokit_rails plugin 1.?.? (says 1.1.4 in about.yml and 1.2.0 in CHANGELOG.rdoc, checked out November 21, 2010)

order by distance doesn't seem to work

I've tryed to order my request by distance but I get an exception:

ActiveRecord::StatementInvalid in PagesController#index 
PGError: ERROR:  syntax error at or near "AS" 
LINE 1: ...S(users.latitude))*COS(RADIANS(users.longitude))+ AS 
alias_2... 
                                                             ^ 
: SELECT * FROM ( SELECT     DISTINCT ON ("users".id) "users".id, 
users.updated_at AS alias_0, users. AS alias_1, 
COS(0.852012144638921)*COS(0.0415964332145236)*COS(RADIANS(users.latitude)) *COS(RADIANS(users.longitude)) 
+ AS alias_2 FROM       "users" LEFT OUTER JOIN "pictures" ON 
"pictures"."user_id" = "users"."id" WHERE 
(((pictures.picture_file_name IS NOT NULL) AND 
(users.latitude>47.9181925059163 AND users.latitude<49.7152074574837 
AND users.longitude>1.01890820654452 AND 
users.longitude<3.74769192543548)) AND ( 
(ACOS(least(1,COS(0.852012144638921)*COS(0.0415964332145236)*COS(RADIANS(us ers.latitude))*COS(RADIANS(users.longitude)) 
+ 
COS(0.852012144638921)*SIN(0.0415964332145236)*COS(RADIANS(users.latitude)) *SIN(RADIANS(users.longitude)) 
+ 
SIN(0.852012144638921)*SIN(RADIANS(users.latitude))))*6376.77271) 
          <= 100))) AS id_list ORDER BY id_list.alias_0 DESC, 
id_list.alias_1 , id_list.alias_2  LIMIT 15

I've well added this lines to my User model:
acts_as_mappable :distance_field_name => :distance,
:lat_column_name => :latitude,
:lng_column_name => :longitude

And this is my AR query:

@users_carousel = User.find(:all, 
                            :order => 'users.updated_at desc, users.distance', 
                            :limit => User::MAX_USERS_IN_CAROUSEL,
                            :origin => visitor_ip, 
                            :include => :pictures,
                            :conditions => 'pictures.picture_file_name IS NOT NULL')

I'me using rails3, with pgsql.

Thank you very much for you work!!! :)

FInd suggested bounds of a country by reverse geocoding

res = Geokit::Geocoders::GoogleGeocoder.reverse_geocode(params[:coordinate])
addresses = res.all
country = addresses.last

Above code correctly displays country as

{
"state_fips": null,
"is_us?": true,
"provider": "google",
"state": null,
"block_fips": null,
"ll": "37.09024,-95.712891",
"province": null,
"street_address": null,
"full_address": "United States",
"zip": null,
"precision": "country",
"district": null,
"country_code": "US",
"lat": 37.09024,
"city": null,
"lng": -95.712891,
"success": true,
"district_fips": null
}

How ever country.suggested_bounds is incorrectly found as
{"ne":{"lng":-115.229126,"lat":36.03131},"sw":{"lng":-115.231824,"lat":36.028612}}

Select columns does not work ( :select=>'field1,field2')!

Example:

Model 1

class Point < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true
acts_as_mappable
end

Model 2

class Product < ActiveRecord::Base
has_many :points, :as => :imageable
acts_as_mappable :through => :points
end

Controller

class ProductsController < ApplicationController
def near
@products = Product.all(:include => :points, :select => "title", :origin => origin, :within => distance)
respond_to do |format|
format.xml { render :xml => @products.to_xml(:include => :points) }
end
end
end

Using :select => "title" does not work - the rule is ignored (all fields are selected not just title).

AAM::add_distance_to_select: uses "*" for SQL SELECT clause, better would be "table_name.*"

diff --git a/vendor/plugins/geokit-rails/lib/geokit-rails/acts_as_mappable.rb b/vendor/plugins/geokit-rails/lib/g
index b64af74..339ccfa 100644
--- a/vendor/plugins/geokit-rails/lib/geokit-rails/acts_as_mappable.rb
+++ b/vendor/plugins/geokit-rails/lib/geokit-rails/acts_as_mappable.rb
@@ -388,7 +388,7 @@ module Geokit
         def add_distance_to_select(options, origin, units=default_units, formula=default_formula)
           if origin
             distance_selector = distance_sql(origin, units, formula) + " AS #{distance_column_name}"
-            selector = options.has_key?(:select) && options[:select] ? options[:select] : "*"
+            selector = options.has_key?(:select) && options[:select] ? options[:select] : "#{self.table_name}.*"
             options[:select] = "#{selector}, #{distance_selector}"  
           end
         end

.order('distance DESC') fails with ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'distance' in 'order clause'

Barn.within(5, :origin => "30093").order('distance DESC')

This produces:

Barn Load (0.3ms)  SELECT `contacts`.* FROM `contacts` WHERE `contacts`.`type` IN ('Barn') AND ((
 (ACOS(least(1,COS(0.591845095540716)*COS(-1.4692258158522673)*COS(RADIANS(contacts.lat))*COS(RADIANS(contacts.lng))+
 COS(0.591845095540716)*SIN(-1.4692258158522673)*COS(RADIANS(contacts.lat))*SIN(RADIANS(contacts.lng))+
 SIN(0.591845095540716)*SIN(RADIANS(contacts.lat))))*3963.19)
 <= 5)) ORDER BY distance DESC
ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'distance' in 'order clause': SELECT `contacts`.* FROM `contacts`  WHERE `contacts`.`type` IN ('Barn') AND ((
          (ACOS(least(1,COS(0.591845095540716)*COS(-1.4692258158522673)*COS(RADIANS(contacts.lat))*COS(RADIANS(contacts.lng))+
          COS(0.591845095540716)*SIN(-1.4692258158522673)*COS(RADIANS(contacts.lat))*SIN(RADIANS(contacts.lng))+
          SIN(0.591845095540716)*SIN(RADIANS(contacts.lat))))*3963.19)
          <= 5)) ORDER BY distance DESC
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/connection_adapters/mysql_adapter.rb:308:in `query'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/connection_adapters/mysql_adapter.rb:308:in `block in exec_without_stmt'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/connection_adapters/abstract_adapter.rb:280:in `block in log'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activesupport-3.2.14/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/connection_adapters/abstract_adapter.rb:275:in `log'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/connection_adapters/mysql_adapter.rb:307:in `exec_without_stmt'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/connection_adapters/mysql_adapter.rb:290:in `exec_query'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/connection_adapters/mysql_adapter.rb:430:in `select'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/connection_adapters/abstract/database_statements.rb:18:in `select_all'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/connection_adapters/abstract/query_cache.rb:63:in `select_all'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/querying.rb:38:in `block in find_by_sql'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/explain.rb:41:in `logging_query_plan'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/querying.rb:37:in `find_by_sql'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/relation.rb:171:in `exec_queries'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/relation.rb:160:in `block in to_a'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/explain.rb:41:in `logging_query_plan'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/relation.rb:159:in `to_a'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/relation.rb:498:in `inspect'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/railties-3.2.14/lib/rails/commands/console.rb:47:in `start'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/railties-3.2.14/lib/rails/commands/console.rb:8:in `start'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/railties-3.2.14/lib/rails/commands.rb:41:in `<top (required)>'
        from script/rails:6:in `require'
        from script/rails:6:in `<main>'

Doing the order directly in the search works, but the SQL doesn't appear to do anything with distance:

Barn.within(5, :origin => "30093", :order => 'distance desc')
Barn Load (4.3ms)  SELECT `contacts`.* FROM `contacts` WHERE `contacts`.`type` IN ('Barn') AND ((
 (ACOS(least(1,COS(0.591845095540716)*COS(-1.4692258158522673)*COS(RADIANS(contacts.lat))*COS(RADIANS(contacts.lng))+
 COS(0.591845095540716)*SIN(-1.4692258158522673)*COS(RADIANS(contacts.lat))*SIN(RADIANS(contacts.lng))+
 SIN(0.591845095540716)*SIN(RADIANS(contacts.lat))))*3963.19)
 <= 5))

And then the distance column isn't available:

barns[0].distance
NoMethodError: undefined method `distance' for #<Barn:0xb5a525c>
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activemodel-3.2.14/lib/active_model/attribute_methods.rb:407:in `method_missing'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/activerecord-3.2.14/lib/active_record/attribute_methods.rb:149:in `method_missing'
        from (irb):9
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/railties-3.2.14/lib/rails/commands/console.rb:47:in `start'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/railties-3.2.14/lib/rails/commands/console.rb:8:in `start'
        from /home/riding/.rvm/gems/ruby-1.9.3-p362@rr-3xmid/gems/railties-3.2.14/lib/rails/commands.rb:41:in `<top (required)>'
        from script/rails:6:in `require'
        from script/rails:6:in `<main>'

Here's relevant bits from the model:

acts_as_mappable :auto_geocode => true, :distance_field_name => :distance

I tried adding attr_accessor :distance and attr_accessible :distance to no avail.

Here's my Gemfile:

source 'https://rubygems.org'

gem 'rails', '3.2.14'

gem 'mysql'

# Gems used only for assets and not required
# in production environments by default.
group :assets do
  gem 'sass-rails',   '~> 3.2.3'
  gem 'coffee-rails', '~> 3.2.1'
  # See https://github.com/sstephenson/execjs#readme for more supported runtimes
  gem 'therubyracer', :platforms => :ruby
  gem 'uglifier', '>= 1.0.3'
end

gem 'jquery-rails', '3.0.4'
gem 'twitter', '4.8.1'
gem 'haml', '2.2.6'
gem 'formtastic', '2.2.1'
gem 'json', '1.8.0'
gem 'tld', '0.6.4'
gem 'addressable', '2.1.0'
gem 'stringex', '2.1.0'
gem 'will_paginate', '~> 3.0.0'
gem 'geokit-rails', '2.0.1'
gem 'awesome_print', '1.1.0'
gem 'paperclip', '3.5.2'
gem 'gruff', '0.3.6'
gem 'rmagick', '2.11.0'

group :development, :test do
  gem 'rspec-rails', '~> 2.0'
  gem 'shoulda-matchers', '2.4.0'
  gem "capybara", '2.1.0'
  gem "webrick", '~> 1.3.1'
  gem "pry", '0.9.12.3'
end

group :test do
  gem 'database_cleaner', '1.1.1'
end

I would be happy to test additional things, but I'm not sure where to start digging.

Added support for :through associations in XXX.sort_by_distance_from

I needed to have results include calculated distances, so per your docs I tried to use

bounds=Bounds.from_point_and_radius(home,5)
stores=Store.find :all, :include=>[:reviews,:cities] :bounds=>bounds
stores.sort_by_distance_from(home)

Where Store has act_as_mappable :through => :location

Unfortunately sort_by distance_from doesnt work with :through associations, but you can fix it with the following
In acts_as_mappable.rb in sort_by_distance_from function replace the following line:

e.send("#{distance_attribute_name}=", e.distance_to(origin,opts))

with the following 2 lines:

responder = e.through.blank? ? e : e.send(e.through)
e.send("#{distance_attribute_name}=", responder.distance_to(origin,opts))

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.