package net.osmand.plus.srtmplugin;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import net.osmand.IndexConstants;
import net.osmand.PlatformUtil;
import net.osmand.data.QuadRect;
import net.osmand.data.QuadTree;
import net.osmand.data.RotatedTileBox;
import net.osmand.map.TileSourceManager.TileSourceTemplate;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.SQLiteTileSource;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.api.SQLiteAPI.SQLiteConnection;
import net.osmand.plus.views.MapTileLayer;
import net.osmand.plus.views.OsmandMapLayer;
import org.apache.commons.logging.Log;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.AsyncTask;
public class HillshadeLayer extends MapTileLayer {
private final static Log log = PlatformUtil.getLog(HillshadeLayer.class);
private Map<String, SQLiteTileSource> resources = new LinkedHashMap<String, SQLiteTileSource>();
private final static String HILLSHADE_CACHE = "hillshade.cache";
private int ZOOM_BOUNDARY = 15;
private final static int MAX_TRANSPARENCY_ZOOM = 17;
private final static int DEFAULT_ALPHA = 100;
private final static int MAX_TRANSPARENCY_ALPHA = 20;
private QuadTree<String> indexedResources = new QuadTree<String>(new QuadRect(0, 0, 1 << (ZOOM_BOUNDARY+1), 1 << (ZOOM_BOUNDARY+1)), 8, 0.55f);
public HillshadeLayer(MapActivity activity, SRTMPlugin srtmPlugin) {
super(false);
final OsmandApplication app = activity.getMyApplication();
indexHillshadeFiles(app);
setAlpha(DEFAULT_ALPHA);
setMap(createTileSource(activity));
}
@Override
public void onPrepareBufferImage(Canvas canvas, RotatedTileBox tileBox, DrawSettings drawSettings) {
if (tileBox.getZoom() >= MAX_TRANSPARENCY_ZOOM) {
setAlpha(MAX_TRANSPARENCY_ALPHA);
} else {
setAlpha(DEFAULT_ALPHA);
}
super.onPrepareBufferImage(canvas, tileBox, drawSettings);
}
private void indexHillshadeFiles(final OsmandApplication app ) {
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void> () {
private SQLiteDatabase sqliteDb;
@Override
protected Void doInBackground(Void... params) {
File tilesDir = app.getAppPath(IndexConstants.TILES_INDEX_DIR);
File cacheDir = app.getCacheDir();
// fix http://stackoverflow.com/questions/26937152/workaround-for-nexus-9-sqlite-file-write-operations-on-external-dirs
sqliteDb = SQLiteDatabase.openDatabase(new File(cacheDir, HILLSHADE_CACHE).getPath() ,
null, SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING
| SQLiteDatabase.CREATE_IF_NECESSARY );
if(sqliteDb.getVersion() == 0) {
sqliteDb.setVersion(1);
sqliteDb.execSQL("CREATE TABLE TILE_SOURCES(filename varchar2(256), date_modified int, left int, right int, top int, bottom int)");
}
Map<String, Long> fileModified = new HashMap<String, Long>();
Map<String, SQLiteTileSource> rs = readFiles(app, tilesDir, fileModified);
indexCachedResources(fileModified, rs);
indexNonCachedResources(fileModified, rs);
sqliteDb.close();
resources = rs;
return null;
}
@Override
protected void onPostExecute(Void result) {
app.getResourceManager().reloadTilesFromFS();
}
private void indexNonCachedResources(Map<String, Long> fileModified, Map<String, SQLiteTileSource> rs) {
for(String filename : fileModified.keySet()) {
try {
log.info("Indexing hillshade file " + filename);
ContentValues cv = new ContentValues();
cv.put("filename", filename);
cv.put("date_modified", fileModified.get(filename));
SQLiteTileSource ts = rs.get(filename);
QuadRect rt = ts.getRectBoundary(ZOOM_BOUNDARY, 1);
if (rt != null) {
indexedResources.insert(filename, rt);
cv.put("left", (int)rt.left);
cv.put("right",(int) rt.right);
cv.put("top", (int)rt.top);
cv.put("bottom",(int) rt.bottom);
sqliteDb.insert("TILE_SOURCES", null, cv);
}
} catch(RuntimeException e){
log.error(e.getMessage(), e);
}
}
}
private void indexCachedResources(Map<String, Long> fileModified, Map<String, SQLiteTileSource> rs) {
Cursor cursor = sqliteDb.rawQuery("SELECT filename, date_modified, left, right, top, bottom FROM TILE_SOURCES",
new String[0]);
if(cursor.moveToFirst()) {
do {
String filename = cursor.getString(0);
long lastModified = cursor.getLong(1);
Long read = fileModified.get(filename);
if(rs.containsKey(filename) && read != null && lastModified == read) {
int left = cursor.getInt(2);
int right = cursor.getInt(3);
int top = cursor.getInt(4);
float bottom = cursor.getInt(5);
indexedResources.insert(filename, new QuadRect(left, top, right, bottom));
fileModified.remove(filename);
}
} while(cursor.moveToNext());
}
cursor.close();
}
private Map<String, SQLiteTileSource> readFiles(final OsmandApplication app, File tilesDir, Map<String, Long> fileModified) {
Map<String, SQLiteTileSource> rs = new LinkedHashMap<String, SQLiteTileSource>();
File[] files = tilesDir.listFiles();
if(files != null) {
for(File f : files) {
if(f != null && f.getName().endsWith(IndexConstants.SQLITE_EXT) &&
f.getName().toLowerCase().startsWith("hillshade")) {
SQLiteTileSource ts = new SQLiteTileSource(app, f, new ArrayList<TileSourceTemplate>());
rs.put(f.getName(), ts);
fileModified.put(f.getName(), f.lastModified());
}
}
}
return rs;
}
};
executeTaskInBackground(task);
}
private SQLiteTileSource createTileSource(MapActivity activity) {
return new SQLiteTileSource(activity.getMyApplication(), null, new ArrayList<TileSourceTemplate>()) {
@Override
protected SQLiteConnection getDatabase() {
throw new UnsupportedOperationException();
}
public boolean isLocked() {
return false;
};
List<String> getTileSource(int x, int y, int zoom) {
ArrayList<String> ls = new ArrayList<String>();
int z = (zoom - ZOOM_BOUNDARY);
if (z > 0) {
indexedResources.queryInBox(new QuadRect(x >> z, y >> z, (x >> z), (y >> z)), ls);
} else {
indexedResources.queryInBox(new QuadRect(x << -z, y << -z, (x + 1) << -z, (y + 1) << -z), ls);
}
return ls;
}
@Override
public boolean exists(int x, int y, int zoom) {
List<String> ts = getTileSource(x, y, zoom);
for (String t : ts) {
SQLiteTileSource sqLiteTileSource = resources.get(t);
if(sqLiteTileSource != null && sqLiteTileSource.exists(x, y, zoom)) {
return true;
}
}
return false;
}
@Override
public Bitmap getImage(int x, int y, int zoom, long[] timeHolder) {
List<String> ts = getTileSource(x, y, zoom);
for (String t : ts) {
SQLiteTileSource sqLiteTileSource = resources.get(t);
if (sqLiteTileSource != null) {
Bitmap bmp = sqLiteTileSource.getImage(x, y, zoom, timeHolder);
if (bmp != null) {
return sqLiteTileSource.getImage(x, y, zoom, timeHolder);
}
}
}
return null;
}
@Override
public int getBitDensity() {
return 32;
}
@Override
public int getMinimumZoomSupported() {
return 5;
}
@Override
public int getMaximumZoomSupported() {
return 11;
}
@Override
public int getTileSize() {
return 256;
}
@Override
public boolean couldBeDownloadedFromInternet() {
return false;
}
@Override
public String getName() {
return "Hillshade";
}
@Override
public String getTileFormat() {
return "jpg";
}
};
}
}