Cách tốt nhất để khắc phục sự cố giao lộ không gật đầu trong PostGIS?


38

Tôi đang sử dụng một PL/Rhàm và PostGISđể tạo các đa giác voronoi xung quanh một tập hợp các điểm. Hàm mà tôi đang sử dụng được định nghĩa ở đây . Khi tôi sử dụng chức năng này trên một tập dữ liệu cụ thể, tôi nhận được thông báo lỗi sau:

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

Từ việc kiểm tra phần này của thông báo lỗi:

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

Đây là những gì vấn đề được liệt kê ở trên trông giống như:

nhập mô tả hình ảnh ở đây

Ban đầu tôi nghĩ rằng thông điệp này có thể là do sự tồn tại của các điểm giống hệt nhau và đã cố gắng giải quyết vấn đề này bằng cách sử dụng st_translate()hàm, được sử dụng theo cách sau:

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

Điều này không khắc phục được vấn đề, nhưng mối quan tâm của tôi là hiện tại tôi đang dịch tất cả các điểm lên tới ~ 20m theo hướng x / y. Tôi cũng không thể biết số lượng dịch phù hợp là cần thiết. Ví dụ, trong bộ dữ liệu này thông qua bản dùng thử và lỗi a 20m * random numberlà ok, nhưng làm thế nào tôi có thể biết nếu cái này cần phải lớn hơn?

Dựa vào hình ảnh trên tôi nghĩ vấn đề là điểm đang giao nhau với đường thẳng trong khi thuật toán đang cố gắng giao điểm với đa giác. Tôi không chắc mình nên làm gì để đảm bảo rằng điểm nằm trong đa giác, thay vì giao nhau với một đường. Lỗi xảy ra trên dòng này:

"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'');"

Tôi đã đọc qua câu hỏi trước đó, "Giao lộ không gật đầu" là gì? để cố gắng hiểu rõ hơn vấn đề này và sẽ đánh giá cao bất kỳ lời khuyên nào về cách tốt nhất để giải quyết nó.


Nếu đầu vào của bạn không hợp lệ để bắt đầu, hãy chạy ST_MakeValid () trên chúng. Nếu chúng hợp lệ, thêm entropy, như bạn đang làm, là thủ thuật tiếp theo có sẵn và có lẽ là mẹo cuối cùng cho đến bây giờ.
Paul Ramsey

Có, tôi đang sử dụng WHERE ST_IsValid(p.geom)để lọc các điểm ban đầu.
djq

Câu trả lời:


30

Theo kinh nghiệm của tôi, vấn đề này gần như luôn luôn gây ra bởi:

  1. Độ chính xác cao trong tọa độ của bạn (43.231499999999996), kết hợp với
  2. Các đường gần như trùng khớp nhưng không giống nhau

Cách tiếp cận "nâng niu" của các ST_Buffergiải pháp cho phép bạn thoát khỏi # 2, nhưng bất cứ điều gì bạn có thể làm để giải quyết các nguyên nhân cơ bản này, như đưa hình học của bạn vào lưới 1e-6, sẽ giúp cuộc sống của bạn dễ dàng hơn. Các hình học được đệm thường tốt cho các tính toán trung gian như vùng chồng lấp, nhưng bạn sẽ muốn cẩn thận trong việc giữ lại chúng vì chúng có thể làm cho các vấn đề gần nhưng không hoàn toàn của bạn trở nên tồi tệ hơn trong một đoạn đường dài.

Khả năng xử lý ngoại lệ của PostgreSQL cho phép bạn viết các hàm bao bọc để xử lý các trường hợp đặc biệt này, chỉ đệm khi cần. Đây là một ví dụ cho ST_Intersection; Tôi sử dụng một chức năng tương tự cho ST_Difference. Bạn sẽ cần phải quyết định xem liệu bộ đệm và lợi nhuận tiềm năng của một đa giác trống có được chấp nhận trong tình huống của bạn hay không.

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;

Một lợi ích khác với phương pháp này là bạn có thể xác định chính xác các hình học thực sự gây ra vấn đề của bạn; chỉ cần thêm một số RAISE NOTICEcâu lệnh trong EXCEPTIONkhối để xuất WKT hoặc một cái gì đó khác sẽ giúp bạn theo dõi vấn đề.


Đó là một ý tưởng thông minh. Tôi thường thấy rằng các vấn đề giao nhau xuất phát từ các linestrings xuất hiện trong các kết hợp của liên hiệp, khác biệt, bộ đệm, v.v., có thể được khắc phục bằng cách đệm mọi thứ hoặc bỏ mọi thứ và chỉ chọn Đa giác / Mutlipolygons. Đây là một cách tiếp cận thú vị.
John Powell

Bạn đề cập đến việc chụp hình học vào lưới 1e-6, nhưng tôi đang ngồi đây tự hỏi liệu chụp theo công suất 2 sẽ tốt hơn. PostGIS (và GEOS) sử dụng các số dấu phẩy động, do đó, việc nắm bắt công suất 10 có thể không thực sự cắt các tọa độ rất nhiều vì số này có thể không có biểu diễn nhị phân có độ dài hữu hạn. Nhưng nếu bạn chụp nhanh để nói 2 ^ -16, tôi tin rằng điều đó sẽ được đảm bảo để cắt bớt bất kỳ phần phân số nào chỉ còn 2 byte. Hay tôi đang nghĩ sai?
jpmc26

12

Thông qua rất nhiều thử nghiệm và sai sót, cuối cùng tôi nhận ra rằng non-noded intersectionkết quả từ một vấn đề tự giao nhau. Tôi tìm thấy một chủ đề mà đề xuất sử dụng ST_buffer(geom, 0)có thể được sử dụng để khắc phục vấn đề (mặc dù nó làm cho nó chậm hơn rất nhiều về tổng thể). Sau đó tôi đã cố gắng sử dụng ST_MakeValid()và khi áp dụng trực tiếp vào hình học trước bất kỳ chức năng nào khác. Điều này dường như để khắc phục vấn đề mạnh mẽ.

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

Tôi đã đánh dấu đây là câu trả lời vì dường như đó là cách tiếp cận duy nhất khắc phục vấn đề của tôi.


11

Tôi gặp vấn đề tương tự (Postgres 9.1.4, PostGIS 2.1.1) và điều duy nhất có hiệu quả với tôi là bọc hình học bằng một bộ đệm rất nhỏ.

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

ST_MakeValidkhông làm việc cho tôi, cũng không kết hợp ST_NodeST_Dump. Bộ đệm dường như không dẫn đến bất kỳ sự suy giảm nào về hiệu suất, nhưng nếu tôi làm cho nó nhỏ hơn, tôi vẫn nhận được một lỗi giao cắt không gật đầu.

Xấu xí, nhưng nó hoạt động.

Cập nhật:

Chiến lược ST_Buffer có vẻ hoạt động tốt, nhưng tôi gặp phải một vấn đề trong đó nó tạo ra lỗi khi chuyển hình học sang địa lý. Ví dụ: nếu một điểm ban đầu là -90,0 và được đệm bởi 0,0000001, thì bây giờ là -90.0000001, đó là một địa lý không hợp lệ.

Điều này có nghĩa rằng mặc dù ST_IsValid(geom)t, ST_Area(geom::geography)trở lại NaNđối với nhiều tính năng.

Để tránh vấn đề giao lộ không gật đầu, trong khi duy trì địa lý hợp lệ, tôi đã kết thúc bằng cách sử dụng ST_SnapToGridnhư vậy

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

6

Trong postgis, ST_Node sẽ phá vỡ một loạt các đường tại các giao lộ, điều này sẽ giải quyết vấn đề giao cắt không gật đầu. Việc gói này trong ST_Dump tạo ra mảng tổng hợp của các dòng bị hỏng.

Liên quan một chút, có một bài thuyết trình tuyệt vời PostGIS: Lời khuyên cho người dùng quyền lực trong đó nêu rõ các loại vấn đề và giải pháp.


Đó là một bài thuyết trình tuyệt vời (cảm ơn @PaulRamsey). Tôi nên sử dụng như thế nào ST_NodeST_Dump? Tôi tưởng tượng rằng tôi sẽ cần sử dụng chúng gần phần này của chức năng, nhưng không chắc chắn: st_intersection(''SRID=''||st_srid(%2$s)||'';%4$s''::text,''%5$s'')trong
djq

Hmmm tôi đã không nhận thấy rằng hai dòng có tọa độ giống hệt nhau, sẽ ổn thôi. Nếu bạn vẽ các tọa độ đó, điểm giao nhau cách giao lộ khoảng 18cm. Không thực sự là một giải pháp, chỉ là một quan sát.
WolfOdrade

Không hoàn toàn rõ ràng về cách tôi sử dụng st_nodeở đây - tôi có thể sử dụng nó trước đây st_intersectionkhông?
djq

1
Bài thuyết trình không còn nữa. Tôi bị mắc kẹt với cùng một vấn đề, khi cố gắng ST_Clip (rast, đa giác)
Jackie

1
@Jackie: Tôi đã sửa liên kết đến bản trình bày trong câu trả lời: PostGIS: Mẹo dành cho người dùng quyền lực .
Pete

1

Theo kinh nghiệm của tôi, tôi đã giải quyết non-noded intersectionlỗi của mình bằng cách sử dụng hàm St_SnapToGrid để giải quyết vấn đề có độ chính xác cao trong tọa độ đỉnh của đa giác.

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';
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.