Introducción a AJAX

9.3. Google Maps

9.3.1. Contexto

Google Maps fue una de las primeras aplicaciones basadas en AJAX de uso masivo por parte de los usuarios. Su gran éxito ha provocado que todos sus rivales hayan copiado el funcionamiento de sus mapas.

Además, Google ofrece de forma gratuita una API con la que poder desarrollar aplicaciones a medida basadas en los mapas de Google, integrar los mapas en otras aplicaciones e incluso hacer "mash-up" o mezclas de Google Maps y otras aplicaciones web que también disponen de una API pública.

9.3.2. Solución propuesta

Antes de utilizar la API de los mapas de Google, es necesario obtener una clave personal y única para cada sitio web donde se quiere utilizar. El uso de la API es gratuito para cualquier aplicación que pueda ser accedida libremente por los usuarios. La clave de la API se puede obtener desde: http://www.google.com/apis/maps/

Para usos comerciales de la API también existen servicios de pago que requieren el uso de otras claves.

Las claves se solicitan por cada ruta del servidor. De esta forma, si se solicita una clave para http://www.misitio.com/ruta1, cualquier aplicación o página que se encuentre bajo esa ruta del servidor podrá hacer uso de la API de los mapas con esa clave.

Si no se dispone de un sitio web público, es posible indicar como servidor el valor http://localhost para poder hacer pruebas en un servidor local. Para solicitar la clave, es necesario disponer de una cuenta de usuario de Google (se puede utilizar por ejemplo la cuenta de Gmail). La clave generada depende por tanto del dominio de la aplicación y de la cuenta de usuario.

Las claves de la API de Google Maps consisten en una cadena de texto muy larga con un aspecto similar al siguiente: ABQIAAAA30JtKUU8se-7KKPRGSfCMBT2yXp_ZAY8_ufC3CFXhHIE1NvwkxRZNdns2BwZvEY-V68DvlyUYwi1-Q.

Una vez obtenida la clave, cualquier página que quiera hacer uso de la API debe enlazar el siguiente archivo de JavaScript:

<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;hl=es&amp;key=ABQIAAAA30JtKUU8se-7KKPRGSfCMBT2yXp_ZAY8_ufC3CFXhHIE1NvwkxRZNdns2BwZvEY-V68DvlyUYwi1-Q" type="text/javascript"></script>

Los parámetros necesarios son file, que indica el tipo de archivo que se quiere cargar (en este caso la API), v, que indica la versión de la API (en este caso 2), hl, que permite indicar el idioma en el que se muestran los mapas (si no se indica este parámetro, los mapas se muestran en inglés) y el parámetro key, que contiene la clave que se acaba de obtener.

Una vez obtenida la clave, es muy sencillo crear el primer mapa de Google:

<!DOCTYPE HTML PUBLIC " *W3C*DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd">
<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Ejemplo de uso de Google Maps</title>
<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;hl=es&amp;key=ABQIAAAA30JtKUU8se-7KKPRGSfCMBT2yXp_ZAY8_ufC3CFXhHIE1NvwkxRZNdns2BwZvEY-V68DvlyUYwi1-Q" type="text/javascript"></script>
<script type="text/javascript">
function load() {
  if (GBrowserIsCompatible()) {
    var latitud = 48.858729;
    var longitud = 2.352448;
    var zoom = 15;
    var mapa = new GMap2(document.getElementById("mapa"));
    mapa.setCenter(new GLatLng(latitud, longitud), zoom);
  }
}
</script>
</head>

<body onload="load()" onunload="GUnload()">
  <div id="mapa" style="width: 500px; height: 400px"></div>
</body>
</html>

El anterior código HTML y JavaScript genera el siguiente mapa:

Mapa sencillo creado con la API de Google Maps y las coordenadas de longitud y latitud indicadas

Figura 9.5 Mapa sencillo creado con la API de Google Maps y las coordenadas de longitud y latitud indicadas

En primer lugar, se define un elemento (normalmente un <div>) en el que se mostrará el mapa. El tamaño del mapa será igual al tamaño de su elemento contenedor, a no ser que se indique explícitamente otro tamaño:

<div id="mapa" style="width: 500px; height: 400px"></div>

Una vez definido el contenedor del mapa, se establecen los eventos necesarios en la página que lo contiene:

<body onload="load()" onunload="GUnload()">

Los ejemplos que se pueden consultar en la documentación de Google utilizan esa asignación directa de eventos en la etiqueta <body>. Evidentemente, los eventos también se podrían asignar de forma semántica mediante el siguiente código:

window.onload = load;
window.onunload = GUnload;

La función load() es la que se ejecuta al cargar la página y la que establece las opciones con las que se crea el mapa. En primer lugar, se ejecuta la función GBrowserIsCompatible(), que indica si el navegador actual del usuario es compatible con el uso de los mapas de Google. En caso contrario, no se ejecutan las instrucciones y no se muestra el mapa:

if (GBrowserIsCompatible()) {
  ...
}

Para crear un nuevo mapa, se utiliza la clase GMap2, que representa un mapa en una página. Las páginas pueden contener más de un mapa, pero cada uno de ellos hace referencia a una instancia diferente de la clase GMap2. El único argumento obligatorio para crear el mapa es la referencia al elemento que contendrá el mapa (obtenida mediante su id):

var mapa = new GMap2(document.getElementById("mapa"));

Una vez instanciada la clase GMap2, el mapa ya ha sido creado. Sin embargo, el mapa no se muestra correctamente hasta que se indique en que punto geográfico está centrado. El método setCenter() permite centrar un mapa y opcionalmente, indicar el nivel de zoom y el tipo de mapa que se muestra:

var latitud = 42.845007;
var longitud = -2.673;
var zoom = 15;
mapa.setCenter(new GLatLng(latitud, longitud), zoom);

El punto geográfico en el que está centrado el mapa se indica mediante un objeto de tipo GLatLng() que toma dos parámetros: el primero es el valor de la latitud del punto geográfico y el otro parámetro indica la longitud de esa posición geográfica. De forma opcional se puede indicar el nivel de zoom del mapa.

La latitud puede tomar un valor entre +90 (90 grados al norte del ecuador) y -90 (90 grados al sur del ecuador), la longitud puede tomar un valor entre +180 (180 grados al este de Greenwitch) y -180 (180 grados al oeste de Greenwitch). El nivel de zoom puede variar entre 1 (en el que se ve la Tierra entera) y 18 (en el que se ven los detalles de cada calle). No obstante, se debe tener en cuenta que no todos los puntos geográficos disponen de todos los niveles de zoom.

Después de crear un mapa básico, es muy sencillo añadir los controles para aumentar o disminuir el nivel de zoom y el control que indica el tipo de mapa que se muestra:

mapa.addControl(new GSmallMapControl());
mapa.addControl(new GMapTypeControl());

Ahora, el mapa permite variar el nivel de zoom y mostrar otro tipo de mapa, como se muestra en la siguiente imagen:

Mapa sencillo creado con la API de Google Maps y que permite variar su nivel de zoom y el tipo de mapa que se muestra

Figura 9.6 Mapa sencillo creado con la API de Google Maps y que permite variar su nivel de zoom y el tipo de mapa que se muestra

Google Maps incluye una documentación muy extensa sobre el uso de la API y todos los métodos disponibles. La documentación inicial con ejemplos básicos se puede consultar en: http://www.google.com/apis/maps/documentation/

La referencia completa de clases, propiedades y métodos de la API está disponible en: http://www.google.com/apis/maps/documentation/reference.html

Los mapas de Google permiten controlar un gran número de eventos, pudiendo asociar funciones o ejecutar directamente código JavaScript cada vez que se produce un evento. El siguiente ejemplo muestra un mensaje de tipo alert() con las coordenadas del centro del mapa cada vez que el usuario suelta el mapa después de haberlo movido:

GEvent.addListener(mapa, "moveend", function() {
  var centro = mapa.getCenter();
  alert(centro.toString());
});

Además del evento moveend existen muchos otros, como por ejemplo move que permite ejecutar cualquier función de forma repetida a medida que el usuario mueve el mapa. Los mapas también permiten colocar marcadores para mostrar una posición. El siguiente ejemplo hace uso del evento click sobre los mapas para mostrar marcadores:

GEvent.addListener(mapa, "click", function(marcador, punto) {
  mapa.addOverlay(new GMarker(punto));
});

Ahora, cada vez que se pulsa sobre un punto del mapa, se añade un nuevo marcador en ese punto:

Los eventos de la API de los mapas de Google permiten añadir marcadores de posición cada vez que se pincha en un punto del mapa

Figura 9.7 Los eventos de la API de los mapas de Google permiten añadir marcadores de posición cada vez que se pincha en un punto del mapa

Modificando ligeramente el ejemplo anterior, es posible borrar un marcador si se pulsa sobre el:

GEvent.addListener(mapa, "click", function(marcador, punto) {
  if(marcador) {
    mapa.removeOverlay(marcador);
  } else {
    mapa.addOverlay(new GMarker(punto));
  }
});

Modificando de nuevo el código anterior, es posible añadir eventos a los marcadores para que cada vez que se pinche en uno de ellos se muestre un mensaje con sus coordenadas geográficas:

GEvent.addListener(mapa, "click", function(marcador, punto) {
  var nuevoMarcador = new GMarker(punto);
  GEvent.addListener(nuevoMarcador, "click", function() {
    this.openInfoWindowHtml("Lat: " + this.getPoint().lat() + "<br/>Lon: " + this.getPoint().lng());
  });
  mapa.addOverlay(nuevoMarcador);
});
Al pulsar sobre cada marcador de posición en el mapa, se muestra un mensaje informando sobre la longitud y latitud de la posición del marcador

Figura 9.8 Al pulsar sobre cada marcador de posición en el mapa, se muestra un mensaje informando sobre la longitud y latitud de la posición del marcador

A continuación se muestra la explicación detallada del código anterior. En primer lugar se establece un nuevo evento cada vez que se pincha sobre el mapa:

GEvent.addListener(mapa, "click", function(marcador, punto) {

Cada vez que se pincha, se crea un nuevo marcador con las coordenadas de la posición en la que se ha pinchado:

var nuevoMarcador = new GMarker(punto);

Antes de mostrar el marcador en el mapa, se añade un evento al propio marcador, que se ejecutará cada vez que se pinche sobre el:

GEvent.addListener(nuevoMarcador, "click", function() {

El método openInfoWindowHtml() permite mostrar una pequeña ventana con un mensaje que puede contener código HTML. Dentro de la función manejadora del evento, la variable this se resuelve en el propio marcador, por lo que se puede hacer uso de ella para obtener sus coordenadas y para mostrar el mensaje:

this.openInfoWindowHtml("Lat: " + this.getPoint().lat() + "<br/>Lon: " + this.getPoint().lng());

Por último, se añade el marcador al mapa:

mapa.addOverlay(nuevoMarcador);

Ejercicio 22

Partiendo del mapa básico que se acaba de crear, se pide:

1) En la misma página se debe incluir otro mapa que muestre las antípodas del punto geográfico inicial del primer mapa. Este segundo mapa no debe mostrar ningún control de zoom ni de tipo de mapa.

Aspecto del mapa principal y del mapa secundario que muestra el lugar geográfico definido como "antípodas" del lugar mostrado en el mapa principal

Figura 9.9 Aspecto del mapa principal y del mapa secundario que muestra el lugar geográfico definido como "antípodas" del lugar mostrado en el mapa principal

2) Al mover el primer mapa, el segundo mapa debe mostrar en todo momento las antípodas de ese lugar. Además, el zoom y el tipo de mapa también debe estar sincronizado, de forma que el segundo mapa muestre en todo momento el mismo nivel de zoom y el mismo tipo de mapa que el primero.

3) Cuando se pinche en el primer mapa, se muestra un marcador con su longitud y su latitud. Además, automáticamente se crea un marcador en el segundo mapa indicando el lugar exacto que corresponde a su antípoda.

Cuando se crea un marcador en el mapa principal, automáticamente debe crearse otro marcador de posición en el lugar geográfico correspondiente a su antípoda

Figura 9.10 Cuando se crea un marcador en el mapa principal, automáticamente debe crearse otro marcador de posición en el lugar geográfico correspondiente a su antípoda

Ver solución

Ejercicio 23

La disponibilidad de una API pública, sencilla, gratuita y muy potente permite integrar los mapas de Google con cualquier otra aplicación abierta que genere información geoespacial.

Un ejemplo típico de esta mezcla de aplicaciones es la información metereológica. Con la ayuda de los mapas de Google, es posible mostrar en cada punto del mapa la previsión metereológica en forma de marcador de posición con un icono personalizado, tal y como se muestra en la siguiente imagen:

Mapa de previsión metereológica construido con los mapas de Google y que utiliza iconos personalizados para mostrar cada marcador de posición

Figura 9.11 Mapa de previsión metereológica construido con los mapas de Google y que utiliza iconos personalizados para mostrar cada marcador de posición

1) Mostrar un mapa de Google de tamaño 600x600 píxel, centrado en [40.41558722527384, -3.6968994140625], que muestre por defecto el mapa de tipo satélite y que no incluya ningún tipo de control.

2) Cada cierto tiempo se debe mostrar la previsión meteorológica descargada desde el servidor. El script del servidor se denomina previsionMeteorologica.php y no es necesario pasar ningún parámetro. El servidor devuelve un array en formato JSON con la lista de puntos geográficos junto con su previsión meteorológica:

[
{ latlon: [42.779275360242, -2.63671875], prediccion: "tormentas" },
{ latlon: [43.245202722034, -8.32763671875], prediccion: "nieve" },
{ latlon: [42.228517356209, -7.36083984375], prediccion: "lluvia" },
...
{ latlon: [41.54147766679, -3.75732421875], prediccion: "nublado" },
]

3) La información meteorológica de cada punto se muestra mediante un marcador personalizado. Pistas: la clase GMarkerManager permite gestionar conjuntos grandes de marcadores. La clase GMarker permite definir un icono personalizado para el marcador. La clase GIcon permite crear nuevos iconos listos para los marcadores. En la carpeta imagenes se proporcionan iconos para cada una de las condiciones meteorológicas: lluvia.png, nieve.png, nublado.png, etc. Las imágenes utilizadas en este ejemplo, pertenecen al excelente conjunto de iconos del proyecto Tango.

Descargar archivo ZIP con las imágenes y el script previsionMeteorologica.php

Ver solución