Geocoding addresses with geocoder gem and PostGIS database with RGeo

activerecord, callback, geocoding, postgis, ruby-on-rails

Solution

It was a bloody typo...

It works. I leave it up, maybe someone else has the same issue or a better solution...

I have also added the `reverse_geocode` case.

Now look what it can do:

1.9.3p194 :310 > loc = Location.new(name: "Vatikan")
 => #<Location id: nil, name: "Vatikan", latlon: #<RGeo::Geographic::SphericalPointImpl:0x8148add8 "POINT (0.0 0.0)">, created_at: nil, updated_at: nil, address: nil> 
1.9.3p194 :311 > loc.save
   (0.2ms)  BEGIN
  SQL (0.6ms)  INSERT INTO "locations" ("address", "created_at", "latlon", "name", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["address", "Via Pio X, 00120, Vatican City"], ["created_at", Sat, 17 Nov 2012 19:26:59 UTC +00:00], ["latlon", #<RGeo::Geographic::SphericalPointImpl:0x81570554 "POINT (12.453389 41.902916)">], ["name", "Vatikan"], ["updated_at", Sat, 17 Nov 2012 19:26:59 UTC +00:00]]
   (0.9ms)  COMMIT
 => true 

I love this! :-)

Problem

I am trying to use the geocoder gem for looking up addresses and coordinates. I want it to work together with my PostGIS spatial database and the RGeo gem, which uses POINT features instead of saving latitude and longitude values separately. So I have tried to work into my model to save the results from the geocoder lookup into an RGeo POINT feature: ``` class Location < ActiveRecord::Base attr_accessible :latlon, :name, :address set_rgeo_factory_for_column(:latlon, RGeo::Geographic.spherical_factory(:srid => 4326)) geocoded_by :name_and_address do |obj, results| if geo = results.first obj.latlon = Location.rgeo_factory_for_column(:latlon).point(geo.longitude, geo.latitude) end end after_initialize :init after_validation :geocode def init self.latlon ||= Location.rgeo_factory_for_column(:latlon).point(0, 0) end def latitude self.latlon.lat end def latitude=(value) lon = self.latlon.lon self.latlon = Location.rgeo_factory_for_column(:latlon).point(lon, value) end def longitude self.latlon.lon end def longitude=(value) lat = self.latlon.lat self.latlon = Location.rgeo_factory_for_column(:latlon).point(value, lat) end def name_and_address "#{self.name}, #{self.address}" end end ``` In the Rails console I can now do: ``` test = Location.new(name: "Eiffel Tower") // => #<Location id: nil, name: "Eiffel Tower", latlon: #<RGeo::Geographic::SphericalPointImpl:0x81579974 "POINT (0.0 0.0)">, created_at: nil, updated_at: nil, address: nil> test.geocode // => #<RGeo::Geographic::SphericalPointImpl:0x81581ac0 "POINT (2.294254 48.858278)"> ``` Wonderful! However, when I want to save my test Location, which triggers the `:after_validation` call for `:geocode` I get an error: ``` NoMethodError: undefined method `lon' for nil:NilClass from /Users/sg/rails-projects/geo_rails_test/app/models/location.rb:24:in `latitude=' from /Users/sg/rails-projects/geo_rails_test/app/models/location.rb:7:in `block in <class:Location>' from /Users/sg/.rvm/gems/ruby-1.9.3-p194/gems/geocoder-1.1.2/lib/geocoder/stores/base.rb:108:in `call' from /Users/sg/.rvm/gems/ruby-1.9.3-p194/gems/geocoder-1.1.2/lib/geocoder/stores/base.rb:108:in `do_lookup' from /Users/sg/.rvm/gems/ruby-1.9.3-p194/gems/geocoder-1.1.2/lib/geocoder/stores/active_record.rb:278:in `geocode' from /Users/sg/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/callbacks.rb:405:in `_run__1498365873506454431__validation__51840359655181017__callbacks' [...] ``` It seems that the geocode callback deletes the RGeo object so that its built in methods as well as my getter and setter methods are no longer available. Why? Any ideas?

Original source