19

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

英国地图

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

这是代码(用于 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);    

}

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

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

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

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

如果有人可以帮助我,我将不胜感激!

谢谢

4

10 回答 10

46

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

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

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

拉斐尔;)

<?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];

?>

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

于 2012-05-01T17:36:23.243 回答
12

除了 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);
}
于 2012-11-10T16:04:30.803 回答
10

这是另一个 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
}
于 2017-05-09T04:38:50.220 回答
9

我已将 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};
}
于 2014-12-05T09:51:29.097 回答
8

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

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

于 2010-01-20T18:41:22.017 回答
4

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

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

于 2010-11-18T22:48:03.070 回答
4

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)
于 2015-03-18T20:37:05.037 回答
1

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)
    };
}
于 2019-01-15T22:12:54.343 回答
0

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

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

于 2012-11-10T16:36:02.627 回答
0

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

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

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

$y =  (($worldMapWidth / 2 * log((1 + sin($lat)) / (1 - sin($lat)))) - $mapOffsetY);
于 2016-01-30T17:05:04.233 回答