Jupyter Notebook + GIS:打造可交互、可复现的空间分析报告

Python
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

很多 GIS 项目真正痛苦的地方,不是不会做空间分析,而是分析过程难复现。今天用的是一版人口数据,明天甲方又换一版社区边界;这次服务范围阈值是 800 米,下次又要改成 1000 米。如果你的流程依赖 ArcGIS Pro 或 QGIS 里的手工点击,最终往往只剩下一份截图很多、过程很少的静态报告。Jupyter Notebook + GIS 的价值,就在于把“数据读取、坐标处理、空间分析、地图输出、文字解释”放进同一个可执行文档里,让空间分析报告既可交互,也可复现。

本文不讲空泛概念,而是围绕一个典型 GIS 场景展开:以城市公园 15 分钟步行可达性分析为例,演示如何用 Jupyter Notebook 组织空间分析流程,如何输出交互式地图,如何保证同事换电脑后仍能跑通,最后再说清楚这种做法和传统桌面 GIS 报告的边界与差异。

为什么 GIS 空间分析报告总是难复现

传统 GIS 报告的问题通常出在三个环节。第一,数据版本不固定。一个项目里可能同时存在多个 shp、GeoJSON、Excel 坐标表和底图截图,分析时用了哪个版本,过几周自己都记不清。第二,参数留痕不足。缓冲半径、空间连接方式、投影坐标系、面积单位这些关键参数,常常只存在于某一次点击操作里。第三,结果和过程割裂。Word 报告里只放结论图和表,别人看得到结果,却无法验证过程。

这正是 Jupyter Notebook GIS 工作流 适合介入的地方。Notebook 不是单纯的“写代码界面”,而是把说明文字、代码单元、运行结果、地图输出和参数配置放在一个连续文档中。你修改数据路径或服务半径,再从头执行一次,新的图、表和说明就能一起更新。对空间分析项目来说,这比单独交付一堆脚本或一份静态 PDF 更接近真实协作需求。

Jupyter Notebook + GIS 的核心原理是什么

可以把 Notebook 理解成“可执行的分析报告”。文档中的每个代码单元都会明确记录你对数据做了什么处理,例如读取道路网络、统一坐标系、生成服务区、叠加人口栅格或统计街道级覆盖率。这样一来,空间分析不再只是“做过”,而是“能被重跑”。

在 GIS 场景里,这种模式通常由几类 Python 库共同完成。geopandas 负责矢量数据读写和空间叠加,osmnx 负责从 OpenStreetMap 获取路网,networkx 负责路网最短路径或可达性计算,foliumgeopandas.explore() 负责交互式地图展示,matplotlib 则适合输出报告里的静态图表。它们并不是为了替代桌面 GIS,而是为了把分析逻辑显式化。

Jupyter Notebook GIS 可复现空间分析报告与公园可达性分析工作流
把数据、代码、地图、参数和环境说明放进同一份 Notebook,是 GIS 分析报告实现可复现和可交互的关键。

问题背景:什么样的 GIS 任务最适合放进 Notebook

不是所有 GIS 工作都必须搬进 Notebook,但以下任务非常适合。比如每周都要重跑一次门店服务区分析,每次只是换一版点位;比如做多区县指标统计,流程完全一样,只是输入边界和统计字段不同;再比如给领导或客户演示不同参数下的分析结果,想让对方直接看到“步行 10 分钟”和“步行 15 分钟”的差别。这些需求有一个共同点:流程重复、参数明确、结果需要解释。

本文选用“城市公园 15 分钟步行可达性分析”作为例子,就是因为它同时包含了 GIS 项目中最常见的几类动作:抓取路网、读取公园面数据、做坐标统一、生成网络可达范围、统计覆盖人口,并将结果用交互地图和表格展示出来。这个案例既能体现 Jupyter Notebook 空间分析报告 的完整链路,也足够贴近真实规划、住建和城市更新场景。

实操流程:用 Notebook 做一份可复现的公园可达性分析报告

第一步:先固定环境,而不是先写代码

很多 Notebook 失败,不是分析逻辑有问题,而是环境不可复现。你本机能跑,别人电脑报错,往往是因为 geopandasshapelyfionapyproj 的版本不一致。所以第一步建议先建独立环境,再启动 JupyterLab。

conda create -n gis_notebook python=3.11
conda activate gis_notebook
conda install -c conda-forge jupyterlab geopandas osmnx networkx folium mapclassify matplotlib openpyxl
conda env export > environment.yml

environment.yml 很重要。它相当于这份 GIS 报告的“运行说明书”。后续交付时,把 .ipynb 和这个环境文件一起给同事,别人就能尽可能在同样的软件条件下复现结果。

第二步:把输入数据和参数写在 Notebook 最前面

可复现不是把代码堆出来,而是让别人一眼看懂“这份报告吃什么数据、跑什么参数”。实践中建议把研究区、投影、时间阈值、步行速度、输入文件路径统一放在第一个参数单元。这样以后改需求,不必满篇找变量。

study_area = "Huangpu District, Shanghai, China"
walk_minutes = 15
walk_speed_mps = 1.2
target_crs = "EPSG:4547"
parks_file = "data/parks.shp"
communities_file = "data/communities.shp"

这里特意加入投影参数,是因为很多 GIS Notebook 第一个大坑就是直接在经纬度坐标下算距离。空间分析报告只要涉及长度、面积、服务半径,尽量切到适合本地区的投影坐标系再算,不要在 WGS 84 经纬度下直接做米级分析。

第三步:读取数据后,先做坐标和几何质量检查

桌面 GIS 用户很容易把“图层能显示”误认为“数据没问题”。在 Notebook 里,建议把检查动作显式写出来:看坐标系、看字段、看空几何、看范围。这样做虽然多几行代码,但后面排错时间会少很多。

import geopandas as gpd

parks = gpd.read_file(parks_file)
communities = gpd.read_file(communities_file)

print(parks.crs)
print(communities.crs)
print(parks.is_empty.sum(), communities.is_empty.sum())

parks = parks.to_crs(target_crs)
communities = communities.to_crs(target_crs)

如果这里不把坐标统一,后面做最近邻、公园中心点贴路网、社区覆盖面积统计时,很可能会出现“算出来了,但不可信”的情况。可复现报告的价值,不只是每次都能跑出结果,更是每次都能跑出同一套合理结果。

第四步:获取步行路网,并把分析逻辑写清楚

以公园可达性为例,真正合理的服务范围通常不是简单缓冲区,而是基于道路网络的步行可达范围。Notebook 适合把这个逻辑解释得很清楚:先抓取路网,再把公园入口或中心点匹配到路网节点,接着按步行时间求可达节点集合,最后再把这些节点转成服务区面。

import osmnx as ox

G = ox.graph_from_place(study_area, network_type="walk")
nodes, edges = ox.graph_to_gdfs(G)
nodes = nodes.to_crs(target_crs)
edges = edges.to_crs(target_crs)

如果你的业务要求更精细,建议不要直接用公园面中心点,而是整理公园出入口点位。原因很现实:大型公园中心点可能落在草坪或湖面上,用它去贴最近路网节点,得到的 15 分钟步行圈会偏离真实入口位置。这类“为什么这么做”的说明,正是 Notebook 比纯脚本更适合做报告的地方。

第五步:生成服务区,并把中间结果保留下来

很多人只关心最终服务区面,但在项目协作里,中间结果同样重要。比如公园匹配到哪个路网节点、每个节点的最短步行时间是多少、哪些社区被判定为覆盖,这些都值得输出成表或图层,便于复核。

如果你用 networkx 计算网络距离,可以把每个公园的可达节点集合写成一个 GeoDataFrame,再生成服务区边界面。服务区生成方式可以是凸包,也可以是更贴边界的 Alpha Shape,但要在报告里写明你用的是哪一种,否则后续解释会混乱。

import networkx as nx

walk_seconds = walk_minutes * 60

# 示例思路:先给每条边补行走时间
for u, v, k, data in G.edges(keys=True, data=True):
    length = data.get("length", 0)
    data["walk_time"] = length / walk_speed_mps

# 之后可从公园入口节点出发,按 walk_time 做阈值搜索
# 得到的可达节点再转为服务区面

这一步最容易出现“代码能跑,但方法不严谨”的问题,所以最好同时写一段文字说明服务区是基于步行网络而不是欧氏缓冲,阈值是 15 分钟,默认步行速度是多少,是否考虑单行线、封闭路段和真实出入口。这样,报告才真正具备审阅价值。

第六步:把结果做成交互式地图,而不是只截静态图

如果文章标题里写的是“可交互”,那就不要只停留在 matplotlib 出图。Notebook 很适合直接生成可以缩放、平移、开关图层的地图。对于 GIS 报告,最实用的交互通常不是花哨动画,而是图层控制、悬浮信息、结果筛选和不同阈值对比。

m = communities.explore(
    column="covered_ratio",
    cmap="YlGn",
    tooltip=["name", "covered_ratio"],
    legend=True
)

parks.explore(
    m=m,
    color="#1f78b4",
    tooltip=["park_name"]
)

这样输出的地图可以直接嵌在 Notebook 里。你在汇报时不仅能展示“哪些社区覆盖不足”,还能让对方现场放大到某个街道、查看具体属性值。对规划分析、选址分析、服务区分析这类任务,这种交互能力比一张静态配图更有说服力。

第七步:把结果表、图和解释文字写进同一份报告

空间分析报告的质量,不只取决于地图好不好看,还取决于结论是否可追溯。Notebook 的好处是你可以在输出地图后,紧接着写一段解释:哪些街道覆盖率低,可能是什么原因,是因为公园总量不足,还是被高架、河流、封闭小区边界切断。然后再补上一张统计表,让图和表互相印证。

输出内容 Notebook 中的作用 适用场景
交互式地图 查看覆盖范围、盲区位置和图层叠加关系 汇报展示、方案比选
统计表 量化每个街道或社区的覆盖率、服务人口 指标考核、决策支撑
文字解释 说明分析假设、参数来源和结果限制 正式报告、项目归档

这一步很关键,因为很多所谓的“数据分析 Notebook”只停留在跑代码,没有形成可交付成果。GIS 项目要的是可被团队使用的分析报告,而不是只有作者自己看得懂的实验记录。

如何把 Notebook 交付给别人,而不是只留在自己电脑里

做完分析后,至少要考虑三种交付方式。第一种是导出 HTML,适合分享只读版结果,地图交互通常还能保留。第二种是连同 environment.yml 一起交付 .ipynb,适合团队内部继续复算和改参数。第三种是进一步用 Voila 之类的方式把 Notebook 包成轻量 Web 应用,让非技术人员只通过滑块、下拉框和按钮来操作分析参数。

如果你的目标读者是项目经理或业务人员,通常不建议直接给原始 Notebook,因为他们未必愿意配置 Python 环境。更稳妥的办法是交付两层成果:一份 HTML 结果报告给业务侧,一份原始 Notebook 加环境文件给技术侧。这样既保留可复现能力,也不增加非技术成员的门槛。

常见坑点:为什么你的 GIS Notebook 看起来对,其实不稳

  • 只保存了 ipynb,没有保存环境。别人一装包就报错,报告根本复现不了。
  • 在经纬度坐标下直接算距离。地图看着没问题,但服务范围和面积统计会失真。
  • 中间结果全靠内存,没有落盘。一旦中间步骤出错,很难检查到底是哪一环出了问题。
  • 把缓冲区当成网络服务区。对于步行、配送、消防等场景,直线距离往往不具备业务意义。
  • 交互地图太重。一次性把几十万个面直接塞进 folium,Notebook 会明显变卡。
  • 参数散落在各个代码单元。后续改服务阈值、投影或输入路径时,容易漏改。

这些问题的共同点是:它们不会总是直接报错,但会让空间分析报告在协作、复算和交付时变得脆弱。写 Notebook 的目标不是“跑一次成功”,而是“半年后换人也能接着跑”。

Jupyter Notebook + GIS 和传统桌面 GIS 报告怎么选

这不是非此即彼的关系。桌面 GIS 仍然非常适合做人工编辑、制图整饰、质量检查和复杂交互式修图;Notebook 更适合流程固定、参数可控、需要多次重跑和批量生成结果的分析型任务。两者在实际项目里完全可以配合使用。

方式 优势 更适合的任务
Jupyter Notebook + GIS 可复现、易批量重跑、过程透明、便于交付交互地图 重复性分析、参数对比、技术归档
ArcGIS Pro / QGIS 图形界面 上手直观、制图成熟、人工修正高效 数据编辑、版式制图、快速探索
桌面 GIS + Notebook 混合 既保留人工判断,也保证核心分析可复跑 正式项目生产流程

如果你所在团队经常面临“这个月重跑上个月同类分析”的需求,那么把核心空间分析流程转成 Notebook,通常会在第二次、第三次复用时迅速回本。

一份可复现 GIS Notebook 报告的检查清单

  • 输入数据路径、研究区、投影和关键阈值是否集中写在开头。
  • 是否显式检查了坐标系、空几何、字段完整性和范围。
  • 长度、面积、服务半径分析是否在合适投影坐标系下进行。
  • 是否说明了网络服务区、缓冲区或插值方法的选择依据。
  • 是否保留了中间结果,便于复核和排错。
  • 是否同时输出地图、表格和文字解释,而不只是其中一种。
  • 是否导出了 environment.yml 或等价环境说明。
  • 是否测试过在新环境中重新执行一遍 Notebook。

FAQ:Jupyter Notebook 做 GIS 报告时最常见的问题

Jupyter Notebook 能替代 ArcGIS Pro 或 QGIS 吗

不能简单理解为替代。Notebook 更擅长可复现分析和自动化报告,桌面 GIS 更擅长数据编辑、制图整饰和人工检查。对于很多团队来说,最优解是把高频、重复的空间分析流程迁到 Notebook,而把人工修图和最终版式输出留给桌面 GIS。

Jupyter Notebook GIS 报告适合哪些空间分析任务

特别适合服务区分析、批量缓冲分析、空间连接统计、分区覆盖率计算、专题图自动生成、固定流程的遥感指标计算等任务。只要流程稳定、参数明确、需要反复重跑,就很值得写成 Notebook。

交互式地图导出后还能保持交互吗

如果是用 foliumgeopandas.explore() 生成的前端交互地图,导出成 HTML 后通常还能保留缩放、平移和图层查看能力。但涉及 Python 后端实时计算的交互控件,往往需要借助 Voila 或其他服务化方式才能继续工作。

为什么同一份 Notebook 换台电脑就跑不起来

大多数情况不是 Notebook 文件坏了,而是环境版本不一致。GIS Python 生态对底层依赖比较敏感,尤其是 gdalfionapyprojshapely 这些包。解决思路不是临时报一个包装一个包,而是一开始就导出环境配置文件并在新环境里完整重建。

总结

Jupyter Notebook + GIS 真正改变的,不是地图怎么画,而是空间分析报告怎么被生产、复查和复用。它把原本散落在桌面 GIS 点击、截图和 Word 文档里的过程,重新组织成一份可执行、可解释、可重跑的工作文档。对于公园可达性、门店服务区、覆盖率统计、固定流程专题图这类任务,只要你愿意把参数、数据和步骤写清楚,Notebook 就能显著提升 GIS 报告的稳定性和协作效率。