«

»

jun 12 2012

OSMDROID – Mapas offline con OpenStreetMap (OSM Parte II)

Si una carencia hay que destacar de la API de Google Maps es el poder incluir mapas offline en nuestras apps, de hecho hasta hace poco ni siquiera en la app oficial se podía y ahora que si se puede espero que sea un indicativo de que en un futuro disfrutaremos de esta funcionalidad en la API. Pero como no nos gusta esperar y como no hay que limitarse a una única opción hoy os venimos a contar la características mas interesante de OpenStreetMap y de la librería que nos atañe, los mapas offline en OSMDROID.

 

 

NOTA: Para el aprovechamiento de este tutorial se dan por asimiladas unas nociones básicas sobre el uso de la librería OSMDROID, si no tenéis esas nociones os recomendamos pasar por el tutorial de introducción publicado con anterioridad.


¿COMO FUNCIONA?

La arquitectura de OSMDROID está basada en una serie de Providers para la obtención de “Tiles” (las tiles son las secciones que conformaran nuestro mapa). Esta arquitectura está diseñada de forma modular y extensible proporcionando acceso a las diversas fuentes que se nos proporcionan, entre las que se incluyen recursos online, cache, archivos…

El sistema comprobará si disponemos de los recursos necesarios en cache y si no es así buscara los mismos a través de las siguientes opciones disponibles, entre las que estarán los mapas que hayamos incluidos dentro de nuestra app. Por tanto, si así lo desearamos podríamos elimiar de nuestra app los permisos de acceso a internet ya que nuestra aplicacion no haría uso de ellos.

 

OBTENCIÓN DE TILES A TRAVES DE MOBAC

Para tener acceso a los mapas offline deberemos descargarlos nosotros mismos e incluirlos dentro de de nuestras apps. Para esto disponemos de varias opciones, una de estas, y la más sencilla, es usar Mobile Atlas Creator (MOBAC), una aplicación creada precisamente para la obtención de estos mapas. Una vez nos hemos descargado la aplicación y la hemos ejecutado nos encontraremos ante la siguiente pantalla:


 

Aquí deberemos indicar el nombre de nuestro Atlas así como el formato en el que lo guardaremos, en nuestro caso “osmdroid ZIP” o “osmdroid SQLITE” (aunque este último aún no he conseguido que funcione así que nos quedaremos con el primero).

 

Ya en la pantalla principal de la aplicación deberemos configurar varias cosas:

  • Origen de los mapas: Seleccionaremos OpenStreetMap Mapnik.
  • Coordenadas de la zona: Deberemos selecciona la zona que queremos descargar a partir de sus coordenadas. Podemos introducirlas manualmente o realizando una selección con el raton sobre el mapa que se muestra.
  • Niveles de zoom: Prodremos elegir tambien los niveles de zoom que deseamos descargarnos, dato importante a tener en cuenta y que deberemos madurar en función de las necesidades de nuestra app. ¿Y esto porque? Pues por que resulta que conforme aumentamos el nivel del zoom se incrementan el número de tiles necesarios para mapear una zona y por tanto el tamañó de los datos pudiendo alcanzar estos un tamaño considerable.

 

 

Una vez seleccionados estos datos, en la zona “Atlas Content” deberemos añadir la selección deseada. En la sección “Saved profile” tenemos la opción de almacenar estas configuraciones para futuras descargas. Con todo ya configurado solo tenemos que hacer click en “Create atlas” y nuestros mapas serán descargados, mostrandonos una ventana desde la que podremos acceder a los ficheros generados.

 

Con nuestro ficheros ya generados y descargados tan solo tendremos que copiarlos a nuestra tarjeta SD en la carpeta /sdcart/osmdroid y la librería se encargará sola de acceder a ellos.

 

LIMITANDO EL DESPLAZAMIENTO

Cuando creamos apps que hacen uso de mapas online normalmente desearemos limitar la zona de mapa a visualizar para que esta no exceda la zona de mapa almacena en nuestras aplicaciones, puesto que si no recorreríamos zonas de mapas que no podriamos cargar ni visualizar.  Realizar esta acción no es trivial ya que esta funcionalidad aun no viene incluida en la librería (esperamos que pronto si), así que deberemos descargarnos el código fuente y realizar unos cambios nosotros mismos.

Deberemos descargarnos el código fuente y crear un proyecto en eclipse para nosotros mismos compilar la librería que posteriormente usaremos como os hemos explicado. Importaremos el proyecto osmdroid-android a nuestro entorno de trabajao en eclipse vía SVN desde la siguiente dirección http://osmdroid.googlecode.com/svn/branches/release_3_0_5. Si os aparecen errores en el proeycto no os preocupéis mas adelante os explicaremos como configurarlo bien antes de compilar.

 

Una vez creado deberemos realizar una serie de cambios en el fichero MapView.java. Estos cambios vienen recogidos en el siguiente patch. En la líneas que empiezan por @@ veremos a que líneas corresponden estos cambios y deberemos incluir las líneas que empiezan por + y eliminar las que empiezan por -. No obstante os facilito la clase ya modificada para que tan solo tengáis que sustituirla:

 

MapView.java (46,38 kb)

 

Con esto ya tendríamo listo los cambios, pero como os indicabamos antes de compilar deberemos realizar unos cambios en el proyecto:

  • Ir a la configuración del Java Build Path (hacer click con el boton derecho en el proyecto -> properties -> Java Build Path -> Libraries).
  • Editar la entrada “ANDROID_SDK_PLATFORM/android.jar”.
  • Hacer click en el boton “Variable” y añadir una nueva llamada “ANDROID_SDK_PLATFORM” con la ruta necesaria. Por ejemplo C:/Program Files (x86)/Android/android-sdk/platforms/android-3.
  • Volver a la pestaña Libraries del Build Path y mirar dentro de la entrada ”JUnit 4″ el nombre de fichero de hamrest.core y la ruta de junit.jar. Apuntalas, las necesitaras más adelante.
  • Abandonamos Build Path y cambiamos a las opciones de Builders.
  • Creamos un nuevo Ant Builder añadiendo build.xml al “Buildfile” y añadiendo el proyecto como Base Directory.
  • Cambiamos a la pestaña “Targets” y hacemos click en “Set Targets…” para Manual Build.
  • Comprobamos que “jar” esta seleccionado y aceptamos. Ahora en Manual Build deberían estar seleccionados: “build, jar”.
  • Volvemos a aceptar y nos aseguramo de que el nuevo Ant builder esta activo y el java Builder inactivo.
  • Abrir “build-jar.xml y borrar “depends=”version” en la linea 32
  • Borrar en la línea 37 “.r${version}
  • Cambiar en la línea 43 “destfile” por la ruta donde queremos crear el fichero jar.
  • Guardamos y abrimos “build.xml” y modificamos las siguientes líneas.
  • Línea  9 – en el campo value introducimos la ruta del SDK, la misma que introdujimos anteriormente en la configuración del Build Path.
  • Línea  10 – ruta donde tenemos instalado eclipse.
  • Líneas 16 y 17 – Comprabar que los valores son los correctos, deben corresponderse con los que apuntamos anteriormente en el cuarto paso.
  • Ejecutamos “build.xml” como “Ant Build” y nuestro jar estará listo para incluir en el proyecto de nuestra app.

Si aparece algún error relacionado con javac puede ser debido a que tenemos congurado el JRE en vez del JDK dentro del proyecto.

 

Con nuestro jar ya listo y compilado e incluido en nuestro proyecto tan solo deberemos usar el siguiente tip con la zona que queremos limitar y todo ira como la seda:

 

BoundingBoxE6 bbox = new BoundingBoxE6(limit north, limit east, limit south, limit west);
mapView.setScrollableAreaLimit(bbox);

 

Para saber las coordenadas que queremos limitar podemos averiguarlo a traves de la aplicación de MOBAC en de la propia web de OpenStreetMap en la sección de Exportar.

 

No obstante otra limitación que quizas queramos imponer es la de los niveles de zoom, puesto que puede que no hallamos descargado tiles para todos estos. En este caso deberíamos crearnos, en nuestro proyecto, nuestro propio MapView que extendiera al de la librería y del que sobreescribiriamos los métodos getMaxZoomLevel() y getMinZoomLevel().

 

public class ZoomLimitMapView extends MapView
{
    /* snip the constructors */

    @Override
    public int getMaxZoomLevel()
    {
        return 15;
    }

    @Override
    public int getMinZoomLevel()
    {
        return 4;
    }

}

 

Y con esto habríamos acabado, espero que os sea de utilidad.

 

OSMDROID | http://code.google.com/p/osmdroid/
Mobile Atlas Creator | http://mobac.sourceforge.net/

Via | Ingens Blog

 

Espero que os haya gustado y si es así no lo dudéis y ¡¡compartid lo aprendido!!

 

Acerca del autor

JMPergar

Mobile Developer at @BeRepublic & Founder of @AndroCode. Silver Speaker & Member of Core Team at @GDGBarcelona.

  • jfp

    Hola,

    Primeramente muchas gracias por tus tutoriales. La primera sesión funcionó a la perfección, aunque por mala suerte no he tenido la misma suerte con esta parte de los mapas offline. He seguido el tutorial correctamente hasta la parte de la compilación con ant. Luego, he itentado seguir paso a paso esta parte, pero llegando al punto 8 (“Comprobamos que “jar” esta seleccionado y aceptamos. Ahora en Manual Build deberían estar seleccionados: “build, jar”) no me aparecen estas selecciones. Lo he intentado y reintentado muchas veces sin llegar a conseguirlo, utilizando la misma versión.

    Luego, después de no tener éxito, decidí montarme una clase en mi proyecto que gestione el offlinemap, extendiendo de mapview, que si no me equivoco es la manera alternativa a modificar esta clase sin tener que recompilar la librería (no cambiamos nada de ella, sino que creo esta nueva clase que extiende de la librería). El caso es que tampoco me funciona, el archivo del mapa está metido en el directorio que comentas, además he probado con distintos tamaños de archivos (por miedo a que tuviera un mapa demasiado grande y tardara demasiado en hacer el loading). Una vez más, no lo consigo, así que me pongo a buscar por la red otros tutoriales, para ver si encuentro el problema, pero primeramente encuentro una web que referencia a este tutorial, y otras donde la información es escasa.

    Mi idea era analizar osmdroid con la finalidad de ver la alternativa a google maps, pero además poder editar mapas y tener mapas offline. pero veo que de momento está bastante verde el tema, aún y así seguiré probando durante unos días.

    No es que obtenga ningún tipo de error, el problema está en que se muestran las cuadrículas del mapa pero el mapa nunca llega a cargarse.

    Tú has conseguido hacer que funcione, te importaría pasarme este pequeño ejemplo o la librería modificada y compilada como has hecho aquí? Me interesa saber qué estoy haciendo mal, y intentar seguir con edición de mapas y indagar un poco más, y no perder más tiempo intentando arreglar algo que hace ya varios días que intento arreglar.

    Ya me dirás alguna cosa en cuánto puedas,

    Muchas gracias.

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      Lo siento, al final en mi proyecto me acabe decantando por otra librería (MapsForge) de la que publicaremos post pronto y el jar que modifique lo he perdido.

  • Pedro

    Hola, estoy intentando programar una app de rutas utilizando OpenStreetMap en modo offline. Esoy teniendo bastantes probemas. En especial dos.

    El primero es que por mas que me intento concentrar en las instrucciones para limitar el mapa no lo consigo. He descargado el parche de limitacion a mi disco duro pero no se como integrarlo. Si usted fuese tan amable de facilitarnos los archivos del codigo fuente para estudiarlo en eclipse estaria muy agradecido.

    El segundo problema y con este me estoy volviendo loco es por temas de memoria y es el que mas me preocupa.

    Yo tengo una lista de rutas, selecciono una de ellas y voy a una actividad donde se reproduce un video, cuando el video termina aparece el mapa.

    Manejo el mapa, activo el gps y funciona bien, el problema es que si salgo a la lista de rutas y empiezo el proceso desde el principio hasta llegar al mapa de nuevo se bloquea, esto me pasa si repito el proceso 3 o 4 veces.

    No se porque pero no libera bien la memoria, he intentado de todo, incluso he utilizado System.gc() pero siempre termina cerrandose el mapa. Si me puede dar alguna pista que podria hacer me vendria de perlas ya que esto de gestionar la memoria cuando utilizamos estos mapas es realmente importante, incluso mas que saber como integrar los mapas en nuestra aplicación.

    Gracias por su ayuda.

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      En cuanto al parche, en el mismo post hay un enlace al fichero java ya modificado para que te ahorres problemas. Y en cuanto a lo segundo… entiendo que cuando vuelves a la lista matas la activity? y que liberas las clases de reproduccion multimedia?

  • Pedro

    Hola, siento no contestar antes, he estado unos dias de vacaciones.

    Respecto a los problemas de memoria parece que he conseguido solucionarlo con algunas medidas buenas y otras muy feas.

    Alguna fea consiste en matar la aplicacion para salir del mapa como ultima solucion a la desesperada con este codigo.

    finish();
    Intent intent = new Intent(getBaseContext(), Lista.class);
    startActivity(intent);
    android.os.Process.killProcess(android.os.Process.myPid());

    Algo mas bonito a sido descargar los azulejos en lugar de .zip los he descargado en GEMP, y posteriormente he convertido el paquete de azulejos a .zip. Con esto he conseguido que los azulejos ocupen bastante menos con el consiguiente ahorro de memoria y ganar algo de fluidez en el mapa.

    Ademas de intentar liberar todos los recursos variables etc… que utilizo.

    Tambien he puesto este comando para impedir que se descargue azulejos desde internet cuando utilizo mi aplicación.

    //Impide descargar losetas de internet
    myOpenMapView.setUseDataConnection(false);

    En cuanto al parche de los limites no me aclaro como integrarlo en mi aplicacion, me conformaria con conseguir un comando como el anterior el que impide cargar azulejos desde internet pero que en este caso impidiera cargar losetas desde la carpeta “osmdroid/tiles/Mapnik” y asi conseguir que solo descargue azulejos desde mi archivo zip pero no se si existe ese comando. He investigado pero sin resultado.

    Si alguien conoce un comando que realice lo ultimo que comento lo agradeceria.

    Un saludo.

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      En cuanto al comando para los limites eso eso, el parche, una vez puesto lo harás con un solo comando, pero tienes que añadir esta funcionalidad al código fuente de la librería ya que no la trae de manera oficial.

      Y en cuanto a la descarga de tiles bastará con que elimines el permiso de acceso a internet en el manifest.

  • Chen

    Las últimas capas de Mapnik están bloqueadas por sobreuso externo de osm.

    He probado con otras fuentes pero no funciona.

    ¿Alguna solución?

  • Pau

    MUY BUENO, el tutorial. Me gusto mucho y es de mucha ayuda. Una consulta, esto no es vectorial ¿correcto? Por lo tanto no se puede personalizar.
    Saludos y excelente

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      No, no es vectorial. Si buscas mapas vectoriales la librería que hemos probado es MapsForge. Pronto publicaremos un post sobre ella.

  • Cristian

    Hola antes que nada, muchas gracias por tu aporte. Tengo una consulta, ¿Solo funciona con android 3 en adelante? o tambien es compatible con versiones anteriores. Desde ya muchas gracias

    • Cristian

      Perdon, antes que me respondas, es que vi mal. En el tutorial anterior decia API lvl3, no android

  • Santiago

    Muchas gracias por los tutoriales, me han ayudado bastante.

  • XOLA

    Hola, la verdad es que el tutorial está muy bien. Llevaba tiempo buscando información sobre este tema y al fin la he encontrado.
    Lo he seguido punto a punto y he conseguido limitar los niveles de zoom, y me funciona bien, pero cuando limito el área tengo varios problemas.
    1. no se ve el mapa, a no ser que que estes manipulándolo
    2. no coge bien los límtes por que sólo se mueve horizontalmente
    No sé que puedo estar haciendo mal.

    ¿Alguna sugerencia?

    Muchas gracias.

    • XOLA

      corregido …. me había equivocado al poner las coordenadas …

      Como decia antes … un buen tutorial.

      Muchas gracias.

  • Juan

    Hola, quisiera saber si lo que voy a plantear es posible.

    Con la siguiente linea de se impide descargar azulejos de la web de openstremap
    mapa.setUseDataConnection(false);

    Lo que me gustaria conocer es si existe algun comando para que tambien impida
    que se descarguen los azulejos de la carpeta Mapnik que se encuentra en la SD, y
    asi conseguir que solo tenga acceso a nuestro zip de mapas.

    Un saludo

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      Bastaría con eliminar el permiso de acceso a internet.

      • Juan

        Primero gracias por atenderme.

        El problema es que no puedo eliminar el premiso a internet por que lo necesita para que la app descargue archivos.

        He averiguado que efectivamente, con este comando impide el acceso a internet.
        Esto en el codigo: mapa.setUseDataConnection(false);
        Y con esto en el manifest:

        Curiosamente si en el manifest coloco este otro permiso ( ) deja de funcionar lo anterior y de nuevo empieza a descargar azulejos utilizando la red. Y es que este permiso lo necesito y no puedo quitarlo.

        De todas formas este ultimo permiso lo he quitado y cuando tiene que descargar algo la app da error, momento que en el try cath salta la excepcion y saco un mensaje diciendo que compruebe la conexion de internet en el movil. Una chapucilla pero esque no se me ocurre otra.

        Esto es de locos, pero creo que ya lo dejare asi.

        Gracias.

  • Juan

    Primero gracias por atenderme.

    El problema es que no puedo eliminar el premiso a internet por que lo necesita para que la app descargue archivos.

    He averiguado que efectivamente, con este comando impide el acceso a internet.
    Esto en el codigo: mapa.setUseDataConnection(false);
    Y con esto en el manifest: ( uses-permission android:name=”android.permission.INTERNET” /uses-permission )

    Curiosamente si en el manifest coloco este otro permiso ( uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE” /uses-permission ) deja de funcionar lo anterior y de nuevo empieza a descargar azulejos utilizando la red. Y es que este permiso lo necesito y no puedo quitarlo.

    De todas formas este ultimo permiso lo he quitado y cuando tiene que descargar algo la app da error, momento que en el try cath salta la excepcion y saco un mensaje diciendo que compruebe la conexion de internet en el movil. Una chapucilla pero esque no se me ocurre otra.

    Esto es de locos, pero creo que ya lo dejare asi.

    Gracias.

  • Juan

    Por cierto, he conseguido este codigo sin utilizar parches para limitar las fronteras, solo que da una excepción cuando se hace zoom menos sobre una frontera. Estoy intentando arreglarlo y de momento no lo he conseguido. Dejo aqui el codigo por si alguien quiere trastearlo para solucionar el problema, seria de gran utilidad para evitar el engorro del parche.

    Codigo:
    mapView.setMapListener(new MapListener() {

    @Override
    public boolean onZoom(ZoomEvent arg0) {
    return false;
    }

    @Override
    public boolean onScroll(ScrollEvent arg0) {

    /* Log.i(“CartoApp”, mapView.getMapCenter().getLatitudeE6() + ” / ” + mapView.getMapCenter().getLongitudeE6());
    Log.i(“CartoApp”, “Lat North: ” + mapView.getBoundingBox().getLatNorthE6());
    Log.i(“CartoApp”, “Lon East: ” + mapView.getBoundingBox().getLonEastE6());
    Log.i(“CartoApp”, “Lat South: ” + mapView.getBoundingBox().getLatSouthE6());
    Log.i(“CartoApp”, “Lon West: ” + mapView.getBoundingBox().getLonWestE6()); */
    if (mapView.getBoundingBox().getLatNorthE6() >= scrollLimitNorthE6 ||
    mapView.getBoundingBox().getLonEastE6() >= scrollLimitEastE6 ||
    mapView.getBoundingBox().getLatSouthE6() <= scrollLimitSouthE6 ||
    mapView.getBoundingBox().getLonWestE6() <= scrollLimitWestE6) {
    mapView.scrollTo(lastVaildX, lastValidY);
    } else {
    lastVaildX = arg0.getX();
    lastValidY = arg0.getY();
    }

    return false;
    }
    });

    Post original del creador.

    http://mappingdev.wordpress.com/tag/scroll/

  • manuel

    buenos dias,

    que buen tutorial, muchas gracias.

    actualmente estoy teniendo un problema es que cuando cargo los mapas en el .zip, no me los muestra, me toca descomprimirlos y guardarlos en la carpeta tiles, asi medio funciona ya que cuando me voy moviento en el mapa, muestra huecos “cuadros grises”, que despues de un buen tiempo 20seg, carga su respectivo azulejo, sinceramente esto asi se vuelve desesperante, esperando 20seg para que cargen algunos azulejos.

    he intenteado con mapas de google, de microsoft y de openstreet.

    muchas gracias por su ayuda.

    • http://www.linkedin.com/in/jmpergar JMPergar

      A mi me pasaba lo mismo, el rendimiento me parecía inaceptable y al final migre a MapsForge

  • http://gravatar.com/tonatiuhnava tonatiuhnava

    tendras el codigo fuente que lo puedas conpartir, gracias.

  • juan

    por favor quisiera que me ayudes a capturar la latitud y longintud de una ubicacion en el mapa

  • LuisPer

    Y yo que estoy programando en Javascript lo mismo que hace gmaps de Google, no sabia que existian estas herramientas free. A alguno le ha funcionado??? Es facil extender la funcionalidad ???? Quizas esto es muy antiguo y ya no tiene importancia.

    • webserveis

      yo tengo una aplicación con mapas offline usando la libreria leafleft y con esa extraigo los tiles

  • Amaia

    Hola!!
    Estoy atascada aquí “Con nuestro ficheros ya generados y descargados tan solo tendremos que copiarlos a nuestra tarjeta SD en la carpeta /sdcart/osmdroid y la librería se encargará sola de acceder a ellos.”
    Tengo que meter el mapa en mi móvil? y si por ejemplo yo subo mi aplicación al market y se la descarga alguien….no le funcionará si no tiene el mapa en su móvil no? Igual es que no lo he entendido bien.
    Gracias.

  • webserveis

    Genial, gran tuto para crear aplicaciones con mapas offline