Skip to content

Commit

Permalink
Merge pull request #393 from ign-packo/createQgisView
Browse files Browse the repository at this point in the history
Create qgis view
  • Loading branch information
amrosu authored Apr 19, 2023
2 parents 439dea3 + 8ca3662 commit b409fe2
Show file tree
Hide file tree
Showing 6 changed files with 678 additions and 38 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/lint-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ jobs:
- name: Analysing the code with pylint
run: |
pylint scripts/update_cache.py scripts/create_cache.py scripts/cache.py scripts/cache_def.py scripts/export_mtd.py scripts/prep_vectorise_graph.py scripts/vectorise_graph.py
pylint scripts/update_cache.py scripts/create_cache.py scripts/cache.py scripts/cache_def.py scripts/export_mtd.py scripts/prep_vectorise_graph.py scripts/vectorise_graph.py scripts/create_qgis_view.py scripts/process_requests.py scripts/process_qlayers.py
continue-on-error: true

- name: Analysing the code with flake8
run: |
flake8 --max-line-length 100 scripts/update_cache.py scripts/create_cache.py scripts/cache.py scripts/cache_def.py scripts/export_mtd.py scripts/prep_vectorise_graph.py scripts/vectorise_graph.py
flake8 --max-line-length 100 scripts/update_cache.py scripts/create_cache.py scripts/cache.py scripts/cache_def.py scripts/export_mtd.py scripts/prep_vectorise_graph.py scripts/vectorise_graph.py scripts/create_qgis_view.py scripts/process_requests.py scripts/process_qlayers.py
60 changes: 51 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,9 @@ Si un cache a une taille de dalle (slabSize) différente de 16x16 tuiles ou une

## Préparation des éléments de la vue PackO pour QGIS

Dans le cas d'utilisation d'un client pour PackO basé sur QGIS, on peut générer des éléments de la vue du chantier en utilisant le script **create_qgis_view.py** :
Dans le cas d'utilisation d'un client pour PackO basé sur QGIS, on peut créer automatiquement la vue contenant les éléments du chantier en utilisant le script **create_qgis_view.py** :
````
usage: create_qgis_view.py [-h] [-u URL] -c CACHE_ID [-b BRANCH_NAME] [-s STYLE_ORTHO] [-o OUTPUT] [-v VERBOSE]
usage: create_qgis_view.py [-h] [-u URL] -c CACHE_ID [-b BRANCH_NAME] [-s {RVB,IR,IRC}] [-o OUTPUT] [-z ZOOM_PIVOT] [--vect VECT] [--bbox BBOX BBOX BBOX BBOX] [-m MACROS] [-v VERBOSE]
options:
-h, --help show this help message and exit
Expand All @@ -323,19 +323,61 @@ options:
-s {RVB,IR,IRC}, --style_ortho {RVB,IR,IRC}
style for ortho to be exported to xml (default: RVB)
-o OUTPUT, --output OUTPUT
output path (default: ./)
output qgis view path (default: ./view.qgs)
-z ZOOM_PIVOT, --zoom_pivot ZOOM_PIVOT
layer visibility scale for surface graph [1:10000000,1:zoom_pivot] & for contour graph [1:zoom_pivot,1:1] (default:3025)
--vect VECT vectors folder path
--bbox BBOX BBOX BBOX BBOX
bounding box defining the view extent (Xmin Ymin Xmax Ymax)
-m MACROS, --macros MACROS
macros file path
-v VERBOSE, --verbose VERBOSE
verbose (default: 0, meaning no verbose)
````
**-c** est l'identifiant du cache de travail dans la base de données : pour le récupérer, on peut demander à l'API la liste des caches en utilisant l'url `http://[serveur]:[port]/caches` ou la commande curl `curl [-v] -X "GET" "http://[serveur]:[PORT]/caches" -H "accept: */*"`.

Actuellement, les éléments de la vue générés avec ce script sont :
Les éléments de la vue générés avec ce script sont :
- une nouvelle branche PackO créée sur le cache indiqué ; le nom de la branche est par défaut "newBranch", nom de branche à indiquer avec **-b**.
- **ortho.xml** et **graph.xml** : les couches ortho et graphe de la nouvelle branche, exportées sous forme de fichiers xml plus des modifications pour QGIS, dans le dossier de sortie (chemin à indiquer avec **-o**). Pour l'ortho, si le style est différent de celui par défaut ("RVB"), il faut l'indiquer avec **-s**.
- **graph_contour.vrt** : la couche contour de graphe générée à partir de graph.xml avec des ajouts et modifications pour QGIS, dans le dossier de sortie

Pour le bon fonctionnement dans QGIS, il est impératif de mettre la variable d'environnement **GDAL_VRT_ENABLE_PYTHON** à **YES**.

- **ortho.xml** et **graphe_surface.xml** : couches ortho et graphe de la nouvelle branche, exportées sous forme de fichiers xml plus des modifications pour QGIS, dans le dossier de sortie (le chemin de la vue à indiquer avec **-o**). Pour l'ortho, si le style est différent de celui par défaut ("RVB"), il faut l'indiquer avec **-s**. L'échelle de visibilité de la couche *graphe_surface* est définie avec l'option **-z** (zoom_pivot, par défaut 3025) : [1:10000000, 1:zoom_pivot].
- **graphe_contour.vrt** : couche contour de graphe générée à partir de graphe_surface.xml avec des ajouts et modifications pour QGIS, dans le dossier de sortie. L'échelle de visibilité de la couche *graphe_contour* : [1:zoom_pivot, 1:1]
- **retouches_graphe.gpkg** : couche vecteur, initialement vide, utilisée pour les retouches du graphe
- **avancement.gpkg** : couche vecteur, initialement vide, utilisée pour garder la trace des zones contrôlées
- **retouches_info.gpkg** : couche vecteur, initialement vide, utilisée pour les retouches infographiques
- **retouches_info_sauv.gpkg** : couche vecteur, initialement vide, utilisée pour les sauvegardes liées aux retouches infographiques
- **remarques.gpkg** : couche vecteur, initialement vide, utilisée pour les remarques sur la vue

Ces éléments sont des couches de la vue PackO pour QGIS (par défaut **view.qgs**), auxquelles s'ajoute une couche OPI générée en important la couche WMTS OPI de la branche du cache.

Des vecteurs externes *.shp* et *.gpkg* peuvent être intégrés à la vue en indiquant le chemin vers le dossier les contenant avec **--vect**.

Dans le cas où l'on veut avoir une emprise pour la vue (emprise de travail) plus petite que l'emprise du chantier (emprise du cache), elle est à indiquer avec **--bbox** Xmin Ymin Xmax Ymax.

Pour intégrer un fichier de macros QGIS à la vue, il faut indiquer le chemin vers le fichier macros prototype avec **-m**. Ce fichier sera adapté au chantier avant d'être intégré à la vue, en remplaçant les clés `__IDBRANCH__`, `__URLSERVER__` et `__TILEMATRIXSET__` avec les valeurs correspondantes pour le chantier - exemple :

- Extrait prototype macros, avant adaptation :
```
id_branch = __IDBRANCH__
url_server = __URLSERVER__
tile_matrix_set = __TILEMATRIXSET__
```
- Extrait macros, après adaptation :
```
id_branch = '32'
url_server = 'http://localhost:8081/'
tile_matrix_set = 'LAMB93_20cm'
```

Un exemple de fichier macros prototype est fourni dans le dossier *ressources*.

Pour le bon fonctionnement dans QGIS, il est impératif de mettre la variable d'environnement **GDAL_VRT_ENABLE_PYTHON** à **YES**. Il faut également définir les variables d'environnement (où `<qgispath>` doit être remplacé par le chemin d'accès au dossier d'installation de QGIS ; exemples de `<qgispath>` sous Linux : **/usr** , sous Windows **C:\Program Files\QGIS XXX\apps\qgis** où 'XXX' est à remplacer avec la version de QGIS) :
- **PYTHONPATH** :
- sous Linux : `export PYTHONPATH=/<qgispath>/share/qgis/python`
- sous Windows : `set PYTHONPATH=C:\<qgispath>\python` ; sous windows, si elle n'existe pas, création automatique de cette variable d'environnement à partir de la variable d'environnement OSGEO4W_ROOT.
- **LD_LIBRARY_PATH** :
- sous Linux : `export LD_LIBRARY_PATH=/<qgispath>/lib`
- sous Windows: `set PATH=C:\<qgispath>\bin;C:\<qgispath>\apps\<qgisrelease>\bin;%PATH%` (où `<qgisrelease>` devrait être remplacé avec le type de release ciblé (ex : qgis-ltr, qgis, qgis-dev)

Si la vue contient des macros, il faut activer leur utilisation lors du chargement de la vue dans QGIS.

## Traitement d'un chantier

Expand Down
235 changes: 235 additions & 0 deletions ressources/example_macros_qgis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
def openProject():
pass

def saveProject():
pass

def closeProject():
pass

import json, requests
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from qgis.utils import iface
from qgis.gui import *
from qgis.core import *
import time

# ===================================
# ==== CONFIG A VERIFIER ============
# ===================================
id_branch = __IDBRANCH__
url_server = __URLSERVER__
tile_matrix_set = __TILEMATRIXSET__
# ===================================

url_graph = url_server + id_branch + '/graph'
url_patch = url_server + id_branch + '/patch'
url_undo = url_server + id_branch + '/patch/undo'
url_redo = url_server + id_branch + '/patch/redo'
url_wmts = url_server + id_branch + '/wmts'
source='contextualWMSLegend=0&crs=EPSG:2154&dpiMode=7&featureCount=10&format=image/png&layers=opi&styles=RVB&tileDimensions=Name%3DXXX&tileMatrixSet='+tile_matrix_set+'&url='+url_wmts+'?SERVICE%3DWMTS%26REQUEST%3DGetCapabilities%26VERSION%3D1.0.0'
OPI=None
color=None
opi_layer = None
ortho_layer = None
patch_layer = None
graph_layer = None
for layer in QgsProject.instance().mapLayers().values():
name = layer.name()[0:3].upper()
if (name == 'OPI'):
opi_layer = layer
if (name == 'ORT'):
ortho_layer = layer
if (name == 'PAT'):
patch_layer = layer
if (name == 'GRA'):
graph_layer = layer


#print("POC PACKO")
# iface.mapCanvas().setCachingEnabled(False)


def sendPatch(feature, OPI, color):
#print("sendPatch:", feature, OPI, color)
exporter=QgsJsonExporter()
patch = json.loads(exporter.exportFeatures([feature]))
#print(patch)
patch['crs']={'type': 'name', 'properties': {'name': 'urn:ogc:def:crs:EPSG::2154'}}
patch['features'][0]['properties']={'color': color, 'opiName': OPI}
res = requests.post(url_patch, json=patch)
return res.text

def selectOPI(x, y):
#print("selectOPI")
res = requests.get(url_graph, params={'x':x, 'y':y})
sel=json.loads(res.text)
#print(sel)
if 'opiName' in sel.keys():
return sel['opiName'], sel['color']
else:
return None, None

def on_key(event):
global OPI
global color
#print("on_key")
touche = event.key()
#print(touche)
iface.messageBar().clearWidgets()
if (touche == Qt.Key_M):
iface.messageBar().pushMessage("PATCH ", "EN COURS : ", level=Qgis.Warning, duration=0)
nb_features = patch_layer.featureCount()
if (OPI is None) or (color is None):
msg = QMessageBox()
msg.setIcon(QMessageBox.Information)
msg.setText("PAS D'OPI SELECTIONNEE'")
msg.setWindowTitle("ERREUR")
msg.setStandardButtons(QMessageBox.Ok )
msg.exec_()
OPI = None
return
if nb_features == 0:
msg = QMessageBox()
msg.setIcon(QMessageBox.Information)
msg.setText("PAS DE RETOUCHE")
msg.setWindowTitle("ERREUR")
msg.setStandardButtons(QMessageBox.Ok )
msg.exec_()
OPI = None
return
if nb_features > 1:
msg = QMessageBox()
msg.setIcon(QMessageBox.Information)
msg.setText("UNE SEULE RETOUCHE A LA FOIS")
msg.setWindowTitle("ERREUR")
msg.setStandardButtons(QMessageBox.Ok )
msg.exec_()
OPI = None
return
patch_layer.startEditing()
feature = list(patch_layer.getFeatures())[0]
mess = sendPatch(feature, OPI, color)
print(mess)
patch_layer.deleteFeature(feature.id())
patch_layer.commitChanges()
iface.messageBar().pushMessage("PATCH ", "APPLIQUÉ : ", level=Qgis.Success, duration=0)
graph_layer.setDataSource(graph_layer.source(), "graphe_contour", "gdal")
ortho_layer.setDataSource(ortho_layer.source(), "ortho", "gdal")
OPI = None
# pour ne pas a avoir a remettre en mode edition pour la prochiane saisie
patch_layer.startEditing()
return

if (touche == Qt.Key_P):
# Pick OPI
lastPoint = iface.mapCanvas().mouseLastXY()
lastPointTerr = iface.mapCanvas().getCoordinateTransform().toMapCoordinates(lastPoint.x(), lastPoint.y())
OPI, color = selectOPI(lastPointTerr.x(), lastPointTerr.y())
if OPI:
#print("ready: ", opi_layer, OPI, color)
opi_layer.setDataSource(source.replace('XXX', OPI), "OPI--"+OPI, "wms")
iface.messageBar().pushMessage("OPI ", "sélection actuelle : " + OPI + ' | ' + str(color), level=Qgis.Success, duration=0)
else:
#print("no OPI selected")
iface.messageBar().pushMessage("OPI ", "sélection impossible", level=Qgis.Critical, duration=0)

return

if (touche == Qt.Key_U):
#print("undo")
res = requests.put(url_undo)
#print(res.text)
#iface.mapCanvas().refreshAllLayers()
graph_layer.setDataSource(graph_layer.source(), "graphe_contour", "gdal")
ortho_layer.setDataSource(ortho_layer.source(), "ortho", "gdal")
iface.messageBar().pushMessage(res.text, level=Qgis.Success, duration=0)
return

if (touche == Qt.Key_R):
#print("redo")
res = requests.put(url_redo)
#print(res.text)
#iface.mapCanvas().refreshAllLayers()
graph_layer.setDataSource(graph_layer.source(), "graphe_contour", "gdal")
ortho_layer.setDataSource(ortho_layer.source(), "ortho", "gdal")
iface.messageBar().pushMessage(res.text, level=Qgis.Success, duration=0)
return

if (touche == Qt.Key_O):
id_opi_layer = QgsProject.instance().layerTreeRoot().findLayer(opi_layer.id())
if id_opi_layer.isVisible() :
id_opi_layer.setItemVisibilityChecked(False)
else :
id_opi_layer.setItemVisibilityChecked(True)
return

if (touche == Qt.Key_V):
id_groupe_vecteur = QgsProject.instance().layerTreeRoot().findGroup('VECTEURS')
if id_groupe_vecteur.isVisible() :
id_groupe_vecteur.setItemVisibilityChecked(False)
else :
id_groupe_vecteur.setItemVisibilityChecked(True)
return

if (touche == Qt.Key_G):
id_graph_layer = QgsProject.instance().layerTreeRoot().findLayer(graph_layer.id())
if id_graph_layer.isVisible() :
id_graph_layer.setItemVisibilityChecked(False)
else :
id_graph_layer.setItemVisibilityChecked(True)
return

if (touche == Qt.Key_Less):

avcmt = QgsProject.instance().mapLayersByName('avancement')[0]
avcmt.startEditing()
largeur_canvas = iface.mapCanvas().size().width()
hauteur_canvas = iface.mapCanvas().size().height()
coords_HG = iface.mapCanvas().getCoordinateTransform().toMapCoordinates(1, 1)
coords_HD = iface.mapCanvas().getCoordinateTransform().toMapCoordinates(largeur_canvas-2, 1)
coords_BD = iface.mapCanvas().getCoordinateTransform().toMapCoordinates(largeur_canvas-2, hauteur_canvas-2)
coords_BG = iface.mapCanvas().getCoordinateTransform().toMapCoordinates(1, hauteur_canvas-2)
geom = QgsGeometry.fromPolygonXY([[coords_HG,coords_HD,coords_BD,coords_BG]])
f = QgsFeature(avcmt.fields())
heurecomplete = time.localtime()
heure = str(heurecomplete.tm_hour).zfill(2)
minute = str(heurecomplete.tm_min).zfill(2)
seconde = str(heurecomplete.tm_sec).zfill(2)
str_heure = heure + minute + seconde
f.setGeometry(geom)
f.setAttribute("H_SAISIE", str_heure)
avcmt.addFeatures([f])
iface.vectorLayerTools().saveEdits(avcmt)
iface.vectorLayerTools().stopEditing(avcmt)
return

Direction={
Qt.Key_1:"coords_BG - coords_HD",
Qt.Key_2:"coords_BG - coords_HG",
Qt.Key_3:"coords_BD - coords_HG",
Qt.Key_4:"coords_HG - coords_HD",
Qt.Key_6:"coords_HD - coords_HG",
Qt.Key_7:"coords_HG - coords_BD",
Qt.Key_8:"coords_HG - coords_BG",
Qt.Key_9:"coords_HD - coords_BG"
}

if touche in Direction :

largeur_canvas = iface.mapCanvas().size().width()
hauteur_canvas = iface.mapCanvas().size().height()
coords_HG = iface.mapCanvas().getCoordinateTransform().toMapCoordinates(1, 1)
coords_HD = iface.mapCanvas().getCoordinateTransform().toMapCoordinates(largeur_canvas-2, 1)
coords_BG = iface.mapCanvas().getCoordinateTransform().toMapCoordinates(1, hauteur_canvas-2)
coords_BD = iface.mapCanvas().getCoordinateTransform().toMapCoordinates(largeur_canvas-2, hauteur_canvas-2)

decalage = eval(Direction[touche])
centre = iface.mapCanvas().center()
new_centre = centre + decalage
iface.mapCanvas().setCenter(new_centre)
iface.mapCanvas().redrawAllLayers()

iface.mapCanvas().keyReleased.connect(on_key)
Loading

0 comments on commit b409fe2

Please sign in to comment.