【问题标题】:Converting Pixels to LatLng Coordinates from google static image从谷歌静态图像将像素转换为 LatLng 坐标
【发布时间】:2018-04-16 19:20:15
【问题描述】:

我正在从google static Map API 加载图像,加载的卫星图像是一个宽和长数百米的地方。

https://maps.googleapis.com/maps/api/staticmap?center=53.4055429,-2.9976502&zoom=16&size=400x400&maptype=satellite&key=YOUR_API_KEY

另外,图像分辨率显示为10米,如下图

。

我的问题是

我知道这个静态图像的居中地理位置(53.4055429,-2.9976502)和分辨率,我如何能够扩展它来计算图像中左上或右下的地理位置,最后计算图像的每个像素

【问题讨论】:

  • 是的,center= 参数后面的坐标是地图的中心 (center=53.4055429,-2.9976502)。为什么你认为他们不是?
  • 只是为了确认。我如何获得每个像素的坐标,是否有可能
  • 必须是静态地图吗? JavaScript API 的 CanvasProjection 具有将像素位置转换为 lat/lng developers.google.com/maps/documentation/javascript/3.exp/… 的功能
  • 你可以试试我的回答。实际上你不需要谷歌地图js api来做简单的数学,比如幂和余弦。请注意,这两个答案在输出控制台中具有相同的结果。
  • 你应该选择最佳答案,否则你的赏金会白白消失。

标签: python google-maps google-maps-api-3 geolocation


【解决方案1】:

我相信您可以使用 Maps JavaScript API 计算边界框。

您有一个中心位置,并且知道从中心到东北和西南的距离为 200 像素,因为您的示例中的大小为 400x400。

看看下面计算NE和SW点的代码

var map;
function initMap() {
  var latLng = new google.maps.LatLng(53.4055429,-2.9976502);

  map = new google.maps.Map(document.getElementById('map'), {
      center: latLng,
      zoom: 16,
      mapTypeId: google.maps.MapTypeId.SATELLITE
  });

  var marker = new google.maps.Marker({
      position: latLng,
      map: map
  });

  google.maps.event.addListener(map, "idle", function() {
      //Verical and horizontal distance from center in pixels
      var h = 200;
      var w = 200;

      var centerPixel = map.getProjection().fromLatLngToPoint(latLng);
      var pixelSize = Math.pow(2, -map.getZoom());

      var nePoint = new google.maps.Point(centerPixel.x + w*pixelSize, centerPixel.y - h*pixelSize);
      var swPoint = new google.maps.Point(centerPixel.x - w*pixelSize, centerPixel.y + h*pixelSize);

      var ne = map.getProjection().fromPointToLatLng(nePoint);
      var sw = map.getProjection().fromPointToLatLng(swPoint);

      var neMarker = new google.maps.Marker({
        position: ne,
        map: map,
        title: "NE: " + ne.toString()
      });

      var swMarker = new google.maps.Marker({
        position: sw,
        map: map,
        title: "SW: " + sw.toString()
      });

      var polygon = new google.maps.Polygon({
          paths: [ne, new google.maps.LatLng(ne.lat(),sw.lng()), sw, new google.maps.LatLng(sw.lat(),ne.lng())],
          map: map, 
          strokeColor: "green"
      });

      console.log("NE: " + ne.toString());
      console.log("SW: " + sw.toString());

  });
}
#map {
  height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html, body {
  height: 100%;
  margin: 0;
  padding: 0;
}
<div id="map"></div>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDztlrk_3CnzGHo7CFvLFqE_2bUKEq1JEU&libraries=geometry&callback=initMap"
    async defer></script>

我希望这会有所帮助!

更新

为了在 python 中解决这个问题,您应该了解 Google Maps JavaScript API 使用的Map and Tile Coordinates 原则,并在 python 中实现类似于 Google Maps API 的投影逻辑。

幸运的是,有人已经完成了这项任务,您可以从我在 python 中的示例中找到实现类似于map.getProjection().fromLatLngToPoint() 和map.getProjection().fromPointToLatLng() 的方法的项目。在 github 上看看这个项目:

https://github.com/hrldcpr/mercator.py

因此,您可以下载 mercator.py 并在您的项目中使用它。我的 JavaScript API 示例转换为以下 python 代码

#!/usr/bin/python

from mercator import *

w = 200
h = 200
zoom = 16
lat = 53.4055429
lng = -2.9976502

centerPixel = get_lat_lng_tile(lat, lng, zoom)
pixelSize = pow(2, -zoom)

nePoint = (centerPixel[0] + w*pixelSize, centerPixel[1] - h*pixelSize)
swPoint = (centerPixel[0] - w*pixelSize, centerPixel[1] + h*pixelSize)

ne = get_tile_lat_lng(zoom, nePoint[0], nePoint[1]);
sw = get_tile_lat_lng(zoom, swPoint[0], swPoint[1]);

print 'NorthEast: ', ne
print 'SouthWest: ', sw 

【讨论】:

  • 我正在研究python,它会是基于python的api吗?
  • 你的python结果肯定是错的,和js结果不一样。
  • @shukshin.ivan 感谢您的评论。我没有彻底测试mercator.py,确实,实现可能不够精确,但它证明了这个想法。有空我会去看看。
  • 我说的不是精度,而是错误。差别很大,超过一百倍。
  • 反正作者好像忘了他的问题)
【解决方案2】:

是什么样的解决方案

看起来你不需要一个 javascript 解决方案,但 python 不是在浏览器中而是在服务器上使用它。我创建了一个 python 示例,但我要坚持的是数学,数学是计算坐标所需的全部。让我也用js来做,让sn-p在浏览器中工作。可以看到,python 和 js 的结果是一样的。

跳转到答案

如果您只需要计算每像素度数的公式,这里就是。它们很简单,你不需要任何外部库,只需要一个 python 的math。可以进一步找到解释。

#!/usr/bin/python
import math

w = 400
h = 400
zoom = 16
lat = 53.4055429
lng = -2.9976502

def getPointLatLng(x, y):
    parallelMultiplier = math.cos(lat * math.pi / 180)
    degreesPerPixelX = 360 / math.pow(2, zoom + 8)
    degreesPerPixelY = 360 / math.pow(2, zoom + 8) * parallelMultiplier
    pointLat = lat - degreesPerPixelY * ( y - h / 2)
    pointLng = lng + degreesPerPixelX * ( x  - w / 2)

    return (pointLat, pointLng)

print 'NE: ', getPointLatLng(w, 0)
print 'SW: ', getPointLatLng(0, h)
print 'NW: ', getPointLatLng(0, 0)
print 'SE: ', getPointLatLng(w, h)

the script 的输出是

$ python getcoords.py
NE:  (53.40810128625675, -2.9933586655761717)
SW:  (53.40298451374325, -3.001941734423828)
NW:  (53.40810128625675, -3.001941734423828)
SE:  (53.40298451374325, -2.9933586655761717)

我们必须从什么开始

我们在 url https://maps.googleapis.com/maps/api/staticmap?center=53.4055429,-2.9976502&amp;zoom=16&amp;size=400x400&amp;maptype=satellite&amp;key=YOUR_API_KEY 中需要一些参数——坐标、缩放、像素大小。

我们来介绍一些初始变量:

var config = {
    lat: 53.4055429,
    lng: -2.9976502,
    zoom: 16,
    size: {
        x: 400,
        y: 400,
    }
};

512 像素地球的数学运算

数学如下。缩放1 表示使用图像大小512 (see the docs for size and zoom) 时地球赤道360° 的完整视图。请参阅example at zoom 1。这是非常重要的一点。比例(每像素度数)不取决于图像大小。当一个人改变图像大小时,一个人看到相同的比例:比较1 和2——第二张图片是较大的一张的裁剪版本。 googleapis 的最大图像大小为 640。

每次放大increases resolution twice。因此,图像的经度宽度为

lngDegrees = 360 / 2**(zoom - 1); // full image width in degrees, ** for power

然后使用线性函数找到图像任意点的坐标。应该提到的是,线性仅适用于高缩放图像,您不能将其用于 5 或​​更少的低缩放。低缩放具有稍微复杂的数学。

lngDegreesPerPixel = lngDegrees / 512 = 360 / 2**(zoom - 1) / 2**9 = 360 / 2**(zoom + 8); 
lngX = config.lng + lngDegreesPerPixel * ( point.x - config.size.x / 2);

纬度不同

赤道上的纬度和经度大小相同,但是如果我们向北或向南,经度会变小,因为地球上的平行环具有较小的半径 - r = R * cos(lat) &lt; R,因此以度为单位的图像高度变为更小(见 PS)。

latDegrees = 360 / 2**(zoom - 1) * cos(lat); // full image height in degrees, ** for power

分别

latDegreesPerPixel = latDegrees / 512 = 360 / 2**(zoom - 1) * cos(lat) / 2**9 = 360 / 2**(zoom + 8) * cos(lat);
latY = config.lat - latDegreesPerPixel * ( point.y - config.size.y / 2)

config.lat 后面的符号与 lngX 的符号不同,因为地球经度方向与图像 x 方向一致,但纬度方向与图像上的 y 方向相反。

所以我们现在可以创建一个简单的函数来使用图片上的x 和y 坐标来查找像素的坐标。

var config = {
    lat: 53.4055429,
    lng: -2.9976502,
    zoom: 16,
    size: {
        x: 400,
        y: 400,
    }
};

function getCoordinates(x, y) {
    var degreesPerPixelX = 360 / Math.pow(2, config.zoom + 8);
    var degreesPerPixelY = 360 / Math.pow(2, config.zoom + 8) * Math.cos(config.lat * Math.PI / 180);

    return {
        lat: config.lat - degreesPerPixelY * ( y - config.size.y / 2),
        lng: config.lng + degreesPerPixelX * ( x  - config.size.x / 2),
    };
}

console.log('SW', getCoordinates(0, config.size.y));
console.log('NE', getCoordinates(config.size.x, 0));
console.log('SE', getCoordinates(config.size.x, config.size.y));
console.log('NW', getCoordinates(0, 0));
console.log('Something at 300,128', getCoordinates(300, 128));

附:您可能会问我,为什么我将 cos(lat) 乘数放在纬度上,而不是作为经度公式的除数。我发现,谷歌选择在不同纬度上的每个像素具有恒定的经度比例,因此,cos 将纬度作为乘数。

【讨论】:

  • 此公式适用于像素的左上角。将0.5添加到函数的参数以获得像素中心的结果。
  • 我正在使用上面的公式来计算任何给定坐标的 Point/(x,y) 但没有运气。
  • Point getPointFromCoordinates(double lat, double lng) { double degreePerPixelX = 360.0 / Math.pow(2, zoom + 8);双度PerPixelY = 360.0 / Math.pow(2, zoom + 8) * Math.cos(centerLat * Math.PI / 180.0);点点 = 新点(); point.y = (int) ((centerLat - lat) / degreePerPixelY) + (size.y / 2); point.x = (int) ((centerLng - lng) / degreePerPixelX) + (size.x / 2);返回点; }
猜你喜欢
  • 2015-07-12
  • 1970-01-01
  • 2014-07-16
  • 1970-01-01
  • 1970-01-01
  • 2012-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多