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?
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! :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With