Postgis'te çokgen içinde normal nokta ızgarası nasıl oluşturulur?


30

Bir poligonun içinde, postgis'de x, y aralıklı düzenli bir nokta ızgarası nasıl oluşturulur? Örnek gibi:

alt metin


Kırpma çokgenlerini bu kodu "postGIS in action" dicing koduyla birleştirmeye çalıştım, ancak yalnızca bir çokgen oluşturuldu ... Bir şey mi unuttum? OLUŞTURMA VEYA FONKSİYON FONKSİYONU makegrid (geometri, tamsayı, tamsayı) Geometriyi AS 'SELECT st_intersection (g1.geom1, g2.geom2) OLARAK ASLA geometri FROM (g1.geom1, g2.geom2) ASLA G1 INNER JOIN (st_setsrid (SELECT $ 1 geom1) g1 INNER JOIN AS ((st_setsrid_tr) ST_Point (x, y), 3 $), st_setsrid (ST_Point (x + $ 2, y + 2 2), $ 3)) geometri olarak), 3 $) geom2 olarak FROM construc_series (floor (st_xmin ($ 1)) :: int, tavan ( st_xmax ($ 1)) :: int, 2 $) x olarak, produc_series (floor (st_ymin (1 $))) int, tavan (st_ymax (
aurel_nc

detaylı cevabımı inceleyin.
Muhammed Imran Siddique

Yanıtlar:


29

Oluşturma_series ile bunu.

Kılavuzun nereden başlayıp durduğunu elle yazmak istemiyorsanız, en kolay olanı bir işlev oluşturmaktır.

Aşağıdakileri doğru şekilde test etmedim, ancak çalışması gerektiğini düşünüyorum:

CREATE OR REPLACE FUNCTION makegrid(geometry, integer)
RETURNS geometry AS
'SELECT ST_Collect(ST_POINT(x,y)) FROM 
generate_series(floor(st_xmin($1))::int, ceiling(st_xmax($1)-st_xmin($1))::int, $2) as x
,generate_series(floor(st_ymin($1))::int, ceiling(st_ymax($1)-st_ymin($1))::int,$2) as y 
where st_intersects($1,ST_POINT(x,y))'
LANGUAGE sql

Kullanmak için şunları yapabilirsiniz:

SELECT makegrid(the_geom, 1000) from mytable;

ilk argüman ızgarayı istediğiniz poligon ve ikinci argüman ızgaradaki noktalar arasındaki mesafedir.

Satır başına bir nokta istiyorsanız, sadece ST_Dump gibi kullanın:

SELECT (ST_Dump(makegrid(the_geom, 1000))).geom as the_geom from mytable;

HTH

Nicklas


1
St_pointSRID () işlevini st_point işlevlerine eklemeniz gerekebilir, aksi takdirde st_intersects çalışmıyor.
JaakL

Test edilmiş sürümümü ayrı cevap olarak ekledim.
JaakL

12

Nicklas Avén makegrid fonksiyon kodunu aldım ve poligon geometrisindeki srid'i okuyarak ve kullanarak daha genel hale getirdim. Aksi taktirde tanımlanmış bir dizili çokgen kullanılması bir hata verir.

İşlev:

CREATE OR REPLACE FUNCTION makegrid(geometry, integer)
RETURNS geometry AS
'SELECT ST_Collect(ST_SetSRID(ST_POINT(x,y),ST_SRID($1))) FROM 
generate_series(floor(st_xmin($1))::int, ceiling(st_xmax($1)-st_xmin($1))::int, $2) as x
,generate_series(floor(st_ymin($1))::int, ceiling(st_ymax($1)-st_ymin($1))::int,$2) as y 
where st_intersects($1,ST_SetSRID(ST_POINT(x,y),ST_SRID($1)))'
LANGUAGE sql

Fonksiyonu kullanmak tam olarak Nicklas Avén'in yazdığı gibi yapılır :

SELECT makegrid(the_geom, 1000) from mytable;

veya satır başına bir nokta istiyorsanız:

SELECT (ST_Dump(makegrid(the_geom, 1000))).geom as the_geom from mytable;

Umarım bu birisi için yararlı olur.

Alex


Kabul edilen cevap SRID Hataları nedeniyle verilerimle çalışmıyor. Bu değişiklik daha iyi çalışır.
Vitaly Isaev

Poligon antimerideri geçerken bir şeyler eklemek isteyebilirsiniz? Bunun xmin / xmax ile ilgili bir soruna neden olacağını hayal edebiliyorum.
Thomas

2
Benim için işe yaramadı. Postgres 9.6 ve PostGIS 2.3.3 kullanımı. Generator_series çağrısında bunu, "tavan (st_xmax ($ 1) -st_xmin ($ 1)) :: int" ve "tavan () yerine ikinci" "(tavan (st_xmax ($ 1)) :: int") parametresi olarak koymak zorunda kaldım. st_ymax (1 $)) :: int "yerine" tavan (st_ymax (1 $) -st_ymin (1 $)) :: int "
Vitor Sapucaia

Önceki yorumu onaylıyorum; Oluşturucu dizilerinin üst sınırı, fark tavanı değil (maks - min) tavan olmalıdır.
R. Bourgeon

10

Wgs84 geometrisi kullanan kişiler muhtemelen bu işlevle ilgili sorun yaşarlar.

generate_series(floor(st_xmin($1))::int, ceiling(st_xmax($1))::int,$2) as x
,generate_series(floor(st_ymin($1))::int, ceiling(st_ymax($1))::int,$2) as y 

sadece tamsayıları döndür. Ülkeler gibi çok büyük geometriler dışında (çoklu lat, lng derecelerinde döşeme yapan), bu, geometriyle kesişmeyen hatta çoğu zaman sadece 1 puan toplayacaktır ... => boş sonuç!

Benim sorunum, bu WSG84 gibi kayan sayılar üzerinde ondalık bir mesafe ile kesinlik mesafesini göremiyorum. (WSG84 gibi).

SELECT ST_Collect(st_setsrid(ST_POINT(x/1000000::float,y/1000000::float),st_srid($1))) FROM 
  generate_series(floor(st_xmin($1)*1000000)::int, ceiling(st_xmax($1)*1000000)::int,$2) as x ,
  generate_series(floor(st_ymin($1)*1000000)::int, ceiling(st_ymax($1)*1000000)::int,$2) as y 
WHERE st_intersects($1,ST_SetSRID(ST_POINT(x/1000000::float,y/1000000::float),ST_SRID($1)))

Temel olarak tamamen aynı. İhtiyacım olduğunda oyuna ondalık girmek için sadece 1000000 ile çarpma ve bölme.

Bunu başarmak için kesinlikle daha iyi bir çözüm var. ++


Bu akıllı bir geçici çözüm. Sonuçları kontrol ettin mi? Tutarlılar mı?
Pablo

Merhaba. Evet pablo. Şimdiye dek sonuçlardan memnunum. Yerden göreceli yükseklikte bir çokgen inşa etmem gerekiyordu. (Her ızgara noktası için istediğim rakımı hesaplamak için SRTM kullanıyorum). Şimdi sadece çokgenin çevresine yayılan noktaları dahil etmenin bir yolunu özlüyorum. Şu anda işlenen şekil kenarda bir miktar kesildi.
Julien Garcia,

Diğerlerinin çözümleri başarısız olduğunda çalıştı, teşekkürler!
Jordan Arseno

7

Bu algoritma iyi olmalı:

createGridInPolygon(polygon, resolution) {
    for(x=polygon.xmin; x<polygon.xmax; x+=resolution) {
       for(y=polygon.ymin; y<polygon.ymax; y+=resolution) {
          if(polygon.contains(x,y)) createPoint(x,y);
       }
    }
}

'Çokgen' çokgen ve 'Çözünürlük' gerekli ızgara çözünürlüğüdür.

PostGIS'te uygulamak için aşağıdaki işlevler gerekebilir:

İyi şanslar!


1
Büyük karmaşık çokgen alanlarınız varsa (örneğin, sahil şeridi tamponu varsa), bu yaklaşımın oldukça uygun olmadığını unutmayın.
JaakL

Öyleyse bunun yerine ne öneriyorsun?
julien

4

Farklı yöntemler kullanarak üç algoritma.

Github Repo Bağlantısı

  1. X ve y yönünden koordinatların gerçek dünya mesafesini kullanan basit ve en iyi yaklaşım. Algoritma herhangi bir SRID ile çalışır, dahili olarak WGS 1984 (EPSG: 4326) ile çalışır ve sonuç SRID girişine geri döner.

İşlev =========================================================================== ==================

CREATE OR REPLACE FUNCTION public.I_Grid_Point_Distance(geom public.geometry, x_side decimal, y_side decimal)
RETURNS public.geometry AS $BODY$
DECLARE
x_min decimal;
x_max decimal;
y_max decimal;
x decimal;
y decimal;
returnGeom public.geometry[];
i integer := -1;
srid integer := 4326;
input_srid integer;
BEGIN
CASE st_srid(geom) WHEN 0 THEN
    geom := ST_SetSRID(geom, srid);
        ----RAISE NOTICE 'No SRID Found.';
    ELSE
        ----RAISE NOTICE 'SRID Found.';
END CASE;
    input_srid:=st_srid(geom);
    geom := st_transform(geom, srid);
    x_min := ST_XMin(geom);
    x_max := ST_XMax(geom);
    y_max := ST_YMax(geom);
    y := ST_YMin(geom);
    x := x_min;
    i := i + 1;
    returnGeom[i] := st_setsrid(ST_MakePoint(x, y), srid);
<<yloop>>
LOOP
IF (y > y_max) THEN
    EXIT;
END IF;

CASE i WHEN 0 THEN 
    y := ST_Y(returnGeom[0]);
ELSE 
    y := ST_Y(ST_Project(st_setsrid(ST_MakePoint(x, y), srid), y_side, radians(0))::geometry);
END CASE;

x := x_min;
<<xloop>>
LOOP
  IF (x > x_max) THEN
      EXIT;
  END IF;
    i := i + 1;
    returnGeom[i] := st_setsrid(ST_MakePoint(x, y), srid);
    x := ST_X(ST_Project(st_setsrid(ST_MakePoint(x, y), srid), x_side, radians(90))::geometry);
END LOOP xloop;
END LOOP yloop;
RETURN
ST_CollectionExtract(st_transform(ST_Intersection(st_collect(returnGeom), geom), input_srid), 1);
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE;

Fonksiyonu basit bir sorgu ile kullanın, geometri geçerli olmalı ve çokgen, çokgen veya zarf tipi olmalı

SELECT I_Grid_Point_Distance(geom, 50, 61) from polygons limit 1;

Sonuç ================================================= =====================

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

  1. Nicklas Avén algoritmasına dayalı ikinci fonksiyon . Herhangi bir SRID ile başa çıkmak için geliştirdim.

    algoritması aşağıdaki değişiklikleri yükseltmek var.

    1. Piksel boyutu için x ve y yönü için ayrı değişken,
    2. Sferoid veya elipsoid içindeki mesafeyi hesaplamak için yeni değişken.
    3. Herhangi bir SRID girişi yapın, fonksiyon Geom'i Spheroid veya Ellipsoid Datum'un çalışma ortamına dönüştürün, ardından her bir tarafa mesafeyi uygulayın, sonucu alın ve SRID girişine dönüştürün.

İşlev =========================================================================== ==================

CREATE OR REPLACE FUNCTION I_Grid_Point(geom geometry, x_side decimal, y_side decimal, spheroid boolean default false)
RETURNS SETOF geometry AS $BODY$ 
DECLARE
x_max decimal; 
y_max decimal;
x_min decimal;
y_min decimal;
srid integer := 4326;
input_srid integer; 
BEGIN
CASE st_srid(geom) WHEN 0 THEN
  geom := ST_SetSRID(geom, srid);
  RAISE NOTICE 'SRID Not Found.';
    ELSE
        RAISE NOTICE 'SRID Found.';
    END CASE;

    CASE spheroid WHEN false THEN
        RAISE NOTICE 'Spheroid False';
        srid := 4326;
        x_side := x_side / 100000;
        y_side := y_side / 100000;
    else
        srid := 900913;
        RAISE NOTICE 'Spheroid True';
    END CASE;
    input_srid:=st_srid(geom);
    geom := st_transform(geom, srid);
    x_max := ST_XMax(geom);
    y_max := ST_YMax(geom);
    x_min := ST_XMin(geom);
    y_min := ST_YMin(geom);
RETURN QUERY
WITH res as (SELECT ST_SetSRID(ST_MakePoint(x, y), srid) point FROM
generate_series(x_min, x_max, x_side) as x,
generate_series(y_min, y_max, y_side) as y
WHERE st_intersects(geom, ST_SetSRID(ST_MakePoint(x, y), srid))
) select ST_TRANSFORM(ST_COLLECT(point), input_srid) from res;
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE STRICT;

Basit bir sorgu ile kullanın.

SELECT I_Grid_Point(geom, 22, 15, false) from polygons;

Sonuç ================================================= ==================görüntü tanımını buraya girin

  1. Seri jeneratöre dayalı fonksiyon.

Fonksiyon ================================================= =================

CREATE OR REPLACE FUNCTION I_Grid_Point_Series(geom geometry, x_side decimal, y_side decimal, spheroid boolean default false)
RETURNS SETOF geometry AS $BODY$
DECLARE
x_max decimal;
y_max decimal;
x_min decimal;
y_min decimal;
srid integer := 4326;
input_srid integer;
x_series DECIMAL;
y_series DECIMAL;
BEGIN
CASE st_srid(geom) WHEN 0 THEN
  geom := ST_SetSRID(geom, srid);
  RAISE NOTICE 'SRID Not Found.';
    ELSE
        RAISE NOTICE 'SRID Found.';
    END CASE;

    CASE spheroid WHEN false THEN
        RAISE NOTICE 'Spheroid False';
    else
        srid := 900913;
        RAISE NOTICE 'Spheroid True';
    END CASE;
    input_srid:=st_srid(geom);
    geom := st_transform(geom, srid);
    x_max := ST_XMax(geom);
    y_max := ST_YMax(geom);
    x_min := ST_XMin(geom);
    y_min := ST_YMin(geom);

    x_series := CEIL ( @( x_max - x_min ) / x_side);
    y_series := CEIL ( @( y_max - y_min ) / y_side );
RETURN QUERY
SELECT st_collect(st_setsrid(ST_MakePoint(x * x_side + x_min, y*y_side + y_min), srid)) FROM
generate_series(0, x_series) as x,
generate_series(0, y_series) as y
WHERE st_intersects(st_setsrid(ST_MakePoint(x*x_side + x_min, y*y_side + y_min), srid), geom);
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE STRICT;

Basit bir sorgu ile kullanın.

SELECT I_Grid_Point_Series(geom, 22, 15, false) from polygons; Sonuç ================================================= =========================

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


3

İşte kesinlikle daha hızlı ve anlaşılması daha kolay olan başka bir yaklaşım.

Örneğin 1000m'lik bir ızgara için 1000m:

SELECT (ST_PixelAsCentroids(ST_AsRaster(the_geom,1000.0,1000.0))).geom 
FROM the_polygon

Ayrıca orijinal SRID korunur.

Bu pasaj, çokgenin geometrisini boş bir rasterine dönüştürür, ardından her pikseli bir noktaya dönüştürür. Avantaj: Orijinal çokgenin noktaları kesip kesmeyeceğini tekrar kontrol etmemize gerek yoktur.

İsteğe bağlı:

Ayrıca ızgara hizalamasını gridx ve gridy parametresiyle de ekleyebilirsiniz. Fakat her pikselin (ancak bir köşe değil) centroidini kullandığımızdan, doğru değeri hesaplamak için bir modulo kullanmamız gerekir:

SELECT (ST_PixelAsCentroids(ST_AsRaster(the_geom,1000.0,1000.0,mod(1000/2,100),mod(1000/2,100)))).geom 
FROM the_polygon

İle mod(grid_size::integer/2,grid_precision)

İşte postgres işlevi:

CREATE OR REPLACE FUNCTION st_makegrid(geometry, float, integer)
RETURNS SETOF geometry AS
'SELECT (ST_PixelAsCentroids(ST_AsRaster($1,$2::float,$2::float,mod($2::int/2,$3),mod($2::int/2,$3)))).geom'
LANGUAGE sql;

Canbe ile kullanılan:

SELECT makegrid(the_geom,1000.0,100) as geom from the_polygon  
-- makegrid(the_geom,grid_size,alignement)

2

Öyleyse benim sabit sürümüm:

CREATE OR REPLACE FUNCTION makegrid(geometry, integer, integer)
RETURNS geometry AS
'SELECT ST_Collect(st_setsrid(ST_POINT(x,y),$3)) FROM 
  generate_series(floor(st_xmin($1))::int, ceiling(st_xmax($1))::int,$2) as x
  ,generate_series(floor(st_ymin($1))::int, ceiling(st_ymax($1))::int,$2) as y 
where st_intersects($1,st_setsrid(ST_POINT(x,y),$3))'
LANGUAGE sql

Kullanımı:

SELECT (ST_Dump(makegrid(the_geom, 1000, 3857))).geom as the_geom from my_polygon_table

1
Merhaba, makegrid işleviyle boş sonuç alıyorum. Şekil dosyası, shp2pgsql kullanılarak PostGIS'e aktarıldı. Neyin başarabileceği konusunda hiçbir fikriniz yok, srs wgs84 olarak ayarlanmış.
Michal Zimmermann

1

Önceki cevaplara küçük bir potansiyel güncelleme - wgs84 için ölçek olarak üçüncü argüman (veya normal olanlar için 1 kullanın) ve ayrıca kodun içine yuvarlayarak birden çok şekildeki ölçeklendirilmiş noktaların hizalanmasını sağlayın.

Umarım bu yardımcı olur, Martin

CREATE OR REPLACE FUNCTION makegrid(geometry, integer, integer)
RETURNS geometry AS



/*geometry column , integer: distance between points, integer: scale factor for distance (useful for wgs84, e.g. use there 50000 as distance and 1000000 as scale factor*/

'
SELECT ST_Collect(st_setsrid(ST_POINT(x/$3::float,y/$3::float),st_srid($1))) FROM 
  generate_series(
                (round(floor(st_xmin($1)*$3)::int/$2)*$2)::int, 
                (round(ceiling(st_xmax($1)*$3)::int/$2)*$2)::int,
                $2) as x ,
  generate_series(
                (round(floor(st_ymin($1)*$3)::int/$2)*$2)::int, 
                (round(ceiling(st_ymax($1)*$3)::int/$2)*$2)::int,
                $2) as y 
WHERE st_intersects($1,ST_SetSRID(ST_POINT(x/$3::float,y/$3::float),ST_SRID($1)))
'

LANGUAGE sql

Geometriyi belirli bir SRID'ye (EPSG: 3857 gibi) dönüştürmek, ölçek faktörü ile çarpmaktan daha iyi olmaz mıydı?
Nikolaus Krismer
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.