【问题标题】:Mercator Projection slightly off墨卡托投影略微偏离
【发布时间】:2014-12-22 08:05:06
【问题描述】:

我正在构建一个需要大量谷歌地图图像的项目。我将这些函数定义为在另一个将自动收集图像的函数中使用。纬度变化很好,但我注意到经度略有偏差。那是近似墨卡托投影方法的产物吗?我的印象是我使用的转换非常准确,除了在接近两极时。

import math
import os
import DLMaps
#Finds the distance covered in a Static Maps image pixel
def PixDist(zoom,scale=2):
    earthCirc = 40075.0 #in Km's
    base = 256 #size of google maps at zoom = 0
    return earthCirc/(base*scale*(2**zoom))

#Finds the Km distance to the next google static maps image based on size of images,
# and distance per pixel
def DistNextImage(distpp, scale=2, size=640):
    return distpp*scale*size

#returns a new Lat, Lon co-ordinate given a starting point, km distance change and
# a NESW direction, values 1-4 being used to represent corresponding direction.
def NewLatLon(lat,lon, dist, direction):
    if direction==1:
        dist = dist/110.54 #approximate change in latitude mercator projection
        lat = lat + dist #heading north
    elif direction == 2:
        dist = dist/(110.32 * math.cos(math.pi*lat/180.0)) #approx change in lon
        lon = lon + dist
    elif direction==3:
        dist = dist/110.54 #approximate change in latitude mercator projection
        lat = lat - dist #heading south
    elif direction ==4:
        dist = dist/(110.32 * math.cos(math.pi*lat/180.0)) #approx change in lon
        lon = lon - dist
    return lat, lon

【问题讨论】:

  • 作为参考,我使用了布里斯班的纬度坐标,-27.4679, 153.0278,缩放级别 17。地图明显移动到远处,并且在起点和新坐标之间丢失了信息-ords.
  • 顺便说一句,任何精度的经纬度都不会唯一指定地球表面上的一个点(它在大约 1 公里以内)。您需要测量 lon、lat 的数据来唯一标识一个点。在某些情况下,基准面是一个球体或椭球体,但对于现代数据,它实际上是一个显示“海平面”不规则性的网格。但是,谷歌在其“投影”中丢弃了基准信息。您看到的差异是由于谷歌使用椭球近似而不是像您那样使用球形近似。 “正确”的近似值是不规则的基准。
  • 谷歌使用的近似值在某处可用吗?我的项目并不特别需要 100% 准确,我只是一个完美主义者。所以我需要重新定义我的 PixDist 函数,使其具有像南北极圆周一样的圆周以及与之垂直的圆周?

标签: python python-2.7


【解决方案1】:

地球不是一个真正的椭球体,有大量的坐标系,从一个坐标系到另一个坐标系绝非易事。您可以查看pyproj 一个 Python 接口,该接口连接到众所周知的 proj.4 库,以从 Lat-Lon(我假设 WGS84 ...)转换为几乎任何其他坐标,当然包括墨卡托。您可以尝试自己滚动,但是有很多警告,例如不同的经络,参考椭球的细微差异,您几乎没有希望得到正确和准确的结果。

但是您在wikipedia 上有一些关于 WGS84 的参考资料

【讨论】:

  • 是的,我所做的是(有点)天真地看待地球,作为一个完美的椭球体。它似乎给了我满意的结果,但如果需要很高的精度,我可以理解计算需要额外的公式
  • 有趣的是,谷歌地图“投影”使用 WGS84 经纬度坐标,但使用墨卡托投影中的基准信息(它假设一个椭球体,而不是比完整的、不规则的基准面)。无论如何,总的来说,您是非常正确的(投影就像密码学:最好留给标准库),但是可以仅使用椭球参数重新创建谷歌的墨卡托投影。您不需要网格化基准信息来执行此操作,如果您正确执行并使用基准信息,您实际上会得到“错误”的答案。
【解决方案2】:

我制作了一个进行类似计算的对象。也许它会给你一些启发。 基本上我把地球当作一个椭球体。沿赤道的 earthCirc 与通过两极的 earthCirc 不同。 我尝试在以米为单位的距离之间进行转换 纬度和经度角。

看看我的对象是否比你的更准确(如果你使用一些极端值,我的肯定有错误)

/**
* @file:  Javascript object to help calculate with latitude, longitude, together with distances (in meter) and angles.
* The initial goal was to calculate the end point (in lat and latitude & longitude) of a line perpendicular to another line.
*
* Planet Earth is approximately an ellipsoid.  The circumference along the equator is
*   somewhat greater than the equator through both poles.
* this javascript object makes calculations that are useful for Google Maps. 
*   This will allow to use pythagoras for coordinates, as if earth is a flat rectangle.
* The precision of the results decreases when the distances increase; and near the poles.
*   Any calculation where the latitude goes beyond the poles ( > 90 or < -90 ) will probably return complete nonsence.
* 
* @author: Emmanuel Delay, emmanueldelay@gmail.com
* copyleft 2014.  Feel free to use, copy, share, improve
*  Please send me the code, if you make improvements.
* 
* Examples:
<script>
  function log(message) {
    document.getElementById('log').innerHTML += message + '<br>';
  }
  window.onload = function() {       
    var dLatLng = Earth.xy2LatLng(5000000, 5000000, 0.0);
    latLng = [dLatLng.lat, dLatLng.lng ]; 
    log(
      'Start from 0,0 ; move 5000km to the north, 5000km to the east: ' +
      latLng[0] +','+ latLng[1]
    );

    var eifel = {lat: 48.8582186, lng: 2.2946114};
    var dLatLng = Earth.xy2LatLng(1000, 2000, eifel.lat);

    latLng = [dLatLng.lat, dLatLng.lng ];
    var dest = [eifel.lat + latLng[0], eifel.lng + latLng[1] ];
    log(
      'Move 1km to the north, 2km to the east of the Eifel Tower: ' +
      dest[0] +','+ dest[1] 
    );

    var dLatLng = Earth.setHeading(eifel.lat, eifel.lng, 10000, 30);
    latLng = [dLatLng.lat, dLatLng.lng ]; 
    log(
      'Move 10km from the Eifel Tower, heading 30° (North = 0; east = 90°; ...): ' +
      latLng[0] +','+ latLng[1]
    );
  }
</script>
<div id="log"></div>

  * note: 
  *   - all distances are in meter.  all angles are in degree
  *   - the d in dLat and dLng stands for delta, being a change in coordinates
  *   - x is along the longitude, y is along latitude
  */

Earth = {
    // @see http://www.space.com/17638-how-big-is-earth.html for the data
    // along the equator
  circumference_equator: 40075000,    
   // throught both poles.
   // Note: this is basically the original definition of the meter; they were 2km off on a distance from pole to equator ( http://en.wikipedia.org/wiki/History_of_the_metre )
  circumference_poles: 40008000,              
  // given a change in latitude, how many meters did you move?
  lat2Y: function(dLat) {
    return this.circumference_poles / 360 * dLat;
  },
  // given a change in longitude and a given latitude, how many meters did you move?
  lng2X: function(dLng, lat) {
    return Math.cos( this.deg2rad(lat) ) * (this.circumference_poles / 360 * dLng);
  },
  // given a distance you move due North (or South), what's the new coordinates?
  // returns a change in latitude
  y2Lat: function(y) {
    return y * 360 / this.circumference_poles;
  },
  // given a distance you move due East (or West) and a given latitude, what's the new coordinates?
  // returns a change in longitude
  x2Lng: function(x, lat) {
    return x * 360 / ( Math.cos( this.deg2rad(lat) ) * this.circumference_poles);
  },
  // (360°) degrees to radials
  deg2rad: function(deg) {
    return deg * Math.PI / 180;
  },
  // returns a change in position
  xy2LatLng: function(y, x, lat) {
    return {
      lat: this.y2Lat(y),
      lng: this.x2Lng(x, lat)
    };
  },
  // @param heading: North = 0; east = 90°; ...
  // returns a change in position
  setHeading: function(lat, lng, dist, heading) {
    var latDestination = lat +  this.y2Lat(dist * Math.cos(this.deg2rad(heading)));
    var lngDestination = lng +  this.x2Lng(dist * Math.sin(this.deg2rad(heading)), lat);
    return {
      lat: latDestination,
      lng: lngDestination
    };
  },
  // returns the absolute position
  moveByXY: function(lat, lng, x, y) {
    var dLatLng = Earth.xy2LatLng(x, y, lat);
    latLng = [dLatLng.lat, dLatLng.lng ];
    return {
      lat: lat + latLng[0], 
      lng: lng + latLng[1]
    }
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-05
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多