/* * Copyright 2010, 2011, 2012 mapsforge.org * Copyright 2012 Hannes Janetzek * * This file is part of the OpenScienceMap project (http://www.opensciencemap.org). * * 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; /** * An implementation of the spherical Mercator projection. */ public final class MercatorProjection { /** * The circumference of the earth at the equator in meters. */ public static final double EARTH_CIRCUMFERENCE = 40075016.686; /** * Maximum possible latitude coordinate of the map. */ public static final double LATITUDE_MAX = 85.05112877980659; /** * Minimum possible latitude coordinate of the map. */ public static final double LATITUDE_MIN = -LATITUDE_MAX; /** * Maximum possible longitude coordinate of the map. */ public static final double LONGITUDE_MAX = 180; /** * Minimum possible longitude coordinate of the map. */ public static final double LONGITUDE_MIN = -LONGITUDE_MAX; /** * 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 scale * the map scale at which the resolution should be calculated. * @return the ground resolution at the given latitude and zoom level. */ public static double groundResolution(double latitude, double scale) { return Math.cos(latitude * (Math.PI / 180)) * EARTH_CIRCUMFERENCE / (Tile.SIZE * scale); } public static float groundResolution(MapPosition pos) { double lat = MercatorProjection.toLatitude(pos.y); return (float) (Math.cos(lat * (Math.PI / 180)) * MercatorProjection.EARTH_CIRCUMFERENCE / (Tile.SIZE * pos.scale)); } /** * Projects a longitude coordinate (in degrees) to the range [0.0,1.0] * * @param latitude * the latitude coordinate that should be converted. * @return the position . */ public static double latitudeToY(double latitude) { double sinLatitude = Math.sin(latitude * (Math.PI / 180)); return 0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI); } public static double toLatitude(double y) { return 90 - 360 * Math.atan(Math.exp((y - 0.5) * (2 * Math.PI))) / Math.PI; } /** * Projects a longitude coordinate (in degrees) to the range [0.0,1.0] * * @param longitude * the longitude coordinate that should be converted. * @return the position . */ public static double longitudeToX(double longitude) { return (longitude + 180.0) / 360.0; } public static double toLongitude(double x) { return 360.0 * (x - 0.5); } public static Point project(GeoPoint p, Point reuse) { if (reuse == null) reuse = new Point(); reuse.x = ((p.longitudeE6 / 1E6) + 180.0) / 360.0; double sinLatitude = Math.sin((p.latitudeE6 / 1E6) * (Math.PI / 180.0)); reuse.y = 0.5 - Math.log((1.0 + sinLatitude) / (1.0 - sinLatitude)) / (4.0 * Math.PI); return reuse; } public static void project(GeoPoint p, double[] out, int pos) { out[pos * 2] = ((p.longitudeE6 / 1E6) + 180.0) / 360.0; double sinLatitude = Math.sin((p.latitudeE6 / 1E6) * (Math.PI / 180.0)); out[pos * 2 + 1] = 0.5 - Math.log((1.0 + sinLatitude) / (1.0 - sinLatitude)) / (4.0 * Math.PI); } public static void project(double latitude, double longitude, double[] out, int pos) { out[pos * 2] = (longitude + 180.0) / 360.0; double sinLatitude = Math.sin(latitude * (Math.PI / 180.0)); out[pos * 2 + 1] = 0.5 - Math.log((1.0 + sinLatitude) / (1.0 - sinLatitude)) / (4.0 * 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); } public static double wrapLongitude(double longitude) { if (longitude < -180) return Math.max(Math.min(360 + longitude, LONGITUDE_MAX), LONGITUDE_MIN); else if (longitude > 180) return Math.max(Math.min(longitude - 360, LONGITUDE_MAX), LONGITUDE_MIN); return longitude; } private MercatorProjection() { } }