Home > iphone, sqlite3 > iPhone Dev Note #21: Route-Me Offline Mapping from Database

iPhone Dev Note #21: Route-Me Offline Mapping from Database

August 12th, 2010 rupert

Part I: Download the osm (openstreetmap) tiles
1. Download the tiles from osm using downloadosmtiles.pl
- Download Geo-OSM-Tiles-0.02.tar.gz from CPAN
- See README file. Compile and build

   perl Makefile.PL
   make
   make test
   make install

- Copy downloadosmtiles.pl to /usr/bin
- Usage:

downloadosmtiles.pl --lat=min_lat:max_lat --lon=min_long:max_long --zoom=min_zoom:max_zoom

How do you set the min_lat, max_lat and min_long, max_long?
- Go to www.openstreetmap.org
- Click on the “Edit” tab
- You will see the extent or bounds of the map under the section “Area to Export”
- Alternatively, you can click on “Manually select a different area” to specifically choose an area.

osm.png

downloadosmtiles.pl --lat=6.9443:7.2261 --lon=125.5082:125.7104 --zoom=6:12

The tiles will be downloaded to the current directory.
dir.png

Part II:Put the tiles in the sqlite database
1. Read Frank’s email to route-me group regarding map2sqlite

2. Download map2sqlite-1.0.tar.bz2.

3. Build the map2sqlite XCodeProj. Afterwards, find map2sqlite and drop it in /usr/bin.

cp map2sqlite /usr/bin

4. Run map2sqlite to import the tiles in sqlite.

map2sqlite -db ph-1.0.0.db -mapdir ph-osm-map/
 
2010-08-12 17:24:40.749 map2sqlite[14113:903] map2sqlite 1.0
2010-08-12 17:24:40.756 map2sqlite[14113:903] Creating ph-1.0.0.db
2010-08-12 17:24:40.761 map2sqlite[14113:903] Importing map tiles at ph-osm-map/
2010-08-12 17:25:03.169 map2sqlite[14113:903] 
2010-08-12 17:25:03.170 map2sqlite[14113:903] Map statistics
2010-08-12 17:25:03.170 map2sqlite[14113:903] --------------
2010-08-12 17:25:03.171 map2sqlite[14113:903] map db:            ph-1.0.0.db
2010-08-12 17:25:03.171 map2sqlite[14113:903] file size:         13758464 bytes
2010-08-12 17:25:03.172 map2sqlite[14113:903] tile directory:    ph-osm-map/
2010-08-12 17:25:03.172 map2sqlite[14113:903] number of tiles:   9091
2010-08-12 17:25:03.173 map2sqlite[14113:903] zoom levels:       6 - 11
2010-08-12 17:25:03.218 map2sqlite[14113:903] zoom level  6:        12 tiles, (    28,    52)x(    31,    54), {x=112.500000,y=21.943047}x{x=129.375000,y=0.000000}
2010-08-12 17:25:03.219 map2sqlite[14113:903] zoom level  7:        35 tiles, (    56,   105)x(    62,   109), {x=115.312500,y=21.943047}x{x=129.375000,y=2.811371}
2010-08-12 17:25:03.222 map2sqlite[14113:903] zoom level  8:       117 tiles, (   112,   210)x(   124,   218), {x=115.312500,y=21.943047}x{x=127.968750,y=4.214943}
2010-08-12 17:25:03.223 map2sqlite[14113:903] zoom level  9:       450 tiles, (   225,   420)x(   249,   437), {x=115.312500,y=21.289375}x{x=127.968750,y=4.214943}
2010-08-12 17:25:03.225 map2sqlite[14113:903] zoom level 10:      1715 tiles, (   450,   841)x(   498,   875), {x=115.664062,y=21.289375}x{x=127.968750,y=4.565474}
2010-08-12 17:25:03.231 map2sqlite[14113:903] zoom level 11:      6762 tiles, (   900,  1683)x(   997,  1751), {x=115.839844,y=21.289375}x{x=127.968750,y=4.565474}

5. ph-osm-map is 43.9 MB but was compressed to ph-1.0.0.db (13.8 MB)

Part III: Downlaod the route-me code from trunk and run some examples.
- Follow this previous tutorial

Part IV: Patch the trunk to incorporate the RMDBMapSource from Frank Schroder

1. What we need to add to the trunk. Download RMDBMapSource.zip

+ RMDBMapSource.h
+ RMDBMapSource.m
+ RMDBTileImage.h
+ RMDBTileImage.m

Copy the files above to the “Map” directory.

route-me-1.png

2. Edit RMTileImage.h and RMTileImage.m base on the patch below.

Index: MapView/Map/RMTileImage.h
===================================================================
--- MapView/Map/RMTileImage.h	(revision 605)
+++ MapView/Map/RMTileImage.h	(working copy)
@@ -37,8 +37,10 @@
 #import "RMNotifications.h"
 #import "RMTile.h"
 #import "RMTileProxy.h"
+#import "FMDatabase.h"
 
 @class RMTileImage;
+@class NSData;
 
 @interface RMTileImage : NSObject {
 	// I know this is a bit nasty.
@@ -64,6 +66,7 @@
 + (RMTileImage*)imageForTile: (RMTile) tile withURL: (NSString*)url;
 + (RMTileImage*)imageForTile: (RMTile) tile fromFile: (NSString*)filename;
 + (RMTileImage*)imageForTile: (RMTile) tile withData: (NSData*)data;
++ (RMTileImage*)imageForTile: (RMTile) tile fromDB: (FMDatabase*)db;
 
 - (void)moveBy: (CGSize) delta;
 - (void)zoomByFactor: (float) zoomFactor near:(CGPoint) center;
Index: MapView/Map/RMTileImage.m
===================================================================
--- MapView/Map/RMTileImage.m	(revision 605)
+++ MapView/Map/RMTileImage.m	(working copy)
@@ -29,6 +29,7 @@
 #import "RMWebTileImage.h"
 #import "RMTileLoader.h"
 #import "RMFileTileImage.h"
+#import "RMDBTileImage.h"
 #import "RMTileCache.h"
 #import "RMPixel.h"
 #import <QuartzCore/QuartzCore.h>
@@ -108,6 +109,11 @@
 	return [image autorelease];
 }
 
++ (RMTileImage*)imageForTile:(RMTile) _tile fromDB: (FMDatabase*)db
+{
+	return [[[RMDBTileImage alloc] initWithTile: _tile fromDB:db] autorelease];
+}
+
 -(void) cancelLoading
 {
 	[[NSNotificationCenter defaultCenter] postNotificationName:RMMapImageLoadingCancelledNotification

We just need to add these lines on RMTileImage.h:

+#import "FMDatabase.h"
...
++ (RMTileImage*)imageForTile: (RMTile) tile fromDB: (FMDatabase*)db;

do the same for RMTileImage.m:

+#import "RMDBTileImage.h"
...
++ (RMTileImage*)imageForTile:(RMTile) _tile fromDB: (FMDatabase*)db
+{
+	return [[[RMDBTileImage alloc] initWithTile: _tile fromDB:db] autorelease];
+}

UPDATE (OCT 15, 2010): I am attaching my current RMTileImage.h and RMTileImage.m so you guys could double-check the changes I made. RMTileImage.zip

4. Still with me? Comment NSAssert on 609 on RMMapContents.m
rmcontents.png

Part V: RouteMeSampleMapDBOffline code
1. Download RouteMeSampleMapDBOffline.zip

2. Drop the project in the samples directory.
sample_proj.png

3. Build. You should be able to build this since the header path is relative to the route-me trunk.
build-config.png

4. Run from the simulator
finish.png

Categories: iphone, sqlite3 Tags: , ,
  1. August 30th, 2010 at 03:31 | #1

    Hi, thank you for this article which helps me a lot to read offline map from database. I have integrated the code into my project and it loads the db well. But I am not able to export the tiles from OpenStreetMap, the first link seems invalid currently.

    Could you please describe how to use downloadosmtiles.pl?

  2. August 30th, 2010 at 08:50 | #2

    Hey,

    I updated the tutorial explaining downloadosmtiles.pl. You can get it from CPAN, compile and build. Afterwards, you can grab the bounds of your map from osm.

    Cheers,
    rupert

  3. September 1st, 2010 at 06:43 | #3

    Hello, Thank you for your article but i have a few questions:
    - you say (Part III: Download the route-me code from trunk and run some examples.)
    ok i did and it is revision 633
    - you say (2. Edit RMTileImage.h and RMTileImage.m base on the patch below.)
    which exactly patch file is it?
    (http://groups.google.com/group/route-me-map/files)
    thanks in advance!

  4. September 15th, 2010 at 08:10 | #4

    This is great – thanks so much!

    I have a static set of map tile loaded in and it all works wonderfully.
    I would love to find a way to have the map view not overshoot the edges of my included tiles and go off into “grey background no-man’s land”.

    Is there a way to either:

    – add to the image loading method and respond to a nil image

    or better

    – have map view stop scrolling a specific lat/lng boundaries

    thanks again for your help.

    Greg

  5. September 27th, 2010 at 21:49 | #5

    Hello,

    Thank you for this nice tutorial. I followed all the Steps, but now i get an Exception,wich i will post at the End of this Comment, and don`t know how to fix it. I tried with route-me Version 0.5 and 0.4. The normal Version of Route
    -me with your previous Tutorial is running without any Problems. After patching the Files for loading the tiles from my own Database i get the Exception with my own Project and with your RouteMeSampleMapOffline Example as well. A Difference between your tutorial and my Worksteps ist that in the RMMapContents.m there is no NSAssert on Line 608. There is no NSAssert like this in the whole File. Is it possible that you have used an older Version than 0.4 of route-me for your tutorial?

    It would be nice if you could contact me.

    Greets Marcus

    Exception:

    2010-09-27 13:36:07.809 RouteMeSampleMapDBOffline[15914:207] Failed to create writable database file with message ‘The operation couldn’t be completed. No such file or directory’.
    2010-09-27 13:36:07.814 RouteMeSampleMapDBOffline[15914:207] initWithFrame: logged method call: -[MapView at 0,0--2,0 initWithFrame:] (line 77)
    2010-09-27 13:36:07.815 RouteMeSampleMapDBOffline[15914:207] performInitialSetup logged method call: -[MapView at 0,0-320,460 performInitialSetup] (line 58)
    2010-09-27 13:36:07.817 RouteMeSampleMapDBOffline[15914:207] Opening db map source ph-1.0.0.db
    2010-09-27 13:36:07.818 RouteMeSampleMapDBOffline[15914:207] Tile size: -2147483648 pixel
    2010-09-27 13:36:07.819 RouteMeSampleMapDBOffline[15914:207] Supported zoom range: 0 – -1042284544
    2010-09-27 13:36:07.820 RouteMeSampleMapDBOffline[15914:207] Coverage area: (-2147483648.000000,-2147483648.000000) x (-2147483648.000000,-2147483648.000000)
    2010-09-27 13:36:07.821 RouteMeSampleMapDBOffline[15914:207] Center: (-2147483648.000000,-2147483648.000000)
    2010-09-27 13:36:07.823 RouteMeSampleMapDBOffline[15914:207] -[RMFractalTileProjection initFromProjection:tileSideLength:maxZoom:minZoom:]: unrecognized selector sent to instance 0x5d2e0b0
    2010-09-27 13:36:07.826 RouteMeSampleMapDBOffline[15914:207] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[RMFractalTileProjection initFromProjection:tileSideLength:maxZoom:minZoom:]: unrecognized selector sent to instance 0x5d2e0b0′
    *** Call stack at first throw:
    (
    0 CoreFoundation 0x026eb919 __exceptionPreprocess + 185
    1 libobjc.A.dylib 0x025005de objc_exception_throw + 47
    2 CoreFoundation 0x026ed42b -[NSObject(NSObject) doesNotRecognizeSelector:] + 187
    3 CoreFoundation 0x0265d116 ___forwarding___ + 966
    4 CoreFoundation 0x0265ccd2 _CF_forwarding_prep_0 + 50
    5 RouteMeSampleMapDBOffline 0x00019eda -[RMDBMapSource initWithPath:] + 1253
    6 RouteMeSampleMapDBOffline 0x00002bb6 -[RouteMeSampleMapDBOfflineViewController viewWillAppear:] + 368
    7 UIKit 0x0037198a -[UIView(Hierarchy) _willMoveToWindow:withAncestorView:] + 207
    8 UIKit 0×00373742 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 326
    9 UIKit 0x00371d87 -[UIView(Hierarchy) addSubview:] + 57
    10 RouteMeSampleMapDBOffline 0x0000268e -[RouteMeSampleMapDBOfflineAppDelegate applicationDidFinishLaunching:] + 123
    11 UIKit 0x0034459c -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1252
    12 UIKit 0x003469a1 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 346
    13 UIKit 0×00350452 -[UIApplication handleEvent:withNewEvent:] + 1958
    14 UIKit 0×00349074 -[UIApplication sendEvent:] + 71
    15 UIKit 0x0034dac4 _UIApplicationHandleEvent + 7495
    16 GraphicsServices 0x02dd7afa PurpleEventCallback + 1578
    17 CoreFoundation 0x026ccdc4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 52
    18 CoreFoundation 0x0262d737 __CFRunLoopDoSource1 + 215
    19 CoreFoundation 0x0262a9c3 __CFRunLoopRun + 979
    20 CoreFoundation 0x0262a280 CFRunLoopRunSpecific + 208
    21 CoreFoundation 0x0262a1a1 CFRunLoopRunInMode + 97
    22 UIKit 0×00346226 -[UIApplication _run] + 625
    23 UIKit 0x00351b58 UIApplicationMain + 1160
    24 RouteMeSampleMapDBOffline 0x000025f0 main + 102
    25 RouteMeSampleMapDBOffline 0×00002581 start + 53
    )
    terminate called after throwing an instance of ‘NSException’
    Program received signal: “SIGABRT”.

  6. September 28th, 2010 at 18:39 | #6

    I solved the Problem by using the latest Revision from the SVN Repository instead of using the 0.5 Release from Google Code Page.

  7. September 28th, 2010 at 20:59 | #7

    @Bourzanis:

    Don’t use the patch found in the google group files section (route-me-trunk-db-tilesource-rev605-2009-12-23.patch
    ) because it refers to rev605 which obviously is not trunk.

    I recommend you download the trunk, then follow the code above on how to edit RMTileImage.h and RMTIleImage.m.

  8. September 28th, 2010 at 21:09 | #8

    @greg:
    – have map view stop scrolling a specific lat/lng boundaries

    [rmcontents setMaxZoom:17.0f];
    [rmcontents setMinZoom:6.0f];

    If we can set the maxZoom and minZoom, then I assume this is possible from RMContents. Hang on..

    Quick search from RouteMe Google Group yields:

    I suggest you study Anna’s code attached on this post: http://groups.google.com/group/route-me-map/browse_thread/thread/4f07179530ae3f94#

    Let me know if it works for you…

  9. September 28th, 2010 at 21:18 | #9

    @marcus

    Glad you figured it out :)

    For others, the way this tutorial works is by using route-me-trunk and the edited files. (im not using the word “patch” anymore as not to confuse it with the patch files found in the google group file section).

    Thus, by using trunk, we get the latest route-me code + offfline support..

  10. tobinfisher
    November 20th, 2010 at 07:19 | #10

    It would appear that RMDBTileImage leaks a NSMutableData object on map move and scroll. It looks like the NSMutableData object used for creating the image is retained when setting the layer.contents property in UpdateImageUsingImage, although it is not clear how to reduce the retain count on the NSMutableData object at this point.

    I’ve checked to make sure that all of the objects being allocated are getting released and can’t find any obvious issues – looks like a very pesky bug!

    Here’s the stack trace on the object that’s getting leaked:
    9 -[RMMapContents initWithView:tilesource:centerLatLon:zoomLevel:maxZoomLevel:minZoomLevel:backgroundImage:] MapView/Map/RMTileLoader.m:211
    8 -[RMTileImageSet addTiles:ToDisplayIn:] MapView/Map/RMTileImageSet.m:365
    7 -[RMTileImageSet addTile:At:] MapView/Map/RMTileImageSet.m:263
    6 -[RMCachedTileSource tileImage:] /MapView/Map/RMCachedTileSource.m:75
    5 -[RMDBMapSource tileImage:] /MapView/Map/RMDBMapSource.m:222
    4 +[RMTileImage imageForTile:fromDB:] Map/RMTileImage.m:168
    3 -[RMDBTileImage initWithTile:fromDB:] Map/RMDBTileImage.m:106
    2 -[FMResultSet dataForColumnIndex:] /Users/tobin1/Documents/Sutro Project/iPhone Guides/mobilelocal-offlinemaps/Classes/../Classes/FMResultSet.m:244
    1 Foundation +[NSMutableData(NSMutableData) dataWithLength:]
    0 Foundation -[NSConcreteMutableData initWithLength:]

  11. tobinfisher
    November 20th, 2010 at 07:20 | #11


    tobinfisher:

    It would appear that RMDBTileImage leaks a NSMutableData object on map move and scroll.

    “Move and zoom” that is… (basically any action that initiates loading a new tile)

  12. dcrawkstar
    November 24th, 2010 at 05:04 | #12

    the new route-me at Git-hub is patched with the update.

  13. November 24th, 2010 at 09:19 | #13

    Finally, someone took the initiative of forking this to github https://github.com/route-me/route-me

  14. sandeep_iphone
    January 31st, 2011 at 21:34 | #14

    Hello rupert,

    i am new in offline db mode of maps. please tell me when i go to downloadosmtiles.pl link, to download open street map tiles there are 4 format available to export. i m confused which format i will download for my demo project ?

    Thanks in advance.

  15. cellininicholas
    February 1st, 2011 at 13:29 | #15

    Hey rupert,

    I would really appreciate some help with my app.

    I followed your tutorial and the app I built runs fine in the simulator running iOS 4.2.

    But when I built it for a device, an actual iphone. The mapView doesn’t load at all.

    It seems to me that it is a problem with the loading of my .db file…?

    In the simulator I get:
    2011-02-01 13:26:29.368 app[33801:207] Opening db map source KensingtonMap.db
    2011-02-01 13:26:29.371 app[33801:207] Tile size: 256 pixel
    2011-02-01 13:26:29.372 app[33801:207] Supported zoom range: 0 – 1076756480
    2011-02-01 13:26:29.372 app[33801:207] Coverage area: (-33.912594,151.222687) x (-33.922852,-33.922852)
    2011-02-01 13:26:29.373 app[33801:207] Center: (-33.917725,151.230927)
    2011-02-01 13:26:29.374 app[33801:207] logged method call: -[ initWithView:tilesource:] (line 97)
    2011-02-01 13:26:29.375 app[33801:207] logged method call: -[ initWithView:tilesource:centerLatLon:zoomLevel:maxZoomLevel:minZoomLevel:backgroundImage:] (line 119)
    2011-02-01 13:26:29.376 app[33801:207] initializing memory cache with capacity 32

    But when running on my iPhone, I get:
    2011-02-01 13:28:06.715 app[4204:307] Opening db map source KensingtonMap.db
    2011-02-01 13:28:06.721 app[4204:307] Tile size: -2147483648 pixel
    2011-02-01 13:28:06.727 app[4204:307] Supported zoom range: 0 – -1042284544
    2011-02-01 13:28:06.733 app[4204:307] Coverage area: (-2147483648.000000,-2147483648.000000) x (-2147483648.000000,-2147483648.000000)
    2011-02-01 13:28:06.739 app[4204:307] Center: (-2147483648.000000,-2147483648.000000)
    2011-02-01 13:28:06.758 app[4204:307] logged method call: -[ initWithView:tilesource:] (line 97)
    2011-02-01 13:28:06.764 app[4204:307] logged method call: -[ initWithView:tilesource:centerLatLon:zoomLevel:maxZoomLevel:minZoomLevel:backgroundImage:] (line 119)
    2011-02-01 13:28:06.774 app[4204:307] initializing memory cache with capacity 32

    It seems like its not allocating the .db file correctly, I can’t fix it for the life of me. Any help would be GREATLY appreciated.

  16. February 7th, 2011 at 14:22 | #16

    @sandeep_iphone: The downloadosmtiles.pl is a perl script which you run from the terminal. Obviously, you need Perl to run the script. Once it runs, it creates the directory structure and download the tiles automatically ( i think in png–don’t hold me onto this). There is no option export format that I am aware of in the perl script. It only needs the extent/bounds and zoom.

    If you are referring to the “Format to Export” option found in Openstreetmap.org (edit tab), don’t use those. I only used that page to know/control the map bounds that I want to export. I explicitly specify the map bounds to the perl script (downloadosmtiles.pl)

  17. February 7th, 2011 at 14:41 | #17

    @cellininicholas:
    >It seems to me that it is a problem with the loading of my .db file…?

    Just to be sure.. start fresh..

    1. From the device, remove the app, thus removing its documents, db, etc.

    2. Build and Debug on device. It should copy the db (i.e KensingtonMap.db) to the ../Documents/..

    (See createCopyOfDatabaseIfNeeded in AppDelegate.m–I am recalling from top of my head, may not be exact method name.)

    Note: Do this if you have changes in the tile db, I don’t think it gets overwritten everytime you “build and debug”. Need to check. Not sure.

    3. If you get a gray background, then MapView loaded, its just that the center is somewhere..

    This may be hard to troubleshoot, I need to see code and map.db. I know this coordinate (-33.912594,151.222687) is somewhere in AU, so drop me an email at rupert@2rmobile.com so I can reply offlist for contact etc.

    cheers,
    rupert

  18. Hepedo
    February 8th, 2011 at 22:35 | #18

    Perfectly… you did a great job!!!

    Could you help me with the following error:

    Undefined symbols:
    “_OBJC_CLASS_$_RMDBMapSource”, referenced from:
    objc-class-ref-to-RMDBMapSource in RouteMeSampleMapDBOfflineViewController.o
    ld: symbol(s) not found
    collect2: ld returned 1 exit status

    All Settings in Header Search Paths are set correctly…

    I don’t know, how to get rid of this last 1 error…

    Thanks…

  19. sandeep_iphone
    February 14th, 2011 at 22:57 | #19

    Hello Rupert,

    I have tried to run perl, but as you know i m not good in perl so nothing is done. for you i did this in my mac terminal window.

    Last login: Mon Feb 14 15:59:58 on ttys000
    Sandeep-Singhs-Mac-mini:~ Sandeep$ perl
    /user/bin/perl
    Bareword found where operator expected at – line 1, near “/user/bin”
    (Missing operator before bin?)
    cd ~/desktop
    perl downloadosmtiles.pl –lat=6.9443:7.2261 –lon=125.5082:125.7104 –zoom=6:12

    perl Makefile.PL
    make
    make test
    make install

    But nothing happen on my desktop. Please help me because i don’t know a single word in Perl.

    Thanks in Advance….

  20. maksumon
    June 16th, 2011 at 23:26 | #20

    while using downloadosmtiles.pl, it gives me the following error –

    bash: /usr/bin/downloadosmtiles.pl: Permission denied

    Could anyone please help me to solve the issue?

  21. fabdelamora
    October 8th, 2011 at 05:18 | #21

    I need help to load my own .db file, I can’t figure out how to do that and how to center the map. Any help?

  22. ektas
    November 12th, 2011 at 00:12 | #22

    Hi Rupert

    I am also getting this error

    Undefined symbols:
    “_OBJC_CLASS_$_RMDBMapSource”, referenced from:
    objc-class-ref-to-RMDBMapSource in RouteMeSampleMapDBOfflineViewController.o
    ld: symbol(s) not found
    collect2: ld returned 1 exit status

    please help me to resolve it

Comments are closed.