Ruby on Rails
Utilizzo di Google Maps con Rails
Ricerca…
Aggiungi il tag javascript di google maps all'intestazione del layout
Per fare in modo che google maps funzioni correttamente con i turbolinks , aggiungi il tag javascript direttamente all'intestazione del layout piuttosto che includerlo in una vista.
# app/views/layouts/my_layout.html.haml
!!!
%html{:lang => 'en'}
%head
- # ...
= google_maps_api_script_tag
google_maps_api_script_tag
è meglio definito in un helper.
# 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
Puoi registrare la tua domanda con google e ottenere la tua chiave API nella console di Google API . Google ha una breve guida su come richiedere una chiave API per le API di javascript di google maps .
La chiave API è memorizzata nel file secrets.yml
:
# config/secrets.yml
development:
google_maps_api_key: '...'
# ...
production:
google_maps_api_key: '...'
# ...
Non dimenticate di aggiungere config/secrets.yml
al .gitignore
di file e makre sicuri di non commettere la chiave API al repository.
Geocodifica il modello
Supponiamo che i tuoi utenti e / o gruppi abbiano profili e che tu voglia visualizzare i campi del profilo dell'indirizzo su una mappa di 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
Un ottimo modo per geocodificare gli indirizzi, ovvero fornire longitude
e latitude
è la gemma del geocoder .
Aggiungi geocoder al tuo Gemfile
ed esegui bundle
per installarlo.
# Gemfile
gem 'geocoder', '~> 1.3'
Aggiungi colonne di database per latitude
e longitude
per salvare la posizione nel database. Questo è più efficiente dell'interrogazione del servizio di geocoding ogni volta che è necessario il percorso. È più veloce e non stai raggiungendo il limite della query così velocemente.
➜ 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
Aggiungi il meccanismo di geocoding al tuo modello. In questo esempio, la stringa di indirizzo è memorizzata nell'attributo value
. Configura la geocodifica da eseguire quando il record è cambiato e solo se è presente un valore:
# 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
Per impostazione predefinita, geocoder utilizza google come servizio di ricerca. Ha molte caratteristiche interessanti come i calcoli della distanza o la ricerca di prossimità. Per maggiori informazioni, date un'occhiata al README del geocoder .
Mostra indirizzi su una mappa di google nella vista profilo
Nella vista profilo, mostra i campi del profilo di un utente o di un gruppo in un elenco, nonché i campi degli indirizzi su una mappa di google.
- # app/views/profiles/show.html.haml
%h1 Contact Information
.profile_fields
= render @profile_fields
.google_map{data: address_fields: @address_fields.to_json }
I @profile_fields
e @address_fields
appropriati sono impostati nel controller:
# 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
Inizializza la mappa, posiziona i marcatori, imposta lo zoom e altre impostazioni della mappa con javascript.
Imposta i marcatori sulla mappa con javascript
Supponiamo che esista un div .google_map
, che diventerà la mappa e che abbia i campi degli indirizzi da mostrare come marcatori come attributo dei data
.
Per esempio:
<!-- 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>
Per utilizzare l'evento $(document).ready
con i turbolinks senza gestire manualmente gli eventi turbolinks, utilizzare la gem jquery.turbolinks .
Se si desidera eseguire altre operazioni con la mappa, in seguito, ad esempio il filtro o le finestre informative, è conveniente che la mappa sia gestita da una classe di script caffè .
# 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
Quando si utilizzano diversi file di script caffè, che sono assegnati in modo predefinito per nome, è conveniente definire uno spazio dei nomi di App
globale, condiviso da tutti i file di script caffè.
Quindi, passa attraverso (possibilmente diversi) .google_map
div e crea un'istanza della classe App.GoogleMap
per ognuno di essi.
# 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
Inizializza la mappa utilizzando una classe di script caffè.
Fornita una classe di script per caffè App.GoogleMap
, la mappa di google può essere inizializzata in questo modo:
# 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
}
Per ulteriori informazioni sulle possibili opzioni map_configuration
, dai un'occhiata alla documentazione di MapOptions di google e alla loro guida per aggiungere elementi di controllo .
Per riferimento, la classe google.maps.Map
è ampiamente documentata qui .
Inizializza gli indicatori di mappa utilizzando una classe di script caffè
A condizione che una classe di script caffè App.GoogleMap
e le informazioni sul marker vengano memorizzate nell'attributo data-address-fields
del div .google_map
, gli indicatori di mappa possono essere inizializzati sulla mappa in questo modo:
# 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
Per ulteriori informazioni sulle opzioni dei marker, dai un'occhiata alla documentazione di MarkerOptions di google e alla loro guida ai marcatori .
Zoom automatico di una mappa utilizzando una classe di script caffè
Fornita una classe di script per caffè App.GoogleMap
con la google.maps.Map
memorizzata come @map
e i google.maps.Marker
s memorizzati come @markers
, la mappa può essere ingrandita automaticamente, ovvero regolata affinché tutti i marcatori siano visibili, come questo : sulla mappa in questo modo:
# 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
Per saperne di più sui limiti, dai un'occhiata alla documentazione di LatLngBounds di google.
Esporre le proprietà del modello come json
Per visualizzare i campi del profilo dell'indirizzo come marcatori su una mappa di google, gli oggetti del campo indirizzo devono essere passati come oggetti json a javascript.
Attributi regolari del database
Quando si chiama to_json
su un oggetto ApplicationRecord
, gli attributi del database vengono automaticamente esposti.
Dato un modello ProfileFields::Address
con gli attributi label
, value
, longitude
e latitude
, address_field.as_json
risulta in un Hash
, ad es. Rappresentazione,
address_field.as_json # =>
{label: "Work address", value: "Willy-Brandt-Straße 1\n10557 Berlin",
longitude: ..., latitude: ...}
che viene convertito in una stringa json di to_json
:
address_field.to_json # =>
"{\"label\":\"Work address\",\"value\":\"Willy-Brandt-Straße 1\\n
10557 Berlin\",\"longitude\":...,\"latitude\":...}"
Ciò è utile perché consente di utilizzare l' label
e il value
seguito in javascript, ad esempio per mostrare suggerimenti sugli strumenti per gli indicatori di mappa.
Altri attributi
Altri attributi virtuali possono essere esposti sovrascrivendo il metodo as_json
.
Ad esempio, per esporre un attributo title
, as_json
nell'hash unito as_json
:
# 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'esempio precedente utilizza super
per chiamare il metodo as_json
originale, che restituisce l'hash dell'attributo originale dell'oggetto e lo unisce con l'hash della posizione richiesta.
Per capire la differenza tra as_json
e to_json
, dai un'occhiata a questo post sul blog di jjulian .
Posizione
Per rendere i marcatori, l'API di google maps, per impostazione predefinita, richiede un hash di position
che ha la longitudine e la latitudine memorizzate come lng
e lat
rispettivamente.
Questo hash di posizione può essere creato in javascript, in seguito, o qui quando si definisce la rappresentazione json del campo dell'indirizzo:
Per fornire questa position
come attributo json del campo dell'indirizzo, basta sovrascrivere il metodo as_json
sul modello.
# 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