代码之家  ›  专栏  ›  技术社区  ›  betamax

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

  •  14
  • betamax  · 技术社区  · 15 年前

    我有这样的图像: http://imgur.com/99tSz.png . 英国地图(不包括南爱尔兰)。

    我已经成功地得到了经纬度,并用英国最左边的经度和最右边的经度把它标在地图上,然后用它们计算出该点在地图上的位置。

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

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

    我不知道怎么做。我发现的所有例子都在解释如何为整个世界做这件事。 This 是一个很好的例子资源,解释了如何实现投影,但我还没能让它工作。

    另一个资源是 Extreme points of the United Kingdom 在那里我得到了英国周围边界框的纬度和经度值。它们也在这里:

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

    如果有人能帮我,我会非常感激的!

    谢谢

    10 回复  |  直到 15 年前
        1
  •  7
  •   Greg Hewgill    15 年前

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

    你可以通过你正在尝试的计算得到“足够接近”的结果,但是为了达到最佳的准确度,你可能需要使用一个定义良好的投影的地图,或者创建你自己的地图。

        2
  •  39
  •   Raphael    13 年前

    我写了一个函数,它可以完全满足您的需求。我知道有点晚了,但也许还有其他人感兴趣。

    你需要一张墨卡托投影的地图,你需要知道你的地图的纬度/经度位置。 您可以从 tilemill which is a free software from mapbox 获得具有完美匹配lat/lon位置的伟大自定义Mercator地图!

    我正在使用这个脚本,并用谷歌地球的一些位置测试它。它在像素级上工作得很好。实际上,我没有在不同或更大的地图上测试这个。希望对你有帮助!

    拉斐尔;)

    <?PHP
    
    $mapwidth=1500;
    $mapheight=1577;
    
    $MAPLONLEFT=9.8;
    $MAPLONRIGHT=10.2;
    $MAPLONDelta=$MAPLONRIGHT-$MAPLONLEFT;
    
    $maplatbottom=53.45;
    $maplatBottomDegree=$maplatBottom*m_pi/180;
    
    函数convertgeotopixel($lat,$lon)
    {
    全局$MapWidth,$MapHeight,$MapLonLeft,$MapLonDelta,$MapLatBottom,$MapLatBottomDegree;
    
    $X=($LON-$MAPLONLEFT)*($MAPWIDth/$MAPLONDelta);
    
    $lat=$lat*m_pi/180;
    $worldmapwidth=($mapwidth/$maplondeta)*360)/(2*m_pi);
    $mapoffsety=($worldmapwidth/2*日志((1+sin($maplatBottomDegree))/(1-sin($maplatBottomDegree)));
    $Y=$mapheight-($worldmapwidth/2*log((1+sin($lat))/(1-sin($lat)))-$mapoffsety);
    
    返回数组($x,$y);
    }
    
    $position=convertgeotopixel(53.7,9.95);
    echo“x:”..$position[0]。“/”..$position[1];
    
    ?gt;
    < /代码> 
    
    

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

    为…我知道有点晚了,但也许还有其他人感兴趣。

    你需要一张墨卡托投影的地图,你需要知道你的地图的纬度/经度位置。 您可以从TileMill哪个是免费软件MapBox!

    我正在使用这个脚本,并用谷歌地球的一些位置测试它。它在像素级上工作得很好。实际上,我没有在不同或更大的地图上测试这个。希望对你有帮助!

    拉斐尔;

    <?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创建的图像,我在本例中使用了它:map image

        3
  •  10
  •   Xarinko    13 年前

    除了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);
    }
    
        4
  •  9
  •   Rob Willett    10 年前

    我已经将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};
    }
    
        5
  •  4
  •   winwaed    14 年前

    我知道不久前有人问过这个问题,但是proj4js库非常适合在javascript中转换不同的地图投影。

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

        6
  •  4
  •   Cully    8 年前

    这是另一个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
    }
    
        7
  •  3
  •   nauti    10 年前

    @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)
    
        8
  •  0
  •   Chris Wilson    13 年前

    如果你想避免项目4JS固有的lat/lng投影的一些更混乱的方面,你可以使用d3,它提供了许多烘焙投影,并且渲染得很漂亮。这里有一个 interactive example 有几种方位投影。我更喜欢美国地图的阿尔伯斯。

    如果d3不是最终用户选项——比如,您需要支持ie 7/8——您可以在d3中渲染,然后从d3生成的结果svg文件中获取xy坐标。然后可以在拉斐尔中渲染这些xy坐标。

        9
  •  0
  •   alQemist    9 年前

    这个函数对我很有用,因为我想根据我要绘制的地图定义地图高度。我正在生成PDF地图。我所要做的就是通过地图的max lat,min lon,它将地图的像素大小返回为[高度,宽度]。

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

    在设置$Y的最后一步中,注意不要从地图高度中减去计算结果,如果坐标系“xy”从底部/左侧开始,就像使用pdfs一样,这将反转地图。

    $y =  (($worldMapWidth / 2 * log((1 + sin($lat)) / (1 - sin($lat)))) - $mapOffsetY);
    
        10
  •  0
  •   Alex McMillan    6 年前

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