You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Cartopy在西南印度洋地图上标注国家名称?

解决Cartopy西南印度洋地图国家标注问题

问题说明

尝试用Cartopy绘制西南印度洋区域地图,添加国家名称标注(如莫桑比克)时报错,目标是为地图范围内所有国家添加标注。以下是尝试标注的报错代码和无标注的正常代码:

报错的标注尝试代码

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER

def plot_southwest_indian_ocean():
    # 定义西南印度洋的经纬度范围
    lat_range = [-40, 0]
    lon_range = [-340, -260]

    # 创建带投影的画布和轴
    fig, ax = plt.subplots(figsize=(8, 6), subplot_kw={'projection': ccrs.PlateCarree()})

    # 设置地图边界
    ax.set_extent(lon_range + lat_range, crs=ccrs.PlateCarree())

    # 添加陆地和海洋要素
    ax.add_feature(cfeature.LAND, edgecolor='black')
    ax.add_feature(cfeature.OCEAN)

    # 添加国界线
    ax.add_feature(cfeature.BORDERS, linestyle='-', alpha=0.5)

    # 尝试添加国家标签
    countries = cfeature.NaturalEarthFeature(
        category='cultural',
        name='admin_0_countries',
        scale='50m',
        facecolor='none',
        edgecolor='gray'
    )
    ax.add_feature(countries, linewidth=0.5)

    country_name = 'Mozambique'
    country_boundaries = [country for country in countries.geometries() if country.properties['ADMIN'] == country_name]

    for boundary in country_boundaries:
        centroid = boundary.centroid
        ax.annotate(
            country_name,
            xy=(centroid.x, centroid.y),
            xytext=(5, 5),
            textcoords='offset points',
            ha='left',
            va='bottom',
            fontsize=8,
            color='black',
            bbox=dict(boxstyle='round,pad=0.2', fc='white', ec='gray', lw=0.5)
        )

    # 添加网格线
    gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True, linewidth=1, color='gray', alpha=0.5, linestyle='--')
    gl.ylabels_right = False
    gl.xformatter = LONGITUDE_FORMATTER
    gl.yformatter = LATITUDE_FORMATTER
    gl.xlabel_style = {'size': 12, 'color': 'gray'}
    gl.ylabel_style = {'size': 12, 'color': 'gray'}

    # 添加标题
    ax.set_title("西南印度洋区域")

    # 显示地图
    plt.show()

无标注的正常代码

def plot_southwest_indian_ocean():
    # 定义西南印度洋的经纬度范围
    lat_range = [-40, 0]
    lon_range = [-340, -260]

    # 创建带投影的画布和轴
    fig, ax = plt.subplots(figsize=(8, 6), subplot_kw={'projection': ccrs.PlateCarree()})

    # 设置地图边界
    ax.set_extent(lon_range + lat_range, crs=ccrs.PlateCarree())

    # 添加陆地和海洋要素
    ax.add_feature(cfeature.LAND, edgecolor='black')
    ax.add_feature(cfeature.OCEAN)

    # 添加国界线
    ax.add_feature(cfeature.BORDERS, linestyle='-', alpha=0.5)

    # 添加网格线
    gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True, linewidth=1, color='gray', alpha=0.5, linestyle='--')
    gl.ylabels_right = False
    gl.xformatter = LONGITUDE_FORMATTER
    gl.yformatter = LATITUDE_FORMATTER
    gl.xlabel_style = {'size': 12, 'color': 'gray'}
    gl.ylabel_style = {'size': 12, 'color': 'gray'}

    # 添加标题
    ax.set_title("西南印度洋区域")

    # 显示地图
    plt.show()

西南印度洋区域地图示例

错误原因

原代码中cfeature.NaturalEarthFeature的geometries()方法仅返回shapely几何对象,不包含国家属性(如ADMIN名称),直接访问country.properties会引发属性错误。此外,未判断国家是否在当前地图范围内,可能导致标注出现在视图外。

修正后的代码

使用cartopy.io.shapereader读取NaturalEarth的国家shapefile,获取带属性的要素,筛选出地图范围内的国家后添加标注:

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
from cartopy.io import shapereader

def plot_southwest_indian_ocean():
    # 定义西南印度洋的经纬度范围
    lat_range = [-40, 0]
    lon_range = [-340, -260]
    # 转换为0-360经度范围(适配shapefile数据)
    lon_range_360 = [lon + 360 if lon < 0 else lon for lon in lon_range]

    # 创建带投影的画布和轴
    fig, ax = plt.subplots(figsize=(8, 6), subplot_kw={'projection': ccrs.PlateCarree()})

    # 设置地图边界
    ax.set_extent(lon_range + lat_range, crs=ccrs.PlateCarree())

    # 添加陆地和海洋要素
    ax.add_feature(cfeature.LAND, edgecolor='black')
    ax.add_feature(cfeature.OCEAN)

    # 添加国界线
    ax.add_feature(cfeature.BORDERS, linestyle='-', alpha=0.5)

    # 读取NaturalEarth国家数据
    shp_filename = shapereader.natural_earth(
        category='cultural',
        name='admin_0_countries',
        scale='50m'
    )
    reader = shapereader.Reader(shp_filename)

    # 遍历所有国家,筛选出地图范围内的并添加标注
    for country in reader.records():
        # 获取国家名称和几何
        name = country.attributes['ADMIN']
        geom = country.geometry

        # 判断国家几何是否与地图范围相交
        bbox = geom.bounds
        # 处理跨0度经线的情况
        geom_lon_min = bbox[0] if bbox[0] >=0 else bbox[0]+360
        geom_lon_max = bbox[2] if bbox[2] >=0 else bbox[2]+360
        if (geom_lon_max < lon_range_360[0] or geom_lon_min > lon_range_360[1]):
            continue
        if (bbox[1] > lat_range[1] or bbox[3] < lat_range[0]):
            continue

        # 计算几何中心
        centroid = geom.centroid
        # 转换回负经度(如果需要)
        centroid_lon = centroid.x if centroid.x <=180 else centroid.x -360

        # 添加标注
        ax.annotate(
            name,
            xy=(centroid_lon, centroid.y),
            xytext=(5, 5),
            textcoords='offset points',
            ha='left',
            va='bottom',
            fontsize=8,
            color='black',
            bbox=dict(boxstyle='round,pad=0.2', fc='white', ec='gray', lw=0.5),
            transform=ccrs.PlateCarree()
        )

    # 添加网格线
    gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True, linewidth=1, color='gray', alpha=0.5, linestyle='--')
    gl.ylabels_right = False
    gl.xformatter = LONGITUDE_FORMATTER
    gl.yformatter = LATITUDE_FORMATTER
    gl.xlabel_style = {'size': 12, 'color': 'gray'}
    gl.ylabel_style = {'size': 12, 'color': 'gray'}

    # 添加标题
    ax.set_title("西南印度洋区域")

    # 显示地图
    plt.show()

代码说明

  1. 使用shapereader读取带属性的国家shapefile,能直接获取国家名称等信息
  2. 处理了经度范围转换(原代码用-340到-260,对应20到100东经),确保筛选逻辑正确
  3. 判断国家几何是否与地图范围相交,只标注视图内的国家
  4. 添加transform=ccrs.PlateCarree()确保标注坐标正确匹配地图投影

内容的提问来源于stack exchange,提问作者MateaMar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 12:52:07