diff --git a/docs/Changelog.md b/docs/Changelog.md
index febdc923..fd2ecd67 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -6,7 +6,8 @@
- Render themes: line symbol [#124](https://github.com/mapsforge/vtm/issues/124)
- Render themes: stroke dash array [#131](https://github.com/mapsforge/vtm/issues/131)
- POI Search example [#394](https://github.com/mapsforge/vtm/issues/394)
-- Mapsforge fix artifacts for zoom > 17 [#231](https://github.com/mapsforge/vtm/issues/231)
+- Core utilities [#396](https://github.com/mapsforge/vtm/issues/396)
+- Mapsforge fix artifacts zoom > 17 [#231](https://github.com/mapsforge/vtm/issues/231)
- vtm-theme-comparator module [#387](https://github.com/mapsforge/vtm/issues/387)
- Many other minor improvements and bug fixes
- [Solved issues](https://github.com/mapsforge/vtm/issues?q=is%3Aclosed+milestone%3A0.9.0)
diff --git a/vtm-tests/test/org/oscim/core/MercatorProjectionTest.java b/vtm-tests/test/org/oscim/core/MercatorProjectionTest.java
new file mode 100644
index 00000000..1e1fe699
--- /dev/null
+++ b/vtm-tests/test/org/oscim/core/MercatorProjectionTest.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2010, 2011, 2012, 2013 mapsforge.org
+ * Copyright 2017 devemux86
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.core;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class MercatorProjectionTest {
+ private static final int[] TILE_SIZES = {256, 128, 376, 512, 100};
+ private static final int ZOOM_LEVEL_MAX = 30;
+ private static final int ZOOM_LEVEL_MIN = 0;
+
+ private static void verifyInvalidGetMapSize(byte zoomLevel) {
+ try {
+ MercatorProjection.getMapSize(zoomLevel);
+ Assert.fail("zoomLevel: " + zoomLevel);
+ } catch (IllegalArgumentException e) {
+ Assert.assertTrue(true);
+ }
+ }
+
+ private static void verifyInvalidPixelXToLongitude(double pixelX, byte zoomLevel) {
+ try {
+ MercatorProjection.pixelXToLongitude(pixelX, MercatorProjection.getMapSize(zoomLevel));
+ Assert.fail("pixelX: " + pixelX + ", zoomLevel: " + zoomLevel);
+ } catch (IllegalArgumentException e) {
+ Assert.assertTrue(true);
+ }
+ }
+
+ private static void verifyInvalidPixelYToLatitude(double pixelY, byte zoomLevel) {
+ try {
+ MercatorProjection.pixelYToLatitude(pixelY, MercatorProjection.getMapSize(zoomLevel));
+ Assert.fail("pixelY: " + pixelY + ", zoomLevel: " + zoomLevel);
+ } catch (IllegalArgumentException e) {
+ Assert.assertTrue(true);
+ }
+ }
+
+ @Test
+ public void getMapSizeTest() {
+ for (int tileSize : TILE_SIZES) {
+ Tile.SIZE = tileSize;
+ for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) {
+ long factor = Math.round(MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(Tile.SIZE * factor, MercatorProjection.getMapSize(zoomLevel));
+ Assert.assertEquals(MercatorProjection.getMapSizeWithScale(MercatorProjection.zoomLevelToScale(zoomLevel)),
+ MercatorProjection.getMapSize(zoomLevel));
+ }
+ verifyInvalidGetMapSize((byte) -1);
+ }
+ }
+
+ @Test
+ public void latitudeToPixelYTest() {
+ for (int tileSize : TILE_SIZES) {
+ Tile.SIZE = tileSize;
+ for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) {
+ long mapSize = MercatorProjection.getMapSize(zoomLevel);
+ double pixelY = MercatorProjection.latitudeToPixelY(MercatorProjection.LATITUDE_MAX, mapSize);
+ Assert.assertEquals(0, pixelY, 0);
+
+ pixelY = MercatorProjection.latitudeToPixelYWithScale(MercatorProjection.LATITUDE_MAX, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(0, pixelY, 0);
+
+ pixelY = MercatorProjection.latitudeToPixelY(0, mapSize);
+ Assert.assertEquals((float) mapSize / 2, pixelY, 0);
+ pixelY = MercatorProjection.latitudeToPixelYWithScale(0, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals((float) mapSize / 2, pixelY, 0);
+
+ pixelY = MercatorProjection.latitudeToPixelY(MercatorProjection.LATITUDE_MIN, mapSize);
+ Assert.assertEquals(mapSize, pixelY, 0);
+ pixelY = MercatorProjection.latitudeToPixelYWithScale(MercatorProjection.LATITUDE_MIN, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(mapSize, pixelY, 0);
+ }
+ }
+ }
+
+ @Test
+ public void latitudeToTileYTest() {
+ for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) {
+ long tileY = MercatorProjection.latitudeToTileY(MercatorProjection.LATITUDE_MAX, zoomLevel);
+ Assert.assertEquals(0, tileY);
+ tileY = MercatorProjection.latitudeToTileYWithScale(MercatorProjection.LATITUDE_MAX, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(0, tileY);
+
+ tileY = MercatorProjection.latitudeToTileY(MercatorProjection.LATITUDE_MIN, zoomLevel);
+ Assert.assertEquals(Tile.getMaxTileNumber(zoomLevel), tileY);
+ tileY = MercatorProjection.latitudeToTileYWithScale(MercatorProjection.LATITUDE_MIN, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(Tile.getMaxTileNumber(zoomLevel), tileY);
+ }
+ }
+
+ @Test
+ public void longitudeToPixelXTest() {
+ for (int tileSize : TILE_SIZES) {
+ Tile.SIZE = tileSize;
+ for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) {
+ long mapSize = MercatorProjection.getMapSize(zoomLevel);
+ double pixelX = MercatorProjection.longitudeToPixelX(MercatorProjection.LONGITUDE_MIN, mapSize);
+ Assert.assertEquals(0, pixelX, 0);
+ pixelX = MercatorProjection.longitudeToPixelXWithScale(MercatorProjection.LONGITUDE_MIN, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(0, pixelX, 0);
+
+ pixelX = MercatorProjection.longitudeToPixelX(0, mapSize);
+ Assert.assertEquals((float) mapSize / 2, pixelX, 0);
+ mapSize = MercatorProjection.getMapSizeWithScale(MercatorProjection.zoomLevelToScale(zoomLevel));
+ pixelX = MercatorProjection.longitudeToPixelXWithScale(0, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals((float) mapSize / 2, pixelX, 0);
+
+ pixelX = MercatorProjection.longitudeToPixelX(MercatorProjection.LONGITUDE_MAX, mapSize);
+ Assert.assertEquals(mapSize, pixelX, 0);
+ pixelX = MercatorProjection.longitudeToPixelXWithScale(MercatorProjection.LONGITUDE_MAX, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(mapSize, pixelX, 0);
+ }
+ }
+ }
+
+ @Test
+ public void longitudeToTileXTest() {
+ for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) {
+ long tileX = MercatorProjection.longitudeToTileX(MercatorProjection.LONGITUDE_MIN, zoomLevel);
+ Assert.assertEquals(0, tileX);
+ tileX = MercatorProjection.longitudeToTileXWithScale(MercatorProjection.LONGITUDE_MIN, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(0, tileX);
+
+ tileX = MercatorProjection.longitudeToTileX(MercatorProjection.LONGITUDE_MAX, zoomLevel);
+ Assert.assertEquals(Tile.getMaxTileNumber(zoomLevel), tileX);
+ tileX = MercatorProjection.longitudeToTileXWithScale(MercatorProjection.LONGITUDE_MAX, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(Tile.getMaxTileNumber(zoomLevel), tileX);
+ }
+ }
+
+ @Test
+ public void metersToPixelTest() {
+ for (int tileSize : TILE_SIZES) {
+ Tile.SIZE = tileSize;
+ Assert.assertTrue(MercatorProjection.metersToPixels(10, 10.0, MercatorProjection.getMapSize((byte) 1)) < 1);
+ Assert.assertTrue(MercatorProjection.metersToPixels((int) (40 * 10e7), 10.0, MercatorProjection.getMapSize((byte) 1)) > 1);
+ Assert.assertTrue(MercatorProjection.metersToPixels(10, 10.0, MercatorProjection.getMapSize((byte) 20)) > 1);
+ Assert.assertTrue(MercatorProjection.metersToPixels(10, 89.0, MercatorProjection.getMapSize((byte) 1)) < 1);
+ Assert.assertTrue(MercatorProjection.metersToPixels((int) (40 * 10e3), 50, MercatorProjection.getMapSize((byte) 10)) > 1);
+ }
+ }
+
+ @Test
+ public void pixelXToLongitudeTest() {
+ for (int tileSize : TILE_SIZES) {
+ Tile.SIZE = tileSize;
+ for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) {
+ long mapSize = MercatorProjection.getMapSize(zoomLevel);
+ double longitude = MercatorProjection.pixelXToLongitude(0, mapSize);
+ Assert.assertEquals(MercatorProjection.LONGITUDE_MIN, longitude, 0);
+ longitude = MercatorProjection.pixelXToLongitudeWithScale(0, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(MercatorProjection.LONGITUDE_MIN, longitude, 0);
+
+ longitude = MercatorProjection.pixelXToLongitude((float) mapSize / 2, mapSize);
+ Assert.assertEquals(0, longitude, 0);
+ mapSize = MercatorProjection.getMapSizeWithScale(MercatorProjection.zoomLevelToScale(zoomLevel));
+ longitude = MercatorProjection.pixelXToLongitudeWithScale((float) mapSize / 2, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(0, longitude, 0);
+
+ longitude = MercatorProjection.pixelXToLongitude(mapSize, mapSize);
+ Assert.assertEquals(MercatorProjection.LONGITUDE_MAX, longitude, 0);
+ longitude = MercatorProjection.pixelXToLongitudeWithScale(mapSize, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(MercatorProjection.LONGITUDE_MAX, longitude, 0);
+ }
+
+ verifyInvalidPixelXToLongitude(-1, (byte) 0);
+ verifyInvalidPixelXToLongitude(Tile.SIZE + 1, (byte) 0);
+ }
+ }
+
+ @Test
+ public void pixelXToTileXTest() {
+ for (int tileSize : TILE_SIZES) {
+ Tile.SIZE = tileSize;
+ for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) {
+ Assert.assertEquals(0, MercatorProjection.pixelXToTileX(0, zoomLevel));
+ Assert.assertEquals(0, MercatorProjection.pixelXToTileXWithScale(0, MercatorProjection.zoomLevelToScale(zoomLevel)));
+ }
+ }
+ }
+
+ @Test
+ public void pixelYToLatitudeTest() {
+ for (int tileSize : TILE_SIZES) {
+ Tile.SIZE = tileSize;
+ for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) {
+ long mapSize = MercatorProjection.getMapSize(zoomLevel);
+ double latitude = MercatorProjection.pixelYToLatitude(0, mapSize);
+ Assert.assertEquals(MercatorProjection.LATITUDE_MAX, latitude, 0);
+ latitude = MercatorProjection.pixelYToLatitudeWithScale(0, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(MercatorProjection.LATITUDE_MAX, latitude, 0);
+
+ latitude = MercatorProjection.pixelYToLatitude((float) mapSize / 2, mapSize);
+ Assert.assertEquals(0, latitude, 0);
+ mapSize = MercatorProjection.getMapSizeWithScale(MercatorProjection.zoomLevelToScale(zoomLevel));
+ latitude = MercatorProjection.pixelYToLatitudeWithScale((float) mapSize / 2, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(0, latitude, 0);
+
+ latitude = MercatorProjection.pixelYToLatitude(mapSize, mapSize);
+ Assert.assertEquals(MercatorProjection.LATITUDE_MIN, latitude, 0);
+ latitude = MercatorProjection.pixelYToLatitudeWithScale(mapSize, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(MercatorProjection.LATITUDE_MIN, latitude, 0);
+ }
+
+ verifyInvalidPixelYToLatitude(-1, (byte) 0);
+ verifyInvalidPixelYToLatitude(Tile.SIZE + 1, (byte) 0);
+ }
+ }
+
+ @Test
+ public void pixelYToTileYTest() {
+ for (int tileSize : TILE_SIZES) {
+ Tile.SIZE = tileSize;
+ for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) {
+ Assert.assertEquals(0, MercatorProjection.pixelYToTileY(0, zoomLevel));
+ Assert.assertEquals(0, MercatorProjection.pixelYToTileYWithScale(0, MercatorProjection.zoomLevelToScale(zoomLevel)));
+ }
+ }
+ }
+
+ @Test
+ public void tileToPixelTest() {
+ for (int tileSize : TILE_SIZES) {
+ Tile.SIZE = tileSize;
+ Assert.assertEquals(0, MercatorProjection.tileToPixel(0));
+ Assert.assertEquals(Tile.SIZE, MercatorProjection.tileToPixel(1));
+ Assert.assertEquals(Tile.SIZE * 2, MercatorProjection.tileToPixel(2));
+ }
+ }
+
+ @Test
+ public void tileXToLongitudeTest() {
+ for (int tileSize : TILE_SIZES) {
+ Tile.SIZE = tileSize;
+ for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) {
+ double longitude = MercatorProjection.tileXToLongitude(0, zoomLevel);
+ Assert.assertEquals(MercatorProjection.LONGITUDE_MIN, longitude, 0);
+ longitude = MercatorProjection.tileXToLongitudeWithScale(0, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(MercatorProjection.LONGITUDE_MIN, longitude, 0);
+
+ long tileX = MercatorProjection.getMapSize(zoomLevel) / Tile.SIZE;
+ longitude = MercatorProjection.tileXToLongitude(tileX, zoomLevel);
+ Assert.assertEquals(MercatorProjection.LONGITUDE_MAX, longitude, 0);
+ tileX = MercatorProjection.getMapSizeWithScale(MercatorProjection.zoomLevelToScale(zoomLevel)) / Tile.SIZE;
+ longitude = MercatorProjection.tileXToLongitudeWithScale(tileX, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(MercatorProjection.LONGITUDE_MAX, longitude, 0);
+ }
+ }
+ }
+
+ @Test
+ public void tileYToLatitudeTest() {
+ for (int tileSize : TILE_SIZES) {
+ Tile.SIZE = tileSize;
+ for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) {
+ double latitude = MercatorProjection.tileYToLatitude(0, zoomLevel);
+ Assert.assertEquals(MercatorProjection.LATITUDE_MAX, latitude, 0);
+ latitude = MercatorProjection.tileYToLatitudeWithScale(0, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(MercatorProjection.LATITUDE_MAX, latitude, 0);
+
+ long tileY = MercatorProjection.getMapSize(zoomLevel) / Tile.SIZE;
+ latitude = MercatorProjection.tileYToLatitude(tileY, zoomLevel);
+ Assert.assertEquals(MercatorProjection.LATITUDE_MIN, latitude, 0);
+ tileY = MercatorProjection.getMapSizeWithScale(MercatorProjection.zoomLevelToScale(zoomLevel)) / Tile.SIZE;
+ latitude = MercatorProjection.tileYToLatitudeWithScale(tileY, MercatorProjection.zoomLevelToScale(zoomLevel));
+ Assert.assertEquals(MercatorProjection.LATITUDE_MIN, latitude, 0);
+ }
+ }
+ }
+
+ @Test
+ public void zoomLevelToScaleTest() {
+ for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) {
+ double scale = MercatorProjection.zoomLevelToScale(zoomLevel);
+ Assert.assertEquals(zoomLevel, MercatorProjection.scaleToZoomLevel(scale), 0.0001f);
+ }
+ }
+}
diff --git a/vtm/src/org/oscim/core/BoundingBox.java b/vtm/src/org/oscim/core/BoundingBox.java
index fd183daa..4de0b816 100644
--- a/vtm/src/org/oscim/core/BoundingBox.java
+++ b/vtm/src/org/oscim/core/BoundingBox.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
* Copyright 2014 Hannes Janetzek
- * Copyright 2016 devemux86
+ * Copyright 2016-2017 devemux86
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
@@ -321,6 +321,46 @@ public class BoundingBox {
&& getMinLatitude() <= boundingBox.getMaxLatitude() && getMinLongitude() <= boundingBox.getMaxLongitude();
}
+ /**
+ * Returns if an area built from the geoPoints intersects with a bias towards
+ * returning true.
+ * The method returns fast if any of the points lie within the bbox. If none of the points
+ * lie inside the box, it constructs the outer bbox for all the points and tests for intersection
+ * (so it is possible that the area defined by the points does not actually intersect)
+ *
+ * @param geoPoints the points that define an area
+ * @return false if there is no intersection, true if there could be an intersection
+ */
+ public boolean intersectsArea(GeoPoint[][] geoPoints) {
+ if (geoPoints.length == 0 || geoPoints[0].length == 0) {
+ return false;
+ }
+ for (GeoPoint[] outer : geoPoints) {
+ for (GeoPoint geoPoint : outer) {
+ if (this.contains(geoPoint)) {
+ // if any of the points is inside the bbox return early
+ return true;
+ }
+ }
+ }
+
+ // no fast solution, so accumulate boundary points
+ double tmpMinLat = geoPoints[0][0].getLatitude();
+ double tmpMinLon = geoPoints[0][0].getLongitude();
+ double tmpMaxLat = geoPoints[0][0].getLatitude();
+ double tmpMaxLon = geoPoints[0][0].getLongitude();
+
+ for (GeoPoint[] outer : geoPoints) {
+ for (GeoPoint geoPoint : outer) {
+ tmpMinLat = Math.min(tmpMinLat, geoPoint.getLatitude());
+ tmpMaxLat = Math.max(tmpMaxLat, geoPoint.getLatitude());
+ tmpMinLon = Math.min(tmpMinLon, geoPoint.getLongitude());
+ tmpMaxLon = Math.max(tmpMaxLon, geoPoint.getLongitude());
+ }
+ }
+ return this.intersects(new BoundingBox(tmpMinLat, tmpMinLon, tmpMaxLat, tmpMaxLon));
+ }
+
@Override
public String toString() {
return new StringBuilder()
diff --git a/vtm/src/org/oscim/core/MercatorProjection.java b/vtm/src/org/oscim/core/MercatorProjection.java
index ba497847..e4c81020 100644
--- a/vtm/src/org/oscim/core/MercatorProjection.java
+++ b/vtm/src/org/oscim/core/MercatorProjection.java
@@ -1,7 +1,8 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
* Copyright 2012 Hannes Janetzek
- * Copyright 2016 devemux86
+ * Copyright 2014 Ludwig M Brinckmann
+ * Copyright 2016-2017 devemux86
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
@@ -22,6 +23,13 @@ import org.oscim.utils.FastMath;
/**
* An implementation of the spherical Mercator projection.
+ *
+ * There are generally two methods for each operation: one taking a byte zoom level and
+ * a parallel one taking a double scale. The scale is Math.pow(2, zoomLevel)
+ * for a given zoom level, but the operations take intermediate values as well.
+ * The zoom level operation is a little faster as it can make use of shift operations,
+ * the scale operation offers greater flexibility in computing the values for
+ * intermediate zoom levels.
*/
public final class MercatorProjection {
/**
@@ -49,6 +57,106 @@ public final class MercatorProjection {
*/
public static final double LONGITUDE_MIN = -LONGITUDE_MAX;
+ /**
+ * Get GeoPoint from Pixels.
+ */
+ public static GeoPoint fromPixelsWithScale(double pixelX, double pixelY, double scale) {
+ return new GeoPoint(pixelYToLatitudeWithScale(pixelY, scale),
+ pixelXToLongitudeWithScale(pixelX, scale));
+ }
+
+ /**
+ * Get GeoPoint from Pixels.
+ */
+ public static GeoPoint fromPixels(double pixelX, double pixelY, long mapSize) {
+ return new GeoPoint(pixelYToLatitude(pixelY, mapSize),
+ pixelXToLongitude(pixelX, mapSize));
+ }
+
+ /**
+ * @param scale the scale factor for which the size of the world map should be returned.
+ * @return the horizontal and vertical size of the map in pixel at the given scale.
+ * @throws IllegalArgumentException if the given scale factor is < 1
+ */
+ public static long getMapSizeWithScale(double scale) {
+ if (scale < 1) {
+ throw new IllegalArgumentException("scale factor must not < 1 " + scale);
+ }
+ return (long) (Tile.SIZE * (Math.pow(2, scaleToZoomLevel(scale))));
+ }
+
+ /**
+ * @param zoomLevel the zoom level for which the size of the world map should be returned.
+ * @return the horizontal and vertical size of the map in pixel at the given zoom level.
+ * @throws IllegalArgumentException if the given zoom level is negative.
+ */
+ public static long getMapSize(byte zoomLevel) {
+ if (zoomLevel < 0) {
+ throw new IllegalArgumentException("zoom level must not be negative: " + zoomLevel);
+ }
+ return (long) Tile.SIZE << zoomLevel;
+ }
+
+ public static Point getPixelWithScale(GeoPoint geoPoint, double scale) {
+ double pixelX = MercatorProjection.longitudeToPixelXWithScale(geoPoint.getLongitude(), scale);
+ double pixelY = MercatorProjection.latitudeToPixelYWithScale(geoPoint.getLatitude(), scale);
+ return new Point(pixelX, pixelY);
+ }
+
+ public static Point getPixel(GeoPoint geoPoint, long mapSize) {
+ double pixelX = MercatorProjection.longitudeToPixelX(geoPoint.getLongitude(), mapSize);
+ double pixelY = MercatorProjection.latitudeToPixelY(geoPoint.getLatitude(), mapSize);
+ return new Point(pixelX, pixelY);
+ }
+
+ /**
+ * Calculates the absolute pixel position for a map size and tile size
+ *
+ * @param geoPoint the geographic position.
+ * @param mapSize precomputed size of map.
+ * @return the absolute pixel coordinates (for world)
+ */
+
+ public static Point getPixelAbsolute(GeoPoint geoPoint, long mapSize) {
+ return getPixelRelative(geoPoint, mapSize, 0, 0);
+ }
+
+ /**
+ * Calculates the absolute pixel position for a map size and tile size relative to origin
+ *
+ * @param geoPoint the geographic position.
+ * @param mapSize precomputed size of map.
+ * @return the relative pixel position to the origin values (e.g. for a tile)
+ */
+ public static Point getPixelRelative(GeoPoint geoPoint, long mapSize, double x, double y) {
+ double pixelX = MercatorProjection.longitudeToPixelX(geoPoint.getLongitude(), mapSize) - x;
+ double pixelY = MercatorProjection.latitudeToPixelY(geoPoint.getLatitude(), mapSize) - y;
+ return new Point(pixelX, pixelY);
+ }
+
+
+ /**
+ * Calculates the absolute pixel position for a map size and tile size relative to origin
+ *
+ * @param geoPoint the geographic position.
+ * @param mapSize precomputed size of map.
+ * @return the relative pixel position to the origin values (e.g. for a tile)
+ */
+ public static Point getPixelRelative(GeoPoint geoPoint, long mapSize, Point origin) {
+ return getPixelRelative(geoPoint, mapSize, origin.x, origin.y);
+ }
+
+ /**
+ * Calculates the absolute pixel position for a tile and tile size relative to origin
+ *
+ * @param geoPoint the geographic position.
+ * @param tile tile
+ * @return the relative pixel position to the origin values (e.g. for a tile)
+ */
+ public static Point getPixelRelativeToTile(GeoPoint geoPoint, Tile tile) {
+ return getPixelRelative(geoPoint, tile.mapSize, tile.getOrigin());
+ }
+
/**
* Calculates the distance on the ground that is represented by a single
* pixel on the map.
@@ -56,9 +164,9 @@ public final class MercatorProjection {
* @param latitude the latitude coordinate at which the resolution should be
* calculated.
* @param scale the map scale at which the resolution should be calculated.
- * @return the ground resolution at the given latitude and zoom level.
+ * @return the ground resolution at the given latitude and scale.
*/
- public static double groundResolution(double latitude, double scale) {
+ public static double groundResolutionWithScale(double latitude, double scale) {
return Math.cos(latitude * (Math.PI / 180)) * EARTH_CIRCUMFERENCE
/ (Tile.SIZE * scale);
}
@@ -70,6 +178,83 @@ public final class MercatorProjection {
/ (Tile.SIZE * pos.scale));
}
+ /**
+ * Calculates the distance on the ground that is represented by a single pixel on the map.
+ *
+ * @param latitude the latitude coordinate at which the resolution should be calculated.
+ * @param mapSize precomputed size of map.
+ * @return the ground resolution at the given latitude and map size.
+ */
+ public static double groundResolution(double latitude, long mapSize) {
+ return Math.cos(latitude * (Math.PI / 180)) * EARTH_CIRCUMFERENCE / mapSize;
+ }
+
+ /**
+ * Converts a latitude coordinate (in degrees) to a pixel Y coordinate at a certain scale.
+ *
+ * @param latitude the latitude coordinate that should be converted.
+ * @param scale the scale factor at which the coordinate should be converted.
+ * @return the pixel Y coordinate of the latitude value.
+ */
+ public static double latitudeToPixelYWithScale(double latitude, double scale) {
+ double sinLatitude = Math.sin(latitude * (Math.PI / 180));
+ long mapSize = getMapSizeWithScale(scale);
+ // FIXME improve this formula so that it works correctly without the clipping
+ double pixelY = (0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI)) * mapSize;
+ return Math.min(Math.max(0, pixelY), mapSize);
+ }
+
+ /**
+ * Converts a latitude coordinate (in degrees) to a pixel Y coordinate at a certain zoom level.
+ *
+ * @param latitude the latitude coordinate that should be converted.
+ * @param zoomLevel the zoom level at which the coordinate should be converted.
+ * @return the pixel Y coordinate of the latitude value.
+ */
+ public static double latitudeToPixelY(double latitude, byte zoomLevel) {
+ double sinLatitude = Math.sin(latitude * (Math.PI / 180));
+ long mapSize = getMapSize(zoomLevel);
+ // FIXME improve this formula so that it works correctly without the clipping
+ double pixelY = (0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI)) * mapSize;
+ return Math.min(Math.max(0, pixelY), mapSize);
+ }
+
+ /**
+ * Converts a latitude coordinate (in degrees) to a pixel Y coordinate at a certain map size.
+ *
+ * @param latitude the latitude coordinate that should be converted.
+ * @param mapSize precomputed size of map.
+ * @return the pixel Y coordinate of the latitude value.
+ */
+ public static double latitudeToPixelY(double latitude, long mapSize) {
+ double sinLatitude = Math.sin(latitude * (Math.PI / 180));
+ // FIXME improve this formula so that it works correctly without the clipping
+ double pixelY = (0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI)) * mapSize;
+ return Math.min(Math.max(0, pixelY), mapSize);
+ }
+
+ /**
+ * Converts a latitude coordinate (in degrees) to a tile Y number at a certain scale.
+ *
+ * @param latitude the latitude coordinate that should be converted.
+ * @param scale the scale factor at which the coordinate should be converted.
+ * @return the tile Y number of the latitude value.
+ */
+ public static int latitudeToTileYWithScale(double latitude, double scale) {
+ return pixelYToTileYWithScale(latitudeToPixelYWithScale(latitude, scale), scale);
+ }
+
+ /**
+ * Converts a latitude coordinate (in degrees) to a tile Y number at a certain zoom level.
+ *
+ * @param latitude the latitude coordinate that should be converted.
+ * @param zoomLevel the zoom level at which the coordinate should be converted.
+ * @return the tile Y number of the latitude value.
+ */
+ public static int latitudeToTileY(double latitude, byte zoomLevel) {
+ return pixelYToTileY(latitudeToPixelY(latitude, zoomLevel), zoomLevel);
+ }
+
/**
* Projects a latitude coordinate (in degrees) to the range [0.0,1.0]
*
@@ -81,8 +266,78 @@ public final class MercatorProjection {
return FastMath.clamp(0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI), 0.0, 1.0);
}
- public static double toLatitude(double y) {
- return 90 - 360 * Math.atan(Math.exp((y - 0.5) * (2 * Math.PI))) / Math.PI;
+ /**
+ * @param latitude the latitude value which should be checked.
+ * @return the given latitude value, limited to the possible latitude range.
+ */
+ public static double limitLatitude(double latitude) {
+ return Math.max(Math.min(latitude, LATITUDE_MAX), LATITUDE_MIN);
+ }
+
+ /**
+ * @param longitude the longitude value which should be checked.
+ * @return the given longitude value, limited to the possible longitude
+ * range.
+ */
+ public static double limitLongitude(double longitude) {
+ return Math.max(Math.min(longitude, LONGITUDE_MAX), LONGITUDE_MIN);
+ }
+
+ /**
+ * Converts a longitude coordinate (in degrees) to a pixel X coordinate at a certain scale factor.
+ *
+ * @param longitude the longitude coordinate that should be converted.
+ * @param scale the scale factor at which the coordinate should be converted.
+ * @return the pixel X coordinate of the longitude value.
+ */
+ public static double longitudeToPixelXWithScale(double longitude, double scale) {
+ long mapSize = getMapSizeWithScale(scale);
+ return (longitude + 180) / 360 * mapSize;
+ }
+
+ /**
+ * Converts a longitude coordinate (in degrees) to a pixel X coordinate at a certain zoom level.
+ *
+ * @param longitude the longitude coordinate that should be converted.
+ * @param zoomLevel the zoom level at which the coordinate should be converted.
+ * @return the pixel X coordinate of the longitude value.
+ */
+ public static double longitudeToPixelX(double longitude, byte zoomLevel) {
+ long mapSize = getMapSize(zoomLevel);
+ return (longitude + 180) / 360 * mapSize;
+ }
+
+ /**
+ * Converts a longitude coordinate (in degrees) to a pixel X coordinate at a certain map size.
+ *
+ * @param longitude the longitude coordinate that should be converted.
+ * @param mapSize precomputed size of map.
+ * @return the pixel X coordinate of the longitude value.
+ */
+ public static double longitudeToPixelX(double longitude, long mapSize) {
+ return (longitude + 180) / 360 * mapSize;
+ }
+
+ /**
+ * Converts a longitude coordinate (in degrees) to the tile X number at a certain scale factor.
+ *
+ * @param longitude the longitude coordinate that should be converted.
+ * @param scale the scale factor at which the coordinate should be converted.
+ * @return the tile X number of the longitude value.
+ */
+ public static int longitudeToTileXWithScale(double longitude, double scale) {
+ return pixelXToTileXWithScale(longitudeToPixelXWithScale(longitude, scale), scale);
+ }
+
+ /**
+ * Converts a longitude coordinate (in degrees) to the tile X number at a certain zoom level.
+ *
+ * @param longitude the longitude coordinate that should be converted.
+ * @param zoomLevel the zoom level at which the coordinate should be converted.
+ * @return the tile X number of the longitude value.
+ */
+ public static int longitudeToTileX(double longitude, byte zoomLevel) {
+ return pixelXToTileX(longitudeToPixelX(longitude, zoomLevel), zoomLevel);
}
/**
@@ -95,8 +350,137 @@ public final class MercatorProjection {
return (longitude + 180.0) / 360.0;
}
- public static double toLongitude(double x) {
- return 360.0 * (x - 0.5);
+ /**
+ * Converts meters to pixels at latitude for zoom-level.
+ *
+ * @param meters the meters to convert
+ * @param latitude the latitude for the conversion.
+ * @param scale the scale factor for the conversion.
+ * @return pixels that represent the meters at the given zoom-level and latitude.
+ */
+ public static double metersToPixelsWithScale(float meters, double latitude, double scale) {
+ return meters / MercatorProjection.groundResolutionWithScale(latitude, scale);
+ }
+
+ /**
+ * Converts meters to pixels at latitude for zoom-level.
+ *
+ * @param meters the meters to convert
+ * @param latitude the latitude for the conversion.
+ * @param mapSize precomputed size of map.
+ * @return pixels that represent the meters at the given zoom-level and latitude.
+ */
+ public static double metersToPixels(float meters, double latitude, long mapSize) {
+ return meters / MercatorProjection.groundResolution(latitude, mapSize);
+ }
+
+ /**
+ * Converts a pixel X coordinate at a certain scale to a longitude coordinate.
+ *
+ * @param pixelX the pixel X coordinate that should be converted.
+ * @param scale the scale factor at which the coordinate should be converted.
+ * @return the longitude value of the pixel X coordinate.
+ * @throws IllegalArgumentException if the given pixelX coordinate is invalid.
+ */
+ public static double pixelXToLongitudeWithScale(double pixelX, double scale) {
+ long mapSize = getMapSizeWithScale(scale);
+ if (pixelX < 0 || pixelX > mapSize) {
+ throw new IllegalArgumentException("invalid pixelX coordinate at scale " + scale + ": " + pixelX);
+ }
+ return 360 * ((pixelX / mapSize) - 0.5);
+ }
+
+ /**
+ * Converts a pixel X coordinate at a certain map size to a longitude coordinate.
+ *
+ * @param pixelX the pixel X coordinate that should be converted.
+ * @param mapSize precomputed size of map.
+ * @return the longitude value of the pixel X coordinate.
+ * @throws IllegalArgumentException if the given pixelX coordinate is invalid.
+ */
+
+ public static double pixelXToLongitude(double pixelX, long mapSize) {
+ if (pixelX < 0 || pixelX > mapSize) {
+ throw new IllegalArgumentException("invalid pixelX coordinate " + mapSize + ": " + pixelX);
+ }
+ return 360 * ((pixelX / mapSize) - 0.5);
+ }
+
+ /**
+ * Converts a pixel X coordinate to the tile X number.
+ *
+ * @param pixelX the pixel X coordinate that should be converted.
+ * @param scale the scale factor at which the coordinate should be converted.
+ * @return the tile X number.
+ */
+ public static int pixelXToTileXWithScale(double pixelX, double scale) {
+ return (int) Math.min(Math.max(pixelX / Tile.SIZE, 0), scale - 1);
+ }
+
+ /**
+ * Converts a pixel X coordinate to the tile X number.
+ *
+ * @param pixelX the pixel X coordinate that should be converted.
+ * @param zoomLevel the zoom level at which the coordinate should be converted.
+ * @return the tile X number.
+ */
+ public static int pixelXToTileX(double pixelX, byte zoomLevel) {
+ return (int) Math.min(Math.max(pixelX / Tile.SIZE, 0), Math.pow(2, zoomLevel) - 1);
+ }
+
+ /**
+ * Converts a pixel Y coordinate at a certain scale to a latitude coordinate.
+ *
+ * @param pixelY the pixel Y coordinate that should be converted.
+ * @param scale the scale factor at which the coordinate should be converted.
+ * @return the latitude value of the pixel Y coordinate.
+ * @throws IllegalArgumentException if the given pixelY coordinate is invalid.
+ */
+ public static double pixelYToLatitudeWithScale(double pixelY, double scale) {
+ long mapSize = getMapSizeWithScale(scale);
+ if (pixelY < 0 || pixelY > mapSize) {
+ throw new IllegalArgumentException("invalid pixelY coordinate at scale " + scale + ": " + pixelY);
+ }
+ double y = 0.5 - (pixelY / mapSize);
+ return 90 - 360 * Math.atan(Math.exp(-y * (2 * Math.PI))) / Math.PI;
+ }
+
+ /**
+ * Converts a pixel Y coordinate at a certain map size to a latitude coordinate.
+ *
+ * @param pixelY the pixel Y coordinate that should be converted.
+ * @param mapSize precomputed size of map.
+ * @return the latitude value of the pixel Y coordinate.
+ * @throws IllegalArgumentException if the given pixelY coordinate is invalid.
+ */
+ public static double pixelYToLatitude(double pixelY, long mapSize) {
+ if (pixelY < 0 || pixelY > mapSize) {
+ throw new IllegalArgumentException("invalid pixelY coordinate " + mapSize + ": " + pixelY);
+ }
+ double y = 0.5 - (pixelY / mapSize);
+ return 90 - 360 * Math.atan(Math.exp(-y * (2 * Math.PI))) / Math.PI;
+ }
+
+ /**
+ * Converts a pixel Y coordinate to the tile Y number.
+ *
+ * @param pixelY the pixel Y coordinate that should be converted.
+ * @param scale the scale factor at which the coordinate should be converted.
+ * @return the tile Y number.
+ */
+ public static int pixelYToTileYWithScale(double pixelY, double scale) {
+ return (int) Math.min(Math.max(pixelY / Tile.SIZE, 0), scale - 1);
+ }
+
+ /**
+ * Converts a pixel Y coordinate to the tile Y number.
+ *
+ * @param pixelY the pixel Y coordinate that should be converted.
+ * @param zoomLevel the zoom level at which the coordinate should be converted.
+ * @return the tile Y number.
+ */
+ public static int pixelYToTileY(double pixelY, byte zoomLevel) {
+ return (int) Math.min(Math.max(pixelY / Tile.SIZE, 0), Math.pow(2, zoomLevel) - 1);
}
public static Point project(GeoPoint p, Point reuse) {
@@ -130,20 +514,75 @@ public final class MercatorProjection {
}
/**
- * @param latitude the latitude value which should be checked.
- * @return the given latitude value, limited to the possible latitude range.
+ * Converts a scale factor to a zoomLevel.
+ * Note that this will return a double, as the scale factors cover the
+ * intermediate zoom levels as well.
+ *
+ * @param scale the scale factor to convert to a zoom level.
+ * @return the zoom level.
*/
- public static double limitLatitude(double latitude) {
- return Math.max(Math.min(latitude, LATITUDE_MAX), LATITUDE_MIN);
+ public static double scaleToZoomLevel(double scale) {
+ return FastMath.log2((int) scale);
}
/**
- * @param longitude the longitude value which should be checked.
- * @return the given longitude value, limited to the possible longitude
- * range.
+ * @param tileNumber the tile number that should be converted.
+ * @return the pixel coordinate for the given tile number.
*/
- public static double limitLongitude(double longitude) {
- return Math.max(Math.min(longitude, LONGITUDE_MAX), LONGITUDE_MIN);
+ public static long tileToPixel(long tileNumber) {
+ return tileNumber * Tile.SIZE;
+ }
+
+ /**
+ * Converts a tile X number at a certain scale to a longitude coordinate.
+ *
+ * @param tileX the tile X number that should be converted.
+ * @param scale the scale factor at which the number should be converted.
+ * @return the longitude value of the tile X number.
+ */
+ public static double tileXToLongitudeWithScale(long tileX, double scale) {
+ return pixelXToLongitudeWithScale(tileX * Tile.SIZE, scale);
+ }
+
+ /**
+ * Converts a tile X number at a certain zoom level to a longitude coordinate.
+ *
+ * @param tileX the tile X number that should be converted.
+ * @param zoomLevel the zoom level at which the number should be converted.
+ * @return the longitude value of the tile X number.
+ */
+ public static double tileXToLongitude(long tileX, byte zoomLevel) {
+ return pixelXToLongitude(tileX * Tile.SIZE, getMapSize(zoomLevel));
+ }
+
+ /**
+ * Converts a tile Y number at a certain scale to a latitude coordinate.
+ *
+ * @param tileY the tile Y number that should be converted.
+ * @param scale the scale factor at which the number should be converted.
+ * @return the latitude value of the tile Y number.
+ */
+ public static double tileYToLatitudeWithScale(long tileY, double scale) {
+ return pixelYToLatitudeWithScale(tileY * Tile.SIZE, scale);
+ }
+
+ /**
+ * Converts a tile Y number at a certain zoom level to a latitude coordinate.
+ *
+ * @param tileY the tile Y number that should be converted.
+ * @param zoomLevel the zoom level at which the number should be converted.
+ * @return the latitude value of the tile Y number.
+ */
+ public static double tileYToLatitude(long tileY, byte zoomLevel) {
+ return pixelYToLatitude(tileY * Tile.SIZE, getMapSize(zoomLevel));
+ }
+
+ public static double toLatitude(double y) {
+ return 90 - 360 * Math.atan(Math.exp((y - 0.5) * (2 * Math.PI))) / Math.PI;
+ }
+
+ public static double toLongitude(double x) {
+ return 360.0 * (x - 0.5);
}
public static double wrapLongitude(double longitude) {
@@ -155,6 +594,16 @@ public final class MercatorProjection {
return longitude;
}
+ /**
+ * Converts a zoom level to a scale factor.
+ *
+ * @param zoomLevel the zoom level to convert.
+ * @return the corresponding scale factor.
+ */
+ public static double zoomLevelToScale(byte zoomLevel) {
+ return 1 << zoomLevel;
+ }
+
private MercatorProjection() {
}
}
diff --git a/vtm/src/org/oscim/core/Tile.java b/vtm/src/org/oscim/core/Tile.java
index b1097490..51ae0ece 100644
--- a/vtm/src/org/oscim/core/Tile.java
+++ b/vtm/src/org/oscim/core/Tile.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
* Copyright 2013 Hannes Janetzek
- * Copyright 2016 devemux86
+ * Copyright 2016-2017 devemux86
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
@@ -40,6 +40,11 @@ public class Tile {
*/
public static int TILE_SIZE_MULTIPLE = 64;
+ /**
+ * the map size implied by zoom level and tileSize, to avoid multiple computations.
+ */
+ public final long mapSize;
+
/**
* The X number of this tile.
*/
@@ -55,6 +60,9 @@ public class Tile {
*/
public final byte zoomLevel;
+ private BoundingBox boundingBox;
+ private Point origin;
+
/**
* @param tileX the X number of the tile.
* @param tileY the Y number of the tile.
@@ -64,6 +72,7 @@ public class Tile {
this.tileX = tileX;
this.tileY = tileY;
this.zoomLevel = zoomLevel;
+ this.mapSize = MercatorProjection.getMapSize(zoomLevel);
}
@Override
@@ -119,4 +128,63 @@ public class Tile {
return Math.max(TILE_SIZE_MULTIPLE,
Math.round(scaled / TILE_SIZE_MULTIPLE) * TILE_SIZE_MULTIPLE);
}
+
+ /**
+ * Gets the geographic extend of this Tile as a BoundingBox.
+ *
+ * @return boundaries of this tile.
+ */
+ public BoundingBox getBoundingBox() {
+ if (this.boundingBox == null) {
+ double minLatitude = Math.max(MercatorProjection.LATITUDE_MIN, MercatorProjection.tileYToLatitude(tileY + 1, zoomLevel));
+ double minLongitude = Math.max(-180, MercatorProjection.tileXToLongitude(this.tileX, zoomLevel));
+ double maxLatitude = Math.min(MercatorProjection.LATITUDE_MAX, MercatorProjection.tileYToLatitude(this.tileY, zoomLevel));
+ double maxLongitude = Math.min(180, MercatorProjection.tileXToLongitude(tileX + 1, zoomLevel));
+ if (maxLongitude == -180) {
+ // fix for dateline crossing, where the right tile starts at -180 and causes an invalid bbox
+ maxLongitude = 180;
+ }
+ this.boundingBox = new BoundingBox(minLatitude, minLongitude, maxLatitude, maxLongitude);
+ }
+ return this.boundingBox;
+ }
+
+ /**
+ * Return the BoundingBox of a rectangle of tiles defined by upper left and lower right tile.
+ *
+ * @param upperLeft tile in upper left corner.
+ * @param lowerRight tile in lower right corner.
+ * @return BoundingBox defined by the area around upperLeft and lowerRight Tile.
+ */
+ public static BoundingBox getBoundingBox(Tile upperLeft, Tile lowerRight) {
+ BoundingBox ul = upperLeft.getBoundingBox();
+ BoundingBox lr = lowerRight.getBoundingBox();
+ return ul.extendBoundingBox(lr);
+ }
+
+ /**
+ * @return the maximum valid tile number for the given zoom level, 2zoomLevel -1.
+ */
+ public static int getMaxTileNumber(byte zoomLevel) {
+ if (zoomLevel < 0) {
+ throw new IllegalArgumentException("zoomLevel must not be negative: " + zoomLevel);
+ } else if (zoomLevel == 0) {
+ return 0;
+ }
+ return (2 << zoomLevel - 1) - 1;
+ }
+
+ /**
+ * Returns the top-left point of this tile in absolute coordinates.
+ *
+ * @return the top-left point
+ */
+ public Point getOrigin() {
+ if (this.origin == null) {
+ double x = MercatorProjection.tileToPixel(this.tileX);
+ double y = MercatorProjection.tileToPixel(this.tileY);
+ this.origin = new Point(x, y);
+ }
+ return this.origin;
+ }
}
diff --git a/vtm/src/org/oscim/layers/LocationLayer.java b/vtm/src/org/oscim/layers/LocationLayer.java
index 35bf6fa4..05f9ed46 100644
--- a/vtm/src/org/oscim/layers/LocationLayer.java
+++ b/vtm/src/org/oscim/layers/LocationLayer.java
@@ -48,7 +48,7 @@ public class LocationLayer extends Layer {
public void setPosition(double latitude, double longitude, double accuracy) {
double x = MercatorProjection.longitudeToX(longitude);
double y = MercatorProjection.latitudeToY(latitude);
- double radius = accuracy / MercatorProjection.groundResolution(latitude, 1);
+ double radius = accuracy / MercatorProjection.groundResolutionWithScale(latitude, 1);
locationRenderer.setLocation(x, y, radius);
locationRenderer.animate(true);
}
diff --git a/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java b/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java
index d660f84f..49eed862 100644
--- a/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java
+++ b/vtm/src/org/oscim/layers/tile/buildings/BuildingLayer.java
@@ -1,6 +1,6 @@
/*
* Copyright 2013 Hannes Janetzek
- * Copyright 2016 devemux86
+ * Copyright 2016-2017 devemux86
* Copyright 2016 Robin Boldt
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
@@ -101,7 +101,7 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
double lat = MercatorProjection.toLatitude(tile.y);
float groundScale = (float) MercatorProjection
- .groundResolution(lat, 1 << tile.zoomLevel);
+ .groundResolutionWithScale(lat, 1 << tile.zoomLevel);
ebs.buckets = Inlist.push(ebs.buckets,
new ExtrusionBucket(0, groundScale,
diff --git a/vtm/src/org/oscim/layers/tile/buildings/S3DBTileLoader.java b/vtm/src/org/oscim/layers/tile/buildings/S3DBTileLoader.java
index 3656479b..7b4f1621 100644
--- a/vtm/src/org/oscim/layers/tile/buildings/S3DBTileLoader.java
+++ b/vtm/src/org/oscim/layers/tile/buildings/S3DBTileLoader.java
@@ -80,7 +80,7 @@ class S3DBTileLoader extends TileLoader {
private void initTile(MapTile tile) {
double lat = MercatorProjection.toLatitude(tile.y);
mGroundScale = (float) MercatorProjection
- .groundResolution(lat, 1 << mTile.zoomLevel);
+ .groundResolutionWithScale(lat, 1 << mTile.zoomLevel);
mRoofs = new ExtrusionBucket(0, mGroundScale, Color.get(247, 249, 250));
diff --git a/vtm/src/org/oscim/utils/GeoPointUtils.java b/vtm/src/org/oscim/utils/GeoPointUtils.java
new file mode 100644
index 00000000..853288e1
--- /dev/null
+++ b/vtm/src/org/oscim/utils/GeoPointUtils.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 devemux86
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.oscim.utils;
+
+import org.oscim.core.GeoPoint;
+
+public final class GeoPointUtils {
+
+ /**
+ * Find if the given point lies within this polygon.
+ * See http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
+ *
+ * @return true if this polygon contains the given point, false otherwise.
+ */
+ public static boolean contains(GeoPoint[] geoPoints, GeoPoint geoPoint) {
+ boolean result = false;
+ for (int i = 0, j = geoPoints.length - 1; i < geoPoints.length; j = i++) {
+ if ((geoPoints[i].getLatitude() > geoPoint.getLatitude()) != (geoPoints[j].getLatitude() > geoPoint.getLatitude())
+ && (geoPoint.getLongitude() < (geoPoints[j].getLongitude() - geoPoints[i].getLongitude()) * (geoPoint.getLatitude() - geoPoints[i].getLatitude())
+ / (geoPoints[j].getLatitude() - geoPoints[i].getLatitude()) + geoPoints[i].getLongitude())) {
+ result = !result;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Find if this way is closed.
+ *
+ * @return true if this way is closed, false otherwise.
+ */
+ public static boolean isClosedWay(GeoPoint[] geoPoints) {
+ return geoPoints[0].distance(geoPoints[geoPoints.length - 1]) < 0.000000001;
+ }
+
+ private GeoPointUtils() {
+ throw new IllegalStateException();
+ }
+}