如何使用Python cartopy在地图上给国家标注?

10

使用Python3和Cartopy,有以下代码:

import matplotlib.pyplot as plt
import cartopy
import cartopy.io.shapereader as shpreader
import cartopy.crs as ccrs

ax = plt.axes(projection=ccrs.PlateCarree())
ax.add_feature(cartopy.feature.LAND)
ax.add_feature(cartopy.feature.OCEAN)
ax.add_feature(cartopy.feature.COASTLINE)
ax.add_feature(cartopy.feature.BORDERS, linestyle='-', alpha=.5)
ax.add_feature(cartopy.feature.LAKES, alpha=0.95)
ax.add_feature(cartopy.feature.RIVERS)

ax.set_extent([-150, 60, -25, 60])

shpfilename = shpreader.natural_earth(resolution='110m',
                                      category='cultural',
                                      name='admin_0_countries')

reader = shpreader.Reader(shpfilename)
countries = reader.records()

for country in countries:
    if country.attributes['SOVEREIGNT'] == "Bulgaria":
        ax.add_geometries(country.geometry, ccrs.PlateCarree(), facecolor=(0, 1, 0), label = "A")
    else:
        ax.add_geometries(country.geometry, ccrs.PlateCarree(), facecolor=(1, 1, 1), label = country.attributes['SOVEREIGNT'])
plt.rcParams["figure.figsize"] = (50,50)
plt.show()

我遇到了这个问题:

enter image description here

问题: 为了在保加利亚(或其他你在 country.attributes['SOVEREIGNT'] 中提到的国家)上方显示一个红色的"A",我应该写什么?目前标签根本没有显示出来,我不确定如何更改标签的字体。因此,似乎以下代码只改变了颜色,而没有添加标签:

ax.add_geometries(country.geometry, ccrs.PlateCarree(), facecolor=(0, 1, 0), label = "A")
1个回答

12

您可以获取几何图形的重心并在该位置绘制文本:

import matplotlib.patheffects as PathEffects

for country in countries:

    if country.attributes['SOVEREIGNT'] == "Bulgaria":
        g = ax.add_geometries(country.geometry, ccrs.PlateCarree(), facecolor=(0, 1, 0), label="A")

        x = country.geometry.centroid.x        
        y = country.geometry.centroid.y

        ax.text(x, y, 'A', color='red', size=15, ha='center', va='center', transform=ccrs.PlateCarree(), 
                path_effects=[PathEffects.withStroke(linewidth=5, foreground="k", alpha=.8)])

    else:
        ax.add_geometries(country.geometry, ccrs.PlateCarree(), facecolor=(1, 1, 1), label = country.attributes['SOVEREIGNT'])

针对"保加利亚"所关注的地区,它看起来像是这样:

输入图片描述

编辑:

如果要将"依赖项"分开,请考虑使用admin_0_map_units而非admin_0_map_countries,请参阅Natural Earth文档

为了突出小国家/地区,您可以使用类似以下内容的几何缓冲区:

highlight = ['Singapore', 'Liechtenstein']

for country in countries:

    if country.attributes['NAME'] in highlight:

        if country.geometry.area < 2:
            geom = [country.geometry.buffer(2)]
        else:
            geom = [country.geometry]

        g = ax.add_geometries(geom, ccrs.PlateCarree(), facecolor=(0, 0.5, 0, 0.6), label="A", zorder=99)

        x = country.geometry.centroid.x        
        y = country.geometry.centroid.y

        ax.text(x, y+5, country.attributes['NAME'], color='red', size=14, ha='center', va='center', transform=ccrs.PlateCarree(), 
                path_effects=[PathEffects.withStroke(linewidth=3, foreground="k", alpha=.8)])

    else:
        ax.add_geometries(country.geometry, ccrs.PlateCarree(), facecolor=(1, 1, 1), label=country.attributes['NAME'])

您可以使用以下代码将特定国家进行分割,它使用Shapely在几何图形的中心执行交集。最终将绘图和空间分析(拆分等)分为更明确的步骤可能会更好。像这样混合可能会使代码更难被用于其他情况。

输入图像描述在此

from shapely.geometry import LineString, MultiLineString

for country in countries:

    if country.attributes['NAME'] in 'China':

        # line at the centroid y-coord of the country
        l = LineString([(-180, country.geometry.centroid.y), 
                        (180, country.geometry.centroid.y)])

        north_poly = MultiLineString([l, north_line]).convex_hull
        south_poly = MultiLineString([l, south_line]).convex_hull

        g = ax.add_geometries([country.geometry.intersection(north_poly)], ccrs.PlateCarree(), facecolor=(0.8, 0.0, 0.0, 0.4), zorder=99)
        g = ax.add_geometries([country.geometry.intersection(south_poly)], ccrs.PlateCarree(), facecolor=(0.0, 0.0, 0.8, 0.4), zorder=99)

        x = country.geometry.centroid.x        
        y = country.geometry.centroid.y

        ax.text(x, y, country.attributes['NAME'], color='k', size=16, ha='center', va='center', transform=ccrs.PlateCarree(), 
                path_effects=[PathEffects.withStroke(linewidth=5, foreground="w", alpha=1)], zorder=100)

    else:
        ax.add_geometries(country.geometry, ccrs.PlateCarree(), facecolor=(1, 1, 1), label=country.attributes['NAME'])

在这里输入图片描述


绝对是一个好答案,感谢您的努力!另外一个完全无关的问题 - 如果我想在地图上将新加坡或列支敦士登标记为绿色,我该怎么做? - Vityata
1
我对答案进行了编辑。如果面积小于2平方度(不是最好的制图单位),则图像显示具有2度缓冲区的多边形。我添加了标签偏移量以避免在国家本身上进行重叠绘制。 - Rutger Kassies
我通过检查 country.attributes['NAME_EN'] == "France" 成功过滤掉了依赖领土。不过,关于“新加坡”和“列支敦士登”,我想我应该尝试手动提供坐标? - Vityata
1
这些国家在大地图上可能非常小,但我认为你仍然可以使用相同的概念来完成它。 - Rutger Kassies
1
我在文档中注意到了这个备注:“如果您想看到依赖的海外地区被分解(例如像ISO代码一样,请参见法国),请改用地图单位。” 我认为在这种情况下使用它可能更合适。 它在以下“关于”部分中:https://www.naturalearthdata.com/downloads/10m-cultural-vectors/10m-admin-0-countries/ - Rutger Kassies
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接