Tworzysz regularną siatkę wielokątów w PostGIS?

61

Jak stworzyć, w kształcie wielokąta, regularną siatkę wielokątów / kwadratów o danym rozmiarze, w postgis?

Pomyślałem o funkcji takiej jak Jak utworzyć regularną siatkę punktów wewnątrz wielokąta w Postgis? tylko dla kwadratów, tak aby kwadraty mogły mieć wymiary 5 x 5 m lub nawet 10 x 10 m. Ale nie mam pojęcia, jak to zmienić we właściwy sposób.

mk.archaeo
źródło
2
Uogólnienie, którego szukasz, nie jest jasne. Czy mówisz, że zaczynasz od (dowolnego) pojedynczego wielokąta i chcesz pokryć płaszczyznę przystającymi kopiami? Zasadniczo nie jest to możliwe, ale może ten wielokąt ma szczególne właściwości (być może na przykład jest znany jako równoległobok, trójkąt lub sześciokąt).
whuber

Odpowiedzi:

60

Oto funkcja zwracająca zestaw, ST_CreateFishnetktóra tworzy siatkę 2D o geometrii wielokątów:

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;

gdzie nrowi ncolsą liczbą wierszy i kolumn xsizeoraz ysizesą długościami wielkości komórki, i są opcjonalne x0i y0są współrzędnymi lewego dolnego rogu.

Powoduje to, rowa colcyfry od 1 w dolnym lewym rogu, a geomprostokątnych wielokąty dla każdej komórki. Na przykład:

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)

Lub utworzyć pojedynczą kolekcję geometrii dla pełnej siatki:

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

Siatka 4x6

Możesz dodać przesunięcia x0/ y0origin (domyślnie ustawione na zero).

Mike T.
źródło
1
Dzięki! Teraz muszę tylko połączyć kabaretkę z BBoxem wielokąta.
mk.archaeo
Jest to bardzo pomocne .. Mam jedno zapytanie. Jak mogę tworzyć siatki wewnątrz wielokąta / bboksa?
Mohammed shafeek
Dobra robota Mike, to jest bardzo pomocne.
Mounaim,
56

Oto specyficzny wariant generowania dla sytuacji, gdy trzeba utworzyć siatkę dla mapy geograficznej ze stałym krokiem metrycznym (komórki mogą być użyte do grupowania wartości, np. Natężenia błyskawicy w regionie).

Funkcja nie jest zbyt elegancka, ale nie znalazłem lepszego rozwiązania dla tego zadania (w tym funkcja Mike'a Toewsa powyżej). Więc masz związany wielokąt (np. Przybył z interfejsu Google Maps), masz wartość kroku w metrach:

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

Jak tego użyć:

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

Możesz więc zobaczyć, że linie sformatowane przez wygenerowane wielokąty leżą wzdłuż równoleżników geograficznych i południków - to bardzo wygodne.

Przykład siatki z krokiem 50 km

Wskazówka: Jeśli obliczasz coś w rodzaju gęstości (np. Mapa uderzeń pioruna według komórek), a siatka jest generowana dynamicznie Aby zwiększyć wydajność, sugerowałbym użycie tabel tymczasowych do przechowywania komórek jako wielokątów geometrycznych, z indeksem przestrzennym na kolumnie reprezentującym komórka.

Aleksander Palamarchuk
źródło
Chciałbym móc ponownie głosować w tej sprawie ... to było idealne rozwiązanie! a możliwość dostosowania układu współrzędnych jest fantastyczna ~!
DPSSpatial
Niewielka sugestia, zamiast używać ST_GeomFromTextpodczas tworzenia pola do dodania sectors, możesz użyć ST_MakeEnvelopei po prostu określić współrzędne dolnego lewego i prawego górnego pola.
Matt
Daje to potencjały
nickves
11

Możesz stworzyć regularną siatkę po prostu wektoryzując pusty raster:

SELECT (ST_PixelAsPolygons(ST_AddBand(ST_MakeEmptyRaster(100, 100, 1.1, 1.1, 1.0), '8BSI'::text, 1, 0), 1, false)).geom
Pierre Racine
źródło
1
To takie proste rozwiązanie, ponieważ tyle razy robiłem to wektorowo.
John Powell,
6

Stworzyłem wariant funkcji @ Alexander, który nie wymaga transformacji na inny SRID. Pozwala to uniknąć problemu ze znalezieniem projekcji, która wykorzystuje mierniki jako jednostki dla określonego regionu. Służy ST_Projectdo prawidłowego kroczenia przy użyciu danej projekcji. Dodałem również width_stepi, height_stepaby pozwolić na prostokątne płytki zamiast wymagać, aby były kwadratami.

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

Możesz go używać w następujący sposób:

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;
Matt
źródło
5

Oto zoptymalizowany i wydajny algorytm do tworzenia kabaretki, siatki regularnej, siatki wielokątnej, siatki prostokątnej wewnątrz dowolnej obwiedni, wielokąta lub wielokąta. prawie obsłużyć dowolny SRID;

GitHub Repo Link

wprowadź opis zdjęcia tutaj

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;

Użyj go za pomocą prostego zapytania; wejście powinno być poprawnym wielokątem, wielokątem lub kopertą.

select I_Grid_Regular(st_setsrid(g.geom, 4326), .0001, .0001 ), geom from polygons limit 1
Muhammad Imran Siddique
źródło