OSMDroid Loading Custom Offline tiles From Assets Folder

隐身守侯 提交于 2019-12-09 16:15:43

问题


I was wondering whether it was possible to do such a thing. I know that one would need to modify some of the existing code to pull this off but I was wondering if anyone had any direction on where to look and how to do this.

I am placing a few custom tiles on a specific area on the map as a replacement for OSM tiles providers but need them to be stored in the /assets/ folder. Any ideas?


回答1:


I use the nexts classes to do that.

import java.io.InputStream;

import org.osmdroid.ResourceProxy.string;
import org.osmdroid.tileprovider.util.StreamUtils;

import android.content.res.AssetManager;
import android.graphics.drawable.Drawable;

public class AssetsTileSource extends CustomBitmapTileSourceBase {
        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, aResourceId, 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;
        }
}

MapTileFileAssetsProvider.java

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() : MAXIMUM_ZOOMLEVEL;
            }

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

            @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;
                    }
            }
    }

And

import java.io.File;
import java.io.InputStream;
import java.util.Random;

import org.osmdroid.ResourceProxy;
import org.osmdroid.ResourceProxy.string;
import org.osmdroid.tileprovider.ExpirableBitmapDrawable;
import org.osmdroid.tileprovider.MapTile;
import org.osmdroid.tileprovider.constants.OpenStreetMapTileProviderConstants;
import org.osmdroid.tileprovider.tilesource.ITileSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;

public abstract class CustomBitmapTileSourceBase implements ITileSource,
                OpenStreetMapTileProviderConstants {

        private static final Logger logger = LoggerFactory.getLogger(CustomBitmapTileSourceBase.class);

        private static int globalOrdinal = 0;

        private final int mMinimumZoomLevel;
        private final int mMaximumZoomLevel;

        private final int mOrdinal;
        protected final String mName;
        protected final String mImageFilenameEnding;
        protected final Random random = new Random();

        private final int mTileSizePixels;

        private final string mResourceId;

        public CustomBitmapTileSourceBase(final String aName, final string aResourceId,
                        final int aZoomMinLevel, final int aZoomMaxLevel, final int aTileSizePixels,
                        final String aImageFilenameEnding) {
                mResourceId = aResourceId;
                mOrdinal = globalOrdinal++;
                mName = aName;
                mMinimumZoomLevel = aZoomMinLevel;
                mMaximumZoomLevel = aZoomMaxLevel;
                mTileSizePixels = aTileSizePixels;
                mImageFilenameEnding = aImageFilenameEnding;
        }

        @Override
        public int ordinal() {
                return mOrdinal;
        }

        @Override
        public String name() {
                return mName;
        }

        public String pathBase() {
                return mName;
        }

        public String imageFilenameEnding() {
                return mImageFilenameEnding;
        }

        @Override
        public int getMinimumZoomLevel() {
                return mMinimumZoomLevel;
        }

        @Override
        public int getMaximumZoomLevel() {
                return mMaximumZoomLevel;
        }

        @Override
        public int getTileSizePixels() {
                return mTileSizePixels;
        }

        @Override
        public String localizedName(final ResourceProxy proxy) {
                return proxy.getString(mResourceId);
        }

        @Override
        public Drawable getDrawable(final String aFilePath) {
                try {
                        // default implementation will load the file as a bitmap and create
                        // a BitmapDrawable from it
                        final Bitmap bitmap = BitmapFactory.decodeFile(aFilePath);
                        if (bitmap != null) {
                                return new ExpirableBitmapDrawable(bitmap);
                        } else {
                                // if we couldn't load it then it's invalid - delete it
                                try {
                                        new File(aFilePath).delete();
                                } catch (final Throwable e) {
                                        logger.error("Error deleting invalid file: " + aFilePath, e);
                                }
                        }
                } catch (final OutOfMemoryError e) {
                        logger.error("OutOfMemoryError loading bitmap: " + aFilePath);
                        System.gc();
                }
                return null;
        }

        @Override
        public String getTileRelativeFilenameString(final MapTile tile) {
                final StringBuilder sb = new StringBuilder();
                sb.append(pathBase());
                sb.append('/');
                sb.append(tile.getX());
                sb.append('_');
                sb.append(tile.getY());
                sb.append('_');
                sb.append(tile.getZoomLevel());
                sb.append(imageFilenameEnding());
                return sb.toString();
        }


        @Override
        public Drawable getDrawable(final InputStream aFileInputStream) {
                try {
                        // default implementation will load the file as a bitmap and create
                        // a BitmapDrawable from it
                        final Bitmap bitmap = BitmapFactory.decodeStream(aFileInputStream);
                        if (bitmap != null) {
                                return new ExpirableBitmapDrawable(bitmap);
                        }
                        System.gc();
                } catch (final OutOfMemoryError e) {
                        logger.error("OutOfMemoryError loading bitmap");
                        System.gc();
                        //throw new LowMemoryException(e);
                }
                return null;
        }

        public final class LowMemoryException extends Exception {
                private static final long serialVersionUID = 146526524087765134L;

                public LowMemoryException(final String pDetailMessage) {
                        super(pDetailMessage);
                }

                public LowMemoryException(final Throwable pThrowable) {
                        super(pThrowable);
                }
        }
}

Modify method getTileRelativeFilenameString() to get yout tiles (i use the next format: x_y_zoom.png)

Example:

mapView = new MapView(getApplicationContext(), 256);
mapView.setClickable(true);
mapView.setTag("Mapa");
mapView.setTileSource(TileSourceFactory.MAPNIK);
mapView.setMultiTouchControls(true);
mapView.setUseDataConnection(true);

MapTileModuleProviderBase moduleProvider = 
    new MapTileFileAssetsProvider(ASSETS_TILE_SOURCE);
SimpleRegisterReceiver simpleReceiver = 
    new SimpleRegisterReceiver(getApplicationContext());
MapTileProviderArray tileProviderArray = 
    new MapTileProviderArray(ASSETS_TILE_SOURCE, simpleReceiver, 
        new MapTileModuleProviderBase[] { moduleProvider });
TilesOverlay tilesOverlay = 
    new TilesOverlay(tileProviderArray, getApplicationContext());

mapView.getOverlays().add(tilesOverlay);



回答2:


Instead to read directly from assets I copy/deploy the maptiles zipped (following osmdroid map tiles directory structure format) into osmdroid maptiles directory and then declare 3 tile providers, archive, cache and online provider.

public class MapTileProviderAssets extends MapTileProviderArray 
        implements IMapTileProviderCallback {

    private static final String LOG_TAG = "MapTileProviderAssets";

    private static final String ASSETS_MAP_DIRECTORY = "map";
    private static final String SDCARD_PATH = Environment.getExternalStorageDirectory().getPath();
    private static final String OSMDROID_MAP_FILE_SOURCE_DIRECTORY = "osmdroid";
    private static final String OSMDROID_MAP_FILE_SOURCE_DIRECTORY_PATH = 
            SDCARD_PATH + "/" + OSMDROID_MAP_FILE_SOURCE_DIRECTORY;

    public MapTileProviderAssets(final Context pContext) {
        this(pContext, TileSourceFactory.DEFAULT_TILE_SOURCE);
    }

    public MapTileProviderAssets(final Context pContext, final ITileSource pTileSource) {
        this(pContext, new SimpleRegisterReceiver(pContext), 
                new NetworkAvailabliltyCheck(pContext), pTileSource);

    }

    public MapTileProviderAssets(final Context pContext, final IRegisterReceiver pRegisterReceiver,
                                 final INetworkAvailablityCheck aNetworkAvailablityCheck, 
                                 final ITileSource pTileSource) {
        super(pTileSource, pRegisterReceiver);

        final TileWriter tileWriter = new TileWriter();

        // copy assets delivered in apk into osmdroid map source dir
        // load zip archive first, then cache, then online
        final List<String> zipArchivesRelativePathInAssets = 
                listArchives(pContext.getAssets(), ASSETS_MAP_DIRECTORY);
        for (final String zipFileRelativePathInAssets : zipArchivesRelativePathInAssets) {
            final String copiedFilePath = copyAssetFile(
                    pContext.getAssets(), zipFileRelativePathInAssets, 
                    OSMDROID_MAP_FILE_SOURCE_DIRECTORY);
            Log.d(LOG_TAG, String.format(
                    "Archive zip file copied into map source directory %s", copiedFilePath));
        }
        // list zip files in map archive directory
        final Set<String> setZipFileArchivesPath = new HashSet<String>();
        FileTools.listFiles(setZipFileArchivesPath, new File(
                OSMDROID_MAP_FILE_SOURCE_DIRECTORY_PATH), ".zip", true);
        final Set<IArchiveFile> setZipFileArchives = new HashSet<IArchiveFile>();
        for (final String zipFileArchivesPath : setZipFileArchivesPath) {
            final File zipfile = new File(zipFileArchivesPath);
            final IArchiveFile archiveFile = ArchiveFileFactory.getArchiveFile(zipfile);
            if (archiveFile != null) {
                setZipFileArchives.add(archiveFile);
            }
            setZipFileArchives.add(archiveFile);
            Log.d(LOG_TAG, String.format(
                    "Archive zip file %s added to map source ", zipFileArchivesPath));
        }

        final MapTileFileArchiveProvider archiveProvider;
        Log.d(LOG_TAG, String.format(
                "%s archive zip files will be used as source", setZipFileArchives.size()));
        if (setZipFileArchives.size() > 0) {
            final IArchiveFile[] pArchives = 
                    setZipFileArchives.toArray(new IArchiveFile[setZipFileArchives.size()]);
            archiveProvider = new MapTileFileArchiveProvider(
                    pRegisterReceiver, pTileSource, pArchives);
        } else {
            archiveProvider = new MapTileFileArchiveProvider(
                    pRegisterReceiver, pTileSource);
        }
        mTileProviderList.add(archiveProvider);

        // cache
        final MapTileFilesystemProvider fileSystemProvider = 
                new MapTileFilesystemProvider(pRegisterReceiver, pTileSource);
        mTileProviderList.add(fileSystemProvider);

        // online tiles
        final MapTileDownloader downloaderProvider = 
                new MapTileDownloader(pTileSource, tileWriter, aNetworkAvailablityCheck);
        mTileProviderList.add(downloaderProvider);
    }

    public static List<String> listArchives(final AssetManager assetManager, 
                                            final String subDirectory) {
        final List<String> listArchives = new ArrayList<String>();
        try {
            final String[] lstFiles = assetManager.list(subDirectory);
            if (lstFiles != null && lstFiles.length > 0) {
                for (final String file : lstFiles) {
                    if (isZip(file)) {
                        listArchives.add(subDirectory + "/" + file);
                    }
                    // filter files (xxxxx.xxx format) and parse only directories, 
                    // with out this all files are parsed and
                    // the process is VERY slow
                    // WARNNING: we could have directories with dot for versioning
                    else if (isDirectory(file)) {// (file.lastIndexOf(".") != (file.length() - 4)) {
                        listArchives(assetManager, subDirectory + "/" + file);
                    }
                }
            }
        } catch (final IOException e) {
            Log.w(LOG_TAG, String.format("List error: can't list %s, exception %s", 
                    subDirectory, Log.getStackTraceString(e)));
        } catch (final Exception e) {
            Log.w(LOG_TAG, String.format("List error: can't list %s, exception %s", 
                    subDirectory, Log.getStackTraceString(e)));
        }
        return listArchives;
    }

    private static boolean isZip(final String file) {
        return file.endsWith(".zip");
    }

    private static boolean isDirectory(final String file) {
        return file.lastIndexOf(".") != (file.length() - 4);
    }

    private static String copyAssetFile(final AssetManager assetManager, 
                                        final String assetRelativePath,
                                        final String destinationDirectoryOnSdcard) {
        InputStream in = null;
        OutputStream out = null;
        final String newfilePath = SDCARD_PATH + "/" + 
                destinationDirectoryOnSdcard + "/" + assetRelativePath;
        final File newFile = new File(newfilePath);
        // copy file only if it doesn't exist yet
        if (!newFile.exists()) {
            Log.d(LOG_TAG, String.format(
                    "Copy %s map archive in assets into %s", assetRelativePath, newfilePath));
            try {
                final File directory = newFile.getParentFile();
                if (!directory.exists()) {
                    if (directory.mkdirs()) {
                        // Log.d(LOG_TAG, "Directory created: " + directory.getAbsolutePath());
                    }
                }
                in = assetManager.open(assetRelativePath);
                out = new FileOutputStream(newfilePath);
                copyFile(in, out);
                in.close();
                in = null;
                out.flush();
                out.close();
                out = null;
            } catch (final Exception e) {
                Log.e(LOG_TAG, "Exception during copyAssetFile: " + Log.getStackTraceString(e));
            }
        }
        return newfilePath;
    }

    private static void copyFile(final InputStream in, final OutputStream out) throws IOException {
        final byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
    }

}


来源:https://stackoverflow.com/questions/12693684/osmdroid-loading-custom-offline-tiles-from-assets-folder

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!