Question

I am using OSMdroid to display both online and offline data in my app. The offline data are stored in a .zip file with the required structure.

Is it possible to have these offline tiles stored in a directory (extracted .zip file with the same structure)? Could somebody please tell me how could I achive this?

Thank you.

Was it helpful?

Solution 2

Well, as far as I understand what you are trying to get... this is more or less what the standard XYTileSource is already doing.

So if you simply use a ready-to-use tile source like this one:

map.setTileSource(TileSourceFactory.MAPNIK);

you will see downloaded tiles files stored in /sdcard/osmdroid/tiles/Mapnik/

The main difference is that it adds a ".tile" extension at the end of each tile file (probably to prevent tools like Android gallery to index all those images).

If you have a ZIP file with tiles ready to use, you could extract them in this directory, and add .tile extension to each tile (355.png => 355.png.tile)

And TileSourceFactory.MAPNIK will be able to use them.

OTHER TIPS

I am sorry. I should try more before asking. But I am leaving this question here, somebody could find it useful.

Solution:

New MapTileFileProvider. I called it MapTileFileFolderProvider, it is a lightly modified MapTileFileArchiveProvider. It is using folders instead of archives. The modifications are not perfect, it is a "hot solution" that needs someone more experienced in Java/Android to make it properly.

Benefits from loading Tiles from folders:

  • Faster loading of tiles (I know, I won't recognize the difference).
  • Easier updates focused only on changed tiles not whole map plans.
  • Application can download tiles when is in "online mode" and then use the downloaded Tiles offline.

MapTileFileFolderProvider - only modifications

public class MapTileFileArchiveProvider extends MapTileFileStorageProviderBase

public class MapTileFileFolderProvider extends MapTileFileStorageProviderBase {
  private final boolean mSpecificFoldersProvided;
  private final ArrayList<String> mFolders = new ArrayList<String>();
  private final AtomicReference<ITileSource> mTileSource = new AtomicReference<ITileSource>();

  ...
}

public MapTileFileArchiveProvider(...)

public MapTileFileFolderProvider(final IRegisterReceiver pRegisterReceiver,
                               final ITileSource pTileSource, 
                               final String[] pFolders) {
  super(pRegisterReceiver, NUMBER_OF_TILE_FILESYSTEM_THREADS,
                         TILE_FILESYSTEM_MAXIMUM_QUEUE_SIZE);
  setTileSource(pTileSource);
  if (pFolders == null) {
    mSpecificFoldersProvided = false;
    findFolders();
  } else {
    mSpecificFoldersProvided = true;
    for (int i = pFolders.length - 1; i >= 0; i--) {
      mFolders.add(pFolders[i]);
    }
  }
}

findArchiveFiles()

private void findFolders() {
  mFolders.clear();
  if (!getSdCardAvailable()) {
    return;
  }

  String baseDirPath = Environment.getExternalStorageDirectory().toString()+"/ctu_navigator"; // TODO get from Config
  File dir=new File(baseDirPath);
  final File[] files = dir.listFiles();
  if (files != null) {
  String fileName;
    for (File file : files) {
      if (file.isDirectory()) {
        fileName = baseDirPath + '/' + file.getName();
        mFolders.add(fileName);
        Utils.log(PlanTileProviderFactory.class, "Added map source: " + fileName);
      }
    }
  }
}

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

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

protected class TileLoader extends MapTileModuleProviderBase.TileLoader {

  @Override
  public Drawable loadTile(final MapTileRequestState pState) {
    ITileSource tileSource = mTileSource.get();
    if (tileSource == null) {
      return null;
    }

    final MapTile pTile = pState.getMapTile();

    // if there's no sdcard then don't do anything
    if (!getSdCardAvailable()) {
      Utils.log("No sdcard - do nothing for tile: " + pTile);
      return null;
    }

    InputStream inputStream = null;
    try {
      inputStream = getInputStream(pTile, tileSource);
      if (inputStream != null) {
        Utils.log("Use tile from folder: " + pTile);
        final Drawable drawable = tileSource.getDrawable(inputStream);
        return drawable;
      }
    } catch (final Throwable e) {
      Utils.log("Error loading tile");
      Utils.logError(getClass(), (Exception) e);
    } finally {
      if (inputStream != null) {
        StreamUtils.closeStream(inputStream);
      }
    }
    return null;
  }

  private synchronized InputStream getInputStream(final MapTile pTile, final ITileSource tileSource) {
    for (final String folder : mFolders) {
      final String path = folder + '/' + tileSource.getTileRelativeFilenameString(pTile);
      File mapTileFile = new File(path);
      InputStream in = null;
      try {
        in = new BufferedInputStream(new FileInputStream(mapTileFile));
      } catch (IOException e) {
        //Utils.log("Tile " + pTile + " not found in " + path);
      }

      if (in != null) {
        Utils.log("Found tile " + pTile + " in " + path);
        return in;
      }
    }
    Utils.log("Tile " + pTile + " not found.");
    return null;
  }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top