using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace tileserver
{
class Mercator
{
/**
* Tile size.
*/
public static int TILE_SIZE = 256;
/**
* Maximum latitude.
*/
private static double MAX_LAT = 85.05112877980659;
/**
* Minimum latitude.
*/
private static double MIN_LAT = -85.05112877980659;
/**
* Equatorial earth radius for EPSG:3857 (Mercator).
*/
private static double EARTH_RADIUS = 6378137;
/**
* Returns earth radius size at zoom level.
* @param aZoomlevel Zoom level.
* @return Earth radius size at zoom level.
*/
public static double radius(int aZoomlevel)
{
return (TILE_SIZE * (1 << aZoomlevel)) / (2.0 * Math.PI);
}
/**
* Returns the absolut number of pixels in y or x, defined as: 2^Zoomlevel * TILE_WIDTH where TILE_WIDTH is the width of a tile in pixels.
* @param aZoomlevel zoom level to request pixel data.
* @return number of pixels.
*/
public static int getMaxPixels(int aZoomlevel)
{
return TILE_SIZE * (1 << aZoomlevel);
}
public static int falseEasting(int aZoomlevel)
{
return getMaxPixels(aZoomlevel) / 2;
}
public static int falseNorthing(int aZoomlevel)
{
return (-1 * getMaxPixels(aZoomlevel) / 2);
}
/**
* Transform pixelspace to coordinates and get the distance.
*
* @param x1 the first x coordinate.
* @param y1 the first y coordinate.
* @param x2 the second x coordinate.
* @param y2 the second y coordinate.
* @param zoomLevel the zoom level.
* @return the distance.
*/
public static double getDistance(int x1, int y1, int x2, int y2, int zoomLevel)
{
double la1 = YToLatitude(y1, zoomLevel);
double lo1 = XToLongitude(x1, zoomLevel);
double la2 = YToLatitude(y2, zoomLevel);
double lo2 = XToLongitude(x2, zoomLevel);
return getDistance(la1, lo1, la2, lo2);
}
/**
* Gets the distance using Spherical law of cosines.
*
* @param la1 the Latitude in degrees.
* @param lo1 the Longitude in degrees.
* @param la2 the Latitude from 2nd coordinate in degrees.
* @param lo2 the Longitude from 2nd coordinate in degrees.
* @return the distance.
*
*/
public static double getDistance(double la1, double lo1, double la2, double lo2)
{
double aStartLat = ToRadians(la1);
double aStartLong = ToRadians(lo1);
double aEndLat = ToRadians(la2);
double aEndLong = ToRadians(lo2);
double distance = Math.Acos(Math.Sin(aStartLat) * Math.Sin(aEndLat) + Math.Cos(aStartLat) * Math.Cos(aEndLat) * Math.Cos(aEndLong - aStartLong));
return (EARTH_RADIUS * distance);
}
/**
* Transform longitude to pixelspace
*
*
* Mathematical optimization
*
* x = radius(aZoomlevel) * toRadians(aLongitude) + falseEasting(aZoomLevel)
*
* x = getMaxPixels(aZoomlevel) / (2 * PI) * (aLongitude * PI) / 180 + getMaxPixels(aZoomlevel) / 2
* x = getMaxPixels(aZoomlevel) * aLongitude / 360 + 180 * getMaxPixels(aZoomlevel) / 360
* x = getMaxPixels(aZoomlevel) * (aLongitude + 180) / 360
*
* Mathematical optimization
*
* x = radius(aZoomlevel) * toRadians(aLongitude) + falseEasting(aZoomLevel)
*
* x = getMaxPixels(aZoomlevel) / (2 * PI) * (aLongitude * PI) / 180 + getMaxPixels(aZoomlevel) / 2
* x = getMaxPixels(aZoomlevel) * aLongitude / 360 + 180 * getMaxPixels(aZoomlevel) / 360
* x = getMaxPixels(aZoomlevel) * (aLongitude + 180) / 360
*
* Mathematical optimization
*
* log(u) := log((1.0 + sin(toRadians(aLat))) / (1.0 - sin(toRadians(aLat))
*
*
* y = -1 * (radius(aZoomlevel) / 2 * log(u)))) - falseNorthing(aZoomlevel))
* y = -1 * (getMaxPixel(aZoomlevel) / 2 * PI / 2 * log(u)) - -1 * getMaxPixel(aZoomLevel) / 2
* y = getMaxPixel(aZoomlevel) / (-4 * PI) * log(u)) + getMaxPixel(aZoomLevel) / 2
* y = getMaxPixel(aZoomlevel) * ((log(u) / (-4 * PI)) + 1/2)
*
* Mathematical optimization
*
* log(u) := log((1.0 + sin(toRadians(aLat))) / (1.0 - sin(toRadians(aLat))
*
*
* y = -1 * (radius(aZoomlevel) / 2 * log(u)))) - falseNorthing(aZoomlevel))
* y = -1 * (getMaxPixel(aZoomlevel) / 2 * PI / 2 * log(u)) - -1 * getMaxPixel(aZoomLevel) / 2
* y = getMaxPixel(aZoomlevel) / (-4 * PI) * log(u)) + getMaxPixel(aZoomLevel) / 2
* y = getMaxPixel(aZoomlevel) * ((log(u) / (-4 * PI)) + 1/2)
*
* Mathematical optimization
*
* lon = toDegree((aX - falseEasting(aZoomlevel)) / radius(aZoomlevel))
*
* lon = 180 / PI * ((aX - getMaxPixels(aZoomlevel) / 2) / getMaxPixels(aZoomlevel) / (2 * PI)
* lon = 180 * ((aX - getMaxPixels(aZoomlevel) / 2) / getMaxPixels(aZoomlevel))
* lon = 360 / getMaxPixels(aZoomlevel) * (aX - getMaxPixels(aZoomlevel) / 2)
* lon = 360 * aX / getMaxPixels(aZoomlevel) - 180
*