Android + SQLite + Spatial Data

bLero

Hola,

Tengo un dilema bastante importante con una aplicación en Android. Espero que podáis darme alguna idea.

Se trata de una aplicación que consume POIs (puntos de interés) que obtengo mediante peticiones a un servicio web a través de REST.

Sólo necesito obtener en cada petición los POI que están en un radio de 5km de mi posición, por lo que en la petición establezco el radio como parámetro.

En el servidor cuento con una base de datos MySQL y puedo perfectamente realizar una query para devolver los POI según el radio especificado, utilizando the harvesine formula.

La query sería algo tal que así:

"SELECT id, name, lat, lon, ( 6356 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM pois HAVING distance < 5000 ORDER BY distance ASC

Hasta ahí todo bien. El problema viene de que la aplicación también debe funcionar sin conexión, es decir, debo sincronizar la base de datos de la aplicación (SQLite) con la base de datos del servidor (MySQL), algo así como tener una copia local de los POI que he solicitado previamente al servicio web y realizar las consultas siempre a la base de datos local (SQLite).

El problema con SQLite es que, aun teniendo los mismos datos, la query anterior no la puedo realizar porque no dispone de funciones trigonométricas. Tampoco puedo crear una función ya que Android no lo permite.

De momento la única solución que se me ha ocurrido es:

Al guardar los POI en la base de datos local, guardar también en nuevas columnas el cos(lat), sin(lat), cos(lon), sin(lon), etc.

Desaprovecho espacio, pero al menos puedo realizar la query, y también incremento el rendimiento al realizar las queries al no tener que hacer operaciones trigonométricas.

¿Alguna otra idea?

JuAn4k4

No he buscado mucho, igual te sirve

https://code.google.com/p/spatialite-android/

https://bitbucket.org/nutiteq/android-map-samples/src/4c79d8058d55/jni/

https://groups.google.com/forum/?fromgroups#!searchin/spatialite-users/android/spatialite-users/8oU_YszO7JA/_3906OE5h5IJ

No se si sabías que Mysql ya tiene un modulo spatial, que puedes usar para hacer ese tipo de consultas sin tener que liarte a senos y cosenos y arcotangentes ni leches.

http://dev.mysql.com/doc/refman/5.5/en/spatial-extensions.html

1 respuesta
bLero

#2 las 2 primeras alternativas necesitan otra base de datos independiente de la que provee android, mi idea es utilizar la que dispone cada dispositivo de fábrica para no tener que meterme en fregaos de instalar una base de datos cuando el usuario inicie la aplicación por primera vez.

Lo tercero ya lo he aplicado para Mysql

1 respuesta
JuAn4k4

#3 Lo de almacenar el sen/cos no se si te valdra, ya que tienes que basar la respuesta en la localización del usuario no ? y al final tienes "cos( radians( poi.lng ) - radians(-user.lng) )"

Edit: Al parecer al meterte en cosas de estas espaciales vas a necesitar operaciones trigonometricas si o si.

¿ Como tienes tus pois ? ¿ Los tienes por pais / población o algo ? Yo lo que haría sería obtener un resultado aproximado, y despues filtrar, intentando no atascar la app.

1 respuesta
bLero

#4

Si se puede, ya que el cos(radians(poi.lng)) lo generas desde el cliente y se lo pasas a la query.

Los pois constan de los siguientes campos: id, name, user_id, lat, lon, shared

En Mysql tengo unos índices especiales para lat y lon, para acelerar las consultas.

Para SQLite de momento tendría esa alternativa, que ya he probado, o tambien creo que se podría filtrar así:

poi.lat BETWEEN poi.lat + x AND poi.lat - x AND poi.lng BETWEEN poi.lng + y AND poi.lng - y

Donde x,y serían los desplazamientos que estoy dispuesto a filtrar. Esta última forma sería sin duda la más eficiente ya que son operaciones muy básicas, pero en este caso no podría filtrar una distancia exacta (o casi exacta) pues en primer lugar estaría filtrando un cuadrado en vez de un círculo, y tampoco es un cuadrado perfecto pues la longitud varía según cual sea la latitud.

Tig

¿de cuántos POIs estás hablando? Hace mucho que no toco mapas, pero si no recuerdo mal google se encargaba de mostrarlos según el zoom y coordenadas, es decir según si eran visibles o no en el cuadrado del usuario. Si son pocos, puedes meterlos todos a saco, pero si hablamos de miles, te quedas sin memoria.

De esto hace más de 1 año y era la v1.0 de maps, no he tocado la v2.0

1 respuesta
bLero

#6

Los POIs pueden ser millones, por eso quiero filtrarlos por la posición, para solo mostrar en el mapa los necesarios y no quedarme sin memoria y también para que la transferencia de datos entre la BD y el dispositivo sea lo más rápida posible (A menor tamaño del JSON, mayor velocidad de transferencia entre C/S y mayor velocidad de parseo en el cliente).

1 respuesta
JuAn4k4

#7 http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates
La formula esa es incorrecta, pero aproximada, perderías alguno dado que los lados del cuadrado en lat,lng no son rectos, sino curvos debido a la curvatura de la tierra.

1 respuesta
bLero

#8 es otra posibilidad, usar coordenadas límites es bastante eficiente con índices en lat, lng y en principio me serviría, pero creo que mientras funcione bien de la manera en la que lo tengo planteado en #1 (que es más precisa, a costa de 4 campos double más en la BD), utilizaré esa forma.

Con el tiempo, cuando la aplicación tenga muchos POIs almacenados si noto que las consultas son muy lentas entonces me dedicaré a optimizar este sistema.

Usuarios habituales

  • bLero
  • JuAn4k4
  • Tig