Á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&amp;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étodo google.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 valor false. No podemos habilitar draggable porque eso sobre-escribiría el valor establecido en el clickable, 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 y red. {n} puede ser un número entre el 1 y el 99 y para los que no se requiere número se puede usar blank.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) o StreetViewPanorama
  • El objeto en donde se quiere añadir la burbuja. Esta es opcional si se menciona la propiedad position en el InfoWindowOptions.
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:

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 imagen
  • size: El tamaño de la imagen
  • origin: La parte de la imagen a usar. Debe usarse con la clase google.maps.Point. Esto se usa mucho con la técnica llamada sprites
  • anchor: 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-abajo
  • scaledSize: Permite mostrar la imagen más pequeña o más grande al tamaño original. Si se usa esta propiedad se debe ajustar el anchor 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-derecha
  • circ: requiere de tres valores que son: x,y y el radio
  • poly: consiste de varios puntos x,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 propiedad flat en el objeto MarkerOptions de la clase google.maps.Marker. Por defecto tiene como valor false

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);
};