Formation Python pour gĂ©omaticiens 🐍🌏

11 DĂ©cembre 2023

Matthieu Viry

Programme de la demi-journĂ©e 📝


  • Partie 1 : Introduction Ă  Python
  • Partie 2 : Mise en pratique
  • Partie 3 : Python et QGIS
  • Partie 4 : Mise en pratique
  • Conclusion

Objectifs : DĂ©couvrir le langage de programmation Python et son Ă©cosystĂšme, et apprendre Ă  l’utiliser pour rĂ©aliser des traitements gĂ©omatiques dans QGIS.


Rappels des instructions pour installer l’environnement Python : Installation d’un environnement Python pour Windows avec Anaconda et VSCodium

Partie 1 - Introduction Ă  Python

Pourquoi programmer ?

  • Automatiser des tĂąches rĂ©pĂ©titives (et ainsi gagner en productivitĂ©)
  • Prendre le contrĂŽle des analyses et des traitements que vous rĂ©alisez
  • RĂ©aliser des analyses reproductibles
  • Pour le fun ! 🎉

Et pourquoi en Python ?

  • C’est un des langages de programmation les plus populaires et les plus utilisĂ©s dans le monde
  • C’est un langage relativement simple Ă  apprendre

Historique et présentation sur le papier




  • Langage crĂ©Ă© Ă  la toute fin des annĂ©es 80 - premiĂšre version publique sortie en 1991.

  • Libre (rĂ©git par la Python Software Foundation License, Ă©quivalent Ă  BSD)

  • Langage polyvalent, interprĂ©tĂ©, multi-paradigme (impĂ©ratif, fonctionnel, OO, ..)

  • Typage dynamique fort (et duck typing)

  • Toujours en dĂ©veloppement actif (derniĂšre version stable en octobre 2023)

Python

Python vs. R đŸ„Š


Python

  • General-purpose programming language

  • “Jack of all trades, master of none” ?

  • UtilisĂ© par Google, Facebook, Microsoft, etc.

R

  • For statistical computing and graphics

  • “By statisticians, for statisticians”

  • UtilisĂ© par Google, Facebook, Microsoft, etc.


  • Les deux langages sont souvent comparĂ©s, notamment dans le domaine de l’analyse de donnĂ©es.

  • La question se pose car Python est de plus en plus compĂ©tent pour l’analyse de donnĂ©es


PrĂ©sentation de l’écosystĂšme

Implémentation de référence et distributions

  • Une implĂ©mentation de rĂ©fĂ©rence du langage Python : CPython (mais d’autres implĂ©mentations : Jython, IronPython, PyPy, etc.)

  • Plusieurs distributions de Python (principalement Anaconda, une distribution des langages de programmation Python et R dĂ©diĂ©e Ă  la science des donnĂ©es et Ă  l’apprentissage automatique)

Source: anaconda.org

Environnements de développement

  • Plusieurs IDE apprĂ©ciĂ©s par les utilisateurs de Python : Spyder (IDE “scientifique”, interface comparable Ă  RStudio), PyCharm (logiciel propriĂ©taire, plutĂŽt pour dĂ©velopper des bibliothĂšques), Jupyter Lab et plus rĂ©cemment Visual Studio Code (VSCode, VSCodium).

Batteries included 🔋

Le langage de programmation Python a une bibliothÚque standard trÚs complÚte, comportant de nombreux modules qui permettent de réaliser des tùches courantes sans avoir à réinventer la roue.

Modules de la bibliothĂšque standard :

  • math (fonctions mathĂ©matiques de base)
  • statistics (statistiques descriptives)
  • random (gĂ©nĂ©ration de nombres alĂ©atoires)
  • datetime (gestion des dates et heures)
  • os (fonctions d’interaction avec le systĂšme d’exploitation)
  • sys (fonctions et variables d’interaction avec l’interprĂ©teur Python)
  • re (expressions rĂ©guliĂšres)
  • csv (lecture et Ă©criture de fichiers CSV)
  • json (lecture et Ă©criture de fichiers JSON)
  • sqlite3 (interface avec la base de donnĂ©es SQLite)
  • etc.

Modules additionnels (bibliothĂšques)


Une des forces du langage de programmation Python est son Ă©cosystĂšme, qui est trĂšs riche et trĂšs actif.

Les packages sont des bibliothĂšques Python qui peuvent ĂȘtre installĂ©es Ă  l’aide du gestionnaire de packages pip (ou conda dans le cas d’Anaconda).

Ces packages sont gĂ©nĂ©ralement hĂ©bergĂ©s sur PyPI (Python Package Index) (et conda-forge pour les utilisateurs d’Anaconda).


Avant de se lancer dans une tùche de programmation, il est donc important de vérifier si un package spécialisé existe déjà pour réaliser cette tùche.

Modules additionnels (bibliothĂšques)

Écosystùme Python scientifique


Documentation

Un point fort de Python est sa documentation officielle, qui contient tout le nĂ©cessaire pour utiliser le langage de maniĂšre proactive (tutoriel, rĂ©fĂ©rence de l’ensemble des fonctions et des objets de tous les modules de la bibliothĂšque standard, etc.) et qui est mise Ă  disposition dans plusieurs langues (Anglais, Français, Espagnol, CorĂ©en, Japonais, Chinois, etc.).

Documentation / Modules additionnels

Il n’existe toutefois pas un seul standard pour consulter la documentation des diffĂ©rents modules additionnels Python (cf. Documentation Matplotlib, Documentation pandas).

En terme de syntaxe ça donne quoi ?

Premier pas avec la syntaxe Python


# On importe deux fonctions du module statistics
from statistics import mean, stdev

# DĂ©finition d'une fonction
def cv(x):
    moy = mean(x)
    s = stdev(x)
    result = s / moy
    # Retourne le résultat
    return result 

# La fonction est appelée avec une liste de valeurs,
# et le résultat est stocké dans une variable 
res = cv([5, 6, 3, 8, 9, 12])

# Affichage du résultat
print(res)
0.44490991790020906

Python et l’indentation du code



my_list = [1, 2, 3]

for item in my_list:
    if item % 2 == 0:
        print('even')
    else:
    print('odd')
IndentationError: expected an indented block after 'else' statement on line 6 (660691277.py, line 7)

Python et l’indentation du code



my_list = [1, 2, 3]

for item in my_list:
    if item % 2 == 0:
        print('even')
    else:
        print('odd')
odd
even
odd

Python et l’indentation du code



  • Respecter les rĂšgles d’indentations est obligatoire en Python.

  • Ce n’est pas une contrainte lors d’une session de travail car les IDE guident la position du curseur.

  • Cette indentation a un rĂŽle direct sur le contrĂŽle du flux d’exĂ©cution.

  • Elle permet notamment d’éviter l’utilisation d’accolades (curly brackets) pour dĂ©limiter les blocs et de point-virgules pour dĂ©limiter les instructions.

Python et l’indentation du code



Le code qui suit est volontairement incorrect mais ne gĂ©nĂšre pas d’erreur lors de l’exĂ©cution :

li1 = [1, 2, 3, 4, 5, 6]
li2 = [4, 20, 31, 87, 123, 621]

# Additionnons les deux listes, éléments par éléments,
# et stockons chaque résultat dans une nouvelle liste
result = []

for item1, item2 in zip(li1, li2):
    new_item = item1 + item2
result.append(new_item)

print(result)
[627]

Python et l’indentation du code



Une fois l’indentation corrigĂ©e


li1 = [1, 2, 3, 4, 5, 6]
li2 = [4, 20, 31, 87, 123, 621]

# Additionnons les deux listes, éléments par éléments,
# et stockons chaque résultat dans une nouvelle liste
result = []

for item1, item2 in zip(li1, li2):
    new_item = item1 + item2
    result.append(new_item)

print(result)
[5, 22, 34, 91, 128, 627]

L’interprĂ©teur Python


  • L’interprĂ©teur Python est un programme qui lit et exĂ©cute du code Python.
  • Il est possible d’interagir avec l’interprĂ©teur Python en utilisant un terminal (ou une console) ou en utilisant un IDE.
  • L’interprĂ©teur Python est lancĂ© en tapant python dans un terminal.
  • Il est possible de quitter l’interprĂ©teur Python en tapant exit() ou en appuyant sur Ctrl + D.
  • L’interprĂ©teur Python peut ĂȘtre utilisĂ© pour exĂ©cuter des scripts Python (fichiers .py) en utilisant la commande python mon_script.py.

Ouvrir l’interprĂ©teur Python (installĂ© par Anaconda)


  • Dans le menu DĂ©marrer, chercher “Anaconda Prompt” et cliquer dessus. Une fenĂȘtre de terminal s’ouvre. Celle-ci est prĂȘte Ă  exĂ©cuter des commandes Python (dans l’environnement de base d’Anaconda).

  • Il est possible d’installer des packages Python supplĂ©mentaires dans l’environnement de base d’Anaconda en utilisant des commandes telles que :

    • conda install mon_package (pour installer un package depuis le dĂ©pĂŽt Anaconda)
    • pip install mon_package (pour installer un package depuis le dĂ©pĂŽt PyPI)
    • conda install --channel conda-forge mon_package (pour installer un package depuis le dĂ©pĂŽt conda-forge)
  • Il est possible de lancer un interprĂ©teur Python en tapant python dans le terminal.

🚀 Ouvrez un terminal et lancez l’interprĂ©teur Python ! Lors des dĂ©monstrations qui suivent, vous pourrez tester les commandes dans l’interprĂ©teur Python. 🚀

Calculs simples et variables


Une des premiĂšres fonctionnalitĂ©s que l’on souhaite tester dans un langage de programmation est la possibilitĂ© d’effectuer des calculs simples.

>>> 1 + 2
3
>>> 3 * 4
12

Vous pouvez ou non utiliser des espaces autour des opérateurs (+, -, *, /, etc.) :

>>> 1+2
3

La double Ă©toile reprĂ©sente l’exposant.

>>> 2 ** 3
8

Calculs simples et variables


Il est possible de stocker des valeurs dans des variables :

>>> a = 1
>>> b = 2
>>> result = a + b

Le rĂ©sultat n’est donc plus affichĂ© directement dans l’interprĂ©teur Python mais stockĂ© dans la variable result. On peut ensuite afficher le contenu de la variable result :

>>> result
3

Ou en utilisant la fonction print :

>>> print(result)

Calculs simples et variables

En Python le typage est dynamique, il n’est pas nĂ©cessaire de dĂ©clarer le type d’une variable avant de l’utiliser et il est possible de changer le type d’une variable au cours de son existence :

>>> a = 1
>>> type(a)
<class 'int'>
>>> a = 'Hello world!'
>>> type(a)
<class 'str'>

Types natifs
et data structures natives

Les structures de donnĂ©es sont les Ă©lĂ©ments fondamentaux autour desquels vous construisez vos programmes. Chaque structure de donnĂ©es fournit une maniĂšre particuliĂšre d’organiser les donnĂ©es afin d’y accĂ©der efficacement.

Types de base

Les types de base sont les types de données les plus simples disponibles dans Python. Ils sont utilisés pour représenter des valeurs simples.

  • int (entier)
my_int = 42


  • float (nombre Ă  virgule flottante)
my_float = 3.14


  • bool (boolĂ©en)
my_bool = True # ou False
  • str (chaĂźne de caractĂšres)
my_str = 'Hello world!'


my_str = "Hello world!"
  • NoneType (type spĂ©cial pour la valeur None)
my_none = None

Types de base - str

  • CrĂ©ation, avec des guillemets simples ou doubles :
my_str = 'Hello world!'
my_str = "Hello world!"
my_long_str = '''Hello
world
and Python!'''


  • ConcatĂ©nation :
my_str = 'Hello' + ' ' + 'world!'
  • AccĂšs aux caractĂšres :
print(my_str[0]) # Premier caractĂšre
H
print(my_str[-1]) # Dernier caractĂšre
!
print(my_str[0:5]) # Sous-chaĂźne
Hello

Types de base - str

  • Formatage :
a = 'world'

my_str = 'Hello {}!'.format(a)
print(my_str)
Hello world!
a = 'world'

my_str = f'Hello {a}!'
print(my_str)
Hello world!
  • Remplacement :
my_str = 'Hello world!'
my_str = my_str.replace('world', 'Python')
print(my_str)
Hello Python!

La mĂ©thode replace retourne une nouvelle chaĂźne de caractĂšres, la chaĂźne de caractĂšres initiale n’est pas modifiĂ©e. Cette mĂ©thode remplace toutes les occurrences de la sous-chaĂźne par la nouvelle sous-chaĂźne.

Elle peut aussi ĂȘtre utilisĂ©e pour supprimer une sous-chaĂźne :

my_str = 'Hello world!'

my_str = my_str.replace(' world', '')
print(my_str)
Hello!

Structures de données

Python est livré avec un ensemble complet de structures de données dans sa bibliothÚque standard :

  • dict (tableau associatif)
my_dict = { 'john': 32, 'jane': 27, 'jack': 30 }
  • tuple (un conteneur immuable),
my_coordinates = (2.349014, 48.864716)
  • list (tableau dynamique mutable),
my_list = [12, 34, 2, 1, 21, 4]
other_list = ['a', 12, (2.349014, 48.864716), { "foo": 42 }]
  • array (tableau typĂ© de base), enum (Ă©numĂ©ration), etc.

Structures de données - list (1)

Les listes sont des tableaux dynamiques, elles peuvent contenir des éléments de différents types et sont mutables.

  • CrĂ©ation :

Liste vide :

ma_liste = []

Ou crĂ©ation d’une liste avec des Ă©lĂ©ments :

ma_liste = [12, 34, 2, 1, 21, 43]


  • Connaitre la taille d’une liste :
len(ma_liste)
6
  • AccĂšs aux Ă©lĂ©ments :
ma_liste[0] # Premier élément
12
ma_liste[-1] # Dernier élément
43
ma_liste[1] = 42 # Modification d'un élément


  • AccĂšs Ă  un sous-ensemble d’élĂ©ments (slicing) :
my_slice = ma_liste[1:3]
my_slice
[42, 2]

Structures de données - list (2)

De nombreuses méthodes permettant de manipuler les listes.

  • Ajouter des Ă©lĂ©ments Ă  une liste existante :
ma_liste.append(322)


  • Supprimer un Ă©lĂ©ment d’une liste :
 # Supprime la premiÚre occurrence de l'élément 322
ma_liste.remove(322)
 # Supprime l'élément à l'indice 0 (et le retourne)
ma_liste.pop(0)
12

Il existe Ă©galement des mĂ©thodes permettant de trier une liste (sort), d’ajouter un Ă©lĂ©ment Ă  une position prĂ©cise (insert), etc. etc.

Structures de données - tuple

Puisqu’il s’agit d’un type immuable, il n’est pas possible de modifier un tuple une fois qu’il a Ă©tĂ© crĂ©Ă© :

  • CrĂ©ation :
my_tuple = (1, 2, 3)
  • AccĂšs aux Ă©lĂ©ments :
my_tuple[0] # Comme pour une liste
1
  • Pas de modification possible :
my_tuple[0] = 42
TypeError: 'tuple' object does not support item assignment

Structures de données - dict (1)

Il s’agit d’un tableau associatif, les Ă©lĂ©ments sont stockĂ©s sous forme de clĂ© / valeur. Il ne s’agit donc pas d’objets sĂ©quentiels comme les listes, mais plutĂŽt d’objets dits de correspondance (mapping objects en anglais).

  • CrĂ©ation d’un dictionnaire vide :
my_dict = {}


  • CrĂ©ation d’un dictionnaire avec des Ă©lĂ©ments :
my_dict = { 'john': 32, 'jane': 27, 'jack': 30 }


  • AccĂšs aux Ă©lĂ©ments :
my_dict['john']
32
  • Modification d’un Ă©lĂ©ment :
my_dict['john'] = 33


  • Ajout d’un Ă©lĂ©ment :
my_dict['jill'] = 29


  • Suppression d’un Ă©lĂ©ment :
del my_dict['john']

Structures de données - dict (2)

  • AccĂšs aux Ă©lĂ©ments :
my_dict['jill']
29


Une erreur est renvoyĂ©e si la clĂ© n’existe pas :

my_dict['johana']
KeyError: 'johana'


Tester l’existence d’une clĂ© dans un dictionnaire :

'johana' in my_dict
False

Demander la valeur associĂ©e Ă  une clĂ© en utilisant la mĂ©thode get (et de spĂ©cifier une valeur par dĂ©faut si la clĂ© n’existe pas) :

my_dict.get('johana')
my_dict.get('johana', -1)
-1


  • RĂ©cupĂ©rer l’ensemble des clĂ©s :
my_dict.keys()
dict_keys(['jane', 'jack', 'jill'])


  • RĂ©cupĂ©rer l’ensemble des valeurs :
my_dict.values()
dict_values([27, 30, 29])

Des listes de listes ?

Il est possible et fréquent de combiner des listes (ou des listes et des tuples) pour représenter des données tabulaires :

data = [
    ['Jill', 32, 1.65],
    ['Jack', 27, 1.73],
    ['Jane', 30, 1.80]
]

print(data[0][1])
32
data = [
    ('Jill', 32, 1.65),
    ('Jack', 27, 1.73),
    ('Jane', 30, 1.80)
]

print(data[0][1])
32

Des listes de dictionnaires ?

Il est également classique de combiner des listes et des dictionnaires pour représenter, par exemple, des données tabulaires :

data = [
    { 'name': 'Jill', 'age': 32,  'height': 1.65 },
    { 'name': 'Jack', 'age': 27,  'height': 1.73 },
    { 'name': 'Jane', 'age': 30,  'height': 1.80 }
]

print(data[0]['age'])
32

Des dictionnaires de dictionnaires ?

Il est trÚs fréquent de rencontrer des dictionnaires emboßtés dans des dictionnaires :

data = {
    'jill': { 'age': 32,  'height': 1.65 },
    'jack': { 'age': 27,  'height': 1.73 },
    'jane': { 'age': 30,  'height': 1.80 }
}

print(data['jill']['age'])
32

Des dictionnaires de dictionnaires ?

Il est trÚs fréquent de rencontrer des dictionnaires emboßtés dans des dictionnaires :

geojson_pt = {
    'type': 'Feature',
    'geometry': {
        'type': 'Point',
        'coordinates': [2.349014, 48.864716]
    },
    'properties': {
        'name': 'Paris'
    }
}

print(geojson_pt['geometry']['coordinates'])
print(geojson_pt['properties']['name'])
[2.349014, 48.864716]
Paris

ContrĂŽle du flux d’exĂ©cution - if

Le contrĂŽle du flux d’exĂ©cution permet de contrĂŽler l’ordre dans lequel les instructions sont exĂ©cutĂ©es.

  • if :
altitude = 2500

if altitude >= 2000:
    print('Haute altitude')
Haute altitude


  • if / else :
altitude = 2500

if altitude >= 2000:
    print('Haute altitude')
else:
    print('Basse altitude')
Haute altitude
  • if / elif / else :
altitude = 2500

if altitude >= 6000:
    print('Altitude extrĂȘmement Ă©levĂ©e')
elif altitude >= 4000:
    print('TrĂšs haute altitude')
elif altitude >= 2000:
    print('Haute altitude')
else:
    print('Basse altitude')
Haute altitude

Les seuils proviennent de Wikipedia:Altitude#Adaptations_physiologiques_et_réponses_physiopathologiques_du_corps_humain

ContrĂŽle du flux d’exĂ©cution - for (1)

La boucle for permet d’itĂ©rer sur une sĂ©quence (une liste, un tuple, un dictionnaire, etc.) :

  • for :
for i in range(5):
    print(i)
0
1
2
3
4


Ici, range() est une fonction native qui retourne une sĂ©quence d’entiers :

  • range(5) retourne la sĂ©quence 0, 1, 2, 3, 4.

  • range(2, 7) retourne la sĂ©quence 2, 3, 4, 5, 6.

  • for sur une liste :
my_list = [12, 34, 2, 1, 3]

for item in my_list:
    print(item)
12
34
2
1
3


  • for sur une liste, en utilisant enumerate :
for index, item in enumerate(my_list):
    print(f'Index: {index}, item: {item}')
Index: 0, item: 12
Index: 1, item: 34
Index: 2, item: 2
Index: 3, item: 1
Index: 4, item: 3

ContrĂŽle du flux d’exĂ©cution - for (2)

  • for sur un dictionnaire :
my_dict = { 'john': 32, 'jane': 27, 'jack': 30 }

for key in my_dict:
    print(key)
john
jane
jack


for key, value in my_dict.items():
    print(f'{key} a {value} ans')
john a 32 ans
jane a 27 ans
jack a 30 ans
  • for sur une chaĂźne de caractĂšres :
for letter in 'Hello !':
    print(letter)
H
e
l
l
o
 
!


for letter in 'Hello !':
    if letter.lower() in 'aeiouy':
        print(letter)
e
o

ContrĂŽle du flux d’exĂ©cution - while

La boucle while permet d’exĂ©cuter un bloc d’instructions tant qu’une condition est vraie :

  • while :
i = 0

while i < 5:
    print(i)
    i += 1
0
1
2
3
4
  • while avec break :
i = 0

while True:
    print(i)
    i += 1
    if i >= 5:
        break
0
1
2
3
4

Fonctions

Les fonctions permettent de regrouper des instructions et de les exécuter plusieurs fois, en leur passant des paramÚtres différents.

  • DĂ©finition d’une fonction :
def my_function():
    print('Hello world!')


  • Appel d’une fonction :
my_function()
Hello world!
  • Fonction avec paramĂštres et valeur de retour :
def compute_NDVI(red, nir):
    ndvi = (nir - red) / (nir + red)
    return ndvi


ndvi = compute_NDVI(0.2, 0.8)
print(ndvi)
0.6000000000000001

Classes


Comme Python est un langage orientĂ©-objet, il est Ă©galement possible de dĂ©finir des classes, permettant de facilement modĂ©liser des catĂ©gories d’objets


class Point:
    def __init__(self, x, y):
      self.x = x
      self.y = y

    def distance(self, other):
      return (
        (other.x - self.x) ** 2 + (other.y - self.y) ** 2
      ) ** 0.5

Classes


Instanciation et utilisation :

p1 = Point(2, 6)
p2 = Point(9, 9)

print('Distance =', p1.distance(p2))
Distance = 7.615773105863909

Classes, méthodes et attributs

MĂ©thodes :

Une mĂ©thode est une fonction qui appartient Ă  une classe. Elle s’applique donc Ă  l’instance de la classe sur laquelle elle est appelĂ©e.

Dans les exemples précédents :

  • append, remove et pop sont des mĂ©thodes de la classe list
  • keys, get et values sont des mĂ©thodes de la classe dict
  • format et replace sont des mĂ©thodes de la classe str
  • distance est une mĂ©thode de notre classe Point
  • etc.

Attributs :

Un attribut est une variable qui appartient Ă  une classe.

Elle est donc accessible depuis toutes les instances de la classe.

p1 = Point(2, 6)
print(p1.x)

p2 = Point(9, 9)
print(p2.x)
2
9

Actions courantes : lire et Ă©crire des fichiers

  • open permet d’ouvrir un fichier en lecture ou en Ă©criture.
  • read permet de lire le contenu d’un fichier.
  • write permet d’écrire dans un fichier.
  • close permet de fermer un fichier.
  • with (il s’agit d’un context manager) permet d’ouvrir un fichier et de s’assurer que ce dernier sera bien fermĂ© Ă  la fin du bloc d’instructions.

Actions courantes : lire et Ă©crire des fichiers

  • Lire le contenu d’un fichier :
f = open('data/data.txt', 'r')
content = f.read()
f.close()

print(content)
Ceci est une fichier test
avec plusieurs
lignes de texte.


with open('data/data.txt', 'r') as f:
    content = f.read()
    
print(content)
Ceci est une fichier test
avec plusieurs
lignes de texte.
  • Écrire dans un fichier :
with open('data/test.txt', 'w') as f:
    f.write('Hello world!')
  • Lire le contenu d’un fichier ligne par ligne :
with open('data/data.txt', 'r') as f:
    for line in f:
        print(line)
Ceci est une fichier test

avec plusieurs

lignes de texte.
  • Écrire dans un fichier ligne par ligne :
content = ['Hello', 'world', '!']

with open('data/test.txt', 'w') as f:
    for line in content:
        f.write(line + '\n')

Actions courantes : lire et Ă©crire des fichiers


Une action trùs courante est de lire le contenu d’un fichier pour remplacer une sous-chaüne par une autre sous-chaüne (ex: changer un chemin d’accùs dans un fichier de configuration, etc.).

with open('data/data.txt', 'r') as f:
    content = f.read()

# Si on veut replacer plusieurs sous-chaĂźne
# il suffit d'utiliser la méthode `replace` plusieurs fois
content = content.replace('world', 'Python')
content = content.replace('Hello', 'Hi')

with open('data/test.txt', 'w') as f:
    f.write(content)

Actions courantes : lire et Ă©crire des fichiers


Il est Ă©galement possible d’utiliser la bibliothĂšque re (pour regular expressions) pour remplacer une sous-chaĂźne par une autre sous-chaĂźne Ă  l’aide d’une expression rĂ©guliĂšre.

import re

with open('data/data.txt', 'r') as f:
    content = f.read()

content = re.sub(r'Python', 'world', content)
content = re.sub(r'H[a-z]+', 'Hello', content)

with open('data/test.txt', 'w') as f:
    f.write(content)

Actions courantes : lire et Ă©crire des fichiers

  • Lire le contenu d’un fichier JSON :
import json

with open('data/data.json', 'r') as f:
    data = json.load(f)

print(data.keys()) # Affiche les clés du dictionnaire
print(data['width']) # Affiche la valeur associée à la clé 'width'
print(data['height']) # Affiche la valeur associée à la clé 'height'
print(data['values']) # Affiche la valeur associée à la clé 'values'
dict_keys(['name', 'width', 'height', 'values'])
10
8
[4.89660102624323, 44.28666475726375, 63.19205593664028, 25.600654682048397, 12.396597859405256, 47.74217773481377, 82.1282233918879, 20.461709629717117, 43.14007201145414, 92.09111564877499, 66.10474793901118, 5.981854240753348, 62.36444980847118, 26.484129728400106, 28.585238255340673, 18.168713356537314, 58.29298433750647, 36.72646161808159, 39.21966177175944, 2.5239257181935315, 25.68948435710354, 5.746177258012164, 83.17560682114357, 92.41140143912419, 81.95913926172874, 17.95672256157632, 54.73222062966203, 34.14726213372885, 37.66069091485904, 4.9971489135753, 9.629874863571587, 26.11616061595896, 52.39279104216252, 4.409036771310193, 87.94817236709085, 86.90800909249256, 40.131680383106236, 34.757243107452055, 68.34846216933484, 9.115225836083486, 96.2948762888116, 14.785000870870913, 82.90458952078782, 94.3546533775768, 36.070853187885696, 48.61360572542684, 48.80266621681662, 95.97637602986042, 88.09463273968636, 46.16549471860469, 98.85130425205733, 95.07729375139553, 60.12494077469199, 96.41099703758123, 93.03262834682238, 18.029942680392775, 11.578224489176737, 66.54777051141645, 66.24579571250378, 77.35832658434794, 66.48245778316362, 89.47373075457898, 47.62754695573741, 6.491000041761219, 6.9108065570427035, 28.117695769441255, 90.3767310590909, 57.09679362132212, 8.449761608454299, 81.09668828848551, 70.18367543675741, 72.39998306179697, 19.07599243099758, 11.172563819832316, 62.83339916658433, 3.210529148078445, 74.17682507195889, 83.82816776965089, 59.02166349796729, 81.46110256415608]
  • Écrire dans un fichier JSON :
import json

data = { 'name': 'John', 'age': 32 }

with open('data/test.json', 'w') as f:
    json.dump(data, f)

Actions courantes : lire et Ă©crire des fichiers

  • Lire le contenu d’un fichier CSV :
import csv

with open('data/data.csv', 'r') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)
['name', 'age', 'height']
['John', '30', '1.65']
['Mary', '29', '1.7']
['Tom', '31', '1.9']
['Jill', '48', '1.6']
['Sue', '22', '1.75']
  • Écrire dans un fichier CSV :
import csv

data = [
    ['name', 'age'],
    ['John', 32],
    ['Jane', 27],
    ['Jack', 30]
]

with open('data/test.csv', 'w') as f:
    writer = csv.writer(f)
    writer.writerows(data)

Python pour l’analyse de donnĂ©es


  • Python est un langage de programmation gĂ©nĂ©raliste, sa bibliothĂšque standard n’est pas dĂ©diĂ©e Ă  l’analyse de donnĂ©es.

  • Il existe de nombreuses bibliothĂšques additionnelles pour l’analyse de donnĂ©es (et il s’agit d’un Ă©cosystĂšme trĂšs riche et vivant).

  • La bibliothĂšque la plus utilisĂ©e pour l’analyse de donnĂ©es est pandas. Cette bibliothĂšque permet de manipuler des donnĂ©es tabulaires (des tableaux de donnĂ©es).

  • Une autre composante de base de l’écosystĂšme Python pour l’analyse de donnĂ©es est NumPy. Cette bibliothĂšque permet de manipuler des tableaux de donnĂ©es typĂ©s de maniĂšre efficace (la bibliothĂšque pandas est basĂ©e sur NumPy).

Analyse de données - pandas

Pandas permet d’ouvrir des fichiers CSV, JSON, Excel, etc. et de les manipuler facilement grñce à des DataFrames (comparables aux data.frames de R).

  • Ouverture :
import pandas as pd

df = pd.read_csv('data/data.csv')


  • Affichage :
df.head()
name age height
0 John 30 1.65
1 Mary 29 1.70
2 Tom 31 1.90
3 Jill 48 1.60
4 Sue 22 1.75
  • SĂ©lection d’une colonne :
df['name']
0    John
1    Mary
2     Tom
3    Jill
4     Sue
Name: name, dtype: object
  • SĂ©lection selon une condition :
df[df['age'] > 30]
name age height
2 Tom 31 1.9
3 Jill 48 1.6
df.query('height > 1.70').query('age > 30')
name age height
2 Tom 31 1.9

Analyse de données - pandas

  • Ajout d’une colonne :
df['weight'] = [80, 65, 75, 54, 90]

df['BMI'] = df['weight'] / df['height'] ** 2

df.head()
name age height weight BMI
0 John 30 1.65 80 29.384757
1 Mary 29 1.70 65 22.491349
2 Tom 31 1.90 75 20.775623
3 Jill 48 1.60 54 21.093750
4 Sue 22 1.75 90 29.387755


  • Suppression d’une (ou plusieurs) colonne(s) :
df.drop(
  columns=['weight', 'height'],
  inplace=True,
)
  • Tri :
df.sort_values(by='BMI', inplace=True)

df.head()
name age BMI
2 Tom 31 20.775623
3 Jill 48 21.093750
1 Mary 29 22.491349
0 John 30 29.384757
4 Sue 22 29.387755


  • Export :
df.to_csv('data/test.csv', index=False)

Analyse de données - pandas

La bibliothÚque pandas permet également de faire des statistiques descriptives, de réaliser simplement des graphiques, etc.

  • Visualisation :
df.plot.bar(x='name', y='BMI')
<Axes: xlabel='name'>

  • Statistiques descriptives :
df.describe()
age BMI
count 5.000000 5.000000
mean 32.000000 24.626647
std 9.617692 4.392579
min 22.000000 20.775623
25% 29.000000 21.093750
50% 30.000000 22.491349
75% 31.000000 29.384757
max 48.000000 29.387755

Analyse de données - Autres bibliothÚques


  • NumPy - Manipulation de tableaux de donnĂ©es typĂ©s (voir le notebook sur NumPy pour dĂ©couvrir les fonctionnalitĂ©s de base)
  • SciPy - BibliothĂšque d’algorithmes scientifiques
  • Matplotlib - Visualisation de donnĂ©es
  • Scikit-learn - Apprentissage automatique
  • Statsmodels - ModĂšles statistiques
  • Seaborn - Visualisation de donnĂ©es
  • Plotly - Visualisation de donnĂ©es

ÉcosystĂšme pour le gĂ©ospatial

Données vectorielles


Le package GeoPandas


Un projet open source pour faciliter le travail avec des donnĂ©es gĂ©ospatiales vectorielles en Python. GeoPandas Ă©tend Pandas pour permettre de disposer d’un type de colonne gĂ©omĂ©trique (comme sf en R) et pour permettre d’effectuer des opĂ©rations spatiales.

Les opérations géométriques sont réalisées avec shapely (bindings de GEOS) les accÚs en lecture / écriture aux fichiers utilisent fiona (bindings de OGR) et la visualisation utilise matplotlib.

Format des GeoDataFrame

Le package GeoPandas - Exemples

Avec la fonction read_file :

import geopandas as gpd

nybb = gpd.read_file(file_path)

Avec la méthode plot des GeoDataFrame :

nybb.plot()
<Axes: >

Affichage :

print(nybb.crs)
epsg:2263

Transformation :

nybb_geo = nybb.to_crs('EPSG:4326')

En utilisant la bibliothĂšque contextily :

import contextily as cx

ax = nybb.to_crs('EPSG:3857').plot()
cx.add_basemap(ax, source='https://tile.openstreetmap.org/{z}/{x}/{y}.png')
ax.set_axis_off()
ax
<Axes: >

Simplement en utilisant l’attribut centroid des GeoDataFrame :

nybb.centroid.plot()
<Axes: >

Simplement en utilisant l’attribut boundary des GeoDataFrame :

nybb.boundary.plot()
<Axes: >

En utilisant la méthode buffer des GeoDataFrame :

buff_nybb = nybb.buffer(3000)
ax = buff_nybb.plot(color="red")
nybb.plot(ax=ax, color="aliceblue")
<Axes: >

En utilisant la méthode dissolve des GeoDataFrame :

# On peut utiliser dissolve(by="nom_colonne") si on veut aggréger selon les valeurs d'une colonne
agg = nybb.dissolve()
agg.plot()
<Axes: >

En utilisant la méthode intersection des GeoDataFrame :

from shapely import wkt

point = wkt.loads('Point(998769.1146889535 174169.7607268664)')
intersecting = nybb.intersection(point.buffer(35000))


ax = nybb.plot()
ax = intersecting.plot(ax=ax, color="red")
ax
<Axes: >




Et/ou voir le notebook correspondant

Données raster

Rasterio (Gillies et al., 2013) :

  • lecture / Ă©criture de raster (wrapper de haut niveau autour de GDAL)
  • donnĂ©es reprĂ©sentĂ©es sous forme d’array NumPy
  • reprojection
  • resampling
  • virtual files
  • etc.

Rasterstats :

  • rĂ©sumer des donnĂ©es raster sur la base de gĂ©omĂ©tries vectorielles
  • extraction de valeurs Ă  un point prĂ©cis

xarray (Hoyer et al., s. d.) et rioxarray (Snow, Brochart, et al., 2023):

  • xarray pour travailler avec des tableaux multidimensionnels Ă©tiquetĂ©s
  • rioxarray pour ouvrir des jeux des donnĂ©es raster avec rasterio, les stocker dans le format de xarray, et avoir accĂšs Ă  diffĂ©rentes fonctionnalitĂ©s de rasterio
  • moins “rustique” que d’utiliser rasterio seul / dĂ©pend de l’usage souhaitĂ©

Le package Rasterio

Exemple d’utilisation :

import rasterio as rio

with rio.open('./data/MODIS_ARRAY.nc') as f:
    # Métadonnées :
    metadata = f.meta
    # Lire toutes les bandes :
    data = f.read()
    # Ou f.read(1) pour lire seulement la premiĂšre bande
  • Un dictionnaire de mĂ©tadonnĂ©es + un tableau NumPy contenant la (ou les) bande(s) :
print(metadata)
print(data)
{'driver': 'netCDF', 'dtype': 'int16', 'nodata': -28672.0, 'width': 200, 'height': 200, 'count': 1, 'crs': CRS.from_wkt('PROJCS["unknown",GEOGCS["unknown",DATUM["unknown",SPHEROID["unknown",6371007.181,0]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]]],PROJECTION["Sinusoidal"],PARAMETER["longitude_of_center",0],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH]]'), 'transform': Affine(231.6563582639561, 0.0, -7274009.649486291,
       0.0, -231.65635826375018, 5050108.61015275)}
[[[-28672 -28672 -28672 ...    656    656    554]
  [-28672 -28672 -28672 ...    694    694    642]
  [-28672 -28672 -28672 ...    456    575    642]
  ...
  [   993    817    817 ...    471    479    498]
  [   893    893    816 ...    479    479    469]
  [   816    816    832 ...    515    469    485]]]

Les package xarray et rioxarray

Exemple d’utilisation :

import rioxarray
import xarray

xds = xarray.open_dataarray("./data/MODIS_ARRAY.nc")
  • Un objet de type xarray.DataArray ou xarray.Dataset qui contient les diffĂ©rentes mĂ©ta-donnĂ©es et les donnĂ©es :
<xarray.DataArray (y: 200, x: 200)>
[40000 values with dtype=float32]
Coordinates:
  * y        (y) float64 5.05e+06 5.05e+06 5.05e+06 ... 5.004e+06 5.004e+06
  * x        (x) float64 -7.274e+06 -7.274e+06 ... -7.228e+06 -7.228e+06
Attributes:
    crs:        +a=6371007.181 +b=6371007.181 +lon_0=0 +no_defs +proj=sinu +u...
    res:        [231.65635826 231.65635826]
    is_tiled:   0
    nodata:     -28672.0
    transform:  [ 2.31656358e+02  0.00000000e+00 -7.27400965e+06  0.00000000e...
filled = xds.rio.interpolate_na()

En utilisant la mĂ©thode plot des objets xarray.DataArray (ici aprĂšs sĂ©lection d’un subset des donnĂ©es) :

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(15, 5))
ax1, ax2 = (fig.add_subplot(131), fig.add_subplot(133))

xds.isel(x=slice(0, 20), y=slice(0, 20)).plot(ax=ax1)
filled.isel(x=slice(0, 20), y=slice(0, 20)).plot(ax=ax2)
<matplotlib.collections.QuadMesh at 0x7f34ec9eaf50>

  • Affichage :
xds.rio.crs
CRS.from_wkt('PROJCS["unknown",GEOGCS["unknown",DATUM["unknown",SPHEROID["unknown",6371007.181,0]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]]],PROJECTION["Sinusoidal"],PARAMETER["longitude_of_center",0],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH]]')
  • Transformation :
xds = xds.rio.reproject("EPSG:6623")
  • DĂ©coupage selon l’emprise des donnĂ©es contenues dans un GeoDataFrame (ici gdf) :
clipped = xds.rio.clip(gdf.geometry.values, gdf.crs, drop=False, invert=True)

Analyse spatiale

Écosystùme de bibliothùques pour l’analyse spatiale PySAL
(Python Spatial Analysis Library, Rey et Anselin (2007)) :

Explorer :

  • Analyse exploratoire des donnĂ©es spatiales (package esda)
  • Analyse de la dynamique des donnĂ©es spatiales longitudinales (package giddy)
  • Mesure des inĂ©galitĂ©s dans l’espace et dans le temps (package inequality)
  • Analyse statistique de motifs ponctuels planaires (package pointpats)
  • Mesure de la sĂ©grĂ©gation dans le temps et dans l’espace (package segregation)
  • MorphomĂ©trie urbaine (package momepy)

Modéliser :

  • RĂ©gression gĂ©ographiquement pondĂ©rĂ©e Ă  plusieurs Ă©chelles (package mgwr)
  • ModĂšles linĂ©aires gĂ©nĂ©ralisĂ©s Ă©pars (package spglm)
  • ModĂšles d’interaction spatiale (package spint)
  • ModĂšles de rĂ©gression spatiale (package spreg)
  • ModĂšles de composantes de variance Ă  corrĂ©lation spatiale Ă  plusieurs niveaux (package spvcm)
  • Areal interpolation et cartographie dasymĂ©trique (package tobler)
  • AccessibilitĂ© spatiale (package access)
  • Optimisation spatiale (package spopt)

Autres packages utiles 


  • Binding Python pour GRASS + IntĂ©gration dans les notebooks Jupyter
  • Iris - Analyse et visualisation de donnĂ©es des sciences de la Terre.

  • Pour la cartographie : cartopy et geoplot basĂ©s sur cartopy ; PyGMT ; geoviews (selon les usages).

Python pour quoi d’autre ?

Un Ă©cosystĂšme riche pour de nombreux autres usages.

Machine learning et Deep learning :

Python pour quoi d’autre ?


Web scraping :

  • Beautiful Soup (pour extraire des donnĂ©es de pages web)
  • Selenium (pour automatiser des actions dans un navigateur web)
  • Scrapy (pour crĂ©er des robots d’indexation de sites web)

Web development :

  • Django (pour crĂ©er des sites web)
  • Flask (pour crĂ©er des sites web)
  • FastAPI (pour crĂ©er des API web)
  • Dash (pour crĂ©er des applications web interactives)
  • Streamlit (pour crĂ©er des applications web interactives)

Python pour quoi d’autre ?

  • Gradio - CrĂ©er des interfaces graphiques pour des modĂšles d’apprentissage automatique en quelques lignes de code
import gradio as gr

def greet(name, is_morning, temperature):
    salutation = "Good morning" if is_morning else "Good evening"
    greeting = f"{salutation} {name}. It is {temperature} degrees today"
    celsius = (temperature - 32) * 5 / 9
    return greeting, round(celsius, 2)

demo = gr.Interface(
    fn=greet,
    inputs=["text", "checkbox", gr.Slider(0, 100)],
    outputs=["text", "number"],
)

if __name__ == "__main__":
    demo.launch()

Python pour quoi d’autre ?

  • Gradio - CrĂ©er des interfaces graphiques pour des modĂšles d’apprentissage automatique en quelques lignes de code

Python pour quoi d’autre ?

DĂ©veloppement d’applications :

  • PyQt / PySide (pour crĂ©er des applications de bureau)
  • Kivy (pour crĂ©er des applications de bureau et des applications mobiles)
  • PySimpleGUI (pour crĂ©er des interfaces graphiques - Exemple en ligne)

Python dans des logiciels

Python dans Inkscape

La majoritĂ© des extensions de Inkscape sont des scripts Python (d’aprĂšs la documentation), et il existe des ressources pour faciliter l’écriture de scripts Python pour Inkscape.

Et bien sûr dans QGIS ! Mais aussi dans ArcGIS, dans Autodesk Maya, etc.

Python dans Blender

  • Logiciel libre de modĂ©lisation, d’animation et de rendu en 3D

  • IntĂšgre une console Python, permet d’interagir avec de nombreux aspects de Blender dont notamment l’animation, le rendu, l’import et l’export, la crĂ©ation d’objet et l’éxĂ©cution automatisĂ©e de tĂąches rĂ©pĂ©titives

Ressources pour l’apprentissage de Python


Ressources Python GĂ©ospatial


Les ressources qui suivent supposent pour la plupart une connaissance de base du langage Python (boucles, conditions, fonctions, types de base, etc.). Elles mobilisent les bibliothĂšques Python le plus souvent utilisĂ©es pour l’analyse de donnĂ©es et le traitement de donnĂ©es gĂ©ospatiales (pandas, geopandas, rasterio, scipy, etc.).


Partie 2 - Mise en pratique

Utiliser Python depuis VSCodium


  • L’extension Python permet d’utiliser Python depuis VSCodium et l’extension Jupyter permet d’utiliser des notebooks Jupyter depuis VSCodium.

  • Il est possible d’ouvrir un terminal depuis VSCodium en utilisant la commande Ctrl + Shift + P puis en tapant Terminal: Create New Integrated Terminal.

  • Il est possible d’exĂ©cuter un script Python depuis VSCodium en utilisant la commande Ctrl + Shift + P puis en tapant Python: Run Python File in Terminal.

  • Il est possible d’exĂ©cuter seulement une partie d’un script Python depuis VSCodium en sĂ©lectionnant le code Ă  exĂ©cuter puis en utilisant la commande Ctrl + Shift + P puis en tapant Python: Run Selection/Line in Python Terminal.

Utiliser des notebooks Jupyter depuis VSCodium


  • Ouvrir un notebook Jupyter depuis VSCodium : Ctrl + Shift + P puis en tapant Jupyter: Create New Blank Notebook.

  • ExĂ©cuter une cellule de code dans un notebook Jupyter : Ctrl + Enter.

  • ExĂ©cuter une cellule de code dans un notebook Jupyter et passer Ă  la cellule suivante : Shift + Enter.

  • ExĂ©cuter une cellule de code dans un notebook Jupyter et crĂ©er une nouvelle cellule en dessous : Alt + Enter.

Exercice 1 - Modifier un fichier texte existant

Objectif : Créez un programme en Python qui permet de modifier un fichier texte existant.

Instructions :

  1. CrĂ©ez un fichier texte nommĂ© cities.csv contenant le texte prĂ©sent dans l’encart situĂ© Ă  droite.

  2. CrĂ©ez un programme Python qui permet de modifier l’entĂȘte du fichier cities.csv en remplaçant les valeurs coord1 et coord2 par latitude et longitude. Nous traitons ici ce fichier comme un fichier texte, il n’est donc pas nĂ©cessaire d’utiliser la bibliothĂšque csv. Le nouveau contenu doit ĂȘtre Ă©crit dans un nouveau fichier nommĂ© cities-new.csv.

  3. ExĂ©cutez le programme et vĂ©rifiez que le fichier cities-new.csv a bien Ă©tĂ© crĂ©Ă© et qu’il contient le nouveau contenu.

  • Pour aller plus loin : importer la classe date du module datetime (from datetime import date) et essayez d’utiliser sa mĂ©thode today() pour faire en sortes que le nom du fichier de sortie contienne la date de l’exĂ©cution du programme (par exemple cities-2021-01-01.csv).

Contenu du fichier data.csv :

nom,coord1,coord2,value
Paris,48.8566,2.3522,10
Lyon,45.7640,4.8357,20
Marseille,43.2964,5.3700,30
Grenoble,45.1885,5.7245,40


ModĂšle de programme Python Ă  utiliser :

with open('cities.csv', 'r') as f:
    # Lire le contenu du fichier
    content = f.read()

# Écrire ici la ou les instructions permettant
# de modifier le contenu du fichier

with open('cities-new.csv', 'w') as f:
    # Écrire le nouveau contenu dans le fichier
    f.write(content)

Exercice 2 - Lister les couches d’un flux WMS ou WFS


Dans cet exercice, vous utiliserez un notebook Jupyter existant et contenant dĂ©jĂ  des instructions Python pour lister les couches d’un flux WMS ou WFS et les sauvegarder dans un fichier CSV.

L’utilisation d’un notebook Jupyter permet de tester facilement des instructions Python et de les exĂ©cuter pas Ă  pas.


Instructions :

  • RĂ©cupĂ©rez le notebook Jupyter ogc-web-services.ipynb et ouvrez-le dans VSCodium. Modifiez le selon les consignes que vous y trouverez.

Exercice 3 - Effectuer des géotraitements simples avec GeoPandas et Rasterio


Dans cet exercice, vous utiliserez un notebook Jupyter existant et contenant déjà des instructions Python pour effectuer des géotraitements simples avec GeoPandas et Rasterio.

L’utilisation d’un notebook Jupyter permet de tester facilement des instructions Python et de les exĂ©cuter pas Ă  pas.


Instructions :

  • RĂ©cupĂ©rer les donnĂ©es d’exemple data.zip et dĂ©compresser cette archive.

  • RĂ©cupĂ©rez le notebook Jupyter geopandas-rasterio.ipynb et ouvrez-le dans VSCodium. Modifiez-le selon les consignes que vous y trouverez. Vous devrez peut-ĂȘtre y modifier le chemin vers les donnĂ©es d’exemple.

Partie 3 - Python et

Pourquoi Python-QGIS ?


  • Lancer des commandes dans la console Python de QGIS

  • CrĂ©er et utiliser des extensions

  • ExĂ©cuter automatiquement un programme Python quand QGIS dĂ©marre

  • CrĂ©er des algorithmes de traitement

  • CrĂ©er des fonctions pour des expressions dans QGIS

  • CrĂ©er des applications personnalisĂ©es basĂ©es sur l’API QGIS

L’interface de programmation PyQGIS


L’interface de programmation PyQGIS permet d’accĂ©der Ă  la majoritĂ© des fonctionnalitĂ©s de QGIS depuis Python.

Elle est gĂ©nĂ©rĂ©e automatiquement Ă  partir du code C++ de QGIS et comporte plus de 2000 classes dĂ©crivant les diffĂ©rents aspects de l’application (QgsProject, QgsProcessingAlgorithm, QgsDialog, etc.), des donnĂ©es qui y figurent (QgsVectorLayer, QgsRasterLayer, QgsFeature, QgsGeometry, etc.) et des fonctionnalitĂ©s disponibles (QgsVectorFileWriter, etc.).

Il est nécessaire de consulter la documentation pour connaitre les méthodes et les attributs disponibles pour chaque classe.

Étendre les expressions QGIS avec Python


Dans QGIS, les expressions peuvent ĂȘtre utilisĂ©es dans diffĂ©rents mĂ©canismes de base : sĂ©lection par expression, calcul des valeurs d’un champ (calculatrice de champ), rĂšgles de style, Ă©tiquettes, etc.


QGIS prend Ă©galement en charge les expressions dĂ©finies par l’utilisateur. GrĂące Ă  Python, vous pouvez dĂ©finir vos propres fonctions qui peuvent ĂȘtre utilisĂ©es dans le moteur d’expression.

Étendre les expressions QGIS avec Python

Étendre les expressions QGIS avec Python

Étendre les expressions QGIS avec Python

Console Python dans QGIS


QGIS fournit une console interactive Python.

Elle peut ĂȘtre ouverte de plusieurs maniĂšres :

  • depuis le menu Extensions > Console Python
  • en utilisant le raccourci clavier Ctrl + Alt + P`
  • en utilisant le bouton dans la barre d’outils

Lors de l’utilisation de la console Python dans QGIS, une variable iface est dĂ©jĂ  existante. Cette variable est une instance de la classe QgisInterface et permet d’accĂ©der Ă  la majoritĂ© des fonctions de QGIS (accĂšs au canevas de la carte, aux couches, aux menus, barres d’outils, etc. etc.).

Exécuter des commandes dans la console Python


Exemple 1 : Afficher le nom des couches vecteur ou raster chargĂ©es dans QGIS et le nombre d’entitĂ©s pour les couches vecteur

for layer in iface.mapCanvas().layers():
    if layer.type() == QgsMapLayer.VectorLayer:
        print(f"Vecteur : {layer.name()} ({layer.featureCount()} entités) - {layer.crs().authid()}")
    elif layer.type() == QgsMapLayer.RasterLayer:
        print(f"Raster : {layer.name()} - {layer.crs().authid()}")


Exemple 2 : Accéder à la projection du projet

print(iface.mapCanvas().mapSettings().destinationCrs().authid())

Exécuter des commandes dans la console Python

Exemple 3 : Obtenir une référence vers une couche vecteur à partir de son nom et itérer sur ses entités

layer = QgsProject.instance().mapLayersByName('cities')[0]

# Une expression peut ĂȘtre utilisĂ©e pour filtrer les entitĂ©s
expression = "$id < 5"

# On peut utiliser la méthode getFeatures() de la couche
# pour obtenir un itérateur sur les entités :
# - si on ne lui donne pas d'argument,
#   on obtient toutes les entités de la couche
# - si on lui donne une expression,
#   on obtient seulement les entités qui vérifient l'expression
for feature in layer.getFeatures(expression):
    # Ici 'feature' est donc un objet de type QgsFeature
    # Nous pouvons consulter la documentation pour connaitre les méthodes et les attributs disponibles
    print(feature['name'])
    # On peut aussi accéder à la géométrie de l'entité
    geom = feature.geometry()
    print(geom.asWkt())

Exécuter des commandes dans la console Python

Exemple 4 : CrĂ©er une couche vecteur Ă  partir d’une couche existante et l’ajouter au projet

import random

layer = QgsProject.instance().mapLayersByName('cities')[0]

# On crée une couche mémoire vide
mem_layer = QgsVectorLayer('Polygon?crs=EPSG:4326', 'cities-buffer', 'memory')
pr = mem_layer.dataProvider()

# Spécification de deux champs
pr.addAttributes([
    QgsField("Buffer_value", QVariant.Double),
])
mem_layer.updateFields()

for feature in layer.getFeatures():
    ft = QgsFeature()
    # On calcule le buffer
    geom = QgsGeometry(feature.geometry())
    buffer_value = random.randint(1, 20)
    buffer = geom.buffer(buffer_value, 5)
    # On ajoute la géométrie à l'entité
    ft.setGeometry(buffer)
    ft.setAttributes([buffer_value])
    # On ajoute l'entité à la couche mémoire
    pr.addFeature(ft)

mem_layer.updateExtents()
    
# On ajoute la couche mémoire au projet
QgsProject.instance().addMapLayer(mem_layer)

Utiliser les algorithmes de traitement existants


QGIS fournit un ensemble d’algorithmes de traitement qui peuvent ĂȘtre utilisĂ©s dans la console Python, en utilisant le module Python processing.

# On peut lister les algorithmes disponibles
for alg in QgsApplication.processingRegistry().algorithms():
    print("{}:{} --> {}".format(
        alg.provider().name(), alg.name(), alg.displayName()))


Il est possible de consulter la documentation de chaque algorithme en utilisant la méthode help().

processing.algorithmHelp("qgis:voronoipolygons")

Ou de consulter la documentation en ligne : https://docs.qgis.org/latest/en/docs/user_manual/processing_algs/qgis/vectorgeometry.html#voronoi-polygons

C’est grĂące Ă  ces informations que vous aurez connaissance des paramĂštres nĂ©cessaires pour utiliser un algorithme.

Notez que les paramĂštres sont les mĂȘmes que ceux utilisĂ©s dans l’interface graphique (et qui peuvent ĂȘtre consultĂ©s dans la boite de dialogue de l’algorithme).

Utiliser les algorithmes de traitement existants


import processing

# Pour utiliser un algorithme, il faut connaitre son nom complet
# Généralement, deux arguments sont nécessaires : son nom et les paramÚtres
result = processing.run("qgis:voronoipolygons", {
    'INPUT': 'cities',
    'BUFFER': 0,
    'OUTPUT': 'TEMPORARY_OUTPUT'
})

# On peut ensuite ajouter le résultat au projet
# (l'objet retourné par la méthode run() est un dictionnaire,
# on peut donc accéder à la couche résultat avec la clé 'OUTPUT')
QgsProject.instance().addMapLayer(result['OUTPUT'])

Utiliser les algorithmes de traitement existants

Notez que les paramĂštres sont les mĂȘmes que ceux utilisĂ©s dans l’interface graphique (et qui peuvent ĂȘtre consultĂ©s dans la boite de dialogue de l’algorithme).

Créer des algorithmes de traitement

Il est possible de crĂ©er des algorithmes de traitement personnalisĂ©s sous forme d’un script Python.

Ils seront ajoutĂ©s Ă  la boite Ă  outils de traitement et pourront ĂȘtre utilisĂ©s comme n’importe quel autre algorithme.

Créer des algorithmes de traitement

Ces algorithmes peuvent rĂ©utiliser d’autres algorithmes existants dĂ©jĂ  dans la boite Ă  outils gĂ©otraitement (cf. exemple dans la documentation) ou non (cf. exemple ci-dessous).

from qgis import processing
from qgis.processing import alg
from qgis.core import (
    QgsProject,
    QgsVectorLayer,
    QgsField,
    QgsFeature,
    QgsGeometry
)
from PyQt5.QtCore import QVariant
import random

@alg(name='bufferrandomdistance', label='Our custom buffer operation with random distance',
     group='examplescripts', group_label='Example scripts')
@alg.input(type=alg.SOURCE, name='INPUT', label='Input vector layer')
@alg.input(type=alg.VECTOR_LAYER_DEST, name='OUTPUT', label='Buffer output')
@alg.input(type=alg.INT, name='BUFFERDISTMIN', label='BUFFER MINIMUM DISTANCE',
           default=1.0)
@alg.input(type=alg.INT, name='BUFFERDISTMAX', label='BUFFER MAXIMUM DISTANCE',
           default=100.0)
@alg.output(type=alg.NUMBER, name='NUMBEROFFEATURES',
            label='Number of features processed')
def bufferrandomdistance(instance, parameters, context, feedback, inputs):
    """
    Description of the algorithm.
    (If there is no comment here, you will get an error)
    """
    input_featuresource = instance.parameterAsSource(
      parameters, 'INPUT', context)
    numfeatures = input_featuresource.featureCount()
    bufferdistmin = instance.parameterAsInt(
      parameters, 'BUFFERDISTMIN', context)
    bufferdistmax = instance.parameterAsInt(
        parameters, 'BUFFERDISTMAX', context)

    if feedback.isCanceled():
        return {}

    # On crée une couche mémoire vide
    mem_layer = QgsVectorLayer(
    f'Polygon?crs={input_featuresource.sourceCrs().authid()}',
    'Buffer output',
    'memory',
    )
    pr = mem_layer.dataProvider()
    
    # Spécification de deux champs
    pr.addAttributes([
        QgsField("Buffer_value", QVariant.Double),
    ])
    mem_layer.updateFields()
    
    for feature in input_featuresource.getFeatures():
        ft = QgsFeature()
        # On calcule le buffer
        geom = QgsGeometry(feature.geometry())
        buffer_value = random.randint(bufferdistmin, bufferdistmax)
        buffer = geom.buffer(buffer_value, 5)
        # On ajoute la géométrie à l'entité
        ft.setGeometry(buffer)
        ft.setAttributes([buffer_value])
        # On ajoute l'entité à la couche mémoire
        pr.addFeatures([ft])
    
    if feedback.isCanceled():
        return {}

    QgsProject.instance().addMapLayer(mem_layer)
    
    return {
      'OUTPUT': mem_layer.id(),
      'NUMBEROFFEATURES': numfeatures
    }

Créer un plugin QGIS

Il est possible de crĂ©er des plugins QGIS en Python. Par rapport Ă  la crĂ©ation d’algorithmes de traitement, les plugins :

  • permettent de crĂ©er des interfaces graphiques plus complexes (avec plusieurs fenĂȘtres, des onglets, etc.),
  • permettent d’inclure une documentation complexe au format HTML,
  • permettent d’ajouter des donnĂ©es d’exemples,
  • permettent d’ajouter des fonctionnalitĂ©s Ă  QGIS qui ne sont pas des algorithmes de traitement (par exemple : ajouter une barre d’outils, ajouter un menu, etc.),
  • peuvent ĂȘtre partagĂ©s avec la communautĂ© via le QGIS Python Plugins Repository.


À l’inverse des algorithmes de traitement, vous dĂ©velloperez un plugin QGIS dans un environnement de dĂ©veloppement Python classique (VSCodium, Spyder, etc.) et non dans l’éditeur de code de QGIS.

Créer un plugin QGIS

Étude de cas : Le plugin DistanceCartogram permet de crĂ©er des cartogrammes de distance dans QGIS.

RĂ©fĂ©rences sur l’API Python de QGIS



Partie 4 - Mise en pratique

Choisir une des 3 mises en situation proposées dans ce qui suit.

Mise en situation 1

Objectif : Étendre les expressions QGIS avec Python

Instructions :

  • Ouvrez un nouveau projet QGIS et ajoutez les donnĂ©es d’exemple (vous pouvez utiliser un projet QGIS personnel si vous le souhaitez).
  • Copiez-collez le code de la fonction (prĂ©sentĂ©e prĂ©cĂ©demment et rappelĂ© ci-contre) dans la fenĂȘtre permettant de dĂ©finir une nouvelle fonction dans QGIS.
  • Testez la fonction dans la fenĂȘtre de sĂ©lection par expression pour la couche de points.
  • Que manque-t-il Ă  cette fonction pour qu’elle soit utilisable pour une couche de lignes ou de polygones ? Modifiez la fonction pour qu’elle puisse ĂȘtre utilisĂ©e pour des couches de lignes ou de polygones.
  • Testez la fonction dans la fenĂȘtre de sĂ©lection par expression pour la couche de polygones.


from qgis.core import *
from qgis.gui import *
import math

@qgsfunction(args='auto', group='Custom', usesgeometry=True)
def get_utm_zone(feature, parent):
    """
    Return the UTM Zone of the
    feature's geometry as a String
    """
    pt = feature.geometry().asPoint()
    longitude = pt.x()
    latitude = pt.y()
    zone_number = math.floor(((longitude + 180) / 6) % 60) + 1

    if latitude >= 0:
        zone_letter = 'N'
    else:
        zone_letter = 'S'

    return f"{int(zone_number)}{zone_letter}"

Mise en situation 2

Objectif : Se familiariser avec la console Python de QGIS et manipuler les données du projet actuel.

Instructions :

  • Ouvrez un nouveau projet QGIS et ajoutez les donnĂ©es d’exemple (vous pouvez utiliser un projet QGIS personnel si vous le souhaitez).

  • Ouvrez la console Python de QGIS.

  • Testez l’ensemble des commandes prĂ©sentĂ©es prĂ©cĂ©demment dans la console Python. Vous pouvez d’abord copier ces morceaux de code dans l’éditeur de texte de la console Python puis les exĂ©cuter en utilisant le bouton ▶ ou le raccourci clavier Ctrl + e.

  • Testez de nouveaux gĂ©otraitements, au choix :

    • un ou plusieurs algorithmes de traitement existants (voir la liste des algorithmes disponibles dans la console Python),
    • en itĂ©rant sur les entitĂ©s d’une couche vecteur et en choisissant une mĂ©thode de traitement (par exemple : calcul de la distance entre deux entitĂ©s, calcul de la longueur d’une ligne, etc.),

Mise en situation 3

Objectif : Comprendre le fonctionnement d’un plugin QGIS existant (pas de code Ă  Ă©crire dans cette mise en situation).

Instructions :

  • Consultez le code source du plugin Semi-Automatic Classification sur GitHub.
  • Sur la base des indications, prĂ©sentes dans la documentation officielle et affichĂ©es ci-dessous, identifiez les diffĂ©rents fichiers Python responsables de :
    • dĂ©finir de la classe principale (permettant d’enregistrer le plugin dans QGIS),
    • dĂ©finir des interfaces graphiques,
    • dĂ©finir les fonctionnalitĂ©s centrales du plugin (calcul du profil altimĂ©trique, etc.).

Outro

Corrections des exercices


Partie 2


Partie 4

Références bibliographiques

Dorman, M., Graser, A., Nowosad, J. et Lovelace, R. (2022). Geocomputation with Python. CRC Press. https://py.geocompx.org/
GDAL/OGR contributors. (2022). GDAL/OGR Geospatial Data Abstraction software Library. Open Source Geospatial Foundation. https://doi.org/10.5281/zenodo.5884351
GEOS contributors. (2021). GEOS coordinate transformation software library. Open Source Geospatial Foundation. https://libgeos.org/
Gillies, S. et al. (2013). Rasterio: geospatial raster I/O for Python programmers. Mapbox. https://github.com/rasterio/rasterio
Gillies, S., Buffat, R., Arnott, J., Taves, M. W., Wurster, K., Snow, A. D., Cochran, M., Sales de Andrade, E. et Perry, M. (2023, janvier). Fiona. https://github.com/Toblerity/Fiona
Gillies, S., Wel, C. van der, Van den Bossche, J., Taves, M. W., Arnott, J., Ward, B. C., et al. (2023, janvier). Shapely. https://doi.org/10.5281/zenodo.5597138
Hoyer, S., Roos, M., Joseph, H., Magin, J., Cherian, D., Fitzgerald, C., Hauser, M., Fujii, K., Maussion, F., Imperiale, G., Clark, S., Kleeman, A., Nicholas, T., Kluyver, T., Westling, J., Munroe, J., Amici, A., Barghini, A., Banihirwe, A., Bell, R., et al.Wolfram, P. J. (s. d.). xarray. https://doi.org/10.5281/zenodo.598201
Jordahl, K., Bossche, J. V. den, Fleischmann, M., Wasserman, J., McBride, J., Gerard, J., Tratner, J., Perry, M., Badaracco, A. G., Farmer, C., Hjelle, G. A., Snow, A. D., Cochran, M., Gillies, S., Culbertson, L., Bartos, M., Eubank, N., maxalbert, Bilogur, A., Rey, S., et al.Leblanc, F. (2020, juillet). geopandas/geopandas: v0.8.1. Zenodo. https://doi.org/10.5281/zenodo.3946761
PROJ contributors. (2021). PROJ coordinate transformation software library. Open Source Geospatial Foundation. https://proj.org/
Rey, S. J. et Anselin, L. (2007). PySAL: A Python Library of Spatial Analytical Methods. The Review of Regional Studies, 37(1), 5‑27.
Snow, A. D., Brochart, D., Raspaud, M., Taves, M., Bell, R., RichardScottOZ, Chegini, T., stefank0, Amici, A., Braun, R. et al., et. (2023). corteva/rioxarray: 0.14.1 Release. https://doi.org/10.5281/zenodo.7829704
Snow, A. D., Whitaker, J., Cochran, M., Miara, I., Bossche, J. V. den, Mayo, C., Cochrane, P., Lucas, G., Kloe, J. de, Karney, C. et al., et. (2023). pyproj4/pyproj: 3.5.0 Release. https://doi.org/10.5281/zenodo.7776548
The pandas development team. (s. d.). pandas-dev/pandas: Pandas. https://github.com/pandas-dev/pandas