Membuat kisi poligon reguler di PostGIS?


61

Bagaimana cara membuat, pada bentuk poligon, kisi-kisi poligon / bujur sangkar dengan ukuran tertentu, dalam postgis?

Saya telah memikirkan fungsi seperti Bagaimana cara membuat titik biasa di dalam poligon di Postgis? hanya untuk kotak, sehingga kotak dapat 5m x 5m atau bahkan 10m x 10m. Tetapi tidak punya ide untuk mengubahnya dengan cara yang benar.


2
Generalisasi yang Anda cari tidak jelas. Apakah Anda mengatakan Anda mulai dengan poligon tunggal (sewenang-wenang) dan ingin memasang pesawat dengan salinan yang sama? Secara umum ini tidak mungkin, tetapi mungkin poligon ini memiliki sifat-sifat tertentu (mungkin itu dikenal sebagai jajaran genjang, segitiga, atau segi enam, misalnya).
whuber

Jawaban:


60

Berikut adalah fungsi pengembalian set ST_CreateFishnetyang membuat kisi 2D geometri poligon:

CREATE OR REPLACE FUNCTION ST_CreateFishnet(
        nrow integer, ncol integer,
        xsize float8, ysize float8,
        x0 float8 DEFAULT 0, y0 float8 DEFAULT 0,
        OUT "row" integer, OUT col integer,
        OUT geom geometry)
    RETURNS SETOF record AS
$$
SELECT i + 1 AS row, j + 1 AS col, ST_Translate(cell, j * $3 + $5, i * $4 + $6) AS geom
FROM generate_series(0, $1 - 1) AS i,
     generate_series(0, $2 - 1) AS j,
(
SELECT ('POLYGON((0 0, 0 '||$4||', '||$3||' '||$4||', '||$3||' 0,0 0))')::geometry AS cell
) AS foo;
$$ LANGUAGE sql IMMUTABLE STRICT;

di mana nrowdan ncoljumlah baris dan kolom, xsizedan ysizepanjang ukuran sel, dan opsional x0dan y0merupakan koordinat untuk sudut kiri bawah.

Hasilnya adalah rowdan colangka, mulai dari 1 di sudut kiri bawah, dan geompoligon persegi panjang untuk setiap sel. Jadi misalnya:

SELECT *
FROM ST_CreateFishnet(4, 6, 10, 10) AS cells;
 row | col |         geom
-----+-----+--------------------------------
   1 |   1 | 0103000000010000000500000000...
   2 |   1 | 0103000000010000000500000000...
   3 |   1 | 0103000000010000000500000000...
   4 |   1 | 0103000000010000000500000000...
   1 |   2 | 0103000000010000000500000000...
   2 |   2 | 0103000000010000000500000000...
   ...
   3 |   6 | 0103000000010000000500000000...
   4 |   6 | 0103000000010000000500000000...
(24 rows)

Atau untuk membuat koleksi geometri tunggal untuk kisi penuh:

SELECT ST_Collect(cells.geom)
FROM ST_CreateFishnet(4, 6, 10, 10) AS cells;

Kotak 4x6

Anda dapat menambahkan x0/ y0asal offset (ini default ke nol).


1
Terima kasih! Sekarang saya hanya perlu mengikat jala ke BBox dari poligon.
mk.archaeo

Ini sangat membantu .. Saya punya satu permintaan. Bagaimana cara membuat kisi di dalam poligon / kotak?
Mohammed shafeek

Kerja Bagus Mike, Ini sangat membantu.
Mounaim

56

Berikut adalah varian spesifik penghasil, untuk situasi ketika Anda perlu membuat kisi untuk peta geografis dengan langkah metrik konstan (sel mungkin digunakan untuk nilai pengelompokan, misalnya kerapatan petir di suatu wilayah).

Fungsi tidak terlalu elegan, tetapi saya tidak menemukan solusi yang lebih baik untuk tugas itu (termasuk fungsi Mike Toews di atas). Jadi Anda memiliki poligon terikat (mis. Tiba dari antarmuka Google Maps), miliki nilai langkah dalam meter:

CREATE OR REPLACE FUNCTION public.makegrid_2d (
  bound_polygon public.geometry,
  grid_step integer,
  metric_srid integer = 28408 --metric SRID (this particular is optimal for the Western Russia)
)
RETURNS public.geometry AS
$body$
DECLARE
  BoundM public.geometry; --Bound polygon transformed to the metric projection (with metric_srid SRID)
  Xmin DOUBLE PRECISION;
  Xmax DOUBLE PRECISION;
  Ymax DOUBLE PRECISION;
  X DOUBLE PRECISION;
  Y DOUBLE PRECISION;
  sectors public.geometry[];
  i INTEGER;
BEGIN
  BoundM := ST_Transform($1, $3); --From WGS84 (SRID 4326) to the metric projection, to operate with step in meters
  Xmin := ST_XMin(BoundM);
  Xmax := ST_XMax(BoundM);
  Ymax := ST_YMax(BoundM);

  Y := ST_YMin(BoundM); --current sector's corner coordinate
  i := -1;
  <<yloop>>
  LOOP
    IF (Y > Ymax) THEN  --Better if generating polygons exceeds the bound for one step. You always can crop the result. But if not you may get not quite correct data for outbound polygons (e.g. if you calculate frequency per sector)
        EXIT;
    END IF;

    X := Xmin;
    <<xloop>>
    LOOP
      IF (X > Xmax) THEN
          EXIT;
      END IF;

      i := i + 1;
      sectors[i] := ST_GeomFromText('POLYGON(('||X||' '||Y||', '||(X+$2)||' '||Y||', '||(X+$2)||' '||(Y+$2)||', '||X||' '||(Y+$2)||', '||X||' '||Y||'))', $3);

      X := X + $2;
    END LOOP xloop;
    Y := Y + $2;
  END LOOP yloop;

  RETURN ST_Transform(ST_Collect(sectors), ST_SRID($1));
END;
$body$
LANGUAGE 'plpgsql';

Bagaimana cara menggunakannya:

SELECT cell FROM 
(SELECT (
ST_Dump(makegrid_2d(ST_GeomFromText('Polygon((35.099577 45.183417,47.283415 45.183417,47.283415 49.640445,35.099577 49.640445,35.099577 45.183417))',
 4326), -- WGS84 SRID
 10000) -- cell step in meters
)).geom AS cell) AS q_grid

Jadi Anda dapat melihat bahwa garis yang diformat oleh poligon yang dihasilkan terletak di sepanjang paralel geografis dan garis meridian - itu sangat nyaman.

Contoh kotak dengan langkah 50 km

Saran: Jika Anda menghitung sesuatu seperti kerapatan (misalnya peta sambaran petir oleh sel), dan grid dihasilkan secara dinamis Untuk meningkatkan kinerja, saya akan menyarankan menggunakan tabel sementara untuk menyimpan sel sebagai poligon geometri, dengan indeks spasial pada kolom mewakili sel.


Saya berharap saya dapat memilih lagi ini ... ini adalah solusi yang sempurna! dan kemampuan untuk menyesuaikan sistem koordinat fantastis ~!
DPSSpatial

Hanya saran kecil, alih-alih menggunakan ST_GeomFromTextsaat membuat kotak untuk ditambahkan sectors, Anda dapat menggunakan ST_MakeEnvelopedan cukup menentukan koordinat kiri bawah dan kanan atas kotak.
Mat

Ini membawa potensi
nikel

11

Anda dapat membuat kisi-kisi biasa hanya menjadi vektorisasi raster kosong:

SELECT (ST_PixelAsPolygons(ST_AddBand(ST_MakeEmptyRaster(100, 100, 1.1, 1.1, 1.0), '8BSI'::text, 1, 0), 1, false)).geom

1
Itu adalah solusi yang sangat sederhana, telah melakukannya dengan cara vektor berkali-kali.
John Powell

6

Saya telah membuat varian fungsi @ Alexander yang tidak mengharuskan kita mengubah ke SRID lain. Ini menghindari masalah harus menemukan proyeksi yang menggunakan meter sebagai unit untuk wilayah tertentu. Ini digunakan ST_Projectuntuk melangkah dengan benar menggunakan proyeksi yang diberikan. Saya juga telah menambahkan width_stepdan height_stepuntuk memungkinkan ubin persegi panjang alih-alih mengharuskan mereka menjadi kotak.

CREATE OR REPLACE FUNCTION public.makegrid_2d (
  bound_polygon public.geometry,
  width_step integer,
  height_step integer
)
RETURNS public.geometry AS
$body$
DECLARE
  Xmin DOUBLE PRECISION;
  Xmax DOUBLE PRECISION;
  Ymax DOUBLE PRECISION;
  X DOUBLE PRECISION;
  Y DOUBLE PRECISION;
  NextX DOUBLE PRECISION;
  NextY DOUBLE PRECISION;
  CPoint public.geometry;
  sectors public.geometry[];
  i INTEGER;
  SRID INTEGER;
BEGIN
  Xmin := ST_XMin(bound_polygon);
  Xmax := ST_XMax(bound_polygon);
  Ymax := ST_YMax(bound_polygon);
  SRID := ST_SRID(bound_polygon);

  Y := ST_YMin(bound_polygon); --current sector's corner coordinate
  i := -1;
  <<yloop>>
  LOOP
    IF (Y > Ymax) THEN  
        EXIT;
    END IF;

    X := Xmin;
    <<xloop>>
    LOOP
      IF (X > Xmax) THEN
          EXIT;
      END IF;

      CPoint := ST_SetSRID(ST_MakePoint(X, Y), SRID);
      NextX := ST_X(ST_Project(CPoint, $2, radians(90))::geometry);
      NextY := ST_Y(ST_Project(CPoint, $3, radians(0))::geometry);

      i := i + 1;
      sectors[i] := ST_MakeEnvelope(X, Y, NextX, NextY, SRID);

      X := NextX;
    END LOOP xloop;
    CPoint := ST_SetSRID(ST_MakePoint(X, Y), SRID);
    NextY := ST_Y(ST_Project(CPoint, $3, radians(0))::geometry);
    Y := NextY;
  END LOOP yloop;

  RETURN ST_Collect(sectors);
END;
$body$
LANGUAGE 'plpgsql';

Anda dapat menggunakannya seperti ini:

SELECT ST_AsGeoJSON(cell) FROM (
  SELECT (
    ST_Dump(
      makegrid_2d(
        ST_GeomFromText(
          'Polygon((35.099577 45.183417,47.283415 45.183417,47.283415 49.640445,35.099577 49.640445,35.099577 45.183417))',
          4326
        ),
         10000, -- width step in meters
         10000  -- height step in meters
       ) 
    )
  ) .geom AS cell
)q;

5

Berikut ini adalah algoritma yang dioptimalkan dan efisien untuk membuat jala, kisi biasa, kisi poligon, kisi persegi di dalam setiap amplop, poligon, atau Multipoligon. hampir menangani SRID apa pun;

Tautan Repo GitHub

masukkan deskripsi gambar di sini

DROP FUNCTION IF EXISTS PUBLIC.I_Grid_Regular(geometry, float8, float8);
CREATE OR REPLACE FUNCTION PUBLIC.I_Grid_Regular
( geom geometry, x_side float8, y_side float8, OUT geometry )
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;
geom_cell geometry := ST_GeomFromText(FORMAT('POLYGON((0 0, 0 %s, %s %s, %s 0,0 0))',
                                        $3, $2, $3, $2), srid);
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;
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 With foo AS (
    SELECT
    ST_Translate( geom_cell, j * $2 + x_min, i * $3 + y_min ) AS cell
    FROM
        generate_series ( 0, x_series ) AS j,
        generate_series ( 0, y_series ) AS i
    ) SELECT ST_CollectionExtract(ST_Collect(ST_Transform ( ST_Intersection(cell, geom), input_srid)), 3)
    FROM foo where ST_intersects (cell, geom);
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE STRICT;

Gunakan dengan permintaan sederhana; input harus berupa poligon, Multipolygon, atau amplop yang valid.

select I_Grid_Regular(st_setsrid(g.geom, 4326), .0001, .0001 ), geom from polygons limit 1
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.