14

I am in search of a way to develop an app for android with embedded maps which can work without an internet connection(i.e. can i get the tiles of the open streetmap for offline use?).

I have developed web apps before by using the google maps api v3. I need a general answer as a specific one.I'm trying to find the concepts and technologies i need to learn :)

(Meanwhile i found osmdroid.Does it work offline?)

4
  • This does not really answer the question. If you have a different question, you can ask it by clicking Ask Question. You can also add a bounty to draw more attention to this question once you have enough reputation. Nov 24, 2013 at 19:22
  • Osmand doesn't allow to import any customized maps at user wish but it only allows to download maps straight from app for offline use. As in my case, I need to overlay toposheets (Raster maps) which were converted into *.kmz. My requirement is for offline usage. I am looking for a proper guidance to develop such offline map support android application.
    – surap
    Feb 4, 2016 at 13:22
  • This doesn't really answer the original question of where and how to get maps for offline use.
    – MaryBeth
    Feb 4, 2016 at 14:01
  • This does not really answer the question. If you have a different question, you can ask it by clicking Ask Question. You can also add a bounty to draw more attention to this question once you have enough reputation. - From Review Feb 4, 2016 at 14:05

7 Answers 7

13

I did an offline map using the ESRI Android SDK and a custom tile layer. I used an ArcGIS server to generate a tile based map cache, then inserted those tiles into a SQLite database and query them based on row and column from there. It works great and if you need custom maps end to end, it is a very usable method.

package com.main.utilinspect;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.List;

import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonParser;

import android.content.Context;
import android.util.Log;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.esri.core.internal.c.d;
import com.esri.core.internal.c.h;
import com.esri.core.internal.c.l;

import com.esri.android.map.TiledServiceLayer;

public class OfflineDbTiledLayer extends TiledServiceLayer {

File workingDirectory;
String mapDefinition;
String databaseName;
private SQLiteDatabase database;
File blankImage;

byte[] blankImageBytes;

private final Object lock = new Object();


private static final String TAG = "OfflineTiledLayer";  


public OfflineDbTiledLayer(Context paramContext, File workingDirectory, String mapDefinition, String databaseName)  {
    super("required");
    this.workingDirectory = workingDirectory;
    this.mapDefinition = mapDefinition;
    this.databaseName = databaseName;

    String databasePath = workingDirectory.getAbsolutePath() + File.separator + databaseName;

    this.database = SQLiteDatabase.openDatabase(databasePath, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS); 

    this.blankImage = new File(workingDirectory.getAbsolutePath() + File.separator + "blank.png");

    RandomAccessFile raFile = null;

    try {
        raFile = new RandomAccessFile(this.blankImage, "r");

        blankImageBytes = new byte[(int) raFile.length()];
        raFile.readFully(blankImageBytes);

    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }       
    finally {
        if(raFile != null) {
            try {
                raFile.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }   

    h h1 = null;

    try
    {

    JsonParser paramJsonParser = new JsonFactory()
    .createJsonParser(new File(workingDirectory.getAbsolutePath() + File.separator + mapDefinition));
    paramJsonParser.nextToken();    

    h1 = h.a(paramJsonParser, "test");
    }
    catch(Exception ex){

    }       


    setFullExtent(h1.f());      
    setDefaultSpatialReference(h1.c());
    setInitialExtent(h1.e());

    l l1;
    List list;
    int i;
    double ad[] = new double[i = (list = (l1 = h1.d()).h).size()];
    double ad1[] = new double[i];
    for(int j = 0; j < list.size(); j++)
    {
        ad[j] = ((d)list.get(j)).b();
        ad1[j] = ((d)list.get(j)).a();
    }

    setTileInfo(new com.esri.android.map.TiledServiceLayer.TileInfo(l1.f, ad, ad1, i, l1.c, l1.b, l1.a));

    super.initLayer();
    return;     
}   

private void openDatabase(){
    if(!database.isOpen()){
        String databasePath = workingDirectory.getAbsolutePath() + File.separator + databaseName;
        this.database = SQLiteDatabase.openDatabase(databasePath, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS); 
    }
}

private void closeDatabase(){
    if(database.isOpen()){
        this.database.close();
    }
}

@Override
protected byte[] getTile(int level, int column, int row) throws Exception {
    byte[] tileImage;       

    Log.i(TAG, "getTile");

    Log.i(TAG, "getTile - retrieving tile");            

    synchronized(lock) {

        Log.i(TAG, "getTile - entered synchronized block");

        openDatabase();     

        // First check to see if the tile exists in the database
        Cursor tileCursor = database.rawQuery("SELECT image FROM tiles WHERE level = " + Integer.toString(level) + " AND row = " + Integer.toString(row) + " AND column = " + Integer.toString(column), null);

        if(tileCursor != null && tileCursor.getCount() > 0) {
            tileCursor.moveToFirst();
            tileImage = tileCursor.getBlob(0);
            Log.i(TAG, "getTile - tile found, returning image");                        
        }
        else {
            // The tile does not exist in the database, read the blank placeholder tile and serve it
            tileImage = blankImageBytes;
            Log.i(TAG, "getTile - tile not found returning blank");
        }   

        tileCursor.close();     
        this.database.close();
    }

    Log.i(TAG, "getTile - exited synchronized block");

    return tileImage;   
}

}

2
  • Can you please share some code?
    – gregm
    Mar 1, 2013 at 19:02
  • I added my custom layer class that does the work.
    – eptiliom
    Mar 5, 2013 at 22:06
8

Are you looking for Development for Mobile/Cell App? as the new Google Tablet comes with Offline Google Maps in vector format rather than raster tiles.

Google Honeycomb (Tablet) has full support of Google Maps Offline (Vector)

http://androidandme.com/2010/12/news/andy-rubin-demos-honeycomb-running-on-a-dual-core-motorola-tablet/

Video at the bottom explains and shows the application.

7

I have developed an application with offline maps for Android. The only free tool you would want to use is OSMDroid, otherwise ESRI. OSMDroid API is identical to Google's, so in case you want to switch from one to another, it's a breeze.

Check out this post, with a sample of how to integrate Google Maps into OSMDroid (it also gives you an idea how to use it in general).

2

There is a great app called xGPS for iPhone and other devices. You might want to look at that to get ideas because it allows users to pre-cache the maps they will need. I use it when i'm deep in the bush in Africa, Florida Everglades, boating, or anywhere i have no cell phone coverage. I will cache the maps before my trip and then can use the GPS on my iphone normally with NO INTERNET.

Basically what they xGPS does and what you would want to do is cache the map tiles locally on your phone. Space on these mobile phones are limited. Yes even if you have 32gb on your phone, it might just be enough to cache a small country on all zoom levels (scales). None of these phones (unless somehow attached to external storage) are going to have enough space to cache the world at all zoom levels. I imagine this would require petabytes of storage.

So you need to be able to ask your user to cache the "regions" or maps when he's online so he can use them offline. With xGPS i'm able to draw a polygon over my region of interest and specify what zoom levels i want to cache. Then it will start downloading it all for me on to my phone, either from the phone itself or a program on my computer. Then there is a button on the app that allows me to switch to "offline" mode when i loose internet connectivity.

2

Try to use NextGIS Mobile SDK. Online/offline mode include editing and autosync with server (NextGIS Web). Raster and vector layers, custom input forms, all types of geometry support.

An example application NextGIS Mobile can be found in Google Play store - https://play.google.com/store/apps/details?id=com.nextgis.mobile

Sources are here: https://github.com/nextgis/android_gisapp

Documentation: http://docs.nextgis.com/docs_ngmobile/source/toc.html

Another simple application are here: https://github.com/nextgis/android_muni_gisconf

SDK documentation: http://docs.nextgis.com/ngmobile_dev/toc.html

1

There is full support for offline maps with the current release of ArcGIS Android SDK. This sample will show you how to work with a local Tile Package as a basemap and persist online features to offline features from which there are ways you can edit. I have a gist available on github as well for a simplified reference.

1

I suggest to take a look to Nutiteq 3D maps SDK which has offline capabilities and interesting extras like 3D support. It is made by specialized developer company behind it, so it has dedicated commercial support and more flexibility than big vendors provide. Disclaimer: I'm developer of it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.