Як найкраще виправити проблему перехрестя без вузла в PostGIS?


38

Я використовую PL/Rфункцію і PostGISгенерую багатокутники вороного навколо набору точок. Функція , яку я використовую визначається тут . Коли я використовую цю функцію на певному наборі даних, я отримую таке повідомлення про помилку:

Error : ERROR:  R interpreter expression evaluation error
DETAIL:  Error in pg.spi.exec(sprintf("SELECT %3$s AS id,   
st_intersection('SRID='||st_srid(%2$s)||';%4$s'::text,'%5$s') 
AS polygon FROM %1$s WHERE st_intersects(%2$s::text,'SRID='||st_srid(%2$s)||';%4$s');",  
:error in SQL statement : Error performing intersection: TopologyException: found non-noded 
intersection between LINESTRING (571304 310990, 568465 264611) and LINESTRING (568465 
264611, 594406 286813) at 568465.05533706467 264610.82749605528
CONTEXT:  In R support function pg.spi.exec In PL/R function r_voronoi

Розглянувши цю частину повідомлення про помилку:

Error performing intersection: TopologyException: found non-noded intersection between
LINESTRING (571304 310990, 568465 264611) and LINESTRING (568465 264611, 594406 286813) 
at 568465.05533706467 264610.82749605528

Ось як виглядає перерахована вище проблема:

введіть тут опис зображення

Я спочатку подумав , що це повідомлення може бути викликано наявністю ідентичних точок, і намагалися вирішити цю проблему з допомогою st_translate()функції, використовуваної в такий спосіб:

ST_Translate(geom, random()*20, random()*20) as geom 

Це все-таки вирішує проблему, але я хвилююсь, що зараз я перекладаю всі точки до ~ 20 м у напрямку x / y. Я також не можу сказати, яка потрібна сума перекладу. Наприклад, у цьому наборі даних через пробну помилку і помилку a 20m * random numberце нормально, але як я можу сказати, чи потрібно цього більше?

Виходячи із зображення, поданого вище, я думаю, що проблема полягає в тому, що точка перетинається з лінією, а алгоритм намагається перетинати точку з багатокутником. Я не впевнений, що мені робити, щоб точка знаходилася в полігоні, а не перетиналася з лінією. Помилка трапляється в цьому рядку:

"SELECT 
  %3$s AS id, 
  st_intersection(''SRID=''||st_srid(%2$s)||'';%4$s''::text,''%5$s'') AS polygon 
FROM 
  %1$s 
WHERE 
  st_intersects(%2$s::text,''SRID=''||st_srid(%2$s)||'';%4$s'');"

Я прочитав це попереднє запитання, що таке "перехрестя без кивок"? щоб спробувати краще зрозуміти цю проблему, і був би вдячний за будь-яку пораду, як найкраще її вирішити.


Якщо ваші входи невірні для початку, запустіть на них ST_MakeValid (). Якщо вони дійсні, додавання ентропії, як ви робите, - це наступний трюк, і, можливо, останній фокус на даний момент.
Пол Рамзі

Так, я WHERE ST_IsValid(p.geom)спочатку використовую для фільтрування очок.
djq

Відповіді:


30

На мій досвід, ця проблема майже завжди викликана:

  1. Висока точність у ваших координатах (43.231499999999996) у поєднанні з
  2. Рядки, які майже збігаються, але не тотожні

Підхід «підштовхування» ST_Bufferрішень дозволяє вам піти з №2, але все, що ви можете зробити, щоб вирішити ці основні причини, як-от прив’язання геометрії до сітки 1e-6, полегшить ваше життя. Захищені геометрії зазвичай добре підходять для проміжних обчислень, таких як область перекриття, але ви хочете бути обережними щодо їх збереження, оскільки вони можуть погіршити ваші близькі, але не зовсім проблеми, на довгому відстані.

Можливість обробки винятків PostgreSQL дозволяє писати функції обгортки для обробки цих спеціальних випадків, буферизуючи лише за потреби. Ось приклад для ST_Intersection; Я використовую аналогічну функцію для ST_Difference. Вам потрібно буде вирішити, чи буферизація та потенційне повернення порожнього багатокутника прийнятні у вашій ситуації.

CREATE OR REPLACE FUNCTION safe_isect(geom_a geometry, geom_b geometry)
RETURNS geometry AS
$$
BEGIN
    RETURN ST_Intersection(geom_a, geom_b);
    EXCEPTION
        WHEN OTHERS THEN
            BEGIN
                RETURN ST_Intersection(ST_Buffer(geom_a, 0.0000001), ST_Buffer(geom_b, 0.0000001));
                EXCEPTION
                    WHEN OTHERS THEN
                        RETURN ST_GeomFromText('POLYGON EMPTY');
    END;
END
$$
LANGUAGE 'plpgsql' STABLE STRICT;

Ще одна перевага при такому підході полягає в тому, що ви можете точно визначити геометрії, які насправді викликають ваші проблеми; просто додайте RAISE NOTICEв EXCEPTIONблок деякі заяви, щоб вивести WKT або щось інше, що допоможе вам відстежити проблему.


Це розумна ідея. Я часто виявляв, що проблеми перетину виникають із рядків рядків, що виникають під час комбінацій об'єднань, відмінностей, буферів тощо, які можна виправити буферизацією всього або скиданням всього і вибору лише полігонів / мутліполігонів. Це цікавий підхід.
Джон Пауелл

Ви згадуєте прив’язати геометрію до сітки 1e-6, але я сиджу тут, цікавлячись, чи краще прив’язання до потужності 2. PostGIS (і GEOS), використовуючи числа з плаваючою комою, тому прив'язка до потужності 10 може насправді не дуже сильно обрізати координати, оскільки число може не мати двійкового подання кінцевої довжини. Але якщо ви скажете сказати 2 ^ -16, я вважаю, що було б гарантовано обрізати будь-яку дробову частину лише в 2 байти. Або я неправильно думаю?
jpmc26

12

Через багато спроб та помилок я врешті зрозумів, що це non-noded intersectionрезультат проблеми самоперетину. Я знайшов тему, яку запропоноване використання ST_buffer(geom, 0)можна використовувати для вирішення проблеми (хоча це робить її набагато повільніше загалом). Потім я спробував використовувати ST_MakeValid()і коли застосовувався безпосередньо до геометрії перед будь-якою іншою функцією. Це, здається, вирішує проблему настійно.

ipoint <- pg.spi.exec(
        sprintf(
            "SELECT 
                    %3$s AS id, 
                    st_intersection(ST_MakeValid(''SRID=''||st_srid(%2$s)||'';%4$s''::text), ST_MakeValid(''%5$s'', 0)) AS polygon 
            FROM %1$s 
            WHERE 
                ST_Intersects(ST_MakeValid(%2$s::text),ST_MakeValid(''SRID=''||st_srid(%2$s)||'';%4$s''));",
            arg1,
            arg2,
            arg3,
            curpoly,
            buffer_set$ewkb[1]
        )
    )

Я позначив це як відповідь, оскільки, здається, це єдиний підхід, який вирішує мою проблему.


11

Я зіткнувся з цією ж проблемою (Postgres 9.1.4, PostGIS 2.1.1), і єдине, що працювало для мене, - це обернути геометрію дуже маленьким буфером.

SELECT ST_Intersection(
    (SELECT geom FROM table1), ST_Union(ST_Buffer(geom, 0.0000001))
) FROM table2

ST_MakeValidне працювало для мене, а також поєднання ST_Nodeта ST_Dump. Здається, буфер не призвів до погіршення продуктивності, але якщо я зробив його меншим, я все-таки отримав помилку перетину, що не кивнув.

Некрасиво, але це працює.

Оновлення:

Стратегія ST_Buffer, здається, працює добре, але я зіткнувся з проблемою, де вона створювала помилки під час передачі геометрії географії. Наприклад, якщо точка спочатку становила -90,0, а вона буферизована 0,0000001, вона зараз становить -90,0000001, що є невірною географією.

Це означало, що хоч і ST_IsValid(geom)було t, ST_Area(geom::geography)повернулося NaNза багато функцій.

Щоб уникнути проблеми з незваженим перетином, зберігаючи дійсну географію, я в кінцевому підсумку використовував ST_SnapToGridподібне

SELECT ST_Union(ST_MakeValid(ST_SnapToGrid(geom, 0.0001))) AS geom, common_id
    FROM table
    GROUP BY common_id;

6

У postgis ST_Node повинен перервати ряд ліній на перехрестях, що має вирішити проблему перехрестя, що не кивається. Згортаючи це в ST_Dump, генерується складений масив розбитих ліній.

Трохи пов'язана, є дивовижна презентація PostGIS: Поради для владних користувачів, яка чітко окреслює подібні проблеми та рішення.


Це чудова презентація (спасибі @PaulRamsey). Як я повинен використовувати ST_Nodeта ST_Dump? Я думаю, мені потрібно було б використовувати їх поруч із цією частиною функції, але я не впевнений: st_intersection(''SRID=''||st_srid(%2$s)||'';%4$s''::text,''%5$s'')in
djq

Гммм, я не помітив, що у двох ліній однакова координата, що має бути добре. Якщо ви накреслите ці координати, то точка перетину знаходиться близько 18 см від перетину. Насправді не рішення, а лише спостереження.
WolfOdrade

Не зовсім зрозуміло, як я st_nodeтут користуюся - чи можу я його використовувати раніше st_intersection?
djq

1
Презентація більше не доступна. Я застряг з тією ж проблемою, коли намагаюся ST_Clip (rast, polygon)
Джекі

1
@Jackie: Я зафіксував посилання на презентацію у відповіді: PostGIS: Поради для користувачів енергії .
Піт

1

На моєму досвіді я вирішив свою non-noded intersectionпомилку за допомогою функції St_SnapToGrid, яка вирішила проблему високої точності в координатах вершини багатокутників.

SELECT dissolve.machine, dissolve.geom FROM (
        SELECT machine, (ST_Dump(ST_Union(ST_MakeValid(ST_SnapToGrid(geom,0.000001))))).geom 
        FROM cutover_automatique
        GROUP BY machine) as dissolve
WHERE ST_isvalid(dissolve.geom)='t' AND ST_GeometryType(dissolve.geom) = 'ST_Polygon';
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.