第21章:静态可视化(Matplotlib)
地图可视化是地理信息系统的核心功能之一。GeoPandas 基于 Matplotlib 提供了强大而灵活的静态地图绑制能力,使我们能够通过简洁的 Python 代码创建专业级的地理数据可视化作品。本章将系统介绍 GeoPandas 的静态可视化方法,从基础绑图到高级样式定制,帮助读者掌握地理数据可视化的完整工作流程。
21.1 GeoPandas 可视化概述
21.1.1 可视化在 GIS 中的重要性
地图可视化是将抽象的地理数据转化为直观图形表达的过程。在 GIS 分析工作流中,可视化承担着多重关键角色:
- 数据探索:快速了解数据的空间分布特征
- 质量检查:发现数据中的异常值和错误
- 结果展示:将分析结果以直观方式呈现给决策者
- 报告制作:生成用于出版和报告的高质量地图
# 导入核心库
import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
# 读取示例数据
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
print(f"数据集包含 {len(world)} 个国家/地区")
print(f"几何类型: {world.geom_type.unique()}")
print(f"坐标参考系: {world.crs}")
输出:
数据集包含 177 个国家/地区
几何类型: ['MultiPolygon' 'Polygon']
坐标参考系: EPSG:4326
21.1.2 GeoPandas 可视化架构
GeoPandas 的可视化功能建立在 Matplotlib 之上,采用分层架构设计:
| 层次 | 组件 | 说明 |
|---|---|---|
| 顶层 | GeoDataFrame.plot() | GeoPandas 提供的高级绑图接口 |
| 中间层 | Matplotlib Axes | 坐标轴对象,管理图形元素 |
| 底层 | Matplotlib Figure | 画布对象,控制整体布局 |
# 可视化架构示例
fig, ax = plt.subplots(figsize=(10, 6))
# GeoPandas 的 plot() 方法返回 matplotlib 的 Axes 对象
ax = world.plot(ax=ax, color='lightblue', edgecolor='gray')
# 可以继续使用 matplotlib 的方法自定义
ax.set_title('世界地图', fontsize=16)
ax.set_xlabel('经度')
ax.set_ylabel('纬度')
plt.tight_layout()
plt.savefig('world_map.png', dpi=150)
plt.show()
注意: GeoPandas 的
plot()方法本质上是对 Matplotlib 的封装,因此所有 Matplotlib 的自定义功能都可以在 GeoPandas 图形上使用。
21.2 plot() 基础用法
21.2.1 基本绑图
GeoDataFrame.plot() 是 GeoPandas 中最常用的可视化方法,只需一行代码即可生成地图:
# 最简单的绑图方式
world.plot()
plt.title('基础世界地图')
plt.show()
plot() 方法的常用基础参数:
| 参数 | 类型 | 说明 | 默认值 |
|---|---|---|---|
| color | str | 统一填充颜色 | None |
| edgecolor | str | 边界线颜色 | None |
| linewidth | float | 边界线宽度 | None |
| alpha | float | 透明度 (0-1) | None |
| figsize | tuple | 图形大小 (宽, 高) | None |
# 自定义基础样式
world.plot(
color='#a8d8a8', # 浅绿色填充
edgecolor='#333333', # 深灰色边界
linewidth=0.5 # 细边界线
)
plt.title('自定义样式的世界地图')
plt.show()
21.2.2 控制图形大小
通过 figsize 参数控制输出图形的尺寸,单位为英寸:
# 创建不同尺寸的地图
fig, axes = plt.subplots(1, 2, figsize=(16, 5))
# 小尺寸效果
world.plot(ax=axes[0], color='skyblue', edgecolor='white', linewidth=0.3)
axes[0].set_title('标准视图', fontsize=12)
# 使用 aspect 参数控制纵横比
world.plot(ax=axes[1], color='salmon', edgecolor='white', linewidth=0.3, aspect='equal')
axes[1].set_title('等比例视图', fontsize=12)
plt.tight_layout()
plt.show()
# 也可以直接在 plot() 中指定 figsize
ax = world.plot(figsize=(12, 8), color='wheat', edgecolor='gray')
ax.set_title('大尺寸地图 (12×8 英寸)')
plt.show()
注意:
figsize的单位是英寸,最终像素大小 = figsize × dpi。例如figsize=(10, 6)在dpi=150下将生成 1500×900 像素的图片。
21.3 按属性分类着色
21.3.1 column 参数
column 参数是 GeoPandas 可视化中最强大的功能之一,它允许根据属性值对几何要素进行着色:
# 按大洲着色
world.plot(
column='continent',
figsize=(12, 6),
edgecolor='white',
linewidth=0.5,
legend=True
)
plt.title('按大洲分类着色')
plt.show()
# 按人口数量着色
world.plot(
column='pop_est',
figsize=(12, 6),
edgecolor='gray',
linewidth=0.3,
legend=True
)
plt.title('按人口数量着色')
plt.show()
21.3.2 分类数据与连续数据着色
GeoPandas 会自动根据数据类型选择不同的着色策略:
# 分类数据 - 使用离散色彩
fig, axes = plt.subplots(1, 2, figsize=(18, 6))
# 分类数据:每个类别对应一种颜色
world.plot(
ax=axes[0],
column='continent',
categorical=True,
edgecolor='white',
linewidth=0.3,
legend=True,
legend_kwds={'fontsize': 8, 'loc': 'lower left'}
)
axes[0].set_title('分类数据着色(大洲)', fontsize=13)
# 连续数据:使用渐变色彩映射
world.plot(
ax=axes[1],
column='gdp_md_est',
edgecolor='white',
linewidth=0.3,
legend=True,
legend_kwds={'label': 'GDP(百万美元)', 'shrink': 0.6}
)
axes[1].set_title('连续数据着色(GDP)', fontsize=13)
plt.tight_layout()
plt.show()
注意: 对于分类数据,设置
categorical=True可以确保 GeoPandas 使用离散色彩方案;对于连续数据,GeoPandas 会自动使用渐变色彩映射。
21.4 色彩方案(cmap)
21.4.1 常用色彩映射表
Matplotlib 提供了丰富的色彩映射表(colormap),通过 cmap 参数指定:
| 类别 | 色彩映射名称 | 适用场景 |
|---|---|---|
| 顺序型 | viridis, plasma, inferno, magma | 单变量连续数据 |
| 顺序型 | Blues, Greens, Reds, Oranges | 单色系渐变 |
| 顺序型 | YlOrRd, YlGnBu, RdPu, BuGn | 多色系渐变 |
| 发散型 | RdYlGn, RdBu, coolwarm, seismic | 有中心值的数据 |
| 定性型 | Set1, Set2, Set3, Paired, tab10 | 分类数据 |
| 循环型 | twilight, hsv | 周期性数据 |
# 对比不同色彩映射的效果
cmaps = ['viridis', 'plasma', 'YlOrRd', 'Blues', 'RdYlGn', 'coolwarm']
fig, axes = plt.subplots(2, 3, figsize=(18, 10))
axes = axes.flatten()
for ax, cmap_name in zip(axes, cmaps):
world.plot(
ax=ax,
column='pop_est',
cmap=cmap_name,
edgecolor='gray',
linewidth=0.2,
legend=True,
legend_kwds={'shrink': 0.5}
)
ax.set_title(f'cmap="{cmap_name}"', fontsize=11)
ax.set_axis_off()
plt.suptitle('不同色彩映射方案对比', fontsize=16, y=1.02)
plt.tight_layout()
plt.show()
21.4.2 自定义颜色
除了使用预定义的色彩映射,还可以自定义颜色方案:
from matplotlib.colors import LinearSegmentedColormap, ListedColormap
# 方法1:使用自定义颜色列表创建离散色彩映射
custom_colors = ['#2166ac', '#67a9cf', '#d1e5f0', '#fddbc7', '#ef8a62', '#b2182b']
custom_cmap = ListedColormap(custom_colors)
world.plot(
column='continent',
cmap=custom_cmap,
figsize=(12, 6),
edgecolor='white',
linewidth=0.3
)
plt.title('自定义离散色彩映射')
plt.show()
# 方法2:创建自定义连续色彩映射
colors_list = ['#ffffcc', '#a1dab4', '#41b6c4', '#225ea8']
custom_continuous = LinearSegmentedColormap.from_list('custom', colors_list)
world.plot(
column='gdp_md_est',
cmap=custom_continuous,
figsize=(12, 6),
edgecolor='gray',
linewidth=0.2,
legend=True
)
plt.title('自定义连续色彩映射')
plt.show()
# 方法3:反转色彩映射(在名称后加 _r)
fig, axes = plt.subplots(1, 2, figsize=(16, 5))
world.plot(ax=axes[0], column='pop_est', cmap='YlOrRd',
edgecolor='gray', linewidth=0.2, legend=True,
legend_kwds={'shrink': 0.5})
axes[0].set_title('YlOrRd(正序)')
world.plot(ax=axes[1], column='pop_est', cmap='YlOrRd_r',
edgecolor='gray', linewidth=0.2, legend=True,
legend_kwds={'shrink': 0.5})
axes[1].set_title('YlOrRd_r(反转)')
plt.tight_layout()
plt.show()
21.5 分类方案(scheme)
21.5.1 使用 mapclassify 分类
对于连续数值数据,直接使用线性色彩映射可能无法有效展示数据分布。scheme 参数允许使用 mapclassify 库进行数据分类:
# 安装 mapclassify(如尚未安装)
# pip install mapclassify
import mapclassify
# 查看人口数据的分布
print(world['pop_est'].describe())
输出:
count 1.770000e+02
mean 3.979760e+07
std 1.380552e+08
min 1.400000e+02
25% 3.228608e+06
50% 8.776364e+06
75% 2.879504e+07
max 1.379302e+09
Name: pop_est, dtype: float64
# 使用分类方案进行可视化
world.plot(
column='pop_est',
scheme='NaturalBreaks',
k=5,
cmap='YlOrRd',
figsize=(12, 6),
edgecolor='gray',
linewidth=0.3,
legend=True,
legend_kwds={'fontsize': 9, 'loc': 'lower left'}
)
plt.title('自然断点法分类(5类)')
plt.show()
21.5.2 常用分类方法
mapclassify 提供了多种常用的地图分类方法:
| 分类方法 | scheme 名称 | 说明 |
|---|---|---|
| 自然断点法 | NaturalBreaks | 基于数据的自然聚类,最小化组内方差 |
| 分位数法 | Quantiles | 每个类别包含相同数量的要素 |
| 等间距法 | EqualInterval | 将数据范围等分为 k 个区间 |
| Fisher-Jenks | FisherJenks | 类似自然断点,计算更精确 |
| 标准差法 | StdMean | 基于均值和标准差进行分类 |
| 手动分类 | UserDefined | 用户自定义断点值 |
# 对比不同分类方法的效果
schemes = ['NaturalBreaks', 'Quantiles', 'EqualInterval', 'FisherJenks']
fig, axes = plt.subplots(2, 2, figsize=(16, 10))
axes = axes.flatten()
for ax, scheme in zip(axes, schemes):
world.plot(
ax=ax,
column='pop_est',
scheme=scheme,
k=5,
cmap='YlOrRd',
edgecolor='gray',
linewidth=0.2,
legend=True,
legend_kwds={'fontsize': 7, 'loc': 'lower left'}
)
ax.set_title(f'分类方法: {scheme}', fontsize=12)
ax.set_axis_off()
plt.suptitle('不同分类方法对比', fontsize=15)
plt.tight_layout()
plt.show()
# 使用自定义断点进行分类
world.plot(
column='pop_est',
scheme='UserDefined',
classification_kwds={'bins': [1e6, 1e7, 5e7, 1e8, 5e8, 1.5e9]},
cmap='RdPu',
figsize=(12, 6),
edgecolor='gray',
linewidth=0.3,
legend=True,
legend_kwds={'fontsize': 9, 'loc': 'lower left'}
)
plt.title('自定义断点分类')
plt.show()
注意: 使用
scheme参数时需要安装mapclassify库。参数k用于指定分类数量,默认为 5。
21.6 图例控制
21.6.1 legend 参数
通过 legend=True 启用图例显示:
# 基本图例
world.plot(
column='continent',
figsize=(12, 6),
edgecolor='white',
linewidth=0.3,
legend=True
)
plt.title('默认图例位置')
plt.show()
# 连续数据的色带图例
world.plot(
column='gdp_md_est',
figsize=(12, 6),
cmap='viridis',
edgecolor='gray',
linewidth=0.2,
legend=True,
legend_kwds={
'label': 'GDP(百万美元)',
'orientation': 'horizontal',
'shrink': 0.5,
'pad': 0.05
}
)
plt.title('水平色带图例')
plt.show()
21.6.2 legend_kwds 自定义图例样式
legend_kwds 参数接受一个字典,用于精细控制图例外观:
# 分类数据的图例自定义
world.plot(
column='continent',
figsize=(14, 7),
cmap='Set2',
edgecolor='white',
linewidth=0.3,
legend=True,
legend_kwds={
'loc': 'lower left',
'fontsize': 9,
'frameon': True,
'framealpha': 0.8,
'edgecolor': 'gray',
'title': '大洲',
'title_fontsize': 11
}
)
plt.title('自定义图例样式')
plt.show()
# 连续数据色带图例的高级定制
fig, ax = plt.subplots(figsize=(12, 6))
world.plot(
ax=ax,
column='pop_est',
cmap='YlOrRd',
scheme='Quantiles',
k=5,
edgecolor='gray',
linewidth=0.2,
legend=True,
legend_kwds={
'loc': 'lower left',
'fontsize': 8,
'title': '人口数量',
'title_fontsize': 10,
'frameon': True,
'fancybox': True,
'shadow': True
}
)
ax.set_title('带分类方案的自定义图例', fontsize=14)
ax.set_axis_off()
plt.tight_layout()
plt.show()
注意: 对于连续数据(无
scheme),图例为色带(colorbar),legend_kwds传递给fig.colorbar();对于分类数据或使用scheme时,图例为离散图例,legend_kwds传递给ax.legend()。
21.7 多图层叠加显示
21.7.1 使用 ax 参数叠加图层
在 GIS 中,地图通常由多个图层叠加组成。GeoPandas 通过共享 ax 参数实现图层叠加:
from shapely.geometry import Point
# 准备多个图层
countries = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
cities = gpd.read_file(gpd.datasets.get_path('naturalearth_cities'))
# 叠加绘制
fig, ax = plt.subplots(figsize=(12, 6))
# 第一层:国家面
countries.plot(ax=ax, color='lightyellow', edgecolor='gray', linewidth=0.5)
# 第二层:城市点
cities.plot(ax=ax, color='red', markersize=10, zorder=5)
ax.set_title('国家与城市叠加显示', fontsize=14)
ax.set_xlabel('经度')
ax.set_ylabel('纬度')
plt.tight_layout()
plt.show()
21.7.2 控制图层顺序与透明度
通过 zorder 和 alpha 参数控制图层的叠加效果:
# 创建模拟的缓冲区图层
cities_buffered = cities.copy()
cities_buffered['geometry'] = cities.geometry.buffer(5)
fig, ax = plt.subplots(figsize=(12, 6))
# 底层:国家
countries.plot(ax=ax, color='#f0f0f0', edgecolor='#cccccc', linewidth=0.5, zorder=1)
# 中间层:城市缓冲区(半透明)
cities_buffered.plot(ax=ax, color='lightskyblue', alpha=0.4, edgecolor='steelblue',
linewidth=0.5, zorder=2)
# 顶层:城市点
cities.plot(ax=ax, color='darkred', markersize=15, zorder=3, label='城市')
ax.set_title('多图层叠加与透明度控制', fontsize=14)
ax.legend(loc='lower left')
plt.tight_layout()
plt.show()
# 使用不同颜色区分大洲,叠加河流或边界
fig, ax = plt.subplots(figsize=(14, 7))
# 按大洲着色
countries.plot(
ax=ax,
column='continent',
cmap='Pastel2',
edgecolor='white',
linewidth=0.5,
legend=True,
legend_kwds={'loc': 'lower left', 'fontsize': 8}
)
# 叠加城市
cities.plot(
ax=ax,
color='black',
markersize=8,
zorder=5
)
ax.set_title('大洲分类着色与城市叠加', fontsize=14)
ax.set_axis_off()
plt.tight_layout()
plt.show()
21.8 自定义样式
21.8.1 edgecolor 与 linewidth
边界线的颜色和宽度是地图样式的重要元素:
# 不同边界线样式对比
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
# 无边界线
world.plot(ax=axes[0], color='lightblue', edgecolor='none')
axes[0].set_title('无边界线 (edgecolor="none")')
# 细边界线
world.plot(ax=axes[1], color='lightblue', edgecolor='gray', linewidth=0.5)
axes[1].set_title('细边界线 (linewidth=0.5)')
# 粗边界线
world.plot(ax=axes[2], color='lightblue', edgecolor='black', linewidth=2)
axes[2].set_title('粗边界线 (linewidth=2)')
for ax in axes:
ax.set_axis_off()
plt.tight_layout()
plt.show()
21.8.2 alpha 透明度
alpha 参数控制要素的透明度,范围从 0(完全透明)到 1(完全不透明):
# 透明度对比
fig, axes = plt.subplots(1, 4, figsize=(20, 4))
alphas = [0.2, 0.4, 0.7, 1.0]
for ax, a in zip(axes, alphas):
world.plot(ax=ax, color='steelblue', edgecolor='navy',
linewidth=0.5, alpha=a)
ax.set_title(f'alpha={a}')
ax.set_axis_off()
plt.suptitle('不同透明度效果对比', fontsize=14)
plt.tight_layout()
plt.show()
21.8.3 markersize 点大小
对于点类型的几何数据,markersize 参数控制点的大小:
cities = gpd.read_file(gpd.datasets.get_path('naturalearth_cities'))
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
# 固定大小
cities.plot(ax=axes[0], markersize=5, color='red')
axes[0].set_title('固定大小 (markersize=5)')
# 较大的点
cities.plot(ax=axes[1], markersize=30, color='blue', alpha=0.6)
axes[1].set_title('较大的点 (markersize=30)')
# 根据属性设置动态大小
cities['marker_col'] = np.random.randint(10, 100, len(cities))
cities.plot(ax=axes[2], markersize=cities['marker_col'], color='green', alpha=0.5)
axes[2].set_title('动态大小(基于属性)')
for ax in axes:
world.plot(ax=ax, color='lightyellow', edgecolor='gray', linewidth=0.3, zorder=0)
plt.tight_layout()
plt.show()
# 使用 marker 参数改变点的形状
fig, ax = plt.subplots(figsize=(12, 6))
world.plot(ax=ax, color='#f5f5f5', edgecolor='gray', linewidth=0.3)
cities.plot(ax=ax, marker='^', markersize=20, color='crimson',
edgecolor='black', linewidth=0.5)
ax.set_title('使用三角形标记的城市', fontsize=13)
ax.set_axis_off()
plt.show()
21.9 标注与注记
21.9.1 添加文字标注
在地图上添加文字标注可以增强信息表达:
fig, ax = plt.subplots(figsize=(14, 8))
# 绘制底图
world.plot(ax=ax, color='lightyellow', edgecolor='gray', linewidth=0.5)
# 为主要城市添加标注
cities = gpd.read_file(gpd.datasets.get_path('naturalearth_cities'))
cities.plot(ax=ax, color='red', markersize=15, zorder=5)
# 使用 annotate 添加标注
for idx, row in cities.head(10).iterrows():
ax.annotate(
text=row['name'],
xy=(row.geometry.x, row.geometry.y),
xytext=(5, 5),
textcoords='offset points',
fontsize=7,
color='darkblue',
fontweight='bold'
)
ax.set_title('城市标注示例', fontsize=14)
ax.set_axis_off()
plt.tight_layout()
plt.show()
# 使用 ax.text 方法添加自由文字
fig, ax = plt.subplots(figsize=(12, 6))
world.plot(ax=ax, column='continent', cmap='Set3', edgecolor='white', linewidth=0.3)
# 在指定坐标添加文字
ax.text(0, 0, '赤道', fontsize=9, ha='center', va='bottom',
color='red', fontstyle='italic')
# 添加水平参考线表示赤道
ax.axhline(y=0, color='red', linewidth=0.5, linestyle='--', alpha=0.5)
ax.set_title('添加参考线和文字标注', fontsize=14)
ax.set_axis_off()
plt.show()
21.9.2 添加标题和坐标轴标签
fig, ax = plt.subplots(figsize=(12, 7))
world.plot(ax=ax, column='pop_est', cmap='YlOrRd',
edgecolor='gray', linewidth=0.3,
legend=True, legend_kwds={'shrink': 0.6, 'label': '人口数量'})
# 设置标题(支持多级标题)
ax.set_title('世界人口分布图', fontsize=16, fontweight='bold', pad=15)
# 设置坐标轴标签
ax.set_xlabel('经度 (°)', fontsize=12)
ax.set_ylabel('纬度 (°)', fontsize=12)
# 添加数据来源注释
ax.text(0.99, 0.01, '数据来源: Natural Earth',
transform=ax.transAxes,
fontsize=8, ha='right', va='bottom',
fontstyle='italic', color='gray')
plt.tight_layout()
plt.show()
# 设置坐标轴范围(聚焦特定区域)
fig, ax = plt.subplots(figsize=(10, 8))
world.plot(ax=ax, color='lightyellow', edgecolor='gray', linewidth=0.5)
cities.plot(ax=ax, color='red', markersize=10)
# 聚焦东亚地区
ax.set_xlim([70, 150])
ax.set_ylim([10, 55])
ax.set_title('东亚地区', fontsize=14)
ax.set_xlabel('经度')
ax.set_ylabel('纬度')
ax.grid(True, linestyle='--', alpha=0.3)
plt.tight_layout()
plt.show()
21.10 底图叠加(contextily)
21.10.1 安装与基础用法
contextily 库可以为 GeoPandas 地图添加在线瓦片底图:
# 安装 contextily
# pip install contextily
import contextily as ctx
# 注意:contextily 需要 Web Mercator 投影 (EPSG:3857)
world_mercator = world.to_crs(epsg=3857)
fig, ax = plt.subplots(figsize=(12, 8))
# 绘制面数据(半透明)
world_mercator.plot(ax=ax, alpha=0.4, edgecolor='black', linewidth=0.5)
# 添加底图
ctx.add_basemap(ax, source=ctx.providers.OpenStreetMap.Mapnik)
ax.set_title('叠加 OpenStreetMap 底图', fontsize=14)
ax.set_axis_off()
plt.tight_layout()
plt.show()
注意: 使用 contextily 之前必须将数据投影转换为 EPSG:3857(Web Mercator),否则底图将无法正确对齐。
21.10.2 底图提供商选择
contextily 支持多种底图提供商:
| 提供商 | 代码 | 特点 |
|---|---|---|
| OpenStreetMap | ctx.providers.OpenStreetMap.Mapnik | 通用地图,信息丰富 |
| Stamen Toner | ctx.providers.Stamen.Toner | 黑白色调,适合叠加数据 |
| Stamen Terrain | ctx.providers.Stamen.Terrain | 地形底图 |
| CartoDB Positron | ctx.providers.CartoDB.Positron | 浅色底图,适合数据展示 |
| CartoDB DarkMatter | ctx.providers.CartoDB.DarkMatter | 深色底图,视觉冲击强 |
| Esri WorldImagery | ctx.providers.Esri.WorldImagery | 卫星影像 |
# 使用不同底图提供商
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
providers = [
(ctx.providers.CartoDB.Positron, 'CartoDB Positron'),
(ctx.providers.CartoDB.DarkMatter, 'CartoDB DarkMatter')
]
for ax, (provider, name) in zip(axes, providers):
world_mercator.plot(ax=ax, alpha=0.3, edgecolor='red', linewidth=0.5)
ctx.add_basemap(ax, source=provider)
ax.set_title(name, fontsize=12)
ax.set_axis_off()
plt.tight_layout()
plt.show()
# 控制底图的缩放级别
fig, ax = plt.subplots(figsize=(10, 8))
# 筛选中国区域
china = world_mercator[world_mercator['name'] == 'China']
china.plot(ax=ax, alpha=0.3, edgecolor='red', linewidth=1)
# zoom 参数控制底图详细程度
ctx.add_basemap(ax, source=ctx.providers.OpenStreetMap.Mapnik, zoom=4)
ax.set_title('中国区域底图叠加', fontsize=14)
ax.set_axis_off()
plt.tight_layout()
plt.show()
21.11 子图与组合图
21.11.1 plt.subplots 创建子图
使用 Matplotlib 的子图功能创建多面板地图:
# 创建 2x2 子图布局
fig, axes = plt.subplots(2, 2, figsize=(16, 12))
# 亚洲
asia = world[world['continent'] == 'Asia']
asia.plot(ax=axes[0, 0], color='#ff9999', edgecolor='white', linewidth=0.5)
axes[0, 0].set_title('亚洲', fontsize=13)
# 欧洲
europe = world[world['continent'] == 'Europe']
europe.plot(ax=axes[0, 1], color='#99ccff', edgecolor='white', linewidth=0.5)
axes[0, 1].set_title('欧洲', fontsize=13)
# 非洲
africa = world[world['continent'] == 'Africa']
africa.plot(ax=axes[1, 0], color='#99ff99', edgecolor='white', linewidth=0.5)
axes[1, 0].set_title('非洲', fontsize=13)
# 南美洲
south_america = world[world['continent'] == 'South America']
south_america.plot(ax=axes[1, 1], color='#ffcc99', edgecolor='white', linewidth=0.5)
axes[1, 1].set_title('南美洲', fontsize=13)
for ax in axes.flatten():
ax.set_axis_off()
plt.suptitle('各大洲地图', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
21.11.2 不同属性对比展示
# 对比不同属性的空间分布
fig, axes = plt.subplots(1, 3, figsize=(20, 6))
# 人口分布
world.plot(
ax=axes[0], column='pop_est', cmap='YlOrRd',
scheme='Quantiles', k=5,
edgecolor='gray', linewidth=0.2,
legend=True, legend_kwds={'fontsize': 7, 'loc': 'lower left'}
)
axes[0].set_title('人口分布', fontsize=13)
# GDP 分布
world.plot(
ax=axes[1], column='gdp_md_est', cmap='Blues',
scheme='Quantiles', k=5,
edgecolor='gray', linewidth=0.2,
legend=True, legend_kwds={'fontsize': 7, 'loc': 'lower left'}
)
axes[1].set_title('GDP 分布', fontsize=13)
# 人均 GDP
world_copy = world.copy()
world_copy['gdp_per_cap'] = world_copy['gdp_md_est'] / (world_copy['pop_est'] / 1e6)
world_copy.plot(
ax=axes[2], column='gdp_per_cap', cmap='Greens',
scheme='Quantiles', k=5,
edgecolor='gray', linewidth=0.2,
legend=True, legend_kwds={'fontsize': 7, 'loc': 'lower left'}
)
axes[2].set_title('人均 GDP', fontsize=13)
for ax in axes:
ax.set_axis_off()
plt.suptitle('经济与人口指标空间对比', fontsize=15, fontweight='bold')
plt.tight_layout()
plt.show()
# 使用 gridspec 实现不规则子图布局
import matplotlib.gridspec as gridspec
fig = plt.figure(figsize=(16, 10))
gs = gridspec.GridSpec(2, 3, figure=fig)
# 主图占据左侧 2/3
ax_main = fig.add_subplot(gs[:, :2])
world.plot(ax=ax_main, column='continent', cmap='Set2',
edgecolor='white', linewidth=0.3)
ax_main.set_title('世界地图', fontsize=14)
ax_main.set_axis_off()
# 右上小图
ax_top = fig.add_subplot(gs[0, 2])
europe.plot(ax=ax_top, color='#99ccff', edgecolor='white', linewidth=0.5)
ax_top.set_title('欧洲', fontsize=11)
ax_top.set_axis_off()
# 右下小图
ax_bottom = fig.add_subplot(gs[1, 2])
asia.plot(ax=ax_bottom, color='#ff9999', edgecolor='white', linewidth=0.5)
ax_bottom.set_title('亚洲', fontsize=11)
ax_bottom.set_axis_off()
plt.tight_layout()
plt.show()
21.12 导出地图
21.12.1 保存为图片
GeoPandas 生成的地图可以通过 Matplotlib 导出为多种格式:
fig, ax = plt.subplots(figsize=(12, 6))
world.plot(
ax=ax,
column='continent',
cmap='Set2',
edgecolor='white',
linewidth=0.3,
legend=True,
legend_kwds={'loc': 'lower left', 'fontsize': 8}
)
ax.set_title('世界地图 - 按大洲着色', fontsize=14)
ax.set_axis_off()
# 保存为 PNG 格式
fig.savefig('world_map.png', dpi=300, bbox_inches='tight',
facecolor='white', edgecolor='none')
# 保存为 SVG 矢量格式(适合出版)
fig.savefig('world_map.svg', bbox_inches='tight',
facecolor='white', edgecolor='none')
# 保存为 PDF 格式(适合打印)
fig.savefig('world_map.pdf', bbox_inches='tight',
facecolor='white', edgecolor='none')
plt.show()
print("地图已导出为 PNG、SVG 和 PDF 格式")
输出:
地图已导出为 PNG、SVG 和 PDF 格式
常用导出格式对比:
| 格式 | 扩展名 | 类型 | 适用场景 |
|---|---|---|---|
| PNG | .png | 位图 | 网页展示、演示文稿 |
| JPEG | .jpg | 位图 | 照片类地图、文件较小 |
| SVG | .svg | 矢量 | 网页嵌入、可缩放 |
| 矢量 | 出版、打印 | ||
| TIFF | .tiff | 位图 | 高质量印刷 |
21.12.2 控制分辨率
dpi(dots per inch)参数决定了导出图片的分辨率:
# 不同分辨率对比
fig, ax = plt.subplots(figsize=(10, 5))
world.plot(ax=ax, color='lightblue', edgecolor='gray', linewidth=0.5)
ax.set_title('分辨率测试')
ax.set_axis_off()
# 低分辨率(适合快速预览)
fig.savefig('map_72dpi.png', dpi=72, bbox_inches='tight')
# 标准分辨率(屏幕显示)
fig.savefig('map_150dpi.png', dpi=150, bbox_inches='tight')
# 高分辨率(出版印刷)
fig.savefig('map_300dpi.png', dpi=300, bbox_inches='tight')
plt.show()
| DPI | 用途 | 10×5英寸图片大小(约) |
|---|---|---|
| 72 | 快速预览 | 720×360 像素 |
| 150 | 屏幕展示 | 1500×750 像素 |
| 300 | 出版印刷 | 3000×1500 像素 |
| 600 | 高质量印刷 | 6000×3000 像素 |
# 导出透明背景的地图
fig, ax = plt.subplots(figsize=(10, 5))
world.plot(ax=ax, color='steelblue', edgecolor='white', linewidth=0.5)
ax.set_axis_off()
# transparent=True 导出透明背景
fig.savefig('map_transparent.png', dpi=300,
bbox_inches='tight', transparent=True)
print("已导出透明背景地图")
plt.show()
输出:
已导出透明背景地图
注意: 高分辨率图片文件较大,建议根据实际用途选择合适的 DPI 值。出版物通常要求 300 DPI 以上,网页展示 150 DPI 即可。
21.13 本章小结
本章系统介绍了 GeoPandas 基于 Matplotlib 的静态可视化方法。以下是各节核心内容的回顾:
| 主题 | 方法/参数 | 关键要点 |
|---|---|---|
| 基础绘图 | gdf.plot() | 一行代码即可生成地图,支持 figsize 控制大小 |
| 属性着色 | column 参数 | 支持分类数据和连续数据的自动着色 |
| 色彩方案 | cmap 参数 | 丰富的预定义色彩映射,支持自定义和反转 |
| 分类方案 | scheme 参数 | 配合 mapclassify 实现多种数据分类方法 |
| 图例控制 | legend, legend_kwds | 灵活定制图例位置、样式和标签 |
| 图层叠加 | ax 参数 | 通过共享 Axes 对象实现多图层叠加 |
| 样式定制 | edgecolor, alpha, markersize | 精细控制边界线、透明度和点大小 |
| 标注注记 | annotate, text | 为地图添加文字标注和标题信息 |
| 底图叠加 | contextily | 添加在线瓦片底图,需转换为 EPSG:3857 |
| 子图组合 | plt.subplots | 创建多面板对比地图 |
| 导出地图 | savefig | 支持 PNG/SVG/PDF 多种格式,dpi 控制分辨率 |
核心要诀:
- 选择合适的色彩方案:顺序型用于连续数据,发散型用于有中心值的数据,定性型用于分类数据
- 使用分类方案:通过
scheme参数配合 mapclassify 可以更有效地展示数据分布差异 - 图层叠加是关键:通过共享
ax对象,可以将多个数据集叠加在同一张地图上 - 注意坐标参考系:使用 contextily 底图时,务必先将数据转换为 EPSG:3857 投影
- 导出时关注分辨率:根据用途选择合适的 DPI 和文件格式
在下一章(第22章)中,我们将学习交互式可视化方法,使用 Folium 和 Plotly 等库创建可以在浏览器中交互操作的动态地图。