【问题标题】:Convert long/lat to pixel x/y on a given picture在给定图片上将 long/lat 转换为像素 x/y
【发布时间】:2011-02-08 17:10:13
【问题描述】:

我有一张莫斯科的城市地图。我们用一些艺术元素修改了一张谷歌地图图像,但 GPS 坐标和像素之间的关系保持不变。

问题:如何将我们拥有的各种数据点的 GPS 坐标转换为图像中的像素坐标?

理想情况下,我可以在 Javascript 中执行此操作,但 PHP 可以。


我知道在小规模(例如城市规模)上,它足够简单(有必要了解哪些地理坐标具有图片角之一,然后了解地理坐标中一个像素的“价格”)图片分别在轴 OX 和 OY 上)。

但在大尺度(国家尺度)上,一个像素的“价格”不会是一个常数,并且会发生足够大的变化,无法应用上述方法。

如何解决国家层面的问题?


更新:

我不使用 API 谷歌地图,我只有:对象的地理坐标(它们来自谷歌地图),我的网站上还有一张简单的图片 *。 gif,我必须在其中画一个点对应的地理坐标。

【问题讨论】:

  • 你使用的是v2还是v3的api?
  • “在谷歌的坐标中”到底是什么意思?具体来说,你的意思是你有墨卡托东向和北向坐标,还是你有纬度和经度的地理坐标?
  • 另外,这可能对你有用:sharpgis.net/post/2007/07/27/…
  • @Daniel Pryden,我会根据您的 cmets 纠正我的问题。我的意思是地理坐标。
  • @Peter Bailey,我不使用 API。我直接从谷歌地图获取的地理坐标;网站上有一张“城市地图”,只是一张简单的图片 *.gif。

标签: javascript google-maps coordinates google-static-maps


【解决方案1】:

如果假设每个像素位于同一区域,那么以下关于将距离转换为经度/纬度坐标的文章可能会对您有所帮助:

http://www.johndcook.com/blog/2009/04/27/converting-miles-to-degrees-longitude-or-latitude/

【讨论】:

  • 离我的问题已经够远了,不知道能不能用。
【解决方案2】:

您需要公式将纬度和经度转换为直角坐标。有很多可供选择,每个都会以不同的方式扭曲地图。 Wolfram MathWorld 有很好的收藏:

http://mathworld.wolfram.com/MapProjection.html

点击“另见”链接。

【讨论】:

    【解决方案3】:

    你可以看看code that used on gheat,它是从js移植到python的。

    【讨论】:

    • 链接离线
    【解决方案4】:

    您正在处理的翻译与Map Projection 有关,这就是我们世界的球面如何被翻译成二维渲染的方式。有多种方法(投影)可以在二维表面上渲染世界。

    如果您的地图仅使用特定投影(Mercator 很受欢迎),您应该能够找到方程式、一些示例代码和/或一些库(例如,一个墨卡托解决方案 - Convert Lat/Longs to X/Y Co-ordinates。如果那样的话不这样做,我相信你可以找到其他样本 - https://stackoverflow.com/search?q=mercator。如果你的图像不是使用墨卡托投影的地图,你需要确定它使用什么投影来找到正确的平移方程。

    如果您尝试支持多个地图投影(您想支持许多使用不同投影的不同地图),那么您肯定想使用像 PROJ.4 这样的库,但我再次不确定您会做什么查找 Javascript 或 PHP。

    【讨论】:

    • PROJ.4 很棒,但是当您已经在处理从 Google 地图获取的地图时,使用它已经提供的坐标转换 API 会更容易。这可以处理投影转换(谷歌地图似乎使用墨卡托http://code.google.com/apis/maps/documentation/reference.html#GMercatorProjection)并且可以很容易地从纬度/经度转换为像素。
    • @Donnelly - 很好 - 她确实说她的地图图像与 Google 的地图图像相匹配。 GMercatorProjection 本身应该是一个答案 - 您应该将其添加为单独的答案 [或者,我将更新此答案以包含它]。我确实阅读了她问题的第二部分(行下方的部分),以更一般地了解如何将纬度/经度坐标与一般地图图像匹配(不一定是谷歌),所以我试图给出一个更一般的答案。我可能一直在阅读她的问题。
    【解决方案5】:

    所有这一切的关键是理解map projections。正如其他人指出的那样,失真的原因是球形(或更准确地说是椭圆形)地球被投影到平面上。

    为了实现您的目标,您首先必须了解有关数据的两件事:

    1. 您的地图所在的投影。如果它们纯粹源自Google Maps,那么它们很可能使用spherical Mercator projection
    2. 您的纬度/经度坐标使用的地理坐标系。这可能会有所不同,因为在地球上定位纬度/经度有不同的方法。最常见的 GCS 是 WGS84,用于大多数网络地图应用和 GPS。

    我假设您的数据位于这些坐标系中。

    球形墨卡托投影为地球表面定义了一个以米为单位的坐标对。这意味着,对于每个纬度/经度坐标,都有一个匹配的米/米坐标。这使您可以使用以下过程进行转换:

    1. 查找图像角的 WGS84 纬度/经度。
    2. 将 WGS 纬度/经度转换为球形墨卡托投影。那里有转换工具,我最喜欢的是使用 PROJ4 project 中的 cs2cs 工具。
    3. 您可以安全地执行简单的线性变换,以在图像上的点与球形墨卡托投影中地球上的点之间进行转换,然后再转换回来。

    为了从 WGS84 点到图像上的一个像素,现在的过程是:

    1. 将纬度/经度投影到球形墨卡托。这可以使用proj4js library. 来完成
    2. 使用上面发现的线性关系将球面墨卡托坐标转换为图像像素坐标。

    您可以像这样使用 proj4js 库:

    // include the library
    <script src="lib/proj4js-combined.js"></script>  //adjust the path for your server
                                                     //or else use the compressed version
    // creating source and destination Proj4js objects
    // once initialized, these may be re-used as often as needed
    var source = new Proj4js.Proj('EPSG:4326');    //source coordinates will be in Longitude/Latitude, WGS84
    var dest = new Proj4js.Proj('EPSG:3785');     //destination coordinates in meters, global spherical mercators projection, see http://spatialreference.org/ref/epsg/3785/
    
    
    // transforming point coordinates
    var p = new Proj4js.Point(-76.0,45.0);   //any object will do as long as it has 'x' and 'y' properties
    Proj4js.transform(source, dest, p);      //do the transformation.  x and y are modified in place
    
    //p.x and p.y are now EPSG:3785 in meters
    

    【讨论】:

    • @fmark,插入代码,正如你所说,脚本在Proj4js.Proj = OpenLayers.Class({...}) 行出现错误,它说:OpenLayers is not defined。如何解决问题?
    【解决方案6】:

    所以您想获取纬度/经度坐标并找出该位置图像上的像素坐标?

    主要的 GMap2 类提供与显示地图上的像素和纬度/经度坐标之间的转换:

    Gmap2.fromLatLngToContainerPixel(latlng)
    

    例如:

    var gmap2 = new GMap2(document.getElementById("map_canvas"));
    var geocoder = new GClientGeocoder();
    
    geocoder.getLatLng( "1600 Pennsylvania Avenue NW Washington, D.C. 20500",
        function( latlng ) {
            var pixel_coords = gmap2.fromLatLngToContainerPixel(latlng);
    
            window.alert( "The White House is at pixel coordinates (" + 
                pixel_coodrs.x + ", " + pixel_coords.y + ") on the " +
                "map image shown on this page." );
        }
    );
    

    因此,假设您的地图图像是 Google 地图显示的屏幕抓图,那么这将为您提供该图像上的正确像素坐标的纬度/经度坐标。

    如果您抓取平铺图像并自己将它们拼接在一起,事情会变得更加棘手,因为完整的平铺集区域将位于显示的地图区域之外。

    在这种情况下,您需要使用左上角图像平铺的 left 和 top 值作为 fromLatLngToContainerPixel(latlng:GLatLng) 给您的坐标的偏移量,从 x 坐标中减去左坐标,然后从 y 坐标顶部。因此,如果左上角的图像位于 (-50, -122) (left, top),并且 fromLatLngToContainerPixel() 告诉您纬度/经度位于像素坐标 (150, 320) 处,则在图像上从瓦片,坐标的真实位置在 (150 - (-50), 320 - (-122)) 即 (200, 442)。

    也有可能是类似的GMap2坐标平移函数:

    GMap2.fromLatLngToDivPixel(latlng:GLatLng)
    

    将为您提供拼接瓷砖案例的正确纬度/经度到像素的转换 - 我没有对此进行测试,API 文档也不是 100% 清楚。

    更多信息请看这里: http://code.google.com/apis/maps/documentation/reference.html#GMap2.Methods.Coordinate-Transformations

    【讨论】:

      【解决方案7】:

      您必须以您的语言实现 Google Maps API 投影。我有这个的 C# 源代码:

      public class GoogleMapsAPIProjection
      {
          private readonly double PixelTileSize = 256d;
          private readonly double DegreesToRadiansRatio = 180d / Math.PI;
          private readonly double RadiansToDegreesRatio = Math.PI / 180d;
          private readonly PointF PixelGlobeCenter;
          private readonly double XPixelsToDegreesRatio;
          private readonly double YPixelsToRadiansRatio;
      
          public GoogleMapsAPIProjection(double zoomLevel)
          {
              var pixelGlobeSize = this.PixelTileSize * Math.Pow(2d, zoomLevel);
              this.XPixelsToDegreesRatio = pixelGlobeSize / 360d;
              this.YPixelsToRadiansRatio = pixelGlobeSize / (2d * Math.PI);
              var halfPixelGlobeSize = Convert.ToSingle(pixelGlobeSize / 2d);
              this.PixelGlobeCenter = new PointF(
                  halfPixelGlobeSize, halfPixelGlobeSize);
          }
      
          public PointF FromCoordinatesToPixel(PointF coordinates)
          {
              var x = Math.Round(this.PixelGlobeCenter.X
                  + (coordinates.X * this.XPixelsToDegreesRatio));
              var f = Math.Min(
                  Math.Max(
                       Math.Sin(coordinates.Y * RadiansToDegreesRatio),
                      -0.9999d),
                  0.9999d);
              var y = Math.Round(this.PixelGlobeCenter.Y + .5d * 
                  Math.Log((1d + f) / (1d - f)) * -this.YPixelsToRadiansRatio);
              return new PointF(Convert.ToSingle(x), Convert.ToSingle(y));
          }
      
          public PointF FromPixelToCoordinates(PointF pixel)
          {
              var longitude = (pixel.X - this.PixelGlobeCenter.X) /
                  this.XPixelsToDegreesRatio;
              var latitude = (2 * Math.Atan(Math.Exp(
                  (pixel.Y - this.PixelGlobeCenter.Y) / -this.YPixelsToRadiansRatio))
                  - Math.PI / 2) * DegreesToRadiansRatio;
              return new PointF(
                  Convert.ToSingle(latitude),
                  Convert.ToSingle(longitude));
          }
      }
      

      来源:

      http://code.google.com/p/geographical-dot-net/source/browse/trunk/GeographicalDotNet/GeographicalDotNet/Projection/GoogleMapsAPIProjection.cs

      【讨论】:

      • 我仔细检查过,这与上面提到的 gheat 代码相匹配,该代码是从谷歌地图服务器提供的实际 JS 中提取的。所以我想我们可以说:问题解决了。
      • 感谢@Jader Dias。你的代码对我有用。我采用了大小为 256*256 的静态谷歌地图。
      • 不应该 FromPixelToCoordinates 方法返回 PointF(Convert.ToSingle(longitude), Convert.ToSingle(latitude)) 吗? [+1]
      【解决方案8】:

      我的方法可以在没有库和裁剪地图的情况下使用。意味着它仅适用于墨卡托图像中的部分。也许它可以帮助某人:https://stackoverflow.com/a/10401734/730823

      【讨论】:

        【解决方案9】:

        为此苦苦挣扎 - 同时拥有 openstreet 地图和 google 街道地图,并且想要投影外部图形图像

        var map = new OpenLayers.Map({
                    div:"map-id",
                    allOverlays: true
            });
            var osm = new OpenLayers.Layer.OSM("OpenStreeMao");
            var gmap = new OpenLayers.Layer.Google("Google Streets", {visibility: false});
        
            map.addLayers([osm,gmap]);
        
            var vectorLayer = new OpenLayers.Layer.Vector("IconLayer");
        
        
            var lonlatObject = new OpenLayers.LonLat(24.938622,60.170421).transform(
                    new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject()
            );
            console.log(lonlatObject);
        
            var point = new OpenLayers.Geometry.Point(lonlatObject.lon, lonlatObject.lat);
            console.log(point);
        
            var point2 = new OpenLayers.Geometry.Point(lonlatObject.x, lonlatObject.y);
            console.log(point2);
        
            var feature = new OpenLayers.Feature.Vector(point, null, {
                externalGraphic:  "http://cdn1.iconfinder.com/data/icons/SUPERVISTA/networking/png/72/antenna.png",
                graphicWidth: 72,
                graphicHeight: 72,
                fillOpacity: 1
            });
        
        
            vectorLayer.addFeatures(feature);
        
            map.addLayer(vectorLayer);
        
        
            map.setCenter(
                    new OpenLayers.LonLat(24.938622,60.170421).transform(
                    new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject()
                    ),
                    12);
        
             map.addControl(new OpenLayers.Control.LayerSwitcher());
        

        http://jsfiddle.net/alexcpn/N9dLN/8/

        【讨论】:

          【解决方案10】:

          要考虑的重要事项之一是投影的“缩放”级别(特别是对于 Google 地图)。

          正如谷歌解释的那样:

          在缩放级别 1 时,地图由 4 个 256x256 像素的图块组成,结果 在 512x512 的像素空间中。在缩放级别 19 上,每个 x 和 y 像素 可以使用 0 到 256 * 2^19 之间的值来引用地图

          (见https://developers.google.com/maps/documentation/javascript/maptypes?hl=en#MapCoordinates

          为了考虑“缩放”值,我推荐下面简单有效的 deltaLonPerDeltaXdeltaLatPerDeltaY 函数。虽然 x 像素和经度严格成比例,但 y 像素和纬度并非如此,公式需要初始纬度。

          // Adapted from : http://blog.cppse.nl/x-y-to-lat-lon-for-google-maps
          
          
          window.geo = {
          
              glOffset: Math.pow(2,28), //268435456,
              glRadius:  Math.pow(2,28) / Math.PI,
              a: Math.pow(2,28),
              b: 85445659.4471,
              c: 0.017453292519943,
              d: 0.0000006705522537,
              e: Math.E, //2.7182818284590452353602875,
              p: Math.PI / 180,
          
              lonToX: function(lon) {
                  return Math.round(this.glOffset + this.glRadius * lon * this.p);
              },
          
              XtoLon: function(x) {
                  return -180 + this.d * x;
              },
          
              latToY: function(lat) {
                  return Math.round(this.glOffset - this.glRadius *
                                    Math.log((1 + Math.sin(lat * this.p)) /
                                    (1 - Math.sin(lat * this.p))) / 2);
              },
          
              YtoLat: function(y) {
                  return Math.asin(Math.pow(this.e,(2*this.a/this.b - 2*y/this.b)) /
                                           (Math.pow(this.e, (2*this.a/this.b - 2*y/this.b))+1) -
                                           1/(Math.pow(this.e, (2*this.a/this.b - 2*y/this.b))+1)
                                  ) / this.c;
              },
          
              deltaLonPerDeltaX: function(deltaX, zoom) {
                  // 2^(7+zoom) pixels <---> 180 degrees
                  return deltaX * 180 / Math.pow(2, 7+zoom);
              },
          
              deltaLatPerDeltaY: function(deltaY, zoom, startLat) {
                  // more complex because of the curvature, we calculte it by difference
                  var startY = this.latToY(startLat),
                      endY = startY + deltaY * Math.pow(2, 28-7-zoom),
                      endLat = this.YtoLat(endY);
          
                  return ( endLat - startLat ); // = deltaLat
              }
          }
          

          【讨论】:

          • 你能举一个使用的例子吗?
          猜你喜欢
          • 1970-01-01
          • 2021-10-16
          • 1970-01-01
          • 2020-09-11
          • 2015-05-06
          • 1970-01-01
          • 2013-05-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多