From 27ed10f0782cc6e2a38499edd9c0e4f9390decf6 Mon Sep 17 00:00:00 2001 From: Victor Shcherb Date: Fri, 8 Oct 2010 16:30:26 +0000 Subject: [PATCH] fix zoom issues git-svn-id: https://osmand.googlecode.com/svn/trunk@544 e29c36b1-1cfa-d876-8d93-3434fc2bb7b8 --- .../src/net/osmand/ToDoConstants.java | 4 +- OsmAnd/src/net/osmand/ResourceManager.java | 23 +- OsmAnd/src/net/osmand/RotatedTileBox.java | 158 ++++++ .../net/osmand/activities/MapActivity.java | 4 - .../osmand/render/MapRenderRepositories.java | 355 ++++++------ .../src/net/osmand/render/RendererLayer.java | 83 +-- .../net/osmand/views/ContextMenuLayer.java | 2 +- .../src/net/osmand/views/FavoritesLayer.java | 26 +- OsmAnd/src/net/osmand/views/GPXLayer.java | 26 +- OsmAnd/src/net/osmand/views/MapInfoLayer.java | 12 +- .../net/osmand/views/MultiTouchSupport.java | 2 +- OsmAnd/src/net/osmand/views/OsmBugsLayer.java | 18 +- .../src/net/osmand/views/OsmandMapLayer.java | 3 +- .../net/osmand/views/OsmandMapTileView.java | 527 +++++++++--------- OsmAnd/src/net/osmand/views/POIMapLayer.java | 18 +- .../net/osmand/views/PointLocationLayer.java | 6 +- .../osmand/views/PointNavigationLayer.java | 3 +- .../src/net/osmand/views/RouteInfoLayer.java | 2 +- OsmAnd/src/net/osmand/views/RouteLayer.java | 4 +- .../net/osmand/views/TransportInfoLayer.java | 19 +- .../net/osmand/views/TransportStopsLayer.java | 17 +- .../net/osmand/views/YandexTrafficLayer.java | 13 +- 22 files changed, 694 insertions(+), 631 deletions(-) create mode 100644 OsmAnd/src/net/osmand/RotatedTileBox.java diff --git a/DataExtractionOSM/src/net/osmand/ToDoConstants.java b/DataExtractionOSM/src/net/osmand/ToDoConstants.java index 70915449ab..1b6a88783f 100644 --- a/DataExtractionOSM/src/net/osmand/ToDoConstants.java +++ b/DataExtractionOSM/src/net/osmand/ToDoConstants.java @@ -15,10 +15,8 @@ public class ToDoConstants { // ! 81. Add some objects to POI category (1) to add them into OSM 2) to help navigation) // highway (?), traffic_calming (?), barrier(?), military(?-), landuse (?), office(?), man_made(?), power(?), // railway( station, subway?) - issue 17 - // !_22. Verify all POI has a point_type (in order to search them) + // !_22. Verify all POI has a point_type (in order to search them) - ! - // !_30. Fix rotate (_) - // Outside base 0.4 release // 97. For voice navigation consider current speed of vehicle. Especially when speed > 50 pronounce more than 200 m // 98. Implement rendering of different app mode. For Car render streets name with large font. diff --git a/OsmAnd/src/net/osmand/ResourceManager.java b/OsmAnd/src/net/osmand/ResourceManager.java index c537d17e31..6a04200f0a 100644 --- a/OsmAnd/src/net/osmand/ResourceManager.java +++ b/OsmAnd/src/net/osmand/ResourceManager.java @@ -30,7 +30,6 @@ import org.apache.commons.logging.Log; import android.database.sqlite.SQLiteException; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.graphics.RectF; import android.os.Environment; /** @@ -560,14 +559,14 @@ public class ResourceManager { } ////////////////////////////////////////////// Working with map //////////////////////////////////////////////// - public boolean updateRenderedMapNeeded(RectF tilesRect, int zoom, float rotate){ - return renderer.updateMapIsNeeded(tilesRect, zoom, rotate); + public boolean updateRenderedMapNeeded(RotatedTileBox rotatedTileBox){ + return renderer.updateMapIsNeeded(rotatedTileBox); } - public void updateRendererMap(RectF tileRect, RectF boundsTileRect, int zoom, float rotate){ + public void updateRendererMap(RotatedTileBox rotatedTileBox){ renderer.interruptLoadingMap(); asyncLoadingTiles.requestToLoadMap( - new MapLoadRequest(tileRect, boundsTileRect, zoom, rotate)); + new MapLoadRequest(new RotatedTileBox(rotatedTileBox))); } public MapRenderRepositories getRenderer() { @@ -721,17 +720,11 @@ public class ResourceManager { } private static class MapLoadRequest { - public final RectF tileRect; - public final RectF boundsTileRect; - public final int zoom; - public final float rotate; + public final RotatedTileBox tileBox; - public MapLoadRequest(RectF tileRect, RectF boundsTileRect, int zoom, float rotate) { + public MapLoadRequest(RotatedTileBox tileBox) { super(); - this.tileRect = tileRect; - this.boundsTileRect = boundsTileRect; - this.zoom = zoom; - this.rotate = rotate; + this.tileBox = tileBox; } } @@ -788,7 +781,7 @@ public class ResourceManager { } else if(req instanceof MapLoadRequest){ if(!mapLoaded){ MapLoadRequest r = (MapLoadRequest) req; - renderer.loadMap(r.tileRect, r.boundsTileRect, r.zoom, r.rotate); + renderer.loadMap(r.tileBox); mapLoaded = true; } } diff --git a/OsmAnd/src/net/osmand/RotatedTileBox.java b/OsmAnd/src/net/osmand/RotatedTileBox.java new file mode 100644 index 0000000000..22029ee184 --- /dev/null +++ b/OsmAnd/src/net/osmand/RotatedTileBox.java @@ -0,0 +1,158 @@ +package net.osmand; +import net.osmand.osm.MapUtils; +import android.graphics.PointF; +import android.graphics.RectF; +import android.util.FloatMath; + +public class RotatedTileBox { + private float leftTileX; + private float topTileY; + private float tileWidth; + private float tileHeight; + private float rotate; + private int zoom; + private float rotateCos; + private float rotateSin; + + public RotatedTileBox(float leftTileX, float topTileY, float tileWidth, float tileHeight, float rotate, int zoom) { + set(leftTileX, topTileY, tileWidth, tileHeight, rotate, zoom); + } + + public RotatedTileBox(RotatedTileBox r){ + set(r.leftTileX, r.topTileY, r.tileWidth, r.tileHeight, r.rotate, r.zoom); + } + + private void init() { + float rad = (float) Math.toRadians(this.rotate); + rotateCos = FloatMath.cos(rad); + rotateSin = FloatMath.sin(rad); + } + + public void set(float leftTileX, float topTileY, float tileWidth, float tileHeight, float rotate, int zoom) { + this.leftTileX = leftTileX; + if(rotate < 0){ + rotate += 360; + } else if(rotate > 360){ + rotate -= 360; + } + this.rotate = rotate; + this.tileHeight = tileHeight; + this.tileWidth = tileWidth; + this.topTileY = topTileY; + this.zoom = zoom; + init(); + } + + public float getRotateCos() { + return rotateCos; + } + + public float getRotateSin() { + return rotateSin; + } + + public int getZoom() { + return zoom; + } + + public float getRotate() { + return rotate; + } + + public float getTileHeight() { + return tileHeight; + } + + public float getTileWidth() { + return tileWidth; + } + + public float getLeftTileX() { + return leftTileX; + } + + public float getTopTileY() { + return topTileY; + } + + public boolean containsTileBox(RotatedTileBox box) { + PointF temp = new PointF(); + if(box.getZoom() != zoom){ + throw new UnsupportedOperationException(); + } + box.calcPointTile(0, 0, temp); + if(!containsPoint(temp.x, temp.y)){ + return false; + } + box.calcPointTile(box.tileWidth, 0, temp); + if(!containsPoint(temp.x, temp.y)){ + return false; + } + box.calcPointTile(0, box.tileHeight, temp); + if(!containsPoint(temp.x, temp.y)){ + return false; + } + box.calcPointTile(box.tileWidth, box.tileHeight, temp); + if(!containsPoint(temp.x, temp.y)){ + return false; + } + return true; + } + + public RectF calculateLatLonBox(RectF rectF) { + float tx = calcPointTileX(tileWidth, 0); + float tx2 = calcPointTileX(tileWidth, tileHeight); + float tx3 = calcPointTileX(0, tileHeight); + float minTileX = Math.min(Math.min(leftTileX, tx), Math.min(tx2, tx3)) ; + float maxTileX = Math.max(Math.max(leftTileX, tx), Math.max(tx2, tx3)) ; + + rectF.left = (float) MapUtils.getLongitudeFromTile(zoom, minTileX); + rectF.right = (float) MapUtils.getLongitudeFromTile(zoom, maxTileX); + + float ty = calcPointTileY(tileWidth, 0); + float ty2 = calcPointTileY(tileWidth, tileHeight); + float ty3 = calcPointTileY(0, tileHeight); + + float minTileY = Math.min(Math.min(topTileY, ty), Math.min(ty2, ty3)) ; + float maxTileY = Math.max(Math.max(topTileY, ty), Math.max(ty2, ty3)) ; + + rectF.top = (float) MapUtils.getLatitudeFromTile(zoom, minTileY); + rectF.bottom = (float) MapUtils.getLatitudeFromTile(zoom, maxTileY); + + return rectF; + } + + public boolean containsPoint(float tileX, float tileY) { + tileX -= leftTileX; + tileY -= topTileY; + double tx = rotateCos * tileX - rotateSin * tileY; + double ty = rotateSin * tileX + rotateCos * tileY; + return tx >= 0 && tx <= tileWidth && ty >= 0 && ty <= tileHeight; + } + + protected PointF calcPointTile(float dx, float dy, PointF p){ + float tx = rotateCos * dx + rotateSin * dy + leftTileX; + float ty = - rotateSin * dx + rotateCos * dy + topTileY; + p.set(tx, ty); + return p; + } + + protected float calcPointTileX(float dx, float dy){ + return rotateCos * dx + rotateSin * dy + leftTileX; + } + + protected float calcPointTileY(float dx, float dy){ + return - rotateSin * dx + rotateCos * dy + topTileY; + } + + public float getRightBottomTileX() { + return calcPointTileX(tileWidth, tileHeight); + } + + public float getRightBottomTileY() { + return calcPointTileY(tileWidth, tileHeight); + } + + + +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/activities/MapActivity.java b/OsmAnd/src/net/osmand/activities/MapActivity.java index e60926788d..647a90e704 100644 --- a/OsmAnd/src/net/osmand/activities/MapActivity.java +++ b/OsmAnd/src/net/osmand/activities/MapActivity.java @@ -379,10 +379,6 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso Intent newIntent = new Intent(MapActivity.this, SearchActivity.class); startActivity(newIntent); return true; - } else if (keyCode == KeyEvent.KEYCODE_R) { - // Is it needed - mapView.setRotate(mapView.getRotate() + 45); - return true; } return super.onKeyDown(keyCode, event); } diff --git a/OsmAnd/src/net/osmand/render/MapRenderRepositories.java b/OsmAnd/src/net/osmand/render/MapRenderRepositories.java index a75315ac7a..80071df132 100644 --- a/OsmAnd/src/net/osmand/render/MapRenderRepositories.java +++ b/OsmAnd/src/net/osmand/render/MapRenderRepositories.java @@ -23,6 +23,7 @@ import java.util.Set; import net.osmand.IProgress; import net.osmand.LogUtil; import net.osmand.OsmandSettings; +import net.osmand.RotatedTileBox; import net.osmand.data.index.IndexConstants; import net.osmand.osm.MapRenderObject; import net.osmand.osm.MapRenderingTypes; @@ -35,7 +36,6 @@ import org.apache.commons.logging.Log; import android.content.Context; import android.graphics.Bitmap; import android.graphics.RectF; -import android.util.FloatMath; public class MapRenderRepositories { @@ -47,18 +47,20 @@ public class MapRenderRepositories { private Map pZoom1 = new LinkedHashMap(); private Map pZoom2 = new LinkedHashMap(); private OsmandRenderer renderer; + - private double cTopY; - private double cBottomY; - private double cLeftX; - private double cRightX; - private int cZoom; - private float cRotate; - - // cached objects in order to rotate without + // lat/lon box of requested vector data + private RectF cObjectsBox = new RectF(); + // cached objects in order to render rotation without reloading data from db private List cObjects = new LinkedList(); - private RectF cachedWaysLoc = new RectF(); - private float cachedRotate = 0; + + // currently rendered box (not the same as already rendered) + // this box is checked for interrupted process or + private RotatedTileBox requestedBox = null; + + // location of rendered bitmap + private RotatedTileBox bmpLocation = null; + // already rendered bitmap private Bitmap bmp; private boolean interrupted = false; @@ -179,14 +181,8 @@ public class MapRenderRepositories { return bounds; } - - // if cache was changed different instance will be returned - public RectF getCachedWaysLoc() { - return cachedWaysLoc; - } - - public float getCachedRotate() { - return cachedRotate; + public RotatedTileBox getBitmapLocation() { + return bmpLocation; } protected void closeConnection(Connection c, String file){ @@ -208,32 +204,34 @@ public class MapRenderRepositories { } } - - public boolean updateMapIsNeeded(RectF tileRect, int zoom, float rotate){ - if (connections.isEmpty()) { + + public boolean updateMapIsNeeded(RotatedTileBox box){ + if (connections.isEmpty() || box == null) { return false; } - boolean inside = insideBox(tileRect.top, tileRect.left, tileRect.bottom, tileRect.right, zoom); - if(rotate < 0){ - rotate += 360; - } - - return !inside || Math.abs(rotate - cRotate) > 45; // leave only 15 to find that UI box out of searched + if(requestedBox == null){ + return true; + } + if(requestedBox.getZoom() != box.getZoom()){ + return true; + } + float deltaRotate = requestedBox.getRotate() - box.getRotate(); + if(deltaRotate > 180){ + deltaRotate -= 360; + } else if(deltaRotate < -180){ + deltaRotate += 360; + } + if(Math.abs(deltaRotate) > 25){ + return true; + } + return !requestedBox.containsTileBox(box); } public boolean isEmpty(){ return connections.isEmpty(); } - -// MapUtils.getLatitudeFromTile(17, topY) - private boolean insideBox(double topY, double leftX, double bottomY, double rightX, int zoom) { - boolean inside = cZoom == zoom && cTopY <= topY && cLeftX <= leftX && cRightX >= rightX - && cBottomY >= bottomY; - return inside; - } - - + private static String loadMapQuery = "SELECT "+IndexConstants.IndexMapRenderObject.ID +", " + IndexConstants.IndexMapRenderObject.NODES +", " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ IndexConstants.IndexMapRenderObject.NAME + ", " + IndexConstants.IndexMapRenderObject.TYPE + //$NON-NLS-1$ " FROM " + IndexConstants.IndexMapRenderObject.getTable() + " WHERE "+IndexConstants.IndexMapRenderObject.ID+ //$NON-NLS-1$//$NON-NLS-2$ @@ -261,151 +259,167 @@ public class MapRenderRepositories { } private boolean checkWhetherInterrupted(){ - if(interrupted){ - // clear zoom to enable refreshing next time - cZoom = 1; + if(interrupted || (currentRenderingContext != null && currentRenderingContext.interrupted)){ + requestedBox = bmpLocation; return true; } return false; } - public synchronized void loadMap(RectF tileRect, RectF boundsTileRect, int zoom, float rotate) { - interrupted = false; - // currently doesn't work properly (every rotate bounds will be outside) - boolean inside = insideBox(boundsTileRect.top, boundsTileRect.left, boundsTileRect.bottom, boundsTileRect.right, zoom); - cRotate = rotate < 0 ? rotate + 360 : rotate; - if (!inside) { - cTopY = boundsTileRect.top; - cLeftX = boundsTileRect.left; - cRightX = boundsTileRect.right; - cBottomY = boundsTileRect.bottom; - double cBottomLatitude = MapUtils.getLatitudeFromTile(zoom, cBottomY); - double cTopLatitude = MapUtils.getLatitudeFromTile(zoom, cTopY); - double cLeftLongitude = MapUtils.getLongitudeFromTile(zoom, cLeftX); - double cRightLongitude = MapUtils.getLongitudeFromTile(zoom, cRightX); - cZoom = zoom; + private boolean loadVectorData(RectF dataBox, int zoom){ + double cBottomLatitude = dataBox.bottom; + double cTopLatitude = dataBox.top; + double cLeftLongitude = dataBox.left; + double cRightLongitude = dataBox.right; - log.info(String.format("BLat=%s, TLat=%s, LLong=%s, RLong=%s, zoom=%s", //$NON-NLS-1$ - cBottomLatitude, cTopLatitude, cLeftLongitude, cRightLongitude, cZoom)); - - long now = System.currentTimeMillis(); + log.info(String.format("BLat=%s, TLat=%s, LLong=%s, RLong=%s, zoom=%s", //$NON-NLS-1$ + cBottomLatitude, cTopLatitude, cLeftLongitude, cRightLongitude, zoom)); + long now = System.currentTimeMillis(); + if (connections.isEmpty()) { + cObjectsBox = dataBox; + cObjects = new ArrayList(); + return true; + } + try { + int count = 0; + ArrayList tempList = new ArrayList(); + System.gc(); // to clear previous objects +// Set ids = new HashSet(1000); + TLongSet ids = new TLongHashSet(); + Map> multiPolygons = new LinkedHashMap>(); + for (Connection c : connections.keySet()) { + RectF r = connections.get(c); + boolean intersects = r.top >= cBottomLatitude && r.left <= cRightLongitude && r.right >= cLeftLongitude && + r.bottom <= cTopLatitude; + if(!intersects){ + continue; + } - if (connections.isEmpty()) { - cObjects = new ArrayList(); - // keep old results + PreparedStatement statement = null; + if (zoom >= 15) { + statement = pZoom0.get(c); + } else if (zoom >= 10) { + statement = pZoom1.get(c); + } else if (zoom >= 6) { + statement = pZoom2.get(c); + } else { + // TODO show raster tiles ? + continue; + } + statement.setDouble(1, cBottomLatitude); + statement.setDouble(2, cTopLatitude); + statement.setDouble(3, cLeftLongitude); + statement.setDouble(4, cRightLongitude); + ResultSet result = statement.executeQuery(); + + + try { + while (result.next()) { + long id = result.getLong(1); + if (PerformanceFlags.checkForDuplicateObjectIds) { + if (ids.contains(id)) { + // do not add object twice + continue; + } + ids.add(id); + } + int type = result.getInt(4); + MapRenderObject obj = new MapRenderObject(id); + obj.setType(type); + obj.setData(result.getBytes(2)); + obj.setName(result.getString(3)); + + count++; + int mainType = obj.getMainType(); + // be attentive we need 16 bits from main type (not 15 bits!) + // the last bit shows direction of multipolygon way + registerMultipolygon(multiPolygons, mainType, obj); + int sec = obj.getSecondType(); + if(sec != 0){ + registerMultipolygon(multiPolygons, sec, obj); + } + for (int k = 0; k < obj.getMultiTypes(); k++) { + registerMultipolygon(multiPolygons, obj.getAdditionalType(k), obj); + } + if(checkWhetherInterrupted()){ + return false; + } + tempList.add(obj); + } + + } finally { + result.close(); + } + } + int leftX = MapUtils.get31TileNumberX(cLeftLongitude); + int rightX = MapUtils.get31TileNumberX(cRightLongitude); + int bottomY = MapUtils.get31TileNumberY(cBottomLatitude); + int topY = MapUtils.get31TileNumberY(cTopLatitude); + List pMulti = proccessMultiPolygons(multiPolygons, leftX, rightX, bottomY, topY); + tempList.addAll(pMulti); + log.info(String.format("Search has been done in %s ms. %s results were found.", System.currentTimeMillis() - now, count)); //$NON-NLS-1$ + + cObjects = tempList; + cObjectsBox = dataBox; + } catch (java.sql.SQLException e) { + log.debug("Search failed", e); //$NON-NLS-1$ + return false; + } + + return true; + } + + + public synchronized void loadMap(RotatedTileBox tileRect) { + interrupted = false; + if(currentRenderingContext != null){ + currentRenderingContext = null; + } + // prevent editing + requestedBox = new RotatedTileBox(tileRect); + + // calculate data box + RectF dataBox = requestedBox.calculateLatLonBox(new RectF()); + if (cObjectsBox.left > dataBox.left || cObjectsBox.top > dataBox.top || + cObjectsBox.right < dataBox.right || cObjectsBox.bottom < dataBox.bottom) { + // increase data box in order for rotate + if ((dataBox.right - dataBox.left) > (dataBox.top - dataBox.bottom)) { + double wi = (dataBox.right - dataBox.left) * .2; + dataBox.left -= wi; + dataBox.right += wi; + } else { + double hi = (dataBox.bottom - dataBox.top) * .2; + dataBox.top -= hi; + dataBox.bottom += hi; + } + boolean loaded = loadVectorData(dataBox, requestedBox.getZoom()); + if(!loaded || checkWhetherInterrupted()){ return; } - try { - int count = 0; - cObjects = new ArrayList(); - System.gc(); // to clear previous objects -// Set ids = new HashSet(1000); - TLongSet ids = new TLongHashSet(); - Map> multiPolygons = new LinkedHashMap>(); - for (Connection c : connections.keySet()) { - RectF r = connections.get(c); - boolean intersects = r.top >= cBottomLatitude && r.left <= cRightLongitude && r.right >= cLeftLongitude && - r.bottom <= cTopLatitude; - if(!intersects){ - continue; - } - - PreparedStatement statement = null; - if (zoom >= 15) { - statement = pZoom0.get(c); - } else if (zoom >= 10) { - statement = pZoom1.get(c); - } else if (zoom >= 6) { - statement = pZoom2.get(c); - } else { - // TODO show tiles ? - continue; - } - statement.setDouble(1, cBottomLatitude); - statement.setDouble(2, cTopLatitude); - statement.setDouble(3, cLeftLongitude); - statement.setDouble(4, cRightLongitude); - ResultSet result = statement.executeQuery(); - - - try { - while (result.next()) { - long id = result.getLong(1); - if (PerformanceFlags.checkForDuplicateObjectIds) { - if (ids.contains(id)) { - // do not add object twice - continue; - } - ids.add(id); - } - int type = result.getInt(4); - MapRenderObject obj = new MapRenderObject(id); - obj.setType(type); - obj.setData(result.getBytes(2)); - obj.setName(result.getString(3)); - - count++; - int mainType = obj.getMainType(); - // be attentive we need 16 bits from main type (not 15 bits!) - // the last bit shows direction of multipolygon way - registerMultipolygon(multiPolygons, mainType, obj); - int sec = obj.getSecondType(); - if(sec != 0){ - registerMultipolygon(multiPolygons, sec, obj); - } - for (int k = 0; k < obj.getMultiTypes(); k++) { - registerMultipolygon(multiPolygons, obj.getAdditionalType(k), obj); - } - if(checkWhetherInterrupted()){ - return; - } - cObjects.add(obj); - } - - } finally { - result.close(); - } - } - int leftX = MapUtils.get31TileNumberX(cLeftLongitude); - int rightX = MapUtils.get31TileNumberX(cRightLongitude); - int bottomY = MapUtils.get31TileNumberY(cBottomLatitude); - int topY = MapUtils.get31TileNumberY(cTopLatitude); - List pMulti = proccessMultiPolygons(multiPolygons, leftX, rightX, bottomY, topY); - if(checkWhetherInterrupted()){ - return; - } - cObjects.addAll(pMulti); - log.info(String - .format("Search has been done in %s ms. %s results were found.", System.currentTimeMillis() - now, count)); //$NON-NLS-1$ - } catch (java.sql.SQLException e) { - log.debug("Search failed", e); //$NON-NLS-1$ - } } - // create new instance to distinguish that cache was changed - RectF newLoc = new RectF((float)MapUtils.getLongitudeFromTile(zoom, tileRect.left), (float)MapUtils.getLatitudeFromTile(zoom, tileRect.top), - (float)MapUtils.getLongitudeFromTile(zoom, tileRect.right), (float)MapUtils.getLatitudeFromTile(zoom, tileRect.bottom)); - - int width = (int) calcDiffPixelX(cRotate, tileRect.right - tileRect.left, tileRect.bottom - tileRect.top); - int height = (int) calcDiffPixelY(cRotate, tileRect.right - tileRect.left, tileRect.bottom - tileRect.top); currentRenderingContext = new OsmandRenderer.RenderingContext(); - currentRenderingContext.leftX = tileRect.left; - currentRenderingContext.topY = tileRect.top; - currentRenderingContext.zoom = cZoom; - currentRenderingContext.rotate = cRotate; - currentRenderingContext.width = width; - currentRenderingContext.height = height; - Bitmap bmp = renderer.generateNewBitmap(currentRenderingContext, cObjects, OsmandSettings.usingEnglishNames(context)); - if(currentRenderingContext.interrupted){ - cZoom = 1; + currentRenderingContext.leftX = (float) requestedBox.getLeftTileX(); + currentRenderingContext.topY = (float) requestedBox.getTopTileY(); + currentRenderingContext.zoom = requestedBox.getZoom(); + currentRenderingContext.rotate = requestedBox.getRotate(); + currentRenderingContext.width = (int) (requestedBox.getTileWidth() * OsmandRenderer.TILE_SIZE); + currentRenderingContext.height = (int) (requestedBox.getTileHeight() * OsmandRenderer.TILE_SIZE); + if(checkWhetherInterrupted()){ return; } + + Bitmap bmp = renderer.generateNewBitmap(currentRenderingContext, cObjects, OsmandSettings.usingEnglishNames(context)); + if(checkWhetherInterrupted()){ + currentRenderingContext = null; + return; + } + currentRenderingContext = null; Bitmap oldBmp = this.bmp; this.bmp = bmp; - cachedWaysLoc = newLoc; - cachedRotate = cRotate; + this.bmpLocation = tileRect; if(oldBmp != null){ oldBmp.recycle(); } @@ -416,24 +430,15 @@ public class MapRenderRepositories { return bmp; } - public float calcDiffPixelX(float rotate, float dTileX, float dTileY){ - float rad = (float) Math.toRadians(rotate); - return (FloatMath.cos(rad) * dTileX - FloatMath.sin(rad) * dTileY) * OsmandRenderer.TILE_SIZE; - } - - public float calcDiffPixelY(float rotate, float dTileX, float dTileY){ - float rad = (float) Math.toRadians(rotate); - return (FloatMath.sin(rad) * dTileX + FloatMath.cos(rad) * dTileY) * OsmandRenderer.TILE_SIZE; - } public synchronized void clearCache() { cObjects.clear(); - cBottomY = cLeftX = cRightX = cTopY = cRotate = cZoom = 0; + cObjectsBox = new RectF(); if(bmp != null){ bmp.recycle(); bmp = null; } - cachedWaysLoc = new RectF(); + requestedBox = bmpLocation = null; } diff --git a/OsmAnd/src/net/osmand/render/RendererLayer.java b/OsmAnd/src/net/osmand/render/RendererLayer.java index 222f5ede64..f3ac0266d3 100644 --- a/OsmAnd/src/net/osmand/render/RendererLayer.java +++ b/OsmAnd/src/net/osmand/render/RendererLayer.java @@ -1,6 +1,7 @@ package net.osmand.render; import net.osmand.ResourceManager; +import net.osmand.RotatedTileBox; import net.osmand.osm.MapUtils; import net.osmand.views.OsmandMapLayer; import net.osmand.views.OsmandMapTileView; @@ -10,14 +11,13 @@ import android.graphics.Paint; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; -import android.util.FloatMath; public class RendererLayer implements OsmandMapLayer { private OsmandMapTileView view; private final static int startZoom = 5; private Rect pixRect = new Rect(); - private RectF tileRect = new RectF(); + private RotatedTileBox rotatedTileBox = new RotatedTileBox(0, 0, 0, 0, 0, 0); private ResourceManager resourceManager; private Paint paintImg; @@ -41,42 +41,55 @@ public class RendererLayer implements OsmandMapLayer { paintImg = new Paint(); paintImg.setFilterBitmap(true); } + + private void updateRotatedTileBox(){ + float ts = view.getTileSize(); + float xL = view.calcDiffTileX(pixRect.left - view.getCenterPointX(), pixRect.top - view.getCenterPointY()) + view.getXTile(); + float yT = view.calcDiffTileY(pixRect.left - view.getCenterPointX(), pixRect.top - view.getCenterPointY()) + view.getYTile(); + rotatedTileBox.set(xL, yT, ((float) pixRect.width()) / ts, ((float) pixRect.height()) / ts, view.getRotate(), view.getZoom()); + } + + @Override - public void onDraw(Canvas canvas) { + public void onDraw(Canvas canvas, RectF latLonBounds) { if (view.getZoom() >= startZoom && visible) { - pixRect.set(0, 0, view.getWidth(), view.getHeight()); - view.calculateTileRectangle(pixRect, view.getCenterPointX(), - view.getCenterPointY(), view.getXTile(), view.getYTile(), tileRect); - if (view.getFloatZoom() == view.getZoom() - && resourceManager.updateRenderedMapNeeded(tileRect, view.getZoom(), view.getRotate())) { - pixRect.set(-view.getWidth(), -view.getHeight()/2, 2*view.getWidth(), 3*view.getHeight()/2); - view.calculateTileRectangle(pixRect, view.getCenterPointX(), - view.getCenterPointY(), view.getXTile(), view.getYTile(), tileRect); - float xL = view.calcDiffTileX(pixRect.left - view.getCenterPointX(), pixRect.top - view.getCenterPointY()) + view.getXTile(); - float xR = view.calcDiffTileX(pixRect.right - view.getCenterPointX(), pixRect.bottom - view.getCenterPointY()) + view.getXTile(); - float yT = view.calcDiffTileY(pixRect.left - view.getCenterPointX(), pixRect.top - view.getCenterPointY())+ view.getYTile(); - float yB = view.calcDiffTileY(pixRect.right - view.getCenterPointX(), pixRect.bottom - view.getCenterPointY()) + view.getYTile(); - RectF verticesRect = new RectF(xL, yT, xR, yB); - resourceManager.updateRendererMap(verticesRect, tileRect, view.getZoom(), view.getRotate()); + if (!view.isZooming()){ + pixRect.set(0, 0, view.getWidth(), view.getHeight()); + updateRotatedTileBox(); + if(resourceManager.updateRenderedMapNeeded(rotatedTileBox)){ + pixRect.set(-view.getWidth(), -view.getHeight() / 2, 2 * view.getWidth(), 3 * view.getHeight() / 2); + updateRotatedTileBox(); + resourceManager.updateRendererMap(rotatedTileBox); + } } MapRenderRepositories renderer = resourceManager.getRenderer(); Bitmap bmp = renderer.getBitmap(); - if (renderer != null && bmp != null) { - RectF newLoc = renderer.getCachedWaysLoc(); - float rot = renderer.getCachedRotate(); - float leftX1 = (float) MapUtils.getTileNumberX(view.getFloatZoom(), newLoc.left); - float rightX1 = (float) MapUtils.getTileNumberX(view.getFloatZoom(), newLoc.right); - float topY1 = (float) MapUtils.getTileNumberY(view.getFloatZoom(), newLoc.top); - float bottomY1 = (float) MapUtils.getTileNumberY(view.getFloatZoom(), newLoc.bottom); - - float x1 = calcDiffPixelX(rot, leftX1 - view.getXTile(), topY1 - view.getYTile()) + view.getCenterPointX(); - float y1 = calcDiffPixelY(rot, leftX1 - view.getXTile(), topY1 - view.getYTile()) + view.getCenterPointY(); - float x2 = calcDiffPixelX(rot, rightX1 - view.getXTile(), bottomY1 - view.getYTile()) + view.getCenterPointX(); - float y2 = calcDiffPixelY(rot, rightX1 - view.getXTile(), bottomY1 - view.getYTile()) + view.getCenterPointY(); + RotatedTileBox bmpLoc = renderer.getBitmapLocation(); + if (bmp != null && bmpLoc != null) { + float rot = bmpLoc.getRotate(); + float mult = (float) MapUtils.getPowZoom(view.getZoom() - bmpLoc.getZoom()); + + float tx = view.getXTile(); + float ty = view.getYTile(); + float dleftX1 = (bmpLoc.getLeftTileX() * mult - tx) ; + float dtopY1 = (bmpLoc.getTopTileY() * mult - ty); + + + float cos = bmpLoc.getRotateCos(); + float sin = bmpLoc.getRotateSin(); + float x1 = MapUtils.calcDiffPixelX(sin, cos, dleftX1, dtopY1, view.getTileSize()) + view.getCenterPointX(); + float y1 = MapUtils.calcDiffPixelY(sin, cos, dleftX1, dtopY1, view.getTileSize()) + view.getCenterPointY(); + + /*float drightX1 = (bmpLoc.getRightBottomTileX() * mult - tx) ; + float dbottomY1 = (bmpLoc.getRightBottomTileY() * mult - ty); + float x2 = MapUtils.calcDiffPixelX(sin, cos, drightX1, dbottomY1, view.getTileSize()) + view.getCenterPointX(); + float y2 = MapUtils.calcDiffPixelY(sin, cos, drightX1, dbottomY1, view.getTileSize()) + view.getCenterPointY(); + destImage.set(x1, y1, x2, y2);*/ + canvas.rotate(-rot, view.getCenterPointX(), view.getCenterPointY()); - destImage.set(x1, y1, x2, y2); + destImage.set(x1, y1, x1 + bmpLoc.getTileWidth() * mult * view.getTileSize(), y1 + bmpLoc.getTileHeight() * mult * view.getTileSize()); if(!bmp.isRecycled()){ canvas.drawBitmap(bmp, null, destImage, paintImg); } @@ -84,16 +97,6 @@ public class RendererLayer implements OsmandMapLayer { } } - - public float calcDiffPixelX(float rotate, float dTileX, float dTileY){ - float rad = (float) Math.toRadians(rotate); - return (FloatMath.cos(rad) * dTileX - FloatMath.sin(rad) * dTileY) * view.getTileSize(); - } - - public float calcDiffPixelY(float rotate, float dTileX, float dTileY){ - float rad = (float) Math.toRadians(rotate); - return (FloatMath.sin(rad) * dTileX + FloatMath.cos(rad) * dTileY) * view.getTileSize() ; - } public void setVisible(boolean visible) { this.visible = visible; view.refreshMap(); diff --git a/OsmAnd/src/net/osmand/views/ContextMenuLayer.java b/OsmAnd/src/net/osmand/views/ContextMenuLayer.java index bb3b23fcb7..f8aee9f3d6 100644 --- a/OsmAnd/src/net/osmand/views/ContextMenuLayer.java +++ b/OsmAnd/src/net/osmand/views/ContextMenuLayer.java @@ -93,7 +93,7 @@ public class ContextMenuLayer implements OsmandMapLayer { } @Override - public void onDraw(Canvas canvas) { + public void onDraw(Canvas canvas, RectF latLonBounds) { if(latLon != null){ int x = view.getMapXForPoint(latLon.getLongitude()); int y = view.getMapYForPoint(latLon.getLatitude()); diff --git a/OsmAnd/src/net/osmand/views/FavoritesLayer.java b/OsmAnd/src/net/osmand/views/FavoritesLayer.java index c8e0bd3999..627c8b3c8d 100644 --- a/OsmAnd/src/net/osmand/views/FavoritesLayer.java +++ b/OsmAnd/src/net/osmand/views/FavoritesLayer.java @@ -7,7 +7,6 @@ import net.osmand.activities.FavouritesActivity; import net.osmand.activities.FavouritesActivity.FavouritePoint; import net.osmand.activities.FavouritesActivity.FavouritesDbHelper; import net.osmand.osm.LatLon; -import net.osmand.osm.MapUtils; import android.content.Context; import android.content.DialogInterface.OnClickListener; import android.graphics.Canvas; @@ -15,7 +14,6 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PointF; -import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Paint.Style; import android.util.DisplayMetrics; @@ -31,8 +29,6 @@ public class FavoritesLayer implements OsmandMapLayer, ContextMenuLayer.IContext private OsmandMapTileView view; private List favouritePoints; private List additionalPoints; - private Rect pixRect = new Rect(); - private RectF tileRect = new RectF(); private Path path; private Path pathDst; private Paint paint; @@ -76,7 +72,6 @@ public class FavoritesLayer implements OsmandMapLayer, ContextMenuLayer.IContext paintBlack.setAntiAlias(true); paintBlack.setStrokeWidth(2); - pixRect.set(0, 0, view.getWidth(), view.getHeight()); reloadFavorites(view.getContext()); } @@ -99,22 +94,17 @@ public class FavoritesLayer implements OsmandMapLayer, ContextMenuLayer.IContext public boolean drawInScreenPixels() { return false; } - + + @Override - public void onDraw(Canvas canvas) { + public void onDraw(Canvas canvas, RectF latLonBounds) { if (view.getZoom() >= startZoom) { - pixRect.set(0, 0, view.getWidth(), view.getHeight()); - view.calculateTileRectangle(pixRect, view.getCenterPointX(), view.getCenterPointY(), view.getXTile(), view.getYTile(), - tileRect); - double topLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.top); - double leftLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.left); - double bottomLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.bottom); - double rightLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.right); + // request to load for (FavouritePoint o : favouritePoints) { - if (o.getLatitude() <= topLatitude && o.getLatitude() >= bottomLatitude && o.getLongitude() >= leftLongitude - && o.getLongitude() <= rightLongitude) { + if (o.getLatitude() >= latLonBounds.bottom && o.getLatitude() <= latLonBounds.top && o.getLongitude() >= latLonBounds.left + && o.getLongitude() <= latLonBounds.right ) { int x = view.getMapXForPoint(o.getLongitude()); int y = view.getMapYForPoint(o.getLatitude()); matrix.setTranslate(x, y); @@ -125,8 +115,8 @@ public class FavoritesLayer implements OsmandMapLayer, ContextMenuLayer.IContext } if(additionalPoints != null){ for (FavouritePoint o : additionalPoints) { - if (o.getLatitude() <= topLatitude && o.getLatitude() >= bottomLatitude && o.getLongitude() >= leftLongitude - && o.getLongitude() <= rightLongitude) { + if (o.getLatitude() >= latLonBounds.bottom && o.getLatitude() <= latLonBounds.top && o.getLongitude() >= latLonBounds.left + && o.getLongitude() <= latLonBounds.right ) { int x = view.getMapXForPoint(o.getLongitude()); int y = view.getMapYForPoint(o.getLatitude()); matrix.setTranslate(x, y); diff --git a/OsmAnd/src/net/osmand/views/GPXLayer.java b/OsmAnd/src/net/osmand/views/GPXLayer.java index c0134a4a6d..cf245f4c96 100644 --- a/OsmAnd/src/net/osmand/views/GPXLayer.java +++ b/OsmAnd/src/net/osmand/views/GPXLayer.java @@ -3,13 +3,11 @@ package net.osmand.views; import java.util.ArrayList; import java.util.List; -import net.osmand.osm.MapUtils; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PointF; -import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Paint.Cap; import android.graphics.Paint.Join; @@ -21,8 +19,6 @@ public class GPXLayer implements OsmandMapLayer { private OsmandMapTileView view; - private Rect boundsRect; - private RectF tileRect; private List> points = new ArrayList>(); private Paint paint; @@ -34,8 +30,6 @@ public class GPXLayer implements OsmandMapLayer { private void initUI() { - boundsRect = new Rect(0, 0, view.getWidth(), view.getHeight()); - tileRect = new RectF(); paint = new Paint(); paint.setColor(Color.argb(180, 160, 10, 215)); paint.setStyle(Style.STROKE); @@ -56,20 +50,10 @@ public class GPXLayer implements OsmandMapLayer { @Override - public void onDraw(Canvas canvas) { - + public void onDraw(Canvas canvas, RectF latLonBounds) { if(points.isEmpty()){ return; } - int w = view.getWidth(); - int h = view.getHeight(); - boundsRect = new Rect(0, 0, w, h); - view.calculateTileRectangle(boundsRect, view.getCenterPointX(), view.getCenterPointY(), view.getXTile(), view.getYTile(), - tileRect); - double topLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.top); - double leftLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.left); - double bottomLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.bottom); - double rightLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.right); for (List l : points) { path.rewind(); @@ -79,12 +63,12 @@ public class GPXLayer implements OsmandMapLayer { for (int i = 0; i < l.size(); i++) { Location ls = l.get(i); if (startIndex == -1) { - if (leftLongitude <= ls.getLongitude() && ls.getLongitude() <= rightLongitude && bottomLatitude <= ls.getLatitude() - && ls.getLatitude() <= topLatitude) { + if (ls.getLatitude() >= latLonBounds.bottom && ls.getLatitude() <= latLonBounds.top && ls.getLongitude() >= latLonBounds.left + && ls.getLongitude() <= latLonBounds.right ) { startIndex = i > 0 ? i - 1 : i; } - } else if (!(leftLongitude <= ls.getLongitude() + 0.01 && ls.getLongitude() - 0.01 <= rightLongitude - && bottomLatitude <= ls.getLatitude() + 0.01 && ls.getLatitude() - 0.01 <= topLatitude)) { + } else if (!(latLonBounds.left <= ls.getLongitude() + 0.01 && ls.getLongitude() - 0.01 <= latLonBounds.right + && latLonBounds.bottom <= ls.getLatitude() + 0.01 && ls.getLatitude() - 0.01 <= latLonBounds.top)) { endIndex = i; // do not continue make method more efficient (because it calls in UI thread) // this break also has logical sense ! diff --git a/OsmAnd/src/net/osmand/views/MapInfoLayer.java b/OsmAnd/src/net/osmand/views/MapInfoLayer.java index 1a605370a7..50604ba047 100644 --- a/OsmAnd/src/net/osmand/views/MapInfoLayer.java +++ b/OsmAnd/src/net/osmand/views/MapInfoLayer.java @@ -190,7 +190,7 @@ public class MapInfoLayer implements OsmandMapLayer { } @Override - public void onDraw(Canvas canvas) { + public void onDraw(Canvas canvas, RectF latlonBounds) { // prepare data (left distance, speed) if(map.getPointToNavigate() != null){ int d = 0; @@ -287,18 +287,18 @@ public class MapInfoLayer implements OsmandMapLayer { // update cache - if (view.getFloatZoom() != (int) view.getFloatZoom()) { + if (view.isZooming()) { rulerDistName = null; - } else if(view.getFloatZoom() != rulerCZoom || + } else if(view.getZoom() != rulerCZoom || Math.abs(view.getXTile() - rulerCTileX) + Math.abs(view.getYTile() - rulerCTileY) > 1){ - rulerCZoom = (int) view.getFloatZoom(); + rulerCZoom = view.getZoom(); rulerCTileX = view.getXTile(); rulerCTileY = view.getYTile(); double latitude = view.getLatitude(); double tileNumberLeft = rulerCTileX - ((double) view.getWidth()) / (2d * view.getTileSize()); double tileNumberRight = rulerCTileX + ((double) view.getWidth()) / (2d * view.getTileSize()); - double dist = MapUtils.getDistance(latitude, MapUtils.getLongitudeFromTile(view.getFloatZoom(), tileNumberLeft), latitude, - MapUtils.getLongitudeFromTile(view.getFloatZoom(), tileNumberRight)); + double dist = MapUtils.getDistance(latitude, MapUtils.getLongitudeFromTile(view.getZoom(), tileNumberLeft), latitude, + MapUtils.getLongitudeFromTile(view.getZoom(), tileNumberRight)); dist *= screenPercent; int baseDist = 5; diff --git a/OsmAnd/src/net/osmand/views/MultiTouchSupport.java b/OsmAnd/src/net/osmand/views/MultiTouchSupport.java index 6b072f01d7..c4df2dbaf8 100644 --- a/OsmAnd/src/net/osmand/views/MultiTouchSupport.java +++ b/OsmAnd/src/net/osmand/views/MultiTouchSupport.java @@ -91,7 +91,7 @@ public class MultiTouchSupport { Float x2 = (Float) getX.invoke(event, 1); Float y1 = (Float) getY.invoke(event, 0); Float y2 = (Float) getY.invoke(event, 1); - float distance = FloatMath.sqrt((x2 - x1)*(x2 -x1) + (y2-y1)*(y2-y1)); + float distance = FloatMath.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); previousZoom = distance / zoomStartedDistance; if (actionCode == ACTION_POINTER_DOWN) { centerPoint = new PointF((x1 + x2) / 2, (y1 + y2) / 2); diff --git a/OsmAnd/src/net/osmand/views/OsmBugsLayer.java b/OsmAnd/src/net/osmand/views/OsmBugsLayer.java index 0a36f27432..e38176a76d 100644 --- a/OsmAnd/src/net/osmand/views/OsmBugsLayer.java +++ b/OsmAnd/src/net/osmand/views/OsmBugsLayer.java @@ -15,7 +15,6 @@ import net.osmand.LogUtil; import net.osmand.OsmandSettings; import net.osmand.R; import net.osmand.osm.LatLon; -import net.osmand.osm.MapUtils; import org.apache.commons.logging.Log; @@ -29,7 +28,6 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PointF; -import android.graphics.Rect; import android.graphics.RectF; import android.os.Handler; import android.os.Looper; @@ -49,8 +47,6 @@ public class OsmBugsLayer implements OsmandMapLayer, ContextMenuLayer.IContextMe private OsmandMapTileView view; private Handler handlerToLoop; - private Rect pixRect = new Rect(); - private RectF tileRect = new RectF(); private List objects = new ArrayList(); private Paint pointClosedUI; @@ -98,7 +94,6 @@ public class OsmBugsLayer implements OsmandMapLayer, ContextMenuLayer.IContextMe pointClosedUI.setColor(Color.GREEN); pointClosedUI.setAlpha(200); pointClosedUI.setAntiAlias(true); - pixRect.set(0, 0, view.getWidth(), view.getHeight()); } @Override @@ -122,19 +117,10 @@ public class OsmBugsLayer implements OsmandMapLayer, ContextMenuLayer.IContextMe } @Override - public void onDraw(Canvas canvas) { + public void onDraw(Canvas canvas, RectF latLonBounds) { if (view.getZoom() >= startZoom) { - pixRect.set(0, 0, view.getWidth(), view.getHeight()); - view.calculateTileRectangle(pixRect, view.getCenterPointX(), - view.getCenterPointY(), view.getXTile(), view.getYTile(), tileRect); - double topLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.top); - double leftLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.left); - double bottomLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.bottom); - double rightLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.right); - - // request to load - requestToLoad(topLatitude, leftLongitude, bottomLatitude, rightLongitude, view.getZoom()); + requestToLoad(latLonBounds.top, latLonBounds.left, latLonBounds.bottom, latLonBounds.right, view.getZoom()); for (OpenStreetBug o : objects) { int x = view.getMapXForPoint(o.getLongitude()); int y = view.getMapYForPoint(o.getLatitude()); diff --git a/OsmAnd/src/net/osmand/views/OsmandMapLayer.java b/OsmAnd/src/net/osmand/views/OsmandMapLayer.java index f6e09451b5..560a28da81 100644 --- a/OsmAnd/src/net/osmand/views/OsmandMapLayer.java +++ b/OsmAnd/src/net/osmand/views/OsmandMapLayer.java @@ -2,13 +2,14 @@ package net.osmand.views; import android.graphics.Canvas; import android.graphics.PointF; +import android.graphics.RectF; public interface OsmandMapLayer { public void initLayer(OsmandMapTileView view); - public void onDraw(Canvas canvas); + public void onDraw(Canvas canvas, RectF latlonRect); public void destroyLayer(); diff --git a/OsmAnd/src/net/osmand/views/OsmandMapTileView.java b/OsmAnd/src/net/osmand/views/OsmandMapTileView.java index a76e50d41f..6453af0186 100644 --- a/OsmAnd/src/net/osmand/views/OsmandMapTileView.java +++ b/OsmAnd/src/net/osmand/views/OsmandMapTileView.java @@ -1,6 +1,5 @@ package net.osmand.views; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -45,69 +44,72 @@ import android.view.GestureDetector.OnDoubleTapListener; import android.view.GestureDetector.OnGestureListener; import android.view.SurfaceHolder.Callback; -public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCallback, - Callback, AnimateDraggingCallback, OnGestureListener, OnDoubleTapListener, MultiTouchZoomListener { +public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCallback, Callback, AnimateDraggingCallback, OnGestureListener, + OnDoubleTapListener, MultiTouchZoomListener { public static final int OVERZOOM_IN = 2; - + protected final int emptyTileDivisor = 16; - - public interface OnTrackBallListener{ + + public interface OnTrackBallListener { public boolean onTrackBallEvent(MotionEvent e); } - + public interface OnLongClickListener { public boolean onLongPressEvent(PointF point); } + public interface OnClickListener { public boolean onPressEvent(PointF point); } - - + protected static final Log log = LogUtil.getLog(OsmandMapTileView.class); /** * zoom level - could be float to show zoomed tiles */ private float zoom = 3; - + private double longitude = 0d; private double latitude = 0d; - + private float rotate = 0; + private float rotateSin = 0; + private float rotateCos = 1; + private int mapPosition; - + private boolean showMapPosition = true; - - // name of source map + + // name of source map private ITileSource map = null; - + private IMapLocationListener locationListener; - + private OnLongClickListener onLongClickListener; - + private OnClickListener onClickListener; - + private OnTrackBallListener trackBallDelegate; - + private List layers = new ArrayList(); private Map zOrders = new HashMap(); - + // UI Part - // handler to refresh map (in ui thread - not necessary in ui thread, but msg queue is desirable). + // handler to refresh map (in ui thread - not necessary in ui thread, but msg queue is desirable). protected Handler handler = new Handler(); - + private AnimateDraggingMapThread animatedDraggingThread; - + private float initialMultiTouchZoom; private PointF initialMultiTouchCenterPoint; private LatLon initialMultiTouchLocation; - + private GestureDetector gestureDetector; - + private MultiTouchSupport multiTouchSupport; - + Paint paintGrayFill; Paint paintWhiteFill; Paint paintCenter; @@ -116,22 +118,21 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall private DisplayMetrics dm; private final OsmandApplication application; - - + public OsmandMapTileView(Context context, AttributeSet attrs) { super(context, attrs); initView(); application = (OsmandApplication) context.getApplicationContext(); } - + public OsmandMapTileView(Context context) { super(context); initView(); application = (OsmandApplication) context.getApplicationContext(); } - - /////////////////////////////// INITIALIZING UI PART /////////////////////////////////// - public void initView(){ + + // ///////////////////////////// INITIALIZING UI PART /////////////////////////////////// + public void initView() { paintGrayFill = new Paint(); paintGrayFill.setColor(Color.GRAY); paintGrayFill.setStyle(Style.FILL); @@ -143,35 +144,33 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall paintWhiteFill.setStyle(Style.FILL); // when map rotate paintWhiteFill.setAntiAlias(true); - + paintCenter = new Paint(); paintCenter.setStyle(Style.STROKE); paintCenter.setColor(Color.rgb(60, 60, 60)); paintCenter.setStrokeWidth(2); paintCenter.setAntiAlias(true); - + paintBitmap = new Paint(); paintBitmap.setFilterBitmap(true); setClickable(true); setLongClickable(true); setFocusable(true); - + getHolder().addCallback(this); - + animatedDraggingThread = new AnimateDraggingMapThread(); animatedDraggingThread.setCallback(this); gestureDetector = new GestureDetector(getContext(), this); multiTouchSupport = new MultiTouchSupport(getContext(), this); gestureDetector.setOnDoubleTapListener(this); - + WindowManager mgr = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); dm = new DisplayMetrics(); mgr.getDefaultDisplay().getMetrics(dm); } - - - + @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { refreshMap(); @@ -185,13 +184,11 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall @Override public void surfaceDestroyed(SurfaceHolder holder) { } - - - - public void addLayer(OsmandMapLayer layer, float zOrder){ - int i=0; - for(i=0; i zOrder){ + + public void addLayer(OsmandMapLayer layer, float zOrder) { + int i = 0; + for (i = 0; i < layers.size(); i++) { + if (zOrders.get(layers.get(i)) > zOrder) { break; } } @@ -199,147 +196,156 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall layers.add(i, layer); zOrders.put(layer, zOrder); } - - public void removeLayer(OsmandMapLayer layer){ + + public void removeLayer(OsmandMapLayer layer) { layers.remove(layer); zOrders.remove(layer); layer.destroyLayer(); } - + public List getLayers() { return layers; } - + public OsmandApplication getApplication() { return application; } - - /////////////////////////// NON UI PART (could be extracted in common) ///////////////////////////// + // ///////////////////////// NON UI PART (could be extracted in common) ///////////////////////////// + /** + * Returns real tile size in pixels for float zoom . + */ public float getTileSize() { float res = map == null ? 256 : map.getTileSize(); - if(zoom != (int) zoom){ - res *= (float) Math.pow(2, zoom - (int) zoom); - } - // TODO introduce settings for these part - if(dm != null && dm.density > 1f){ - res *= dm.density; + if (zoom != (int) zoom) { + res *= (float) Math.pow(2, zoom - (int) zoom); } + + // that trigger allows to scale tiles for certain devices + // for example for device with density > 1 draw tiles the same size as with density = 1 + // It makes text bigger but blurry, the settings could be introduced for that +// if (dm != null && dm.density > 1f) { +// res *= dm.density; +// } return res; } - + public int getSourceTileSize() { return map == null ? 256 : map.getTileSize(); } - - public float getXTile(){ - return (float) MapUtils.getTileNumberX(zoom, longitude); + /** + * @return x tile based on (int) zoom + */ + public float getXTile() { + return (float) MapUtils.getTileNumberX(getZoom(), longitude); } - - public float getYTile(){ - return (float) MapUtils.getTileNumberY(zoom, latitude); + + /** + * @return y tile based on (int) zoom + */ + public float getYTile() { + return (float) MapUtils.getTileNumberY(getZoom(), latitude); } - - - public void setZoom(float zoom){ - if ((map == null && zoom < 22 && zoom > 0) || - (map != null && (map.getMaximumZoomSupported() + OVERZOOM_IN) >= zoom && map.getMinimumZoomSupported() <= zoom)) { + + public void setZoom(float zoom) { + if ((map == null && zoom < 22 && zoom > 0) + || (map != null && (map.getMaximumZoomSupported() + OVERZOOM_IN) >= zoom && map.getMinimumZoomSupported() <= zoom)) { animatedDraggingThread.stopAnimating(); this.zoom = zoom; refreshMap(); } } - + // for internal usage @Override public void zoomTo(float zoom, boolean notify) { - if ((map == null && zoom < 23) || - (map != null && (map.getMaximumZoomSupported() + OVERZOOM_IN) >= zoom && map.getMinimumZoomSupported() <= zoom)) { + if ((map == null && zoom < 23) + || (map != null && (map.getMaximumZoomSupported() + OVERZOOM_IN) >= zoom && map.getMinimumZoomSupported() <= zoom)) { this.zoom = zoom; refreshMap(); - if(notify && locationListener != null){ + if (notify && locationListener != null) { locationListener.locationChanged(latitude, longitude, this); } } } - + public void setRotate(float rotate) { - float dif = this.rotate - rotate; - if (dif > 2 || dif < -2) { + float diff = this.rotate - rotate; + if (diff > 4 || diff < -4) { this.rotate = rotate; + float rotateRad = (float) Math.toRadians(rotate); + this.rotateCos = FloatMath.cos(rotateRad); + this.rotateSin = FloatMath.sin(rotateRad); animatedDraggingThread.stopAnimating(); refreshMap(); } } - public boolean isShowMapPosition() { return showMapPosition; } - + public void setShowMapPosition(boolean showMapPosition) { this.showMapPosition = showMapPosition; } - + public float getRotate() { return rotate; } - + public ITileSource getMap() { return map; } - + public void setMap(ITileSource map) { this.map = map; - if(map !=null && map.getMaximumZoomSupported() + OVERZOOM_IN < this.zoom){ + if (map != null && map.getMaximumZoomSupported() + OVERZOOM_IN < this.zoom) { zoom = map.getMaximumZoomSupported() + OVERZOOM_IN; } - if(map !=null && map.getMinimumZoomSupported() > this.zoom){ + if (map != null && map.getMinimumZoomSupported() > this.zoom) { zoom = map.getMinimumZoomSupported(); } refreshMap(); } - - public void setLatLon(double latitude, double longitude){ + + public void setLatLon(double latitude, double longitude) { animatedDraggingThread.stopAnimating(); this.latitude = latitude; this.longitude = longitude; refreshMap(); } - + public double getLatitude() { return latitude; } - + public double getLongitude() { return longitude; } - + public int getZoom() { return (int) zoom; } - - public float getFloatZoom(){ - return zoom; + + public boolean isZooming(){ + return zoom != getZoom(); } - - public void setMapLocationListener(IMapLocationListener l){ + public void setMapLocationListener(IMapLocationListener l) { locationListener = l; } /** - * Adds listener to control when map is dragging + * Adds listener to control when map is dragging */ - public IMapLocationListener setMapLocationListener(){ + public IMapLocationListener setMapLocationListener() { return locationListener; } - - - //////////////////////////////// DRAWING MAP PART ///////////////////////////////////////////// - - protected void drawEmptyTile(Canvas cvs, float x, float y, float ftileSize){ + + // ////////////////////////////// DRAWING MAP PART ///////////////////////////////////////////// + + protected void drawEmptyTile(Canvas cvs, float x, float y, float ftileSize) { float tileDiv = (ftileSize / emptyTileDivisor); for (int k1 = 0; k1 < emptyTileDivisor; k1++) { for (int k2 = 0; k2 < emptyTileDivisor; k2++) { @@ -354,27 +360,26 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall } } - public int getCenterPointX(){ + public int getCenterPointX() { return getWidth() / 2; } - - public int getCenterPointY(){ - if(mapPosition == OsmandSettings.BOTTOM_CONSTANT){ + + public int getCenterPointY() { + if (mapPosition == OsmandSettings.BOTTOM_CONSTANT) { return 3 * getHeight() / 4; } return getHeight() / 2; } - - public void setMapPosition(int type){ + + public void setMapPosition(int type) { this.mapPosition = type; } - - - private void drawOverMap(Canvas canvas){ + + private void drawOverMap(Canvas canvas, RectF latlonRect) { int w = getCenterPointX(); int h = getCenterPointY(); canvas.restore(); - + for (int i = 0; i < layers.size(); i++) { try { OsmandMapLayer layer = layers.get(i); @@ -382,7 +387,7 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall if (!layer.drawInScreenPixels()) { canvas.rotate(rotate, w, h); } - layer.onDraw(canvas); + layer.onDraw(canvas, latlonRect); canvas.restore(); } catch (IndexOutOfBoundsException e) { // skip it @@ -393,8 +398,8 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall canvas.drawCircle(w, h, 7 * dm.density, paintCenter); } } - - public void calculateTileRectangle(Rect pixRect, float cx, float cy, float ctilex, float ctiley, RectF tileRect){ + + public void calculateTileRectangle(Rect pixRect, float cx, float cy, float ctilex, float ctiley, RectF tileRect) { float x1 = calcDiffTileX(pixRect.left - cx, pixRect.top - cy); float x2 = calcDiffTileX(pixRect.left - cx, pixRect.bottom - cy); float x3 = calcDiffTileX(pixRect.right - cx, pixRect.top - cy); @@ -409,8 +414,8 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall float b = Math.max(Math.max(y1, y2), Math.max(y3, y4)) + ctiley; tileRect.set(l, t, r, b); } - - public void calculatePixelRectangle(Rect pixelRect, float cx, float cy, float ctilex, float ctiley, RectF tileRect){ + + public void calculatePixelRectangle(Rect pixelRect, float cx, float cy, float ctilex, float ctiley, RectF tileRect) { float x1 = calcDiffPixelX(tileRect.left - ctilex, tileRect.top - ctiley); float x2 = calcDiffPixelX(tileRect.left - ctilex, tileRect.bottom - ctiley); float x3 = calcDiffPixelX(tileRect.right - ctilex, tileRect.top - ctiley); @@ -425,49 +430,56 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall int b = Math.round(Math.max(Math.max(y1, y2), Math.max(y3, y4)) + cy); pixelRect.set(l, t, r, b); } - - // used only to save space & reuse + + // used only to save space & reuse protected RectF tilesRect = new RectF(); + protected RectF latlonRect = new RectF(); protected Rect boundsRect = new Rect(); protected RectF bitmapToDraw = new RectF(); protected Rect bitmapToZoom = new Rect(); - - private void refreshMapInternal(){ - if(handler.hasMessages(1)){ + + private void refreshMapInternal() { + if (handler.hasMessages(1)) { return; } - + boolean useInternet = OsmandSettings.isUsingInternetToDownloadTiles(getContext()); if (useInternet) { - MapTileDownloader.getInstance().refuseAllPreviousRequests(); + MapTileDownloader.getInstance().refuseAllPreviousRequests(); } float ftileSize = getTileSize(); - int tileSize = map == null ? 256 : map.getTileSize(); - float tileX = getXTile(); - float tileY = getYTile(); - float w = getCenterPointX(); - float h = getCenterPointY(); + int tileSize = getSourceTileSize(); + SurfaceHolder holder = getHolder(); synchronized (holder) { - int nzoom = (int) zoom; + int nzoom = getZoom(); + float tileX = (float) MapUtils.getTileNumberX(nzoom, longitude); + float tileY = (float) MapUtils.getTileNumberY(nzoom, latitude); + float w = getCenterPointX(); + float h = getCenterPointY(); Canvas canvas = holder.lockCanvas(); if (canvas != null) { canvas.save(); boundsRect.set(0, 0, getWidth(), getHeight()); - canvas.rotate(rotate, w , h); + canvas.rotate(rotate, w, h); try { + calculateTileRectangle(boundsRect, w, h, tileX, tileY, tilesRect); + int left = (int) FloatMath.floor(tilesRect.left); + int top = (int) FloatMath.floor(tilesRect.top ); + int width = (int) FloatMath.ceil(tilesRect.right - left); + int height = (int) FloatMath.ceil(tilesRect.bottom - top); + latlonRect.top = (float) MapUtils.getLatitudeFromTile(nzoom, tilesRect.top); + latlonRect.left = (float) MapUtils.getLongitudeFromTile(nzoom, tilesRect.left); + latlonRect.bottom = (float) MapUtils.getLatitudeFromTile(nzoom, tilesRect.bottom); + latlonRect.right = (float) MapUtils.getLongitudeFromTile(nzoom, tilesRect.right); if (map != null) { ResourceManager mgr = getApplication().getResourceManager(); useInternet = useInternet && OsmandSettings.isInternetConnectionAvailable(getContext()) && map.couldBeDownloadedFromInternet(); int maxLevel = Math.min(OsmandSettings.getMaximumLevelToDownloadTile(getContext()), map.getMaximumZoomSupported()); + - calculateTileRectangle(boundsRect, w, h, tileX, tileY, tilesRect); - int left = (int) FloatMath.floor(tilesRect.left); - int top = (int) FloatMath.floor(tilesRect.top); - int width = (int) (FloatMath.ceil(tilesRect.right) - left); - int height = (int) (FloatMath.ceil(tilesRect.bottom) - top); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { float x1 = (i + left - tileX) * ftileSize + w; @@ -524,11 +536,6 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall } } } else { - calculateTileRectangle(boundsRect, w, h, tileX, tileY, tilesRect); - int left = (int) FloatMath.floor(tilesRect.left); - int top = (int) FloatMath.floor(tilesRect.top); - int width = (int) (FloatMath.ceil(tilesRect.right) - left); - int height = (int) (FloatMath.ceil(tilesRect.bottom) - top); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { float x1 = (i + left - tileX) * ftileSize + w; @@ -537,28 +544,29 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall } } } - drawOverMap(canvas); + drawOverMap(canvas, latlonRect); } finally { holder.unlockCanvasAndPost(canvas); } } } - + } - - public boolean mapIsRefreshing(){ + + public boolean mapIsRefreshing() { return handler.hasMessages(1); } - - public boolean mapIsAnimating(){ + + public boolean mapIsAnimating() { return animatedDraggingThread != null && animatedDraggingThread.isAnimating(); } + // this method could be called in non UI thread public void refreshMap() { - if(!handler.hasMessages(1)){ - Message msg = Message.obtain(handler, new Runnable(){ + if (!handler.hasMessages(1)) { + Message msg = Message.obtain(handler, new Runnable() { @Override - public void run() { + public void run() { refreshMapInternal(); } }); @@ -566,21 +574,20 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall handler.sendMessageDelayed(msg, 20); } } - - + public void tileDownloaded(DownloadRequest request) { - if (request == null || rotate != 0 ) { + if (request == null || rotate != 0) { // if image is rotated call refresh the whole canvas - // because we can't find dirty rectangular region but all pixels should be drawn + // because we can't find dirty rectangular region - // we don't know exact images were changed + // if request null then we don't know exact images were changed refreshMap(); return; } - if(request.error){ + if (request.error) { return; } - if (request.zoom != this.zoom) { + if (request.zoom != getZoom()) { return; } float w = getCenterPointX(); @@ -592,16 +599,16 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall synchronized (holder) { tilesRect.set(request.xTile, request.yTile, request.xTile + 1, request.yTile + 1); calculatePixelRectangle(boundsRect, w, h, tileX, tileY, tilesRect); - - if(boundsRect.left > getWidth() || boundsRect.right < 0 || boundsRect.bottom < 0 || boundsRect.top > getHeight()){ + + if (boundsRect.left > getWidth() || boundsRect.right < 0 || boundsRect.bottom < 0 || boundsRect.top > getHeight()) { return; } - + Canvas canvas = holder.lockCanvas(boundsRect); if (canvas != null) { canvas.save(); - canvas.rotate(rotate, w , h); - + canvas.rotate(rotate, w, h); + try { Bitmap bmp = null; if (map != null) { @@ -619,116 +626,107 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall bitmapToDraw.set(x, y, x + tileSize, y + tileSize); canvas.drawBitmap(bmp, bitmapToZoom, bitmapToDraw, paintBitmap); } - drawOverMap(canvas); + drawOverMap(canvas, latlonRect); } finally { holder.unlockCanvasAndPost(canvas); } } - + } } - - - /////////////////////////////////// DRAGGING PART /////////////////////////////////////// - public float calcDiffTileY(float dx, float dy){ - float rad = (float) Math.toRadians(rotate); - return (-FloatMath.sin(rad) * dx + FloatMath.cos(rad) * dy) / (getTileSize() ); + // ///////////////////////////////// DRAGGING PART /////////////////////////////////////// + public float calcDiffTileY(float dx, float dy) { + return (-rotateSin * dx + rotateCos * dy) / getTileSize(); } - - public float calcDiffTileX(float dx, float dy){ - float rad = (float) Math.toRadians(rotate); - return (FloatMath.cos(rad) * dx + FloatMath.sin(rad) * dy) / (getTileSize() ); + + public float calcDiffTileX(float dx, float dy) { + return (rotateCos * dx + rotateSin * dy) / getTileSize(); } - - public float calcDiffPixelY(float dTileX, float dTileY){ - float rad = (float) Math.toRadians(rotate); - return (FloatMath.sin(rad) * dTileX + FloatMath.cos(rad) * dTileY) * getTileSize() ; + + public float calcDiffPixelY(float dTileX, float dTileY) { + return (rotateSin * dTileX + rotateCos * dTileY) * getTileSize(); } - - public float calcDiffPixelX(float dTileX, float dTileY){ - float rad = (float) Math.toRadians(rotate); - return (FloatMath.cos(rad) * dTileX - FloatMath.sin(rad) * dTileY) * getTileSize() ; + + public float calcDiffPixelX(float dTileX, float dTileY) { + return (rotateCos * dTileX - rotateSin * dTileY) * getTileSize(); } - - + /** * These methods do not consider rotating */ - public int getMapXForPoint(double longitude){ - double tileX = MapUtils.getTileNumberX(zoom, longitude); + public int getMapXForPoint(double longitude) { + double tileX = MapUtils.getTileNumberX(getZoom(), longitude); return (int) ((tileX - getXTile()) * getTileSize() + getCenterPointX()); } - public int getMapYForPoint(double latitude){ - double tileY = MapUtils.getTileNumberY(zoom, latitude); - return (int) ((tileY - getYTile()) * getTileSize() + getCenterPointY()); + + public int getMapYForPoint(double latitude) { + double tileY = MapUtils.getTileNumberY(getZoom(), latitude); + return (int) ((tileY - getYTile()) * getTileSize() + getCenterPointY()); } - - public int getRotatedMapXForPoint(double latitude, double longitude){ + + public int getRotatedMapXForPoint(double latitude, double longitude) { int cx = getCenterPointX(); - double xTile = MapUtils.getTileNumberX(zoom, longitude); - double yTile = MapUtils.getTileNumberY(zoom, latitude); + double xTile = MapUtils.getTileNumberX(getZoom(), longitude); + double yTile = MapUtils.getTileNumberY(getZoom(), latitude); return (int) (calcDiffPixelX((float) (xTile - getXTile()), (float) (yTile - getYTile())) + cx); } - public int getRotatedMapYForPoint(double latitude, double longitude){ + + public int getRotatedMapYForPoint(double latitude, double longitude) { int cy = getCenterPointY(); - double xTile = MapUtils.getTileNumberX(zoom, longitude); - double yTile = MapUtils.getTileNumberY(zoom, latitude); + double xTile = MapUtils.getTileNumberX(getZoom(), longitude); + double yTile = MapUtils.getTileNumberY(getZoom(), latitude); return (int) (calcDiffPixelY((float) (xTile - getXTile()), (float) (yTile - getYTile())) + cy); } - - - public boolean isPointOnTheRotatedMap(double latitude, double longitude){ + + public boolean isPointOnTheRotatedMap(double latitude, double longitude) { int cx = getCenterPointX(); int cy = getCenterPointY(); - double xTile = MapUtils.getTileNumberX(zoom, longitude); - double yTile = MapUtils.getTileNumberY(zoom, latitude); + double xTile = MapUtils.getTileNumberX(getZoom(), longitude); + double yTile = MapUtils.getTileNumberY(getZoom(), latitude); int newX = (int) (calcDiffPixelX((float) (xTile - getXTile()), (float) (yTile - getYTile())) + cx); int newY = (int) (calcDiffPixelY((float) (xTile - getXTile()), (float) (yTile - getYTile())) + cy); - if(newX >= 0 && newX <= getWidth() && newY >=0 && newY <= getHeight()){ + if (newX >= 0 && newX <= getWidth() && newY >= 0 && newY <= getHeight()) { return true; - } + } return false; } - + @Override - public void dragTo(float fromX, float fromY, float toX, float toY, boolean notify){ - float dx = (fromX - toX) ; + public void dragTo(float fromX, float fromY, float toX, float toY, boolean notify) { + float dx = (fromX - toX); float dy = (fromY - toY); moveTo(dx, dy); - if(locationListener != null && notify){ + if (locationListener != null && notify) { locationListener.locationChanged(latitude, longitude, this); } } - + @Override public void setLatLon(double latitude, double longitude, boolean notify) { this.latitude = latitude; this.longitude = longitude; refreshMap(); - if(locationListener != null && notify){ + if (locationListener != null && notify) { locationListener.locationChanged(latitude, longitude, this); } - + } - + public void moveTo(float dx, float dy) { float fy = calcDiffTileY(dx, dy); float fx = calcDiffTileX(dx, dy); - - this.latitude = MapUtils.getLatitudeFromTile(zoom, getYTile() + fy); - this.longitude = MapUtils.getLongitudeFromTile(zoom, getXTile() + fx); + + this.latitude = MapUtils.getLatitudeFromTile(getZoom(), getYTile() + fy); + this.longitude = MapUtils.getLongitudeFromTile(getZoom(), getXTile() + fx); refreshMap(); // do not notify here listener - + } - - - - + @Override public boolean onTouchEvent(MotionEvent event) { - if(event.getAction() == MotionEvent.ACTION_DOWN){ + if (event.getAction() == MotionEvent.ACTION_DOWN) { animatedDraggingThread.stopAnimating(); } if (!multiTouchSupport.onTouchEvent(event)) { @@ -736,69 +734,67 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall } return true; } - - + @Override public boolean onTrackballEvent(MotionEvent event) { - if(trackBallDelegate != null){ + if (trackBallDelegate != null) { trackBallDelegate.onTrackBallEvent(event); } return super.onTrackballEvent(event); } - + public void setTrackBallDelegate(OnTrackBallListener trackBallDelegate) { this.trackBallDelegate = trackBallDelegate; } - - + public void setOnLongClickListener(OnLongClickListener l) { this.onLongClickListener = l; } - + public void setOnClickListener(OnClickListener l) { this.onClickListener = l; } - @Override public boolean onDown(MotionEvent e) { - // enable double tap animation -// animatedDraggingThread.stopAnimating(); + // enable double tap animation + // animatedDraggingThread.stopAnimating(); return false; } - + @Override public void onZoomEnded(float distance, float relativeToStart) { float dz = (float) (Math.log(relativeToStart) / Math.log(2) * 1.5); float calcZoom = initialMultiTouchZoom + dz; setZoom(Math.round(calcZoom)); - zoomPositionChanged(getFloatZoom()); + zoomPositionChanged(getZoom()); } - - - + @Override public void onZoomStarted(float distance, PointF centerPoint) { initialMultiTouchCenterPoint = centerPoint; initialMultiTouchLocation = getLatLonFromScreenPoint(centerPoint.x, centerPoint.y); initialMultiTouchZoom = zoom; } - private void zoomPositionChanged(float calcZoom){ - float dtx = calcDiffTileX(getCenterPointX() - initialMultiTouchCenterPoint.x, getCenterPointY() - initialMultiTouchCenterPoint.y); - float dty = calcDiffTileY(getCenterPointX() - initialMultiTouchCenterPoint.x, getCenterPointY() - initialMultiTouchCenterPoint.y); - double tx = MapUtils.getTileNumberX(calcZoom, initialMultiTouchLocation.getLongitude()); - double ty = MapUtils.getTileNumberY(calcZoom, initialMultiTouchLocation.getLatitude()); - double lat = MapUtils.getLatitudeFromTile(calcZoom, ty + dty); - double lon = MapUtils.getLongitudeFromTile(calcZoom, tx + dtx); + + private void zoomPositionChanged(float calcZoom) { + float dx = initialMultiTouchCenterPoint.x - getCenterPointX(); + float dy = initialMultiTouchCenterPoint.y - getCenterPointY(); + float ex = calcDiffTileX(dx, dy); + float ey = calcDiffTileY(dx, dy); + int z = (int)calcZoom; + double tx = MapUtils.getTileNumberX(z, initialMultiTouchLocation.getLongitude()); + double ty = MapUtils.getTileNumberY(z, initialMultiTouchLocation.getLatitude()); + double lat = MapUtils.getLatitudeFromTile(z, ty - ey); + double lon = MapUtils.getLongitudeFromTile(z, tx - ex); setLatLon(lat, lon); } - - + @Override public void onZooming(float distance, float relativeToStart) { float dz = (float) (Math.log(relativeToStart) / Math.log(2) * 1.5); float calcZoom = initialMultiTouchZoom + dz; - if(Math.abs(calcZoom - zoom) > 0.05){ + if (Math.abs(calcZoom - zoom) > 0.05) { setZoom(calcZoom); zoomPositionChanged(calcZoom); } @@ -806,25 +802,26 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if(Math.abs(e1.getX() - e2.getX()) + Math.abs(e1.getX() - e2.getX()) > 50 * dm.density){ - animatedDraggingThread.startDragging(Math.abs(velocityX/1000), Math.abs(velocityY/1000), e1.getX(), e1.getY(), e2.getX(), e2.getY()); + if (Math.abs(e1.getX() - e2.getX()) + Math.abs(e1.getX() - e2.getX()) > 50 * dm.density) { + animatedDraggingThread.startDragging(Math.abs(velocityX / 1000), Math.abs(velocityY / 1000), e1.getX(), e1.getY(), e2.getX(), + e2.getY()); } else { onScroll(e1, e2, e1.getX() - e2.getX(), e1.getY() - e2.getY()); } return true; } - + public AnimateDraggingMapThread getAnimatedDraggingThread() { return animatedDraggingThread; } @Override public void onLongPress(MotionEvent e) { - if(multiTouchSupport.isInZoomMode()){ + if (multiTouchSupport.isInZoomMode()) { return; } - if(log.isDebugEnabled()){ - log.debug("On long click event "+ e.getX() + " " + e.getY()); //$NON-NLS-1$ //$NON-NLS-2$ + if (log.isDebugEnabled()) { + log.debug("On long click event " + e.getX() + " " + e.getY()); //$NON-NLS-1$ //$NON-NLS-2$ } PointF point = new PointF(e.getX(), e.getY()); for (int i = layers.size() - 1; i >= 0; i--) { @@ -832,12 +829,10 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall return; } } - if(onLongClickListener != null && onLongClickListener.onLongPressEvent(point)){ + if (onLongClickListener != null && onLongClickListener.onLongPressEvent(point)) { return; } } - - @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { @@ -852,37 +847,35 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall @Override public boolean onSingleTapUp(MotionEvent e) { PointF point = new PointF(e.getX(), e.getY()); - if(log.isDebugEnabled()){ - log.debug("On click event "+ point.x + " " + point.y); //$NON-NLS-1$ //$NON-NLS-2$ + if (log.isDebugEnabled()) { + log.debug("On click event " + point.x + " " + point.y); //$NON-NLS-1$ //$NON-NLS-2$ } for (int i = layers.size() - 1; i >= 0; i--) { if (layers.get(i).onTouchEvent(point)) { return true; } } - if(onClickListener != null && onClickListener.onPressEvent(point)){ + if (onClickListener != null && onClickListener.onPressEvent(point)) { return true; } return false; } - - - - public LatLon getLatLonFromScreenPoint(float x, float y){ + + public LatLon getLatLonFromScreenPoint(float x, float y) { float dx = x - getCenterPointX(); float dy = y - getCenterPointY(); float fy = calcDiffTileY(dx, dy); float fx = calcDiffTileX(dx, dy); - double latitude = MapUtils.getLatitudeFromTile(zoom, getYTile() + fy); - double longitude = MapUtils.getLongitudeFromTile(zoom, getXTile() + fx); + double latitude = MapUtils.getLatitudeFromTile(getZoom(), getYTile() + fy); + double longitude = MapUtils.getLongitudeFromTile(getZoom(), getXTile() + fx); return new LatLon(latitude, longitude); } @Override public boolean onDoubleTap(MotionEvent e) { LatLon l = getLatLonFromScreenPoint(e.getX(), e.getY()); - getAnimatedDraggingThread().startMoving(getLatitude(), getLongitude(), - l.getLatitude(), l.getLongitude(), getZoom(), getZoom() + 1, getSourceTileSize(), getRotate(), true); + getAnimatedDraggingThread().startMoving(getLatitude(), getLongitude(), l.getLatitude(), l.getLongitude(), getZoom(), getZoom() + 1, + getSourceTileSize(), getRotate(), true); return true; } @@ -895,9 +888,5 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall public boolean onSingleTapConfirmed(MotionEvent e) { return false; } - - - - } diff --git a/OsmAnd/src/net/osmand/views/POIMapLayer.java b/OsmAnd/src/net/osmand/views/POIMapLayer.java index dc999fa328..eeedb96131 100644 --- a/OsmAnd/src/net/osmand/views/POIMapLayer.java +++ b/OsmAnd/src/net/osmand/views/POIMapLayer.java @@ -10,7 +10,6 @@ import net.osmand.ResourceManager; import net.osmand.activities.EditingPOIActivity; import net.osmand.data.Amenity; import net.osmand.osm.LatLon; -import net.osmand.osm.MapUtils; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; @@ -18,14 +17,12 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PointF; -import android.graphics.Rect; import android.graphics.RectF; import android.util.DisplayMetrics; import android.view.WindowManager; import android.widget.Toast; public class POIMapLayer implements OsmandMapLayer, ContextMenuLayer.IContextMenuProvider { - // it is very slow to use with 15 level private static final int startZoom = 10; public static final int LIMIT_POI = 200; @@ -102,7 +99,6 @@ public class POIMapLayer implements OsmandMapLayer, ContextMenuLayer.IContextMen pointAltUI.setAlpha(200); pointAltUI.setAntiAlias(true); resourceManager = view.getApplication().getResourceManager(); - pixRect.set(0, 0, view.getWidth(), view.getHeight()); } public int getRadiusPoi(int zoom){ @@ -121,22 +117,12 @@ public class POIMapLayer implements OsmandMapLayer, ContextMenuLayer.IContextMen return (int) (r * dm.density); } - Rect pixRect = new Rect(); - RectF tileRect = new RectF(); - @Override - public void onDraw(Canvas canvas) { + public void onDraw(Canvas canvas, RectF latLonBounds) { if (view.getZoom() >= startZoom) { - pixRect.set(0, 0, view.getWidth(), view.getHeight()); - view.calculateTileRectangle(pixRect, view.getCenterPointX(), - view.getCenterPointY(), view.getXTile(), view.getYTile(), tileRect); - double topLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.top); - double leftLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.left); - double bottomLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.bottom); - double rightLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.right); objects.clear(); - resourceManager.searchAmenitiesAsync(topLatitude, leftLongitude, bottomLatitude, rightLongitude, view.getZoom(), filter, objects); + resourceManager.searchAmenitiesAsync(latLonBounds.top, latLonBounds.left, latLonBounds.bottom, latLonBounds.right, view.getZoom(), filter, objects); for (Amenity o : objects) { int x = view.getMapXForPoint(o.getLocation().getLongitude()); int y = view.getMapYForPoint(o.getLocation().getLatitude()); diff --git a/OsmAnd/src/net/osmand/views/PointLocationLayer.java b/OsmAnd/src/net/osmand/views/PointLocationLayer.java index b5eccc7025..cb3e74acdc 100644 --- a/OsmAnd/src/net/osmand/views/PointLocationLayer.java +++ b/OsmAnd/src/net/osmand/views/PointLocationLayer.java @@ -82,11 +82,11 @@ public class PointLocationLayer implements OsmandMapLayer { } @Override - public void onDraw(Canvas canvas) { + public void onDraw(Canvas canvas, RectF latLonBounds) { if (isLocationVisible(lastKnownLocation)) { int locationX = view.getMapXForPoint(lastKnownLocation.getLongitude()); int locationY = view.getMapYForPoint(lastKnownLocation.getLatitude()); - int radius = MapUtils.getLengthXFromMeters(view.getFloatZoom(), view.getLatitude(), view.getLongitude(), + int radius = MapUtils.getLengthXFromMeters(view.getZoom(), view.getLatitude(), view.getLongitude(), lastKnownLocation.getAccuracy(), view.getTileSize(), view.getWidth()); if(appMode == ApplicationMode.CAR){ @@ -111,7 +111,7 @@ public class PointLocationLayer implements OsmandMapLayer { int radiusBearing = (int) (30 * dm.density); if(lastKnownLocation.hasSpeed() && appMode != ApplicationMode.CAR){ radiusBearing = - Math.max(MapUtils.getLengthXFromMeters(view.getFloatZoom(), view.getLatitude(), view.getLongitude(), + Math.max(MapUtils.getLengthXFromMeters(view.getZoom(), view.getLatitude(), view.getLongitude(), lastKnownLocation.getSpeed(), view.getTileSize(), view.getWidth()) * 2, radiusBearing); radiusBearing = Math.min(radiusBearing, view.getHeight() / 4); } diff --git a/OsmAnd/src/net/osmand/views/PointNavigationLayer.java b/OsmAnd/src/net/osmand/views/PointNavigationLayer.java index 5b68d798b5..5226552882 100644 --- a/OsmAnd/src/net/osmand/views/PointNavigationLayer.java +++ b/OsmAnd/src/net/osmand/views/PointNavigationLayer.java @@ -8,6 +8,7 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PointF; +import android.graphics.RectF; import android.graphics.Paint.Style; import android.location.Location; import android.util.DisplayMetrics; @@ -46,7 +47,7 @@ public class PointNavigationLayer implements OsmandMapLayer { @Override - public void onDraw(Canvas canvas) { + public void onDraw(Canvas canvas, RectF latLonBounds) { if(pointToNavigate == null){ return; } diff --git a/OsmAnd/src/net/osmand/views/RouteInfoLayer.java b/OsmAnd/src/net/osmand/views/RouteInfoLayer.java index 341d56979a..6cc0d4ca32 100644 --- a/OsmAnd/src/net/osmand/views/RouteInfoLayer.java +++ b/OsmAnd/src/net/osmand/views/RouteInfoLayer.java @@ -165,7 +165,7 @@ public class RouteInfoLayer implements OsmandMapLayer, IRouteInformationListener } @Override - public void onDraw(Canvas canvas) { + public void onDraw(Canvas canvas, RectF latLonBounds) { if(isVisible()){ border.set(layout.getLeft() - 10 * dm.density, layout.getTop() - 4 * dm.density, layout.getRight() - 5 * dm.density, layout.getBottom() + 4 * dm.density); diff --git a/OsmAnd/src/net/osmand/views/RouteLayer.java b/OsmAnd/src/net/osmand/views/RouteLayer.java index deb8d16241..2207ccd419 100644 --- a/OsmAnd/src/net/osmand/views/RouteLayer.java +++ b/OsmAnd/src/net/osmand/views/RouteLayer.java @@ -58,7 +58,7 @@ public class RouteLayer implements OsmandMapLayer { @Override - public void onDraw(Canvas canvas) { + public void onDraw(Canvas canvas, RectF latLonBounds) { path.reset(); if (helper.hasPointsToShow()) { long time = System.currentTimeMillis(); @@ -78,7 +78,7 @@ public class RouteLayer implements OsmandMapLayer { double rightLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.right); double lat = topLatitude - bottomLatitude + 0.1; double lon = rightLongitude - leftLongitude + 0.1; - helper.fillLocationsToShow(topLatitude +lat, leftLongitude - lon, bottomLatitude - lat, rightLongitude + lon, points); + helper.fillLocationsToShow(topLatitude + lat, leftLongitude - lon, bottomLatitude - lat, rightLongitude + lon, points); if((System.currentTimeMillis() - time) > 80){ Log.e(LogUtil.TAG, "Calculate route layer " + (System.currentTimeMillis() - time)); //$NON-NLS-1$ } diff --git a/OsmAnd/src/net/osmand/views/TransportInfoLayer.java b/OsmAnd/src/net/osmand/views/TransportInfoLayer.java index 8ced6e5415..10edb6640b 100644 --- a/OsmAnd/src/net/osmand/views/TransportInfoLayer.java +++ b/OsmAnd/src/net/osmand/views/TransportInfoLayer.java @@ -8,13 +8,11 @@ import net.osmand.activities.TransportRouteHelper; import net.osmand.data.TransportRoute; import net.osmand.data.TransportStop; import net.osmand.osm.LatLon; -import net.osmand.osm.MapUtils; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PointF; -import android.graphics.Rect; import android.graphics.RectF; import android.util.DisplayMetrics; import android.view.WindowManager; @@ -23,9 +21,7 @@ import android.widget.Toast; public class TransportInfoLayer implements OsmandMapLayer { private final TransportRouteHelper routeHelper; - private Rect pixRect; private OsmandMapTileView view; - private RectF tileRect; private Paint paintInt; private Paint paintEnd; private boolean visible = true; @@ -37,8 +33,6 @@ public class TransportInfoLayer implements OsmandMapLayer { public void initLayer(OsmandMapTileView view) { this.view = view; - pixRect = new Rect(); - tileRect = new RectF(); dm = new DisplayMetrics(); WindowManager wmgr = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE); wmgr.getDefaultDisplay().getMetrics(dm); @@ -65,16 +59,9 @@ public class TransportInfoLayer implements OsmandMapLayer { } @Override - public void onDraw(Canvas canvas) { + public void onDraw(Canvas canvas, RectF latLonBounds) { if(routeHelper.routeIsCalculated() && visible){ List list = routeHelper.getRoute(); - pixRect.set(0, 0, view.getWidth(), view.getHeight()); - view.calculateTileRectangle(pixRect, view.getCenterPointX(), - view.getCenterPointY(), view.getXTile(), view.getYTile(), tileRect); - double topLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.top); - double leftLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.left); - double bottomLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.bottom); - double rightLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.right); for(RouteInfoLocation l : list){ if(l == null){ // once l is null in list @@ -100,8 +87,8 @@ public class TransportInfoLayer implements OsmandMapLayer { } if(start){ LatLon location = st.getLocation(); - if(location.getLatitude() <= topLatitude && location.getLatitude() >= bottomLatitude && - location.getLongitude() >= leftLongitude && location.getLongitude() <= rightLongitude){ + if (location.getLatitude() >= latLonBounds.bottom && location.getLatitude() <= latLonBounds.top && location.getLongitude() >= latLonBounds.left + && location.getLongitude() <= latLonBounds.right ) { int x = view.getRotatedMapXForPoint(location.getLatitude(), location.getLongitude()); int y = view.getRotatedMapYForPoint(location.getLatitude(), location.getLongitude()); canvas.drawRect(x - getRadius(), y - getRadius(), x + getRadius(), y + getRadius(), toShow); diff --git a/OsmAnd/src/net/osmand/views/TransportStopsLayer.java b/OsmAnd/src/net/osmand/views/TransportStopsLayer.java index 50aeaca221..369b376062 100644 --- a/OsmAnd/src/net/osmand/views/TransportStopsLayer.java +++ b/OsmAnd/src/net/osmand/views/TransportStopsLayer.java @@ -8,14 +8,12 @@ import net.osmand.R; import net.osmand.TransportIndexRepository; import net.osmand.data.TransportStop; import net.osmand.osm.LatLon; -import net.osmand.osm.MapUtils; import android.content.Context; import android.content.DialogInterface.OnClickListener; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PointF; -import android.graphics.Rect; import android.graphics.RectF; import android.util.DisplayMetrics; import android.view.WindowManager; @@ -41,7 +39,6 @@ public class TransportStopsLayer implements OsmandMapLayer, ContextMenuLayer.ICo pointAltUI.setColor(Color.rgb(0, 0, 255)); pointAltUI.setAlpha(150); pointAltUI.setAntiAlias(true); - pixRect.set(0, 0, view.getWidth(), view.getHeight()); } public TransportStop getFromPoint(PointF point){ @@ -116,22 +113,12 @@ public class TransportStopsLayer implements OsmandMapLayer, ContextMenuLayer.ICo } } - Rect pixRect = new Rect(); - RectF tileRect = new RectF(); @Override - public void onDraw(Canvas canvas) { + public void onDraw(Canvas canvas, RectF latLonBounds) { if (view.getZoom() >= startZoom) { - pixRect.set(0, 0, view.getWidth(), view.getHeight()); - view.calculateTileRectangle(pixRect, view.getCenterPointX(), - view.getCenterPointY(), view.getXTile(), view.getYTile(), tileRect); - double topLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.top); - double leftLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.left); - double bottomLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.bottom); - double rightLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.right); - objects.clear(); - view.getApplication().getResourceManager().searchTransportAsync(topLatitude, leftLongitude, bottomLatitude, rightLongitude, view.getZoom(), objects); + view.getApplication().getResourceManager().searchTransportAsync(latLonBounds.top, latLonBounds.left, latLonBounds.bottom, latLonBounds.right, view.getZoom(), objects); int r = 3 * getRadiusPoi(view.getZoom()) / 4; for (TransportStop o : objects) { int x = view.getMapXForPoint(o.getLocation().getLongitude()); diff --git a/OsmAnd/src/net/osmand/views/YandexTrafficLayer.java b/OsmAnd/src/net/osmand/views/YandexTrafficLayer.java index 5436b506ee..cca6b3d8a6 100644 --- a/OsmAnd/src/net/osmand/views/YandexTrafficLayer.java +++ b/OsmAnd/src/net/osmand/views/YandexTrafficLayer.java @@ -208,16 +208,15 @@ public class YandexTrafficLayer implements OsmandMapLayer { } @Override - public void onDraw(Canvas canvas) { + public void onDraw(Canvas canvas, RectF latLonBounds) { if (visible) { pixRect.set(0, 0, view.getWidth(), view.getHeight()); - double lat = MapUtils.getLatitudeFromTile(view.getFloatZoom(), view.getYTile()); - float tileY = (float) MapUtils.getTileEllipsoidNumberY(view.getFloatZoom(), lat); + float tileY = (float) MapUtils.getTileEllipsoidNumberY(view.getZoom(), view.getLatitude()); view.calculateTileRectangle(pixRect, view.getCenterPointX(), view.getCenterPointY(), view.getXTile(), tileY, tileRect); - double topLat = MapUtils.getLatitudeFromEllipsoidTileY(view.getFloatZoom(), (int) tileRect.top); - double leftLon = MapUtils.getLongitudeFromTile(view.getFloatZoom(), (int) tileRect.left); - int x = view.getRotatedMapXForPoint(topLat, leftLon); - int y = view.getRotatedMapYForPoint(topLat, leftLon); + double topLat = MapUtils.getLatitudeFromEllipsoidTileY(view.getZoom(), (int) tileRect.top); + double leftLon = MapUtils.getLongitudeFromTile(view.getZoom(), (int) tileRect.left); + int x = view.getMapXForPoint(leftLon); + int y = view.getMapYForPoint(topLat); checkedCachedImages(view.getZoom()); float right = FloatMath.ceil(tileRect.right); float bottom = FloatMath.ceil(tileRect.bottom);