惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - Franky
酷 壳 – CoolShell
酷 壳 – CoolShell
Google Online Security Blog
Google Online Security Blog
Engineering at Meta
Engineering at Meta
U
Unit 42
Security Latest
Security Latest
G
Google Developers Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
D
Docker
T
Tailwind CSS Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
云风的 BLOG
云风的 BLOG
Hugging Face - Blog
Hugging Face - Blog
M
MIT News - Artificial intelligence
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
MongoDB | Blog
MongoDB | Blog
H
Help Net Security
Stack Overflow Blog
Stack Overflow Blog
C
Check Point Blog
S
Security Affairs
T
The Exploit Database - CXSecurity.com
S
SegmentFault 最新的问题
N
News and Events Feed by Topic
The GitHub Blog
The GitHub Blog
Apple Machine Learning Research
Apple Machine Learning Research
S
Securelist
IT之家
IT之家
P
Palo Alto Networks Blog
D
DataBreaches.Net
Help Net Security
Help Net Security
N
Netflix TechBlog - Medium
B
Blog RSS Feed
AWS News Blog
AWS News Blog
Scott Helme
Scott Helme
爱范儿
爱范儿
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
腾讯CDC
I
Intezer
J
Java Code Geeks
大猫的无限游戏
大猫的无限游戏
Microsoft Security Blog
Microsoft Security Blog
人人都是产品经理
人人都是产品经理
G
GRAHAM CLULEY
N
News | PayPal Newsroom
博客园 - 三生石上(FineUI控件)
A
Arctic Wolf
F
Fortinet All Blogs
The Register - Security
The Register - Security
Recent Commits to openclaw:main
Recent Commits to openclaw:main
博客园 - 【当耐特】

博客园 - 我才是银古

第16章:常见问题、排错与最佳实践 第15章:扩展生态、MCAD 与外部集成 第12章:实战案例:机械结构与 3D 打印零件 第14章:构建、测试、调试与贡献流程 第13章:OpenSCAD 源码架构与核心执行流程 第11章:预览、渲染、网格精度与性能优化 第09章:列表推导、递归与算法建模 第08章:参数化零件库与复用设计 第10章:导入导出、命令行与自动化 第06章:CSG 布尔建模方法 第07章:二维图形、拉伸、旋转与投影 第05章:基础几何、坐标系与变换 第04章:参数、变量、函数、模块与作用域 OpenSCAD 教程目录 第03章:OpenSCAD 语言基础 第02章:安装、环境配置与开发工作流 第01章:OpenSCAD 项目全景与学习路线 第02章:源码获取、编译与开发环境配置 第01章:OCCT项目全景与学习路线 第18章:二次开发实战与综合案例 第18章:综合实战案例 第17章:数据交换与协同 第16章:源码架构与二次开发 第15章:插件与自定义工作台开发 第14章:Python脚本宏与自动化 第13章:FEM仿真分析 第12章:CAM数控加工 第11章:SurfaceMesh与逆向工程 第10章:Draft二维绘图与BIM建筑 第09章:工程图TechDraw 第07章:参数化表达式与Spreadsheet 第08章:装配设计Assembly 第06章:Part工作台与几何内核 第05章:PartDesign实体特征建模 第04章:草图Sketcher约束建模 第02章:安装版本与工作环境配置 第03章:界面工作台与基础操作 第01章:项目全景与学习路线 第十二章:插件开发、研究功能与最佳实践 第十章:定时任务与自动化(Cron) 第七章:技能、记忆与自学习闭环 第八章:MCP 集成与上下文文件 第六章:工具系统与终端后端 第五章:模型供应商与配置体系 Hermes Agent 教程目录 第十一章:语音、视觉、浏览器与子代理协作 第四章:CLI/TUI 与会话管理 第十二章:学习路线、实战方案与最佳实践 第十一章:源码结构、开发调试与插件开发 第十章:自动化、远程访问、日志与排障 第九章:Control UI、节点、Canvas 与语音能力 第七章:工具、技能、插件与能力扩展 第八章:安全模型、访问控制与沙箱实践 第六章:Agent 工作区、会话与多智能体路由 第五章:多通道消息接入与聊天平台配置 第四章:配置体系、模型接入与认证管理 第三章:Gateway 架构、协议与运行机制 第二章:安装、环境准备与快速上手 第一章:OpenClaw 项目概览与核心定位 oh-my-openagent 教程目录 09-命令模型回退与配置参考 10-实战案例最佳实践与故障排除 05-工作模式-Ultrawork-Prometheus-Atlas 08-Hooks与MCP系统 06-Category与Skill系统 07-核心工具链 04-智能体全景详解 03-安装与环境配置 02-整体架构与多模型编排机制 01-项目简介与核心理念 01-项目概览与学习路线 02-安装部署与工具适配 03-Skill机制与using-superpowers 05-TDD系统化调试与完成前验证 04-需求澄清方案设计与计划编写 07-并行智能体子智能体与Git-Worktree 第六章:代码审查、反馈处理与分支收尾 08-中国特色Skills与本土团队落地 09-MCP构建工作流执行与自定义Skill 第23章:FreeCAD-Python-API Clipper2 C# 源码解读教程 第19章:PolyTree 多边形树结构 第20章:实际应用与最佳实践 第18章:Minkowski 和与差 第17章:RectClip 矩形裁剪优化 第16章:ClipperOffset 偏移类详解 第15章:填充规则详解 第14章:布尔运算执行流程 第13章:ClipperD 浮点裁剪类 第11章:OutRec 与 OutPt 输出结构 第9章:Active 活动边结构 第10章:Vertex 顶点与 LocalMinima 局部极小值 第12章:Clipper64 裁剪类详解 第7章:高精度运算与128位整数 第8章:ClipperBase 基类详解 第5章:枚举类型与常量定义 第6章:InternalClipper 内部工具类 第2章:核心数据结构 - Point64、PointD 第3章:路径与多边形表示 - Path64、PathD、Paths64、PathsD 第4章:矩形边界 - Rect64、RectD
第21章:静态可视化(Matplotlib)
我才是银古 · 2026-06-23 · via 博客园 - 我才是银古

第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 控制图层顺序与透明度

通过 zorderalpha 参数控制图层的叠加效果:

# 创建模拟的缓冲区图层
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 矢量 网页嵌入、可缩放
PDF .pdf 矢量 出版、打印
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 等库创建可以在浏览器中交互操作的动态地图。