lamp ard
lamp ard

Reputation: 871

How to use assets as a source for osmdroid offline maps

I'm trying to use osmdroid for offline map usage in my app, but the thing which I'm not able to find is how to set the tile source from the /assets folder. So I've got the tiles (from Mobile Atlas Creator), placed in /assets/maps.zip and I'm trying something like this:

final ITileSource tileSource = 
   new XYTileSource("maps", null, 15, 17, 256, "png", "/assets");
mapview.setBuiltInZoomControls(true);
mapview.setTileSource(tileSource);
mapview.getController().setZoom(15);
mapview.setUseDataConnection(false); 

.. which seems to be wrong some way. So can anybody point me on how to do that?

Upvotes: 1

Views: 2613

Answers (3)

Leo DroidCoder
Leo DroidCoder

Reputation: 15046

OK, the question has been asked a couple of years ago,
but there is still no clear info on how to use offline maps from the assets folder.
So here is my solution.

  1. Create the maps with Mobile Atlas Creator.
    I used Atlas format = osmdroid zip and maps source = OpenStreetMap 4UMaps.eu
  2. Unzip the maps and put into your assets folder:
    assets/map/14, where 14 - is for instance a folder with the corresponding zoom level (you will probably have several folders)
  3. Add classes which I found somewhere on the internet:

    import android.content.res.AssetManager;
    import android.graphics.drawable.Drawable;    
    import org.osmdroid.ResourceProxy.string;
    import org.osmdroid.tileprovider.tilesource.BitmapTileSourceBase;
    import org.osmdroid.tileprovider.util.StreamUtils;
    
    import java.io.InputStream;
    
    /**
     * Custom tile source,
     * used to load map tiles from the assets
     */
    public class AssetsTileSource extends BitmapTileSourceBase {
        private final AssetManager mAssetManager;
    
        public AssetsTileSource(final AssetManager assetManager, final String aName, final string aResourceId,
                final int aZoomMinLevel, final int aZoomMaxLevel, final int aTileSizePixels,
                final String aImageFilenameEnding) {
            super(aName, aZoomMinLevel, aZoomMaxLevel, aTileSizePixels, aImageFilenameEnding);
            mAssetManager = assetManager;
        }
    
        @Override
        public Drawable getDrawable(final String aFilePath) {
            InputStream inputStream = null;
            try {
                inputStream = mAssetManager.open(aFilePath);
                if (inputStream != null) {
                    final Drawable drawable = getDrawable(inputStream);
                    return drawable;
                }
            } catch (final Throwable e) {
                // Tile does not exist in assets folder.
                // Ignore silently
            } finally {
                if (inputStream != null) {
                    StreamUtils.closeStream(inputStream);
                }
            }       
            return null;
        }
    }
    

and

/**
 * Map tile provider, loads tile from assets folder
 */
public class MapTileFileAssetsProvider extends MapTileModuleProviderBase {

    protected ITileSource mTileSource;

    public MapTileFileAssetsProvider(final ITileSource pTileSource) {
        super(OpenStreetMapTileProviderConstants.NUMBER_OF_TILE_FILESYSTEM_THREADS, OpenStreetMapTileProviderConstants.TILE_FILESYSTEM_MAXIMUM_QUEUE_SIZE);

        mTileSource = pTileSource;
    }

    @Override
    public boolean getUsesDataConnection() {
        return false;
    }

    @Override
    protected String getName() {
        return "Assets Folder Provider";
    }

    @Override
    protected String getThreadGroupName() {
        return "assetsfolder";
    }

    @Override
    protected Runnable getTileLoader() {
        return new TileLoader();
    }

    @Override
    public int getMinimumZoomLevel() {
        return mTileSource != null ? mTileSource.getMinimumZoomLevel() : Constants.MAP_ZOOM_ZOOM_MAX;
    }

    @Override
    public int getMaximumZoomLevel() {
        return mTileSource != null ? mTileSource.getMaximumZoomLevel() : Constants.MAP_ZOOM_ZOOM_MIN;
    }

    @Override
    public void setTileSource(final ITileSource pTileSource) {
        mTileSource = pTileSource;
    }

    private class TileLoader extends MapTileModuleProviderBase.TileLoader {

        @Override
        public Drawable loadTile(final MapTileRequestState pState) throws CantContinueException {

            if (mTileSource == null) {
                return null;
            }

            final MapTile pTile = pState.getMapTile();
            String path = mTileSource.getTileRelativeFilenameString(pTile);

            Drawable drawable;
            try {
                drawable = mTileSource.getDrawable(path);
            } catch (final LowMemoryException e) {
                // low memory so empty the queue
                throw new CantContinueException(e);
            }

            return drawable;
        }
    }
}
  1. Add the constants:
 // map min zoom level    
 public static final int MAP_ZOOM_ZOOM_MIN = 12;    
 // map max zoom level    
 public static final int MAP_ZOOM_ZOOM_MAX = 14;     
 // maps folder name in assets       
 public static final String MAP_ASSETS_FOLDER_NAME = "map";  

As you probably guessed, they represent minimum and maximum zoom level, which we previously created with the Mobile Atlas Creator.

  1. The last, but not the least,
    the snippet of implementation code:

        // making the map not use internet     
        mBinding.mapView.setUseDataConnection(false);    
    
        // Initializing the tile provider to use offline maps from the assets
        // This will load for instance from /map/14/12345/12345.png
        AssetsTileSource tileSource = new AssetsTileSource(
                getAssets(),
                Constants.MAP_ASSETS_FOLDER_NAME,
                ResourceProxy.string.offline_mode,
                Constants.MAP_ZOOM_ZOOM_MIN,
                Constants.MAP_ZOOM_ZOOM_MAX,
                256, ".png");
    
        MapTileModuleProviderBase moduleProvider = new
                MapTileFileAssetsProvider(tileSource);
        SimpleRegisterReceiver simpleReceiver = new
                SimpleRegisterReceiver(this);
        MapTileProviderArray tileProviderArray = new
                MapTileProviderArray(tileSource, simpleReceiver, new
                MapTileModuleProviderBase[] { moduleProvider });
    
        mBinding.mapView.setTileProvider(tileProviderArray);
    
        // not forget to invalidate the map on zoom
        mBinding.mapView.setMapListener(new MapListener() {
            @Override
            public boolean onScroll(ScrollEvent scrollEvent) {
                return false;
            }
    
            @Override
            public boolean onZoom(ZoomEvent zoomEvent) {
                mBinding.mapView.invalidate();
                return false;
            }
        });
    

Upvotes: 2

JJD
JJD

Reputation: 51804

Take a look at BitmapAssetTileSource. Also the iBurn-2012 might be useful to see it in action.

Upvotes: 0

Fabian Knapp
Fabian Knapp

Reputation: 1382

asset path is "file:///android_asset/" if you need a inputstream you get it via AssetManager.

Upvotes: 0

Related Questions