编辑:哇,我刚刚意识到这个问题已经快两年了。对于那个很抱歉。
好的,所以我认为这里发生了一些事情。最重要的是您没有进行您提供的链接中提到的墨卡托投影。您可以通过 google maps API 文档中的代码示例查看它的实际效果。我认为您变得有些接近的原因是缩放级别 20 的比例因子是如此之大,以至于它淹没了问题的细节。
要获得边界,您需要获取中心纬度/经度,将其转换为像素坐标,加/减以获得所需角的像素坐标,然后再转换回纬度/经度。第一个链接中的代码可以进行投影和逆投影。下面我把它翻译成c#。
在上面的解决方案中,您正在获取纬度/经度坐标并添加/减去世界坐标(像素坐标/比例),因此您正在组合两个不同的坐标系。
我在试图解决这个问题时遇到的一个问题是你的Coordinate
课程有点混乱。希望我没有把这个倒过来,但看起来你把纬度和经度倒过来了。这很重要,因为墨卡托投影在每个方向上都是不同的,以及其他原因。您的 X/Y 映射似乎也向后,因为纬度是您的北/南位置,投影时应该是 Y 坐标,而经度是您的东/西位置,投影时应该是 X 坐标。
要找到边界,需要三个坐标系:经度/纬度、世界坐标和像素坐标。该过程是中心纬度/经度-(墨卡托投影)->中心世界坐标->中心像素坐标->NE/SW像素坐标->NE/SW世界坐标-(逆墨卡托投影)->NE/SW纬度/经度.
您可以通过添加/减去图像/2 的尺寸来找到像素坐标。像素坐标系从左上角开始,因此要获得 NE 角,您需要从 x 中添加 width/2 并从 y 中减去 height/2。对于 SW 角,您需要从 x 中减去 width/2 并将 height/2 添加到 y。
这是 c# 中的投影代码(作为您的 GoogleMapsAPI 类的一部分),来自 javascript 上面的第一个链接的翻译:
static GoogleMapsAPI()
{
OriginX = TileSize / 2;
OriginY = TileSize / 2;
PixelsPerLonDegree = TileSize / 360.0;
PixelsPerLonRadian = TileSize / (2 * Math.PI);
}
public static int TileSize = 256;
public static double OriginX, OriginY;
public static double PixelsPerLonDegree;
public static double PixelsPerLonRadian;
public static double DegreesToRadians(double deg)
{
return deg * Math.PI / 180.0;
}
public static double RadiansToDegrees(double rads)
{
return rads * 180.0 / Math.PI;
}
public static double Bound(double value, double min, double max)
{
value = Math.Min(value, max);
return Math.Max(value, min);
}
//From Lat, Lon to World Coordinate X, Y. I'm being explicit in assigning to
//X and Y properties.
public static Coordinate Mercator(double latitude, double longitude)
{
double siny = Bound(Math.Sin(DegreesToRadians(latitude)), -.9999, .9999);
Coordinate c = new Coordinate(0,0);
c.X = OriginX + longitude*PixelsPerLonDegree;
c.Y = OriginY + .5 * Math.Log((1 + siny) / (1 - siny)) * -PixelsPerLonRadian;
return c;
}
//From World Coordinate X, Y to Lat, Lon. I'm being explicit in assigning to
//Latitude and Longitude properties.
public static Coordinate InverseMercator(double x, double y)
{
Coordinate c = new Coordinate(0, 0);
c.Longitude = (x - OriginX) / PixelsPerLonDegree;
double latRadians = (y - OriginY) / -PixelsPerLonRadian;
c.Latitude = RadiansToDegrees(Math.Atan(Math.Sinh(latRadians)));
return c;
}
您可以查看原始 javascript 代码以获取更详细的注释。
我通过手动逼近边界然后与代码给出的答案进行比较来测试它。我对 NE 角的手动近似值是 (51.15501, 4.796695) 并且代码吐出(51.155005..., 4.797038...)看起来非常接近。SW 角近似为 (51.154572, 4.796007),代码吐出(51.154574..., 4.796006...)。
这是一个有趣的,我希望这会有所帮助!
编辑:意识到我没有包含新GetBounds
功能:
public static MapCoordinates GetBounds(Coordinate center, int zoom, int mapWidth, int mapHeight)
{
var scale = Math.Pow(2, zoom);
var centerWorld = Mercator(center.Latitude, center.Longitude);
var centerPixel = new Coordinate(0, 0);
centerPixel.X = centerWorld.X * scale;
centerPixel.Y = centerWorld.Y * scale;
var NEPixel = new Coordinate(0, 0);
NEPixel.X = centerPixel.X + mapWidth / 2.0;
NEPixel.Y = centerPixel.Y - mapHeight / 2.0;
var SWPixel = new Coordinate(0, 0);
SWPixel.X = centerPixel.X - mapWidth / 2.0;
SWPixel.Y = centerPixel.Y + mapHeight / 2.0;
var NEWorld = new Coordinate(0, 0);
NEWorld.X = NEPixel.X / scale;
NEWorld.Y = NEPixel.Y / scale;
var SWWorld = new Coordinate(0, 0);
SWWorld.X = SWPixel.X / scale;
SWWorld.Y = SWPixel.Y / scale;
var NELatLon = InverseMercator(NEWorld.X, NEWorld.Y);
var SWLatLon = InverseMercator(SWWorld.X, SWWorld.Y);
return new MapCoordinates() { NorthEast = NELatLon, SouthWest = SWLatLon };
}
只需记住确保您的纬度和经度正确:
var result = GoogleMapsAPI.GetBounds(new Coordinate(51.15479, 4.79635), 20, 512, 512);
我知道代码不是最好的,但我希望它很清楚。