GeoPandas读取Shapefile?GIS地图可视化怎么画?
很多同学第一次用 Python 做空间数据分析时,都会遇到“GeoPandas读取Shapefile?GIS地图可视化怎么画?”这个问题:文件明明在本地,为什么读不出来?读出来以后为什么中文乱码、坐标不对、地图空白?本文用一个可复现的 GeoPandas 工作流,讲清楚 Shapefile 读取、坐标检查、属性查看和 GIS 地图可视化的完整步骤。

引言:用 GeoPandas 读取 Shapefile 并画出第一张 GIS 地图
GeoPandas 是 Python 生态中最常用的矢量 GIS 数据处理库之一。它把 pandas 的表格能力和 Shapely 的几何对象结合起来,可以直接读取 Shapefile、GeoJSON、GeoPackage 等空间数据,并用几行代码完成地图可视化。
如果你的目标是快速完成一张基础 GIS 地图,核心流程并不复杂:
- 准备完整的 Shapefile 文件组。
- 使用
geopandas.read_file()读取数据。 - 检查坐标系、字段和几何类型。
- 使用
GeoDataFrame.plot()绘制地图。 - 根据需要设置颜色、分类、图例、边界和导出图片。
下面的示例以行政区、道路、地块、采样点等常见 Shapefile 都适用。你只需要把路径和字段名替换成自己的数据即可。
背景:为什么 GeoPandas 读取 Shapefile 经常报错
Shapefile 不是一个单独文件,而是一组文件。最少通常包括:
.shp:存储几何图形。.shx:空间索引文件。.dbf:属性表文件。.prj:坐标参考系统信息,虽然不是强制文件,但非常重要。
很多 GeoPandas 读取 Shapefile 失败,并不是代码写错,而是数据文件不完整、路径包含特殊字符、编码不匹配,或者当前 Python 环境缺少底层 GIS 依赖。
最常见的问题包括:
- 只复制了
.shp文件,没有复制.dbf和.shx。 - 路径中有中文、空格、云盘同步目录,导致读取异常。
- Shapefile 属性表使用 GBK 编码,默认 UTF-8 读取后出现中文乱码。
.prj缺失,读取后crs为None。- 数据坐标是经纬度,但拿来按米计算面积或距离。
- 几何对象存在空几何、无效几何,绘图时结果异常。
原理:GeoPandas 读取 Shapefile 后得到的是什么
GeoPandas 读取 Shapefile 后,返回的是一个 GeoDataFrame。你可以把它理解为“带有空间几何列的 pandas DataFrame”。
普通表格数据只有字段和记录,而 GeoDataFrame 多了一个特殊列,通常叫 geometry。这个列中保存点、线、面等几何对象。
| 对象 | 作用 | 常用操作 |
|---|---|---|
GeoDataFrame |
空间数据表 | 查看字段、筛选记录、投影转换、绘图 |
geometry |
几何列 | 存储点、线、面对象 |
crs |
坐标参考系统 | 判断数据是经纬度坐标还是投影坐标 |
plot() |
基础地图绘制 | 按字段着色、设置图例、设置边界样式 |
GeoPandas 的地图可视化本质上是把几何对象交给 Matplotlib 绘制。因此它适合做静态地图、分析过程图、结果检查图。如果要做交互式 WebGIS 地图,则通常会结合 Folium、Leaflet、OpenLayers 或导出为 GeoJSON 后再发布。
步骤:GeoPandas 读取 Shapefile 的完整代码
1. 安装 GeoPandas 环境
推荐使用 conda 安装 GeoPandas,因为它依赖 GDAL、PROJ、Fiona、pyogrio 等 GIS 底层库,直接用 pip 在部分 Windows 环境中容易遇到依赖问题。
conda create -n gis python=3.11
conda activate gis
conda install -c conda-forge geopandas matplotlib pyogrio
如果你习惯使用 pip,也可以尝试:
pip install geopandas matplotlib pyogrio
环境安装后,先检查是否能正常导入:
import geopandas as gpd
import matplotlib.pyplot as plt
print(gpd.__version__)
2. 准备 Shapefile 文件路径
建议把 Shapefile 放在一个路径简单的目录中,例如:
D:/gis_data/city_boundary/city_boundary.shp
不要只放 .shp 文件,应该保证同名文件组都在同一目录下:
city_boundary.shp
city_boundary.shx
city_boundary.dbf
city_boundary.prj
在 Python 中读取时,推荐使用原始字符串,避免反斜杠转义问题:
import geopandas as gpd
shp_path = r"D:/gis_data/city_boundary/city_boundary.shp"
gdf = gpd.read_file(shp_path)
print(gdf.head())
print(gdf.crs)
print(gdf.geometry.geom_type.value_counts())
3. 处理中文字段或属性乱码
如果 GeoPandas 读取 Shapefile 后中文属性显示乱码,可以显式指定编码。国内很多旧 Shapefile 的属性表是 GBK 或 GB18030 编码。
gdf = gpd.read_file(shp_path, encoding="gbk")
print(gdf.head())
如果 gbk 仍然乱码,可以尝试:
gdf = gpd.read_file(shp_path, encoding="gb18030")
注意,编码只影响属性表文字,不会改变几何坐标。如果地图位置不对,应优先检查坐标系,而不是编码。
4. 检查坐标系 CRS
读取成功后,一定要检查 crs。这是 GIS 地图可视化和空间分析中最容易被忽略的一步。
print(gdf.crs)
常见结果示例:
EPSG:4326:经纬度坐标,单位是度。EPSG:3857:Web Mercator,常用于在线地图底图。EPSG:4490:CGCS2000 地理坐标系。None:数据没有坐标系信息,需要根据数据来源确认。
如果数据没有 CRS,但你明确知道它是 WGS84 经纬度,可以设置 CRS:
gdf = gdf.set_crs(epsg=4326)
如果数据已有 CRS,只是需要转换到另一个坐标系,应使用 to_crs():
gdf_3857 = gdf.to_crs(epsg=3857)
注意:
set_crs()是“声明当前坐标系”,不会改变坐标值;to_crs()是“坐标转换”,会改变坐标值。两者不能混用。
5. 用 GeoPandas plot 绘制基础地图
最简单的 GIS 地图可视化只需要一行代码:
import matplotlib.pyplot as plt
gdf.plot()
plt.show()
为了让地图更清晰,可以设置面颜色、边界颜色、线宽和图幅大小:
fig, ax = plt.subplots(figsize=(8, 8))
gdf.plot(
ax=ax,
facecolor="#d9ecff",
edgecolor="#2b6cb0",
linewidth=0.8
)
ax.set_title("Shapefile 地图可视化结果")
ax.set_axis_off()
plt.show()
如果是道路、河流等线数据,可以这样绘制:
fig, ax = plt.subplots(figsize=(10, 8))
gdf.plot(
ax=ax,
color="#444444",
linewidth=0.6
)
ax.set_title("线状 Shapefile 可视化")
ax.set_axis_off()
plt.show()
如果是采样点、POI 等点数据,可以设置点大小和颜色:
fig, ax = plt.subplots(figsize=(8, 8))
gdf.plot(
ax=ax,
color="red",
markersize=8
)
ax.set_title("点状 Shapefile 可视化")
ax.set_axis_off()
plt.show()
6. 按属性字段进行分级设色
GIS 地图可视化通常不只是把边界画出来,还需要根据属性字段表达差异。例如按人口、面积、等级、类型等字段着色。
先查看字段名:
print(gdf.columns)
假设有一个字段叫 population,可以按该字段绘制专题图:
fig, ax = plt.subplots(figsize=(9, 8))
gdf.plot(
column="population",
ax=ax,
legend=True,
cmap="YlOrRd",
edgecolor="white",
linewidth=0.5
)
ax.set_title("按人口字段绘制分级设色图")
ax.set_axis_off()
plt.show()
如果是类别字段,例如 type,可以使用分类图:
fig, ax = plt.subplots(figsize=(9, 8))
gdf.plot(
column="type",
ax=ax,
legend=True,
categorical=True,
edgecolor="black",
linewidth=0.3
)
ax.set_title("按类型字段绘制分类地图")
ax.set_axis_off()
plt.show()
7. 叠加多个 Shapefile 图层
实际 GIS 制图往往需要叠加多个图层,例如行政区边界、道路和点位。关键是确保所有图层在同一个坐标系中。
boundary = gpd.read_file(r"D:/gis_data/boundary.shp")
roads = gpd.read_file(r"D:/gis_data/roads.shp")
points = gpd.read_file(r"D:/gis_data/points.shp")
roads = roads.to_crs(boundary.crs)
points = points.to_crs(boundary.crs)
fig, ax = plt.subplots(figsize=(10, 10))
boundary.plot(
ax=ax,
facecolor="#f7fbff",
edgecolor="#2171b5",
linewidth=0.8
)
roads.plot(
ax=ax,
color="#636363",
linewidth=0.5
)
points.plot(
ax=ax,
color="#e31a1c",
markersize=12
)
ax.set_title("行政区、道路和点位叠加图")
ax.set_axis_off()
plt.show()
如果叠加后看不到某个图层,首先检查 CRS 是否一致,其次检查数据范围是否重叠。
8. 导出地图图片
完成可视化后,可以保存为 PNG 或 PDF:
fig, ax = plt.subplots(figsize=(10, 10))
gdf.plot(
ax=ax,
facecolor="#deebf7",
edgecolor="#3182bd",
linewidth=0.6
)
ax.set_title("GeoPandas Shapefile 可视化")
ax.set_axis_off()
plt.savefig(
"geopandas_shapefile_map.png",
dpi=300,
bbox_inches="tight"
)
plt.show()
如果要用于论文、报告或公众号配图,建议设置 dpi=300,并使用 bbox_inches="tight" 去掉多余空白。
常见坑:GeoPandas 读取 Shapefile 和地图可视化排错
1. 只复制了 .shp 文件
这是最常见的问题。Shapefile 必须作为文件组一起使用。如果缺少 .dbf,可能没有属性表;如果缺少 .shx,可能无法正常索引几何;如果缺少 .prj,就无法自动识别坐标系。
解决方法:
- 从原始数据目录完整复制所有同名文件。
- 不要手动改其中某一个文件名。
- 压缩传输时,把整个 Shapefile 文件组一起压缩。
2. 中文路径或编码导致读取失败
新版本 GeoPandas 对中文路径支持已经比过去好很多,但在部分 Windows、旧 GDAL 或复杂环境中仍可能出问题。
建议排查:
- 把数据移动到
D:/gis_data/这类简单路径。 - 路径不要放在微信、QQ、OneDrive、网盘同步目录中。
- 属性乱码时尝试
encoding="gbk"或encoding="gb18030"。
3. 坐标系不一致导致图层错位
如果多个 Shapefile 叠加后不重合,大概率是坐标系不一致。例如一个图层是 EPSG:4326,另一个是投影坐标系。
正确做法是先查看 CRS,再统一到同一个 CRS:
print(boundary.crs)
print(roads.crs)
roads = roads.to_crs(boundary.crs)
不要直接改坐标值,也不要在不知道原始坐标系的情况下随意 set_crs()。
4. 地图显示为空白
GeoPandas 绘图空白通常有几种原因:
- 数据本身为空,
len(gdf)为 0。 - 几何列为空,
gdf.geometry.is_empty中存在大量True。 - 坐标值异常,例如经纬度数据出现百万级坐标,或投影坐标被误认为经纬度。
- 叠加图层时范围差异太大,小图层被挤在一个角落。
可以用下面的代码快速检查:
print(len(gdf))
print(gdf.total_bounds)
print(gdf.geometry.is_empty.sum())
print(gdf.geometry.is_valid.value_counts())
5. 面积和距离计算结果不对
如果 Shapefile 是经纬度坐标,例如 EPSG:4326,直接计算面积和距离通常不符合实际,因为单位是“度”,不是“米”。
应先转换到合适的投影坐标系,再计算面积或距离:
gdf_proj = gdf.to_crs(epsg=3857)
gdf_proj["area_m2"] = gdf_proj.area
对于正式项目,不建议无脑使用 EPSG:3857 计算面积。应根据研究区选择合适的等积投影或本地投影坐标系。
方法比较:GeoPandas、QGIS 和 ArcGIS Pro 该怎么选
| 方法 | 适合场景 | 优点 | 局限 |
|---|---|---|---|
| GeoPandas | 批量处理、自动化制图、Python 空间分析 | 代码可复现,适合和 pandas、NumPy、Matplotlib 结合 | 交互式编辑能力弱,复杂制图不如专业 GIS 软件直观 |
| QGIS | 可视化检查、投影转换、符号化制图、数据编辑 | 免费开源,图形界面友好,插件丰富 | 批量自动化需要额外学习表达式、模型构建器或 PyQGIS |
| ArcGIS Pro | 企业级 GIS 项目、规范制图、地理处理模型 | 工具链完整,制图和空间分析能力强 | 商业软件,授权和环境成本较高 |
| Folium 或 WebGIS | 交互式网页地图展示 | 适合浏览器交互、点选、弹窗和在线发布 | 需要考虑数据量、切片、前端性能和服务部署 |
如果你只是想检查 Shapefile 是否正确,QGIS 更直观;如果你要批量读取多个 Shapefile、统一坐标系、自动输出地图图片,GeoPandas 更适合;如果你要制作高质量版式地图,ArcGIS Pro 和 QGIS 的布局工具会更方便。
检查清单:GeoPandas 读取 Shapefile 前后必查项
- Shapefile 文件组是否完整:
.shp、.shx、.dbf、.prj是否在同一目录。 - 文件路径是否简单,是否避免了奇怪符号、过长路径和同步盘目录。
- 读取后
gdf.head()是否能看到正常属性。 - 中文字段或属性是否乱码,是否需要指定
encoding="gbk"。 gdf.crs是否为空,是否知道数据原始坐标系。- 多个图层叠加前,是否使用
to_crs()统一坐标系。 gdf.total_bounds是否符合研究区范围。- 几何对象是否存在空几何或无效几何。
- 专题图字段是否为数值字段或合理的类别字段。
- 导出图片时是否设置了合适的尺寸、分辨率和边界空白。
FAQ:GeoPandas读取Shapefile常见问题
1. GeoPandas 读取 Shapefile 必须安装 GDAL 吗?
GeoPandas 底层需要依赖 GDAL、PROJ、pyogrio 或 Fiona 等 GIS I/O 相关库。普通用户不一定要单独手动安装 GDAL,但安装 GeoPandas 时环境中需要有这些依赖。为了减少 Windows 环境问题,推荐使用 conda-forge 安装。
2. GeoPandas 读取 Shapefile 中文乱码怎么办?
优先尝试在 read_file() 中指定编码:
gdf = gpd.read_file(shp_path, encoding="gbk")
如果仍然乱码,再尝试 gb18030。如果数据来自较新的软件,也可能是 UTF-8 编码。编码问题只影响属性文字,不影响空间坐标。
3. GeoPandas 地图可视化为什么没有底图?
gdf.plot() 只绘制矢量数据本身,不会自动加载高德、OSM 或影像底图。如果需要底图,可以把数据转换到 EPSG:3857 后结合 contextily 使用在线瓦片底图。但这涉及网络访问、瓦片版权和坐标转换问题。
4. Shapefile 没有 .prj 文件还能读取吗?
通常可以读取几何和属性,但 GeoPandas 无法知道它的坐标系,gdf.crs 会是 None。这种情况下必须根据数据来源确认原始 CRS,再使用 set_crs() 声明。不要盲目猜测坐标系。
5. GeoPandas 能不能直接修改 Shapefile 并保存?
可以。修改属性、筛选要素或投影转换后,可以使用 to_file() 保存:
gdf.to_file(
r"D:/gis_data/output/result.shp",
encoding="utf-8"
)
但 Shapefile 对字段名长度、字段类型、编码支持都有历史限制。如果是新项目,建议优先考虑 GeoPackage,文件更稳定,也更适合保存中文字段和复杂数据。
6. GeoPandas 读取 Shapefile 后如何只显示某个区域?
可以先按属性筛选,再绘图。例如字段 name 中有城市名称:
sub = gdf[gdf["name"] == "杭州市"]
sub.plot()
plt.show()
也可以根据空间范围裁剪,但需要使用边界图层或指定裁剪框,这属于进一步的空间分析操作。
结论:先读对数据,再画好地图
GeoPandas读取Shapefile并完成GIS地图可视化,关键不在于代码有多复杂,而在于流程是否正确:文件组完整、编码正确、坐标系明确、几何有效、字段适合制图。
实际项目中,建议先用 read_file() 读取 Shapefile,再用 head()、crs、total_bounds 和 geom_type 做基础检查。确认数据没有问题后,再用 plot() 做基础地图、分类图或分级设色图。
如果你是 GIS 初学者,可以把本文的代码保存成一个模板。以后遇到行政区、道路、点位、地块等 Shapefile 数据时,只要替换路径、字段名和符号参数,就能快速完成一张可用的 Python GIS 可视化地图。