martes, 7 de octubre de 2014

Textos completos en JQuery Mobile

Por defecto, cuando el framework Jquery Mobile detecta que un texto excede del tamaño reservado de la barra de título, lo recorta y pone unos puntos suspensivos.

Para evitar esto, hay que modificar el estilo CSS del emento html utilizado para el título, añadiendo la propiedad white-space:normal.
white-space: normal

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.

martes, 29 de julio de 2014

Operaciones de mantenimiento sobre tablas MyISAM de MySQL sin bloquearlas

Actualmente lookingformaps tiene indexados en torno al millón de mapas, y aproximadamente 30 o 40 millones de puntos de interés. Esto ha hecho que la base de datos con la que gestiona la información haya alcanzado dimensiones considerables (en torno a 10 Gb. de información, incluyendo índices).






Looking4Maps trabaja con la base de datos MySQL, por ser muy lígera, y estar muy extendida (trabajando con ella la casi totalidad de proveedores de servicios de hosting de Internet). Además, MySQL proporciona capacidades de gestión espacial básicas, con tipos geométricos, índices espaciales y operaciones espaciales básicas a nivel de rectángulos envolventes (bounding box).

Sin embargo, para poder trabajar con los tipos de datos geométricos, el motor de almacenamiento con el que trabaje deberá ser obligatoriamente MyISAM.

MyISAM es el motor más rapido en operaciones de lectura, pero cuando se van a realizar operaciones de escritura tiene un gran inconveniente: hace un bloqueo a nivel de toda la tabla, de tal forma que en muchas ocasiones ni siquiera permite que el resto de usuarios puedan realizar operaciones de lectura.

Muchas operaciones de MyISAM (INSERT, UPDATE, ALTER TABLE) realizan un bloqueo a nivel de tabla.


Esto hace que, operaciones tan simples como pueda ser modificar el esquema de la tabla para añadir un campo, pueda llegar a dejar inoperativo Looking4Maps mientras la base de datos termina de hacer sus operaciones internas para añadir el campo a la tabla, puesto que no permite realizar accesos de lectura para los nuevos usuarios.

Frente a esto, la mejor solución que he encontrado es mantener dos tablas sincronizadas: una de trabajo, que es con la que trabaja lookingformaps para proporcionar mapas a los usuarios, y otra de mantenimiento, que es la tabla sobre la que el motor de indexación realiza los volcados de los nuevos mapas que va encontrado, o sobre la que yo opero (previa interrupción del robot de búsquedas) para hacer modificaciones estructurales de la base de datos.

Para ello, MySQL permite hacer una copia instantánea de una tabla así:

CREATE TABLE COPIA_TABLA LIKE TABLA;
INSERT INTO COPIA_TABLA SELECT * FROM TABLA;



De tal forma que las operaciones que se realicen sobre COPIA_TABLA no estorban en absoluto al resto de usuarios, que pueden seguir trabajando ya que los accesos al sistema en producción se hacen contra TABLA, no contra COPIA_TABLA.


Una vez terminadas las operaciones sobre la tabla de escritura, se propagan a la tabla principal así:

RENAME TABLE TABLA TO TABLA_VIEJA;
RENAME TABLE COPIA_TABLA TO TABLA;
RENAME TABLE TABLA_VIEJA TO COPIA_TABLA;

Esta solución funciona para un sistema como Looking4Maps, pensado solo para la búsqueda e indexación, no para la creación de nuevos contenidos. Obviamente, llegado el caso de evolucionar Looking4Maps para que sea un sistema de creación de mapas, habrá que cambiar la arquitectura.







lunes, 28 de julio de 2014

Ríos que atraviesan Tokio: Sumida y Tama (隅田川,多摩川)

En las últimas dos semanas están llegando múltiples visitas a la web de  lookingformaps en busca de los ríos que atraviesan Tokio. Los dos ríos principales que atraviesan Tokio son el río Sumida y el río Tama. Se pueden localizar a través de la categoría Ríos_de_Tokio


Desembocadura del río Sumida en la bahía de Tokio.


El río Sumida (en japonés: 隅田川, Sumida-gawa) se origina en la bifurcación artificial del  río Arakawa en Iwabuchi, y desemboca en la bahía de Tokio. A su paso por Tokyo el rio es bastante caudaloso con lo que es navegable. A parte de pequeños barcos que navegan por sus aguas también existe, en Tokyo, una red de transporte por el rio con diversas paradas. Esta red es llamada 水上バス  (Suijō Basu) , que se podría traducir como autobuses acuáticos o de agua. Los principales embarcaderos de estas lineas son 4: Asakusa, Hama Rikyu, Hinode y Odaiba. Hay varias combinaciones que se pueden hacer pero la mas común suele ser la que va desde Asakusa hasta Odaiba o viceversa, esta ruta puede ser directa o con paradas en otros puertos.

El río Tama (多摩川 Tama-gawa) atraviesa Tokio, estableciendo la línea divisoria entre Tokio y Kanagawa. En Tokio es una zona muy popular para la práctica de deportes o simplemente para echar un día de campo, debido a los parques y pistas deportivas que existen a lo largo de la orilla a su paso por Tokio.