Geocoding, Places and Maps

Episode #84 by Teacher's Avatar David Kimura

Summary

Using the Geocoder gem and Google APIs, learn how to add some geolocation functionality to your application.
rails javascript api maps 11:29

Resources

Geocoder Site - http://www.rubygeocoder.com/
Geocoder Github - https://github.com/alexreisner/geocoder
Google Developer Console - https://console.developers.google.com
Source - https://github.com/driftingruby/084-geocoding-places-and-maps

Forgot to mention in the video that there is a collection on the places resource in the routes. This is part of the request to get the list of nearby places.

# routes.rb
Rails.application.routes.draw do
  resources :places do
    collection do
      put :get_locations
    end
  end
  root 'places#index'
end

Summary

# Gemfile
gem 'geocoder'

Add Latitude and Longitude floats to your model.

# Terminal
# rails g scaffold places name address latitude:float longitude:float
- or -
# rails g migration add_coordinates_to_places latitude:float longitude:float

# rails db:migrate

# place.rb
class Place < ApplicationRecord
  geocoded_by :address
  after_validation :geocode, if: :address_changed?
end

# rails console
Place.near("Big Ben")
Place.first.nearbys
place = Place.first
place.distance_to("Eiffel Tower")
place.distance_to(Place.second)

Generate Geocoder Config

# Terminal
rails generate geocoder:config

# index.html.erb
  <%= content_tag :tbody, id: :places do %>
    <%= render @places %>
  <% end %>

  ...

<%= content_tag :button, 'Get Places', class: 'btn btn-info get_location' %>
<%= tag :div, class: 'location_results_list' %>

# get_places.coffee
@getPlaces = ->
  failure = ->
    alert 'problem getting location' 
  $('.get_location').each (index) ->
    $(this).on 'click', (event) ->
      if navigator.geolocation
        navigator.geolocation.getCurrentPosition ((position) ->
          $.ajax
            type: 'PUT'
            url: '/places/get_locations' + '?latitude=' + position.coords.latitude + '&longitude=' + position.coords.longitude
        ), failure,
          enableHighAccuracy: true
          timeout: 5000
      event.preventDefault()

$(document).on('turbolinks:load', @getPlaces)

@createPlace = ->
  $('.location_results_list button').on 'click', (event) ->
    data = JSON.parse(JSON.stringify($(this).data('attributes')))
    $.ajax '/places', 
      type: 'POST'
      dataType: 'script'
      data: {
        place: {
          name: data['name']
          address: data['vicinity']
          latitude: data['geometry']['location']['lat']
          longitude: data['geometry']['location']['lng']
        }
      }
      success: (data, status) ->
        # console.log 'Data: ' + data + '\nStatus: ' + status

    event.preventDefault()

# places_controller.rb
  def get_locations
    url = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=#{params[:latitude]},#{params[:longitude]}&radius=500&key=#{Rails.application.secrets.google_places_key}"
    http_call = open(url).read
    response = JSON.parse(http_call, {:symbolize_names => true})
    @locations = response[:results]
  end

# routes.rb
Rails.application.routes.draw do
  resources :places do
    collection do
      put :get_locations
    end
  end
  root 'places#index'
end

# get_locations.js.erb
<% @locations.each do |loc| %>
  $('.location_results_list').append('<li><button data-attributes="<%= j loc.to_json.to_s %>"><%= loc[:name] %></button></li>');
<% end %>
createPlace();

# create.js.erb
$('#places').append('<%= j render @place %>');

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