diff --git a/qmapshaper/classes/class_qmapshaper_command_builder.py b/qmapshaper/classes/class_qmapshaper_command_builder.py index 18d58eb..b7b42bc 100644 --- a/qmapshaper/classes/class_qmapshaper_command_builder.py +++ b/qmapshaper/classes/class_qmapshaper_command_builder.py @@ -6,15 +6,15 @@ class QMapshaperCommandBuilder: - @staticmethod - def prepare_console_commands(input_data_path: str, - output_data_path: str, - command: str, - arguments: List[str], - clean_before: bool = False, - clean_after: bool = False) -> List[str]: - + def prepare_console_commands( + input_data_path: str, + output_data_path: str, + command: str, + arguments: List[str], + clean_before: bool = False, + clean_after: bool = False, + ) -> List[str]: commands = [] commands.append(input_data_path) @@ -38,9 +38,8 @@ def prepare_console_commands(input_data_path: str, @staticmethod def prepare_console_output_data(output_data_path: str) -> List[str]: - command = ["-o"] - command.append('format={}'.format(QMapshaperFile.get_format(output_data_path))) + command.append("format={}".format(QMapshaperFile.get_format(output_data_path))) command.append(output_data_path) return command diff --git a/qmapshaper/classes/class_qmapshaper_data_preparer.py b/qmapshaper/classes/class_qmapshaper_data_preparer.py index 1aa758f..1bdb04e 100755 --- a/qmapshaper/classes/class_qmapshaper_data_preparer.py +++ b/qmapshaper/classes/class_qmapshaper_data_preparer.py @@ -1,23 +1,27 @@ from typing import List, Union -from qgis.core import (QgsVectorLayer, QgsVectorLayerUtils, QgsMemoryProviderUtils, - QgsVectorFileWriter, QgsCoordinateTransformContext, QgsVectorLayerJoinInfo, - QgsField) -from qgis.PyQt.QtCore import QVariant - from processing.algs.gdal.GdalUtils import GdalUtils +from qgis.core import ( + QgsCoordinateTransformContext, + QgsField, + QgsMemoryProviderUtils, + QgsVectorFileWriter, + QgsVectorLayer, + QgsVectorLayerJoinInfo, + QgsVectorLayerUtils, +) +from qgis.PyQt.QtCore import QVariant from ..text_constants import TextConstants from .class_qmapshaper_file import QMapshaperFile, QMapshaperGeojsonFile class QMapshaperDataPreparer: - @staticmethod def copy_to_memory_layer(layer: QgsVectorLayer) -> QgsVectorLayer: - - memory_layer = QgsMemoryProviderUtils.createMemoryLayer(layer.name(), layer.fields(), - layer.wkbType(), layer.crs()) + memory_layer = QgsMemoryProviderUtils.createMemoryLayer( + layer.name(), layer.fields(), layer.wkbType(), layer.crs() + ) memory_layer.startEditing() @@ -30,9 +34,7 @@ def copy_to_memory_layer(layer: QgsVectorLayer) -> QgsVectorLayer: return memory_layer @staticmethod - def write_layer_with_minimal_attributes(layer: QgsVectorLayer, file: str, - col_index: Union[int, List[int]]) -> None: - + def write_layer_with_minimal_attributes(layer: QgsVectorLayer, file: str, col_index: Union[int, List[int]]) -> None: options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = QMapshaperFile.driver_name() @@ -41,30 +43,24 @@ def write_layer_with_minimal_attributes(layer: QgsVectorLayer, file: str, else: options.attributes = col_index - QgsVectorFileWriter.writeAsVectorFormatV3(layer=layer, - fileName=file, - transformContext=QgsCoordinateTransformContext(), - options=options) + QgsVectorFileWriter.writeAsVectorFormatV3( + layer=layer, fileName=file, transformContext=QgsCoordinateTransformContext(), options=options + ) @staticmethod - def write_layer_with_as_geojson(layer: QgsVectorLayer, - file: str, - decimal_precision: int = None) -> None: - + def write_layer_with_as_geojson(layer: QgsVectorLayer, file: str, decimal_precision: int = None) -> None: options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = QMapshaperGeojsonFile.driver_name() if decimal_precision: options.layerOptions = ["COORDINATE_PRECISION={}".format(decimal_precision)] - QgsVectorFileWriter.writeAsVectorFormatV3(layer=layer, - fileName=file, - transformContext=QgsCoordinateTransformContext(), - options=options) + QgsVectorFileWriter.writeAsVectorFormatV3( + layer=layer, fileName=file, transformContext=QgsCoordinateTransformContext(), options=options + ) @staticmethod def write_output_file(layer: QgsVectorLayer, file: str, layer_name: str) -> None: - fields = layer.fields() fields_indexes = [x for x in range(0, fields.count())] @@ -80,17 +76,14 @@ def write_output_file(layer: QgsVectorLayer, file: str, layer_name: str) -> None options.attributes = fields_indexes options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteFile - QgsVectorFileWriter.writeAsVectorFormatV3(layer=layer, - fileName=file, - transformContext=QgsCoordinateTransformContext(), - options=options) + QgsVectorFileWriter.writeAsVectorFormatV3( + layer=layer, fileName=file, transformContext=QgsCoordinateTransformContext(), options=options + ) @staticmethod - def join_fields_back(layer_to_join_to: QgsVectorLayer, - layer_to_join_from: QgsVectorLayer, - prefix: str = "", - join_fids_back=True) -> None: - + def join_fields_back( + layer_to_join_to: QgsVectorLayer, layer_to_join_from: QgsVectorLayer, prefix: str = "", join_fids_back=True + ) -> None: join = QgsVectorLayerJoinInfo() join.setTargetFieldName(TextConstants.JOIN_FIELD_NAME) join.setJoinLayer(layer_to_join_from) @@ -105,32 +98,28 @@ def join_fields_back(layer_to_join_to: QgsVectorLayer, @staticmethod def add_mapshaper_id_field(layer: QgsVectorLayer) -> int: - layer.startEditing() - field_index = layer.addExpressionField( - "$id", QgsField(TextConstants.JOIN_FIELD_NAME, QVariant.Int)) + field_index = layer.addExpressionField("$id", QgsField(TextConstants.JOIN_FIELD_NAME, QVariant.Int)) layer.commitChanges() return field_index @staticmethod - def add_mapshaper_generalization_field(layer: QgsVectorLayer, - ids: List[int], - selected_generalize: bool = True) -> int: - + def add_mapshaper_generalization_field( + layer: QgsVectorLayer, ids: List[int], selected_generalize: bool = True + ) -> int: ids_string = ",".join([str(x) for x in ids]) - expr = f'array_contains(array({ids_string}), $id)' + expr = f"array_contains(array({ids_string}), $id)" if not selected_generalize: expr = "NOT " + expr layer.startEditing() - field_index = layer.addExpressionField( - expr, QgsField(TextConstants.GENERALIZATION_FIELD_NAME, QVariant.Bool)) + field_index = layer.addExpressionField(expr, QgsField(TextConstants.GENERALIZATION_FIELD_NAME, QVariant.Bool)) layer.commitChanges() diff --git a/qmapshaper/classes/class_qmapshaper_file.py b/qmapshaper/classes/class_qmapshaper_file.py index 3a6a61f..9b57957 100644 --- a/qmapshaper/classes/class_qmapshaper_file.py +++ b/qmapshaper/classes/class_qmapshaper_file.py @@ -4,10 +4,8 @@ class QMapshaperFile: - @staticmethod def get_format(file_name: str) -> str: - file_types = [QMapshaperFile, QMapshaperGeojsonFile, QMapshaperTopoJsonFile] for file_type in file_types: @@ -18,7 +16,6 @@ def get_format(file_name: str) -> str: @staticmethod def random_temp_filename() -> str: - filename = "{}.{}".format(uuid4(), QMapshaperFile.extension()) return QgsProcessingUtils.generateTempFilename(filename) @@ -37,10 +34,8 @@ def extension() -> str: class QMapshaperGeojsonFile: - @staticmethod def random_temp_filename() -> str: - filename = "{}.{}".format(uuid4(), QMapshaperGeojsonFile.extension()) return QgsProcessingUtils.generateTempFilename(filename) @@ -59,10 +54,8 @@ def extension() -> str: class QMapshaperTopoJsonFile: - @staticmethod def random_temp_filename() -> str: - filename = "{}.{}".format(uuid4(), QMapshaperTopoJsonFile.extension()) return QgsProcessingUtils.generateTempFilename(filename) diff --git a/qmapshaper/classes/class_qmapshaper_paths.py b/qmapshaper/classes/class_qmapshaper_paths.py index 209812c..eb3d530 100644 --- a/qmapshaper/classes/class_qmapshaper_paths.py +++ b/qmapshaper/classes/class_qmapshaper_paths.py @@ -1,29 +1,24 @@ -from pathlib import Path import platform +from pathlib import Path from processing.core.ProcessingConfig import ProcessingConfig from ..text_constants import TextConstants -from ..utils import log class QMapshaperPaths: - @staticmethod def mapshaper_executable_path() -> str: - mapshaper_folder = QMapshaperPaths.mapshaper_folder() if mapshaper_folder: - path = QMapshaperPaths.subfolder_bin( - mapshaper_folder) / QMapshaperPaths.mapshaper_command_name() + path = QMapshaperPaths.subfolder_bin(mapshaper_folder) / QMapshaperPaths.mapshaper_command_name() return path.as_posix() return "" @staticmethod def subfolder_bin(folder) -> Path: - command = "mapshaper" exec_path = Path(folder) / command @@ -39,18 +34,19 @@ def subfolder_bin(folder) -> Path: @staticmethod def mapshaper_folder() -> str: - folder = ProcessingConfig.getSetting(TextConstants.MAPSHAPER_FOLDER) if not folder: folder = QMapshaperPaths.guess_mapshaper_folder() - return folder if folder else '' + return folder if folder else "" @staticmethod def guess_mapshaper_folder() -> str: - - from .class_qmapshaper_runner import MapshaperProcessChecker, NpmPackageLocationCheckerProcess + from .class_qmapshaper_runner import ( + MapshaperProcessChecker, + NpmPackageLocationCheckerProcess, + ) # globally available mp = MapshaperProcessChecker("") @@ -69,7 +65,6 @@ def guess_mapshaper_folder() -> str: @staticmethod def mapshaper_command_name() -> str: - tool = ProcessingConfig.getSetting(TextConstants.MAPSHAPER_TOOL_NAME) if not tool: @@ -86,13 +81,10 @@ def guess_mapshaper_command_name() -> str: @staticmethod def mapshaper_command_call() -> str: - mapshaper_bin_folder = QMapshaperPaths.mapshaper_executable_path() if mapshaper_bin_folder: - return mapshaper_bin_folder else: - return QMapshaperPaths.mapshaper_command_name() diff --git a/qmapshaper/classes/class_qmapshaper_runner.py b/qmapshaper/classes/class_qmapshaper_runner.py index 5ef6a4e..64377c9 100644 --- a/qmapshaper/classes/class_qmapshaper_runner.py +++ b/qmapshaper/classes/class_qmapshaper_runner.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Union, Optional +from typing import Optional, Union from qgis.PyQt.QtCore import QProcess @@ -7,7 +7,6 @@ class MapshaperProcess(QProcess): - output_lines: str = "" error_lines: str = "" @@ -15,7 +14,6 @@ class MapshaperProcess(QProcess): finished_correctly = False def __init__(self) -> None: - super().__init__() self.setProcessChannelMode(QProcess.MergedChannels) @@ -30,7 +28,6 @@ def command_to_run(self) -> str: return " ".join(commands) def run(self): - self.start() self.waitForStarted() @@ -38,7 +35,6 @@ def run(self): self.waitForFinished() def read_output(self): - self.output_lines = bytes(self.readAllStandardOutput()).decode("utf8") self.error_lines = bytes(self.readAllStandardError()).decode("utf8") @@ -47,15 +43,11 @@ def read_output(self): class MapshaperProcessChecker(QProcess): - output_lines: str found = False - path: str - def __init__(self, path: Optional[Union[str, Path]] = None) -> None: - super().__init__() self.path = path @@ -63,13 +55,11 @@ def __init__(self, path: Optional[Union[str, Path]] = None) -> None: self.setProcessChannelMode(QProcess.MergedChannels) if self.path: - path_ms = Path(self.path) / "mapshaper" self.path = path_ms.absolute().as_posix() else: - self.path = "mapshaper" self.setProgram(self.path) @@ -87,7 +77,6 @@ def __init__(self, path: Optional[Union[str, Path]] = None) -> None: class NpmPackageLocationCheckerProcess: - output_lines: str packages_location: str = None @@ -97,11 +86,9 @@ class NpmPackageLocationCheckerProcess: _mapshaper_location: str = None def __init__(self) -> None: - super().__init__() def npm_exist(self) -> bool: - p = QProcess() p.setProgram("npm") @@ -120,7 +107,6 @@ def npm_exist(self) -> bool: return False def npm_package_locations(self) -> bool: - p = QProcess() p.setProgram("npm") @@ -135,7 +121,6 @@ def npm_package_locations(self) -> bool: output_lines = bytes(p.readAllStandardOutput()).decode("utf8") if 0 < len(output_lines): - self.packages_location = output_lines.strip() return True @@ -143,7 +128,6 @@ def npm_package_locations(self) -> bool: return False def mapshaper_exists(self) -> bool: - p = QProcess() p.setProgram("npm") @@ -158,7 +142,6 @@ def mapshaper_exists(self) -> bool: output_lines = bytes(p.readAllStandardOutput()).decode("utf8") if "mapshaper" in output_lines: - self.mapshaper_present = True path = Path(self.packages_location) / "mapshaper" @@ -170,13 +153,9 @@ def mapshaper_exists(self) -> bool: return False def mapshaper_path(self) -> Optional[str]: - if self.npm_exist(): - if self.npm_package_locations(): - if self.mapshaper_exists(): - return self._mapshaper_location return None diff --git a/qmapshaper/classes/classes_workers.py b/qmapshaper/classes/classes_workers.py index eebaf32..7144486 100644 --- a/qmapshaper/classes/classes_workers.py +++ b/qmapshaper/classes/classes_workers.py @@ -1,12 +1,8 @@ -from typing import List - -from qgis.PyQt.QtCore import (QRunnable, QObject, pyqtSignal, pyqtSlot, QThread) +from qgis.PyQt.QtCore import QObject, QRunnable, QThread, pyqtSignal, pyqtSlot class WaitWorker(QRunnable): - def __init__(self, percent: int, wait_time: int = 200): - super(WaitWorker, self).__init__() self.signals = WorkerSignals() @@ -17,16 +13,13 @@ def __init__(self, percent: int, wait_time: int = 200): @pyqtSlot() def run(self): - QThread.msleep(self.wait_time) self.signals.percent.emit(self.percent) class WaitWorkerCommand(QRunnable): - def __init__(self, command: str): - super(WaitWorkerCommand, self).__init__() self.signals = WorkerSignals() @@ -35,7 +28,6 @@ def __init__(self, command: str): @pyqtSlot() def run(self): - QThread.msleep(750) self.signals.command.emit(self.command) diff --git a/qmapshaper/gui/dialog_tool_console.py b/qmapshaper/gui/dialog_tool_console.py index 4d1e5b1..540711f 100755 --- a/qmapshaper/gui/dialog_tool_console.py +++ b/qmapshaper/gui/dialog_tool_console.py @@ -1,22 +1,26 @@ -from qgis.core import (QgsVectorLayer, QgsMapLayerProxyModel) -from qgis.gui import (QgsMapCanvas, QgsMapLayerComboBox, QgisInterface, QgsFieldComboBox) -from qgis.PyQt.QtWidgets import (QDialog, QLabel, QVBoxLayout, QDialogButtonBox, QLineEdit) +from qgis.core import QgsMapLayerProxyModel, QgsVectorLayer +from qgis.gui import QgisInterface, QgsFieldComboBox, QgsMapCanvas, QgsMapLayerComboBox +from qgis.PyQt.QtCore import Qt, QThreadPool, pyqtSignal from qgis.PyQt.QtGui import QPalette -from qgis.PyQt.QtCore import (Qt, QThreadPool, pyqtSignal) +from qgis.PyQt.QtWidgets import ( + QDialog, + QDialogButtonBox, + QLabel, + QLineEdit, + QVBoxLayout, +) -from ..utils import log -from ..text_constants import TextConstants from ..classes.classes_workers import WaitWorkerCommand +from ..text_constants import TextConstants +from ..utils import log from .processes.interactive_console_process import InteractiveConsoleProcess class InteractiveConsoleTool(QDialog): - map_updated = pyqtSignal() data_processed = pyqtSignal() def __init__(self, parent=None, iface: QgisInterface = None): - super().__init__(parent) self.process = InteractiveConsoleProcess(self) @@ -79,13 +83,10 @@ def setup_canvas(self): self.canvas.setLayers([self.layer_selection.currentLayer()]) def set_required_field(self): - self.process.field = self.field.currentField() def set_input_data(self): - if self.layer_selection.currentLayer(): - self.field.setLayer(self.layer_selection.currentLayer()) self.process.set_input_data(self.layer_selection.currentLayer()) @@ -93,33 +94,27 @@ def set_input_data(self): self.process_layer() def process_layer(self) -> None: - self.process.process_layer(self.console_command.text()) self.data_processed.emit() def load_processed_data(self) -> None: - if self.process.processed_data_only_geometry: - self.canvas.setLayers([self.process.processed_data_only_geometry]) self.canvas.redrawAllLayers() self.map_updated.emit() def get_layer_for_project(self) -> QgsVectorLayer: - return self.process.processed_data_with_attributes def create_wait_worker(self) -> None: - wait_worker = WaitWorkerCommand(self.console_command.text()) wait_worker.signals.command.connect(self.run_update) self.threadpool.start(wait_worker) def run_update(self, command: str): - log(f"Prev: {command} - curr: {self.console_command.text()}") if command == self.console_command.text(): diff --git a/qmapshaper/gui/dialog_tool_interactive_simplifier.py b/qmapshaper/gui/dialog_tool_interactive_simplifier.py index 7b21290..7c66439 100755 --- a/qmapshaper/gui/dialog_tool_interactive_simplifier.py +++ b/qmapshaper/gui/dialog_tool_interactive_simplifier.py @@ -1,24 +1,28 @@ -from qgis.core import (QgsVectorLayer, QgsMapLayerProxyModel) -from qgis.gui import (QgsMapCanvas, QgsMapLayerComboBox, QgisInterface) -from qgis.PyQt.QtWidgets import (QDialog, QLabel, QVBoxLayout, QComboBox, QDialogButtonBox, - QCheckBox) -from qgis.PyQt.QtCore import (QThreadPool, pyqtSignal) +from qgis.core import QgsMapLayerProxyModel, QgsVectorLayer +from qgis.gui import QgisInterface, QgsMapCanvas, QgsMapLayerComboBox +from qgis.PyQt.QtCore import QThreadPool, pyqtSignal +from qgis.PyQt.QtWidgets import ( + QCheckBox, + QComboBox, + QDialog, + QDialogButtonBox, + QLabel, + QVBoxLayout, +) +from ..classes.classes_workers import WaitWorker from ..processing.tool_simplify import SimplifyAlgorithm -from ..utils import log from ..text_constants import TextConstants -from ..classes.classes_workers import WaitWorker -from .processes.interactive_simplifier_process import InteractiveSimplifierProcess +from ..utils import log from .percentsliderspinbox import PercentSliderSpinBox +from .processes.interactive_simplifier_process import InteractiveSimplifierProcess class InteractiveSimplifierTool(QDialog): - map_updated = pyqtSignal() data_generalized = pyqtSignal() def __init__(self, parent=None, iface: QgisInterface = None): - super().__init__(parent) self.process = InteractiveSimplifierProcess(parent=self) @@ -42,7 +46,6 @@ def __init__(self, parent=None, iface: QgisInterface = None): self.set_input_data() def set_up_ui(self) -> None: - self.layer_selection = QgsMapLayerComboBox(self) self.layer_selection.setFilters(QgsMapLayerProxyModel.VectorLayer) @@ -60,8 +63,7 @@ def set_up_ui(self) -> None: self.button_box = QDialogButtonBox(QDialogButtonBox.Cancel | QDialogButtonBox.Ok, self) - self.modify_only_part = QCheckBox("Generalize only part of layer (based on selection)", - self) + self.modify_only_part = QCheckBox("Generalize only part of layer (based on selection)", self) self.modify_only_part.stateChanged.connect(self.set_selection) self.modify_selection = QComboBox(self) @@ -101,11 +103,9 @@ def setup_canvas(self): self.canvas.setLayers([self.layer_selection.currentLayer()]) def set_input_data(self): - log(f"Modify only part: {self.modify_only_part.isChecked()}") if self.layer_selection.currentLayer(): - self.process.generalize_select = self.modify_only_part.isChecked() self.process.set_input_data(self.layer_selection.currentLayer()) @@ -120,17 +120,15 @@ def set_input_data(self): self.generalize_layer() def generalize_layer(self) -> None: - - self.process.process_layer(simplify_percent=self.percent_widget.value(), - simplify_method=SimplifyAlgorithm.get_method( - self.methods.currentIndex())) + self.process.process_layer( + simplify_percent=self.percent_widget.value(), + simplify_method=SimplifyAlgorithm.get_method(self.methods.currentIndex()), + ) self.data_generalized.emit() def load_generalized_data(self) -> None: - if self.process.processed_data_only_geometry: - self.process.apply_selection_to_generalized_data() self.canvas.setLayers([self.process.processed_data_only_geometry]) @@ -139,15 +137,12 @@ def load_generalized_data(self) -> None: self.map_updated.emit() def get_layer_for_project(self) -> QgsVectorLayer: - return self.process.processed_data_with_attributes def trigger_input_parameters_change(self) -> None: - self.generalize_layer() def set_selection(self): - self.modify_selection.setEnabled(self.modify_only_part.isChecked()) self.process.process_selection = self.modify_only_part.isChecked() @@ -156,14 +151,12 @@ def set_selection(self): self.generalize_layer() def set_generalization_type(self): - self.process.process_selected_features = self.modify_selection.currentData() self.set_input_data() self.generalize_layer() def set_clean_data(self): - self.process.clean_data = self.clean_data.isChecked() self.generalize_layer() diff --git a/qmapshaper/gui/percentsliderspinbox.py b/qmapshaper/gui/percentsliderspinbox.py index d34a991..9262a60 100644 --- a/qmapshaper/gui/percentsliderspinbox.py +++ b/qmapshaper/gui/percentsliderspinbox.py @@ -1,22 +1,23 @@ -from typing import Union, Optional +from typing import Optional, Union -from qgis.PyQt.QtWidgets import (QWidget, QSlider, QSpinBox, QHBoxLayout) -from qgis.PyQt.QtCore import (Qt, pyqtSignal, QThreadPool) +from qgis.PyQt.QtCore import Qt, QThreadPool, pyqtSignal +from qgis.PyQt.QtWidgets import QHBoxLayout, QSlider, QSpinBox, QWidget from ..classes.classes_workers import WaitWorker class PercentSliderSpinBox(QWidget): - valueChanged = pyqtSignal() valueChangedInteractionStopped = pyqtSignal() - def __init__(self, - defaultValue: int = 50, - minimum: int = 1, - maximum: int = 99, - parent: Optional['QWidget'] = None, - flags: Union[Qt.WindowFlags, Qt.WindowType] = Qt.WindowType.Widget) -> None: + def __init__( + self, + defaultValue: int = 50, + minimum: int = 1, + maximum: int = 99, + parent: Optional["QWidget"] = None, + flags: Union[Qt.WindowFlags, Qt.WindowType] = Qt.WindowType.Widget, + ) -> None: super().__init__(parent, flags) layout = QHBoxLayout() @@ -67,12 +68,10 @@ def spin_box_value_change(self): self._set_value(self.slider, self.spin_box.value()) def run_update(self, percent: int): - if percent == self.spin_box.value(): self.valueChangedInteractionStopped.emit() def create_wait_worker(self) -> None: - wait_worker = WaitWorker(self.spin_box.value()) wait_worker.signals.percent.connect(self.run_update) diff --git a/qmapshaper/gui/processes/interactive_console_process.py b/qmapshaper/gui/processes/interactive_console_process.py index 74d1da9..6a75420 100755 --- a/qmapshaper/gui/processes/interactive_console_process.py +++ b/qmapshaper/gui/processes/interactive_console_process.py @@ -1,22 +1,19 @@ -from qgis.PyQt.QtWidgets import (QDialog) +from qgis.PyQt.QtWidgets import QDialog -from ...classes.class_qmapshaper_file import QMapshaperFile from ...classes.class_qmapshaper_command_builder import QMapshaperCommandBuilder +from ...classes.class_qmapshaper_file import QMapshaperFile from ...processing.tool_console import ConsoleAlgorithm from ...utils import log from .interactive_process import InteractiveProcess class InteractiveConsoleProcess(InteractiveProcess): - def __init__(self, parent: QDialog = None) -> None: - super(InteractiveConsoleProcess, self).__init__(parent=parent) self.result_layer_name = "processed" def process_layer(self, console_call: str) -> None: - self.remove_previous_data() self.processed_data_filename = QMapshaperFile.random_temp_filename() @@ -29,7 +26,8 @@ def process_layer(self, console_call: str) -> None: command=commands[0], arguments=commands[1:], clean_before=self.clean_data, - clean_after=self.clean_data) + clean_after=self.clean_data, + ) log(f"COMMAND TO RUN: {' '.join(commands)}") diff --git a/qmapshaper/gui/processes/interactive_process.py b/qmapshaper/gui/processes/interactive_process.py index 3e5714e..52310d3 100644 --- a/qmapshaper/gui/processes/interactive_process.py +++ b/qmapshaper/gui/processes/interactive_process.py @@ -1,28 +1,26 @@ -from pathlib import Path -from typing import Optional, List +import platform import random import shutil -import platform +from pathlib import Path +from typing import List, Optional -from qgis.core import (QgsVectorLayer, QgsSingleSymbolRenderer, QgsFillSymbol) -from qgis.PyQt.QtCore import (QObject, pyqtSignal) -from qgis.PyQt.QtWidgets import (QDialog) +from qgis.core import QgsFillSymbol, QgsSingleSymbolRenderer, QgsVectorLayer +from qgis.PyQt.QtCore import QObject, pyqtSignal +from qgis.PyQt.QtWidgets import QDialog -from ...classes.class_qmapshaper_file import QMapshaperFile from ...classes.class_qmapshaper_data_preparer import QMapshaperDataPreparer +from ...classes.class_qmapshaper_file import QMapshaperFile from ...classes.class_qmapshaper_runner import MapshaperProcess from ...text_constants import TextConstants from ...utils import log class InteractiveProcess(QObject): - input_layer_changed = pyqtSignal() processed_layer_changed = pyqtSignal() processed_layer_prepared = pyqtSignal() def __init__(self, parent: QDialog = None) -> None: - super().__init__() self.parent_gui = parent @@ -50,13 +48,12 @@ def __init__(self, parent: QDialog = None) -> None: self.processed_layer_changed.connect(self.load_processed_layer) def set_input_data(self, layer: QgsVectorLayer) -> None: - if self.process_selection: - self.stored_ids = layer.selectedFeatureIds() generalize_field_index = QMapshaperDataPreparer.add_mapshaper_generalization_field( - layer, self.stored_ids, self.process_selected_features) + layer, self.stored_ids, self.process_selected_features + ) self.memory_layer = QMapshaperDataPreparer.copy_to_memory_layer(layer) @@ -65,7 +62,6 @@ def set_input_data(self, layer: QgsVectorLayer) -> None: self.input_layer_changed.emit() def export_for_processing(self) -> None: - self.input_data_filename = QMapshaperFile.random_temp_filename() field_index_id = QMapshaperDataPreparer.add_mapshaper_id_field(self.memory_layer) @@ -73,22 +69,19 @@ def export_for_processing(self) -> None: fields = [field_index_id] if self.process_selection and self.stored_ids: - - field_index_generalization = self.memory_layer.fields().lookupField( - TextConstants.GENERALIZATION_FIELD_NAME) + field_index_generalization = self.memory_layer.fields().lookupField(TextConstants.GENERALIZATION_FIELD_NAME) fields.append(field_index_generalization) if self.field: required_field = self.memory_layer.fields().lookupField(self.field) fields.append(required_field) - QMapshaperDataPreparer.write_layer_with_minimal_attributes(layer=self.memory_layer, - file=self.input_data_filename, - col_index=fields) + QMapshaperDataPreparer.write_layer_with_minimal_attributes( + layer=self.memory_layer, file=self.input_data_filename, col_index=fields + ) log(f"Data stored at: {self.input_data_filename}") def remove_previous_data(self) -> None: - self._processed_data_layer = None if self.processed_data_filename and platform.system() != "Windows": @@ -97,29 +90,25 @@ def remove_previous_data(self) -> None: shutil.rmtree(path.parent) def load_processed_layer(self) -> None: + self._processed_data_layer = QgsVectorLayer( + self.processed_data_filename, "{} data".format(self.result_layer_name), "ogr" + ) - self._processed_data_layer = QgsVectorLayer(self.processed_data_filename, - "{} data".format(self.result_layer_name), - "ogr") - - self._processed_data_layer_memory = QMapshaperDataPreparer.copy_to_memory_layer( - self._processed_data_layer) + self._processed_data_layer_memory = QMapshaperDataPreparer.copy_to_memory_layer(self._processed_data_layer) - QMapshaperDataPreparer.join_fields_back(self._processed_data_layer_memory, - self.memory_layer) + QMapshaperDataPreparer.join_fields_back(self._processed_data_layer_memory, self.memory_layer) self._processed_data_layer_memory = QMapshaperDataPreparer.copy_to_memory_layer( - self._processed_data_layer_memory) + self._processed_data_layer_memory + ) fields_to_delete = [] - index = self._processed_data_layer_memory.fields().lookupField( - TextConstants.JOIN_FIELD_NAME) + index = self._processed_data_layer_memory.fields().lookupField(TextConstants.JOIN_FIELD_NAME) fields_to_delete.append(index) - index = self._processed_data_layer_memory.fields().lookupField( - TextConstants.GENERALIZATION_FIELD_NAME) + index = self._processed_data_layer_memory.fields().lookupField(TextConstants.GENERALIZATION_FIELD_NAME) if 0 < index: fields_to_delete.append(index) @@ -128,25 +117,21 @@ def load_processed_layer(self) -> None: self._processed_data_layer_memory.deleteAttributes(fields_to_delete) self._processed_data_layer_memory.commitChanges() - self._processed_data_layer_memory.setName("{} {}".format(self.memory_layer.name(), - self.result_layer_name)) + self._processed_data_layer_memory.setName("{} {}".format(self.memory_layer.name(), self.result_layer_name)) self._processed_data_layer_memory.setCrs(self.memory_layer.crs()) if self._processed_data_layer.renderer() is None: - random_number = random.randint(0, 16777215) hex_number = str(hex(random_number)) - hex_number = '#' + hex_number[2:] + hex_number = "#" + hex_number[2:] - sym1 = QgsFillSymbol.createSimple({'color': hex_number}) + sym1 = QgsFillSymbol.createSimple({"color": hex_number}) renderer = QgsSingleSymbolRenderer(sym1) self._processed_data_layer_memory.setRenderer(renderer) else: - - self._processed_data_layer_memory.setRenderer( - self._processed_data_layer.renderer().clone()) + self._processed_data_layer_memory.setRenderer(self._processed_data_layer.renderer().clone()) self.processed_layer_prepared.emit() @@ -156,18 +141,15 @@ def processed_data_with_attributes(self) -> QgsVectorLayer: @property def processed_data_only_geometry(self) -> Optional[QgsVectorLayer]: - if self._processed_data_layer: return self._processed_data_layer else: return None def processed_layer_updated(self) -> None: - self.processed_layer_changed.emit() def run_mapshaper_process(self, commands: List[str]) -> None: - process = MapshaperProcess() process.finished.connect(self.processed_layer_updated) @@ -177,9 +159,7 @@ def run_mapshaper_process(self, commands: List[str]) -> None: process.run() def apply_selection_to_generalized_data(self) -> None: - - index = self.processed_data_only_geometry.fields().indexOf( - TextConstants.GENERALIZATION_FIELD_NAME) + index = self.processed_data_only_geometry.fields().indexOf(TextConstants.GENERALIZATION_FIELD_NAME) value = 1 @@ -187,7 +167,6 @@ def apply_selection_to_generalized_data(self) -> None: value = 0 if -1 < index: - expr = "{} = {}".format(TextConstants.GENERALIZATION_FIELD_NAME, value) self.processed_data_only_geometry.selectByExpression(expr) diff --git a/qmapshaper/gui/processes/interactive_simplifier_process.py b/qmapshaper/gui/processes/interactive_simplifier_process.py index c817b8c..8be2467 100755 --- a/qmapshaper/gui/processes/interactive_simplifier_process.py +++ b/qmapshaper/gui/processes/interactive_simplifier_process.py @@ -1,7 +1,7 @@ -from qgis.PyQt.QtWidgets import (QDialog) +from qgis.PyQt.QtWidgets import QDialog -from ...classes.class_qmapshaper_file import QMapshaperFile from ...classes.class_qmapshaper_command_builder import QMapshaperCommandBuilder +from ...classes.class_qmapshaper_file import QMapshaperFile from ...processing.tool_simplify import SimplifyAlgorithm from ...text_constants import TextConstants from ...utils import log @@ -9,15 +9,12 @@ class InteractiveSimplifierProcess(InteractiveProcess): - def __init__(self, parent: QDialog = None) -> None: - super(InteractiveSimplifierProcess, self).__init__(parent=parent) self.result_layer_name = "generalized" def process_layer(self, simplify_percent: float, simplify_method: str) -> None: - self.remove_previous_data() self.processed_data_filename = QMapshaperFile.random_temp_filename() @@ -29,10 +26,9 @@ def process_layer(self, simplify_percent: float, simplify_method: str) -> None: else: field_name = None - arguments = SimplifyAlgorithm.prepare_arguments(simplify_percent=simplify_percent, - method=simplify_method, - planar=planar, - field=field_name) + arguments = SimplifyAlgorithm.prepare_arguments( + simplify_percent=simplify_percent, method=simplify_method, planar=planar, field=field_name + ) commands = QMapshaperCommandBuilder.prepare_console_commands( input_data_path=self.input_data_filename, @@ -40,7 +36,8 @@ def process_layer(self, simplify_percent: float, simplify_method: str) -> None: command=SimplifyAlgorithm.command(), arguments=arguments, clean_before=self.clean_data, - clean_after=self.clean_data) + clean_after=self.clean_data, + ) log(f"COMMAND TO RUN: {' '.join(commands)}") diff --git a/qmapshaper/processing/mapshaper_algorithm.py b/qmapshaper/processing/mapshaper_algorithm.py index e697763..a51c864 100755 --- a/qmapshaper/processing/mapshaper_algorithm.py +++ b/qmapshaper/processing/mapshaper_algorithm.py @@ -1,19 +1,22 @@ import abc from pathlib import Path -from typing import List, Dict - +from typing import Dict, List + +from qgis.core import ( + QgsProcessingAlgorithm, + QgsProcessingException, + QgsProcessingFeedback, + QgsVectorLayer, +) from qgis.PyQt.QtGui import QIcon -from qgis.core import (QgsProcessingAlgorithm, QgsVectorLayer, QgsProcessingFeedback, - QgsProcessingException) -from ..classes.class_qmapshaper_runner import MapshaperProcess from ..classes.class_qmapshaper_command_builder import QMapshaperCommandBuilder from ..classes.class_qmapshaper_data_preparer import QMapshaperDataPreparer from ..classes.class_qmapshaper_file import QMapshaperFile +from ..classes.class_qmapshaper_runner import MapshaperProcess class MapshaperAlgorithm(QgsProcessingAlgorithm): - __metaclass__ = abc.ABCMeta input_layer_memory: QgsVectorLayer @@ -64,8 +67,7 @@ def get_arguments(self, parameters, context, feedback: QgsProcessingFeedback) -> return None @abc.abstractmethod - def process_field(self, parameter_name, parameters, context, - feedback: QgsProcessingFeedback) -> None: + def process_field(self, parameter_name, parameters, context, feedback: QgsProcessingFeedback) -> None: return None @staticmethod @@ -81,9 +83,7 @@ def return_dict(self) -> Dict[str, str]: def prepare_arguments() -> List[str]: return None - def get_console_commands(self, parameters, context, - feedback: QgsProcessingFeedback) -> List[str]: - + def get_console_commands(self, parameters, context, feedback: QgsProcessingFeedback) -> List[str]: arguments = self.get_arguments(parameters, context, feedback) commands = QMapshaperCommandBuilder.prepare_console_commands( @@ -92,12 +92,12 @@ def get_console_commands(self, parameters, context, command=self.name(), arguments=arguments, clean_before=self.clean_data_before, - clean_after=self.clean_data_after) + clean_after=self.clean_data_after, + ) return commands def processAlgorithm(self, parameters, context, feedback: QgsProcessingFeedback): - self.prepare_data(parameters, context, feedback) commands = self.get_console_commands(parameters, context, feedback) @@ -135,9 +135,7 @@ def simplified_field_shortened(self) -> str: return self.simplify_field[0:10] return None - def process_input_layer(self, parameter_name, parameters, context, - feedback: QgsProcessingFeedback) -> None: - + def process_input_layer(self, parameter_name, parameters, context, feedback: QgsProcessingFeedback) -> None: layer = self.parameterAsVectorLayer(parameters, parameter_name, context) if not layer: @@ -155,33 +153,31 @@ def process_input_layer(self, parameter_name, parameters, context, index = self.input_layer_memory.fields().lookupField(self.simplified_field_full_name) fields.append(index) - QMapshaperDataPreparer.write_layer_with_minimal_attributes(layer=self.input_layer_memory, - file=self.mapshaper_input, - col_index=fields) + QMapshaperDataPreparer.write_layer_with_minimal_attributes( + layer=self.input_layer_memory, file=self.mapshaper_input, col_index=fields + ) if self.needs_join: - QMapshaperDataPreparer.write_layer_with_minimal_attributes( - layer=self.input_layer_memory, file=self.mapshaper_join, col_index=fields) + layer=self.input_layer_memory, file=self.mapshaper_join, col_index=fields + ) def process_output_layer(self, feedback: QgsProcessingFeedback): - layer_generalized = QgsVectorLayer(self.mapshaper_output, "data", "ogr") memory_layer = QMapshaperDataPreparer.copy_to_memory_layer(layer_generalized) memory_layer.setCrs(self.input_layer_memory.crs()) - QMapshaperDataPreparer.join_fields_back(memory_layer, - self.input_layer_memory, - join_fids_back=self.join_fid_field_back) + QMapshaperDataPreparer.join_fields_back( + memory_layer, self.input_layer_memory, join_fids_back=self.join_fid_field_back + ) - QMapshaperDataPreparer.write_output_file(layer=memory_layer, - file=self.result_layer_location, - layer_name=self.input_layer_memory.name()) + QMapshaperDataPreparer.write_output_file( + layer=memory_layer, file=self.result_layer_location, layer_name=self.input_layer_memory.name() + ) def icon(self): - location = Path(__file__).parent.parent / "icons" / "main_icon.png" return QIcon(location.absolute().as_posix()) diff --git a/qmapshaper/processing/tool_console.py b/qmapshaper/processing/tool_console.py index 0c64bd0..f6242d0 100644 --- a/qmapshaper/processing/tool_console.py +++ b/qmapshaper/processing/tool_console.py @@ -1,49 +1,49 @@ -from typing import List, Union, Dict import re +from typing import Dict, List, Union -from qgis.core import (QgsProcessingParameterVectorLayer, QgsProcessingFeedback, - QgsProcessingParameterVectorDestination, QgsProcessingParameterString, - QgsProcessingParameterField) +from qgis.core import ( + QgsProcessingFeedback, + QgsProcessingParameterField, + QgsProcessingParameterString, + QgsProcessingParameterVectorDestination, + QgsProcessingParameterVectorLayer, +) -from .mapshaper_algorithm import MapshaperAlgorithm from ..classes.class_qmapshaper_command_builder import QMapshaperCommandBuilder +from .mapshaper_algorithm import MapshaperAlgorithm class ConsoleAlgorithm(MapshaperAlgorithm): - INPUT_LAYER = "Input" CONSOLE = "Console" OUTPUT_LAYER = "Output" FIELD = "Field" def initAlgorithm(self, config=None): - self.addParameter(QgsProcessingParameterVectorLayer(self.INPUT_LAYER, "Input layer")) self.addParameter(QgsProcessingParameterString(self.CONSOLE, "Console Command")) self.addParameter( - QgsProcessingParameterField(self.FIELD, - "Select field that is needed for command", - parentLayerParameterName=self.INPUT_LAYER, - optional=True, - allowMultiple=False)) + QgsProcessingParameterField( + self.FIELD, + "Select field that is needed for command", + parentLayerParameterName=self.INPUT_LAYER, + optional=True, + allowMultiple=False, + ) + ) - self.addParameter( - QgsProcessingParameterVectorDestination(self.OUTPUT_LAYER, "Output Layer")) + self.addParameter(QgsProcessingParameterVectorDestination(self.OUTPUT_LAYER, "Output Layer")) def prepare_data(self, parameters, context, feedback: QgsProcessingFeedback) -> None: - self.process_field(self.FIELD, parameters, context, feedback) self.process_input_layer(self.INPUT_LAYER, parameters, context, feedback) - self.result_layer_location = self.parameterAsOutputLayer(parameters, self.OUTPUT_LAYER, - context) - - def process_field(self, field_name: str, parameters, context, - feedback: QgsProcessingFeedback) -> None: + self.result_layer_location = self.parameterAsOutputLayer(parameters, self.OUTPUT_LAYER, context) + def process_field(self, field_name: str, parameters, context, feedback: QgsProcessingFeedback) -> None: field = self.parameterAsFields(parameters, field_name, context) if field: @@ -51,9 +51,7 @@ def process_field(self, field_name: str, parameters, context, self.simplify_field = field - def get_console_commands(self, parameters, context, - feedback: QgsProcessingFeedback) -> List[str]: - + def get_console_commands(self, parameters, context, feedback: QgsProcessingFeedback) -> List[str]: console_command = self.parameterAsString(parameters, self.CONSOLE, context) list_of_commands = self.split_text_into_parts(console_command) @@ -64,7 +62,8 @@ def get_console_commands(self, parameters, context, command=list_of_commands[0], arguments=list_of_commands[1:], clean_before=self.clean_data_before, - clean_after=self.clean_data_after) + clean_after=self.clean_data_after, + ) return commands @@ -81,25 +80,20 @@ def createInstance(self): return ConsoleAlgorithm() @staticmethod - def prepare_arguments(simplify_percent: Union[int, float, str] = 50, - method: str = "dp", - planar: bool = False, - field: str = None) -> List[str]: - + def prepare_arguments( + simplify_percent: Union[int, float, str] = 50, method: str = "dp", planar: bool = False, field: str = None + ) -> List[str]: arguments = [method] if field: - - arguments.extend([ - "variable", "percentage", "=", "{} ? {} : 1".format(field, - float(simplify_percent) / 100) - ]) + arguments.extend( + ["variable", "percentage", "=", "{} ? {} : 1".format(field, float(simplify_percent) / 100)] + ) else: + arguments.append("{}%".format(simplify_percent)) - arguments.append('{}%'.format(simplify_percent)) - - arguments.append('keep-shapes') + arguments.append("keep-shapes") if planar: arguments.append("planar") @@ -112,7 +106,6 @@ def regex_quoted() -> re.Pattern: @staticmethod def split_text_into_parts(text: str) -> List[str]: - replace_constant = "---" quoted_regex = ConsoleAlgorithm.regex_quoted() @@ -121,30 +114,24 @@ def split_text_into_parts(text: str) -> List[str]: text_replaced = ConsoleAlgorithm.replace_parts(quoted_regex, text, replace_constant) - return ConsoleAlgorithm.replace_parts_back(text_replaced, - quoted_parts, - replacement=replace_constant) + return ConsoleAlgorithm.replace_parts_back(text_replaced, quoted_parts, replacement=replace_constant) @staticmethod def extract_parts(quoted_regex: re.Pattern, text: str) -> List[str]: - quoted_parts = quoted_regex.findall(text) return quoted_parts @staticmethod def replace_parts(quoted_regex: re.Pattern, text: str, replacement: str = "---") -> List[str]: - text_replaced = quoted_regex.sub(replacement, text) return text_replaced @staticmethod - def replace_parts_back(base_text: str, - replacements: List[str], - split_by: str = " ", - replacement: str = "---") -> List[str]: - + def replace_parts_back( + base_text: str, replacements: List[str], split_by: str = " ", replacement: str = "---" + ) -> List[str]: text_splitted = base_text.split(" ") for quoted_part in replacements: diff --git a/qmapshaper/processing/tool_simplify.py b/qmapshaper/processing/tool_simplify.py index 0438011..5c763f8 100644 --- a/qmapshaper/processing/tool_simplify.py +++ b/qmapshaper/processing/tool_simplify.py @@ -1,15 +1,19 @@ -from typing import List, Union, Dict - -from qgis.core import (QgsProcessingParameterVectorLayer, QgsProcessingParameterNumber, - QgsProcessingParameterEnum, QgsProcessingFeedback, - QgsProcessingParameterVectorDestination, QgsProcessingParameterField, - QgsProcessingParameterBoolean) +from typing import Dict, List, Union + +from qgis.core import ( + QgsProcessingFeedback, + QgsProcessingParameterBoolean, + QgsProcessingParameterEnum, + QgsProcessingParameterField, + QgsProcessingParameterNumber, + QgsProcessingParameterVectorDestination, + QgsProcessingParameterVectorLayer, +) from .mapshaper_algorithm import MapshaperAlgorithm class SimplifyAlgorithm(MapshaperAlgorithm): - INPUT_LAYER = "Input" SIMPLIFY = "Simplify" METHOD = "Method" @@ -18,49 +22,47 @@ class SimplifyAlgorithm(MapshaperAlgorithm): OUTPUT_LAYER = "Output" def initAlgorithm(self, config=None): - self.addParameter(QgsProcessingParameterVectorLayer(self.INPUT_LAYER, "Input layer")) self.addParameter( - QgsProcessingParameterNumber(self.SIMPLIFY, - "Simplify %", - type=QgsProcessingParameterNumber.Integer, - defaultValue=50, - minValue=1, - maxValue=99)) + QgsProcessingParameterNumber( + self.SIMPLIFY, + "Simplify %", + type=QgsProcessingParameterNumber.Integer, + defaultValue=50, + minValue=1, + maxValue=99, + ) + ) self.addParameter( - QgsProcessingParameterEnum(self.METHOD, - "Simplification method", - options=list(self.methods().keys()), - defaultValue=0)) + QgsProcessingParameterEnum( + self.METHOD, "Simplification method", options=list(self.methods().keys()), defaultValue=0 + ) + ) self.addParameter( - QgsProcessingParameterField(self.FIELD, - "Perform simplification based on feature field", - parentLayerParameterName=self.INPUT_LAYER, - optional=True, - allowMultiple=False)) + QgsProcessingParameterField( + self.FIELD, + "Perform simplification based on feature field", + parentLayerParameterName=self.INPUT_LAYER, + optional=True, + allowMultiple=False, + ) + ) - self.addParameter( - QgsProcessingParameterBoolean(self.CLEAN_DATA, - "Clean data prior and after simplification")) + self.addParameter(QgsProcessingParameterBoolean(self.CLEAN_DATA, "Clean data prior and after simplification")) - self.addParameter( - QgsProcessingParameterVectorDestination(self.OUTPUT_LAYER, "Output Layer")) + self.addParameter(QgsProcessingParameterVectorDestination(self.OUTPUT_LAYER, "Output Layer")) def prepare_data(self, parameters, context, feedback: QgsProcessingFeedback) -> None: - self.process_field(self.FIELD, parameters, context, feedback) self.process_input_layer(self.INPUT_LAYER, parameters, context, feedback) - self.result_layer_location = self.parameterAsOutputLayer(parameters, self.OUTPUT_LAYER, - context) - - def process_field(self, field_name: str, parameters, context, - feedback: QgsProcessingFeedback) -> None: + self.result_layer_location = self.parameterAsOutputLayer(parameters, self.OUTPUT_LAYER, context) + def process_field(self, field_name: str, parameters, context, feedback: QgsProcessingFeedback) -> None: field = self.parameterAsFields(parameters, field_name, context) if field: @@ -69,7 +71,6 @@ def process_field(self, field_name: str, parameters, context, self.simplify_field = field def get_arguments(self, parameters, context, feedback: QgsProcessingFeedback): - clean_data = self.parameterAsBool(parameters, self.CLEAN_DATA, context) self.clean_data_before = clean_data @@ -83,10 +84,9 @@ def get_arguments(self, parameters, context, feedback: QgsProcessingFeedback): planar = not self.input_layer_memory.crs().isGeographic() - arguments = self.prepare_arguments(simplify_percent=simplify_percent, - method=method, - planar=planar, - field=self.simplified_field_shortened) + arguments = self.prepare_arguments( + simplify_percent=simplify_percent, method=method, planar=planar, field=self.simplified_field_shortened + ) return arguments @@ -107,25 +107,20 @@ def createInstance(self): return SimplifyAlgorithm() @staticmethod - def prepare_arguments(simplify_percent: Union[int, float, str] = 50, - method: str = "dp", - planar: bool = False, - field: str = None) -> List[str]: - + def prepare_arguments( + simplify_percent: Union[int, float, str] = 50, method: str = "dp", planar: bool = False, field: str = None + ) -> List[str]: arguments = [method] if field: - - arguments.extend([ - "variable", "percentage", "=", "{} ? {} : 1".format(field, - float(simplify_percent) / 100) - ]) + arguments.extend( + ["variable", "percentage", "=", "{} ? {} : 1".format(field, float(simplify_percent) / 100)] + ) else: + arguments.append("{}%".format(simplify_percent)) - arguments.append('{}%'.format(simplify_percent)) - - arguments.append('keep-shapes') + arguments.append("keep-shapes") if planar: arguments.append("planar") diff --git a/qmapshaper/processing/tool_simplify_lines.py b/qmapshaper/processing/tool_simplify_lines.py index fddfc49..d289cd3 100755 --- a/qmapshaper/processing/tool_simplify_lines.py +++ b/qmapshaper/processing/tool_simplify_lines.py @@ -1,16 +1,20 @@ -from typing import List, Union, Dict +from typing import Dict, List, Union + +from qgis.core import ( + QgsProcessing, + QgsProcessingFeedback, + QgsProcessingParameterBoolean, + QgsProcessingParameterEnum, + QgsProcessingParameterNumber, + QgsProcessingParameterVectorDestination, + QgsProcessingParameterVectorLayer, +) -from qgis.core import (QgsProcessingParameterVectorLayer, QgsProcessingParameterNumber, - QgsProcessingParameterEnum, QgsProcessingFeedback, - QgsProcessingParameterVectorDestination, QgsProcessingParameterField, - QgsProcessingParameterBoolean, QgsProcessing) - -from .mapshaper_algorithm import MapshaperAlgorithm from ..text_constants import TextConstants +from .mapshaper_algorithm import MapshaperAlgorithm class SimplifyPolygonLinesAlgorithm(MapshaperAlgorithm): - INPUT_LAYER = "Input" SIMPLIFY = "Simplify" METHOD = "Method" @@ -25,47 +29,43 @@ def __init__(self): self.join_fid_field_back = False def initAlgorithm(self, config=None): - self.addParameter( - QgsProcessingParameterVectorLayer(self.INPUT_LAYER, "Input layer", - [QgsProcessing.TypeVectorPolygon])) + QgsProcessingParameterVectorLayer(self.INPUT_LAYER, "Input layer", [QgsProcessing.TypeVectorPolygon]) + ) self.addParameter( - QgsProcessingParameterNumber(self.SIMPLIFY, - "Simplify %", - type=QgsProcessingParameterNumber.Integer, - defaultValue=50, - minValue=1, - maxValue=99)) + QgsProcessingParameterNumber( + self.SIMPLIFY, + "Simplify %", + type=QgsProcessingParameterNumber.Integer, + defaultValue=50, + minValue=1, + maxValue=99, + ) + ) self.addParameter( - QgsProcessingParameterEnum(self.METHOD, - "Simplification method", - options=list(self.methods().keys()), - defaultValue=0)) + QgsProcessingParameterEnum( + self.METHOD, "Simplification method", options=list(self.methods().keys()), defaultValue=0 + ) + ) self.addParameter( - QgsProcessingParameterEnum(self.LINES, - "Generalize polygon's lines", - options=list(self.lines().keys()), - defaultValue=0)) + QgsProcessingParameterEnum( + self.LINES, "Generalize polygon's lines", options=list(self.lines().keys()), defaultValue=0 + ) + ) - self.addParameter( - QgsProcessingParameterBoolean(self.CLEAN_DATA, - "Clean data prior and after simplification")) + self.addParameter(QgsProcessingParameterBoolean(self.CLEAN_DATA, "Clean data prior and after simplification")) - self.addParameter( - QgsProcessingParameterVectorDestination(self.OUTPUT_LAYER, "Output Layer")) + self.addParameter(QgsProcessingParameterVectorDestination(self.OUTPUT_LAYER, "Output Layer")) def prepare_data(self, parameters, context, feedback: QgsProcessingFeedback) -> None: - self.process_input_layer(self.INPUT_LAYER, parameters, context, feedback) - self.result_layer_location = self.parameterAsOutputLayer(parameters, self.OUTPUT_LAYER, - context) + self.result_layer_location = self.parameterAsOutputLayer(parameters, self.OUTPUT_LAYER, context) def get_arguments(self, parameters, context, feedback: QgsProcessingFeedback): - clean_data = self.parameterAsBool(parameters, self.CLEAN_DATA, context) self.clean_data_before = clean_data @@ -83,11 +83,13 @@ def get_arguments(self, parameters, context, feedback: QgsProcessingFeedback): planar = not self.input_layer_memory.crs().isGeographic() - arguments = self.prepare_arguments(simplify_percent=simplify_percent, - method=method, - lines=lines_type, - planar=planar, - join_file=self.mapshaper_join) + arguments = self.prepare_arguments( + simplify_percent=simplify_percent, + method=method, + lines=lines_type, + planar=planar, + join_file=self.mapshaper_join, + ) return arguments @@ -108,35 +110,35 @@ def createInstance(self): return SimplifyPolygonLinesAlgorithm() @staticmethod - def prepare_arguments(simplify_percent: Union[int, float, str] = 50, - lines: str = "inner", - method: str = "dp", - planar: bool = False, - join_file: str = None) -> List[str]: - + def prepare_arguments( + simplify_percent: Union[int, float, str] = 50, + lines: str = "inner", + method: str = "dp", + planar: bool = False, + join_file: str = None, + ) -> List[str]: arguments = [ - '-simplify', + "-simplify", method, - 'variable', - 'percentage', - '=', - 'TYPE == "{}" ? {} : 1'.format(lines, - float(simplify_percent) / 100), + "variable", + "percentage", + "=", + 'TYPE == "{}" ? {} : 1'.format(lines, float(simplify_percent) / 100), ] - arguments.append('keep-shapes') + arguments.append("keep-shapes") if planar: - arguments.append('planar') + arguments.append("planar") - arguments.append('-polygons') + arguments.append("-polygons") if join_file: - arguments.extend(['-join', 'largest-overlap', join_file]) + arguments.extend(["-join", "largest-overlap", join_file]) - arguments.extend(['-filter-fields', TextConstants.JOIN_FIELD_NAME]) + arguments.extend(["-filter-fields", TextConstants.JOIN_FIELD_NAME]) - arguments.extend(['-dissolve2', TextConstants.JOIN_FIELD_NAME]) + arguments.extend(["-dissolve2", TextConstants.JOIN_FIELD_NAME]) return arguments diff --git a/qmapshaper/processing/tool_to_topojson.py b/qmapshaper/processing/tool_to_topojson.py index a0b0470..c63107e 100644 --- a/qmapshaper/processing/tool_to_topojson.py +++ b/qmapshaper/processing/tool_to_topojson.py @@ -1,17 +1,25 @@ -from typing import List, Dict +from typing import Dict, List + +from qgis.core import ( + QgsField, + QgsProcessingException, + QgsProcessingFeedback, + QgsProcessingParameterField, + QgsProcessingParameterFileDestination, + QgsProcessingParameterNumber, + QgsProcessingParameterVectorLayer, +) -from qgis.core import (QgsProcessingParameterVectorLayer, QgsProcessingParameterField, - QgsProcessingFeedback, QgsProcessingParameterFileDestination, QgsField, - QgsProcessingParameterNumber, QgsProcessingException) - -from .mapshaper_algorithm import MapshaperAlgorithm -from ..classes.class_qmapshaper_file import QMapshaperGeojsonFile, QMapshaperTopoJsonFile -from ..classes.class_qmapshaper_data_preparer import QMapshaperDataPreparer from ..classes.class_qmapshaper_command_builder import QMapshaperCommandBuilder +from ..classes.class_qmapshaper_data_preparer import QMapshaperDataPreparer +from ..classes.class_qmapshaper_file import ( + QMapshaperGeojsonFile, + QMapshaperTopoJsonFile, +) +from .mapshaper_algorithm import MapshaperAlgorithm class ConvertToTopoJSONAlgorithm(MapshaperAlgorithm): - INPUT_LAYER = "Input" OUTPUT_FILE = "OutputFile" DECIMAL_NUMBERS = "DecimalNumbers" @@ -29,43 +37,45 @@ def __init__(self): self.output_layer = None def initAlgorithm(self, config=None): - self.addParameter(QgsProcessingParameterVectorLayer(self.INPUT_LAYER, "Input layer")) self.addParameter( - QgsProcessingParameterField(self.FIELDS, - "Select fields to retain", - parentLayerParameterName=self.INPUT_LAYER, - allowMultiple=True, - optional=True)) + QgsProcessingParameterField( + self.FIELDS, + "Select fields to retain", + parentLayerParameterName=self.INPUT_LAYER, + allowMultiple=True, + optional=True, + ) + ) self.addParameter( - QgsProcessingParameterNumber(self.DECIMAL_NUMBERS, - "Number of decimal places for coordinates", - defaultValue=3, - minValue=0, - maxValue=16, - type=QgsProcessingParameterNumber.Integer)) + QgsProcessingParameterNumber( + self.DECIMAL_NUMBERS, + "Number of decimal places for coordinates", + defaultValue=3, + minValue=0, + maxValue=16, + type=QgsProcessingParameterNumber.Integer, + ) + ) self.addParameter( - QgsProcessingParameterFileDestination(self.OUTPUT_FILE, - "Output TopoJSON", - fileFilter="Topojson (*.topojson)")) + QgsProcessingParameterFileDestination( + self.OUTPUT_FILE, "Output TopoJSON", fileFilter="Topojson (*.topojson)" + ) + ) def prepare_data(self, parameters, context, feedback: QgsProcessingFeedback) -> None: - self.fields_to_retain = self.parameterAsFields(parameters, self.FIELDS, context) self.process_input_layer(self.INPUT_LAYER, parameters, context, feedback) - self.result_layer_location = self.parameterAsFileOutput(parameters, self.OUTPUT_FILE, - context) + self.result_layer_location = self.parameterAsFileOutput(parameters, self.OUTPUT_FILE, context) self.mapshaper_output = self.result_layer_location - def process_input_layer(self, parameter_name, parameters, context, - feedback: QgsProcessingFeedback) -> None: - + def process_input_layer(self, parameter_name, parameters, context, feedback: QgsProcessingFeedback) -> None: layer = self.parameterAsVectorLayer(parameters, parameter_name, context) decimal_numbers = self.parameterAsInt(parameters, self.DECIMAL_NUMBERS, context) @@ -83,9 +93,7 @@ def process_input_layer(self, parameter_name, parameters, context, field: QgsField for field in fields_existing: - if field.name() not in self.fields_to_retain: - fields_to_delete.append(fields_existing.indexFromName(field.name())) if 0 < len(fields_to_delete): @@ -93,12 +101,11 @@ def process_input_layer(self, parameter_name, parameters, context, self.input_layer_memory.commitChanges() - QMapshaperDataPreparer.write_layer_with_as_geojson(layer=self.input_layer_memory, - file=self.mapshaper_input, - decimal_precision=decimal_numbers) + QMapshaperDataPreparer.write_layer_with_as_geojson( + layer=self.input_layer_memory, file=self.mapshaper_input, decimal_precision=decimal_numbers + ) def get_arguments(self, parameters, context, feedback: QgsProcessingFeedback): - arguments = self.prepare_arguments() return arguments @@ -124,20 +131,18 @@ def createInstance(self): @staticmethod def prepare_arguments() -> List[str]: - arguments = ["allow-overlaps", "allow-empty"] return arguments - def get_console_commands(self, parameters, context, - feedback: QgsProcessingFeedback) -> List[str]: - + def get_console_commands(self, parameters, context, feedback: QgsProcessingFeedback) -> List[str]: arguments = self.get_arguments(parameters, context, feedback) commands = QMapshaperCommandBuilder.prepare_console_commands( input_data_path=self.mapshaper_input, output_data_path=self.mapshaper_output, command=ConvertToTopoJSONAlgorithm.command(), - arguments=arguments) + arguments=arguments, + ) return commands diff --git a/qmapshaper/qmapshaper_plugin.py b/qmapshaper/qmapshaper_plugin.py index 931386e..3597b83 100755 --- a/qmapshaper/qmapshaper_plugin.py +++ b/qmapshaper/qmapshaper_plugin.py @@ -1,17 +1,17 @@ +import inspect import os import sys -import inspect -from qgis.core import (QgsApplication, QgsProject) -from qgis.gui import (QgisInterface) +from qgis.core import QgsApplication, QgsProject +from qgis.gui import QgisInterface from qgis.PyQt.QtGui import QIcon from qgis.PyQt.QtWidgets import QAction -from .qmapshaper_provider import QMapshaperProvider -from .utils import get_icon_path, log -from .gui.dialog_tool_interactive_simplifier import InteractiveSimplifierTool from .gui.dialog_tool_console import InteractiveConsoleTool +from .gui.dialog_tool_interactive_simplifier import InteractiveSimplifierTool +from .qmapshaper_provider import QMapshaperProvider from .text_constants import TextConstants +from .utils import get_icon_path cmd_folder = os.path.split(inspect.getfile(inspect.currentframe()))[0] @@ -19,10 +19,8 @@ sys.path.insert(0, cmd_folder) -class QMapshaperPlugin(): - +class QMapshaperPlugin: def __init__(self, iface): - self.iface: QgisInterface = iface self.provider = QMapshaperProvider() @@ -41,17 +39,21 @@ def initProcessing(self): def initGui(self): self.initProcessing() - self.add_action(icon_path=get_icon_path("qmapshaper.png"), - text=TextConstants.tool_name_interactive_simplifier, - callback=self.run_tool_interactive_simplifier, - add_to_toolbar=False, - add_to_specific_toolbar=self.toolbar) - - self.add_action(icon_path=get_icon_path("qmapshaper_console.svg"), - text=TextConstants.tool_name_interactive_console, - callback=self.run_tool_interactive_console, - add_to_toolbar=False, - add_to_specific_toolbar=self.toolbar) + self.add_action( + icon_path=get_icon_path("qmapshaper.png"), + text=TextConstants.tool_name_interactive_simplifier, + callback=self.run_tool_interactive_simplifier, + add_to_toolbar=False, + add_to_specific_toolbar=self.toolbar, + ) + + self.add_action( + icon_path=get_icon_path("qmapshaper_console.svg"), + text=TextConstants.tool_name_interactive_console, + callback=self.run_tool_interactive_console, + add_to_toolbar=False, + add_to_specific_toolbar=self.toolbar, + ) def unload(self): QgsApplication.processingRegistry().removeProvider(self.provider) @@ -64,18 +66,19 @@ def unload(self): del self.toolbar - def add_action(self, - icon_path, - text, - callback, - enabled_flag=True, - add_to_menu=True, - add_to_toolbar=True, - status_tip=None, - whats_this=None, - parent=None, - add_to_specific_toolbar=None): - + def add_action( + self, + icon_path, + text, + callback, + enabled_flag=True, + add_to_menu=True, + add_to_toolbar=True, + status_tip=None, + whats_this=None, + parent=None, + add_to_specific_toolbar=None, + ): icon = QIcon(icon_path) action = QAction(icon, text, parent) action.triggered.connect(callback) @@ -103,25 +106,21 @@ def add_action(self, return action def run_tool_interactive_simplifier(self): - dlg = InteractiveSimplifierTool(parent=self.iface.mainWindow(), iface=self.iface) result = dlg.exec_() if result == 1: - QgsProject.instance().addMapLayer(dlg.get_layer_for_project()) dlg = None def run_tool_interactive_console(self): - dlg = InteractiveConsoleTool(parent=self.iface.mainWindow(), iface=self.iface) result = dlg.exec_() if result == 1: - QgsProject.instance().addMapLayer(dlg.get_layer_for_project()) dlg = None diff --git a/qmapshaper/qmapshaper_provider.py b/qmapshaper/qmapshaper_provider.py index 38192ae..34cb499 100644 --- a/qmapshaper/qmapshaper_provider.py +++ b/qmapshaper/qmapshaper_provider.py @@ -1,47 +1,51 @@ -from pathlib import Path import configparser +from pathlib import Path -from qgis.PyQt.QtGui import QIcon -from qgis.core import QgsProcessingProvider from processing.core.ProcessingConfig import ProcessingConfig, Setting +from qgis.core import QgsProcessingProvider +from qgis.PyQt.QtGui import QIcon from .classes.class_qmapshaper_paths import QMapshaperPaths +from .processing.tool_console import ConsoleAlgorithm from .processing.tool_simplify import SimplifyAlgorithm -from .processing.tool_to_topojson import ConvertToTopoJSONAlgorithm from .processing.tool_simplify_lines import SimplifyPolygonLinesAlgorithm -from .processing.tool_console import ConsoleAlgorithm +from .processing.tool_to_topojson import ConvertToTopoJSONAlgorithm from .text_constants import TextConstants class QMapshaperProvider(QgsProcessingProvider): - def __init__(self): super().__init__() - path = Path(__file__).parent / 'metadata.txt' + path = Path(__file__).parent / "metadata.txt" config = configparser.ConfigParser() config.read(path) - self.version = config['general']['version'] + self.version = config["general"]["version"] def load(self) -> bool: - ProcessingConfig.settingIcons[self.name()] = self.icon() ProcessingConfig.addSetting( - Setting(self.name(), - TextConstants.MAPSHAPER_FOLDER, - self.tr('Mapshaper folder'), - QMapshaperPaths.guess_mapshaper_folder(), - valuetype=Setting.FOLDER)) + Setting( + self.name(), + TextConstants.MAPSHAPER_FOLDER, + self.tr("Mapshaper folder"), + QMapshaperPaths.guess_mapshaper_folder(), + valuetype=Setting.FOLDER, + ) + ) ProcessingConfig.addSetting( - Setting(self.name(), - TextConstants.MAPSHAPER_TOOL_NAME, - self.tr('Mapshaper tool name'), - QMapshaperPaths.guess_mapshaper_command_name(), - valuetype=Setting.STRING)) + Setting( + self.name(), + TextConstants.MAPSHAPER_TOOL_NAME, + self.tr("Mapshaper tool name"), + QMapshaperPaths.guess_mapshaper_command_name(), + valuetype=Setting.STRING, + ) + ) ProcessingConfig.readSettings() diff --git a/qmapshaper/text_constants.py b/qmapshaper/text_constants.py index 0167106..7a1108e 100644 --- a/qmapshaper/text_constants.py +++ b/qmapshaper/text_constants.py @@ -1,5 +1,4 @@ class TextConstants: - plugin_id = "qmapshaper" plugin_name = "QMapshaper" diff --git a/qmapshaper/utils.py b/qmapshaper/utils.py index 376e105..80273fc 100755 --- a/qmapshaper/utils.py +++ b/qmapshaper/utils.py @@ -1,8 +1,8 @@ import os -from typing import Any from pathlib import Path +from typing import Any -from qgis.core import (QgsMessageLog, QgsVectorLayer, Qgis) +from qgis.core import Qgis, QgsMessageLog, QgsVectorLayer from .text_constants import TextConstants @@ -18,7 +18,6 @@ def get_icons_folder() -> Path: def get_icon_path(file_name: str) -> str: - file: Path = get_icons_folder() / file_name return file.absolute().as_posix() @@ -30,7 +29,6 @@ def log(text: Any) -> None: def features_count_with_non_empty_geoms(layer: QgsVectorLayer) -> int: - count = 0 for feature in layer.getFeatures():