Как создать регулярную точечную сетку внутри многоугольника в Postgis?

31

Как создать внутри многоугольника правильную сетку точек, расположенных на расстоянии x, y в postgis? Как в примере:

альтернативный текст

Pablo
источник
Я пытался сделать обтравочные полигоны, объединяя этот код с кодом кубинга "postGIS in action", но создан только один полигон ... Я что-то забыл? СОЗДАТЬ ИЛИ ЗАМЕНИТЬ ФУНКЦИЮ makegrid (geometry, integer, integer) ВОЗВРАЩАЕТ геометрию AS SELECT st_intersection (g1.geom1, g2.geom2) AS geom FROM (ВЫБРАТЬ $ 1 AS geom1) КАК g1 INNER JOIN (Выбрать st_setsrid (CAST (ST_MakeBox2d (st_sets) ST_Point (x, y), $ 3), st_setsrid (ST_Point (x + $ 2, y + $ 2), $ 3)) как геометрия), $ 3) как geom2 FROM generate_series (floor (st_xmin ($ 1)) :: int, потолок ( st_xmax ($ 1)) :: int, $ 2) как x, generate_series (floor (st_ymin ($ 1)) :: int, потолок (st_ymax (
aurel_nc
посмотрите на мой подробный ответ.
Мухаммед Имран Сиддик

Ответы:

30

Вы делаете это с помощью generate_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;

НТН

Никлас

Никлас Авен
источник
2
Вам может понадобиться добавить st_setSRID () к функциям st_point, иначе st_intersects не работает.
JaakL
Добавил мою проверенную версию в качестве отдельного ответа.
JaakL
12

Я взял код функции Nicklas Avén makegrid и сделал его более универсальным, читая и используя srid из геометрии многоугольника. В противном случае использование многоугольника с определенным 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;

Надеюсь, это будет полезно для кого-то.

Alex

Александр Нето
источник
Принятый ответ не работает с моими данными из-за ошибок SRID. Эта модификация работает лучше.
Виталий Исаев
Возможно, вы захотите что-то добавить, когда полигон пересекает антимеридиан? Я могу предположить, что это вызвало бы проблему с xmin / xmax.
Томас
2
Это не сработало для меня. Использование Postgres 9.6 и PostGIS 2.3.3. Внутри вызова generate_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
Я одобряю предыдущий комментарий; верхняя граница generate_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 

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

Моя проблема была в том, что я не могу использовать generate_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_Contains для проверки, содержит ли полигон точку,
  • и 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 в рабочую среду Spheroid или Ellipsoid Datum, затем примените расстояние к каждой стороне, получите результат и преобразуйте его во входной 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
JaakL
источник
1
Привет, я получаю пустой результат с функцией makegrid. Шейп-файл был импортирован в PostGIS с помощью shp2pgsql. Понятия не имею, что может вызвать проблемы, srs установлен на wgs84.
Михал Циммерман
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;

Может использоваться с:

SELECT makegrid(the_geom,1000.0,100) as geom from the_polygon  
-- makegrid(the_geom,grid_size,alignement)
obchardon
источник
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) не будет лучше, чем просто умножение на коэффициент масштабирования?
Николаус