Ingens Networks  Ingens Developments  Ingens Biometrics  Ingens Hostings

Mapas Offline con OpenStreetMap en iOS

13 junio 2012 12:30 by rafael.aguilar

Apple presentó este lunes lo nuevo en iOS 6, nuevos MacBooks, Mountain Lion.... Pero una de las cosas que más llamó la atención es el "temido" divorcio de Apple-Google con el tema del servicio de mapas. Parece que Apple se ha asociado con Tom Tom y otras compañías para que les ofrezcan los datos necesarios para su nuevo servicio.

 

Aún no se sabe si estos mapas podrán ofrecernos servicios Offline, y creo que es una feature casi imprescindible actualmente. Por eso la intención de este artículo es de cómo implementar en nuestros proyectos mapas offline con OpenStreetMap.

 

 

Primero de todo, nos tendremos que descargar las tiles de la zona en cuestión y para ello utilizaremos el script downloadosmtiles.pl. Este script descarga todas las tiles del mapa de un servidor de OpenStreetMap de una región geográfica específica con un rango de niveles de zoom. Las tiles serán imágenes PNG.

 


¿QUÉ ES ESO DE TILES?


Literalmente en inglés significa "azulejos" pero aquí nos referimos a fragmentos del mapa. Los gráficos de mapa de bits cuadrados que aparecen en una disposición de la red para mostrar un mapa (como si una red de azulejos se tratase).

 

 

DESCARGANDO TILES


Para ello deberemos descargar, compilar e instalar GEO-OSM-Tiles 0.04. Está escrito en Perl para ello deberemos ejecutar estos comandos desde la terminal:

 

#Si no tenemos instalado wget podemos sustituirlo por curl -O
wget http://search.cpan.org/CPAN/authors/id/R/RO/ROTKRAUT/Geo-OSM-Tiles-0.04.tar.gz
tar -xf Geo-OSM-Tiles-0.04.tar.gz
cd Geo-OSM-Tiles-0.04

# Nos tenemos que asegurar que no da ningún tipo de errores o warning
perl Makefile.PL
make
make test
sudo make install

 

Si nos devuelve un error similar a este "Warning: prerequisite YAML 0 not found." es que faltan por habilitar/instalar algunas librerías Perl. Para ello, lo más fácil que podemos hacer es ejecutar este comando en la terminal y una vez hecho volver a ejecutar el paso anterior:

 

sudo perl -MCPAN -e 'install Bundle::CPAN'

 

Ahora deberemos conseguir las coordenadas de la zona que queremos descargar. Para ello nos iremos al site de http://www.openstreetmap.org/ y haremos click en el apartado "Exportar". Ahora nos aparecerá cuatro campos de coordenadas en "Area a exportar", estos son los que nos interesa pero queremos una zona en concreto así que pulsaremos "Seleccionar a mano otra área" y elegimos la que deseemos en el mapa.

 

 

Ahora deberemos ejecutar downloadosmtiles.pl en la terminal con las coordenadas que nos devolvió antes y el nivel de zoom (en nuestro caso 15).

 

downloadosmtiles.pl --lat=41.3641:41.4113 --long=2.068:2.1605 --zoom=0:15 --destdir=/mydir/Desktop/mapsOfflineBarcelona/

 

Nos deberá haber quedado algo similar a esto:

 

 

 


CONVERTIR TILES A SQLITE


Deberemos descargar y ejecutar map2sqlite para convertir las tiles en una base de datos sqlite, la cual la utilizaremos posteriormente con la librería route-me

 

wget http://www.ingens-networks.com/blog/file.axd?file=2012%2f6%2fmap2sqlite-bin.zip
tar -xf map2sqlite-bin.zip

./map2sqlite -db /dir/mapBarcelona.sqlite -mapdir /tu/carpeta/mapsOfflineBarcelona

 

 

UTILIZAR SQLITE CON ROUTE-ME


Route-me funciona actualmente con OpenStreetMap, Microsoft VirtualEarth, CloudMade, OpenAerialMap, OpenCycleMap, SpatialCloud, TileStream7, entre otros. En este artículo vamos a utilizar un proyecto de ejemplo que ya viene con Route-me "samples/SimpleMap/SimpleMap.xcodeproj".

 

1 - Nos descargamos el proyecto desde https://github.com/route-me/route-me

 

2 - Abrimos el proyecto que hemos comentado anteriormente, elegimos el scheme "SimpleMap" y ejecutamos para ver que no tenemos errores.

 

3 - Añadimos el sqlite generado antes al proyecto y comprobamos que lo tenemos también añadido al "Copy Bundle Resources".

 

 

4 - Debemos importar RMDBMapSource.h al fichero de implementación de MapViewViewController (MapViewViewController.m)

 

#import "RMDBMapSource.h"

 

5 - Añadimos estas líneas al método viewDidLoad de MapViewViewController.m:

 

  // Localización cercana a las oficinas de Ingens Networks
  CLLocationCoordinate2D coolPlace;
  coolPlace.latitude = 41.364925;
  coolPlace.longitude = 2.137699;
    
  // Utilizamos el sqlite generado anteriormente
  RMDBMapSource *mapSrc = [[[RMDBMapSource alloc] initWithPath:@"mapBarcelona.sqlite"] autorelease];
  [[[RMMapContents alloc] initWithView:mapView tilesource:mapSrc] autorelease];
    
  // Bloqueamos la navegación a solo los tiles que tenemos habilitado en nuestro sqlite
  [mapView setConstraintsSW:CLLocationCoordinate2DMake(mapSrc.bottomRightOfCoverage.latitude, mapSrc.topLeftOfCoverage.longitude)
                           NE:CLLocationCoordinate2DMake(mapSrc.topLeftOfCoverage.latitude, mapSrc.bottomRightOfCoverage.longitude)];

  // Centramos el mapa a la localización indicada en coolPlace
  [mapView moveToLatLong:coolPlace];

 

Lo arrancamos en nuestro dispositivo y nos tendría que salir algo similar a esto.

 

 

6 - Ahora vamos a añadir POI´s (puntos de interes - marcas) a nuestro mapa.

 

  // Localización cercana a las oficinas de Ingens Networks
  CLLocationCoordinate2D coolPlace;
  coolPlace.latitude = 41.364925;
  coolPlace.longitude = 2.137699;
  
  // Segunda localización
  CLLocationCoordinate2D coolPlace2;
  coolPlace2.latitude = 41.365478;
  coolPlace2.longitude = 2.137799;
    
  // Utilizamos el sqlite generado anteriormente
  RMDBMapSource *mapSrc = [[[RMDBMapSource alloc] initWithPath:@"mapBarcelona.sqlite"] autorelease];
  [[[RMMapContents alloc] initWithView:mapView tilesource:mapSrc] autorelease];
    
  RMMarkerManager *markerManager = [mapView markerManager];
  [mapView setDelegate:self];
    
  // Bloqueamos la navegación a solo los tiles que tenemos habilitado en nuestro sqlite
  [mapView setConstraintsSW:CLLocationCoordinate2DMake(mapSrc.bottomRightOfCoverage.latitude, mapSrc.topLeftOfCoverage.longitude)
                           NE:CLLocationCoordinate2DMake(mapSrc.topLeftOfCoverage.latitude, mapSrc.bottomRightOfCoverage.longitude)];

  // Centramos el mapa a la localización indicada en coolPlace
  [mapView moveToLatLong:coolPlace];
  
  // Creamos los POIs
  RMMarker *marker = [[RMMarker alloc]initWithUIImage:[UIImage imageNamed:@"marker-blue.png"]
											anchorPoint:CGPointMake(0.5, 1.0)];
  [marker setTextForegroundColor:[UIColor blueColor]];
  [marker changeLabelUsingText:@"Ingens Networks"];
  [markerManager addMarker:marker AtLatLong:coolPlace];
  [marker release];
    
  RMMarker *marker2 = [[RMMarker alloc]initWithUIImage:[UIImage imageNamed:@"marker-red.png"]
											anchorPoint:CGPointMake(0.5, 1.0)];
  [marker2 setTextForegroundColor:[UIColor blackColor]];
  [marker2 changeLabelUsingText:@"Segunda"];
  [markerManager addMarker:marker2 AtLatLong:coolPlace2];
  [marker2 release];

 

 

Ahora bien, si queremos pintar nuestra posición obtenida a través del GPS, es tan fácil como utilizar el framework Core Location que ya está importada al proyecto. Declaramos en el archivo MapViewViewController.h que MapViewViewController cumple con el protocolo CLLocationManagerDelegate. Así pues el fichero de cabecerá quedará así:

 

#import <UIKit/UIKit.h>
#import "RMMapView.h"

@interface MapViewViewController : UIViewController <RMMapViewDelegate, CLLocationManagerDelegate> {
	IBOutlet RMMapView * mapView;
	BOOL tap;
	NSInteger tapCount;
    CLLocationManager *locationManager;
}

@property (nonatomic, retain) IBOutlet RMMapView * mapView;

@end

 

Ya solo nos faltará implementar los métodos delegados del protocolo CLLocationManagerDelegate en el fichero de implementación (MapViewViewController.m). En este ejemplo hacemos que cuando nuestra posición GPS se modifique, coja uno de los POI que creamos anteriormente y lo ubique en nuestra posición actual.

 

#pragma mark -
#pragma mark CLLocationManagerDelegate methods

-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    NSLog(@"No se puede encontrar la localización: %@", error);
}

-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
    if (mapView) {
        RMMarker *marker = [[mapView markerManager].markers objectAtIndex:1];
        [mapView.markerManager moveMarker:marker AtLatLon:newLocation.coordinate];
    }
}

Tags: , , , , , , ,

iOS

Comentarios (11) -

13/06/2012 15:37:09 #

Jose Hidalgo

Genial articulo, Rafa.

Una pregunta, los mapas offline obtienen los tiles de la SQLite, según he entendido, y con setConstraints sobre el MapView limitas la navegación a los "bounds" del mapa que tienes descargados. ¿Que pasaría si, al obtener tu posición con CL, como indicas en tu último ejemplo, éste quedara fuera de esos límites que tienes impuestos sobre el mapa?

Jose Hidalgo

13/06/2012 15:59:06 #

Richie

Que bueno, tio.

Estoy guardando tus artículos. Dentro de poco los imprimiré y los sacaré a la venta como "Advanced iOS Programming Cookbook". Me voy a hacer de oro. Smile

Richie

13/06/2012 16:26:58 #

rafael.aguilar

Muy buena pregunta Jose.

No pasaría nada si nuestra posición se saliera de los límites que indicamos con setConstraints ya que lo que movemos es el POI (indica nuestra posición) y este no aparecería porque no estamos dentro de sus límites.

Pero eso me ha llevado a otra prueba, ¿qué pasaría si en el momento que mandamos el mensaje "moveToLatLong:" a la instancia de MKMapView para centrar el mapa, le pasamos como parámetro una localización que está fuera de los límites?. Pues he hecho la prueba y me cargaría dicha localización pero no mostraría nada (fondo gris) ya que no se encuentra en el "mapSource" que hemos indicado al principio (mapBarcelona.sqlite).

rafael.aguilar

13/06/2012 16:28:34 #

rafael.aguilar

Respecto a ti Richie.... Los abogados de Ingens te siguen de cerca ;)

rafael.aguilar

22/06/2012 17:04:46 #

Brais Gab&#237;n

Creo que hay una errata, o por lo menos para que me funcionará ami he tenido que cambiarlo. El ejemplo de sentencia para descargar los tiles en lat hay que intercambiarlos quedaria:

downloadosmtiles.pl --lat=41.3641:41.4113 --long=2.068:2.1605 --zoom=0:15 --destdir=/mydir/Desktop/mapsOfflineBarcelona/

Gracias por el articulo, muy interesante.

Brais Gabín

26/06/2012 16:31:40 #

rafael.aguilar

Hola, Brais.

Efectivamente, tenías razón. Ya ha sido solucionado la pequeña errata.


Un Saludo

rafael.aguilar

16/07/2012 18:10:19 #

arjo

como trazo una ruta conociendo la lat y long de 2 puntos

arjo

17/07/2012 19:50:02 #

jose roberto

hola oye una pregunta como puedo agregar el mapa en una vista mas chica es que se me hizo interesante lo que estas haciendo y pues me di a la tarea de hacerlo y es que mi problema es el siguiente quiero que me aparezca el mapa una área especifica porque igual tu lo estas vaciando como si fuera una Wallpaper por medio del viewWallpaper por lo cual no puedo agregar una subvista donde pueda agregar pines por medio de un menú por que siempre que cambio de vista el mapa me vuelve a parecer.no se en algo que me puedas ayudar con implementar el mapa en una vista en una área especifica.

jose roberto

24/07/2012 11:26:10 #

rafael.aguilar

Buenos días, Arjo.

Mira este hilo en el "Google groups" oficial de Route-me http://goo.gl/OiJbi. Espero que te ayude

rafael.aguilar

24/07/2012 11:37:06 #

rafael.aguilar

Hola, Jose Roberto.

No sé si te he entendido bien. Creo que tienes dos problemas:

1- Uno que deseas incorporar el mapa en una View con un tamaño que no ocupe toda la pantalla -> Para esto te recomiendo que lo hagas desde un proyecto desde cero e incorpores Route-me a ese proyecto y no utilices los Samples que utilizamos en el artículo. Una vez hecho ya podrás darle el tamaño que desees a la View.

2- Implementar un mapa con un área específica -> Esto ya lo estamos haciendo en el ejemplo con el método setConstraintsSW:NE:

Saludos

rafael.aguilar

21/11/2012 19:37:00 #

arturo alatorre

me parece interesante tus aportaciones amigo una pregunta

¿como puedo hacer un ruteo de un punto A y un punto B? no se si puede ser desde el mapa online o necesito hacerlo en tiles ? muchas gracias

arturo alatorre

Agregar comentario

biuquote
  • Comentario
  • Vista previa
Loading

Archivo

Pregunta

¿Cuanto dinero gastas mensualmente en la store de tu smartphone (googleplay, appstore, marketplace,...)?





Show Results
 Ingens Networks SL en LinkedIn Ingens Networks SL en Twitter