如何将经纬度坐标按照地球表面的N-E米距离进行平移?

3

如何在地球表面进行一定的位移(以米为单位)后,从大地参考点(采用大地经度/纬度表示)获取新的大地坐标,并且需要使用真实的椭球模型(例如WGS84)进行计算。

例如:

  • 假设我有一个参考点,其大地坐标为10.32E,-4.31N
  • 然后我将其沿东方向移动3000米,沿南方向移动2000米在地球表面上
  • 接着我需要获取新点的大地坐标

谢谢。


当你谈论向东和向南移动时,你真的是指沿着纬度和经度线移动吗?还是你是指沿着其他网格的网格线移动,比如英国使用的军事测量局网格? - Raedwald
这是关于经纬度的内容。这就是为什么我称之为北/南 - 东/西,但我需要以地球表面的米为单位移动,而不是以球坐标的度为单位,并且我需要再次以球坐标(实际上是椭球坐标)的形式得到结果。 - uray
1
以什么顺序?先东再南,还是先南再东?这是有区别的。 - Alexandre C.
先东西方向,再南北方向。注意:这里的“米”距离不是直线距离,而是“弧长”,因为它是在球面上的距离。 - uray
5个回答

2
请看开源库PROJ.4,你可以使用它将地理坐标(纬度/经度)精确转换为投影坐标(米),然后再反向转换回去。在你的情况下,你可以投影到WGS 84 / 世界墨卡托(EPSG:3395),以米为单位进行转换,然后再反向投影回地理坐标。

目前我正在这样做,即将大地测量(纬度/经度)的当前参考点转换为UTM(通用横向墨卡托投影),并使用UTM坐标进行移动,然后将新位置从UTM转换回大地测量。但是,我讨厌UTM的一件事情是它们有区域/球体枚举,我正在寻找没有任何分区的墨卡托投影,你知道吗? - uray
WGS84世界墨卡托投影与UTM不同。WGS84世界墨卡托的投影边界为:-20037508.3428,-15496570.7397,20037508.3428,18764656.2314。这覆盖了整个世界。 - badgerr

1

0

如果涉及获取两个纬度/经度坐标之间距离的问题,这里有更好的文章和代码:http://www.movable-type.co.uk/scripts/latlong-vincenty.html,但是反转它并不是一件容易的事情,这就是为什么我在这里询问的原因。 - uray
@uray:即使是(可能很昂贵的)数值反演也不行吗? - Alexandre C.
@alex:嗯,我不是大地测量数学方面的专家,因为它涉及椭球面,如果是完美的球体可能会更简单。 - uray
@uray:它不依赖于底层模型,你有一个函数,它接受一个位置并返回一个位移。您可以使用有限差分导数的Newton方法对其进行反演。 - Alexandre C.
使用它非常简单。只需获取您所在位置向北1度的弧长,将您向北的平移除以该值,即可得到纬度变化量。然后获取您新位置向东1度的弧长,并将您向东的平移除以该值,即可得到经度变化量。 - Null Set
@null:我做不到,我需要精确到厘米的准确度。 - uray

0
以下是稍作修改的C++代码,源自苏黎世联邦理工学院。该文件仅依赖于Eigen库(如果需要自己编写矩阵乘法函数,则可以通过一些微不足道的工作来消除该依赖)。您可以使用geodetic2ned()函数将纬度、经度和高度转换为NED框架。
//GeodeticConverter.hpp
#ifndef air_GeodeticConverter_hpp
#define air_GeodeticConverter_hpp

#include <math>
#include <eigen3/Eigen/Dense>

class GeodeticConverter
{
 public:
  GeodeticConverter(double home_latitude = 0, double home_longitude = 0, double home_altitude = 0)
    : home_latitude_(home_latitude), home_longitude_(home_longitude)
  {
    // Save NED origin
    home_latitude_rad_ = deg2Rad(latitude);
    home_longitude_rad_ = deg2Rad(longitude);
    home_altitude_ = altitude;

    // Compute ECEF of NED origin
    geodetic2Ecef(latitude, longitude, altitude, &home_ecef_x_, &home_ecef_y_, &home_ecef_z_);

    // Compute ECEF to NED and NED to ECEF matrices
    double phiP = atan2(home_ecef_z_, sqrt(pow(home_ecef_x_, 2) + pow(home_ecef_y_, 2)));

    ecef_to_ned_matrix_ = nRe(phiP, home_longitude_rad_);
    ned_to_ecef_matrix_ = nRe(home_latitude_rad_, home_longitude_rad_).transpose();    
  }

  void getHome(double* latitude, double* longitude, double* altitude)
  {
    *latitude = home_latitude_;
    *longitude = home_longitude_;
    *altitude = home_altitude_;
  }

  void geodetic2Ecef(const double latitude, const double longitude, const double altitude, double* x,
                     double* y, double* z)
  {
    // Convert geodetic coordinates to ECEF.
    // http://code.google.com/p/pysatel/source/browse/trunk/coord.py?r=22
    double lat_rad = deg2Rad(latitude);
    double lon_rad = deg2Rad(longitude);
    double xi = sqrt(1 - kFirstEccentricitySquared * sin(lat_rad) * sin(lat_rad));
    *x = (kSemimajorAxis / xi + altitude) * cos(lat_rad) * cos(lon_rad);
    *y = (kSemimajorAxis / xi + altitude) * cos(lat_rad) * sin(lon_rad);
    *z = (kSemimajorAxis / xi * (1 - kFirstEccentricitySquared) + altitude) * sin(lat_rad);
  }

  void ecef2Geodetic(const double x, const double y, const double z, double* latitude,
                     double* longitude, double* altitude)
  {
    // Convert ECEF coordinates to geodetic coordinates.
    // J. Zhu, "Conversion of Earth-centered Earth-fixed coordinates
    // to geodetic coordinates," IEEE Transactions on Aerospace and
    // Electronic Systems, vol. 30, pp. 957-961, 1994.

    double r = sqrt(x * x + y * y);
    double Esq = kSemimajorAxis * kSemimajorAxis - kSemiminorAxis * kSemiminorAxis;
    double F = 54 * kSemiminorAxis * kSemiminorAxis * z * z;
    double G = r * r + (1 - kFirstEccentricitySquared) * z * z - kFirstEccentricitySquared * Esq;
    double C = (kFirstEccentricitySquared * kFirstEccentricitySquared * F * r * r) / pow(G, 3);
    double S = cbrt(1 + C + sqrt(C * C + 2 * C));
    double P = F / (3 * pow((S + 1 / S + 1), 2) * G * G);
    double Q = sqrt(1 + 2 * kFirstEccentricitySquared * kFirstEccentricitySquared * P);
    double r_0 = -(P * kFirstEccentricitySquared * r) / (1 + Q)
        + sqrt(
            0.5 * kSemimajorAxis * kSemimajorAxis * (1 + 1.0 / Q)
                - P * (1 - kFirstEccentricitySquared) * z * z / (Q * (1 + Q)) - 0.5 * P * r * r);
    double U = sqrt(pow((r - kFirstEccentricitySquared * r_0), 2) + z * z);
    double V = sqrt(
        pow((r - kFirstEccentricitySquared * r_0), 2) + (1 - kFirstEccentricitySquared) * z * z);
    double Z_0 = kSemiminorAxis * kSemiminorAxis * z / (kSemimajorAxis * V);
    *altitude = U * (1 - kSemiminorAxis * kSemiminorAxis / (kSemimajorAxis * V));
    *latitude = rad2Deg(atan((z + kSecondEccentricitySquared * Z_0) / r));
    *longitude = rad2Deg(atan2(y, x));
  }

  void ecef2Ned(const double x, const double y, const double z, double* north, double* east,
                double* down)
  {
    // Converts ECEF coordinate position into local-tangent-plane NED.
    // Coordinates relative to given ECEF coordinate frame.

    Vector3d vect, ret;
    vect(0) = x - home_ecef_x_;
    vect(1) = y - home_ecef_y_;
    vect(2) = z - home_ecef_z_;
    ret = ecef_to_ned_matrix_ * vect;
    *north = ret(0);
    *east = ret(1);
    *down = -ret(2);
  }

  void ned2Ecef(const double north, const double east, const double down, double* x, double* y,
                double* z)
  {
    // NED (north/east/down) to ECEF coordinates
    Vector3d ned, ret;
    ned(0) = north;
    ned(1) = east;
    ned(2) = -down;
    ret = ned_to_ecef_matrix_ * ned;
    *x = ret(0) + home_ecef_x_;
    *y = ret(1) + home_ecef_y_;
    *z = ret(2) + home_ecef_z_;
  }

  void geodetic2Ned(const double latitude, const double longitude, const double altitude,
                    double* north, double* east, double* down)
  {
    // Geodetic position to local NED frame
    double x, y, z;
    geodetic2Ecef(latitude, longitude, altitude, &x, &y, &z);
    ecef2Ned(x, y, z, north, east, down);
  }

  void ned2Geodetic(const double north, const double east, const double down, double* latitude,
                    double* longitude, double* altitude)
  {
    // Local NED position to geodetic coordinates
    double x, y, z;
    ned2Ecef(north, east, down, &x, &y, &z);
    ecef2Geodetic(x, y, z, latitude, longitude, altitude);
  }

  void geodetic2Enu(const double latitude, const double longitude, const double altitude,
                    double* east, double* north, double* up)
  {
    // Geodetic position to local ENU frame
    double x, y, z;
    geodetic2Ecef(latitude, longitude, altitude, &x, &y, &z);

    double aux_north, aux_east, aux_down;
    ecef2Ned(x, y, z, &aux_north, &aux_east, &aux_down);

    *east = aux_east;
    *north = aux_north;
    *up = -aux_down;
  }

  void enu2Geodetic(const double east, const double north, const double up, double* latitude,
                    double* longitude, double* altitude)
  {
    // Local ENU position to geodetic coordinates

    const double aux_north = north;
    const double aux_east = east;
    const double aux_down = -up;
    double x, y, z;
    ned2Ecef(aux_north, aux_east, aux_down, &x, &y, &z);
    ecef2Geodetic(x, y, z, latitude, longitude, altitude);
  }

private:
  // Geodetic system parameters
  static double kSemimajorAxis = 6378137;
  static double kSemiminorAxis = 6356752.3142;
  static double kFirstEccentricitySquared = 6.69437999014 * 0.001;
  static double kSecondEccentricitySquared = 6.73949674228 * 0.001;
  static double kFlattening = 1 / 298.257223563;

  typedef Eigen::Vector3d Vector3d;
  typedef Eigen::Matrix<double, 3, 3> Matrix3x3d;

  inline Matrix3x3d nRe(const double lat_radians, const double lon_radians)
  {
    const double sLat = sin(lat_radians);
    const double sLon = sin(lon_radians);
    const double cLat = cos(lat_radians);
    const double cLon = cos(lon_radians);

    Matrix3x3d ret;
    ret(0, 0) = -sLat * cLon;
    ret(0, 1) = -sLat * sLon;
    ret(0, 2) = cLat;
    ret(1, 0) = -sLon;
    ret(1, 1) = cLon;
    ret(1, 2) = 0.0;
    ret(2, 0) = cLat * cLon;
    ret(2, 1) = cLat * sLon;
    ret(2, 2) = sLat;

    return ret;
  }

  inline double rad2Deg(const double radians)
  {
    return (radians / M_PI) * 180.0;
  }

  inline double deg2Rad(const double degrees)
  {
    return (degrees / 180.0) * M_PI;
  }

  double home_latitude_rad_, home_latitude_;
  double home_longitude_rad_, home_longitude_;
  double home_altitude_;

  double home_ecef_x_;
  double home_ecef_y_;
  double home_ecef_z_;

  Matrix3x3d ecef_to_ned_matrix_;
  Matrix3x3d ned_to_ecef_matrix_;

}; // class GeodeticConverter

#endif

0

你可以找一些地理库,或者自己进行三角函数计算。

无论如何,你应该更准确地阐述你的问题。特别是你说:

然后我对(3000,-2000)米进行转换(即在地球表面向东移动3000米,向南移动2000米)。

你应该注意,先向东移动3公里,再向南移动2公里先向南移动2公里,再向东移动3公里不同。这些不是可交换的操作。因此,用偏移量(3000,-2000)来称呼它是不正确的。


3
“或者你可以自己做三角运算”,这并不是一项简单的任务:因为地球的表面并不是一个球体。 - Raedwald
1
不,正如Raedwald所指出的那样,这不是数学问题。信息不足。当问题指出需要WGS-84模型时,它已经承认了这一点。 - MSalters
@Raedwald:我并没有说这是一项琐碎的任务。然而,它仍然属于解析几何领域。只要具备适当的数学背景,就没有问题可以完成这个任务。 - valdo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接