【问题标题】:Convert latitude, longitude & altitude to local ENU coordinates in Python在 Python 中将纬度、经度和高度转换为本地 ENU 坐标
【发布时间】:2019-04-23 19:53:06
【问题描述】:

如何使用 Python 将大地(纬度、经度、高度)坐标转换为局部切平面 ENU(东、北、上)坐标?

pyproj 包似乎没有正确的功能...

【问题讨论】:

  • 东北上的经纬度是 3D 变换。你没想到它会出现在 2D 投影中,

标签: python-2.7 coordinate-systems coordinate-transformation pyproj


【解决方案1】:

您可以使用pymap3d 包:

安装

pip install pymap3d

简单示例

让我们以this page 上显示的示例值为例。

import pymap3d as pm

# The local coordinate origin (Zermatt, Switzerland)
lat0 = 46.017 # deg
lon0 = 7.750  # deg
h0 = 1673     # meters

# The point of interest
lat = 45.976  # deg
lon = 7.658   # deg
h = 4531      # meters

pm.geodetic2enu(lat, lon, h, lat0, lon0, h0)

产生

(-7134.757195979863, -4556.321513844541, 2852.3904239436915)

分别是东、北和上分量。

默认使用的椭球是WGS84。所有可用的椭球模型(可以作为geodetic2enu 的参数提供)可以在here 看到。以下是使用 WGS72 参考椭球计算相同 ENU 坐标的方法:

pm.geodetic2enu(lat, lon, h, lat0, lon0, h0, ell=pm.utils.Ellipsoid('wgs72'))
# (-7134.754845247729, -4556.320150825548, 2852.3904257449926)

【讨论】:

    【解决方案2】:

    在没有 pymap3d 的情况下使用 pyproj

    import numpy as np
    import pyproj
    import scipy.spatial.transform     
    
    def geodetic2enu(lat, lon, alt, lat_org, lon_org, alt_org):
        transformer = pyproj.Transformer.from_crs(
            {"proj":'latlong', "ellps":'WGS84', "datum":'WGS84'},
            {"proj":'geocent', "ellps":'WGS84', "datum":'WGS84'},
            )
        x, y, z = transformer.transform( lon,lat,  alt,radians=False)
        x_org, y_org, z_org = transformer.transform( lon_org,lat_org,  alt_org,radians=False)
        vec=np.array([[ x-x_org, y-y_org, z-z_org]]).T
    
        rot1 =  scipy.spatial.transform.Rotation.from_euler('x', -(90-lat_org), degrees=True).as_matrix()#angle*-1 : left handed *-1
        rot3 =  scipy.spatial.transform.Rotation.from_euler('z', -(90+lon_org), degrees=True).as_matrix()#angle*-1 : left handed *-1
    
        rotMatrix = rot1.dot(rot3)    
       
        enu = rotMatrix.dot(vec).T.ravel()
        return enu.T
    
    def enu2geodetic(x,y,z, lat_org, lon_org, alt_org):
        transformer1 = pyproj.Transformer.from_crs(
            {"proj":'latlong', "ellps":'WGS84', "datum":'WGS84'},
            {"proj":'geocent', "ellps":'WGS84', "datum":'WGS84'},
            )
        transformer2 = pyproj.Transformer.from_crs(
            {"proj":'geocent', "ellps":'WGS84', "datum":'WGS84'},
            {"proj":'latlong', "ellps":'WGS84', "datum":'WGS84'},
            )
        
        x_org, y_org, z_org = transformer1.transform( lon_org,lat_org,  alt_org,radians=False)
        ecef_org=np.array([[x_org,y_org,z_org]]).T
        
        rot1 =  scipy.spatial.transform.Rotation.from_euler('x', -(90-lat_org), degrees=True).as_matrix()#angle*-1 : left handed *-1
        rot3 =  scipy.spatial.transform.Rotation.from_euler('z', -(90+lon_org), degrees=True).as_matrix()#angle*-1 : left handed *-1
    
        rotMatrix = rot1.dot(rot3)
    
        ecefDelta = rotMatrix.T.dot( np.array([[x,y,z]]).T )
        ecef = ecefDelta+ecef_org
        lon, lat, alt = transformer2.transform( ecef[0,0],ecef[1,0],ecef[2,0],radians=False)
    
        return [lat,lon,alt]
    
    
    
    if __name__ == '__main__':
        # The local coordinate origin (Zermatt, Switzerland)
        lat_org = 46.017 # deg
        lon_org = 7.750  # deg
        alt_org   = 1673     # meters
    
        # The point of interest
        lat = 45.976  # deg
        lon = 7.658   # deg
        alt = 4531      # meters
    
        res1 = geodetic2enu(lat, lon, alt, lat_org, lon_org, alt_org)
        print (res1)
        #[-7134.75719598 -4556.32151385  2852.39042395]
        
        x=res1[0]
        y=res1[1]
        z=res1[2]
        res2 = enu2geodetic(x,y,z, lat_org, lon_org, alt_org)
        print (res2)
        #[45.97600000000164, 7.658000000000001, 4531.0000001890585]
    

    参考。 1https://gssc.esa.int/navipedia/index.php/Transformations_between_ECEF_and_ENU_coordinates

    参考 2 https://www.nsstc.uah.edu/users/phillip.bitzer/python_doc/pyltg/_modules/pyltg/utilities/latlon.html

    参考 3 https://gist.github.com/sbarratt/a72bede917b482826192bf34f9ff5d0b

    【讨论】:

      【解决方案3】:

      Pymap3d 模块,https://scivision.github.io/pymap3d/ 提供坐标变换和大地测量功能,包括 ENU (long, lat, alt)。

      这里有一些例子。

      import pymap3d
      # create an ellipsoid object
      ell_clrk66 = pymap3d.Ellipsoid('clrk66')
      # print ellipsoid's properties
      ell_clrk66.a, ell_clrk66.b, ell_clrk66.f
      
      # output
      (6378206.4, 6356583.8, 0.0033900753039287634)
      

      假设我们定义了一个 ENU 坐标系,其原点位于 (lat0, lon0, h0 = 5.0, 48.0, 10.0)。并设一个坐标为 ENU: (0,0,0) 的点 (point_1) 作为测试点,这个point_1 将用于进行正反变换。

      lat0, lon0, h0 = 5.0, 48.0, 10.0   # origin of ENU, (h is height above ellipsoid)
      e1, n1, u1     =  0.0,  0.0,  0.0  # ENU coordinates of test point, `point_1`
      # From ENU to geodetic computation
      lat1, lon1, h1 = pymap3d.enu2geodetic(e1, n1, u1, \
                                            lat0, lon0, h0, \
                                            ell=ell_clrk66, deg=True)  # use clark66 ellisoid
      print(lat1, lon1, h1)
      # display: (5.000000000000001, 48.0, 10.000000000097717)
      

      现在,使用获得的 (lat1, lon1, h1),我们计算大地到 ENU 的转换。

      e2, n2, u2 = pymap3d.geodetic2enu(lat1, lon1, h1, \
                                 lat0, lon0, h0, \
                                 ell=ell_clrk66, deg=True)
      print(e2, n2, u2)
      

      输出应该与 (e1, n1, u1) 一致。对于这种计算,小的差异是正常的。

      在上述计算中,ell 选项的默认值为WGS84 ellipsoid。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-23
        • 2020-02-23
        相关资源
        最近更新 更多