package org.osmdroid.mapsforge;
import android.app.Application;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import org.mapsforge.core.graphics.GraphicFactory;
import org.mapsforge.core.model.BoundingBox;
import org.mapsforge.core.model.Tile;
import org.mapsforge.map.android.graphics.AndroidGraphicFactory;
import org.mapsforge.map.android.graphics.AndroidTileBitmap;
import org.mapsforge.map.datastore.MapDataStore;
import org.mapsforge.map.datastore.MultiMapDataStore;
import org.mapsforge.map.layer.cache.InMemoryTileCache;
import org.mapsforge.map.layer.cache.TileCache;
import org.mapsforge.map.layer.labels.TileBasedLabelStore;
import org.mapsforge.map.layer.renderer.DatabaseRenderer;
import org.mapsforge.map.layer.renderer.RendererJob;
import org.mapsforge.map.model.DisplayModel;
import org.mapsforge.map.reader.MapFile;
import org.mapsforge.map.reader.ReadBuffer;
import org.mapsforge.map.rendertheme.InternalRenderTheme;
import org.mapsforge.map.rendertheme.XmlRenderTheme;
import org.mapsforge.map.rendertheme.rule.RenderThemeFuture;
import org.osmdroid.api.IMapView;
import org.osmdroid.tileprovider.MapTile;
import org.osmdroid.tileprovider.tilesource.BitmapTileSourceBase;
import java.io.File;
/**
* Adapted from code from here: https://github.com/MKergall/osmbonuspack, which is LGPL
* http://www.salidasoftware.com/how-to-render-mapsforge-tiles-in-osmdroid/
*
* @author Salida Software
* Adapted from code found here : http://www.sieswerda.net/2012/08/15/upping-the-developer-friendliness/
*/
public class MapsForgeTileSource extends BitmapTileSourceBase {
// Reasonable defaults ..
public static int MIN_ZOOM = 3;
public static int MAX_ZOOM = 20;
public static final int TILE_SIZE_PIXELS = 256;
private final DisplayModel model = new DisplayModel();
private final float scale = DisplayModel.getDefaultUserScaleFactor();
private RenderThemeFuture theme = null;
private XmlRenderTheme mXmlRenderTheme = null;
private DatabaseRenderer renderer;
private MultiMapDataStore mapDatabase;
/**
* The reason this constructor is protected is because all parameters,
* except file should be determined from the archive file. Therefore a
* factory method is necessary.
*
* @param minZoom
* @param maxZoom
* @param tileSizePixels
* @param file
* @param xmlRenderTheme the theme to render tiles with
*/
protected MapsForgeTileSource(String cacheTileSourceName, int minZoom, int maxZoom, int tileSizePixels, File[] file, XmlRenderTheme xmlRenderTheme, MultiMapDataStore.DataPolicy dataPolicy) {
super(cacheTileSourceName, minZoom, maxZoom, tileSizePixels, ".png","© OpenStreetMap contributors");
mapDatabase = new MultiMapDataStore(dataPolicy);
for (int i = 0; i < file.length; i++)
mapDatabase.addMapDataStore(new MapFile(file[i]), false, false);
if (AndroidGraphicFactory.INSTANCE==null) {
throw new RuntimeException("Must call MapsForgeTileSource.createInstance(context.getApplication()); once before MapsForgeTileSource.createFromFiles().");
}
// mapsforge0.6
// renderer = new DatabaseRenderer(mapDatabase, AndroidGraphicFactory.INSTANCE, new InMemoryTileCache(2));
// mapsforge0.6.1
InMemoryTileCache tileCache = new InMemoryTileCache(2);
renderer = new DatabaseRenderer(mapDatabase, AndroidGraphicFactory.INSTANCE, tileCache,
new TileBasedLabelStore(tileCache.getCapacityFirstLevel()), true, true);
minZoom = MIN_ZOOM;
maxZoom = renderer.getZoomLevelMax();
Log.d(IMapView.LOGTAG, "min=" + minZoom + " max=" + maxZoom + " tilesize=" + tileSizePixels);
if (xmlRenderTheme == null)
xmlRenderTheme = InternalRenderTheme.OSMARENDER;
//we the passed in theme is different that the existing one, or the theme is currently null, create it
if (xmlRenderTheme != mXmlRenderTheme || theme == null) {
theme = new RenderThemeFuture(AndroidGraphicFactory.INSTANCE, xmlRenderTheme, model);
//super important!! without the following line, all rendering activities will block until the theme is created.
new Thread(theme).start();
}
}
public BoundingBox getBounds(){
return mapDatabase.boundingBox();
}
public org.osmdroid.util.BoundingBox getBoundsOsmdroid(){
BoundingBox boundingBox = mapDatabase.boundingBox();
org.osmdroid.util.BoundingBox bounds = new org.osmdroid.util.BoundingBox(boundingBox.maxLatitude, boundingBox.maxLongitude,
boundingBox.minLatitude, boundingBox.minLongitude);
return bounds;
}
/**
* Creates a new MapsForgeTileSource from file.
* <p></p>
* Parameters minZoom and maxZoom are obtained from the
* database. If they cannot be obtained from the DB, the default values as
* defined by this class are used, which is zoom = 3-20
*
* @param file
* @return the tile source
*/
public static MapsForgeTileSource createFromFiles(File[] file) {
//these settings are ignored and are set based on .map file info
int minZoomLevel = MIN_ZOOM;
int maxZoomLevel = MAX_ZOOM;
int tileSizePixels = TILE_SIZE_PIXELS;
return new MapsForgeTileSource(InternalRenderTheme.OSMARENDER.name(), minZoomLevel, maxZoomLevel, tileSizePixels, file, InternalRenderTheme.OSMARENDER, MultiMapDataStore.DataPolicy.RETURN_ALL);
}
/**
* Creates a new MapsForgeTileSource from file[].
* <p></p>
* Parameters minZoom and maxZoom are obtained from the
* database. If they cannot be obtained from the DB, the default values as
* defined by this class are used, which is zoom = 3-20
*
* @param file
* @param theme this can be null, in which case the default them will be used
* @param themeName when using a custom theme, this sets up the osmdroid caching correctly
* @return
*/
public static MapsForgeTileSource createFromFiles(File[] file, XmlRenderTheme theme, String themeName) {
//these settings are ignored and are set based on .map file info
int minZoomLevel = MIN_ZOOM;
int maxZoomLevel = MAX_ZOOM;
int tileSizePixels = TILE_SIZE_PIXELS;
return new MapsForgeTileSource(themeName, minZoomLevel, maxZoomLevel, tileSizePixels, file, theme, MultiMapDataStore.DataPolicy.RETURN_ALL);
}
/**
* Creates a new MapsForgeTileSource from file[].
* <p></p>
* Parameters minZoom and maxZoom are obtained from the
* database. If they cannot be obtained from the DB, the default values as
* defined by this class are used, which is zoom = 3-20
*
* @param file
* @param theme this can be null, in which case the default them will be used
* @param themeName when using a custom theme, this sets up the osmdroid caching correctly
* @param dataPolicy use this to override the default, which is "RETURN_ALL"
* @return
*/
public static MapsForgeTileSource createFromFiles(File[] file, XmlRenderTheme theme, String themeName, MultiMapDataStore.DataPolicy dataPolicy) {
//these settings are ignored and are set based on .map file info
int minZoomLevel = MIN_ZOOM;
int maxZoomLevel = MAX_ZOOM;
int tileSizePixels = TILE_SIZE_PIXELS;
return new MapsForgeTileSource(themeName, minZoomLevel, maxZoomLevel, tileSizePixels, file, theme, dataPolicy);
}
//The synchronized here is VERY important. If missing, the mapDatabase read gets corrupted by multiple threads reading the file at once.
public synchronized Drawable renderTile(MapTile pTile) {
Tile tile = new Tile(pTile.getX(), pTile.getY(), (byte) pTile.getZoomLevel(), 256);
model.setFixedTileSize(256);
//You could try something like this to load a custom theme
//try{
// jobTheme = new ExternalRenderTheme(themeFile);
//}
//catch(Exception e){
// jobTheme = InternalRenderTheme.OSMARENDER;
//}
if (mapDatabase==null)
return null;
try {
//Draw the tile
RendererJob mapGeneratorJob = new RendererJob(tile, mapDatabase, theme, model, scale, false, false);
AndroidTileBitmap bmp = (AndroidTileBitmap) renderer.executeJob(mapGeneratorJob);
if (bmp != null)
return new BitmapDrawable(AndroidGraphicFactory.getBitmap(bmp));
} catch (Exception ex) {
Log.d(IMapView.LOGTAG, "###################### Mapsforge tile generation failed", ex);
}
//Make the bad tile easy to spot
Bitmap bitmap = Bitmap.createBitmap(TILE_SIZE_PIXELS, TILE_SIZE_PIXELS, Bitmap.Config.RGB_565);
bitmap.eraseColor(Color.YELLOW);
return new BitmapDrawable(bitmap);
}
public static void createInstance(Application app) {
AndroidGraphicFactory.createInstance(app);
// see https://github.com/mapsforge/mapsforge/issues/868
ReadBuffer.setMaximumBufferSize(6500000);
}
public void dispose() {
theme.decrementRefCount();
theme=null;
renderer=null;
if (mapDatabase!=null)
mapDatabase.close();
mapDatabase=null;
}
}