Upload
xilinus
View
2.529
Download
2
Embed Size (px)
DESCRIPTION
Slide de ma session sur l'integration d'une google map dans une application Rails. Le code est disponible sur github: http://github.com/xilinus/gmaps_demo/
Citation preview
Intégration de GoogleMaps dans une Application Rails 2.0
Sébastien GruhierXilinus
Railscamp - Paris - 17 mai 2008
GoogleMapshttp://code.google.com/apis/maps/
• Affichage géolocalisé d’information (point, forme vectorielle, trafic ...)
• Librairie Javascript
• Gratuit sous certaines conditions
• Nécessite une API key
Plugin GeoKithttp://geokit.rubyforge.org/
• Ajout de finder geolocalisé à ActiveRecord
• MultiProvider (GoogleMap, YahooMaps...)
• Calcul de distance
• IP localisation via hostip.info
• ...
Application de demo
• Application Rails 2.0
• Visualisation des utilisateurs sur une googlemap
• Plugin restful_authenticated + geokit
Initialisation
• Assez parlé, codons un exemple
rails gmaps_demo -d mysql; cd gmaps_demo
• plugin geokit
./script/plugin install svn://rubyforge.org/var/svn/geokit/trunkgit module add git://github.com/ptb/geokit.git vendor/plugins/geokit
• plugin restful_authentication
./script/plugin install http://svn.techno-weenie.net/projects/plugins/restful_authenticationgit submodule add git://github.com/technoweenie/restful-authentication.git vendor/plugins/restful_authentication
• GIT: git submodule init
GeoKitTest
./script/console Loading development environment (Rails 2.0.2)include GeoKit::Geocoders>> include GeoKit::Geocoders=> Object>> home = MultiGeocoder.geocode("151 rue montmartre, paris")=> #<GeoKit::GeoLoc:0x27769b8 @country_code="FR", @lat=48.870519, @street_address="151, Rue Montmartre", @full_address="151, Rue Montmartre, 75002 2ème Arrondissement Paris, Paris, France", @precision="address", @zip="75002", @state="Ile-de-France", @success=true, @provider="google", @lng=2.342718, @city="Paris">
>> home = MultiGeocoder.geocode("151 rue montmartre, paris")>> rennes = MultiGeocoder.geocode("rennes, france")>> home.distance_to(rennes, :units => :kms) => 308.473068506021
Calcul “in memory”
Initialisation
• Editer database.yml si nécessaire
rake db:create
• Génération de l’authentification (sans activation ni stateful)
./script/generate authenticated user sessions rake db:migraterake db:fixtures:load
Initialisation
• Supprimer public/index.html
• Ajouter un layout simple
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> <title>Google Maps Demo</title> </head> <body> <%= yield %> </body></html>
Initialisation
• Ajouter une vue users/index.html.erb
• Avec un partial users/_user.html.erb
User List<table border="0" cellspacing="5" cellpadding="5"> <tr> <th>Login</th> <th>Email</th> <th>Created At</th> </tr> <%= render :partial => "user", :collection => @users %></table><br/><%= link_to 'Add a new user', new_user_path %>
<tr> <td><%= user.login %></td> <td><%= user.email %></td> <td><%= user.created_at.to_s :short %></td></tr>
Initialisation fin!
• On ajoute une route par défaut
• Déplacer le code de environment.rb dans
config/initializers/geokit.rb avec une clé google valide
• Et on peut enfin s’amuser avec GoogleMaps :)
map.root :controller => 'users'
Géolocalisation
• Ajouter des attributs au model User./script/generate migration add_geolocalisation_to_users
•
class AddGeolocalisationToUsers < ActiveRecord::Migration def self.up add_column :users, :address, :string add_column :users, :lat, :decimal, :precision => 15, :scale => 10 add_column :users, :lng, :decimal, :precision => 15, :scale => 10 end
def self.down remove_column :users, :address remove_column :users, :lat remove_column :users, :lng endend
Géolocalisation
• Mettre à jour les deux fixturesUtilise la console !!
•
./script/console Loading development environment (Rails 2.0.2)>> MultiGeocoder.geocode("10 rue de la republique, paris, france")
=> #<GeoKit::GeoLoc:0x1102bdc @country_code="FR", @lat=48.866816, @street_address="10, Avenue De La République", @full_address="10, Avenue de la République, 75011 11ème Arrondissement Paris, Paris, France", @precision="address", @zip="75011", @state="Ile-de-France", @success=true, @provider="google", @lng=2.366405, @city="Paris">
Géolocalisation
• Ajouter au modèle User
• Ajouter edit/update + adresse à un user (dans la vue)
attr_accessible ..., :address
acts_as_mappable :auto_geocode => truebefore_validation_on_update :auto_geocode_address
GoogleMap
• Ajouter une route users/map
• Une action, un layout fullscreen: map.html.erb
map.resources :users, :collection => {:map => :get}
GoogleMaplayouts/map.html.erb
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> <title>Google Maps Demo</title> <%= javascript_include_tag :all %> <script src="http://maps.google.com/maps?file=api&v=2&key=<%= GeoKit::Geocoders::google %>" type="text/javascript"></script> <style type="text/css"> html, body { margin:0; height:100% } #map { min-height: 100%; background-color: #DDD; } * html #map { height: 100%; } </style> </head> <body> <%= yield %> </body></html>
GoogleMapviews/users/map.html.erb
<div id="map"></div><%= javascript_tag "new Application('map', '#{@users.to_json(:only => [:login, :lat, :lng])}');" %>
def map @users = User.find :all render :layout => 'map'end
Action
Vue
GoogleMapapplication.js
Application = new Class.create({ initialize: function(map, users) { this.element = $(map); this.users = users.evalJSON(); document.observe("dom:loaded", this.init.bind(this)); }, init: function() { if (GBrowserIsCompatible()) { // Create a google map this.map = new GMap2(this.element); // Center on France this.map.setCenter(new GLatLng(46.227638, 2.213749), 6); // Add controls this.map.addControl(new GLargeMapControl()); this.map.addControl(new GMapTypeControl()); // Add Markers if (this.users) { this.users.each(function(user) { var marker = new GMarker(new GLatLng(user.lat, user.lng)); GEvent.addListener(marker, "click", function() { marker.openInfoWindowHtml(user.login); }); this.map.addOverlay(marker); }.bind(this)) } } }});
MultiMap
Pour le fun, ajouter un map par user sur la page index :)
<tr> <td><%= user.login %></td> <td><%= user.email %></td> <td><%= user.lat %></td> <td><%= user.lng %></td> <td><%= user.created_at.to_s :short %></td> <td><div id="<%= dom_id(user) %>" class="minimap"></div></td> <td><%= link_to 'edit', edit_user_path(user) %></td></tr><%= javascript_tag "new MiniMap('#{dom_id(user)}', '#{user.to_json(:only => [:login, :lat, :lng])}');" %>
MultiMapClasse MiniMap
MiniMap = new Class.create({ initialize: function(map, user) { this.element = $(map); this.user = user.evalJSON(); document.observe("dom:loaded", this.init.bind(this)); }, init: function() { if (GBrowserIsCompatible()) { // Create a google map this.map = new GMap2(this.element); // Center on France this.map.setCenter(new GLatLng(this.user.lat, this.user.lng), 6); var marker = new GMarker(new GLatLng(this.user.lat, this.user.lng)); this.map.addOverlay(marker); } }});
GeoKitAutres fonctions
• sort_by_distance_from
• Column distance
• GeoKit::Bounds
>> u = User.find :first>> users.User.find :all>> users.sort_by_distance_from u
>> User.find :all, :origin => u, :within => 500, :order => 'distance'
>> bounds = GeoKit::Bounds.new(sw_point, ne_point) >> users = User.find :all, :bounds => bounds
GeoKitAutres fonctions
• eager loading
•
• IP Geocoding
•
>> users = Users.find :all, :origin => home, :include => [:articles], :within => 5, :order => 'distance'
>> location = GeoKit::Geocoders::IpGeocoder.geocode('82.236.140.196')=> #<GeoKit::GeoLoc:0x2784fe0 @country_code="FR", @lat=48.0833, @street_address=nil, @precision="unknown", @zip=nil, @state=nil, @success=true, @provider="hostip", @lng=-1.68333, @city="Rennes">
GeoKitAutres fonctions
• IP Geocoding
class ApplicationController < ActionController::Base # Auto-geocode the user's ip address and store # it in the session. geocode_ip_address
def loc # @location is a GeoLoc instance. @location = session[:geo_location] end end
GeoKitAutres fonctions
• Et encore!
# Finds within a distance radius.def find_within(distance, options={})alias find_inside find_within # Finds beyond a distance radius.def find_beyond(distance, options={})alias find_outside find_beyond # Finds according to a range. Accepts inclusive or exclusive ranges.def find_by_range(range, options={}) # Finds the closest to the origin.def find_closest(options={})alias find_nearest find_closest # Finds the farthest from the origin.def find_farthest(options={})
# Finds within rectangular bounds (sw,ne).def find_within_bounds(bounds, options={})
Done!!
• Code disponible sur githubgit://github.com/xilinus/gmaps_demo.git