Commit d4b50c5c authored by kastner's avatar kastner

Checkout tile cache from old commit to speed up heatmap

parent 4a1a6820
......@@ -565,7 +565,7 @@ public class HybridMap {
public void clear(){
if(usingStaticMap) {
staticMap.refreshWorldMapBitmap();
staticMap.clear();
}
}
......
......@@ -5,7 +5,6 @@ import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
......@@ -15,12 +14,12 @@ import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.TileProvider;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import java.io.IOException;
import java.util.SortedSet;
import java.util.TreeSet;
......@@ -45,6 +44,7 @@ public class StaticWorldMap {
private final int zoomLevel;
private TileProvider tileProvider;
private TileCache tileProviderCache;
@NonNull
private final SortedSet<Marker> markers;
......@@ -145,6 +145,7 @@ public class StaticWorldMap {
}
private StaticWorldMap(@NonNull ImageView worldMapImageView, @NonNull Bitmap worldMapBitmap, int zoomLevel){
tileProviderCache = new TileCache(worldMapImageView.getContext());
this.worldMapBitmap = worldMapBitmap;
this.worldMapImageView = worldMapImageView;
this.worldMapImageView.setOnTouchListener((view, motionEvent) -> {
......@@ -261,13 +262,13 @@ public class StaticWorldMap {
int maxX = 0;
/*draw the main tile grid*/
for(int x = 0; x < mapSideLenTiles; x++){
for (int x = 0; x < mapSideLenTiles; x++) {
final int left = ((int) lngOffset + x * 256) % mapSideLenPixels;
if(left >= maxLeft){
maxLeft = left;
maxX = x;
if (left >= maxLeft) {
maxLeft = left;
maxX = x;
}
for(int y = 0; y < mapSideLenTiles; y++){
for (int y = 0; y < mapSideLenTiles; y++) {
final int top = y * 256;
final int y_final = y;
final int x_final = x;
......@@ -277,10 +278,20 @@ public class StaticWorldMap {
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
worldMapCanvas.drawBitmap(loadedImage, left, top, null);
if(tileProvider != null){
Log.i("HEATMAP tile", x_final + " " + y_final + " " + zoomLevel);
byte tileBytes[] = tileProvider.getTile(x_final, y_final, zoomLevel).data;
Bitmap tileBitmap = BitmapFactory.decodeByteArray(tileBytes, 0, tileBytes.length);
if (tileProvider != null) {
Bitmap tileBitmap;
TileSpecifier tile = new TileSpecifier(x_final, y_final, zoomLevel);
if (tileProviderCache.hasTile(tile)) {
tileBitmap = tileProviderCache.getTile(tile);
} else {
byte tileBytes[] = tileProvider.getTile(x_final, y_final, zoomLevel).data;
tileBitmap = BitmapFactory.decodeByteArray(tileBytes, 0, tileBytes.length);
try {
tileProviderCache.putTile(tile, tileBitmap);
} catch (IOException e) {
Log.e("StaticWorldMap", "Failed to cache map tile");
}
}
worldMapCanvas.drawBitmap(tileBitmap, left, top, null);
}
counter.taskCompleted();
......@@ -292,7 +303,7 @@ public class StaticWorldMap {
/* The leftmost column of tiles needs to be redrawn to the right of the main grid
* so that the entire screen is always covered. */
final int left = maxLeft - mapSideLenPixels;
for(int y = 0; y < mapSideLenTiles; y++){
for (int y = 0; y < mapSideLenTiles; y++) {
final int top = y * 256;
final int y_final = y;
final int maxX_final = maxX;
......@@ -301,18 +312,32 @@ public class StaticWorldMap {
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
worldMapCanvas.drawBitmap(loadedImage, left, top, null);
if(tileProvider != null){
Log.i("HEATMAP tile", maxX_final + " " + y_final + " " + zoomLevel);
Bitmap tileBitmap;
TileSpecifier tile = new TileSpecifier(maxX_final, y_final, zoomLevel);
if (tileProviderCache.hasTile(tile)) {
tileBitmap = tileProviderCache.getTile(tile);
} else {
byte tileBytes[] = tileProvider.getTile(maxX_final, y_final, zoomLevel).data;
Bitmap tileBitmap = BitmapFactory.decodeByteArray(tileBytes, 0, tileBytes.length);
worldMapCanvas.drawBitmap(tileBitmap, left, top, null);
tileBitmap = BitmapFactory.decodeByteArray(tileBytes, 0, tileBytes.length);
try {
tileProviderCache.putTile(tile, tileBitmap);
} catch (IOException e) {
Log.e("StaticWorldMap", "Failed to cache map tile");
}
}
worldMapCanvas.drawBitmap(tileBitmap, left, top, null);
counter.taskCompleted();
}
});
}
}
public void clear(){
tileProviderCache.clearTiles();
refreshWorldMapBitmap();
}
/**
* Redraw the map and all markers on it.
* This function must be called after any changes to the markers set and its contents.
......
package edu.umd.umiacs.newsstand.staticmap;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This class is used when downloading tiles from a Tile Service to reduce the number of requests
* made. Since most devices will need need 16 or fewer tiles, caching all of them locally does not
* require much storage but can save time and data usage on downloads.
*/
class TileCache {
private static final String TILE_FILE_FORMAT = "tile_x%d_y%d_z%d.png";
private static final Pattern TILE_FILE_REGEX = Pattern.compile("tile_x\\d+_y\\d+_z\\d+\\.png");
@NonNull
private final Context context;
public TileCache(@NonNull Context context){
this.context = context;
}
/**
* Loads a cached tile from local storage. This method assumes that the tile exists on the device
* so, check for the tile with hasTile before using this method.
*/
@Nullable
public Bitmap getTile(@NonNull TileSpecifier tile){
File tileFile = getTileFile(tile);
return BitmapFactory.decodeFile(tileFile.getAbsolutePath());
}
/**
* Check for the presence of a tile in the cache. Returns true if the tile is cached.
*/
public boolean hasTile(@NonNull TileSpecifier tile){
return getTileFile(tile).exists();
}
/**
* Add a tile to the cache so that it can be retrieved using later calls to getTile.
*/
public void putTile(@NonNull TileSpecifier tile, @NonNull Bitmap bitmap) throws IOException {
File tileFile = getTileFile(tile);
try (FileOutputStream out = new FileOutputStream(tileFile)){
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
}
}
/* Remove all cached tile files */
public void clearTiles(){
for(String fileName : context.fileList()){
Matcher matcher = TILE_FILE_REGEX.matcher(fileName);
if (matcher.matches()){
context.deleteFile(fileName);
}
}
}
/**
* Utility function for getting a file handle for a given tile.
*/
@NonNull
private File getTileFile(@NonNull TileSpecifier tile){
return new File(context.getFilesDir(), getTileFileName(tile));
}
@NonNull
private static String getTileFileName(TileSpecifier tile){
return String.format(TILE_FILE_FORMAT, tile.x, tile.y, tile.z);
}
}
package edu.umd.umiacs.newsstand.staticmap;
/**
* Tile obtained from a tile service are specified by and x coordinate, y coordinate and a zoom level.
* This (immutable) class contains this data.
*/
class TileSpecifier {
public final int x, y, z;
public TileSpecifier(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
@Override
public String toString() {
return "TileSpecifier{" +
"x=" + x +
", y=" + y +
", z=" + z +
'}';
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment