Introduce cache
This commit is contained in:
parent
a92bac55f7
commit
b779b8b61f
1 changed files with 68 additions and 19 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue