Cursos de la Maestría en Software Libre de la UOC

Los cursos listados a continuación hacen parte de la Maestría en Software Libre de la Universidad de Cataluña y que han sido publicados bajo la licencia de Creative Commons a través de su sitio de OpenCourseWare.

50039 – Fundamentos de fotografía e imagen digital
50051 – Sistemas de vídeo y de tratamiento de la imagen
50070 – Laboratorio de PHP y MySQL
75143 – Trabajo final de carrera .NET
75555 – Prácticas de programación
75591 – Estructura de redes de computadores
75611 – Fundamentos físicos de la informática
75614 – Iniciación a las matemáticas para la ingeniería
76514 – Imagen y lenguaje visual
76517 – Arquitectura de la información
76521 – Redes Multimedia
76526 – Composición digital
76529 – Interfaces para sistemas multimedia
76531 – Animación 3D
76532 – Fotografía digital
81504 – Física I
89018 – Trabajo final de carrera de telecomunicación
93133 – Introducción a la creación de páginas web
93134 – Marcadores sociales, una herramienta para organizar la Red
93149 – Fotografia de viajes
B0019 – Estética y narrativa en medios digitales
B0762 – Servicios OGC
B0763 – Programación y Personalización SIG
M1302 – Ingeniería de la usabilidad
M1308 – Tecnología y desarrollo en dispositivos móviles
M2101 – Introducción al software libre
M2102 – Sistema operativo GNU/Linux básico
M2103 – Administración avanzada del sistema operativo GNU/Linux
M2104 – Implantación de sistemas de software libre
M2105 – Redes de computadores
M2107 – Aspectos avanzados de seguridad en redes
M2108 – Desarrollo de aplicaciones web
M2109 – Bases de datos
M2110 – Introducción al desarrollo de software
M2111 – Conceptos avanzados en desarrollo de software libre
M2112 – Ingeniería del software en entornos del software libre
M2113 – Utilidades y herramientas de software libre
M2114 – Aspectos legales y de explotación del software libre
M2114 – Aspectos legales y de explotación del software libre (b)
M2115 – Aspectos económicos y modelos de negocio del software llibre
M2117 – El software libre en el sector público
M2123 – Proyecto web
M2124 – Desarrollo de proyectos de software libre
M2125 – Proyecto de dirección de sistemas de información
M2132 – Proyecto en Administración de Redes y Sistemas Operativos basados en GNU/Linux
M2150 – Administración de sistemas GNU/Linux

Enlaces

Convertir coordenadas geográficas grados-minutos-segundos a notacional decimal (3)

Introducción.

Después de encontrar dos métodos (A y B) para la conversión de coordenadas entre formatos decimal y grados, minutos y segundos, encontré en el libro HTML5 Geolocation de Anthony T. Holdener III la implementación de dos funciones en Javascript que realizan con precisión este procedimiento y que me parecieron muy intersantes.  Este corto libro es en general muy interesante y recomiendo su lectura.

Conversión de coordenadas: formato decimal a grados-minutos-segundos.

/**
 * Convierte una coordenada en formato decimal a su correspondiente
 * versión en formato grados-minutos-segundos.
 *
 * @param	Float	Valor real de la coordenada.
 * @param	Int	Tipo de la coordenada {Coordenada.LATITUD, Coordenada.LONGITUD}.
 * @return	Array	['grados', 'minutos', 'segundos', 'direccion', 'valor'].
 */

function dec2gms(valor, tipo)
{
	grados    = Math.abs(parseInt(valor));
	minutos   = (Math.abs(valor) - grados) * 60;
	segundos  = minutos;
	minutos   = Math.abs(parseInt(minutos));
	segundos  = Math.round((segundos - minutos) * 60 * 1000000) / 1000000;
	signo     = (valor < 0) ? -1 : 1; 	direccion = (tipo == Coordenada.LATITUD) ?
                    ((signo > 0) ? 'N' : 'S') :
	            ((signo > 0) ? 'E' : 'W');

	if(isNaN(direccion))
		grados = grados * signo;

	return {
		'grados'   : grados,
		'minutos'  : minutos,
		'segundos' : segundos,
		'direccion': direccion,
		'valor'    : grados + "u00b0 " + minutos + "' "+ segundos +
		             """ + ((isNaN(direccion)) ? (' ' + direccion) : '')
	};
}

Por ejemplo.

var x = 48.853;

r1 = dec2gms(x, Coordenada.LATITUD);

alert('Conversion dec2gms = ' + r1.valor);

Conversión de coordenadas: formato grados-minutos-segundos a decimal.

/**
 * Convierte una coordenada en formato grados-minutos-segundos a su
 * correspondiente versión en formato decimal.
 *
 * @param	Float	Grados de la coordenada.
 * @param	Float	Minutos de la coordenada.
 * @param	Float	Segundos de la coordenada.
 * @param	String	Sentido de la coordenada {Coordenada.NORTE,
					Coordenada.SUR, Coordenada.ORIENTE,
					Coordenada.OCCIDENTE}
 * @return	Array	['decimal', 'valor'].
 */

function gms2dec(grados, minutos, segundos, direccion)
{
	if(direccion)
	{
		signo     = (direccion.toLowerCase() == 'w' ||
		             direccion.toLowerCase() == 's') ?
		            -1 : 1;
		direccion = (direccion.toLowerCase() == 'w' ||
		             direccion.toLowerCase() == 's' ||
		             direccion.toLowerCase() == 'n' ||
		             direccion.toLowerCase() == 'e') ?
		            direccion.toLowerCase() : '';
	}
	else
	{
		signo     = (grados < 0) ? -1 : 1;
		direccion = '';
	}

	dec = Math.round((Math.abs(grados) + ((minutos * 60) + segundos) / 3600) * 1000000) / 1000000;

	if(isNaN(direccion) || direccion == '')
		dec = dec * signo;

	return {
		'decimal': dec,
		'valor'  : dec + "u00b0" + ((isNaN(direccion) || direccion == '') ? (' ' + direccion) : '')
	};
}

Por ejemplo.

var xg = 48;
var xm = 51;
var xs = 10.8;

r2 = gms2dec(xg, xm, xs, Coordenada.NORTE);

alert('Conversion gms2dec = ' + r2.valor);

Código fuente y demostración.

Obtenga el código fuente del artículo junto con la ejecución de la demostración en la siguiente ubicación.

http://demo.jorgeivanmeza.com/JavaScript/CoordConverter/0.3/

Como acceder a la geolocalización desde Javascript

Introducción.

La geolocalización es la posibilidad de conocer cual es la ubicación física de un dispositivo.  Esta ubicación es geográfica, es decir, se define especialmente mediante sus correspondientes valores de latitud y longitud.

Los dispositivos pueden obtener esta información desde diferentes fuentes, las cuales estarán relacionadas a su vez con precisión de estos datos.  Los orígenes mas comúnes para la información de geolocalización de un dispositivo son los siguientes.

  1. Utilizando un GPS (Global Positioning System).
  2. Mediante su identificación en celdas GSM/CDMA.
  3. Mediante su dirección IP.
  4. Mediante la dirección MAC de WiFi y Bluetooth.
  5. Especificado por el usuario manualmente.

Estos métodos se encuentran disponibles para las aplicaciones nativas que pueden acceder libremente al hardware del dispositivo, sin embargo ahora también es posible acceder a esta información desde una aplicación web utilizando Javascript gracias a la integración del API de geolocalización de la W3C en los navegadores mas recientes (incluyendo los móviles).

  • Firefox (3.5+)
  • Chrome (5.0+)
  • Internet Explorer (9.0+)
  • Safari (5.0+)
  • Opera (10.6+)
  • iPhone (3.1+)
  • Android (2.0+)
  • BlackBerry (6+)

Para dar soporte a navegadores antíguos se requiere que se utilice una capa adicional como Google Gears que supla la falencia de esta tecnología.

En este artículo se explicará el API necesario para acceder al API de geolocalización mediante el uso del lenguaje Javascript utilizando un navegador moderno como los mencionados.

Verificar el soporte

El primer paso debe ser siempre verificar si el navegador web en el cual se despliega la aplicación tiene o no el soporte para esta tecnología, a partir de este resultado se deberá decidir que hacer.

if(navigator.geolocation)
{
    // Se cuenta con el soporte para geolocalización, entonces ...
}
else
{
    // No se cuenta con soporte para geolocalización, manejar la situación.
}

Solicitar la ubicación al navegador.

El siguiente paso consiste directamente en solicitarle al navegador web cual es la ubicación del dispositivo.  El llamado a esta función es asíncrono, es decir, no retorna inmediatamente un valor sino que invoca a una función específica (callback) cuando este se obtiene.  Así mismo, como el conocer la ubicación de un dispositivo (y por ende su usuario) tiene implicaciones en la privacidad de las personas, la especificación dispone que el usuario esté siempre facultado para aceptar o negar el acceso a esta información.  Por este motivo los navegadores desplegarán barras como la mostrada a continuación solicitando la confirmación final.

Permitir el acceso a la información de geolocalización en Chrome
Permitir el acceso a la información de geolocalización en Chrome

A continuación se muestra la invocación general de la función para obtener la geolocalización del dispositivo.

navigator.geolocation.getCurrentPosition(onSuccessGeolocating,
                                         onErrorGeolocating,
                                         options);

Los dos primeros parámetros corresponden a funciones Javascript que se ejecutarán según se tenga éxito o no durante el proceso de geolocalización (se analizarán a continuación), el tercer parámetro es opcional y corresponde con uno o varios de los modificadores descritos.

Opción Tipo Valor por defecto Descripción
enableHighAccuracy Booleano false Intenta obtener la información mas precisa que sea posible.  Debe tenerse en cuenta que esta opción también toma mas tiempo y probablemente consume una mayor cantidad de batería del dispositivo.
maximumAge Entero 0 Acepta como resultado el último valor histórico siempre y cuando este no sea mas antíguo que el valor especificado en milisegundos.
timeout Entero 0 Espera máximo la cantidad de milisegundos especificada antes de abortar el procedimiento.

A continuación se muestra un ejemplo de implementación de esta función.  En este se definen las funciones onSuccessGeolocating y onErrorGeolocating para determinar la implementación del manejo de éxito y error respectivamente de la geolocalización, y define además que se intente realizar el procedimiento con la mayor precisión posible, se acepten valores en caché con máximo 5 segundos de antiguedad y se espere máximo 10 segundos para terminar el procedimiento.

navigator.geolocation.getCurrentPosition(onSuccessGeolocating,
                                         onErrorGeolocating,
                                         {
                                       		enableHighAccuracy: true,
                                       		maximumAge:         5000,
                                       		timeout:            10000
                                         });

Establecer que hacer con la información de geolocalización.

Si el procedimiento fue exitoso, se define la función onSuccessGeolocating referenciada en el paso inmediatamente anterior, en ella se establece que se deberá hacer con la información obtenida.

function onSuccessGeolocating(position)
{
    // ...
}

El objeto Position que se recibe a través de los parámetros de la función incluye la siguiente información.

Atributo Descripción
coords Información de la ubicación obtenida
timestamp Información del momento en que fue obtenida la información

A su vez, el objeto Coordinates incluye la siguiente información en su interior.

Atributo Descripción
latitude Valor de la latitud de la ubicación del dispositivo medida en grados decimales
longitude Valor de la longitud de la ubicación del dispositivo medido en grados decimales
altitude Valor de la altura geográfica de la ubicación del dispositivo medida en metros
accuracy Precisión de la latitud y longitud medida en metros
altitudeAccuracy Precisión de la altitud medida en metros
heading Dirección en la que se mueve el dispositivo. Medida en grados desde 0 hasta 360. Su valor será NaN cuando el dispositivo se encuentra inmóvil o null si esta característica no es soportada
speed Valor de la longitud de la ubicación del dispositivo

Esto significa que la información básica de la ubicación del dispositivo esta finalmente almacenada en position.coords.latitude y position.coords.longitude.

Establecer que hacer en caso de error.

En algunas ocasiones la geolocalización puede fallar, por ejemplo cuando se utiliza el GPS pero no se cuenta con la información de suficientes satélites o cuando el usuario impide el procedimiento.  Para manejar esta conducta se define la función onErrorGeolocating referenciada durante la invocación de navigator.geolocation.getCurrentPosition.

function onErrorGeolocating(error)
{
    // ...
}

La información del error puede obtenerse del objeto PositionError recibido por parámetro el cual cuenta con los siguientes atributos en su interior.

Atributo Descripción
code Código numérico que referencia al tipo de error sucedido.  Su valor corresponde con una de las siguientes constantes 

PERMISSION_DENIED: no se permitió o no se tienen suficientes privilegios para acceder al servicio de geolocalización.

POSITION_UNAVAILABLE: el dispositivo no pudo determinar correctamente su ubicación.

TIMEOUT: el intento de geolocalización tomó mas tiempo del permitido (opción timeout).

message Texto que describe el error sucedido.  Este mensaje se utiliza para depuración en desarrollo, no se debe utilizar para mostrarse al usuario final.

A continuación se muestra un ejemplo básico de implementación de esta función.

function onErrorGeolocating(error)
{
	switch(error.code)
	{
		case error.PERMISSION_DENIED:
			alert('ERROR: User denied access to track physical position!');
		break;

		case error.POSITION_UNAVAILABLE:
			alert("ERROR: There is a problem getting the position of the device!");
		break;

		case error.TIMEOUT:
			alert("ERROR: The application timed out trying to get the position of the device!");
		break;

		default:
			alert("ERROR: Unknown problem!");
		break;
	}
}

El prototipo.

Como demostración de esta API se incluye el siguiente prototipo el cual intenta obtener la ubicación actual del usuario y desplegarla en un mapa de Google Maps.  Se recomienda consultar su código fuente para complementar los contenidos de este artículo.

 

Demostración de geolocalización con Javascript
Demostración de geolocalización con Javascript

Enlaces.

En dónde estoy y hacia dónde me dirijo ? (versión Bing)

Introducción.

Después de implementada una primera versión de este prototipo utilizando el servicio de Google Maps, encontré una seria limitación en la funcionalidad que requería para el experimento: Google Maps no permite trazar rutas entre marcadores en Colombia.  Esta característica es funcional en otros paises y por esto implementé la posibilidad de teletransportarse para probarla, sin embargo en Colombia no es posible.

Investigando encontré que Bing Maps, servicio de mapas similar de Microsoft, si permite realizar el trazado de rutas entre marcadores en Colombia.  Así que en la noche de ayer me dí a la tarea de revisar el API de este proveedor y de crear un nuevo prototipo con él.

Efectivamente el trazado de rutas funciona muy bien.  Con respecto a la comparación de los servicios encontré el API de Bing Maps fácil de entender y con buena documentación en Internet y provee todas las características que se habían utilizado en el prototipo anterior.  Como desventajas aparentes he encontrado que las rutas trazadas parecen no variar (además del tiempo de recorrido) independientemente si se realizan conduciendo o caminando, y el servicio de geocodificación parece ser mucho mas limitado que el de Google.

El prototipo.

Prototipo de aplicación - Where am I and where am I heading to ?
Prototipo de aplicación - Where am I and where am I heading to ?

En dónde estoy y hacia dónde me dirijo ? (versión Google)

Introducción.

Esta semana decidí experimentar un poco mas con la versión 3 del API de Google Maps que fue liberado hace relativamente poco.  Esta vez, quise hacer un prototipo sencillo pero interesante que integrara las principales funcionalidades que el servicio de Google Maps ofrece en los navegadores web actuales.

En este caso utilicé los mapas, la geocodificación y el trazado de rutas de Google y el servicio de geolocalización basada en el navegador web para obtener la siguiente funcionalidad.

  • El servicio de geolocalización del navegador web permite situar al usuario en el espacio sin el uso de un GPS, esto gracias a una aproximación que aparentemente depende de la infraestructura del ISP.  A través de este servicio hace un par de meses lograba una geolocalización casi precisa de mi casa, mientras que recientemente sugiere que vivo en Cali/Valle, una ciudad que queda a unas 3 horas de viaje.
  • El servicio de mapas permite situar gráficamente dos puntos: uno el del usuario geolocalizado y otro, un punto de destino que se elige libremente con el botón izquierdo del ratón.
  • El servicio de rutas de Google permite trazar el camino necesario para llegar desde el punto de orígen hasta el punto de destino distinguiendo si el recorrido se hace caminando o conduciendo un vehículo.  Este servicio se complementa con el servicio de direcciones que determina las indicaciones que se deben seguir para llegar efectivamente al destino elegido.
  • El último servicio utilizado corresponde con el de geocodificación que permite convertir el nombre de una ubicación (una ciudad por ejemplo) a su correspondiente coordenada geográfica (latitud y longitud).  Esto se utiliza para el sistema de teletransportación del usuario que permite ubicarlo en cualquier lugar que se especifique.

El aprovechamiento de estos APIs desde Javascript fue muy simple de implementar y fácilmente se desarrolló el prototipo.  Desafortunadamente Google no cuenta con el servicio de trazado de rutas en Colombia, motivo por el cual estoy revisando los servicios ofrecidos por BingMaps y OpenStreetMaps para continuar mi experimentación.

El prototipo.

Prototipo de aplicación - Where am I and where am I heading to ?
Prototipo de aplicación - Where am I and where am I heading to ?

Enlaces.

Georreferenciar inversamente con el API v3 de Google Maps utilizando Javascript

Introducción.

La georreferenciación inversa es el proceso contrario al descrito anteriormente, corresponde al obtener el nombre de una ubicación (topónimo) a partir de su ubicación geográfica, en términos de latitud y longitud.

La aplicación de demostración.

Aplicación de demostración para la georreferenciación inversa con el API 3 de Google Maps
Aplicación de demostración para la georreferenciación inversa con el API 3 de Google Maps

En el ejemplo equivale a conocer la ubicación del sitio: latitud = 5.073375longitud = -75.495155, y con esta información determinar que los siguientes sitios corresponden con esa posición.

  • Carrera 12, Manizales, Caldas, Colombia.
  • Manizales, Caldas, Colombia.
  • Caldas, Colombia.
  • Colombia.

La implementación.

Su implementación es una versión ligeramente mas simple que la georreferenciación directa.

A continuación se detallan a nivel general las variaciones de esta modalidad de georreferenciación con respecto a la expuesta en un artículo anterior.

Del lado de Javascript.

Se asocia el evento de clic sobre el mapa con la función que realizará la georreferenciación inversa (processReverseGeocoding).

google.maps.event.addListener(map, 'click', function(event) {
    processReverseGeocoding(event.latLng, showMarkerInfo);
});

En esta función se realiza el proceso de georreferenciación inversa el cual, al igual que su contraparte directa, es asíncrono y por este motivo se utiliza la misma estrategia del callback.

var request = {
    latLng: location
}

geocoder.geocode(request, function(results, status) {
    // ...
});

La función showMarkerInfo es ejecutada cuando se finaliza la georreferenciación inversa y esta ha obtenido resultados válidos.  Su misión es la mostrar la información resultante en el mapa.

    function showMarkerInfo(locations)
    {
        // Centra el mapa en la ubicación especificada
        map.setCenter(locations[0].geometry.location);

        // Crea el mensaje para mostrar la información georreferenciada
        var infoWindow = new google.maps.InfoWindow();

        infoWindow.setPosition(locations[0].geometry.location);

        // Prepara el mensaje con la información obtenida del proceso
        // de georreferenciación inversa

        var content = 'Latitud:  ' + locations[0].geometry.location.lat() + '<br />' +
                      'Longitud:  ' + locations[0].geometry.location.lng() + '<br />' +
                      '<br />Topónimos:<br /><ul>';

        for (var i=0; i<locations.length; i++)
        {
            if (locations[i].formatted_address)
                content += '<li>' + locations[i].formatted_address + '</li>';
            else
                content += '<li>No se encontró información.</li>';
        }

        content += "</ul>";

        infoWindow.setContent(content);

        // Muestra el mensaje sobre el mapa

        infoWindow.open(map);
    }

Enlaces.

Georreferenciar con el API v3 de Google Maps utilizando Javascript

Introducción.

La georreferenciación es el proceso de determinar la ubicación (latitud, longitud) de una ubicación en la tierra según su nombre (topónimo).

Si estoy en Manizales, Caldas, Colombia estaré realmente en latitud 5.07, longitud -75.52056.

Existen varias formas de realizar este proceso dependiendo de los lenguajes de programación, interfaces y fuentes de información utilizados.  Anteriormente expliqué como hacerlo utilizando C#/Mono y el servicio web de Geonames, en este caso voy a utilizar el API Javascript versión 3 de Google Maps.

La aplicación de demostración.

Aplicación de demostración para la georreferenciación con el API 3 de Google Maps
Aplicación de demostración para la georreferenciación con el API 3 de Google Maps

Como se mencionó anteriormente, la funcionalidad es precisa: convertir el nombre de una ubicación en su correspondiente localización en términos de latitud y longitud, y mostrarla en el mapa.

La implementación.

Del lado de Javascript.

Incluír la referencia al API de Google Maps.

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

Instanciar el geocodificador.

var geocoder = new google.maps.Geocoder();

Crear el mapa.

window.onload = function() {
    var options = {
        zoom: 5,
        center: new google.maps.LatLng(4, -72),
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };

    map = new google.maps.Map(document.getElementById('map'), options);

    // ...

Establecer la funcionalidad del botón Buscar.

    document.getElementById('search').onclick = function() {
            // Obtiene la ubicación (string) a georreferenciar

            var location = document.getElementById('location').value;

            // Inicia el proceso de georreferenciación (asíncrono)

            processGeocoding(location, addMarkers);

            // Detiene el procesamiento del evento

            return false;
        }
    } // Fin del método window.onload

El proceso de georreferenciación es asíncrono así que es necesario especificar una función (callback) para que se ejecute si la georreferenciación termina exitosamente.

El núcleo de la georreferenciación se realiza en la invocación al método geocoder.geocode(opciones, implementacion).  Es necesario determinar el estado final (status) después de la ejecución asíncrona para determinar que acción puede realizarse con los datos resultantes.

    function processGeocoding(location, callback)
    {
        // Propiedades de la georreferenciación

        var request = {
            address: location
        }

        // Invocación a la georreferenciación (proceso asíncrono)

        geocoder.geocode(request, function(results, status) {

            /*
             * OK
             * ZERO_RESULTS
             * OVER_QUERY_LIMIT
             * REQUEST_DENIED
             * INVALID_REQUEST
             */

            // Notifica al usuario el resultado obtenido

            document.getElementById('message').innerHTML = "Respuesta obtenida: " + status;

            // En caso de terminarse exitosamente el proyecto ...

            if(status == google.maps.GeocoderStatus.OK)
            {
                // Invoca la función de callback

                callback (results);

                // Retorna los resultados obtenidos

                return results;
            }

            // En caso de error retorna el estado

            return status;
        });
    }

La función enviada como callback (addMarkers) es la responsable de tomar las ubicaciones retornadas por la georreferenciación, crearles marcadores, ventanas de información, asociarlas al evento de clic y mostrarlas en el mapa.

   function addMarkers(geocodes)
    {
        for(i=0; i<geocodes.length; i++)
        {
            // Centra el mapa en la nueva ubicación

            map.setCenter(geocodes[i].geometry.location);

            // Valores iniciales del marcador

            var marker = new google.maps.Marker({
                map: map,
                title: geocodes[i].formatted_address
            });

            // Establece la ubicación del marcador

            marker.setPosition(geocodes[i].geometry.location);

            // Establece el contenido de la ventana de información

            var infoWindow = new google.maps.InfoWindow();

            content = "Ubicación: " + geocodes[i].formatted_address + "<br />" +
                      "Tipo: " + geocodes[i].types + "<br />" +
                      "Latitud: " + geocodes[i].geometry.location.lat() + "<br />" +
                      "Longitud: " + geocodes[i].geometry.location.lng();

            infoWindow.setContent(content);

            // Asocia el evento de clic sobre el marcador con el despliegue
            // de la ventana de información

            google.maps.event.addListener(marker, 'click', function(event) {
                infoWindow.open(map, marker);
            });
        }
    }

Del lado de HTML.

Se implementa una estructura muy simple para desplegar la información.  Una primera parte permite desplegar el mensaje de información acerca del estado del proceso y le permite al usuario ingresar el nombre de la ubicación a georreferenciar.

    <!-- Mensaje de estado -->

    <div id="message"></div>

    <!-- Ubicación a georreferenciarse -->

    <label for="location">Ubicación:</label>
    <input type="text" id="location" name="location" value="" size="40" />

    <!-- Botón para inciar la georreferenciación -->

    <input type="button" id="search" name="search" value="Buscar" />

Una segunda parte establece la ubicación para desplegar el mapa.

<!-- Lugar de despliegue del mapa -->

<div id="map"></div>

Enlaces.

Georreferenciando de manera fácil y simple con GeoNames utilizando C#/Mono

Introducción.

Últimamente he tenido ganas de desarrollar en C# para retomar este lenguaje que tengo un poco abandonado y al mismo tiempo probar los avances que ha tenido el proyecto Mono.  Que mejor inicio que desarrollar una aplicación muy simple para georreferenciar ciudades aprovechando el servicio web de Geonames, esta a su vez me servirá para complementarla y solucionar un problema de información que tengo para otro proyecto.

Como se podrá apreciar, el código es muy sencillo ya que no utilizo ninguna librería adicional a las estándar, sin embargo es interesante y puede serle de utilidad a alguien mas.

Cuál es la necesidad ?

El problema que se tiene es la necesidad de georreferenciar ubicaciones, es decir, conociendo su nombre obtener su ubicación en términos de latitud y longitud.

Si estoy en Manizales, Caldas, Colombia estaré realmente en latitud 5.07, longitud -75.52056.

Es necesario tener en cuenta que muchas veces el servicio de georreferenciación retorna múltiples resultados ya que pueden haberse referenciado varios sitios con el mismo nombre, haberse cambiado el nombre de la ubicación o tenerse varios hitos de diferente nivel administrativo.  En Geonames esto último se identifica mediante los campos de características.  Consulte el sitio web de Geonames para mas información acerca de los feature codes.

Cómo es el servicio web ?

El servicio web de búsquedas de Geonames recibe una cadena de texto con la ubicación que se desea georreferenciar y retorna una cadena de texto en formato XML (en este caso) con los resultados obtenidos.

Por ejemplo, la consulta de Manizales, Caldas, Colombia se realizaría a través del siguiente URL.

http://ws.geonames.org/search?lang=es&type=xml&style=FULL&q=Manizales,Caldas,Colombia

Retornará el siguiente resultado.

<geonames style="FULL">
    <totalResultsCount>2</totalResultsCount>
    <geoname>
        <toponymName>Manizales</toponymName>
        <name>Manizales</name>
        <lat>5.07</lat>
        <lng>-75.52056</lng>
        <geonameId>3675443</geonameId>
        <countryCode>CO</countryCode>
        <countryName>Colombia</countryName>
        <fcl>P</fcl>
        <fcode>PPLA</fcode>
        <fclName>city, village,...</fclName>
        <fcodeName>seat of a first-order administrative division</fcodeName>
        <population>357814</population>
        <alternateNames>Manisales,Manisalesas,Manizales,Манисалес</alternateNames>
        <elevation/>
        <continentCode>SA</continentCode>
        <adminCode1>37</adminCode1>
        <adminName1>Caldas</adminName1>
        <adminCode2/>
        <adminName2/>
        <alternateName lang="en">Manizales</alternateName>
        <alternateName lang="pl">Manizales</alternateName>
        <alternateName lang="lt">Manisalesas</alternateName>
        <alternateName lang="bg">Манисалес</alternateName>
        <alternateName lang="cs">Manizales</alternateName>
        <alternateName lang="link">http://en.wikipedia.org/wiki/Manizales</alternateName>
        <alternateName lang="de">Manizales</alternateName>
        <alternateName lang="sv">Manizales</alternateName>
        <alternateName lang="es">Manizales</alternateName>
        <alternateName lang="no">Manizales</alternateName>
        <alternateName lang="pt">Manizales</alternateName>
        <timezone dstOffset="-5.0" gmtOffset="-5.0">America/Bogota</timezone>
        <score>1.0</score>
    </geoname>
    <geoname>
        <toponymName>Manizales</toponymName>
        <name>Manizales</name>
        <lat>5.08333</lat>
        <lng>-75.5</lng>
        <geonameId>3675444</geonameId>
        <countryCode>CO</countryCode>
        <countryName>Colombia</countryName>
        <fcl>A</fcl>
        <fcode>ADM2</fcode>
        <fclName>country, state, region,...</fclName>
        <fcodeName>second-order administrative division</fcodeName>
        <population/>
        <alternateNames/>
        <elevation/>
        <continentCode>SA</continentCode>
        <adminCode1>37</adminCode1>
        <adminName1>Caldas</adminName1>
        <adminCode2>3675444</adminCode2>
        <adminName2>Manizales</adminName2>
        <timezone dstOffset="-5.0" gmtOffset="-5.0">America/Bogota</timezone>
        <score>0.3448537290096283</score>
    </geoname>
</geonames>

Finalmente la tarea de la aplicación que consuma el servicio será procesar el documento XML y obtener la información que necesite.

Cómo es la implementación ?

Las librerías (namespaces) utilizados son los siguientes.

using System;
using System.Net;
using System.Text;
using System.Xml;
using System.Collections.Generic;

Se prepara el URL para consumir el servicio con la ubicación a geocodificar.  En este caso, especifico una por defecto si no suministra una.

string location = (args.Length == 0) ? "Manizales, Caldas, Colombia" : args[0];
string url = "http://ws.geonames.org/search?lang=es&type=xml&style=FULL&q=" + location;

Se consume el servicio web y se obtiene la cadena de texto con la respuesta en formato XML.

WebClient client = new WebClient();
string xml = client.DownloadString(url);

Se crea un objeto para manipular el documento XML recién recibido, se obtienen los nodos relacionados con la respuesta de la georreferenciación (geoname)  y se verifica que haya registros, de lo contrario no habrá nada que hacer.

XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);

XmlNodeList geonames = doc.GetElementsByTagName("geoname");

if(geonames.Count == 0)
 throw new Exception("There is no geonames in the result!");
else
 Console.WriteLine("I found " + geonames.Count + " places!n");

Se recorren los resultados obtenidos extrayéndoles la información georreferenciada que se requiera.

foreach(XmlElement place in geonames)
{
 Console.WriteLine("* LUGAR: " + place["name"].InnerText + "n");

 Console.WriteLine("tNombre: "          + place["name"].InnerText);
 Console.WriteLine("tDepartamento: "    + place["adminName1"].InnerText);
 Console.WriteLine("tLatitud: "         + place["lat"].InnerText);
 Console.WriteLine("tLongitud: "        + place["lng"].InnerText);
 Console.WriteLine("tPaís: "            + place["countryName"].InnerText +
                   " (" + place["countryCode"].InnerText + ")");
 Console.WriteLine("tPoblación: "       + place["population"].InnerText + " habitantes");
 Console.WriteLine("tPaís: "            + place["countryName"].InnerText +
                   " (" + place["countryCode"].InnerText + ")");
 Console.WriteLine("tCaracterísticas: " + place["fcl"].InnerText + "/" + place["fcode"].InnerText +
                   "> " + place["fclName"].InnerText + " (" + place["fcodeName"].InnerText + ")");

 Console.WriteLine();
}

Cómo construyo el programa ?

Si se utiliza VisualStudio/MonoDevelop sólo es necesario abrir la solución (incluída en el paquete de demostración) y ejecutar el comando de Build o Run del menú.

Si se desea realizar manualmente deberá ejecutarse un comando como el siguiente.

$ gmcs “/out:GeoCoderSimple.exe” “/r:/usr/lib/mono/2.0/System.dll” “/r:/usr/lib/mono/2.0/System.Xml.dll” /t:exe “geonames_search_raw/Main.cs

Donde geonames_search_raw/Main.cs es el código fuente de la aplicación y GeoCoderSimple.exe será el archivo binario resultante.

Cómo ejecuto el ejemplo ?

Ya sea invocándolo desde el IDE o desde línea de comando, el programa recibe un argumento.  En caso de obviarse este argumento se utilizará el valor por defecto.

$ ./GeoCoderSimple.exe

I found 2 places!

* LUGAR: Manizales

 Nombre: Manizales
 Departamento: Caldas
 Latitud: 5.07
 Longitud: -75.52056
 País: Colombia (CO)
 Población: 357814 habitantes
 País: Colombia (CO)
 Características: P/PPLA> city, village,... (seat of a first-order administrative division)

* LUGAR: Manizales

 Nombre: Manizales
 Departamento: Caldas
 Latitud: 5.08333
 Longitud: -75.5
 País: Colombia (CO)
 Población:  habitantes
 País: Colombia (CO)
 Características: A/ADM2> country, state, region,... (second-order administrative division)

Enlaces.

Convertir coordenadas geográficas grados-minutos-segundos a notacional decimal (2): versión simple

Introducción.

El método que utilizo habitualmente para realizar la conversión entre notaciones de coordenadas se basa en el método descrito anteriormente y es mas simple aunque aparentemente tiene un ligeramente mayor margen de pérdida de decimales en los cálculos.

A continuación se describe rápidamente las fórmulas matemáticas utilizadas para estos cálculos y se presentan los enlaces para la aplicación de deomstración y el formulario en Excel para pruebas.

Procedimiento.

Notación Grados-Minutos-Segundos a notación decimal.

latitude_sign = (latitude_dir == “N”) ? 1 : -1;
latitude  = (latitude_deg  + (latitude_min / 60.0)  + (latitude_sec / 60.0 / 60.0))  * latitude_sign;

longitude_sign = (longitude_dir == “E”) ? 1 : -1;
longitude = (longitude_deg + (longitude_min / 60.0) + (longitude_sec / 60.0 / 60.0)) * longitude_sign;

Notación Decimal a notación Grados-Minutos-Segundos.

latitude_deg = Math.floor(Math.abs(latitude));
latitude_min = Math.floor((Math.abs(latitude) – latitude_deg) * 60);
latitude_sec = Math.ceil(((Math.abs(latitude) – latitude_deg) * 60 – latitude_min) * 60);
latitude_dir = (latitude_sign > 0) ? “N” : “S”;

longitude_deg = Math.floor(Math.abs(longitude));
longitude_min = Math.floor((Math.abs(longitude) – longitude_deg) * 60);
longitude_sec = Math.ceil(((Math.abs(longitude) – longitude_deg) * 60 – longitude_min) * 60);
longitude_dir = (longitude_sign > 0) ? “E” : “W”;

Enlaces.

Convertir coordenadas geográficas grados-minutos-segundos a notacional decimal

Historial.

Versión 0.2 Versión mejorada apropiando el código de  Anthony T. Holdener.
(mayor precisión en los cálculos) 
Versión 0.1a Simplificación de los cálculos realizados.
Versión 0.1 Basado en la implementación de la aplicación de FCC.
(este artículo) 

Introducción.

Finalmente pude encontrar el método para convertir las coordenadas geográficas desde la notación grados-minutos-segundos a la notación decimal y viceversa.  La aplicación de la FCC para esta conversión me ayudó bastante.

La utilidad de esta conversión radica en que muchas veces se manejan las coordenadas en el formato GMS (latitud 5o4’59” norte y longitud 75o30’0″ oeste) mientras que muchas páginas web y aplicaciones (incluído Google Maps) requieren la información en la notación decimal (latitud 5.083056 y longitud -75.5).

Durante los experimentación se creó una clase en Javascript que expone dos métodos simplificados para realizar estas conversiones.  Los cálculos pueden ser fácilmente migrados a otros lenguajes de programación ya que sólo utilizan funciones matemáticas como floor, abs y round además de los operadores aritméticos.

Demostración.

Para la prueba y demostración de la clase Javascript se desarrolló un formulario muy sencillo que permite realizar la transformación de coordenadas geográficas entre las dos notaciones mencionadas.

Demostración de la conversión de coordenadas
Demostración de la conversión de coordenadas

En la demostración puede verificar las coordenadas enunciadas anteriormente las cuales corresponden con la ciudad de Manizales/Caldas/Colombia.

Notación GMS.

  • latitud: 5o4’59” N
  • longitud: 75o30’0″ W

Notación decimal.

  • latitud: 5.083056
  • longitud: -75.5

Enlaces.