martes, 19 de enero de 2016

Libro Google maps Javascript - Capítulo 4: Tareas Comunes en Google Maps

 

 

Volver al indice

 

 

Trabajando con múltiples overlays


Hasta ahora hemos aprendido como crear overlays simples de uno en uno,  pero existen  ocasiones donde tenemos que crear más de un overlay a la vez. Por ejemplo supongamos que tenemos que agregar 5 overlays markers en el mapa, esto lo podríamos hacer perfectamente creando uno por uno ¿ Pero, qué pasa si tenemos que agregar 20 overlays markers al mapa ? nuestro código serian líneas repetidas 20 veces.

En estos casos es necesario usar un ciclo FOR, para crear los overlays de manera automática y liberarnos del problema. Podemos abrir  la carpeta de ejemplo numero “13-0”  y ejecutar el archivo “index.html”, la cual cargará nuestro mapa. Inicialmente solo se muestra el mapa pero si hacemos click en el botón del lado izquierdo “click me” verás aparecer 15 overlays markers. Para ver como funciona este ejemplo, podemos abrir el archivo “googleMaps.js” en el cual encontraremos 2 métodos que son ejecutados en el evento de click del botón “click me”.

$('#actionButton').on('click',function(){   
    var arrayLatlng = CreateLatLngArray();
    CreateMarkers(arrayLatlng);
});

El método “CreateLatLngArray” lo usamos para generar puntos latlng aleatorios, que usaremos luego para crear los overlays markers. Este método nos devuelve, un arreglo de objetos donde cada elemento del arreglo es un objeto que tiene 2 propiedades “lat” y “lng” de la siguiente manera :

{
    lat:41.4220,
    lng:-97.4373
}

Con este arreglo guardado en la variable “arrayLatlng”, crearemos los overlays pasándolo como parámetro al  método llamado “CreateMarkers” que es el encargado de crear los overlays.


function CreateMarkers (latlngs){
    var arrayMarkers = [];
    for (var i = 0; i < latlngs.length; i++) {   
        var latlng = new google.maps.LatLng(latlngs[i].lat,latlngs[i].lng);   
        var marker = new google.maps.Marker({
            position: latlng,
            map: map
        });   
        arrayMarkers.push(marker);
    }
    return arrayMarkers;
}

En este método usamos un ciclo FOR, para iterar en el arreglo de objetos que contiene los  puntos lalngs. Dentro del ciclo primero creamos el punto latlng que será usado por nuestro overlay marker, usando las propiedades “lat” y “lng” del elemento actual.  Con el latlng creado solo queda pasarlo como parámetro a la clase google.maps.Marker para crear nuestro overlay. Cada overlay creado es guardado en el arreglo “arrayMarkers” y  devuelto por el método. Una cosa importante que tienes que notar es que la variable “map” es global para nuestro ejemplo, de esa manera podemos usarla desde nuestro método.

Hasta aquí todo trabaja perfectamente en nuestro ejemplo, pero vamos hacer algunas modificaciones para que cada overlay marker tenga un infowindows. En nuestro ejemplo existe una variable global llamada “infowindow”, donde se ha guardado un objeto infowindow creado  a partir de la clase “google.maps.InfoWindow”, usaremos este objeto para todos los overlays markers.

Vamos a crear un método llamado “AttachEventInfowindow” que recibe como parámetro un overlay marker y el index del ciclo FOR, y a continuación creará  el evento click para este overlay.

function AttachEventInfowindow(currentMarker,index) {
    google.maps.event.addListener(currentMarker, 'click', function() {
        infowindow.setContent("Hi I am the marker number : " + index);
        infowindow.open(map,this);
    });
}

Cada vez que este método es ejecutado, creará un evento click sobre el overlay marker. Cuando el usuario haga click sobre el overlay marker, se asignará el contenido a mostrar al objeto infowindow con “setContent” y después abrirá el infowindow en el mapa con el método “open”, que recibe como parámetros el mapa donde aparecer y un punto ancla que segun la documentacion es aconsejable que sea  un overlay marker.

Si te estás preguntando por que el segundo parámetro del método “open” se llama “this”, es por que cuando el evento click ocurre y se ejecuta este método,”this” esta haciendo referencia al objeto al cual se ha hecho click, en este caso es un overlay marker.

Ahora necesitamos llamar al método desde “CreateMarkers” :

function CreateMarkers (latlngs){
    var arrayMarkers = [];
    for (var i = 0; i < latlngs.length; i++) {   
        var latlng = new google.maps.LatLng(latlngs[i].lat,latlngs[i].lng);   
        var marker = new google.maps.Marker({
            position: latlng,
            map: map
        });   
        AttachEventInfowindow(marker,i);   
        arrayMarkers.push(marker);
    }
    return arrayMarkers;
}

Cada vez que un overlay marker es creado, lo pasamos como parámetro al método “AttachEventInfowindows” junto con el índice del ciclo FOR, para crear el evento sobre el overlay actual. Y esto es todo, para que al hacer click en cualquiera de los overlay marker creados, aparezca una infowindow. Para ver un ejemplo completo, puedes ver el archivo de ejemplo en la carpeta numero “13-1”.

Nota: La creacion del evento click para los overlay marker, tiene su propio método fuera del ciclo FOR,  por que no es posible crear eventos dentro de un ciclo. Si creas un evento dentro de un ciclo FOR, el método que crea el evento recibe la variable en sí misma y no su valor,  entonces cuando el ciclo FOR termina de ejecutarse, las variables contienen el ultimo valor asignado por el  ciclo, por lo cual se pierden todos los datos anteriores para el  evento.

Esto sucede porque el evento click, se ejecuta mucho después cuando el ciclo FOR a terminado, por lo tanto el método click solo puede acceder al valor de la última iteración del ciclo FOR.  Para entender un poco esto, recomiendo leer este artículo de Douglas Crockford y también uno es español aqui.

Demasiados Maker en el mapa


Para que tengamos una mejor idea de lo que vamos tratar en este capítulo, vayamos a la carpeta de práctica número “13-2” y abramos el “index.html”. Veremos nuestro mapa vacío, si  ahora hacemos click en el botón “click me”, verás como aparecen muchos marker en el mapa de forma aleatoria , entre más veces hagas click mas overlay marker van aparecer.


Como puedes ver hay demasiados markers en el mapa, lo cual hace difícil para el usuario enfocarse en un punto determinado, y que el desempeño del mapa sea más lento entre más markers hay en el viewport . Hay que aclarar algo primero,  en dependencia del navegador que el usuario esté usando, el mapa se pondra mas lento. Por ejemplo Google Chrome soporta más markers en pantalla que Internet Explorer.

Si revisamos el código de nuestro ejemplo, podemos ver que el responsable de generar los markers aleatorios es un método llamado “GenerateRandomMarkers”, que se ejecuta cada vez que hay un click en el botón. Hablando de forma general el método “GenerateRandomMarkers”,  toma el bounds del viewport, después calcula un punto medio dentro del bounds, el cual después es multiplicado por un número al azar para generar latLng aleatorios. No explicare como funciona este método en detalle porque esta fuera del ámbito del libro, que es aprender usar Google Maps.

Ahora lo importante, resolveremos este problema de muchos markers en pantalla para el usuario con librerías externas a Google Maps.



MarkerClusterer


MarkerCluster es una librería del lado del cliente que nos ayuda a reducir el número de markers visibles en pantallas, y reemplazarlos  por una representación en número de acuerdo al zoom del mapa y a la proximidad de cada uno.

La librería puede ser encontrada en este link:

El archivo que necesitamos es “markerclusterer.js” que se encuentra en “src” , en el mismo link puedes encontrar ejemplos y la ayuda de referencia para ver las opciones de la librería.

Para ver esta librería en acción podemos abrir el archivo de práctica de la carpeta “14” y abrimos el “index.html”. Veremos a continuación que aparecen unos círculos de colores llamados “cluster” con un número en el centro, este número es la cantidad de markers en esa zona .








Hablemos un poco del código,  lo primero que debes notar es que en la carpeta “14”, hay un nuevo archivo llamado  “markerclusterer.js”  este archivo es la librería que hemos descargado y también en el archivo “index.html” ahora tenemos esta nueva línea, que agrega la librería a nuestro ejemplo.

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

Esto es todo lo que necesitamos para cargar la librería en nuestro ejemplo. Ahora pasemos a ver un poco de como usarla. Lo primero que tienes que notar en este ejemplo, es el llamado al método “GenerateRandomMarkers”, ahora se encuentra en nuestro evento “'idle” del mapa para crear los markers al inicio de la aplicación. También el método  “GenerateRandomMarkers” ha sido modificado un poco para adecuarlo para el uso de la librería:

function GenerateRandomMarkers(){
    var markerRandom = [] ;   
    var bounds = map.getBounds();
    var southWest = bounds.getSouthWest();
    var northEast = bounds.getNorthEast();
    var latSpan = northEast.lat() - southWest.lat();
    var lngSpan = northEast.lng() - southWest.lng();   
    for (var i = 0; i < 300; i++) {   
        var lat = southWest.lat() + latSpan * Math.random();
        var lng = southWest.lng() + lngSpan * Math.random();
        var latlng = new google.maps.LatLng(lat, lng);       
        var marker = new google.maps.Marker({
            position: latlng,
            map: map
        });
        markerRandom.push(marker);
    }
    return markerRandom;
}

Primero se declara un arreglo llamado “markerRandom”, después por cada marker generado aleatoriamente lo guardamos en una variable llamada “marker”, la cual es usada para agregar un nuevo elemento al arreglo, de esta manera por cada marker generado agregamos un elemento al arreglo. Después cuando el ciclo FOR termina, devolvemos el arreglo con todos los elementos, donde cada uno de ellos es un marker.

Hasta aquí sólo hemos generado un arreglo, donde cada elemento del mismo es un marker aleatorio en el mapa. Ahora pasamos a la siguiente línea de código:

google.maps.event.addListenerOnce(map, 'idle', function(){
    var markerArray = GenerateRandomMarkers();
    var markerclusterer = new MarkerClusterer(map, markerArray);
});

El resultado de “GenerateRandomMarkers” , lo usamos como parámetro para crear nuestro “markerclusterer” haciendo un instancia de la clase, la cual recibe los siguientes parametros:

MarkerClusterer(map:Map ,  markers?:Array , options?:Object )

Con esto es suficiente, para que nuestro markerclusterer funcione en nuestro mapa. A continuación veremos un poco qué es lo que significan esos colores de los clusters.

  • Azul : menos de 10 markers.
  • Amarillo: 10 a 100 Markers.
  • Red : Menos de 1000 markers.
  • Purpura : Menos de 10.000 markers.
  • Purpura Oscuro : Mas de 10.000 markers.

Para métodos adicionales y configuración, consulta la ayuda de referencia de la librería.


Marker Spiderfier


Esta librería no oculta los markers en el mapa, sino que los expande en una especie de árbol para una mejor visualización, por lo que no mejoraría el rendimiento del mapa en caso de que tengas muchos overlay markers. Abramos el archivo “index.html” de la carpeta de practica numero “15”,  y hacemos click en unos de los marker veremos como se expanden y contraen con cada click.

La librería y su documentación puede ser encontrada aquí Overlapping Marker Spiderfier

El codigo para usar esta librería se ha modificado un poco. Ahora pasaremos a explicar un poco de el. La parte principal donde hay que poner mucha atención es esta:

function GenerateRandomMarkers(){   
    var bounds = map.getBounds();
    var southWest = bounds.getSouthWest();
    var northEast = bounds.getNorthEast();
    var latSpan = northEast.lat() - southWest.lat();
    var lngSpan = northEast.lng() - southWest.lng();   
    var lat = southWest.lat() + latSpan * Math.random();
    var lng = southWest.lng() + lngSpan * Math.random();
    var latlng = new google.maps.LatLng(lat, lng);       
    var marker = new google.maps.Marker({
        position: latlng,
        map: map
    });   
    return marker;
}



Ahora nuestro método que genera nuestros random markers, ya no devuelve un arreglo de markers. Ahora solo devuelve un marker cada vez que es ejecutado este método, y también tenemos lo siguiente.

google.maps.event.addListenerOnce(map, 'idle', function(){
    oms = new OverlappingMarkerSpiderfier(map);
    for (var i = 0; i < 300; i++) {
        var markerCreated = GenerateRandomMarkers();
        oms.addMarker(markerCreated);
    }
    map.setZoom(3);
});
   
Lo primero que estamos haciendo es una instancia de la clase “OverlappingMarkerSpiderfier”,  pasando como único parámetro el objeto mapa. Ahora nuestro ciclo FOR, esta afuera de nuestro método y ejecutamos el método dentro de él. Lo que conseguimos con esto, es un marker en cada ciclo que podemos asignar a la ”oms.addMarker()” que según la documentación, solo recibe como parámetro un único marker y no un arreglo, es por esa razón  que el código de fue escrito de esta manera.

Para usar esta librería adecuadamente, primero tienes que identificar las zonas donde los marker esta aglomerados y muy cercanos y después agregarlos. Para ver en detalle más métodos y configuraciones  la página oficial de esta librería .

MarkerManager


MarkerManager es otra librería para trabajar con markers, aunque implica un poco más de trabajo la configuración. Básicamente lo que hace es,  por cada  nivel de zoom tu decides que Markers mostrar en el mapa, de esta manera cuando el usuario hace zoom In o zoom out en el mapa,  MarkerManager oculta ó  muestra Markers basado en la configuración que decidas.

Para esta práctica vamos a trabajar de un manera un poco diferente, anteriormente siempre teníamos el ejemplo completo y después explicaba el código, Ahora vamos hacerlo paso a paso para un mejor comprensión. Así que para comenzar abre el archivo de pratica “16”, y ejecuta el “index.html” el cual muestra un mapa con 6 markers localizados en diferentes zonas.







Lo primero a notar en este ejemplo es el archivo “index.html”, donde hemos agregado la librería MarkerManager. He usado la versión “packed” de la librería que es más pequeña.

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

Después tenemos en nuestro script “googlemaps.js”, el siguiente método que  se encarga de crear los markers  y ubicarlos en el mapa.

function GetMarkers() {
    var locationMarker = [
    {"lat":40.1284,"lng": -98.4594},
    {"lat":40.6181,"lng": -99.3988},
    {"lat":40.3214,"lng":-97.3223},
    {"lat":35.3980,"lng": -81.8536},
    {"lat":35.4360,"lng": -81.0186},
    {"lat":34.8859,"lng": -81.0928}
    ];
    var markers = [];
    for (var i=0; i < locationMarker.length;i++)
    {
        var myLatlng = new google.maps.LatLng(locationMarker[i].lat,locationMarker[i].lng);
        var marker = new google.maps.Marker({
            position: myLatlng,
            title:"Hello I am " + i
        });
        markers.push(marker);
    }
    return markers;
}


En este método, existe un arreglo de objetos que contienen las coordenadas LatLng de los markers y lo único que hacemos es recorrer ese arreglo con el FOR, los cuales vamos agregando a un arreglo llamado “markers” que es devuelto por el método para usarlo después.  Hay que notar que no estamos asignando todavía los overlay marker al mapa. Este método es llamado en  evento “idle” del mapa.

Ahora vamos a crear nuestro objeto MarkerManager, el único parámetro obligatorio para crearlo es el objeto mapa.  

MarkerManager(map:Map, options?:Object)

Sabiendo esto vamos agregar la siguiente línea de código :

google.maps.event.addListenerOnce(map, 'idle', function(){
    var markersInMap = GetMarkers();
    mgr = new MarkerManager(map);
});

Con esto hemos creado nuestro objeto MarkerManager, ahora solo basta con agregar los markers que hemos creado anteriormente. MarkerManager tiene un método llamado “addMarkers()”  el cual recibe como parámetros un arreglo de overlays arker y el nivel de zoom mínimo en el cual los markers deben aparecer.

addMarkers(markers:Array of Marker, minZoom:Number, opt_maxZoom:Number)

Antes de usar este método tienes que saber que MarkerManager trabaja a modo asíncrono, esto significa que tenemos que esperar hasta que MarkerManager esté listo para trabajar con el,    igualmente a como lo hacemos con el objeto mapa de Google Maps. Esto lo podemos hacer escuchando el evento “loaded” del objeto MarkerManager.

google.maps.event.addListenerOnce(map, 'idle', function(){
    var markersInMap = GetMarkers();
    mgr = new MarkerManager(map);       
    google.maps.event.addListener(mgr, 'loaded', function() {
        //your code here
    });
});   

Ahora podemos usar “addMarkers”, pasando nuestro arreglo de markers que devuelve el método “GetMarkers” y especificando el zoom mínimo donde queremos que aparezcan. En este caso usaremos 5 como zoom mínimo.


google.maps.event.addListener(mgr, 'loaded', function() {
    mgr.addMarkers(markersMap, 5);
    mgr.refresh();
});

Notar aquí que no estamos asignando los markers al mapa, sino al objeto MarkerManager. El método “refresh” se asegura que el objeto MarkerManager funciona correctamente,  aunque no es necesario en nuestro ejemplo, puedes usarlo para actualizar el MarkerManager si agregas markers dinámicamente a tu objeto.

Ahora si actualizas nuestro ejemplo podrás ver que no aparece ningún marker en el mapa, pero si te acercas haciendo zoom lo suficiente aparecerán los markers en el mapa. Esto es porque  inicialmente nuestro mapa tiene un zoom de 3 y al llegar al nivel 5, MarkerManager muestra los markers que fueron agregados.

Para ver la documentación MarkerManager puedes visitar este link


Usando MarkerManager como Cluster


Un uso muy conveniente que podemos lograr con MarkerManager, es crear nuestros propios cluster personalizados para nuestros markers. Para lograr esto haremos uso de algunos iconos que podemos encontrar en : http://mapicons.nicolasmollet.com/, usando el mismo ejemplo anterior.

Ahora lo primero que vamos a crear, será un arreglo que contenga nuestros clusters :

google.maps.event.addListenerOnce(map, 'idle', function(){
    var markersMap = GetMarkers();
    mgr = new MarkerManager(map);   
   
    var phantom = new google.maps.Marker({
        position: new google.maps.LatLng(40.1284, -98.4594),
        icon: 'phantomWhite.png'
    });   
    var phantomWhite = new google.maps.Marker({
        position: new google.maps.LatLng(35.4360, -81.0186),
        icon: 'phantom.png'
    });




    var cluster = [phantom ,phantomWhite];   
    google.maps.event.addListener(mgr, 'loaded', function() {
        mgr.addMarkers(markersMap, 5);
        mgr.refresh();
    });
});

Primero creamos 2 markers con iconos personalizados en una ubicación que nosotros queramos, por ejemplo esos valores Latlng en nuestro ejemplo son en el centro del grupo de markers del ejemplo anterior. Después asignamos estos markers a un arreglo llamado “cluster”

El siguiente paso, es agregar nuestro arreglo de clusters al MarkerManager, pero esta vez usaremos el parámetro opcional “opt_maxZoom”.

addMarkers(markers:Array of Marker, minZoom:Number, opt_maxZoom:Number)

Con este parámetro opcional, podemos especificar un rango de niveles de zoom donde solo queremos que aparezcan los markers que deseamos. Ahora agregamos nuestro cluster a MarkerManager.

google.maps.event.addListenerOnce(map, 'idle', function(){
    var markersMap = GetMarkers();
    mgr = new MarkerManager(map);   
    var phantom = new google.maps.Marker({
        position: new google.maps.LatLng(40.1284, -98.4594),
        icon: 'phantomWhite.png'
    });       
    var phantomWhite = new google.maps.Marker({
        position: new google.maps.LatLng(35.4360, -81.0186),
        icon: 'phantom.png'
    });       
    var cluster = [phantom ,phantomWhite];       
    google.maps.event.addListener(mgr, 'loaded', function() {
        mgr.addMarkers(markersMap, 5);
        mgr.addMarkers(cluster, 1 , 4 );
        mgr.refresh();
    });
});
Lo que estamos haciendo aquí con la primera línea de código es :

mgr.addMarkers(markersMap, 5);

Estamos diciendo: Solo queremos que estos markers aparezcan desde el nivel de zoom 5 en adelante, y con esta línea:

mgr.addMarkers(cluster, 1 , 4 );

Estamos diciendo:  Estos markers solo queremos que aparezcan, cuando el zoom esté entre 1 y 4 en el mapa.  Lo que conseguimos con esto es un efecto de cluster muy bonito que podemos personalizar con nuestros propios iconos.

Si ahora actualizamos nuestro ejemplo en el navegador, podrás notar que aparecen unos iconos con unos fantasmas, pero si haces zoom lo suficiente podrás ver como desaparecen y se muestran las demas markers y viceversa.

Hasta ahora tenemos nuestro ejemplo muy bonito, pero creo que podemos mejorarlo aún más. ¿ Que te parece si agregamos un funcionalidad, que al hacer click en los fantasmas aparezcan los markers ocultos ? ok manos a la obra.

Vamos a modificar nuestro código de la siguiente manera, antes de asignar nuestros markers fantasmas al arreglo “cluster” les vamos a crear un evento click, donde vamos a ejecutar ciertas acciones.

var phantom = new google.maps.Marker({
    position: new google.maps.LatLng(40.1284, -98.4594),
    icon: 'phantomWhite.png'
});
google.maps.event.addListener(phantom, 'click', function() {
    map.setZoom(5);
    map.setCenter(phantom.getPosition());
});       
var phantomWhite = new google.maps.Marker({
    position: new google.maps.LatLng(35.4360, -81.0186),
    icon: 'phantom.png'
});   
google.maps.event.addListener(phantomWhite, 'click', function() {
    map.setZoom(5);
    map.setCenter(phantomWhite.getPosition());
});
 
Lo que pasara ahora, es que cuando hagamos click en los markers fantasmas vamos a asignar el nivel de zoom 5 al mapa, que es el nivel donde los markers ocultos aparecen y adicional a eso, vamos a centrar el mapa en esa misma posición usando “getPostion” del marker.

Ahora si actualizamos nuestro ejemplo, podremos comprobar este comportamiento. En la carpeta de práctica número “17”, podrás encontrar el código de MakerManager final de todos estos ejercicios.


Maplabel


Ahora veremos una librería muy útil para mostrar información junto con los markers llamada MapLabel. Es muy sencilla de usar, primero abramos nuestro archivo de práctica de la carpeta número “18”, después ejecutamos el “index.html” veremos a continuación 3 markers con texto debajo de ellos.





Si arrastras los overlay marker, el texto también se arrastrara junto con ellos. Ahora veamos el código.

Lo primero, es que hemos agregado la librería Maplabel a nuestro ejemplo en el archivo “index.html” , la cual podemos encontrar aqui, de esta manera.

<script type="text/javascript" src="maplabel-compiled.js"></script>

Ahora en el archivo “googlemaps.js”,  tenemos el método “SetMarkerInMap” el cual es el siguiente:

function SetMarkerInMap() {
    var locationMarker = [
        {"lat":40.1284,"lng": -98.4594},
        {"lat":40.6181,"lng": -99.3988},
        {"lat":40.3214,"lng":-97.3223}
    ];
   
    for (var i=0; i < locationMarker.length;i++)
    {
        var myLatlng = new google.maps.LatLng(locationMarker[i].lat,locationMarker[i].lng);   
        var marker = new google.maps.Marker({
            position: myLatlng,
            map: map,
            draggable:true,
        });       
        var mapLabel = new MapLabel({
            text: 'I am ' + i,
            position: myLatlng,
            map: map,
            fontSize: 20,
            align: 'center',
            fontColor:'#00FF00',
            strokeColor:'#D2691E'
        });
        marker.bindTo('position', mapLabel);
    }
}

Ya hemos explicado este método en los ejemplos anteriores, aquí lo nuevo seria que despues de crear el objeto Latlng y el overlay marker, creamos nuestro objeto “mapLabel” y definimos  sus opciones de como debería de lucir el texto. La opciones obligatorias para “mapLabel” son :

map : El mapa donde aparecerá el texto.
text : El texto que queremos mostrar.
position: La posición en el mapa.  

Puedes consultar las demás opciones de Maplabel aqui. Con esto es suficiente para mostrar texto junto con nuestros markers. Ahora con la siguientes lineas:

marker.bindTo('position', mapLabel);

Aqui vinculamos nuestro objeto “maplabel” con el marker, así cuando el marker es arrastrado, el texto tambien lo hara junto con el.

Con eso terminamos con Maplabel, que es un librería muy sencilla de usar. Existen muchas librerías en internet con las que deberías de experimentar, en el siguiente enlace puedes encontrar algunas de ellas link.


Localizar al usuario


Localizar a nuestros usuarios en el mapa es una tarea muy común hoy en día. Muchas Apps en el mercado hacen uso de esta información para mostrar contenido basado en la ubicación del usuario y ofrecer una mejor experiencia.

Localizar al usuario en el mapa es muy sencillo gracias a HTML 5  y su nueva API de geolocalizacion,  haciendo uso del objeto navigator.  Podemos abrir el archivo de práctica de la carpeta “19” y  ejecutamos el archivo “index.html”.

En este punto hay que hacer algunas aclaraciones. Dependiendo de la configuración de privacidad del navegador del usuario,  se le preguntará si quiere proporcionar su ubicación a la aplicación que la esta solicitando,  en caso de que el usuario esté de acuerdo podremos tener acceso a sus coordenadas Latlng. Pero si el usuario no accede a esta confirmación es imposible saber sus coordenadas. Por ejemplo la siguiente imagen muestra como en Google Chrome se muestra cuando el usuario a negado el permiso a ser localizado.






 Estas coordenadas no son la ubicación exacta del usuario, sino son coordenadas relativas del proveedor de internet del usuario,  pero aun asi es una ubicación aproximada de donde podria encontrarse el usuario. En internet explorer por ejemplo esta seria la pantalla de confirmación.



Y el código que ubica el usuario es el siguiente :

google.maps.event.addListenerOnce(map, 'idle', function(){
    GeoLocate();
});   

function GeoLocate () {   
    if (navigator.geolocation) {       
        navigator.geolocation.getCurrentPosition(function (position) {   
            var lat = position.coords.latitude;
            var lng =  position.coords.longitude;
            var position = new google.maps.LatLng(lat,lng);
            map.setCenter(position);
        });       
    }else {
        alert('Geolocation is not supported by this browser.');
    }   
}

Aquí podemos ver que cuando tenemos el mapa listo llamamos al método “Geolocate”, donde lo primero que hacemos es verificar que el objeto “navigator” de HTML 5 está disponible. Una vez confirmado llamamos al método “getCurrentPosition” que nos proporcionará las coordenadas Latlng, si te fijas bien hemos pasado un método anónimo como parámetro  a este método, la razón es que “getCurrentPosition” funciona a modo asíncrono y ejecutará nuestro método cuando haya terminado, pasando para parametro el objeto “position” donde se encuentran las coordenadas.

Después solamente resta crear nuestro objeto Latlng, para centrar nuestro mapa con el. Para saber mas informacion sobre el objeto “navigator”puedes consultar el siguiente enlace.  Nota importante es que la geolocalización  solo funcionara en sitios webs con https activado.


Places Autocomplete


Una importante característica de cualquier aplicación de mapas, es la de permitir al usuario buscar direcciones o nombres de lugares y ubicarlos en el mapa. En Google Maps existe una libreria llamada “place” de la que podemos hacer uso para buscar lugares , países y puntos de interés  definidos en la API. Digo definidos en la API, por que si el termino de busqueda no esta registrado en la API el usuario no obtendrá resultados. Por esta razón no funciona muy bien en todos los países, pero funciona perfecto en países donde Google Maps está presente.

Algunos de los resultados que podemos obtener pueden ser establecimientos (como restaurantes, tiendas, oficinas) y resultados de "codificación geográfica", que indican direcciones, regiones políticas (como pueblos y ciudades) y otros puntos en la zona.

Para continuar podemos abrir nuestro archivo de práctica en la carpeta número “20” y ejecutar el archivo “index.html”, donde encontraremos nuestro mapa con una caja de texto para nuestras búsquedas,  este será nuestro archivo base  para comenzar a trabajar.

Lo primero que tienes que notar es que en el archivo “index.html”, hemos cargado la librería “places” para poder utilizarla , ya que la librería no es cargada por defecto en Google Maps.

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

Y en nuestro código html también tenemos un elemento input con el id “searchInput”. Lo primero que haremos es crear nuestro autocomplete en el evento “idle” del mapa.

google.maps.event.addListenerOnce(map, 'idle', function(){   
    var input = document.getElementById("searchInput");
    var autocomplete = new google.maps.places.Autocomplete(input);
    autocomplete.setBounds(map.getBounds());                   
});

Con estas 3 líneas basta para crear nuestro autocomplete, si refrescamos nuestra página y escribimos en el input por ejemplo “texas” veremos como abajo del input aparece una lista de resultados basado en el texto de búsqueda.

Lo primero que hacemos en la primera línea es seleccionar el elemento input HTML de tipo texto que se utilizará para escribir los terminos de busqueda. En la segunda línea creamos una instancia de la clase “google.maps.places.Autocomplete” que espera como parámetro nuestro input.

Autocomplete(inputField:HTMLInputElement, opts?:AutocompleteOptions)

Con la tercera línea de código, definimos el área de búsqueda en la cual se basará el servicio “places” para  mostrar sus resultados, pero no se restringe obligatoriamente sólo  a esta zona logrando mostrar resultados adicionales fuera de esta. Para definir el área usamos el método “setBounds” del autocomplete, que recibe como parámetro un bounds de la zona en la que basará sus resultados, Por lo general siempre es el viewport del mapa.

setBounds(bounds:LatLngBounds)





También puedes probar escribir direcciones como por ejemplo “4441 Collins Avenue Miami Beach ” o “Eiffel Tower” o “Torre Eiffel”.

Por ahora solo hemos creado el autocomplete, pero sí seleccionamos algunos de los resultados de la lista notaras que no pasa nada en nuestro mapa. Esto es debido a que es  nuestro trabajo decidir qué hacer con el resultado que el usuario elija.

Lo siguiente que haremos, es que al seleccionar un resultado de la lista ubicaremos el viewport del mapa en ese lugar. Nuestro objeto autocomplete tiene un evento que se dispara cada vez que un resultado es seleccionado por el usuario, es en este evento que necesitamos ejecutar nuestras acciones.  Para esto usamos el siguiente código:

google.maps.event.addListenerOnce(map, 'idle', function(){
    var input = document.getElementById("searchInput");
    var autocomplete = new google.maps.places.Autocomplete(input);
    autocomplete.setBounds(map.getBounds());

    google.maps.event.addListener(autocomplete, 'place_changed', function() {
        var place = autocomplete.getPlace();
        if (place.geometry.viewport) {
            map.fitBounds(place.geometry.viewport);
        } else {
            map.setCenter(place.geometry.location);
        }
    });
});

Ok en este código hemos creado un evento que será ejecutado cada vez que ocurra un  “place_change” del objeto autocomplete, lo que ocurrirá cada vez que el usuario seleccione un resultado de la lista.

Cada vez que este evento ocurre, podemos tener acceso a un objeto llamado “placeResult”  a través del método “getPlace()” del objeto autocomplete. El cual guardamos en una variable llamada “place”, la cual contiene toda la información sobre el lugar, nosotros en nuestro ejemplo sólo necesitamos la localización del resultado seleccionado, pero este objeto contiene mucha información relevante sobre el lugar como por ejemplo: fotos , críticas , rating , etc. puedes consultar la API para tener mayor detalle sobre esto.

En el siguiente código :

if (place.geometry.viewport) {
   map.fitBounds(place.geometry.viewport);
} else {
   map.setCenter(place.geometry.location);
}

El objeto “geometry” de “placeResult” es donde se almacenan las coordenadas que necesitamos para ubicar al usuario, este objeto contiene “viewport” que es un bounds y “location” qué es el Latlng del lugar.

En nuestro código, verificamos que “place.geometry.viewport” esta disponible en caso contrario  usamos “location” en su lugar. Esto es debido a que “viewport” en algunas ocasiones devuelve NULL si no es conocido el bounds del lugar. Por eso yo personalmente uso “location” pero quería demostrar el uso de los 2 elementos.

Una vez que sabemos cual objeto esta disponible, solo nos resta usar el método apropiado del objeto mapa para ubicar al usuario, ya sea con bounds o con Latlng. Si lo prefieres puedes crear un marker y ubicarlo en el lugar usando las coordenadas Latlng de “location”.

Puedes probar nuestro ejemplo completo en la carpeta “21” y veras como el mapa es ubicado según el resultado seleccionado.


Reverse Geocoding


Para entender lo que es Reverse Geocoding, primero tenemos que saber que es Geocoding. Geocoding en Google Maps es cuando escribes una dirección de algún lugar por ejemplo y con esa dirección buscamos un punto Latlng en el mapa para ubicar al usuario, eso es lo que hemos estado haciendo en los ejemplos de autocomplete usando la librería “places”.

Reverse Geocoding es exactamente lo contrario en lugar de buscar un punto Latlng basado en una dirección, vamos a buscar una dirección usando un punto LatLng del mapa. Para realizar nuestro Reverse Geocoding usaremos la clase “google.maps.Geocoder” para consultar el servicio de codificación geográfica de Google Maps.

Ok vamos manos a obra, podemos abrir nuestro archivo de práctica “24” que contiene el ejemplo base con el comenzaremos este ejercicio.  Lo primero a notar es que tenemos nuestro mapa y sobre él tenemos un input con un botón que dicen “search”. En este ejemplo he puesto un valor por defecto de un punto Latlng en el input, que será el que usaremos para buscar nuestra dirección.
Para este ejemplo vamos a usar un marker,  para localizar en el mapa el punto Latlng del cual se quiere buscar la dirección y una infowindows para mostrar el resultado del reverse geocoding. Primero vamos crear la infowindow y el objeto “geocoder” para poder hacer nuestra consulta al servicio de codificación geográfica.

google.maps.event.addListenerOnce(map, 'idle', function(){
    infowindow = new google.maps.InfoWindow();
    geocoder = new google.maps.Geocoder();
});

En este bloque de código hemos creado los objetos “infowindow” y “geocoder” instanciando las clases correspondientes y guardarlas en variables globales, Hasta aquí nada fuera de lo normal. Ahora necesitamos ejecutar nuestro metodo de busqueda cuando el usuario haga click en el botón con el texto “search”, para esto solo creamos un evento click con jquery en el botón que tiene el id “search” y ejecutamos nuestro método de búsqueda, en este caso se llamará “SearchAddress”.

$('#search').on('click',function(){   
    SearchAddress($('#searchInput').val());
});

Al hacer click en el botón “search”, se ejecutará el método “SearchAddress” al cual se le pasará como parámetro el texto que sea introducido en el input de lado izquierdo del botón usando el método “val()” de jquery para obtener ese valor. Ahora vamos a la definición de nuestro método “search”

function SearchAddress(latlngUser) {   
    var latlngArray = latlngUser.split(",");
    var lat = parseFloat(latlngArray[0]);
    var lng = parseFloat(latlngArray[1]);
}

En este método estamos recibiendo el texto del input y lo hemos procesado para obtener los valores, en este caso hemos obtenido los valores del texto separados por comas y convertido en un arreglo con el método “split” de javascript y después convertimos estos valores de cadena en números con el método “parseFloat”.

El objeto “geocoder” que hemos creado previamente en el evento “idle” del mapa, lo usaremos para hacer nuestra consulta al servicio de codificación geográfica. Para hacer esto usamos su unico metodo disponible “geocode()” el cual recibe solo 2 parámetros: un objeto llamado “GeocoderRequest” y un método callback que se ejecutará cuando la consulta termine, debido a que este método funciona a modo asíncrono.

geocode (request:GeocoderRequest, callback:function());

Entonces lo primero que tenemos que hacer es crear nuestro objeto “GeocoderRequest” el cual tiene 4 propiedades, de las cuales nosotros solo vamos usar una llamada “location” para hacer nuestra búsqueda basado en un Latlng.

function SearchAddress(latlngUser) {   
    var latlngArray = latlngUser.split(",");
    var lat = parseFloat(latlngArray[0]);
    var lng = parseFloat(latlngArray[1]);

    var geocoderRequest = {
        location: new google.maps.LatLng(lat,lng)
    }
}

Con los valores anteriores que habíamos guardado creamos un objeto Latlng para asignarlo en la propiedad “location” que usaremos. Ahora solo nos resta llamar al método “geocode”, para hacer la consulta al  servicio de codificación geográfica.

function SearchAddress(latlngUser) {   
    var latlngArray = latlngUser.split(",");
    var lat = parseFloat(latlngArray[0]);
    var lng = parseFloat(latlngArray[1]);
    var geocoderRequest = {
        location: new google.maps.LatLng(lat,lng)
    }
    geocoder.geocode(geocoderRequest, function(results, status) {
        //código para manejar la respuesta
    });
}

Con esto estamos haciendo una consulta al servicio de codificación geográfica y pasamos nuestro método callback que se ejecutará cuando el servicio termine. Ahora veremos como manejar la respuesta del servicio. El método “geocode” pasa 2 valores al método callback cuando este devuelve una respuesta, que son “results” y “status”.

status : Este valor nos indica el estado de la consulta, si fue exitosa o no. Los posibles valores que puede contener esta definidos en la clase “google.maps.GeocoderStatus”, como constantes de la clase, las cuales podemos usar para saber si la consulta fue exitosa. Nosotros usaremos el valor de la constante “OK” para saber si consulta se ejecutó correctamente.

function SearchAddress(latlngUser) {   
    var latlngArray = latlngUser.split(",");
    var lat = parseFloat(latlngArray[0]);
    var lng = parseFloat(latlngArray[1]);
    var geocoderRequest = {
        location: new google.maps.LatLng(lat,lng)
    }
    geocoder.geocode(geocoderRequest, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
       
        }
    });
}

Recuerda que si el estado de la consulta no es “OK”, tienes que tener alguna manera de informar al usuario que ha ocurrido algún error.

Ahora vamos hablar de “results”, el segundo valor que recibimos en nuestra callback. El cual contiene toda la información del geocoding de la consulta. Es un objeto JSON que contiene más de un resultado, por lo tanto viene en forma de un arreglo. La razón por la cual devuelve más de un resultado nos la ofrece Google Maps en su documentación  que dice :

“ El geocoder inverso a menudo devuelve más de un resultado. Las "direcciones" de codificación geográfica no son solo direcciones postales, sino cualquier forma de definir de forma geográfica una ubicación. Por ejemplo, al codificar de forma geográfica un punto de la ciudad de Chicago, el punto codificado se puede etiquetar como una dirección postal, como la ciudad (Chicago), como su estado (Illinois) o como un país (Estados Unidos). Todas ellas son direcciones para el geocoder. El geocoder inverso devuelve todos esos resultados. El geocoder inverso encuentra coincidencias con entidades políticas (países, provincias, ciudades y barrios), con direcciones y con códigos postales.

Las direcciones se devuelven ordenadas de mayor a menor coincidencia. Normalmente, la dirección más exacta es el resultado más destacado. Ten en cuenta que se devuelven diferentes tipos de direcciones, desde la dirección postal más específica hasta entidades políticas menos específicas como barrios, ciudades, países, estados, etc”

Si quieres leer un poco más de la documentación oficial puedes visitar este link

Cada elemento del arreglo es un objeto “google.maps.GeocoderResult”, el cual contiene propiedades que podemos usar para saber la direccion que estamos buscando. Vamos a ver una breve descripción de cada propiedad, pero en la API puedes obtener más información en este  enlace.

  • types : Este es un arreglo que contiene, qué tipo de localización fue devuelto en el resultado. Puede ser por ejemplo un país ó una ciudad. para saber más puedes ver este link
  • formatted_address: Esta es una cadena de texto que tiene la información que pueden leer los usuarios más información en este link
  • address_components : Es un conjunto que incluye los diferentes componentes de la dirección más información en este link.
  • geometry : Este es un objeto que tiene muchas propiedades, pero la más importante es “location” el cual contiene la position en un objeto Latlng de la dirección, para más información de este objeto visita este link.

Ahora ya que sabemos que el método “geocoder” puede devolver más de un resultado, lo único que nos queda es confiar en que el primero resultado del arreglo es el más relevante entre todos, recuerda que los resultados se devuelven de mayor a menor coincidencia.

Ahora que hemos leído toda esta información vamos a modificar nuestro método de esta manera :

function SearchAddress(latlngUser) {   
    var latlngArray = latlngUser.split(",");
    var lat = parseFloat(latlngArray[0]);
    var lng = parseFloat(latlngArray[1]);
    var geocoderRequest = {
        location: new google.maps.LatLng(lat,lng)
    }
    geocoder.geocode(geocoderRequest, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            marker = new google.maps.Marker({
                position: results[0].geometry.location,
                map: map
            });       
            infowindow.setContent(results[0].formatted_address);
            infowindow.open(map, marker);
        }
    });
}

Una vez que hemos verificado  el estatus, creamos un objeto marker al cual le asignamos la posición usando “location” del objeto geometry, recuerda que es el que contiene las coordenadas de la dirección, después mostramos la dirección encontrada en nuestra infowindow usando la propiedad “formatted_address”, la cual es un formato que puede leer el usuario. En este código hemos usado el primero valor devuelto del arreglo en results[0].

Bueno y eso seria todo para que nuestro servicio de Reverse Geocoding funcione. Si quieres ver el ejemplo completo, puedes abrir el archivo de ejemplo de la carpeta número “25”.

El objeto geocoder también puede ser usado de la misma manera que usamos el “autoComplete”, si quieres buscar puntos Latlng basado en una dirección solo tienes que usar la propiedad “address” del objeto “GeocoderRequest” en lugar de “location” como lo hemos hecho.


Static Maps API


Hasta ahora hemos creado mapas dinámicos usando la API javascript de Google Maps, pero también tenemos a nuestra disposición la Static Map API  , que nos brinda la posibilidad de insertar un mapa en nuestra web usando una simple imagen. La diferencia es que sera una imagen comun  y corriente que solo mostrara la localización en el mapa. Esta API también tiene sus límites de uso por lo cual recomiendo que leas un poco sobre esto en la documentacion.

Static Maps API nos permite construir una URL la cual nos devolverá una imagen, en esta URL  podemos especificar la localización del mapa, el tamaño de la imagen, el formato de la imagen y ubicar algunos markers, la estructura de una URL sería de esta manera:

http://maps.googleapis.com/maps/api/staticmap?parameters

Donde “paramaters”  pueden ser cualquiera de los especificados en este link, siendo la mayoría opcionales , los únicos parámetros obligatorios son :

center : Define el centro del mapa , basado un punto Latlng separados por comas por ejemplo :  "40.714728,-73.998672" o una dirección   "city hall, new york, ny" recuerda que las direcciones no funcionan en todos los países por eso es recomendable usar Latlng.

zoom : Nivel de zoom del mapa basados en los nivel de la API javascript de Google Maps.

size: El tamaño deseado de la imagen, en el siguiente formato {horizontal_value}x{vertical_value} por ejemplo : “300x200”

sensor : Al igual que la API javascript de Google Maps es usado para determinar si la aplicación geolocalizara  al usuario.

Con estos parámetros podemos crear una imagen estática, la cual podemos insertar en cualquier sitio web por medio de la etiqueta <img> con su atributo “src” y mostrar un mapa sencillo sin usar código javascript ni la API  de Google Maps, lo que  resulta útil para pequeños proyectos web. Ejemplo:

http://maps.googleapis.com/maps/api/staticmap?center=6.5597,203.3579&zoom=12&size=400x300&sensor=false&format=png32&markers=6.5353,3.3201|6.523,3.3643|6.5670,3.3846|6.5759,3.3358

Con esta URL estamos creando la siguiente imagen:





Solo hemos visto lo básico para crear una imagen estática simple,  pero te invito a que revises la documentación de Static Map API para consultar parámetros adicional que puedes usar.

 



6 comentarios:

  1. Hola este libro es muy buena y me ha servido de mucho, pero tengo una duda por que si paso el ejemplo de búsqueda de lugares a mi servidor deja de funcionar

    ResponderEliminar
    Respuestas
    1. Hola silver, por favor crea un ejemplo en jsfiddle.net o sitio parecido para ver tu ejemplo y poderte ayudar.

      Eliminar
    2. https://jsfiddle.net/daniel_mtz_gtz/245f1gz5/1/ Gracias por responder te envió nuevamente mi código básicamente es el mismo mismo ejemplo 21 solamente hice uno pequeños cambios de la ubicación del mapa y un pequeño bloque para cargar un Geojson. pero como te comento si lo cargo en mi servidor local deja de funcionar el auto completado, para que cargue el geojson por el XMLHttp

      Eliminar
    3. Aqui esta corregido: https://jsfiddle.net/5kok51k3/

      Tienes que poner atencion en las librerias que se usan en los ejemplos. en este caso te hace falta jquery y segundo siempre revisa el modo consolo de los navegadores para comprobar que no hay errores en el codigo. en este segundo caso no podias cargar google maps por que lo estabas haciendo via http y lo debes hacer https.

      Revisa el codigo que te he enviado

      Eliminar
    4. Gracias a tu ayuda ya funciona mi ejemplo, te agradezco nuevamente

      Saludos.

      Eliminar
  2. Este comentario ha sido eliminado por el autor.

    ResponderEliminar