package com.arman.osmdroidmapsforge.map;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import org.mapsforge.core.model.Tile;
import org.mapsforge.map.android.graphics.AndroidBitmap;
import org.mapsforge.map.android.graphics.AndroidGraphicFactory;
import org.mapsforge.map.android.util.AndroidUtil;
import org.mapsforge.map.datastore.MapDataStore;
import org.mapsforge.map.layer.cache.TileCache;
import org.mapsforge.map.layer.renderer.DatabaseRenderer;
import org.mapsforge.map.layer.renderer.RendererJob;
import org.mapsforge.map.model.DisplayModel;
import org.mapsforge.map.model.MapViewPosition;
import org.mapsforge.map.reader.MapFile;
import org.mapsforge.map.rendertheme.ExternalRenderTheme;
import org.mapsforge.map.rendertheme.XmlRenderTheme;
import org.mapsforge.map.rendertheme.rule.RenderThemeFuture;
import org.osmdroid.ResourceProxy;
import org.osmdroid.tileprovider.BitmapPool;
import org.osmdroid.tileprovider.MapTile;
import org.osmdroid.tileprovider.ReusableBitmapDrawable;
import org.osmdroid.tileprovider.tilesource.ITileSource;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.util.Log;
public class MFTileSource implements ITileSource{
private final int mMinimumZoomLevel;
private final int mMaximumZoomLevel;
private final int mTileSizePixels;
static final String TAG = "MFTileSource";
private static String mapFilePath = null;
private static File mapFile = null;
private static AndroidGraphicFactory androidGraphicFactory;
private static DisplayModel displayModel ;
private static MapViewPosition mapViewPosition ;
private static DatabaseRenderer databaseRenderer;
private static XmlRenderTheme xmlRenderTheme;
private static RendererJob rendererJob ;
private static Context context;
private static RenderThemeFuture renderThemeFuture;
private static MapDataStore mapDataStore;
public MFTileSource(int minZoom, int maxZoom, int tileSizePixels, String mapsFolder, String renderThemeAddr, Context context) {
mTileSizePixels = tileSizePixels;
mMinimumZoomLevel = minZoom;
mMaximumZoomLevel = maxZoom;
this.context = context;
initialize(mapsFolder, renderThemeAddr, context);
}
public static boolean initialize(String locationOfMapFile,String renderThemeAddr, Context _context) {
Log.i(TAG, "Initilizing started");
mapFilePath = locationOfMapFile;
androidGraphicFactory = AndroidGraphicFactory.INSTANCE;
mapFile = new File(mapFilePath);
displayModel = new DisplayModel();
mapViewPosition = new MapViewPosition(displayModel);
mapDataStore = new MapFile(mapFile);
displayModel.setFixedTileSize(256);
TileCache tileCache = AndroidUtil.createTileCache(context, "111", 256, 1.0f, 1d);
// TileCache tileCache = null;
databaseRenderer = new DatabaseRenderer(mapDataStore, androidGraphicFactory, tileCache);
context = _context;
try {
xmlRenderTheme = new ExternalRenderTheme(new File(renderThemeAddr ));
Log.i(TAG, "xmlRenderTheme initialized successfully");
}
catch(Throwable e)
{
Log.i(TAG, "xmlRenderTheme error initializing ");
e.printStackTrace();
}
renderThemeFuture = new RenderThemeFuture(AndroidGraphicFactory.INSTANCE, xmlRenderTheme, displayModel);
new Thread(renderThemeFuture).run();
Log.i(TAG, "Mapsforge Initializing Ended");
return true;
}
@Override
public int getMinimumZoomLevel() {
return mMinimumZoomLevel;
}
@Override
public int getMaximumZoomLevel() {
return mMaximumZoomLevel;
}
@Override
public int getTileSizePixels() {
return mTileSizePixels;
}
@Override
public Drawable getDrawable(final String aFilePath) {
try {
// default implementation will load the file as a bitmap and create
// a BitmapDrawable from it
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
BitmapPool.getInstance().applyReusableOptions(bitmapOptions);
final Bitmap bitmap = BitmapFactory.decodeFile(aFilePath, bitmapOptions);
if (bitmap != null) {
return new ReusableBitmapDrawable(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 Drawable getDrawable(final InputStream aFileInputStream) {
try {
// default implementation will load the file as a bitmap and create
// a BitmapDrawable from it
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
BitmapPool.getInstance().applyReusableOptions(bitmapOptions);
final Bitmap bitmap = BitmapFactory.decodeStream(aFileInputStream, null, bitmapOptions);
if (bitmap != null) {
return new ReusableBitmapDrawable(bitmap);
}
} 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);
}
}
// Save rendered tile to cache folder
private class SaveTileAsync extends AsyncTask<Object, Integer, Boolean> {
@Override
protected Boolean doInBackground(Object... objects) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
((AndroidBitmap)objects[0]).compress(bos);
byte[] bitmapdata = bos.toByteArray();
File cachedTile = new File((String)objects[1]);
//write the bytes in file
FileOutputStream fos = new FileOutputStream(cachedTile);
fos.write(bitmapdata);
fos.flush();
fos.close();
} catch (Throwable e) {
e.printStackTrace();
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
}
private Object renderLock = new Object();
private int numOfConcurrentRenders = 0;
@SuppressWarnings("deprecation")
public Drawable renderTile(MapTile pTile) {
synchronized (renderLock) {
// Allow to have 10 concurrent tile renders, if it exceeds 10 concurrent renders,
// wait for previous renders to complete.
if (numOfConcurrentRenders > 10) {
try {
renderLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
numOfConcurrentRenders++;
String cachedTilePath = context.getFilesDir().getPath() + "/MapCache/MapsForge/" +
pTile.getZoomLevel() + "/" + pTile.getX() + "/" + pTile.getY() + ".png";
File cachedTile = new File(cachedTilePath);
if (cachedTile.exists()) {
numOfConcurrentRenders--;
renderLock.notifyAll();
return new BitmapDrawable(BitmapFactory.decodeFile(cachedTilePath));
} else {
Tile tile = new Tile(pTile.getX(), pTile.getY(), (byte) pTile.getZoomLevel(), mTileSizePixels);
rendererJob = new RendererJob(tile, mapDataStore, renderThemeFuture, displayModel, 0.8f, true, false);
try {
AndroidBitmap bitmap = (AndroidBitmap) databaseRenderer.executeJob(rendererJob);
if (bitmap != null) {
new File(context.getFilesDir().getPath() + "/MapCache/MapsForge/" +
pTile.getZoomLevel() + "/" + pTile.getX()).mkdirs();
SaveTileAsync saveTileAsync = new SaveTileAsync();
saveTileAsync.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, bitmap, cachedTilePath);
Drawable d = new BitmapDrawable(AndroidGraphicFactory.getBitmap(bitmap));
numOfConcurrentRenders--;
renderLock.notifyAll();
return d;
}
} catch (Throwable e) {
Log.i("TileCache", "TileCache get failed");
Log.i("TileCache", e.getMessage());
numOfConcurrentRenders--;
renderLock.notifyAll();
return null;
}
return null;
}
}
}
// Use it for clear cache folder, currently has no usage.
private static void DeleteRecursive(File fileOrDirectory) {
if (fileOrDirectory.isDirectory())
for (File child : fileOrDirectory.listFiles())
DeleteRecursive(child);
fileOrDirectory.delete();
}
@Override
public int ordinal() {
// TODO Auto-generated method stub
return 0;
}
@Override
public String name() {
// TODO Auto-generated method stub
return null;
}
@Override
public String localizedName(ResourceProxy proxy) {
// TODO Auto-generated method stub
return null;
}
@Override
public String getTileRelativeFilenameString(MapTile aTile) {
// TODO Auto-generated method stub
return null;
}
}