我在BigQuery中使用ST_MAKEPOLYGON函数,如下所示:
with data AS (
SELECT
61680 AS id, 139.74862575531006 AS lon,
35.674973127377314 AS lat union all
SELECT
61680,
139.75087881088257,
35.673909836018375 union all
SELECT
61680,
139.747037887573,
35.6765767531247 union all
SELECT
61680,
139.75308895111,
35.6813525780394 union all
SELECT
61680,
139.747509956359,
35.6798884869144 union all
SELECT
61680,
139.754590988159,
35.6799930657428 union all
SELECT
61680,
139.754977226257,
35.6762281415729 union all
SELECT
61680,
139.750170707702,
35.6815268728124 union all
SELECT
61680,
139.755363464355,
35.6782500673754
)
SELECT
ST_makepolygon(ST_MAKELINE(ARRAY_AGG(st_geogpoint(lon,
lat)))) AS valid
FROM
`w_nagakawa.geo_test`
GROUP BY
id
我得到像这样的错误:
Error: ST_MakePolygon failed: Invalid polygon loop: Edge 3 has duplicate vertex with edge 10
ST_MAKEPOLYGON中的地理参数是可以的,所有的纬度似乎都不同。
我想知道为什么会发生,并想知道一些解决办法。
谢谢。
最佳答案
第一拳问题……
我想知道为什么会发生……
将ST_MAKEPOLYGON与线串输入配合使用(通过ST_MAKELINE)需要正确地组装线,以确保数据不会发生交集(因为线是使用出现的[random]顺序的点构建的)
取而代之的是,您将需要下面的蓝色线条-所有的地理点都经过排序,从而形成非自交叉的线
注意:线串必须关闭:也就是说,第一个和最后一个顶点必须相同。如果第一个和最后一个顶点不同,则该函数会构造从第一个顶点到最后一个顶点的最终边。
使用“ proper_line”构建多边形将完美工作并产生以下结果
现在的第二个问题……
…,并想知道一些解决方案
因此,显然,我们需要以某种方式正确地订购地理位置
这可以手动完成(使用此选项很有趣),也可以通过编程方式完成
以下是在BigQuery(Standard SQL)中如何执行此操作的想法以及实现的详细信息
因此,我们要通过以下步骤为每个点分配适当的序列号:
步骤1 –让我们确定所有点(红色图钉)的质心(下图中的绿色图钉)
我们可以使用以下语句:
SELECT ST_CENTROID(ST_UNION_AGG(ST_GEOGPOINT(lon, lat))) centroid FROM `data`
步骤2-然后,对于每个点,我们应该计算质心点线与水平线相交质心之间的角度
我们正在使用锚点(图像上的蓝色圆圈)
WITH stats AS (
SELECT ST_CENTROID(ST_UNION_AGG(ST_GEOGPOINT(lon, lat))) centroid FROM `data`
)
SELECT point, centroid, anchor,
ACOS(ST_DISTANCE(centroid, anchor) / ST_DISTANCE(centroid, point)) angle
FROM (
SELECT centroid,
ST_GEOGPOINT(lon, lat) point,
ST_GEOGPOINT(lon, ST_Y(centroid)) anchor
FROM `data`, stats
)
步骤3-现在我们要将这些角度转换为反映各个点象限的适当顺序
SELECT point, centroid, anchor,
CASE
WHEN ST_X(point) > ST_X(centroid) AND ST_Y(point) > ST_Y(centroid) THEN 3.14 - angle
WHEN ST_X(point) > ST_X(centroid) AND ST_Y(point) < ST_Y(centroid) THEN 3.14 + angle
WHEN ST_X(point) < ST_X(centroid) AND ST_Y(point) < ST_Y(centroid) THEN 6.28 - angle
ELSE angle
END sequence
FROM (.. previous subquery here …)
第4步-现在,最后,我们可以使用序列列对点进行正确排序
最终查询如下:
WITH `data` AS (
SELECT 61680 AS id, 139.74862575531006 AS lon, 35.674973127377314 AS lat UNION ALL SELECT 61680, 139.75087881088257, 35.673909836018375 UNION ALL SELECT 61680, 139.747037887573, 35.6765767531247 UNION ALL SELECT 61680, 139.75308895111, 35.6813525780394 UNION ALL SELECT 61680, 139.747509956359, 35.6798884869144 UNION ALL SELECT 61680, 139.754590988159, 35.6799930657428 UNION ALL SELECT 61680, 139.754977226257, 35.6762281415729 UNION ALL SELECT 61680, 139.750170707702, 35.6815268728124 UNION ALL SELECT 61680, 139.755363464355, 35.6782500673754
), stats AS (
SELECT ST_CENTROID(ST_UNION_AGG(ST_GEOGPOINT(lon, lat))) centroid FROM `data`
)
SELECT ST_MAKEPOLYGON(ST_MAKELINE(ARRAY_AGG(point ORDER BY sequence))) AS polygon
FROM (
SELECT point,
CASE
WHEN ST_X(point) > ST_X(centroid) AND ST_Y(point) > ST_Y(centroid) THEN 3.14 - angle
WHEN ST_X(point) > ST_X(centroid) AND ST_Y(point) < ST_Y(centroid) THEN 3.14 + angle
WHEN ST_X(point) < ST_X(centroid) AND ST_Y(point) < ST_Y(centroid) THEN 6.28 - angle
ELSE angle
END sequence
FROM (
SELECT point, centroid,
ACOS(ST_DISTANCE(centroid, anchor) / ST_DISTANCE(centroid, point)) angle
FROM (
SELECT centroid,
ST_GEOGPOINT(lon, lat) point,
ST_GEOGPOINT(lon, ST_Y(centroid)) anchor
FROM `data`, stats
)
)
)
最终结果是:
注意:这种想法/解决方案-仍然只能限于像您这样的明显案例-我没有机会针对一般案例进行探索和/或测试