From 74284be8cbb6f248caa67cd48ccf57ea03123a35 Mon Sep 17 00:00:00 2001 From: ptresson <paul.tresson@ird.fr> Date: Mon, 2 Sep 2024 13:22:47 +0200 Subject: [PATCH] reorganize plugin structure --- src/iamap/iamap.py | 234 ----------------------------------------- src/iamap/provider.py | 46 -------- src/iamap/utils/geo.py | 118 --------------------- 3 files changed, 398 deletions(-) delete mode 100644 src/iamap/iamap.py delete mode 100644 src/iamap/provider.py delete mode 100644 src/iamap/utils/geo.py diff --git a/src/iamap/iamap.py b/src/iamap/iamap.py deleted file mode 100644 index a0a36a0..0000000 --- a/src/iamap/iamap.py +++ /dev/null @@ -1,234 +0,0 @@ -import processing -from PyQt5.QtWidgets import ( - QAction, - QToolBar, - QApplication, - QDialog -) -from PyQt5.QtCore import pyqtSignal, QObject -from qgis.core import QgsApplication -from qgis.gui import QgisInterface -from .provider import IAMapProvider -from .icons import QIcon_EncoderTool, QIcon_ReductionTool, QIcon_ClusterTool, QIcon_SimilarityTool, QIcon_RandomforestTool - - -class IAMap(QObject): - execute_iamap = pyqtSignal() - - def __init__(self, iface: QgisInterface, cwd: str): - super().__init__() - self.iface = iface - self.cwd = cwd - - def initProcessing(self): - self.provider = IAMapProvider() - QgsApplication.processingRegistry().addProvider(self.provider) - - def initGui(self): - self.initProcessing() - - self.toolbar: QToolBar = self.iface.addToolBar('IAMap Toolbar') - self.toolbar.setObjectName('IAMapToolbar') - self.toolbar.setToolTip('IAMap Toolbar') - - self.actionEncoder = QAction( - QIcon_EncoderTool, - "Deep Learning Image Encoder", - self.iface.mainWindow() - ) - self.actionReducer = QAction( - QIcon_ReductionTool, - "Reduce dimensions", - self.iface.mainWindow() - ) - self.actionCluster = QAction( - QIcon_ClusterTool, - "Cluster raster", - self.iface.mainWindow() - ) - self.actionSimilarity = QAction( - QIcon_SimilarityTool, - "Compute similarity", - self.iface.mainWindow() - ) - self.actionRF = QAction( - QIcon_RandomforestTool, - "Use Random Forest algorithm", - self.iface.mainWindow() - ) - self.actionEncoder.setObjectName("mActionEncoder") - self.actionReducer.setObjectName("mActionReducer") - self.actionCluster.setObjectName("mActionCluster") - self.actionSimilarity.setObjectName("mactionSimilarity") - self.actionRF.setObjectName("mactionRF") - - self.actionEncoder.setToolTip( - "Encode a raster with a deep learning backbone") - self.actionReducer.setToolTip( - "Reduce raster dimensions") - self.actionCluster.setToolTip( - "Cluster raster") - self.actionSimilarity.setToolTip( - "Compute similarity") - self.actionRF.setToolTip( - "Use Random Forest ") - - self.actionEncoder.triggered.connect(self.encodeImage) - self.actionReducer.triggered.connect(self.reduceImage) - self.actionCluster.triggered.connect(self.clusterImage) - self.actionSimilarity.triggered.connect(self.similarityImage) - self.actionRF.triggered.connect(self.rfImage) - - self.toolbar.addAction(self.actionEncoder) - self.toolbar.addAction(self.actionReducer) - self.toolbar.addAction(self.actionCluster) - self.toolbar.addAction(self.actionSimilarity) - self.toolbar.addAction(self.actionRF) - - def unload(self): - # self.wdg_select.setVisible(False) - self.iface.removeToolBarIcon(self.actionEncoder) - self.iface.removeToolBarIcon(self.actionReducer) - self.iface.removeToolBarIcon(self.actionCluster) - self.iface.removeToolBarIcon(self.actionSimilarity) - self.iface.removeToolBarIcon(self.actionRF) - - del self.actionEncoder - del self.actionReducer - del self.actionCluster - del self.actionSimilarity - del self.actionRF - del self.toolbar - QgsApplication.processingRegistry().removeProvider(self.provider) - - def encodeImage(self): - ''' - ''' - result = processing.execAlgorithmDialog('iamap:encoder', {}) - print(result) - # Check if algorithm execution was successful - if result: - # Retrieve output parameters from the result dictionary - if 'OUTPUT_RASTER' in result: - output_raster_path = result['OUTPUT_RASTER'] - - # Add the output raster layer to the map canvas - self.iface.addRasterLayer(str(output_raster_path), 'merged features') - else: - # Handle missing or unexpected output - print('Output raster not found in algorithm result.') - else: - # Handle algorithm execution failure or cancellation - print('Algorithm execution was not successful.') - # processing.execAlgorithmDialog('', {}) - # self.close_all_dialogs() - - - def reduceImage(self): - ''' - ''' - result = processing.execAlgorithmDialog('iamap:reduction', {}) - print(result) - # Check if algorithm execution was successful - if result: - # Retrieve output parameters from the result dictionary - if 'OUTPUT_RASTER' in result: - output_raster_path = result['OUTPUT_RASTER'] - - # Add the output raster layer to the map canvas - self.iface.addRasterLayer(str(output_raster_path), 'reduced features') - else: - # Handle missing or unexpected output - print('Output raster not found in algorithm result.') - else: - # Handle algorithm execution failure or cancellation - print('Algorithm execution was not successful.') - # processing.execAlgorithmDialog('', {}) - - - def clusterImage(self): - ''' - ''' - result = processing.execAlgorithmDialog('iamap:cluster', {}) - print(result) - # Check if algorithm execution was successful - if result: - # Retrieve output parameters from the result dictionary - if 'OUTPUT_RASTER' in result: - output_raster_path = result['OUTPUT_RASTER'] - - # Add the output raster layer to the map canvas - self.iface.addRasterLayer(str(output_raster_path), 'clustering') - else: - # Handle missing or unexpected output - print('Output raster not found in algorithm result.') - else: - # Handle algorithm execution failure or cancellation - print('Algorithm execution was not successful.') - # processing.execAlgorithmDialog('', {}) - - - def similarityImage(self): - ''' - ''' - result = processing.execAlgorithmDialog('iamap:similarity', {}) - print(result) - # Check if algorithm execution was successful - if result: - # Retrieve output parameters from the result dictionary - if 'OUTPUT_RASTER' in result: - output_raster_path = result['OUTPUT_RASTER'] - - # Add the output raster layer to the map canvas - self.iface.addRasterLayer(str(output_raster_path), 'similarity map') - else: - # Handle missing or unexpected output - print('Output raster not found in algorithm result.') - else: - # Handle algorithm execution failure or cancellation - print('Algorithm execution was not successful.') - # processing.execAlgorithmDialog('', {}) - - def rfImage(self): - ''' - ''' - result = processing.execAlgorithmDialog('iamap:Random_forest', {}) - print(result) - # Check if algorithm execution was successful - if result: - # Retrieve output parameters from the result dictionary - if 'OUTPUT_RASTER' in result: - output_raster_path = result['OUTPUT_RASTER'] - - # Add the output raster layer to the map canvas - self.iface.addRasterLayer(str(output_raster_path), 'random forest map') - else: - # Handle missing or unexpected output - print('Output raster not found in algorithm result.') - else: - # Handle algorithm execution failure or cancellation - print('Algorithm execution was not successful.') - # processing.execAlgorithmDialog('', {}) - - - - def close_all_dialogs(self): - # Get the main QGIS window (QgisInterface) - qgis_main_window = self.iface.mainWindow() - - # Get all open dialogs associated with the main window - open_dialogs = qgis_main_window.findChildren(QDialog) - - # Iterate through the open dialogs and close them - for dialog in open_dialogs: - # Check if the dialog is visible (to avoid closing hidden dialogs) - if dialog.isVisible(): - # Close the dialog - dialog.close() - - - - - - - diff --git a/src/iamap/provider.py b/src/iamap/provider.py deleted file mode 100644 index 5d7797b..0000000 --- a/src/iamap/provider.py +++ /dev/null @@ -1,46 +0,0 @@ -from qgis.core import QgsProcessingProvider - -from .encoder import EncoderAlgorithm -from .reduction import ReductionAlgorithm -from .clustering import ClusterAlgorithm -from .similarity import SimilarityAlgorithm -from .random_forest import RFAlgorithm -from .icons import QIcon_EncoderTool - - -class IAMapProvider(QgsProcessingProvider): - - def loadAlgorithms(self, *args, **kwargs): - self.addAlgorithm(EncoderAlgorithm()) - self.addAlgorithm(ReductionAlgorithm()) - self.addAlgorithm(ClusterAlgorithm()) - self.addAlgorithm(SimilarityAlgorithm()) - self.addAlgorithm(RFAlgorithm()) - # add additional algorithms here - # self.addAlgorithm(MyOtherAlgorithm()) - - def id(self, *args, **kwargs): - """The ID of your plugin, used for identifying the provider. - - This string should be a unique, short, character only string, - eg "qgis" or "gdal". This string should not be localised. - """ - return 'iamap' - - def name(self, *args, **kwargs): - """The human friendly name of your plugin in Processing. - - This string should be as short as possible (e.g. "Lastools", not - "Lastools version 1.0.1 64-bit") and localised. - """ - return self.tr('IAMap') - - def icon(self): - """Should return a QIcon which is used for your provider inside - the Processing toolbox. - """ - return QIcon_EncoderTool - - def longName(self) -> str: - return self.name() - diff --git a/src/iamap/utils/geo.py b/src/iamap/utils/geo.py deleted file mode 100644 index 4d5d6f8..0000000 --- a/src/iamap/utils/geo.py +++ /dev/null @@ -1,118 +0,0 @@ -from typing import Callable, Union -import rasterio -import geopandas as gpd -import numpy as np -import warnings -from rasterio.io import MemoryFile -from rasterio.merge import merge - -def replace_nan_with_zero(array): - array[array != array] = 0 # Replace NaN values with zero - return array - -def custom_method_avg(merged_data, new_data, merged_mask, new_mask, **kwargs): - """Returns the average value pixel. - cf. https://amanbagrecha.github.io/posts/2022-07-31-merge-rasters-the-modern-way-using-python/index.html - """ - mask = np.empty_like(merged_mask, dtype="bool") - np.logical_or(merged_mask, new_mask, out=mask) - np.logical_not(mask, out=mask) - np.nanmean([merged_data, new_data], axis=0, out=merged_data, where=mask) - np.logical_not(new_mask, out=mask) - np.logical_and(merged_mask, mask, out=mask) - np.copyto(merged_data, new_data, where=mask, casting="unsafe") - -def merge_tiles( - tiles:list, - dst_path, - dtype:str = 'float32', - nodata=None, - #method:str | Callable ='first', - method: Union[str, Callable] = 'first', - ): - """ - cf. https://amanbagrecha.github.io/posts/2022-07-31-merge-rasters-the-modern-way-using-python/index.html - """ - - file_handler = [rasterio.open(ds) for ds in tiles] - extents = [ds.bounds for ds in file_handler] - # Extract individual bounds - lefts, bottoms, rights, tops = zip(*extents) - union_extent = ( - min(lefts), # Left - min(bottoms), # Bottom - max(rights), # Right - max(tops) # Top - ) - - if method == 'average': - method = custom_method_avg - - # memfile = MemoryFile() - merge(datasets=file_handler, # list of dataset objects opened in 'r' mode - bounds=union_extent, # tuple - nodata=nodata, # float - dtype=dtype, # dtype - # resampling=Resampling.nearest, - method=method, # strategy to combine overlapping rasters - # dst_path=memfile.name, # str or PathLike to save raster - dst_path=dst_path, - # dst_kwds={'blockysize':512, 'blockxsize':512} # Dictionary - ) - -def get_mean_sd_by_band(path, force_compute=True, ignore_zeros=True): - ''' - Reads metadata or computes mean and sd of each band of a geotiff. - If the metadata is not available, mean and standard deviation can be computed via numpy. - - Parameters - ---------- - path : str - path to a geotiff file - ignore_zeros : boolean - ignore zeros when computing mean and sd via numpy - - Returns - ------- - means : list - list of mean values per band - sds : list - list of standard deviation values per band - ''' - - src = rasterio.open(path) - means = [] - sds = [] - if force_compute: - for band in range(1, src.count+1): - arr = src.read(band) - arr = replace_nan_with_zero(arr) - if ignore_zeros: - mean = np.ma.masked_equal(arr, 0).mean() - sd = np.ma.masked_equal(arr, 0).std() - else: - mean = np.mean(arr) - sd = np.std(arr) - means.append(float(mean)) - sds.append(float(sd)) - - else: - for band in range(1, src.count+1): - try: - tags = src.tags(band) - if 'STATISTICS_MEAN' in tags and 'STATISTICS_STDDEV' in tags: - mean = float(tags['STATISTICS_MEAN']) - sd = float(tags['STATISTICS_STDDEV']) - means.append(mean) - sds.append(sd) - else: - raise KeyError("Statistics metadata not found.") - - except KeyError: - warnings.warn("Statistics metadata not found and computation not enabled.", UserWarning) - except Exception as e: - print(f"Error processing band {band}: {e}") - - src.close() - return means, sds - -- GitLab