diff --git a/OsmAnd-java/src/main/java/net/osmand/data/RotatedTileBox.java b/OsmAnd-java/src/main/java/net/osmand/data/RotatedTileBox.java index 57c0080625..84a5a39cdd 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/RotatedTileBox.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/RotatedTileBox.java @@ -2,6 +2,8 @@ package net.osmand.data; import net.osmand.util.MapUtils; +import java.util.Objects; + public class RotatedTileBox { /// primary fields @@ -69,6 +71,47 @@ public class RotatedTileBox { } } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RotatedTileBox tileBox = (RotatedTileBox) o; + return Double.compare(tileBox.lat, lat) == 0 && + Double.compare(tileBox.lon, lon) == 0 && + Float.compare(tileBox.rotate, rotate) == 0 && + Float.compare(tileBox.density, density) == 0 && + zoom == tileBox.zoom && + Double.compare(tileBox.mapDensity, mapDensity) == 0 && + Double.compare(tileBox.zoomAnimation, zoomAnimation) == 0 && + Double.compare(tileBox.zoomFloatPart, zoomFloatPart) == 0 && + cx == tileBox.cx && + cy == tileBox.cy && + pixWidth == tileBox.pixWidth && + pixHeight == tileBox.pixHeight && + Double.compare(tileBox.zoomFactor, zoomFactor) == 0 && + Double.compare(tileBox.rotateCos, rotateCos) == 0 && + Double.compare(tileBox.rotateSin, rotateSin) == 0 && + Double.compare(tileBox.oxTile, oxTile) == 0 && + Double.compare(tileBox.oyTile, oyTile) == 0; + } + + @Override + public int hashCode() { + int result = 1 + (int)lat + + 3* (int)lon + + 5* (int)rotate + + 7* (int)density + + 11* (int)zoom + + 13* (int)mapDensity + + 17* (int)zoomAnimation + + 19* (int)zoomFloatPart + + 23* (int)cx + + 29* (int)cy + + 31* (int)pixWidth + + 37* (int)pixHeight; + return result; + } + public void calculateDerivedFields() { zoomFactor = Math.pow(2, zoomAnimation + zoomFloatPart) * 256 * mapDensity; double rad = Math.toRadians(this.rotate); diff --git a/OsmAnd/src/net/osmand/plus/server/ApiRouter.java b/OsmAnd/src/net/osmand/plus/server/ApiRouter.java index 03e3e85fd7..cfaa752abb 100644 --- a/OsmAnd/src/net/osmand/plus/server/ApiRouter.java +++ b/OsmAnd/src/net/osmand/plus/server/ApiRouter.java @@ -1,37 +1,28 @@ package net.osmand.plus.server; import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; import android.util.Log; +import android.util.Pair; import android.webkit.MimeTypeMap; -import androidx.core.util.Pair; import com.google.gson.Gson; import fi.iki.elonen.NanoHTTPD; -import net.osmand.data.*; -import net.osmand.map.ITileSource; -import net.osmand.map.TileSourceManager; +import net.osmand.data.FavouritePoint; +import net.osmand.data.RotatedTileBox; import net.osmand.plus.OsmandApplication; import net.osmand.plus.activities.MapActivity; -import net.osmand.plus.resources.ResourceManager; -import net.osmand.plus.server.map.LayersDraw; -import net.osmand.plus.server.map.MapTileMiniLayer; -import net.osmand.plus.server.map.OsmandMapMiniLayer; -import net.osmand.plus.server.map.OsmandMapTileMiniView; import net.osmand.plus.views.OsmandMapLayer; +import net.osmand.plus.views.OsmandMapTileView; import java.io.*; import java.nio.charset.Charset; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Scanner; +import java.util.*; +import java.util.concurrent.*; import static fi.iki.elonen.NanoHTTPD.newFixedLengthResponse; -public class ApiRouter { +public class ApiRouter implements OsmandMapTileView.IMapImageDrawListener { private OsmandApplication androidContext; + public OsmandApplication getAndroidContext() { return androidContext; } @@ -42,80 +33,92 @@ public class ApiRouter { //change to weakreference public static MapActivity mapActivity; - public ApiRouter(){ + public ApiRouter() { initRoutes(); } private void initRoutes() { ApiEndpoint favorites = new ApiEndpoint(); favorites.uri = "/favorites"; - favorites.apiCall = new ApiEndpoint.ApiCall(){ + favorites.apiCall = new ApiEndpoint.ApiCall() { @Override public NanoHTTPD.Response call(NanoHTTPD.IHTTPSession session) { return newFixedLengthResponse(getFavoritesJson()); } }; - endpoints.put(favorites.uri,favorites); + endpoints.put(favorites.uri, favorites); final ApiEndpoint tile = new ApiEndpoint(); tile.uri = "/tile"; - tile.apiCall = new ApiEndpoint.ApiCall(){ + tile.apiCall = new ApiEndpoint.ApiCall() { @Override public NanoHTTPD.Response call(NanoHTTPD.IHTTPSession session) { - try{ - int zoom = 0; - double lat = 0;//50.901430; - double lon = 0;//34.801775; - try{ - String fullUri = session.getUri().replace("/tile/",""); - Scanner s = new Scanner(fullUri).useDelimiter("/"); - zoom = s.nextInt(); - lat = s.nextDouble();//50.901430; - lon = s.nextDouble();//34.801775; - } - catch (Exception e){ - e.printStackTrace(); - return ErrorResponses.response500; - } - Log.d("TILE","HAVING VALUES" + zoom + " " + lat + " " + lon); - RotatedTileBox rotatedTileBox = mapActivity.getMapView().getCurrentRotatedTileBox().copy(); - rotatedTileBox.setZoom(zoom); - rotatedTileBox.setLatLonCenter(lat,lon); - rotatedTileBox.setPixelDimensions(512,512); - mapActivity.getMapView().setIntZoom(zoom); - mapActivity.getMapView().setLatLon(lat,lon); - OsmandMapLayer.DrawSettings param = - new OsmandMapLayer.DrawSettings(androidContext.getDaynightHelper().isNightMode(), - false); - mapActivity.getMapView().refreshMap(); - mapActivity.getMapView().refreshMapInternal(param); - mapActivity.getMapView().refreshBaseMapInternal(rotatedTileBox, param); - Bitmap bitmap = mapActivity.getMapView().currentCanvas; - Canvas canvas = new Canvas(bitmap); - OsmandMapLayer.DrawSettings drawSettings = new OsmandMapLayer.DrawSettings( - androidContext.getDaynightHelper().isNightMode(), - true); -// mapActivity.getMapView().drawOverMap(canvas, -// rotatedTileBox, -// drawSettings); - //Bitmap bitmap = Bitmap.createBitmap(512,512,Bitmap.Config.ARGB_8888); - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); - byte[] byteArray = stream.toByteArray(); - ByteArrayInputStream str = new ByteArrayInputStream(byteArray); - return newFixedLengthResponse( - NanoHTTPD.Response.Status.OK, - "image/png", - str, - str.available()); - } - catch (Exception e){ + try { + return tileApiCall(session); + } catch (Exception e) { e.printStackTrace(); } return ErrorResponses.response500; } }; - endpoints.put(tile.uri,tile); + endpoints.put(tile.uri, tile); + } + + ExecutorService executor = Executors.newFixedThreadPool(3); + + Map hashMap = new HashMap<>(); + Map map = Collections.synchronizedMap(hashMap); + + private NanoHTTPD.Response tileApiCall(NanoHTTPD.IHTTPSession session) { + int zoom = 0; + double lat = 0;//50.901430; + double lon = 0;//34.801775; + try { + String fullUri = session.getUri().replace("/tile/", ""); + Scanner s = new Scanner(fullUri).useDelimiter("/"); + zoom = s.nextInt(); + lat = s.nextDouble(); + lon = s.nextDouble(); + } catch (Exception e) { + e.printStackTrace(); + return ErrorResponses.response500; + } + mapActivity.getMapView().setMapImageDrawListener(this); + Future> future; + final RotatedTileBox rotatedTileBox = mapActivity.getMapView().getCurrentRotatedTileBox().copy(); + rotatedTileBox.setZoom(zoom); + rotatedTileBox.setLatLonCenter(lat, lon); + rotatedTileBox.setPixelDimensions(512, 512); + future = executor.submit(new Callable>() { + @Override + public Pair call() throws Exception { + Bitmap bmp; + while((bmp = map.get(rotatedTileBox)) == null) { + Thread.sleep(1000); + } + return Pair.create(rotatedTileBox,bmp); + } + }); + mapActivity.getMapView().setCurrentRotatedTileBox(rotatedTileBox); + try { + Pair pair = future.get(); + Bitmap bitmap = pair.second;// mapActivity.getMapView().currentCanvas; + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); + byte[] byteArray = stream.toByteArray(); + ByteArrayInputStream str = new ByteArrayInputStream(byteArray); + return newFixedLengthResponse( + NanoHTTPD.Response.Status.OK, + "image/png", + str, + str.available()); + } catch (ExecutionException e) { + e.printStackTrace(); + return ErrorResponses.response500; + } catch (InterruptedException e) { + e.printStackTrace(); + return ErrorResponses.response500; + } } public void setAndroidContext(OsmandApplication androidContext) { @@ -132,10 +135,9 @@ public class ApiRouter { uri.contains("/fonts/") || uri.contains("/favicon.ico") ) return getStatic(uri); - if (isApiUrl(uri)){ + if (isApiUrl(uri)) { return routeApi(session); - } - else { + } else { return routeContent(session); } } @@ -143,18 +145,18 @@ public class ApiRouter { private NanoHTTPD.Response routeApi(NanoHTTPD.IHTTPSession session) { String uri = session.getUri(); //TODO rewrite - if (uri.contains("tile")){ + if (uri.contains("tile")) { return endpoints.get("/tile").apiCall.call(session); } ApiEndpoint endpoint = endpoints.get(uri); - if (endpoint != null){ + if (endpoint != null) { return endpoint.apiCall.call(session); } return ErrorResponses.response404; } private boolean isApiUrl(String uri) { - for (String endpoint : endpoints.keySet()){ + for (String endpoint : endpoints.keySet()) { //TODO rewrite contains if (endpoint.equals(uri) || uri.contains("tile")) return true; } @@ -240,13 +242,18 @@ public class ApiRouter { text.append(json); text.append(","); } - return "[" + text.substring(0,text.length()-1) + "]"; + return "[" + text.substring(0, text.length() - 1) + "]"; } private String jsonFromFavorite(FavouritePoint favouritePoint) { return gson.toJson(favouritePoint); } + @Override + public void onDraw(RotatedTileBox viewport, Bitmap bmp) { + this.map.put(viewport,bmp); + } + static class ErrorResponses { static NanoHTTPD.Response response404 = newFixedLengthResponse(NanoHTTPD.Response.Status.NOT_FOUND, diff --git a/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java b/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java index 752481d1bd..4ecc092cd7 100644 --- a/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java +++ b/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java @@ -105,7 +105,6 @@ public class OsmandMapTileView implements IMapDownloaderCallback { protected static final int emptyTileDivisor = 16; - public interface OnTrackBallListener { public boolean onTrackBallEvent(MotionEvent e); } @@ -122,6 +121,10 @@ public class OsmandMapTileView implements IMapDownloaderCallback { public void onDrawOverMap(); } + public interface IMapImageDrawListener { + public void onDraw(RotatedTileBox viewport,Bitmap bmp); + } + protected static final Log LOG = PlatformUtil.getLog(OsmandMapTileView.class); @@ -145,6 +148,8 @@ public class OsmandMapTileView implements IMapDownloaderCallback { private OnTrackBallListener trackBallDelegate; + private IMapImageDrawListener iMapImageDrawListener; + private AccessibilityActionsProvider accessibilityActions; private List layers = new ArrayList<>(); @@ -367,6 +372,14 @@ public class OsmandMapTileView implements IMapDownloaderCallback { return wasZoomInMultiTouch; } + public IMapImageDrawListener getMapImageDrawListener() { + return iMapImageDrawListener; + } + + public void setMapImageDrawListener(IMapImageDrawListener iMapImageDrawListener) { + this.iMapImageDrawListener = iMapImageDrawListener; + } + public boolean mapGestureAllowed(OsmandMapLayer.MapGestureType type) { for (OsmandMapLayer layer : layers) { if (!layer.isMapGestureAllowed(type)) { @@ -518,8 +531,8 @@ public class OsmandMapTileView implements IMapDownloaderCallback { public void restoreMapRatio() { RotatedTileBox box = currentViewport.copy(); - float rx = (float)box.getCenterPixelX() / box.getPixWidth(); - float ry = (float)box.getCenterPixelY() / box.getPixHeight(); + float rx = (float) box.getCenterPixelX() / box.getPixWidth(); + float ry = (float) box.getCenterPixelY() / box.getPixHeight(); if (mapPosition == OsmandSettings.BOTTOM_CONSTANT) { ry -= 0.35; } @@ -616,6 +629,9 @@ public class OsmandMapTileView implements IMapDownloaderCallback { } long end = SystemClock.elapsedRealtime(); additional.calculateFPS(start, end); + if (iMapImageDrawListener != null){ + iMapImageDrawListener.onDraw(tileBox,bufferBitmap); + } } public void refreshMapInternal(DrawSettings drawSettings) { @@ -871,6 +887,20 @@ public class OsmandMapTileView implements IMapDownloaderCallback { return currentViewport; } + public void setCurrentRotatedTileBox(net.osmand.data.RotatedTileBox tileBox) { + float rx = (float) tileBox.getCenterPixelX() / tileBox.getPixWidth(); + float ry = (float) tileBox.getCenterPixelY() / tileBox.getPixHeight(); + if (mapPosition == OsmandSettings.BOTTOM_CONSTANT) { + ry -= 0.35; + } + tileBox.setCenterLocation(rx, ry); + LatLon screenCenter = tileBox.getLatLonFromPixel(tileBox.getPixWidth() / 2f, tileBox.getPixHeight() / 2f); + mapRatioX = 0; + mapRatioY = 0; + setLatLon(screenCenter.getLatitude(), screenCenter.getLongitude()); + currentViewport = tileBox; + } + public float getDensity() { return currentViewport.getDensity(); }