Librerías y manejo masivos de marcadores
Un problema común para los desarrolladores es la necesidad de trabajar con grandes números de marcadores, se puede dificultar el navegar con muchos marcadores porque se vuelve lento el navegador y también visualizar los datos del mapa.
Lo primero que debemos preguntarnos es ¿cuántos marcadores se consideran muchos? Eso dependerá de varios factores, entre ellos:
- Rendimiento: mientras más marcadores añadas más lento se vuelve el mapa. Indicar a cual número de marcador se vuelve lento el mapa es difícil de decir. Todo va a depender de qué navegador se use
- Usabilidad: mientras más marcadores añadas más difícil puede ser para el usuario encontrar el punto que desea
Por lo general con menos de 100 marcadores no debe haber problemas (siempre y cuando los marcadores estén distribuidos a través del mapa y se pueda visualizar corréctamente), cuando hay más de 100 se debe cuestionar:
- ¿Es el mapa lento?
- ¿Es difícil de visualizar?
- ¿Es difícil de verificar la data en el mapa?
Si respondes que sí a una de esas preguntas, entonces piensa como mejorar el rendimiento o la forma de como se debe visualizar la data.
Manejando los marcadores
Una forma de trabajar los marcadores es filtrándolos, crear un menú y que tenga solo seleccionado algunos por defecto y que el usuario luego escoja aquellos que desee visualizar.
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>test</title> <style> *{ margin: 0; padding: 0; } html, body, #map{ width: 100%; height: 100%; } #menu{ width: 200px; margin: 0 auto; background-color: #fff; border: 1px solid #333; position: relative; top: -50px; text-align: center; padding: 5px; } </style> <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&language=es"></script> <script type="text/javascript" src="js/map.js"></script> </head> <body> <div id="map"></div> <div id="menu"> <form action="index.html" method="get"> <input type="checkbox" id="foo" name="fooBarBaz" /> <label for="foo">Foo</label> <input type="checkbox" id="bar" name="fooBarBaz" /> <label for="bar">Bar</label> <input type="checkbox" id="baz" name="fooBarBaz" /> <label for="baz">Baz</label> </form> </div> </body> </html>
map.js
/** * Listener para el formulario */ function addListener(element, type, expression, bubbling) { bubbling = bubbling || false; if(element.addEventListener) { // Standard element.addEventListener(type, expression, bubbling); return true; }else if(element.attachEvent) { // IE element.attachEvent('on' + type, expression); return true; }else return false; } window.onload = function(){ var fooArr = [], barArr = [], bazArr = []; var options = { zoom: 9 , center: new google.maps.LatLng(18.2, -66.3) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var southWest = new google.maps.LatLng(17.85, -67.35); var northEast = new google.maps.LatLng(18.55, -65.2); var lngSpan = northEast.lng() - southWest.lng(); var latSpan = northEast.lat() - southWest.lat(); for(var i=1; i<=1000; 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 }); if((i%3) == 0){ bazArr.push(marker); } else if((i%2) == 0){ barArr.push(marker); } else{ fooArr.push(marker); } } document.getElementById('foo').checked = true; for(var i in fooArr){ fooArr[i].setMap(map); } addListener(document.getElementById('foo'), 'click', function(){ if(this.checked){ for(var i in fooArr){ fooArr[i].setMap(map); } }else{ for(var i in fooArr){ fooArr[i].setMap(null); } } }); addListener(document.getElementById('bar'), 'click', function(){ if(this.checked){ for(var i in barArr){ barArr[i].setMap(map); } }else{ for(var i in barArr){ barArr[i].setMap(null); } } }); addListener(document.getElementById('baz'), 'click', function(){ if(this.checked){ for(var i in bazArr){ bazArr[i].setMap(map); } }else{ for(var i in bazArr){ bazArr[i].setMap(null); } } }); };
Librerías
Google maps tiene librerías adicionales para trabajar con el mapa. Todas las librerías adicionales las podemos ver en Libraries y las mencionadas en este capítulo son MarkerClusterer y MarkerManager.
Nota: Podemos usar las librerías desde la dirección web, pero lo aconsajable es que descarguen el archivo (o copien y peguen el código en un archivo js), para evitar que si hacen una actualización al código, no se afecte lo que han trabajado. También es aconsejable trabajar con la compilación. Para propósitos de enseñanza lo trabajaremos indicando la dirección web oficial directamente.
MarkerClusterer
MarkerCulterer agrupa los marcadores y muestra la cantidad de marcadores que hay en cierto segmento. En la documentación en el directorio llamado examples, muestra varios ejemplos de como utilizar esta librería. Para usarla se puede bajar el archivo markerclusterer.js, markerclusterer_compiled.js o colocar en el src
de script
directamente la dirección de la librería.
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>test</title> <style> *{ margin: 0; padding: 0; } html, body, #map{ width: 100%; height: 100%; } </style> <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&language=es"></script> <script type="text/javascript" src="http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/src/markerclusterer_compiled.js"></script> <script type="text/javascript" src="js/map.js"></script> </head> <body> <div id="map"></div> </body> </html>
map.js
window.onload = function(){ var markers = []; var options = { zoom: 9 , center: new google.maps.LatLng(18.2, -66.3) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var southWest = new google.maps.LatLng(17.85, -67.35); var northEast = new google.maps.LatLng(18.55, -65.2); var lngSpan = northEast.lng() - southWest.lng(); var latSpan = northEast.lat() - southWest.lat(); for(var i=1; i<=5000; 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 }); markers.push(marker); } var markerclusterer = new MarkerClusterer(map, markers); };
Estos son los posibles colores que se pueden mostrar en el mapa que viene por defecto:
- Azul 2-9
- Amarillo 10-99
- Rojo 100-999
- Violeta 1,000-9,999
- Violeta oscuro 10,000+
El comportamiento que trae por defecto la clase MarkerClusterer
se puede modificar en el tercer parametro. Las posibles opciones son:
gridSize
: Número entero. Valor por defecto 60maxZoom
: Número entero entre el 1 al 23. Indica hasta donde debe hacer el agrupamientozoomOnClick
: Valor booleano para hacer “zoom” al pulsar en el marcador.true
ofalse
. Valor por defectotrue
averageCenter
: Centrar aproximádamente el marcador.true
ofalse
. Valor por defectotrue
minimumClusterSize
: Número mínimo para agrupar los marcadores. Por defecto 2
styles
: Es un array que contiene uno o varios objetos de MarkerStyleOptions
.
Posibles valores son:
url
: La dirección de la imagen a mostrarheight
: El alto de la imagenwidth
: El ancho de la imagenanchor
: Array con la posiciónx
yy
de los números en la imagen. Por defecto centradotextColor
: Color de los números. Por defectoblack
textSize
: Tamaño de los números. Por defecto11
backgroundPosition
: Posición del fondo
Es importante el orden en que se indican los objetos en el tercer parametro de la clase MarkerClusterer
, las imágenes se van a mostrar conforme al orden de los valores numéricos indicados en los colores por defecto.
También los números de imágenes a colocar son importante, ya que si se coloca uno, esa es la imagen a mostrar en cada uno de los niveles indicados en los colores, si son dos la primera representa el primer valor numérico y la segunda del segundo al quinto, si se colocan tres, la primera representa el primer valor numérico, la segunda el segundo y la tercera del tercero al quinto y así sucesivamente.
map.js
window.onload = function(){ var markers = []; var options = { zoom: 9 , center: new google.maps.LatLng(18.2, -66.3) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var southWest = new google.maps.LatLng(17.85, -67.35); var northEast = new google.maps.LatLng(18.55, -65.2); var lngSpan = northEast.lng() - southWest.lng(); var latSpan = northEast.lat() - southWest.lat(); for(var i=1; i<=1000; 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 }); markers.push(marker); } var markerclusterer = new MarkerClusterer(map, markers, { gridSize: 60 , maxZoom: 11 , zoomOnClick: false , minimumClusterSize: 4 , averageCenter: true , styles: [{ url: "http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/people35.png" , height: 35 , width: 35 , textColor: 'white' , textSize: 12 , anchor: [1,1] },{ url: "http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/people45.png" , height: 45 , width: 45 , textColor: 'white' , textSize: 14 , anchor: [1,30] },{ url: "http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/people55.png" , height: 55 , width: 55 , textColor: 'white' , textSize: 16 , anchor: [40,40] }] }); };
MarkerManager
MarkerManager es bien similar a MarkerClusterer
en que puede agrupar varios marcadores, pero su trabajo principal es mostrar solo aquellos marcadores en el “viewport” corriente. Al mover el mapa se van mostrando los puntos de acuerdo al “viewport” que se encuentra el usuario. También se puede indicar desde cuál nivel de “zoom” deseamos mostrar los marcadores. Cuando el usuario acerque o aleje el mapa, MarkerManager
mostrará aquel o aquellos marcadores indicados en ese “zoom”.
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>test</title> <style> *{ margin: 0; padding: 0; } html, body, #map{ width: 100%; height: 100%; } </style> <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&language=es"></script> <script type="text/javascript" src="http://google-maps-utility-library-v3.googlecode.com/svn/tags/markermanager/1.0/src/markermanager_packed.js"></script> <script type="text/javascript" src="js/map.js"></script> </head> <body> <div id="map"></div> </body> </html>
map.js
window.onload = function(){ var markers = []; var options = { zoom: 9 , center: new google.maps.LatLng(18.2, -66.3) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var markerManager = new MarkerManager(map); var southWest = new google.maps.LatLng(17.85, -67.35); var northEast = new google.maps.LatLng(18.55, -65.2); var lngSpan = northEast.lng() - southWest.lng(); var latSpan = northEast.lat() - southWest.lat(); for(var i=1; i<=1000; 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 }); markers.push(marker); } google.maps.event.addListener(markerManager, 'loaded', function() { markerManager.addMarkers(markers, 8, 10); markerManager.refresh(); }); };
En la clase MarkerManager
también se puede modificar algunos de sus comportamientos, en el segundo parametro. Las posibles opciones son:
maxZoom
: Número entero entre el 1 al 23. Indica hasta donde debe trabajarMarkerManager
borderPadding
:MarkerManager
muestra solo aquellos que están en el “viewport”, pero tiene una zona adicional al “viewport” para mostrar marcadores adicionales. La razón es que cuando se mueva el mapa a cortas distancias ya tenga recargado también algunos marcadores. Por defecto esta zona de buffer es de 100trackMarkers
:true
ofalse
. Si cambiamos la posición de un marcador después que lo hayas añadido alMarkerManager
se van a reflejar dos simultaneamente. Al colocar esta opción como valortrue
le indicamos alMarkerManager
que recargue de nuevo los marcadores y así se ve reflejado una sola vez el marcador que se haya modificado. Esto representa que el código corra más lento, así que si no se modificara la posición de los marcadores se aconseja dejarlo como viene por defecto con valorfalse
map.js
window.onload = function(){ var markers = []; var options = { zoom: 9 , center: new google.maps.LatLng(18.2, -66.3) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var markerManager = new MarkerManager(map, { maxZoom: 10 , borderPadding: 60 , trackMarkers: true }); var southWest = new google.maps.LatLng(17.85, -67.35); var northEast = new google.maps.LatLng(18.55, -65.2); var lngSpan = northEast.lng() - southWest.lng(); var latSpan = northEast.lat() - southWest.lat(); for(var i=1; i<=1000; 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 }); markers.push(marker); } google.maps.event.addListener(markerManager, 'loaded', function() { markerManager.addMarkers(markers, 8, 10); markerManager.refresh(); }); };
Trabajando con los niveles
Se trabaja similar al MarkerClusterer
pero se puede indicar en cada uno de los niveles de “zoom”, qué tipo de imagen queremos mostrar.
map.js
window.onload = function(){ var options = { zoom: 13 , center: new google.maps.LatLng(18.372201, -66.139797) , mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), options); var mnr = new MarkerManager(map); google.maps.event.addListenerOnce(map, 'bounds_changed', function(){ var markers = []; var bounds = map.getBounds(); var southWest = bounds.getSouthWest(); var northEast = bounds.getNorthEast(); var lngSpan = northEast.lng() - southWest.lng(); var latSpan = northEast.lat() - southWest.lat(); for (var i = 0; i < 1000; 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 }); markers.push(marker); } var bayamon = new google.maps.Marker({ position: new google.maps.LatLng(18.383563, -66.162713) , icon: 'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/people35.png' }); google.maps.event.addListener(bayamon, 'click', function() { map.setZoom(14); map.setCenter(this.getPosition()); }); var guaynabo = new google.maps.Marker({ position: new google.maps.LatLng(18.3580678, -66.112674) , icon: 'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/people35.png' }); google.maps.event.addListener(guaynabo, 'click', function() { map.setZoom(14); map.setCenter(this.getPosition()); }); var towns = [bayamon, guaynabo]; google.maps.event.addListener(mnr, 'loaded', function() { this.addMarkers(towns, 10, 13); this.addMarkers(markers, 14); this.refresh(); }); }); };
Si alejamos el mapa continuará con la misma imagen hasta el “zoom” 10, pero si lo acercamos veremos todos los marcadores. Es una buena forma de agrupar y mostrar de acuerdo al nivel de “zoom” que deseamos, esto nos da un mejor control.
El próximo capítulo estará dedicado a Geocoder.
Que bueno, justo este fin de semana me dedique a optimizar un mapa de mi sitio que muestra propiedades en venta en mi ciudad. si bien no agrupo los marcadores (por ahora) pude actualizar a la V3 y se carga mucho mas rápido. Por otro lado pude experimentar con la carga externa de datos, es decir, los marcadores se encuentran en un xml dinámico y los pido mediante javascript. A demás el usuario decide que tipo de propiedades puede visualizar mediante checkbox. Todavia me falta diseñar un par de iconos, cuando lo termine te muestro. Excelente articulo bien completo como me gusta a mi.
Una manera de trabajar mapas que tengan muchos marcadores, es poner un menu en el lado izquierdo, o derecho como queiras, y que al pulsar el link del mapa este active el marcador y que de inmediato salga un bocadillo con la imformacion de la marca!
Saludos
Lo hacen ver tan facil, pero todavia no le entiendo como funciona al 100%, prometo que lo intentare haber que sucede, gracias.
Abimael:
Esta info que nos das nos fué de gran utilidad:
Librerías
Google maps tiene librerías adicionales para trabajar con el mapa. Todas las librerías adicionales las podemos ver en Libraries y las mencionadas en este capítulo son MarkerClusterer y MarkerManager.
Nota: Podemos usar las librerías desde la dirección web, pero lo aconsajable es que descarguen el archivo (o copien y peguen el código en un archivo js), para evitar que si hacen una actualización al código, no se afecte lo que han trabajado. También es aconsejable trabajar con la compilación. Para propósitos de enseñanza lo trabajaremos indicando la dirección web oficial directamente.
Muchas Gracias
McVincent
Hola que tal, muy interesante el articulo, estoy haciendo un trabajo con google maps, consulta de tarifa de taxi que me muestre la distancia recorrida y el costo total, donde tengo que poner mi código para que me pueda calcular la tarifa?
felicidades, muy buenos los tutoriales por lo cual me impacienta el esperar el siguiente capitulo jajaja para cuando estara listo?
saludos
Buenisimas las explicaciones Abimael.
Se estan sirviendo muchisimo para empezar a conocer el api de google maps.
A ver si me podes ayuda.. quisiera poder activar la opcion que muestra las coordenadas en el mapa a medida que se va moviendo el mouse por el mismo. Me recorri todos los mapTypeControlOptions del centro de ayuda y no lo puedo encontrar. Creo que desde ahi se le daria la activacion no?
Bueno muchas gracias y estare atento a nuevos capitulos!
Te recomiendo que postees la pregunta en http://www.forosdelweb.com/f171/, porque puede que tengas ciertas dudas más y de ahí se puede elaborar mejor 😉
hola quiero saber como hago para hacer los marcadores dinamicos que yo le detal ciudd y el automaticamente los busque sin yo tener q agregarlos por que si son 30 0 50 marcadores se ve muy engorroso te agradesco
disculpe saber como hacerle hide a los puntos de un MarkerClusterer, y como se Habilita e inhabilita un control personalizado…