diff --git a/orientation/i18n/en.ts b/orientation/i18n/en.ts new file mode 100644 index 0000000..c86209d --- /dev/null +++ b/orientation/i18n/en.ts @@ -0,0 +1,53 @@ + + + + + Orientation + + Zone étendue + Large area + + + La zone est vraiment trop étendue, descendre en-dessous de 1:20000 + The area is too large, lower the scale below 1:20000 + + + La zone est étendue et le téléchargement risque de prendre longtemps. +Continuer ? + The area is large and the downloading might take time. +Continue? + + + Sélectionnez un dossier de travail pour enregistrer le projet QGIS + Select a working directory to save the QGIS project + + + Attention + Warning + + + Vous n'avez pas installé l'extension QuickOSM + You haven't installed QuickOSM extension + + + Chargement + Loading + + + Récupération des data en ligne + Gettin gonline data + + + Erreur + Error + + + Les champs suivants sont en doublons et ont été supprimés au profit de leur doublon en minuscule : +- + - + The following fields were duplicated and have been deleted using the lower-case field in priority: +- + - + + + diff --git a/orientation/i18n/fr.ts b/orientation/i18n/fr.ts deleted file mode 100644 index 739632b..0000000 --- a/orientation/i18n/fr.ts +++ /dev/null @@ -1,11 +0,0 @@ - - - - @default - - - Good morning - Bonjour - - - diff --git a/orientation/i18n/orientation.pro b/orientation/i18n/orientation.pro new file mode 100644 index 0000000..1213997 --- /dev/null +++ b/orientation/i18n/orientation.pro @@ -0,0 +1,2 @@ +TRANSLATIONS = en.ts +SOURCES = ../orientation.py diff --git a/orientation/metadata.txt b/orientation/metadata.txt index e8a4459..ac4d257 100644 --- a/orientation/metadata.txt +++ b/orientation/metadata.txt @@ -6,7 +6,7 @@ name=CaLiÉc qgisMinimumVersion=3.0 description=Réaliser des cartes d’orientation -version=2.1 +version=2.2 author=Association Linux-Alpes email=caliec@linux-alpes.org diff --git a/orientation/orientation.py b/orientation/orientation.py index 4667ce2..9733d4b 100644 --- a/orientation/orientation.py +++ b/orientation/orientation.py @@ -20,27 +20,38 @@ * * ***************************************************************************/ """ -from qgis.PyQt.QtCore import Qt, QSettings, QTranslator, QCoreApplication -from qgis.PyQt.QtGui import QIcon -from qgis.PyQt.QtWidgets import QAction, qApp, QFileDialog, QProgressBar, QMessageBox, QMenu, QToolButton - -from qgis.core import QgsCoordinateReferenceSystem, QgsCoordinateTransformContext, QgsProject, QgsVectorFileWriter, QgsVectorLayer, QgsFeatureRequest -from qgis.utils import OverrideCursor +import os.path +import shutil +import tempfile +from pathlib import Path +import processing from processing import QgsProcessingException - +from processing.tools import dataobjects +from qgis.core import ( + QgsCoordinateReferenceSystem, + QgsCoordinateTransformContext, + QgsFeatureRequest, + QgsProject, + QgsVectorFileWriter, + QgsVectorLayer, +) +from qgis.PyQt.QtCore import QCoreApplication, QSettings, Qt, QTranslator, QUrl +from qgis.PyQt.QtGui import QDesktopServices, QIcon +from qgis.PyQt.QtWidgets import ( + QAction, + QFileDialog, + QMenu, + QMessageBox, + QProgressBar, + QToolButton, + qApp, +) +from qgis.utils import OverrideCursor # Initialize Qt resources from file resources.py from .resources import * -import processing -import os.path -import shutil -import tempfile - -from pathlib import Path - -from processing.tools import dataobjects class Orientation: """QGIS Plugin Implementation.""" @@ -58,11 +69,10 @@ class Orientation: # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale - locale = QSettings().value('locale/userLocale')[0:2] + locale = QSettings().value("locale/userLocale")[0:2] locale_path = os.path.join( - self.plugin_dir, - 'i18n', - 'Orientation_{}.qm'.format(locale)) + self.plugin_dir, "i18n", "Orientation_{}.qm".format(locale) + ) if os.path.exists(locale_path): self.translator = QTranslator() @@ -71,7 +81,7 @@ class Orientation: # Declare instance attributes self.actions = [] - self.menu = self.tr('CaLiEc') + self.menu = "CaLiÉc" # Check if plugin was started the first time in current QGIS session # Must be set in initGui() to survive plugin reloads @@ -90,8 +100,7 @@ class Orientation: :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass - return QCoreApplication.translate('Orientation', message) - + return QCoreApplication.translate("Orientation", message) def add_action( self, @@ -103,7 +112,8 @@ class Orientation: add_to_toolbar=True, status_tip=None, whats_this=None, - parent=None): + parent=None, + ): """Add a toolbar icon to the toolbar. :param icon_path: Path to the icon for this action. Can be a resource @@ -159,9 +169,7 @@ class Orientation: self.iface.addToolBarIcon(action) if add_to_menu: - self.iface.addPluginToMenu( - self.menu, - action) + self.iface.addPluginToMenu(self.menu, action) self.actions.append(action) @@ -170,38 +178,68 @@ class Orientation: def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" - icon_path = ':/plugins/caliec/icon.png' + icon_path = ":/plugins/caliec/icon.png" my_menu = QMenu() - my_menu.addActions([self.add_action(icon_path=":/plugins/caliec/vignette_caliec", text=self.tr("Style CaLiÉc"), parent=my_menu, - add_to_toolbar=False, add_to_menu=False, - callback=lambda: self.run("style_caliec")), - self.add_action(icon_path=":/plugins/caliec/vignette_kid", text=self.tr("Style kid"), parent=my_menu, - add_to_toolbar=False, add_to_menu=False, - callback=lambda: self.run("style_kid")), - self.add_action(icon_path=":/plugins/caliec/vignette_jardin", text=self.tr("Style jardin"), parent=my_menu, - add_to_toolbar=False, add_to_menu=False, - callback=lambda: self.run("style_jardin"))]) + my_menu.addActions( + [ + self.add_action( + icon_path=":/plugins/caliec/vignette_caliec", + text=self.tr("Style CaLiÉc"), + parent=my_menu, + add_to_toolbar=False, + add_to_menu=False, + callback=lambda: self.run("style_caliec"), + ), + self.add_action( + icon_path=":/plugins/caliec/vignette_kid", + text=self.tr("Style kid"), + parent=my_menu, + add_to_toolbar=False, + add_to_menu=False, + callback=lambda: self.run("style_kid"), + ), + self.add_action( + icon_path=":/plugins/caliec/vignette_jardin", + text=self.tr("Style jardin"), + parent=my_menu, + add_to_toolbar=False, + add_to_menu=False, + callback=lambda: self.run("style_jardin"), + ), + ] + ) menu_action = self.add_action( icon_path, - text=self.tr('DEV - Créer le projet'), + text=self.tr("Créer le projet"), callback=lambda: None, - parent=self.iface.mainWindow()) + parent=self.iface.mainWindow(), + ) menu_action.setMenu(my_menu) - self.iface.pluginToolBar().widgetForAction(menu_action).setPopupMode(QToolButton.InstantPopup) + self.iface.pluginToolBar().widgetForAction(menu_action).setPopupMode( + QToolButton.InstantPopup + ) + + self.help_action = QAction(QIcon(icon_path), "CaLiÉc", self.iface.mainWindow()) + self.iface.pluginHelpMenu().addAction(self.help_action) + self.help_action.triggered.connect(self.show_help) # will be set False in run() self.first_start = True + @staticmethod + def show_help(): + QDesktopServices.openUrl( + QUrl("https://forge.chapril.org/linux_alpes/caliec/wiki/CaLi%C3%89c") + ) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: - self.iface.removePluginMenu( - self.tr('CaLiÉc'), - action) + self.iface.removePluginMenu("CaLiÉc", action) self.iface.removeToolBarIcon(action) - + self.iface.pluginHelpMenu().removeAction(self.help_action) + del self.help_action def run(self, main_style): try: @@ -213,7 +251,7 @@ class Orientation: def main(self, main_style): # Paramètres du projet tempdir = tempfile.TemporaryDirectory() - styles_url = f'https://forge.chapril.org/linux_alpes/caliec/raw/branch/master/{main_style}/styles/' + styles_url = f"https://forge.chapril.org/linux_alpes/caliec/raw/branch/master/{main_style}/styles/" lambert93 = QgsCoordinateReferenceSystem("EPSG:2154") wgspm = QgsCoordinateReferenceSystem("EPSG:3857") scr = wgspm @@ -226,7 +264,13 @@ class Orientation: context.setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck) if len(project.mapLayersByName("OpenStreetMap")) == 0: - QMessageBox.warning(self.iface.mainWindow(), "Attention", "Affichez d'abord un fond Openstreetmap (menu XYZ tiles) et cadrez la zone voulue") + QMessageBox.warning( + self.iface.mainWindow(), + self.tr("Attention"), + self.tr( + "Affichez d'abord un fond Openstreetmap (menu XYZ tiles) et cadrez la zone voulue" + ), + ) return # Correspondances des noms des couches et des styles: @@ -234,31 +278,48 @@ class Orientation: # Commenter les couches non désirées # L'ordre impacte directement l'ordre final des couches names = { - #"OUTPUT_OTHER_RELATIONS": (None, "Autres"), - "OUTPUT_MULTIPOLYGONS": ("multipolygon", "Surfaces"), - #"OUTPUT_MULTILINESTRINGS": (None, "Multilignes"), - "OUTPUT_LINES": ("linestring", "Lignes"), - "OUTPUT_POINTS": ("point", "Points"), - } + # "OUTPUT_OTHER_RELATIONS": (None, "Autres"), + "OUTPUT_MULTIPOLYGONS": ("multipolygon", "Surfaces"), + # "OUTPUT_MULTILINESTRINGS": (None, "Multilignes"), + "OUTPUT_LINES": ("linestring", "Lignes"), + "OUTPUT_POINTS": ("point", "Points"), + } # Vérif de l'échelle if self.iface.mapCanvas().scale() > 10000: if self.iface.mapCanvas().scale() > 20000: - QMessageBox.warning(self.iface.mainWindow(), "Zone étendue", - "La zone est vraiment trop étendue, descendre en-dessous de 1:20000", - QMessageBox.Ok) + QMessageBox.warning( + self.iface.mainWindow(), + self.tr("Zone étendue"), + self.tr( + "La zone est vraiment trop étendue, descendre en-dessous de 1:20000" + ), + QMessageBox.Ok, + ) return - if QMessageBox.warning(self.iface.mainWindow(), "Zone étendue", - "La zone est étendue et le téléchargement risque de prendre longtemps.\n" - "Continuer ?", - QMessageBox.Yes | QMessageBox.No) == QMessageBox.No: + if ( + QMessageBox.warning( + self.iface.mainWindow(), + self.tr("Zone étendue"), + self.tr( + "La zone est étendue et le téléchargement risque de prendre longtemps.\n" + "Continuer ?" + ), + QMessageBox.Yes | QMessageBox.No, + ) + == QMessageBox.No + ): return # Paramétrage du dossier de travail settings = QSettings() - dir = QFileDialog.getExistingDirectory(self.iface.mainWindow(), - "Sélectionnez un dossier de travail", - settings.value("caliec/workingDir")) + dir = QFileDialog.getExistingDirectory( + self.iface.mainWindow(), + self.tr( + "Sélectionnez un dossier de travail pour enregistrer le projet QGIS" + ), + settings.value("caliec/workingDir"), + ) if dir == "": return settings.setValue("caliec/workingDir", dir) @@ -266,10 +327,16 @@ class Orientation: # Construction de la requête overpass try: - url = processing.run("quickosm:buildqueryextent", - parameters={"EXTENT": self.iface.mapCanvas().extent()})["OUTPUT_URL"] + url = processing.run( + "quickosm:buildqueryextent", + parameters={"EXTENT": self.iface.mapCanvas().extent()}, + )["OUTPUT_URL"] except QgsProcessingException: - QMessageBox.warning(self.iface.mainWindow(), "Attention", "Vous n'avez pas installé l'extension QuickOSM") + QMessageBox.warning( + self.iface.mainWindow(), + self.tr("Attention"), + self.tr("Vous n'avez pas installé l'extension QuickOSM"), + ) return # Barre de chargement animée @@ -277,7 +344,9 @@ class Orientation: message_bar = self.iface.messageBar() progress_bar = QProgressBar() progress_bar.setMaximum(0) - widget = message_bar.createMessage("Chargement", "Récupération des data en ligne") + widget = message_bar.createMessage( + self.tr("Chargement"), self.tr("Récupération des data en ligne") + ) widget.layout().addWidget(progress_bar) message_bar.pushWidget(widget) @@ -290,18 +359,25 @@ class Orientation: style_name, layer_name = style_layer_names # On explose les champs - layer = processing.run("native:explodehstorefield", - parameters={"INPUT": layers[osm_name], - "FIELD": "other_tags", - "OUTPUT": "memory:"}, - context=context)["OUTPUT"] + layer = processing.run( + "native:explodehstorefield", + parameters={ + "INPUT": layers[osm_name], + "FIELD": "other_tags", + "OUTPUT": "memory:", + }, + context=context, + )["OUTPUT"] # On enlève les champs en doublons en gardant celui des doublons qui est écrit tout en minuscule field_names = [a.name() for a in layer.fields()] field_names_lower = [a.lower() for a in field_names] expected_fields, deleted_fields = [], [] for field_name, field_name_lower in zip(field_names, field_names_lower): - if field_names_lower.count(field_name_lower) == 1 or field_name.lower() == field_name: + if ( + field_names_lower.count(field_name_lower) == 1 + or field_name.lower() == field_name + ): expected_fields.append(field_name) else: deleted_fields.append(field_name) @@ -309,30 +385,52 @@ class Orientation: # S'il y a des doublons if len(deleted_fields) > 0: # On réexplose les champs sans doublons - layer = processing.run("native:explodehstorefield", - parameters={"INPUT": layers[osm_name], - "FIELD": "other_tags", - "OUTPUT": "memory:", - "EXPECTED_FIELDS": ",".join(expected_fields)}, - context=context)["OUTPUT"] + layer = processing.run( + "native:explodehstorefield", + parameters={ + "INPUT": layers[osm_name], + "FIELD": "other_tags", + "OUTPUT": "memory:", + "EXPECTED_FIELDS": ",".join(expected_fields), + }, + context=context, + )["OUTPUT"] # On enregistre le layer dans le gpkg options.layerName = layer_name - code, error = QgsVectorFileWriter.writeAsVectorFormatV2(layer, str(workDir / "data.gpkg"), QgsCoordinateTransformContext(), options) + code, error = QgsVectorFileWriter.writeAsVectorFormatV2( + layer, + str(workDir / "data.gpkg"), + QgsCoordinateTransformContext(), + options, + ) if code != 0: with OverrideCursor(Qt.ArrowCursor): - QMessageBox.warning(self.iface.mainWindow(), 'Erreur', f"Erreur à l'export de la couche {layer_name} : \n\n{error[:2000]}") + QMessageBox.warning( + self.iface.mainWindow(), + self.tr("Erreur"), + self.tr( + f"Erreur à l'export de la couche {layer_name} : \n\n{error[:2000]}" + ), + ) return - new_layer = QgsVectorLayer(str(workDir / f"data.gpkg|layername={layer_name}"), layer_name) + new_layer = QgsVectorLayer( + str(workDir / f"data.gpkg|layername={layer_name}"), layer_name + ) # Les layers suivants seront enregistrés dans le gpkg déjà existant options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer # On charge le style if style_name is not None: - stylefile = str((Path(tempdir.name) / style_name).with_suffix('.qml')) - processing.run("native:filedownloader", - parameters={"URL": styles_url + f"{style_name}.qml", "OUTPUT": stylefile}) + stylefile = str((Path(tempdir.name) / style_name).with_suffix(".qml")) + processing.run( + "native:filedownloader", + parameters={ + "URL": styles_url + f"{style_name}.qml", + "OUTPUT": stylefile, + }, + ) new_layer.loadNamedStyle(stylefile) # On charge le nouveau layer @@ -343,11 +441,15 @@ class Orientation: except IndexError: pass self.iface.mapCanvas().refreshAllLayers() - project.write(str(workDir / 'orient.qgs')) + project.write(str(workDir / "orient.qgs")) if len(deleted_fields) > 0: with OverrideCursor(Qt.ArrowCursor): - QMessageBox.warning(self.iface.mainWindow(), "Attention", - "Les champs suivants sont en doublons et ont été supprimés au profit de leur doublon en minuscule :\n- " + - "\n - ".join(deleted_fields)) - + QMessageBox.warning( + self.iface.mainWindow(), + self.tr("Attention"), + self.tr( + "Les champs suivants sont en doublons et ont été supprimés au profit de leur doublon en minuscule :\n- " + + "\n - ".join(deleted_fields) + ), + )