Tạo lưới đa giác thông thường trong PostGIS?


61

Làm thế nào để tạo, trên hình dạng của một đa giác, một lưới đa giác / hình vuông thông thường có kích thước nhất định, trong postgis?

Tôi đã nghĩ về một chức năng như Làm thế nào để tạo lưới điểm thông thường bên trong một đa giác trong Postgis? chỉ dành cho hình vuông, sao cho hình vuông có thể là 5m x 5m hoặc thậm chí 10m x 10m. Nhưng không có ý tưởng để thay đổi điều này một cách chính xác.


2
Sự khái quát mà bạn tìm kiếm không rõ ràng. Bạn đang nói rằng bạn bắt đầu với một đa giác đơn (tùy ý) và muốn xếp mặt phẳng với các bản sao đồng dạng của nó? Nói chung, điều này là không thể, nhưng có lẽ đa giác này có các thuộc tính cụ thể (có lẽ nó được biết đến là hình bình hành, hình tam giác hoặc hình lục giác chẳng hạn).
whuber

Câu trả lời:


60

Đây là một hàm trả về được thiết lập ST_CreateFishnetđể tạo ra một lưới 2D của hình học đa giác:

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;

trong đó nrowncollà số lượng hàng và cột, xsizeysizelà độ dài của kích thước ô, và tùy chọn x0y0là tọa độ cho góc dưới bên trái.

Kết quả là rowvà các colsố, bắt đầu từ 1 ở góc dưới bên trái và geomđa giác hình chữ nhật cho mỗi ô. Ví dụ:

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)

Hoặc để tạo một bộ sưu tập hình học duy nhất cho toàn lưới:

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

Lưới 4x6

Bạn có thể thêm x0/ y0offset gốc (chúng được mặc định là 0).


1
Cảm ơn! Bây giờ tôi chỉ cần liên kết fishnet với BBox của đa giác.
mk.archaeo

Điều này rất hữu ích .. Tôi có một truy vấn. Làm cách nào để tạo lưới trong đa giác / bbox?
Mohammed shafeek

Nice Work Mike, Điều này rất hữu ích.
Mounaim

56

Đây là một biến thể cụ thể của việc tạo, cho tình huống khi bạn cần tạo lưới cho bản đồ địa lý với bước số liệu không đổi (các ô có thể được sử dụng để nhóm các giá trị, ví dụ mật độ sét trong một vùng).

Chức năng không phải là rất thanh lịch, nhưng tôi đã không tìm thấy giải pháp nào tốt hơn cho nhiệm vụ đó (bao gồm chức năng của Mike Toews ở trên). Vì vậy, bạn có một đa giác bị ràng buộc (ví dụ: đến từ giao diện Google Maps), có giá trị bước tính bằng mét:

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

Làm thế nào để sử dụng nó:

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

Vì vậy, bạn có thể thấy rằng các dòng được định dạng bởi các đa giác được tạo nằm dọc theo vĩ tuyến địa lý và kinh tuyến - điều đó rất thuận tiện.

Một ví dụ về lưới điện với bước 50 km

Lời khuyên: Nếu bạn tính toán một cái gì đó như mật độ (ví dụ: bản đồ các tia sét theo các ô) và lưới được tạo động để tăng hiệu suất, tôi sẽ đề xuất sử dụng các bảng tạm thời để lưu trữ các ô dưới dạng đa giác hình học, với chỉ số không gian trên cột thể hiện tế bào.


Tôi ước tôi có thể bình chọn lại lần nữa ... đây là một giải pháp hoàn hảo! và khả năng tùy chỉnh hệ tọa độ thật tuyệt vời ~!
DPSSpatial

Chỉ là một gợi ý nhỏ, thay vì sử dụng ST_GeomFromTextkhi tạo hộp để thêm vào sectors, bạn có thể sử dụng ST_MakeEnvelopevà chỉ định tọa độ dưới cùng bên trái và trên cùng bên phải của hộp.
Matt

Điều này mang lại tiềm năng
biệt danh

11

Bạn có thể tạo một lưới thông thường chỉ đơn giản là vectơ một raster trống:

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

1
Đó là một giải pháp đơn giản như vậy, đã thực hiện nó theo cách vector rất nhiều lần.
John Powell

6

Tôi đã tạo một biến thể của chức năng @ Alexanders không yêu cầu chúng tôi chuyển đổi sang SRID khác. Điều này tránh được vấn đề phải tìm một phép chiếu sử dụng mét làm đơn vị cho một khu vực cụ thể. Nó sử dụng ST_Projectđể sải chân đúng cách sử dụng phép chiếu đã cho. Tôi cũng đã thêm width_stepheight_stepcho phép gạch hình chữ nhật thay vì yêu cầu chúng là hình vuông.

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

Bạn có thể sử dụng nó như vậy:

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

Dưới đây là một thuật toán tối ưu và hiệu quả để tạo fishnet, lưới thông thường, lưới đa giác, lưới hình chữ nhật bên trong bất kỳ đường bao, đa giác hoặc Multipolygons. gần như xử lý bất kỳ SRID nào;

Liên kết Repit GitHub

nhập mô tả hình ảnh ở đây

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;

Sử dụng nó với một truy vấn đơn giản; đầu vào phải là đa giác, Đa giác hoặc đường bao hợp lệ.

select I_Grid_Regular(st_setsrid(g.geom, 4326), .0001, .0001 ), geom from polygons limit 1
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.