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.

Plantillas para documentos XHTML y otros recursos web

En el sitio de WebStandards se puede encontrar un listado con las plantillas base para los documentos XHTML 1.0/1.1 y HTML4.

Además de estos documentos encontré otros recursos interesantes relacionados con el desarrollo web.

http://www.webstandards.org/learn/tutorials/accessible-forms/

Jugando con InnerHTML (3)

Al ejemplo anterior de tablas dinámicas utilizando JavaScript que permitía agregar y eliminar filas dinámicamente del lado del cliente le hice algunas modificaciones que encontré necesarias cuando lo utilicé en un caso práctico. El ejemplo permite ahora agregar un documento de identidad que actuará como llave primaria del registro. El documento se muestra como texto estático y además se incluye como campo oculto (hidden) para que sea reconocido por la aplicación del lado del servidor.

Los demás campos son presentados en la tabla como campos editables (input) para permitir su modificación por parte del usuario hasta el momento de hacer el envío.

El ejemplo puede consultarse en el siguiente enlace: Test InnerHTML 3

Jugando con InnerHTML (2)

Bien, encontré una mejor forma de hacerlo … con DOM.

Insertar una nueva fila en una tabla (destino) es fácil, sólo es establecer donde se quiere e insertarla.

var nuevo_indice = destino.rows.length;
fila = destino.insertRow(nuevo_indice);

Después es necesario especificar el contenido de las celdas, para el ejemplo, un checkbox y dos valores de texto.

celda = fila.insertCell(0);
valor = document.createElement("input");
valor.type = "checkbox";
valor.id = "marca";
valor.name = "marca";
valor.value = nuevo_indice;
celda.appendChild(valor);

celda = fila.insertCell(1);
valor = document.createTextNode(nombres + " - " + nuevo_indice);
celda.appendChild(valor);

celda = fila.insertCell(2);
valor = document.createTextNode(apellidos);
celda.appendChild(valor);

Para remover las filas obtengo una referencia a los elementos que sean input y pertenezcan a la tabla.

var inputs = origen.getElementsByTagName("input");

Después filtro los que sean checkbox y sean los que me interesan (id=’marca’). Los almaceno en un arreglo.

for (i=0; i<inputs.length; i++)
{
if (inputs[i].type == "checkbox" &&
inputs[i].id == "marca" &&
inputs[i].checked)
{
checkboxes[chk_cuenta] = inputs[i];
chk_cuenta ++;
}
}

Después de filtrados procedo a eliminarlos.

for (i=0; i<checkboxes.length; i++)
{
origen.deleteRow(checkboxes[i].value - 1*i);
}

En este punto encontré un serio problema. El value de los checkboxes seleccionados me indica cuales son las filas que deseo remover, pero después de remover la primera de ellas la continuidad de las filas se altera ya que se eliminó un elemento. Para compensar esto se disminuye 1*i el índice de la fila, siendo 0 para el primer caso cuando no se ha alterado, 1 cuando se ha removido una fila y así sucesivamente.

La solución anterior soluciona el problema durante un procedimiento de eliminación de filas, sin embargo al terminar la tabla queda con sus índices alterados y por consiguiente va a fallar la siguiente eliminación y posiblemente la adición de filas también va a ser confusa. Para esto tuve que corregir los índices de las filas tan pronto como se termina la remoción.

for (i=0; i<inputs.length; i++)
{
if (inputs[i].type == "checkbox" &&
inputs[i].id == "marca")
{
inputs[i].value = i + 1;
}
}

El archivo fuente del ejemplo se puede descargar del siguiente enlace: test_inner2.html

Jugando con InnerHTML

Con este ejemplo muy sencillo es posible agregar filas de una tabla en el lado del cliente sin hacer un requerimiento al servidor y ahorrarse uno la consabida lentitud, útil en momentos de agregar items a una lista. Obviamente, a diferencia de Ajax, los elementos sólo viven en el lado del cliente hasta que se envían, en un sólo requerimiento, hacia el servidor donde son almacenados en la base de datos.

La idea es crear una tabla (info) que va a contener los datos.

<table id="info" name="info" class="tabla">
<tr class="titulos">
<td>
Nombres
</td>
<td>
Apellidos
</td>
</tr>
</table>

Un formulario desde el cual se agregarán nueva información.

<form id="datos" name="datos" onsubmit="return agregar_fila(this)">
Nombres: <input type="text" id="nombres" name="nombres" value="" size="25" />
Apellidos: <input type="text" id="apellidos" name="apellidos" value="" size="25" />

<input type="submit" id="agregar" name="agregar" value="Agregar" />
</form>

Y una función de JavaScript que agregará la nueva información (nombres y apellidos) a la tabla.

function agregar_fila(origen)
{
nom = origen.nombres.value;
apl = origen.apellidos.value;

document.getElementById("info").innerHTML += "<tr class='fila'>
<td>
" + nom + "
</td>
<td>
" + apl + "
</td>
</tr>";

return false;
}

El archivo de ejemplo puede descargarse desde el siguiente enlace: test_inner.html

Por fin un ejemplo de Box Model entendible (2da. parte)

Bueno, no me aguanté las ganas de seguir haciendo pruebas. Esta vez con Layout #13 de Layout Gala.

El ejemplo anterior tomado de Matthew Levine estaba elegante pero no quiso funcionar en IE.

Simplifiqué un poco el ejemplo y esto fue lo que obtuve.

test_div2a.gif

Me hace falta lograr que las columnas izquierda (azul) y derecha (rojo) ocupen tanto espacio como ocupe la columna central (salmón), sin embargo si utilizo height: 100% intentan tomar el alto de la pantalla.

Por ahora este es un gran paso para -mi- humanidad en la lucha contra las cajas.

El archivo de prueba puede descargarse del siguiente enlace: test_div2a.html.

Por fin un ejemplo de Box Model entendible

Hace hace muy poco no había querido trabajar con CSS. Siempre encontraba una rápida respuesta para mejor hacerlo de la manera convencional … es mas rápido, no tengo tantos problemas, no se complica tanto, etc. En el último par de meses en los que he estado de webmaster adaptando la presentación de algunos portales a la provista por el publicista de la Fundación he aprendido algunas cositas y como todo lo que aprendo, me ha terminado gustando mucho.

Una de las cosas de las que en realidad no he logrado entender completamente es el box model (modelo de cajas) de CSS. Yo quiero hacer lo que la teoría sugiere: en el HTML dejar sólo el contenido y en CSS especificar como se va a presentar esa información. Según esto, yo quiero que el layout (distribución) de mi página esté dada por el CSS con DIVs y no en el HTML con TABLE. Pero ah que cosa mas extraña para mi.

Le he sacado tiempo en varias oportunidades pero no lo he logrado. Lo mas cerca que he estado funcionaba perfecto en Firefox pero se corría inexplicablemente en IE … como siempre.

Hoy amanecí con ganas de volver a intentarlo y encontré un par de enlaces muy interesantes. Entre ellos me gustó In Search of the Holy Grail de Matthew Levine quien se toma su tiempo para explicar la implementación de una página con tres columnas, dos de ellas de ancho fijo y el centro líquido (consume el tamaño complementario) y con una estructura de DIVs simple, tal y como yo la quería.

<div id="header">Este es el header</div>
<div id="container">

<div id="center" class="column">Este es el centro</div>
<div id="left" class="column">Esta es la izquierda</div>
<div id="right" class="column">Esta es la derecha</div>
</div>
<div id="footer">Estos son los pies</div>

Este código HTML produce el siguiente resultado después de ser mezclado con el CSS correspondiente.

demo_css_div

Como siempre … en Firefox funciona perfecto, lo acabo de probar en IE7 y se enloquece, a pesar de que el autor menciona que funciona en los principales navegadores.

El archivo de prueba que utilicé se puede descargar del siguiente enlace: test_div.html.

Espero mas adelante tener un poco mas de tiempo y hacerlo funcionar ya que no quiero seguir haciendo páginas basadas en TABLEs.

Otro enlace relacionado con el tema que se ve prometedor es Layout Gala (http://blog.html.it/layoutgala/) en el que se presentan varios diseños basados en columnas.

Problemas entre capas y objetos Flash en HTML

Tenía que publicar en uno de los portales un bloque con un logo hecho en Flash. La puesta fue muy fácil, el problema apareció cuando los menúes desplegables empezaron a aparecer por debajo del objeto Flash.

Hice pruebas envolviendo el object en un div y modificando el z-index, pero nadita.

Los objetos Flash traen ahora una propiedad que se llama wmode a la cual si se le asigna el valor de transparent no solo hace el fondo del SWF transparente sino que hace que el objeto respete el índice de profundidad.

Hay que poner el valor sugerido como parámetro del object y dentro de la etiqueta embed para que funcione en los dos navegadores.

Ejemplo:

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0" width="185"
height="205">
<param name="wmode" value="transparent">
<param name="movie" value="banner_iso9001_ntcgp1000.swf">
<param name="quality" value="high">
<embed src="banner_iso9001_ntcgp1000.swf" quality="high"wmode="transparent"
pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash"
type="application/x-shockwave-flash"
width="185"
height="205"></embed>
</object>