Convertir les sommets d'entités surfaciques en points à l'aide de Virtual Layer dans QGIS
J'ai une couche de polygones et je souhaite utiliser une couche virtuelle qui crée des points à partir des sommets de la couche de polygones. La question est de savoir comment écrire une requête qui fait cela?
Je connais l'outil "Extract vertices"
, mais ce n'est pas ce que je recherche.
Ce que j'ai essayé:
select fid, nodes_to_points(p.geometry) as vertices from polygon as p
nodes_to_points()
est une expression QGIS, mais la documentation dit :
Les fonctions des expressions QGIS peuvent également être utilisées dans une requête de couche virtuelle.
Cependant, il ne dit rien comment l'inclure dans la syntaxe de requête (SQL). Mise à jour: il semble que toutes les expressions QGIS ne puissent pas être utilisées dans la couche virtuelle, voir ici pour une liste des fonctions pouvant être utilisées: Liste de toutes les fonctions disponibles dans la couche virtuelle de QGIS
J'ai également regardé dans la liste de référence des fonctions SQL de SpatiaLite 4.2.0 , mais je n'ai trouvé aucune fonction pour créer des points individuels pour chaque sommet. Le plus proche que je suis venu utilisait
ST_DissolvePoints()
:select fid, ST_DissolvePoints (p.geometry) as vertices from polygon as p
Cela crée l'effet visuel des sommets, mais pas en tant que points individuels, mais en une seule entité multipoint par polygone:

Réponses
Voici ma solution:
Requirements
- QGIS 3. x
- Une couche (multi) -polygone avec un
id
champ avec des valeurs distinctes
Features
- Une requête
- Fonctionne avec des multipolygones
- Configurer le nom de la couche à un seul endroit
- Sortie du calque
id
, du numéro de pièce, du numéro de l'anneau et du numéro de sommet - Déposer le dernier sommet dupliqué (dans un polygone, début du sommet = fin du sommet)
- Ouvrez QGIS, allez à Database > Database > DB Manager... > Virtual Layers
- Ouvrez un nouveau SQL Window
- Copiez le script SQL ci-dessous et remplacez, dans la lyrpièce,
polygons
par votre vrai nom de couche (ou renommez sous QGIS votre couchepolygons
):
-- 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
Malheureusement, les fonctions ST_Dump()et ST_DumpPoints()ne sont pas encore implémentées dans Virtual Layer. Par conséquent, je propose une solution de contournement sur la façon d'extraire des sommets d'entités polygonales.
Cette solution intègre plusieurs fonctions, à savoir NumInteriorRings(), ST_InteriorRingN(), ST_ExteriorRing(), ST_PointN(), et ST_NPoints().
Quelqu'un peut trouver que cette solution n'est pas parfaite en termes de performances et de complexité, en raison de la vaste sortie de la CROSS JOINclause qui produit des index redondants (pour les anneaux intérieurs ainsi que pour les sommets) qui sont réellement nécessaires. Si quelqu'un sait comment le démêler, n'hésitez pas à améliorer la requête ou à donner un indice.
Supposons qu'il existe une couche de polygones appelée 'polygons'
, voir l'image ci-dessous.

Avec la requête suivante
-- 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
il est possible d'obtenir tous les sommets

Le résultat correspond à la sortie du géoalgorithme "Extraire les sommets" .
Explications:
- Dans la requête ci - dessus, n'oubliez pas de changer quatre fois la
"polygons"
chaîne en votre nom de couche de polygone réel
FAIRE:
- étendre la section d'explication
- essayer avec des multipolygones
- ajouter un nouveau champ
"id"
Les références:
- Utilisation de RECURSIVE dans Virtual Layer