Introduce cache

This commit is contained in:
Victor Shcherb 2020-09-07 18:57:54 +02:00
parent a92bac55f7
commit b779b8b61f

View file

@ -9,41 +9,62 @@ import net.osmand.data.RotatedTileBox;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.resources.AsyncLoadingThread; import net.osmand.plus.resources.AsyncLoadingThread;
import net.osmand.plus.server.OsmAndHttpServer; import net.osmand.plus.server.OsmAndHttpServer;
import net.osmand.plus.views.OsmandMapTileView;
import net.osmand.util.MapUtils; import net.osmand.util.MapUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Scanner; import java.util.concurrent.ConcurrentLinkedQueue;
import static fi.iki.elonen.NanoHTTPD.newFixedLengthResponse; import static fi.iki.elonen.NanoHTTPD.newFixedLengthResponse;
public class TileEndpoint implements OsmAndHttpServer.ApiEndpoint { public class TileEndpoint implements OsmAndHttpServer.ApiEndpoint {
private static final int TILE_SIZE_PX = 512; private static final int TILE_SIZE_PX = 256;
private static final int TILE_DENSITY = 2;
private static final int TIMEOUT_STEP = 500; private static final int TIMEOUT_STEP = 500;
private static final int TIMEOUT = 5000; private static final int TIMEOUT = 5000;
private static final int METATILE_SIZE = 2;
private static final int MAX_CACHE_SIZE = 4;
private static final Log LOG = PlatformUtil.getLog(TileEndpoint.class); private static final Log LOG = PlatformUtil.getLog(TileEndpoint.class);
private final MapActivity mapActivity; private final MapActivity mapActivity;
private final List<MetaTileCache> cache = new ArrayList<>(); // TODO store on file system
private final ConcurrentLinkedQueue<MetaTileCache> cache = new ConcurrentLinkedQueue<>();
private static class MetaTileCache { private static class MetaTileCache {
Bitmap bmp; Bitmap bmp;
int x; int sx;
int y; int sy;
int ex;
int ey;
int zoom; int zoom;
// to be used in file name
public String getTileId() {
return zoom + "_" + METATILE_SIZE + "_" + sx + "_" + sy;
}
public Bitmap getSubtile(int x, int y) {
// TODO cut subtitle
return bmp;
}
} }
public TileEndpoint(MapActivity mapActivity) { public TileEndpoint(MapActivity mapActivity) {
this.mapActivity = mapActivity; this.mapActivity = mapActivity;
} }
private void addToMemoryCache(MetaTileCache res) {
while (cache.size() > MAX_CACHE_SIZE) {
cache.poll();
}
cache.add(res);
}
@Override @Override
public NanoHTTPD.Response process(NanoHTTPD.IHTTPSession session, String url) { public NanoHTTPD.Response process(NanoHTTPD.IHTTPSession session, String url) {
// https://tile.osmand.net/hd/6/55/25.png // https://tile.osmand.net/hd/6/55/25.png
@ -61,12 +82,24 @@ public class TileEndpoint implements OsmAndHttpServer.ApiEndpoint {
int zoom = Integer.parseInt(prms[1]); int zoom = Integer.parseInt(prms[1]);
int x = Integer.parseInt(prms[2]); int x = Integer.parseInt(prms[2]);
int y = Integer.parseInt(prms[3]); int y = Integer.parseInt(prms[3]);
Bitmap bitmap = requestTile(x, y, zoom); MetaTileCache res = null;
if (bitmap == null) { for (MetaTileCache r : cache) {
if (r.zoom == zoom && r.ex >= x && r.ey >= y && r.sx <= x && r.sy <= y) {
res = r;
}
}
if(res == null) {
res = requestMetatile(x, y, zoom);
}
if (res == null) {
return OsmAndHttpServer.ErrorResponses.response500; return OsmAndHttpServer.ErrorResponses.response500;
} }
ByteArrayOutputStream stream = new ByteArrayOutputStream(); ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); Bitmap bmp = res.getSubtile(x, y);
if (bmp == null) {
return OsmAndHttpServer.ErrorResponses.response500;
}
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray(); byte[] byteArray = stream.toByteArray();
ByteArrayInputStream str = new ByteArrayInputStream(byteArray); ByteArrayInputStream str = new ByteArrayInputStream(byteArray);
return newFixedLengthResponse( return newFixedLengthResponse(
@ -74,28 +107,42 @@ public class TileEndpoint implements OsmAndHttpServer.ApiEndpoint {
str, str.available()); str, str.available());
} }
private synchronized Bitmap requestTile(int x, int y, int zoom) { private synchronized MetaTileCache requestMetatile(int x, int y, int zoom) {
double lat = MapUtils.getLatitudeFromTile(zoom, y); int mx = (x / METATILE_SIZE) * METATILE_SIZE;
double lon = MapUtils.getLongitudeFromTile(zoom, x); int my = (y / METATILE_SIZE) * METATILE_SIZE;
double lat = MapUtils.getLatitudeFromTile(zoom, my + 0.5 * METATILE_SIZE);
double lon = MapUtils.getLongitudeFromTile(zoom, mx + 0.5 * METATILE_SIZE);
final RotatedTileBox cp = mapActivity.getMapView().getCurrentRotatedTileBox(); final RotatedTileBox cp = mapActivity.getMapView().getCurrentRotatedTileBox();
final RotatedTileBox rotatedTileBox = new RotatedTileBox.RotatedTileBoxBuilder() final RotatedTileBox rotatedTileBox = new RotatedTileBox.RotatedTileBoxBuilder()
.setLocation(lat, lon) .setLocation(lat, lon)
.setMapDensity(cp.getMapDensity()).density(cp.getDensity()) .setMapDensity(cp.getMapDensity()).density(TILE_DENSITY)
.setZoom(zoom) .setZoom(zoom)
.setPixelDimensions(TILE_SIZE_PX, TILE_SIZE_PX, 0.5f, 0.5f).build(); .setPixelDimensions(TILE_SIZE_PX * TILE_DENSITY * METATILE_SIZE, TILE_SIZE_PX * TILE_DENSITY * METATILE_SIZE, 0.5f, 0.5f).build();
mapActivity.getMapView().setCurrentViewport(rotatedTileBox); mapActivity.getMapView().setCurrentViewport(rotatedTileBox);
int timeout = 0; int timeout = 0;
try { try {
AsyncLoadingThread athread = mapActivity.getMyApplication().getResourceManager().getAsyncLoadingThread(); AsyncLoadingThread athread = mapActivity.getMyApplication().getResourceManager().getAsyncLoadingThread();
Thread.sleep(TIMEOUT_STEP); // line not correct Thread.sleep(TIMEOUT_STEP); // TODO line should be removed in future
Bitmap res = null; MetaTileCache res = null;
while (athread.areResourcesLoading() && timeout < TIMEOUT) { while (athread.areResourcesLoading() && timeout < TIMEOUT) {
Thread.sleep(TIMEOUT_STEP); Thread.sleep(TIMEOUT_STEP);
timeout += TIMEOUT_STEP; timeout += TIMEOUT_STEP;
} }
if (!athread.areResourcesLoading()) { if (!athread.areResourcesLoading()) {
res = mapActivity.getMapView().getBufferBitmap(); res = new MetaTileCache();
res.sx = mx;
res.ex = mx + METATILE_SIZE - 1;
res.sy = my;
res.ey = my + METATILE_SIZE - 1;
res.zoom = zoom;
RotatedTileBox tilebox = mapActivity.getMapView().getBufferImgLoc();
// TODO here we need to properly cut image according to tilebox
res.bmp = mapActivity.getMapView().getBufferBitmap();
LOG.debug(mapActivity.getMapView().getBufferImgLoc()); LOG.debug(mapActivity.getMapView().getBufferImgLoc());
addToMemoryCache(res);
} }
return res; return res;
} catch (InterruptedException e) { } catch (InterruptedException e) {
@ -103,4 +150,6 @@ public class TileEndpoint implements OsmAndHttpServer.ApiEndpoint {
} }
return null; return null;
} }
} }