# -*- coding: utf-8 -*-
"""
/***************************************************************************
 WiscSIMSTool
                                 A QGIS plugin
 useful tools for WiscSIMS sessions
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2020-08-01
        git sha              : $Format:%H$
        copyright            : (C) 2019 by WiscSIMS
        email                : kitajima@wisc.edu
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
"""
from PyQt5.QtCore import (
    QSettings,
    QTranslator,
    qVersion,
    QCoreApplication,
    Qt,
    QSize,
    QModelIndex,
    QVariant,
    QSizeF,
    QPointF
)
from PyQt5.QtGui import (
    QIcon,
    QPixmap,
    QColor,
    QTextDocument
)
from PyQt5.QtWidgets import (
    QAction,
    QInputDialog,
    QFileDialog,
    QAbstractItemView,
    QMessageBox,
    QLineEdit
)
from qgis.core import (
    QgsProject,
    QgsFeature,
    QgsVectorLayer,
    QgsField,
    QgsVectorFileWriter,
    QgsGeometry,
    QgsMarkerSymbol,
    QgsPalLayerSettings,
    QgsTextAnnotation,
    QgsPointXY,
    QgsMargins,
    QgsVectorLayerSimpleLabeling,
    QgsPropertyCollection
)
from qgis.gui import (
    QgsRubberBand,
    QgsMapCanvasAnnotationItem
)

# Initialize Qt resources from file resources.py
from .resources import *  # noqa: F401, F403

# Import custom tools
from .tools.alignmentTool import AlignmentModel, AlignmentMarker
from .tools.canvasMapTool import CanvasMapTool
from .tools.sumTableTool import SumTableTool
from .tools.coordinateTool import CoordinateTool

# Import the code for the DockWidget
from .wiscsims_tool_dockwidget import WiscSIMSToolDockWidget

import os.path
import os
import re
import json
import math


class WiscSIMSTool:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):

        # self.debug = False
        """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

        self.window = self.iface.mainWindow()
        self.canvas = self.iface.mapCanvas()

        # Tool instances
        self.model = AlignmentModel()
        self.ref_marker = AlignmentMarker(self.canvas, self.model)
        self.cot = CoordinateTool()

        # 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',
            'WiscSIMSTool_{}.qm'.format(locale))

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

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&WiscSIMS')
        # TODO: We are going to let the user set this up in a future iteration
        self.toolbar = self.iface.addToolBar(u'WiscSIMS')
        self.toolbar.setObjectName(u'WiscSIMS Tool')

        self.pluginIsActive = False
        self.dockwidget = None

        self.rb = QgsRubberBand(self.canvas, True)
        self.rb2 = QgsRubberBand(self.canvas, True)
        self.rb_s = QgsRubberBand(self.canvas, False)
        self.rb_label = None
        self.start_point, self.end_point = None, None
        self.line_in_progress = False
        self.prev_tool = None

        self.preset_points = []
        self.undo_preset = []

        self.scale = 1

    # 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('WiscSIMS Tool', 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:
            self.toolbar.addAction(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/wiscsims_tool/img/icon.png'
        self.wiscsims_tool_action = self.add_action(
            icon_path,
            text=self.tr(u'WiscSIMS Tool'),
            callback=self.run,
            parent=self.iface.mainWindow())

        self.wiscsims_tool_action.setCheckable(True)

    # --------------------------------------------------------------------------

    def onClosePlugin(self):
        """Cleanup necessary items here when plugin dockwidget is closed"""

        # disconnects
        # self.dockwidget.closingPlugin.disconnect(self.onClosePlugin)
        self.wiscsims_tool_action.setChecked(False)
        # remove this statement if ockwidget is to remain
        # for reuse if plugin is reopened
        # Commented next statement since it causes QGIS crashe
        # when closing the docked window:

        self.remove_legend_connections()
        self.remove_ui_connections()

        self.dockwidget = None

        self.unsetMapTool()

        self.clear_preview_points()

        self.pluginIsActive = False

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""

        self.clear_preview_points()
        self.ref_marker.init_ref_point_markers()

        for action in self.actions:
            self.iface.removePluginMenu(
                self.tr(u'&WiscSIMS'),
                action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar

    # --------------------------------------------------------------------------

    def run(self):
        """Run method that loads and starts the plugin"""
        print('run')
        if self.pluginIsActive:
            print('already activated')
            if not self.wiscsims_tool_action.isChecked():
                print('set to active!!!!')
                self.wiscsims_tool_action.setChecked(True)
            self.init_map_tool()
            return

        self.pluginIsActive = True
        # dockwidget may not exist if:
        #    first run of plugin
        #    removed on close (see self.onClosePlugin method)
        if self.dockwidget is None:
            # Create the dockwidget (after translation) and keep reference
            plugin_path = ':plugins/wiscsims_tool'
            self.dockwidget = WiscSIMSToolDockWidget()
            icon_open_folder = QPixmap(os.path.join(plugin_path, 'img', 'icon_open_folder.png'))
            # icon_open_folder = os.path.join(plugin_path, 'img', 'icon_open_folder.png')
            self.dockwidget.Btn_Select_Workbook.setIcon(
                QIcon(icon_open_folder))
            self.dockwidget.Btn_Select_Workbook.setIconSize(QSize(16, 16))

            self.init_alignmentTable()
            self.dockwidget.Grp_Workbook.setEnabled(False)
            self.dockwidget.Grp_Layer.setEnabled(False)
            self.prev_workbook_path = ''
            self.workbook_path = ''

            self.init_preset_layer_combobox()

        # connect to provide cleanup on closing of dockwidget
        self.dockwidget.closingPlugin.connect(self.onClosePlugin)

        self.create_ui_connections()
        self.create_legend_connections()

        # show the dockwidget
        # TODO: fix to allow choice of dock location
        self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dockwidget)
        self.dockwidget.show()

        self.init_map_tool()

    def handle_layers_changed(self, layers):
        if self.dockwidget.Cmb_Target_Layer.isEnabled():
            self.update_import_layers()
        self.init_preset_layer_combobox()

    def create_legend_connections(self):
        ltr = QgsProject.instance().layerTreeRoot()
        ltr.visibilityChanged.connect(self.handle_layers_changed)
        ltr.addedChildren.connect(self.handle_layers_changed)
        ltr.removedChildren.connect(self.handle_layers_changed)
        ltr.nameChanged.connect(self.handle_layers_changed)

    def remove_legend_connections(self):
        ltr = QgsProject.instance().layerTreeRoot()
        ltr.visibilityChanged.disconnect(self.handle_layers_changed)
        ltr.addedChildren.disconnect(self.handle_layers_changed)
        ltr.removedChildren.disconnect(self.handle_layers_changed)
        ltr.nameChanged.disconnect(self.handle_layers_changed)

    def create_ui_connections(self):

        dock = self.dockwidget

        dock.Grp_Alignment.toggled.connect(self.toggle_use_alignment)
        dock.Tbv_Alignment.clicked.connect(self.change_tableview_selection)
        dock.Gbx_Alignment_Ref_Point_Markers.toggled.connect(self.update_ref_point_markers)
        dock.Cbx_Alignment_Ref_Points.toggled.connect(self.update_ref_point_markers)
        dock.Cbx_Alignment_Ref_Names.toggled.connect(self.update_ref_point_markers)
        dock.Btn_Import_Alignments.clicked.connect(self.import_alignments)
        dock.Btn_Select_Workbook.clicked.connect(self.select_workbook)
        dock.Tbx_Workbook.textChanged.connect(self.workbook_udpated)
        dock.Btn_Create_New_Layer.clicked.connect(self.create_new_layer)
        dock.Btn_Import_From_Excel.clicked.connect(self.import_from_excel)
        dock.Btn_Refresh_Import_Layers.clicked.connect(self.update_import_layers)

        dock.Tab_Preset_Mode.currentChanged.connect(self.preset_tool_changed)

        dock.Spn_Grid_Step_Size_X.valueChanged.connect(self.update_grid)
        dock.Spn_Grid_Step_Size_Y.valueChanged.connect(self.update_grid)
        dock.Spn_Grid_N_Point_X.valueChanged.connect(self.update_grid)
        dock.Spn_Grid_N_Point_Y.valueChanged.connect(self.update_grid)
        dock.Cmb_Grid_Move_Order.currentIndexChanged.connect(self.update_grid)

        dock.Spn_Line_Step_Size.valueChanged.connect(self.update_line)
        dock.Spn_Line_N_Spot.valueChanged.connect(self.update_line)

        dock.Btn_Create_Preset_Layer.clicked.connect(self.create_preset_layer)

        dock.Btn_Grid_Add_Points.clicked.connect(self.add_preset_points)
        dock.Btn_Line_Add_Points.clicked.connect(self.add_preset_points)

        dock.Btn_Reset_Current_Number.clicked.connect(self.reset_current_number)
        dock.Btn_Undo_Add_Preset_Point.clicked.connect(self.undo_add_preset_point)
        dock.Btn_Refresh_Preset_Layers.clicked.connect(self.init_preset_layer_combobox)

        dock.Spn_Preset_Pixel_Size.valueChanged.connect(self.change_pixel_size)
        dock.Spn_Preset_Spot_Size.valueChanged.connect(self.set_spot_size)

        dock.Tbx_Comment.textChanged.connect(self.reset_current_number)
        dock.Tbx_Comment.textChanged.connect(self.handle_comment_change_preview)

    def remove_ui_connections(self):

        dock = self.dockwidget

        dock.Grp_Alignment.toggled.disconnect(self.toggle_use_alignment)
        dock.Tbv_Alignment.clicked.disconnect(self.change_tableview_selection)
        dock.Gbx_Alignment_Ref_Point_Markers.toggled.disconnect(self.update_ref_point_markers)
        dock.Cbx_Alignment_Ref_Points.toggled.disconnect(self.update_ref_point_markers)
        dock.Cbx_Alignment_Ref_Names.toggled.disconnect(self.update_ref_point_markers)
        dock.Btn_Import_Alignments.clicked.disconnect(self.import_alignments)
        dock.Btn_Select_Workbook.clicked.disconnect(self.select_workbook)
        dock.Tbx_Workbook.textChanged.disconnect(self.workbook_udpated)
        dock.Btn_Create_New_Layer.clicked.disconnect(self.create_new_layer)
        dock.Btn_Import_From_Excel.clicked.disconnect(self.import_from_excel)
        dock.Btn_Refresh_Import_Layers.clicked.disconnect(self.update_import_layers)

        dock.Tab_Preset_Mode.currentChanged.disconnect(self.preset_tool_changed)

        dock.Spn_Grid_Step_Size_X.valueChanged.disconnect(self.update_grid)
        dock.Spn_Grid_Step_Size_Y.valueChanged.disconnect(self.update_grid)
        dock.Spn_Grid_N_Point_X.valueChanged.disconnect(self.update_grid)
        dock.Spn_Grid_N_Point_Y.valueChanged.disconnect(self.update_grid)
        dock.Cmb_Grid_Move_Order.currentIndexChanged.disconnect(self.update_grid)

        dock.Spn_Line_Step_Size.valueChanged.disconnect(self.update_line)
        dock.Spn_Line_N_Spot.valueChanged.disconnect(self.update_line)

        dock.Btn_Create_Preset_Layer.clicked.disconnect(self.create_preset_layer)

        dock.Btn_Grid_Add_Points.clicked.disconnect(self.add_preset_points)
        dock.Btn_Line_Add_Points.clicked.disconnect(self.add_preset_points)

        dock.Btn_Reset_Current_Number.clicked.disconnect(self.reset_current_number)
        dock.Btn_Undo_Add_Preset_Point.clicked.disconnect(self.undo_add_preset_point)
        dock.Btn_Refresh_Preset_Layers.clicked.disconnect(self.init_preset_layer_combobox)

        dock.Spn_Preset_Pixel_Size.valueChanged.disconnect(self.change_pixel_size)
        dock.Spn_Preset_Spot_Size.valueChanged.disconnect(self.set_spot_size)

        dock.Tbx_Comment.textChanged.disconnect(self.reset_current_number)
        dock.Tbx_Comment.textChanged.disconnect(self.handle_comment_change_preview)

    def init_map_tool(self):

        # check the plugin activation state
        if self.wiscsims_tool_action.isChecked():
            # when the user activate the WiscSIMS Tool
            self.prev_tool = self.canvas.mapTool()
            self.wiscsims_tool_action.setChecked(True)
            self.canvasMapTool = CanvasMapTool(self.canvas, self.wiscsims_tool_action)

            self.canvas.mapToolSet.connect(self.mapToolChanged)
            self.canvasMapTool.canvasClicked.connect(self.canvasClicked)
            self.canvasMapTool.canvasClickedRight.connect(self.canvasClickedRight)
            self.canvasMapTool.canvasDoubleClicked.connect(self.canvasDoubleClicked)
            self.canvasMapTool.canvasMoved.connect(self.canvasMoved)

            self.canvas.setMapTool(self.canvasMapTool)
        else:
            # when the user deactivate the WiscSIMS Tool
            try:
                self.wiscsims_tool_action.setChecked(False)
                self.canvas.mapToolSet.disconnect(self.mapToolChanged)
                self.canvas.unsetMapTool(self.canvasMapTool)
                if not re.search('wiscsims_tool', self.prev_tool):
                    self.canvas.setMapTool(self.prev_tool)
                self.dockwidget.setEnabled(False)

            except Exception:
                pass

    def toggle_use_alignment(self, status):
        wb_status = True
        if status and not self.model.isAvailable():
            wb_status = False
        self.dockwidget.Grp_Workbook.setEnabled(wb_status)

    def init_alignmentTable(self):
        self.dockwidget.Tbv_Alignment.setModel(self.model)
        hiddenColumns = [
            'point_1', 'point_2',
            'scale', 'offset', 'rotation',
            'ref1', 'ref2'
        ]
        [self.dockwidget.Tbv_Alignment.setColumnHidden(self.model.getColumnIndex(c), True)
         for c in hiddenColumns]
        self.dockwidget.Tbv_Alignment.setColumnWidth(0, 20)
        self.dockwidget.Tbv_Alignment.horizontalHeader().setStretchLastSection(True)
        self.dockwidget.Tbv_Alignment.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.dockwidget.Tbv_Alignment.setSelectionMode(QAbstractItemView.SingleSelection)

    def change_tableview_selection(self, index):
        if type(index) == QModelIndex:
            if index.column() == 0:
                return
            row = index.row()
        else:
            row = index
        # stage1, canvas1 = self.model.getRefPoint(row, 1)
        # stage2, canvas2 = self.model.getRefPoint(row, 2)
        # scale, offset, rotation = self.model.getAlignmentParams(row)
        self.update_ref_point_markers()

    def format_alignment_to_json(self, aln):
        for a in aln:
            a['point_1'] = [
                [a['point_1'][0][0], a['point_1'][0][1]],
                [a['point_1'][1][0], a['point_1'][1][1]]
            ]
            a['point_2'] = [
                [a['point_2'][0][0], a['point_2'][0][1]],
                [a['point_2'][1][0], a['point_2'][1][1]]
            ]
            a['offset'] = a['point_1'][::-1]
        return aln

    def format_json_to_alignment(self, aln):
        for a in aln:
            for pt in ['point_1', 'point_2']:
                a[pt] = [QgsPointXY(p[0], p[1]) for p in a[pt]]
            a['offset'] = a['point_1'][::-1]
        return aln

    def import_alignments(self, alnFile=None):
        project_path = os.path.dirname(QgsProject.instance().fileName())
        if not alnFile:
            alnFile, _filter = QFileDialog.getOpenFileName(
                self.window,
                "Open Stage Navigator alignment file",
                project_path,
                "Stage Navigator alignment file (*.json)")

        self.dockwidget.Grp_Workbook.setEnabled(False)
        if alnFile == '':
            return

        with open(alnFile) as data_file:
            obj = json.load(data_file)

            if self.model.isAvailable():
                self.model.clear()
                self.model = AlignmentModel()
                self.init_alignmentTable()
            self.model.ImportFromsJson(self.format_json_to_alignment(obj))
            # params = self.getParams()
            # self.canvas.setRotation(params['rotation'])
            self.dockwidget.Grp_Workbook.setEnabled(True)
            # calculate average scale for preset
            scales = self.model.getScales()
            average_scale = sum(scales) / len(scales)
            # set average scale to preset scales
            self.dockwidget.Spn_Preset_Pixel_Size.setValue(average_scale)
            self.update_ref_point_markers()
            self.update_canvas_rotation()

    def update_ref_point_markers(self):
        self.ref_marker.init_ref_point_markers()
        # shwo ref points?
        if not self.dockwidget.Gbx_Alignment_Ref_Point_Markers.isChecked():
            return

        alignments = self.model.ExportAsObject()
        selected_alignment = self.dockwidget.Tbv_Alignment.currentIndex().row()
        for aln in alignments:
            current_flag = selected_alignment == aln['r']
            self.handle_ref_marker(aln['point_1'][1], aln['refname'] + ' (1)', current_flag)
            self.handle_ref_marker(aln['point_2'][1], aln['refname'] + ' (2)', current_flag)
        self.update_canvas_rotation()

    def update_canvas_rotation(self):
        if self.model.isAvailable():
            params = self.model.getParams()
            self.canvas.setRotation(params['rotation'])

    def handle_ref_marker(self, pt, name, current=False):
        if self.dockwidget.Cbx_Alignment_Ref_Points.isChecked():
            self.ref_marker.add_ref_marker(pt, name, current)
        if self.dockwidget.Cbx_Alignment_Ref_Names.isChecked():
            self.ref_marker.add_ref_marker_annotation(pt, name, current)

    def select_workbook(self):
        title = 'Select WiscSIMS sesssion Workbook'
        mypath = os.path.dirname(QgsProject.instance().fileName())
        self.workbook_path, _ = QFileDialog.getOpenFileName(
            self.window,
            title,
            mypath,
            'Excel (*.xls *.xlsx)'
        )

        if self.workbook_path == '':
            return
        if self.prev_workbook_path == self.workbook_path:
            return

        self.xl = SumTableTool(self.workbook_path)
        if self.xl.ws is None:
            QMessageBox.warning(
                self.window,
                'Excel File Selection',
                'Open appropriate workbook in Excel, then select the workbook.\n\n'
                'The workbook must have a sheet named "Sum_table" or "Data".'
            )
            self.workbook_path = None
            self.dockwidget.Grp_Layer.setEnabled(False)
        else:
            self.prev_workbook_path = self.workbook_path
            self.dockwidget.Tbx_Workbook.setText(
                os.path.basename(self.workbook_path))

    def is_ok_to_import(self):
        """ check importing condition """
        if self.dockwidget.Grp_Alignment.isChecked() and not self.model.isAvailable():
            """ use alignment but no alignment file imported """
            return False

        if self.xl.ws is None:
            """ no excel file """
            return False

        if self.dockwidget.Cmb_Target_Layer.currentIndex() == -1:
            """ no layer is selected """
            return False
        return True

    def import_from_excel(self):
        if not self.is_ok_to_import():
            return

        if self.dockwidget.Opt_Comment.isChecked():
            importing_data = self.xl.filter_by_comment(self.dockwidget.Tbx_Comment_Match.text())
        else:
            start_idx = self.dockwidget.Cmb_Excel_From.currentIndex()
            end_idx = self.dockwidget.Cmb_Excel_To.currentIndex()
            if start_idx > end_idx:
                start_idx, end_idx = end_idx, start_idx
                self.dockwidget.Cmb_Excel_From.setCurrentIndex(start_idx)
                self.dockwidget.Cmb_Excel_To.setCurrentIndex(end_idx)
            start_asc = self.dockwidget.Cmb_Excel_From.itemData(start_idx)
            end_asc = self.dockwidget.Cmb_Excel_To.itemData(end_idx)
            importing_data = self.xl.filter_by_asc(start=start_asc, end=end_asc)

        importing_layer = self.dockwidget.Cmb_Target_Layer.itemData(
            self.dockwidget.Cmb_Target_Layer.currentIndex())
        X, Y = self.xl.find_columns(['X', 'Y'], False)
        features = []
        i = 0
        is_direct_import = not self.dockwidget.Grp_Alignment.isChecked()
        for d in importing_data:
            if d[X] == "" or d[Y] == "":
                continue
            i += 1
            if is_direct_import:
                canvasX, canvasY = [d[X], d[Y]]
            else:
                canvasX, canvasY = self.getWtAverageStageToCanvas([d[X], d[Y]])
            feature = QgsFeature()
            feature.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(canvasX, canvasY)))
            feature.setAttributes(d)
            features.append(feature)
        dpr = importing_layer.dataProvider()
        dpr.addFeatures(features)
        importing_layer.setDisplayExpression('File')
        importing_layer.triggerRepaint()
        QMessageBox.information(
            self.window,
            'Import Excel Data',
            f'{i} data were imported!'
        )
        # self.canvas.refresh()

    def workbook_udpated(self):
        # addItems asc files
        if self.xl.ws is None:
            self.xl = SumTableTool(self.workbook_path)
        asc_list = self.xl.get_asc_list()
        # asc_list_s = map(lambda x: x[8:], asc_list)
        self.dockwidget.Cmb_Excel_From.clear()
        self.dockwidget.Cmb_Excel_To.clear()
        [self.dockwidget.Cmb_Excel_From.addItem(
            asc[8:], asc) for asc in asc_list]
        [self.dockwidget.Cmb_Excel_To.addItem(
            asc[8:], asc) for asc in asc_list]
        self.dockwidget.Cmb_Excel_To.setCurrentIndex(len(asc_list) - 1)
        self.dockwidget.Grp_Layer.setEnabled(True)
        self.dockwidget.Opt_Range.setChecked(True)
        # add appropriate layers to combobox
        self.update_import_layers()

    def update_import_layers(self):
        current_layer_index = self.dockwidget.Cmb_Target_Layer.currentIndex()

        self.dockwidget.Cmb_Target_Layer.clear()
        layers = self.get_excel_layers(self.xl)
        if len(layers) > 0:
            [self.dockwidget.Cmb_Target_Layer.addItem(l.name(), l) for l in layers]

            self.dockwidget.Btn_Import_From_Excel.setEnabled(True)
            if current_layer_index > -1:
                self.dockwidget.Cmb_Target_Layer.setCurrentIndex(current_layer_index)
        else:
            self.dockwidget.Btn_Import_From_Excel.setEnabled(False)

    def create_new_layer(self, fpath=None):
        project_path = os.path.dirname(QgsProject.instance().fileName())
        saveFile, _ = QFileDialog.getSaveFileName(
            self.window,
            'Save a Shape file',
            project_path,
            'ESRI Shapefile (*.shp)'
        )

        if saveFile == '':
            return None

        vl = QgsVectorLayer("Point?crs=epsg:4326",
                            "temporary_points", "memory")
        vl.setProviderEncoding("UTF-8")
        pr = vl.dataProvider()
        labels = self.xl.get_headers()
        attrs = [QgsField(label, self.get_field_type(label))
                 for label in labels]

        pr.addAttributes(attrs)
        vl.setDisplayExpression('File')
        vl.updateFields()

        reg = QgsProject.instance()
        # Remove layers which have same souce file as shape file to be overwritten
        layers = [l.source() for l in self.get_layer_list(layertype=0)]
        [reg.removeMapLayer(layer) for layer in layers if layer == saveFile]

        # write shape + etc files
        error, _ = QgsVectorFileWriter.writeAsVectorFormat(
            vl,
            saveFile,
            "UTF-8",
            vl.crs(),
            "ESRI Shapefile"
        )
        if not fpath:
            fpath = self.xl.get_workbook_name()
        parsedPath = os.path.basename(fpath).split('.')[:-1]
        layerName = '.'.join(parsedPath)
        text, okPressed = QInputDialog.getText(
            self.window,
            'Input Layer Name',
            'Layer name: ',
            QLineEdit.Normal,
            layerName
        )
        if okPressed:
            layerName = text

        if error == QgsVectorFileWriter.NoError:
            newVlayer = QgsVectorLayer(saveFile, layerName, "ogr")
            newVlayer.setCustomProperty("workbookPath", fpath)
            props = newVlayer.renderer().symbol().symbolLayer(0).properties()
            props['name'] = "circle"
            props['size_unit'] = "MapUnit"
            spotSize = 1.0 * self.dockwidget.Spn_Spot_Size.value()
            if self.model.isAvailable():
                scale = self.get_average(self.model.getScales())
            else:
                scale = 1.0
                # print 'No alignment'
            # scale = self.ct[self.getNavAlign()].scale / 1.0
            props['size'] = str(spotSize / scale)
            mySimpleSymbol = QgsMarkerSymbol.createSimple(props)
            newVlayer.renderer().setSymbol(mySimpleSymbol)
            newVlayer.updateExtents()
            QgsProject.instance().addMapLayer(newVlayer)
            self.iface.setActiveLayer(newVlayer)
            self.update_import_layers()
            self.dockwidget.Cmb_Target_Layer.setCurrentIndex(
                self.dockwidget.Cmb_Target_Layer.findText(layerName))
            return newVlayer

        return None

    def get_fields(self, layer):
        return [f.name() for f in layer.fields()]

    def get_field_type(self, fieldName):
        if re.match(r'^(file|comment|sample)', fieldName, re.I):
            return QVariant.String
        return QVariant.Double

    def intersect(self, list1, list2):
        return [l for l in list1 if l in list2]

    def get_layer_list(self, layertype=-1):
        # Layer Types
        #  Vector: 0
        #  Raster: 1
        #  Plugin: 2
        #  Mesh:   3
        layers = QgsProject.instance().mapLayers().values()
        if layertype == -1:
            return layers
        # Filter by layer type.
        return [layer for layer in layers if layer.type() == layertype]

    def get_vector_point_layers(self):
        # Filter by layer type 0: vector
        layers = self.get_layer_list(layertype=0)
        # filter only point geometry layer 0: point
        return [l for l in layers if l.geometryType() == 0]

    def get_true_shapefile_headers(self, headers):
        # create tmporal shapefile with provided headers
        tmp_file_path = 'KK_tmp_file.shp'
        vl = QgsVectorLayer("Point?crs=epsg:4326",
                            "temporary_points", "memory")
        vl.setProviderEncoding("UTF-8")
        pr = vl.dataProvider()
        attrs = [QgsField(header, self.get_field_type(header))
                 for header in headers]

        pr.addAttributes(attrs)
        vl.setDisplayExpression('File')
        vl.updateFields()
        error, _error_str = QgsVectorFileWriter.writeAsVectorFormat(
            vl,
            tmp_file_path,
            "UTF-8",
            vl.crs(),
            "ESRI Shapefile"
        )
        if error != QgsVectorFileWriter.NoError:
            return
        # open shapefile, then get and return headers
        tmp_layer = QgsVectorLayer(tmp_file_path, 'kk', 'ogr')
        fields = self.get_fields(tmp_layer)

        return fields

    def is_identical_field_names(self, layer, xl_fields, field_max_len=5):
        my_fields = self.get_fields(layer)
        my_fileds_names = [f[:field_max_len] for f in my_fields]
        xl_fields_len = len(self.intersect(my_fileds_names, xl_fields))
        return xl_fields_len == len(my_fields)

    def get_excel_layers(self, xl):
        field_max_len = 5
        # field names in vector layers have been chopped
        if xl.ws is None:
            return []
        xl_fields = [h[:field_max_len] for h in xl.get_headers()]
        legend = QgsProject.instance().layerTreeRoot()
        try:
            vec_layers = self.get_vector_point_layers()
            layers = [layer for layer in vec_layers if legend.findLayer(layer.id()).isVisible()]
        except Exception:
            return []

        return [l for l in layers if self.is_identical_field_names(l, xl_fields, field_max_len)]

    """
    Functions for Aliginment and Coordinate Calculations
    """

    def get_average(self, arr):
        return sum(arr) / len(arr) * 1.0

    def getParams(self):
        if not self.model.isAvailable():
            return None
        scales = self.model.getScales()
        rotations = self.model.getRotations()
        return {
            'scale': self.get_average(scales),
            'rotation': self.get_average(rotations)
        }

    def my_round_int(self, val):
        return int((val * 2 + 1) // 2)

    def getWtAverageStageToCanvas(self, pt):
        model = self.model.ExportAsObject()
        weights, wpt_x, wpt_y, np = [], [], [], [0, 0]
        tmp_out = []
        for m in model:
            if m['used'] == 0:
                continue
            params = [m['scale'], m['offset'], m['rotation']]
            for i in [1, 2]:
                pt_name = 'point_' + str(i)
                d = self.cot.getDistance(pt, m[pt_name][0])
                if d == 0:
                    d = 1E-10
                w = 1 / (d**2)
                if i == 1:
                    np = self.cot.toCanvasCoordinates2(pt, params)
                tmp_out.append(w)
                weights.append(w)
                wpt_x.append(w * np[0])
                wpt_y.append(w * np[1])
        sum_weights = sum(weights)
        return [self.my_round_int(p / sum_weights) for p in [sum(wpt_x), sum(wpt_y)]]

    """
    Functions for Preset
    """

    def init_preset_layer_combobox(self):
        # get current index (selected item) for refresh contents

        current_layer_index = self.dockwidget.Cmb_Preset_Layer.currentIndex()
        # layer = None
        # if current_layer_index > -1:
        #     layer = self.dockwidget.Cmb_Preset_Layer.itemText(current_layer_index)

        # clear current items
        self.dockwidget.Cmb_Preset_Layer.clear()
        # Add layers which have Vector, Point geometry and 'Comment' field to combobox for preset
        layers = self.get_vector_point_layers()
        if len(layers) == 0:
            return
        # filter layers which has 'Comment' field
        layers = [
            layer for layer in layers if 'Comment' in self.get_fields(layer)]
        # add to Cmb_Preset_Layer
        [self.dockwidget.Cmb_Preset_Layer.addItem(l.name(), l) for l in layers]

        if current_layer_index > -1:
            self.dockwidget.Cmb_Preset_Layer.setCurrentIndex(
                current_layer_index)

    def create_preset_layer(self):
        # ask layer name
        project_path = os.path.dirname(QgsProject.instance().fileName())
        saveFile, _filter = QFileDialog.getSaveFileName(
            self.window,
            'Save a Shape file',
            os.path.join(project_path, 'preset.shp'),
            'ESRI Shapefile (*.shp)'
        )

        if saveFile == '':
            return None

        # create layer with given name
        #  field: [Comment]
        vl = QgsVectorLayer("Point?crs=epsg:4326",
                            "temporary_points", "memory")
        vl.setProviderEncoding("UTF-8")
        pr = vl.dataProvider()
        attrs = [QgsField('Comment', QVariant.String)]

        pr.addAttributes(attrs)
        vl.setDisplayExpression('Comment')
        vl.updateFields()

        error, _ = QgsVectorFileWriter.writeAsVectorFormat(
            vl,
            saveFile,
            "UTF-8",
            vl.crs(),
            "ESRI Shapefile"
        )

        # Set layer name without duplication
        layer_names = [l.name() for l in self.get_vector_point_layers()]
        layer_name = 'Preset'
        i = 1
        while layer_name in layer_names:
            i += 1
            layer_name = "Preset_{}".format(i)

        spot_size = self.dockwidget.Spn_Preset_Spot_Size.value()
        if error != QgsVectorFileWriter.NoError:
            return

        newVlayer = QgsVectorLayer(saveFile, layer_name, "ogr")

        props = newVlayer.renderer().symbol().symbolLayer(0).properties()
        props['name'] = "circle"
        props['size_unit'] = "MapUnit"
        spot_size = 1.0 * self.scale * spot_size
        props['size'] = str(spot_size)
        mySimpleSymbol = QgsMarkerSymbol.createSimple(props)
        newVlayer.renderer().setSymbol(mySimpleSymbol)
        newVlayer.updateExtents()

        # load and register created layer to mapCanvas
        QgsProject.instance().addMapLayer(newVlayer)
        self.iface.setActiveLayer(newVlayer)

        settings = QgsPalLayerSettings()
        settings.enabled = True
        settings.fieldName = "Comment"
        settings.displayAll = True
        coll = QgsPropertyCollection('preset')
        coll.setProperty(settings.ShadowDraw, True)
        coll.setProperty(settings.BufferDraw, True)
        settings.setDataDefinedProperties(coll)

        newVlayer.setLabelsEnabled(True)
        newVlayer.setLabeling(QgsVectorLayerSimpleLabeling(settings))
        newVlayer.triggerRepaint()

        # add created layer to Cmb_Preset_Layer
        self.init_preset_layer_combobox()
        # selet/highlight created layer
        self.dockwidget.Cmb_Preset_Layer.setCurrentIndex(
            self.dockwidget.Cmb_Preset_Layer.findText(layer_name))

    def set_spot_size(self, size):
        preset_layer = self.dockwidget.Cmb_Preset_Layer
        if preset_layer.currentIndex() < 0:
            return

        layer = preset_layer.itemData(preset_layer.currentIndex())
        layer.renderer().symbol().symbolLayer(0).setSize(1.0 * self.scale * size)
        layer.triggerRepaint()

    def reset_current_number(self, btn):
        # hit reset button => btn = bool
        # comment changed => btn = unicode
        if isinstance(btn, str) and not self.dockwidget.Cbx_Reset_Increment_Number.isChecked():
            return
        self.dockwidget.Spn_Current_Number.setValue(1)

    def get_current_tool(self):
        tools = ['import', 'preset', 'alignment']
        return tools[self.dockwidget.Tab_Tool.currentIndex()]

    def get_preset_mode(self):
        modes = ['point', 'line', 'grid']
        return modes[self.dockwidget.Tab_Preset_Mode.currentIndex()]

    def add_preset_points(self):

        if len(self.preset_points) == 0:
            return
        if self.dockwidget.Cmb_Preset_Layer.currentIndex() == -1:
            return

        # maximum # of undo
        undo_max = 100

        layer = self.dockwidget.Cmb_Preset_Layer.itemData(
            self.dockwidget.Cmb_Preset_Layer.currentIndex())
        layer.startEditing()
        fields = layer.fields()
        features = []
        for p in self.preset_points:
            feature = QgsFeature()
            feature.setFields(fields)
            geom = QgsGeometry.fromPointXY(QgsPointXY(p[1], p[2]))
            comment = p[0]
            feature.setGeometry(geom)
            feature['Comment'] = comment
            features.append(feature)
        dpr = layer.dataProvider()
        results, newFeatures = dpr.addFeatures(features)
        layer.commitChanges()
        points_for_undo = [f.id() for f in newFeatures]
        self.undo_preset.append(points_for_undo)

        # check number of undos
        if len(self.undo_preset) > undo_max:
            del self.undo_preset[0]

        self.update_undo_btn_state()

        if self.dockwidget.Cbx_Number_Increment.isChecked():
            self.dockwidget.Spn_Current_Number.setValue(
                self.dockwidget.Spn_Current_Number.value() + len(self.preset_points))
        self.init_rubber_bands()

    def connect_controls(self):
        pass

    # def get_comment(self):
    #     comment = self.get_raw_comment()
    #     if self.dockwidget.Cbx_Number_Increment.isChecked():
    #         comment = self.increment_comment(comment)
    #     return comment

    # def get_raw_comment(self):
    #     return self.dockwidget.Tbx_Comment.text()

    # def set_comment(self, comment):
    #     self.dockwidget.Tbx_Comment.setText(comment)
    #     return comment

    # def increment_comment(self, comment):
    #     num = self.get_current_num()
    #     self.set_current_num(num + 1)
    #     return comment.replace('$', num)

    # def get_current_num(self):
    #     return self.dockwidget.Spn_Current_Number.value()

    # def set_current_num(self, num):
    #     self.dockwidget.Spn_Current_Number.setValue(int(num))
    #     return num

    def add_preset_point(self, pt):
        comment = self.get_comment(0)
        if self.dockwidget.Opt_Comment_Popup.isChecked():
            comment, okPressed = QInputDialog.getText(
                self.window,
                'Input Comment',
                'Comment: ',
                QLineEdit.Normal,
                comment
            )
            if not okPressed:
                return
        self.preset_points = [[comment, pt[0], pt[1]]]
        self.add_preset_points()

    def undo_add_preset_point(self):
        if len(self.undo_preset) == 0:
            return
        current_layer_index = self.dockwidget.Cmb_Preset_Layer.currentIndex()
        if current_layer_index == -1:
            return

        # get last item from self.undo_preset
        removing_items = self.undo_preset.pop()
        layer = self.dockwidget.Cmb_Preset_Layer.itemData(current_layer_index)
        layer.startEditing()
        layer.deleteFeatures(removing_items)
        layer.commitChanges()

        # update increment number
        if self.dockwidget.Cbx_Number_Increment.isChecked():
            self.dockwidget.Spn_Current_Number.setValue(
                self.dockwidget.Spn_Current_Number.value() - len(removing_items))

        # self.undo_preset = []
        # self.dockwidget.Btn_Undo_Add_Preset_Point.setEnabled(False)
        self.update_undo_btn_state()

    def update_undo_btn_state(self):
        if len(self.undo_preset):
            self.dockwidget.Btn_Undo_Add_Preset_Point.setEnabled(True)
        else:
            self.dockwidget.Btn_Undo_Add_Preset_Point.setEnabled(False)

    def clear_preview_points(self):
        if self.rb.size() > 0:
            self.rb.reset()
            self.rb2.reset()
            self.rb_s.reset()
            self.init_annotation()

    def init_rb(self):
        # Line
        if self.rb.size() > 0:
            self.rb.reset()
        # self.rb = QgsRubberBand(self.canvas, True)  # False = not a polygon
        self.rb.setWidth(1)
        self.rb.setColor(QColor(255, 20, 20, 60))
        self.dockwidget.Txt_Line_Length.setText('0')

    def init_rb2(self):
        # Points
        if self.rb2.size() > 0:
            self.rb2.reset()
        # self.rb2 = QgsRubberBand(self.canvas, False)  # False = not a polygon
        self.rb2.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.rb2.setIconSize(10)
        self.rb2.setColor(QColor(255, 20, 20, 90))
        self.preset_points = []
        self.init_annotation()

    def is_annotation_item(self, item):
        return issubclass(type(item), QgsMapCanvasAnnotationItem)

    def remove_item(self, item):
        self.canvas.scene().removeItem(item)

    def init_annotation(self):
        [self.remove_item(i) for i in self.canvas.scene().items()
         if self.is_annotation_item(i)]

    def init_rb_s(self):
        if self.rb_s.size() > 0:
            self.rb_s.reset()
        # self.rb_s = QgsRubberBand(self.canvas, False)  # False = not a polygon
        self.rb_s.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.rb_s.setIconSize(5)
        self.rb_s.setColor(QColor(255, 0, 255, 250))

    def init_rubber_bands(self):
        self.init_rb()
        self.init_rb2()
        self.init_rb_s()

    def preset_line(self, pt, line_end=False):
        if line_end:
            if self.end_point is not None:
                self.rb.removeLastPoint()
                self.init_rb2()
            self.end_point = pt
            self.rb.addPoint(pt, True)
            self.draw_line_points()
            self.line_in_progress = False
        else:  # start line drawing
            self.init_rubber_bands()
            self.start_point = pt
            self.rb.addPoint(pt, True)
            self.line_in_progress = True

        self.rb_s.addPoint(self.start_point, True)

    def update_line(self):
        if self.start_point and self.end_point:
            self.init_rb2()
            self.draw_line_points()
            self.rb_s.removeLastPoint()
            self.rb_s.addPoint(self.start_point, True)

    def draw_line_points(self):
        length = self.get_distance(self.start_point, self.end_point) / self.scale
        self.dockwidget.Txt_Line_Length.setText('{:.2f}'.format(length))
        if self.dockwidget.Opt_Line_Step_Size.isChecked():
            step = self.dockwidget.Spn_Line_Step_Size.value()
            n_spots = int(length / step) + 1
            self.set_value_without_signal(
                self.dockwidget.Spn_Line_N_Spot, n_spots)
        else:
            n_spots = self.dockwidget.Spn_Line_N_Spot.value()
            step = length / (n_spots - 1)
            self.set_value_without_signal(
                self.dockwidget.Spn_Line_Step_Size, step)
        angle = self.get_angle(self.start_point, self.end_point)
        cos = step * math.cos(angle)
        sin = step * math.sin(angle)
        i, x, y = 0, 0, 0
        pt = QgsPointXY(x, y)
        for n in range(n_spots):
            x = self.start_point[0] + cos * n * self.scale
            y = self.start_point[1] + sin * n * self.scale
            comment = self.get_comment(i)
            pt = QgsPointXY(x, y)
            self.rb2.addPoint(pt, False)
            self.add_rb_label(comment, pt)
            self.preset_points.append([comment, x, y])
            i += 1
        self.rb2.addPoint(pt, True)
        self.rb2.removeLastPoint()
        self.canvas.refresh()

    def preset_grid(self, pt):
        self.init_rubber_bands()
        self.start_point = pt
        self.draw_grid_points()

    def update_grid(self):
        if self.start_point is None:
            return
        self.init_rubber_bands()
        self.draw_grid_points()

    def draw_grid_points(self):
        order = self.dockwidget.Cmb_Grid_Move_Order.currentIndex()
        step_x = self.dockwidget.Spn_Grid_Step_Size_X.value()
        step_y = self.dockwidget.Spn_Grid_Step_Size_Y.value()
        n_step_x = self.dockwidget.Spn_Grid_N_Point_X.value()
        n_step_y = self.dockwidget.Spn_Grid_N_Point_Y.value()
        rotation = self.canvas.rotation()
        points = self.calculate_grid_positions(
            self.start_point, [step_x, step_y], [n_step_x, n_step_y], order, self.scale, rotation)
        i = 0
        for point in points:
            pt = QgsPointXY(point[0], point[1])
            comment = self.get_comment(i)
            self.rb.addPoint(pt, True)
            self.rb2.addPoint(pt, True)
            self.add_rb_label(comment, pt)
            self.preset_points.append([comment, point[0], point[1]])
            i += 1
            if self.rb2.numberOfVertices() == 1:
                self.rb_s.addPoint(pt, True)
        self.canvas.refresh()

    def add_rb_label(self, comment, pt):
        # layer = self.iface.activeLayer()
        symbol = QgsMarkerSymbol()
        symbol.setSize(0)
        html = '<body style="color: #222; background-color: #eee;"><b>{}</b></body>'.format(
            comment)
        a = QgsTextAnnotation()
        c = QTextDocument()
        c.setHtml(html)
        a.setDocument(c)
        a.setFrameSize(QSizeF(c.size().width(), c.size().height()))
        a.setMarkerSymbol(symbol)
        a.setFrameOffsetFromReferencePoint(QPointF(15, -30))
        a.setMapPosition(pt)
        a.setContentsMargin(QgsMargins(0, 0, 0, 0))
        # a.setMapPositionCrs(QgsCoordinateReferenceSystem(layer.crs()))
        QgsMapCanvasAnnotationItem(a, self.canvas)

    def calculate_grid_positions(self, start_xy, step_xy, n_xy, order, scale=1, rotation=0):
        # start, step, n are lists
        # order: 0 = Horizontal, then vertical
        #        1 = Vertical, then horizontal
        start_xy = list(start_xy)
        if order == 0:
            for i in [start_xy, step_xy, n_xy]:
                i.reverse()
        p_n, s_n = n_xy
        p_step, s_step = step_xy
        p_pt, s_pt = start_xy
        out = []
        for p in range(p_n):  #
            p_val = p_pt + p * p_step * scale
            for s in range(s_n):
                s_val = s_pt + s * s_step * scale
                xy = [s_val, p_val]
                if order:
                    xy.reverse()
                out.append(xy)
        if rotation:
            if order == 0:
                start_xy.reverse()
            out = self.rotate_coordinates(out, rotation, start_xy)
        return out

    def rotate_coordinates(self, points, rotation, origin=[0, 0]):
        # poinsts: list of point
        # rotation: rotation in degree (0 to +180, 0 to -180), CW = +, CCW = -
        # origin: coordiates of origin of rotation. Default is [0, 0]
        r = math.radians(rotation)
        cos, sin = math.cos(r), math.sin(r)
        out = map(lambda p: [
            (p[0] - origin[0]) * cos - (p[1] - origin[1]) * sin + origin[0],
            (p[0] - origin[0]) * sin + (p[1] - origin[1]) * cos + origin[1]
        ], points)
        return out

    def get_distance(self, pt1, pt2):
        return ((pt2[0] - pt1[0])**2 + (pt2[1] - pt1[1])**2)**0.5

    def get_angle(self, pt1, pt2):
        return math.atan2((pt2[1] - pt1[1]), (pt2[0] - pt1[0]))

    def preset_tool_changed(self, tool_index):
        self.clear_preview_points()

    def set_value_without_signal(self, target, value):
        target.blockSignals(True)
        target.setValue(value)
        target.blockSignals(False)

    def handle_comment_change_preview(self):
        current_mode = self.get_preset_mode()
        if current_mode == "point" or self.rb.size() == 0:
            return
        if current_mode == "line":
            self.update_line()
        elif current_mode == "grid":
            self.update_grid()

    def get_comment(self, n):
        comment_base = self.dockwidget.Tbx_Comment.text()
        flag_comment_format = self.dockwidget.Cbx_Number_Increment.isChecked()
        current_num = self.dockwidget.Spn_Current_Number.value()
        if not flag_comment_format:
            return comment_base
        return self.format_comment(comment_base, current_num + n)

    def format_comment(self, comment, num):
        try:
            out = comment.format(num)
        except (IndexError, KeyError, ValueError):
            out = comment
        return out

    def change_pixel_size(self, val):
        self.scale = 1 / val
        current_mode = self.get_preset_mode()
        if current_mode == "grid":
            self.update_grid()
        elif current_mode == "line":
            self.update_line()
        self.set_spot_size(self.dockwidget.Spn_Preset_Spot_Size.value())

    """
    Canvas related
    """

    def mapToolChanged(self, tool, prev_tool):
        #     r'wiscsims_tool', str(tool)))
        if re.search(r'wiscsims_tool', str(tool)) or re.search(r'WiscSIMSTool', str(tool)):
            self.dockwidget.setEnabled(True)
            return

        try:
            self.unsetMapTool()
            self.clear_preview_points()
            self.wiscsims_tool_action.setChecked(False)
            self.dockwidget.setEnabled(False)
        except Exception:
            pass

    def unsetMapTool(self):
        self.canvas.mapToolSet.disconnect(self.mapToolChanged)
        self.canvas.unsetMapTool(self.canvasMapTool)

    def canvasClicked(self, pt):
        if self.get_current_tool() != 'preset':
            return

        mode = self.get_preset_mode()
        if mode == 'point':
            self.add_preset_point(pt)
        elif mode == 'line':
            self.preset_line(pt)
        elif mode == 'grid':
            self.preset_grid(pt)

    def canvasClickedRight(self, pt):
        if self.get_current_tool() != 'preset':
            return
        mode = self.get_preset_mode()
        if mode == 'point':
            self.undo_add_preset_point()
        elif mode == 'line':
            self.preset_line(pt, True)

    def canvasDoubleClicked(self, pt):
        if self.get_current_tool() != 'preset':
            return
        self.clear_preview_points()

    def canvasMoved(self, pt):
        if self.get_current_tool() != 'preset':
            return
        mode = self.get_preset_mode()
        if self.dockwidget.Tab_Tool.currentIndex() == 1 \
                and mode == 'line' \
                and self.line_in_progress:
            if self.end_point:
                self.rb.removeLastPoint()
            self.end_point = pt
            self.rb.addPoint(pt, True)
