text: # -*- coding: utf-8 -*-
  """
  /***************************************************************************
   QRVT
                                   A QGIS plugin
   RVT plugin lets you compute different visualizations from raster DEM.
   Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                                -------------------
          begin                : 2020-10-12
          git sha              : $Format:%H$
          copyright            : (C) 2020 by Research Centre of the Slovenian Academy of Sciences and Arts and
                                 University of Ljubljana, Faculty of Civil and Geodetic Engineering
          email                : ziga.kokalj@zrc-sazu.si
   ***************************************************************************/
  
  /***************************************************************************
   *                                                                         *
   *   This program is free software; you can redistribute it and/or modify  *
   *   it under the terms of the GNU General Public License as published by  *
   *   the Free Software Foundation; either version 2 of the License, or     *
   *   (at your option) any later version.                                   *
   *                                                                         *
   ***************************************************************************/
  """
  import importlib
  import time
  import subprocess
  import threading
  import json
  import os
  import sys
  import webbrowser
  import PyQt5
  from PyQt5.QtCore import QSettings, QTranslator, qVersion, QCoreApplication, QFile, QFileInfo, Qt, QThread, QRunnable, \
      QThreadPool
  from PyQt5.QtGui import QIcon, QMovie, QPixmap, QPalette, QColor, QPainterPath
  from PyQt5.QtWidgets import QAction, QFileDialog, QGroupBox, QLineEdit, QCheckBox, QComboBox, QWidget, QLabel, \
      QProgressBar, QApplication, QMessageBox, QErrorMessage, QDialog, QDesktopWidget
  from PyQt5 import uic
  
  from qgis.core import QgsProject, QgsRasterLayer, QgsTask, QgsApplication, Qgis
  
  from osgeo import gdal
  import numpy as np
  
  # Initialize Qt resources from file resources.py
  from .resources import *
  
  # Import the code for the dialog
  from .qrvt_dialog import QRVTDialog
  
  sys.path.append(os.path.dirname(__file__))
  import rvt.default
  
  importlib.reload(rvt.default)
  import rvt.blend
  
  importlib.reload(rvt.blend)
  import rvt.blend_func
  
  importlib.reload(rvt.blend_func)
  import rvt.vis
  
  importlib.reload(rvt.vis)
  from processing_provider.provider import Provider
  
  
  class LoadingScreenDlg:
      """Loading screen animation."""
  
      def __init__(self, gif_path):
          self.dlg = QDialog()
          self.dlg.setWindowTitle("Loading")
          self.dlg.setWindowModality(False)
          self.dlg.setFixedSize(200, 200)
          self.dlg.setWindowFlags(Qt.X11BypassWindowManagerHint | Qt.CustomizeWindowHint)
          pal = QPalette()
          role = QPalette.Background
          pal.setColor(role, QColor(255, 255, 255))
          self.dlg.setPalette(pal)
          self.label_animation = QLabel(self.dlg)
          self.movie = QMovie(gif_path)
          self.label_animation.setMovie(self.movie)
  
      def start_animation(self):
          self.movie.start()
          self.dlg.show()
  
      def stop_animation(self):
          self.movie.stop()
          self.dlg.done(0)
  
  
  class AboutDlg:
      """About dialog."""
  
      def __init__(self):
          self.dlg = QDialog()
          uic.loadUi(os.path.join(os.path.dirname(__file__), 'qrvt_dialog_about.ui'), self.dlg)
          self.dlg.setWindowTitle("About")
          self.dlg.setWindowFlags(Qt.X11BypassWindowManagerHint | Qt.WindowStaysOnTopHint | Qt.CustomizeWindowHint)
          self.dlg.setWindowModality(False)
  
          # if close button clicked
          self.dlg.button_close.clicked.connect(self.dlg.close)
  
          # if report a bug button clicked
          self.dlg.button_report_bug.clicked.connect(lambda: self.button_report_bug_clicked())
          self.dlg.exec_()
  
      def button_report_bug_clicked(self):
          webbrowser.open('https://github.com/EarthObservation/rvt-qgis/issues')
  
  
  class QRVT:
      """QGIS Plugin Implementation."""
  
      def __init__(self, iface):
          """Constructor.
  
          :param iface: An interface instance that will be passed to this class
              which provides the hook by which you can manipulate the QGIS
              application at run time.
          :type iface: QgsInterface
          """
          # Save reference to the QGIS interface
          self.iface = iface
          # initialize plugin directory
          self.plugin_dir = os.path.dirname(__file__)
          # initialize locale
          locale = QSettings().value('locale/userLocale')[0:2]
          locale_path = os.path.join(
              self.plugin_dir,
              'i18n',
              'QRVT_{}.qm'.format(locale))
  
          if os.path.exists(locale_path):
              self.translator = QTranslator()
              self.translator.load(locale_path)
              QCoreApplication.installTranslator(self.translator)
  
          # Create the dialog (after translation) and keep reference
          self.dlg = QRVTDialog()
  
          # resize window
          # try:
          #     # self.dlg.adjustSize()  # resize dialog to fit content, doesn't work as before because of scroll area
          #     # size_dlg = self.dlg.size()  # get curr size
          #     size_screen = QDesktopWidget().screenGeometry(-1)  # get screen size
          #     self.dlg.resize(size_screen.width() * 2 / 3, size_screen.height() * 4 / 5)
          # except:
          #     pass
  
          # Declare instance attributes
          self.actions = []
          self.menu = self.tr(u'&Relief Visualization Toolbox')
          if self.iface:
              self.toolbar = self.iface.addToolBar(u'Relief Visualization Toolbox')
              self.toolbar.setObjectName(u'Relief Visualization Toolbox')
  
          self.cwd = os.getcwd()
  
          self.rvt_select_input = {}  # qgis DEM rasters, available in rvt select box
  
          # if a layer is added / removed update the available raster layers in the
          # selection dialog
          QgsProject.instance().layersAdded.connect(lambda: self.load_raster_layers())
          QgsProject.instance().layersRemoved.connect(lambda: self.load_raster_layers())
  
          # read settings from .json file and fill visualizations dialog
          self.default = rvt.default.DefaultValues()
          self.default_settings_path = os.path.abspath(os.path.join(self.plugin_dir, "settings", "default_settings.json"))
          if os.path.isfile(self.default_settings_path):  # if default_settings.json exists
              self.default.read_default_from_file(self.default_settings_path)  # load values in dialog
          else:  # if doesn't exist
              if not os.path.exists(os.path.dirname(self.default_settings_path)):  # if dir settings doesn't exists
                  os.makedirs(os.path.dirname(self.default_settings_path))  # create settings dir
              self.default.save_default_to_file(self.default_settings_path)  # create default_settings.json
  
          # blender
          # read combinations
          self.default_blender_combinations_path = os.path.abspath(os.path.join(self.plugin_dir, "settings",
                                                                                "default_blender_combinations.json"))
          self.default_blender_combinations = rvt.blend.BlenderCombinations()
          self.default_blender_combinations.read_from_file(self.default_blender_combinations_path)
          self.load_combinations2cb()  # loads combinations to combo box
          self.combination = rvt.blend.BlenderCombination()  # current combination
  
          # dlg layers
          self.dlg_combo_vis_list = [self.dlg.combo1_vis, self.dlg.combo2_vis, self.dlg.combo3_vis,
                                     self.dlg.combo4_vis, self.dlg.combo5_vis]
          self.dlg_combo_norm_list = [self.dlg.combo1_norm, self.dlg.combo2_norm, self.dlg.combo3_norm,
                                      self.dlg.combo4_norm, self.dlg.combo5_norm]
          self.dlg_line_min_list = [self.dlg.line1_min, self.dlg.line2_min, self.dlg.line3_min, self.dlg.line4_min,
                                    self.dlg.line5_min]
          self.dlg_line_max_list = [self.dlg.line1_max, self.dlg.line2_max, self.dlg.line3_max, self.dlg.line4_max,
                                    self.dlg.line5_max]
          self.dlg_combo_blend_mode_list = [self.dlg.combo1_blend_mode, self.dlg.combo2_blend_mode,
                                            self.dlg.combo3_blend_mode, self.dlg.combo4_blend_mode,
                                            self.dlg.combo5_blend_mode]
          self.dlg_scroll_opacity_list = [self.dlg.scroll1_opacity, self.dlg.scroll2_opacity, self.dlg.scroll3_opacity,
                                          self.dlg.scroll4_opacity, self.dlg.scroll5_opacity]
          self.dlg_label_opacity_list = [self.dlg.label1_opacity, self.dlg.label2_opacity, self.dlg.label3_opacity,
                                         self.dlg.label4_opacity, self.dlg.label5_opacity]
  
          # load terrains settings
          self.terrains_settings = rvt.blend.TerrainsSettings()
          self.terrains_settings_path = os.path.abspath(os.path.join(self.plugin_dir, "settings",
                                                                     "default_terrains_settings.json"))
          self.terrains_settings.read_from_file(self.terrains_settings_path)
          self.load_terrains_settings2dlg()
          self.terrain_settings = rvt.blend.TerrainSettings()
  
          # on init (first) load blender combination
          self.load_blender_combination()
          self.combo_vis_check()
          self.slider_opacity_label_check()
  
          self.load_default2dlg()  # load values in dialog
  
          # loading gif path
          self.gif_path = os.path.join(self.plugin_dir, "loading.gif")
  
          # processing provider
          self.provider = None
  
          # task manager
          self.tm = QgsApplication.taskManager()
  
          # is already calculating something
          self.is_calculating = False
  
          # add all gui events (button clicks, cb state changes, ...)
          self.add_gui_events()
  
          # load saved plugin size if exists
          self.plugin_size_json_path = os.path.abspath(os.path.join(self.plugin_dir, "settings", "plugin_size.json"))
          if os.path.isfile(self.plugin_size_json_path):
              self.load_plugin_size(self.plugin_size_json_path)
  
      def initProcessing(self):
          self.provider = Provider()
          QgsApplication.processingRegistry().addProvider(self.provider)
  
      # noinspection PyMethodMayBeStatic
      def tr(self, message):
          """Get the translation for a string using Qt translation API.
  
          We implement this ourselves since we do not inherit QObject.
  
          :param message: String for translation.
          :type message: str, QString
  
          :returns: Translated version of message.
          :rtype: QString
          """
          # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
          return QCoreApplication.translate('QRVT', message)
  
      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 a toolbar icon to the toolbar.
  
          :param icon_path: Path to the icon for this action. Can be a resource
              path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
          :type icon_path: str
  
          :param text: Text that should be shown in menu items for this action.
          :type text: str
  
          :param callback: Function to be called when the action is triggered.
          :type callback: function
  
          :param enabled_flag: A flag indicating if the action should be enabled
              by default. Defaults to True.
          :type enabled_flag: bool
  
          :param add_to_menu: Flag indicating whether the action should also
              be added to the menu. Defaults to True.
          :type add_to_menu: bool
  
          :param add_to_toolbar: Flag indicating whether the action should also
              be added to the toolbar. Defaults to True.
          :type add_to_toolbar: bool
  
          :param status_tip: Optional text to show in a popup when mouse pointer
              hovers over the action.
          :type status_tip: str
  
          :param parent: Parent widget for the new action. Defaults None.
          :type parent: QWidget
  
          :param whats_this: Optional text to show in the status bar when the
              mouse pointer hovers over the action.
  
          :returns: The action that was created. Note that the action is also
              added to self.actions list.
          :rtype: QAction
          """
  
          icon = QIcon(icon_path)
          action = QAction(icon, text, parent)
          action.triggered.connect(callback)
          action.setEnabled(enabled_flag)
  
          if status_tip is not None:
              action.setStatusTip(status_tip)
  
          if whats_this is not None:
              action.setWhatsThis(whats_this)
  
          if add_to_toolbar:
              # Adds plugin icon to Plugins toolbar
              self.iface.addToolBarIcon(action)
  
          if add_to_menu:
              self.iface.addPluginToRasterMenu(
                  self.menu,
                  action)
  
          self.actions.append(action)
  
          return action
  
      def initGui(self):
          """Create the menu entries and toolbar icons inside the QGIS GUI."""
          icon_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "icon.png"))
          self.add_action(
              icon_path,
              text=self.tr(u'Relief Visualization Toolbox'),
              callback=self.run,
              parent=self.iface.mainWindow())
          self.initProcessing()
  
      def unload(self):
          """Removes the plugin menu item and icon from QGIS GUI."""
          QgsApplication.processingRegistry().removeProvider(self.provider)
          for action in self.actions:
              self.iface.removePluginRasterMenu(self.menu,
                                                action)
              self.iface.removeToolBarIcon(action)
  
      def run(self):
          """Run method that performs all the real work"""
          self.load_raster_layers()
  
          # show the dialog
          self.dlg.show()
  
      def add_gui_events(self):
          # about button clicked
          self.dlg.button_about.clicked.connect(lambda: AboutDlg())
  
          # save to rast loc checkbox
          self.dlg.check_sav_rast_loc.stateChanged.connect(lambda: self.checkbox_save_to_rast_loc())
  
          # save to pressed
          self.dlg.button_save_to.clicked.connect(lambda: self.save_to_clicked())
  
          # close the application if the exit button is pressed
          self.dlg.button_close.clicked.connect(self.dlg.close)
          self.dlg.button_close_2.clicked.connect(self.dlg.close)
  
          self.dlg.button_select_all.clicked.connect(lambda: self.activate_all_visualizations())
          self.dlg.button_select_none.clicked.connect(lambda: self.deactivate_all_visualizations())
  
          self.dlg.button_sel_all_dem.clicked.connect(lambda: self.activate_all_dem())
          self.dlg.button_sel_none_dem.clicked.connect(lambda: self.deactivate_all_dem())
  
          # VISUALIZATIONS
          # start button pressed
          self.dlg.button_start.clicked.connect(lambda: self.compute_visualizations_clicked())
          self.dlg.button_start.clicked.connect(
              lambda: self.save_plugin_size(self.plugin_size_json_path))  # save plugin size
          # check float 8bit checkbox changes
          self.check_checkbox_float_8bit_change()
          # check svf noise rem
          self.dlg.check_svf_noise.stateChanged.connect(lambda: self.checkbox_svf_noise_check())
  
          # BLENDER
          # check if blender combination changed
          self.check_combination_change()
  
          # if tab widget changes set visualization checkboxes from blender layers
          self.dlg.tab_widget.currentChanged.connect(lambda: self.combo_vis_check())
  
          # preset terrain values
          self.check_preset_terrain_settings_change()
  
          # check if any value in blender layers dialog changed
          self.check_dlg_blender_layers_change()
  
          # check float 8bit checkbox changes in blender
          self.check_blender_checkbox_float_8bit_change()
  
          # add combination button clicked
          self.dlg.buttton_comb_add.clicked.connect(lambda: self.add_combination_clicked())
  
          # remove combination button clicked
          self.dlg.button_comb_rem.clicked.connect(lambda: self.remove_combination_clicked())
  
          # save to combination button clicked
          self.dlg.button_save_comb.clicked.connect(lambda: self.save_combination_to_clicked())
  
          # load from combination button clicked
          self.dlg.button_load_comb.clicked.connect(lambda: self.load_combination_from_clicked())
  
          # blend images button clicked
          self.dlg.button_blend.clicked.connect(lambda: self.compute_blended_image_clicked())
          self.dlg.button_blend.clicked.connect(
              lambda: self.save_plugin_size(self.plugin_size_json_path))  # save plugin size
  
          # OTHER
          # Cut-off button clicked
          self.dlg.button_cutoff.clicked.connect(lambda: self.compute_cut_off_norm_8bit_clicked())
          # Fill no-data
          self.check_fill_no_data_other_fill_method_combo_change()
          self.dlg.button_fill_no_data.clicked.connect(lambda: self.compute_fill_no_data_clicked())
  
      def load_raster_layers(self):
          rvt_select_input = {}
          self.dlg.select_input_files.clear()
  
          for layer in QgsProject.instance().mapLayers().values():
              # If layer is a raster and it is not a multiband type
              if layer.type() == 1 and layer.bandCount() == 1:
                  layer_name = layer.name()
                  layer_path = layer.dataProvider().dataSourceUri()
                  self.dlg.select_input_files.addItem(layer_name)
                  rvt_select_input[layer_name] = layer_path
          self.rvt_select_input = rvt_select_input
  
      def activate_all_dem(self):
          self.dlg.select_input_files.selectAllOptions()
  
      def deactivate_all_dem(self):
          self.dlg.select_input_files.deselectAllOptions()
  
      def checkbox_save_to_rast_loc(self):
          """"Check box save to raster location state changed."""
          if self.dlg.check_sav_rast_loc.isChecked():
              self.dlg.line_save_loc.setEnabled(False)
              self.dlg.button_save_to.setEnabled(False)
          else:
              self.dlg.line_save_loc.setEnabled(True)
              self.dlg.button_save_to.setEnabled(True)
  
      def save_to_clicked(self):
          """Save to button clicked"""
          save_dir = str(QFileDialog.getExistingDirectory(self.dlg, 'Select a directory'))
          self.dlg.line_save_loc.setText(save_dir)
  
      def checkbox_svf_noise_check(self):
          if self.dlg.check_svf_noise.isChecked():
              self.dlg.combo_svf_noise.setEnabled(True)
          else:
              self.dlg.combo_svf_noise.setEnabled(False)
  
      def activate_all_visualizations(self):
          """Activate all visualizations."""
          self.dlg.group_hillshade.setChecked(True)
          self.dlg.group_hillshade_multiple.setChecked(True)
          self.dlg.group_slope.setChecked(True)
          self.dlg.group_local_relief.setChecked(True)
          self.dlg.group_sky_view.setChecked(True)
          self.dlg.group_anisotropic.setChecked(True)
          self.dlg.group_openess_pos.setChecked(True)
          self.dlg.group_openess_neg.setChecked(True)
          # self.dlg.group_illumination.setChecked(True)
          self.dlg.group_local_dominance.setChecked(True)
          self.dlg.group_multi_relief.setChecked(True)
          self.dlg.group_multi_scale_top_pos.setChecked(True)
  
      def deactivate_all_visualizations(self):
          """Deactivate all visualizations."""
          self.dlg.group_hillshade.setChecked(False)
          self.dlg.group_hillshade_multiple.setChecked(False)
          self.dlg.group_slope.setChecked(False)
          self.dlg.group_local_relief.setChecked(False)
          self.dlg.group_sky_view.setChecked(False)
          self.dlg.group_anisotropic.setChecked(False)
          self.dlg.group_openess_pos.setChecked(False)
          self.dlg.group_openess_neg.setChecked(False)
          # self.dlg.group_illumination.setChecked(False)
          self.dlg.group_local_dominance.setChecked(False)
          self.dlg.group_multi_relief.setChecked(False)
          self.dlg.group_multi_scale_top_pos.setChecked(False)
  
      def check_dlg_blender_layers_change(self):
          """Check if any layer (combo box, line edit, scroll slider) in blender layers dialog changed
          and do events."""
          for i_layer in range(5):
              # vis combo boxes
              self.dlg_combo_vis_list[i_layer].currentIndexChanged.connect(lambda: self.combo_vis_check())
              self.dlg_combo_vis_list[i_layer].currentIndexChanged.connect(lambda: self.check_dlg_comb_default_combs())
              # norm combo boxes
              self.dlg_combo_norm_list[i_layer].currentIndexChanged.connect(lambda: self.check_dlg_comb_default_combs())
              # min lines
              self.dlg_line_min_list[i_layer].textChanged.connect(lambda: self.check_dlg_comb_default_combs())
              # max lines
              self.dlg_line_max_list[i_layer].textChanged.connect(lambda: self.check_dlg_comb_default_combs())
              # blending mode combo boxes
              self.dlg_combo_blend_mode_list[i_layer].currentIndexChanged.connect(
                  lambda: self.check_dlg_comb_default_combs())
              # opacity slider
              self.dlg_scroll_opacity_list[i_layer].valueChanged.connect(lambda: self.slider_opacity_label_check())
              self.dlg_scroll_opacity_list[i_layer].valueChanged.connect(lambda: self.check_dlg_comb_default_combs())
  
      def check_preset_terrain_settings_change(self):
          self.dlg.chech_terrain_preset.stateChanged.connect(lambda: self.load_terrain2dlg())
          self.dlg.combo_terrains.currentIndexChanged.connect(lambda: self.load_terrain2dlg())
  
      def load_terrain2dlg(self):
          # checkbox use preset values for terrain type
          if self.dlg.chech_terrain_preset.checkState():
              self.dlg.combo_terrains.setEnabled(True)  # enable combobox
              selected_terrain = str(self.dlg.combo_terrains.currentText())
              terrain_setting = self.terrains_settings.select_terrain_settings_by_name(selected_terrain)
              self.load_dlg2combination()
              self.load_dlg2default()
              terrain_setting.apply_terrain(self.default, self.combination)
              self.load_combination2dlg(self.combination, terrain_bool=True)
              self.load_default2dlg()
          else:
              self.dlg.combo_terrains.setEnabled(False)  # disable combobox
          self.load_dlg2terrain()
          self.load_dlg2default()
          self.load_dlg2combination()
  
      def slider_opacity_label_check(self):
          """Change scroll slider label value according to scroll slider value."""
          for i_layer in range(5):
              self.dlg_label_opacity_list[i_layer].setText(str(self.dlg_scroll_opacity_list[i_layer].value()))
  
      def load_dlg2combination(self):
          """Get combination (rvt.blend.BlenderCombination()) from blender dlg."""
          combination = rvt.blend.BlenderCombination()
          for i_layer in range(5):
              visualization = str(self.dlg_combo_vis_list[i_layer].currentText())
              norm = str(self.dlg_combo_norm_list[i_layer].currentText())
              minimum = float(self.dlg_line_min_list[i_layer].text())
              maximum = float(self.dlg_line_max_list[i_layer].text())
              blend_mode = str(self.dlg_combo_blend_mode_list[i_layer].currentText())
              opacity = int(self.dlg_scroll_opacity_list[i_layer].value())
              if visualization != "None":
                  combination.create_layer(vis_method=visualization, normalization=norm, minimum=minimum, maximum=maximum,
                                           blend_mode=blend_mode, opacity=opacity)
          self.combination = combination
  
      def check_dlg_comb_default_combs(self):
          """Checks if dlg blender combination values are same as any default combination or they
           are Custom combination."""
          self.load_dlg2combination()
          if self.dlg.combo_combinations.currentText() == "enhanced Multi-Scale Topographic Position version 3" or \
                  self.dlg.combo_combinations.currentText() == "Archaeological combined (VAT combined)":
              pass
          else:
              # find if dlg_combination has same attributes as one of the combinations
              dlg_combination_name = self.default_blender_combinations.combination_in_combinations(self.combination)
              if dlg_combination_name is not None:
                  self.dlg.combo_combinations.setCurrentText(dlg_combination_name)
              else:
                  self.dlg.combo_combinations.setCurrentText("Custom")
  
      def load_dlg2terrain(self):
          self.load_dlg2default()
          self.load_dlg2combination()
          terrain_settings = rvt.blend.TerrainSettings()
          terrain_settings.slp_output_units = self.default.slp_output_units
          terrain_settings.hs_sun_azi = self.default.hs_sun_azi
          terrain_settings.hs_sun_el = self.default.hs_sun_el
          terrain_settings.mhs_nr_dir = self.default.mhs_nr_dir
          terrain_settings.mhs_sun_el = self.default.mhs_sun_el
          terrain_settings.slrm_rad_cell = self.default.slrm_rad_cell
          terrain_settings.svf_n_dir = self.default.svf_n_dir
          terrain_settings.svf_r_max = self.default.svf_r_max
          terrain_settings.svf_noise = self.default.svf_noise
          terrain_settings.asvf_dir = self.default.asvf_dir
          terrain_settings.asvf_level = self.default.asvf_level
          terrain_settings.sim_sky_mod = self.default.sim_sky_mod
          terrain_settings.sim_nr_dir = self.default.sim_nr_dir
          terrain_settings.sim_shadow_dist = self.default.sim_shadow_dist
          terrain_settings.sim_shadow_az = self.default.sim_shadow_az
          terrain_settings.sim_shadow_el = self.default.sim_shadow_el
          terrain_settings.ld_min_rad = self.default.ld_min_rad
          terrain_settings.ld_max_rad = self.default.ld_max_rad
          terrain_settings.ld_rad_inc = self.default.ld_rad_inc
          terrain_settings.ld_anglr_res = self.default.ld_anglr_res
          terrain_settings.ld_observer_h = self.default.ld_observer_h
          terrain_settings.msrm_feature_min = self.default.msrm_feature_min
          terrain_settings.msrm_feature_max = self.default.msrm_feature_max
          terrain_settings.msrm_scaling_factor = self.default.msrm_scaling_factor
          terrain_settings.mstp_lightness = self.default.mstp_lightness
          terrain_settings.mstp_local_scale = self.default.mstp_local_scale
          terrain_settings.mstp_meso_scale = self.default.mstp_meso_scale
          terrain_settings.mstp_broad_scale = self.default.mstp_broad_scale
          for layer in self.combination.layers:
              if layer.vis.lower() == "hillshade":
                  terrain_settings.hs_stretch = (layer.min, layer.max)
              if layer.vis.lower() == "multiple directions hillshade":
                  terrain_settings.mhs_stretch = (layer.min, layer.max)
              if layer.vis.lower() == "slope gradient":
                  terrain_settings.slp_stretch = (layer.min, layer.max)
              if layer.vis.lower() == "simple local relief model":
                  terrain_settings.slrm_stretch = (layer.min, layer.max)
              if layer.vis.lower() == "sky-view factor":
                  terrain_settings.svf_stretch = (layer.min, layer.max)
              if layer.vis.lower() == "anisotropic sky-view factor":
                  terrain_settings.asvf_stretch = (layer.min, layer.max)
              if layer.vis.lower() == "openness - positive":
                  terrain_settings.pos_opns_stretch = (layer.min, layer.max)
              if layer.vis.lower() == "openness - negative":
                  terrain_settings.neg_opns_stretch = (layer.min, layer.max)
              if layer.vis.lower() == "sky illumination":
                  terrain_settings.sim_stretch = (layer.min, layer.max)
              if layer.vis.lower() == "local dominance":
                  terrain_settings.ld_stretch = (layer.min, layer.max)
              if layer.vis.lower() == "multi-scale relief model":
                  terrain_settings.msrm_stretch = (layer.min, layer.max)
              if layer.vis.lower() == "multi-scale topographic position":
                  terrain_settings.mstp_stretch = (layer.min, layer.max)
          self.terrain_settings = terrain_settings
  
      def combo_vis_check(self):
          """Check all layers, if layer visualization combo box is None then disable(lock) layer else enable(unlock),
          also check which visualizations are present in blender and check their checkboxes in visualizations tab."""
          self.deactivate_all_visualizations()  # set all vis checkboxes to False
          for i_layer in range(5):
              if self.dlg_combo_vis_list[i_layer].currentText() == "None":  # disable all dlg layer att when none
                  self.dlg_combo_norm_list[i_layer].setEnabled(False)
                  self.dlg_line_min_list[i_layer].setEnabled(False)
                  self.dlg_line_max_list[i_layer].setEnabled(False)
                  self.dlg_combo_blend_mode_list[i_layer].setEnabled(False)
                  self.dlg_scroll_opacity_list[i_layer].setEnabled(False)
                  self.dlg_label_opacity_list[i_layer].setEnabled(False)
              else:  # enable when not none
                  self.dlg_combo_norm_list[i_layer].setEnabled(True)
                  self.dlg_line_min_list[i_layer].setEnabled(True)
                  self.dlg_line_max_list[i_layer].setEnabled(True)
                  self.dlg_combo_blend_mode_list[i_layer].setEnabled(True)
                  self.dlg_scroll_opacity_list[i_layer].setEnabled(True)
                  self.dlg_label_opacity_list[i_layer].setEnabled(True)
              # update vis checkboxes
              if self.dlg_combo_vis_list[i_layer].currentText() == "Hillshade":
                  self.dlg.group_hillshade.setChecked(True)
              if self.dlg_combo_vis_list[i_layer].currentText() == "Shadow":
                  self.dlg.group_hillshade.setChecked(True)
                  self.dlg.check_hs_shadow.setChecked(True)
                  self.dlg.line_hs_sun_azi.setText(str(135))
                  self.dlg.line_hs_sun_el.setText(str(35))
              if self.dlg_combo_vis_list[i_layer].currentText() == "Multiple directions hillshade":
                  self.dlg.group_hillshade_multiple.setChecked(True)
              if self.dlg_combo_vis_list[i_layer].currentText() == "Slope gradient":
                  self.dlg.group_slope.setChecked(True)
              if self.dlg_combo_vis_list[i_layer].currentText() == "Simple local relief model":
                  self.dlg.group_local_relief.setChecked(True)
              if self.dlg_combo_vis_list[i_layer].currentText() == "Sky-View Factor":
                  self.dlg.group_sky_view.setChecked(True)
              if self.dlg_combo_vis_list[i_layer].currentText() == "Anisotropic Sky-View Factor":
                  self.dlg.group_anisotropic.setChecked(True)
              if self.dlg_combo_vis_list[i_layer].currentText() == "Openness - Positive":
                  self.dlg.group_openess_pos.setChecked(True)
              if self.dlg_combo_vis_list[i_layer].currentText() == "Openness - Negative":
                  self.dlg.group_openess_neg.setChecked(True)
              # if self.dlg_combo_vis_list[i_layer].currentText() == "Sky illumination":
              #     self.dlg.group_illumination.setChecked(True)
              if self.dlg_combo_vis_list[i_layer].currentText() == "Local dominance":
                  self.dlg.group_local_dominance.setChecked(True)
              if self.dlg_combo_vis_list[i_layer].currentText() == "Multi-scale relief model":
                  self.dlg.group_multi_reliefgroup_multi_relief.setChecked(True)
              if self.dlg_combo_vis_list[i_layer].currentText() == "Multi-scale topographic position":
                  self.dlg.group_multi_scale_top_pos.setChecked(True)
  
      def check_checkbox_float_8bit_change(self):
          """One of the checkboxes float or 8bit was clicked."""
          self.dlg.check_hs_float.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
          self.dlg.check_hs_8bit.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
          self.dlg.check_mhs_float.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
          self.dlg.check_mhs_8bit.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
          self.dlg.check_slp_float.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
          self.dlg.check_slp_8bit.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
          self.dlg.check_slrm_float.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
          self.dlg.check_slrm_8bit.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
          self.dlg.check_svf_float.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
          self.dlg.check_svf_8bit.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
          # self.dlg.check_sim_float.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
          # self.dlg.check_sim_8bit.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
          self.dlg.check_ld_float.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
          self.dlg.check_ld_8bit.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
          self.dlg.check_msrm_float.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
          self.dlg.check_msrm_8bit.stateChanged.connect(lambda: self.checkbox_float_8bit_check())
  
      def check_fill_no_data_other_fill_method_combo_change(self):
          self.dlg.combo_fill_method.currentTextChanged.connect(lambda: self.fill_no_data_other_fill_method_combo_check())
  
      def fill_no_data_other_fill_method_combo_check(self):
          """Fill no data method additional parameters checks."""
          if self.dlg.combo_fill_method.currentText() == "Inverse Distance Weighting":
              self.dlg.label_fill_nan_rad.setEnabled(True)
              self.dlg.line_fill_nan_rad.setEnabled(True)
              self.dlg.label_fill_nan_scl.setEnabled(True)
              self.dlg.line_fill_nan_scl.setEnabled(True)
          else:
              self.dlg.label_fill_nan_rad.setEnabled(False)
              self.dlg.line_fill_nan_rad.setEnabled(False)
              self.dlg.label_fill_nan_scl.setEnabled(False)
              self.dlg.line_fill_nan_scl.setEnabled(False)
  
      def check_blender_checkbox_float_8bit_change(self):
          self.dlg.check_blender_save_float.stateChanged.connect(lambda: self.blender_checkbox_float_8bit_check())
          self.dlg.check_blender_save_8bit.stateChanged.connect(lambda: self.blender_checkbox_float_8bit_check())
  
      def checkbox_float_8bit_check(self):
          """Check all float and 8bit checkboxes, if both float and 8bit checkboxes are False for specific visualisation
           then set them both to True. At least one of them has to be True!"""
          if not self.dlg.check_hs_float.isChecked() and not self.dlg.check_hs_8bit.isChecked():
              self.dlg.check_hs_float.setChecked(True)
              self.dlg.check_hs_8bit.setChecked(True)
          if not self.dlg.check_mhs_float.isChecked() and not self.dlg.check_mhs_8bit.isChecked():
              self.dlg.check_mhs_float.setChecked(True)
              self.dlg.check_mhs_8bit.setChecked(True)
          if not self.dlg.check_slp_float.isChecked() and not self.dlg.check_slp_8bit.isChecked():
              self.dlg.check_slp_float.setChecked(True)
              self.dlg.check_slp_8bit.setChecked(True)
          if not self.dlg.check_slrm_float.isChecked() and not self.dlg.check_slrm_8bit.isChecked():
              self.dlg.check_slrm_float.setChecked(True)
              self.dlg.check_slrm_8bit.setChecked(True)
          if not self.dlg.check_svf_float.isChecked() and not self.dlg.check_svf_8bit.isChecked():
              self.dlg.check_svf_float.setChecked(True)
              self.dlg.check_svf_8bit.setChecked(True)
          # if not self.dlg.check_sim_float.isChecked() and not self.dlg.check_sim_8bit.isChecked():
          #     self.dlg.check_sim_float.setChecked(True)
          #     self.dlg.check_sim_8bit.setChecked(True)
          if not self.dlg.check_ld_float.isChecked() and not self.dlg.check_ld_8bit.isChecked():
              self.dlg.check_ld_float.setChecked(True)
              self.dlg.check_ld_8bit.setChecked(True)
          if not self.dlg.check_msrm_float.isChecked() and not self.dlg.check_msrm_8bit.isChecked():
              self.dlg.check_msrm_float.setChecked(True)
              self.dlg.check_msrm_8bit.setChecked(True)
  
      def blender_checkbox_float_8bit_check(self):
          if not self.dlg.check_blender_save_float.isChecked() and not self.dlg.check_blender_save_8bit.isChecked():
              self.dlg.check_blender_save_float.setChecked(True)
              self.dlg.check_blender_save_8bit.setChecked(True)
  
      def add_combination_clicked(self):
          new_combination_name = str(self.dlg.line_combination_name.text()).strip()
          existing_combinations = self.default_blender_combinations.combinations_names()
          if new_combination_name not in existing_combinations and new_combination_name != "":
              self.load_dlg2combination()  # loads dlg to self.combination
              self.combination.name = new_combination_name  # apply new combination name
              self.default_blender_combinations.add_combination(combination=self.combination)  # add to combinations
              self.default_blender_combinations.save_to_file(file_path=self.default_blender_combinations_path)  # save
              self.load_combinations2cb()  # load new combinations to combobox
              self.load_combination2dlg(combination=self.combination)  # loads new combination
              self.dlg.line_combination_name.setText("")
          if new_combination_name == "":
              self.iface.messageBar().pushMessage("RVT", "Combination name is empty!", level=Qgis.Warning)
  
      def remove_combination_clicked(self):
          selected_combination_name = str(self.dlg.combo_combinations.currentText())
          if selected_combination_name != "Custom":
              self.default_blender_combinations.remove_combination_by_name(name=selected_combination_name)
              self.default_blender_combinations.save_to_file(file_path=self.default_blender_combinations_path)
              self.load_combinations2cb()
  
      def save_combination_to_clicked(self):
          new_combination_name = str(self.dlg.line_combination_name.text()).strip()
          if new_combination_name != "":
              json_path = str(QFileDialog.getSaveFileName(self.dlg, caption='Save combination JSON',
                                                          filter="JSON (*.json)")[0])
              if json_path != "":
                  try:
                      self.load_dlg2combination()
                      self.combination.name = new_combination_name
                      self.combination.save_to_file(json_path)
                      self.dlg.line_combination_name.setText("")
                  except:
                      self.iface.messageBar().pushMessage("RVT", "Can't save combination JSON file!", level=Qgis.Warning)
          else:
              self.iface.messageBar().pushMessage("RVT", "Combination name is empty!", level=Qgis.Warning)
  
      def load_combination_from_clicked(self):
          json_path = str(QFileDialog.getOpenFileName(self.dlg, caption="Load combination JSON",
                                                      filter="JSON (*.json)")[0])
          if os.path.isfile(json_path):
              try:
                  existing_combinations = self.default_blender_combinations.combinations_names()
                  combination = rvt.blend.BlenderCombination()
                  combination.read_from_file(json_path)
                  if combination.name not in existing_combinations and combination.name != "":
                      self.default_blender_combinations.add_combination(combination)
                      self.default_blender_combinations.save_to_file(self.default_blender_combinations_path)
                      self.load_combinations2cb()
                      self.load_combination2dlg(combination=combination)
                      self.combination = combination
              except:
                  self.iface.messageBar().pushMessage("RVT", "Can't read combination JSON file!", level=Qgis.Warning)
  
      def check_combination_change(self):
          """If blender combination combo box changes method triggers other methods."""
          self.dlg.combo_combinations.currentTextChanged.connect(lambda: self.load_blender_combination())
  
      def load_blender_combination(self):
          """Check which blender combination is selected, get that combination and fill blender dlg with
           its values."""
          selected_combination = str(self.dlg.combo_combinations.currentText())
          combination = self.default_blender_combinations.select_combination_by_name(selected_combination)
          if combination is not None:
              self.combination = combination
              self.load_combination2dlg(combination)
  
      def load_combinations2cb(self, combinations=None):
          """Load combinations from self.default_blender_combinations to dlg.combo_combinations combobox."""
          if combinations is None:  # if combinations None it takes self.default_blender_combinations
              combinations = self.default_blender_combinations
          self.dlg.combo_combinations.clear()
          comb_names = combinations.combinations_names()
          for combination_name in comb_names:
              self.dlg.combo_combinations.addItem(combination_name)
          self.dlg.combo_combinations.addItem("Custom")
  
      def load_combination2dlg(self, combination, terrain_bool=False):
          """Fill blender dlg parameters (combo boxes, line edits, scroll sliders) with values from combination."""
          if not terrain_bool:
              self.dlg.chech_terrain_preset.setCheckState(False)
          nr_layers = len(combination.layers)  # number of layers
          self.combination = combination
          for i_layer in range(5):
              layer_number = i_layer + 1
              if nr_layers >= layer_number:
                  layer = combination.layers[i_layer]  # BlenderLayer
                  index_combo_vis = self.dlg_combo_vis_list[i_layer].findText(layer.vis)
                  if index_combo_vis >= 0:
                      self.dlg_combo_vis_list[i_layer].setCurrentIndex(index_combo_vis)  # set vis
                  index_combo_norm = self.dlg_combo_norm_list[i_layer].findText(layer.normalization)
                  if index_combo_norm >= 0:
                      self.dlg_combo_norm_list[i_layer].setCurrentIndex(index_combo_norm)  # set normalization
                  self.dlg_line_min_list[i_layer].setText(str(layer.min))  # set min
                  self.dlg_line_max_list[i_layer].setText(str(layer.max))  # set max
                  index_combo_blend_mode = self.dlg_combo_blend_mode_list[i_layer].findText(layer.blend_mode)
                  if index_combo_blend_mode >= 0:
                      self.dlg_combo_blend_mode_list[i_layer].setCurrentIndex(index_combo_blend_mode)  # blend_mode
                  self.dlg_scroll_opacity_list[i_layer].setSliderPosition(layer.opacity)  # set opacity
              else:
                  self.set_dlg_layer_to_none(layer_number)
  
      def set_dlg_layer_to_none(self, layer_number):
          """Set layer (layer_number) to none."""
          i_layer = layer_number - 1
          self.dlg_combo_vis_list[i_layer].setCurrentIndex(0)
          self.dlg_combo_norm_list[i_layer].setCurrentIndex(0)
          self.dlg_line_min_list[i_layer].setText("0.0000")
          self.dlg_line_max_list[i_layer].setText("0.0000")
          self.dlg_combo_blend_mode_list[i_layer].setCurrentIndex(0)
          self.dlg_scroll_opacity_list[i_layer].setSliderPosition(100)
          self.combo_vis_check()
  
      def remove_layer_by_path(self, layer_path):
          """Remove layer with layer_path from Qgis."""
          layer_path = os.path.abspath(layer_path)
          for layer in QgsProject.instance().mapLayers().values():
              if os.path.abspath(layer.dataProvider().dataSourceUri()) == layer_path:
                  QgsProject.instance().removeMapLayer(layer)
                  return 1
          return 0
  
      def fill_method_translate(self, fill_method):
          """Translates fill_method (no data interpolation method) string for function to text for combo box and
           vice versa."""
          if fill_method.split("_")[0] == "idw":
              return "Inverse Distance Weighting"
          elif fill_method == "Inverse Distance Weighting":
              try:
                  radius = int(self.dlg.line_fill_nan_rad.text())
                  scale = float(self.dlg.line_fill_nan_scl.text())
                  return "idw_{}_{}".format(radius, scale)
              except:
                  return "idw"
          elif fill_method == "kd_tree":
              return "K-D Tree"
          elif fill_method == "K-D Tree":
              return "kd_tree"
          elif fill_method == "nearest_neighbour":
              return "Nearest Neighbour"
          elif fill_method == "Nearest Neighbour":
              return "nearest_neighbour"
  
      def load_default2dlg(self):
          """Reads default (rvt.defaul.DeafultValues()) from default_path and fill visualization dlg."""
          self.dlg.check_overwrite.setChecked(bool(self.default.overwrite))
          self.dlg.line_ve_factor.setText(str(self.default.ve_factor))
          self.dlg.group_hillshade.setChecked(bool(self.default.hs_compute))
          self.dlg.line_hs_sun_azi.setText(str(self.default.hs_sun_azi))
          self.dlg.line_hs_sun_el.setText(str(self.default.hs_sun_el))
          self.dlg.check_hs_shadow.setChecked(bool(self.default.hs_shadow))
          self.dlg.group_hillshade_multiple.setChecked(bool(self.default.mhs_compute))
          self.dlg.line_mhs_nr_dir.setText(str(self.default.mhs_nr_dir))
          self.dlg.line_mhs_sun_el.setText(str(self.default.mhs_sun_el))
          self.dlg.group_slope.setChecked(bool(self.default.slp_compute))
          index_combo_slp_ou = self.dlg.combo_slp_output_units.findText(self.default.slp_output_units)
          if index_combo_slp_ou >= 0:
              self.dlg.combo_slp_output_units.setCurrentIndex(index_combo_slp_ou)
          self.dlg.group_local_relief.setChecked(bool(self.default.slrm_compute))
          self.dlg.line_slrm_rad_cell.setText(str(self.default.slrm_rad_cell))
          self.dlg.group_sky_view.setChecked(bool(self.default.svf_compute))
          index_combo_svf_n_dir = self.dlg.combo_svf_n_dir.findText(str(self.default.svf_n_dir))
          if index_combo_svf_n_dir >= 0:
              self.dlg.combo_svf_n_dir.setCurrentIndex(index_combo_svf_n_dir)
          self.dlg.line_svf_r_max.setText(str(self.default.svf_r_max))
          if self.default.svf_noise == 0:
              self.dlg.check_svf_noise.setChecked(False)
          else:
              self.dlg.check_svf_noise.setChecked(True)
              index_combo_svf_noise = 0
              if self.default.svf_noise == 1:
                  index_combo_svf_noise = self.dlg.combo_svf_noise.findText("low")
              elif self.default.svf_noise == 2:
                  index_combo_svf_noise = self.dlg.combo_svf_noise.findText("medium")
              elif self.default.svf_noise == 3:
                  index_combo_svf_noise = self.dlg.combo_svf_noise.findText("high")
              self.dlg.combo_svf_noise.setCurrentIndex(index_combo_svf_noise)
          self.checkbox_svf_noise_check()
          self.dlg.group_anisotropic.setChecked(bool(self.default.asvf_compute))
          index_combo_asvf_level = self.dlg.combo_asvf_level.findText(str(self.default.asvf_level))
          if index_combo_asvf_level >= 0:
              self.dlg.combo_asvf_level.setCurrentIndex(index_combo_asvf_level)
          self.dlg.line_asvf_dir.setText(str(self.default.asvf_dir))
          self.dlg.group_openess_pos.setChecked(bool(self.default.pos_opns_compute))
          self.dlg.group_openess_neg.setChecked(bool(self.default.neg_opns_compute))
          # self.dlg.group_illumination.setChecked(bool(self.default.sim_compute))
          # index_combo_sim_sky_mod = self.dlg.combo_sim_sky_mod.findText(self.default.sim_sky_mod)
          # if index_combo_sim_sky_mod >= 0:
          #     self.dlg.combo_sim_sky_mod.setCurrentIndex(index_combo_sim_sky_mod)
          # index_combo_sim_nr_dir = self.dlg.combo_sim_nr_dir.findText(str(self.default.sim_nr_dir))
          # if index_combo_sim_nr_dir >= 0:
          #     self.dlg.combo_sim_nr_dir.setCurrentIndex(index_combo_sim_nr_dir)
          # index_combo_sim_shadow_dist = self.dlg.combo_sim_shadow_dist.findText(str(self.default.sim_shadow_dist))
          # if index_combo_sim_shadow_dist >= 0:
          #     self.dlg.combo_sim_shadow_dist.setCurrentIndex(index_combo_sim_shadow_dist)
          self.dlg.group_local_dominance.setChecked(bool(self.default.ld_compute))
          self.dlg.line_ld_min_rad.setText(str(self.default.ld_min_rad))
          self.dlg.line_ld_max_rad.setText(str(self.default.ld_max_rad))
          self.dlg.group_multi_relief.setChecked(bool(self.default.msrm_compute))
          self.dlg.line_msrm_f_min.setText(str(self.default.msrm_feature_min))
          self.dlg.line_msrm_f_max.setText(str(self.default.msrm_feature_max))
          self.dlg.line_msrm_scale.setText(str(self.default.msrm_scaling_factor))
          self.dlg.group_multi_scale_top_pos.setChecked(bool(self.default.mstp_compute))
          self.dlg.line_mstp_loc_min.setText(str(self.default.mstp_local_scale[0]))
          self.dlg.line_mstp_loc_max.setText(str(self.default.mstp_local_scale[1]))
          self.dlg.line_mstp_loc_stp.setText(str(self.default.mstp_local_scale[2]))
          self.dlg.line_mstp_meso_min.setText(str(self.default.mstp_meso_scale[0]))
          self.dlg.line_mstp_meso_max.setText(str(self.default.mstp_meso_scale[1]))
          self.dlg.line_mstp_meso_stp.setText(str(self.default.mstp_meso_scale[2]))
          self.dlg.line_mstp_bro_min.setText(str(self.default.mstp_broad_scale[0]))
          self.dlg.line_mstp_bro_max.setText(str(self.default.mstp_broad_scale[1]))
          self.dlg.line_mstp_bro_stp.setText(str(self.default.mstp_broad_scale[2]))
          self.dlg.line_mstp_light.setText(str(self.default.mstp_lightness))
          self.dlg.check_hs_float.setChecked(bool(self.default.hs_save_float))
          self.dlg.check_hs_8bit.setChecked(bool(self.default.hs_save_8bit))
          self.dlg.check_mhs_float.setChecked(bool(self.default.mhs_save_float))
          self.dlg.check_mhs_8bit.setChecked(bool(self.default.mhs_save_8bit))
          self.dlg.check_slp_float.setChecked(bool(self.default.slp_save_float))
          self.dlg.check_slp_8bit.setChecked(bool(self.default.slp_save_8bit))
          self.dlg.check_slrm_float.setChecked(bool(self.default.slrm_save_float))
          self.dlg.check_slrm_8bit.setChecked(bool(self.default.slrm_save_8bit))
          self.dlg.check_svf_float.setChecked(bool(self.default.svf_save_float))
          self.dlg.check_svf_8bit.setChecked(bool(self.default.svf_save_8bit))
          # self.dlg.check_sim_float.setChecked(bool(self.default.sim_save_float))
          # self.dlg.check_sim_8bit.setChecked(bool(self.default.sim_save_8bit))
          self.dlg.check_ld_float.setChecked(bool(self.default.ld_save_float))
          self.dlg.check_ld_8bit.setChecked(bool(self.default.ld_save_8bit))
          self.dlg.check_msrm_float.setChecked(bool(self.default.msrm_save_float))
          self.dlg.check_msrm_8bit.setChecked(bool(self.default.msrm_save_8bit))
  
      def load_dlg2default(self):
          """Read Qgis plugin dialog visualization functions parameters and fill them to rvt.defaul.DeafultValues() ."""
          self.default.overwrite = int(self.dlg.check_overwrite.isChecked())
          self.default.ve_factor = float(self.dlg.line_ve_factor.text())
          self.default.hs_compute = int(self.dlg.group_hillshade.isChecked())
          self.default.hs_sun_azi = int(self.dlg.line_hs_sun_azi.text())
          self.default.hs_sun_el = int(self.dlg.line_hs_sun_el.text())
          self.default.hs_shadow = int(self.dlg.check_hs_shadow.isChecked())
          self.default.mhs_compute = int(self.dlg.group_hillshade_multiple.isChecked())
          self.default.mhs_nr_dir = int(self.dlg.line_mhs_nr_dir.text())
          self.default.mhs_sun_el = int(self.dlg.line_mhs_sun_el.text())
          self.default.slp_compute = int(self.dlg.group_slope.isChecked())
          self.default.slp_output_units = str(self.dlg.combo_slp_output_units.currentText())
          self.default.slrm_compute = int(self.dlg.group_local_relief.isChecked())
          self.default.slrm_rad_cell = int(self.dlg.line_slrm_rad_cell.text())
          self.default.svf_compute = int(self.dlg.group_sky_view.isChecked())
          self.default.svf_n_dir = int(self.dlg.combo_svf_n_dir.currentText())
          self.default.svf_r_max = int(self.dlg.line_svf_r_max.text())
          if not self.dlg.check_svf_noise.isChecked():
              self.default.svf_noise = 0
          elif self.dlg.combo_svf_noise.currentText() == str("low"):
              self.default.svf_noise = 1
          elif self.dlg.combo_svf_noise.currentText() == str("medium"):
              self.default.svf_noise = 2
          elif self.dlg.combo_svf_noise.currentText() == str("high"):
              self.default.svf_noise = 3
          self.default.asvf_compute = int(self.dlg.group_anisotropic.isChecked())
          if self.dlg.combo_asvf_level.currentText() == str("low"):
              self.default.asvf_level = 1
          elif self.dlg.combo_asvf_level.currentText() == str("high"):
              self.default.asvf_level = 2
          self.default.asvf_dir = int(self.dlg.line_asvf_dir.text())
          self.default.pos_opns_compute = int(self.dlg.group_openess_pos.isChecked())
          self.default.neg_opns_compute = int(self.dlg.group_openess_neg.isChecked())
          # self.default.sim_compute = int(self.dlg.group_illumination.isChecked())
          # self.default.sim_sky_mod = str(self.dlg.combo_sim_sky_mod.currentText())
          # self.default.sim_nr_dir = int(self.dlg.combo_sim_nr_dir.currentText())
          # self.default.sim_shadow_dist = int(self.dlg.combo_sim_shadow_dist.currentText())
          self.default.ld_compute = int(self.dlg.group_local_dominance.isChecked())
          self.default.ld_min_rad = int(self.dlg.line_ld_min_rad.text())
          self.default.ld_max_rad = int(self.dlg.line_ld_max_rad.text())
          self.default.msrm_compute = int(self.dlg.group_multi_relief.isChecked())
          self.default.msrm_feature_min = float(self.dlg.line_msrm_f_min.text())
          self.default.msrm_feature_max = float(self.dlg.line_msrm_f_max.text())
          self.default.msrm_scaling_factor = int(self.dlg.line_msrm_scale.text())
          self.default.mstp_compute = int(self.dlg.group_multi_scale_top_pos.isChecked())
          self.default.mstp_lightness = float(self.dlg.line_mstp_light.text())
          self.default.mstp_local_scale = (int(self.dlg.line_mstp_loc_min.text()),
                                           int(self.dlg.line_mstp_loc_max.text()),
                                           int(self.dlg.line_mstp_loc_stp.text()))
          self.default.mstp_meso_scale = (int(self.dlg.line_mstp_meso_min.text()),
                                          int(self.dlg.line_mstp_meso_max.text()),
                                          int(self.dlg.line_mstp_meso_stp.text()))
          self.default.mstp_broad_scale = (int(self.dlg.line_mstp_bro_min.text()),
                                           int(self.dlg.line_mstp_bro_max.text()),
                                           int(self.dlg.line_mstp_bro_stp.text()))
          self.default.hs_save_float = int(self.dlg.check_hs_float.isChecked())
          self.default.hs_save_8bit = int(self.dlg.check_hs_8bit.isChecked())
          self.default.mhs_save_float = int(self.dlg.check_mhs_float.isChecked())
          self.default.mhs_save_8bit = int(self.dlg.check_mhs_8bit.isChecked())
          self.default.slp_save_float = int(self.dlg.check_slp_float.isChecked())
          self.default.slp_save_8bit = int(self.dlg.check_slp_8bit.isChecked())
          self.default.slrm_save_float = int(self.dlg.check_slrm_float.isChecked())
          self.default.slrm_save_8bit = int(self.dlg.check_slrm_8bit.isChecked())
          self.default.svf_save_float = int(self.dlg.check_svf_float.isChecked())
          self.default.svf_save_8bit = int(self.dlg.check_svf_8bit.isChecked())
          self.default.neg_opns_save_float = int(self.dlg.check_svf_float.isChecked())
          self.default.neg_opns_save_8bit = int(self.dlg.check_svf_8bit.isChecked())
          # self.default.sim_save_float = int(self.dlg.check_sim_float.isChecked())
          # self.default.sim_save_8bit = int(self.dlg.check_sim_8bit.isChecked())
          self.default.ld_save_float = int(self.dlg.check_ld_float.isChecked())
          self.default.ld_save_8bit = int(self.dlg.check_ld_8bit.isChecked())
          self.default.msrm_save_float = int(self.dlg.check_msrm_float.isChecked())
          self.default.msrm_save_8bit = int(self.dlg.check_msrm_8bit.isChecked())
  
      class ComputeVisualizationsTask(QgsTask):
          """Task (thread) for computing visualizations."""
  
          def __init__(self, description, parent):
              super().__init__(description)
              self.parent = parent
              self.loading_screen = LoadingScreenDlg(gif_path=parent.gif_path)  # init loading dlg
              self.loading_screen.start_animation()  # start loading dlg
              self.exception = None
              self.no_raster = False
              self.is_calculating = False
  
          def run(self):
              if self.parent.is_calculating:
                  self.is_calculating = True
                  return False
              else:
                  self.parent.is_calculating = True
                  try:
                      compute = self.parent.compute_visualizations()  # run compute visualizations
                      if compute == "no raster selected":
                          self.no_raster = True
                          self.parent.is_calculating = False
                          return False
                      else:
                          self.no_raster = False
                          self.parent.is_calculating = False
                          return True
                  except:  # something went wrong
                      return False
  
          def finished(self, result):  # when finished close loading dlg and load rasters (visualizations) into Qgis
              if result:  # if self.run returns True
                  add_to_qgis = self.parent.dlg.check_addqgis.isChecked()
                  selected_input_rasters = self.parent.dlg.select_input_files.checkedItems()
                  # add saved/computed to qgis
                  for raster_name in selected_input_rasters:  # loop trough all selected rasters
                      raster_path = self.parent.rvt_select_input[raster_name]
                      # get directory to save in
                      if self.parent.dlg.check_sav_rast_loc.isChecked():  # means to save in raster path
                          save_dir = os.path.dirname(raster_path)
                      else:
                          save_dir = self.parent.dlg.line_save_loc.text()
  
                      if add_to_qgis:  # add calculated layers to Qgis
                          # Slope
                          if self.parent.default.slp_compute:
                              if self.parent.default.slp_save_float:
                                  slope_name = self.parent.default.get_slope_file_name(raster_path)
                                  slope_path = os.path.abspath(os.path.join(save_dir, slope_name))
                                  self.parent.remove_layer_by_path(slope_path)  # remove layer from qgis if exists
                                  self.parent.iface.addRasterLayer(slope_path, slope_name)  # add layer to qgis
                              if self.parent.default.slp_save_8bit:
                                  slope_8bit_name = self.parent.default.get_slope_file_name(raster_path, bit8=True)
                                  slope_8bit_path = os.path.abspath(os.path.join(save_dir, slope_8bit_name))
                                  self.parent.remove_layer_by_path(slope_8bit_path)  # remove layer from qgis if exists
                                  self.parent.iface.addRasterLayer(slope_8bit_path, slope_8bit_name)  # add layer to qgis
                          # Hillshade
                          if self.parent.default.hs_compute:
                              if self.parent.default.hs_save_float:
                                  hillshade_name = self.parent.default.get_hillshade_file_name(raster_path)
                                  hillshade_path = os.path.abspath(os.path.join(save_dir, hillshade_name))
                                  self.parent.remove_layer_by_path(hillshade_path)  # remove layer from qgis if exists
                                  self.parent.iface.addRasterLayer(hillshade_path, hillshade_name)  # add layer to qgis
                              if self.parent.default.hs_save_8bit:
                                  hillshade_8bit_name = self.parent.default.get_hillshade_file_name(raster_path,
                                                                                                    bit8=True)
                                  hillshade_8bit_path = os.path.abspath(os.path.join(save_dir, hillshade_8bit_name))
                                  self.parent.remove_layer_by_path(hillshade_8bit_path)  # remove layer if exists
                                  self.parent.iface.addRasterLayer(hillshade_8bit_path,
                                                                   hillshade_8bit_name)  # add layer to qgis
                              if self.parent.default.hs_shadow:
                                  shadow_name = self.parent.default.get_shadow_file_name(raster_path)
                                  shadow_path = os.path.abspath(os.path.join(save_dir, shadow_name))
                                  self.parent.remove_layer_by_path(shadow_path)  # remove layer from qgis if exists
                                  self.parent.iface.addRasterLayer(shadow_path, shadow_name)  # add layer to qgis
                          # Multiple direction hillshade
                          if self.parent.default.mhs_compute:
                              if self.parent.default.mhs_save_float:
                                  multi_hillshade_name = self.parent.default.get_multi_hillshade_file_name(raster_path)
                                  multi_hillshade_path = os.path.abspath(os.path.join(save_dir, multi_hillshade_name))
                                  self.parent.remove_layer_by_path(multi_hillshade_path)  # remove layer if exists
                                  self.parent.iface.addRasterLayer(multi_hillshade_path,
                                                                   multi_hillshade_name)  # add layer to qgis
                              if self.parent.default.mhs_save_8bit:
                                  multi_hillshade_8bit_name = self.parent.default.get_multi_hillshade_file_name(
                                      raster_path,
                                      bit8=True)
                                  multi_hillshade_8bit_path = os.path.abspath(
                                      os.path.join(save_dir, multi_hillshade_8bit_name))
                                  self.parent.remove_layer_by_path(
                                      multi_hillshade_8bit_path)  # remove layer from qgis if exists
                                  self.parent.iface.addRasterLayer(multi_hillshade_8bit_path,
                                                                   multi_hillshade_8bit_name)  # add to qgis
                          # Simplified local relief model
                          if self.parent.default.slrm_compute:
                              if self.parent.default.slrm_save_float:
                                  slrm_name = self.parent.default.get_slrm_file_name(raster_path)
                                  slrm_path = os.path.abspath(os.path.join(save_dir, slrm_name))
                                  self.parent.remove_layer_by_path(slrm_path)  # remove layer from qgis if exists
                                  self.parent.iface.addRasterLayer(slrm_path, slrm_name)  # add layer to qgis
                              if self.parent.default.slrm_save_8bit:
                                  slrm_8bit_name = self.parent.default.get_slrm_file_name(raster_path, bit8=True)
                                  slrm_8bit_path = os.path.abspath(os.path.join(save_dir, slrm_8bit_name))
                                  self.parent.remove_layer_by_path(slrm_8bit_path)  # remove layer from qgis if exists
                                  self.parent.iface.addRasterLayer(slrm_8bit_path, slrm_8bit_name)  # add layer to qgis
                          # Sky-View factor, Anisotropic Sky-View factor, Positive Openness
                          if self.parent.default.svf_compute or self.parent.default.asvf_compute or \
                                  self.parent.default.pos_opns_compute:
                              if self.parent.default.svf_save_float:
                                  if self.parent.default.svf_compute:
                                      svf_name = self.parent.default.get_svf_file_name(raster_path)
                                      svf_path = os.path.abspath(os.path.join(save_dir, svf_name))
                                      self.parent.remove_layer_by_path(svf_path)  # remove layer from qgis if exists
                                      self.parent.iface.addRasterLayer(svf_path, svf_name)  # add layer to qgis
                                  if self.parent.default.asvf_compute:
                                      asvf_name = self.parent.default.get_asvf_file_name(raster_path)
                                      asvf_path = os.path.abspath(os.path.join(save_dir, asvf_name))
                                      self.parent.remove_layer_by_path(asvf_path)  # remove layer from qgis if exists
                                      self.parent.iface.addRasterLayer(asvf_path, asvf_name)  # add layer to qgis
                                  if self.parent.default.pos_opns_compute:
                                      pos_opns_name = self.parent.default.get_opns_file_name(raster_path)
                                      pos_opns_path = os.path.abspath(os.path.join(save_dir, pos_opns_name))
                                      self.parent.remove_layer_by_path(pos_opns_path)  # remove layer from qgis if exists
                                      self.parent.iface.addRasterLayer(pos_opns_path, pos_opns_name)  # add layer to qgis
                              if self.parent.default.svf_save_8bit:
                                  if self.parent.default.svf_compute:
                                      svf_8bit_name = self.parent.default.get_svf_file_name(raster_path, bit8=True)
                                      svf_8bit_path = os.path.abspath(os.path.join(save_dir, svf_8bit_name))
                                      self.parent.remove_layer_by_path(svf_8bit_path)  # remove layer from qgis if exists
                                      self.parent.iface.addRasterLayer(svf_8bit_path, svf_8bit_name)  # add layer to qgis
                                  if self.parent.default.asvf_compute:
                                      asvf_8bit_name = self.parent.default.get_asvf_file_name(raster_path, bit8=True)
                                      asvf_8bit_path = os.path.abspath(os.path.join(save_dir, asvf_8bit_name))
                                      self.parent.remove_layer_by_path(asvf_8bit_path)  # remove layer from qgis if exists
                                      self.parent.iface.addRasterLayer(asvf_8bit_path,
                                                                       asvf_8bit_name)  # add layer to qgis
                                  if self.parent.default.pos_opns_compute:
                                      pos_opns_8bit_name = self.parent.default.get_opns_file_name(raster_path, bit8=True)
                                      pos_opns_8bit_path = os.path.abspath(os.path.join(save_dir, pos_opns_8bit_name))
                                      self.parent.remove_layer_by_path(
                                          pos_opns_8bit_path)  # remove layer from qgis if exists
                                      self.parent.iface.addRasterLayer(pos_opns_8bit_path,
                                                                       pos_opns_8bit_name)  # add layer to qgis
                          # Negative Openness
                          if self.parent.default.neg_opns_compute:
                              if self.parent.default.neg_opns_save_float:
                                  neg_opns_name = self.parent.default.get_neg_opns_file_name(raster_path)
                                  neg_opns_path = os.path.abspath(os.path.join(save_dir, neg_opns_name))
                                  self.parent.remove_layer_by_path(neg_opns_path)  # remove layer from qgis if exists
                                  self.parent.iface.addRasterLayer(neg_opns_path, neg_opns_name)  # add layer to qgis
                              if self.parent.default.neg_opns_save_8bit:
                                  neg_opns_8bit_name = self.parent.default.get_neg_opns_file_name(raster_path, bit8=True)
                                  neg_opns_8bit_path = os.path.abspath(os.path.join(save_dir, neg_opns_8bit_name))
                                  self.parent.remove_layer_by_path(neg_opns_8bit_path)  # remove layer from qgis if exists
                                  self.parent.iface.addRasterLayer(neg_opns_8bit_path,
                                                                   neg_opns_8bit_name)  # add layer to qgis
                          # Sky illumination
                          # if self.parent.default.sim_compute:
                          #     if self.parent.default.sim_save_float:
                          #         sky_illumination_name = self.parent.default.get_sky_illumination_file_name(raster_path)
                          #         sky_illumination_path = os.path.abspath(os.path.join(save_dir, sky_illumination_name))
                          #         self.parent.remove_layer_by_path(
                          #             sky_illumination_path)  # remove layer from qgis if exists
                          #         self.parent.iface.addRasterLayer(sky_illumination_path,
                          #                                          sky_illumination_name)  # add layer to qgis
                          #     if self.parent.default.sim_save_8bit:
                          #         sky_illumination_8bit_name = self.parent.default.get_sky_illumination_file_name(
                          #             raster_path,
                          #             bit8=True)
                          #         sky_illumination_8bit_path = os.path.abspath(
                          #             os.path.join(save_dir, sky_illumination_8bit_name))
                          #         self.parent.remove_layer_by_path(
                          #             sky_illumination_8bit_path)  # remove layer from qgis if exists
                          #         self.parent.iface.addRasterLayer(sky_illumination_8bit_path,
                          #                                          sky_illumination_8bit_name)  # add layer
                          # Local dominance
                          if self.parent.default.ld_compute:
                              if self.parent.default.ld_save_float:
                                  local_dominance_name = self.parent.default.get_local_dominance_file_name(raster_path)
                                  local_dominance_path = os.path.abspath(os.path.join(save_dir, local_dominance_name))
                                  self.parent.remove_layer_by_path(
                                      local_dominance_path)  # remove layer from qgis if exists
                                  self.parent.iface.addRasterLayer(local_dominance_path,
                                                                   local_dominance_name)  # add layer to qgis
                              if self.parent.default.ld_save_8bit:
                                  local_dominance_8bit_name = self.parent.default.get_local_dominance_file_name(
                                      raster_path,
                                      bit8=True)
                                  local_dominance_8bit_path = os.path.abspath(
                                      os.path.join(save_dir, local_dominance_8bit_name))
                                  self.parent.remove_layer_by_path(local_dominance_8bit_path)  # remove layer from qgis
                                  # if exists
                                  self.parent.iface.addRasterLayer(local_dominance_8bit_path,
                                                                   local_dominance_8bit_name)  # add layer
                          # Multi-scale relief model
                          if self.parent.default.msrm_compute:
                              if self.parent.default.msrm_save_float:
                                  msrm_name = self.parent.default.get_msrm_file_name(raster_path)
                                  msrm_path = os.path.abspath(os.path.join(save_dir, msrm_name))
                                  self.parent.remove_layer_by_path(
                                      msrm_path)  # remove layer from qgis if exists
                                  self.parent.iface.addRasterLayer(msrm_path,
                                                                   msrm_name)  # add layer to qgis
                              if self.parent.default.msrm_save_8bit:
                                  msrm_8bit_name = self.parent.default.get_local_dominance_file_name(
                                      raster_path,
                                      bit8=True)
                                  msrm_8bit_path = os.path.abspath(
                                      os.path.join(save_dir, msrm_8bit_name))
                                  self.parent.remove_layer_by_path(msrm_8bit_path)  # remove layer from qgis
                                  # if exists
                                  self.parent.iface.addRasterLayer(msrm_8bit_path,
                                                                   msrm_8bit_name)  # add layer
                          # Multi-scale topographic position
                          if self.parent.default.mstp_compute:
                              mstp_name = self.parent.default.get_mstp_file_name(raster_path)
                              mstp_path = os.path.abspath(os.path.join(save_dir, mstp_name))
                              self.parent.remove_layer_by_path(mstp_path)  # remove layer from qgis if exists
                              self.parent.iface.addRasterLayer(mstp_path, mstp_name)  # add layer to qgis
  
                  self.loading_screen.stop_animation()
                  self.parent.is_calculating = False
                  self.parent.iface.messageBar().pushMessage("RVT", "Visualizations calculated!", level=Qgis.Success)
              else:  # if self.run returns False
                  self.loading_screen.stop_animation()
                  if self.is_calculating:
                      self.parent.iface.messageBar().pushMessage("RVT", "Wait you are already calculating something!",
                                                                 level=Qgis.Warning)
                  elif self.no_raster:
                      self.parent.iface.messageBar().pushMessage("RVT", "You didn't select raster!", level=Qgis.Warning)
                      self.parent.is_calculating = False
                  else:
                      self.parent.iface.messageBar().pushMessage("RVT", "Visualizations calculation Failed!",
                                                                 level=Qgis.Critical)
                      self.parent.is_calculating = False
  
      def compute_visualizations_clicked(self):
          """Start button clicked (Compute visualization button)."""
          task = self.ComputeVisualizationsTask(description="Compute visualizations", parent=self)
          self.tm.addTask(task)  # add task to task manager and start task
  
      def compute_visualizations(self):
          """Compute checked visualizations with set parameters."""
          self.checkbox_float_8bit_check()  # check for float and 8bit checkboxes
          self.load_dlg2default()  # fill rvt.default.DefaultValues(), vis fn parameters
          self.default.save_default_to_file(file_path=self.default_settings_path)
          add_to_qgis = self.dlg.check_addqgis.isChecked()
          selected_input_rasters = self.dlg.select_input_files.checkedItems()
          self.iface.messageBar().clearWidgets()  # clear all messages
          if len(selected_input_rasters) == 0:  # no raster selected
              return "no raster selected"
  
          for raster_name in selected_input_rasters:  # loop trough all selected rasters
              raster_path = self.rvt_select_input[raster_name]
              # get directory to save in
              if self.dlg.check_sav_rast_loc.isChecked():  # means to save in raster path
                  save_dir = os.path.dirname(raster_path)
              else:
                  save_dir = self.dlg.line_save_loc.text()
  
              # compute and save all visualizations
              self.default.save_visualizations(dem_path=raster_path, custom_dir=save_dir)
          return True
  
      class ComputeBlenderTask(QgsTask):
          """Task (thread) for computing Blended image."""
  
          def __init__(self, description, parent):
              super().__init__(description)
              self.parent = parent
              self.loading_screen = LoadingScreenDlg(gif_path=parent.gif_path)  # init loading dlg
              self.loading_screen.start_animation()  # start loading dlg
              self.exception = None
              self.no_raster = False
              self.is_calculating = False
  
          def run(self):
              if self.parent.is_calculating:
                  self.is_calculating = True
                  return False
              else:
                  self.parent.is_calculating = True
                  try:
                      compute = self.parent.compute_blended_image()  # run compute blended image
                      if compute == "no raster selected":
                          self.no_raster = True
                          self.parent.is_calculating = False
                          return False
                      if not compute:
                          self.no_raster = False
                          self.parent.is_calculating = False
                          return False
                      else:
                          self.no_raster = False
                          self.parent.is_calculating = False
                          return True
                  except:  # something went wrong
                      self.parent.is_calculating = False
                      return False
  
          def finished(self, result):  # when finished close loading dlg and load rasters (blended images) into Qgis
              if result:  # if self.run returns True
                  add_to_qgis = self.parent.dlg.check_addqgis.isChecked()
                  selected_input_rasters = self.parent.dlg.select_input_files.checkedItems()
                  # add saved/computed to qgis
                  for raster_name in selected_input_rasters:  # loop trough all selected rasters
                      raster_path = self.parent.rvt_select_input[raster_name]
                      # get directory to save in
                      if self.parent.dlg.check_sav_rast_loc.isChecked():  # means to save in raster path
                          save_dir = os.path.dirname(raster_path)
                      else:
                          save_dir = self.parent.dlg.line_save_loc.text()
  
                      # get combination name from combo box
                      combination_name = str(self.parent.dlg.combo_combinations.currentText())
                      combination_name = combination_name.strip().replace(" ", "_")  # replace spaces with underscore
                      blend_img_name = "{}_{}".format(raster_name, combination_name)
  
                      terrain_sett_name = None
                      if self.parent.dlg.chech_terrain_preset.checkState():
                          terrain_sett_name = str(self.parent.dlg.combo_terrains.currentText())
                          blend_img_name = "{}_{}".format(blend_img_name, terrain_sett_name)
  
                      blend_img_8bit_name = "{}_8bit.tif".format(blend_img_name)
                      blend_img_name = "{}.tif".format(blend_img_name)
  
                      blend_img_path = os.path.abspath(os.path.join(save_dir, blend_img_name))
                      blend_img_8bit_path = os.path.abspath(os.path.join(save_dir, blend_img_8bit_name))
  
                      # checkboxes status
                      save_float = self.parent.dlg.check_blender_save_float.isChecked()
                      save_8bit = self.parent.dlg.check_blender_save_8bit.isChecked()
  
                      if add_to_qgis:  # add calculated layers to Qgis
                          if save_float:
                              self.parent.remove_layer_by_path(blend_img_path)  # remove layer from qgis if exists
                              self.parent.iface.addRasterLayer(blend_img_path, blend_img_name)  # add layer to qgis
                          if save_8bit:
                              self.parent.remove_layer_by_path(blend_img_8bit_path)  # remove layer from qgis if exists
                              self.parent.iface.addRasterLayer(blend_img_8bit_path, blend_img_8bit_name)  # add to qgis
  
                  self.loading_screen.stop_animation()
                  self.parent.is_calculating = False
                  self.parent.iface.messageBar().pushMessage("RVT", "Blended image calculated!", level=Qgis.Success)
              else:  # if self.run returns False
                  self.loading_screen.stop_animation()
                  if self.is_calculating:
                      self.parent.iface.messageBar().pushMessage("RVT", "Wait you are already calculating something!",
                                                                 level=Qgis.Warning)
                  elif self.no_raster:
                      self.parent.iface.messageBar().pushMessage("RVT", "You didn't select raster!", level=Qgis.Warning)
                      self.parent.is_calculating = False
                  else:
                      self.parent.iface.messageBar().pushMessage("RVT", "Blended image calculation Failed!",
                                                                 level=Qgis.Critical)
                      self.parent.is_calculating = False
  
      def compute_blended_image_clicked(self):
          """Start button clicked (Compute visualization button)."""
          task = self.ComputeBlenderTask(description="Compute blended image", parent=self)
          self.tm.addTask(task)  # add task to task manager and start task
  
      def compute_blended_image(self):
          """Compute Blended image from set parameters (in blender dlg). (blend images button clicked)"""
          selected_input_rasters = self.dlg.select_input_files.checkedItems()
  
          self.iface.messageBar().clearWidgets()  # clear all messages
          if len(selected_input_rasters) == 0:  # no raster selected
              return "no raster selected"
          for raster_name in selected_input_rasters:  # loop trough all selected rasters
              start_time = time.time()
              raster_path = self.rvt_select_input[raster_name]
  
              if self.dlg.check_sav_rast_loc.isChecked():  # means to save in raster path
                  save_dir = os.path.dirname(raster_path)
              else:
                  save_dir = self.dlg.line_save_loc.text()
  
              combination_name = str(self.dlg.combo_combinations.currentText())  # get combination name from combo
              combination_name_u = combination_name.strip().replace(" ", "_")  # replace spaces with underscore
              blend_img_name = "{}_{}".format(raster_name, combination_name_u)
  
              # custom advanced (hard coded) blender combinations (can't be selected in dialog)
              if combination_name == "Archaeological combined (VAT combined)" or \
                      combination_name == "enhanced Multi-Scale Topographic Position version 3":
                  self.blend_advanced_custom_combination(combination_name=combination_name, raster_name=raster_name,
                                                         save_dir=save_dir)
              # normal dialog blender combination
              else:
                  terrain_sett_name = None
                  if self.dlg.chech_terrain_preset.checkState():
                      terrain_sett_name = str(self.dlg.combo_terrains.currentText())
                      blend_img_name = "{}_{}".format(blend_img_name, terrain_sett_name)
  
                  blend_img_name = "{}.tif".format(blend_img_name)
                  blend_img_path = os.path.abspath(os.path.join(save_dir, blend_img_name))
                  dict_arr_res = rvt.default.get_raster_arr(raster_path)
                  self.load_dlg2combination()
                  self.load_dlg2default()
                  self.combination.add_dem_path(raster_path)
                  save_vis = self.dlg.check_blender_save_vis.isChecked()
                  self.combination.add_dem_arr(dem_arr=dict_arr_res["array"],
                                               dem_resolution=dict_arr_res["resolution"][0])
                  save_float = self.dlg.check_blender_save_float.isChecked()
                  save_8bit = self.dlg.check_blender_save_8bit.isChecked()
                  self.combination.render_all_images(default=self.default, save_visualizations=save_vis,
                                                     save_render_path=blend_img_path, save_float=save_float,
                                                     save_8bit=save_8bit)
                  end_time = time.time()
                  compute_time = end_time - start_time
                  self.combination.create_log_file(dem_path=raster_path, combination_name=combination_name,
                                                   render_path=blend_img_path, terrain_sett_name=terrain_sett_name,
                                                   default=self.default, custom_dir=save_dir,
                                                   computation_time=compute_time)
          return True
  
      class ComputeCutoff(QgsTask):
          """Task (thread) for applying cutoff on raster image."""
  
          def __init__(self, description, parent):
              super().__init__(description)
              self.parent = parent
              self.loading_screen = LoadingScreenDlg(gif_path=parent.gif_path)  # init loading dlg
              self.loading_screen.start_animation()  # start loading dlg
              self.exception = None
              self.no_raster = False
              self.is_calculating = False
              self.no_selected_parameters = False
  
          def run(self):
              if self.parent.is_calculating:
                  self.is_calculating = True
                  return False
              else:
                  self.parent.is_calculating = True
                  try:
                      compute = self.parent.compute_cut_off_norm_8bit()  # run compute visualizations
                      if compute == "no raster selected":
                          self.no_raster = True
                          self.parent.is_calculating = False
                          return False
                      elif compute == "no selected parameters":
                          self.no_selected_parameters = True
                          self.parent.is_calculating = False
                          return False
                      else:
                          self.no_raster = False
                          self.parent.is_calculating = False
                          return True
                  except:  # something went wrong
                      return False
  
          def finished(self, result):  # when finished close loading dlg and load rasters (visualizations) into Qgis
              if result:  # if self.run returns True
                  add_to_qgis = self.parent.dlg.check_addqgis.isChecked()
                  selected_input_rasters = self.parent.dlg.select_input_files.checkedItems()
                  # add saved/computed to qgis
                  for raster_name in selected_input_rasters:  # loop trough all selected rasters
                      raster_path = self.parent.rvt_select_input[raster_name]
                      # get directory to save in
                      if self.parent.dlg.check_sav_rast_loc.isChecked():  # means to save in raster path
                          save_dir = os.path.dirname(raster_path)
                      else:
                          save_dir = self.parent.dlg.line_save_loc.text()
  
                      if add_to_qgis:  # add calculated layers to Qgis
                          # get parameters
                          cut_off_mode = str(self.parent.dlg.combo_cutoff_mode.currentText())
                          no_min = False
                          cut_off_min = "min"
                          try:
                              cut_off_min = float(self.parent.dlg.line_cutoff_min.text())
                          except:
                              no_min = True
                          no_max = False
                          cut_off_max = "max"
                          try:
                              cut_off_max = float(self.parent.dlg.line_cutoff_max.text())
                          except:
                              no_max = True
                          cut_off_norm = bool(self.parent.dlg.check_cutoff_norm.isChecked())
                          cut_off_8bit = bool(self.parent.dlg.check_cutoff_8bit.isChecked())
  
                          # get out_raster_name
                          if no_min and no_max:
                              out_raster_name = raster_name
                          else:
                              out_raster_name = raster_name + "_C-OFF_{}_{}_{}".format(cut_off_mode, cut_off_min,
                                                                                       cut_off_max)
                          if cut_off_norm and not cut_off_8bit:
                              out_raster_name += "_norm"
                          elif cut_off_8bit:
                              out_raster_name += "_8bit"
                          out_raster_name += ".tif"
                          out_raster_path = os.path.join(save_dir, out_raster_name)
                          self.parent.remove_layer_by_path(out_raster_path)
                          self.parent.iface.addRasterLayer(out_raster_path, out_raster_name)  # add layer to qgis
  
                  self.loading_screen.stop_animation()
                  self.parent.is_calculating = False
                  self.parent.iface.messageBar().pushMessage("RVT", "Cut-off calculated!", level=Qgis.Success)
              else:  # if self.run returns False
                  self.loading_screen.stop_animation()
                  if self.is_calculating:
                      self.parent.iface.messageBar().pushMessage("RVT", "Wait you are already calculating something!",
                                                                 level=Qgis.Warning)
                  elif self.no_raster:
                      self.parent.iface.messageBar().pushMessage("RVT", "You didn't select raster!", level=Qgis.Warning)
                      self.parent.is_calculating = False
                  elif self.no_selected_parameters:
                      self.parent.iface.messageBar().pushMessage("RVT", "You didn't select any parameters!",
                                                                 level=Qgis.Warning)
                      self.parent.is_calculating = False
                  else:
                      self.parent.iface.messageBar().pushMessage("RVT", "Cut-off calculation Failed!",
                                                                 level=Qgis.Critical)
                      self.parent.is_calculating = False
  
      def compute_cut_off_norm_8bit_clicked(self):
          """Start button clicked (cut_off_norm_8bit start button)."""
          task = self.ComputeCutoff(description="Compute Cut-off", parent=self)
          self.tm.addTask(task)  # add task to task manager and start task
  
      def compute_cut_off_norm_8bit(self):
          """Compute cut_off_norm_8bit with set parameters."""
          selected_input_rasters = self.dlg.select_input_files.checkedItems()
          self.iface.messageBar().clearWidgets()  # clear all messages
          if len(selected_input_rasters) == 0:  # no raster selected
              return "no raster selected"
  
          for raster_name in selected_input_rasters:  # loop trough all selected rasters
              raster_path = self.rvt_select_input[raster_name]
              # get directory to save in
              if self.dlg.check_sav_rast_loc.isChecked():  # means to save in raster path
                  save_dir = os.path.dirname(raster_path)
              else:
                  save_dir = self.dlg.line_save_loc.text()
  
              # get raster arr
              dict_rast = rvt.default.get_raster_arr(raster_path=raster_path)
              raster_arr = dict_rast["array"]
              raster_no_data = dict_rast["no_data"]
  
              # get parameters
              cut_off_mode = str(self.dlg.combo_cutoff_mode.currentText())
              no_min = False
              cut_off_min = "min"
              try:
                  cut_off_min = float(self.dlg.line_cutoff_min.text())
              except:
                  no_min = True
              no_max = False
              cut_off_max = "max"
              try:
                  cut_off_max = float(self.dlg.line_cutoff_max.text())
              except:
                  no_max = True
              cut_off_norm = bool(self.dlg.check_cutoff_norm.isChecked())
              cut_off_8bit = bool(self.dlg.check_cutoff_8bit.isChecked())
  
              # get out_raster_name
              if no_min and no_max:
                  out_raster_name = raster_name
              else:
                  out_raster_name = raster_name + "_C-OFF_{}_{}_{}".format(cut_off_mode, cut_off_min, cut_off_max)
              if cut_off_norm and not cut_off_8bit:
                  out_raster_name += "_norm"
              elif cut_off_8bit:
                  out_raster_name += "_8bit"
              out_raster_name += ".tif"
              out_raster_path = os.path.join(save_dir, out_raster_name)
  
              # if nothing to do
              if no_min and no_max and not cut_off_norm and not cut_off_8bit:
                  return "no selected parameters"
              if no_min:
                  cut_off_min = None
              if no_max:
                  cut_off_max = None
  
              if cut_off_8bit:
                  cut_off_norm = True
              cut_off_arr = rvt.blend_func.cut_off_normalize(image=raster_arr, mode=cut_off_mode, min=cut_off_min,
                                                             max=cut_off_max, bool_norm=cut_off_norm)
  
              if cut_off_8bit:
                  cut_off_arr = rvt.vis.byte_scale(cut_off_arr)
                  rvt.default.save_raster(src_raster_path=raster_path, out_raster_path=out_raster_path,
                                          out_raster_arr=cut_off_arr, no_data=np.nan, e_type=1)
              else:
                  rvt.default.save_raster(src_raster_path=raster_path, out_raster_path=out_raster_path,
                                          out_raster_arr=cut_off_arr, no_data=np.nan, e_type=6)
          return True
  
      class ComputeFillNoData(QgsTask):
          """Task (thread) for filling no data on raster image."""
  
          def __init__(self, description, parent):
              super().__init__(description)
              self.parent = parent
              self.loading_screen = LoadingScreenDlg(gif_path=parent.gif_path)  # init loading dlg
              self.loading_screen.start_animation()  # start loading dlg
              self.exception = None
              self.no_raster = False
              self.is_calculating = False
              self.no_selected_parameters = False
  
          def run(self):
              if self.parent.is_calculating:
                  self.is_calculating = True
                  return False
              else:
                  self.parent.is_calculating = True
                  try:
                      compute = self.parent.compute_fill_no_data()  # run compute visualizations
                      if compute == "no raster selected":
                          self.no_raster = True
                          self.parent.is_calculating = False
                          return False
                      elif compute == "no selected parameters":
                          self.no_selected_parameters = True
                          self.parent.is_calculating = False
                          return False
                      else:
                          self.no_raster = False
                          self.parent.is_calculating = False
                          return True
                  except:  # something went wrong
                      return False
  
          def finished(self, result):  # when finished close loading dlg and load rasters (visualizations) into Qgis
              if result:  # if self.run returns True
                  add_to_qgis = self.parent.dlg.check_addqgis.isChecked()
                  selected_input_rasters = self.parent.dlg.select_input_files.checkedItems()
                  # add saved/computed to qgis
                  for raster_name in selected_input_rasters:  # loop trough all selected rasters
                      raster_path = self.parent.rvt_select_input[raster_name]
                      # get directory to save in
                      if self.parent.dlg.check_sav_rast_loc.isChecked():  # means to save in raster path
                          save_dir = os.path.dirname(raster_path)
                      else:
                          save_dir = self.parent.dlg.line_save_loc.text()
  
                      if add_to_qgis:  # add calculated layers to Qgis
                          fill_method = self.parent.fill_method_translate(str(
                              self.parent.dlg.combo_fill_method.currentText()))
                          out_raster_name = raster_name + "_NODATA_{}".format(fill_method)
                          out_raster_name += ".tif"
                          out_raster_path = os.path.join(save_dir, out_raster_name)
                          self.parent.remove_layer_by_path(out_raster_path)
                          self.parent.iface.addRasterLayer(out_raster_path, out_raster_name)  # add layer to qgis
  
                  self.loading_screen.stop_animation()
                  self.parent.is_calculating = False
                  self.parent.iface.messageBar().pushMessage("RVT", "Fill no data calculated!", level=Qgis.Success)
              else:  # if self.run returns False
                  self.loading_screen.stop_animation()
                  if self.is_calculating:
                      self.parent.iface.messageBar().pushMessage("RVT", "Wait you are already calculating something!",
                                                                 level=Qgis.Warning)
                  elif self.no_raster:
                      self.parent.iface.messageBar().pushMessage("RVT", "You didn't select raster!", level=Qgis.Warning)
                      self.parent.is_calculating = False
                  else:
                      self.parent.iface.messageBar().pushMessage("RVT", "Fill no-data calculation Failed!",
                                                                 level=Qgis.Critical)
                      self.parent.is_calculating = False
  
      def compute_fill_no_data_clicked(self):
          """Start button clicked (compute_fill_no_data start button)."""
          task = self.ComputeFillNoData(description="Compute fill no data", parent=self)
          self.tm.addTask(task)  # add task to task manager and start task
  
      def compute_fill_no_data(self):
          """Compute compute_fill_no_data with set parameters."""
          selected_input_rasters = self.dlg.select_input_files.checkedItems()
          self.iface.messageBar().clearWidgets()  # clear all messages
          if len(selected_input_rasters) == 0:  # no raster selected
              return "no raster selected"
  
          for raster_name in selected_input_rasters:  # loop trough all selected rasters
              raster_path = self.rvt_select_input[raster_name]
              # get directory to save in
              if self.dlg.check_sav_rast_loc.isChecked():  # means to save in raster path
                  save_dir = os.path.dirname(raster_path)
              else:
                  save_dir = self.dlg.line_save_loc.text()
  
              # get raster arr
              dict_rast = rvt.default.get_raster_arr(raster_path=raster_path)
              raster_arr = dict_rast["array"]
              raster_no_data = dict_rast["no_data"]
  
              # get parameters
              fill_method = self.fill_method_translate(str(self.dlg.combo_fill_method.currentText()))
              # get out_raster_name
              out_raster_name = raster_name + "_NODATA_{}".format(fill_method)
              out_raster_name += ".tif"
              out_raster_path = os.path.join(save_dir, out_raster_name)
  
              raster_arr[raster_arr == raster_no_data] = np.nan
              raster_arr = rvt.vis.fill_where_nan(
                  raster_arr, self.fill_method_translate(self.dlg.combo_fill_method.currentText()))
  
              rvt.default.save_raster(src_raster_path=raster_path, out_raster_path=out_raster_path,
                                      out_raster_arr=raster_arr, no_data=np.nan, e_type=6)
          return True
  
      def load_terrains_settings2dlg(self):
          """Fills preset terrains settings combo box."""
          self.dlg.combo_terrains.clear()
          for terrain_settings in self.terrains_settings.terrains_settings:
              self.dlg.combo_terrains.addItem(terrain_settings.name)
  
      def blend_advanced_custom_combination(self, combination_name, raster_name, save_dir):
          """Method for blending advanced custom combination (for example: combination uses other combinations as layers)
           that can't be built in dialog. These combinations are hard coded."""
          save_vis = self.dlg.check_blender_save_vis.isChecked()
          save_float = self.dlg.check_blender_save_float.isChecked()
          save_8bit = self.dlg.check_blender_save_8bit.isChecked()
          if combination_name == "Archaeological combined (VAT combined)":
              # 1st layer: VAT general 50% transparency, 2nd layer: VAT flat
              start_time = time.time()
              self.dlg.chech_terrain_preset.setCheckState(False)  # disable terrain settings
              combination_name_u = combination_name.strip().replace(" ", "_")  # replace spaces with underscore
              blend_img_path = os.path.abspath(
                  os.path.join(save_dir, "{}_{}.tif".format(raster_name, combination_name_u)))
              # 2nd layer: VAT general, 1st layer: VAT flat with 50% transparency
              vat_combination_json_path = os.path.abspath(os.path.join(self.plugin_dir, "settings", "blender_VAT.json"))
  
              default_1 = rvt.default.DefaultValues()  # VAT general
              default_2 = rvt.default.DefaultValues()  # VAT flat
  
              vat_combination_1 = rvt.blend.BlenderCombination()  # VAT general
              vat_combination_2 = rvt.blend.BlenderCombination()  # VAT flat
              vat_combination_1.read_from_file(vat_combination_json_path)
              vat_combination_2.read_from_file(vat_combination_json_path)
              terrains_settings = rvt.blend.TerrainsSettings()
              terrains_settings.read_from_file(self.terrains_settings_path)
              terrain_1 = terrains_settings.select_terrain_settings_by_name("general")  # VAT general
              terrain_2 = terrains_settings.select_terrain_settings_by_name("flat")  # VAT flat
              terrain_1.apply_terrain(default=default_1, combination=vat_combination_1)  # VAT general
              terrain_2.apply_terrain(default=default_2, combination=vat_combination_2)  # VAT flat
  
              raster_path = self.rvt_select_input[raster_name]
              dict_arr_res_nd = rvt.default.get_raster_arr(raster_path=raster_path)
  
              vat_combination_1.add_dem_arr(dem_arr=dict_arr_res_nd["array"],
                                            dem_resolution=dict_arr_res_nd["resolution"][0])
              vat_arr_1 = vat_combination_1.render_all_images(default=default_1, no_data=dict_arr_res_nd["no_data"])
              vat_combination_2.add_dem_arr(dem_arr=dict_arr_res_nd["array"],
                                            dem_resolution=dict_arr_res_nd["resolution"][0])
              vat_arr_2 = vat_combination_2.render_all_images(default=default_2, no_data=dict_arr_res_nd["no_data"])
  
              # blend VAT general and VAT flat together
              combination = rvt.blend.BlenderCombination()
              combination.create_layer(vis_method="VAT general", image=vat_arr_1, normalization="Value", minimum=0,
                                       maximum=1, blend_mode="Normal", opacity=50)
              combination.create_layer(vis_method="VAT flat", image=vat_arr_2, normalization="Value", minimum=0,
                                       maximum=1, blend_mode="Normal", opacity=100)
  
              combination.add_dem_path(dem_path=raster_path)
              combination.render_all_images(save_render_path=blend_img_path, save_visualizations=save_vis,
                                            save_float=save_float, save_8bit=save_8bit,
                                            no_data=dict_arr_res_nd["no_data"])
              end_time = time.time()
              compute_time = end_time - start_time
              self.combination.create_log_file(dem_path=raster_path, combination_name=combination_name,
                                               render_path=blend_img_path, default=self.default,
                                               custom_dir=save_dir, computation_time=compute_time)
              return True
          elif combination_name == "enhanced Multi-Scale Topographic Position version 3":
              start_time = time.time()
              self.dlg.chech_terrain_preset.setCheckState(False)  # disable terrain settings
              combination_name_u = combination_name.strip().replace(" ", "_")  # replace spaces with underscore
              blend_img_path = os.path.abspath(
                  os.path.join(save_dir, "{}_{}.tif".format(raster_name, combination_name_u)))
              blend_img_8bit_path = os.path.abspath(
                  os.path.join(save_dir, "{}_{}_8bit.tif".format(raster_name, combination_name_u)))
              raster_path = self.rvt_select_input[raster_name]
              dict_arr_res_nd = rvt.default.get_raster_arr(raster_path=raster_path)
  
              e3mstp_arr = rvt.blend.e3mstp(dem=dict_arr_res_nd["array"], resolution=dict_arr_res_nd["resolution"][0],
                                            no_data=dict_arr_res_nd["no_data"], default=self.default)
              if save_float:
                  rvt.default.save_raster(src_raster_path=raster_path, out_raster_path=blend_img_path,
                                          out_raster_arr=e3mstp_arr, no_data=np.nan, e_type=6)
              if save_8bit:
                  rvt.default.save_raster(src_raster_path=raster_path, out_raster_path=blend_img_8bit_path,
                                          out_raster_arr=rvt.vis.byte_scale(e3mstp_arr, c_min=0.0, c_max=1.0),
                                          no_data=np.nan, e_type=1)
  
              end_time = time.time()
              compute_time = end_time - start_time
              self.combination.create_log_file(dem_path=raster_path, combination_name=combination_name,
                                               render_path=blend_img_path, default=self.default,
                                               custom_dir=save_dir, computation_time=compute_time)
              return True
          else:
              return False
  
      def save_plugin_size(self, json_path):
          try:
              dat = open(json_path, "w")
              size_dlg = self.dlg.size()
              out_json = {"width": size_dlg.width(), "height": size_dlg.height()}
              json.dump(out_json, dat)
              dat.close()
          except:
              pass
  
      def load_plugin_size(self, json_path):
          try:
              dat = open(json_path, "r")
              in_json = json.load(dat)
              dat.close()
              self.dlg.resize(in_json["width"], in_json["height"])
          except:
              pass
  