aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--main/src/cgeo/geocaching/connector/gc/Tile.java97
-rw-r--r--tests/src/cgeo/geocaching/connector/gc/AutoZoomTest.java49
2 files changed, 119 insertions, 27 deletions
diff --git a/main/src/cgeo/geocaching/connector/gc/Tile.java b/main/src/cgeo/geocaching/connector/gc/Tile.java
index 73ded4d..083179e 100644
--- a/main/src/cgeo/geocaching/connector/gc/Tile.java
+++ b/main/src/cgeo/geocaching/connector/gc/Tile.java
@@ -53,11 +53,15 @@ public class Tile {
private final Viewport viewPort;
public Tile(Geopoint origin, int zoomlevel) {
+ this(calcX(origin, clippedZoomlevel(zoomlevel)), calcY(origin, clippedZoomlevel(zoomlevel)), clippedZoomlevel(zoomlevel));
+ }
+
+ private Tile(int tileX, int tileY, int zoomlevel) {
- this.zoomlevel = Math.max(Math.min(zoomlevel, ZOOMLEVEL_MAX), ZOOMLEVEL_MIN);
+ this.zoomlevel = clippedZoomlevel(zoomlevel);
- tileX = calcX(origin);
- tileY = calcY(origin);
+ this.tileX = tileX;
+ this.tileY = tileY;
viewPort = new Viewport(getCoord(new UTFGridPosition(0, 0)), getCoord(new UTFGridPosition(63, 63)));
}
@@ -66,28 +70,32 @@ public class Tile {
return zoomlevel;
}
+ private static int clippedZoomlevel(int zoomlevel) {
+ return Math.max(Math.min(zoomlevel, ZOOMLEVEL_MAX), ZOOMLEVEL_MIN);
+ }
+
/**
* Calculate the tile for a Geopoint based on the Spherical Mercator.
*
* @see <a
* href="http://developers.cloudmade.com/projects/tiles/examples/convert-coordinates-to-tile-numbers">Cloudmade</a>
*/
- private int calcX(final Geopoint origin) {
- return (int) ((origin.getLongitude() + 180.0) / 360.0 * NUMBER_OF_TILES[this.zoomlevel]);
+ private static int calcX(final Geopoint origin, final int zoomlevel) {
+ return (int) ((origin.getLongitude() + 180.0) / 360.0 * NUMBER_OF_TILES[zoomlevel]);
}
/**
* Calculate the tile for a Geopoint based on the Spherical Mercator.
*
*/
- private int calcY(final Geopoint origin) {
+ private static int calcY(final Geopoint origin, final int zoomlevel) {
// double latRad = Math.toRadians(origin.getLatitude());
// return (int) ((1 - (Math.log(Math.tan(latRad) + (1 / Math.cos(latRad))) / Math.PI)) / 2 * numberOfTiles);
// Optimization from Bing
double sinLatRad = Math.sin(Math.toRadians(origin.getLatitude()));
- return (int) ((0.5 - Math.log((1 + sinLatRad) / (1 - sinLatRad)) / (4 * Math.PI)) * NUMBER_OF_TILES[this.zoomlevel]);
+ return (int) ((0.5 - Math.log((1 + sinLatRad) / (1 - sinLatRad)) / (4 * Math.PI)) * NUMBER_OF_TILES[zoomlevel]);
}
public int getX() {
@@ -121,7 +129,12 @@ public class Tile {
/**
* Calculates the maximum possible zoom level where the supplied points
- * are covered by adjacent tiles on the east/west axis.
+ * are covered by at least by the supplied number of
+ * adjacent tiles on the east/west axis.
+ * This criterion can be exactly met for even numbers of tiles
+ * while it may result in one more tile as requested for odd numbers
+ * of tiles.
+ *
* The order of the points (left/right) is irrelevant.
*
* @param left
@@ -130,17 +143,17 @@ public class Tile {
* Second point
* @return
*/
- public static int calcZoomLon(final Geopoint left, final Geopoint right) {
+ public static int calcZoomLon(final Geopoint left, final Geopoint right, final int numberOfTiles) {
int zoom = (int) Math.floor(
- Math.log(360.0 / Math.abs(left.getLongitude() - right.getLongitude()))
+ Math.log(360.0 * numberOfTiles / (2.0 * Math.abs(left.getLongitude() - right.getLongitude())))
/ Math.log(2)
);
Tile tileLeft = new Tile(left, zoom);
Tile tileRight = new Tile(right, zoom);
- if (tileLeft.tileX == tileRight.tileX) {
+ if (Math.abs(tileLeft.tileX - tileRight.tileX) < (numberOfTiles - 1)) {
zoom += 1;
}
@@ -149,7 +162,12 @@ public class Tile {
/**
* Calculates the maximum possible zoom level where the supplied points
- * are covered by adjacent tiles on the north/south axis.
+ * are covered by at least by the supplied number of
+ * adjacent tiles on the north/south axis.
+ * This criterion can be exactly met for even numbers of tiles
+ * while it may result in one more tile as requested for odd numbers
+ * of tiles.
+ *
* The order of the points (bottom/top) is irrelevant.
*
* @param bottom
@@ -158,21 +176,21 @@ public class Tile {
* Second point
* @return
*/
- public static int calcZoomLat(final Geopoint bottom, final Geopoint top) {
+ public static int calcZoomLat(final Geopoint bottom, final Geopoint top, final int numberOfTiles) {
int zoom = (int) Math.ceil(
- Math.log(2.0 * Math.PI /
+ Math.log(2.0 * Math.PI * numberOfTiles / (
Math.abs(
asinh(tanGrad(bottom.getLatitude()))
- asinh(tanGrad(top.getLatitude()))
- )
+ ) * 2.0)
) / Math.log(2)
);
Tile tileBottom = new Tile(bottom, zoom);
Tile tileTop = new Tile(top, zoom);
- if (Math.abs(tileBottom.tileY - tileTop.tileY) > 1) {
+ if (Math.abs(tileBottom.tileY - tileTop.tileY) > (numberOfTiles - 1)) {
zoom -= 1;
}
@@ -185,7 +203,7 @@ public class Tile {
/**
* Calculates the inverted hyperbolic sine
- * (after Bronstein, Semendjajew: Taschenbuch der Mathematik
+ * (after Bronstein, Semendjajew: Taschenbuch der Mathematik)
*
* @param x
* @return
@@ -233,19 +251,48 @@ public class Tile {
}
/**
- * Calculate needed tiles for the given viewport
- *
+ * Calculate needed tiles for the given viewport to cover it with
+ * max 2x2 tiles
+ *
* @param viewport
* @return
*/
protected static Set<Tile> getTilesForViewport(final Viewport viewport) {
+ return getTilesForViewport(viewport, 2, Tile.ZOOMLEVEL_MIN);
+ }
+
+ /**
+ * Calculate needed tiles for the given viewport.
+ * You can define the minimum number of tiles on the longer axis
+ * and/or the minimum zoom level.
+ *
+ * @param viewport
+ * @param tilesOnAxis
+ * @param minZoom
+ * @return
+ */
+ protected static Set<Tile> getTilesForViewport(final Viewport viewport, final int tilesOnAxis, final int minZoom) {
Set<Tile> tiles = new HashSet<Tile>();
- int zoom = Math.min(Tile.calcZoomLon(viewport.bottomLeft, viewport.topRight),
- Tile.calcZoomLat(viewport.bottomLeft, viewport.topRight));
- tiles.add(new Tile(viewport.bottomLeft, zoom));
- tiles.add(new Tile(new Geopoint(viewport.getLatitudeMin(), viewport.getLongitudeMax()), zoom));
- tiles.add(new Tile(new Geopoint(viewport.getLatitudeMax(), viewport.getLongitudeMin()), zoom));
- tiles.add(new Tile(viewport.topRight, zoom));
+ int zoom = Math.max(
+ Math.min(Tile.calcZoomLon(viewport.bottomLeft, viewport.topRight, tilesOnAxis),
+ Tile.calcZoomLat(viewport.bottomLeft, viewport.topRight, tilesOnAxis)),
+ minZoom);
+
+ Tile tileBottomLeft = new Tile(viewport.bottomLeft, zoom);
+ Tile tileTopRight = new Tile(viewport.topRight, zoom);
+
+ int xLow = Math.min(tileBottomLeft.getX(), tileTopRight.getX());
+ int xHigh = Math.max(tileBottomLeft.getX(), tileTopRight.getX());
+
+ int yLow = Math.min(tileBottomLeft.getY(), tileTopRight.getY());
+ int yHigh = Math.max(tileBottomLeft.getY(), tileTopRight.getY());
+
+ for (int xNum = xLow; xNum <= xHigh; xNum++) {
+ for (int yNum = yLow; yNum <= yHigh; yNum++) {
+ tiles.add(new Tile(xNum, yNum, zoom));
+ }
+ }
+
return tiles;
}
diff --git a/tests/src/cgeo/geocaching/connector/gc/AutoZoomTest.java b/tests/src/cgeo/geocaching/connector/gc/AutoZoomTest.java
index 234ff26..fb49949 100644
--- a/tests/src/cgeo/geocaching/connector/gc/AutoZoomTest.java
+++ b/tests/src/cgeo/geocaching/connector/gc/AutoZoomTest.java
@@ -1,6 +1,9 @@
package cgeo.geocaching.connector.gc;
import cgeo.geocaching.geopoint.Geopoint;
+import cgeo.geocaching.geopoint.Viewport;
+
+import java.util.Set;
import junit.framework.TestCase;
@@ -10,16 +13,58 @@ public class AutoZoomTest extends TestCase {
Geopoint bottomLeft = new Geopoint(49.3, 8.3);
Geopoint topRight = new Geopoint(49.4, 8.4);
- int zoom = Tile.calcZoomLat(bottomLeft, topRight);
+ int zoom = Tile.calcZoomLat(bottomLeft, topRight, 2);
assertTrue(Math.abs(new Tile(bottomLeft, zoom).getY() - new Tile(topRight, zoom).getY()) == 1);
assertTrue(Math.abs(new Tile(bottomLeft, zoom + 1).getY() - new Tile(topRight, zoom + 1).getY()) > 1);
- zoom = Tile.calcZoomLon(bottomLeft, topRight);
+ zoom = Tile.calcZoomLon(bottomLeft, topRight, 2);
assertTrue(new Tile(bottomLeft, zoom).getX() + 1 == new Tile(topRight, zoom).getX());
assertTrue(new Tile(bottomLeft, zoom + 1).getX() + 1 < new Tile(topRight, zoom + 1).getX());
}
+ public static void testZoom2() {
+ Geopoint bottomLeft = new Geopoint(49.3, 8.3);
+ Geopoint topRight = new Geopoint(49.4, 8.4);
+
+ int zoom = Tile.calcZoomLat(bottomLeft, topRight, 3);
+
+ assertTrue(Math.abs(new Tile(bottomLeft, zoom).getY() - new Tile(topRight, zoom).getY()) >= 2);
+ assertTrue(Math.abs(new Tile(bottomLeft, zoom + 1).getY() - new Tile(topRight, zoom + 1).getY()) > 2);
+
+ zoom = Tile.calcZoomLon(bottomLeft, topRight, 3);
+
+ assertTrue(Math.abs(new Tile(bottomLeft, zoom).getX() - new Tile(topRight, zoom).getX()) >= 2);
+ assertTrue(Math.abs(new Tile(bottomLeft, zoom + 1).getX() - new Tile(topRight, zoom + 1).getX()) > 2);
+
+ }
+
+ public static void testTiles1x2() {
+ Geopoint bottomLeft = new Geopoint(49.3, 8.3);
+ Geopoint topRight = new Geopoint(49.4, 8.4);
+
+ Set<Tile> tiles = Tile.getTilesForViewport(new Viewport(bottomLeft, topRight));
+
+ assertEquals(2, tiles.size());
+ }
+
+ public static void testTiles2x3() {
+ Geopoint bottomLeft = new Geopoint(49.3, 8.3);
+ Geopoint topRight = new Geopoint(49.4, 8.4);
+
+ Set<Tile> tiles = Tile.getTilesForViewport(new Viewport(bottomLeft, topRight), 3, Tile.ZOOMLEVEL_MIN);
+
+ assertEquals(6, tiles.size());
+ }
+
+ public static void testTilesZoom13() {
+ Geopoint bottomLeft = new Geopoint(49.3, 8.3);
+ Geopoint topRight = new Geopoint(49.4, 8.4);
+
+ Set<Tile> tiles = Tile.getTilesForViewport(new Viewport(bottomLeft, topRight), 3, 13);
+
+ assertEquals(16, tiles.size());
+ }
}