Commit 6ab9cfc7 authored by Daniel Scheffler's avatar Daniel Scheffler
Browse files

Merge branch 'enhancement/update_gui' into 'master'

Enhancement/update gui

See merge request !14
parents 0aa41dc1 79000ef7
Pipeline #24075 failed with stages
in 61 minutes and 10 seconds
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# enpt_enmapboxapp, A QGIS EnMAPBox plugin providing a GUI for the EnMAP processing tools (EnPT) # enpt_enmapboxapp, A QGIS EnMAPBox plugin providing a GUI for the EnMAP processing tools (EnPT)
# #
# Copyright (C) 2019 Daniel Scheffler (GFZ Potsdam, daniel.scheffler@gfz-potsdam.de) # Copyright (C) 2018-2021 Daniel Scheffler (GFZ Potsdam, daniel.scheffler@gfz-potsdam.de)
# #
# This software was developed within the context of the EnMAP project supported # This software was developed within the context of the EnMAP project supported
# by the DLR Space Administration with funds of the German Federal Ministry of # by the DLR Space Administration with funds of the German Federal Ministry of
...@@ -33,16 +33,16 @@ from threading import Thread ...@@ -33,16 +33,16 @@ from threading import Thread
from queue import Queue from queue import Queue
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from qgis.core import \ from qgis.core import (
(QgsProcessingAlgorithm, QgsProcessingAlgorithm,
QgsProcessingParameterFile, QgsProcessingParameterFile,
QgsProcessingParameterNumber, QgsProcessingParameterNumber,
QgsProcessingParameterFolderDestination, QgsProcessingParameterFolderDestination,
QgsProcessingParameterBoolean, QgsProcessingParameterBoolean,
QgsProcessingParameterString, QgsProcessingParameterDefinition,
QgsProcessingParameterDefinition, QgsProcessingParameterRasterLayer,
QgsProcessingParameterRasterLayer QgsProcessingParameterEnum
) )
from .version import __version__ from .version import __version__
...@@ -60,7 +60,9 @@ class _EnPTBaseAlgorithm(QgsProcessingAlgorithm): ...@@ -60,7 +60,9 @@ class _EnPTBaseAlgorithm(QgsProcessingAlgorithm):
P_output_dir = 'output_dir' P_output_dir = 'output_dir'
P_working_dir = 'working_dir' P_working_dir = 'working_dir'
P_n_lines_to_append = 'n_lines_to_append' P_n_lines_to_append = 'n_lines_to_append'
P_drop_bad_bands = 'drop_bad_bands'
P_disable_progress_bars = 'disable_progress_bars' P_disable_progress_bars = 'disable_progress_bars'
P_output_format = 'output_format'
P_path_earthSunDist = 'path_earthSunDist' P_path_earthSunDist = 'path_earthSunDist'
P_path_solar_irr = 'path_solar_irr' P_path_solar_irr = 'path_solar_irr'
P_scale_factor_toa_ref = 'scale_factor_toa_ref' P_scale_factor_toa_ref = 'scale_factor_toa_ref'
...@@ -68,9 +70,6 @@ class _EnPTBaseAlgorithm(QgsProcessingAlgorithm): ...@@ -68,9 +70,6 @@ class _EnPTBaseAlgorithm(QgsProcessingAlgorithm):
P_enable_vnir_swir_coreg = 'enable_vnir_swir_coreg' P_enable_vnir_swir_coreg = 'enable_vnir_swir_coreg'
P_path_reference_image = 'path_reference_image' P_path_reference_image = 'path_reference_image'
P_enable_ac = 'enable_ac' P_enable_ac = 'enable_ac'
P_auto_download_ecmwf = 'auto_download_ecmwf'
P_enable_ice_retrieval = 'enable_ice_retrieval'
P_enable_cloud_screening = 'enable_cloud_screening'
P_scale_factor_boa_ref = 'scale_factor_boa_ref' P_scale_factor_boa_ref = 'scale_factor_boa_ref'
P_run_smile_P = 'run_smile_P' P_run_smile_P = 'run_smile_P'
P_run_deadpix_P = 'run_deadpix_P' P_run_deadpix_P = 'run_deadpix_P'
...@@ -79,6 +78,8 @@ class _EnPTBaseAlgorithm(QgsProcessingAlgorithm): ...@@ -79,6 +78,8 @@ class _EnPTBaseAlgorithm(QgsProcessingAlgorithm):
P_deadpix_P_interp_spatial = 'deadpix_P_interp_spatial' P_deadpix_P_interp_spatial = 'deadpix_P_interp_spatial'
P_ortho_resampAlg = 'ortho_resampAlg' P_ortho_resampAlg = 'ortho_resampAlg'
P_vswir_overlap_algorithm = 'vswir_overlap_algorithm' P_vswir_overlap_algorithm = 'vswir_overlap_algorithm'
P_target_projection_type = 'target_projection_type'
P_target_epsg = 'target_epsg'
# # Output parameters # # Output parameters
P_OUTPUT_RASTER = 'outraster' P_OUTPUT_RASTER = 'outraster'
...@@ -133,141 +134,152 @@ class _EnPTBaseAlgorithm(QgsProcessingAlgorithm): ...@@ -133,141 +134,152 @@ class _EnPTBaseAlgorithm(QgsProcessingAlgorithm):
super(_EnPTBaseAlgorithm, self).addParameter(param, *args, **kwargs) super(_EnPTBaseAlgorithm, self).addParameter(param, *args, **kwargs)
def initAlgorithm(self, configuration=None): def initAlgorithm(self, configuration=None):
self.addParameter(QgsProcessingParameterFile( self.addParameter(
name=self.P_json_config, description='Configuration JSON template file', QgsProcessingParameterFile(
behavior=QgsProcessingParameterFile.File, extension='json', name=self.P_json_config,
defaultValue=None, description='Configuration JSON template file',
optional=True)) behavior=QgsProcessingParameterFile.File,
extension='json',
self.addParameter(QgsProcessingParameterNumber( defaultValue=None,
name=self.P_CPUs, optional=True))
description='Number of CPU cores to be used for processing',
type=QgsProcessingParameterNumber.Integer, self.addParameter(
defaultValue=cpu_count(), minValue=0, maxValue=cpu_count(), QgsProcessingParameterNumber(
optional=True), name=self.P_CPUs,
description='Number of CPU cores to be used for processing',
type=QgsProcessingParameterNumber.Integer,
defaultValue=cpu_count(), minValue=0, maxValue=cpu_count(),
optional=True),
advanced=True) advanced=True)
self.addParameter(QgsProcessingParameterFile( self.addParameter(
name=self.P_path_l1b_enmap_image, QgsProcessingParameterFile(
description='EnMAP Level-1B image (zip-archive or root directory)')) name=self.P_path_l1b_enmap_image,
description='EnMAP Level-1B image (zip-archive or root directory)'))
self.addParameter(QgsProcessingParameterFile( self.addParameter(
name=self.P_path_l1b_enmap_image_gapfill, QgsProcessingParameterFile(
description='Adjacent EnMAP Level-1B image to be used for gap-filling (zip-archive or root directory)', name=self.P_path_l1b_enmap_image_gapfill,
optional=True), description='Adjacent EnMAP Level-1B image to be used for gap-filling (zip-archive or root directory)',
optional=True),
advanced=True) advanced=True)
self.addParameter(QgsProcessingParameterRasterLayer( self.addParameter(
name=self.P_path_dem, QgsProcessingParameterRasterLayer(
description='Input path of digital elevation model in map or sensor geometry; GDAL compatible file ' name=self.P_path_dem,
'format \n(must cover the EnMAP L1B data completely if given in map geometry or must have the ' description='Input path of digital elevation model in map or sensor geometry; GDAL compatible file '
'same \npixel dimensions like the EnMAP L1B data if given in sensor geometry)', 'format \n(must cover the EnMAP L1B data completely if given in map geometry or must have '
optional=True)) 'the same \npixel dimensions like the EnMAP L1B data if given in sensor geometry)',
optional=True))
self.addParameter(QgsProcessingParameterNumber(
name=self.P_average_elevation, self.addParameter(
description='Average elevation in meters above sea level \n' QgsProcessingParameterNumber(
'(may be provided if no DEM is available and ignored if DEM is given)', name=self.P_average_elevation,
type=QgsProcessingParameterNumber.Integer, description='Average elevation in meters above sea level \n'
defaultValue=0, '(may be provided if no DEM is available and ignored if DEM is given)',
optional=True), type=QgsProcessingParameterNumber.Integer,
defaultValue=0,
optional=True),
advanced=True) advanced=True)
self.addParameter(QgsProcessingParameterFolderDestination( self.addParameter(
name=self.P_output_dir, QgsProcessingParameterFolderDestination(
description='Output directory where processed data and log files are saved', name=self.P_output_dir,
defaultValue=self._get_default_output_dir(), description='Output directory where processed data and log files are saved',
optional=True)) defaultValue=self._get_default_output_dir(),
optional=True))
self.addParameter(QgsProcessingParameterFile(
name=self.P_working_dir, self.addParameter(
description='Directory to be used for temporary files', QgsProcessingParameterFile(
behavior=QgsProcessingParameterFile.Folder, name=self.P_working_dir,
defaultValue=None, description='Directory to be used for temporary files',
optional=True)) behavior=QgsProcessingParameterFile.Folder,
defaultValue=None,
self.addParameter(QgsProcessingParameterNumber( optional=True))
name=self.P_n_lines_to_append,
description='Number of lines to be added to the main image [if not given, use the whole imgap]', self.addParameter(
type=QgsProcessingParameterNumber.Integer, QgsProcessingParameterNumber(
defaultValue=None, name=self.P_n_lines_to_append,
optional=True), description='Number of lines to be added to the main image [if not given, use the whole imgap]',
type=QgsProcessingParameterNumber.Integer,
defaultValue=None,
optional=True),
advanced=True) advanced=True)
self.addParameter(QgsProcessingParameterBoolean( self.addParameter(
name=self.P_disable_progress_bars, QgsProcessingParameterBoolean(
description='Disable all progress bars during processing', name=self.P_drop_bad_bands,
defaultValue=True, description='Do not include bad bands (water absorption bands 1358-1453 nm / 1814-1961 nm) '
optional=True), 'in the L2A product',
defaultValue=True),
advanced=True) advanced=True)
self.addParameter(QgsProcessingParameterFile( self.addParameter(
name=self.P_path_earthSunDist, QgsProcessingParameterBoolean(
description='Input path of the earth sun distance model', name=self.P_disable_progress_bars,
defaultValue=None, description='Disable all progress bars during processing',
optional=True), defaultValue=True),
advanced=True) advanced=True)
self.addParameter(QgsProcessingParameterFile( self.addParameter(
name=self.P_path_solar_irr, QgsProcessingParameterEnum(
description='Input path of the solar irradiance model', name=self.P_output_format,
defaultValue=None, description="Output format (file format of all raster output files).",
optional=True), options=['GTiff', 'ENVI'],
defaultValue=0),
advanced=True) advanced=True)
self.addParameter(QgsProcessingParameterNumber( # output_interleave?
name=self.P_scale_factor_toa_ref,
description='Scale factor to be applied to TOA reflectance result',
type=QgsProcessingParameterNumber.Integer,
defaultValue=10000,
optional=True),
advanced=True)
self.addParameter(QgsProcessingParameterBoolean( self.addParameter(
name=self.P_enable_keystone_correction, QgsProcessingParameterFile(
description='Keystone correction', name=self.P_path_earthSunDist,
defaultValue=False, description='Input path of the earth sun distance model',
optional=True)) defaultValue=None,
optional=True),
self.addParameter(QgsProcessingParameterBoolean(
name=self.P_enable_vnir_swir_coreg,
description='VNIR/SWIR co-registration',
defaultValue=False,
optional=True))
self.addParameter(QgsProcessingParameterRasterLayer(
name=self.P_path_reference_image,
description='Reference image for co-registration.',
defaultValue=None,
optional=True))
self.addParameter(QgsProcessingParameterBoolean(
name=self.P_enable_ac,
description='Enable atmospheric correction using SICOR algorithm',
defaultValue=True,
optional=True))
self.addParameter(QgsProcessingParameterBoolean(
name=self.P_auto_download_ecmwf,
description='Automatically download ECMWF data for atmospheric correction',
defaultValue=False,
optional=True),
advanced=True) advanced=True)
self.addParameter(QgsProcessingParameterBoolean( self.addParameter(
name=self.P_enable_ice_retrieval, QgsProcessingParameterFile(
description='Enable ice retrieval (increases accuracy of water vapour retrieval)', name=self.P_path_solar_irr,
defaultValue=True, description='Input path of the solar irradiance model',
optional=True), defaultValue=None,
optional=True),
advanced=True) advanced=True)
self.addParameter(QgsProcessingParameterBoolean( self.addParameter(
name=self.P_enable_cloud_screening, QgsProcessingParameterNumber(
description='Cloud screening during atmospheric correction', name=self.P_scale_factor_toa_ref,
defaultValue=False, description='Scale factor to be applied to TOA reflectance result',
optional=True), type=QgsProcessingParameterNumber.Integer,
defaultValue=10000),
advanced=True) advanced=True)
self.addParameter(
QgsProcessingParameterBoolean(
name=self.P_enable_keystone_correction,
description='Keystone correction',
defaultValue=False))
self.addParameter(
QgsProcessingParameterBoolean(
name=self.P_enable_vnir_swir_coreg,
description='VNIR/SWIR co-registration',
defaultValue=False))
self.addParameter(
QgsProcessingParameterRasterLayer(
name=self.P_path_reference_image,
description='Reference image for co-registration.',
defaultValue=None,
optional=True))
self.addParameter(
QgsProcessingParameterBoolean(
name=self.P_enable_ac,
description='Enable atmospheric correction using SICOR algorithm',
defaultValue=True))
self.addParameter(QgsProcessingParameterNumber( self.addParameter(QgsProcessingParameterNumber(
name=self.P_scale_factor_boa_ref, name=self.P_scale_factor_boa_ref,
description='Scale factor to be applied to BOA reflectance result', description='Scale factor to be applied to BOA reflectance result',
...@@ -276,61 +288,81 @@ class _EnPTBaseAlgorithm(QgsProcessingAlgorithm): ...@@ -276,61 +288,81 @@ class _EnPTBaseAlgorithm(QgsProcessingAlgorithm):
optional=True), optional=True),
advanced=True) advanced=True)
self.addParameter(QgsProcessingParameterBoolean( self.addParameter(
name=self.P_run_smile_P, QgsProcessingParameterBoolean(
description='Smile detection and correction (provider smile coefficients are ignored)', name=self.P_run_smile_P,
defaultValue=False, description='Smile detection and correction (provider smile coefficients are ignored)',
optional=True)) defaultValue=False))
self.addParameter(QgsProcessingParameterBoolean( self.addParameter(
name=self.P_run_deadpix_P, QgsProcessingParameterBoolean(
description='Dead pixel correction', name=self.P_run_deadpix_P,
defaultValue=True, description='Dead pixel correction',
optional=True)) defaultValue=True))
self.addParameter(QgsProcessingParameterString( self.addParameter(
name=self.P_deadpix_P_algorithm, QgsProcessingParameterEnum(
description="Algorithm for dead pixel correction ('spectral' or 'spatial')", name=self.P_deadpix_P_algorithm,
defaultValue='spectral', description="Algorithm for dead pixel correction",
multiLine=False, options=['spectral', 'spatial'],
optional=True), defaultValue=0),
advanced=True) advanced=True)
self.addParameter(QgsProcessingParameterString( self.addParameter(
name=self.P_deadpix_P_interp_spectral, QgsProcessingParameterEnum(
description="Spectral interpolation algorithm to be used during dead pixel correction " name=self.P_deadpix_P_interp_spectral,
"('linear', 'bilinear', 'cubic', 'spline')", description="Spectral interpolation algorithm to be used during dead pixel correction ",
defaultValue='linear', options=['linear', 'bilinear', 'cubic', 'spline'],
multiLine=False, defaultValue=0),
optional=True),
advanced=True) advanced=True)
self.addParameter(QgsProcessingParameterString( self.addParameter(
name=self.P_deadpix_P_interp_spatial, QgsProcessingParameterEnum(
description="Spatial interpolation algorithm to be used during dead pixel correction " name=self.P_deadpix_P_interp_spatial,
"('linear', 'bilinear', 'cubic', 'spline')", description="Spatial interpolation algorithm to be used during dead pixel correction",
defaultValue='linear', options=['linear', 'bilinear', 'cubic', 'spline'],
multiLine=False, defaultValue=0),
optional=True),
advanced=True) advanced=True)
self.addParameter(QgsProcessingParameterString( self.addParameter(
name=self.P_ortho_resampAlg, QgsProcessingParameterEnum(
description="Ortho-rectification resampling algorithm ('nearest', 'bilinear', 'gauss')", name=self.P_ortho_resampAlg,
defaultValue='bilinear', description="Ortho-rectification resampling algorithm",
multiLine=False, options=['nearest', 'bilinear', 'gauss'],
optional=True), defaultValue=2),
advanced=True) advanced=True)
self.addParameter(QgsProcessingParameterString( self.addParameter(
name=self.P_vswir_overlap_algorithm, QgsProcessingParameterEnum(
description="Algorithm specifying how to deal with the spectral bands in the VNIR/SWIR spectral overlap " name=self.P_vswir_overlap_algorithm,
"region ('order_by_wvl', 'average', 'vnir_only', 'swir_only')", description="Algorithm specifying how to deal with the spectral bands "
defaultValue='swir_only', "in the VNIR/SWIR spectral overlap region",
multiLine=False, options=['VNIR and SWIR bands, order by wavelength', 'average VNIR and SWIR bands',
optional=True), 'VNIR bands only', 'SWIR bands only'],
defaultValue=3),
advanced=True)
self.addParameter(
QgsProcessingParameterEnum(
self.P_target_projection_type,
description='Projection type of the raster output files',
options=['UTM', 'Geographic'],
defaultValue=0),
advanced=True) advanced=True)
self.addParameter(
QgsProcessingParameterNumber(
name=self.P_target_epsg,
description='Custom EPSG code of the target projection (overrides target_projection_type)',
type=QgsProcessingParameterNumber.Integer,
defaultValue=None,
optional=True),
advanced=True)
# TODO:
# "target_coord_grid": "None" /*custom target coordinate grid to which the output is resampled
# ([x0, x1, y0, y1], e.g., [0, 30, 0, 30])*/
@staticmethod @staticmethod
def shortHelpString(*args, **kwargs): def shortHelpString(*args, **kwargs):
"""Example: """Example:
...@@ -367,7 +399,6 @@ class _EnPTBaseAlgorithm(QgsProcessingAlgorithm): ...@@ -367,7 +399,6 @@ class _EnPTBaseAlgorithm(QgsProcessingAlgorithm):
:param cmd: a normal shell command including parameters :param cmd: a normal shell command including parameters
""" """
def reader(pipe, queue): def reader(pipe, queue):
try: try:
with pipe: with pipe:
......
...@@ -75,6 +75,18 @@ class EnPTAlgorithm(_EnPTBaseAlgorithm): ...@@ -75,6 +75,18 @@ class EnPTAlgorithm(_EnPTBaseAlgorithm):
raise ImportError("enpt", "EnPT must be installed into the QGIS Python environment " raise ImportError("enpt", "EnPT must be installed into the QGIS Python environment "
"when calling 'EnPTAlgorithm'.") "when calling 'EnPTAlgorithm'.")
# replace Enum parameters with corresponding strings
for n, opts in [
('output_format', {0: 'GTiff', 1: 'ENVI'}),
('deadpix_P_algorithm', {0: 'spectral', 1: 'spatial'}),
('deadpix_P_interp_spectral', {0: 'linear', 1: 'bilinear', 2: 'cubic', 3: 'spline'}),
('deadpix_P_interp_spatial', {0: 'linear', 1: 'bilinear', 2: 'cubic', 3: 'spline'}),
('ortho_resampAlg', {0: 'nearest', 1: 'bilinear', 2: 'gauss'}),
('vswir_overlap_algorithm', {0: 'order_by_wvl', 1: 'average', 2: 'vnir_only', 3: 'swir_only'}),
('target_projection_type', {0: 'UTM', 1: 'Geographic'}),
]:
parameters[n] = opts[parameters[n]]
feedback.pushInfo("The log messages of the EnMAP processing tool are written to the *.log file " feedback.pushInfo("The log messages of the EnMAP processing tool are written to the *.log file "
"in the specified output folder.") "in the specified output folder.")
...@@ -109,23 +121,30 @@ class EnPTAlgorithm(_EnPTBaseAlgorithm): ...@@ -109,23 +121,30 @@ class EnPTAlgorithm(_EnPTBaseAlgorithm):
) )
# list output dir # list output dir
outdir = parameters['output_dir'] if 'output_dir' in parameters:
outraster_matches = glob(os.path.join(outdir, '*', '*SPECTRAL_IMAGE.GEOTIFF')) outdir = parameters['output_dir']
outraster = outraster_matches[0] if len(outraster_matches) > 0 else None outraster_matches = glob(os.path.join(outdir, '*', '*SPECTRAL_IMAGE.GEOTIFF'))
outraster = outraster_matches[0] if len(outraster_matches) > 0 else None
feedback.pushInfo("The output folder '%s' contains:\n" % outdir)
feedback.pushCommandInfo('\n'.join([os.path.basename(f) for f in os.listdir(outdir)]) + '\n') feedback.pushInfo("The output folder '%s' contains:\n" % outdir)
feedback.pushCommandInfo('\n'.join([os.path.basename(f) for f in os.listdir(outdir)]) + '\n')
if outraster:
subdir = os.path.dirname(outraster_matches[0]) if outraster:
feedback.pushInfo("...where the folder '%s' contains:\n" % os.path.dirname(subdir)) subdir = os.path.dirname(outraster_matches[0])
feedback.pushCommandInfo('\n'.join([os.path.basename(f) for f in os.listdir(subdir)]) + '\n') feedback.pushInfo("...where the folder '%s' contains:\n" % os.path.dirname(subdir))
feedback.pushCommandInfo('\n'.join([os.path.basename(f) for f in os.listdir(subdir)]) + '\n')
# return outputs
return { # return outputs
'success': True, return {
self.P_OUTPUT_RASTER: outraster, 'success': True,
# self.P_OUTPUT_VECTOR: parameters[self.P_OUTPUT_RASTER], self.P_OUTPUT_RASTER: outraster,
# self.P_OUTPUT_FILE: parameters[self.P_OUTPUT_RASTER], # self.P_OUTPUT_VECTOR: parameters[self.P_OUTPUT_RASTER],
self.P_OUTPUT_FOLDER: outdir # self.P_OUTPUT_FILE: parameters[self.P_OUTPUT_RASTER],
} self.P_OUTPUT_FOLDER: outdir
}
else:
feedback.pushInfo('The output was skipped according to user setting.')
# return outputs
return {'success': True}
...@@ -92,8 +92,14 @@ class EnPTEnMAPBoxApp(EnMAPBoxApplication): ...@@ -92,8 +92,14 @@ class EnPTEnMAPBoxApp(EnMAPBoxApplication):
return menu return menu
def showAboutDialog(self): def showAboutDialog(self):
QMessageBox.information(None, self.name, QMessageBox.information(
'Version {}'.format(self.version)) None, self.name,
'EnPT (the EnMAP processing tool) is an automated pre-processing pipeline for the new EnMAP hyperspectral '
'satellite data. It provides free and open-source features to transform EnMAP Level-1B data to Level-2A. '
'The code has been developed at the German Research Centre for Geosciences Potsdam (GFZ) as an alternative '
'to the processing chain of the EnMAP Ground Segment.\n'
'\n'
'Installed GUI version: {}'.format(self.version))
def processingAlgorithms(self): def processingAlgorithms(self):
""" """
......
...@@ -170,6 +170,18 @@ class ExternalEnPTAlgorithm(_EnPTBaseAlgorithm): ...