简体   繁体   English

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

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

I have this image.我有这个形象。 It's a map of the UK (not including Southern Ireland):这是英国的地图(不包括南爱尔兰):

英国地图

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.我找到的所有例子都在解释如何为全世界做这件事。 This is 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 Kingdom where 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谢谢

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 TileMill which 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:这是我用 TileMill 创建的图像,我在这个例子中使用了它:地图图像

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

I've converted the PHP code provided by Raphael to JavaScript and can confirm it worked and this code works myself.我已将 Raphael 提供的 PHP 代码转换为 JavaScript,并且可以确认它有效并且此代码自己有效。 All credit to Raphael.所有功劳都归功于拉斐尔。

/*
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};
}

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

Here's another Javascript implementation.这是另一个 Javascript 实现。 This is a simplification of @Rob Willet's solution above.这是上面@Rob Willet 解决方案的简化。 Instead of requiring computed values as parameters to the function, it only requires essential values and computes everything from them:它不需要计算值作为函数的参数,它只需要基本值并从中计算所有内容:

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
}

@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)

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.英国地图倾向于使用基于横轴墨卡托投影的 OSGB 国家网格。 Ie. IE。 like a conventional Mercator but turned 90 degrees, so that the "equator" becomes a meridian.像传统的墨卡托但转了 90 度,使“赤道”变成了子午线。

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

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.如果您想避免 Proj4JS 固有的 lat/lng 投影的一些混乱方面,您可以使用 D3,它提供了许多烘焙投影和精美的渲染。 Here's an interactive example of several flavors of Azimuthal projections.这是几种方位角投影的交互式示例 I prefer Albers for USA maps.我更喜欢美国地图的阿尔伯斯。

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.如果 D3 不是最终用户选项——比如说,你需要支持 IE 7/8——你可以在 D3 中渲染,然后从 D3 生成的结果 SVG 文件中获取 xy 坐标。 You can then render those xy coordinates in Raphael.然后您可以在 Raphael 中渲染这些 xy 坐标。

This function works great for me because I want to define the mapHeight based on the map I want to plot.这个函数对我很有用,因为我想根据我想绘制的地图定义 mapHeight。 I'm generating PDF maps.我正在生成 PDF 地图。 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].我需要做的就是传入地图的 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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM