DEV Community

Victor Hazbun
Victor Hazbun

Posted on • Edited on

3 2

Best practices: Async Reverse Geocoding with Ruby and Geocoder

What's up guys? Today I will share with you how I approach reverse geocoding using the Geocoder gem.

As you may know the Ruby gem Geocoder lets you do reverse geocoding "automatically" in the model by doing

reverse_geocoded_by :latitude, :longitude

and that is cool, but I found a better way...

First, the Geocoder gem makes a request to the Google maps API, and that takes time making my response a slow. So I decided to do the reverse geocoding in an async process after my record detected a change in my latitude and longitude fields.

app/models/location.rb

class Location < ApplicationRecord

    ...
    after_commit -> { ReverseGeocodingWorker.perform_async(id) }, if: :coordinates_changed?

    private

    def coordinates_changed?
        latitude_changed? && longitude_changed?
    end
    ...
end
Enter fullscreen mode Exit fullscreen mode

Now, lets see the ReverseGeocodingWorker class in charge of updating the location record with the results of the Google Maps API

app/workers/reverse_geocoding_worker.rb

class ReverseGeocodingWorker
  include Sidekiq::Worker
  sidekiq_options retry: true

  def perform(location_id)
    location = Location.find(location_id)
    results = Geocoder.search([location.latitude, location.longitude])
    if results
      result = result.first
      location.update_attributes(
        street_address: result.street_address,
        city: result.city,
        state: result.state,
        postal_code: result.postal_code
      )
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

This is gonna speed up your response time since you are delegating the reverse geocoding process to the worker.

Lastly I want to share one bonus tip for the Geocoder setup which is gonna increase the response time and will also save your Google Maps quota, just by enabling the cache option:

config/initializers/geocoder.rb

Geocoder.configure(
  # Geocoding options
  lookup: :google,
  ip_lookup: :freegeoip,
  use_https: false,
  cache: Redis.new
)
Enter fullscreen mode Exit fullscreen mode

Hope you good luck with your reverse geocoding feature.

Please let me know if you have questions or want me to write new posts about best practices for Ruby apps.

Cheers!

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (2)

Collapse
 
tomk32 profile image
Thomas R. Koll

I'm using GeoCoder as part of Ahoy and its README recommends Geolite2 for a quicker local search which I found totally fine for me purposes. There's even a debian and arch packages for geolite2.

Collapse
 
superails profile image
Yaroslav Shmarov

+1, I also went this way for one project, works well.

here's my geocoder.rb setup

# config/initializers/geocoder.rb
def valid_geo_file
  file_path = "#{Rails.root}/GeoLite2-City.mmdb"
  return file_path if File.exist?(file_path)
end

Geocoder.configure(
  lookup: :mapbox,
  ip_lookup: :geoip2,
  api_key: Rails.application.credentials.dig(:mapbox, :token),
  geoip2: {
    file: valid_geo_file
  }
)
Enter fullscreen mode Exit fullscreen mode

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay