Pas exactement, mais bon quand même un peu quand on se sent chez soi dans QGIS; donc, encore une entrée informatique qui nous éloigne du sujet de l’alimentation, mais ce n’est que pour mieux y revenir plus tard vous verrez… J’ai appris les bases de Python avec ce livre formidable que je recommande à tous.

Un chef d’oeuvre pédagogique !

L’auteur est généreux au point de rendre son livre gratuit en ligne. Personnellement, j’ai acheté une copie. Je préfère l’édition papier et c’est aussi soutenir l’auteur. D’ailleurs je ne suis pas seul: plus de 100,000 copies vendues jusqu’à maintenant! Et une seconde édition élargie (100 nouvelles pages!) est sur le point de paraître. Al Sweigart est un pédagogue hors pair: tout semble simple et aller de soi quand il explique.

Mais qu’est-ce au juste que Python? C’est un langage qui permet de faire beaucoup de choses différentes. Il est très apprécié des scientifiques. En fait, Python comporte une panoplie de modules qui, une fois importés, permettent de faire des statistiques, des calculs géométriques complexes (on l’a vu dans un article précédent avec les courbes Tanaka), des requêtes dans une base de données ou encore de la robotique quand on ajoute, par exemple, un RasberryPi (plus à ce sujet bientôt). Voyons ça dans le détail…

Python 101

D’abord installer sur votre ordinateur la dernière version de Python, puis ouvrir la console. La console est bonne pour du code d’une ligne seulement, par exemple pour des maths basiques:

La console Python

On peut faire du calcul modulaire, par exemple pour savoir quelle heure il sera dans 200 heures. S’il est dix heures présentement, on écrira: 
(10 + 200) % 12

On peut aussi travailler avec des chaînes de caractères. Une chaîne de caractères commence et finit toujours par des guillemets (simples ou doubles, mais soyez constants dans vos choix). Si on entre dans la console ‘Jean’ + ‘Marc’ et on fait Enter, on obtient: ‘JeanMarc’. Par contre, si on tente de lier une chaîne de caractères et un nombre, on obtient un message d’erreur:

>>> ‘Jean’ + 33
Traceback (most recent call last):
File « <pyshell#26> », line 1, in <module>
‘Jean’ + 33
TypeError: Can’t convert ‘int’ object to str implicitly

Il est important de lire les messages d’erreur dans la console car ils nous permettent de voir où est le problème. «Int» (pour integer) ici veut dire un nombre entier (33) et «str» (pour string) signifie chaîne de caractères. Il faut donc transformer ici le nombre en chaîne de caractères en l’entourant de guillemets: ‘Jean’+’33’. On peut aussi multiplier des chaînes de caractères:

>>> ‘Jean’ * 5
‘JeanJeanJeanJeanJean’

Par contre, multiplier une chaîne par une chaîne donne un message d’erreur:

>>> ‘Jean’ * ‘Marc’
Traceback (most recent call last):
File « <pyshell#32> », line 1, in <module>
‘Jean’ * ‘Marc’
TypeError: can’t multiply sequence by non-int of type ‘str’

Python permet de définir des variables. Il faut s’imaginer qu’une variable est une boîte. On peut y mettre ce que l’on veut et le retrouver chaque fois qu’on invoque la variable.
>>> courge = 50
>>> courge
50
>>> haricot = 10
>>> courge + haricot
60
>>> courge = courge + 2
>>> courge
52

On peut remplacer le contenu de la boîte (autrement dit la valeur à laquelle est associée la variable). Le contenu précédent sera par contre effacé.

>>> courge = ‘Antoine’
>>> courge
‘Antoine’
>>> courge = ‘Joshua’
courge
‘Joshua’

Python permet aussi de créer des listes (notez les crochets):
>>> jardin = [‘courge’, ‘haricot’, ‘maïs’, ‘tabac’]

On peut invoquer un élément de la liste en servant de l’index, mais il faut savoir que Python commence à compter avec zéro, donc le deuxième élément sera à la position 1, puisque le premier a un index zéro:

>>> jardin = [‘courge’, ‘haricot’, ‘maïs’, ‘tabac’]
>>> jardin[1]
‘haricot’

On peut aussi sélectionner plusieurs éléments situés entre deux index, mais il faut savoir que Python va alors s’arrêter avant le deuxième index:

>>> jardin = [‘courge’, ‘haricot’, ‘ maïs’, ‘tabac’]
>>> jardin[1:3]
[‘haricot’, ‘maïs’]

On peut ajouter des séparateurs (ici une virgule) entre nos éléments. La fonction print permet d’afficher une série d’éléments comme elle apparaîtrait dans l’interface que nous serions à programmer. On remarquera alors qu’on n’utilise plus les crochets mais les parenthèses (comme après toute fonction):

>>> print(‘courge’, ‘haricot’, ‘maïs’, sep=’, ‘)
courge, haricot, maïs

Python peut aussi nous dire l’étendue (lenght) de notre liste, soit combien d’éléments elle contient:

>>> jardin = [‘courge’, ‘haricot’, ‘maïs’]
>>> len(jardin)
3

On peut éliminer un élément de la liste en indiquant son index:

>>> jardin = [‘haricot’, ‘courge’, ‘maïs’, ‘tabac’]
>>> del jardin[2]
>>> jardin
[‘haricot’, ‘courge’, ‘tabac’]

Il est possible de trier les éléments d’une liste:
>>> jardin = [2, 5, 3.14, 1, -7]
>>> jardin.sort()
>>> jardin
[-7, 1, 2, 3.14, 5]
>>> jardin = [‘haricot’, ‘courge’, ‘maïs’, ‘tabac’]
>>> jardin.sort()
>>> jardin
[‘courge’, ‘haricot’, ‘maïs’, ‘tabac’]

On peut aussi trier en ordre inverse:
>>> jardin.sort(reverse=True)
>>> jardin
[‘tabac’, ‘maïs’, ‘haricot’, ‘courge’]

Pour permettre à Python de fonctionner rapidement, toutes les fonctions ne sont pas déjà présentes. On doit en importer au fur et à mesure qu’on en a besoin, et ce, en important d’abord le module auxquelles les fonctions désirées se rattache, par exemple le module contenant les algorithmes qui simulent le hasard:

import random

Le module est maintenant importé et on peut invoquer certaines des fonctions qu’il contient, par exemple choisir au hasard un chiffre entre 1 et 9:
random.randint(1, 9)

Le module random est particulièrement intéressant en contexte pédagogique. Imaginez que vous devez assigner l’ordre des présentations orales en classe pour un groupe comprenant 5 étudiant.e.s. On pourrait d’abord écrire une liste.
groupe = [1,2,3,4,5]
Et ensuite demander a Python de mélanger ces nombres.
random.shuffle(groupe)
Si maintenant on invoque à nouveau la variable groupe, on obtient ceci:
[4, 2, 3, 5, 1]
Par contre, s’il y a 13 étudiant.e.s dans le groupe, ça va être fastidieux de commencer à rédiger une liste. Mais on peut demander à Python de la faire pour nous avec la fonction appropriée et associer cette liste à une variable:
>>> list(range(14))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
>>> groupe = list(range(14))
>>> groupe
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
>>> random.shuffle(groupe)
>>> groupe
[3, 13, 6, 11, 10, 5, 0, 4, 2, 9, 7, 8, 12, 1]
Comme on ne souhaite pas voir un étudiant.e associé.e au nombre zéro, on aurait aussi pu définir la liste ainsi:
>>> groupe = list(range(1,14))
Autre mise en contexte pédagogique: vous devez attribuer au hasard à 4 étudiants le soin de présider les discussions dans votre séminaire qui en comprend 13. Allez-vous tirer un ou des dés? Les chances qu’un même nombre soit tiré plusieurs fois sont grandes et l’exercice risque donc d’être laborieux. Voici une fonction pratique qui permet de déterminer un échantillon (ici de 4) extrait du groupe (que l’on a déjà défini plus tôt):
>>> random.sample(groupe, 4)
[8, 11, 7, 5]

Un truc qui est plus difficile à comprendre au départ avec Python, c’est le concept de boucles (loops). Il faut le comprendre comme une façon de dire à Python que l’on veut qu’il fasse une opération plusieurs fois de suite. Par exemple, si l’on veut que Python «imprime» (en fait, affiche) 3 fois le mot Jardin, on pourrait le lui dire ainsi:
for i in range(3):
print(‘Jardin’)
L’indentation est importante dans Python. i tient ici pour index.
On ne peut cependant pas écrire ceci dans la console, car la console n’accepte qu’une commande à la fois. Il faut donc ouvrir un nouveau fichier, entrer le code, sauvegarder et enfin exécuter (run ou F5). Le résultat dans la console est:
Jardin
Jardin
Jardin

Un nouveau fichier et la commande d’exécution

Donc, Python a imprimé 3 fois le mot jardin parce qu’il y a 3 positions où l’index peut se trouver dans le nombre 3. Chaque nouvelle boucle commence avec un index différent, ce qui permet à Python de se déplacer d’un index à l’autre. Quand il arrive au dernier index (ici 3), Python comprend que c’est la boucle finale. On peut lui demander de calculer lui-même le nombre total d’index, par exemple ici dans une liste:
Jardin = [‘haricot’, ‘courge’, ‘maïs’, ‘tabac’]
for i in range(len(Jardin)):
print(‘Index ‘ + str(i) + ‘ dans le jardin est: ‘ + Jardin[i])
Le résultat donne:
Index 0 dans le jardin est: haricot
Index 1 dans le jardin est: courge
Index 2 dans le jardin est: maïs
Index 3 dans le jardin est: tabac
Encore une fois, on aura rédigé ceci dans un nouveau fichier et non dans la console puisque notre programme comprend plus d’une ligne. Dès lors qu’on a plus qu’une ligne, on peut parler d’un programme. En voici un petit tout simple:

print('Bonjour-Hi!')
print('c\'est quoi ton nom?')
monNom = input()
print('content de te connaître, '+ monNom)
print('la troisième lettre de ton nom est '+ monNom[2])
print('quel age as-tu?')
monAge = input()
if int(monAge) <= 40:
     print('tu es encore jeune, '+ monNom)
else: 
     print('vive les vieux!')

If et else sont des «instructions» (statements) qui permettent de dire à Python quoi faire quand une valeur correspond (ou non) à des critères que l’on aura définis. C’est ce qu’on appelle des contrôles de flux. On peut tester et partager le programme avec PythonTrinket. On remarquera que, comme dans l’article précédent, j’ai dû utiliser un backslash (barre oblique inverse) devant tous les guillemets et apostrophes pour pas que Python ne les confonde avec des chaînes de caractères ( \’, \ » ). On peut aussi lui signifier un saut de paragraphe (\t) et un saut de ligne ( \n). On verra dans un prochain article que c’est légèrement différent en html. Enfin, si l’on veut écrire un backslash dans notre texte, on le fait précéder d’un autre backslash ( \\ ).

On peut mettre du texte en majuscules:
>>> ‘Hello’.upper()
‘HELLO’
En minuscules:
>>> ‘Hello’.lower()
‘hello’
Et mon préféré (très utile pour polir ses fiches dans Zotero):
>>> ‘A HISTORY OF THE VIKINGS’.title()
‘A History Of The Vikings’

On peut aussi avec Python gérer directement les fichiers sur son ordi en important d’abord le module approprié:
>>> import os
On peut ensuite rapidement créer un dossier et des sous-dossiers:
os.makedirs(‘C:\\jardin\\courge\\graines’)
Vous pouvez obtenir le chemin où est le fichier actuel:
os.getcwd()

Maintenant que nous avons les bases de Python, voyons comment il s’intègre dans QGIS.

PyQGIS

Bien qu’écrit en C++, QGIS contient une console Python.

Avec cette console, on peut sélectionner des entités:

layer = iface.activeLayer()
layer.selectAll()
layer.removeSelection()

On peut attribuer une couleur à l’entité sélectionnée:

iface.mapCanvas().setSelectionColor( QColor("red") )

On peut zoomer:

iface.zoomFull()
iface.zoomToPrevious()
iface.zoomToNext()

On peut obtenir le chemin du dossier où notre projet est enregistré:

QgsProject.instance().readPath("./") 
QgsProject.instance().fileName()

Sélectionner les attributs dans notre table des attributs:

layer = iface.activeLayer()
features = layer.getFeatures()

Compter les attributs:

fc = layer.featureCount()
fc

Encore une fois, quand notre requête fait plus d’une ligne de commande, on doit créer un nouveau fichier puis l’exécuter.

Pour faire un test, entrez le mot «world» dans la case des coordonnées (sans guillemets).

Une couche s’affichera (truc de Josh). Dans la console, entrez

layer = iface.activeLayer()
features = layer.getFeatures() 

Il faut cliquer Enter dans la console entre chaque ligne. Puis, dans l’éditeur, entrez:

for feature in features:
     print(feature['NAME'])
     print(feature[9])

Il faut respecter l’indentation. Exécutez la commande. Le résultat va être le nom des pays, suivi des valeurs qui sont dans la huitième colonne de la table des attributs associées à cette couche, soit le type de gouvernement

Vous pouvez créer des requêtes spécifiques pour filtrer votre table et n’afficher que les valeurs associées par exemple, à un pays:

layer = iface.activeLayer()
iface.showAttributeTable(layer, "name='Canada'")

Pour obtenir le périmètre et l’aire en km carré du Canada, on commence par définir une valeur d par une fonction, on s’assure d’être dans la bonne projection (WGS84), puis on précise la requête et, enfin, on crée une boucle :

d = QgsDistanceArea()
d.setEllipsoid('WGS84')
query = '"NAME" = \'Canada\''
features = layer.getFeatures(QgsFeatureRequest().setFilterExpression(query))
for f in features:
   geom = f.geometry()
   name = f.attribute('NAME')
   print(name)
   print("Perimeter (m):", d.measurePerimeter(geom))
   print("Area (km2):", d.convertAreaMeasurement(d.measureArea(geom), QgsUnitTypes.AreaSquareKilometers))

On peut tout aussi aisément programmer des filtres pour interroger notre table des attributs. Si l’on veut par exemple une liste des pays ayant une population supérieure à cinquante millions d’habitants et dont le produit intérieur brut est supérieur à quinze mille milliards de dollars, on écrira:

layer = iface.activeLayer()
query = '"POP_EST" > 50000000 AND "GDP_MD_EST" > 15000000'
features = layer.getFeatures(QgsFeatureRequest().setFilterExpression(query))
for f in features:
    name = f.attribute('NAME')
    print(name)

Ici, la requête ne donne évidemment qu’un seul résultat: les États-Unis. Ce n’est qu’un survol des possibilités qu’offre l’intégration de Python dans QGIS. Pour approfondir, j’aimerais vous recommander le livre de Gary Sherman. Après tout, il est le fondateur de QGIS. Mais malheureusement ce n’est pas un aussi bon pédagogue qu’Al Sweigart ou Numa Gremling et le livre est assez ardu. Je me suis aussi servi pour écrire ce tutoriel de la documentation de QGIS, qui est probablement un meilleur point de départ.

Pour lecteur très motivé ayant déjà des bases solides