PostGIS'de başını sallamayan bir kavşak problemini nasıl çözebilirim?


38

Bir PL/Rişlev kullanıyorum ve PostGISbir dizi nokta etrafında voronoi çokgenleri oluşturmak için. Kullandığım fonksiyon burada tanımlanmıştır . Bu işlevi belirli bir veri kümesinde kullandığımda aşağıdaki hata iletisini alıyorum:

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

Hata mesajının bu bölümünü incelemeden:

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

Yukarıda listelenen sorunun neye benzediği:

görüntü tanımını buraya girin

Başlangıçta bu mesajın aynı noktaların varlığından kaynaklanabileceğini düşündüm ve bunu st_translate()şu şekilde kullanılan işlevi kullanarak çözmeye çalıştım :

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

Bu sorunu çözdü, ama benim endişem şu anda x / y yönünde tüm noktaları ~ 20m'ye çeviriyorum. Ayrıca uygun bir çeviri miktarının gerekli olduğunu da söyleyemem. Örneğin, bu veri setinde deneme yanılma yoluyla a 20m * random numbertamam, ama bunun daha büyük olması gerektiğini nasıl anlayabilirim?

Yukarıdaki resme dayanarak problemin, algoritma noktayı bir poligonla kesmeye çalışırken, noktanın çizgiyle kesiştiğini düşünüyorum. Noktanın bir çizgiyle kesişmek yerine çokgen içinde olmasını sağlamak için ne yapmam gerektiğinden emin değilim. Hata bu satırda meydana geliyor:

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

Bu önceki soruyu okudum, "başını sallamayan bir kavşak" nedir? Bu sorunu daha iyi anlamaya çalışmak ve bu sorunun en iyi nasıl çözülebileceğine dair tavsiyelerde bulunmaktan memnuniyet duyarız.


Girişleriniz başlamak için geçerli değilse, ST_MakeValid () işlevini çalıştırın. Eğer geçerliyse, yaptığınız gibi entropi eklemek, bir sonraki numaradır ve belki de şu an için son numaradır.
Paul Ramsey

Evet, WHERE ST_IsValid(p.geom)başlangıçta noktaları filtrelemek için kullanıyorum .
djq

Yanıtlar:


30

Tecrübelerime göre bu problem hemen hemen her zaman neden olur:

  1. Koordinatlarınızda (43.23149999999999996) yüksek hassasiyet,
  2. Neredeyse çakışan ancak aynı olmayan çizgiler

ST_BufferÇözümlerin "dürtmek" yaklaşımı # 2 ile kurtulmanıza izin verir, ancak bu temel sebepleri çözmek için yapabileceğiniz her şey, geometrinizi 1e-6 ızgaraya yapıştırmak gibi, hayatınızı kolaylaştıracak. Tamponlanmış geometriler, üst üste binme alanı gibi ara hesaplamalar için genellikle iyidir, ancak yakın çekimde olmama sorunlarınızı uzun vadede daha da kötüleştirebileceklerinden, onları koruma konusunda dikkatli olmak istersiniz.

PostgreSQL'in istisna işleme yeteneği, bu özel durumları ele almak için sarmalayıcı işlevleri yazmanıza izin verir, yalnızca gerektiğinde arabelleğe alma. İşte bir örnek ST_Intersection; Benzer bir işlev kullanıyorum ST_Difference. Boş bir poligonun tamponlanmasının ve potansiyel geri dönüşünün sizin durumunuzda kabul edilebilir olup olmadığına karar vermeniz gerekir.

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;

Bu yaklaşımın bir diğer avantajı, gerçekten problemlerinize neden olan geometrileri tam olarak belirlemenizdir; WKT'yi ya da sorunu izlemenize yardımcı olacak başka bir şeyi çıktılamak RAISE NOTICEiçin EXCEPTIONblokta bazı ifadeler eklemeniz yeterlidir.


Bu akıllıca bir fikir. Kesişme sorunlarının, sendikalar, farklılıklar, tamponlar, vb. Kombinasyonları sırasında ortaya çıkan ve her şeyi tamponlayarak ya da her şeyi attırarak ve sadece Çokgenler / Mutlipolygons'u seçerek düzeltilebilecek bağlantılardan kaynaklandığını sıklıkla buldum. Bu ilginç bir yaklaşım.
John Powell

Geometriyi 1e-6 ızgaraya yapışmasından bahsediyorsunuz, ama ben burada oturuyorum, 2 gücüne yapışmanın daha iyi olup olmadığını merak ediyorum. PostGIS (ve GEOS) kayan nokta sayıları kullanır, bu nedenle 10'luk bir güce yapıştırmak, sayının sonlu bir ikili ikili gösterime sahip olamayacağından koordinatları çok fazla kesmeyebilir. Ancak 2 ^ -16 diyebilirseniz, bunun kesirli bir parçanın sadece 2 bayta kesilmesi garanti edileceğine inanıyorum. Yoksa yanlış mı düşünüyorum?
jpmc26

12

Çok fazla deneme yanılma non-noded intersectionsonucu, sonunda bir kesişme probleminden kaynaklandığını anladım . ST_buffer(geom, 0)Sorunu çözmek için kullanılabileceğini öneren bir iş parçacığı buldum (genel olarak daha yavaş olmasına rağmen). Daha sonra ST_MakeValid()diğer fonksiyonlardan önce doğrudan geometriye uygulandığında kullanmaya çalıştım . Bu sorunu sağlamca çözüyor gibi görünüyor.

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

Bunu, sorunumu çözen tek yaklaşım gibi göründüğü için cevap olarak işaretledim.


11

Aynı problemle karşılaştım (Postgres 9.1.4, PostGIS 2.1.1) ve benim için çalışan tek şey geometriyi çok küçük bir tamponla sarmaktı.

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

ST_MakeValidBenim için çalışmak, ne de bir arada yaptı vermedi ST_Nodeve ST_Dump. Arabellek, performansta herhangi bir bozulmaya yol açmamış gibi görünüyordu, ancak daha küçük bir hale getirdiysem hala başını sallamayan bir kesişim hatası alıyorum.

Çirkin, ama işe yarıyor.

Güncelleme:

ST_Buffer stratejisi iyi çalışıyor gibi görünüyor, ancak geometriyi coğrafyaya dökerken hatalar ürettiği bir sorunla karşılaştım. Örneğin, bir nokta başlangıçta -90.0’da ise ve 0.0000001 tarafından arabelleğe alınmışsa, şimdi geçersiz bir coğrafya olan -90.0000001’dedir.

Bu halde anlamına geliyordu ST_IsValid(geom)oldu t, ST_Area(geom::geography)döndü NaNbirçok özellik için.

Geçerli coğrafya korurken olmayan düğümlüye kavşak sorunu önlemek için, ben kullanarak sona erdi ST_SnapToGridşöyle

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

6

PostGIS'te ST_Node , kesişmemiş noktalardaki kesişmemiş problemi çözmesi gereken bir dizi çizgiyi kırmalıdır . Bunu ST_Dump içine sarmak , kesikli çizgilerin birleşik dizisini oluşturur.

Biraz ilişkili, harika bir sunum var PostGIS: Bu tür sorunları ve çözümleri açıkça belirten İleri Düzey Kullanıcılar için İpuçları .


Bu harika bir sunum (teşekkürler @ PaulRamsey). Nasıl kullanmalıyım ST_Nodeve ST_Dump? Onları fonksiyonun bu kısmına yakın bir yerde kullanmam gerektiğini düşünmüştüm, ancak kesin değil: st_intersection(''SRID=''||st_srid(%2$s)||'';%4$s''::text,''%5$s'')in
djq

Hmmm İki çizginin özdeş bir koordinatta olduğunu fark etmedim, bu iyi olmalı. Bu koordinatları çizerseniz, kavşak noktası kavşaktan yaklaşık 18 cm uzakta olur. Gerçekten bir çözüm değil, sadece bir gözlem.
WolfOdrade

st_nodeBurada nasıl kullandığım konusunda tamamen net değil - daha önce kullanabilir miyim st_intersection?
djq

1
Sunum artık mevcut değil. ST_Clip (rast, polygon) 'ı denediğimde aynı sorunla sıkışıp kaldım
Jackie

1
@Jackie: Cevaptaki sunumun bağlantısını düzelttim: PostGIS: İleri Düzey Kullanıcılar için İpuçları .
Pete,

1

Tecrübelerime göre, poligonun tepe noktasının koordinatlarında yüksek hassasiyete sahip olma problemini çözen St_SnapToGrid işlevini non-noded intersectionkullanarak hatamı çözdüm .

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';
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.