Python空间分析如何快速出图?手把手教你绘制python空间分布图(附:全套代码)

GIS基础理论
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

如果你正在搜索“Python空间分析如何快速出图?手把手教你绘制python空间分布图(附:全套代码)”,大概率是已经有一份点、线、面空间数据,想用 Python 快速完成空间分析、分级渲染和地图出图,而不是只停留在表格统计或 GIS 软件手工制图。

引言:用 Python 空间分析快速出图适合哪些场景

Python 空间分析快速出图,最常见的需求是把行政区、采样点、POI、网格、道路或业务数据,做成一张可以汇报、检查或发布的空间分布图。相比在 QGIS 或 ArcGIS Pro 中逐步点击,Python 的优势在于可复现、可批量、可接入数据处理流程。

本文用一个实用流程演示:读取空间数据,统一坐标系,关联统计字段,绘制 python 空间分布图,并导出高清 PNG 图片。你可以把代码替换成自己的 Shapefile、GeoJSON、GeoPackage 或 CSV 点数据。

Python空间分析快速出图 python空间分布图流程示意
Python 空间分析快速出图的典型流程:数据读取、坐标系统一、空间分析、分级制图和图片导出。

背景:为什么 Python 空间分布图经常画不对

很多同学第一次用 Python 画 GIS 图,会遇到几个典型问题:图能出来,但位置不对;颜色分级不合理;中文乱码;导出的图片模糊;点和面叠加不上;面积、距离计算结果不可信。

这些问题通常不是绘图函数本身造成的,而是空间数据处理链条没有检查完整。Python 空间分析快速出图不是简单调用 plot(),而是至少要确认数据格式、坐标系、字段类型、空间关系和输出参数。

本文示例使用以下 Python GIS 库:

  • GeoPandas:读取和处理矢量空间数据。
  • Matplotlib:绘制地图和导出图片。
  • Mapclassify:进行自然断点、分位数等分级。
  • Contextily:可选,用于添加在线底图。

原理:Python 空间分析出图的核心逻辑

绘制 python 空间分布图,本质上是把“空间几何”和“属性字段”一起表达出来。空间几何决定对象在哪里,属性字段决定对象用什么颜色、大小或符号表示。

一个可靠的 Python 空间分析出图流程通常包括四步:

  1. 读取数据:用 GeoPandas 读取 Shapefile、GeoJSON、GPKG 或 CSV。
  2. 统一坐标系:确认所有图层在同一个 CRS,也就是坐标参考系统。
  3. 计算或关联指标:例如人口密度、点数量、面积占比、缓冲区内统计值。
  4. 制图输出:设置颜色、图例、标题、边界、字体和分辨率。

如果涉及面积和距离计算,应优先使用投影坐标系,而不是直接在经纬度坐标系下计算。经纬度单位是度,不是米,直接计算面积和距离很容易得到误导性结果。

步骤:手把手绘制 python 空间分布图

步骤 1:准备 Python 环境

建议使用 Conda 创建独立环境,避免 GDAL、Fiona、PyProj 等底层依赖冲突。

conda create -n pygis-map python=3.11 -y
conda activate pygis-map
conda install -c conda-forge geopandas matplotlib mapclassify contextily openpyxl -y

如果你使用 pip,也可以安装,但在 Windows 环境下 GIS 依赖更容易出现编译或版本问题。GIS 初学者更推荐 Conda Forge。

步骤 2:准备示例数据结构

假设你有一个行政区面文件和一个统计表:

  • data/districts.shp:行政区边界,包含字段 adcode
  • data/stat.xlsx:统计表,包含字段 adcodevalue

其中 value 可以是人口、销售额、采样数量、风险指数、土地利用比例等需要表达空间分布的指标。

步骤 3:读取空间数据并检查坐标系

import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt

districts = gpd.read_file("data/districts.shp")
stat = pd.read_excel("data/stat.xlsx")

print(districts.head())
print(districts.crs)
print(stat.head())

这里最重要的是 districts.crs。如果输出为 None,说明数据没有定义坐标系。此时不要急着出图,应先确认原始数据到底是什么坐标系,再用 set_crs() 定义。

# 示例:如果确认原始数据是 WGS84 经纬度
districts = districts.set_crs(epsg=4326, allow_override=True)

注意:set_crs() 是“定义坐标系”,不会改变坐标值;to_crs() 是“转换坐标系”,会改变坐标值。二者不能混用。

步骤 4:关联统计字段

用行政区编码 adcode 把空间边界和统计表连接起来。

districts["adcode"] = districts["adcode"].astype(str)
stat["adcode"] = stat["adcode"].astype(str)

gdf = districts.merge(stat, on="adcode", how="left")

print(gdf[["adcode", "value"]].head())
print(gdf["value"].isna().sum())

如果 value 出现大量空值,通常是编码类型不一致、前导零丢失、字段名写错或行政区版本不一致。

步骤 5:绘制分级设色空间分布图

下面这段代码可以直接绘制一张基础的 python 空间分布图。分级方式使用 quantiles,即分位数分级,适合快速观察区域差异。

import matplotlib as mpl

plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei", "Arial Unicode MS"]
plt.rcParams["axes.unicode_minus"] = False

fig, ax = plt.subplots(figsize=(10, 8), dpi=150)

gdf.plot(
    column="value",
    ax=ax,
    cmap="YlOrRd",
    scheme="quantiles",
    k=5,
    legend=True,
    edgecolor="#666666",
    linewidth=0.4,
    missing_kwds={
        "color": "#eeeeee",
        "edgecolor": "#999999",
        "hatch": "///",
        "label": "无数据"
    }
)

ax.set_title("区域指标空间分布图", fontsize=16, pad=15)
ax.set_axis_off()

plt.tight_layout()
plt.savefig("output/python_space_distribution_map.png", dpi=300, bbox_inches="tight")
plt.show()

这段代码完成了颜色分级、边界线绘制、图例显示、中文标题和高清导出。对于日常汇报图、论文初稿图和数据质检图,这已经足够使用。

步骤 6:添加点数据叠加分析

如果你还需要把采样点、监测站、门店或事件点叠加到行政区上,可以读取点图层并统一坐标系。

points = gpd.read_file("data/points.geojson")

if points.crs != gdf.crs:
    points = points.to_crs(gdf.crs)

fig, ax = plt.subplots(figsize=(10, 8), dpi=150)

gdf.plot(
    column="value",
    ax=ax,
    cmap="Blues",
    scheme="quantiles",
    k=5,
    legend=True,
    edgecolor="#555555",
    linewidth=0.4
)

points.plot(
    ax=ax,
    color="red",
    markersize=12,
    alpha=0.75
)

ax.set_title("区域指标与点位叠加空间分布图", fontsize=16, pad=15)
ax.set_axis_off()

plt.tight_layout()
plt.savefig("output/python_overlay_distribution_map.png", dpi=300, bbox_inches="tight")
plt.show()

点面叠加不上时,第一反应应该是检查坐标系,而不是调整颜色或缩放范围。

步骤 7:按行政区统计点数量并出图

很多 Python 空间分析快速出图场景不是直接画已有字段,而是先做空间统计。例如统计每个行政区内有多少个点。

# 确保点和面坐标系一致
if points.crs != districts.crs:
    points = points.to_crs(districts.crs)

joined = gpd.sjoin(points, districts[["adcode", "geometry"]], how="left", predicate="within")

count_df = joined.groupby("adcode").size().reset_index(name="point_count")
district_count = districts.merge(count_df, on="adcode", how="left")
district_count["point_count"] = district_count["point_count"].fillna(0)

fig, ax = plt.subplots(figsize=(10, 8), dpi=150)

district_count.plot(
    column="point_count",
    ax=ax,
    cmap="OrRd",
    scheme="naturalbreaks",
    k=5,
    legend=True,
    edgecolor="#666666",
    linewidth=0.4
)

points.plot(ax=ax, color="#111111", markersize=6, alpha=0.5)

ax.set_title("各行政区点数量空间分布图", fontsize=16, pad=15)
ax.set_axis_off()

plt.tight_layout()
plt.savefig("output/python_point_count_map.png", dpi=300, bbox_inches="tight")
plt.show()

这里的 gpd.sjoin() 是空间连接。它根据几何关系把点匹配到面内,常用于点落区、区域统计、网格统计和空间归属判断。

常见坑:Python 空间分析快速出图最容易出错的地方

坑 1:坐标系看起来一样,实际不是一样

有些数据都显示在中国范围,但一个是 WGS84 经纬度,一个是 CGCS2000 经纬度,一个是 Web Mercator。肉眼看不一定明显,做空间连接、面积计算、缓冲区分析时就会出问题。

  • 先打印 gdf.crs
  • 叠加前统一使用 to_crs()
  • 面积和距离计算前转换到合适的投影坐标系。

坑 2:中文字段或中文标题乱码

Matplotlib 默认字体不一定支持中文。可以设置中文字体:

plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei", "Arial Unicode MS"]
plt.rcParams["axes.unicode_minus"] = False

如果 Shapefile 属性表中文乱码,常见原因是编码问题。可以尝试:

gdf = gpd.read_file("data/districts.shp", encoding="utf-8")
# 或
gdf = gpd.read_file("data/districts.shp", encoding="gbk")

坑 3:导出的空间分布图很模糊

网页预览看起来可以,但插入报告后模糊,通常是导出分辨率不足。建议使用:

  • figsize 控制画布尺寸。
  • dpi=300 或更高用于报告和论文。
  • bbox_inches="tight" 去除多余白边。

坑 4:分级颜色误导判断

分位数分级适合比较排名,但可能让数值差异很小的区域看起来差异很大。自然断点适合突出聚类差异,但不同数据集之间不一定可比。等距分级容易理解,但对极端值敏感。

制作 python 空间分布图时,不要只追求颜色好看,还要考虑指标分布和读图目的。

坑 5:Shapefile 字段名被截断

Shapefile 字段名长度有限,长字段可能被截断。若字段频繁丢失或变形,建议改用 GeoPackage:

gdf.to_file("output/result.gpkg", layer="district_result", driver="GPKG")

方法比较:Python、QGIS 和 ArcGIS Pro 出图怎么选

方法 适合场景 优势 限制
Python GeoPandas + Matplotlib 批量出图、自动化报告、数据分析流程 可复现、易批处理、适合和统计分析结合 版式设计不如专业 GIS 制图界面直观
QGIS 交互式制图、快速检查数据、开源桌面 GIS 可视化操作方便,插件丰富 批量自动化需要额外配置 Processing 或 PyQGIS
ArcGIS Pro 正式制图、企业 GIS、复杂地图布局 制图体系完整,符号化和布局能力强 授权成本较高,自动化通常依赖 ArcPy
Python + Folium 或 Plotly 交互式网页地图、数据探索 可缩放、可点击、适合分享 HTML 不一定适合高质量静态制图和印刷输出

如果你的目标是快速分析并导出多张同类地图,Python 空间分析快速出图更合适。如果你的目标是精细排版、注记避让、地图整饰和印刷级制图,QGIS 或 ArcGIS Pro 会更顺手。

检查清单:出图前务必确认这些项

  • 空间数据是否能正常读取,几何是否为空。
  • 所有图层是否有正确 CRS。
  • 点、线、面叠加前是否已经统一坐标系。
  • 属性连接字段的数据类型是否一致。
  • 统计字段是否存在空值、异常值和极端值。
  • 分级方法是否符合业务解释逻辑。
  • 中文字体是否能正常显示。
  • 图例、标题、颜色和边界线是否清晰。
  • 导出图片是否设置了足够的 dpi。
  • 输出文件夹是否存在,路径是否写对。

FAQ:Python 空间分析快速出图常见问题

1. Python 空间分析快速出图一定要用 GeoPandas 吗?

不一定,但 GeoPandas 是入门和日常矢量空间分析最方便的选择。它把 Pandas 表格处理和 Shapely 几何处理结合起来,适合绘制 python 空间分布图、空间连接、缓冲区、叠加分析和字段统计。

2. 为什么我的点数据和面数据叠加不上?

最常见原因是坐标系不一致,或者某个图层没有正确定义 CRS。先检查 points.crsdistricts.crs,再用 to_crs() 转换到同一坐标系。

3. 经纬度数据能不能直接计算面积和距离?

不建议。经纬度单位是度,不能直接当作米使用。做面积、距离、缓冲区等空间分析前,应转换到合适的投影坐标系。例如局部城市分析可选择对应 UTM 分带或本地投影。

4. python 空间分布图用什么颜色方案比较合适?

连续数值常用 YlOrRdOrRdBlues 等顺序色带;正负差异可以用发散色带,如 RdBu。不要在同一张图里混用过多高饱和颜色,否则会影响读图。

5. 如何批量给多个字段出图?

可以把字段名放入列表,用循环生成多张图。注意每个字段的标题、图例和输出文件名要单独设置。

fields = ["population", "income", "risk_index"]

for field in fields:
    fig, ax = plt.subplots(figsize=(10, 8), dpi=150)
    gdf.plot(
        column=field,
        ax=ax,
        cmap="YlOrRd",
        scheme="quantiles",
        k=5,
        legend=True,
        edgecolor="#666666",
        linewidth=0.4
    )
    ax.set_title(f"{field} 空间分布图", fontsize=16, pad=15)
    ax.set_axis_off()
    plt.tight_layout()
    plt.savefig(f"output/{field}_map.png", dpi=300, bbox_inches="tight")
    plt.close()

6. 为什么空间连接结果为空?

优先检查三点:坐标系是否一致,点是否确实落在面范围内,空间关系参数是否正确。点落区通常使用 predicate="within",如果边界点较多,也可以根据数据特点测试 intersects

结论:快速出图的关键不是 plot,而是完整流程

Python 空间分析快速出图的核心,不是记住某一个绘图函数,而是建立稳定流程:读取数据、检查坐标系、关联或计算指标、选择合理分级、设置制图样式并导出高清图片。

只要把本文代码中的文件路径、连接字段和指标字段替换成自己的数据,就可以快速绘制 python 空间分布图。后续如果需要批量制图、自动化报告或 WebGIS 数据发布,也可以在这个流程上继续扩展。