plugin_gn_tools/gn_tools/plugin_main.py
2026-03-19 14:06:08 +01:00

566 lines
19 KiB
Python
Executable File

#! python3 # noqa: E265
"""Main plugin module."""
# standard
from functools import partial
from pathlib import Path
from typing import Optional
# PyQGIS
from qgis.core import (
Qgis,
QgsApplication,
QgsSettings,
QgsProject,
QgsGeometry,
QgsMapLayerProxyModel,
QgsCoordinateReferenceSystem,
QgsCoordinateTransform,
)
from qgis.gui import QgisInterface
from qgis.PyQt.QtCore import QCoreApplication, QLocale, QTranslator, QUrl
from qgis.PyQt.QtGui import QDesktopServices, QIcon
from qgis.PyQt.QtWidgets import QAction, QMessageBox
from qgis.PyQt.QtNetwork import QNetworkAccessManager
from PyQt5.QtWidgets import QCheckBox, QApplication
# project
from gn_tools.__about__ import (
DIR_PLUGIN_ROOT,
__icon_path__,
__title__,
__uri_homepage__,
__domain_url__,
__biodiv_aura__,
__gbif__,
__api_sn_web__,
__api_sn_exp__,
__api_lst_ex__,
__api_cd_sta__,
)
from gn_tools.gui.dlg_settings import PlgOptionsFactory, PlgOptionsManager
from gn_tools.gui.dlg_gn_tools import gnToolsDialog
from gn_tools.gui.dlg_gn_synthese import gnToolsSynthese
from gn_tools.processing import (
GnToolsProvider,
GetLogin,
ParamWidget,
LoadingImport,
RectangleFeatureTool,
PivotStatus,
GetFilterFromAPI,
)
from gn_tools.toolbelt import PlgLogger
# ############################################################################
# ########## Classes ###############
# ##################################
class GnToolsPlugin:
def __init__(self, iface: QgisInterface):
"""Constructor.
:param iface: An interface instance that will be passed to this class which \
provides the hook by which you can manipulate the QGIS application at run time.
:type iface: QgsInterface
"""
self.iface = iface
self.canvas = iface.mapCanvas()
self.project = QgsProject.instance()
self.log = PlgLogger().log
self.provider: Optional[GnToolsProvider] = None
self.rectangle = None
self.src_bdd = {
'GéoNature':__domain_url__,
"Biodiv'AuRA":__biodiv_aura__
}
self.syn_params = {}
# translation
# initialize the locale
self.locale: str = QgsSettings().value("locale/userLocale", QLocale().name())[
0:2
]
locale_path: Path = (
DIR_PLUGIN_ROOT
/ "resources"
/ "i18n"
/ f"{__title__.lower()}_{self.locale}.qm"
)
self.log(message=f"Translation: {self.locale}, {locale_path}", log_level=Qgis.MessageLevel.NoLevel)
if locale_path.exists():
self.translator = QTranslator()
self.translator.load(str(locale_path.resolve()))
QCoreApplication.installTranslator(self.translator)
lst_src = []
for src,url in self.src_bdd.items():
if url != '':
self.src_bdd[src] = url + '/' if url[-1] != '/' else url
lst_src += [src]
# self.domain_url = self.src_bdd['GéoNature']
self.dlg = gnToolsDialog()
self.dlg_syn = gnToolsSynthese()
# Masquer les widgets
self.dlg.pub_access.setVisible(False)
# self.dlg.syn_tools.setVisible(False)
self.dlg_syn.date_min.clear()
self.dlg_syn.date_max.clear()
self.dlg.status.setDisabled(True)
self.dlg.src_database.addItems(['',*lst_src])
self.dlg.src_database.currentTextChanged.connect(
self.define_domain_url
)
# Biodiv'AuRA check box action
self.dlg.pub_access.toggled.connect(
self.dlg.mAuthConfigSelect.setEnabled
)
self.dlg.pub_access.toggled.connect(
self.dlg.mAuthConfigSelect.setDisabled
)
# Import the extent drawer tool and connect a signal to make the window appear once the extent is drawned
self.move_tool = RectangleFeatureTool(self.project, self.canvas)
self.move_tool.signal.connect(self.activate_window)
# Login to GN
self.dlg.login_gn.clicked.connect(self.connect_src)
# Filter Active Layer
self.dlg.draw_bounds.setDisabled(True)
self.dlg.bound_log.setReadOnly(True)
self.dlg.bound_log.setPlainText('')
self.dlg.draw_bounds.clicked.connect(self.pointer)
self.dlg.active_filter_layer.setDisabled(True)
self.dlg.active_filter_layer.clicked.connect(self.filter_layer_activate)
self.dlg.filter_layer.setDisabled(True)
self.dlg.use_entity.setDisabled(True)
self.dlg.use_entity.setChecked(False)
self.dlg.map_layers.setFilters(QgsMapLayerProxyModel.VectorLayer)
self.dlg.map_layers.setAllowEmptyLayer(True)
self.filter_layers()
self.canvas.layersChanged.connect(self.filter_layers)
self.dlg.filter_layer.setFilters(QgsMapLayerProxyModel.VectorLayer)
self.dlg.filter_layer.setAllowEmptyLayer(True)
self.dlg.filter_layer.layerChanged.connect(self.extend_layer)
self.dlg.map_layers.layerChanged.connect(self.param_cd_taxon)
# Filtre de la synthèse
self.dlg.syn_tools.clicked.connect(self.dlg_syn.show)
# Clear button
self.dlg.clear_filter.setDisabled(True)
self.dlg.clear_filter.clicked.connect(self.clear_filter)
# Finished Button
self.dlg.buttonBox.accepted.disconnect()
self.dlg.buttonBox.accepted.connect(self.accept)
self.dlg.buttonBox.rejected.connect(self.reject)
def initGui(self):
"""Set up plugin UI elements."""
self.manager = QNetworkAccessManager()
self.manager.accepted = False
# settings page within the QGIS preferences menu
self.options_factory = PlgOptionsFactory()
self.iface.registerOptionsWidgetFactory(self.options_factory)
# -- Actions
self.action_help = QAction(
QgsApplication.getThemeIcon("mActionHelpContents.svg"),
self.tr("Help"),
self.iface.mainWindow(),
)
self.action_help.triggered.connect(
partial(QDesktopServices.openUrl, QUrl(__uri_homepage__))
)
self.action_settings = QAction(
QgsApplication.getThemeIcon("console/iconSettingsConsole.svg"),
self.tr("Settings"),
self.iface.mainWindow(),
)
self.action_settings.triggered.connect(
lambda: self.iface.showOptionsDialog(
currentPage="mOptionsPage{}".format(__title__)
)
)
self.action_gn_tools = QAction(
QIcon(str(__icon_path__)),
f"{__title__} - Import project",
self.iface.mainWindow(),
)
self.iface.addToolBarIcon(self.action_gn_tools)
self.action_gn_tools.triggered.connect(
lambda: self.run_gn_tools()
)
# -- Menu
self.iface.addPluginToMenu(__title__, self.action_gn_tools)
self.iface.addPluginToMenu(__title__, self.action_settings)
self.iface.addPluginToMenu(__title__, self.action_help)
# -- Processing
self.initProcessing()
# -- Help menu
# documentation
self.iface.pluginHelpMenu().addSeparator()
self.action_help_plugin_menu_documentation = QAction(
QIcon(str(__icon_path__)),
f"{__title__} - Documentation",
self.iface.mainWindow(),
)
self.action_help_plugin_menu_documentation.triggered.connect(
partial(QDesktopServices.openUrl, QUrl(__uri_homepage__))
)
self.iface.pluginHelpMenu().addAction(
self.action_help_plugin_menu_documentation
)
self.dlg.is_connect.setText('Non connecté..')
def initProcessing(self):
"""Initialize the processing provider."""
self.provider = GnToolsProvider()
QgsApplication.processingRegistry().addProvider(self.provider)
def tr(self, message: str) -> str:
"""Get the translation for a string using Qt translation API.
:param message: string to be translated.
:type message: str
:returns: Translated version of message.
:rtype: str
"""
return QCoreApplication.translate(self.__class__.__name__, message)
def unload(self):
"""Cleans up when plugin is disabled/uninstalled."""
# -- Clean up toolbar
self.iface.removeToolBarIcon(self.action_gn_tools)
# -- Clean up menu
self.iface.removePluginMenu(__title__, self.action_gn_tools)
self.iface.removePluginMenu(__title__, self.action_help)
self.iface.removePluginMenu(__title__, self.action_settings)
# -- Clean up preferences panel in QGIS settings
self.iface.unregisterOptionsWidgetFactory(self.options_factory)
# -- Unregister processing
QgsApplication.processingRegistry().removeProvider(self.provider)
# remove from QGIS help/extensions menu
if self.action_help_plugin_menu_documentation:
self.iface.pluginHelpMenu().removeAction(
self.action_help_plugin_menu_documentation
)
# remove actions
del self.action_gn_tools
del self.action_settings
del self.action_help
def run(self):
"""Main process.
:raises Exception: if there is no item in the feed
"""
try:
self.log(
message=self.tr("Everything ran OK."),
log_level=Qgis.MessageLevel.Success,
push=False,
)
print("tom")
except Exception as err:
self.log(
message=self.tr("Houston, we've got a problem: {}".format(err)),
log_level=Qgis.MessageLevel.Critical,
push=True,
)
def define_domain_url(self):
self.src_name = self.dlg.src_database.currentText()
if self.src_name != '':
self.domain_url = self.src_bdd[self.src_name]
self.dlg.report_url.setText(self.domain_url[:42])
if self.src_name == 'GBIF':
self.dlg.mAuthConfigSelect.setDisabled(True)
self.dlg.login_gn.setDisabled(True)
self.dlg.is_connect.setText('No connection need')
self.print_change()
ParamWidget(
parent=self,network_manager=self.manager
)
else:
self.dlg.mAuthConfigSelect.setEnabled(True)
self.dlg.login_gn.setEnabled(True)
self.dlg.is_connect.setText('Non connecté..')
if self.src_name == "Biodiv'AuRA":
self.dlg.pub_access.setVisible(True)
self.dlg.status.setDisabled(True)
else :
self.dlg.pub_access.setVisible(False)
self.dlg.pub_access.setChecked(False)
self.dlg.status.setEnabled(True)
else:
self.dlg.clear_filter.setDisabled(True)
self.dlg.draw_bounds.setDisabled(True)
self.dlg.active_filter_layer.setDisabled(True)
self.dlg.pub_access.setVisible(False)
self.dlg.pub_access.setChecked(False)
self.dlg.mAuthConfigSelect.setDisabled(True)
self.dlg.login_gn.setDisabled(True)
self.dlg.status.setDisabled(True)
self.dlg.is_connect.setText('Non connecté..')
def filter_layers(self):
checked_layers = self.canvas.layers()
excluded_layers = [l for l in QgsProject.instance().mapLayers().values() if not l in checked_layers]
self.dlg.map_layers.setExceptedLayerList(excluded_layers)
self.dlg.filter_layer.setExceptedLayerList(excluded_layers)
self.param_cd_taxon()
return excluded_layers
def run_gn_tools(self):
"""Main process. Create a QGIS project with ZH data from GeoNature."""
print("****** CREATE PROJECT ******")
# Get base url for all the request from the dlg settings
self.plg_settings = PlgOptionsManager().get_plg_settings()
if self.plg_settings.domain_url:
self.domain_url = self.plg_settings.domain_url
else :
self.domain_url = __domain_url__
if self.domain_url[-1] != '/':
self.domain_url = self.domain_url + '/'
# self.study_area_list = self.plg_settings.study_area_list
# self.dl_zh_pot = self.plg_settings.dl_zh_pot
# self.export_number = self.plg_settings.export_number
if self.domain_url == "":
msg = QMessageBox()
msg.warning(
None,
self.tr("Warning"),
self.tr("You need to inform GeoNature's URL"), # noqa: E501
)
self.iface.showOptionsDialog(currentPage="mOptionsPage{}".format(__title__))
self.run_gn_tools()
else:
# Initiate class
print(self.domain_url)
print(self.dlg.src_database.currentText())
if self.dlg.src_database.currentText() != '':
api = self.src_bdd[self.dlg.src_database.currentText()]
self.dlg.report_url.setText(api[:42])
self.domain_url = api
self.src_name = self.dlg.src_database.currentText()
self.dlg.show()
# Run the dialog event loop
result = self.dlg.exec_()
if result:
# Do something useful here - delete the line containing pass and
# substitute with you
print('ok')
print(result)
pass
def connect_src(self):
print('connect_src : ',self.src_name)
if self.src_name == 'GéoNature':
self.getLogin = GetLogin(parent=self,network_manager=self.manager)
self.getLogin.finished_login.connect(self.param_widget)
elif self.src_name == "Biodiv'AuRA":
if self.dlg.pub_access.isChecked():
user = {
'usr': 'user-public',
'pwd': 'public'
}
self.getLogin = GetLogin(parent=self,network_manager=self.manager,params=user)
self.getLogin.finished_login.connect(self.param_widget)
else :
self.getLogin = GetLogin(parent=self,network_manager=self.manager)
self.getLogin.finished_login.connect(self.param_widget)
# if self.manager.cookieJar():
# self.dlg.is_connect.setText("Good Job !")
# else:
# self.dlg.is_connect.setText("Aie !")
def param_widget(self):
print(self.manager.cookieStatus)
if self.manager.cookieStatus==200:
self.paramWidget = ParamWidget(
parent=self,network_manager=self.manager
)
self.dlg.source_import.currentTextChanged.connect(
self.print_change
)
def param_cd_taxon(self):
layer = self.dlg.map_layers.currentLayer()
if layer:
cols_name = layer.fields().names()
self.dlg.cd_taxon.clear()
self.dlg.cd_taxon.addItems(cols_name)
def print_change(self):
self.dlg.clear_filter.setEnabled(True)
self.dlg.draw_bounds.setEnabled(True)
self.dlg.active_filter_layer.setEnabled(True)
txt = self.dlg.source_import.currentText()
self.dlg.syn_tools.setVisible(True
if txt == 'Synthèse taxons' else False
)
if txt == 'Synthèse taxons':
self.syn = GetFilterFromAPI(
parent=self, network_manager=self.manager
)
print(self.dlg.source_import.currentIndex())
def pointer(self):
self.dlg.filter_layer.setDisabled(True)
self.dlg.use_entity.setDisabled(True)
self.dlg.use_entity.setChecked(False)
self.dlg.bound_log.setPlainText('')
# Add the tool to draw a rectangle
self.iface.mainWindow().activateWindow()
self.canvas.setMapTool(self.move_tool)
def activate_window(self):
# Put the dialog on top once the rectangle is drawn
# self.activateWindow()
self.canvas.unsetMapTool(self.move_tool)
self.check_valid()
def check_valid(self):
self.rectangle = QgsGeometry.fromRect(self.move_tool.new_extent)
sourceCrs = QgsCoordinateReferenceSystem(self.project.crs())
destCrs = QgsCoordinateReferenceSystem(4326)
tr = QgsCoordinateTransform(sourceCrs, destCrs, self.project)
self.rectangle.transform(tr)
self.dlg.bound_log.setPlainText(
'Bound selected : '
+ str(self.rectangle.asWkt())
)
print('rectangle : ',self.rectangle.asJson())
def filter_layer_activate(self):
self.dlg.bound_log.setPlainText('')
self.extend_layer()
self.dlg.filter_layer.setEnabled(True)
self.dlg.use_entity.setEnabled(True)
self.canvas.unsetMapTool(self.move_tool)
def extend_layer(self):
if self.dlg_syn.jdd_box.checkedItemsData():
print('checked : ',self.dlg_syn.jdd_box.checkedItems())
print('checked : ',self.syn_params)
if self.dlg.filter_layer.currentText() != '':
layer = self.dlg.filter_layer.currentLayer()
destCrs = QgsCoordinateReferenceSystem(4326)
self.rectangle = QgsGeometry.fromRect(
layer.extent()
)
if self.dlg.use_entity.isChecked():
geoms = [f.geometry() for f in layer.getSelectedFeatures()]
if geoms:
union = QgsGeometry.unaryUnion(geoms)
self.rectangle = QgsGeometry.fromRect(
union.boundingBox()
)
if layer.crs() != destCrs:
tr = QgsCoordinateTransform(layer.crs(), destCrs, self.project)
self.rectangle.transform(tr)
print(layer.extent())
print(layer.crs())
def clear_filter(self):
self.dlg.bound_log.setPlainText('')
self.rectangle = None
def accept(self):
currentTab = self.dlg.tab.currentWidget().objectName()
if self.manager.accepted or self.src_name=='GBIF' :
self.dlg.buttonBox.blockSignals(False)
if currentTab == 'import_2':
load = LoadingImport(
parent=self,network_manager=self.manager
)
# load.finished_dl.connect()
elif currentTab == 'status':
PivotStatus(
parent=self,network_manager=self.manager,
layer=self.dlg.map_layers.currentLayer(),
cd_nom=self.dlg.cd_taxon.currentText(),
)
# print("Work in progress ..")
# msg = QMessageBox()
# msg.warning(
# None,
# self.tr("Warning"),
# self.tr("Work in progress .."), # noqa: E501
# )
print("Accepted")
else:
msg = QMessageBox()
msg.warning(
None,
self.tr("Warning"),
self.tr("Opération impossible ! \nVous n'êtes pas connectez à GéoNature"), # noqa: E501
)
self.dlg.show()
def reject(self):
QApplication.restoreOverrideCursor()
print("Rejected")