【问题标题】:How do I select latitudes and longitudes to form a "rectangle" on a globe?如何选择纬度和经度以在地球上形成“矩形”?
【发布时间】:2017-05-19 09:25:10
【问题描述】:

我有一个数据集,其中包含经纬度网格上的值。我需要从这个数据集中进行选择,以在北美绘制一个近乎完美的“矩形”。 like this 的东西,但放在北美:

1.如何选择经纬度?

由于经度向两极汇聚,我需要更多的经度向北。

这是我的 hacky 并且可能不正确的尝试。我猜这是cartopy 中的单行代码,但我不知道我在寻找什么转换。

我的矩形的高度从 0° 到 75°N 纬度。我正在计算每个纬度的经度跨度,使得水平宽度(以米为单位)与 0° 纬度从 215° 到 305° 经度的距离相同。 (矩形以 260° 为中心水平居中。)

import numpy as np
import cartopy.crs as ccrs
import matplotlib.pyplot as plt

def long_meters_at_lat(lat):
    """Calculate distance (in meters) between longitudes at a given latitude."""
    a = 6378137.0
    b = 6356752.3142
    e_sqr = a**2 / b**2 -1
    lat = lat * 2 * np.pi / 360
    return np.pi * a * np.cos(lat) / (180 * np.power(1 - e_sqr * np.square(np.sin(lat)), .5))

min_lat, max_lat = 0, 75
min_lon, max_lon = 215, 305  # Desired longitude spread at at min_lat
central_lon = (max_lon + min_lon) // 2

dist_betn_lats = 111000  # In meters.  Roughly constant
lat_range, lon_range = np.arange(max_lat, min_lat-1, -1), np.arange(min_lon, max_lon+1)
x_idxs, y_idxs = np.meshgrid(lon_range, lat_range)
y_meters = (y_idxs - min_lat) * dist_betn_lats
y_lats = y_idxs + min_lat

dist_betn_lons_at_min_lat = long_meters_at_lat(lat_range[-1])
x_meters = (x_idxs - central_lon) * dist_betn_lons_at_min_lat  # Plus/minus around central longitude
x_lons = central_lon + np.round(x_meters/long_meters_at_lat(lat_range)[:, None]).astype('uint16')

assert ((x_lons[:, -1] - x_lons[:, 0]) <= 360).all(), 'The area is wrapping around on itself'
x_lons = np.where(x_lons >= 360, x_lons-360, x_lons)

这就是y_lats, x_lons 的样子,看起来很正常(右上角的低经度已经环绕 360°)。

(array([[75, 75, 75, ..., 75, 75, 75],
        [74, 74, 74, ..., 74, 74, 74],
        [73, 73, 73, ..., 73, 73, 73],
        ..., 
        [ 2,  2,  2, ...,  2,  2,  2],
        [ 1,  1,  1, ...,  1,  1,  1],
        [ 0,  0,  0, ...,  0,  0,  0]]),
 array([[ 87,  91,  94, ...,  66,  69,  73],
        [ 97, 101, 104, ...,  56,  59,  63],
        [107, 110, 113, ...,  47,  50,  53],
        ..., 
        [215, 216, 217, ..., 303, 304, 305],
        [215, 216, 217, ..., 303, 304, 305],
        [215, 216, 217, ..., 303, 304, 305]], dtype=uint16))

2。如何在地球仪上绘制这些纬度/经度的数据?

我在下面尝试了明显的,但只是在右侧找到一条窄条。

crs = ccrs.PlateCarree()
u = np.random.rand(*x_lons.shape)
v = np.random.rand(*x_lons.shape)

ax = plt.axes(projection=ccrs.Orthographic())
ax.add_feature(cartopy.feature.OCEAN, zorder=0)
ax.add_feature(cartopy.feature.LAND, zorder=0, edgecolor='black')

ax.set_global()
ax.scatter(y_lats, x_lons, u, v, transform=crs)

plt.show()

【问题讨论】:

    标签: python cartopy


    【解决方案1】:

    明显的错误是代码中 (long, lat) 的反面。 这是要尝试的正确代码。

    # (second part only)
    crs = ccrs.PlateCarree()
    u = np.random.rand(*x_lons.shape)
    v = np.random.rand(*x_lons.shape)
    
    ax = plt.axes(projection=ccrs.Orthographic(central_longitude=-80, central_latitude=30))
    ax.add_feature(cartopy.feature.OCEAN, zorder=0)
    ax.add_feature(cartopy.feature.LAND, zorder=0, edgecolor='black')
    
    ax.set_global()
    ax.scatter(x_lons, y_lats, u, v, transform=crs)
    
    plt.show()
    

    编辑 1

    这里是完整的代码,只在地图上绘制某个矩形内的数据。

    import matplotlib.pyplot as plt
    import cartopy.crs as ccrs
    import cartopy
    import numpy as np
    import matplotlib.patches as mpatches
    
    # part 1 (minor change)
    
    def long_meters_at_lat(lat):
        """Calculate distance (in meters) between longitudes at a given latitude."""
        a = 6378137.0
        b = 6356752.3142
        e_sqr = a**2 / b**2 -1
        lat = lat * 2 * np.pi / 360
        return np.pi * a * np.cos(lat) / (180 * np.power(1 - e_sqr * np.square(np.sin(lat)), .5))
    
    min_lat, max_lat = 0, 75
    min_lon, max_lon = 215, 305  # Desired longitude spread at at min_lat
    central_lon = (max_lon + min_lon) // 2
    mean_lat = (max_lat + min_lat) // 2
    
    dist_betn_lats = 111000  # In meters.  Roughly constant
    lat_range, lon_range = np.arange(max_lat, min_lat-1, -1), np.arange(min_lon, max_lon+1)
    x_idxs, y_idxs = np.meshgrid(lon_range, lat_range)
    y_meters = (y_idxs - min_lat) * dist_betn_lats
    y_lats = y_idxs + min_lat
    
    dist_betn_lons_at_min_lat = long_meters_at_lat(lat_range[-1])
    x_meters = (x_idxs - central_lon) * dist_betn_lons_at_min_lat  # Plus/minus around central longitude
    x_lons = central_lon + np.round(x_meters/long_meters_at_lat(lat_range)[:, None]).astype('uint16')
    
    assert ((x_lons[:, -1] - x_lons[:, 0]) <= 360).all(), 'The area is wrapping around on itself'
    x_lons = np.where(x_lons >= 360, x_lons-360, x_lons)
    
    # part 2
    
    from_lonlat_degrees = ccrs.PlateCarree()
    
    # map projection to use
    proj1 = ccrs.Orthographic(central_longitude=central_lon, central_latitude=mean_lat)
    
    u = np.random.rand(*x_lons.shape)  # 0-1 values
    v = np.random.rand(*x_lons.shape)
    
    # auxillary axis for building a function (lonlat2gridxy)
    axp = plt.axes( projection = proj1 )
    axp.set_visible(False)
    
    # this function does coord transformation
    def lonlat2gridxy(axp, lon, lat):
        return axp.projection.transform_point(lon, lat, ccrs.PlateCarree())
    
    fig = plt.figure(figsize = (12, 16))  # set size as need
    ax = plt.axes(projection=proj1)
    
    ax.add_feature(cartopy.feature.OCEAN, zorder=0)
    ax.add_feature(cartopy.feature.LAND, zorder=0, edgecolor='black')
    
    # create rectangle for masking (adjust to one's need)
    # here, lower-left corner is (-130, 15) in degrees
    rex = mpatches.Rectangle( ax.projection.transform_point(-130, 15, ccrs.PlateCarree()), \
                              6500000, 4500000, \
                              facecolor="none")
    ax.add_artist(rex)
    bb = rex.get_bbox()   # has .contains() for use later
    
    # plot only lines (x,y), (u,v) if their
    #  (x,y) fall within the rectangle 'rex'
    sc = 1.  # scale for the vector sizes
    for xi,yi,ui,vi in zip(x_lons, y_lats, u, v):
        for xii,yii,uii,vii in zip(xi,yi,ui,vi):
            xj, yj = lonlat2gridxy(axp, xii, yii)
    
            # check only p1:(xj, yj), can also check p2:(xii+uii*sc, yii+vii*sc)
            # if it is inside the rectangle, plot line(p1,p2) in red
            if bb.contains(xj, yj):
                ax.plot((xii, xii+uii*sc), \
                        (yii, yii+vii*sc), \
                        'r-,', \
                        transform=from_lonlat_degrees)  #plot 2 point line
        pass
    
    # remove axp that occupies some figure area
    axp.remove()
    
    # without set_global, only rectangle part is plotted
    ax.set_global()  # plot full globe
    plt.show()
    

    【讨论】:

    • 非常感谢。您是否知道是否有更简单的方法来提取在球体上形成“矩形”的纬度/经度网格上的数据?
    • @capitalistcuttle 请在我编辑的答案中尝试新代码。
    • 非常感谢。帮助我了解我想要的观察者视角的作用(球体上的矩形)。而且,我在做什么(投影在球体上的二维平面上的矩形)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多