lunes, 6 de octubre de 2014

Leaflet: la importancia de map.invalidateSize()

Ultimamente me he estado pegando más de la cuenta con Leaflet, así que comparto aquí el problema encontrado, y la solución, por si le puede servir de ayuda a alguien.

Origen del problema: tenemos un objeto javascript L.Map de Leaflet, al que hemos añadido una serie de elementos vectoriales (objetos de tipo L.Layer).

Si queremos cambiar la capa, y mostrar otra distinta, reutilizando el objeto mapa existente, tenemos que seguir estos dos pasos:

a) Eliminar los objetos "Layer" previamente cargados. En este caso lo más comodo es haberlos agrupado previamente en un contenedor denominado "LayerGroup".

if(currentLayer){
    if(currentLayer instanceof L.LayerGroup)
         currentLayer.clearLayers();
         map.removeLayer(currentLayer);
    }
}

b) Añadir la nueva capa (objetos Layer) agrupada en un contenedor.

 var lyrs = [];//array donde guardar las geometrias
var layerGroup = L.featureGroup(lyrs); 

c) Cambiar el nivel de zoom del mapa, para que se nos muestra la nueva "capa" que vamos a añadir.
map.fitBounds(layerGroup.getBounds());

Y ¡ojo! Aquí empiezan a aparecer los problemas. Es muy posible que el nivel de zoom que se nos muestre sea siempre el máximo,  y que el mapa (la capa de tiles con ortofoto, open street map, o lo que sea que hayamos querido cargar como capa de base) no aparezca centrado. ¿Cual es el motivo?

Si cambiamos nuestro código de cambio del nivel de zoom, por el siguiente:
var bounds = _layerGroup.getBounds();
var center = bounds.getCenter();
var zoomLevel = map.getBoundsZoom(bounds);

map.setView(center, zoomLevel, true);

Veremos que zoomLevel toma el valor 0.


 ACTUALIZACIÓN: En este bug reportado en la web del proyecto Leaflet se describe perfectamente el problema. No solo zoomLevel toma el valor 0, getSize() devuelve un objeto con valores w=0 y h=0.

En este punto es cuando entra en juego el método invalidateSize() del objeto map. Si lo hacemos así:

          var layerGroup = L.featureGroup(lyrs);
         map.invalidateSize();
         map.fitBounds(layerGroup.getBounds()); 

Nuestro mapa se dibujará sin problemas, mostrando la capa que acabamos de añadir.


Moraleja: map.invalidateSize() recalcula los parámetros del objeto map,parámetros que pueden cambiar con un cambio del área de visualización del dispositivo (horizontal a vertical en móvil, cambio del tamaño del navegador en desktop, etc) o con el "bounds" de las geometrías cargadas. Así que es conveniente siempre llamar a map.invalidateSize() antes de cambiar el nivel de zoom, si pensamos que se ha producido un cambio en la inforamación de estado del mapa.

No hay comentarios:

Publicar un comentario en la entrada