Skip to content

Commit

Permalink
MAJ du support
Browse files Browse the repository at this point in the history
  • Loading branch information
Gustry committed Jul 10, 2024
1 parent b1e707f commit c11b5d0
Show file tree
Hide file tree
Showing 12 changed files with 233 additions and 95 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Formation PyQGIS

Visible sur https://docs.3liz.org/formation-pyqgis

## Maintenance

En cas de MAj de la BDTOPO, faire un rechercher&remplacer `BDT_3-3_SHP_LAMB93_D0ZZ-EDYYYY-MM-DD`
4 changes: 2 additions & 2 deletions docs/action.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
* Pour la couche linéaire :
* Nous allons utiliser le script ci-dessous pour créer une couche **très** simple, mais qui permet de voir la construction
d'une ligne en partant de zéro
* Sinon, pour aller **beaucoup** plus vite, ajouter la couche `D_OSM_HYDROGRAPHIE/TRONCON_COURS_EAU.shp`
* Sinon, pour aller **beaucoup** plus vite, ajouter la couche `HYDROGRAPHIE/COURS_D_EAU.shp`
mais on ne voit pas comment construire la géométrie en partant de rien.
* Faire un style rapide pour mettre en évidence le sens de la ligne à l'aide d'une `Ligne de symbole` dans
l'onglet `Symbologie` de la couche en question.

!!! info
La couche `D_OSM_HYDROGRAPHIE/TRONCON_COURS_EAU.shp` est de type multilinestring. Nous allons donc prendre en compte
La couche `HYDROGRAPHIE/COURS_D_EAU.shp` est de type multilinestring. Nous allons donc prendre en compte
ce cas par défaut dans la suite de ce tutoriel.

```python
Expand Down
84 changes: 39 additions & 45 deletions docs/console.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,9 @@
# Introduction à la console Python

## La documentation et les liens utiles

* QGIS est composé de plusieurs centaines de classes écrites en C++.
La plupart de ces classes (et donc des fonctions) sont accessibles à travers un API en Python.
Comme il n'est pas possible de mémoriser entièrement l'API de QGIS, il est nécessaire de connaître la
documentation et comment rechercher des informations.
* QGIS repose sur la librairie Qt version 5 pour l'interface graphique et sur Python version 3.
* Toutes les classes QGIS commencent par `Qgs` et toutes les classes Qt commencent par `Q`.

Voici une liste de liens pour la documentation :

* [https://docs.qgis.org](https://docs.qgis.org) qui regroupe :
* [Le Python Cookbook https://docs.qgis.org/latest/fr/docs/pyqgis_developer_cookbook](https://docs.qgis.org/latest/fr/docs/pyqgis_developer_cookbook/) (recette de cuisine)
* [L'API C++ https://qgis.org/api/3.28/](https://qgis.org/api/3.28/)
* [L'API Python https://qgis.org/pyqgis/3.28/](https://qgis.org/pyqgis/3.28/)
* [Documentation de l'API Qt](https://doc.qt.io/qt-5/classes.html)
* [Documentation de Python](https://docs.python.org/3/library/)
* [Le module os.path par exemple](https://docs.python.org/3/library/os.path.html#module-os.path), module historique pour manipuler des chemins
* [Le module Pathlib](https://docs.python.org/3/library/pathlib.html#module-pathlib)

Voici une liste non exhaustive de blog-post utiles pour manipuler PyQGIS :

* [Optimisation des couches vecteurs](https://nyalldawson.net/2016/10/speeding-up-your-pyqgis-scripts/)
* [Parcourir la légende en 3 parties](https://www.lutraconsulting.co.uk/blog/2014/07/06/qgis-layer-tree-api-part-1/)
* [Plugin Processing](http://www.qgistutorials.com/en/docs/3/processing_python_plugin.html)
* [Workshop sur les expressions en Python](https://madmanwoo.gitlab.io/foss4g-python-workshop/)

Autre lien pour l'apprentissage de Python (sans QGIS) :

* [https://openclassrooms.com/fr/courses/235344-apprenez-a-programmer-en-python](https://openclassrooms.com/fr/courses/235344-apprenez-a-programmer-en-python)

## Configurer le projet

* Commencer un nouveau projet et enregistrer le.
* À côté du projet, ajouter le dossier provenant d’OSM2Igeo, par exemple `201909_11_ILE_DE_FRANCE_SHP_L93_2154`.
* À côté du projet, ajouter le dossier provenant de la BDTopo, par exemple `BDT_3-3_SHP_LAMB93_D0ZZ-EDYYYY-MM-DD`.

## Manipulation dans la console

Expand Down Expand Up @@ -79,12 +48,24 @@ class Voiture{
}
```

On peut continuer en écrivant une classe qui va contenir une **Personne** :

```mermaid
classDiagram
class Personne{
+String Nom
+String Prenom
+Date DateNaissance
+Date DatePermisB
}
```

### Pratique

* Dans QGIS, `Plugins` -> `Console Python`
* QGIS nous donne accès au projet actuel via la classe `QgsProject`
* [https://qgis.org/api/classQgsProject.html](https://qgis.org/api/classQgsProject.html)
* [https://qgis.org/pyqgis/3.28/core/QgsProject.html](https://qgis.org/pyqgis/3.28/core/QgsProject.html)
* [https://qgis.org/pyqgis/3.34/core/QgsProject.html](https://qgis.org/pyqgis/3.34/core/QgsProject.html)

* Dans la documentation (en C++ surtout), on remarque plusieurs sections :
* Public types
Expand All @@ -93,19 +74,32 @@ class Voiture{
* Public Member Functions
* Static Public Member Functions
* Nous verrons progressivement ces différentes sections.
* En haut de la documentation, il y a une explication sur le cas particulier de `QgsProject.instance()`.
* Recherchons `filename`.
```python
project = QgsProject.instance()
project.fileName()
```
* Ajoutons un titre à notre projet, recherchons donc `title` dans la page : `setTitle` dans la classe
* Ajoutons un titre à notre projet. Dans l'interface graphique, cela se passe dans les propriétés de notre projet.
Il y a donc des chances que cela soit aussi dans la classe **QgsProject**
* Recherchons donc `title` dans la page : `setTitle` dans la classe
[QgsProject](https://qgis.org/api/classQgsProject.html).

!!! warning
Il est important de bien pouvoir lire la signature des **méthodes**.
La méthode `title` retourne une **QString** et **ne prend pas** de paramètre.
La méthode `setTitle` retourne **rien**, (**void**) mais elle prend un paramètre, une **QString**.

* Nous souhaitons désormais changer la couleur de fond du projet.
* Recherchons `background`
* Nous allons devoir utiliser aussi la classe [QColor](https://doc.qt.io/qt-5/qcolor.html)

??? "Afficher la solution"
```python
color = QColor("#00A2FF")
QgsProject.instance().setBackgroundColor(color)
```

* Objectif, ajouter une couche vecteur contenu dans un dossier fils :
* Recherchons dans l'API le dossier racine du projet. *Indice*, en informatique, on appelle souvent cela le `home`.
* Nous allons utiliser le module `os.path` pour manipuler les dossiers.
Expand All @@ -115,13 +109,13 @@ project.fileName()
```python
from os.path import join, isfile, isdir
racine = QgsProject.instance().homePath()
join(racine, 'nexistepas')
'/home/etienne/Documents/3liz/formation/nexistepas'
isfile(join(racine,'nexistepas'))
join(racine, 'nexiste_pas')
'/home/etienne/Documents/3liz/formation/nexiste_pas'
isfile(join(racine,'nexiste_pas'))
False
isdir(join(racine,'nexistepas'))
isdir(join(racine,'nexiste_pas'))
False
chemin = join(racine, '201909_11_ILE_DE_FRANCE_SHP_L93_2154', 'H_OSM_ADMINISTRATIF')
chemin = join(racine, 'BDT_3-3_SHP_LAMB93_D0ZZ-EDYYYY-MM-DD', 'ADMINISTRATIF')
fichier_shape = join(chemin, 'COMMUNE.shp')
isfile(fichier_shape)
True
Expand All @@ -147,7 +141,7 @@ QgsProject.instance().addMapLayer(communes)
from pathlib import Path
project = QgsProject.instance()
racine = Path(project.homePath())
chemin = racine.joinpath('202201_OSM2IGEO_91_LANGUEDOC_ROUSSILLON_SHP_L93_2154', 'H_OSM_ADMINISTRATIF')
chemin = racine.joinpath('BDT_3-3_SHP_LAMB93_D0ZZ-EDYYYY-MM-DD', 'ADMINISTRATIF')
fichier_shape = chemin.joinpath('COMMUNE.shp')
# fichier_shape.is_file()
communes = QgsVectorLayer(str(fichier_shape), 'communes', 'ogr')
Expand All @@ -162,7 +156,7 @@ QgsProject.instance().addMapLayer(communes)
project = QgsProject.instance()

racine = project.homePath()
chemin = join(racine, '202103_OSM2IGEO_91_LANGUEDOC_ROUSSILLON_SHP_L93_2154', 'H_OSM_ADMINISTRATIF')
chemin = join(racine, 'BDT_3-3_SHP_LAMB93_D0ZZ-EDYYYY-MM-DD', 'ADMINISTRATIF')
fichier_shape = join(chemin, 'COMMUNE.shp')
communes = QgsVectorLayer(fichier_shape, 'communes', 'ogr')
communes.isValid()
Expand All @@ -171,7 +165,7 @@ QgsProject.instance().addMapLayer(communes)

* Explorer l'objet `communes` qui est un `QgsVectorLayer` à l'aide de la documentation pour chercher sa
géométrie, le nombre d'entités.
[API QgsVectorLayer C++](https://qgis.org/api/classQgsVectorLayer.html), [API QgsVectorLayer Python](https://qgis.org/pyqgis/3.28/core/QgsVectorLayer.html)
[API QgsVectorLayer C++](https://qgis.org/api/classQgsVectorLayer.html), [API QgsVectorLayer Python](https://qgis.org/pyqgis/3.34/core/QgsVectorLayer.html)
* Pour la géométrie, toujours utiliser l'énumération et non pas le chiffre

```python
Expand Down Expand Up @@ -210,7 +204,7 @@ QgsMapLayer <-- QgsRasterLayer

L'objet `QgsVectorLayer` hérite de `QgsMapLayer` qui est une classe commune avec `QgsMapLayer`.

[API QgsMapLayer C++](https://qgis.org/api/classQgsMapLayer.html), [API QgsMapLayer Python](https://qgis.org/pyqgis/3.28/core/QgsMapLayer.html)
[API QgsMapLayer C++](https://qgis.org/api/classQgsMapLayer.html), [API QgsMapLayer Python](https://qgis.org/pyqgis/3.34/core/QgsMapLayer.html)

Regardons la fonction `isinstance` qui permet de tester si un objet est une instance d'une classe :

Expand All @@ -231,8 +225,8 @@ Petit récapitulatif à tester pour voir si cela fonctionne correctement !

```python
from os.path import join
dossier = '201909_11_ILE_DE_FRANCE_SHP_L93_2154'
thematique = 'H_OSM_ADMINISTRATIF'
dossier = 'BDT_3-3_SHP_LAMB93_D0ZZ-EDYYYY-MM-DD'
thematique = 'ADMINISTRATIF'
couche = 'COMMUNE'

racine = QgsProject.instance().homePath()
Expand Down
35 changes: 35 additions & 0 deletions docs/documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Documentation et liens utiles

* QGIS est composé de plusieurs centaines de classes écrites en C++.
La plupart de ces classes (et donc des fonctions) sont accessibles à travers un API en Python.
Comme il n'est pas possible de mémoriser entièrement l'API de QGIS, il est nécessaire de connaître la
documentation et comment rechercher des informations.
* QGIS repose sur la librairie **Qt version 5** pour l'interface graphique et sur **Python version 3**.
* Toutes les classes QGIS commencent par `Qgs` et toutes les classes Qt commencent par `Q`.

!!! tip
QGIS est en train de migrer vers la librairie Qt version 6. QGIS 3.40 va _certainement_ avoir un support pour Qt6
et pouvoir faire des premiers tests PyQGIS.

Voici une liste de liens pour la documentation, tous en anglais, sauf le cookbook :

* [https://docs.qgis.org](https://docs.qgis.org) qui regroupe :
* [Le Python Cookbook https://docs.qgis.org/latest/fr/docs/pyqgis_developer_cookbook](https://docs.qgis.org/latest/fr/docs/pyqgis_developer_cookbook/) (recette de cuisine)
* [L'API C++ https://qgis.org/api/3.34/](https://qgis.org/api/3.34/)
* [L'API Python https://qgis.org/pyqgis/3.34/](https://qgis.org/pyqgis/3.34/)
* [Documentation de l'API Qt](https://doc.qt.io/qt-5/classes.html)
* [Documentation de Python](https://docs.python.org/3/library/)
* [Le module Pathlib](https://docs.python.org/3/library/pathlib.html#module-pathlib), "nouveau" module pour manipuler des chemins
* [Le module os.path](https://docs.python.org/3/library/os.path.html#module-os.path), module "historique" pour manipuler des chemins

Voici une liste non exhaustive de blog-post utiles pour manipuler PyQGIS, tous en anglais :

* [Cours PyQGIS de SpatialThoughts](https://courses.spatialthoughts.com/pyqgis-masterclass.html)
* [Optimisation des couches vecteurs](https://nyalldawson.net/2016/10/speeding-up-your-pyqgis-scripts/)
* [Parcourir la légende en 3 parties](https://www.lutraconsulting.co.uk/blog/2014/07/06/qgis-layer-tree-api-part-1/)
* [Plugin Processing](http://www.qgistutorials.com/en/docs/3/processing_python_plugin.html)
* [Workshop sur les expressions en Python](https://madmanwoo.gitlab.io/foss4g-python-workshop/)

Autre lien pour l'apprentissage de Python (sans QGIS) en français :

* [https://openclassrooms.com/fr/courses/235344-apprenez-a-programmer-en-python](https://openclassrooms.com/fr/courses/235344-apprenez-a-programmer-en-python)
5 changes: 5 additions & 0 deletions docs/extension-deploiement.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,8 @@ Lire [les recommandations](https://plugins.qgis.org/publish/) pour la publicatio

* Code source disponible
* `metadata.txt` avec les bonnes informations et des liens HTTP valides

## Tutoriel pour installer un dépôt

Notre [tutoriel](https://docs.3liz.org/tutorial/qgis-repository-fr/) pour l'installation d'un dépôt, avec ou sans
mot de passe.
43 changes: 29 additions & 14 deletions docs/fonctions-scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ Ci-dessous, voici le dernier script du chapitre précédent, mais avec la gestio

```python
from os.path import join, isfile, isdir
dossier = '201909_11_ILE_DE_FRANCE_SHP_L93_2154'
thematique = 'H_OSM_ADMINISTRATIF'
dossier = 'BDT_3-3_SHP_LAMB93_D0ZZ-EDYYYY-MM-DD'
thematique = 'ADMINISTRATIF'
couche = 'COMMUNE'

racine = QgsProject.instance().homePath()
Expand All @@ -75,7 +75,7 @@ else:
* Essayons de faire une fonction qui prend 2 paramètres
* la thématique (le dossier)
* le nom du shapefile
* La fonction se chargera de faire le nécessaire, par exemple: `charger_couche('H_OSM_ADMINISTRATIF', 'COMMUNE')`
* La fonction se chargera de faire le nécessaire, par exemple: `charger_couche('ADMINISTRATIF', 'COMMUNE')`
* La fonction peut également retourner `False` si la couche n'est pas chargée (une erreur) ou sinon l'objet couche.

```python
Expand All @@ -90,7 +90,7 @@ def charger_couche(thematique, couche):
??? "Afficher la solution intermédiaire"
```python
from os.path import join, isfile, isdir
dossier = '201909_11_ILE_DE_FRANCE_SHP_L93_2154'
dossier = 'BDT_3-3_SHP_LAMB93_D0ZZ-EDYYYY-MM-DD'


def charger_couche(thematique, couche):
Expand All @@ -110,7 +110,7 @@ def charger_couche(thematique, couche):
QgsProject.instance().addMapLayer(layer)
iface.messageBar().pushMessage('Bravo','Well done!', Qgis.Success)

thematique = 'H_OSM_ADMINISTRATIF'
thematique = 'ADMINISTRATIF'
couche = 'COMMUNE'
charger_couche(thematique, couche)
```
Expand All @@ -124,7 +124,7 @@ plus à gauche possible grâce à l'instruction `return` qui ordonne la sortie d

def charger_couche(thematique, couche):
"""Fonction qui charge une couche shapefile dans une thématique."""
dossier = '201909_11_ILE_DE_FRANCE_SHP_L93_2154'
dossier = 'BDT_3-3_SHP_LAMB93_D0ZZ-EDYYYY-MM-DD'

racine = QgsProject.instance().homePath()
if not racine:
Expand All @@ -145,8 +145,8 @@ plus à gauche possible grâce à l'instruction `return` qui ordonne la sortie d
iface.messageBar().pushMessage('Bravo','Well done!', Qgis.Success)
return layer

charger_couche('H_OSM_ADMINISTRATIF', 'COMMUNE')
charger_couche('H_OSM_ADMINISTRATIF', 'ARRONDISSEMENT')
charger_couche('ADMINISTRATIF', 'COMMUNE')
charger_couche('ADMINISTRATIF', 'ARRONDISSEMENT')
```

* Essayons de faire une fonction qui liste les shapefiles d'une certaine thématique.
Expand All @@ -165,7 +165,7 @@ import os

def liste_shapefiles(thematique):
"""Liste les shapefiles d'une thématique."""
dossier = '201909_11_ILE_DE_FRANCE_SHP_L93_2154'
dossier = 'BDT_3-3_SHP_LAMB93_D0ZZ-EDYYYY-MM-DD'
racine = QgsProject.instance().homePath()
shapes = []
for root, directories, files in os.walk(os.path.join(racine, dossier, thematique)):
Expand All @@ -174,7 +174,7 @@ def liste_shapefiles(thematique):
shapes.append(file.replace('.shp', ''))
return shapes

shapes = liste_shapefiles('H_OSM_ADMINISTRATIF')
shapes = liste_shapefiles('ADMINISTRATIF')
print(shapes)
```

Expand All @@ -186,14 +186,14 @@ from pathlib import Path
def liste_shapefiles(thematique):
"""Liste les shapefiles d'une thématique."""
racine = QgsProject.instance().homePath()
dossier = Path(racine).joinpath('202103_OSM2IGEO_91_LANGUEDOC_ROUSSILLON_SHP_L93_2154', thematique)
dossier = Path(racine).joinpath('BDT_3-3_SHP_LAMB93_D0ZZ-EDYYYY-MM-DD', thematique)
shapes = []
for file in dossier.iterdir():
if file.suffix.lower() == '.shp':
shapes.append(file.stem)
return shapes

shapes = liste_shapefiles('H_OSM_ADMINISTRATIF')
shapes = liste_shapefiles('ADMINISTRATIF')
print(shapes)
```

Expand All @@ -207,7 +207,7 @@ print(shapes)
```python
import os
from os.path import join, isfile, isdir
dossier = '202103_OSM2IGEO_91_LANGUEDOC_ROUSSILLON_SHP_L93_2154'
dossier = 'BDT_3-3_SHP_LAMB93_D0ZZ-EDYYYY-MM-DD'
# couche = 'COMMUNE'

def liste_shapesfiles(thematique):
Expand Down Expand Up @@ -251,7 +251,7 @@ print(shapes)
return layer


thematique = 'H_OSM_ADMINISTRATIF'
thematique = 'ADMINISTRATIF'
shapes = liste_shapesfiles(thematique)
for shape in shapes:
charger_couche(thematique, shape)
Expand Down Expand Up @@ -447,3 +447,18 @@ iface.messageBar().pushSuccess(
).format(output_file.parent, output_file)
)
```

## Connection d'un signal à une fonction

Nous avons pu voir que dans la documentation des librairies **Qt** et **QGIS**, il y a une section **Signals**.

Cela sert à déclencher du code Python lorsqu'un signal est émis.

Par exemple, dans la classe `QgsMapLayer`, cherchons un signal qui est émis **après** (before) que la session d'édition
commence.

```python
variable_de_lobjet.nom_du_signal.connect(nom_de_la_fonction)
```

Note, il ne faut pas écrire `nom_de_la_fonction()` car on ne souhaite pas **appeler** la fonction, juste **connecter**.
Loading

0 comments on commit c11b5d0

Please sign in to comment.