Geocoder Complete Ruby geocoding solution with Rails

If you need to work with geographic data, Geocoder is an excellent gem for converting addresses and coordinates, finding nearby locations, determining distances, and more!

Let's get a simple app to use as base to our Geocoder, so let's clone

git clone https://github.com/douglasqsantos/dqs-rails-base.git

Now we need to access the base of the application.

cd dqs-rails-base

Now we need to create a scaffold to use as base to our example

rails g scaffold location address:string latitude:float longitude:float

Now let's run the migration

rake db:migrate

Now let's run the bundle

bundle install

Now we can launch the server.

rails server

Now we can access the locations at: http://localhost:3000/locations

Now let's add the geocoder gem into the Gemfile

vim Gemfile
[...]
gem 'geocoder'

Now let's get the geocoder gem

bundle install

Now we need to configure the model to use the geocode

vim app/models/location.rb
class Location < ActiveRecord::Base

 # Getting the address coordinates after validation
 # and store in latitude and longitude attributes
 geocoded_by :address
 after_validation :geocode, :if => :address_changed?

end 

Now let's test the geocode at: http://localhost:3000/locations/new

Now let's add some locations

  1. Statue of Liberty, NY
  2. Empire State Building, New York
  3. Golden Gate Bridge, San Francisco
  4. Time Square, New York City, NY

After create the Statue of Liberty, NY we will get something like:

Now let's use an built method of geocode that is used to get the nearby location.

Let's add it into show.html.erb

vim app/views/locations/show.html.erb
<p id="notice"><%= notice %></p>

<p>
  <strong>Address:</strong>
  <%= @location.address %>
</p>

<p>
  <strong>Latitude:</strong>
  <%= @location.latitude %>
</p>

<p>
  <strong>Longitude:</strong>
  <%= @location.longitude %>
</p>

<h3>Nearby locations</h3>

<ul>
<% for location in @location.nearbys(10) %>
        <li><%= link_to location.address, location %> (<%= location.distance.round(2) %> miles) (<%= ((location.distance).to_f / 0.62137).round(2) %> kms) </li>
<% end %>
</ul>

<%= link_to 'Edit', edit_location_path(@location) %> |
<%= link_to 'Back', locations_path %>

Now let's take a look at show of Empire State Building, New York we'll get something like:

Now let's add an image of the location we are showing into the show.html.erb

vim app/views/locations/show.html.erb 
<p id="notice"><%= notice %></p>

<p>
  <strong>Address:</strong>
  <%= @location.address %>
</p>

<p>
  <strong>Latitude:</strong>
  <%= @location.latitude %>
</p>

<p>
  <strong>Longitude:</strong>
  <%= @location.longitude %>
</p>

<%= image_tag "http://maps.google.com/maps/api/staticmap?size=450x300&sensor=false&zoom=16&markers=#{@location.latitude}%2C#{@location.longitude}" %>

<h3>Nearby locations</h3>

<ul>
<% for location in @location.nearbys(10) %>
        <li><%= link_to location.address, location %> (<%= location.distance.round(2) %> miles) (<%= ((location.distance).to_f / 0.62137).round(2) %> kms) </li>
<% end %>
</ul>

<%= link_to 'Edit', edit_location_path(@location) %> |
<%= link_to 'Back', locations_path %>

Now let's take a look at: Empire State Building, New York into show, we'll get something like

Now if you need another kind of map you need to take a look at: https://developers.google.com/maps/documentation/

If you want to use an hybrid map we can use as:

vim app/views/locations/show.html.erb 
<p id="notice"><%= notice %></p>

<p>
  <strong>Address:</strong>
  <%= @location.address %>
</p>

<p>
  <strong>Latitude:</strong>
  <%= @location.latitude %>
</p>

<p>
  <strong>Longitude:</strong>
  <%= @location.longitude %>
</p>

<%= image_tag "https://maps.googleapis.com/maps/api/staticmap?maptype=hybrid&center=#{@location.latitude},#{@location.longitude}&zoom=16&size=450x300&markers=color:blue%7Clabel:S%7C#{@location.latitude},#{@location.longitude}&key=PUT_YOUR_KEY_HERE" %>

<h3>Nearby locations</h3>

<ul>
<% for location in @location.nearbys(10) %>
        <li><%= link_to location.address, location %> (<%= location.distance.round(2) %> miles) (<%= ((location.distance).to_f / 0.62137).round(2) %> kms) </li>
<% end %>
</ul>

<%= link_to 'Edit', edit_location_path(@location) %> |
<%= link_to 'Back', locations_path %>

We shall get something like:

Now let's create a search box into index.html.erb to search for a specific address and get the nearby locations of that.

vim app/views/locations/index.html.erb
<p id="notice"><%= notice %></p>

<%= form_tag locations_path, :method => :get do %>
  <p>
    <%= text_field_tag :search, params[:search] %>
    <%= submit_tag "Search Near", :name => nil %>
  </p>
<% end %>

<h1>Listing Locations</h1>

<table>
  <thead>
    <tr>
      <th>Address</th>
      <th>Latitude</th>
      <th>Longitude</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @locations.each do |location| %>
      <tr>
        <td><%= location.address %></td>
        <td><%= location.latitude %></td>
        <td><%= location.longitude %></td>
        <td><%= link_to 'Show', location %></td>
        <td><%= link_to 'Edit', edit_location_path(location) %></td>
        <td><%= link_to 'Destroy', location, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Location', new_location_path %>

Now we need to configure the controller to get the information sent by the search box and process it and return the results.

vim app/controllers/locations_controller.rb
class LocationsController < ApplicationController
  before_action :set_location, only: [:show, :edit, :update, :destroy]

  # GET /locations
  # GET /locations.json
  def index
    if params[:search].present?
      # Get the nearby locations, here we are searching for nearby locations
      # close 50 miles away.
      @locations = Location.near(params[:search], 50, :order => "distance")
    else
      @locations = Location.all
    end
  end

Now if we make a search about: Central Park, NY we will get something like:

Now let's improve the layout of the locations.

Now let's remove the scaffolds.scss

rm -rf app/assets/stylesheets/scaffolds.scss

Now let's reconfigure the index.html.erb

vim app/views/locations/index.html.erb
<ol class="breadcrumb">
  <li><%= link_to('Locations', {:controller => 'locations', :action => 'index'}) %></li>
  <li class="active">Index</li>
</ol>


<%= form_tag locations_path, :class => 'form-inline', :method => :get do %>
  <div class="form-group">
    <%= text_field_tag :search, params[:search], :class => 'form-control' %>
    <%= submit_tag('Search Near', :class => 'btn btn-primary') %>
    <%= link_to("Show All", {:controller => 'locations', :action => 'index'}, :class => 'btn btn-primary') %>
  </div>
<% end %>

<div class="table-responsive">
<h2>Listing Locations</h2>

  <%= link_to("Add New Location", {:action => 'new'}, :class => 'btn btn-primary') %>
  <br>
  <br>

  <div><%= pluralize(@locations.size, 'Locatio') %> found </div>
  <table class="table table-striped table-hover table-bordered" summary="Admin User list">
    <thead class="tables-header">
    <tr>
      <th>Address</th>
      <th>Latitude</th>
      <th>Longitude</th>
      <th colspan="3" class="text-center">Actions</th>
    </tr>
  </thead>

  <tbody>
    <% @locations.each do |location| %>
      <tr>
        <td><%= location.address %></td>
        <td><%= location.latitude %></td>
        <td><%= location.longitude %></td>
        <td><%= link_to '', location, :class => 'glyphicon glyphicon-eye-open' %></td>
        <td><%= link_to '', edit_location_path(location),:class => 'glyphicon glyphicon-pencil' %></td>
        <td><%= link_to '', location,:class => 'glyphicon glyphicon-trash' , method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

We'll something like this into index:

Now let's reconfigure the show.html.erb

vim app/views/locations/show.html.erb
<ol class="breadcrumb">
  <li><%= link_to('Locations', {:controller => 'locations', :action => 'index'}) %></li>
  <li class="active">Show</li>
</ol>
<h2>Show Location</h2>
<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <table class="table table-striped table-hover table-bordered">
      <tr>
        <th class="tables-header">Address</th>
        <td><%= @location.address %></td>
      </tr>
      <tr>
        <th class="tables-header">Latitude</th>
        <td><%= @location.latitude %></td>
      </tr>
      <tr>
        <th class="tables-header">Longitude</th>
        <td><%= @location.longitude %></td>
      </tr>
    </table>

    <!-- Image of the location -->
    <%= image_tag "http://maps.google.com/maps/api/staticmap?size=450x300&sensor=false&zoom=16&markers=#{@location.latitude}%2C#{@location.longitude}" %>

  <!-- Nearby Locations  -->
  <h3>Nearby locations</h3>

  <ul>
  <% for location in @location.nearbys(10) %>
    <li><%= link_to location.address, location %> (<%= location.distance.round(2) %> miles) (<%= ((location.distance).to_f / 0.62137).round(2) %> kms) </li>
  <% end %>
  </ul>

  <!-- Buttons -->
  <%= link_to 'Back', locations_path ,:class => 'btn btn-primary' %> 
  <%= link_to 'Edit', edit_location_path(@location),:class => 'btn btn-primary' %> 

  </div>
</div>

We'll something like this into show:

Now let's change the partial form

vim app/views/locations/_form.html.erb 
<%= form_for(@location) do |f| %>
  <% if @location.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@location.errors.count, "error") %> prohibited this location from being saved:</h2>

      <ul>
      <% @location.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>
  <div class="row">
  <div class="col-md-6 col-md-offset-3">

    <div class="form-group">
    <%= f.label :address %><br>
    <%= f.text_field :address, :class => 'form-control' %>
  </div>
    <div class="form-group">
    <%= f.label :latitude %><br>
    <%= f.text_field :latitude, :class => 'form-control' %>
  </div>
    <div class="form-group">
    <%= f.label :longitude %><br>
    <%= f.text_field :longitude , :class => 'form-control' %>
  </div>
    <div class="form-buttons">
    <%= f.submit :class => 'btn btn-primary' %>
  </div>
<% end %>

Now let's configure the new.html.erb

vim app/views/locations/new.html.erb 
<ol class="breadcrumb">
  <li><%= link_to('Locations', {:controller => 'locations', :action => 'index'}) %></li>
  <li class="active">New</li>
</ol>

<h2>New Location</h2>

<%= render 'form' %>
<%= link_to 'Back', locations_path ,:class => 'btn btn-primary' %> 

  <!-- Close the divs from the form -->
  </div>
</div>

Now let's change the last one edit.html.erb

vim app/views/locations/edit.html.erb 
<ol class="breadcrumb">
  <li><%= link_to('Locations', {:controller => 'locations', :action => 'index'}) %></li>
  <li class="active">Edit</li>
</ol>

<h2>Editing Location</h2>

<%= render 'form' %>

  <!-- Buttons -->
  <%= link_to 'Back', locations_path ,:class => 'btn btn-primary' %> 
  <%= link_to 'Show', @location,:class => 'btn btn-primary' %> 

<!-- Close the divs from the form -->
  </div>
</div>

Now let's take a look at new location:

Now let's take a look at edit location:

References