GeoPandas怎么读?GIS空间分析实战(附:源码)

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

GeoPandas怎么读?GIS空间分析实战(附:源码)这篇文章面向刚开始学习 Python GIS 的同学和 GIS 工程入门用户,目标不是泛泛介绍库名,而是用一个可复现的小案例说明:GeoPandas 读什么数据、怎么读、读完以后如何做一次基础空间分析,并把结果导出给 QGIS 或 ArcGIS Pro 检查。

引言:GeoPandas怎么读,先从一个真实 GIS 任务开始

很多人搜索“GeoPandas怎么读”,其实有两层意思:一是 GeoPandas 这个词怎么理解,二是 GeoPandas 怎么读取空间数据。GeoPandas 可以理解为“带几何字段的 Pandas”,它把表格数据分析能力和空间几何处理能力结合起来,适合处理 Shapefile、GeoJSON、GeoPackage 等常见 GIS 数据。

本文用一个简单的空间分析任务贯穿:读取行政区面数据和点位数据,统一坐标系,判断每个点落在哪个行政区内,统计每个行政区的点数量,最后导出结果文件。

GeoPandas怎么读 GeoPandas读取空间数据空间分析流程
GeoPandas 读取空间数据并完成空间连接统计的基本流程。

背景:为什么 GIS 用户需要学 GeoPandas

在传统 GIS 软件中,叠加分析、空间连接、字段统计通常通过工具箱完成。GeoPandas 的优势是把这些步骤写成代码,便于批处理、复用和自动化。

常见使用场景包括:

  • 批量读取多个 Shapefile 或 GeoJSON 文件。
  • 检查和统一坐标系,避免面积、距离、叠加分析出错。
  • 对点、线、面数据做空间连接、缓冲区、裁剪和叠加。
  • 把分析结果导出为 GeoPackage、Shapefile 或 GeoJSON。
  • 结合 Pandas 做字段清洗、分类统计和报表输出。

如果你已经会一点 Pandas,再学习 GeoPandas 会很自然;如果你来自 QGIS 或 ArcGIS Pro,也可以把 GeoPandas 理解为“用 Python 写出来的 GIS 处理工具链”。

原理:GeoPandas读取空间数据后到底得到什么

GeoPandas 读取空间数据后,核心对象是 GeoDataFrame。它和 Pandas 的 DataFrame 很像,但多了一个特殊字段:geometry。这个字段保存点、线、面等空间几何对象。

一个 GeoDataFrame 通常包含三类信息:

  • 属性字段:例如名称、类型、人口、编号等。
  • 几何字段:例如 Point、LineString、Polygon、MultiPolygon。
  • 坐标参考系统:也就是 CRS,用来说明坐标值属于哪个坐标系。

GeoPandas 的很多空间分析都依赖两个基础条件:

  • 数据必须有有效的 geometry 字段。
  • 参与空间运算的数据最好处于同一个 CRS。

如果两个图层一个是 WGS84 经纬度坐标,一个是投影坐标,直接做空间连接或距离计算,很容易得到错误结果。因此,学习 GeoPandas 不能只学 read_file(),还要学会检查 crs、转换坐标系和验证结果。

步骤:GeoPandas读取空间数据并完成空间分析

1. 安装 GeoPandas 环境

建议优先使用 Conda 或 Mamba 安装,因为 GeoPandas 依赖 GDAL、Fiona、pyproj、Shapely 等空间库,直接用 pip 在部分系统上可能遇到二进制依赖问题。

conda create -n py311_gis python=3.11
conda activate py311_gis
conda install -c conda-forge geopandas pyogrio rtree matplotlib

如果你使用 Jupyter Notebook,可以继续安装:

conda install -c conda-forge jupyterlab

2. 准备示例数据

本文假设你有两个文件:

  • data/districts.gpkg:行政区面数据,图层名为 districts
  • data/poi.geojson:兴趣点数据,例如学校、医院、门店或采样点。

如果你的数据是 Shapefile,也可以把路径改成 data/districts.shp。但在实际项目中,更推荐 GeoPackage,因为它对中文字段名、长字段名和多图层支持更友好。

3. 读取面数据和点数据

import geopandas as gpd

districts = gpd.read_file("data/districts.gpkg", layer="districts")
poi = gpd.read_file("data/poi.geojson")

print(districts.head())
print(poi.head())

print(districts.crs)
print(poi.crs)

gpd.read_file() 是 GeoPandas 读取空间数据最常用的方法。它可以读取 Shapefile、GeoJSON、GeoPackage、FileGDB 等多种格式,具体支持情况取决于底层 GDAL 驱动。

4. 检查 geometry 和 CRS

读取数据后,不要马上分析,先检查几何类型、坐标系和空几何。

print(districts.geom_type.value_counts())
print(poi.geom_type.value_counts())

print("districts CRS:", districts.crs)
print("poi CRS:", poi.crs)

print("districts empty geometry:", districts.geometry.is_empty.sum())
print("poi empty geometry:", poi.geometry.is_empty.sum())

print("districts null geometry:", districts.geometry.isna().sum())
print("poi null geometry:", poi.geometry.isna().sum())

如果点图层中存在空几何,空间连接时这些记录不会得到正确匹配。可以先过滤:

districts = districts[~districts.geometry.is_empty & districts.geometry.notna()].copy()
poi = poi[~poi.geometry.is_empty & poi.geometry.notna()].copy()

5. 统一坐标系

如果两个图层 CRS 不一致,应把其中一个转换到另一个坐标系。对于空间连接,统一 CRS 是必要步骤。

if districts.crs != poi.crs:
    poi = poi.to_crs(districts.crs)

print(districts.crs)
print(poi.crs)

注意:to_crs() 是坐标转换,不是简单修改标签。如果你的数据本来没有 CRS,但你知道它实际是 WGS84,可以使用 set_crs() 先定义坐标系。

poi = poi.set_crs("EPSG:4326", allow_override=True)

只有在你确认原始坐标就是 EPSG:4326 时才应该这样做。不要用 set_crs() 代替投影转换。

6. 执行空间连接:判断点落在哪个行政区

空间连接可以理解为 GIS 软件中的“按位置连接属性”。下面代码把每个 POI 点匹配到包含它的行政区面。

joined = gpd.sjoin(
    poi,
    districts[["district_id", "district_name", "geometry"]],
    how="left",
    predicate="within"
)

print(joined.head())

这里的参数含义如下:

  • poi:左表,表示要被匹配的点。
  • districts:右表,表示行政区面。
  • how="left":保留所有点,即使没有落入任何行政区。
  • predicate="within":判断点是否位于面内部。

如果你希望边界上的点也能被匹配,可以根据数据情况尝试 predicate="intersects"。但在行政区统计中,边界点可能同时与多个面相交,需要额外处理。

7. 统计每个行政区的点数量

空间连接完成后,可以用 Pandas 的分组统计能力计算每个行政区内的点数量。

count_table = (
    joined
    .groupby(["district_id", "district_name"])
    .size()
    .reset_index(name="poi_count")
)

print(count_table.head())

把统计结果回连到行政区面数据:

result = districts.merge(
    count_table,
    on=["district_id", "district_name"],
    how="left"
)

result["poi_count"] = result["poi_count"].fillna(0).astype(int)

print(result[["district_id", "district_name", "poi_count"]].head())

8. 导出结果给 QGIS 或 ArcGIS Pro 检查

推荐导出为 GeoPackage,便于后续在 QGIS、ArcGIS Pro 或其他 GIS 软件中查看。

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

joined.to_file(
    "output/poi_with_district.gpkg",
    layer="poi_with_district",
    driver="GPKG"
)

导出后可以在 QGIS 中打开 district_poi_count.gpkg,用 poi_count 字段做分级设色,检查空间统计结果是否符合直觉。

常见坑:GeoPandas读取和分析最容易错在哪里

1. 文件路径有中文或空格导致读取失败

现代 GeoPandas 对中文路径支持已经比早期好很多,但在不同系统、不同 GDAL 版本下仍可能遇到问题。建议项目路径尽量使用英文、数字和下划线。

data/project_001/input/poi.geojson

不要把数据长期放在桌面、微信下载目录或含特殊符号的临时目录中。

2. CRS 不一致却直接做空间分析

这是 GeoPandas 空间分析中最常见的问题。两个图层看起来都能绘制,但坐标系不同,空间连接、裁剪、叠加可能全部出错。

分析前至少检查:

print(layer1.crs)
print(layer2.crs)

如果 CRS 不一致,应使用 to_crs() 统一,而不是直接继续分析。

3. 把 set_crs 当成 to_crs 使用

set_crs() 是给数据“贴坐标系标签”,不会改变坐标值。to_crs() 才会真正转换坐标值。

方法 作用 是否改变坐标值 典型用途
set_crs() 定义 CRS 数据缺少 CRS,但你确认其真实坐标系
to_crs() 转换 CRS 把 WGS84 转为投影坐标,或统一两个图层 CRS

4. Shapefile 字段名被截断

Shapefile 对字段名长度有限制,导出时长字段名可能被截断。例如 district_name 可能变成较短字段。为了减少字段损失,建议输出 GeoPackage。

5. 面数据几何无效导致叠加失败

如果行政区面存在自相交、空洞异常或重复边界,空间分析可能报错或结果异常。可以先检查几何有效性:

invalid_count = (~districts.geometry.is_valid).sum()
print("invalid geometry:", invalid_count)

对于简单问题,可以尝试:

districts["geometry"] = districts.geometry.make_valid()

但如果是正式生产数据,建议回到数据源头修复,避免把拓扑错误带入后续流程。

方法比较:GeoPandas、QGIS、ArcPy 怎么选

方法 适合场景 优点 限制
GeoPandas Python GIS、批处理、数据清洗、空间连接统计 代码清晰,适合自动化,和 Pandas 结合方便 超大数据性能有限,复杂拓扑处理不如专业 GIS 平台稳定
QGIS 交互式制图、快速检查、可视化处理 免费开源,工具丰富,适合学习和验证结果 大量重复任务需要模型构建器或脚本配合
ArcPy ArcGIS Pro 工作流自动化、企业数据处理 和 ArcGIS 生态结合紧密,工具箱能力强 依赖 ArcGIS 授权和环境
PostGIS 海量空间数据、服务端查询、多用户数据管理 空间索引强,适合 WebGIS 后端和长期数据管理 需要数据库设计和 SQL 能力

如果你的任务是中小规模数据清洗和空间统计,GeoPandas 很合适;如果要处理百万级以上复杂空间查询,建议考虑 PostGIS;如果需要和企业 ArcGIS 工具链对接,ArcPy 更自然。

检查清单:写 GeoPandas 空间分析脚本前先确认这些项

  • 数据格式是否适合当前任务,优先考虑 GeoPackage 而不是老旧 Shapefile。
  • 文件路径是否稳定,是否避免中文、空格和特殊符号。
  • 每个图层是否成功读取为 GeoDataFrame。
  • geometry 字段是否存在,是否有空几何或无效几何。
  • 参与空间分析的图层 CRS 是否一致。
  • 是否正确区分 set_crs()to_crs()
  • 空间连接谓词是否符合业务含义,例如 withincontainsintersects
  • 统计结果是否用 QGIS 或 ArcGIS Pro 可视化检查过。
  • 导出格式是否保留字段名、编码和几何类型。
  • 脚本是否把输入、输出路径分开,避免覆盖原始数据。

FAQ:GeoPandas怎么读相关常见问题

Q1:GeoPandas怎么读 Shapefile?

可以直接使用 gpd.read_file()

import geopandas as gpd

gdf = gpd.read_file("data/roads.shp")
print(gdf.head())
print(gdf.crs)

如果 Shapefile 包含中文字段或中文属性,建议检查编码问题。实际项目中也可以先用 QGIS 转为 GeoPackage,再用 GeoPandas 读取。

Q2:GeoPandas怎么读 GeoJSON?

GeoJSON 也可以直接读取:

gdf = gpd.read_file("data/poi.geojson")
print(gdf.geom_type.value_counts())

GeoJSON 常见坐标系是 WGS84,经纬度单位是度,不适合直接计算米制距离和面积。需要计算距离或面积时,应先转换到合适的投影坐标系。

Q3:GeoPandas读取空间数据后为什么没有 crs?

说明源文件没有写入坐标系信息,或者驱动没有正确识别。此时不要盲目转换,应先确认数据真实坐标系。如果确认是 WGS84,可以设置:

gdf = gdf.set_crs("EPSG:4326")

如果确认是某个地方投影坐标系,应设置对应 EPSG 编码或 CRS 定义。

Q4:GeoPandas空间连接结果为空怎么办?

优先检查四件事:

  • 两个图层 CRS 是否一致。
  • 点和面是否真的在同一空间范围。
  • 是否存在空几何或无效几何。
  • predicate 是否选错,例如边界点用 within 可能匹配不到。

可以打印数据范围辅助判断:

print(poi.total_bounds)
print(districts.total_bounds)

Q5:GeoPandas适合做大数据空间分析吗?

GeoPandas 适合中小规模矢量数据处理和脚本化分析。如果数据量很大,例如千万级要素、复杂叠加、多用户并发查询,建议使用 PostGIS、DuckDB Spatial 或分块处理策略。GeoPandas 更适合作为数据预处理、验证和自动化脚本工具。

Q6:GeoPandas和Pandas是什么关系?

GeoPandas 扩展了 Pandas。普通 DataFrame 主要处理表格字段,GeoDataFrame 在此基础上增加 geometry 字段和 CRS 信息,因此可以执行空间连接、缓冲区、投影转换、叠加分析等 GIS 操作。

结论:GeoPandas怎么读,关键是读懂数据、坐标系和分析流程

学习 GeoPandas,不要只停留在“怎么读文件”这一行代码上。真正实用的 GeoPandas 工作流应该包括:读取数据、检查 geometry、检查 CRS、统一坐标系、执行空间分析、统计结果、导出并可视化验证。

本文的空间连接统计案例覆盖了 Python GIS 入门中最常见的一类任务。掌握这个流程后,你可以把行政区换成网格、缓冲区、服务范围,把点数据换成门店、监测点、事故点或客户点,从而扩展到更多 GIS 空间分析场景。

建议你把上面的源码整理成一个独立脚本,并在自己的数据上跑一遍。只要坚持每次分析前检查 CRS 和 geometry,大多数 GeoPandas 读取空间数据和空间分析问题都能快速定位。