Skip to content
Snippets Groups Projects
Commit 74284be8 authored by paul.tresson_ird.fr's avatar paul.tresson_ird.fr
Browse files

reorganize plugin structure

parent 5b3e5cbe
No related branches found
No related tags found
No related merge requests found
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()
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()
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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment