opensoul.org

Geocoding as easy as 1-2...

3?, nope there is no 3. Geocoding as easy as 1-2!

  1. Create your models
  2. Find them!
event = Event.create :street => "777 NE Martin Luther King, Jr. Blvd.",
  :city => "Portland", :region => "Oregon", :postal_code => 97232

# how far am I from RailsConf 2007?
event.distance_to "49423" #=> 1807.66560483205

# Find our new event, and any other ones in the area
Event.find(:all, :within => 50, :origin => "97232")

# Find the nearest restaurant with beer
Restaurant.find(:nearest, :origin => event, :conditions => 'beer = true')

I know you’re excited, so instead of blabbing on-and-on, FAQ-style:

How are you performing this voodoo?

A slick new plugin called acts_as_geocodable.

How do I get it?

script/plugin install -x git://github.com/collectiveidea/acts_as_geocodable.git

How do I set it up?

Its all in the README.

Why did you write this when there’s already several geocoding plugins?

The Ruby geocoding space is pretty fragmented right now. There’s all kinds of geocoders available, and none of them provide everything. We’re determined to fix that with Graticule and acts_as_geocodable.

We believe that all the heavy lifting of geocoding, distance calculations, etc., should be left to a gem, and only code that is directly related to Rails should be a Rails plugin. Even more, all the different geocoders should be available in the same package and have the same interface. A plugin should then be built on top of the gem that makes your apps geo-capabilities seem like voodoo.

We started our own projects because we didn’t think anyone else got it right. Recently, Bill Eisenhaur and Andre Lewis did a pretty good job, and we borrowed some of their ideas, but I still have issues with their approach and code, mainly that it’s all tied up into a Rails plugin.

Did you get it right?

I think we have a great foundation. It’s not perfect, nor does it have all the features of the other packages, but I think it is well architected.

Why don’t you work together?

Excellent idea! We would love to.

How does this fit in with JWZ’s use case?

(Why are you asking this?) A third of the problem is knowing where, a third is when, and a third is who. You’re on your own for the who and when.

Happy geocoding!

acts_as_geocodable, collectiveidea, geocoding, graticule, plugin, rails, and ruby February 13, 2007

16 Comments

  1. jim jim February 13, 2007

    Great work. This is what I wanted to do but was short on time when I wrote acts_as_geocode. You guys did a much more thorough job. I was just doing something quick to make my life easier on some real estate projects.

    Now I will switch soon.

  2. Shawn Shawn February 13, 2007

    Hi,

    Thanks for the plugin! :)

    I’ve installed Graticule and acts_as_geocodable but I can’t seem to get it to run reliably.

    I’m on Windows XP, using a Yahoo Application id.

    It seems setting Geocode.geocoder in the environment.rb isn’t “sticking”. It set when the app is loaded but is nil when Geocode.rb is hit as part of the save process.

    I’m working around this by using:

    def self.geocode(location)
    if geocoder.nil?
    self.geocoder = Graticule.service(:yahoo).new ‘Dd0Ib1zV34G4ypZoQ_DAedMH5uU7IejCvf5Mzw8llr2YkKFZzkymiyumE_OJ’
    end

    end


    Thanks!


    Shawn

  3. Brandon Brandon February 14, 2007

    Shawn,

    That’s really odd. Looking through the code, I have no clue what would be causing that.

    I don’t have immediate access to a Windows environment, so could you (or anyone else) help me debug it?

  4. Shawn Shawn February 15, 2007

    Hi Brandon,

    Sure – I’m kinda, sorta new to ROR but will try some things to see if cattr’s disappear on some other classes.

    Here’s another item relating to Yahoo geocode requests…

    In acts_as_geocoable.rb, the full_address method has a “\n” after the street. This was causing Yahoo to return ZIP level precision vs STREET level. Google didn’t seem to mind.

    Orignal:

    def full_address
    returning("") { |address|
    address << “#{geo_attribute(:street)}\n” unless geo_attribute(:street).blank?
    address << "#{geo_attribute(:city)}, " unless geo_attribute(:city).blank?
    address << "#{geo_attribute(:region)} " unless geo_attribute(:region).blank?
    address << “#{geo_attribute(:postal_code)}” unless geo_attribute(:postal_code).blank?
    address << " #{geo_attribute(:country)}" unless geo_attribute(:country).blank?
    }.strip
    end

    Changed to:

    1. Return the entire address in one string.
      def full_address
      returning("") { |address|
      address << “#{geo_attribute(:street)},” unless geo_attribute(:street).blank?
      address << "#{geo_attribute(:city)}, " unless geo_attribute(:city).blank?
      address << "#{geo_attribute(:region)} " unless geo_attribute(:region).blank?
      address << “#{geo_attribute(:postal_code)}” unless geo_attribute(:postal_code).blank?
      address << " #{geo_attribute(:country)}" unless geo_attribute(:country).blank?
      }.strip
      end

    Basically I replaced the “\n” with a “,”. Yahoo now returns a STREET level precision on valid address. Google seems okay to.

    Hope this helps!

    Shawn

  5. Shawn Shawn February 15, 2007

    Hi Brandon,

    Tested out the geocoder.nil? issue on Win XP. It seems to only occur when the server is started in development mode. Production mode, the object seems to stay created.

    I tested using Mongrel and WEBrick with same results.

    Hope this helps!

    Shawn

  6. James Stewart James Stewart February 17, 2007

    Any thoughts of adding :through support for acts_as_geocodable? For example, say I have events which belong_to locations, it would be good to be able to search for forthcoming events based on their location.

    I like the way acts_as_locateable implements that, but this plugin overall looks more appropriate for my needs.

  7. PETER PETER March 7, 2007

    Your approach of separating concerns is great.

    It seems to me that being able to order results by distance as well as selecting by :within is important. Do you have plans to do this? Suggestions on how I might do it?

    Thanks again

  8. Brandon Brandon March 14, 2007

    Peter,

    Sorry for the late response, I fell off the face of the earth for a week. You should be able to order the results by passing :order =&gt; 'distance' to the find. Let me know if that doesn’t work.

  9. John John March 17, 2008

    Something weird is going on… I can’t start the mongrel after installing the acts_as_geocodable plugin, it says that Graticules 0.2.0 is required and I have the 0.2.6 installed.. what am I doing wrong??

  10. John Small John Small May 21, 2008

    I’ve installed the latest acts_as_geocodable and the latest Graticule, run the migrations, added the line into environment.rb (in development mode) and It Doesn’t Work™.

    To start checking it works with my models I’ve uncommented the task in acts_as_geocodeable_tasks.rake, purely as a simple way to load my environment and test a model can become geocodeable. But when the environment loads I get “Unititialized constant Geocode”. Now I’m a new Rails user so there’s obviously some Rails idiom which is assumed in the instruction “just add this to your environment.rb”, but I don’t know what it is. Please clue me in, there must be something more I need to add to the environment.rb to get it to work.

  11. Brandon Brandon May 28, 2008

    John,

    Did you restart the server? Any time you install a plugin, you need to restart script/server.

  12. skoppy skoppy June 26, 2008

    I’m running into the same problem that John Small is. Whenever I try to restart the server I get “Unititialized constant Geocode"

    I’ve also tried adding “require ‘graticule’” to the environment.rb file as well with no luck.

    Any help will be greatly appreciated. Thanks.

  13. Daniel Daniel July 22, 2008

    I have the same problem reported about geocoder.nil:

    NoMethodError: You have a nil object when you didn’t expect it!
    The error occurred while evaluating nil.latitude
    from (irb):6

    However, this is on OSX with the latest Rails, Gratitude and ActsAsGeocoder.

    However, I don’t think the issue is that Geocode.geocoder is not sticking because when on the same console session I type

    Geocode.geocoder

    I does return:

    => #<Graticule::Geocoder::Google:0×22998dc @url=#<URI::HTTP:0×114cb10 URL:http://maps.google.com/maps/geo>, @key=“my-key”

    So, it seems the problem is not related to the “stickiness” in environment.rb (neither development nor production). The problem seems to be somewhere in the AR methods added to the model not being able to access Geocode.geocoder.

    Any clues anyone?

  14. Daniel Daniel July 22, 2008

    I think I may know where the problem is.

    I was looking at acts_as_geocodable.rb and found:

    def to_location
    returning Graticule::Location.new do |location|
    [:street, :locality, :region, :postal_code, :country].each do |attr|
    location.send “#{attr}=”, geo_attribute(attr)
    end
    end
    end

    Which has the attributes hard coded in the array. Therefore, if your Model doesn’t have these exact attributes, then attach_geocode will fail and return nil. As a test, I just created instance methods in my model to return the values of my attributes based on the names in the array and now it works just fine.


    It seems to be that this was somewhat documented in the README file and I must have missed it.


    The problem I have is that my models looks something like this:


    class Address < ActiveRecord::Base
    belongs_to :postal_code
    end


    class PostalCode < ActiveRecord::Base
    belongs_to :state
    end


    class State < ActiveRecord::Base
    belongs_to :country
    end


    So, if I want to get the zip, city, and state of an address, I need:


    address.postal_code.zip_code
    address.postal_code.state.iso2
    address.postal_code.state.country.iso2


    respectively.


    If I were to use the recommendations in the README, how can I do this:


    class Address < ActiveRecord::Base
    belongs_to :postal_code
    acts_as_geocodable :address => {:street => :street1, :locality => :postal_code.city, :region => :postal_code.state.iso2, :postal_code => :postal_code.zip_code }
    end


    That doesn’t work for me unless instead I just define corresponding instance methods.


    Any ideas?


    Thanks

  15. Chad Chad November 4, 2008

    The folks with the issue of Geocode.geocoder in the environment file:

    You are likely placing the call inside of the Rails::Initializer.run loop
    Place it AFTER.

  16. Jimmy Jimmy May 3, 2010

    In addition to Chad’s comments (WRT the issue of Geocode.geocoder in the environment file) I’d like to add that if you have installed acts_as_geocodable as a gem, that you need to put the following inside your Rails::Initializer block (inside environment.rb):

    config.gem ‘graticule’
    config.gem ‘acts_as_geocodable’

Post a Comment

Comments use textile. Anonymous comments will be deleted.

My name is Brandon Keepers. I like to build things, usually in Ruby or JavaScript. I work at GitHub and live in Holland, MI.

Popular Posts