Postgisのポリゴン内に通常のポイントグリッドを作成する方法は?


31

postgisでx、y間隔の点の規則的なグリッドをポリゴン内に作成する方法は?例のように:

代替テキスト


このコードを「postGIS in action」ダイシングコードとマージするクリッピングポリゴンを作成しようとしましたが、作成されるポリゴンは1つだけです。何か忘れましたか?関数の作成または置換makegrid(geometry、integer、integer)ジオメトリAS 'SELECT st_intersection(g1.geom1、g2.geom2)AS geom FROM(SELECT $ 1 AS geom1)AS g1 INNER JOIN(Select st_setsrid(CAST(ST_MakeBox2d(st_setsrid( ST_Point(x、y)、$ 3)、st_setsrid(ST_Point(x + $ 2、y + $ 2)、$ 3))as geometry)、$ 3)as geom2 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(
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;

ここで、最初の引数はグリッドを配置するポリゴンであり、2番目の引数はグリッド内のポイント間の距離です。

行ごとに1つのポイントが必要な場合は、ST_Dumpを次のように使用します。

SELECT (ST_Dump(makegrid(the_geom, 1000))).geom as the_geom from mytable;

HTH

ニクラス


2
st_setSRID()をst_point関数に追加する必要がある場合があります。そうしないと、st_intersectsは機能しません。
JaakL

テスト済みのバージョンを個別の回答として追加しました。
JaakL

12

私が拾ってきたニクラス・エイヴンは、機能コードを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

この関数を使用するには、NicklasAvénが書いたとおりに行います。

SELECT makegrid(the_geom, 1000) from mytable;

または、行ごとに1つのポイントが必要な場合:

SELECT (ST_Dump(makegrid(the_geom, 1000))).geom as the_geom from mytable;

これが誰かに役立つことを願っています。

アレックス


SRIDエラーのため、受け入れられた回答は私のデータでは機能しません。この変更はより効果的です。
ビタリーイザエフ14年

ポリゴンが反時計回りを横切っているときに何かを追加したいですか?xmin / xmaxで問題が発生すると想像できます。
トーマス

2
私にはうまくいきませんでした。Postgres 9.6およびPostGIS 2.3.3の使用。generate_series呼び出し内で、これを2番目のパラメーター「ceiling(st_xmax($ 1)-st_xmin($ 1)):: int」および「ceiling( st_ymax($ 1)):: int型天井」の代わりに"(st_ymax($ 1)-st_ymin($ 1)):: INT"
ビトーSapucaia

私は前のコメントを承認します。generate_seriesの上限は、差の上限(max-min)ではなく、maxの上限でなければなりません。
R.ブルジョン

10

wgs​​84ジオメトリを使用している人は、おそらくこの機能に問題があるでしょう。

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ポイントのみが収集されます... =>空の結果!

私の問題は、WSG84などの浮動小数点数で10進数の距離でgenerate_series()を使用できないようでした...これが、とにかく機能するように関数を微調整した理由です:

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を使用して、すべてのグリッドポイントに必要な高度を計算します)。現在、ポリゴンの周囲に配置されているポイントを含める方法だけが欠落しています。現在、レンダリングされたシェイプは、エッジで多少切り捨てられています。
ジュリアンガルシア

他のすべてのソリューションが失敗したとき、働いた、ありがとう!
ヨルダンアルセーノ14

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_XMinST_XMaxST_YMinおよびST_YMaxは、ポリゴンの最小および最大座標を取得します。
  • ST_Containsは、ポリゴンにポイントが含まれているかどうかをテストします。
  • そして、のST_Pointはポイントを作成します。

がんばろう!


1
大きな複雑なポリゴンエリアがある場合(たとえば、海岸線のバッファがある場合)、このアプローチはあまり最適ではないことに注意してください。
JaakL

それでは、代わりに何を提案しますか?
ジュリアン

4

異なる方法を使用する3つのアルゴリズム。

Githubリポジトリリンク

  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. NicklasAvénアルゴリズムに基づく2番目の関数。SRIDを処理できるように拡張しました。

    アルゴリズムの以下の変更をアップグレードしてください。

    1. ピクセルサイズのx方向とy方向の変数、
    2. 回転楕円体または楕円体の距離を計算する新しい変数。
    3. SRIDを入力し、Geomを回転楕円体または楕円体データムの作業環境に関数変換し、各側に距離を適用し、結果を取得し、入力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

1
こんにちは、makegrid関数で空の結果を取得します。シェイプファイルは、shp2pgsqlを使用してPostGISにインポートされました。何が問題を引き起こしているのかわかりません。srsはwgs84に設定されています。
ミハルツィンマーマン

3

確かに高速で理解しやすい別のアプローチがあります。

たとえば、1000m x 1000mのグリッドの場合:

SELECT (ST_PixelAsCentroids(ST_AsRaster(the_geom,1000.0,1000.0))).geom 
FROM the_polygon

元のSRIDも保持されます。

このスニペットは、ポリゴンのジオメトリを空のラスターに変換してから、各ピクセルをポイントに変換します。利点:元のポリゴンがポイントと交差するかどうかを再度確認する必要はありません。

オプション:

パラメータgri​​dxおよび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)

1

前の回答に対する小さな潜在的な更新-wgs84のスケールとしての3番目の引数(または通常のものには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など)に変換する方が、単にスケールファクターを乗算するよりも優れていませんか?
ニコラウスクリスマー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.