Skip to content

Instantly share code, notes, and snippets.

@chao-he
Created June 4, 2014 03:18
Show Gist options
  • Save chao-he/e265a1a65a70ea5da076 to your computer and use it in GitHub Desktop.
Save chao-he/e265a1a65a70ea5da076 to your computer and use it in GitHub Desktop.
#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