在开始使用空间 SQL 时,至少对我而言,最大的挑战之一是拥有一个快速简便的参考,以将你当前的 GIS 工作流转换为 SQL。 有许多令人惊叹的资源可以扩展这方面的知识,但本指南旨在成为一本真正简单的食谱,以开始将你当前的工作流转换为空间 SQL。
推荐:用 NSDT设计器 快速搭建可编程3D场景。
有几点需要注意:
- 我将为 PostGIS 和通用数据仓库编写 SQL 代码
- 并非每个数据仓库都支持投影——例如,BigQuery 和 Snowflake 仅支持 WGS 84 SRID 4326
- 本指南于 2022 年 2 月发布,并将根据需要进行更新
1、几何图形
使用空间SQL可以创建2D/3D几何图形,或者查询几何体的属性。
1.1从纬度/经度对创建点几何
此查询将从包含纬度和经度值的数字列创建几何体。
# PostGIS
SELECT
ST_SetSRID(ST_MakePoint(lng, lat), 4326) as point_geom
FROM table
# BigQuery
SELECT
ST_GEOGPOINT(lng, lat) as point_geom
FROM table
# Snowflake and Redshift
SELECT
ST_MakePoint(lng, lat) as point_geom
FROM table
1.2 从 Well Known Text 创建几何图形
与上面类似,这将从 WKT 字符串创建一个几何图形:
# PostGIS, Redshift
SELECT
ST_GeomFromText('POINT(-71.064544 42.28787)', 4326) as geom
FROM table
# BigQuery
SELECT
ST_GEOGFROMTEXT('POINT(-71.064544 42.28787)') as geom
FROM table
# Snowflake
SELECT
ST_GEOGFROMWKT('POINT(-71.064544 42.28787)') as geom
FROM table
1.3 修改数据的投影
可以使用一个简单的函数将你的数据重新投影到一个使用此查询的新投影:
# PostGIS and Redshift
SELECT
ST_Transform(geom, 4326) as geom
FROM table
1.4 获取几何体经纬度
使用点几何,可以将纬度和经度提取为数值:
# PostGIS, BigQuery, Snowflake, and Redshift
SELECT
ST_X(geom) as longitude,
ST_Y(geom) as latitude
FROM table
2、测量
使用空间SQL可以对几何体进行测量。
2.1 计算多边形的面积
求多边形的面积(以平方米为单位)
# PostGIS, BigQuery, Snowflake, and Redshift
SELECT
ST_Area(geom) as area
FROM table
2.2 计算多边形的周长
与上面类似,但多边形的周长以米为单位
# PostGIS, BigQuery, Snowflake, and Redshift
SELECT
ST_Perimeter(geom) as area
FROM table
2.3 计算一条线的长度
计算一条线的长度(以米为单位)
# PostGIS, BigQuery, Snowflake, and Redshift
SELECT
ST_Length(geom) as length
FROM table
2.4 计算几何体之间的距离
返回两个几何之间的距离(以米为单位)。 每个不同的函数在使用球体或曲面地球计算时都有不同的签名,因此请务必在这种情况下检查文档。
# PostGIS and BigQuery
SELECT
ST_Distance(geom_1, geom_2) as distance
FROM table
2.5 计算两个几何体之间的最短距离
给定两个几何图形,找到几何图形 2 上最接近几何图形 1 的点。该函数将返回一个新点,表示第二个几何图形上与第一个几何图形的最近点。
# PostGIS and BigQuery
SELECT
ST_ClosestPoint(geom_1, geom_2) as closest_point
FROM table
3、几何变换
使用空间SQL可以对几何体进行变换操作。
3.1 在几何体周围创建缓冲区
在几何体周围创建一个缓冲区,它返回一个新的几何体。 下面函数中的数字是缓冲区的距离(以米为单位)。
# PostGIS, BigQuery, and Redshift
SELECT
ST_Buffer(geom, 100) as closest_point
FROM table
3.2 获取多边形或线的质心
如果你想找到一个几何体的质心,你可以使用这个查询来返回一个几何体的质心点:
# PostGIS, BigQuery, Snowflake, and Redshift
SELECT
ST_Centroid(geom) as centroid
FROM table
3.3 创建凹包或凸包
可以从一个几何体或一组几何体创建凹包或凸包,这将返回一个新的几何体:
# PostGIS, BigQuery, and Redshift
SELECT
ST_ConvexHull(geom) as convex_hull
FROM table
SELECT
ST_ConcaveHull(geom) as convex_hull
FROM table
还可以通过对几何图形进行分组来做到这一点:
# ST_Collect will collect the matching geometries together into a GeometryCollection
SELECT
ST_ConvexHull(ST_Collect(geom)) as convex_hull,
category
FROM table
GROUP BY category
3.4 合并几何体
如果你想合并你的几何图形,使用 ST_Union 返回一个新的几何图形:
# PostGIS and BigQuery
SELECT
ST_Union(geom) as geom,
category
FROM table
GROUP BY category
3.5 创建 Voronoi 多边形
围绕你的几何创建 Voronoi 多边形,返回新的几何体:
# PostGIS
SELECT
ST_VoronoiPolygons(geom) as voronoi_polygons
FROM table
3.6 查找交叉点的结果多边形
在 GIS 中通常称为剪切(Clip),使用此函数返回两个几何图形的交叉区域:
# PostGIS, BigQuery, Redshift, and Snowflake
SELECT
ST_Intersection(geom_1, geom_2) as intersection
FROM table
或者,如果你想查找几何关系船的剩余部分,可以使用 ST_Difference:
# PostGIS, BigQuery, Redshift, and Snowflake
SELECT
ST_Difference(geom_1, geom_2) as intersection
FROM table
4、空间关系
使用空间SQL可以查询几何体之间的空间关系。
4.1 查找一定距离内的几何图形
在另一个几何体的一定距离内查找几何体,常用于聚合查询或 WHERE 子句:
# PostGIS, BigQuery, Redshift, and Snowflake
SELECT
*
FROM table
WHERE ST_DWithin(
geom_1,
ST_GeomFromText('POINT(-73.9895258 40.7413529)',4326),
1000)
4.2 评估空间关系
查看两个几何图形是否重叠、接触、交叉、相交、包含等(或评估空间关系)。这是一个更复杂的函数,因为有几个不同的函数来评估空间关系,每个函数之间都有细微差别。 确保检查你正在使用的数据库或数据仓库的文档,因为它们可能会有所不同,或者可能具有不同的容差。
以下是 PostGIS 空间关系的核心功能,可以在此处找到更多详细信息。 此外,每个函数都会根据空间关系的结果返回一个布尔值 (true/false),我们可以使用几种方法(更多内容见下文)。
- ST_Equals——如果两个几何完全相等则返回真
- ST_Intersects——如果两个几何图形共享任何公共空间,则返回 true
- ST_Disjoint – 如果两个几何图形不共享任何空间,则返回 true,或者与ST_intersects 相反
- ST_Crosses – 如果几何图形有一些但不是全部的共同内部点,则返回 true。更详细地说,“如果交集产生的几何尺寸比两个源几何的最大尺寸小一,并且交集在两个源几何的内部,则返回 true”。如果几何相交但相交结果少一维(或者它一次穿过相交的几何),它将返回 true。 适用于多点/多边形、多点/线串、线串/线串、线串/多边形和线串/多多边形比较。
- ST_Overlaps – 如果两个几何图形在空间上重叠或相交但一个不完全包含另一个,则返回 true
- ST_Touches——如果一个几何体接触另一个几何体,但不与内部相交,则返回真
- ST_Within – 如果第一个几何完全在第二个几何内,则返回 true
- ST_Contains – 如果第二个几何完全包含在第一个几何中(与 ST_Within 相反),则返回 true
# PostGIS, BigQuery, Redshift, and Snowflake
SELECT
ST_Intersects(geom_1, geom_2) as intersects
FROM table
5、其他功能
空间SQL可以完成的其他功能,包括简化等。
5.1简化几何体
如果你想简化一个几何图形,你可以使用这些函数来简化和返回新的几何图形。 请注意,有些功能不会保留拓扑(或接触线/关系),但下面的功能会保留。 下面的数字是以米为单位的变化容差。
# PostGIS
SELECT
ST_SimplifyPreserveTopology(geom, 1) as geom
FROM table
# PostGIS, BigQuery, Redshift, and Snowflake
SELECT
ST_Simplify(geom, 1) as geom
FROM table
5.2 创建随机点
在多边形内生成 N 个随机点。 你可以指定编号或使用表格中的数据(如下图所示)。 请记住,每个函数都会返回不同的东西(PostGIS 返回一个多点,BigQuery 返回一个数组)所以如果你想将每个点提取到它自己的一行中,你可能需要使用其他函数:
# PostGIS
SELECT
ST_GeneratePoints(geom, number_column) as geom
FROM table
# BigQuery
WITH point_lists AS (
SELECT `carto-un`.carto.ST_GENERATEPOINTS(geom, number_column) AS points
FROM table
)
SELECT points FROM point_lists CROSS JOIN point_lists.points
5.3 点的聚类
通过创建几何空间集群来分析您的数据。 有两种方法,DBSCAN 和 KMeans,可用于执行此操作。 每个函数将为每行所属的簇返回一个数字,它也是一个窗口函数,因此它在这里也使用 OVER() 操作:
# PostGIS and BigQuery
SELECT
geom,
ST_ClusterDBSCAN(geom, desired_distance, min_geoms_per_cluster) OVER() as cluster_id
FROM table
# PostGIS and BigQuery
SELECT
geom,
ST_ClusterKMeans(geom, number_of_clusters) OVER() as cluster_id
FROM table
6、如何使用空间函数
有许多方法可以使用空间函数,根据你的需要,可以通过几种方式利用这些函数。 这些只是一些示例,但是相当常见的用例。
6.1 单几何体
首先,可以针对单个几何图形查询表中的其他行:
SELECT
ST_Distance(
geom,
ST_GeomFromText('POINT(-73.9895258 40.7413529)')) as distance
FROM table
6.2 同数据表
你还可以与同一个表中的值进行比较——此示例使用子查询来说明它。
SELECT
ST_Distance(
geom,
(SELECT geom FROM table WHERE id = 1)) as distance
FROM table
6.3 WHERE 子句
也可以在 WHERE 子句中使用空间函数来限制结果:
SELECT
*
FROM table
WHERE ST_Distance(
geom,
(SELECT geom FROM table WHERE id = 1)) < 100
6.4 连接
连接(Join)还用于使用空间函数作为条件连接两个表,通常用于聚合和空间连接:
SELECT
a.id,
COUNT(b.id) as count
FROM table a
JOIN other_table b
ON ST_Contains(a.geom, b.geom)
GROUP BY a.id
6.5 交叉连接
交叉连接有多种使用方式,但本质上是根据另一个表的值评估一个表的每个值。 它们还可以用于执行一些更复杂的“批量循环连接”,我们将在下面详细介绍。
SELECT
ST_Distance(a.geom, b.geom) as distance
a.id as id_one,
b.id as id_two
FROM table a, other_table b
6.6 子查询
子查询,如上所示,可以让你从其他表中检索特定值或聚合:
SELECT
a.id,
(SELECT COUNT(id) FROM table WHERE ST_DWithin(a.geom, geom, 400)) as within_400m
FROM other_table a
6.7 聚合
我们上面的连接示例说明了如何使用聚合,但还有许多其他例子。 查看这篇文章了解更多细节和用例。
7、其他复杂或特定的功能
使用空间SQL还可以完成一些复杂的功能。
7.1 H3 Cells
CARTO Spatial Extension 提供创建和管理 H3 单元和其他功能的功能。 此查询将在特定几何图形的边界内创建 H3 单元格(使用 BigQuery 表语法):
WITH
mn AS (
SELECT
geom
FROM
project.dataset.table
WHERE
name = 'Minnesota')
SELECT
h3,
`carto-un`.carto.H3_BOUNDARY(h3) AS geom
FROM (
SELECT `carto-un`.carto.H3_POLYFILL(geom, 5) AS cells
FROM
mn),
UNNEST(cells) AS h3
7.2 创建地图瓦片
还可以在空间 SQL 中创建地图图块。 这个视频详细介绍了如何使用 PostGIS 和 pg_tileserv 中的一些函数来做到这一点。
还可以使用 CARTO Spatial Extension 在各种数据仓库中执行此操作,此处在 BigQuery 中进行了说明:
CALL `carto-un`.carto.CREATE_TILESET(
R'''(
SELECT geom, type
FROM `carto-do-public-data.natural_earth.geography_glo_roads_410`
)
''',
R'''`cartobq.maps.natural_earth_roads`''',
STRUCT(
"Tileset name" AS name,
"Tileset description" AS description,
NULL AS legend,
0 AS zoom_min,
10 AS zoom_max,
"geom" AS geom_column_name,
NULL AS zoom_min_column,
NULL AS zoom_max_column,
1024 AS max_tile_size_kb,
"RAND() DESC" AS tile_feature_order,
true AS drop_duplicates,
R'''
"custom_metadata": {
"version": "1.0.0",
"layer": "layer1"
}
''' AS extra_metadata
)
);
7.3 使用道路或其他网络进行路由
PgRouting 在 PostGIS 中提供路由功能,可以使用一些简单的函数计算路由。 可以在这个教程中找到更多信息。
SELECT * FROM pgr_dijkstra(
'
SELECT gid AS id,
source,
target,
length AS cost
FROM ways
',
1,
2,
directed := false);
可以使用 CARTO Analytics Toolbox 来完成示例。 创建网络后,可以计算最短路径:
CALL `carto-un`.carto.FIND_SHORTEST_PATH_FROM_NETWORK_TABLE(
"mydataset.network_table",
"mydataset.shortest_path_table",
"ST_GEOGPOINT(-74.0, 40.0)",
"ST_GEOGPOINT(-75.0, 41.0)"
);
8、示例codebook
这里列出一些空间SQL的示例codebook。
8.1 简单最近邻
一个很常见的问题,解决一个特定值的最近邻,增加 LIMIT 会增加更多的结果:
# PostGIS
SELECT id, (SELECT geom FROM another_table LIMIT 1) <-> geom AS distance
FROM table
ORDER BY distance
LIMIT 1
# BigQuery
SELECT
a.id,
ARRAY_AGG(b.geoid ORDER BY st_distance(a.geom,
b.geom) ASC LIMIT 1)[SAFE_OFFSET(0)] as id,
ARRAY_AGG(st_distance(a.geom,
b.geom) ORDER BY st_distance(a.geom,
b.geom) ASC LIMIT 1)[SAFE_OFFSET(0)] as dist
FROM
project.dataset.table_1 a
CROSS JOIN project.dataset.table_2 b
GROUP BY a.id
8.2 计算多边形之间的重叠百分比
计算多边形与 ST_Intersection 和 ST_Area 的重叠百分比。 可以对表中每一行的一个多边形或 where 子句中的一个多边形执行此操作,以查找相交一定数量的重叠区域。
# PostGIS
WITH a AS (SELECT geom FROM table WHERE id = 1)
SELECT
ST_Area(ST_Intersection(geom, (SELECT geom FROM a))/ST_Area(geom) AS overlap
FROM table
ORDER BY overlap
# PostGIS
SELECT
COUNT(a.column),
b.id,
b.geom
FROM table b
LEFT JOIN table_2 a
ON ST_Intersects(a.geom, b.geom)
WHERE ST_Area(ST_Intersection(a.geom, b.geom)/ST_Area(geom) > .5
8.3 求两个不接触多边形上最近两点之间的距离
PostGIS:
# PostGIS
SELECT
ST_Distance(ST_ClosestPoint(a.geom,
b.geom),
ST_ClosestPoint(b.geom,
a.geom)) AS distance,
ST_MakeLine(ST_ClosestPoint(a.geom,
b.geom),
ST_ClosestPoint(b.geom,
a.geom)) AS geom,
a.objectid
FROM
table a
CROSS JOIN LATERAL (
SELECT
geom
FROM
table_2
WHERE
ST_Disjoint(a.geom,
geom)
AND a.objectid != objectid
ORDER BY
ST_Distance(geom,
a.geom) ASC LIMIT 1) b
BigQuery:
# BigQuery
SELECT
ST_MakeLine(ST_ClosestPoint(a.geom,
b.geom),
ST_ClosestPoint(b.geom,
a.geom)) AS geom,
ST_Distance(ST_ClosestPoint(a.geom,
b.geom),
ST_ClosestPoint(b.geom,
a.geom)) AS geom,
a.id
FROM
table_one a
LEFT JOIN (
SELECT
one.geom,
two.id,
ROW_NUMBER() OVER (PARTITION BY two.id ORDER BY ST_Distance(one.geom, two.geom) ASC) AS rank
FROM
table_one one,
table_two two
WHERE
ST_Disjoint(one.geom,
two.geom)
ORDER BY
ST_Distance(one.geom,
two.geom) ASC ) b
USING
(id)
WHERE
b.rank = 1
8.4 在几何体周围创建缓冲区并从缓冲区中裁剪出区域
获取一个几何图形,创建一个缓冲区,然后从缓冲区中裁剪出另一层的区域。
# PostGIS
SELECT
ST_Difference(
ST_Union(
ST_Buffer(a.geom, 1609)
),
ST_Union(b.geom))
as clipped_geom
FROM table a
LEFT JOIN table_2 b
ON ST_Intersects(a.geom, b.geom)
# BigQuery
SELECT
ST_Difference(
ST_Union_Agg(
ST_Buffer(a.geom, 1609)
),
ST_Union(b.geom))
as clipped_geom
FROM table a
LEFT JOIN table_2 b
ON ST_Intersects(a.geom, b.geom)
8.5 批量最近邻加入
通过查找从一个表到另一个表中所有行的最近邻居,将两个表连接在一起。 灵感来自 Paul Ramsey 的这篇文章。
# PostGIS
SELECT
a.geom,
b.name,
ST_Distance(a.geom, b.geom) AS distance
FROM
table a
CROSS JOIN LATERAL
(SELECT name, geom
FROM table_2 b
ORDER BY
a.geom <--> geom
LIMIT 1) b
# BigQuery
SELECT
a.id,
ARRAY_AGG(b.geoid ORDER BY st_distance(a.geom,
b.geom) ASC LIMIT 1)[SAFE_OFFSET(0)] as id,
ARRAY_AGG(st_distance(a.geom,
b.geom) ORDER BY st_distance(a.geom,
b.geom) ASC LIMIT 1)[SAFE_OFFSET(0)] as dist
FROM
table_one a
CROSS JOIN table_two b
GROUP BY a.id
8.6 计算相邻多边形中值的平均值
对于每个多边形,找到其相邻多边形的平均值(或其他聚合值)。
# BigQuery
WITH
one AS (
SELECT
a.name,
SUM(b.value) AS total
FROM
table a
LEFT JOIN
table_2 b
ON
ST_Touches(b.geom, a.geom)
GROUP BY
a.name)
SELECT
one.*,
b.geom
FROM
one
JOIN
table b
USING
(name)
# PostGIS
SELECT
a.geom,
a.name,
SUM(b.value) as total
FROM
table a
CROSS JOIN LATERAL
(SELECT value
FROM table_two
WHERE
ST_Intersects(geom, a.geom)
) AS b
GROUP BY a.name, a.geom
8.6 将点聚合到 H3 单元格中
# BigQuery
WITH
data AS (
SELECT
`carto-un`.carto.H3_FROMGEOGPOINT(geog, 4) AS h3id,
COUNT(*) AS agg_total
FROM `cartobq.docs.starbucks_locations_usa`
GROUP BY h3id
)
SELECT
h3id,
agg_total,
`carto-un`.carto.H3_BOUNDARY(h3id) AS geom
FROM
data
8.7 逐行排列最近的 N 个邻居
对于一个表中的每一行,创建一组新的 N 行,其中 N 个最近的邻居具有排名。 简而言之,评估表中的每一行都将根据你要查找的邻居数量返回 N 个新行。
# PostGIS
SELECT
a.id,
t.geom,
ST_Distance(t.geom, a.geom) as closest_dist
FROM a
CROSS JOIN LATERAL (
SELECT *,
ROW_NUMBER() OVER(ORDER BY a.geom <-> geom) AS rank
FROM seattle_parks
LIMIT 5
) as t
# BigQuery
SELECT
b.id,
a.station_id,
a.rank,
a.name,
a.dist
FROM
table b
LEFT JOIN (
SELECT
b.station_id,
b.name,
ST_Distance(c.geom, b.geom) AS dist,
b.id,
ROW_NUMBER() OVER (PARTITION BY c.id ORDER BY ST_Distance(c.geom, b.geom) ASC) AS rank
FROM
table b,
table_2 h ) a
USING
(id)
WHERE
rank < 4
原文链接:Top 31 Spatial SQL用例 — BimAnt