Archivo de la categoría 'GIS/Geolocalización'


Migrar de Google Maps v2 a Google Maps v3

Lunes, 21 feb, 2011 @ 10:39 | Por Gustavo Cantero (The Wolf) | GIS/Geolocalización, JavaScript

Quien haya desarrollado alguna aplicación web que contenga mapas utilizando la versión 2 de la API de Google Maps y haya querido migrar a la versión 3 (ya que es la versión oficial, funciona mucho mejor en dispositivos móviles y la anterior ya ha quedado oficialmente descartada), se habrá dado cuenta que no es tan sencillo como cambiar el JS a utilizar y listo, sino que cambiaron muchas cosas, por ejemplo, ahora las clases no comienzan con la letra G (como GMap) sino que utilizan el namespace google.maps (como google.maps.Map).

Este artículo tiene la idea de ser una guía básica para migrar de la versión 2 a la 3 de esta API, mostrando dónde cambiaron las cosas que comúnmente utilizamos. Para esto dividí el artículo en tareas donde muestro cómo hacerlas con ambas versiones para que sea más fácil de encontrar lo que estamos buscando.

Librería JS

Para comenzar tendremos que utilizar el JS de la nueva versión, por lo cual, donde antes apuntábamos a

http://maps.google.com/maps?file=api&v=2&key=API_KEY

ahora debemos apuntarlo a

http://maps.google.com/maps/api/js?sensor=false

Nótese que ahora no necesitamos utilizar el API_KEY, por lo tanto, ya no es necesario registrar la URL del sitio donde vamos a utilizar los mapas. También hay un nuevo parámetro llamado “sensor”, el cual establece si se debe obtener la ubicación de un usuario a través de un sensor. Para más información sobre este parámetro pueden consultar la Especificación del parámetro sensor.

Creación del mapa

Lo siguiente a modificar es la creación del mapa. Por ejemplo, en la versión 2 creábamos un mapa en el div “miMapa” de esta manera:

var mapa = new GMap2(document.getElementById('miMapa'));

ahora deberemos crearlo de esta manera:

var mapa = new google.maps.Map(document.getElementById('miMapa'));

Me gustaría comentar que en la nueva versión se puede establecer como segundo parámetro del constructor la mayoría de las opciones que veremos a continuación, permitiendo de esta manera crear el mapa de la manera que necesitamos de una vez. Para ver las distintas opciones disponibles consultar la Especificación del objeto MapOptions.

Ubicación (coordenadas) y zoom del mapa

Para centrar el mapa y hacerle zoom antes hacíamos esto:

var miPosicion = new GLatLng(latitud, longitud);
mapa.setCenter(miPosicion, nivelDeZoom);

Ahora debemos hacerlo de esta manera:

var miPosicion = new google.maps.LatLng(latitud, longitud);
mapa.setCenter(miPosicion);
mapa.setZoom(nivelDeZoom);

Si se prefiere, se pueden establecer cambiando las opciones del mapa, de esta manera:

var miPosicion = new google.maps.LatLng(latitud, longitud);
mapa.setOptions({
    center: miPosicion,
    zoom: nivelDeZoom
});

Tipo de mapa

Un punto a tener en cuenta con la nueva versión de la API es que antes, por defecto, nos mostraba el mapa del tipo “normal”, o sea, las calles sin imágenes satelitales, pero ahora si no le establecemos un tipo de mapa no nos muestra nada.
Entonces, antes para establecer el tipo de mapa (por ejemplo, satelital) hacíamos esto:

mapa.setMapType(G_SATELLITE_MAP);

Pero ahora es así:

mapa.setMapTypeId(google.maps.MapTypeId.SATELLITE);

Este valor también se puede establecer al momento de crear el mapa con la opción “mapTypeId”.
Para ver los distintos tipos de mapas posibles para la versión 3 pueden consultar la clase MapTypeId, y para los de la versión 2 pueden consultar la clase GMapType.

Controles de navegación

Antes para agregar un control de navegación, por ejemplo el control “chico”, debíamos hacer:

mapa.addControl(new GSmallMapControl());

Ahora, en cambio debemos hacerlo así:

mapa.setOptions({
    navigationControl: true,
    navigationControlOptions: { style: google.maps.NavigationControlStyle.SMALL}
});

Para ver que otros tipos de controles de navegación hay pueden consultar las constantes que hay en la clase NavigationControlStyle.

Agregar marcadores

Para crear marcadores con una imagen personalizada hasta ahora hacíamos:

var icono = new GIcon(G_DEFAULT_ICON);
icono.image = 'Images/MiImagen.png';
var marcador = new GMarker(new GLatLng(latitud, longitud), {
    icon: icono,
    title: 'Este es mi marcador'
});
mapa.addOverlay(marcador);

En cambio ahora debemos hacer:

var marcador = new google.maps.Marker({
    position: new google.maps.LatLng(latitud, longitud),
    map: mapa,
    icon: 'Images/MiImagen.png',
    title: 'Este es mi marcador'
});

Quitar marcadores

Para sacar un marcador, por ejemplo el creado en el paso anterior, en la versión 2 hacíamos:

mapa.removeOverlay(marcador);

En cambio en la versión 3 hacemos:

marcador.setMap(null);

Algo a tener en cuenta es que en la versión anterior para eliminar todo los marcadores (y demás objetos) del mapa podíamos utilizar la siguiente línea:

mapa.clearOverlays();

Pero en la versión 3 tenemos que guardar la referencia de cada marcador (por ejemplo, en un vector) para luego llamar al “setMap(null)” de cada uno.

InfoWindow en un mapa

Para abrir una “viñeta” con información dentro de un mapa hacíamos:

var posicion = new GLatLng(latitud, longitud);
mapa.openInfoWindowHtml(posicion, 'Esta es mi viñeta');

En cambio ahora es:

var infoWin = new google.maps.InfoWindow({
  content: 'Esta es mi viñeta',
  position: google.maps.LatLng(latitud, longitud)
});

InfoWindow en un marcador

También podíamos abrir “viñetas” sobre un marcador de la siguiente manera:

marcador.openInfoWindowHtml('Esta es mi viñeta');

Pero ahora se hace de esta otra manera:

var infoWin = new google.maps.InfoWindow({
    content: 'Esta es mi viñeta'
});
infoWin.open(mapa, marcador);

Controles personalizados sobre el mapa

Hay veces que necesitamos crear nuestros propios controles y agregarlos sobre el mapa. Para esto la versión 2 de Google Maps nos permitía hacerlo creando una clase que debía heredar de GControl. Por ejemplo, para agregar un DIV llamado “miControl” en la esquina superior izquierda del mapa podíamos hacer esto:

function miControl() { }
miControl.prototype = new GControl();
miControl.prototype.initialize = function (map) {
    var div = document.getElementById('miControl');
    ctlMap.getContainer().appendChild(div);
    return div;
}
miControl.prototype.getDefaultPosition = function () {
    return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(1, 1));
}

mapa.addControl(new miControl());

Pero ahora, con la versión 3 de la API, podemos hacerlo de una manera mucho más sencilla: simplemente agregando nuestro DIV a la pila de controles del mapa:

var div = document.getElementById('miControl');
mapa.controls[google.maps.ControlPosition.TOP_LEFT].push(div);

Ejemplo

Con estas pocas líneas voy a mostrar cómo hacer un mapa que se ubique en Bariloche mostrando el relieve de la región:

new google.maps.Map(document.getElementById('miMapa'), {
    center: new google.maps.LatLng(-41.156686, -71.302299),
    zoom: 10,
    mapTypeId: google.maps.MapTypeId.TERRAIN
};

Conclusión

La nueva versión de la API de Google Maps cambia muchas cosas y migrar los scripts de una versión a otra lleva tiempo, pero la nueva versión trae muchas mejoras y una vez migrado notaremos que la sintaxis es más cómoda en la nueva versión.
La referencia de la versión 2 de la API la pueden encontrar aquí y la de la versión 3 aquí. Cabe mencionar que la propia versión 3 está en continua evolución y ya va, a la fecha, por la versión 3.4. Para ver el historial de los cambios lo pueden hacer desde esta dirección: Javascript Maps API v3 Changelog.

Espero que esta nota les sea de utilidad y los invito a dejar sus comentarios.

¡Suerte!


VN:F [1.7.3_972]
Rating: 9.6/10 (12 votos cast)

Geolocalización con HTML 5

Martes, 28 dic, 2010 @ 12:42 | Por Gustavo Cantero (The Wolf) | GIS/Geolocalización, Navegadores


GeolocalizaciónMuchos de los dispositivos que utilizamos hoy en día tienen GPS incorporado y acceso a internet, por ejemplo, notebooks, Pocket PCs, smartphones (BlackBerry, iPhone, Windows Phone, etc.), tabletas (Tablet PC, iPad, Samsung Galaxy Tab, etc.), y cada vez hay más dispositivos con estas opciones (hasta hay cámaras de fotos con GPS para geotagging e internet para publicarlas en Flickr, Facebook o enviarlas por e-mail).

Una ventaja de estos dispositivos es que podemos utilizarlos para mostrarle al usuario contenido específico de la zona en la que se encuentra, por ejemplo, hoteles cerca de donde está, la forma más rápida de viajar a un punto determinado desde donde se encuentra (se evitaría la necesidad de cargar la dirección en la que está), mostrarle al usuario publicidad de la zona, el estado del tiempo de su ciudad, etc.

Por suerte HTML 5 nos brinda una API de geolocalización en la que el browser nos devuelve las coordenadas de la ubicación del usuario, la altura, velocidad, y otros datos más. Esta API nos abstrae del método de obtención de esta información, ya que el navegador puede obtener esta información utilizando otros medios, por ejemplo, utilizando la IP del usuario, RFID (siglas de Radio Frequency IDentification, o sea, identificación por radiofrecuencia), el MAC address de redes WiFi y Bluetooth, identificadores de antenas GSM/CDMA, etc. En resumen, dependiendo del navegador, del dispositivo y del hardware del que disponga el usuario, vamos a poder obtener la información de su ubicación geográfica de distintas maneras, pero este trabajo (el de fijarse cómo podemos obtenerla) lo realizará el navegador. La especificación de esta API la podemos ver en la siguiente dirección del sitio del World Wide Web Consortium (W3C): Geolocation API Specification.

Los navegadores y dispositivos que actualmente soportan esta API son los siguientes:

  • Internet Explorer 9 *
  • Firefox 3.5 *
  • Opera 10.6
  • Google Chrome 5 *
  • Safari 5 *
  • Android 2 *
  • iPhone OS 3.0
  • Blackberry 5
  • Palm WebOS 2

* Las versiones anteriores soportan geolocalización a través de la API de Gears o utilizando la barra Google.

La API se encuentra en el objeto “navigator” del browser, y posee tres métodos: “getCurrentPosition”, que devuelve la ubicación del dispositivo, “watchPosition”, que llama a una función definida por nosotros cada vez que cambia la posición del dispositivo, y “clearWatch”, que permite dejar de verificar la posición del usuario, o sea, elimina el proceso creado con el método “watchPosition”.

getCurrentPosition


Este método nos devuelve la ubicación del usuario y posee tres parámetros posibles:

void getCurrentPosition(in PositionCallback successCallback,
    in optional PositionErrorCallback errorCallback,
    in optional PositionOptions options);

El primero, successCallback, es la función a la cual llamar una vez que se haya obtenido la ubicación, ya que esta tarea se realiza de forma asíncrona. Esta función debe poseer un parámetro, el cual contendrá los siguientes atributos:

interface Position {

  //Objeto con las coordenadas
  readonly attribute Coordinates coords;

  //Fecha y hora de obtención de la posición
  readonly attribute DOMTimeStamp timestamp;

};

Y a su vez, el atributo “coords” tiene los siguientes atributos:

interface Coordinates {

  //Latitud en coordenadas geográficas
  readonly attribute double latitude;

  //Longitud en coordenadas geográficas
  readonly attribute double longitude;

  //Altitud en metros
  //Si no se puede calcular este valor este atributo vale null.
  readonly attribute double? altitude;

  //Precisión de las coordenadas en metros
  readonly attribute double accuracy;

  //Precisión de la altitud en metros
  //Si no se puede calcular este valor este atributo vale null.
  readonly attribute double? altitudeAccuracy;

  //Dirección en la que se desplaza el usuario en
  //grados centígrados donde 0° es el norte
  readonly attribute double? heading;

  //Velocidad en metros por segundo.
  //Si no se puede calcular este valor este atributo vale null.
  readonly attribute double? speed;

};

El segundo parámetro, errorCallback, es opcional y es la función a llamar cuando se ha producido un error. Esta función también debe tener un parámetro, el cual contendrá los siguientes atributos:

interface PositionError {

  //Código de error, que puede valer:
  //  1 = permiso denegado
  //  2 = posición no disponible
  //  3 = tiempo de espera agotado (timeout)
  readonly attribute unsigned short code;

  //Mensaje de error
  readonly attribute DOMString message;

};

El tercer parámetro, options, también es opcional y nos permite establecer tres opciones: el tiempo máximo de espera en milisegundos para la obtención de las coordenadas, la “edad máxima” de la ubicación en milisegundos (esto es para utilizar las ubicaciones cacheadas para evitar la espera), y por último si se necesita el mejor resultado posible, lo cual puede generar una mayor demora en la obtención del mismo pero el resultado puede ser más exacto.

interface PositionOptions {

  //establece si se necesita el mejor resultado posible
  attribute boolean enableHighAccuracy;

  //Tiempo máximo de espera en milisegundos
  attribute long timeout;

  //”edad máxima” de la ubicación en milisegundos
  attribute long maximumAge;

};

Pero mejor que explicarlo con palabras es mostrar algunas líneas de código, por lo tanto, vamos a ello. A continuación voy a mostrar una función de JavaScript llamada “getLocation”, la cual, si el navegador soporta geolocalización, va a mostrar nuestras coordenadas en un “alert”:

function getLocation() {
	//Primero me fijo si el navegador soporta geolocalización
	if (navigator.geolocation) {
		//Obtiene la posición más exacta que no haya sido tomada
		//hace más de 10 minutos (o sea, 600.000 milisegundos)
		navigator.geolocation.getCurrentPosition(
			showLocation,
			showError,
			{enableHighAccuracy: true, maximumAge: 600000}
		);
	} else {
		alert('Lo siento, su navegador no soporta geolocalización');
	}
}

function showError(error) {
	//Muestro el código y mensaje de error
	alert(error.code + ' ' + error.message);
}

function showLocation(position) {
	//Muestro las coordenadas
	alert('Latitud: ' + position.coords.latitude
		+ '\nLongitud: ' + position.coords.longitude
		+ '\nPrecisión: ' + position.coords.accuracy
		+ '\nAltitud: ' + position.coords.altitude
		+ '\nPrecisión de la altitud: ' + position.coords.altitudeAccuracy
		+ '\nVelocidad: ' + position.coords.speed
		+ '\nDirección: ' + position.coords.heading);
}

Para probar el código anterior sólo pulsen en este link: PROBAR GEOLOCALIZACIÓN.

watchPosition


Este método es muy parecido al getCurrentPosition, pero llamará a nuestra función cada vez que cambie la ubicación del dispositivo. Los parámetros son los mismos que los de la otra función, pero nos devolverá un número de proceso para poder identificarlo luego:

long watchPosition(in PositionCallback successCallback,
	in optional PositionErrorCallback errorCallback,
	in optional PositionOptions options);


clearWatch


Esta última función elimina un proceso creado con watchPosition, pasándole como parámetro el identificador devuelto por la función anterior:

void clearWatch(in long watchId);

Espero que este artículo les sea de utilidad.

¡Suerte y felices fiestas!

VN:F [1.7.3_972]
Rating: 9.5/10 (4 votos cast)

Versión final de Bing Maps Platform

Martes, 10 nov, 2009 @ 12:28 | Por Gustavo Cantero (The Wolf) | GIS/Geolocalización, Silverlight

bing
Para aquellos que, como nosotros, utilizan la versión CTP del control de Silverlight para Virtual Earth les comento que este control funcionará hasta el 31 de diciembre de 2009 inclusive. Para poder seguir utilizando los servicios de Bing Microsoft acaba de lanzar (ayer) la versión final de Bing Maps Platform 1.0, el cual incluye la versión final del control para usarse desde Silverlight.

Para conocer más de la nueva versión del control o para conocer cómo utilizar los servicios del control de Silverlight para bing pueden leer el Getting Started Using the Silverlight Map Control.

También pueden ver el video del anuncio de esta nueva versión en Microsoft Bing Maps for Enterprise.

VN:F [1.7.3_972]
Rating: 0.0/10 (0 votos cast)

Obtener la ciudad y el país de una dirección IP

Domingo, 24 may, 2009 @ 15:24 | Por Gustavo Cantero (The Wolf) | ASP.NET, GIS/Geolocalización, Redes

Muchos habrán notado que al ingresar en algunos sitios web éstos modifican su contenido, incluyendo idioma, publicidad, textos, etc., dependiendo del país desde el que estamos ingresando, por ejemplo, al ingresar en www.linksys.com desde Argentina éste nos redirecciona a www-ar.linksys.com, o al ingresar a www.google.com nos reenvía a www.google.com.ar. Para poder hacer esto la aplicación web busca la IP con la que estamos ingresando en una base de datos donde previamente se cargaron rangos de IP con su respectivo país.

Hasta acá todo parece muy obvio, el problema surge cuando nos preguntamos: ¿Dónde conseguimos estas bases de datos? La respuesta es sencilla: en internet.

En internet hay varios sitios que ofrecen, gratuitamente o como servicio pago, archivos con rangos de IP con su respectivo país. Muchos de estos sitios ofrecen además web services para poder consultar contra su base de datos la ubicación correspondiente a una dirección IP sin necesidad de tener los datos alojados en nuestro sitio. En este artículo voy a mostrar cómo obtener la ubicación consultando los datos en una base de datos local.

Uno de los sitios desde donde se pueden bajar estos datos es WebHosting.Info (www.webhosting.info), un sitio dedicado a dar servicios y estadísticas sobre ISP, WHOIS, Reverse IP Lookup, etc. Los datos que estos sitios ofrecen casi siempre están en archivos CSV, donde el país está codificado en ISO 3166 y la dirección IP está como un entero sin signo de 4 bytes llamado “número IP”.

Para obtener la lista de códigos de países según la ISO 3166 pueden ver en el sitio mismo de ISO, http://www.iso.org/iso/country_codes, o en Wikipedia, http://es.wikipedia.org/wiki/ISO_3166-1.
Para convertir la dirección IP en un número IP (un entero de 4 bytes) es una simple fórmula matemática donde lo que tenemos que hacer es un “shift left” de cada número para “posicionarlo” en el octeto correspondiente o, en español, moverlo hasta el byte correspondiente. Como cada byte representa 256 números (de 0 a 255), lo que debemos hacer es multiplicar cada octeto de la dirección por 256 elevado a la posición de ese byte. Supongamos que nuestra dirección a convertir es A.B.C.D, entonces lo que debemos hacer es:

(A * 256^3) + (B * 256^2) + (C * 256^1) + (D * 256^0)

Si reemplazamos las constantes por su resultado nos quedaría lo siguiente:

(A * 16777216) + (B * 65536) + (C * 256) + D

Ahora supongamos que la IP de mi cliente es la 190.2.8.88, entonces deberíamos hacer:

(190 * 16777216) + (2 * 65536) + (8 * 256) + 88 =
   3187671040    +   131072    +    2048   + 88 = 3187804248

Cabe mencionar que para obtener la dirección IP del usuario que se está conectando a nuestro sitio se debería leer el encabezado de la petición http. Para los desarrolladores de .NET esto se puede hacer simplemente utilizando la propiedad UserHostAddress del objeto Request del Page, por ejemplo, en C# sería:

string strIP = Page.Request.UserHostAddress;

o, para los usuarios de Visual Basic.NET:

Dim strIP as string = Page.Request.UserHostAddress

Obviamente la IP está en un string, con lo cual debería dividir cada octeto de la dirección, convertirlo a un tipo de dato numérico y hacer la fórmula. A continuación les dejo un ejemplo en C#:

//Convierto la IP a un entero de 32 bits
string[] strBytes = Page.Request.UserHostAddress.Split('.');
uint intNumeroIP=
    (uint.Parse(strBytes[0]) * 16777216) +
    (uint.Parse(strBytes[1]) * 65536) +
    (uint.Parse(strBytes[2]) * 256) +
    uint.Parse(strBytes[3]);

y, para los usuarios de Visual Basic.NET:

'Convierto la IP a un entero de 32 bits
Dim strBytes As String() = Page.Request.UserHostAddress.Split('.')
Dim intNumeroIP As UInteger = _
    (UInteger.Parse(strBytes(0)) * 16777216) + _
    (UInteger.Parse(strBytes(1)) * 65536) + _
    (UInteger.Parse(strBytes(2)) * 256) + _
    UInteger.Parse(strBytes(3))

Nótese que en el ejemplo utilicé un entero sin signo, esto es porque el número IP no lleva signo. También cabe notar que, aunque cada octeto se puede representar con un byte, yo lo convierto a un entero sin signo para evitar la conversión del tipo de dato al almacenarlo en la variable final.

Ahora con este número en mente lo podemos buscar en el archivo con la información de la localización de las IPs.  Obviamente lo mejor es guardar previamente la información de este archivo en una base de datos. Como mínimo deberíamos guardar la dirección IP y el país. Muchos de estos archivos de IP nos traen una “dirección desde” y una “dirección hasta” por cada línea del archivos, pero con almacenar la “dirección desde” ya nos alcanza.
Para los usuarios de motores de bases de datos como Oracle o MySql no tendrán problema con guardar los números IP en campos enteros de 4 bytes sin signo, pero para los usuarios de SQL Server se nos complica, ya que este motor no permite utilizar este tipo de datos. Para solucionar este inconveniente podemos hacer dos cosas: guardar el número en un campo del tipo entero de 8 bytes o a cada número IP restarle 2147483648.

Ahora sólo nos quedaría buscar la IP del usuario en la base, con un script sencillo como el siguiente para SQL Server:

SELECT TOP 1 Pais
FROM ListaIPs
WHERE IP >= 3187804248
ORDER BY IP DESC

Si en SQL Server elegimos utilizar un entero de 4 bytes (int) y restarle la mitad para no utilizar un entero de 8 bytes (bigint) habría que utilizar el valor 1040320600 en lugar de 3187804248.

Si quisiéramos hacer esta misma consulta pero utilizando MySql como motor de base de datos deberíamos utilizar un script como el siguiente:

SELECT Pais
FROM ListaIPs
WHERE IP >= 3187804248
ORDER BY IP DESC
LIMIT 1

Bien, hasta acá ya sabemos cómo obtener el país correspondiente a una dirección IP pero, como el título bien dice, también podemos obtener la ciudad correspondiente a esta dirección. Para esto la solución vuelve a ser sencilla, solamente tenemos que buscar en internet donde se puede conseguir una lista de IPs correspondiente a cada ciudad.  Un lugar donde se puede obtener esta lista es en IP Location Tools (http://iplocationtools.com), donde se puede conseguir la lista de país por IP y ciudad por IP, en formato CSV y en SQL. La única diferencia con el proceso anterior es que en la base de datos, además o en lugar de guardar el país, se debería guardar la ciudad.

Espero que este artículo les haya sido de utilidad y, como siempre, los animo a escribir sus consultas, sugerencias y comentarios.

VN:F [1.7.3_972]
Rating: 9.1/10 (14 votos cast)