Javascript 墨卡托经纬度在裁剪地图上的x和y计算(英国)

Javascript 墨卡托经纬度在裁剪地图上的x和y计算(英国),javascript,geolocation,projection,processing.js,proj4js,Javascript,Geolocation,Projection,Processing.js,Proj4js,我有这个图像。这是一张英国地图(不包括南爱尔兰): 我已经成功地获得了一个纬度和经度,并将其绘制到这张地图上,方法是获取英国最左边的经度和最右边的经度,并使用它们计算出在地图上放置点的位置 这是代码(用于Processing.js,但可以用作js或任何东西): 然而,我还没有能够实现对这些值的墨卡托投影。这些图相当准确,但不够好,这种预测可以解决这个问题 我不知道怎么做。我找到的所有例子都在解释如何为全世界做这件事。这是一个很好的例子资源,解释了如何实现投影,但我一直无法让它工作 另一个资源是

我有这个图像。这是一张英国地图(不包括南爱尔兰):

我已经成功地获得了一个纬度和经度,并将其绘制到这张地图上,方法是获取英国最左边的经度和最右边的经度,并使用它们计算出在地图上放置点的位置

这是代码(用于Processing.js,但可以用作js或任何东西):

然而,我还没有能够实现对这些值的墨卡托投影。这些图相当准确,但不够好,这种预测可以解决这个问题

我不知道怎么做。我找到的所有例子都在解释如何为全世界做这件事。这是一个很好的例子资源,解释了如何实现投影,但我一直无法让它工作

另一个资源是我从中获得英国周围边界框的纬度和经度值。他们也在这里:

northLat = 58.666667; 
northLong = -3.366667; 
eastLat = 52.481167; 
eastLong = 1.762833; 
southLat = 49.95;
southLong = -5.2; 
westLat = 54.45;
westLong = -8.166667;
如果有人能帮我,我将不胜感激


谢谢

我认为值得记住的是,并非所有平面地图都是墨卡托投影。如果不进一步了解这张地图,就很难确定了。您可能会发现,世界上一小块区域的大多数地图更可能是锥形投影,其中地图上感兴趣的区域比全球墨卡托投影上的区域“更平坦”。这一点尤其重要,因为你离赤道越远(而英国离赤道也足够远)


您可能可以使用正在尝试的计算“足够接近”,但为了获得最佳精度,您可能希望使用具有定义良好投影的地图,或者创建自己的地图。

我知道刚才有人问过这个问题,但Proj4JS库非常适合在JavaScript中的不同地图投影之间进行转换


英国地图倾向于使用OSGB的国家网格,该网格基于横向墨卡托投影。就像传统的墨卡托,但转了90度,所以“赤道”变成了子午线。

我写了一个函数,它完全符合你的要求。我知道现在有点晚了,但也许还有其他人对它感兴趣

你需要一张墨卡托投影地图,你需要知道地图的纵横位置。 您可以获得伟大的定制墨卡托地图与完美匹配的纬度/经度位置,这是一个免费软件从

我正在使用这个脚本,并用一些google earth位置对其进行了测试。它在像素级上工作得非常好。事实上,我没有在不同或更大的地图上测试这一点。我希望它能帮助你

拉斐尔;)



这是我用TileMill创建的图像,我在本例中使用了它:

除了Raphael Wichmann发布的内容之外(顺便说一句,谢谢!), 以下是actionscript中的反向函数:

function convertPixelToGeo(tx:Number, ty:Number):Point
{   
    /* called worldMapWidth in Raphael's Code, but I think that's the radius since it's the map width or circumference divided by 2*PI  */   
    var worldMapRadius:Number = mapWidth / mapLonDelta * 360/(2 * Math.PI);     
    var mapOffsetY:Number = ( worldMapRadius / 2 * Math.log( (1 + Math.sin(mapLatBottomRadian) ) / (1 - Math.sin(mapLatBottomRadian))  ));
    var equatorY:Number = mapHeight + mapOffsetY;   
    var a:Number = (equatorY-ty)/worldMapRadius;

    var lat:Number = 180/Math.PI * (2 * Math.atan(Math.exp(a)) - Math.PI/2);
    var long:Number = mapLonLeft+tx/mapWidth*mapLonDelta;
    return new Point(lat,long);
}

如果您想避免Proj4JS固有的lat/lng投影的某些混乱方面,可以使用D3,它提供了许多烘焙投影,并且渲染得非常漂亮。这里有几种方位投影的例子。对于美国地图,我更喜欢阿尔伯斯


如果D3不是最终用户选项——比如说,您需要支持IE7/8——您可以在D3中进行渲染,然后从D3生成的SVG文件中获取xy坐标。然后,您可以在Raphael中呈现这些xy坐标。

我已经将Raphael提供的PHP代码转换为JavaScript,并可以确认它工作正常,而这段代码是我自己编写的。一切都归功于拉斐尔

/*
var mapWidth = 1500;
var mapHeight = 1577;

var mapLonLeft = 9.8;
var mapLonRight = 10.2;
var mapLonDelta = mapLonRight - mapLonLeft;

var mapLatBottom = 53.45;
var mapLatBottomDegree = mapLatBottom * Math.PI / 180;
*/

function convertGeoToPixel(latitude, longitude ,
                           mapWidth , // in pixels
                           mapHeight , // in pixels
                           mapLonLeft , // in degrees
                           mapLonDelta , // in degrees (mapLonRight - mapLonLeft);
                           mapLatBottom , // in degrees
                           mapLatBottomDegree) // in Radians
{
    var x = (longitude - mapLonLeft) * (mapWidth / mapLonDelta);

    latitude = latitude * Math.PI / 180;
    var worldMapWidth = ((mapWidth / mapLonDelta) * 360) / (2 * Math.PI);
    var mapOffsetY = (worldMapWidth / 2 * Math.log((1 + Math.sin(mapLatBottomDegree)) / (1 - Math.sin(mapLatBottomDegree))));
    var y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(latitude)) / (1 - Math.sin(latitude)))) - mapOffsetY);

    return { "x": x , "y": y};
}

@Javascript中的Xarinko Actionscript片段(带有一些测试值)


这个函数对我来说非常有用,因为我想根据要绘制的地图定义mapHeight。我正在生成PDF地图。我所需要做的就是传入贴图的max Lat,min Lon,然后它返回贴图的像素大小为[height,width]

convertGeoToPixel(最大纬度、最大经度)

在设置$y的最后一步中,请注意,如果坐标系“xy”从底部/左侧开始(如PDF),则不要从地图高度中减去计算值,这将反转地图

$y =  (($worldMapWidth / 2 * log((1 + sin($lat)) / (1 - sin($lat)))) - $mapOffsetY);

这里是另一个Javascript实现。这是上述@Rob Willet解决方案的简化。它不需要计算值作为函数的参数,而只需要基本值并从中计算所有内容:

function convertGeoToPixel(latitude, longitude,
                  mapWidth, // in pixels
                  mapHeight, // in pixels
                  mapLngLeft, // in degrees. the longitude of the left side of the map (i.e. the longitude of whatever is depicted on the left-most part of the map image)
                  mapLngRight, // in degrees. the longitude of the right side of the map
                  mapLatBottom) // in degrees.  the latitude of the bottom of the map
{
    const mapLatBottomRad = mapLatBottom * Math.PI / 180
    const latitudeRad = latitude * Math.PI / 180
    const mapLngDelta = (mapLngRight - mapLngLeft)

    const worldMapWidth = ((mapWidth / mapLngDelta) * 360) / (2 * Math.PI)
    const mapOffsetY = (worldMapWidth / 2 * Math.log((1 + Math.sin(mapLatBottomRad)) / (1 - Math.sin(mapLatBottomRad))))

    const x = (longitude - mapLngLeft) * (mapWidth / mapLngDelta)
    const y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(latitudeRad)) / (1 - Math.sin(latitudeRad)))) - mapOffsetY)

    return {x, y} // the pixel x,y value of this point on the map image
}
C#实施:

private Point ConvertGeoToPixel(
    double latitude, double longitude, // The coordinate to translate
    int imageWidth, int imageHeight, // The dimensions of the target space (in pixels)
    double mapLonLeft, double mapLonRight, double mapLatBottom // The bounds of the target space (in geo coordinates)
) {
    double mapLatBottomRad = mapLatBottom * Math.PI / 180;
    double latitudeRad = latitude * Math.PI / 180;

    double mapLonDelta = mapLonRight - mapLonLeft;
    double worldMapWidth = (imageWidth / mapLonDelta * 360) / (2 * Math.PI);
    double mapOffsetY = worldMapWidth / 2 * Math.Log((1 + Math.Sin(mapLatBottomRad)) / (1 - Math.Sin(mapLatBottomRad)));

    double x = (longitude - mapLonLeft) * (imageWidth / mapLonDelta);
    double y = imageHeight - ((worldMapWidth / 2 * Math.Log((1 + Math.Sin(latitudeRad)) / (1 - Math.Sin(latitudeRad)))) - mapOffsetY);

    return new Point()
    {
        X = Convert.ToInt32(x),
        Y = Convert.ToInt32(y)
    };
}

我相当肯定这是——在这一页上:它提到谷歌地图使用投影。我把我帖子中的地图(也可以在这里找到:)叠加到谷歌地图版本上,它们完全吻合,这让我觉得是的。仍然可以使用墨卡托投影吗?如果不只是为了看看这是否解决了问题。。或者我应该重新考虑我的地图吗?@betamax:嗯,看起来那张地图可能真的是墨卡托地图。当然可以将lat/lon投影到现有地图上,事实上,对于经度,线性插值方法应该可以很好地工作。但是,对于纬度,您需要使用维基百科页面上显示的投影方程,并计算出实际纬度和地图上坐标之间的关系。它应该是墨卡托
y
和地图坐标
y'
之间的线性关系(仅表示乘法和加法)。也就是说,对于一些A和B,y'=Ay+B。非常感谢分享,很高兴有一些代码可以参考。这看起来像是我最后做的,但我最终用Javascript做的。你有没有可能愿意用这段代码发布你使用的图像?谢谢刚刚添加了图像。干杯,拉斐尔;)@拉斐尔维克曼你有这个的反面吗?转换PixeltoGeo()?我尝试将xarinko的答案转换为Javascript,但没有得到相同的lat/lon。我希望能够转换来回。非常感谢,这对我来说非常有用!在javascript和it中使用
$y =  (($worldMapWidth / 2 * log((1 + sin($lat)) / (1 - sin($lat)))) - $mapOffsetY);
function convertGeoToPixel(latitude, longitude,
                  mapWidth, // in pixels
                  mapHeight, // in pixels
                  mapLngLeft, // in degrees. the longitude of the left side of the map (i.e. the longitude of whatever is depicted on the left-most part of the map image)
                  mapLngRight, // in degrees. the longitude of the right side of the map
                  mapLatBottom) // in degrees.  the latitude of the bottom of the map
{
    const mapLatBottomRad = mapLatBottom * Math.PI / 180
    const latitudeRad = latitude * Math.PI / 180
    const mapLngDelta = (mapLngRight - mapLngLeft)

    const worldMapWidth = ((mapWidth / mapLngDelta) * 360) / (2 * Math.PI)
    const mapOffsetY = (worldMapWidth / 2 * Math.log((1 + Math.sin(mapLatBottomRad)) / (1 - Math.sin(mapLatBottomRad))))

    const x = (longitude - mapLngLeft) * (mapWidth / mapLngDelta)
    const y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(latitudeRad)) / (1 - Math.sin(latitudeRad)))) - mapOffsetY)

    return {x, y} // the pixel x,y value of this point on the map image
}
private Point ConvertGeoToPixel(
    double latitude, double longitude, // The coordinate to translate
    int imageWidth, int imageHeight, // The dimensions of the target space (in pixels)
    double mapLonLeft, double mapLonRight, double mapLatBottom // The bounds of the target space (in geo coordinates)
) {
    double mapLatBottomRad = mapLatBottom * Math.PI / 180;
    double latitudeRad = latitude * Math.PI / 180;

    double mapLonDelta = mapLonRight - mapLonLeft;
    double worldMapWidth = (imageWidth / mapLonDelta * 360) / (2 * Math.PI);
    double mapOffsetY = worldMapWidth / 2 * Math.Log((1 + Math.Sin(mapLatBottomRad)) / (1 - Math.Sin(mapLatBottomRad)));

    double x = (longitude - mapLonLeft) * (imageWidth / mapLonDelta);
    double y = imageHeight - ((worldMapWidth / 2 * Math.Log((1 + Math.Sin(latitudeRad)) / (1 - Math.Sin(latitudeRad)))) - mapOffsetY);

    return new Point()
    {
        X = Convert.ToInt32(x),
        Y = Convert.ToInt32(y)
    };
}