跳转至
Apache Sedona 1.9.0 已正式发布,新增 Spark 4.1 支持、proj4sedona 坐标系转换、Bing Tile 函数等众多特性!

在 Apache Spark 上使用 Sedona 计算距离

本文介绍如何使用 Apache Sedona 与 Apache Spark 计算两个点或几何对象之间的距离。

您将了解如何在二维笛卡尔平面上计算距离,以及如何为地理空间数据计算考虑地球曲率的距离。

先看一个在二维笛卡尔平面上计算两点距离的示例。

使用 Spark 与 Sedona 计算两点之间的距离

假设您有 4 个点,希望分别计算 point_apoint_bpoint_cpoint_d 之间的距离。

两点之间的距离

先创建一个包含这些点的 DataFrame:

df = sedona.createDataFrame(
    [
        (Point(2, 3), Point(6, 4)),
        (Point(6, 2), Point(9, 2)),
    ],
    ["start", "end"],
)

startend 列均为 geometry 类型。

使用 ST_Distance 函数计算两点之间的距离:

df.withColumn("distance", ST_Distance(col("start"), col("end"))).show()

结果如下:

+-----------+-----------+-----------------+
|      start|        end|         distance|
+-----------+-----------+-----------------+
|POINT (2 3)|POINT (6 4)|4.123105625617661|
|POINT (6 2)|POINT (9 2)|              3.0|
+-----------+-----------+-----------------+

借助 ST_Distance,在二维平面上计算两点之间的距离非常直观。

使用 Spark 与 Sedona 计算两个经纬度点之间的距离

下面创建两个经纬度点并计算它们之间的距离。先用经纬度构造 DataFrame:

seattle = Point(-122.335167, 47.608013)
new_york = Point(-73.935242, 40.730610)
sydney = Point(151.2, -33.9)
df = sedona.createDataFrame(
    [
        (seattle, new_york),
        (seattle, sydney),
    ],
    ["place1", "place2"],
)

计算这些点之间的距离:

df.withColumn(
    "st_distance_sphere", ST_DistanceSphere(col("place1"), col("place2"))
).show()

结果如下:

+--------------------+--------------------+--------------------+
|              place1|              place2|  st_distance_sphere|
+--------------------+--------------------+--------------------+
|POINT (-122.33516...|POINT (-73.935242...|  3870075.7867602874|
|POINT (-122.33516...| POINT (151.2 -33.9)|1.2473172370818963E7|
+--------------------+--------------------+--------------------+

我们使用 ST_DistanceSphere 计算距离,它会考虑地球的曲率,返回值的单位为米。

下面看如何使用椭球(spheroid)模型计算两点之间的距离。

使用 Spark 与 Sedona 在椭球模型上计算两点距离

复用前一节的 DataFrame,但改用椭球模型:

res = df.withColumn(
    "st_distance_spheroid", ST_DistanceSpheroid(col("place1"), col("place2"))
)
res.select("place1_name", "place2_name", "st_distance_spheroid").show()

结果如下:

+-----------+-----------+--------------------+
|place1_name|place2_name|st_distance_spheroid|
+-----------+-----------+--------------------+
|    seattle|   new_york|  3880173.4858397646|
|    seattle|     sydney|1.2456531875384018E7|
+-----------+-----------+--------------------+

ST_DistanceSpheroid 返回两地之间的距离(单位:米)。在椭球模型下计算的结果与把地球建模为球时差别不大,但椭球的结果会略微更精确。

使用 Spark 与 Sedona 计算两个几何对象之间的距离

下面看看如何计算一条折线与一个多边形之间的距离。假设有以下对象:

对象之间的距离

两个多边形之间的距离定义为它们之间任意两点的最小欧氏距离。

计算距离:

res = df.withColumn("distance", ST_Distance(col("geom1"), col("geom2")))

结果如下:

+---+---+--------+
|id1|id2|distance|
+---+---+--------+
|a  |b  |2.0     |
+---+---+--------+

可以从图中直观地看出两个多边形之间的最小距离。

三维最小笛卡尔距离

下面看如何在计算两点距离时把高度(elevation)也考虑进来。

我们将比较站在帝国大厦顶端的人与站在海平面的人之间的距离。

构造 DataFrame:

empire_state_ground = Point(-73.9857, 40.7484, 0)
empire_state_top = Point(-73.9857, 40.7484, 380)
df = sedona.createDataFrame(
    [
        (empire_state_ground, empire_state_top),
    ],
    ["point_a", "point_b"],
)

分别计算 2D 与 3D 距离:

res = df.withColumn("distance", ST_Distance(col("point_a"), col("point_b"))).withColumn(
    "3d_distance", ST_3DDistance(col("point_a"), col("point_b"))
)

结果如下:

+--------------------+--------------------+--------+-----------+
|             point_a|             point_b|distance|3d_distance|
+--------------------+--------------------+--------+-----------+
|POINT (-73.9857 4...|POINT (-73.9857 4...|     0.0|      380.0|
+--------------------+--------------------+--------+-----------+

ST_Distance 不考虑高度;ST_3DDistance 会把高度纳入计算。

使用 Spark 与 Sedona 计算 Frechet 距离

构造一个包含以下折线的 Sedona DataFrame:

Frechet 距离

a = LineString([(1, 1), (1, 3), (2, 4)])
b = LineString([(1.1, 1), (1.1, 3), (3, 4)])
c = LineString([(7, 1), (7, 3), (6, 4)])
df = sedona.createDataFrame(
    [
        (a, "a", b, "b"),
        (a, "a", c, "c"),
    ],
    ["geometry1", "geometry1_id", "geometry2", "geometry2_id"],
)

计算 Frechet 距离:

res = df.withColumn(
    "frechet_distance", ST_FrechetDistance(col("geometry1"), col("geometry2"))
)

查看结果:

res.select("geometry1_id", "geometry2_id", "frechet_distance").show()
+------------+------------+----------------+
|geometry1_id|geometry2_id|frechet_distance|
+------------+------------+----------------+
|           a|           b|             1.0|
|           a|           c|             6.0|
+------------+------------+----------------+

下图直观展示了这些距离,便于理解算法:

Frechet 距离

使用 Spark 与 Sedona 计算几何对象之间的最大距离

假设有以下几何对象:

几何对象之间的距离

计算其中一些几何对象之间的最大距离:

res = df.withColumn("max_distance", ST_MaxDistance(col("geom1"), col("geom2")))

查看结果:

res.select("id1", "id2", "max_distance").show(truncate=False)
+---+---+-----------------+
|id1|id2|max_distance     |
+---+---+-----------------+
|a  |b  |8.246211251235321|
|a  |c  |7.615773105863909|
+---+---+-----------------+

由此可以方便地获得两个几何对象之间的最大距离。

结论

Sedona 支持多种类型的距离计算,包括基于不同地球模型的距离,以及考虑高度等更复杂的距离运算。

请根据您的分析需求选择最合适的距离函数。