Criando grade regular de polígonos no PostGIS?


61

Como criar, na forma de um polígono, uma grade regular de polígonos / quadrados de um determinado tamanho, no postgis?

Eu pensei em uma função como Como criar uma grade de pontos regular dentro de um polígono no Postgis? somente para quadrados, de modo que os quadrados possam ter 5m x 5m ou até 10m x 10m. Mas não tem idéia de mudar isso da maneira correta.


2
A generalização que você procura não é clara. Você está dizendo que começa com um polígono único (arbitrário) e deseja ladrilhar o avião com cópias congruentes dele? Em geral, isso não é possível, mas talvez esse polígono tenha propriedades particulares (talvez seja conhecido como um paralelogramo, triângulo ou hexágono, por exemplo).
whuber

Respostas:


60

Aqui está uma função de retorno definida ST_CreateFishnetque cria uma grade 2D de geometrias de polígonos:

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;

onde nrowe ncolsão o número de linhas e colunas, xsizee ysizesão os comprimentos do tamanho da célula, são opcionais x0e y0são coordenadas para o canto inferior esquerdo.

O resultado é rowe colnúmeros, começando em 1 no canto inferior esquerdo e geompolígonos retangulares para cada célula. Então, por exemplo:

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)

Ou para fazer uma única coleção de geometria para a grade completa:

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

Grade 4x6

Você pode adicionar os deslocamentos x0/ y0origin (esses padrão são zero).


1
Obrigado! Agora só tenho que ligar a rede de pesca à caixa do polígono.
Mk.archaeo 1/11

Isso é muito útil .. Eu tenho uma consulta. Como posso criar grades dentro de um polígono / bbox?
Mohammed shafeek

Bom trabalho Mike, isso é muito útil.
Mounaim

56

Aqui está uma variante específica de geração, para uma situação em que você precisa criar uma grade para um mapa geográfico com uma etapa métrica constante (as células podem ser usadas para agrupar valores, por exemplo, densidade de raios em uma região).

A função não é muito elegante, mas não encontrei nenhuma solução melhor para essa tarefa (incluindo a função de Mike Toews acima). Portanto, você tem um polígono vinculado (por exemplo, proveniente de uma interface do Google Maps), tem um valor de etapa em metros:

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

Como usá-lo:

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

Assim, você pode ver que as linhas formatadas pelos polígonos gerados estão ao longo dos paralelos e meridianos geográficos - isso é muito conveniente.

Um exemplo de uma grade com passo de 50 km

Conselho: Se você calcular algo como densidade (por exemplo, mapa de descargas atmosféricas por células) e a grade for gerada dinamicamente Para aumentar o desempenho, sugiro o uso de tabelas temporárias para armazenar células como polígonos de geometria, com um índice espacial na coluna que representa a célula.


Eu gostaria de poder votar novamente ... essa foi uma solução perfeita! e a capacidade de personalizar o sistema de coordenadas é fantástica ~!
precisa saber é o seguinte

Apenas uma sugestão secundária, em vez de usar ST_GeomFromTextao criar uma caixa para adicionar sectors, você pode usar ST_MakeEnvelopee apenas especificar as coordenadas inferior esquerda e superior direita da caixa.
Matt

Isso traz potenciais
nickves 16/01

11

Você pode criar uma grade regular simplesmente vetorizando uma varredura vazia:

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

1
Essa é uma solução tão simples, tendo feito o vetor muitas vezes.
John Powell

6

Eu criei uma variante da função @ Alexander que não exige a transformação em outro SRID. Isso evita a questão de ter que encontrar uma projeção que use medidores como unidades para uma região específica. Usa-se ST_Projectpara caminhar adequadamente usando a projeção fornecida. Também adicionei um width_stepe height_steppara permitir ladrilhos retangulares em vez de exigir que sejam quadrados.

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

Você pode usá-lo assim:

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

Aqui está um algoritmo otimizado e eficiente para criar rede de pesca, grade regular, grade poligonal, grade retangular dentro de qualquer envelope, polígono ou multipolígono. quase lida com qualquer SRID;

Link de repositório do GitHub

insira a descrição da imagem aqui

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;

Use-o com uma consulta simples; A entrada deve ser um polígono, Multipolígono ou envelope válido.

select I_Grid_Regular(st_setsrid(g.geom, 4326), .0001, .0001 ), geom from polygons limit 1
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.