现实世界中,大多数与可测量过程相关的数据都具有地理空间属性。对于那些管理着广阔地理区域内的资产,或者业务流程需要考虑多层地理属性并进行地图绘制的组织来说,当它们开始利用这些数据回答战略问题或优化业务时,其地理空间分析需求将变得更为复杂。这些以地理空间为核心的组织可能会针对其数据提出以下疑问:
有多少资产位于特定的地理边界内?
客户步行或驾车抵达某个地点需要多长时间?
预期每单位面积的客流量密度是多少?
所有这些都是极具价值的地理空间查询,它们要求将多个数据实体整合到统一的存储层中,并且需要对点在多边形内操作(point-in-polygon operations)和地理空间索引等地理空间连接操作进行扩展,以处理大量输入数据。本文将深入探讨如何利用Databricks的功能以及利用Spark实现、Delta通用表存储格式和Unity Catalog [1]的开源工具,来扩展地理空间分析能力,重点关注矢量地理空间数据的批处理分析。
解决方案概述
下方图表总结了在Databricks中构建地理空间数据湖仓的开源方法。地理空间数据集通过多种摄取模式(通常通过公共API)以各种格式落地到云存储中;在Databricks环境中,这可以是一个Unity Catalog目录和Schema中的卷(Volume)。地理空间数据格式主要包括矢量格式(如GeoJSON、.csv文件和Shapefile .shp),它们表示经纬度点、线或多边形及其属性;以及用于影像数据的栅格格式(如GeoTIFF、HDF5)。利用GeoPandas [2]或基于Spark的地理空间工具,例如Mosaic [3]或H3 Databricks SQL函数 [4],可以准备内存中的矢量文件,并以Delta格式将其保存到统一的青铜层(bronze layer)中,其中使用Well Known Text (WKT) 作为点或几何图形的字符串表示形式。

使用Unity Catalog和Databricks中的开源工具构建的地理空间分析工作流概述。图片由作者提供。
青铜层作为摄取数据的审计日志,而青铜层到白银层(silver layer)则是数据准备和所有上游用例通用地理空间连接操作的实施之处。最终的白银层应代表一个单一的地理空间视图,并可能与企业数据模型中的其他非地理空间数据集进行整合;它还提供了一个机会,可以将青铜层中的多张表整合为核心地理空间数据集,这些数据集可能包含多个属性和几何图形,其粒度满足上游聚合的需求。黄金层(gold layer)则是地理空间展示层,用于存储地理空间分析的输出结果,例如行程时间或密度计算。对于Power BI等仪表盘工具的使用,输出结果可以物化为星型Schema;而ESRI Online等云GIS工具则更倾向于GeoJSON文件,以用于特定的地图应用。
地理空间数据准备
除了在数据湖架构中整合多个独立数据源时面临的典型数据质量挑战(例如数据缺失、记录实践不一致等),地理空间数据还具有独特的数据质量和准备挑战。为了使矢量化地理空间数据集能够互操作并易于在上游进行可视化,最佳实践是选择一种地理空间坐标系统,例如WGS 84(广泛使用的国际GPS标准)。在英国,许多公共地理空间数据集会使用其他坐标系统,例如OSGB 36,这是为了提高英国地理特征地图绘制的准确性而优化的系统(这种格式通常以东向坐标和北向坐标表示,而非更常见的经纬度对)。因此,这些数据集需要转换为WGS 84,以避免如下图所示的下游地图绘制中的不准确性。

地理空间坐标系统概述a)以及WGS 84和OSGB 36在英国的叠加b)。图片改编自[5],经作者许可。版权所有(c) Ordnance Survey 2018。
大多数地理空间库,如GeoPandas、Mosaic等,都内置了处理这些转换的函数。例如,根据Mosaic文档,可以通过以下代码将多点几何图形从WGS84转换为Web Mercator投影格式:
df = (
spark.createDataFrame([{'wkt': 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))'}])
.withColumn('geom', st_setsrid(st_geomfromwkt('wkt'), lit(4326)))
)
df.select(st_astext(st_transform('geom', lit(3857)))).show(1, False)
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|MULTIPOINT ((1113194.9079327357 4865942.279503176), (4452779.631730943 3503549.843504374), (2226389.8158654715 2273030.926987689), (3339584.723798207 1118889.9748579597))|
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
另一个矢量地理空间数据独有的数据质量问题是无效几何图形的概念,如下图所示。这些无效的几何图形会导致上游GeoJSON文件或分析中断,因此最好修复它们或在必要时将其删除。大多数地理空间库都提供函数来查找或尝试修复无效几何图形。

无效几何图形类型的示例。图片摘自[6],经作者许可。版权所有(c) 2024 Christoph Rieke。
这些数据质量和准备步骤应尽早实施在数据湖仓的各个层级中;过往的经验表明,通常将其在青铜层到白银层的转换过程中完成,同时完成任何可重用的地理空间连接和其他转换。
扩展地理空间连接和分析
白银层/企业层的地理空间部分,理想情况下应代表一个单一的地理空间视图,为所有上游聚合、分析、机器学习建模和人工智能应用提供数据。除了数据质量检查和修复之外,有时通过聚合或联合操作整合多个地理空间数据集,可以简化数据模型,简化上游查询,并避免重复进行计算成本高昂的地理空间连接。地理空间连接通常计算成本非常高,原因在于表示有时复杂的复合多边形几何图形需要大量位,并且需要进行大量的成对比较。
存在几种策略可以提高这些连接的效率。例如,可以简化复杂的几何图形,有效减少表示它们所需的经纬度对数量;有多种方法可以实现此目的,这些方法可能针对不同的预期输出(例如,保留面积或删除冗余点)。这些方法可以在库中实现,例如在Mosaic中:
df = spark.createDataFrame([{'wkt': 'LINESTRING (0 1, 1 2, 2 1, 3 0)'}])
df.select(st_simplify('wkt', 1.0)).show()
+----------------------------+
| st_simplify(wkt, 1.0) |
+----------------------------+
| LINESTRING (0 1, 1 2, 3 0) |
+----------------------------+
另一种扩展地理空间查询的方法是使用地理空间索引系统,如下图所示。通过将点或多边形几何数据聚合到H3等地理空间索引系统中,相同信息的一个近似值可以通过高度压缩的形式表示,该形式由一个短字符串标识符表示,它映射到一组固定多边形(具有可视化经纬度对),这些多边形覆盖全球,在不同分辨率下涵盖一系列六边形/五边形区域,并且可以在层级中向上或向下滚动。

地理空间索引系统(压缩)的动机[7]以及Uber的H3索引可视化[8]。图片经作者许可改编。版权所有(c) CARTO 2023。版权所有(c) Uber 2018。
在Databricks中,H3索引系统也针对其Spark SQL引擎进行了优化,因此可以编写如下的点在多边形内连接查询,首先将点和多边形转换为所需分辨率(分辨率7约为5平方公里)的H3索引,然后使用H3索引字段作为连接键:
WITH locations_h3 AS (
SELECT
id,
lat,
lon,
h3_pointash3(
CONCAT('POINT(', lon, ' ', lat, ')'),
7
) AS h3_index
FROM locations
),
regions_h3 AS (
SELECT
name,
explode(
h3_polyfillash3(
wkt,
7
)
) AS h3_index
FROM regions
)
SELECT
l.id AS point_id,
r.name AS region_name,
l.lat,
l.lon,
r.h3_index,
h3_boundaryaswkt(r.h3_index) AS h3_polygon_wkt
FROM locations_h3 l
JOIN regions_h3 r
ON l.h3_index = r.h3_index;
如果需要,GeoPandas和Mosaic也支持执行不带任何近似值的地理空间连接,但通常H3的使用对于连接和密度计算等分析而言,已经提供了足够精确的近似值。借助云分析平台,还可以利用API获取实时交通数据和行程时间计算,例如使用Open Route Service [9]等服务,或者使用Overpass API for Open Street Map [10]等工具,为地理空间数据丰富额外的属性(例如,交通枢纽或零售地点)。
地理空间展示层
在完成了一些地理空间查询和聚合,并且分析结果已准备好供下游可视化后,地理空间数据湖仓的展示层可以根据用于消费地图或从数据中派生出的分析结果的下游工具进行结构化。下图概述了两种典型方法。

GeoJSON Feature Collection a) 与维度建模星型Schema b) 作为地理空间展示层输出数据结构的比较。图片由作者提供。
当为ESRI Online或其他带有地图工具的Web应用程序等云地理信息系统(GIS)提供服务时,存储在黄金/展示层卷中的GeoJSON文件,包含创建地图或仪表盘所需的所有数据,可以构成展示层。使用FeatureCollection GeoJSON类型,可以创建一个嵌套的JSON,其中包含多个几何图形和相关属性(“特征”),这些几何图形可以是点、线串或多边形。如果下游的仪表盘工具是Power BI,则可能更倾向于使用星型Schema,其中几何图形和属性可以建模为事实表和维度表,以充分利用其交叉筛选和度量支持功能,输出结果则在展示层中物化为Delta表。
平台架构与集成
地理空间数据通常是更广泛的企业数据模型以及分析和ML/AI用例组合的一部分。这些用例需要(理想情况下)一个云数据平台,并配备一系列上游和下游集成,以部署、编排并最终确保分析结果为组织带来价值。下图展示了一种常见的Azure数据平台高层架构,该架构在处理地理空间数据方面表现出色。

Azure中地理空间数据湖仓的高层架构。图片由作者提供。
数据使用各种ETL工具落地(如果可能,Databricks本身就足够)。在工作区内,维护着原始(青铜)、企业(白银)和展示(黄金)层构成的Medallion架构,利用Unity Catalog的目录.schema.table/volume层级来生成按用例分隔的层级(特别是权限管理),如果需要的话。当可展示的输出准备就绪可以共享时,有多种数据共享、应用程序构建、仪表盘和GIS集成选项。
例如,通过ESRI云,ESRI内部的ADLSG2存储账户连接器允许将写入外部Unity Catalog卷(即GeoJSON文件)的数据提取到ESRI平台中,以集成到地图和仪表盘中。一些组织可能更希望将地理空间输出写入下游系统,例如CRM或其他地理空间数据库。经过整理的地理空间数据及其聚合结果也经常用作机器学习模型的输入特征,这与地理空间Delta表无缝协作。Databricks正在开发内置于工作区内的各种AI分析功能(例如AI BI Genie [11]和Agent Bricks [12]),这些功能能够使用自然语言查询Unity Catalog中的数据,并且长远愿景是让任何地理空间数据都能以与其他表格数据相同的方式与这些AI工具协同工作,只是其可视化输出之一将是地图。
结语
最终,所有这些努力都旨在制作出有助决策制定的实用地图。下图展示了一些典型的地理空间分析成果。地理空间分析归结为理解诸如人员、事件或资产的聚集位置,从A点到B点通常需要多长时间,以及感兴趣属性(可能是栖息地、贫困程度或某种风险因素)的分布情况等信息。这些都是战略规划(例如,消防站应该设在哪里?)、了解客户群(例如,哪些客户在30分钟车程范围内?)或运营决策支持(例如,本周五哪些地点可能需要额外运力?)的重要信息。

地理空间分析示例。a) 行程时间分析 b) 使用H3的热点发现 c) 使用ML的热点聚类。图片由作者提供。
感谢您的阅读,如果您有兴趣进一步讨论或阅读更多内容,请联系或查阅以下参考文献。
https://www.linkedin.com/in/robert-constable-38b80b151/
参考文献
[1] https://learn.microsoft.com/en-us/azure/databricks/data-governance/unity-catalog/
[2] https://geopandas.org/en/stable/
[3] https://databrickslabs.github.io/mosaic/
[5] https://www.ordnancesurvey.co.uk/documents/resources/guide-coordinate-systems-great-britain.pdf
[6] https://github.com/chrieke/geojson-invalid-geometry
[7] https://carto.com/blog/h3-spatial-indexes-10-use-cases
[8] https://www.uber.com/en-GB/blog/h3/
[9] https://openrouteservice.org/dev/#/api-docs
[10] https://wiki.openstreetmap.org/wiki/Overpass_API
[11] https://www.databricks.com/blog/aibi-genie-now-generally-available
