/* * Geopaparazzi - Digital field mapping on Android based devices * Copyright (C) 2016 HydroloGIS (www.hydrologis.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package eu.geopaparazzi.library.util; /** * Created by hydrologis on 15/01/16. */ public class MercatorUtils { private static double originShift = 2 * Math.PI * 6378137 / 2.0; private static final double METER_TO_FEET_CONVERSION_FACTOR = 3.2808399; /** * Converts TMS tile coordinates to Google Tile coordinates. * <p/> * <p>Code copied from: http://code.google.com/p/gmap-tile-generator/</p> * * @param tx the x tile number. * @param ty the y tile number. * @param zoom the current zoom level. * @return the converted values. */ public static int[] tmsTile2GoogleTile(int tx, int ty, int zoom) { return new int[]{tx, (int) ((Math.pow(2, zoom) - 1) - ty)}; } /** * Converts Google tile coordinates to TMS Tile coordinates. * <p/> * <p>Code copied from: http://code.google.com/p/gmap-tile-generator/</p> * * @param tx the x tile number. * @param ty the y tile number. * @param zoom the current zoom level. * @return the converted values. */ public static int[] googleTile2TmsTile(int tx, int ty, int zoom) { return new int[]{tx, (int) ((Math.pow(2, zoom) - 1) - ty)}; } /** * Converts TMS tile coordinates to Microsoft QuadTree. * <p/> * <p>Code copied from: http://code.google.com/p/gmap-tile-generator/</p> * * @param tx tile x. * @param ty tile y. * @param zoom zoomlevel. * @return the quadtree key. */ public static String quadTree(int tx, int ty, int zoom) { String quadKey = ""; //$NON-NLS-1$ ty = (int) ((Math.pow(2, zoom) - 1) - ty); for (int i = zoom; i < 0; i--) { int digit = 0; int mask = 1 << (i - 1); if ((tx & mask) != 0) { digit += 1; } if ((ty & mask) != 0) { digit += 2; } quadKey += (digit + ""); //$NON-NLS-1$ } return quadKey; } /** * <p>Code copied from: http://code.google.com/p/gmap-tile-generator/</p> * * @param tx tile x. * @param ty tile y. * @param zoom zoomlevel. * @param tileSize tile size. * @return [minx, miny, maxx, maxy] */ public static double[] tileLatLonBounds(int tx, int ty, int zoom, int tileSize) { double[] bounds = tileBounds(tx, ty, zoom, tileSize); double[] mins = metersToLatLon(bounds[0], bounds[1]); double[] maxs = metersToLatLon(bounds[2], bounds[3]); return new double[]{mins[1], maxs[0], maxs[1], mins[0]}; } /** * Returns bounds of the given tile in EPSG:900913 coordinates * <p/> * <p>Code copied from: http://code.google.com/p/gmap-tile-generator/</p> * * @param tx tile x. * @param ty tile y. * @param zoom zoomlevel. * @param tileSize tile size. * @return [minx, miny, maxx, maxy] */ public static double[] tileBounds(int tx, int ty, int zoom, int tileSize) { double[] min = pixelsToMeters(tx * tileSize, ty * tileSize, zoom, tileSize); double minx = min[0], miny = min[1]; double[] max = pixelsToMeters((tx + 1) * tileSize, (ty + 1) * tileSize, zoom, tileSize); double maxx = max[0], maxy = max[1]; return new double[]{minx, miny, maxx, maxy}; } /** * Converts XY point from Spherical Mercator EPSG:900913 to lat/lon in WGS84 * Datum * <p/> * <p>Code copied from: http://code.google.com/p/gmap-tile-generator/</p> * * @param mx x * @param my y * @return lat long */ public static double[] metersToLatLon(double mx, double my) { double lon = (mx / originShift) * 180.0; double lat = (my / originShift) * 180.0; lat = 180 / Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180.0)) - Math.PI / 2.0); return new double[]{-lat, lon}; } /** * Equatorial radius of earth is required for distance computation. */ public static final double EQUATORIALRADIUS = 6378137.0; /** * Convert a longitude coordinate (in degrees) to a horizontal distance in meters from the * zero meridian * * @param longitude in degrees * @return longitude in meters in spherical mercator projection */ public static double longitudeToMetersX(double longitude) { return EQUATORIALRADIUS * Math.toRadians(longitude); } /** * Convert a meter measure to a longitude * * @param x in meters * @return longitude in degrees in spherical mercator projection */ public static double metersXToLongitude(double x) { return Math.toDegrees(x / EQUATORIALRADIUS); } /** * Convert a meter measure to a latitude * * @param y in meters * @return latitude in degrees in spherical mercator projection */ public static double metersYToLatitude(double y) { return Math.toDegrees(Math.atan(Math.sinh(y / EQUATORIALRADIUS))); } /** * Convert a latitude coordinate (in degrees) to a vertical distance in meters from the * equator * * @param latitude in degrees * @return latitude in meters in spherical mercator projection */ public static double latitudeToMetersY(double latitude) { return EQUATORIALRADIUS * Math.log(Math.tan(Math.PI / 4 + 0.5 * Math.toRadians(latitude))); } /** * Convert a east-longitude,west-longitude coordinate (in degrees) to distance in meters * * @param east_longitude longitude in degrees * @param west_longitude longitude in degrees * @return meters in spherical mercator projection */ public static double longitudeToMeters(double east_longitude, double west_longitude) { return longitudeToMetersX(east_longitude) - longitudeToMetersX(west_longitude); } /** * Convert a north-latitude,south-latitude coordinate (in degrees) to distance in meters * * @param north_latitude latitude in degrees * @param south_latitude latitude in degrees * @return meters in spherical mercator projection */ public static double latitudeToMeters(double north_latitude, double south_latitude) { return latitudeToMetersY(north_latitude) - latitudeToMetersY(south_latitude); } /** * Converts given lat/lon in WGS84 Datum to XY in Spherical Mercator * EPSG:900913 * * @param lat * @param lon * @return */ public static double[] latLonToMeters(double lat, double lon) { double mx = lon * originShift / 180.0; double my = Math.log(Math.tan((90 + lat) * Math.PI / 360.0)) / (Math.PI / 180.0); my = my * originShift / 180.0; return new double[]{mx, my}; } /** * Converts pixel coordinates in given zoom level of pyramid to EPSG:900913 * <p/> * <p>Code copied from: http://code.google.com/p/gmap-tile-generator/</p> * * @param px pixel x. * @param py pixel y. * @param zoom zoomlevel. * @param tileSize tile size. * @return converted coordinate. */ public static double[] pixelsToMeters(double px, double py, int zoom, int tileSize) { double res = getResolution(zoom, tileSize); double mx = px * res - originShift; double my = py * res - originShift; return new double[]{mx, my}; } /** * <p>Code copied from: http://code.google.com/p/gmap-tile-generator/</p> * * @param px * @param py * @return */ public static int[] pixelsToTile(int px, int py, int tileSize) { int tx = (int) Math.ceil(px / ((double) tileSize) - 1); int ty = (int) Math.ceil(py / ((double) tileSize) - 1); return new int[]{tx, ty}; } /** * Converts EPSG:900913 to pyramid pixel coordinates in given zoom level * <p>Code copied from: http://code.google.com/p/gmap-tile-generator/</p> * * @param mx * @param my * @param zoom * @return */ public static int[] metersToPixels(double mx, double my, int zoom, int tileSize) { double res = getResolution(zoom, tileSize); int px = (int) Math.round((mx + originShift) / res); int py = (int) Math.round((my + originShift) / res); return new int[]{px, py}; } /** * Returns tile for given mercator coordinates * <p>Code copied from: http://code.google.com/p/gmap-tile-generator/</p> * * @return */ public static int[] metersToTile(double mx, double my, int zoom, int tileSize) { int[] p = metersToPixels(mx, my, zoom, tileSize); return pixelsToTile(p[0], p[1], tileSize); } /** * Resolution (meters/pixel) for given zoom level (measured at Equator) * <p/> * <p>Code copied from: http://code.google.com/p/gmap-tile-generator/</p> * * @param zoom zoomlevel. * @param tileSize tile size. * @return resolution. */ public static double getResolution(int zoom, int tileSize) { // return (2 * Math.PI * 6378137) / (this.tileSize * 2**zoom) double initialResolution = 2 * Math.PI * 6378137 / tileSize; return initialResolution / Math.pow(2, zoom); } /** * Convert meters to feet. * * @param meters the value in meters to convert to feet. * @return meters converted to feet. */ public static double toFeet(final double meters) { return meters * METER_TO_FEET_CONVERSION_FACTOR; } }