Apache Sedona怎么用?Spark如何跑GIS?
Apache Sedona怎么用?Spark如何跑GIS? 这是很多 GIS 工程师从桌面软件转向大数据空间分析时遇到的第一个问题:Shapefile、GeoJSON、WKT、Parquet 这些空间数据,能不能像普通表一样在 Spark 里分布式读取、空间过滤、空间连接和统计?答案是可以,核心工具就是 Apache Sedona。
引言:Apache Sedona怎么用,先搞清它解决什么问题
Apache Sedona 是一个面向 Apache Spark 的分布式空间计算框架。你可以把它理解为“给 Spark 增加 GIS 几何类型、空间函数和空间索引能力”的组件。
如果你的数据量只有几万条,用 QGIS、ArcGIS Pro 或 GeoPandas 通常更直接。但当你遇到下面这些场景时,Spark 跑 GIS 就有价值了:
- 全国 POI、道路、轨迹、栅格切片索引等数据达到千万级或亿级。
- 需要做大范围空间连接,例如“点落在哪个行政区”“轨迹点匹配道路缓冲区”。
- 空间数据已经存放在 HDFS、对象存储、Hive、Parquet 或数据湖中。
- 希望把 GIS 处理接入现有 Spark ETL、调度平台或数据仓库流程。
本文以“Spark 如何跑 GIS”为主线,演示 Apache Sedona 的基本用法、空间 SQL、空间连接、结果校验和常见坑。

背景:为什么普通 Spark 不能直接处理 GIS 几何
原生 Spark 擅长处理表格数据,例如字符串、数字、日期、数组和结构体。但 GIS 数据里最核心的是几何对象,例如点、线、面、多边形。普通 Spark 不知道一个 WKT 字符串代表空间对象,也不知道两个多边形是否相交。
例如下面这一列在普通 Spark 看来只是字符串:
POINT (116.391 39.907)
POLYGON ((116.3 39.8, 116.5 39.8, 116.5 40.0, 116.3 40.0, 116.3 39.8))
但在 GIS 分析中,你希望对它执行这些操作:
- 判断点是否在面内:ST_Contains
- 判断两个几何是否相交:ST_Intersects
- 计算距离:ST_Distance
- 构建缓冲区:ST_Buffer
- 读取 WKT、WKB、GeoJSON:ST_GeomFromWKT、ST_GeomFromGeoJSON
Apache Sedona 的作用就是把这些 GIS 能力接入 Spark,使你可以用 DataFrame 和 SQL 的方式完成分布式空间分析。
原理:Apache Sedona 如何让 Spark 跑 GIS
Apache Sedona 的基本思路并不复杂:先把 WKT、WKB、GeoJSON 等格式转换为 Spark 可识别的几何类型,再注册空间函数,然后通过 Spark SQL 或 DataFrame API 执行空间计算。
一个典型流程如下:
- 启动 SparkSession,并加载 Sedona 相关依赖。
- 读取 CSV、Parquet、GeoJSON、WKT 等数据源。
- 用 Sedona 函数创建 geometry 字段。
- 注册临时视图,使用空间 SQL 查询。
- 执行空间过滤、空间连接、缓冲区、距离计算等操作。
- 将结果写出为 Parquet、GeoParquet、CSV 或供后续制图使用的数据。
这里要特别注意:Spark 分布式计算并不会自动让所有 GIS 分析变快。空间连接如果没有合适的分区、索引和过滤条件,仍然可能非常慢。Apache Sedona 提供的是空间函数和分布式执行框架,但数据组织方式仍然很关键。
步骤:Apache Sedona怎么用,跑一个点落行政区示例
步骤 1:准备运行环境
最常见的入门环境是 PySpark 加 Apache Sedona。建议先在本地模式跑通,再迁移到 Spark 集群。
你需要准备:
- Java 环境,与当前 Spark 版本兼容。
- Apache Spark 或 PySpark。
- Apache Sedona Python 包。
- 测试数据:一个点表和一个面表。
Python 环境中可以安装 Sedona 包:
pip install apache-sedona
如果你在生产环境使用 Spark 集群,还需要确保 Driver 和 Executor 都能访问 Sedona 对应的 jar 包。不同 Spark、Scala、Sedona 版本的坐标需要以 Apache Sedona 官方文档为准,避免版本不匹配。
步骤 2:准备示例数据
为了便于理解,这里使用两个 CSV 示例。点表 points.csv:
id,name,lon,lat
1,A点,116.391,39.907
2,B点,121.473,31.230
3,C点,113.264,23.129
面表 regions.csv 使用 WKT 存储行政区或业务区域:
region_id,region_name,wkt
bj,北京示例区,"POLYGON ((116.0 39.5, 116.8 39.5, 116.8 40.3, 116.0 40.3, 116.0 39.5))"
sh,上海示例区,"POLYGON ((121.1 30.9, 121.8 30.9, 121.8 31.5, 121.1 31.5, 121.1 30.9))"
gz,广州示例区,"POLYGON ((112.9 22.8, 113.7 22.8, 113.7 23.5, 112.9 23.5, 112.9 22.8))"
真实项目中,面数据通常来自行政区划、网格、商圈、保护区或业务片区。点数据可能是 POI、订单、轨迹点、设备上报位置或采样点。
步骤 3:创建 SparkSession 并注册 Sedona
下面是一个 PySpark 示例。不同版本的 Sedona 初始化方式可能略有差异,实际项目中请以你安装版本的官方文档为准。
from pyspark.sql import SparkSession
from sedona.spark import SedonaContext
spark = (
SparkSession.builder
.appName("sedona-point-in-polygon-demo")
.master("local[*]")
.config("spark.sql.session.timeZone", "UTC")
.getOrCreate()
)
sedona = SedonaContext.create(spark)
如果你在集群上运行,不建议直接使用 local[*]。应改为提交到 YARN、Kubernetes 或 Standalone 集群,并通过 spark-submit 指定依赖包。
步骤 4:读取点数据并构造 geometry 字段
读取 CSV 后,使用 ST_Point 根据经纬度创建点几何。这里的经度是 x,纬度是 y,顺序不能写反。
points = (
spark.read
.option("header", True)
.option("inferSchema", True)
.csv("data/points.csv")
)
points.createOrReplaceTempView("points_raw")
points_geom = spark.sql("""
SELECT
id,
name,
lon,
lat,
ST_Point(CAST(lon AS DOUBLE), CAST(lat AS DOUBLE)) AS geom
FROM points_raw
""")
points_geom.createOrReplaceTempView("points")
这一步是 Apache Sedona 怎么用的关键:只有把普通经纬度字段转换成 geometry,后续空间函数才能正常工作。
步骤 5:读取面数据并构造 polygon geometry
面表里已有 WKT 字段,可以用 ST_GeomFromWKT 转换为几何对象。
regions = (
spark.read
.option("header", True)
.option("inferSchema", True)
.csv("data/regions.csv")
)
regions.createOrReplaceTempView("regions_raw")
regions_geom = spark.sql("""
SELECT
region_id,
region_name,
ST_GeomFromWKT(wkt) AS geom
FROM regions_raw
""")
regions_geom.createOrReplaceTempView("regions")
如果 WKT 无效,转换结果可能为空或报错。生产环境建议先抽样检查 WKT 是否闭合、坐标是否异常、几何是否自相交。
步骤 6:使用 ST_Contains 做点落面分析
现在就可以在 Spark SQL 中执行 GIS 分析了。下面示例判断每个点落在哪个区域内:
result = spark.sql("""
SELECT
p.id,
p.name,
p.lon,
p.lat,
r.region_id,
r.region_name
FROM points p
LEFT JOIN regions r
ON ST_Contains(r.geom, p.geom)
""")
result.show(truncate=False)
预期结果类似:
+---+----+-------+------+---------+-----------+
|id |name|lon |lat |region_id|region_name|
+---+----+-------+------+---------+-----------+
|1 |A点 |116.391|39.907|bj |北京示例区 |
|2 |B点 |121.473|31.23 |sh |上海示例区 |
|3 |C点 |113.264|23.129|gz |广州示例区 |
+---+----+-------+------+---------+-----------+
这就是 Spark 跑 GIS 的最小闭环:读取数据、创建几何、执行空间关系判断、输出结果。
步骤 7:保存分析结果
如果结果用于后续数据分析,推荐写成 Parquet:
result.write.mode("overwrite").parquet("output/point_region_result")
如果结果需要交给 GIS 软件查看,可以保留经纬度字段,再导出 CSV;或者在后续流程中转换为 GeoParquet、GeoJSON 等空间格式。
result.select(
"id", "name", "lon", "lat", "region_id", "region_name"
).write.mode("overwrite").option("header", True).csv("output/point_region_csv")
常见坑:Spark如何跑GIS时最容易出错的地方
坑 1:经纬度顺序写反
在大多数 GIS 几何函数中,点坐标顺序是 x、y。对于经纬度数据,x 通常是经度,y 通常是纬度。
ST_Point(lon, lat)
如果写成 ST_Point(lat, lon),中国区域的点可能跑到异常位置,空间连接结果会全部为空。
坑 2:坐标系不一致
Apache Sedona 可以计算几何关系,但它不会自动帮你判断两张表是否处在同一个坐标系中。点表是 WGS84 经纬度,面表却是投影坐标时,ST_Contains 基本不会得到正确结果。
排查方法:
- 经纬度坐标通常范围为经度 -180 到 180,纬度 -90 到 90。
- Web Mercator 坐标通常是百万级米单位数值。
- 地方投影坐标可能是几十万、几百万级。
- 做空间关系前,必须统一坐标系。
坑 3:把 ST_Distance 当成真实米制距离
如果 geometry 使用的是经纬度坐标,ST_Distance 计算出来通常是“度”的距离,不是米。做缓冲区、距离筛选、最近邻分析时要特别小心。
正确做法是:
- 小范围分析可先投影到合适的米制坐标系。
- 跨区域距离分析要选择适合的地理距离算法或专门函数。
- 不要在经纬度坐标上直接用
ST_Buffer(geom, 1000)期待得到 1000 米缓冲区。
坑 4:空间连接没有预过滤,任务非常慢
点面连接、线面相交、面面叠加都可能产生大量候选组合。如果没有空间分区、边界框过滤或合理的数据裁剪,Spark 任务会非常慢,甚至产生数据倾斜。
优化思路包括:
- 先用行政区、省份、网格编号等普通字段缩小连接范围。
- 先过滤研究范围,不要全量数据直接相交。
- 对大表进行合理分区,避免单个分区过大。
- 关注 Spark UI 中的 shuffle、倾斜 task 和 executor 内存。
坑 5:几何无效导致结果异常
实际面数据常见自相交、空洞错误、环方向异常、未闭合等问题。桌面 GIS 软件可能能显示,但分布式空间计算中可能导致判断失败或性能下降。
建议在进入 Spark 前或进入 Spark 后做数据质量检查:
- 抽样查看 WKT 或 GeoJSON 是否完整。
- 检查空 geometry。
- 检查极端坐标值。
- 对行政区、网格等关键面数据先在 QGIS 或 PostGIS 中验证。
方法比较:Apache Sedona、PostGIS、GeoPandas、QGIS 怎么选
| 工具 | 适合场景 | 优势 | 限制 |
|---|---|---|---|
| Apache Sedona | 海量空间数据 ETL、Spark 数据湖、分布式空间连接 | 能接入 Spark 生态,适合批处理和大规模数据 | 环境依赖较多,调优门槛高,不适合简单小数据任务 |
| PostGIS | 空间数据库、在线查询、稳定空间分析服务 | 空间索引成熟,SQL 能力强,适合长期管理空间数据 | 超大规模批处理需要数据库资源和索引设计支持 |
| GeoPandas | Python 小到中等规模空间分析、数据探索 | 上手快,适合脚本化处理和 Notebook 分析 | 单机内存限制明显,不适合亿级数据 |
| QGIS | 可视化检查、手工处理、制图、空间数据质检 | 交互友好,适合查看结果和排查坐标问题 | 不适合自动化处理超大批量任务 |
简单说,如果你要回答“Apache Sedona怎么用”,通常说明你的数据已经接近或超过单机 GIS 工具的舒适区;如果只是做一个县域内几千条点面叠加,QGIS 或 GeoPandas 可能更省事。
检查清单:用 Apache Sedona 跑 GIS 前先核对这些项
- 数据格式:确认输入是 WKT、WKB、GeoJSON、CSV 经纬度、Parquet 还是其他格式。
- 坐标系:确认所有图层在同一坐标系下,尤其是点表和面表。
- 坐标顺序:经纬度点应按 lon、lat 构造,而不是 lat、lon。
- 几何有效性:检查空几何、自相交、多余环、异常坐标。
- 空间函数:明确使用
ST_Contains、ST_Intersects、ST_Within还是ST_Distance。 - 数据规模:小数据先本地跑通,大数据再上集群。
- 分区策略:大规模空间连接要关注分区、shuffle 和数据倾斜。
- 结果验证:抽样导出结果,在 QGIS 或 ArcGIS Pro 中叠加检查。
- 版本匹配:Spark、Scala、Sedona jar、Python 包版本要互相兼容。
- 输出用途:分析结果进仓库用 Parquet,需要 GIS 可视化则保留坐标或导出空间格式。
FAQ:Apache Sedona怎么用的常见问题
1. Apache Sedona 和 GeoSpark 是什么关系?
GeoSpark 是 Apache Sedona 的早期名称。现在应优先使用 Apache Sedona 的官方包、文档和函数名称。搜索旧资料时看到 GeoSpark,不要直接照搬依赖版本,先确认是否仍适用于当前 Sedona。
2. Spark 如何跑 GIS,必须用 Scala 吗?
不必须。Apache Sedona 支持在 Spark 生态中使用 SQL、Scala、Java、Python 等方式。对 GIS 学习者和数据分析人员来说,PySpark 加 Sedona SQL 通常更容易入门。
3. Apache Sedona 能直接读取 Shapefile 吗?
Sedona 生态中有读取空间文件的能力,但生产中更推荐把 Shapefile 转成更适合大数据处理的格式,例如 Parquet、GeoParquet 或带 WKT/WKB 字段的表。Shapefile 字段名长度、编码、文件组合结构都不太适合大规模分布式流程。
4. 用 Apache Sedona 做点面连接为什么结果为空?
最常见原因有三个:坐标系不一致、经纬度顺序写反、面几何无效。建议先抽取 10 条点和 1 个面,在 QGIS 中叠加查看,再回到 Spark 中检查 ST_AsText 输出是否正常。
5. ST_Contains 和 ST_Within 有什么区别?
ST_Contains(a, b) 表示 a 包含 b;ST_Within(b, a) 表示 b 在 a 内部。做点落面时,常见写法是 ST_Contains(region.geom, point.geom) 或 ST_Within(point.geom, region.geom)。
6. Apache Sedona 适合实时 WebGIS 查询吗?
一般不作为第一选择。Sedona 更适合大规模批处理、离线空间分析和数据湖加工。如果是 WebGIS 在线查询、地图点击查询、范围检索,PostGIS、Elasticsearch 地理索引或专门的空间服务通常更合适。
7. 为什么本地跑得通,上 Spark 集群就报依赖错误?
通常是 Sedona jar、Spark 版本、Scala 版本或 Python 包不匹配。集群模式下,Driver 和 Executor 都需要正确加载依赖。建议使用 spark-submit 明确指定 packages 或 jars,并让集群节点使用一致的环境。
结论:先用 Sedona SQL 跑通闭环,再考虑分布式优化
学习 Apache Sedona怎么用,不建议一开始就研究复杂调优。更稳妥的路线是:先用小样本跑通“读取数据、创建 geometry、执行空间 SQL、导出结果”这个闭环,再逐步迁移到真实大数据量。
对于“Spark如何跑GIS”这个问题,核心不是把所有 GIS 工具都搬到 Spark 里,而是把适合批处理和大规模计算的空间任务交给 Spark。点面连接、空间过滤、轨迹数据清洗、网格统计、空间数据湖加工,都是 Apache Sedona 的典型应用场景。
最后记住三条实践原则:坐标系先统一,几何字段先验证,小数据先跑通。做到这三点,再去处理分区、索引和集群性能,成功率会高很多。