Created
June 4, 2014 03:18
-
-
Save chao-he/e265a1a65a70ea5da076 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <string> | |
#include <sstream> | |
#include <cstdint> | |
#include <cmath> | |
namespace MapPoint | |
{ | |
const double kEarthRadius = 6378137; | |
const double kMinLatitude = -85.05112878; | |
const double kMaxLatitude = 85.05112878; | |
const double kMinLngitude = -180; | |
const double kMaxLngitude = 180; | |
struct Point | |
{ | |
int x; | |
int y; | |
Point(): x(0), y(0) { } | |
Point(int X, int Y): x(X), y(Y) { } | |
}; | |
struct LatLng | |
{ | |
double lat; | |
double lng; | |
LatLng(): lat(0), lng(0) { } | |
LatLng(double Lat, double Lng): lat(Lat), lng(Lng) { } | |
}; | |
double Distance(double latitude1, double lngitude1, double latitude2, double lngitude2) | |
{ | |
double x1 = latitude1 * M_PI / 180; | |
double y1 = lngitude1 * M_PI / 180; | |
double x2 = latitude2 * M_PI / 180; | |
double y2 = lngitude2 * M_PI / 180; | |
return kEarthRadius * acos((sin(y1) * sin(y2) + cos(y1) * cos(y2) * cos(x2 - x1))); | |
} | |
double Distance(const LatLng &ll1, const LatLng & ll2) | |
{ | |
return Distance(ll1.lat, ll1.lng, ll2.lat, ll2.lng); | |
} | |
// Clips a number to the specified minimum and maximum values. | |
double Clip(double n, double minValue, double maxValue) | |
{ | |
return std::min(std::max(n, minValue), maxValue); | |
} | |
// Determines the map width and height (in pixels) at a specified level | |
// of detail. | |
uint32_t MapSize(int levelOfDetail) | |
{ | |
return (uint32_t) 256 << levelOfDetail; | |
} | |
// Determines the ground resolution (in meters per pixel) at a specified | |
// latitude and level of detail. | |
double GroundResolution(double latitude, int levelOfDetail) | |
{ | |
latitude = Clip(latitude, kMinLatitude, kMaxLatitude); | |
return cos(latitude * M_PI / 180) * 2 * M_PI * kEarthRadius / MapSize(levelOfDetail); | |
} | |
// Determines the map scale at a specified latitude, level of detail, | |
// and screen resolution. | |
double MapScale(double latitude, int levelOfDetail, int screenDpi) | |
{ | |
return GroundResolution(latitude, levelOfDetail) * screenDpi / 0.0254; | |
} | |
// Converts a point from latitude/longitude WGS-84 coordinates (in degrees) | |
// into pixel XY coordinates at a specified level of detail. | |
Point LatLngToPixelXY(double latitude, double longitude, int levelOfDetail) | |
{ | |
latitude = Clip(latitude, kMinLatitude, kMaxLatitude); | |
longitude = Clip(longitude, kMinLngitude, kMaxLngitude); | |
double x = (longitude + 180) / 360; | |
double sinLatitude = sin(latitude * M_PI / 180); | |
double y = 0.5 - log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * M_PI); | |
uint32_t mapSize = MapSize(levelOfDetail); | |
return Point((int) Clip(x * mapSize + 0.5, 0, mapSize - 1), | |
(int) Clip(y * mapSize + 0.5, 0, mapSize - 1)); | |
} | |
// Converts a pixel from pixel XY coordinates at a specified level of detail | |
// into latitude/longitude WGS-84 coordinates (in degrees). | |
LatLng PixelXYToLatLng(int pixelX, int pixelY, int levelOfDetail) | |
{ | |
double mapSize = MapSize(levelOfDetail); | |
double x = (Clip(pixelX, 0, mapSize - 1) / mapSize) - 0.5; | |
double y = 0.5 - (Clip(pixelY, 0, mapSize - 1) / mapSize); | |
return LatLng(90 - 360 * atan(exp(-y * 2 * M_PI)) / M_PI, 360 * x); | |
} | |
// Converts pixel XY coordinates into tile XY coordinates of the tile containing | |
// the specified pixel. | |
Point PixelXYToTileXY(int pixelX, int pixelY) | |
{ | |
return Point(pixelX / 256, pixelY / 256); | |
} | |
// Converts tile XY coordinates into pixel XY coordinates of the upper-left pixel | |
// of the specified tile. | |
Point TileXYToPixelXY(int tileX, int tileY) | |
{ | |
return Point(tileX * 256, tileY * 256); | |
} | |
// Converts tile XY coordinates into a QuadKey at a specified level of detail. | |
std::string TileXYToQuadKey(int tileX, int tileY, int levelOfDetail) | |
{ | |
std::istringstream quadKey; | |
for (int i = levelOfDetail; i > 0; i--) | |
{ | |
char digit = '0'; | |
int mask = 1 << (i - 1); | |
if ((tileX & mask) != 0) | |
{ | |
digit++; | |
} | |
if ((tileY & mask) != 0) | |
{ | |
digit++; | |
digit++; | |
} | |
quadKey>>digit; | |
} | |
return quadKey.str(); | |
} | |
// Converts a QuadKey into tile XY coordinates. | |
Point QuadKeyToTileXY(const std::string &quadKey, int *levelOfDetail) | |
{ | |
if (levelOfDetail) *levelOfDetail = quadKey.length(); | |
Point tile; | |
for (int i = *levelOfDetail; i > 0; i--) | |
{ | |
int mask = 1 << (i - 1); | |
switch (quadKey[*levelOfDetail - i]) | |
{ | |
case '0': | |
break; | |
case '1': | |
tile.x |= mask; | |
break; | |
case '2': | |
tile.y |= mask; | |
break; | |
case '3': | |
tile.x |= mask; | |
tile.y |= mask; | |
break; | |
} | |
} | |
return tile; | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment