Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use MOBAC created OSMDroid SQLite tile source file offline?

I've been pulling my hair out trying to get my own offline Mobile Atlas Creator OSMDroid SQLite map working with OSMDroid 3.0.8 without luck. It's been a long 3 days. I'll try to explain with clips from my application. I've been extending ItemizedIconOverlay and OverlayItem so I hope it doesn't get too confusing.

I created my own OSMDroid SQLite map file with 3 different zoom levels for a small are, like 10 square kms. I copied the resulting "base.sqlite" file into my project /res/raw/ directory. Note that the GeoPoints in my application should be well within the map's tile range. The "base.sqlite" file should get saved to the application specific data directory.

Next I turfed the /osmdroid directory on my phone so I could get the previously cached maps off. I thought I had my own offline maps working until I turned on Airplane mode and noticed the cached maps were still available.

Now all I get is blanks. I have no clue how to get this going. I've see a couple of examples but after a ton of experimentation I haven't been successful in getting any of them working.

private Hashtable<String, NodeOverlayItem> nodes = new Hashtable<String, NodeOverlayItem>();

private MapView mapView;
private Context context;

private LocationManager locManager;

private MapController mapController;
private MapTileProviderArray mapProvider;
private String mapTileArchivePath = "base.sqlite";

private ResourceProxy resourceProxy;

@Override
public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    try {
        this.mapView = new MapView(this, 256);
        this.mapView.setBuiltInZoomControls(true);
        this.mapView.setMultiTouchControls(true);

        this.context = this.getApplicationContext();
        this.resourceProxy = new DefaultResourceProxyImpl(context);

        XYTileSource TILERENDERER = new XYTileSource("test", 
            ResourceProxy.string.offline_mode, 
            1, 20, 256, ".png", "http://127.0.0.1");
        SimpleRegisterReceiver simpleReceiver = new SimpleRegisterReceiver(this.context);

        IArchiveFile[] archives = { ArchiveFileFactory.getArchiveFile(this.getMapsFile()) };
        MapTileModuleProviderBase moduleProvider = new MapTileFileArchiveProvider(
                simpleReceiver, 
                TILERENDERER, 
                archives);
        this.mapProvider = new MapTileProviderArray(TILERENDERER, null, new MapTileModuleProviderBase[] { moduleProvider });
        this.mapProvider.setUseDataConnection(false);
        this.mapView = new MapView(this, 256, this.resourceProxy, this.mapProvider);
        this.mapView.setUseDataConnection(false);

        mapController = mapView.getController();
        mapController.setZoom(18);
        mapController.setCenter(new GeoPoint((int)(45.349622 * 1E6), (int)(-75.880700 *1E6)));

        this.setContentView(mapView);
    } catch(Exception ex) {
        Log.e("test", ex.getMessage());
    }    
}


public File getMapsFile() throws IOException {
    Log.d("test", "Trying to load map tiles to: " + this.mapTileArchivePath);
    FileOutputStream fos = this.openFileOutput(this.mapTileArchivePath, Context.MODE_PRIVATE);

    InputStream in = getResources().openRawResource(R.raw.osmdroid);
    byte[] buff = new byte[1024];
    int read = 0;
    try {
    while ((read = in.read(buff)) > 0) {
      fos.write(buff, 0, read);
    }
    } finally {
      in.close();
      fos.close();
    }
    return new File(this.getFilesDir(), this.mapTileArchivePath);
    }
like image 940
dubmojo Avatar asked Dec 11 '12 19:12

dubmojo


2 Answers

OK! I know what I doing wrong and I have it all working now! (I'm excited :)

Firstly, I had some trouble with writing my Raw resource map file to the application specific directory (e.g. openFileOutput()) I'm using a Galaxy Nexus which doesn't have an SD slot so I can't dump the map file to SD. Ensure the maps file you intend to use is byte compared with the original copy. Eclipse's DDMS perspective is useful to view a device's file structure.

I also switched to the OSMdroid Zip format. I then made sure the XYTileSource() name matched the directory created in the Zip file by MOBAC, plus ensure the tile size and zoom levels match.

XYTileSource TILERENDERER = new XYTileSource("OSM CloudMade 1", ResourceProxy.string.offline_mode, 16, 18, 256, ".png", "http://127.0.0.1");

MOBAC by default will create 256 pixel tiles. I created an atlas file with 16, 17, and 18 zoom levels. PNG is the default MOBAC tile image format.

Also, if your map file has any issues, ArchiveFileFactory.getArchiveFile() will catch them, even before MapTileFileArchiveProvider.

Here's my usage. Just make every effort to get your IArchive setup correctly and you should be ok:

        XYTileSource TILERENDERER = new XYTileSource("OSM CloudMade 1", ResourceProxy.string.offline_mode, 16, 18, 256, ".png", "http://127.0.0.1");
        SimpleRegisterReceiver simpleReceiver = new SimpleRegisterReceiver(this.context);
        IArchiveFile[] archives = { ArchiveFileFactory.getArchiveFile(this.getMapsSdCard()) };
        MapTileModuleProviderBase moduleProvider = new MapTileFileArchiveProvider(
                simpleReceiver, 
                TILERENDERER, 
                archives);
        this.mapProvider = new MapTileProviderArray(TILERENDERER, null, new MapTileModuleProviderBase[] { moduleProvider });
        this.mapProvider.setUseDataConnection(false);
        this.mapView = new MapView(this, 256, this.resourceProxy, this.mapProvider);
        this.mapView.setUseDataConnection(false);

Maybe I'm the only one who had trouble with this, but osmdroid doesn't clearly document how to do this, and when I opened the issue I couldn't get them to comment on my usage. If they had said I was implementing MapTileFileArchiveProvider correctly or included a good offline mapping sample, I would have focused on everything else first.

like image 96
dubmojo Avatar answered Nov 14 '22 04:11

dubmojo


If you want to use sqlite db you only have to change

ArchiveFileFactory.getArchiveFile(this.getMapsSdCard())

to

MBTilesFileArchive.getDatabaseFileArchive(f)

where f is a File that points to your sqlite database.

like image 41
Isidoro Avatar answered Nov 14 '22 05:11

Isidoro