如何在BigQuery中捕获ST_MAKEPOLYGON错误

时间:2019-01-25 08:01:00

标签: google-bigquery postgis

我在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中的地理参数可以,而且所有纬度似乎都不同。

我想知道为什么会发生这种情况,并想知道一些解决办法。

谢谢。

3 个答案:

答案 0 :(得分:3)

第一个拳头问题……

  

我想知道为什么会发生……

   

将ST_MAKEPOLYGON与线串输入配合使用(通过ST_MAKELINE)要求正确地组装线,这样就不会与数据发生交集(因为线是使用出现的[random]顺序的点构建的)

enter image description here

相反,您需要在蓝色中使用如下所示的线-在其中所有地理点都经过排序,从而形成非自交叉线

enter image description here

注意:必须关闭线串:也就是说,第一个和最后一个顶点必须相同。如果第一个和最后一个顶点不同,则该函数将构造从第一个顶点到最后一个顶点的最终边缘。

使用“ proper_line”构建多边形会完美地工作并产生以下结果

enter image description here

第二个问题……

  

...,并且想知道一些解决此问题的方法

因此,显然,我们需要以某种方式正确订购地理点
可以手动完成(使用此选项很有趣),也可以通过编程方式完成
下面是在BigQuery(Standard SQL)中如何执行此操作的想法以及实现的详细信息

因此,我们要通过以下步骤为每个点分配适当的序列号:

步骤1 –让我们确定所有点(红色引脚)的质心(下图中的绿色引脚)

enter image description here

我们可以使用以下语句:

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
    )
  )
)

最终结果是:

enter image description here

注意:这种想法/解决方案-仍然只能局限于像您这样的明显案例-我没有机会针对一般案例进行探索和/或测试

答案 1 :(得分:1)

您的多边形格式错误(可能点的顺序错误)。我试图以点和您提供的顺序可视化多边形,它显示了边缘网格。因此,我以有意义的方式将点排序为多边形。之后,查询开始工作。我测试的顺序如下(它们是相同的点,只是以不同的方式排序):

 with data AS (
  SELECT
    61680 AS id, 139.75087881088257 AS lon,
    35.673909836018375 AS lat union all
  SELECT
    61680,
    139.74862575531006,
    35.674973127377314 union all
  SELECT
    61680,
    139.747037887573,
    35.6765767531247 union all
  SELECT
    61680,
    139.747509956359,
    35.6798884869144 union all
  SELECT
    61680,
    139.750170707702,
    35.6815268728124 union all
  SELECT
    61680,
    139.75308895111,
    35.6813525780394 union all
  SELECT
    61680,
    139.754590988159,
    35.6799930657428 union all
  SELECT
    61680,
    139.755363464355,
    35.6782500673754 union all
  SELECT
    61680,
    139.754977226257,
    35.6762281415729
    )
SELECT
  ST_makepolygon(ST_MAKELINE(ARRAY_AGG(st_geogpoint(lon,
          lat)))) AS valid
FROM
  data
GROUP BY
  id

这是因为BigQuery在加载或生成多边形之前先对其进行验证。

答案 2 :(得分:1)

以下是用于BigQuery标准SQL

感谢惊人的BigQuery小组,通过引入ST_CONVEXHULL解决此类问题变得非常简单

#standardSQL
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_CONVEXHULL(ST_UNION_AGG(ST_GEOGPOINT(lon, lat))) AS valid
FROM `data`

结果显示如下

enter image description here