Google Tile Utils
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
/**
* A set of simple routines to provide information about google tiles (API v1).
* These routines are not written for speed.
* These routines assume a tile size of 256 pixels square.
* Internally a sort of offset mercator projection is used, this places the origin (0,0)
* at the top left and goes to +1,+1 at the bottom right.
* I believe a proper mercator would have 0,0 in the middle and range from -0.5,-0.5 bottom left to +0.5,+0.5 top right.
* I've used this system because it provides a better fit with a typical the pixel coordinate system.
*/
public class GoogleTileUtils {
static int TILE_SIZE = 256;
/**
* Returns the pixel offset of a latitude and longitude within a single typical google tile.
* @param lat
* @param lng
* @param zoom
* @return
*/
public static System.Drawing.Point getPixelOffsetInTile(double lat, double lng, int zoom) {
Point pixelCoords = toZoomedPixelCoords(lat, lng, zoom);
return new Point(pixelCoords.X % TILE_SIZE, pixelCoords.Y % TILE_SIZE);
}
/**
* returns a Rectangle2D with x = lon, y = lat, width=lonSpan, height=latSpan
* for an x,y,zoom as used by google.
*/
public static void getTileRect(int x, int y, int zoom, out double lng, out double bottomLat, out double lngWidth, out double latHeight)
{
int tilesAtThisZoom = 1 << (17 - zoom);
lngWidth = 360.0 / tilesAtThisZoom; // width in degrees longitude
lng = -180 + (x * lngWidth); // left edge in degrees longitude
double latHeightMerc = 1.0 / tilesAtThisZoom; // height in "normalized" mercator 0,0 top left
double topLatMerc = y * latHeightMerc; // top edge in "normalized" mercator 0,0 top left
double bottomLatMerc = topLatMerc + latHeightMerc;
// convert top and bottom lat in mercator to degrees
// note that in fact the coordinates go from about -85 to +85 not -90 to 90!
bottomLat = (180 / Math.PI) * ((2 * Math.Atan(Math.Exp(Math.PI * (1 - (2 * bottomLatMerc)))))
- (Math.PI / 2));
double topLat = (180 / Math.PI) * ((2 * Math.Atan(Math.Exp(Math.PI * (1 - (2 * topLatMerc))))) - (Math.PI / 2));
latHeight = topLat - bottomLat;
}
/**
* returns the lat/lng as an "Offset Normalized Mercator" pixel coordinate,
* this is a coordinate that runs from 0..1 in latitude and longitude with 0,0 being
* top left. Normalizing means that this routine can be used at any zoom level and
* then multiplied by a power of two to get actual pixel coordinates.
* @param lat in degrees
* @param lng in degrees
* @return
*/
public static void toNormalisedPixelCoords(double lat, double lng, out double lat2, out double lng2)
{
// first convert to Mercator projection
// first convert the lat lon to mercator coordintes.
if (lng > 180) {
lng -= 360;
}
lng /= 360;
lng += 0.5;
lat = 0.5 - ((Math.Log(Math.Tan((Math.PI / 4) + ((0.5 * Math.PI * lat) / 180))) / Math.PI) / 2.0);
lng2 = lng;
lat2 = lat;
}
/**
* returns a point that is a google tile reference for the tile containing the lat/lng and at the zoom level.
* @param lat
* @param lng
* @param zoom
* @return
*/
public static Point toTileXY(double lat, double lng, int zoom)
{
double lat2, lng2;
toNormalisedPixelCoords(lat, lng,out lat2,out lng2);
int scale = 1 << (17 - zoom);
// can just truncate to integer, this looses the fractional "pixel offset"
return new Point((int)(lng2 * scale), (int)(lat2 * scale));
}
/**
* returns a point that is a google pixel reference for the particular lat/lng and zoom
* assumes tiles are 256x256.
* @param lat
* @param lng
* @param zoom
* @return
*/
public static Point toZoomedPixelCoords(double lat, double lng, int zoom)
{
double lat2, lng2;
toNormalisedPixelCoords(lat, lng, out lat2, out lng2);
double scale = (1 << (17 - zoom)) * TILE_SIZE;
return new Point((int)(lng2 * scale), (int)(lat2 * scale));
}
/**
* Returns a google maps satellite type string for the tile containing the lat and lng at the zoom level.
* @param lat
* @param lng
* @param zoom
* @return
*/
private static String getSatelliteRef(double lat, double lng, int zoom) {
Point tileXY = toTileXY(lat, lng, zoom);
int invZoom = 17 - zoom;
int stepSize = 1 << (17 - zoom);
int currentX = 0;
int currentY = 0;
System.Text.StringBuilder satString = new System.Text.StringBuilder(zoom);
satString.Append("t");
for (int i = 0; i < invZoom; i++) {
stepSize >>= 1;
if ((currentY + stepSize) > tileXY.Y) {
if ((currentX + stepSize) > tileXY.X) {
satString.Append('q');
}
else {
currentX += stepSize;
satString.Append('r');
}
}
else {
currentY += stepSize;
if ((currentX + stepSize) > tileXY.X) {
satString.Append('t');
}
else {
currentX += stepSize;
satString.Append('s');
}
}
}
return satString.ToString();
}
/** Returns an x,y for a satellite reference
* @param string
* @return
*/
private static Point satelliteRefToTileXY(String satelliteRef) {
// must start with "t"
if ((satelliteRef == null) || (satelliteRef.Length == 0) || (satelliteRef[0] != 't')) {
throw new Exception("satellite string must start with 't'");
}
int x = 0; // x
int y = 0;
for (int i = 1; i < satelliteRef.Length; i++) {
x <<= 1;
y <<= 1;
char c = satelliteRef[i];
switch (c) {
case 's':
y += 1;
x += 1;
break;
case 'r':
x += 1;
break;
case 'q':
y += 0;
break;
case 't':
y += 1;
break;
default:
throw new Exception("satellite char '" + c + "' when decoding keyhole string.");
}
}
return new Point(x, y);
}
/**
* Returns the zoom level for the given satellite reference string
* @param string
* @return
*/
private static int satelliteRefToZoom(String satRef) {
return 18 - satRef.Length;
}
}
浙公网安备 33010602011771号