【问题标题】:OSMDroid Loading Custom Offline tiles From Assets FolderOSMDroid 从资产文件夹加载自定义离线图块
【发布时间】:2012-09-23 12:09:56
【问题描述】:

我想知道是否有可能做这样的事情。我知道需要修改一些现有代码才能实现这一点,但我想知道是否有人对在哪里查看以及如何执行此操作有任何指导。

我在地图上的特定区域放置了一些自定义图块,以替代 OSM 图块提供者,但需要将它们存储在 /assets/ 文件夹中。有什么想法吗?

【问题讨论】:

    标签: android offline osmdroid


    【解决方案1】:

    我使用 nexts 类来做到这一点。

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

    还有

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

    修改 getTileRelativeFilenameString() 方法以获取您的图块(我使用下一种格式:x_y_zoom.png)

    例子:

    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);
    

    【讨论】:

    • 如何在放大/缩小的同时应用坐标导航?
    • 这些类很好用,但例子还不够完整。对于 ASSETS_TILE_SOURCE,它不是常量,而是来自 AssetsTileSource 类的新对象。创建 tileProviderArray 后,我在使用可以将其作为参数的构造函数创建 mapView 时使用 in ,例如MapView mapView = new MapView(this, 256, resourceProxy, tileProviderArray)。让 MapView 与图块源相关联对我来说更直观,而不是与叠加层相关联......也许只是我。
    • @jzafrila 感谢样品。 Bitmap.decodeStream() 不会在主 UI 线程上导致 GC_FOR_ALLOC 吗?
    • @zIronManBox 对此不确定,我没有遇到任何问题,并且按预期工作。也许它可以改善这一点。
    【解决方案2】:

    而不是直接从资产中读取,我将压缩的地图块(遵循 osmdroid 地图块目录结构格式)复制/部署到 osmdroid 地图块目录中,然后声明 3 个块提供者、存档、缓存和在线提供者。

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

    【讨论】:

    • 您的代码不完整。有一种方法不可访问:它是 FileTools.listFiles。我已经评论它并添加了这一行来替换它: setZipFileArchivesPath.add("name_of_your_map_file.zip"); ....我仍然不确定是否必须包含字符串 .zip 作为文件名。我会自己检查。
    • 是的,FileTools.listFiles 的代码不存在,因为它很容易实现。该方法只是简单地列出输入的地图存档目录的所有 zip 文件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-22
    相关资源
    最近更新 更多