Creazione di una griglia poligonale regolare in PostGIS?


61

Come creare, a forma di poligono, una griglia regolare di poligoni / quadrati di una determinata dimensione, in postgis?

Ho pensato a una funzione come Come creare una griglia di punti regolare all'interno di un poligono in Postgis? solo per i quadrati, in modo che i quadrati possano essere 5m x 5m o anche 10m x 10m. Ma non ho idea di cambiarlo nel modo corretto.


2
La generalizzazione che cerchi non è chiara. Stai dicendo che inizi con un singolo poligono (arbitrario) e desideri tessere l'aereo con copie congruenti di esso? In generale questo non è possibile, ma forse questo poligono ha proprietà particolari (forse è noto per essere un parallelogramma, un triangolo o un esagono, per esempio).
whuber

Risposte:


60

Ecco una funzione di ritorno impostata ST_CreateFishnetche crea una griglia 2D di geometrie poligonali:

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;

dove nrowe ncolsono il numero di righe e colonne xsizee ysizesono le lunghezze della dimensione della cella e facoltativo x0e y0sono coordinate per l'angolo in basso a sinistra.

Il risultato è numeri rowe col, a partire da 1 nell'angolo in basso a sinistra, e geompoligoni rettangolari per ogni cella. Quindi per esempio:

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)

Oppure per creare una raccolta di geometrie singole per l'intera griglia:

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

Griglia 4x6

È possibile aggiungere gli offset x0/ y0origin (predefiniti a zero).


1
Grazie! Ora devo solo legare la rete al BBox del poligono.
mk.archaeo,

Questo è molto utile .. Ho una domanda. Come posso creare griglie all'interno di un poligono / bbox?
Mohammed Shafeek,

Bel lavoro Mike, questo è molto utile.
Mounaim,

56

Ecco una variante specifica di generazione, per una situazione in cui è necessario creare una griglia per una mappa geografica con un passo metrico costante (le celle potrebbero essere utilizzate per raggruppare i valori, ad esempio la densità del fulmine in una regione).

La funzione non è molto elegante, ma non ho trovato alcuna soluzione migliore per tale compito (inclusa la funzione di Mike Toews sopra). Quindi hai un poligono associato (ad es. Arrivato da un'interfaccia di Google Maps), hai un valore di passo in metri:

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

Come 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

Quindi puoi vedere che le linee formattate dai poligoni generati si trovano lungo i paralleli geografici e i meridiani: è molto conveniente.

Un esempio di griglia con passo di 50 km

Consiglio: se si calcola qualcosa di simile alla densità (ad es. Mappa dei fulmini per celle) e la griglia viene generata in modo dinamico Per aumentare le prestazioni, suggerirei di utilizzare tabelle temporanee per la memorizzazione di celle come poligoni geometrici, con un indice spaziale sulla colonna che rappresenta la cellula.


Vorrei poter votare di nuovo questo ... questa è stata una soluzione perfetta! e la possibilità di personalizzare il sistema di coordinate è fantastica ~!
DPSSpatial

Solo un piccolo suggerimento, invece di utilizzare ST_GeomFromTextquando si crea una casella da aggiungere sectors, è possibile utilizzare ST_MakeEnvelopee specificare semplicemente le coordinate in basso a sinistra e in alto a destra della casella.
Matt,

Questo porta potenziali
Nick Nick

11

Puoi creare una griglia normale semplicemente vettorializzando un raster vuoto:

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

1
Questa è una soluzione così semplice, dopo averlo fatto in modo vettoriale così tante volte.
John Powell,

6

Ho creato una variante della funzione di @ Alexander che non richiede la trasformazione in un altro SRID. Ciò evita il problema di dover trovare una proiezione che utilizza i contatori come unità per una particolare regione. Usa ST_Projectper camminare correttamente usando la proiezione data. Ho anche aggiunto un width_stepe height_stepper consentire le tessere rettangolari invece di richiedere che siano quadrate.

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

Puoi usarlo così:

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

Ecco un algoritmo ottimizzato ed efficiente per creare reti a rete, griglia regolare, griglia poligonale, griglia rettangolare all'interno di qualsiasi inviluppo, poligono o multipoli. quasi gestire qualsiasi SRID;

GitHub Repo Link

inserisci qui la descrizione dell'immagine

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;

Usalo con una semplice query; l'input deve essere un poligono, un multipoligono o una busta validi.

select I_Grid_Regular(st_setsrid(g.geom, 4326), .0001, .0001 ), geom from polygons limit 1
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.