Как отловить ошибку ST_MAKEPOLYGON в BigQuery - PullRequest
0 голосов
/ 25 января 2019

Я использую функцию ST_MAKEPOLYGON в BigQuery следующим образом:

  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 в порядке, и все латинские, похоже, разные.

Я хотел бы знать, почему это происходит, и хотел бы знать некоторые идеи, чтобы решить эту проблему.

Спасибо.

Ответы [ 2 ]

0 голосов
/ 26 января 2019

Первый вопрос первый ...

Я хотел бы знать, почему это происходит ...

Использование ST_MAKEPOLYGON с вводом линейных строк (через ST_MAKELINE) требует, чтобы линия была правильно собрана, чтобы не было пересечений с вашими данными (поскольку линия строится с использованием точек в [случайном] порядке появления)

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

Примечание: эта идея / решение - все еще может быть ограничено только некоторыми очевидными случаями, подобными вашему - у меня не было возможности исследовать и / или проверить его для общих случаев

0 голосов
/ 25 января 2019

Ваш полигон неверно отформатирован (возможно, точки расположены неверно).Я попытался визуализировать многоугольник с точками и в указанном вами порядке, он показывает сетку ребер.Поэтому я упорядочил точки так, чтобы имело смысл быть многоугольником.После этого запрос сработал.Порядок, который я протестировал, приведен ниже (это те же точки, только что упорядоченные по-разному):

 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 проверяет полигоны перед загрузкой или генерацией.

...