# -*- coding: utf-8 -*-
"""
/***************************************************************************
 DroneSurveyingPlanning
                                 A QGIS plugin
 This plugin allows users to plan an aerial survey.
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2020-08-21
        git sha              : $Format:%H$
        copyright            : (C) 2020 by Politecnico di Milano
        email                : marta1.rossi@mail.polimi.it
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 json
import os

from qgis.PyQt import QtWidgets
from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication
from qgis.PyQt.QtGui import *
from qgis.PyQt.QtWidgets import QAction, QFileDialog, QMessageBox
from qgis.core import *
import geopandas as gpd
from scipy.spatial import distance
from shapely.geometry import Point

from .HELP_dialog import Ui_HELP as DSPHELP
from .DSP_outputs import Ui_Outputs as DSPoutputs
# Import the code for the dialog
from .DSP_dialog import DroneSurveyingPlanningDialog
from .NewDrone import Ui_Dialog as drone
from .NewSensor import Ui_Dialog as sensor
from .progress_msg import Ui_Dialog as progress_msg
from .survey_planning import (NormalCaseMethod,
                              SimulationMethod,
                              SimulationDTM,
                              DroneMaxSpeedError,
                              DroneBatteryError,
                              DroneAltitudeError,
                              UnfilledError,
                              SensorError)


class NoDTMError(KeyError):
    pass

class NoSHPError(KeyError):
    pass

class RadioButtonError(AttributeError):
    pass

class NoFileError(Exception):
    pass

def get_shp_dim(shp):
    gdf = gpd.read_file(shp)
    max_x = float("-inf")
    max_y = float("-inf")
    min_x = float("inf")
    min_y = float("inf")

    # iteration through polygon`s points
    for index, row in gdf.iterrows():
        for pt in list(row['geometry'].exterior.coords):
            xy = Point(pt)
            if xy.x < min_x:
                min_x = xy.x
            elif xy.x > max_x:
                max_x = xy.x

            if xy.y < min_y:
                min_y = xy.y
            elif xy.y > max_y:
                max_y = xy.y

    ####computing distances of the  surveying area
    p_1 = (min_x, min_y)
    p_2 = (max_x, min_y)
    p_3 = (max_x, max_y)

    return [distance.euclidean(p_1, p_2), distance.euclidean(p_2, p_3)]

#define new dialog windows
class DroneDialog(QtWidgets.QDialog):
    def __init__(self, parent):
        super(DroneDialog, self).__init__(parent)
        self.ui = drone()
        self.ui.setupUi(self)

class SensorDialog(QtWidgets.QDialog):
    def __init__(self, parent):
        super(SensorDialog, self).__init__(parent)
        self.ui = sensor()
        self.ui.setupUi(self)

class DSPHELPDialog(QtWidgets.QDialog):
    def __init__(self, parent):
        super(DSPHELPDialog, self).__init__(parent)
        self.ui = DSPHELP()
        self.ui.setupUi(self)

class ProgressMsg(QtWidgets.QDialog):
    def __init__(self, parent):
        super(ProgressMsg, self).__init__(parent)
        self.ui = progress_msg()
        self.ui.setupUi(self)

class DSPoutputsDialog(QtWidgets.QDialog):
    def __init__(self, parent):
        super(DSPoutputsDialog, self).__init__(parent)
        self.ui = DSPoutputs()
        self.ui.setupUi(self)

# Initialize Qt resources from file resources.py
class DroneSurveyingPlanning:
    """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',
            'DroneSurveyingPlanning_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)


        drone_file = open(os.path.join(self.plugin_dir, 'drone_list.json'))
        self.drone_list = json.load(drone_file)['DroneList']

        sensor_file = open(os.path.join(self.plugin_dir, 'sensor_list.json'))
        self.sensor_list = json.load(sensor_file)['SensorList']

        self.setup_imagery()

        # this dictionaries will map the file name to the file path
        self.dtm_list = dict()
        self.shp_list = dict()

        # Create the dialog (after translation) and keep reference
        self.dlg = DroneSurveyingPlanningDialog()

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&DSP')

        # Check if plugin was started the first time in current QGIS session
        # Must be set in initGui() to survive plugin reloads
        self.first_start = None

    # 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('DroneSurveyingPlanning', 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.addPluginToMenu(
                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 = ':/plugins/DSP/icon.png'
        self.add_action(
            icon_path,
            text=self.tr(u'DSP'),
            callback=self.run,
            parent=self.iface.mainWindow())

        self.loadDrone()
        self.loadSensor()
        # will be set False in run()
        self.first_start = True
        # manage shapefile and DTM as inputs
        self.dlg.tb_invector.clicked.connect(self.openVector)
        self.dlg.cb_invector.currentIndexChanged.connect(self.update_area_dim)
        self.dlg.tb_inDTM.clicked.connect(self.openDTM)
        # open new drone and new sensor window
        self.dlg.pb_drone.clicked.connect(self.open_drone_dialog)
        self.dlg.pb_sensor.clicked.connect(self.open_sensor_dialog)
        # display and change web basemaps
        self.dlg.google.clicked.connect(self.change_imagery)
        self.dlg.esri.clicked.connect(self.change_imagery)
        self.dlg.bing.clicked.connect(self.change_imagery)
        # RUN, HELP, CLOSE buttons managing in main dialog
        self.dlg.RUN.clicked.connect(self.run_function_handler)
        self.dlg.HelpPushButton.clicked.connect(self.open_DSPHELP_dialog)
        self.dlg.close_button.clicked.connect(self.dlg.close)

    def change_imagery(self):
        ''' this function changes the current web basemap in QGIS according to the user selection'''
        if self.dlg.google.isChecked():
            imagery = 'Google Satellite'
        elif self.dlg.esri.isChecked():
            imagery = 'ESRI Satellite'
        elif self.dlg.bing.isChecked():
            imagery = 'Bing Satellite'
        else:
            raise Exception

        try:
            QgsProject.instance().removeMapLayer(self.current_imagery)
        except AttributeError:
            pass

        self.current_imagery = QgsRasterLayer(self.imagery_dict[imagery], imagery, "wms")
        QgsProject.instance().addMapLayer(self.current_imagery)

    def update_area_dim(self):
        """Add the dimensions of the shapefile to the boxes when you choose a shapefile"""
        shp_name = self.dlg.cb_invector.currentText()
        try:
            self.dlg.Xdim.setValue(int(self.shp_list[shp_name]["X"]))
            self.dlg.Ydim.setValue(int(self.shp_list[shp_name]["Y"]))
        except KeyError:
            pass

    def setup_imagery(self):
        ''' this function add common web basemaps to QGIS '''
        # connection type, url, zmax, zmin
        self.current_imagery = None
        self.imagery_dict = dict()
        self.imagery_dict['Google Satellite'] = "type=xyz&url=https://mt1.google.com/vt/lyrs%3Ds%26x%3D%7Bx%7D%26y%3D%7By%7D%26z%3D%7Bz%7D&zmax=19&zmin=0"
        self.imagery_dict['ESRI Satellite'] = "type=xyz&url=https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/%7Bz%7D/%7By%7D/%7Bx%7D&zmax=19&zmin=0"
        self.imagery_dict['Bing Satellite'] ="type=xyz&url=http://ecn.t3.tiles.virtualearth.net/tiles/a{q}.jpeg?g=1&zmax=19&zmin=0"

    def loadDrone(self):
        """Add the created new drone to the list of drones in main DSP dialog"""
        self.dlg.cb_drone.clear()
        drone_combobox = []
        for drone in self.drone_list:
            drone_combobox.append(drone['DroneName'])

        self.dlg.cb_drone.addItems(drone_combobox)

    def loadSensor(self):
        """Add the created new sensor to the list of sensors in main DSP dialog"""
        self.dlg.cb_sensor.clear()
        sensor_combobox = []
        for sensor in self.sensor_list:
            sensor_combobox.append(sensor['SensorName'])

        self.dlg.cb_sensor.addItems(sensor_combobox)

    def loadVectors(self):
        """Load vectors from QGIS table of contents"""
        self.dlg.cb_invector.clear()
        layers = [layer for layer in QgsProject.instance().mapLayers().values()]
        vector_layers = []
        for layer in layers:
            if layer.type() == QgsMapLayer.VectorLayer:
                vector_layers.append(layer.name())
                X, Y = get_shp_dim(layer.source())
                self.shp_list[layer.name()] = {
                    "path": layer.source(),  # maps the path to the file name
                    "X": X,
                    "Y": Y
                }
        self.dlg.cb_invector.addItems(vector_layers)

    def openVector(self):
        """Open vector from file dialog"""
        inFile = str(QFileDialog.getOpenFileName(caption = "Open shapefile",
                                                 filter = "Shapefiles(*.shp)")[0])
        if inFile is not None:
            self.file_name_shp = str.split(os.path.basename(inFile), ".")[0]
            self.iface.addVectorLayer(inFile, self.file_name_shp, "ogr")
            self.loadVectors()

    def loadDTM(self):
        """Load DTM from QGIS table of contents"""
        self.dlg.cb_inDTM.clear()

        layers = [layer for layer in QgsProject.instance().mapLayers().values()]
        DTM_layers = []
        for layer in layers:
            if layer.type() == QgsMapLayer.RasterLayer:
                DTM_layers.append(layer.name())
                self.dtm_list[layer.name()] = layer.source()  # maps the path to the file name

        self.dlg.cb_inDTM.addItems(DTM_layers)

    def openDTM(self):
        """Open DTM from file dialog"""
        inFile = str(QFileDialog.getOpenFileName(caption="Open DTM",
                                                 filter="GeoTiff(*.tif)")[0])
        if inFile is not None:
            self.file_name_d = str.split(os.path.basename(inFile), ".")[0]
            self.iface.addRasterLayer(inFile, self.file_name_d)
            self.loadDTM()

    def open_drone_dialog(self):
        """Open drone dialog to create a new drone"""
        self.droneDialog = DroneDialog(parent=self.dlg)
        self.droneDialog.ui.pb_okdrone.clicked.connect(self.collect_drone)
        self.droneDialog.ui.pb_close.clicked.connect(self.droneDialog.close)
        self.droneDialog.show()

    def collect_drone(self):
        """Collect the new drone attributes from drone dialog to main dialog"""
        new_drone = dict()
        new_drone['DroneName'] = self.droneDialog.ui.i_name.text()
        new_drone['MaxAltitude'] = self.droneDialog.ui.sb_altitude.value()
        new_drone['MaxSpeed'] = self.droneDialog.ui.sb_speed.value()
        new_drone['Battery'] = self.droneDialog.ui.sb_battery.value()
        self.drone_list.insert(0, new_drone)
        self.loadDrone()
        self.droneDialog.close()

    def open_sensor_dialog(self):
        """Open dialog to create a new sensor"""
        self.sensorDialog = SensorDialog(parent=self.dlg)
        self.sensorDialog.ui.pb_oksensor.clicked.connect(self.collect_sensor)
        self.sensorDialog.ui.pb_close.clicked.connect(self.sensorDialog.close)
        self.sensorDialog.show()

    def collect_sensor(self):
        """Collect the new sensor attributes """
        new_sensor = dict()
        new_sensor['SensorName'] = self.sensorDialog.ui.i_name.text()
        new_sensor['FocalLength'] = self.sensorDialog.ui.sb_fl.value()
        new_sensor['ShootInterval'] = self.sensorDialog.ui.sb_si.value()
        new_sensor['SizeX'] = self.sensorDialog.ui.sb_sizex.value()
        new_sensor['SizeY'] = self.sensorDialog.ui.sb_sizey.value()
        new_sensor['ImgSizeX'] = self.sensorDialog.ui.sb_imgsizex.value()
        new_sensor['ImgSizeY'] = self.sensorDialog.ui.sb_imgsizey.value()
        self.sensor_list.insert(0, new_sensor)
        self.loadSensor()
        self.sensorDialog.close()

    def open_DSPHELP_dialog(self):
        """Opens the HELP dialog containing a description of plugin fields"""
        dsp_help = DSPHELPDialog(parent=self.dlg)
        dsp_help.show()

    def show_popup(self, title, message):
        msg = QMessageBox()
        msg.setWindowTitle(title)
        msg.setText(message)
        msg.exec()
        return msg

    def overlap_map(self):
        """plots the overlapping map of our method"""
        self.method.plot_overlapping()

    def error_map(self):
        """plots the error map of our method"""
        self.method.plot_error()

    def export_handler(self):
        try:
            self.ExportResults()
        except NoFileError:
            pass

    def ExportResults(self):
        """Export inputs and outputs results as csv file"""
        fileName = str(QFileDialog.getSaveFileName(caption='Save file', filter=".csv")[0])

        # Check if export path is empty:
        if fileName is None:
            raise NoFileError
        # Check to make sure the file has the correct extension:
        if '.csv' != fileName[-4:]:
            fileName += '.csv'

        file = open(fileName, 'w')

        text = "User inputs: " \
               "\n\n Drone parameters:" \
               "\n Drone name, Max. Altitude [m], UAS Max Speed [km/h], Battery Duration [min]\n"\
               + str(self.method.drone_name) + "," + str(self.method.UAS_maxAltitude)\
               + "," + str(self.method.UAS_v) + "," + str(self.method.battery) +\
                "\n\n Sensor parameters:"\
                "\n Sensor name, Focal Lenght [mm], Shooting Interval [s], Sensor Size in X [mm]," \
                "Sensor Size in Y [mm], Image Size in X [px], Image Size in Y [px]\n" \
               + str(self.method.sensor_name) + "," + str(self.method.c) + "," + str(self.method.shooting_int) + "," \
               + str(self.method.fw) + "," + str(self.method.fh) + "," + str(self.method.npx) + ","+ str(self.method.npy) +\
                "\n\n Planning Parameters:"\
                "\n Flight Height [m], Along TRack Overlapping [%], Cross Track Overlapping [%]\n" \
               + str(self.method.h) + ","+ str(self.method.Rl) + ","+ str(self.method.Rt)+\
                "\n\n Model for accuracy prediction:\n" + str(self.method.method_name) +\
                "\n\n Collimation Error [px]:\n" + str(self.method.scoll) +\
                "\n\n\n Flight parameters - outputs:"\
                "\n\n GSDw [m], GSDh [m], " \
                "Image Footprint Width [m], Image Footprint Height [m], Pixel Size [mm]," \
                " UAS min speed [m/s], Max distance allowed [m], Max dist in project [m]," \
                " Baseline [m], Real Baseline [m], Interaxie [m], Real interaxie [m]," \
                " Number of stripes in X, Number of stripes in Y, Real Longitudinal Overlapping [%]," \
                "Real Transversal Overlapping [%], Total number of images\n" + str(self.method.GSDw) + "," + str(self.method.GSDh) + "," +\
                str(self.method.W) + "," + str(self.method.H) + "," + str(self.method.pixel_size) + "," + str(self.method.UAS_v_min)\
                + "," + str(self.method.max_distance) + "," + str(self.method.max_distance_proj)\
                + "," + str(self.method.b) + "," + str(self.method.b_real) + "," + str(self.method.interaxie)\
                + "," + str(self.method.i_real) + "," + str(self.method.nstrip_x) + "," + str(self.method.nstrip_y)\
                + "," + str(self.method.Rl_real) + "," + str(self.method.Rt_real) + "," + str(self.method.num_images)\

        file.write(text)
        file.close()

    def open_DSPoutputs_dialog(self):
        """Opens an outputs dialog containing the computed results"""
        self.outputs = DSPoutputsDialog(parent=self.dlg)
        outputs = self.outputs
        outputs.ui.GSDw.setText("{:.4f}".format(self.method.GSDw))
        outputs.ui.GSDh.setText("{:.4f}".format(self.method.GSDh))
        outputs.ui.footprint_W.setText("{:.4f}".format(self.method.W))
        outputs.ui.footprint_H.setText("{:.4f}".format(self.method.H))
        outputs.ui.px_size.setText("{:.4f}".format(self.method.pixel_size))
        outputs.ui.UAS_min_speed.setText("{:.4f}".format(self.method.UAS_v_min))
        outputs.ui.max_dist.setText("{:.4f}".format(self.method.max_distance))
        outputs.ui.max_dist_proj.setText("{:.4f}".format(self.method.max_distance_proj))
        outputs.ui.baseline.setText("{:.4f}".format(self.method.b))
        outputs.ui.b_real.setText("{:.4f}".format(self.method.b_real))
        outputs.ui.interaxie.setText("{:.4f}".format(self.method.interaxie))
        outputs.ui.i_real.setText("{:.4f}".format(self.method.i_real))
        outputs.ui.nstrip_x.setText("{:.4f}".format(self.method.nstrip_x))
        outputs.ui.nstrip_y.setText("{:.4f}".format(self.method.nstrip_y))
        outputs.ui.n_img.setText("{:.4f}".format(self.method.num_images))
        outputs.ui.Rl_real.setText("{:.4f}".format(self.method.Rl_real))
        outputs.ui.Rt_real.setText("{:.4f}".format(self.method.Rt_real))
        outputs.ui.Rt.setText("{:.4f}".format(self.method.Rt))
        outputs.ui.Rl.setText("{:.4f}".format(self.method.Rl))
        outputs.ui.h.setText("{:.4f}".format(self.method.h))
        outputs.ui.pb_error.clicked.connect(self.error_map)
        outputs.ui.pb_overlap.clicked.connect(self.overlap_map)
        outputs.ui.tb_browse.clicked.connect(self.export_handler)

        outputs.show()

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(
                self.tr(u'&DSP'),
                action)
            self.iface.removeToolBarIcon(action)

    def find_dict_in_list(self, list_, key_name, key_value):
        for i in list_:
            if i[key_name] == key_value:
                return i
        return list_[0]

    def run_function_handler(self):
        try:
            self.run_function()
        except RadioButtonError:
            self.show_popup(title="ERROR",
                            message="You must select both Planning Parameters and a Model for Accuracy Prediction")
        except NoDTMError:
            self.show_popup(title="ERROR",
                            message="You must insert a (valid) DTM to run the Simulation-using-DTM method")
        except NoSHPError:
            self.show_popup(title="ERROR",
                            message="You must insert a (valid) shapefile to run the Simulation-using-DTM method")
        except UnfilledError:
            self.show_popup(title="ERROR", message="Fill all input parameters")

        except SensorError:
            self.show_popup(title="ERROR", message="Fill all sensor parameters")

        except DroneMaxSpeedError:
            self.show_popup(title="ERROR", message="The required drone speed is bigger than the maximum speed.\n" +
                        "Please choose a drone with bigger UAS Max. Speed.")

        except DroneAltitudeError:
            self.show_popup(title="ERROR", message="The flight height is bigger than the drone maximum flight height.\n" +
                        "Please choose a drone with higher Max. Altitude or a lower Flight Height.")

        except DroneBatteryError:
            self.show_popup(title="ERROR", message="The drone battery is not enough to cover the flight path.\n" +
                        "Please choose a drone with a longer Battery Duration.")

    def run_function(self):
        drone = self.find_dict_in_list(list_=self.drone_list,
                                       key_name='DroneName',
                                       key_value=str(self.dlg.cb_drone.currentText()))
        sensor = self.find_dict_in_list(list_=self.sensor_list,
                                        key_name='SensorName',
                                        key_value=str(self.dlg.cb_sensor.currentText()))
        # size of the surveyed area
        X = self.dlg.Xdim.value()  # [m]
        Y = self.dlg.Ydim.value()  # [m]
        GSD = self.dlg.GSD_dim.value()  # [m]
        target_sz = self.dlg.target_sz.value()  # [mm]

        # check if user choose "Manual" set of flight planning parameters
        if self.dlg.Manual.isChecked():
            h = self.dlg.h.value()
            Rl = [self.dlg.Rl.value()]
            Rt = [self.dlg.Rt.value()]

        # check if user choose "Automatic generation" of flight planning parameters
        elif self.dlg.Auto.isChecked():
            try:
                h = (GSD * sensor['FocalLength'] * sensor['ImgSizeX']) / sensor['SizeX']
            except ZeroDivisionError:
                raise SensorError
            Rl = [70., 80., 90.]
            Rt = [70., 80., 90.]
        else:
            raise RadioButtonError

        # accuracy in image collimation
        scoll = self.dlg.scoll.value()  # [px]

        # display progress message while running outputs computation
        progress = ProgressMsg(parent=self.dlg)
        progress.show()

        # check the density for the simulated points and call the Normal Case model for accuracy prediction
        if self.dlg.NormalCase.isChecked():
            if self.dlg.cb_density_n.currentText() == "Low":
                delta = min(Y, X) / 15  # number of points we want on the smaller axis
            elif self.dlg.cb_density_n.currentText() == "Medium":
                delta = min(Y, X) / 25  # number of points we want on the smaller axis
            else:  # High
                delta = min(Y, X) / 35  # number of points we want on the smaller axis
            try:
                self.method = NormalCaseMethod(drone, sensor, X, Y, h, Rl, Rt, scoll, delta, target_sz=target_sz)
            finally:
                progress.close()

        # check the density for the simulated points and call the Simulation(without DTM) model for accuracy prediction
        elif self.dlg.Simulation.isChecked():
            if self.dlg.cb_density_s.currentText() == "Low":
                delta = min(Y, X) / 15  # number of points we want on the smaller axis
            elif self.dlg.cb_density_s.currentText() == "Medium":
                delta = min(Y, X) / 25  # number of points we want on the smaller axis
            else:  # High
                delta = min(Y, X) / 35  # number of points we want on the smaller axis
            try:
                self.method = SimulationMethod(drone, sensor, X, Y, h, Rl, Rt, scoll, delta, target_sz=target_sz)
            finally:
                progress.close()

        # call the Simulation using DTM model for accuracy prediction and retrieve path of both DEM and shapefile
        elif self.dlg.DTM.isChecked():
            dtm_name = str(self.dlg.cb_inDTM.currentText())
            shp_name = str(self.dlg.cb_invector.currentText())
            try:
                dtm = self.dtm_list[dtm_name]
            except KeyError:
                progress.close()
                raise NoDTMError
            try:
                shp = self.shp_list[shp_name]["path"]
            except KeyError:
                progress.close()
                raise NoSHPError
            try:
                self.method = SimulationDTM(drone, sensor, X, Y, h, Rl, Rt, scoll, dtm, shp, target_sz=target_sz)
            finally:
                progress.close()
        else:
            progress.close()
            raise RadioButtonError

        # show the outputs dialog
        self.open_DSPoutputs_dialog()


    def run(self):
        """Run method that shows the plugin dialog, loads vector and DTM"""
        # Create the dialog with elements (after translation) and keep reference
        # Only create GUI ONCE in callback, so that it will only load when the plugin is started

        # show the dialog
        self.dlg.show()
        self.loadVectors()
        self.loadDTM()


if __name__ == "__main__":
    win = DroneSurveyingPlanning()
    win.run()