Python 如何选择纬度和经度在地球仪上形成一个“矩形”?

Python 如何选择纬度和经度在地球仪上形成一个“矩形”?,python,cartopy,Python,Cartopy,我有一个数据集,其中的值位于经纬度网格上。我需要从这个数据集中选择,在北美绘制一个近乎完美的矩形。一些东西,但放在北美: 一,。我如何选择纬度和经度 由于经度向两极会聚,我需要更多的向北经度 这是我的恶意尝试,可能是错误的。我猜这是cartopy中的一行,但我不知道我在寻找什么样的转换 我的矩形的高度在0°到75°N纬度之间。我在计算每个纬度的经度跨度,以米为单位的水平宽度与0°纬度下215°到305°经度的距离相同。矩形水平居中于260°左右 import numpy as np import

我有一个数据集,其中的值位于经纬度网格上。我需要从这个数据集中选择,在北美绘制一个近乎完美的矩形。一些东西,但放在北美:

一,。我如何选择纬度和经度

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

这是我的恶意尝试,可能是错误的。我猜这是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)
二,。如何在地球仪上绘制这些纬度/经度的数据

我试着从下面看清楚,但只是从右边剪下一条窄缝

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()

明显的错误是与代码中的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()

非常感谢。你知道有没有更简单的方法可以在球体上形成矩形的lat/lon网格上提取数据?@capitalcuttle请在我编辑的答案中尝试新代码。非常感谢。帮助我理解查看器透视图的作用,因为我想要球体上的矩形。我所做的是在投影到球体上的二维平面上的矩形。
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()