From b779b8b61f63be22c73ba322d7542ba0092be4e7 Mon Sep 17 00:00:00 2001 From: Victor Shcherb Date: Mon, 7 Sep 2020 18:57:54 +0200 Subject: [PATCH] Introduce cache --- .../plus/server/endpoints/TileEndpoint.java | 87 +++++++++++++++---- 1 file changed, 68 insertions(+), 19 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/server/endpoints/TileEndpoint.java b/OsmAnd/src/net/osmand/plus/server/endpoints/TileEndpoint.java index 2774bb6eab..3811a098fe 100644 --- a/OsmAnd/src/net/osmand/plus/server/endpoints/TileEndpoint.java +++ b/OsmAnd/src/net/osmand/plus/server/endpoints/TileEndpoint.java @@ -9,41 +9,62 @@ import net.osmand.data.RotatedTileBox; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.resources.AsyncLoadingThread; import net.osmand.plus.server.OsmAndHttpServer; -import net.osmand.plus.views.OsmandMapTileView; import net.osmand.util.MapUtils; import org.apache.commons.logging.Log; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.net.URL; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; -import java.util.Scanner; +import java.util.concurrent.ConcurrentLinkedQueue; import static fi.iki.elonen.NanoHTTPD.newFixedLengthResponse; 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 = 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 final MapActivity mapActivity; - private final List cache = new ArrayList<>(); + // TODO store on file system + private final ConcurrentLinkedQueue cache = new ConcurrentLinkedQueue<>(); private static class MetaTileCache { Bitmap bmp; - int x; - int y; + int sx; + int sy; + int ex; + int ey; 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) { this.mapActivity = mapActivity; } + private void addToMemoryCache(MetaTileCache res) { + while (cache.size() > MAX_CACHE_SIZE) { + cache.poll(); + } + cache.add(res); + } + @Override public NanoHTTPD.Response process(NanoHTTPD.IHTTPSession session, String url) { // 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 x = Integer.parseInt(prms[2]); int y = Integer.parseInt(prms[3]); - Bitmap bitmap = requestTile(x, y, zoom); - if (bitmap == null) { + MetaTileCache res = 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; } 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(); ByteArrayInputStream str = new ByteArrayInputStream(byteArray); return newFixedLengthResponse( @@ -74,28 +107,42 @@ public class TileEndpoint implements OsmAndHttpServer.ApiEndpoint { str, str.available()); } - private synchronized Bitmap requestTile(int x, int y, int zoom) { - double lat = MapUtils.getLatitudeFromTile(zoom, y); - double lon = MapUtils.getLongitudeFromTile(zoom, x); + private synchronized MetaTileCache requestMetatile(int x, int y, int zoom) { + int mx = (x / METATILE_SIZE) * METATILE_SIZE; + 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 rotatedTileBox = new RotatedTileBox.RotatedTileBoxBuilder() .setLocation(lat, lon) - .setMapDensity(cp.getMapDensity()).density(cp.getDensity()) + .setMapDensity(cp.getMapDensity()).density(TILE_DENSITY) .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); + + int timeout = 0; try { AsyncLoadingThread athread = mapActivity.getMyApplication().getResourceManager().getAsyncLoadingThread(); - Thread.sleep(TIMEOUT_STEP); // line not correct - Bitmap res = null; + Thread.sleep(TIMEOUT_STEP); // TODO line should be removed in future + MetaTileCache res = null; while (athread.areResourcesLoading() && timeout < TIMEOUT) { Thread.sleep(TIMEOUT_STEP); timeout += TIMEOUT_STEP; } - if(!athread.areResourcesLoading()) { - res = mapActivity.getMapView().getBufferBitmap(); + if (!athread.areResourcesLoading()) { + 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()); + + addToMemoryCache(res); } return res; } catch (InterruptedException e) { @@ -103,4 +150,6 @@ public class TileEndpoint implements OsmAndHttpServer.ApiEndpoint { } return null; } + + }