Створення регулярної багатокутної сітки в PostGIS?


61

Як створити за формою багатокутника звичайну сітку багатокутників / квадратів заданого розміру в постгісах?

Я подумав про таку функцію, як " Створити регулярну точкову сітку всередині багатокутника в Postgis?" тільки для квадратів, так що квадрати можуть бути 5м х 5м або навіть 10м х 10м. Але не маю ідеї змінити це правильним чином.


2
Узагальнення, яке ви шукаєте, не зрозуміло. Ви хочете сказати, що ви починаєте з (довільного) одиночного багатокутника і хочете плитку площині конгруентними його копіями? Загалом це неможливо, але, можливо, цей багатокутник має особливі властивості (можливо, він, наприклад, паралелограм, трикутник або шестикутник).
whuber

Відповіді:


60

Ось набір функцій повернення, ST_CreateFishnetяка створює 2D сітку геометрії багатокутника:

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;

де nrowі ncol- кількість рядків і стовпців, xsizeі ysizeдовжини розміру комірки, і необов'язкові x0і y0є координатами для нижнього лівого кута.

Результат rowі colцифри, починаючи з 1 в нижньому лівому кутку, і geomпрямокутних полігонів для кожного осередку. Так, наприклад:

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)

Або зробити єдину колекцію геометрії для повної сітки:

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

4х6 сітка

Ви можете додати компенсації x0/ y0origin (вони за замовчуванням до нуля).


1
Дякую! Тепер мені залишається лише прив’язати рибальську мережу до BBox полігону.
mk.archaeo

Це дуже корисно .. У мене є один запит. Як я можу створити сітки всередині полігона / bbox?
Мухаммед shafeek

Гарний робочий Майк, це дуже корисно.
Мунайм

56

Ось конкретний варіант генерації для ситуації, коли вам потрібно створити сітку для географічної карти з постійним метричним кроком (комірки можуть використовуватися для групування значень, наприклад, щільності блискавки в регіоні).

Функція не дуже елегантна, але я не знайшов кращого рішення для цієї задачі (включаючи функцію Mike Toews вище). Отже, у вас є зв'язаний багатокутник (наприклад, прибутий з інтерфейсу Карт Google), значення кроку в метрах:

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

Як ним користуватися:

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

Тож ви можете бачити, що лінії, відформатовані генерованими багатокутниками, лежать уздовж географічних паралелей та меридіанів - це дуже зручно.

Приклад сітки з кроком 50 км

Порада: Якщо ви обчислите щось на кшталт щільності (наприклад, карта ударів блискавки по осередках), а сітка створюється динамічно. Для підвищення продуктивності я б запропонував використовувати тимчасові таблиці для зберігання комірок як полігонів геометрії з просторовим індексом на стовпці, що представляє клітина.


Я б хотів, щоб я міг знову проголосувати за це ... це було ідеальне рішення! а можливість налаштування системи координат фантастична ~!
DPSSpatial

Лише незначна пропозиція, замість того, щоб використовувати ST_GeomFromTextпри створенні поля для додавання sectors, ви можете використовувати ST_MakeEnvelopeта просто вказати нижню ліву та верхню праві координати поля.
Метт

Це приносить потенції
ніки

11

Можна створити звичайну сітку, просто векторизуючи порожній растр:

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

1
Це таке просте рішення, так багато разів робив це векторним шляхом.
Джон Пауелл

6

Я створив варіант функції @ Alexander, який не потребує перетворення на інший SRID. Це дозволяє уникнути проблеми пошуку проекції, яка використовує лічильники як одиниці для певного регіону. Він використовує ST_Projectдля правильного кроку за допомогою заданої проекції. Я також додав width_stepі, height_stepщоб дозволити прямокутні плитки, а не вимагати, щоб вони були квадратами.

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

Ви можете використовувати його так:

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

Ось оптимізований та ефективний алгоритм для створення рибальської мережі, звичайної сітки, багатокутної сітки, прямокутної сітки всередині будь-якого конверта, багатокутника чи багатополігонів. майже обробляти будь-який SRID;

GitHub Repo Link

введіть тут опис зображення

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;

Використовуйте його за допомогою простого запиту; вхід повинен бути дійсним багатокутником, мультиполігоном або конвертом.

select I_Grid_Regular(st_setsrid(g.geom, 4326), .0001, .0001 ), geom from polygons limit 1
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.