Konvertieren Sie Scheitelpunkte von Polygon-Features mithilfe der virtuellen Ebene in QGIS in Punkte

Dec 06 2020

Ich habe eine Polygonebene und möchte eine virtuelle Ebene verwenden, die Punkte aus den Scheitelpunkten der Polygonebene erstellt. Die Frage ist, wie man eine Abfrage schreibt, die dies tut.

Ich bin mir des Tools bewusst "Extract vertices", aber das ist nicht das, wonach ich suche.

Was ich versucht habe:

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

nodes_to_points()ist ein QGIS-Ausdruck, aber die Dokumentation sagt :

Funktionen von QGIS-Ausdrücken können auch in einer Abfrage auf einer virtuellen Ebene verwendet werden.

Es sagt jedoch nichts darüber aus, wie es in die (SQL-) Abfragesyntax aufgenommen werden soll. Update: Es scheint, dass nicht alle QGIS-Ausdrücke in der virtuellen Schicht verwendet werden können. Hier finden Sie eine Liste der Funktionen, die verwendet werden können: Auflistung aller in der virtuellen Schicht von QGIS verfügbaren Funktionen

  1. Ich habe auch in der Referenzliste der SQL-Funktionen von SpatiaLite 4.2.0 nachgesehen , konnte jedoch keine Funktion zum Erstellen einzelner Punkte für jeden Scheitelpunkt finden. Das nächste, was ich kam, war ST_DissolvePoints():

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

Dadurch wird der visuelle Effekt von Scheitelpunkten erzeugt, jedoch nicht als einzelne Punkte, sondern als einzelnes Mehrpunkt-Feature pro Polygon:

Antworten

2 J.Monticolo Dec 18 2020 at 06:01

Hier meine Lösung:


Requirements

  • QGIS 3. x
  • Eine (Multi) -Polygon-Ebene mit einem idFeld mit unterschiedlichen Werten

Features

  • Eine Abfrage
  • Arbeitet mit Multipolygonen
  • Konfigurieren Sie den Ebenennamen nur an einer Stelle
  • Geben Sie die Ebene id, die Teilenummer, die Ringnummer und die Scheitelpunktnummer aus
  • Löschen Sie den letzten doppelten Scheitelpunkt (in einem Polygon ist Scheitelpunktstart = Scheitelpunktende).

  1. Öffnen Sie QGIS, gehen Sie zu Database > Database > DB Manager... > Virtual Layers
  2. Öffne ein neues SQL Window
  3. Kopieren Sie das folgende SQL-Skript und ersetzen Sie es im lyrTeil polygonsdurch Ihren tatsächlichen Ebenennamen (oder benennen Sie Ihre Ebene unter QGIS um 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

Leider sind die Funktionen ST_Dump()und ST_DumpPoints()noch nicht in Virtual Layer implementiert. Daher biete ich eine Problemumgehung zum Extrahieren von Scheitelpunkten von Polygon-Features an.

Diese Lösung enthält mehrere Funktionen, nämlich NumInteriorRings(), ST_InteriorRingN(), ST_ExteriorRing(), ST_PointN(), und ST_NPoints().

Jemand kann feststellen, dass diese Lösung in Bezug auf Leistung und Komplexität nicht perfekt ist, da die CROSS JOINKlausel, die redundante Indizes (für Innenringe sowie für Scheitelpunkte) erzeugt, die tatsächlich benötigt werden, eine enorme Ausgabe liefert. Wenn jemand weiß, wie man es entwirrt, können Sie die Abfrage verbessern oder einen Hinweis geben.


Nehmen wir an, es gibt eine Polygonebene namens 'polygons'(siehe Abbildung unten).

Mit der folgenden Abfrage

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

Es ist möglich, alle Eckpunkte zu erhalten

Das Ergebnis stimmt mit der Ausgabe des Geoalgorithmus "Eckpunkte extrahieren " überein.


Erklärungen:

  • Vergessen Sie in der obigen Abfrage nicht , die vierfache "polygons"Zeichenfolge in Ihren tatsächlichen Namen der Polygonebene zu ändern

MACHEN:

  • Erweitern Sie den Erklärungsabschnitt
  • versuchen Sie es mit Multipolygonen
  • Fügen Sie ein neues Feld hinzu "id"

Verweise:

  • Verwenden von RECURSIVE in Virtual Layer