Marcadores, posicionar una imagen en el mapa
El uso más común del mapa es visualizar la posición geográfica de algo, los marcadores son los ideales para lograrlo. Un marcador es básicamente una pequeña imagen posicionada en un lugar específico del mapa.
Área de trabajo
Usaremos el mismo área de trabajo del primer capítulo (Google Maps API V3 introducción y primeros pasos):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>test</title> <style> *{ margin: 0; padding: 0; } html, body, #map{ width: 100%; height: 100%; } </style> <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&language=es"></script> <script type="text/javascript" src="js/map.js"></script> </head> <body> <div id="map"></div> </body> </html>
Dentro del archivo map.js
, es donde estaremos trabajando el resto de los ejemplos.
Crear marcador
Para crear un marcador, necesitas usar el objeto google.maps.Marker
. Este toma un sólo argumento y es google.maps.MarkerOptions. MarkerOptions
tiene varias propiedades que puedes usar para hacer que el marcador se vea y comporte de diversas formas. Solo dos propiedades son requisitos:
position
: Define las coordenadas donde el marcador va a estar posicionado. Toma las coordenadas usando el métodogoogle.maps.LatLng
.map
: Es una referencia al mapa donde quieres añadir el marcador.
Estos son algunas de las opciones que se pueden usar en el marcador:
window.onload = function(){ var options = { zoom: 9 , center: new google.maps.LatLng(18.2, -66.5) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); new google.maps.Marker({ position: map.getCenter() , map: map , title: 'Pulsa aquí' , icon: 'http://gmaps-samples.googlecode.com/svn/trunk/markers/green/blank.png' , cursor: 'default' , draggable: true }); };
Notas:
- Si deseamos que el marcador no se pueda seleccionar podemos inhabilitarlo usando
clickable
y como valorfalse
. No podemos habilitardraggable
porque eso sobre-escribiría el valor establecido en elclickable
, ya que para poder mover el marcador se requiere que se pueda seleccionar. - En la dirección
http://gmaps-samples.googlecode.com/svn/trunk/markers/{color}/marker{n}.png
puedes obtener diferentes tipos de marcadores, donde{color}
puede ser:blue
,green
,orange
,pink
yred
.{n}
puede ser un número entre el 1 y el 99 y para los que no se requiere número se puede usarblank.png
, así como en el ejemplo. Más marcadores en http://code.google.com/p/google-maps-icons/wiki/AllUpdates.
InfoWindow
Para añadir la burbuja que contiene información acerca del marcador solo tienes que hacer referencia a la clase google.maps.InfoWindow
y esta toma un solo argumento que es google.maps.InfoWindowOptions. InfoWindowOptions
tiene varias propiedades, pero la más importante es content
. En esa propiedad podemos añadir cualquier tipo de datos que queramos, como por ejemplo: texto plano, HTML, video y puedes modificar el contenido usando CSS.
Para poder visualizar la burbuja se requiere usar el método open
que está disponible en la clase google.maps.InfoWindow
. Este método tiene dos argumento y el primero es requisito:
map
: Es una referencia al objeto que contiene el mapa que se va a usar (la razón es que puede que tengamos varios mapas en la misma página) oStreetViewPanorama
- El objeto en donde se quiere añadir la burbuja. Esta es opcional si se menciona la propiedad
position
en elInfoWindowOptions
.
window.onload = function(){ var options = { zoom: 9 , center: new google.maps.LatLng(18.2, -66.5) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var marker = new google.maps.Marker({ position: map.getCenter() , map: map , title: 'Pulsa aquí' , icon: 'http://gmaps-samples.googlecode.com/svn/trunk/markers/blue/blank.png' }); var popup = new google.maps.InfoWindow({ content: 'Wohoooo, salió el InfoWindow, pero ¿por qué sale exactamente en el punto del marcador?' , position: map.getCenter() }); popup.open(map); };
Si prestamos atención, vemos que la posición de la burbuja es exactamente donde está ubicado el marcador, pero la realidad es que deseamos que la burbuja aparezca encima del marcador. Una forma de lograrlo es creando una nueva instancia de google.maps.LatLng
y asignando valores que logre colocar la burbuja encima del marcador. Otra forma y la más sencilla es asignando el segundo argumento del método de open
e indicar que tome como base el marcador.
window.onload = function(){ var options = { zoom: 9 , center: new google.maps.LatLng(18.2, -66.5) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var marker = new google.maps.Marker({ position: map.getCenter() , map: map , title: 'Pulsa aquí' , icon: 'http://gmaps-samples.googlecode.com/svn/trunk/markers/pink/blank.png' }); var popup = new google.maps.InfoWindow({ content: 'Wohoooo, salió el InfoWindow y encima del marcador, ahora ¿qué más puedo hacer con esto?' }); popup.open(map, marker); };
Si queremos visualizar la ventana usando algún evento (cuando se pulse el ratón, o cuando presionemos alguna tecla) se puede usar el método google.maps.event.addListener()
. Requiere de tres argumentos:
- El objeto a añadir
- El evento que debe escuchar
- Una función que se ejecutará cuando ocurra el evento. Esta función se le conoce como gestión de eventos (event handler en ingles)
window.onload = function(){ var options = { zoom: 9 , center: new google.maps.LatLng(18.2, -66.5) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var marker = new google.maps.Marker({ position: map.getCenter() , map: map , title: 'Pulsa aquí' , icon: 'http://gmaps-samples.googlecode.com/svn/trunk/markers/orange/blank.png' }); var popup = new google.maps.InfoWindow({ content: 'Wohoooo, salió el InfoWindow cuando pulsé el marcador, pero ¿hay más?' }); google.maps.event.addListener(marker, 'click', function(){ popup.open(map, marker); }); };
Para una lista de los eventos, pueden verlo en la clase google.maps.Map en la categoría que dice Events.
Añadir varios marcadores
Podemos añadir varios marcadores de la misma forma como hemos añadido uno. Se puede hacer ingresándolo uno a uno o en forma más dinámica usando arrays.
window.onload = function(){ var n=1; var options = { zoom: 9 , center: new google.maps.LatLng(18.2, -66.5) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var place = new Array(); place['San Juan'] = new google.maps.LatLng(18.465, -66.105); place['Fajardo'] = new google.maps.LatLng(18.336, -65.65); for(var i in place){ var marker = new google.maps.Marker({ position: place[i] , map: map , title: i , icon: 'http://gmaps-samples.googlecode.com/svn/trunk/markers/red/marker' + n++ + '.png' }); google.maps.event.addListener(marker, 'click', function(){ var popup = new google.maps.InfoWindow(); var note = 'Wohoooo, salió el InfoWindow cuando pulsé el marcador y en el lugar: ' + i + ', pero ¿por qué todos se muestran en ' + i + '?'; popup.setContent(note); popup.open(map, marker); }) } };
Siempre que seleccionamos cualquier marcador nos dará el último creado en el array. La razón es que estamos usando como referencia la variable y no el valor, y esto siempre nos dará el último valor indicado en el array. Hay varias formas de corregirlo. Una de las formas más sencilla es hacer referencia al objeto corriente usando la palabra clave this
:
window.onload = function(){ var n=1; var options = { zoom: 9 , center: new google.maps.LatLng(18.2, -66.5) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var place = new Array(); place['San Juan'] = new google.maps.LatLng(18.465, -66.105); place['Fajardo'] = new google.maps.LatLng(18.336, -65.65); for(var i in place){ var marker = new google.maps.Marker({ position: place[i] , map: map , title: i , icon: 'http://gmaps-samples.googlecode.com/svn/trunk/markers/red/marker' + n++ + '.png' }); google.maps.event.addListener(marker, 'click', function(){ var popup = new google.maps.InfoWindow(); var note = 'Wohoooo, salió el InfoWindow cuando pulsé el marcador y en el lugar: ' + this.title + ', por fin se arreglo, pero ¿por qué salen varias burbujas?'; popup.setContent(note); popup.open(map, this); }); } };
Otra situación que se presenta es que si seleccionas una burbuja y luego otra, tanto la primera como la segunda se quedan abiertas. Para la versión 2 automáticamente se cerraban. En la versión 3 ya no hay esa limitación. Esto puede representar una ventaja y desventaja. Entre las ventajas es posible ahora comparar datos en ambas burbujas y verlo a la misma vez.
Entre las desventajas es que tenemos más clicks y puede que se vuelva problemático. Una forma de evitar que salgan varias burbujas es, declarar la variable que guarda la instancia de InfoWindow
fuera del bucle y luego en la función del evento verificar si existe ya una instancia, si no es así entonces crearla, si es así usar la que ya existe.
window.onload = function(){ var popup; var n=1; var options = { zoom: 9 , center: new google.maps.LatLng(18.2, -66.5) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var place = new Array(); place['San Juan'] = new google.maps.LatLng(18.465, -66.105); place['Fajardo'] = new google.maps.LatLng(18.336, -65.65); for(var i in place){ var marker = new google.maps.Marker({ position: place[i] , map: map , title: i , icon: 'http://gmaps-samples.googlecode.com/svn/trunk/markers/orange/marker' + n++ + '.png' }); google.maps.event.addListener(marker, 'click', function(){ if(!popup){ popup = new google.maps.InfoWindow(); } var note = 'Wohoooo, salió el InfoWindow cuando pulsé el marcador, en el lugar: ' + this.title + ' y no se repiten las burbujas, ufff, ya estoy fatigado de emocionarme...'; popup.setContent(note); popup.open(map, this); }) } };
Ajustar automáticamente la ventana para adaptarse a todos los marcadores
A veces sabemos cuantos marcadores va haber en el mapa, pero cuando empezamos a crear aplicaciones que toman valores automáticos, es bueno dejar que la aplicación se encargue de establecer los límites. Con la clase google.maps.LatLngBounds
es posible lograrlo.
LatLngBounds
establece los límites en forma rectangular y se puede pasar dos argumentos (opcionales) que son las coordenadas suroccidental
y nororiental
expresadas con la clase google.maps.LatLng()
. El primer argumento debe ser el marcador que se encuentre más a la izquierda y el segundo el que esté más a la derecha. Luego con el método fitBounds
de la clase map
reajustamos el mapa.
window.onload = function(){ var popup; var n=1; var options = { zoom: 3 , center: new google.maps.LatLng(18.2, -66.5) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var place = new Array(); place['San Juan'] = new google.maps.LatLng(18.465, -66.105); place['Fajardo'] = new google.maps.LatLng(18.336, -65.65); marker = new google.maps.Marker({ position: place['San Juan'] , map: map , title: 'San Juan' , icon: 'http://gmaps-samples.googlecode.com/svn/trunk/markers/red/blank.png' }); var marker = new google.maps.Marker({ position: place['Fajardo'] , map: map , title: 'Fajardo' , icon: 'http://gmaps-samples.googlecode.com/svn/trunk/markers/blue/blank.png' }); var limits = new google.maps.LatLngBounds( place['San Juan'] , place['Fajardo'] ); map.fitBounds(limits); };
Pero ¿cómo podemos hacer que lo haga automático? La clase LatLngBounds
tiene un método llamado extend
que nos ayudará en lo que queremos hacer. Este toma un solo argumento y es google.maps.LatLng()
del marcador en curso. Primero instanciamos la clase LatLngBounds
fuera del bucle y dentro del bucle usamos el método extend
, al final, fuera del bucle, llamamos el método fitBounds
.
window.onload = function(){ var popup; var n=1; var options = { zoom: 3 , center: new google.maps.LatLng(18.2, -66.5) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var limits = new google.maps.LatLngBounds(); var place = new Array(); place['San Juan'] = new google.maps.LatLng(18.465, -66.105); place['Fajardo'] = new google.maps.LatLng(18.336, -65.65); place['Culebras'] = new google.maps.LatLng(18.315, -65.3); for(var i in place){ var marker = new google.maps.Marker({ position: place[i] , map: map , title: i , icon: 'http://gmaps-samples.googlecode.com/svn/trunk/markers/red/marker' + n++ + '.png' }); limits.extend(place[i]); } map.fitBounds(limits); };
Personalizar el marcador con MarkerImage
google.maps.MarkerImage es una clase que contiene información acerca de la imagen y/o sombra a usar en el marcador. Tiene cinco argumentos, pero solo el primero es requisito.
url
: La dirección apuntando a la imagensize
: El tamaño de la imagenorigin
: La parte de la imagen a usar. Debe usarse con la clasegoogle.maps.Point
. Esto se usa mucho con la técnica llamada spritesanchor
: Establece la parte de la imagen que va a usar para apuntar la localización en el mapa. Si no se establece, toma como base la parte del medio-abajoscaledSize
: Permite mostrar la imagen más pequeña o más grande al tamaño original. Si se usa esta propiedad se debe ajustar elanchor
al tamaño establecida de la imagen
Se puede definir qué áreas del marcador son clickeables usando shape
en el objeto MarkerOptions. Si no se usa esta propiedad toda la imagen es clickeable. Esta propiedad tiene como valor un objeto literal con dos propiedades: type
que define cual es la figura (rect
, circ
, poly
) a usar y coord
que establece las coordenadas a usar pero dependiendo el tipo de figura que se haya seleccionado.
rect
: requiere dos valores que son las esquinas de: arriba-izquierda, abajo-derechacirc
: requiere de tres valores que son:x
,y
y elradio
poly
: consiste de varios puntosx
,y
en forma lineal y como los poligonos son figuras cerradas, el último punto automaticamente se conecta con el primero
Notas:
- Cuando no se requiera usar algún argumento opcional, solo debes añadir
null
hasta llegar al argumento deseado - Es buena programación indicar en los argumentos opcionales los valores, para que así el navegador no necesite hacer cálculos innecesarios, como por ejemplo indicar el
size
- ¿Por qué no usar una imagen que contenga la sombra directamente? Para tener más flexibilidad. Podriamos usar una imagen y darle brillo conforme al evento que esté ocurriendo al momento, como por ejemplo
hover
. Veremos luego, como podemos cambiar la imagen conforme al evento que ocurra. También podríamos inhablitar la sombra para mostrar mejor algún detalle en el mapa - Para inhabilitar la sombra del marcador, solo se tiene que declarar como valor
true
la propiedadflat
en el objeto MarkerOptions de la clasegoogle.maps.Marker
. Por defecto tiene como valorfalse
Hemos tomado el logo de este sitio web, lo cambiamos a forma vertical y lo convertimos a una imagen png con transparencia, luego visitamos http://www.powerhut.co.uk/googlemaps/custom_markers.php para obtener la sombra y el shape
con sus coordenadas a usar en el marcador de una forma sencilla. También crea la imagen usando la técnica de sprites
. En este ejemplo usaremos la técnica de sprites
:
window.onload = function(){ var popup; var n=1; var options = { zoom: 3 , center: new google.maps.LatLng(18.2, -66.5) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var limits = new google.maps.LatLngBounds(); var place = new Array(); place['San Juan'] = new google.maps.LatLng(18.465, -66.105); place['Fajardo'] = new google.maps.LatLng(18.336, -65.65); place['Culebras'] = new google.maps.LatLng(18.315, -65.3); place['Vieques'] = new google.maps.LatLng(18.125, -65.44); place['Humacao'] = new google.maps.LatLng(18.14, -65.88); var image = new google.maps.MarkerImage( 'http://www.maestrosdelweb.com/images/2011/04/sprite.png' , new google.maps.Size(30,43) , new google.maps.Point(0,0) , new google.maps.Point(15,43) ); var shadow = new google.maps.MarkerImage( 'http://www.maestrosdelweb.com/images/2011/04/sprite.png' , new google.maps.Size(56,43) , new google.maps.Point(30,0) , new google.maps.Point(15,43) ); var shape = { coord: [ 2,0,3,1,5,2,7,3,8,4,10,5,11,6,29,7,29,8,29,9,29,10,29,11,29,12, 29,13,29,14,28,15,27,16,26,17,25,18,23,19,21,20,20,21,26,22,26, 23,26,24,26,25,26,26,26,27,25,28,24,29,23,30,21,31,20,32,18,33, 17,34,15,35,15,36,15,37,15,38,15,39,15,40,15,41,15,42,14,42,14, 41,13,40,12,39,10,38,8,37,7,36,6,35,5,34,4,33,4,32,4,31,4,30,4, 29,5,28,10,27,8,26,7,25,5,24,4,23,3,22,3,21,3,20,3,19,3,18,3,17, 3,16,11,15,9,14,8,13,6,12,4,11,3,10,2,9,1,8,0,7,0,6,0,5,0,4,0,3, 0,2,0,1,0,0,2,0 ] , type: 'poly' }; for(var i in place){ var marker = new google.maps.Marker({ position: place[i] , map: map , title: i , icon: image , shadow: shadow , shape: shape }); limits.extend(place[i]); } map.fitBounds(limits); };
Cambiar imagen conforme a un evento
La clase google.maps.Marker
tiene unos getters/setters que nos ayudará para poder modificar la imagen conforme al evento que ocurre. Solo necesitamos indicar el evento usando google.maps.event.addListener
y en el tercer argumento indicamos que queremos modificar la imagen del objeto en uso, con el método setIcon
de la clase google.maps.Marker
.
window.onload = function(){ var popup; var n=1; var options = { zoom: 3 , center: new google.maps.LatLng(18.2, -66.5) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var limits = new google.maps.LatLngBounds(); var place = new Array(); place['San Juan'] = new google.maps.LatLng(18.465, -66.105); place['Fajardo'] = new google.maps.LatLng(18.336, -65.65); place['Culebras'] = new google.maps.LatLng(18.315, -65.3); place['Vieques'] = new google.maps.LatLng(18.125, -65.44); place['Humacao'] = new google.maps.LatLng(18.14, -65.88); place['Ponce'] = new google.maps.LatLng(18.025, -66.615); place['Mayagüez'] = new google.maps.LatLng(18.215, -67.14); for(var i in place){ var marker = new google.maps.Marker({ position: place[i] , map: map , title: i , icon: 'http://gmaps-samples.googlecode.com/svn/trunk/markers/blue/blank.png' }); google.maps.event.addListener(marker, 'mouseover', function(){ this.setIcon('http://gmaps-samples.googlecode.com/svn/trunk/markers/green/blank.png'); }); google.maps.event.addListener(marker, 'mouseout', function(){ this.setIcon('http://gmaps-samples.googlecode.com/svn/trunk/markers/blue/blank.png'); }); google.maps.event.addListener(marker, 'mousedown', function(){ this.setIcon('http://gmaps-samples.googlecode.com/svn/trunk/markers/red/blank.png'); }); google.maps.event.addListener(marker, 'mouseup', function(){ this.setIcon('http://gmaps-samples.googlecode.com/svn/trunk/markers/green/blank.png'); }); limits.extend(place[i]); } map.fitBounds(limits); };
Como crítica constructiva sugiero se agrege el ejemplo, para ver el resultado en caliente de lo que se va desarrollando, pues así como lo muestran no hay ningún incentivo o ayuda visual.
Interesante, ¿no le salen las imagenes? Porque por cada uno de los códigos mencionados, colocamos una imagen debajo.
Están las imágenes, aunque imagina de poner el iframe respectivo de cada google map. Aunque si llegamos a incluir todos los ejemplos esta página va a estar bastante sobrecargada. Algunos navegadores van a odiarnos 😛
Excelente trabajo, Abimael. Sólo resta decir que con un poco de javascript e imaginación a esto se le puede sacar mucho provecho. Este juego: http://www.tomscott.com/realworldracer/, por ejemplo, utiliza las cosas mencionadas en este artículo y, aunque está basado an la api versión 2, tras la lectura de tu magnífico artículo poco costará adaptarlo a esta nueva versión.
Gracias Andrés, muy bueno el juego 😉
Abimael Excelente tu explicacion
k mierda de enlace jjaa
solo digo que muy buen post excelente diría!!!!
fantastico post!!!
me ha ayudado mucho
GRACIAS!!!!
hasta que encuentro el tema bien detallado, a la fecha tenía que conformarme con el marcador que viene por defecto. buen aporte.
Excelente post ahora tengo el conocimiento requerido para agregar esta funcionalidad a mis webs !!
Gracias
Hola, felicidades por el post, muy bueno.
He estado migrando desde la api V2 a la V3 con tu tutorial y me ha ido de lujo, pero ahora estoy haciendo cambios en la web y me gustaria poder cambiar de marcador (latitud, longitud, infoWindow diferente, etc) en tiempo de ejecución pero si vuelvo a usar la función initialize, me cambia a otra latitud y longitud diferente de las que le pongo, aunque la infoWindow me aparezca como quiero. ¿Sabrias decirme la forma de hacerlo?
Muchas Gracias.
Muy buen post, use google maps en la v. 1 y ahora estoy tratando de actualizarme y todo este texto me ha ayudado bastante espero sigas posteando