/* INFINITY CODE */
/* https://infinity-code.com */
using System;
using System.Text;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain
{
///
/// Provides utility methods for geographic and Mercator coordinate conversions.
///
public static class RealWorldTerrainGeo
{
///
/// The radius of the Earth.
///
public const float EARTH_RADIUS = 6371;
///
/// The length of the equator.
///
public const int EQUATOR_LENGTH = 40075;
///
/// The maximum elevation in the world.
///
public const int MAX_ELEVATION = 15000;
///
/// Converts geographic coordinates to SRTM data index.
///
/// Geographic coordinates
/// SRTM data index
public static Vector2 LanLongToFlat(Vector2 pos)
{
return new Vector2(Mathf.FloorToInt(pos.x / 5.0f) * 5 + 180, 90 - Mathf.FloorToInt(pos.y / 5.0f) * 5);
}
///
/// Converts geographic coordinates to Mercator coordinates.
///
/// Longitude
/// Latitude
public static void LatLongToMercat(ref double x, ref double y)
{
double sy = Math.Sin(y * RealWorldTerrainMath.DEG2RAD);
x = (x + 180) / 360;
y = 0.5 - Math.Log((1 + sy) / (1 - sy)) / (Math.PI * 4);
}
///
/// Converts geographic coordinates to Mercator coordinates.
///
/// Longitude
/// Latitude
/// Output Mercator X
/// Output Mercator Y
public static void LatLongToMercat(double x, double y, out double mx, out double my)
{
double sy = Math.Sin(y * RealWorldTerrainMath.DEG2RAD);
mx = (x + 180) / 360;
my = 0.5 - Math.Log((1 + sy) / (1 - sy)) / (Math.PI * 4);
}
///
/// Converts geographic coordinates to the index of the tile.
/// What is the tiles, and how it works, you can read here:
/// https://developers.google.com/maps/documentation/javascript/v2/overlays?csw=1#Google_Maps_Coordinates
///
/// Longitude
/// Latitude
/// Zoom
/// Output tile X
/// Output tile Y
public static void LatLongToTile(double dx, double dy, int zoom, out double tx, out double ty)
{
LatLongToMercat(ref dx, ref dy);
uint mapSize = (uint)RealWorldTerrainUtils.TILE_SIZE << zoom;
double px = RealWorldTerrainMath.Clamp(dx * mapSize + 0.5, 0, mapSize - 1);
double py = RealWorldTerrainMath.Clamp(dy * mapSize + 0.5, 0, mapSize - 1);
tx = px / RealWorldTerrainUtils.TILE_SIZE;
ty = py / RealWorldTerrainUtils.TILE_SIZE;
}
///
/// Converts Mercator coordinates to geographic coordinates.
///
/// Mercator X
/// Mercator Y
/// Output longitude
/// Output latitude
public static void MercatToLatLong(double mx, double my, out double x, out double y)
{
uint mapSize = (uint)RealWorldTerrainUtils.TILE_SIZE << 20;
double px = RealWorldTerrainMath.Clamp(mx * mapSize + 0.5, 0, mapSize - 1);
double py = RealWorldTerrainMath.Clamp(my * mapSize + 0.5, 0, mapSize - 1);
mx = px / RealWorldTerrainUtils.TILE_SIZE;
my = py / RealWorldTerrainUtils.TILE_SIZE;
TileToLatLong(mx, my, 20, out x, out y);
}
///
/// Converts tile coordinates to geographic coordinates.
///
/// Tile X
/// Tile Y
/// Zoom level
/// Output longitude
/// Output latitude
public static void TileToLatLong(double tx, double ty, int zoom, out double lx, out double ly)
{
double mapSize = RealWorldTerrainUtils.TILE_SIZE << zoom;
lx = 360 * (RealWorldTerrainMath.Repeat(tx * RealWorldTerrainUtils.TILE_SIZE, 0, mapSize - 1) / mapSize - 0.5);
ly = 90 - 360 * Math.Atan(Math.Exp(-(0.5 - RealWorldTerrainMath.Clamp(ty * RealWorldTerrainUtils.TILE_SIZE, 0, mapSize - 1) / mapSize) * 2 * Math.PI)) / Math.PI;
}
///
/// Converts tile index to quadkey.
/// What is the tiles and quadkey, and how it works, you can read here:
/// http://msdn.microsoft.com/en-us/library/bb259689.aspx
///
/// Tile X
/// Tile Y
/// Zoom
/// Quadkey
public static string TileToQuadKey(int x, int y, int zoom)
{
StringBuilder quadKey = new StringBuilder();
for (int i = zoom; i > 0; i--)
{
char digit = '0';
int mask = 1 << (i - 1);
if ((x & mask) != 0) digit++;
if ((y & mask) != 0)
{
digit++;
digit++;
}
quadKey.Append(digit);
}
return quadKey.ToString();
}
}
}