Permalink
Browse files

Merge pull request #1149 from osmdroid/feature/#1144

feature/#1144 - using the new MapTileAreaList class in CacheManager (refactoring)
  • Loading branch information...
monsieurtanuki committed Oct 10, 2018
2 parents a16b390 + 4aa694b commit 78d28e4b958ed0a12a6203d9a4d9c7af58422101
@@ -24,6 +24,8 @@
import org.osmdroid.util.BoundingBox;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.util.IterableWithSize;
import org.osmdroid.util.MapTileArea;
import org.osmdroid.util.MapTileAreaList;
import org.osmdroid.util.MapTileIndex;
import org.osmdroid.util.MyMath;
import org.osmdroid.util.TileSystem;
@@ -35,14 +37,10 @@
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
;
/**
* Provides various methods for managing the local filesystem cache of osmdroid tiles: <br>
* - Dowloading of tiles inside a specified area, <br>
@@ -111,17 +109,25 @@ public int getPendingJobs(){
return mPendingTasks.size();
}
/**
* @deprecated Use {@link TileSystem#getTileXFromLongitude(double, int)} and
* {@link TileSystem#getTileYFromLatitude(double, int)} instead
*/
@Deprecated
public static Point getMapTileFromCoordinates(final double aLat, final double aLon, final int zoom) {
final int y = (int) Math.floor((1 - Math.log(Math.tan(aLat * Math.PI / 180) + 1 / Math.cos(aLat * Math.PI / 180)) / Math.PI) / 2 * (1 << zoom));
final int x = (int) Math.floor((aLon + 180) / 360 * (1 << zoom));
final int y = MapView.getTileSystem().getTileYFromLatitude(aLat, zoom);
final int x = MapView.getTileSystem().getTileXFromLongitude(aLon, zoom);
return new Point(x, y);
}
/**
* @deprecated Use {@link TileSystem#getLatitudeFromTileY(int, int)} and
* {@link TileSystem#getLongitudeFromTileX(int, int)} instead
*/
@Deprecated
public static GeoPoint getCoordinatesFromMapTile(final int x, final int y, final int zoom) {
double n = Math.PI - 2 * Math.PI * y / (1 << zoom);
final double lat = (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))));
final double lon = (360.0 * x / (1 << zoom)) - 180.0;
final double lat = MapView.getTileSystem().getLatitudeFromTileY(y, zoom);
final double lon = MapView.getTileSystem().getLongitudeFromTileX(x, zoom);
return new GeoPoint(lat, lon);
}
@@ -220,86 +226,11 @@ public boolean isTileToBeDownloaded(final ITileSource pTileSource, final long pM
*/
static IterableWithSize<Long> getTilesCoverageIterable(final BoundingBox pBB,
final int pZoomMin, final int pZoomMax) {
return new IterableWithSize<Long>() {
private final Map<Integer, Rect> zoomToTilesMap = new LinkedHashMap<>();
private final int size;
{
int totalSize = 0;
for (int zoomLevel = pZoomMin; zoomLevel <= pZoomMax; zoomLevel++) {
Rect tilesRect = getTilesRect(pBB, zoomLevel);
int mapTileUpperBound = 1 << zoomLevel;
int width = (tilesRect.width()) % mapTileUpperBound;
int height = (tilesRect.height()) % mapTileUpperBound;
totalSize += (width * height);
zoomToTilesMap.put(zoomLevel, tilesRect);
}
size = totalSize;
}
@Override
public int size() {
return size;
}
@Override
public Iterator<Long> iterator() {
return new Iterator<Long>() {
private Point currentPoint;
private Point lowerRight;
private Point upperLeft;
private int currentZoom;
private int mapTileUpperBound;
{
init(pZoomMin);
}
@Override
public boolean hasNext() {
return currentZoom <= pZoomMax;
}
@Override
public Long next() {
// save current point coordinates
int x = MyMath.mod(currentPoint.x, mapTileUpperBound);
int y = MyMath.mod(currentPoint.y, mapTileUpperBound);
int zoomLevel = currentZoom;
// now calculate the coordinates of the next point
// if there's still a room for y to go then do so
if (y < lowerRight.y - 1) {
currentPoint.y++;
} else if (x < lowerRight.x - 1) { //otherwise, if there's a room for x to go
// switch to the next "column"
currentPoint.y = upperLeft.y;
currentPoint.x++;
} else { // if the latter conditions fail it means currentPoint is the last one
// for the current zoom. Move to the next zoom.
init(zoomLevel + 1);
}
// return the tile index of the current point
return MapTileIndex.getTileIndex(zoomLevel, x, y);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
private void init(int zoomLevel) {
currentZoom = zoomLevel;
mapTileUpperBound = 1 << currentZoom;
Rect tilesRect = zoomToTilesMap.get(currentZoom);
if (tilesRect != null) {
upperLeft = new Point(tilesRect.left, tilesRect.top);
currentPoint = new Point(upperLeft);
lowerRight = new Point(tilesRect.right, tilesRect.bottom);
}
}
};
}
};
final MapTileAreaList list = new MapTileAreaList();
for (int zoomLevel = pZoomMin; zoomLevel <= pZoomMax; zoomLevel++) {
list.getList().add(new MapTileArea().set(zoomLevel, getTilesRect(pBB, zoomLevel)));
}
return list;
}
/**
@@ -309,24 +240,22 @@ private void init(int zoomLevel) {
* @param pZoomLevel the given zoom level
* @return the {@link Rect} reflecting the tiles coverage
*/
private static Rect getTilesRect(final BoundingBox pBB,
final int pZoomLevel){
public static Rect getTilesRect(final BoundingBox pBB,
final int pZoomLevel){
final int mapTileUpperBound = 1 << pZoomLevel;
Point lowerRight = getMapTileFromCoordinates(
pBB.getLatSouth(), pBB.getLonEast(), pZoomLevel);
final Point upperLeft = getMapTileFromCoordinates(
pBB.getLatNorth(), pBB.getLonWest(), pZoomLevel);
int width = lowerRight.x - upperLeft.x + 1; // handling the modulo
final int right = MapView.getTileSystem().getTileXFromLongitude(pBB.getLonEast(), pZoomLevel);
final int bottom = MapView.getTileSystem().getTileYFromLatitude(pBB.getLatSouth(), pZoomLevel);
final int left = MapView.getTileSystem().getTileXFromLongitude(pBB.getLonWest(), pZoomLevel);
final int top = MapView.getTileSystem().getTileYFromLatitude(pBB.getLatNorth(), pZoomLevel);
int width = right - left + 1; // handling the modulo
if (width <= 0) {
width += mapTileUpperBound;
}
int height = lowerRight.y - upperLeft.y + 1; // handling the modulo
int height = bottom - top + 1; // handling the modulo
if (height <= 0) {
height += mapTileUpperBound;
}
lowerRight = new Point(upperLeft.x + width, upperLeft.y + height);
return new Rect(upperLeft.x, upperLeft.y, lowerRight.x, lowerRight.y);
return new Rect(left, top, left + width - 1, top + height - 1);
}
/**
@@ -387,7 +316,9 @@ private static Rect getTilesRect(final BoundingBox pBB,
wayPoint.setLatitude(((latRad * 180.0 / Math.PI)));
wayPoint.setLongitude(((lonRad * 180.0 / Math.PI)));
tile = getMapTileFromCoordinates(wayPoint.getLatitude(), wayPoint.getLongitude(), pZoomLevel);
tile = new Point(
MapView.getTileSystem().getTileXFromLongitude(wayPoint.getLongitude(), pZoomLevel),
MapView.getTileSystem().getTileYFromLatitude(wayPoint.getLatitude(), pZoomLevel));
if (!tile.equals(prevTile)) {
//Log.d(Constants.APP_TAG, "New Tile lat " + tile.x + " lon " + tile.y);
@@ -407,7 +338,9 @@ private static Rect getTilesRect(final BoundingBox pBB,
}
} else {
tile = getMapTileFromCoordinates(geoPoint.getLatitude(), geoPoint.getLongitude(), pZoomLevel);
tile = new Point(
MapView.getTileSystem().getTileXFromLongitude(geoPoint.getLongitude(), pZoomLevel),
MapView.getTileSystem().getTileYFromLatitude(geoPoint.getLatitude(), pZoomLevel));
prevTile = tile;
int ofsx = tile.x >= 0 ? 0 : -tile.x;
@@ -981,16 +914,16 @@ public CacheManagerTask cleanAreaAsync(Context ctx, List<Long> tiles, int zoomMi
*/
public BoundingBox extendedBoundsFromGeoPoints(ArrayList<GeoPoint> geoPoints, int minZoomLevel) {
BoundingBox bb = BoundingBox.fromGeoPoints(geoPoints);
Point mLowerRight = getMapTileFromCoordinates(bb.getLatSouth() , bb.getLonEast() , minZoomLevel);
GeoPoint lowerRightPoint = getCoordinatesFromMapTile(mLowerRight.x+1, mLowerRight.y+1, minZoomLevel);
Point mUpperLeft = getMapTileFromCoordinates(bb.getLatNorth() , bb.getLonWest(), minZoomLevel);
GeoPoint upperLeftPoint = getCoordinatesFromMapTile(mUpperLeft.x-1, mUpperLeft.y-1, minZoomLevel);
BoundingBox extendedBounds = new BoundingBox(upperLeftPoint.getLatitude(), upperLeftPoint.getLongitude(), lowerRightPoint.getLatitude(), lowerRightPoint.getLongitude());
return extendedBounds;
final BoundingBox bb = BoundingBox.fromGeoPoints(geoPoints);
final int right = MapView.getTileSystem().getTileXFromLongitude(bb.getLonEast(), minZoomLevel);
final int bottom = MapView.getTileSystem().getTileYFromLatitude(bb.getLatSouth(), minZoomLevel);
final int left = MapView.getTileSystem().getTileXFromLongitude(bb.getLonWest(), minZoomLevel);
final int top = MapView.getTileSystem().getTileYFromLatitude(bb.getLatNorth(), minZoomLevel);
return new BoundingBox(
MapView.getTileSystem().getLatitudeFromTileY(top - 1, minZoomLevel),
MapView.getTileSystem().getLongitudeFromTileX(right + 1, minZoomLevel),
MapView.getTileSystem().getLatitudeFromTileY(bottom + 1, minZoomLevel),
MapView.getTileSystem().getLongitudeFromTileX(left - 1, minZoomLevel));
}
/**
@@ -711,4 +711,46 @@ public String toStringLongitudeSpan() {
public String toStringLatitudeSpan() {
return "[" + getMinLatitude() + "," + getMaxLatitude() + "]";
}
/**
* @since 6.0.3
*/
public int getTileXFromLongitude(final double pLongitude, final int pZoom) {
return clipTile((int) Math.floor(getX01FromLongitude(pLongitude) * (1 << pZoom)), pZoom);
}
/**
* @since 6.0.3
*/
public int getTileYFromLatitude(final double pLatitude, final int pZoom) {
return clipTile((int) Math.floor(getY01FromLatitude(pLatitude) * (1 << pZoom)), pZoom);
}
/**
* @since 6.0.3
*/
public double getLatitudeFromTileY(final int pY, final int pZoom) {
return getLatitudeFromY01(((double)clipTile(pY, pZoom)) / (1 << pZoom));
}
/**
* @since 6.0.3
*/
public double getLongitudeFromTileX(final int pX, final int pZoom) {
return getLongitudeFromX01(((double)clipTile(pX, pZoom)) / (1 << pZoom));
}
/**
* @since 6.0.3
*/
private int clipTile(final int pTile, final int pZoom) {
if (pTile < 0) {
return 0;
}
final int max = 1 << pZoom;
if (pTile >= max) {
return max - 1;
}
return pTile;
}
}
@@ -6,8 +6,8 @@
*/
public class TileSystemWebMercator extends TileSystem{
private static final double MinLatitude = -85.05112877980659;
private static final double MaxLatitude = 85.05112877980659;
private static final double MinLatitude = -85.05112877980658;
private static final double MaxLatitude = 85.05112877980658;
private static final double MinLongitude = -180;
private static final double MaxLongitude = 180;
@@ -1,6 +1,6 @@
package org.osmdroid.tileprovider.cachemanager;
import android.graphics.Point;
import android.graphics.Rect;
import org.junit.Assert;
import org.junit.Test;
@@ -9,6 +9,8 @@
import org.osmdroid.util.IterableWithSize;
import org.osmdroid.util.MapTileIndex;
import org.osmdroid.util.MyMath;
import org.osmdroid.util.TileSystem;
import org.osmdroid.views.MapView;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@@ -17,13 +19,15 @@
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import static org.osmdroid.tileprovider.cachemanager.CacheManager.getMapTileFromCoordinates;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class CacheManagerTest {
private final Random mRandom = new Random();
/**
* Make sure {@link org.osmdroid.tileprovider.cachemanager.CacheManager#getTilesCoverageIterable(BoundingBox, int, int)} returns the
* same size and elements as the {@link org.osmdroid.tileprovider.cachemanager.CacheManager#getTilesCoverage(BoundingBox, int, int)}
@@ -86,25 +90,50 @@ private void verifyGetTilesIterable(int pMinZoom, int pMaxZoom) {
private static Collection<Long> getTilesCoverage(final BoundingBox pBB, final int pZoomLevel){
final Set<Long> result = new LinkedHashSet<>();
final int mapTileUpperBound = 1 << pZoomLevel;
final Point lowerRight = getMapTileFromCoordinates(
pBB.getLatSouth(), pBB.getLonEast(), pZoomLevel);
final Point upperLeft = getMapTileFromCoordinates(
pBB.getLatNorth(), pBB.getLonWest(), pZoomLevel);
int width = lowerRight.x - upperLeft.x + 1; // handling the modulo
if (width <= 0) {
width += mapTileUpperBound;
}
int height = lowerRight.y - upperLeft.y + 1; // handling the modulo
if (height <= 0) {
height += mapTileUpperBound;
}
for (int i = 0 ; i < width ; i ++) {
for (int j = 0 ; j < height ; j ++) {
final int x = MyMath.mod(upperLeft.x + i, mapTileUpperBound);
final int y = MyMath.mod(upperLeft.y + j, mapTileUpperBound);
final Rect rect = CacheManager.getTilesRect(pBB, pZoomLevel);
for (int j = rect.top ; j <= rect.bottom ; j ++) {
for (int i = rect.left ; i <= rect.right ; i ++) { // x incrementing first for the test
final int x = MyMath.mod(i, mapTileUpperBound);
final int y = MyMath.mod(j, mapTileUpperBound);
result.add(MapTileIndex.getTileIndex(pZoomLevel, x, y));
}
}
return result;
}
/**
* @since 6.0.3
*/
@Test
public void testGetTilesRectSingleTile() {
final TileSystem tileSystem = MapView.getTileSystem();
final BoundingBox box = new BoundingBox();
for (int zoom = 0 ; zoom <= TileSystem.getMaximumZoomLevel() ; zoom ++) {
final double longitude = tileSystem.getRandomLongitude(mRandom.nextDouble());
final double latitude = tileSystem.getRandomLatitude(mRandom.nextDouble());
box.set(latitude, longitude, latitude, longitude); // single point
final Rect rect = CacheManager.getTilesRect(box, zoom);
Assert.assertEquals(rect.left, rect.right); // single tile expected
Assert.assertEquals(rect.top, rect.bottom); // single tile expected
}
}
/**
* @since 6.0.3
*/
@Test
public void testGetTilesRectWholeWorld() {
final TileSystem tileSystem = MapView.getTileSystem();
final BoundingBox box = new BoundingBox( // whole world
tileSystem.getMaxLatitude(), tileSystem.getMaxLongitude(),
tileSystem.getMinLatitude(), tileSystem.getMinLongitude());
for (int zoom = 0 ; zoom <= TileSystem.getMaximumZoomLevel() ; zoom ++) {
final Rect rect = CacheManager.getTilesRect(box, zoom);
Assert.assertEquals(0, rect.left);
Assert.assertEquals(0, rect.top);
final int maxSize = -1 + (1 << zoom);
Assert.assertEquals(maxSize, rect.bottom);
Assert.assertEquals(maxSize, rect.right);
}
}
}
Oops, something went wrong.

0 comments on commit 78d28e4

Please sign in to comment.