Una pequeña aplicación cartográfica con Fusion Tables y Google Maps

19 de Abril de 2012

[EDIT diciembre 2013] La demo no funiona mas. Buscando un poco parece que es un problema con mi versión de Jquery UI. No tengo el tiempo de solucionar el problema pero el tutorial a continuación sigue válido.

Fusion Tables es un servicio de Google que permite almacenar y compartir datos a partir de su cuenta de usuario Google. Una de las ventajas de Fusion Tables es la diversidad de representación de los datos a disposición del usuario (tabla, mapa, gráfico, etc...). En este post vamos a ver como se puede utilizar este servicio en una aplicación cartográfica.

Para los que están en modo tl;dr, pueden acceder a la demo.

El producto

Para esta aplicación muy básica necesitaremos las tecnologías siguientes:

El aspecto positivo de usar Fusion Tables es que no tenemos que perder tiempo en la parte base de datos, Google lo hace para nosotros. Obviamente, lo malo de eso es que no somos dueños de nuestros datos y además, si queremos publicar un mapa con datos almacenados en Fusion Tables, debemos permitir el acceso a esta tabla a todo el mundo. Entonces, no es para usar con datos confidenciales.

Los datos

El ministerio de educación, a través de su plataforma ESCALE publica diferentes datos sobre los establecimientos escolares. En la sección Mapa de Escuelas pude descargar los institutos escolares públicos de Lima y del Callao. A este nivel, tengo 2 archivos con 18 102 IIEE públicos (16 309 para Lima y 1 793 para Callao).

Fusion Tables

Antes de importar mis tablas en Fusion Tables, hice 3 modificaciones:

Para crear una nueva tabla en Fusion Tables, una vez conectado a su cuenta Google, a partir de la página Fusion Tables, hacer clic sobre "SEE MY TABLES". Luego, clic sobre "CREATE" y seleccionar "Table (beta)".

Crear una tabla Fusion Tables

Primero, importé el archivo de los establecimientos escolares de Lima. Una vez subido el archivo, escogido el nombre de la tabla (en mi caso "institutos escolares") se valida la creación de la tabla. El resultado es el siguiente:

View table

Fusion Tables reconoce 4 tipos de datos diferentes: Texto, Numérico, Fecha y Location para los campos geográficos (en amarillo en la imagen arriba). Por defecto, identifica los campos "Departamento", "Nom.CCPP" y "coords" como campos geográficos. Solamente vamos a guardar el último. Pero antes hacer este cambio, tenemos que importar los datos del Callao. Para eso, hacer clic en "File" en el menu arriba a la izquierda y seleccionar "Import more rows". Luego, subir el archivo y validar.

Ahora, podemos cambiar el tipo de los campos "Departamento" y "Nom.CCPP". En el menu, clic en "Edit" y seleccionar "Modify columns". En la ventana que se abre, cambiar Location por Text en el menu Type para estos 2 campos.

Ya que hemos indicado a Fusion Tables que campo tiene que usar para ubicar los puntos, podemos ver el resultado en un mapa. En el menu, hacer clic en "Visualize" y luego "Map".

View map

Haciendo un clic sobre un punto, se abre una ventana con algunos los campos de la tabla. El contenido de esta ventana se puede modificar haciendo clic en "Configure info window".

Info window  

Solamente hay que escoger los campos que se quiere ver en la ventana de información. La pestaña "Custom" permite hacer cosas mas avanzadas como insertar imagenes o enlaces.

Desarrollo de la aplicación

Antes de empezar el desarrollo de mi aplicación voy a modificar las opciones de visibilidad de mi tabla (quiero que todo el mundo pueda verla). Para eso, hacer clic en el botón "Share" arriba a la derecha de la página y selecionar "Public" en la parte "Visibility options".
Necesitamos una última información antes de empezar, el identificador de nuestra tabla. Se encuentra el menu File > About > Numeric ID. En nuestro caso: 3585574.

Ya estamos listos para empezar. La aplicación se presentará de la manera siguiente: Una mapa con todos los IIEE de Lima y Callao y arriba del mapa un formulario en el cual se puede ingresar el nombre de un distrito, lo cual permitara hacer un zoom sobre este distrito y ver solamente los IIEE de este distrito. 

El contenido HTML del body es el siguiente:

<body onload="initialize()">

  <h1>Los institutos escolares públicos de Lima y Callao</h1>

  <br />

  <form id="zoom" action="" method="post">

    <label>Distrito</label>

    <input type="text" id="distrito">

    <button type="submit">Buscar</button>

  </form>

  <br />

  <div id="map_canvas"></div>

</body>

Algunas observaciones sobre este código:

Antes de escribir la función initialize, tenemos que agregar las fuentes de código javascript y CSS para poder usar la API de Google Maps. En el head de nuestra página, agregamos:

<link href="http://code.google.com/apis/maps/documentation/javascript/examples/default.css" rel="stylesheet" type="text/css" />

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>

De bajo de eso creamos nuestra función initialize entre 2 etiquetas script:

<script type="text/javascript">

  function initialize() {

    var myLatlng = new google.maps.LatLng(-12.1, -77);

    var myOptions = {

    zoom: 10, // El nivel de zoom por defecto

    // Centro el mapa en las coordenadas -12.1, -77

    center: myLatlng,

    mapTypeId: google.maps.MapTypeId.ROADMAP

    }

  var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

  // Creo una capa a partir de mi tabla Fusion Tables, indicando su ID  

  layer = new google.maps.FusionTablesLayer(3585574);

  layer.setMap(map);

  }

</script>

A este nivel, si cargamos esta página en un navegador veremos la capa de los establecimientos escolares con un fondo Google Maps pero el formulario de búsqueda no funciona. Es lo que vamos a hacer ahora.

Hay 49 distritos en Lima y Callao, lo cual es mucho, entonces en vez de dejar el usuario escribir el nombre entero del distrito (y posiblemente equivocarse) le facilitaremos la vida implementando un Autocomplete en nuestro formulario.

Para eso usaremos un objeto JSON que consiste en pares nombres: valores. Nuestro objeto tendrá 2 nombres, uno llamado "nombre" que tiene como valor una tabla de los nombres de los 49 distritos y uno llamado "bbox" con valor asociado una tabla de las bounding box de cada distrito (para hacer un zoom sobre el distrito elegido).

var distritos = {nombre : [

    "ANCON",

    "ATE",

    ...

    "VILLA MARIA DEL TRIUNFO"

  ],

  bbox :[

    [-11.824736326684398, -77.199308589473674, -11.573009770063598, -77.004890605545484],

    [-12.084808579623619,-76.998095716871461,-11.992277343347604, -76.783874997416746],

    ...

    [-12.232451213553716, -76.958931680561591, -12.11894332274192,-76.882279343402473]

  ]

};

Guardamos nuesto objeto en un archivo javascript llamado distritos.js que importamos en el head de nuestro HTML (antes del bloque javascript arriba):

<script type="text/javascript" src="distritos.js"></script>

También en el head agregamos lo necesario para usar Jquery:

<link rel="stylesheet" type="text/css" href="http://static.jquery.com/api/style.css" />

<link rel="stylesheet" href="http://code.jquery.com/ui/1.8.18/themes/base/jquery-ui.css" type="text/css" media="all" />

<script src="http://code.jquery.com/jquery.min.js" type="text/javascript"></script>

<script src="http://code.jquery.com/ui/1.8.18/jquery-ui.min.js" type="text/javascript">

</script>

Ya tenemos todo para implementar el autocomplete y el zoom. En el mismo bloque javascript que arriba, justo después de la función initialize:

$(function(){

  // Implementamos un autocomplete en el elemento input del formulario

  $( "#distrito" ).autocomplete({

    // La fuente de datos es el nombre "nombre" del objeto JSON distritos

    source: distritos.nombre

  });

  // Al hacer clic sobre el botón del formulario, ejecutamos la función zoomToDistrict

  $("#zoom").submit(function(){

    zoomToDistrict();

    return false;

  });

});

Entre los 2 últimos }); escribimos la función zoomToDistrict:

function zoomToDistrict() {

  // Recuperamos el nombre del distrito escogido por el usuario

  var distrito = $("#zoom input[id=distrito]").val();

  var myOptions = {

    mapTypeId: google.maps.MapTypeId.ROADMAP

  }


  var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

  layer = new google.maps.FusionTablesLayer(3585574, {

    // Seleccionamos los puntos del distrito elegido

    query: "SELECT coords FROM 3585574 WHERE Distrito = "+ "'" +distrito +"'"});

 

  // Con las 2 variables pt_se y pt_nw recuperamos las coordenadas

  // de la bounding box del distrito elegido

  var pt_se = new google.maps.LatLng(distritos.bbox[distritos.nombre.indexOf(distrito)][0], distritos.bbox[distritos.nombre.indexOf(distrito)][1]);

 

  var pt_nw = new google.maps.LatLng(distritos.bbox[distritos.nombre.indexOf(distrito)][2], distritos.bbox[distritos.nombre.indexOf(distrito)][3]);

 

  var latlngbounds = new google.maps.LatLngBounds( );

  // En Google Maps para hacer un zoom sobre una extensión geográfica,

  // necesitamos las coordenadas de 2 puntos (sur-este y norte-oeste)

  latlngbounds.extend(pt_se);

  latlngbounds.extend(pt_nw);

  map.fitBounds(latlngbounds);

  layer.setMap(map);

}

Hecho! Para jugar con la demo es aquí. Pueden descargar los 2 archivos HTML y javascript aquí.

Conclusión

Esta aplicación es muy básica (y ligera, no necesita un servidor) pero es un buen inicio para hacer algo más avanzado. Por ejemplo podriamos agregar:

Twittear