Search code examples
ruby-on-railsrails-geocoder

Geocoder to work with two addresses


rails 4.1.3 application with Geocoder gem has the following attributes on a model

  t.decimal :origin_lon, :precision => 15, :scale => 10
  t.decimal :origin_lat, :precision => 15, :scale => 10
  t.point :origin_lonlat, :srid => 3857
  t.decimal :destination_lon, :precision => 15, :scale => 10
  t.decimal :destination_lat, :precision => 15, :scale => 10
  t.point :destination_lonlat, :srid => 3857

The model is defined with

geocoded_by :origin, :latitude  => :origin_lat, :longitude => :origin_lon
geocoded_by :destination, :latitude  => :destination_lat, :longitude => :destination_lon

However if I run in the console:

Circuit.create(origin: 'avenue des Champs-Élysées, 90 Paris', destination: 'Place Mariejol, Antibes')

don't look it up, it's the Picasso Museum...
only destination data is being populated. If the order of the geocoded_by instructions are inverted and the server is re-started, then the origin data is being populated. So clearly only one call for geocoded_by is possible under this syntax.

What syntax allows for the two simultaneous searches?


Solution

  • The problem is geocoded_by calls geocoder_init, which only contains one set of options, so geocoded_by :destination clobbers geocoded_by :origin.

    def geocoder_init(options)
      unless defined?(@geocoder_options)
        @geocoder_options = {}
        require "geocoder/stores/#{geocoder_file_name}"
        include Geocoder::Store.const_get(geocoder_module_name)
      end
      @geocoder_options.merge! options
    end
    

    You can write your own ActiveRecord hook to geocode both:

    class Circuit < ActiveRecord::Base
      before_save :geocode_endpoints
    
      private
    
      def geocode_endpoints
        if origin_changed?
          geocoded = Geocoder.search(origin).first
          if geocoded
            self.origin_lat = geocoded.latitude
            self.origin_lon = geocoded.longitude
          end
        end
        # Repeat for destination
      end
    end
    

    Take a look at the geocode method to see how the gem does it, what kind of errors, it handles. etc. Unfortunately, we can't use do_lookup because that relies on the configured options instead of accepting parameters.