Cómo Hacer Mapas Georeferenciados de México para Visualización

La visualización de información es uno de los aspectos fundamentales del análisis de datos en la actualidad, como en el caso de la reciente visualización de amistad en Facebook que tuvo gran éxito

En OCCMundial estamos trabajando en geolocalización de ofertas de trabajo. Obtuvimos datos muy interesantes pero al tratar de visualizarlos con un mapa de fondo para hacer más interesante la presentación de los datos me encontré con el problema de conseguir un mapa georeferenciado sobre el cual mostrar la información. De otras regiones tales como el mundo completo, Estados Unidos o Europa es sencillo conseguirlos pero fue más complicado en el caso de México. Tampoco encontré un procedimiento sencillo para generarlo, por lo que en este post pongo mi experiencia y los resultados esperando que le sirvan a alguien más. La idea vino de Minigis que lo usa en un mapa mundial.

El problema consiste en conseguir una imagen de un mapa en el cual se tenga una correspondencia entre coordenadas geográficas y los pixeles. Los buenos mapas que escalan con el nivel de zoom son imágenes vectoriales.Wikimedia es un excelente recurso para comenzar. Los mapas estan en formato SVG. Aquí se encuentran los mapas de México. Es importante fijarse en el tipo de proyección ya que afecta el aspecto del mapa. Para este caso necesitamos un mapa equirectangular en el cual las coordenadas geográficas se pueden mapear de forma lineal con los pixeles de la imagen. Aunque el mejor mapa de México que conseguí tiene división política por municipios, lo que lo hace pesado y no muy bonito para algunos casos, pero es suficientemente bueno para nuestro uso.

Desafortunadamente el mapa no especifica las coordenadas que comprende por lo que es necesario averiguarlas. Usando Inkscape abrí el mapa y encontré que tiene marcados paralelos y meridianos cada 5 grados. Dado que el primer meridiano que se muestra es el 15 y está en la posición Y=48.4, el siguiente es el 20 y está en la posición Y=173.739 podemos comprobar que cada 5 grados corresponden a 125 pixeles, 1 grado a 2.06 pixeles y 0.03999 grados a 1 pixel. De la misma forma podemos comprobar para los paralelos ya que el último paralelo mostrado es el 115W (-115.0) en posición X=56.79 y se mantiene la misma proporción de grados a pixeles (lo que es correcto dado que es una proyección equirectangular). Con esto podemos calcular que el origen de la imagen se encuentra en 13.064° de latitud y -117.271° de longitud (dicho sea de paso, usaremos grados decimales por simplicidad).

Con esto se puede generar una imagen bitmap para ser usado en algún programa o con lenguaje de programación. En este caso estoy usando Processing para generar las visualizaciones. Importante, en general en las imágenes raster el origen no se encuentra en la esquina inferior izquierda como en el SVG sino en la superior izquierda, por lo que el origen se encuentra en 34.6586° lat y -117.271° long, que son las coordenadas se deben usar para calcular las posiciones de los pixeles. La longitud mínima se encuentra en -86.54. Para calcular los pixeles se usan las funciones lineales descritas abajo. La derivación es sencilla por lo que dejo solo una imagen de mis notas por si le sirve a alguien.

En el sketch de processing se puede ver una prueba con algunos puntos interesantes para comprobar que funciona. IMPORTANTE: los datos son muy aproximados, por lo que no recomendaría usar estos métodos para algo mas que visualizaciones sencillas, no mediciones o para ubicar exactamente alguna posición.

Cómo usar:

Se puede usar esta imagen con las dimensiones descritas arriba y las siguientes funciones para calcular pixeles a partir de coordenadas.

 

 
  int x = int( (117.271-lng)*770/(117.271-86.54) );
  int y = int( (34.6586-lat)*540/(34.6586-13.064) );
  

 

Otra imagen, con fondo negro que suele ser mejor para visualizaciones.

En caso de requerir una imagen de otro tamaño no es dificil escalar estas imágenes y encontrar las correspondientes funciones.

Sketch de Processing bajar

//Mantiene las mismas dimensiones del SVG original
int imgx=770;
int imgy=540;

void setup()
{
  //También se puede bajar con la URL directa a Wikimedia 
  //http://commons.wikimedia.org/wiki/File:Municipalities_of_Mexico_(equirectangular_projection).svg
  PShape mx = loadShape( "Municipalities_of_Mexico_(equirectangular_projection).svg" );
 
  size(imgx,imgy);
  shape(mx,0,0,imgx,imgy);
  smooth();
  noLoop();
  
  //Puntos de prueba
  plotAt(19.43333,-99.1333333,5); //DF
  plotAt(25.6666,-100.5, 5);   //MTY
  plotAt(20.6666,-103.3333, 5);  //GDL
 
  plotAt(32.534, -117.123,5); //Tijuana 
  plotAt(21.227, -86.7199,5); //Isla Mujeres
  
  plotAt(14.5176, -92.2199, 5); //Sur
  plotAt(17.8212, -89.1551, 5); //Belice
  plotAt(20.7959, -90.4006, 5); //Campeche
  plotAt(25.9393, -97.1231, 5); //Tamps
  plotAt(29.8605, -102.2996, 5); //Coah Nte
  plotAt(31.7820, -106.5058, 5); //Chih
  plotAt(32.7125, -114.7015, 5); //Nte
  plotAt(22.8865, -109.9164, 5); //Cabos
  plotAt(20.6,-100.38333, 5); //Qro
  plotAt(16.85,-99.93333, 5); //Acapulco
  
  save("mexico.jpg");
}

void plotAt(float lat, float lng, float rad)
{
  lng=-lng; //Si viene en long oeste
  int x = int( (117.271-lng)*770/(117.271-86.54) );
  int y = int( (34.6586-lat)*540/(34.6586-13.064) );
  

  println(x);
  println(y);
  
  fill(10,10,60,80);
  ellipse(x,y,rad,rad);
 }

Espejo del mapa SVG

Imagen con los puntos de prueba

Visualización – Algunas ofertas de empleo actuales (naranja), y principales ciudades (azul)