¿Crear una cuadrícula poligonal regular en PostGIS?

61

¿Cómo crear, en la forma de un polígono, una cuadrícula regular de polígonos / cuadrados de un tamaño dado, en postgis?

He pensado en una función como ¿Cómo crear una cuadrícula de puntos regular dentro de un polígono en Postgis? solo para cuadrados, de modo que los cuadrados pueden ser de 5m x 5m o incluso de 10m x 10m. Pero no tengo idea de cambiar esto de la manera correcta.

mk.archaeo
fuente
2
La generalización que busca no está clara. ¿Estás diciendo que comienzas con un único polígono (arbitrario) y deseas enlosar el plano con copias congruentes? En general, esto no es posible, pero tal vez este polígono tiene propiedades particulares (tal vez se sabe que es un paralelogramo, un triángulo o un hexágono, por ejemplo).
whuber

Respuestas:

60

Aquí hay una función de retorno de conjunto ST_CreateFishnetque crea una cuadrícula 2D de geometrías 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;

donde nrowy ncolson el número de filas y columnas, xsizey ysizeson las longitudes del tamaño de celda, y son opcionales x0y y0son coordenadas para la esquina inferior izquierda.

El resultado es rowy colnúmeros, comenzando desde 1 en la esquina inferior izquierda, y geompolígonos rectangulares para cada celda. Así por ejemplo:

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)

O para hacer una única colección de geometría para la cuadrícula completa:

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

Cuadrícula 4x6

Puede agregar las compensaciones x0/ y0origin (estas predeterminadas a cero).

Mike T
fuente
1
¡Gracias! Ahora solo tengo que atar la red al BBox del polígono.
mk.archaeo
Esto es muy útil. Tengo una consulta. ¿Cómo puedo crear cuadrículas dentro de un polígono / bbox?
Mohammed Shafeek
Buen trabajo Mike, esto es muy útil.
Mounaim
56

Aquí hay una variante específica de generación, para una situación en la que necesita crear una cuadrícula para un mapa geográfico con un paso métrico constante (las celdas pueden usarse para agrupar valores, por ejemplo, densidad de rayos en una región).

La función no es muy elegante, pero no encontré ninguna solución mejor para esa tarea (incluida la función de Mike Toews anterior). Entonces, tiene un polígono enlazado (por ejemplo, llegado de una interfaz de Google Maps), tiene un valor de paso en 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';

Cómo usarlo:

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

Entonces puede ver que las líneas formateadas por polígonos generados se encuentran a lo largo de los paralelos geográficos y meridianos, eso es muy conveniente.

Un ejemplo de una cuadrícula con un paso de 50 km.

Consejo: Si calcula algo como la densidad (por ejemplo, el mapa de rayos por celdas), y la cuadrícula se genera dinámicamente Para aumentar el rendimiento, sugeriría usar tablas temporales para almacenar celdas como polígonos de geometría, con un índice espacial en la columna que representa la célula.

Alexander Palamarchuk
fuente
Desearía poder votar esto de nuevo ... ¡esta fue una solución perfecta! ¡y la capacidad de personalizar el sistema de coordenadas es fantástica ~!
DPSSpatial
Solo una sugerencia menor, en lugar de usar ST_GeomFromTextal crear un cuadro para agregar sectors, puede usar ST_MakeEnvelopey solo especificar las coordenadas inferior izquierda y superior derecha del cuadro.
Matt
Esto trae potenciales
nickves
11

Puede crear una cuadrícula regular simplemente vectorizando un ráster vacío:

SELECT (ST_PixelAsPolygons(ST_AddBand(ST_MakeEmptyRaster(100, 100, 1.1, 1.1, 1.0), '8BSI'::text, 1, 0), 1, false)).geom
Pierre Racine
fuente
1
Esa es una solución tan simple, haberlo hecho de forma vectorial tantas veces.
John Powell
6

He creado una variante de la función de @ Alexander que no requiere que nos transformemos a otro SRID. Esto evita el problema de tener que encontrar una proyección que use medidores como unidades para una región en particular. Se utiliza ST_Projectpara caminar adecuadamente usando la proyección dada. También he agregado un width_stepy height_steppara permitir mosaicos rectangulares en lugar de requerir que sean cuadrados.

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

Puedes usarlo así:

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;
Mate
fuente
5

Aquí hay un algoritmo optimizado y eficiente para crear mallas, cuadrículas regulares, cuadrículas de polígonos, cuadrículas rectangulares dentro de cualquier sobre, polígono o multipolígonos. casi manejar cualquier SRID;

Enlace de repositorio de GitHub

ingrese la descripción de la imagen aquí

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;

Úselo con una consulta simple; la entrada debe ser un polígono, multipolígono o sobre válido.

select I_Grid_Regular(st_setsrid(g.geom, 4326), .0001, .0001 ), geom from polygons limit 1
Muhammad Imran Siddique
fuente