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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2103924/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-22 22:47:24  来源:igfitidea点击:

Mercator longitude and latitude calculations to x and y on a cropped map (of the UK)

javascriptgeolocationprojectionprocessing.jsproj4js

提问by betamax

I have this image. It's a map of the UK (not including Southern Ireland):

我有这个形象。这是英国的地图(不包括南爱尔兰):

UK Map

英国地图

I have successfully managed to get a latitude and longitude and plot it onto this map by taking the leftmost longitude and rightmost longitude of the UK and using them to work out where to put the point on the map.

我已经成功地获得了纬度和经度,并通过获取英国最左边的经度和最右边的经度并将其绘制到这张地图上,并使用它们来确定在地图上放置点的位置。

This is the code (for use in Processing.js but could be used as js or anything):

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

// Size of the map
int width = 538;
int height = 811;
// X and Y boundaries
float westLong = -8.166667;
float eastLong = 1.762833;
float northLat = 58.666667;
float southLat = 49.95;

void drawPoint(float latitude, float longitude){

 fill(#000000);

 x = width * ((westLong-longitude)/(westLong-eastLong));
 y = (height * ((northLat-latitude)/(northLat-southLat)));

 console.log(x + ", " + y);
 ellipseMode(RADIUS);
 ellipse(x, y, 2, 2);    

}

However, I haven't been able to implement a Mercator projection on these values. The plots are reasonably accurate but they are not good enough and this projection would solve it.

但是,我无法对这些值实施墨卡托投影。这些图相当准确,但还不够好,这个投影可以解决这个问题。

I can't figure out how to do it. All the examples I find are explaining how to do it for the whole world. Thisis a good resource of examples explaining how to implement the projection but I haven't been able to get it to work.

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

Another resource is the Extreme points of the United Kingdomwhere I got the latitude and longitude values of the bounding box around the UK. They are also here:

另一个资源是英国极限点,我在那里获得了英国周围边界框的纬度和经度值。他们也在这里:

northLat = 58.666667; 
northLong = -3.366667; 
eastLat = 52.481167; 
eastLong = 1.762833; 
southLat = 49.95;
southLong = -5.2; 
westLat = 54.45;
westLong = -8.166667;

If anyone could help me with this, I would greatly appreciate it!

如果有人能帮助我解决这个问题,我将不胜感激!

Thanks

谢谢

采纳答案by Greg Hewgill

I think it's worthwhile to keep in mind that not all flat maps are Mercator projections. Without knowing more about that map in particular, it's hard to be sure. You may find that most maps of a small area of the world are more likely to be a conicaltype projection, where the area of interest on the map is "flatter" than would be on a global Mercator projection. This is especially more important the further you get away from the equator (and the UK is far enough away for it to matter).

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

You may be able to get "close enough" using the calculations you're trying, but for best accuracy you may want to either use a map with a well-defined projection, or create your own map.

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

回答by Raphael

I wrote a function which does exactly what you were looking for. I know it's a bit late, but maybe there are some other people interested in.

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

You need a map which is a mercator projection and you need to know the lat / lon positions of your map. You get great customized mercator maps with perfect matching lat / lon positions from TileMillwhich is a free software from MapBox!

您需要一张墨卡托投影地图,并且您需要知道地图的纬度/经度位置。您可以从MapBox的免费软件TileMill获得具有完美匹配纬度/经度位置的定制墨卡托地图!

I'm using this script and tested it with some google earth positions. It worked perfect on a pixel level. Actually I didnt test this on different or larger maps. I hope it helps you!

我正在使用这个脚本并用一些谷歌地球位置对其进行了测试。它在像素级别上完美运行。实际上我没有在不同或更大的地图上测试这个。我希望它能帮助你!

Raphael ;)

拉斐尔 ;)

<?php

$mapWidth = 1500;
$mapHeight = 1577;

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

$mapLatBottom = 53.45;
$mapLatBottomDegree = $mapLatBottom * M_PI / 180;

function convertGeoToPixel($lat, $lon)
{
    global $mapWidth, $mapHeight, $mapLonLeft, $mapLonDelta, $mapLatBottom, $mapLatBottomDegree;

    $x = ($lon - $mapLonLeft) * ($mapWidth / $mapLonDelta);

    $lat = $lat * M_PI / 180;
    $worldMapWidth = (($mapWidth / $mapLonDelta) * 360) / (2 * M_PI);
    $mapOffsetY = ($worldMapWidth / 2 * log((1 + sin($mapLatBottomDegree)) / (1 - sin($mapLatBottomDegree))));
    $y = $mapHeight - (($worldMapWidth / 2 * log((1 + sin($lat)) / (1 - sin($lat)))) - $mapOffsetY);

    return array($x, $y);
}

$position = convertGeoToPixel(53.7, 9.95);
echo "x: ".$position[0]." / ".$position[1];

?>

Here is the image I created with TileMill and which I used in this example: map image

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

回答by Xarinko

In addition to what Raphael Wichmann has posted (Thanks, by the way!), here is the reverse function, in actionscript :

除了 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);
}

回答by Rob Willett

I've converted the PHP code provided by Raphael to JavaScript and can confirm it worked and this code works myself. All credit to Raphael.

我已将 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};
}

回答by Cully

Here's another Javascript implementation. This is a simplification of @Rob Willet's solution above. Instead of requiring computed values as parameters to the function, it only requires essential values and computes everything from them:

这是另一个 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
}

回答by nauti

@Xarinko Actionscript snippet in Javascript (with some testing values)

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

var mapWidth = 1500;
var mapHeight = 1577;

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

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



function convertPixelToGeo(tx, ty)
{   
    /* 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 = mapWidth / mapLonDelta * 360/(2 * Math.PI);     
    var mapOffsetY = ( worldMapRadius / 2 * Math.log( (1 + Math.sin(mapLatBottomRadian) ) / (1 - Math.sin(mapLatBottomRadian))  ));
    var equatorY = mapHeight + mapOffsetY;   
    var a = (equatorY-ty)/worldMapRadius;

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

convertPixelToGeo(241,444)

回答by winwaed

I know the question was asked a while ago, but the Proj4JS library is ideal for transforming between different map projections in JavaScript.

我知道这个问题是不久前提出的,但 Proj4JS 库非常适合在 JavaScript 中的不同地图投影之间进行转换。

UK maps tend to use the OSGB's National Grid which is based on a Transverse Mercator projection. Ie. like a conventional Mercator but turned 90 degrees, so that the "equator" becomes a meridian.

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

回答by Chris Wilson

If you want to avoid some of the messier aspects of lat/lng projections intrinsic to Proj4JS, you can use D3, which offers many baked-in projections and renders beautifully. Here's an interactive exampleof several flavors of Azimuthal projections. I prefer Albers for USA maps.

如果您想避免 Proj4JS 固有的 lat/lng 投影的一些混乱方面,您可以使用 D3,它提供了许多烘焙投影和精美的渲染。这是几种方位角投影的交互式示例。我更喜欢美国地图的阿尔伯斯。

If D3 is not an end-user option -- say, you need to support IE 7/8 -- you can render in D3 and then snag the xy coordinates from the resultant SVG file that D3 generates. You can then render those xy coordinates in Raphael.

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

回答by alQemist

This function works great for me because I want to define the mapHeight based on the map I want to plot. I'm generating PDF maps. All I need to do is pass in the map's max Lat , min Lon and it returns the pixels size for the map as [height,width].

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

convertGeoToPixel(maxlatitude, maxlongitude)

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

One note in the final step where $y is set, do not subtract the calculation from the mapHeight if your coordinate system 'xy' starts at the bottom/left , like with PDFs, this will invert the map.

在设置 $y 的最后一步中的一个注意事项,如果您的坐标系 'xy' 从底部/左侧开始,则不要从 mapHeight 中减去计算,就像使用 PDF 一样,这将反转地图。

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

回答by Alex McMillan

C# implementation:

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)
    };
}