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
- Statue of Liberty, NY
- Empire State Building, New York
- Golden Gate Bridge, San Francisco
- 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¢er=#{@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: