Як створити регулярну точкову сітку всередині багатокутника в Postgis?


31

Як створити всередині багатокутника звичайну сітку з точкою, розміщеною x, y у postgis? Приклад:

alt текст


Я намагався зробити відсікання багатокутників, об'єднуючи цей код із кодом диктування "postGIS в дії", але створений лише один багатокутник ... Я щось забув? СТВОРИТИ АБО ЗАМОВИТИ ФУНКЦІЮ makegrid (геометрія, ціле число, ціле число) ПОВЕРНУТИСЯ геометрію ЯК ВИБІР st_intersection (g1.geom1, g2.geom2) ЯК geom FROM (SELECT $ 1 AS geom1) AS g1 INNER JOIN (Виберіть st_setsrid (CAST (ST_MakeBoxtsdd (ST_MakeBoxtsdd) ST_Point (x, y), $ 3), st_setsrid (ST_Point (x + $ 2, y + $ 2), $ 3)) як геометрія), $ 3) як geom2 від generator_series (пол (st_xmin ($ 1)) :: int, стеля ( st_xmax ($ 1)): int, $ 2) як x, create_series (пол (st_ymin ($ 1)) :: int, стеля (st_ymax (
aurel_nc

подивіться на мою детальну відповідь.
Мухаммед Імран Сіддіке

Відповіді:


30

Ви робите це з create_series.

Якщо ви не хочете вручну писати, де сітка запускається і зупиняється, найпростіше створити функцію.

Я не перевіряв наведене нижче належним чином, але думаю, що це має працювати:

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

Для його використання ви можете:

SELECT makegrid(the_geom, 1000) from mytable;

де перший аргумент - це багатокутник, в який ви хочете сітку, а другий аргумент - відстань між точками в сітці.

Якщо ви хочете по одній точці в рядку, ви просто використовуєте ST_Dump, наприклад:

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

HTH

Ніклас


2
Можливо, вам потрібно буде додати st_setSRID () до функцій st_point, інакше st_intersects не працює.
JaakL

Як окрему відповідь додана моя перевірена версія.
JaakL

12

Я підібрав код функції гри Nicklas Avén і зробив його трохи більш загальним, читаючи та використовуючи сітку з геометрії багатокутника. В іншому випадку використання багатокутника з визначеним srid, призведе до помилки.

Функція:

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

Використовувати функцію робиться саме так, як писав Ніклас Авен :

SELECT makegrid(the_geom, 1000) from mytable;

або якщо ви хочете по одній точці в рядку:

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

Сподіваюся, це комусь буде корисно.

Олексій


Прийнята відповідь не працює з моїми даними через помилки SRID. Ця модифікація працює краще.
Віталій Ісаєв

Ви можете додати щось, коли багатокутник перетинає антимеридіан? Я можу уявити, що це призведе до проблеми з xmin / xmax.
Томас

2
Це не працювало для мене. Використання Postgres 9.6 та PostGIS 2.3.3. Усередині виклику generator_series я повинен був поставити це як другий параметр "стеля (st_xmax ($ 1)) :: int" замість "стель (st_xmax ($ 1) -st_xmin ($ 1)) :: int", і "стеля ( st_ymax ($ 1)) :: int "замість" стеля (st_ymax ($ 1) -st_ymin ($ 1)) :: int "
Vitor Sapucaia

Я схвалюю попередній коментар; верхня межа generator_series повинна бути максимальною стелею, а не стелею різниці (max - min).
Р. Бурген

10

Люди, які використовують геометрию wgs84, мабуть, будуть мати проблеми з цією функцією

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 

повернути лише цілі числа. За винятком дуже великих геометрій, таких як країни (які лежать на декількох латах, lng градусах), це призведе до збору лише 1 бала, який більшість часу навіть не перетинає саму геометрію ... => порожній результат!

Моя проблема полягала в тому, що я не можу використовувати Gene_series () з десятковою відстані на плаваючих числах, таких як WSG84 ... Ось чому я налаштував функцію, щоб все-таки вона працювала:

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

В основному точно так само. Просто множення та ділення на 1000000, щоб отримати десяткові знаки в грі, коли мені це потрібно.

Напевно, є кращого рішення для досягнення цього. ++


Це розумне вирішення. Ви перевірили результати? Чи відповідають вони?
Пабло

Привіт. Так пабло. Я задоволений результатами поки що. Мені це знадобилося для побудови якогось багатокутника з відносною висотою над землею. (Я використовую SRTM для обчислення потрібної висоти для кожної точки сітки). Мені зараз лише не вистачає способу включити точки, які лежать по периметру багатокутника. В даний час надана форма дещо врізана на краю.
Жульєн Гарсія

працювали, коли всі інші рішення провалилися, дякую!
Йордан Арсено

7

Цей алгоритм повинен бути добре:

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);
       }
    }
}

де "багатокутник" - це багатокутник, а "роздільна здатність" - необхідна роздільна здатність сітки.

Для його реалізації в PostGIS можуть знадобитися такі функції:

  • ST_XMin , ST_XMax , ST_YMin та ST_YMax, щоб отримати мінімальні та максимальні координати багатокутника,
  • ST_Утримує для перевірки, чи багатокутник містить крапку,
  • та ST_Point, щоб створити точку.

Удачі!


1
Зауважте, що якщо у вас є великі складні багатокутники (наприклад, у мене буфер узбережжя), такий підхід не зовсім оптимальний.
JaakL

Тоді, що ви пропонуєте замість цього?
липень

4

Три алгоритми, що використовують різні методи.

Github Repo Link

  1. Простий і найкращий підхід, використовуючи фактичну відстань земних координат від напрямку x і y. Алгоритм працює з будь-яким SRID, внутрішньо він працює з WGS 1984 (EPSG: 4326) і перетворює результат назад на вхід SRID.

Функція ===================================================== ===================

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;

Використовуйте функцію за допомогою простого запиту, геометрія повинна бути дійсною і типу багатокутник, багатокутник або конверт

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

Результат ===================================================== ======================

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

  1. Друга функція, заснована на алгоритмі Нікласа Авена . Я вдосконалив його для обробки будь-якого SRID.

    оновити наступні зміни в алгоритмі.

    1. Роздільна змінна для x і y напрямку для розміру пікселів,
    2. Нова змінна для обчислення відстані в сфероїді чи еліпсоїді.
    3. Введіть будь-яку SRID, функцію перетворення Geom в робоче середовище сфероїда або еліпсоїдальної дати, потім застосуйте відстань до кожної сторони, отримайте результат і перетворіть на вхід SRID.

Функція ===================================================== ===================

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;

Використовуйте його за допомогою простого запиту.

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

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

  1. Функція заснована на генераторі серій.

Функція ===================================================== =================

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;

Використовуйте його за допомогою простого запиту.

SELECT I_Grid_Point_Series(geom, 22, 15, false) from polygons; Результат ===================================================== ==========================

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


3

Тож моя фіксована версія:

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

Використання:

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

1
Привіт, я отримую порожній результат за допомогою функції makegrid. Файл форми імпортовано до PostGIS за допомогою shp2pgsql. Поняття не маєте, що може спричинити неприємності, srs встановлено на wgs84.
Michal Zimmermann

3

Ось ще один підхід, який, безумовно, швидший і простіший для розуміння.

Наприклад для сітки 1000 м на 1000 м:

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

Також зберігається оригінальний SRID.

Цей фрагмент перетворить геометрію багатокутника в порожній растр, а потім перетворить кожен піксель у точку. Перевага: нам не доведеться ще раз перевіряти, чи оригінальний багатокутник перетинає точки.

Необов’язково:

Ви також можете додати відстеження сітки за допомогою параметра gridx та gridy. Але оскільки ми використовуємо центроїд кожного пікселя (а не кут), нам потрібно використовувати модуль для обчислення потрібного значення:

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

З mod(grid_size::integer/2,grid_precision)

Ось функція postgres:

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 використовується з:

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

1

Невелике потенційне оновлення попередніх відповідей - третій аргумент як шкала для wgs84 (або використовувати 1 для звичайних), а також округлення всередині коду, щоб масштабовані точки на декількох фігурах були вирівняні.

Сподіваюся, це допоможе, Мартіне

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

Чи не перетворення геометрії на конкретний SRID (наприклад, EPSG: 3857) було б краще, ніж просто множення на масштабний коефіцієнт?
Ніколаус Крисмер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.