Core utilities #396 (#397)

This commit is contained in:
Emux 2017-09-10 15:59:54 +03:00 committed by GitHub
parent 3d7a09c457
commit 095ed65eb3
9 changed files with 926 additions and 23 deletions

View File

@ -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)

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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);
}
}
}

View File

@ -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()

View File

@ -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.
* <p/>
* 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() {
}
}

View File

@ -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, 2<sup>zoomLevel</sup> -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;
}
}

View File

@ -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);
}

View File

@ -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,

View File

@ -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));

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
package org.oscim.utils;
import org.oscim.core.GeoPoint;
public final class GeoPointUtils {
/**
* Find if the given point lies within this polygon.<br/>
* 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();
}
}