如何使用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()
代码说明
- 使用
shapereader读取带属性的国家shapefile,能直接获取国家名称等信息 - 处理了经度范围转换(原代码用-340到-260,对应20到100东经),确保筛选逻辑正确
- 判断国家几何是否与地图范围相交,只标注视图内的国家
- 添加
transform=ccrs.PlateCarree()确保标注坐标正确匹配地图投影
内容的提问来源于stack exchange,提问作者MateaMar
相关产品推荐
相关产品推荐

