Google Maps
Add Google maps with the ym4r plugin
Terms of use:
- available for any web site that is free to consumers
- API key for a specific domain/directory
- API key signup
- no limit on the number of page views per day
- 50,000 geocode requests per day per Maps API key
Install the ym4r plugin
ruby script/plugin install svn://rubyforge.org/var/svn/ym4r/Plugins/GM/trunk/ym4r_gm
Signup for API key and configure the plugin
- get a key for http://wierzba.wzks.uj.edu.pl at the API key signup
- open the file config/gmaps_api_key.yml
- in the 'development' section:
- prepend localhost: to the left of the default key
- add a new line: wierzba.wzks.uj.edu.pl: your_api_key
(mind the space after the colon) gmaps_api_key.yml
Include GMap API in the layout
- Open the layout file app/views/layouts/coffeehouse.rhtml
- Add this snippet to the HEAD section:
<% if @map %>
<%= GMap.header(:host => @request.host) %>
<%= javascript_include_tag 'ym4r-gm' %>
<%= javascript_include_tag 'application' %>
<%= @map.to_html %>
<% end %>
- what this does is as follows: if the map is set
- GMap.header: output the header with Google Map API code
- @map.to html: initialize the map
CAUTION: make sure the application renders with layout. If the html source has no headers -- the layout is missing. Easiest way to fix this -- add the layout command to ApplicationController.
Display coffee house on map -- controller
app/controllers/coffee_house_controller.rb
def show
@coffee_house = CoffeeHouse.find(params[:id])
@map = GMap.new("map_div")#name of the html element that will hold the map
@map.control_init(:large_map => true,:map_type => true)
@map.center_zoom_init([@coffee_house.latitude,@coffee_house.longitude],@coffee_house.zoom)
@map.overlay_init(GMarker.new([@coffee_house.latitude,@coffee_house.longitude],:title => @coffee_house.name))
end
Display coffee house on map -- view
app/views/coffee_house/show.rhtml
add this snippet where you want the map displayed:
<%= @map.div(:width => 600, :height => 400) %>
Place pointer on map -- controller
- app/controllers/coffee_house_controller.rb
add this method at the end
private
def set_map_for_editing
@map = GMap.new("map_div")
loc = [@coffee_house.latitude,@coffee_house.longitude]
@map.control_init(:large_map => true,:map_type => true)
@map.center_zoom_init(loc, @coffee_house.zoom)
marker = GMarker.new(loc,:draggable => true)
@map.overlay_global_init(marker,"marker")
@map.record_init marker.on_dragend("gmap_update_position")
@map.record_init @map.on_zoomend("gmap_update_zoom")
end
- call set_map_for_editing where we want to edit the map
app/controllers/coffee_house_controller.rb
def new
@coffee_house = CoffeeHouse.new
@cities = City.find(:all)
@companies = Company.find(:all)
set_map_for_editing
end
def create
@coffee_house = CoffeeHouse.new(params[:coffee_house])
if @coffee_house.save
flash[:notice] = 'CoffeeHouse was successfully created.'
redirect_to :action => 'list'
else
@cities = City.find(:all)
@companies = Company.find(:all)
set_map_for_editing
render :action => 'new'
end
end
def edit
@coffee_house = CoffeeHouse.find(params[:id])
@cities = City.find(:all)
@companies = Company.find(:all)
set_map_for_editing
end
def update
@coffee_house = CoffeeHouse.find(params[:id])
if @coffee_house.update_attributes(params[:coffee_house])
flash[:notice] = 'CoffeeHouse was successfully updated.'
redirect_to :action => 'show', :id => @coffee_house
else
@cities = City.find(:all)
@companies = Company.find(:all)
set_map_for_editing
render :action => 'edit'
end
end
Place pointer on map -- view
- public/javascripts/application.js
function gmap_update_position(){
lat = document.getElementById('coffee_house_latitude');
lon = document.getElementById('coffee_house_longitude');
lat.value = ""+ marker.getPoint().lat();
lon.value = ""+ marker.getPoint().lng();
}
function gmap_update_zoom(){
zoom = document.getElementById('coffee_house_zoom');
zoom.value = ""+ map.getZoom();
}
- app/views/coffee_house/_form.rhtml
add this line:
<%= @map.div(:width => 600, :height => 400) %>
- in app/views/coffee_house/_form.rhtml -- uncomment the latitude, longitude and zoom input fields to see how they are updated with javascript
Map of all coffee houses in a given city
- app/models/city.rb
add association with coffee_houses;
has_many :coffee_houses
- app/controllers/city_controller
add 'show' action:
def show
@city = City.find_by_id(params[:id])
redirect_to :action => 'list' and return unless @city
@map = GMap.new("map_div")
@map.control_init(:large_map => true, :map_type => true)
@coffee_houses = @city.coffee_houses
@coffee_houses.each do |ch|
tabs = []
t1 = GInfoWindowTab.new
t1.tab = 'Address'
t1.content = "<b>#{ch.name}</b><br>#{ch.street} #{ch.building_number}"
t1.content += '<br><a href="'+url_for(:controller=>'coffee_house',:action=>'show',:id=>ch)+'">more</a>'
tabs<< t1
if ch.photos.size > 0
t2 = GInfoWindowTab.new
t2.tab = 'Photo'
photo = ch.photos.first.thumbnails.find_by_thumbnail('thumb')
t2.content = '<img src="'+url_for(:controller=>photo.public_filename)
t2.content +='" width="'+photo.width.to_s+'" height="'+photo.height.to_s+'">'
tabs<< t2
end
@map.overlay_init(GMarker.new([ch.latitude,ch.longitude], :title => ch.name,:info_window_tabs => tabs))
end
if @coffee_houses.length > 0
@selected = @coffee_houses.first
@map.center_zoom_init([@selected.latitude,@selected.longitude],@selected.zoom)
end
end
- create the view -- app/views/city/show.rhtml
<h1><%= @city.name %></h1>
<%= @map.div(:width => 800, :height => 600) %>
- click on the marker to see the info bubbles
City geocoding (trivial example)
Instead of centering the map of all coffee houses on the first one, center it on the city centre using the Google geocoder. Replace the old centering code in app/controllers/city_controller.rb with:
geo_results = Geocoding::get(@city.name+' Poland',{:host=>@request.host})
if geo_results.status == Geocoding::GEO_SUCCESS
coords = geo_results[0].latlon
@map.center_zoom_init(coords,13)
end
(it's at the end of the show action -- delete/comment out code from "if @coffee_houses.length > 0" on and replace with the above snippet)
Tasks
- add a 'updated_at' column to the coffee_houses table and use it to display a man of recently updated coffee houses on the main page
- add latitude and longitude columns to cities, and store the geocoded coordinates there instead of querying the geocoder each time