QGISの仮想レイヤーを使用してポリゴンフィーチャの頂点をポイントに変換します

Dec 06 2020

ポリゴンレイヤーがあり、ポリゴンレイヤーの頂点からポイントを作成する仮想レイヤーを使用したいと思います。問題は、これを行うクエリをどのように作成するかです。

私はツールを知っています"Extract vertices"が、それは私が探しているものではありません。

私が試したこと:

  1.  select fid, nodes_to_points(p.geometry) as vertices
     from polygon as p
    

nodes_to_points()はQGIS式ですが、ドキュメントには次のように書かれています

QGIS式の関数は、仮想レイヤークエリでも使用できます。

ただし、(SQL)クエリ構文に含める方法については何も述べていません。更新:すべてのQGIS式が仮想レイヤーで使用できるわけではないようです。使用できる関数のリストについては、こちらを参照してください:QGISの仮想レイヤーで使用可能なすべての関数のリスト

  1. SpatiaLite 4.2.0 SQL関数のリファレンスリストも調べましたが、頂点ごとに個別のポイントを作成する関数が見つかりませんでした。私が来た最も近いものは使用していましたST_DissolvePoints()

     select fid, ST_DissolvePoints (p.geometry) as vertices
     from polygon as p
    

これにより、頂点の視覚効果が作成されますが、個々のポイントとしてではなく、ポリゴンごとに1つのマルチポイントフィーチャが作成されます。

回答

2 J.Monticolo Dec 18 2020 at 06:01

ここに私の解決策:


Requirements

  • QGIS 3. x
  • 異なる値を持つidフィールドを持つ(マルチ)ポリゴンレイヤー

Features

  • 1つのクエリ
  • マルチポリゴンで動作します
  • レイヤー名を1か所で構成する
  • レイヤーid、部品番号、リング番号、頂点番号を出力します
  • 最後に重複した頂点をドロップします(ポリゴンでは、頂点の開始=頂点の終了)

  1. オープンQGISに行きますDatabase > Database > DB Manager... > Virtual Layers
  2. 新しいを開く SQL Window
  3. 以下のSQLスクリプトをコピーし、lyr一部polygonsを実際のレイヤー名に置き換えます(またはQGISでレイヤーの名前を変更しますpolygons):
-- list parts
WITH RECURSIVE gs_part(id, part) AS (
    SELECT conf_p.id, conf_p.start
    FROM conf_p
    UNION ALL
    SELECT conf_p.id, part + 1
    FROM gs_part gs, conf_p
    WHERE
        gs.id = conf_p.id
        AND gs.part + 1 <= conf_p.stop
),
-- list interior rings
gs_ring(id, part, ring) AS (
    SELECT conf_i.id, conf_i.part, conf_i.start
    FROM conf_i
    UNION ALL
    SELECT conf_i.id, conf_i.part, ring + 1
    FROM gs_ring gs, conf_i
    WHERE
        gs.id = conf_i.id
        AND gs.part = conf_i.part
        AND gs.ring + 1 <= conf_i.stop
),
-- list vertices
gs_vert(id, part, ring, vert) AS (
    SELECT conf_v.id, conf_v.part, conf_v.ring, conf_v.start
    FROM conf_v
    UNION ALL
    SELECT conf_v.id, conf_v.part, conf_v.ring, vert + 1
    FROM gs_vert gs, conf_v
    WHERE
        gs.id = conf_v.id
        AND gs.part = conf_v.part
        AND gs.ring = conf_v.ring
        AND gs.vert + 1 < conf_v.stop
),
--
parts AS (
    SELECT lyr.id, gs_part.part, st_geometryn(lyr.geometry, gs_part.part) AS geometry
    FROM gs_part, lyr
    WHERE lyr.id = gs_part.id
),
--
rings AS (
    SELECT
        'interior' AS info,
        parts.id,
        parts.part,
        gs_ring.ring,
        interior_ring_n(parts.geometry, ring) AS geometry
    FROM gs_ring, parts
    WHERE
        gs_ring.ring > 0
        AND gs_ring.id = parts.id
        AND gs_ring.part = parts.part
    UNION ALL
    SELECT
        'exterior',
        parts.id,
        parts.part,
        0,
        exterior_ring(parts.geometry)
    FROM parts
),
-- configuration
-- for parts
conf_p AS (
    SELECT id, 1 AS start, st_numgeometries(geometry) AS stop
    FROM lyr
),
-- for interior rings
conf_i AS (
    SELECT id, part, 0 AS start, num_interior_rings(geometry) AS stop
    FROM parts
),
-- for vertices
conf_v AS (
    SELECT id, part, ring, 1 AS start, st_npoints(geometry) AS stop
    FROM rings
),
-- for layer
lyr AS (
    SELECT
        'polygons' AS lyr_name, -- ## replace here with the 'layer name' ##
        p.*
    FROM polygons p --  ## replace here with the layer name ##
),
-- get layer crs
crs AS (
    SELECT CAST(
        SUBSTR(layer_property(lyr_name, 'crs'), 6, 15)
        AS integer
    ) AS id
    FROM lyr
    LIMIT 1
)

SELECT
    rings.info,
    rings.id,
    rings.part,
    rings.ring,
    gs_vert.vert AS vertex,
    SetSRID(st_pointn(rings.geometry, gs_vert.vert), crs.id) AS geometry
FROM gs_vert, rings, crs
WHERE
    gs_vert.id = rings.id
    AND gs_vert.part = rings.part
    AND gs_vert.ring = rings.ring
7 Taras Dec 07 2020 at 04:16

残念ながら、ST_Dump()andST_DumpPoints()関数はまだ仮想レイヤーに実装されていません。したがって、ポリゴンフィーチャの頂点を抽出する方法に関する回避策を提供しています。

このソリューションは、すなわち、いくつかの機能を組み込んだNumInteriorRings()、ST_InteriorRingN()、ST_ExteriorRing()、ST_PointN()、とST_NPoints()。

CROSS JOIN実際に必要な冗長インデックス(内部リングと頂点)を生成する句の膨大な出力のために、このソリューションはパフォーマンスと複雑さの点で完全ではないことがわかります。誰かがそれを解明する方法を知っているなら、質問を改善するか、ヒントを与えてください。


と呼ばれるポリゴンレイヤーがあるとしましょう'polygons'。下の画像を参照してください。

次のクエリで

-- generating the second series required to extract all vertices from polygons' contours
WITH RECURSIVE generate_series2(vert) AS (
    SELECT 1
    UNION ALL
    SELECT vert + 1
    FROM generate_series2, max_num_points_per_feature
    WHERE vert + 1 <= stop2
),

-- finding max number of vertices in all features
max_num_points_per_feature AS (
    SELECT MAX(st_npoints(geom1)) AS stop2
    FROM inter_outer_contours
),

-- union of geometries of both outer and inner contours, represented as polylines
inter_outer_contours AS (
    -- interior rings from all polygons
    WITH interior AS (
        -- generating the first series required to extract all interior rings
        WITH RECURSIVE generate_series1(ring) AS (
            SELECT 1
            UNION ALL
            SELECT ring + 1
            FROM generate_series1, max_num_rings_per_polys
            WHERE ring + 1 <= stop1
        ),

        -- finding max number of interior rings within all polygons
        max_num_rings_per_polys AS (
            SELECT MAX(numrings) AS stop1
            FROM num_rings_per_poly
        ),

        -- finding how many interior rings each polygon has
        num_rings_per_poly AS (
            SELECT id, NumInteriorRings(geometry) AS numrings
            FROM "polygons"
            GROUP BY id
        )
        
        -- query to extract all interior rings from all polygons
        SELECT p.id AS origin, -- a field represents the original polygon id
               'interior' AS info, -- a text field represents the interior attribute
               interior_ring_n(geometry, ring) AS geom1 -- setting geometry for a polyline
        FROM "polygons" AS p
        CROSS JOIN generate_series1
        WHERE geom1 IS NOT NULL -- no null geometries accepted, needed because of the cross join
    ),

    -- exterior rings from all polygons
    exterior AS (
        SELECT p.id AS origin, -- a field represents the original polygon id
               'exterior' AS info, -- a text field represents the exterior attribute 
               st_exteriorring(geometry) AS geom1 -- setting geometry for a polyline
        FROM "polygons" AS p
    )

    -- a union between interior and exterior rings from all polygons
    SELECT *
    FROM interior
    UNION
    SELECT *
    FROM exterior
)

-- query to achieve all vertices from all polygons
SELECT ioc.origin,
       ioc.info,
       gs2.vert, -- a field represents the vertice index
       st_pointn(ioc.geom1, gs2.vert) AS geom2 -- setting geometry for a point
FROM inter_outer_contours AS ioc
JOIN generate_series2 AS gs2
WHERE geom2 IS NOT NULL -- no null geometries accepted, needed because of the cross join

すべての頂点を取得することが可能です

結果は、「頂点の抽出」地理アルゴリズムの出力と一致します。


説明:

  • 上記のクエリでは、4回の文字列を実際のポリゴンレイヤー名に変更することを忘れないでください"polygons"

TODO:

  • 説明セクションを拡張する
  • マルチポリゴンで試してください
  • 新しいフィールドを追加します "id"

参照:

  • 仮想レイヤーでのRECURSIVEの使用