We are going to setup a map feature that will allow you to see all your adventures on a map and search for adventures by address.
First let’s setup our migrations
In your command line, generate the migrations for address, latitutde and longitude.
$ rails generate migration AddAddressToAdventures address:string
$ rails generate migration AddLatitudeAndLongitudeToAdventures latitude:float longitude:float
Then run your migrations.
$ rake db:migrate
Then, add address field to form. We will setup our app so we only need to input an address and not coordinates.
#app/views/adventures/_form.html.erb
<div class="field">
<%= f.label :address %><br>
<%= f.text_field :address %>
</div>
Because we’ve added new attributes to our form, we need to permit them in our params in our controller. Add address attribute to the permitted params of adventures_controller.
#app/controllers/adventures_controller.rb
def adventure_params
params.require(:adventure).permit(:name, :description, :picture, :visit, :address)
end
Let’s also add in our map attributes to our adventures/page
# app/views/adventures/show.html.erb
<p><b>Name: </b><%= @adventure.name %></p>
<p><b>Description: </b><%= @adventure.description %></p>
+ <p><b>Address: </b><%= @adventure.address %></p>
+ <p><b>Latitude: </b><%= @adventure.latitude %></p>
+ <p><b>Longitude: </b><%= @adventure.longitude %></p>
Now create a new adventure and add in an address. You’ll know it works when you see the the coordinates of your address on your show page.
Now let’s setup our geocoder gem. Geocoder is an excellent gem for converting addresses and coordinates, finding nearby locations and determining distances.
In your Gemfile add
#Gemfile
gem 'geocoder'
Bundle in your command line
$ bundle install
Setup your adventure model for geocoder
# app/models/adventure.rb
class Adventure < ActiveRecord::Base
+ geocoded_by :address
+ after_validation :geocode # this command auto fetches our coordinates
mount_uploader :picture, PictureUploader
has_many :comments
end
Setting up gmaps4rails
The gmaps4rails gem allows us to use google maps with our own custom overlays (markers, infowindows).
Add gmaps4rails to your gemfile
# Gemfile
gem 'gmaps4rails'
and bundle again in your command line
$ bundle install
Create a new file in your views/pages directory called map.html.erb.
#views/pages/map.html.erb
You will need to add scripts that connect with Google’s map API and a div that holds the map. Add in the code below to that map file. You can change the size of the map by messing with the width and height on the <div id=map.
# views/pages/map.html.erb
<div style='width: 800px;'>
<div id="map" style='width: 800px; height: 400px;'></div>
</div>
<script src="//maps.google.com/maps/api/js?v=3.18&sensor=false&client=&key=&libraries=geometry&language=&hl=®ion="></script>
<script src="//google-maps-utility-library-v3.googlecode.com/svn/tags/markerclustererplus/2.0.14/src/markerclusterer_packed.js"></script>
<script type="text/javascript">
var handler = Gmaps.build('Google');
handler.buildMap({ internal: {id: 'custom_style'}, provider: {}, internal: {id: 'map'}}, function(){
markers = handler.addMarkers(<%=raw @hash.to_json %>);
handler.bounds.extendWith(markers);
handler.fitMapToBounds();
});
</script>
For this to work, we need underscoreJS. Go to http://underscorejs.org/ and click on production version(1.8.3). Copy everything in that file.
Create a new file in vendor/assets/javascripts directory called underscore.js. Paste in what you copied
#vendor/assets/javascripts/underscore.js
#Paste code here
Next, setup your javascript by requiring underscore and gmaps/google in your application.js
We need to do this so our javascript can see those gems.
#app/assets/javascript/application.js
+ //= require underscore
+ //= require gmaps/google
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
Whenever we add to our application.js file, we should restart our server to see those changes.
In the command line, restart your server.
Next, we’ll fix up our controller. Add in a map method to pages_controller to incorporate the map.
#app/controllers/pages_controller.rb
def map
if params[:search].present?
@adventures = Adventure.near(params[:search], 1000, :order => :address)
else
@adventures = Adventure.all
end
@hash = Gmaps4rails.build_markers(@adventures) do |adventure, marker|
marker.lat adventure.latitude
marker.lng adventure.longitude
marker.infowindow adventure.name
marker.picture({
"width" => 32,
"height" => 32})
marker.json({ name: adventure.name})
end
end
Add static maps and nearby locations in the adventures/show page. The image tag shows a static map and the nearby locations will find all the nearby locations of the address you entered in a 1000 mile radius.
# views/adventures/show.html.erb
<%= image_tag "http://maps.google.com/maps/api/staticmap?size=450x300&sensor=false&zoom=16&markers=#{@adventure.latitude}%2C#{@adventure.longitude}" %>
<h3>Nearby Adventures</h3>
<ul>
<% for location in @adventure.nearbys(10) %>
<li><%= link_to location.address, location %> (<%= location.distance.round(2) %> miles)</li>
<% end %>
</ul>
Go to one of your adventure#show pages to see your map and nearby locations.
###Adding search field
Finally, let’s add a search input feature to your views which allows you to enter an address or location name, and will get the nearest adventures to that inputted area.
#app/views/pages/map.html.erb
<%= form_tag adventures_path, :method => :get do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search Near", :name => nil %>
</p>
<% end %>
And there you have it. Try creating a few more adventure to populate your map.