Ruby on Rails
Usando GoogleMaps con Rails
Buscar..
Agregue la etiqueta javascript de google maps al encabezado de diseño
Para que Google Maps funcione correctamente con turbolinks , agregue la etiqueta javascript directamente al encabezado del diseño en lugar de incluirla en una vista.
# app/views/layouts/my_layout.html.haml
!!!
%html{:lang => 'en'}
%head
- # ...
= google_maps_api_script_tag
El google_maps_api_script_tag
se define mejor en un ayudante.
# 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
Puede registrar su aplicación en google y obtener su clave de api en la consola de google api . Google tiene una breve guía sobre cómo solicitar una clave de API para la API de Google Maps javascript .
La clave api se almacena en el archivo secrets.yml
:
# config/secrets.yml
development:
google_maps_api_key: '...'
# ...
production:
google_maps_api_key: '...'
# ...
No se olvide de añadir config/secrets.yml
a su .gitignore
archivo y makre asegurarse de que no se compromete la clave de API en el repositorio.
Geocodificar el modelo
Supongamos que sus usuarios y / o grupos tienen perfiles y desea mostrar los campos de perfil de dirección en un mapa de 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
Una gran manera de geocodificar las direcciones, es decir, proporcionar longitude
y latitude
es la gema del geocodificador .
Agregue geocodificador a su Gemfile
y ejecute bundle
para instalarlo.
# Gemfile
gem 'geocoder', '~> 1.3'
Agregue columnas de la base de datos para latitude
y longitude
para guardar la ubicación en la base de datos. Esto es más eficiente que consultar el servicio de geocodificación cada vez que necesita la ubicación. Es más rápido y no alcanzas el límite de consulta tan rápido.
➜ 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
Agregue el mecanismo de geocodificación a su modelo. En este ejemplo, la cadena de dirección se almacena en el atributo de value
. Configure la geocodificación para que se realice cuando el registro haya cambiado, y solo cuando esté presente un valor:
# 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
Por defecto, el geocodificador usa google como servicio de búsqueda. Tiene muchas características interesantes como cálculos de distancia o búsqueda de proximidad. Para más información, eche un vistazo al README del geocodificador .
Mostrar direcciones en un mapa de Google en la vista de perfil
En la vista de perfil, muestre los campos de perfil de un usuario o grupo en una lista, así como los campos de dirección en un mapa de Google.
- # app/views/profiles/show.html.haml
%h1 Contact Information
.profile_fields
= render @profile_fields
.google_map{data: address_fields: @address_fields.to_json }
Los @profile_fields
y @address_fields
apropiados se configuran en el controlador:
# 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
Inicialice el mapa, coloque los marcadores, configure el zoom y otras configuraciones del mapa con javascript.
Establecer los marcadores en el mapa con javascript
Supongamos que hay un div .google_map
, que se convertirá en el mapa, y que tiene los campos de dirección para mostrar como marcadores como atributo de data
.
Por ejemplo:
<!-- 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>
Para utilizar el evento $(document).ready
con turbolinks sin gestionar los eventos turbolinks a mano, use la gema jquery.turbolinks .
Si desea realizar otras operaciones con el mapa, más adelante, por ejemplo, mediante el filtrado o las ventanas de información, es conveniente que el mapa sea administrado por una clase de script de 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
Cuando se usan varios archivos de script de café, que tienen espacios de nombre por defecto, es conveniente definir un espacio de nombres de App
global, que sea compartido por todos los archivos de script de café.
Luego, .google_map
(posiblemente varios) divs .google_map
y cree una instancia de la clase App.GoogleMap
para cada uno de ellos.
# 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
Inicialice el mapa usando una clase de script de café.
Siempre que haya una clase de script de café App.GoogleMap
, el mapa de google se puede inicializar así:
# 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
}
Para aprender más acerca de los posibles map_configuration
opciones, echar un vistazo a Google de documentación MapOptions y su guía a la adición de elementos de control .
Para referencia, la clase google.maps.Map
está ampliamente documentada aquí .
Inicialice los marcadores de mapa usando una clase de script de café
Si se proporciona una clase de script de café App.GoogleMap
y la información del marcador se almacena en el atributo data-address-fields
de la div .google_map
, los marcadores del mapa se pueden inicializar en el mapa de la siguiente manera:
# 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
Para obtener más información sobre las opciones de marcadores, consulte la documentación de MarkerOptions y su guía de marcadores .
Zoom automático de un mapa usando una clase de script de café
Se proporcionó una clase de script de café App.GoogleMap
con google.maps.Map
almacenado como @map
y google.maps.Marker
s almacenado como @markers
, el mapa puede @markers
automáticamente, es decir, ajustado para que todos los marcadores sean visibles, de esta manera : en el mapa como este:
# 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
Para obtener más información sobre los límites, eche un vistazo a la documentación de LatLngBounds de Google .
Exponiendo las propiedades del modelo como json.
Para mostrar los campos de perfil de dirección como marcadores en un mapa de Google, los objetos del campo de dirección deben pasarse como objetos json a javascript.
Atributos regulares de la base de datos
Al llamar a to_json
en un objeto ApplicationRecord
, los atributos de la base de datos se exponen automáticamente.
Dado un ProfileFields::Address
Modelo de ProfileFields::Address
con atributos de label
, value
, longitude
y latitude
, address_field.as_json
da address_field.as_json
resultado un Hash
, por ejemplo, representación,
address_field.as_json # =>
{label: "Work address", value: "Willy-Brandt-Straße 1\n10557 Berlin",
longitude: ..., latitude: ...}
que se convierte en una cadena json por to_json
:
address_field.to_json # =>
"{\"label\":\"Work address\",\"value\":\"Willy-Brandt-Straße 1\\n
10557 Berlin\",\"longitude\":...,\"latitude\":...}"
Esto es útil porque permite usar label
y value
más adelante en javascript, por ejemplo, para mostrar sugerencias de herramientas para los marcadores de mapa.
Otros atributos
Otros atributos virtuales se pueden exponer anulando el método as_json
.
Por ejemplo, para exponer un atributo de title
, as_json
hash as_json
combinado:
# 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
El ejemplo anterior usa super
para llamar al método as_json
original, que devuelve el hash del atributo original del objeto, y lo combina con el hash de posición requerido.
Para comprender la diferencia entre as_json
y to_json
, to_json
un vistazo a esta publicación del blog de jjulian .
Posición
Para representar marcadores, la API de google maps, de forma predeterminada, requiere un hash de position
que tiene la longitud y la latitud almacenadas como lng
y lat
respectivamente.
Este hash de posición se puede crear en javascript, más tarde, o aquí cuando se define la representación json del campo de dirección:
Para proporcionar esta position
como atributo json del campo de dirección, simplemente anule el método as_json
en el modelo.
# 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