diff --git a/CenRa_FLUX/FluxCEN.py b/CenRa_FLUX/FluxCEN.py new file mode 100644 index 00000000..31ad09ca --- /dev/null +++ b/CenRa_FLUX/FluxCEN.py @@ -0,0 +1,898 @@ +# -*- coding: utf-8 -*- +""" +/*************************************************************************** + FluxCEN + A QGIS plugin + Centralisation des flux WFS/WMS utilisés au CEN NA + Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/ + ------------------- + begin : 2022-03-23 + git sha : $Format:%H$ + copyright : (C) 2022 by Romain MONTILLET + email : r.montillet@cen-na.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +""" +from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication, Qt, QUrl +from qgis.PyQt.QtGui import * +from qgis.PyQt.QtWidgets import * +from PyQt5 import * + +# Initialize Qt resources from file resources.py +from .resources import * +# Import the code for the dialog +from .FluxCEN_dialog import FluxCENDialog +import os.path, os, shutil +from qgis.core import * +from qgis.gui import * +from qgis.utils import * +import processing +import psycopg2 +import psycopg2.extras +from PyQt5.QtXml import QDomDocument +import csv +import os +import io +import re +import random +# Deal with SSL +import ssl +import urllib +from urllib import request, parse +import socket +import json +import requests +import base64 + +ssl._create_default_https_context = ssl._create_unverified_context + +from .tools.resources import maj_verif +from .tools.PythonSQL import * +first_conn = psycopg2.connect("host=" + host + " port=" + port + " dbname="+dbname+" user=first_cnx password=" + password) +first_cur = first_conn.cursor(cursor_factory = psycopg2.extras.DictCursor) +first_cur.execute("SELECT mdp_w, login_w FROM pg_catalog.pg_user t1, admin_sig.vm_users_sig t2 WHERE t2.oid = t1.usesysid AND (login_w = '" + os_user + "' OR login_w = '" + os_user + "')") +res_ident = first_cur.fetchone() +mdp = base64.b64decode(str(res_ident[0])).decode('utf-8') +user = res_ident[1] +#con = psycopg2.connect("host=" + host + " port=" + port + " dbname="+dbname+" user=" + user + " password=" + mdp) +#cur = con.cursor(cursor_factory = psycopg2.extras.DictCursor) +#from .HubToTea import gitea +#gitea() +first_conn.close() + + +''' +# Vérifier la connexion à internet +try: + # Vérifier si l'utilisateur est connecté à internet en ouvrant une connexion avec un site web + host = socket.gethostbyname("www.google.com") + s = socket.create_connection((host, 80), 2) + s.close() +except socket.error: + # Afficher un message si l'utilisateur n'est pas connecté à internet + QMessageBox.warning(None, 'Avertissement', + 'Vous n\'êtes actuellement pas connecté à internet. Veuillez vous connecter pour pouvoir utiliser FluxCEN !') +''' + +class Flux: + def __init__(self, t, c, nc, l, u, p): + self.type = t + self.category = c + self.nom_commercial = nc + self.layer = l + self.url = u + self.parameters = p + maj_verif('CenRa_FLUX') + + +class Popup(QWidget): + def __init__(self, parent=None): + super(Popup, self).__init__(parent) + + self.plugin_dir = os.path.dirname(__file__) + + self.text_edit = QTextBrowser() + fp = urllib.request.urlopen("https://raw.githubusercontent.com/CEN-Nouvelle-Aquitaine/fluxcen/main/info_changelog.html") + mybytes = fp.read() + html_changelog = mybytes.decode("utf8") + fp.close() + + self.text_edit.setHtml(html_changelog) + self.text_edit.setFont(QtGui.QFont("Calibri",weight=QtGui.QFont.Bold)) + self.text_edit.anchorClicked.connect(QtGui.QDesktopServices.openUrl) + self.text_edit.setOpenLinks(False) + + self.text_edit.setWindowTitle("Nouveautés") + self.text_edit.setMinimumSize(600,450) + +class FluxCEN: + """QGIS Plugin Implementation.""" + + def __init__(self, iface): + """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 + """ + # Save reference to the QGIS interface + self.iface = iface + # initialize plugin directory + self.plugin_dir = os.path.dirname(__file__) + # initialize locale + #print(QSettings().value('locale/userLocale')) + locale = QSettings().value('locale/userLocale')[0:2] + locale_path = os.path.join( + self.plugin_dir, + 'i18n', + 'FluxCEN_{}.qm'.format(locale)) + + if os.path.exists(locale_path): + self.translator = QTranslator() + self.translator.load(locale_path) + QCoreApplication.installTranslator(self.translator) + + # Declare instance attributes + self.actions = [] + self.menu = self.tr(u'&FluxCEN') + self.dlg = FluxCENDialog() + + self.plugin_path = os.path.dirname(__file__) + + # Check if plugin was started the first time in current QGIS session + # Must be set in initGui() to survive plugin reloads + self.first_start = None + + self.dlg.tableWidget.setSelectionBehavior(QTableWidget.SelectRows) + self.dlg.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) + + self.dlg.comboBox_2.addItem("SIG") + self.dlg.comboBox_2.addItem('REF') +# self.dlg.comboBox.addItem('07') +# self.dlg.comboBox.addItem('26') +# self.dlg.comboBox.addItem('42') +# self.dlg.comboBox.addItem('69') +# self.dlg.comboBox.addItem('form') + + self.dlg.comboBox.currentIndexChanged.connect(self.initialisation_flux) + self.dlg.commandLinkButton.clicked.connect(self.selection_flux) + self.dlg.tableWidget.itemDoubleClicked.connect(self.selection_flux) + self.dlg.pushButton_2.clicked.connect(self.limite_flux) + self.dlg.commandLinkButton_2.clicked.connect(self.suppression_flux) + self.dlg.tableWidget_2.itemDoubleClicked.connect(self.suppression_flux) + self.dlg.comboBox_2.currentIndexChanged.connect(self.bd_source) + #self.dlg.commandLinkButton_3.clicked.connect(self.option_OSM) + #self.dlg.commandLinkButton_4.clicked.connect(self.option_google_maps) + + #self.dlg.commandLinkButton_5.clicked.connect(self.popup) + # iface.mapCanvas().extentsChanged.connect(self.test5) + +# url_open = urllib.request.urlopen("https://raw.githubusercontent.com/CEN-Rhone-Alpes/Plugin_QGIS/main/flux.csv") +# colonnes_flux = csv.DictReader(io.TextIOWrapper(url_open, encoding='utf8'), delimiter=';') + + #mots_cles = [row["categorie"] for row in colonnes_flux if row["categorie"]] + #categories = list(set(mots_cles)) + #categories.sort() + + #self.dlg.comboBox.addItems(categories) + layout = QVBoxLayout() + self.dlg.lineEdit.textChanged.connect(self.filtre_dynamique) + layout.addWidget(self.dlg.lineEdit) + self.dlg.lineEdit.mousePressEvent = self._mousePressEvent + + metadonnees_plugin = open(self.plugin_path + '/metadata.txt') + infos_metadonnees = metadonnees_plugin.readlines() + +# derniere_version = urllib.request.urlopen("https://sig.dsi-cen.org/qgis/downloads/last_version_fluxcen.txt") +# num_last_version = derniere_version.readlines()[0].decode("utf-8") + + # Connect the itemClicked signal to the open_url function + #self.dlg.tableWidget.itemClicked.connect(self.open_url) + +# version_utilisateur = infos_metadonnees[8].splitlines() + +# if infos_metadonnees[8].splitlines() == num_last_version.splitlines(): +# iface.messageBar().pushMessage("Plugin à jour", "Votre version de FluxCEN %s est à jour !" %version_utilisateur, level=Qgis.Success, duration=5) +# else: +# iface.messageBar().pushMessage("Information :", "Une nouvelle version de FluxCEN est disponible, veuillez mettre à jour le plugin !", level=Qgis.Info, duration=120) + + def _mousePressEvent(self, event): + self.dlg.lineEdit.setText("") + self.dlg.lineEdit.mousePressEvent = None + + # noinspection PyMethodMayBeStatic + def tr(self, message): + """Get the translation for a string using Qt translation API. + + We implement this ourselves since we do not inherit QObject. + + :param message: String for translation. + :type message: str, QString + + :returns: Translated version of message. + :rtype: QString + """ + # noinspection PyTypeChecker,PyArgumentList,PyCallByClass + return QCoreApplication.translate('FluxCEN', message) + + + def add_action( + self, + icon_path, + text, + callback, + dbtype=None, + enabled_flag=True, + add_to_menu=True, + add_to_toolbar=True, + status_tip=None, + whats_this=None, + parent=None): + """Add a toolbar icon to the toolbar. + + :param icon_path: Path to the icon for this action. Can be a resource + path (e.g. ':/plugins/foo/bar.png') or a normal file system path. + :type icon_path: str + + :param text: Text that should be shown in menu items for this action. + :type text: str + + :param callback: Function to be called when the action is triggered. + :type callback: function + + :param enabled_flag: A flag indicating if the action should be enabled + by default. Defaults to True. + :type enabled_flag: bool + + :param add_to_menu: Flag indicating whether the action should also + be added to the menu. Defaults to True. + :type add_to_menu: bool + + :param add_to_toolbar: Flag indicating whether the action should also + be added to the toolbar. Defaults to True. + :type add_to_toolbar: bool + + :param status_tip: Optional text to show in a popup when mouse pointer + hovers over the action. + :type status_tip: str + + :param parent: Parent widget for the new action. Defaults None. + :type parent: QWidget + :param whats_this: Optional text to show in the status bar when the + mouse pointer hovers over the action. + + :returns: The action that was created. Note that the action is also + added to self.actions list. + :rtype: QAction + """ + + icon = QIcon(icon_path) + action = QAction(icon, text, parent) + action.triggered.connect(callback) + action.setEnabled(enabled_flag) + + if status_tip is not None: + action.setStatusTip(status_tip) + + if whats_this is not None: + action.setWhatsThis(whats_this) + + if add_to_toolbar: + # Adds plugin icon to Plugins toolbar + #self.iface.addToolBarIcon(action) + self.toolBar.addAction(action) + + if add_to_menu: + self.iface.addPluginToMenu( + self.menu, + action) + + self.actions.append(action) + + return action + + def initGui(self): + self.toolBar = self.iface.addToolBar("FluxCEN") + self.toolBar.setObjectName("FluxCEN") + """Create the menu entries and toolbar icons inside the QGIS GUI.""" + icon_path = ':/plugins/CenRa_FLUX/reficon.png' + self.add_action( + icon_path, + text=self.tr(u'SigCEN'), + callback=self.bd_source, + dbtype='"+dbname+"', + parent=self.iface.mainWindow()) + ''' + icon_path_2 = ':/plugins/CenRa_FLUX/reficon.png' + self.add_action( + icon_path_2, + text=self.tr(u'RefCEN'), + callback=self.run_ref, + dbtype='ref_geo4269', + parent=self.iface.mainWindow()) + ''' + + # will be set False in run() + self.first_start = False + + def unload(self): + """Removes the plugin menu item and icon from QGIS GUI.""" + for action in self.actions: + self.iface.removePluginMenu( + self.tr(u'&SigCEN'), + action) + self.iface.removeToolBarIcon(action) + for action in self.actions: + self.iface.removePluginMenu( + self.tr(u'&RefCEN'), + action) + self.iface.removeToolBarIcon(action) + + def bd_source(self): + bd_origine=self.dlg.comboBox_2.currentText() + if bd_origine == 'REF': + self.run_ref() + if bd_origine == 'SIG': + self.run_sig() + + def run_ref(self): + """Run method that performs all the real work""" + while self.dlg.tableWidget_2.rowCount() > 0: + self.dlg.tableWidget_2.removeRow(self.dlg.tableWidget_2.rowCount()-1) +# print(self.dlg.tableWidget_2.rowCount()) + global cur,con,dbtype + dbtype=refdb + con = psycopg2.connect("host=" + host + " port=" + port + " dbname="+dbtype+" user=" + user + " password=" + mdp) + cur = con.cursor(cursor_factory = psycopg2.extras.DictCursor) + self.initialisation_flux() + self.combobox_custom() + # Create the dialog with elements (after translation) and keep reference + # Only create GUI ONCE in callback, so that it will only load when the plugin is started + if self.first_start == True: + self.first_start = False + # show the dialog + self.dlg.show() + # Run the dialog event loop + result = self.dlg.exec_() + # See if OK was pressed + if result: + # Do something useful here - delete the line containing pass and + # substitute with your code. + pass + + def run_sig(self): + """Run method that performs all the real work""" + while self.dlg.tableWidget_2.rowCount() > 0: + self.dlg.tableWidget_2.removeRow(self.dlg.tableWidget_2.rowCount()-1) + global cur,con,dbtype + dbtype=sigdb + con = psycopg2.connect("host=" + host + " port=" + port + " dbname="+dbtype+" user=" + user + " password=" + mdp) + cur = con.cursor(cursor_factory = psycopg2.extras.DictCursor) + self.initialisation_flux() + self.combobox_custom() + # Create the dialog with elements (after translation) and keep reference + # Only create GUI ONCE in callback, so that it will only load when the plugin is started + if self.first_start == True: + self.first_start = False + # show the dialog + self.dlg.show() + # Run the dialog event loop + result = self.dlg.exec_() + # See if OK was pressed + if result: + # Do something useful here - delete the line containing pass and + # substitute with your code. + pass + + def suppression_flux(self): + self.dlg.tableWidget_2.removeRow(self.dlg.tableWidget_2.currentRow()) + ''' + def option_OSM(self): + tms = 'type=xyz&url=https://tile.openstreetmap.org/{z}/{x}/{y}.png&zmax=19&zmin=0' + layer = QgsRasterLayer(tms, 'OSM', 'wms') + + if not QgsProject.instance().mapLayersByName("OSM"): + QgsProject.instance().addMapLayer(layer) + else: + QMessageBox.question(iface.mainWindow(), u"Fond OSM déjà chargé !", "Le fond de carte OSM est déjà chargé", QMessageBox.Ok) + + OSM_layer = QgsProject.instance().mapLayersByName("OSM")[0] + + root = QgsProject.instance().layerTreeRoot() + + # Move Layer + OSM_layer = root.findLayer(OSM_layer.id()) + myClone = OSM_layer.clone() + parent = OSM_layer.parent() + parent.insertChildNode(-1, myClone) + parent.removeChildNode(OSM_layer) + + + def option_google_maps(self): + tms = 'type=xyz&zmin=0&zmax=20&url=https://mt1.google.com/vt/lyrs%3Ds%26x%3D{x}%26y%3D{y}%26z%3D{z}' + layer = QgsRasterLayer(tms, 'Google Satelitte', 'wms') + + if not QgsProject.instance().mapLayersByName("Google Satelitte"): + QgsProject.instance().addMapLayer(layer) + else: + QMessageBox.question(iface.mainWindow(), u"Fond Google Sat' déjà chargé !", "Le fond de carte Google Satelitte est déjà chargé", QMessageBox.Ok) + + google_layer = QgsProject.instance().mapLayersByName("Google Satelitte")[0] + + root = QgsProject.instance().layerTreeRoot() + + # Move Layer + google_layer = root.findLayer(google_layer.id()) + myClone = google_layer.clone() + parent = google_layer.parent() + parent.insertChildNode(-1, myClone) + parent.removeChildNode(google_layer) + ''' + + def open_url(self, item): + url = item.data(Qt.UserRole) + #if url: + # Open the URL, you might use QDesktopServices.openUrl for this in a standalone PyQt application + # QDesktopServices.openUrl(QUrl(url)) + + def initialisation_flux(self): + ''' + def csv_import(url): + url_open = urllib.request.urlopen(url) + csvfile = csv.reader(io.TextIOWrapper(url_open, encoding='utf8'), delimiter=';') + #on ne lit pas la première ligne correspondant aux noms des colonnes avec next() + next(csvfile) + return csvfile; + + data = [] + data2 = [] + model = QStandardItemModel() + + raw = csv_import( + "https://raw.githubusercontent.com/CEN-Rhone-Alpes/Plugin_QGIS/main/flux.csv") + for row in raw: + data.append(row) + data2.append(row) + data = [k for k in data if self.dlg.comboBox.currentText() in k] + data.sort() + data2.sort() + items = [ + QStandardItem(field) + for field in row] + + model.appendRow(items) + + row=0 + data=['a1','a2','a3'] + data2=[['b1','c1','d1'],['b2','c2','d2'],['b3','c3','d3']] + if self.dlg.comboBox.currentText() == 'toutes les catégories': + # print(str(data2[0])) + # del data2[0] + # print(str(data2[0])) + nb_row = len(data2) + nb_col = len(data2[0]) + print(data2[row][2]) + self.dlg.tableWidget.setRowCount(nb_row) + self.dlg.tableWidget.setColumnCount(nb_col) + for row in range(nb_row): + for col in range(nb_col): + item = QTableWidgetItem(str(data2[row][col])) + # Access the value from the 6th column for the current row (style here) + value_from_2nd_column = str(data2[row][2]) + # Set tooltip for each row + tooltip = f"Nom du flux: {value_from_2nd_column}" + item.setToolTip(tooltip) + + # Check if the current column is the "Résumé des métadonnées" column + if col == 7: + # Set icon for the "Résumé des métadonnées" column + icon_path = self.plugin_path + '/info_metadata.png' # Replace 'path_to_your_icon.png' with the actual path to your icon + icon = QIcon(icon_path) + item.setIcon(icon) + # Store the URL in the item's data for later retrieval + url_from_6th_column = str(data2[row][7]) # Assuming the URL is in the next column + item.setData(Qt.UserRole, url_from_6th_column)''' + if dbtype == sigdb: + if self.dlg.comboBox.currentText() == 'toutes les catégories': + custom_list=schemaname_list + elif self.dlg.comboBox.currentText() == 'travaux': + custom_list="""(SELECT schemaname,tablename from pg_catalog.pg_tables + where schemaname like '"""+ str(self.dlg.comboBox.currentText()) +"""%' order by schemaname,tablename) UNION (SELECT schemaname,matviewname AS tablename FROM pg_catalog.pg_matviews where schemaname like '"""+ str(self.dlg.comboBox.currentText()) +"""%' order by schemaname,tablename) order by schemaname,tablename;""" + else: + custom_list="""(SELECT schemaname,tablename from pg_catalog.pg_tables + where schemaname like '\_"""+ str(self.dlg.comboBox.currentText()) +"""%' order by schemaname,tablename) UNION (SELECT schemaname,matviewname AS tablename FROM pg_catalog.pg_matviews where schemaname like '\_"""+ str(self.dlg.comboBox.currentText()) +"""%' order by schemaname,tablename) order by schemaname,tablename;""" + else: + if self.dlg.comboBox.currentText() == 'toutes les catégories': + custom_list=schemaname_list_ref + else: + custom_list="""SELECT schemaname,tablename from pg_catalog.pg_tables + where schemaname like '"""+ str(self.dlg.comboBox.currentText()) +"""' order by schemaname,tablename;""" + cur.execute(custom_list) + list_schema = cur.fetchall() + + self.dlg.tableWidget.setRowCount(len(list_schema)) + self.dlg.tableWidget.setColumnCount(3) + i=0 + for value in list_schema: + if dbtype == sigdb: + type_val = str(value[0])[1:3] + schema_name=str(value[0])[4:] + table_name=str(value[1]) + else: + type_val = '' + schema_name=str(value[0]) + table_name=str(value[1]) + if type_val == 'fo': + type_val=str(value[0])[1:5] + schema_name=str(value[0])[6:] + table_name=str(value[1]) + elif type_val == 'ra': + type_val='travaux' + schema_name=str(value[0]) + table_name=str(value[1]) + elif type_val != '00' and type_val != '01' and type_val != '07' and type_val != '26' and type_val != '42' and type_val != '69' and type_val != 'ra': + type_val='agregation' + schema_name=str(value[0]) + table_name=str(value[1]) + + item = QTableWidgetItem(type_val) + self.dlg.tableWidget.setItem(i,0,item) + item = QTableWidgetItem(schema_name) + self.dlg.tableWidget.setItem(i,1,item) + item = QTableWidgetItem(table_name) + self.dlg.tableWidget.setItem(i,2,item) + i=i+1 + self.dlg.tableWidget.setColumnWidth(0, 20) + self.dlg.tableWidget.setColumnWidth(1, 300) + self.dlg.tableWidget.setColumnWidth(2, 300) + self.dlg.tableWidget.setHorizontalHeaderLabels(["Code","Schema","Table"]) + ''' + else: + nb_row = len(data) + nb_col = len(data[0]) + self.dlg.tableWidget.setRowCount(nb_row) + self.dlg.tableWidget.setColumnCount(nb_col) + for row in range(nb_row): + for col in range(nb_col): + item = QTableWidgetItem(str(data[row][col])) + # Access the value from the 6th column for the current row (style here) + value_from_2nd_column = str(data[row][1]) + # Set tooltip for each row + tooltip = f"Nom du flux: {value_from_2nd_column}" + item.setToolTip(tooltip) + + # Check if the current column is the "Résumé des métadonnées" column + if col == 7: + # Set icon for the "Résumé des métadonnées" column + icon_path = self.plugin_path + '/metadata.png' # Replace 'path_to_your_icon.png' with the actual path to your icon + icon = QIcon(icon_path) + item.setIcon(icon) + # Store the URL in the item's data for later retrieval + url_from_6th_column = str(data2[row][7]) # Assuming the URL is in the next column + item.setData(Qt.UserRole, url_from_6th_column) + + self.dlg.tableWidget.setItem(row, col, item) + + self.dlg.tableWidget.setHorizontalHeaderLabels(["Service", "Catégorie", "Flux", "Nom technique", "Url d'accès", "Source", "Style", "Infos"]) + + self.dlg.tableWidget.setColumnWidth(0, 76) + self.dlg.tableWidget.setColumnWidth(1, 0) + self.dlg.tableWidget.setColumnWidth(2, 610) + self.dlg.tableWidget.setColumnWidth(3, 0) + self.dlg.tableWidget.setColumnWidth(4, 0) + self.dlg.tableWidget.setColumnWidth(5, 88) + self.dlg.tableWidget.setColumnWidth(6, 0) + self.dlg.tableWidget.setColumnWidth(7, 30) + + self.dlg.tableWidget.selectRow(0) + ''' + def selection_flux(self): + selected_row = 0 + selected_items = self.dlg.tableWidget.selectedItems() + + # Assuming you want to compare items in the first column for uniqueness + new_item_text = selected_items[2].text() + + if not self.item_already_exists(new_item_text): + self.dlg.tableWidget_2.insertRow(selected_row) + + for column in range(self.dlg.tableWidget.columnCount()): + cloned_item = selected_items[column].clone() + self.dlg.tableWidget_2.setHorizontalHeaderLabels(["Code","Schema", "Table"]) + self.dlg.tableWidget_2.setColumnCount(3) + self.dlg.tableWidget_2.setItem(selected_row, column, cloned_item) + + self.dlg.tableWidget_2.setColumnWidth(0, 50) + self.dlg.tableWidget_2.setColumnWidth(1, 300) + self.dlg.tableWidget_2.setColumnWidth(2, 300) + + + def item_already_exists(self, new_item_text): + # Assuming you want to compare items in the first column for uniqueness + existing_items = self.dlg.tableWidget_2.findItems(new_item_text, QtCore.Qt.MatchExactly) + + # Check if there are any existing items with the same text in the first column + return len(existing_items) > 0 + + + + def limite_flux(self): + + if self.dlg.tableWidget_2.rowCount() > 3: + self.QMBquestion = QMessageBox.question(iface.mainWindow(), u"Attention !", + "Le nombre de flux à charger en une seule fois est limité à 3 pour des questions de performances. Souhaitez vous tout de même charger les " + str( + self.dlg.tableWidget_2.rowCount()) + " flux sélectionnés ? (risque de plantage de QGIS)", + QMessageBox.Yes | QMessageBox.No) + if self.QMBquestion == QMessageBox.Yes: + self.chargement_flux() + + if self.QMBquestion == QMessageBox.No: + print("Annulation du chargement des couches") + + if self.dlg.tableWidget_2.rowCount() <= 3: + self.chargement_flux() + + def chargement_flux(self): + + managerAU = QgsApplication.authManager() + k = managerAU.availableAuthMethodConfigs().keys() + + def REQUEST(type): + switcher = { + 'WFS': "GetFeature", + 'WMS': "GetMap", + 'WMS+Vecteur': "GetMap", + 'WMS+Raster': "GetMap", + 'WMTS': "GetMap" + } + return switcher.get(type, "nothing") + + + def displayOnWindows(type, uri, name): + ''' + if type == 'WFS': + vlayer = QgsVectorLayer(uri, name, "WFS") + # vlayer.setScaleBasedVisibility(True) + QgsProject.instance().addMapLayer(vlayer) + + layers = QgsProject.instance().mapLayers() # dictionary + + # rowCount() This property holds the number of rows in the table + for row in range(self.dlg.tableWidget_2.rowCount()): + # item(row, 0) Returns the item for the given row and column if one has been set; otherwise returns nullptr. + _item = self.dlg.tableWidget_2.item(row, 2).text() + _legend = self.dlg.tableWidget_2.item(row, 6).text() + # print(_item) + # print(_legend) + + for layer in layers.values(): + if layer.name() == _item: + if len(_legend) > 1: + styles_url = 'https://raw.githubusercontent.com/CEN-Nouvelle-Aquitaine/fluxcen/main/styles_couches/' + _legend + '.qml' + + fp = urllib.request.urlopen(styles_url) + mybytes = fp.read() + + document = QDomDocument() + document.setContent(mybytes) + + res = layer.importNamedStyle(document) + layer.triggerRepaint() + + else: + print("Pas de style à charger pour cette couche") + + elif type == 'WMS' or type == 'WMS Raster' or type == 'WMS Vecteur' or type == 'WMTS': + rlayer = QgsRasterLayer(uri, name, "WMS") + QgsProject.instance().addMapLayer(rlayer) + else: + print("Unknown datatype !") + ''' + p = [] + + for row in range(0, self.dlg.tableWidget_2.rowCount()): + ## supression de la partie de l'url après le point d'interrogation + if dbtype == sigdb: + code = self.dlg.tableWidget_2.item(row,0).text() + schema = '_'+code+'_'+self.dlg.tableWidget_2.item(row,1).text() + if code == 'travaux' or code == 'agregation': + schema = self.dlg.tableWidget_2.item(row,1).text() + table = self.dlg.tableWidget_2.item(row,2).text()#.split("?", 1)[0] + if dbtype == refdb: +# code = self.dlg.tableWidget_2.item(row,0).text() + schema = self.dlg.tableWidget_2.item(row,1).text() + table = self.dlg.tableWidget_2.item(row,2).text()#.split("?", 1)[0] + uri = QgsDataSourceUri() + uri.setConnection(host ,port ,dbtype ,user ,mdp) + # nom du schéma à remplacer: "hydrographie" à supprimer et mettre "couches_collaboratives" lorsqu'on aura regroupé les couches à modifier dans un même schéma + uri.setDataSource(schema, table, "geom") + uri.setKeyColumn('gid') + # Chargement de la couche PostGIS + layer = QgsVectorLayer(uri.uri(), table, "postgres") + # Ajout de la couche au canevas QGIS + QgsProject.instance().addMapLayer(layer) + ''' + try: + service = re.search('SERVICE=(.+?)&VERSION', self.dlg.tableWidget_2.item(row,4).text()).group(1) + except: + service = '1.0.0' + try: + version = re.search('VERSION=(.+?)&REQUEST', self.dlg.tableWidget_2.item(row,4).text()).group(1) + except: + version = '1.0.0' + + if self.dlg.tableWidget_2.item(row,0).text() == 'WMS' or self.dlg.tableWidget_2.item(row,0).text() == 'WMS Vecteur' or self.dlg.tableWidget_2.item(row,0).text() == 'WMS Raster': + a = Flux( + self.dlg.tableWidget_2.item(row,0).text(), + self.dlg.tableWidget_2.item(row,1).text(), + self.dlg.tableWidget_2.item(row,2).text(), + self.dlg.tableWidget_2.item(row,3).text(), + "url="+url, + { + 'service': self.dlg.tableWidget_2.item(row,0).text(), + 'version': version, + 'crs': "EPSG:2154", + 'format' : "image/png", + 'layers': self.dlg.tableWidget_2.item(row,3).text()+"&styles" + } + ) + + p.append(a) + + uri = p[row].url + '&' + urllib.parse.unquote(urllib.parse.urlencode(p[row].parameters)) + # print(uri) + if not QgsProject.instance().mapLayersByName(p[row].nom_commercial): + displayOnWindows(p[row].type, uri, p[row].nom_commercial) + else: + print("Couche "+p[row].nom_commercial+" déjà chargée") + + + elif self.dlg.tableWidget_2.item(row,0).text() == 'WFS': + + a = Flux( + self.dlg.tableWidget_2.item(row, 0).text(), + self.dlg.tableWidget_2.item(row, 1).text(), + self.dlg.tableWidget_2.item(row, 2).text(), + self.dlg.tableWidget_2.item(row, 3).text(), + url, + { + 'VERSION': version, + 'TYPENAME': self.dlg.tableWidget_2.item(row, 3).text(), + 'request': "GetFeature", + + } + ) + + p.append(a) + + uri = p[row].url + '?' + urllib.parse.unquote(urllib.parse.urlencode(p[row].parameters)) + + try: + response = requests.get(uri) + + if response.status_code == 401: + print("Statut de réponse: 401") + + if len(list(k)) == 0: + QMessageBox.question(iface.mainWindow(), u"Attention", "Veuillez ajouter une entrée de configuration d'authentification dans QGIS pour accéder aux flux CEN-NA sécurisés par un mot de passe (Flux 'FoncierCEN')", QMessageBox.Ok) + else: + # Add 'authcfg' to the parameters dictionary + p[row].parameters['authcfg'] = list(k)[0] + + # Update the URI with the modified parameters + uri = p[row].url + '?' + urllib.parse.unquote(urllib.parse.urlencode(p[row].parameters)) + + # Make the request again with the updated URI + response = requests.get(uri) + elif response.status_code == 200: + print("Statut de réponse: 200.") + else: + print(f"Statut de réponse: {response.status_code}") + + except requests.exceptions.RequestException as e: + print(f"problème de requete: {e}") + + + if not QgsProject.instance().mapLayersByName(p[row].nom_commercial): + displayOnWindows(p[row].type, uri, p[row].nom_commercial) + else: + print("Couche "+p[row].nom_commercial+" déjà chargée") + + + elif self.dlg.tableWidget_2.item(row, 0).text() == 'PostGIS': + + # Connexion à la base de données PostGIS + uri = QgsDataSourceUri() + uri.setConnection("sandbox.cen-nouvelle-aquitaine.dev", "5432", "piezo", "", "") + # nom du schéma à remplacer: "hydrographie" à supprimer et mettre "couches_collaboratives" lorsqu'on aura regroupé les couches à modifier dans un même schéma + uri.setDataSource("collaboratif", self.dlg.tableWidget_2.item(row, 3).text(), "geom") + # Chargement de la couche PostGIS + layer = QgsVectorLayer(uri.uri(), self.dlg.tableWidget_2.item(row, 2).text(), "postgres") + + # Ajout de la couche au canevas QGIS + QgsProject.instance().addMapLayer(layer) + + else: + print("Les flux WMTS et autres ne sont pas encore gérés par le plugin") + ''' + def combobox_custom(self): + if dbtype == sigdb: + self.dlg.comboBox.clear() + self.dlg.comboBox.addItem("toutes les catégories") + self.dlg.comboBox.addItem('00') + self.dlg.comboBox.addItem('01') + self.dlg.comboBox.addItem('07') + self.dlg.comboBox.addItem('26') + self.dlg.comboBox.addItem('42') + self.dlg.comboBox.addItem('69') + self.dlg.comboBox.addItem('agregation') + self.dlg.comboBox.addItem('travaux') + self.dlg.comboBox.addItem('form') + if dbtype == refdb: + custom_list=schemaname_distinct + cur.execute(custom_list) + list_schema = cur.fetchall() + self.dlg.comboBox.clear() + self.dlg.comboBox.addItem("toutes les catégories") + for baxval in list_schema: + self.dlg.comboBox.addItem(baxval[0]) + def filtre_dynamique(self, filter_text): + + for i in range(self.dlg.tableWidget.rowCount()): + for j in range(self.dlg.tableWidget.columnCount()): + item = self.dlg.tableWidget.item(i, j) + match = filter_text.lower() not in item.text().lower() + self.dlg.tableWidget.setRowHidden(i, match) + if not match: + break + + + + def popup(self): + + self.dialog = Popup() # +++ - self + self.dialog.text_edit.show() + +# from owslib.wfs import WebFeatureService +# import csv + +# wfs = WebFeatureService(url='https://opendata.cen-nouvelle-aquitaine.org/geoserver/agriculture/wfs') +# agriculture = list(wfs.contents) +# with open('C:/Users/Romain/Desktop/test.csv', "a+", encoding="ISO-8859-1", newline='') as f: +# writer = csv.writer(f) +# for row in agriculture: +# writer.writerow(row.split()) +# +# from owslib.wms import WebMapService +# wms = WebMapService('https://opendata.cen-nouvelle-aquitaine.org/geoserver/fond_carto/wms') +# fonds_carto = list(wms.contents) +# with open('C:/Users/Romain/Desktop/test.csv', "a+", encoding="ISO-8859-1", newline='') as f: +# writer = csv.writer(f) +# for row in fonds_carto: +# writer.writerow(row.split()) +# +# import csv +# +# fluxWMS = ['AGG_TMM', '16-014_Brandes_de_Soyaux_2020-05', '17IMERIS_Bois-Charles_Vallée-du-Larry_2022_01', '17IMERIS_Grand-Champ_2022-01', '19PTOR_MNS_filtre_futurs_travaux_2021-10_L93', '19PTOR_MNS_filtre_travaux_realises_2021-10_L93', '19PTOR_ortho_2021-10_L93', '23CELI_marais_du_chancelier_2022_03_24', '23CLAM_Rocher_de_Clamouzat_2020-11', '23DIAB_lande_du_pont_du_diable_nord_2021-10_L93', '23DIAB_lande_du_pont_du_diable_sud_2021-10_L93', '23LAND_RNN_etang_des_landes_2020-08_L93', '33_Lagune-108-2021-08', '79BLVI_Blanchère-de-Viennay_2021-10', '79VGAT_Vallée-du-Gâteau_Pressigny_2020-02', '79VGAT_Vallée-du-Gâteau_Pressigny_2021-10', '86-001_TMM_CA-CD_2020-07', '86-500_Clain-sud_Etang-du-Pin', '86_AT_Chalandray_2021-10', '87CREN_siege_saint_gence_2021-09', '87GRLA_grandes_landes_2021-09-24', '87SANA_sanadie_2021-09-24', 'a_16_030_Prairies_de_Vouharte_2019_09', 'a_17_474_Estauaire_de_la_Gironde_Les_Pr_s_de_la_Rouille_2019_08', 'a_17_474_Estauaire_de_la_Gironde_Moulin_Rompu_2019_08', 'a_17_474_Estuaire_de_la_Gironde_Zone_Humide_de_la_Motte_Ronde_2021_04', 'a_17_IMERIS_Carriere_du_Planton_2021_08_12', 'a_17_LGV_Ragouillis_2021_08_12', 'a_33_Lagune_058_2021_08', 'a_33_Lagune_070_2021_08', 'a_33_Lagune_094_2021_08', 'a_33_Lagune_162_2021_08', 'a_33_Lagune_165_2021_08', 'a_33_Lagunes_207_208_209_2021_08', 'a_79_001_Clussais_la_Pommeraie_2020_11', 'a_79_008_Landes_de_L_Hopiteau_2019_09', 'a_79_020_Bessines_1_avant_travaux_2019_10', 'a_79_020_Bessines_2_pendant_travaux_2019_11', 'a_79_020_Bessines_3_apres_travaux_2020_12', 'a_79_044_Carriere_des_Landes_2020_09', 'a_79_AT_Vernoux_en_Gatine_2020_09', 'a_79_Sources_de_la_Sevre_Niortaise_Pierre_levee_2020_09', 'a_86_001_TMM_AA_2020_06', 'a_86_001_TMM_AB_2020_06', 'a_86_001_TMM_AC_2020_06', 'a_86_001_TMM_AD_2020_06', 'a_86_001_TMM_AE_2020_06', 'a_86_001_TMM_AF_2020_06', 'a_86_001_TMM_AG_2020_07', 'a_86_001_TMM_BA_2020_06', 'a_86_001_TMM_BB_2020_06', 'a_86_001_TMM_BC_2020_06', 'a_86_001_TMM_BD_2020_06', 'a_86_001_TMM_BE_2020_07', 'a_86_001_TMM_BF_2021_06', 'a_86_001_TMM_CB_2020_07', 'a_86_001_TMM_CC_2020_07', 'a_86_001_TMM_CC_2021_06', 'a_86_001_TMM_CD_2021_06', 'a_86_001_TMM_CE_2020_07', 'a_86_001_TMM_CF_2020_09', 'a_86_001_TMM_DA_2020_07', 'a_86_001_TMM_DB_2020_09', 'a_86_001_TMM_DC_2021_06', 'a_86_001_TMM_EA_2020_06', 'a_86_001_TMM_EB_2020_06', 'a_86_001_TMM_EC_2020_06', 'a_86_001_TMM_FA_2020_06', 'a_86_001_TMM_FB_2020_07', 'a_86_001_TMM_FC_2020_07', 'a_86_001_TMM_FC_2021_06', 'a_86_001_TMM_HA_2020_09', 'a_86_001_TMM_IA_2020_06', 'a_86_001_TMM_IB_2020_06', 'a_86_001_TMM_IC_2020_06', 'a_86_001_TMM_JA_2020_07', 'a_86_001_TMM_JB_2020_07', 'a_86_001_TMM_JC_2020_07', 'a_86_001_TMM_JE_2020_07', 'a_86_001_TMM_KA_2020_07', 'a_86_001_TMM_KB_2020_07', 'a_86_003_Falunieres_de_Moulin_Pochas_2019_09', 'a_86_006_Landes_et_pelouses_de_Lussac_Sillars_2019_08', 'a_86_011_Landes_de_Sainte_Marie_2019_09', 'a_86_025_Marais_des_Ragouillis_2020_11', 'a_86_025_Marais_des_Ragouillis_2021_02', 'a_86_026_Etangs_Baro_2019_09', 'a_86_029_Vallee_de_la_Longere_2019_09', 'a_86_037_Tourbiere_des_Regeasses_2021_06', 'a_86_038_Vallees_de_la_Vienne_et_du_Clain_Persac_2019_09', 'a_86_038_Vallees_de_la_Vienne_et_du_Clain_Persac_2020_12', 'a_86_052_Fontaine_le_Comte_nord_2020_11', 'a_86_052_Fontaine_le_Comte_sud_2020_11', 'a_86_054_Vallee_de_la_Vonne_2020_11', 'a_86_058_Carriere_de_Puy_Herve_2021_02_09', 'a_86_058_Carriere_de_Puy_Herve_2021_02_25', 'a_86_060_Bocage_de_la_Geoffronniere_2020_11', 'a_86_Le_Cormier_2021_05'] +# +# with open('C:/Users/Romain/Desktop/test.csv', "a+", encoding="ISO-8859-1", newline='') as f: +# writer = csv.writer(f) +# for row in fluxWMS: +# writer.writerow(row.split()) + +#### Récupération des métadonnées des couches quand disponibles: + +# from owslib.wms import WebMapService +# wms = WebMapService('http://geoservices.brgm.fr/geologie?service=WMS+Raster', version='1.1.1') +# print(list(wms.contents)) +# +# print(wms['IDPR'].abstract) \ No newline at end of file diff --git a/CenRa_FLUX/__init__.py b/CenRa_FLUX/__init__.py new file mode 100644 index 00000000..d9f00200 --- /dev/null +++ b/CenRa_FLUX/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +""" +/*************************************************************************** + FluxCEN + A QGIS plugin + Flux IGN etc etc + Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/ + ------------------- + begin : 2022-04-04 + copyright : (C) 2022 by Romain Montillet + email : r.montillet@cen-na.org + git sha : $Format:%H$ + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + This script initializes the plugin, making it known to QGIS. +""" + + +# noinspection PyPep8Naming +def classFactory(iface): # pylint: disable=invalid-name + """Load FluxCEN class from file FluxCEN. + + :param iface: A QGIS interface instance. + :type iface: QgsInterface + """ + # + from .FluxCEN import FluxCEN + return FluxCEN(iface) diff --git a/CenRa_FLUX/arrow-bottom.png b/CenRa_FLUX/arrow-bottom.png new file mode 100644 index 00000000..845e4821 Binary files /dev/null and b/CenRa_FLUX/arrow-bottom.png differ diff --git a/CenRa_FLUX/arrow-up.png b/CenRa_FLUX/arrow-up.png new file mode 100644 index 00000000..13314224 Binary files /dev/null and b/CenRa_FLUX/arrow-up.png differ diff --git a/CenRa_FLUX/cenra.png b/CenRa_FLUX/cenra.png new file mode 100644 index 00000000..d53a910f Binary files /dev/null and b/CenRa_FLUX/cenra.png differ