Sélection intelligente
Téléchargez les couches decathlon_france et iso_iris. Ce sont des points et leurs zones isochrones constituées de portions d'IRIS à moins 15 minutes en voiture de ces points.
Mettez une transparence complète sur les zones isochrones.
Maintenant que vous savez faire des sélections et enregistrer des fonctions dans la mémoire Python, voyons un exemple concret d'usage de fonction dans un SIG.
Imaginez que vous ayez une couche contenant des entités se superposant. Il est malaisé de les examiner sans faire de sélection attributaire, jouer sur la symbologie, les styles ou l'ordre d'affichage... Créons donc une fonction afin de les faire resortir suite à un événement (ici, un clic sur une entité, ou une simple sélection manuelle de quelques entités).
Mais certaines de ces zones se superposent, et les IRIS, tout ou partie, existent autant de fois qu'il appartiennent à une zone. Le code suivant va sélectionner les IRIS d'une zone isochrone après sélection d'un magasin (et donc faire apparaître la zone isochrone concernée).
layer_selected = QgsProject.instance().mapLayersByName("decathlon_france")[0] layer_to_select = QgsProject.instance().mapLayersByName("iso_iris")[0] def SelectionAuto(): selected_features = layer_selected.selectedFeatures() for i in selected_features: attrs = i.__geo_interface__ id_mag = i['id'] #print (id_mag) myselect = layer_to_select.getFeatures( QgsFeatureRequest().setFilterExpression ( u'"id_mag" = \'%s\'' % id_mag ) ) layer_to_select.selectByIds( [ f.id() for f in myselect ] ) #iface.mapCanvas().zoomToSelected(layer_to_select) layer_selected.selectionChanged.connect(SelectionAuto)
Dans la suite de cet article, vous apprendrez à faire des symbologies en Python, vous pourrez donc enrichir ce type de fonction.
- Note personnelle pour plus tard : enrichir la fonction d'une symbologie et d'un ordre d'affichage des entités sélectionnées. L'objectif étant de ne plus avoir à mettre de la transparence sur la couche pour rendre cette fonction utile.
Action enregistrée
Mais à ce stade votre sélection intelligente ne fonctionnera que tant que votre projet QGIS est ouvert. Fermez-le, et le code Python ne sera pas pris en compte à sa prochaine ouverture.
Pour enregistrer ce code dans votre projet, il faudra l'enregistrer au niveau des Actions d'une couche. Allez dans les propriétés d'une de vos couches (n'importe laquelle pour ce qui nous concerne, mais celle des magasins est la plus à propos), onglet Actions, choisissez le type Python (😄), nommez votre action, décrivez-la brièvement et collez-y le précédent code. Cochez également l'option Canvas Scope afin de la faire apparaître dans le menu de QGIS.
Votre code est désormais activable dans votre projet QGIS sous l'icône Actions, par un clic sur celle-ci, suivi d'un autre sur la couche stockant l'action (avec la petite croix qui apparaîtra). Sélectionnez maintenant un magasin, toute sa zone isochrone sera aussi sélectionnée.
Les 3 lois d'Asimov
Cependant si les machines n'ont a priori pas de sentiment, elles sont tout-de-même capricieuses, et des événements inopportuns peuvent se produire.
En effet votre fonction n'a de sens qu'après sélection d'un seul magasin, d'ailleurs elle ne sélectionne finalement que les entités liées à un seul magasin. Alors que se passe-t-il en cas de sélection de plusieurs magasins ? De plus si jamais vous sélectionnez tous vos magasins en même temps (Ctrl+A), QGIS ne lancera-t-il pas un important calcul, avec risque de plantage ? Faites quelques tests pour comprendre le problème.
Il serait donc bel et bon d'ajouter un comptage, une éventuelle désélection, ainsi que des conditions (nos 3 lois robotiques ! 😄) pour que la sélection ne se déclenche que si un seul magasin a été sélectionné, et désélectionne d'éventuelles entités précédentes. Ceci afin de ne pas induire l'utilisateur en erreur.
Utilisons les méthodes selectedFeatureCount()
et removeSelection()
, dans des conditions if
.
layer_selected = QgsProject.instance().mapLayersByName("decathlon_france")[0] layer_to_select = QgsProject.instance().mapLayersByName("iso_iris")[0] def SelectionAuto(): selected_features = layer_selected.selectedFeatures() for i in selected_features: if layer_selected.selectedFeatureCount() == 1: id_mag = i['id'] myselect = layer_to_select.getFeatures( QgsFeatureRequest().setFilterExpression ( u'"id_mag" = \'%s\'' % id_mag ) ) layer_to_select.selectByIds( [ f.id() for f in myselect ] ) if layer_selected.selectedFeatureCount() > 1: layer_to_select.removeSelection() layer_selected.selectionChanged.connect(SelectionAuto)
Dans la ligne 7, une 1ère condition if
vérifie que le nombre de magasins sélectionnés est égal à 1
, cela grâce à la fonction selectedFeatureCount()
, qui renvoie le nombre d'entités sélectionnées. Si c'est bien le cas, le reste du contenu de cette condition s'exécute.
À la ligne 11, un autre if
se contente de désélectionner (removeSelection()
) la zone éventuellement sélectionnée auparavant, à condition qu'il y ait plus d'1
magasin sélectionné.
- Note personnelle pour plus tard : faire la même chose mais sans fonction
Def
, dans une action activable au clic dans la table attributaire ou via l'icône action. Ceci afin de ne pas être confronté à une sélection des zones en permanence (impossibilité de désactiver cette actionDef
).
Variante pour afficher plusieurs zones isochrones après sélection de plusieurs magasins
layer_selected = QgsProject.instance().mapLayersByName("decathlon_france")[0] layer_to_select = QgsProject.instance().mapLayersByName("iso_iris")[0] def SelectionAuto(): myList = [] selected_features = layer_selected.selectedFeatures() for i in selected_features: id_mag = i['id'] myList.append(id_mag) myList = str(myList).replace("[", "").replace("]", "") print(myList) myselect = layer_to_select.getFeatures( QgsFeatureRequest().setFilterExpression ( u'"id_mag" IN (%s)' % myList ) ) layer_to_select.selectByIds( [ f.id() for f in myselect ] ) layer_selected.selectionChanged.connect(SelectionAuto)