/** * 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 scaleFactor the scale factor at which the coordinate should be converted. * @return the pixel X coordinate of the longitude value. */ public static double longitudeToPixelXWithScaleFactor(double longitude, double scaleFactor, int tileSize) { long mapSize = getMapSizeWithScaleFactor(scaleFactor, tileSize); return (longitude + 180) / 360 * mapSize; }
/** * 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 scaleFactor the scale at which the resolution should be calculated. * @return the ground resolution at the given latitude and scale. */ public static double calculateGroundResolutionWithScaleFactor(double latitude, double scaleFactor, int tileSize) { long mapSize = getMapSizeWithScaleFactor(scaleFactor, tileSize); return Math.cos(latitude * (Math.PI / 180)) * EARTH_CIRCUMFERENCE / 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 scaleFactor 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 pixelXToLongitudeWithScaleFactor(double pixelX, double scaleFactor, int tileSize) { long mapSize = getMapSizeWithScaleFactor(scaleFactor, tileSize); if (pixelX < 0 || pixelX > mapSize) { throw new IllegalArgumentException("invalid pixelX coordinate at scale " + scaleFactor + ": " + pixelX); } return 360 * ((pixelX / mapSize) - 0.5); }
/** * 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 scaleFactor the scale factor at which the coordinate should be converted. * @return the pixel Y coordinate of the latitude value. */ public static double latitudeToPixelYWithScaleFactor(double latitude, double scaleFactor, int tileSize) { double sinLatitude = Math.sin(latitude * (Math.PI / 180)); long mapSize = getMapSizeWithScaleFactor(scaleFactor, tileSize); // 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 pixel Y coordinate at a certain scale to a latitude coordinate. * * @param pixelY the pixel Y coordinate that should be converted. * @param scaleFactor 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 pixelYToLatitudeWithScaleFactor(double pixelY, double scaleFactor, int tileSize) { long mapSize = getMapSizeWithScaleFactor(scaleFactor, tileSize); if (pixelY < 0 || pixelY > mapSize) { throw new IllegalArgumentException("invalid pixelY coordinate at scale " + scaleFactor + ": " + pixelY); } double y = 0.5 - (pixelY / mapSize); return 90 - 360 * Math.atan(Math.exp(-y * (2 * Math.PI))) / Math.PI; }
/** * 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 scaleFactor the scale factor at which the coordinate should be converted. * @return the pixel X coordinate of the longitude value. */ public static double longitudeToPixelXWithScaleFactor(double longitude, double scaleFactor, int tileSize) { long mapSize = getMapSizeWithScaleFactor(scaleFactor, tileSize); return (longitude + 180) / 360 * mapSize; }
@Test public void getMapSizeTest() { for (int tileSize : TILE_SIZES) { for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) { long factor = Math.round(MercatorProjection.zoomLevelToScaleFactor(zoomLevel)); Assert.assertEquals(tileSize * factor, MercatorProjection.getMapSize(zoomLevel, tileSize)); Assert.assertEquals(MercatorProjection.getMapSizeWithScaleFactor(MercatorProjection.zoomLevelToScaleFactor(zoomLevel), tileSize), MercatorProjection.getMapSize(zoomLevel, tileSize)); } verifyInvalidGetMapSize((byte) -1, tileSize); } }
/** * 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 scaleFactor the scale at which the resolution should be calculated. * @return the ground resolution at the given latitude and scale. */ public static double calculateGroundResolutionWithScaleFactor(double latitude, double scaleFactor, int tileSize) { long mapSize = getMapSizeWithScaleFactor(scaleFactor, tileSize); return Math.cos(latitude * (Math.PI / 180)) * EARTH_CIRCUMFERENCE / 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 scaleFactor 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 pixelXToLongitudeWithScaleFactor(double pixelX, double scaleFactor, int tileSize) { long mapSize = getMapSizeWithScaleFactor(scaleFactor, tileSize); if (pixelX < 0 || pixelX > mapSize) { throw new IllegalArgumentException("invalid pixelX coordinate at scale " + scaleFactor + ": " + pixelX); } return 360 * ((pixelX / mapSize) - 0.5); }
/** * Converts a pixel Y coordinate at a certain scale to a latitude coordinate. * * @param pixelY the pixel Y coordinate that should be converted. * @param scaleFactor 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 pixelYToLatitudeWithScaleFactor(double pixelY, double scaleFactor, int tileSize) { long mapSize = getMapSizeWithScaleFactor(scaleFactor, tileSize); if (pixelY < 0 || pixelY > mapSize) { throw new IllegalArgumentException("invalid pixelY coordinate at scale " + scaleFactor + ": " + pixelY); } double y = 0.5 - (pixelY / mapSize); return 90 - 360 * Math.atan(Math.exp(-y * (2 * Math.PI))) / Math.PI; }
/** * 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 scaleFactor the scale factor at which the coordinate should be converted. * @return the pixel Y coordinate of the latitude value. */ public static double latitudeToPixelYWithScaleFactor(double latitude, double scaleFactor, int tileSize) { double sinLatitude = Math.sin(latitude * (Math.PI / 180)); long mapSize = getMapSizeWithScaleFactor(scaleFactor, tileSize); // 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); }
@Test public void tileXToLongitudeTest() { for (int tileSize : TILE_SIZES) { for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) { double longitude = MercatorProjection.tileXToLongitude(0, zoomLevel); Assert.assertEquals(LatLongUtils.LONGITUDE_MIN, longitude, 0); longitude = MercatorProjection.tileXToLongitudeWithScaleFactor(0, MercatorProjection.zoomLevelToScaleFactor(zoomLevel)); Assert.assertEquals(LatLongUtils.LONGITUDE_MIN, longitude, 0); long tileX = MercatorProjection.getMapSize(zoomLevel, tileSize) / tileSize; longitude = MercatorProjection.tileXToLongitude(tileX, zoomLevel); Assert.assertEquals(LatLongUtils.LONGITUDE_MAX, longitude, 0); tileX = MercatorProjection.getMapSizeWithScaleFactor(MercatorProjection.zoomLevelToScaleFactor(zoomLevel), tileSize) / tileSize; longitude = MercatorProjection.tileXToLongitudeWithScaleFactor(tileX, MercatorProjection.zoomLevelToScaleFactor(zoomLevel)); Assert.assertEquals(LatLongUtils.LONGITUDE_MAX, longitude, 0); } } }
@Test public void tileYToLatitudeTest() { for (int tileSize : TILE_SIZES) { 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.tileYToLatitudeWithScaleFactor(0, MercatorProjection.zoomLevelToScaleFactor(zoomLevel)); Assert.assertEquals(MercatorProjection.LATITUDE_MAX, latitude, 0); long tileY = MercatorProjection.getMapSize(zoomLevel, tileSize) / tileSize; latitude = MercatorProjection.tileYToLatitude(tileY, zoomLevel); Assert.assertEquals(MercatorProjection.LATITUDE_MIN, latitude, 0); tileY = MercatorProjection.getMapSizeWithScaleFactor(MercatorProjection.zoomLevelToScaleFactor(zoomLevel), tileSize) / tileSize; latitude = MercatorProjection.tileYToLatitudeWithScaleFactor(tileY, MercatorProjection.zoomLevelToScaleFactor(zoomLevel)); Assert.assertEquals(MercatorProjection.LATITUDE_MIN, latitude, 0); } } }
@Test public void longitudeToPixelXTest() { for (int tileSize : TILE_SIZES) { for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) { long mapSize = MercatorProjection.getMapSize(zoomLevel, tileSize); double pixelX = MercatorProjection.longitudeToPixelX(LatLongUtils.LONGITUDE_MIN, mapSize); Assert.assertEquals(0, pixelX, 0); pixelX = MercatorProjection.longitudeToPixelXWithScaleFactor(LatLongUtils.LONGITUDE_MIN, MercatorProjection.zoomLevelToScaleFactor(zoomLevel), tileSize); Assert.assertEquals(0, pixelX, 0); pixelX = MercatorProjection.longitudeToPixelX(0, mapSize); Assert.assertEquals((float) mapSize / 2, pixelX, 0); mapSize = MercatorProjection.getMapSizeWithScaleFactor(MercatorProjection.zoomLevelToScaleFactor(zoomLevel), tileSize); pixelX = MercatorProjection.longitudeToPixelXWithScaleFactor(0, MercatorProjection.zoomLevelToScaleFactor(zoomLevel), tileSize); Assert.assertEquals((float) mapSize / 2, pixelX, 0); pixelX = MercatorProjection.longitudeToPixelX(LatLongUtils.LONGITUDE_MAX, mapSize); Assert.assertEquals(mapSize, pixelX, 0); pixelX = MercatorProjection.longitudeToPixelXWithScaleFactor(LatLongUtils.LONGITUDE_MAX, MercatorProjection.zoomLevelToScaleFactor(zoomLevel), tileSize); Assert.assertEquals(mapSize, pixelX, 0); } } }
@Test public void pixelYToLatitudeTest() { for (int tileSize : TILE_SIZES) { for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) { long mapSize = MercatorProjection.getMapSize(zoomLevel, tileSize); double latitude = MercatorProjection.pixelYToLatitude(0, mapSize); Assert.assertEquals(MercatorProjection.LATITUDE_MAX, latitude, 0); latitude = MercatorProjection.pixelYToLatitudeWithScaleFactor(0, MercatorProjection.zoomLevelToScaleFactor(zoomLevel), tileSize); Assert.assertEquals(MercatorProjection.LATITUDE_MAX, latitude, 0); latitude = MercatorProjection.pixelYToLatitude((float) mapSize / 2, mapSize); Assert.assertEquals(0, latitude, 0); mapSize = MercatorProjection.getMapSizeWithScaleFactor(MercatorProjection.zoomLevelToScaleFactor(zoomLevel), tileSize); latitude = MercatorProjection.pixelYToLatitudeWithScaleFactor((float) mapSize / 2, MercatorProjection.zoomLevelToScaleFactor(zoomLevel), tileSize); Assert.assertEquals(0, latitude, 0); latitude = MercatorProjection.pixelYToLatitude(mapSize, mapSize); Assert.assertEquals(MercatorProjection.LATITUDE_MIN, latitude, 0); latitude = MercatorProjection.pixelYToLatitudeWithScaleFactor(mapSize, MercatorProjection.zoomLevelToScaleFactor(zoomLevel), tileSize); Assert.assertEquals(MercatorProjection.LATITUDE_MIN, latitude, 0); } verifyInvalidPixelYToLatitude(-1, (byte) 0, tileSize); verifyInvalidPixelYToLatitude(tileSize + 1, (byte) 0, tileSize); } }
@Test public void pixelXToLongitudeTest() { for (int tileSize : TILE_SIZES) { for (byte zoomLevel = ZOOM_LEVEL_MIN; zoomLevel <= ZOOM_LEVEL_MAX; ++zoomLevel) { long mapSize = MercatorProjection.getMapSize(zoomLevel, tileSize); double longitude = MercatorProjection.pixelXToLongitude(0, mapSize); Assert.assertEquals(LatLongUtils.LONGITUDE_MIN, longitude, 0); longitude = MercatorProjection.pixelXToLongitudeWithScaleFactor(0, MercatorProjection.zoomLevelToScaleFactor(zoomLevel), tileSize); Assert.assertEquals(LatLongUtils.LONGITUDE_MIN, longitude, 0); longitude = MercatorProjection.pixelXToLongitude((float) mapSize / 2, mapSize); Assert.assertEquals(0, longitude, 0); mapSize = MercatorProjection.getMapSizeWithScaleFactor(MercatorProjection.zoomLevelToScaleFactor(zoomLevel), tileSize); longitude = MercatorProjection.pixelXToLongitudeWithScaleFactor((float) mapSize / 2, MercatorProjection.zoomLevelToScaleFactor(zoomLevel), tileSize); Assert.assertEquals(0, longitude, 0); longitude = MercatorProjection.pixelXToLongitude(mapSize, mapSize); Assert.assertEquals(LatLongUtils.LONGITUDE_MAX, longitude, 0); longitude = MercatorProjection.pixelXToLongitudeWithScaleFactor(mapSize, MercatorProjection.zoomLevelToScaleFactor(zoomLevel), tileSize); Assert.assertEquals(LatLongUtils.LONGITUDE_MAX, longitude, 0); } verifyInvalidPixelXToLongitude(-1, (byte) 0, tileSize); verifyInvalidPixelXToLongitude(tileSize + 1, (byte) 0, tileSize); } }