Comment faire référence à une autre couche dans la calculatrice de champ?

26

Existe-t-il un moyen de sélectionner un attribut dans une couche de polygones et d'insérer la valeur dans un champ virtuel d'une couche de points en utilisant "dedans" dans la calculatrice de champ?

CASE
 WHEN within($geometry, geometry_polygon) THEN attribute_polygon
END

entrez la description de l'image ici

Mer lunaire
la source
1
Pourquoi ne pas utiliser le plugin «Outil d'échantillonnage ponctuel» pour cela?
Jakob
Parce que j'ai besoin de mises à jour dynamiques lors de la création de nouveaux points ou du déplacement de points existants.
Lunar Sea
Vous feriez mieux de scénariser cette interaction plutôt que de compter sur des outils prêts à l'emploi.
nagytech
Malheureusement, je n'ai aucune expérience de script.
Lunar Sea
@LunarSea J'ai écrit un exemple ci-dessous à suivre, mais vous devrez peut-être le modifier pour l'adapter à vos besoins.
nagytech

Réponses:

22

Les jointures spatiales sont disponibles dans le calculateur de champ après l'installation du plugin refFunctions.

geomwithin(targetLayer,targetField)
Mer lunaire
la source
Les plug-ins sont beaucoup plus simples que d'utiliser un script personnalisé. Merci!
jpmc26
geomwithin ('targetLayer', 'targetField').
Raja
19

Par défaut, la calculatrice de champ ne prend pas en charge les jointures spatiales entre les couches d'entités. Mais, si vous jetez un œil à la publication de NathanW sur l' éditeur de fonction pour les expressions qgis, vous pourrez constater que nous pouvons écrire notre propre interaction de données.

Le script suivant vous permettra d'exprimer ce que vous recherchez. Cela fonctionne en itérant à travers toutes les entités sur la couche polygonale et s'il y a une jointure spatiale, puis référencez les données tabulaires de la colonne spécifiée:

from qgis.core import *
from qgis.gui import *
from qgis.utils import iface

allfeatures = None
index = QgsSpatialIndex()
indexMade = 0
refLayer = None

@qgsfunction(args="auto", group='Custom')
def spatialJoinLookup(layerName, refColumn, defaultValue, geom, feature, parent):

    if geom is None:
        return defaultValue

    # globals so we don't create the index, refLayer more than once
    global allfeatures
    global index
    global indexMade
    global refLayer

    # Get the reference layer
    if refLayer is None:
        for layer in iface.mapCanvas().layers():
            if layerName == layer.name():
                refLayer = layer
                break
    if refLayer is None:
        raise Exception("Layer [" + layerName + "] not found")

    # Create the index if not exists
    if indexMade == 0:
        index = QgsSpatialIndex()
        allAttrs = layer.pendingAllAttributesList()
        layer.select(allAttrs)
        allfeatures = {feature.id(): feature for (feature) in refLayer.getFeatures()}
        for f in allfeatures.values():
            index.insertFeature(f)
        indexMade = 1

    # Use spatail index to find intersect 
    fid = None
    ids = index.intersects(geom.boundingBox())
    for id in ids:
        fid = id
        break # Only get the first match.
    if fid is not None:
        return allfeatures[fid].attribute(refColumn)

    # Default
    return defaultValue

Exemple de couche de polygone

Vous trouverez ci-dessous un exemple de couche de polygones que vous pourriez avoir. J'ai également créé une couche de points correspondante que vous verrez dans l'image finale.

entrez la description de l'image ici

Utilisation de l'expression

Remarque: si vous souhaitez utiliser une colonne distincte, vous devez modifier le deuxième argument pour qu'il corresponde au nom de la colonne dans le jeu de données du polygone. Par exemple, vous pouvez utiliser la colonne «AreaNumber», mais vous devez faire correspondre le type de colonne dans les paramètres de la calculatrice de champ.

entrez la description de l'image ici

Résultat

Vous pouvez voir que la valeur de colonne par défaut a été appliquée là où il n'y a pas de jointure spatiale et que les autres ont correspondu aux données correctes. Notez que le script que j'ai donné ne se joindra qu'au premier match. Vous auriez besoin de créer une autre logique métier si vos polygones se chevauchaient.

entrez la description de l'image ici

nagytech
la source
Merci beaucoup, votre script fonctionne bien en utilisant 'geom' au lieu de 'geometry' dans la première instruction 'if'. La jonction de géométries de cette manière peut être très utile, par exemple pour créer plusieurs étiquettes sur des polygones.
Lunar Sea
Désolé, je ne sais pas comment j'ai raté ça. J'espère qu'il n'y a pas de problèmes de performances - je ne l'ai essayé qu'avec un très petit sous-ensemble d'enregistrements.
nagytech
N'ayant que plus de 100 fonctionnalités ponctuelles, QGIS a des problèmes de performances. L'ajout d'une nouvelle entité ponctuelle est vraiment pénible, même si aucune entité ponctuelle n'est affichée dans le canevas acutal. Sinon, lorsque le zoom dans QGIS s'accélère. J'ai essayé `CASE WHEN $ scale <10000 THEN spatialJoinLookupI ('Polygons', 'AreaName', 'None', $ geometry) END 'mais cela ne fonctionne pas. Puis-je faire quelque chose pour améliorer les performances?
Lunar Sea
@LunarSea J'ai mis à jour la fonction pour utiliser un index spatial. Cela devrait être raisonnablement plus rapide.
nagytech
Merci de votre aide. La jointure spatiale est beaucoup plus rapide maintenant, mais malheureusement, quelque chose ne fonctionne pas correctement. J'obtiens des résultats différents pour les points d'un même polygone.
Lunar Sea
8

Cela peut être fait dans Field Calculator avec fonction aggregate(). Dans la couche de points, créez un nouveau champ avec une expression de calculatrice de champ comme celle-ci:

aggregate(
layer:= 'polygon_layer_name',
aggregate:='concatenate',
expression:=joining_field_name,
concatenator:=', ',
filter:=intersects($geometry, geometry(@parent))
)

Où le layernom de la couche de polygone est écrit comme une chaîne, aggreagateest une fonction d'agrégation (peut également être utilisé comme somme, etc.), si le expressionchamp des valeurs sera pris, concatenatorjoint la chaîne de caractères (doit être définie, même dans ce cas) et filterfiltre les entités en fonction lors de l'expression (dans ce cas, intersecte la géométrie du calque avec la géométrie du calque parent).

Pour plus d'informations, consultez la documentation Aggregates QGIS .

Pour les mises à jour automatiques, des champs virtuels peuvent être utilisés ou vous pouvez définir l'expression comme valeur par défaut dans les paramètres du formulaire d'attributs dans les propriétés du calque ( documentation sur les paramètres du formulaire d'attribut ).

entrez la description de l'image ici

Oto Kaláb
la source
3
Il convient de noter que les fonctions spatiales (avec geometry(@parent)) ne sont prises en charge qu'à partir de QGIS 3. Juste au cas où quelqu'un lisant ceci utilise toujours la 2.18 ...
she_weeds
Merci. Fonctionne comme un charme.
pensées spatiales