使用DuckDB Spatial扩展进行GIS分析

更新时间:
复制 MD 格式

DuckDBspatial扩展为其提供了强大的GIS(地理信息系统)能力,支持加载地理数据文件(Shapefile、GeoJSON、GeoParquet等)、执行空间查询与分析(距离计算、包含关系、空间连接等)。结合EMR Serverless Spark中内置的DuckDB环境,可以方便地进行轻量级的地理空间数据分析。本文通过一个完整的示例,介绍如何在Serverless Spark Notebook中使用DuckDB Spatial扩展分析地理数据。

前提条件

  • Notebook会话版本需要esr-3.7.0/esr-4.8.0及以上。

  • 需要公网访问能力:spatial扩展安装时需要从公网下载扩展包。请确保Serverless Spark实例所在的VPC已配置公网NAT网关。具体配置方式请参见EMR Serverless Spark与其他VPC间网络互通

  • 建议将Spark driver资源适当调大(如spark.driver.cores=4spark.driver.memory=16g),因为DuckDB是单机引擎,仅使用driver资源。

使用限制

  • 仅支持在Notebook、PySpark任务中使用DuckDB Spatial扩展。

实验数据准备

本文使用Natural Earth公开数据集中的城市数据(ne_10m_populated_places_simple)。请先将数据下载并上传到OSS。

  1. 下载数据文件(约20 MB)。

    wget https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_10m_populated_places_simple.geojson
  2. 上传到OSS。

    # 请替换为实际的Bucket和路径
    ossutil cp ne_10m_populated_places_simple.geojson oss://your-bucket/data/geo/
    说明

    请将下文示例中所有oss://your-bucket/data/geo/替换为实际的OSS路径。

操作步骤

Serverless Spark控制台的数据开发中,创建Notebook并连接Notebook会话,使用Python + DuckDB进行空间分析。

步骤一:安装并加载扩展

import duckdb

con = duckdb.connect()
con.execute("INSTALL spatial")
con.execute("LOAD spatial")
print("spatial extension loaded successfully")

步骤二:加载城市数据并分析

# 从OSS加载Natural Earth城市数据
con.execute("""
    CREATE TABLE cities AS
    SELECT * FROM ST_Read(
        'oss://your-bucket/data/geo/ne_10m_populated_places_simple.geojson'
    )
""")

# 筛选中国城市并获取坐标
df = con.execute("""
    SELECT
        name,
        adm1name AS province,
        pop_max AS population,
        ST_X(geom) AS longitude,
        ST_Y(geom) AS latitude
    FROM cities
    WHERE sov0name = 'China'
    ORDER BY pop_max DESC
    LIMIT 20
""").fetchdf()

print(df.to_string())

步骤三:计算城市间距离

# 各城市到北京的球面距离
result = con.execute("""
    WITH beijing AS (
        SELECT geom FROM cities
        WHERE name = 'Beijing' AND sov0name = 'China'
        LIMIT 1
    )
    SELECT
        c.name,
        c.adm1name AS province,
        ROUND(ST_Distance_Sphere(c.geom, b.geom) / 1000, 1) AS distance_to_beijing_km
    FROM cities c, beijing b
    WHERE c.sov0name = 'China'
    ORDER BY distance_to_beijing_km
    LIMIT 15
""").fetchdf()

print(result.to_string())

步骤四:空间范围查询

# 以上海为中心、300km半径内的城市
result = con.execute("""
    WITH shanghai AS (
        SELECT geom FROM cities
        WHERE name = 'Shanghai' AND sov0name = 'China'
        LIMIT 1
    )
    SELECT
        c.name,
        c.adm1name AS province,
        ROUND(ST_Distance_Sphere(c.geom, s.geom) / 1000, 1) AS distance_km
    FROM cities c, shanghai s
    WHERE c.sov0name = 'China'
      AND ST_Distance_Sphere(c.geom, s.geom) / 1000 < 300
    ORDER BY distance_km
""").fetchdf()

print("Cities within 300km of Shanghai:")
print(result.to_string())

步骤五:可视化城市分布

以下代码使用Python标准库生成SVG地图,并通过IPython内置的display直接在Notebook中渲染显示,无需安装matplotlib等第三方库。

from IPython.display import SVG, display

# 获取中国城市坐标
cities_data = con.execute("""
    SELECT name, ST_X(geom) AS lon, ST_Y(geom) AS lat, pop_max
    FROM cities
    WHERE sov0name = 'China'
    ORDER BY pop_max DESC
    LIMIT 30
""").fetchall()

# 生成SVG可视化
def generate_svg(data, width=600, height=500):
    min_lon, max_lon = 73, 135
    min_lat, max_lat = 18, 54

    def project(lon, lat):
        x = (lon - min_lon) / (max_lon - min_lon) * width
        y = height - (lat - min_lat) / (max_lat - min_lat) * height
        return x, y

    parts = [
        f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}">',
        f'<rect width="{width}" height="{height}" fill="#f0f8ff"/>'
    ]
    for name, lon, lat, pop in data:
        x, y = project(lon, lat)
        r = max(3, min(12, pop / 3000000))
        parts.append(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="{r}" fill="#e74c3c" opacity="0.7"/>')
        parts.append(f'<text x="{x+r+2:.1f}" y="{y+3:.1f}" font-size="8">{name}</text>')
    parts.append('</svg>')
    return '\n'.join(parts)

svg_content = generate_svg(cities_data)

# 直接在Notebook中渲染显示
display(SVG(data=svg_content))

常用GIS函数速览

类别

函数

说明

读取数据

ST_Read(path)

读取地理数据文件。

创建几何

ST_Point(x, y)

创建点几何。

坐标提取

ST_X(geom) / ST_Y(geom)

提取经度/纬度。

距离计算

ST_Distance_Sphere(a, b)

计算球面距离(单位:米)。

空间关系

ST_Contains(a, b)

判断包含关系。

空间关系

ST_Intersects(a, b)

判断相交关系。

聚合

ST_Collect(list)

合并几何集合。

分析

ST_Centroid(geom)

计算质心。

缓冲区

ST_Buffer(geom, distance)

创建缓冲区。

格式转换

ST_AsGeoJSON(geom)

转为GeoJSON格式。

更完整的函数列表和用法详情,请参考DuckDB Spatial Functions官方文档

相关文档