Ruby on Rails
Utilisation de GoogleMaps avec Rails
Recherche…
Ajouter la balise javascript google maps à l'en-tête de la mise en page
Pour que Google Maps fonctionne correctement avec turbolinks , ajoutez le tag javascript directement dans l’en-tête de la présentation plutôt que de l’inclure dans une vue.
# app/views/layouts/my_layout.html.haml
!!!
%html{:lang => 'en'}
%head
- # ...
= google_maps_api_script_tag
google_maps_api_script_tag
est mieux défini dans un assistant.
# app/helpers/google_maps_helper.rb
module GoogleMapsHelper
def google_maps_api_script_tag
javascript_include_tag google_maps_api_source
end
def google_maps_api_source
"https://maps.googleapis.com/maps/api/js?key=#{google_maps_api_key}"
end
def google_maps_api_key
Rails.application.secrets.google_maps_api_key
end
end
Vous pouvez enregistrer votre application avec google et obtenir votre clé API dans la console google api . Google a un petit guide sur la façon de demander une clé d'API pour l'API JavaScript de Google Maps .
La clé api est stockée dans le fichier secrets.yml
:
# config/secrets.yml
development:
google_maps_api_key: '...'
# ...
production:
google_maps_api_key: '...'
# ...
N'oubliez pas d'ajouter config/secrets.yml
à votre fichier .gitignore
et assurez-vous de ne pas valider la clé api dans le dépôt.
Géocoder le modèle
Supposons que vos utilisateurs et / ou groupes ont des profils et que vous souhaitez afficher les champs de profil d'adresse sur une carte google.
# app/models/profile_fields/address.rb
class ProfileFields::Address < ProfileFields::Base
# Attributes:
# label, e.g. "Work address"
# value, e.g. "Willy-Brandt-Straße 1\n10557 Berlin"
end
Le géocodeur est un excellent moyen de géocoder les adresses, c’est-à-dire de fournir la longitude
et la latitude
.
Ajoutez le géocodeur à votre Gemfile
et exécutez le bundle
pour l'installer.
# Gemfile
gem 'geocoder', '~> 1.3'
Ajouter des colonnes de base de données pour la latitude
et la longitude
afin de sauvegarder l'emplacement dans la base de données. C'est plus efficace que d'interroger le service de géocodage chaque fois que vous avez besoin de l'emplacement. C'est plus rapide et la limite de requête n'est pas atteinte si rapidement.
➜ bin/rails generate migration add_latitude_and_longitude_to_profile_fields \
latitude:float longitude:float
➜ bin/rails db:migrate # Rails 5, or:
➜ rake db:migrate # Rails 3, 4
Ajoutez le mécanisme de géocodage à votre modèle. Dans cet exemple, la chaîne d'adresse est stockée dans l'attribut value
. Configurez le géocodage pour qu'il fonctionne lorsque l'enregistrement a changé et que seule une valeur est présente:
# app/models/profile_fields/address.rb
class ProfileFields::Address < ProfileFields::Base
geocoded_by :value
after_validation :geocode, if: ->(address_field){
address_field.value.present? and address_field.value_changed?
}
end
Par défaut, geocoder utilise google comme service de recherche. Il a beaucoup de fonctionnalités intéressantes comme les calculs de distance ou la recherche de proximité. Pour plus d'informations, consultez le fichier README du géocodeur .
Afficher les adresses sur une carte google dans la vue de profil
Dans la vue de profil, affichez les champs de profil d'un utilisateur ou d'un groupe dans une liste, ainsi que les champs d'adresse sur une carte Google.
- # app/views/profiles/show.html.haml
%h1 Contact Information
.profile_fields
= render @profile_fields
.google_map{data: address_fields: @address_fields.to_json }
Les @profile_fields
et @address_fields
appropriés sont définis dans le contrôleur:
# app/controllers/profiles_controller.rb
class ProfilesController < ApplicationController
def show
# ...
@profile_fields = @user_or_group.profile_fields
@address_fields = @profile_fields.where(type: 'ProfileFields::Address')
end
end
Initialisez la carte, placez les marqueurs, définissez le zoom et les autres paramètres de la carte avec JavaScript.
Définir les marqueurs sur la carte avec javascript
Supposons qu'il y ait un div .google_map
, qui deviendra la carte, et qui comporte les champs d'adresse à afficher en tant que marqueurs en tant qu'attribut de data
.
Par exemple:
<!-- http://localhost:3000/profiles/123 -->
<div class="google_map" data-address-fields="[
{label: 'Work address', value: 'Willy-Brandt-Straße 1\n10557 Berlin',
position: {lng: ..., lat: ...}},
...
]"></div>
Pour utiliser l'événement $(document).ready
avec turbolinks sans gérer les événements turbolinks à la main, utilisez la gem jquery.turbolinks .
Si vous souhaitez effectuer d'autres opérations avec la carte, ultérieurement, par exemple, des fenêtres de filtrage ou des fenêtres d'informations, il est pratique de faire gérer la carte par une classe de script café .
# app/assets/javascripts/google_maps.js.coffee
window.App = {} unless App?
class App.GoogleMap
constructor: (map_div)->
# TODO: initialize the map
# TODO: set the markers
Lorsque vous utilisez plusieurs fichiers de script de café, qui sont placés par nom par défaut, il est pratique de définir un espace de noms App
global, partagé par tous les fichiers de script de café.
Ensuite, parcourez (éventuellement plusieurs) div .google_map
et créez une instance de la classe App.GoogleMap
pour chacun d'eux.
# app/assets/javascripts/google_maps.js.coffee
# ...
$(document).ready ->
App.google_maps = []
$('.google_map').each ->
map_div = $(this)
map = new App.GoogleMap map_div
App.google_maps.push map
Initialisez la carte en utilisant une classe de script café.
Si vous App.GoogleMap
une classe de script App.GoogleMap
, la carte Google peut être initialisée comme App.GoogleMap
:
# app/assets/javascripts/google_maps.js.coffee
# ...
class App.GoogleMap
map_div: {}
map: {}
constructor: (map_div)->
@map_div = map_div
@init_map()
@reference_the_map_as_data_attribute
# To access the GoogleMap object or the map object itself
# later via the DOM, for example
#
# $('.google_map').data('GoogleMap')
#
# store references as data attribute of the map_div.
#
reference_the_map_as_data_attribute: ->
@map_div.data 'GoogleMap', this
@map_div.data 'map', @map
init_map: ->
@map = new google.maps.Map(@dom_element, @map_configuration) if google?
# `@map_div` is the jquery object. But google maps needs
# the real DOM element.
#
dom_element: ->
@map_div.get(0)
map_configuration: -> {
scrollWheel: true
}
Pour en savoir plus sur les options possibles de map_configuration
, consultez la documentation de Google MapOptions et leur guide sur l'ajout d'éléments de contrôle .
Pour référence, la classe google.maps.Map
est largement documentée ici .
Initialiser les marqueurs de carte à l'aide d'une classe de script café
Si une classe de script App.GoogleMap
Coffee et les informations de marqueur sont stockées dans l'attribut data-address-fields
du .google_map
div, les marqueurs de carte peuvent être initialisés sur la carte comme .google_map
:
# app/assets/javascripts/google_maps.js.coffee
# ...
class App.GoogleMap
# ...
markers: []
constructor: (map_div)->
# ...
@init_markers()
address_fields: ->
@map_div.data('address-fields')
init_markers: ->
self = this # to reference the instance as `self` when `this` is redefined.
self.markers = []
for address_field in self.address_fields()
marker = new google.maps.Marker {
map: self.map,
position: {
lng: address_field.longitude,
lat: address_field.latitude
},
# # or, if `position` is defined in `ProfileFields::Address#as_json`:
# position: address_field.position,
title: address_field.value
}
self.markers.push marker
Pour en savoir plus sur les options de marqueur, consultez la documentation MarkerOptions de Google et son guide des marqueurs .
Zoom automatique sur une carte en utilisant une classe de script café
Avec une classe de script App.GoogleMap
Coffee avec google.maps.Map
stockée en tant que @map
et google.maps.Marker
stockée en tant que @markers
, la carte peut être agrandie automatiquement, c'est-à-dire ajustée pour que tous les marqueurs soient visibles, comme ceci: : sur la carte comme ceci:
# app/assets/javascripts/google_maps.js.coffee
# ...
class App.GoogleMap
# ...
bounds: {}
constructor: (map_div)->
# ...
@auto_zoom()
auto_zoom: ->
@init_bounds()
# TODO: Maybe, adjust the zoom to have a maximum or
# minimum zoom level, here.
init_bounds: ->
@bounds = new google.maps.LatLngBounds()
for marker in @markers
@bounds.extend marker.position
@map.fitBounds @bounds
Pour en savoir plus sur les limites, consultez la documentation de Google LatLngBounds .
Exposition des propriétés du modèle en json
Pour afficher les champs de profil d'adresse sous forme de marqueurs sur une carte google, les objets du champ d'adresse doivent être transmis en tant qu'objets json à JavaScript.
Attributs de base de données réguliers
Lors de l'appel à to_json
sur un objet ApplicationRecord
, les attributs de la base de données sont automatiquement exposés.
Étant donné un modèle ProfileFields::Address
avec des attributs label
, value
, longitude
et latitude
, address_field.as_json
produit un Hash
, par exemple une représentation,
address_field.as_json # =>
{label: "Work address", value: "Willy-Brandt-Straße 1\n10557 Berlin",
longitude: ..., latitude: ...}
qui est converti en chaîne json par to_json
:
address_field.to_json # =>
"{\"label\":\"Work address\",\"value\":\"Willy-Brandt-Straße 1\\n
10557 Berlin\",\"longitude\":...,\"latitude\":...}"
Ceci est utile car il permet d'utiliser plus tard l' label
et la value
en javascript, par exemple pour afficher des info-bulles pour les marqueurs de carte.
Autres attributs
D'autres attributs virtuels peuvent être exposés en as_json
méthode as_json
.
Par exemple, pour exposer un attribut title
, incluez-le dans le hachage as_json
fusionné:
# app/models/profile_fields/address.rb
class ProfileFields::Address < ProfileFields::Base
# ...
# For example: "John Doe, Work address"
def title
"#{self.parent.name}, #{self.label}"
end
def as_json
super.merge {
title: self.title
}
end
end
L'exemple ci-dessus utilise super
pour appeler la méthode as_json
origine, qui renvoie l'attribut d'origine de l'objet et le fusionne avec la position requise.
Pour comprendre la différence entre as_json
et to_json
, consultez cet article de jjulian .
Position
Pour rendre les marqueurs, Google Maps api nécessite, par défaut, une position
hachée qui contient respectivement la longitude et la latitude lng
et lat
.
Ce hachage de position peut être créé dans JavaScript, plus tard, ou ici lors de la définition de la représentation json du champ d'adresse:
Pour fournir cette position
tant qu'attribut json du champ d'adresse, remplacez simplement la méthode as_json
sur le modèle.
# app/models/profile_fields/address.rb
class ProfileFields::Address < ProfileFields::Base
# ...
def as_json
super.merge {
# ...
position: {
lng: self.longitude,
lat: self.latitude
}
}
end
end