【问题标题】:Drawing Circles with cartopy in orthographic projection在正交投影中使用 cartopy 绘制圆
【发布时间】:2019-02-05 21:37:57
【问题描述】:

我正在尝试使用 cartopy 在给定地理坐标处绘制具有一定半径的圆圈。我想使用以圆心为中心的正交投影进行绘制。

我使用以下python代码进行测试:

import numpy as np
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

# example: draw circle with 45 degree radius around the North pole
lon = 0
lat = 90
r = 45

# find map ranges (with 5 degree margin)
minLon = lon - r - 5
maxLon = lon + r + 5
minLat = lat - r - 5
maxLat = lat + r + 5

# define image properties
width = 800
height = 800
dpi = 96
resolution = '50m'

# create figure
fig = plt.figure(figsize=(width / dpi, height / dpi), dpi=dpi)
ax = fig.add_subplot(1, 1, 1, projection=ccrs.Orthographic(central_longitude=lon, central_latitude=lat))
ax.set_extent([minLon, maxLon, minLat, maxLat])
ax.imshow(np.tile(np.array([[cfeature.COLORS['water'] * 255]], dtype=np.uint8), [2, 2, 1]), origin='upper', transform=ccrs.PlateCarree(), extent=[-180, 180, -180, 180])
ax.add_feature(cfeature.NaturalEarthFeature('physical', 'land', resolution, edgecolor='black', facecolor=cfeature.COLORS['land']))
ax.add_feature(cfeature.NaturalEarthFeature('cultural', 'admin_0_countries', resolution, edgecolor='black', facecolor='none'))
ax.add_feature(cfeature.NaturalEarthFeature('physical', 'lakes', resolution, edgecolor='none', facecolor=cfeature.COLORS['water']), alpha=0.5)
ax.add_feature(cfeature.NaturalEarthFeature('physical', 'rivers_lake_centerlines', resolution, edgecolor=cfeature.COLORS['water'], facecolor='none'))
ax.add_feature(cfeature.NaturalEarthFeature('cultural', 'admin_1_states_provinces_lines', resolution, edgecolor='gray', facecolor='none'))
ax.add_patch(mpatches.Circle(xy=[lon, lat], radius=r, color='red', alpha=0.3, transform=ccrs.PlateCarree(), zorder=30))
fig.tight_layout()
plt.savefig('CircleTest.png', dpi=dpi)

plt.show()

我在赤道得到了正确的结果(在上面的示例中将 lat 设置为 0):

但是当我走向一根杆子时,形状会变形(lat = 45):

在杆子上我只看到四分之一的圆:

如果视图居中正确,我希望在正交投影中始终看到一个完美的圆。我还尝试在 add_patch 方法中使用不同的变换,但随后圆圈完全消失了!

【问题讨论】:

    标签: python orthographic cartopy


    【解决方案1】:

    这可能有点晚了,但 Cartopy 中有一个方便的功能。

    我们可以使用 Cartopy 的 .circle 函数 (documentation) 从测地坐标系中的特定(经度和纬度)生成具有指定半径的点环,然后使用 Shapely 绘制带有这些点的多边形。

    这看起来像下面这样

    circle_points = cartopy.geodesic.Geodesic().circle(lon=lon, lat=lat, radius=radius_in_meters, n_samples=n_points, endpoint=False)
    geom = shapely.geometry.Polygon(circle_points)
    ax.add_geometries((geom,), crs=cartopy.crs.PlateCarree(), facecolor='red', edgecolor='none', linewidth=0)
    

    将 crs 指定为 PlateCarree 无关紧要,只是避免使用 Shapely 发出警告。您将保持您想要的投影。但是,如果您直接使用杆上的圆心进行绘图,您可能仍然会遇到问题并且可能需要进行一些花哨的转换(最近没有测试过,但回想几个月前它有点不稳定) .

    您还可以使用 Cartopy 使用的 pyproj 库(特别是 Geod 类)手动计算这些点。选择一个带半径的点并循环穿过方位角,无论您希望您的圆使用 .inv 或 .fwd 函数,这与https://stackoverflow.com/a/57002776/2430454 中的建议类似。我不推荐这种方法,但很久以前就用它来完成同样的事情。

    【讨论】:

      【解决方案2】:

      您在 PlateCarree 坐标中定义圆的方法行不通,因为这是一个笛卡尔投影,在其上绘制的圆在球面几何中不一定是圆形(除非圆在 (0, 0) 处)锯)。

      由于您希望结果在正交投影中是圆形的,因此您可以在原始坐标中绘制圆形。这需要首先定义以圆心为中心的正交投影,然后计算圆的半径(以度为单位指定)在投影坐标中(距投影中心的距离)。这样做很方便,因为它还为您提供了一种确定正确地图范围的简洁方法。下面的示例通过将一个点从投影中心向北 45 度(或向南,如果更方便的话)变换来计算正交坐标中的半径,并给出以下内容:

      完整代码如下:

      import numpy as np
      import cartopy.crs as ccrs
      import cartopy.feature as cfeature
      import matplotlib.pyplot as plt
      import matplotlib.patches as mpatches
      
      # example: draw circle with 45 degree radius around the North pole
      lat = 51.4198101
      lon = -0.950854653584
      r = 45
      
      # Define the projection used to display the circle:
      proj = ccrs.Orthographic(central_longitude=lon, central_latitude=lat)
      
      
      def compute_radius(ortho, radius_degrees):
          phi1 = lat + radius_degrees if lat <= 0 else lat - radius_degrees
          _, y1 = ortho.transform_point(lon, phi1, ccrs.PlateCarree())
          return abs(y1)
      
      # Compute the required radius in projection native coordinates:
      r_ortho = compute_radius(proj, r)
      
      # We can now compute the correct plot extents to have padding in degrees:
      pad_radius = compute_radius(proj, r + 5)
      
      # define image properties
      width = 800
      height = 800
      dpi = 96
      resolution = '50m'
      
      # create figure
      fig = plt.figure(figsize=(width / dpi, height / dpi), dpi=dpi)
      ax = fig.add_subplot(1, 1, 1, projection=proj)
      # Deliberately avoiding set_extent because it has some odd behaviour that causes
      # errors for this case. However, since we already know our extents in native
      # coordinates we can just use the lower-level set_xlim/set_ylim safely.
      ax.set_xlim([-pad_radius, pad_radius])
      ax.set_ylim([-pad_radius, pad_radius])
      ax.imshow(np.tile(np.array([[cfeature.COLORS['water'] * 255]], dtype=np.uint8), [2, 2, 1]), origin='upper', transform=ccrs.PlateCarree(), extent=[-180, 180, -180, 180])
      ax.add_feature(cfeature.NaturalEarthFeature('physical', 'land', resolution, edgecolor='black', facecolor=cfeature.COLORS['land']))
      ax.add_feature(cfeature.NaturalEarthFeature('cultural', 'admin_0_countries', resolution, edgecolor='black', facecolor='none'))
      ax.add_feature(cfeature.NaturalEarthFeature('physical', 'lakes', resolution, edgecolor='none', facecolor=cfeature.COLORS['water']), alpha=0.5)
      ax.add_feature(cfeature.NaturalEarthFeature('physical', 'rivers_lake_centerlines', resolution, edgecolor=cfeature.COLORS['water'], facecolor='none'))
      ax.add_feature(cfeature.NaturalEarthFeature('cultural', 'admin_1_states_provinces_lines', resolution, edgecolor='gray', facecolor='none'))
      ax.add_patch(mpatches.Circle(xy=[lon, lat], radius=r_ortho, color='red', alpha=0.3, transform=proj, zorder=30))
      fig.tight_layout()
      plt.savefig('CircleTest.png', dpi=dpi)
      plt.show()
      

      【讨论】:

      • 很好的答案@ajdawson +1
      • 运行此示例并将投影更改为 NorthPolarStereo 使圆固定在北极,忽略纬度和经度坐标。对此有任何提示吗? @ajdawson
      猜你喜欢
      • 2019-11-21
      • 2019-04-11
      • 2018-06-23
      • 2017-07-03
      • 2017-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多