Correction - Exercice 3 : Géométries et calcul de champ

Exemple de script permettant de répondre à la question en créant un champ “NEIGHBORS” et un champ “SUM” pour chaque entité de la couche puis en récupérant :

  • la valeur du champ “SUM” pour la Hongrie

  • le nombre de pays sans voisin

Exercice3.py

from qgis.utils import iface
from PyQt5.QtCore import QVariant

# Nom des champs à utiliser :
_NAME_FIELD = 'NAME'
_SUM_FIELD = 'POP_EST'

# Nom des champs à créer
_NEW_NEIGHBORS_FIELD = 'NEIGHBORS'
_NEW_SUM_FIELD = 'SUM'

layer = iface.activeLayer()

# Création des deux nouveaux champs après avoir activé le mode édition
layer.startEditing()
layer.dataProvider().addAttributes(
        [QgsField(_NEW_NEIGHBORS_FIELD, QVariant.String),
         QgsField(_NEW_SUM_FIELD, QVariant.LongLong)])
layer.updateFields()

# Construction d'un index spatial :
index = QgsSpatialIndex()
for f in layer.getFeatures():
    index.insertFeature(f)

# Parcours de l'ensemble de nos entités :
for f in layer.getFeatures():
    # print('Calcul en cours pour {}'.format(f[_NAME_FIELD]))
    geom = f.geometry()
    # Recherche des entités qui intersectent la bbox de l'entité courrante
    # L'utilisation d'un index spatial permet à ce moment de n'itérer que
    # sur les entités qui intersectent sa bbox et non pas sur l'ensemble
    # des entités.
    intersecting_ids = index.intersects(geom.boundingBox())
    # Pour stocker la liste des voisins :
    neighbors = []
    neighbors_sum = 0
    request = QgsFeatureRequest().setFilterFids(intersecting_ids)
    for intersecting_f in layer.getFeatures(request):
        # Ici on considère qu'une entité est voisine si elle touche
        # l'entité en cours. Nous utilisons donc le prédicat spatial
        # 'touches' pour vérifier cela:
        if intersecting_f.geometry().touches(geom):
            neighbors.append(intersecting_f[_NAME_FIELD])
            neighbors_sum += intersecting_f[_SUM_FIELD]
    f[_NEW_NEIGHBORS_FIELD] = ','.join(neighbors)
    f[_NEW_SUM_FIELD] = neighbors_sum
    # Mise à jour de l'entité modifiée :
    layer.updateFeature(f)

layer.commitChanges()

# Récupération de l'entité correspondant à la Hongrie
feature_hungary = list(layer.getFeatures("\"ISO_A2\" is 'HU'"))[0]
# On lit la valeur de son champ 'SUM'
print("Les pays voisins de la Hongrie totalisent environ {} habitants"
    .format(feature_hungary['SUM']))

# Pour savoir la nombre de pays sans voisins
# on va lister ceux qui ont une valeur NULL
# dans le champ 'NEIGHBORS'
features_without_neighbors = list(layer.getFeatures("\"NEIGHBORS\" is NULL"))
# On regarde la longeur de la liste obtenue en résultat
print("{} pays n'ont pas de voisin"
    .format(len(features_without_neighbors)))

➜ Les pays voisins de la Hongrie totalisent environ 91 173 260 habitants.

83 pays n’ont pas de voisin