Exemplo n.º 1
0
    def test_noGroupby(self):
        out_dir = os.path.join(TmpDir, 'TestCalculateImageIndices',
                               'no-groupby')
        if not os.path.exists(out_dir):
            os.makedirs(out_dir)
        bm = BandMapping(green=2, infrared=4, rededge=1, mask=5)

        image_file = os.path.realpath(
            this_dir + '/data/rasters/area1_rgbi_jan_50cm_84sutm54.tif')
        poly_shapefile = os.path.realpath(
            this_dir + '/data/PolyMZ_wgs84_MixedPartFieldsTypes.shp')
        indices = CalculateIndices(**bm).valid_indices()
        files = calc_indices_for_block(image_file,
                                       2,
                                       BandMapping(red=3,
                                                   green=2,
                                                   infrared=4,
                                                   rededge=1),
                                       out_dir,
                                       indices=indices,
                                       image_epsg=32754,
                                       image_nodata=0,
                                       polygon_shapefile=poly_shapefile,
                                       out_epsg=28354)

        self.assertEqual(len(indices), 3)
        self.assertEqual(len(files), len(indices))
        with rasterio.open(files[0]) as src:
            self.assertEqual(src.nodata, -9999)
            self.assertEqual(src.crs, rasterio.crs.CRS.from_epsg(28354))
            self.assertEqual(src.dtypes, ('float32', ))
            self.assertEqual(src.res, (2.0, 2.0))
            self.assertEqual(src.count, 1)
Exemplo n.º 2
0
    def test_band_mapping(self):
        with self.assertRaises(AttributeError) as msg:
            bm = BandMapping(red=4, green=3, blud=2)
        self.assertEqual("BandMapping has no attribute blud. Allowed keys are "
                "blue, mask, infrared, rededge, green, red", str(msg.exception))

        self.assertDictEqual(BandMapping(),
                             {'blue': 0, 'mask': 0, 'infrared': 0, 'rededge': 0, 'green': 0,
                              'red': 0})

        mydict = {'rededge': 1, 'red': 3, 'infrared': 4, 'mask': 5, }
        self.assertDictEqual(BandMapping(**mydict),
                             {'blue': 0, 'mask': 5, 'infrared': 4, 'rededge': 1, 'green': 0,
                              'red': 3})

        bm = BandMapping(red=3, green=2)
        self.assertEqual(bm.allocated_bands(), [2, 3])

        bm.update(infrared=4, mask=5)
        self.assertDictEqual(bm, {'blue': 0, 'mask': 5, 'infrared': 4, 'rededge': 0, 'green': 2,
                                  'red': 3})

        del bm['mask']
        self.assertDictEqual(bm, {'blue': 0, 'mask': 0, 'infrared': 4, 'rededge': 0, 'green': 2,
                                  'red': 3})
    def __init__(self, iface, parent=None):

        super(CalculateImageIndicesDialog, self).__init__(parent)

        # Set up the user interface from Designer.
        self.setupUi(self)
        self.iface = iface
        self.DISP_TEMP_LAYERS = read_setting(PLUGIN_NAME + '/DISP_TEMP_LAYERS', bool)
        self.DEBUG = config.get_debug_mode()

        # Catch and redirect python errors directed at the log messages python error tab.
        QgsApplication.messageLog().messageReceived.connect(errorCatcher)

        if not os.path.exists(TEMPDIR):
            os.mkdir(TEMPDIR)

        # Setup for validation messagebar on gui-----------------------------
        self.messageBar = QgsMessageBar(self)  # leave this message bar for bailouts
        self.validationLayout = QtWidgets.QFormLayout(self)  # new layout to gui

        if isinstance(self.layout(), (QtWidgets.QFormLayout, QtWidgets.QGridLayout)):
            # create a validation layout so multiple messages can be added and cleaned up.
            self.layout().insertRow(0, self.validationLayout)
            self.layout().insertRow(0, self.messageBar)
        else:
            self.layout().insertWidget(0, self.messageBar)  # for use with Vertical/horizontal layout box

        self.band_mapping = BandMapping()

        # GUI Runtime Customisation -----------------------------------------------
        self.mcboPolygonLayer.setFilters(QgsMapLayerProxyModel.PolygonLayer)
        self.mcboPolygonLayer.setExcludedProviders(['wms'])
        self.mcboPolygonLayer.setLayer(None)
               
        self.mcboRasterLayer.setFilters(QgsMapLayerProxyModel.RasterLayer)
        self.mcboRasterLayer.setExcludedProviders(['wms'])
        
        rastlyrs_df = build_layer_table([self.mcboRasterLayer.layer(i) for i in range(self.mcboRasterLayer.count())])
        if self.mcboRasterLayer.count() > 0:
            exc_lyrs = rastlyrs_df[rastlyrs_df['bandcount']<=1]
            self.mcboRasterLayer.setExceptedLayerList(exc_lyrs['layer'].tolist())
        
        self.updateRaster()
      
        # self.chkAddToDisplay.setChecked(False)
        # self.chkAddToDisplay.hide()
        self.chkgrpIndices.setExclusive(False)  # allow for multi selection

        self.setWindowIcon(QtGui.QIcon(':/plugins/pat/icons/icon_calcImgIndices.svg'))
Exemplo n.º 4
0
    def __init__(self, iface, parent=None):

        super(CalculateImageIndicesDialog, self).__init__(parent)

        # Set up the user interface from Designer.
        self.setupUi(self)
        self.iface = iface
        self.DISP_TEMP_LAYERS = read_setting(PLUGIN_NAME + '/DISP_TEMP_LAYERS',
                                             bool)
        self.DEBUG = config.get_debug_mode()

        # Catch and redirect python errors directed at the log messages python error tab.
        QgsMessageLog.instance().messageReceived.connect(errorCatcher)

        if not os.path.exists(TEMPDIR):
            os.mkdir(TEMPDIR)

        # Setup for validation messagebar on gui-----------------------------
        self.messageBar = QgsMessageBar(
            self)  # leave this message bar for bailouts
        self.validationLayout = QtGui.QFormLayout(self)  # new layout to gui

        if isinstance(self.layout(), (QtGui.QFormLayout, QtGui.QGridLayout)):
            # create a validation layout so multiple messages can be added and cleaned up.
            self.layout().insertRow(0, self.validationLayout)
            self.layout().insertRow(0, self.messageBar)
        else:
            self.layout().insertWidget(
                0,
                self.messageBar)  # for use with Vertical/horizontal layout box

        self.band_mapping = BandMapping()
        self.outQgsCRS = None

        self.chkgrpIndices.setExclusive(False)  # allow for multi selection
        self.exclude_map_layers()
        self.updateRaster()
        self.updateUseSelected()
        self.autoSetCoordinateSystem()

        # GUI Runtime Customisation -----------------------------------------------
        self.chkAddToDisplay.setChecked(False)
        # self.chkAddToDisplay.hide()

        self.add_blank_field_to_cbo()

        self.setWindowIcon(
            QtGui.QIcon(':/plugins/pat/icons/icon_calcImgIndices.svg'))
Exemplo n.º 5
0
    def test_calculateIndices(self):

        self.assertEqual(CalculateIndices(red=3, infrared=4, mask=5).valid_indices(),
                         ['NDVI', 'PCD'])
        ci = CalculateIndices()
        self.assertDictEqual(ci.band_map,
                             {'blue': 0, 'mask': 0, 'infrared': 0, 'rededge': 0, 'green': 0,
                              'red': 0})

        ci.band_map.update({'rededge': 1, 'green': 2, 'mask': 5, 'red': 3, 'infrared': 4})
        self.assertDictEqual(ci.band_map,
                             {'blue': 0, 'green': 2, 'infrared': 4, 'mask': 5, 'red': 3,
                              'rededge': 1})
        self.assertEqual(ci.band_map.allocated_bands(), [1, 2, 3, 4, 5])
        self.assertEqual(ci.valid_indices(), ['NDVI', 'PCD', 'GNDVI', 'NDRE', 'CHLRE'])

        bm = BandMapping(red=3, green=2, infrared=4)
        ci.band_map = bm
        self.assertEqual(ci.valid_indices(), ['NDVI', 'PCD', 'GNDVI'])

        file_image = os.path.realpath(this_dir + "/data/rasters/area1_rgbi_jan_50cm_84sutm54.tif")

        with self.assertRaises(KeyError) as msg:
            ci.calculate('NDVIa', file_image, src_nodata=0)
        self.assertEqual("'NDVIA'", str(msg.exception))
Exemplo n.º 6
0
    def test09_calcImageIndices_allopts(self):
        out_fold = os.path.join(TmpDir, 'calcindex_allopts')
        if not os.path.exists(out_fold):
            os.mkdir(out_fold)
        bm = BandMapping(green=2, infrared=4, rededge=1, mask=5)
        indices = CalculateIndices(**bm).valid_indices()
        files = calc_indices_for_block(fileImage,
                                       2.5,
                                       bm,
                                       out_fold,
                                       indices,
                                       image_nodata=0,
                                       image_epsg=32754,
                                       polygon_shapefile=fileBox,
                                       out_epsg=28354)

        self.gridextract_files += files

        self.assertEqual(len(files), 3)

        with rasterio.open(files[0]) as src:
            self.assertEqual(src.nodata, -9999)
            self.assertEqual(src.crs, rasterio.crs.CRS.from_epsg(28354))
            self.assertEqual(src.meta['dtype'], 'float32')
            self.assertEqual(src.res, (2.5, 2.5))
            self.assertEqual(src.count, 1)
Exemplo n.º 7
0
    def test_noShapefile(self):
        """ Use Full Image......
            No Shapfile,
            No Non-Vine mask
        """
        out_dir = os.path.join(TmpDir, 'TestCalculateImageIndices',
                               'no-shapefile')
        if not os.path.exists(out_dir):
            os.makedirs(out_dir)

        image_file = os.path.realpath(
            this_dir + '/data/rasters/area1_rgbi_jan_50cm_84sutm54.tif')
        indices = CalculateIndices(red=3, infrared=4, mask=5).valid_indices()
        files = calc_indices_for_block(image_file,
                                       2,
                                       BandMapping(red=3,
                                                   green=2,
                                                   infrared=4,
                                                   rededge=1),
                                       out_dir,
                                       indices=indices,
                                       image_epsg=32754,
                                       image_nodata=0,
                                       out_epsg=28354)

        dst_crs = rasterio.crs.CRS.from_epsg(28354)
        self.assertEqual(len(files), len(indices))
        with rasterio.open(files[0]) as src:
            self.assertEqual(src.count, 1)
            self.assertEqual(src.crs.wkt, dst_crs.wkt)
            self.assertEqual(src.tags(1)['name'], indices[0])
            self.assertEqual(src.nodata, -9999)
            self.assertEqual(src.meta['dtype'], 'float32')

            # test for values at coords
            row, col = src.index(300725, 6181571)
            self.assertAlmostEqual(
                src.read(1)[int(row), int(col)], -0.05087604, 4)

            row, col = src.index(300647.0, 6181561.0)
            self.assertAlmostEqual(
                src.read(1)[int(row), int(col)], 0.02232674, 4)

            row, col = src.index(300881.342, 6181439.444)
            self.assertEqual(src.read(1)[int(row), int(col)], -9999)
Exemplo n.º 8
0
    def test_allOptions(self):
        """ All Options includes:
            Use a non-vine mask.
            Original image nodata is None so set to 0
            Reproject Image
            Use Shapefile AND groupby field
        """

        out_dir = os.path.join(TmpDir, 'TestCalculateImageIndices', 'all-opts')
        if not os.path.exists(out_dir):
            os.makedirs(out_dir)

        image_file = os.path.realpath(
            this_dir + '/data/rasters/area1_rgbi_jan_50cm_84sutm54.tif')
        poly_shapefile = os.path.realpath(
            this_dir + '/data/PolyMZ_wgs84_MixedPartFieldsTypes.shp')
        indices = CalculateIndices(red=3, infrared=4, mask=5).valid_indices()
        files = calc_indices_for_block(image_file,
                                       2,
                                       BandMapping(red=3,
                                                   green=2,
                                                   infrared=4,
                                                   rededge=1,
                                                   mask=5),
                                       out_dir,
                                       indices=indices,
                                       image_epsg=32754,
                                       image_nodata=0,
                                       polygon_shapefile=poly_shapefile,
                                       groupby='part_type',
                                       out_epsg=28354)

        dst_crs = rasterio.crs.CRS.from_epsg(28354)
        self.assertEqual(len(files), 4)
        with rasterio.open(files[0]) as src:
            self.assertEqual(src.count, 1)
            self.assertEqual(src.crs.wkt, dst_crs.wkt)
            self.assertEqual(src.tags(1)['name'], indices[0])
            self.assertEqual(src.nodata, -9999)
            self.assertEqual(src.meta['dtype'], 'float32')

            # coords 300725.0, 6181571.0
            self.assertEqual(src.read(1)[47, 62], -9999)
            # coords (300647.0, 6181561.0)
            self.assertAlmostEqual(src.read(1)[52, 23], 0.20820355, 4)
Exemplo n.º 9
0
    def test_dontApplyNonVineMask(self):
        out_dir = os.path.join(TmpDir, 'TestCalculateImageIndices',
                               'no-nonvine')
        if not os.path.exists(out_dir):
            os.makedirs(out_dir)

        image_file = os.path.realpath(
            this_dir + '/data/rasters/area1_rgbi_jan_50cm_84sutm54.tif')
        poly_shapefile = os.path.realpath(
            this_dir + '/data/PolyMZ_wgs84_MixedPartFieldsTypes.shp')
        indices = CalculateIndices(red=3, infrared=4, mask=5).valid_indices()
        files = calc_indices_for_block(image_file,
                                       2,
                                       BandMapping(red=3,
                                                   green=2,
                                                   infrared=4,
                                                   rededge=1),
                                       out_dir,
                                       indices=indices,
                                       image_epsg=32754,
                                       image_nodata=0,
                                       polygon_shapefile=poly_shapefile,
                                       groupby='part_type',
                                       out_epsg=28354)

        dst_crs = rasterio.crs.CRS.from_epsg(28354)
        self.assertEqual(len(files), 4)
        with rasterio.open(files[0]) as src:
            self.assertEqual(src.count, 1)
            self.assertEqual(src.crs.wkt, dst_crs.wkt)
            self.assertEqual(src.tags(1)['name'], indices[0])
            self.assertEqual(src.nodata, -9999)
            self.assertEqual(src.meta['dtype'], 'float32')

            # coords 300725.0, 6181571.0
            self.assertEqual(src.read(1)[47, 62], -9999)
            # coords (300647.0, 6181561.0)
            self.assertAlmostEqual(src.read(1)[52, 23], 0.070868947, 4)
Exemplo n.º 10
0
class CalculateImageIndicesDialog(QtGui.QDialog, FORM_CLASS):
    """Calculate image indices for blocks """
    toolKey = 'CalculateImageIndicesDialog'

    def __init__(self, iface, parent=None):

        super(CalculateImageIndicesDialog, self).__init__(parent)

        # Set up the user interface from Designer.
        self.setupUi(self)
        self.iface = iface
        self.DISP_TEMP_LAYERS = read_setting(PLUGIN_NAME + '/DISP_TEMP_LAYERS',
                                             bool)
        self.DEBUG = config.get_debug_mode()

        # Catch and redirect python errors directed at the log messages python error tab.
        QgsMessageLog.instance().messageReceived.connect(errorCatcher)

        if not os.path.exists(TEMPDIR):
            os.mkdir(TEMPDIR)

        # Setup for validation messagebar on gui-----------------------------
        self.messageBar = QgsMessageBar(
            self)  # leave this message bar for bailouts
        self.validationLayout = QtGui.QFormLayout(self)  # new layout to gui

        if isinstance(self.layout(), (QtGui.QFormLayout, QtGui.QGridLayout)):
            # create a validation layout so multiple messages can be added and cleaned up.
            self.layout().insertRow(0, self.validationLayout)
            self.layout().insertRow(0, self.messageBar)
        else:
            self.layout().insertWidget(
                0,
                self.messageBar)  # for use with Vertical/horizontal layout box

        self.band_mapping = BandMapping()
        self.outQgsCRS = None

        self.chkgrpIndices.setExclusive(False)  # allow for multi selection
        self.exclude_map_layers()
        self.updateRaster()
        self.updateUseSelected()
        self.autoSetCoordinateSystem()

        # GUI Runtime Customisation -----------------------------------------------
        self.chkAddToDisplay.setChecked(False)
        # self.chkAddToDisplay.hide()

        self.add_blank_field_to_cbo()

        self.setWindowIcon(
            QtGui.QIcon(':/plugins/pat/icons/icon_calcImgIndices.svg'))

    def cleanMessageBars(self, AllBars=True):
        """Clean Messages from the validation layout.
        Args:
            AllBars (bool): Remove All bars including those which haven't timed-out. Defaults to True
        """
        layout = self.validationLayout
        for i in reversed(range(layout.count())):
            # when it timed out the row becomes empty....
            if layout.itemAt(i).isEmpty():
                # .removeItem doesn't always work. so takeAt(pop) it instead
                item = layout.takeAt(i)
            elif AllBars:  # ie remove all
                item = layout.takeAt(i)
                # also have to remove any widgets associated with it.
                if item.widget() is not None:
                    item.widget().deleteLater()

    def send_to_messagebar(self,
                           message,
                           title='',
                           level=QgsMessageBar.INFO,
                           duration=5,
                           exc_info=None,
                           core_QGIS=False,
                           addToLog=False,
                           showLogPanel=False):
        """ Add a message to the forms message bar.

        Args:
            message (str): Message to display
            title (str): Title of message. Will appear in bold. Defaults to ''
            level (QgsMessageBarLevel): The level of message to log. Defaults to QgsMessageBar.INFO
            duration (int): Number of seconds to display message for. 0 is no timeout. Defaults to 5
            core_QGIS (bool): Add to QGIS interface rather than the dialog
            addToLog (bool): Also add message to Log. Defaults to False
            showLogPanel (bool): Display the log panel
            exc_info () : Information to be used as a traceback if required

        """

        if core_QGIS:
            newMessageBar = self.iface.messageBar()
        else:
            newMessageBar = QgsMessageBar(self)

        widget = newMessageBar.createMessage(title, message)

        if showLogPanel:
            button = QPushButton(widget)
            button.setText('View')
            button.setContentsMargins(0, 0, 0, 0)
            button.setFixedWidth(35)
            button.pressed.connect(openLogPanel)
            widget.layout().addWidget(button)

        newMessageBar.pushWidget(widget, level, duration=duration)

        if not core_QGIS:
            rowCount = self.validationLayout.count()
            self.validationLayout.insertRow(rowCount + 1, newMessageBar)

        if addToLog:
            if level == 1:  # 'WARNING':
                LOGGER.warning(message)
            elif level == 2:  # 'CRITICAL':
                # Add a traceback to log only for bailouts only
                if exc_info is not None:
                    exc_type, exc_value, exc_traceback = sys.exc_info()
                    mess = str(traceback.format_exc())
                    message = message + '\n' + mess

                LOGGER.critical(message)
            else:  # INFO = 0
                LOGGER.info(message)

    def exclude_map_layers(self):
        """ Run through all loaded layers to find ones which should be excluded. In this case exclude services."""

        exVlayer_list = []
        exRlayer_list = []
        for layer in self.iface.legendInterface().layers():
            if layer.type() == QgsMapLayer.RasterLayer:
                if layer.providerType() != 'gdal':
                    exRlayer_list.append(layer)
                if layer.bandCount() < 2:
                    exRlayer_list.append(layer)

            elif layer.type() == QgsMapLayer.VectorLayer:
                if layer.providerType() != 'ogr':
                    exVlayer_list.append(layer)

        if len(exRlayer_list) > 0:
            self.mcboRasterLayer.setExceptedLayerList(exRlayer_list)

        if len(exVlayer_list) > 0:
            self.mcboPolygonLayer.setExceptedLayerList(exVlayer_list)

    def updateRaster(self):
        """Update form elements based on raster metadata elements"""
        if self.mcboRasterLayer.currentLayer() is None: return

        layer = self.mcboRasterLayer.currentLayer()
        provider = layer.dataProvider()

        if provider.srcHasNoDataValue(1):
            self.spnNoDataVal.setValue(provider.srcNoDataValue(1))
        elif len(provider.userNoDataValues(1)) > 0:
            self.spnNoDataVal.setValue(provider.userNoDataValues(1)[0].min())
        else:
            self.spnNoDataVal.setValue(0)

        # add a band list to the drop down box
        bandCount = self.mcboRasterLayer.currentLayer().bandCount()
        band_list = ['Band {: >2}'.format(i) for i in range(1, bandCount + 1)]
        for obj in [
                self.cboBandRed, self.cboBandGreen, self.cboBandIR,
                self.cboBandRedEdge, self.cboBandNonVine
        ]:
            obj.setMaxCount(bandCount + 1)
            obj.clear()
            obj.addItems([u''] + sorted(band_list))

    def update_bandlist(self):
        """update the band list to the drop down box"""
        if self.mcboRasterLayer.currentLayer() is None: return

        # list of all bands for image
        band_list = range(1,
                          self.mcboRasterLayer.currentLayer().bandCount() + 1)

        for obj in [
                self.cboBandRed, self.cboBandGreen, self.cboBandIR,
                self.cboBandRedEdge, self.cboBandNonVine
        ]:
            ''' Hide items from combo which have already been allocated to a band. 
            NOTE: deleting the items changes the current index, and resetting to the corrected index will trigger 
            a change event which turns into an endless loop
            source https://stackoverflow.com/a/49778675/9567306 '''

            for i in band_list:
                idx = obj.findText('Band {: >2}'.format(i))

                if i in self.band_mapping.allocated_bands(
                ) and 'Band {: >2}'.format(i) != obj.currentText():
                    obj.view().setRowHidden(idx, True)
                else:
                    obj.view().setRowHidden(idx, False)

        indices = CalculateIndices(**self.band_mapping).valid_indices()

        for x in self.chkgrpIndices.buttons():
            if x.text().upper() in indices:
                x.setEnabled(True)
            else:
                x.setEnabled(False)
                x.setChecked(False)

    def updateUseSelected(self):
        """Update use selected checkbox if active layer has a feature selection"""

        self.chkUseSelected.setChecked(False)

        if self.mcboPolygonLayer.count() == 0:
            return

        polygon_lyr = self.mcboPolygonLayer.currentLayer()
        self.mFieldComboBox.setLayer(polygon_lyr)
        if len(polygon_lyr.selectedFeatures()) > 0:
            self.chkUseSelected.setText(
                'Use the {} selected feature(s) ?'.format(
                    len(polygon_lyr.selectedFeatures())))
            self.chkUseSelected.setEnabled(True)
        else:
            self.chkUseSelected.setText('No features selected')
            self.chkUseSelected.setEnabled(False)

    def autoSetCoordinateSystem(self):
        """Calculate a projected coordinate systems from a set of coordinates"""

        if self.mcboRasterLayer.count() == 0:
            return

        self.cleanMessageBars()

        raster_lyr = self.mcboRasterLayer.currentLayer()

        raster_utm_crs = crs.getProjectedCRSForXY(
            raster_lyr.extent().xMinimum(),
            raster_lyr.extent().yMinimum(),
            int(raster_lyr.crs().authid().replace('EPSG:', '')))
        self.outQgsCRS = None

        if raster_utm_crs is not None:
            raster_crs = QgsCoordinateReferenceSystem('EPSG:{}'.format(
                raster_utm_crs.epsg_number))
            self.outQgsCRS = raster_crs

        if self.outQgsCRS is not None:
            self.lblOutCRS.setText('{}  -  {}'.format(
                self.outQgsCRS.description(), self.outQgsCRS.authid()))
            self.lblOutCRSTitle.setStyleSheet('color:black')
            self.lblOutCRS.setStyleSheet('color:black')
        else:
            self.lblOutCRSTitle.setStyleSheet('color:red')
            self.lblOutCRS.setStyleSheet('color:red')
            self.lblOutCRS.setText('Unspecified')
            self.send_to_messagebar(
                'Auto detect coordinate system Failed. Check coordinate system of input raster layer',
                level=QgsMessageBar.CRITICAL,
                duration=5)

        return

    def add_blank_field_to_cbo(self, set=True):
        """ Add a blank string to the field combo box.  Fixed in qgis 3"""

        if self.mFieldComboBox.findText('', QtCore.Qt.MatchFixedString) == -1:
            self.mFieldComboBox.addItem(u'')
            if set == True:
                self.mFieldComboBox.setField(u'')

    def on_mcboRasterLayer_layerChanged(self):
        self.updateRaster()
        self.autoSetCoordinateSystem()

    def on_mcboPolygonLayer_layerChanged(self):
        self.updateUseSelected()
        self.autoSetCoordinateSystem()

        # ToDo: QGIS 3 implement QgsMapLayerComboBox.allowEmptyLayer() instead of chkUsePoly checkbox
        self.chkUsePoly.setChecked(True)

        self.add_blank_field_to_cbo()

    def on_cboBandRed_currentIndexChanged(self, index):
        band_num = 0
        if self.cboBandRed.currentText() != '':
            band_num = int(self.cboBandRed.currentText().replace('Band ', ''))

        if self.band_mapping['red'] != band_num:
            self.band_mapping['red'] = band_num
            self.update_bandlist()

    def on_cboBandGreen_currentIndexChanged(self, index):
        band_num = 0
        if self.cboBandGreen.currentText() != '':
            band_num = int(self.cboBandGreen.currentText().replace(
                'Band ', ''))

        if self.band_mapping['green'] != band_num:
            self.band_mapping['green'] = band_num
            self.update_bandlist()

    def on_cboBandIR_currentIndexChanged(self, index):
        band_num = 0
        if self.cboBandIR.currentText() != '':
            band_num = int(self.cboBandIR.currentText().replace('Band ', ''))

        if self.band_mapping['infrared'] != band_num:
            self.band_mapping['infrared'] = band_num
            self.update_bandlist()

    def on_cboBandRedEdge_currentIndexChanged(self, index):
        band_num = 0
        if self.cboBandRedEdge.currentText() != '':
            band_num = int(self.cboBandRedEdge.currentText().replace(
                'Band ', ''))

        if self.band_mapping['rededge'] != band_num:
            self.band_mapping['rededge'] = band_num
            self.update_bandlist()

    def on_cboBandNonVine_currentIndexChanged(self, index):
        band_num = 0
        if self.cboBandNonVine.currentText() != '':
            band_num = int(self.cboBandNonVine.currentText().replace(
                'Band ', ''))

        self.band_mapping['mask'] = band_num
        self.update_bandlist()

    @QtCore.pyqtSlot(int)
    def on_chkUsePoly_stateChanged(self, state):

        self.add_blank_field_to_cbo()

        self.mFieldComboBox.setEnabled(state)
        self.lblGroupByField.setEnabled(state)

    def on_chkUseSelected_stateChanged(self, state):
        if self.chkUseSelected.isChecked():
            self.chkUsePoly.setChecked(True)

    @QtCore.pyqtSlot(name='on_cmdOutCRS_clicked')
    def on_cmdOutCRS_clicked(self):
        dlg = QgsGenericProjectionSelector(self)
        dlg.setMessage(self.tr('Select coordinate system'))
        if dlg.exec_():
            if dlg.selectedAuthId() != '':
                self.outQgsCRS = QgsCoordinateReferenceSystem(
                    dlg.selectedAuthId())

                if self.outQgsCRS.geographicFlag():
                    self.outQgsCRS = None
                    self.send_to_messagebar(unicode(
                        self.
                        tr("Geographic coordinate systems are not allowed. Resetting to default.."
                           )),
                                            level=QgsMessageBar.WARNING,
                                            duration=5)
            else:
                # Yes this can happen if you navigate by the categories and click Ok prior to a selection
                self.outQgsCRS = None

            if self.outQgsCRS is None:
                self.autoSetCoordinateSystem()

            self.lblOutCRSTitle.setStyleSheet('color:black')
            self.lblOutCRS.setStyleSheet('color:black')
            self.lblOutCRS.setText(
                self.tr('{}  -  {}'.format(self.outQgsCRS.description(),
                                           self.outQgsCRS.authid())))

    @QtCore.pyqtSlot(name='on_cmdOutputFolder_clicked')
    def on_cmdOutputFolder_clicked(self):
        self.messageBar.clearWidgets()

        if self.lneOutputFolder.text() is None:
            outFolder = ''
        else:
            outFolder = self.lneOutputFolder.text()

        if outFolder == '':
            outFolder = read_setting(PLUGIN_NAME + "/" + self.toolKey +
                                     "/LastOutFolder")
            if outFolder is None or not os.path.exists(outFolder):
                outFolder = read_setting(PLUGIN_NAME + '/BASE_OUT_FOLDER')

        s = QtGui.QFileDialog.getExistingDirectory(
            self,
            self.
            tr("Save output files to a folder. A sub-folder will be created from the image name"
               ), outFolder, QtGui.QFileDialog.ShowDirsOnly)

        self.cleanMessageBars(self)
        if s == '' or s is None:
            return
        s = os.path.normpath(s)
        self.lblOutputFolder.setStyleSheet('color:black')
        self.lneOutputFolder.setStyleSheet('color:black')
        self.lneOutputFolder.setText(s)
        write_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastOutFolder", s)

    def validate(self):
        """Check to see that all required gui elements have been entered and are valid."""
        self.messageBar.clearWidgets()
        self.cleanMessageBars(AllBars=True)
        try:
            errorList = []

            if self.mcboRasterLayer.currentLayer() is None:
                self.lblRasterLayer.setStyleSheet('color:red')
                errorList.append(self.tr("Input image layer required."))
            else:
                self.lblRasterLayer.setStyleSheet('color:black')

            if self.dsbPixelSize.value() <= 0:
                self.lblPixelSize.setStyleSheet('color:red')
                errorList.append(self.tr("Pixel size must be greater than 0."))
            else:
                self.lblPixelSize.setStyleSheet('color:black')

            # get list of allocated bands from band mapping excluding Nones and blanks
            if len(self.band_mapping.allocated_bands()) >= 2:
                self.lblBandMap.setStyleSheet('color:black')
            else:
                self.lblBandMap.setStyleSheet('color:red')
                errorList.append(
                    self.
                    tr("Please set at least two bands to display index options"
                       ))

            # check if any index is selected
            if any(x.isChecked() for x in self.chkgrpIndices.buttons()):
                self.lblCalcStats.setStyleSheet('color:black')
            else:
                self.lblCalcStats.setStyleSheet('color:red')
                errorList.append(
                    self.tr("Please select at least one index to calculate."))

            if self.outQgsCRS is None:
                self.lblOutCRSTitle.setStyleSheet('color:red')
                self.lblOutCRS.setStyleSheet('color:red')
                errorList.append(
                    self.tr("Select output projected coordinate system"))
            else:
                if self.outQgsCRS.geographicFlag():
                    self.lblOutCRSTitle.setStyleSheet('color:red')
                    self.lblOutCRS.setStyleSheet('color:red')
                    errorList.append(
                        self.
                        tr("Output projected coordinate system (not geographic) required"
                           ))
                else:
                    self.lblOutCRSTitle.setStyleSheet('color:black')
                    self.lblOutCRS.setStyleSheet('color:black')

            if self.lneOutputFolder.text() == '':
                self.lblOutputFolder.setStyleSheet('color:red')
                errorList.append(self.tr("Select output data folder"))
            elif not os.path.exists(self.lneOutputFolder.text()):
                self.lneOutputFolder.setStyleSheet('color:red')
                self.lblOutputFolder.setStyleSheet('color:red')
                errorList.append(self.tr("Output data folder does not exist"))
            else:
                self.lblOutputFolder.setStyleSheet('color:black')
                self.lneOutputFolder.setStyleSheet('color:black')

            if len(errorList) > 0:
                raise ValueError(errorList)

        except ValueError as e:
            self.cleanMessageBars(True)
            if len(errorList) > 0:
                for i, ea in enumerate(errorList):
                    self.send_to_messagebar(unicode(ea),
                                            level=QgsMessageBar.WARNING,
                                            duration=(i + 1) * 5)
                return False

        return True

    def accept(self, *args, **kwargs):
        """Run the processing"""
        try:

            if not self.validate():
                return False

            # disable form via a frame, this will still allow interaction with the message bar
            self.fraMain.setDisabled(True)
            # clean gui and Qgis messagebars
            self.cleanMessageBars(True)

            # Change cursor to Wait cursor
            QtGui.qApp.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
            self.iface.mainWindow().statusBar().showMessage(
                'Processing {}'.format(self.windowTitle()))
            LOGGER.info('{st}\nProcessing {}'.format(self.windowTitle(),
                                                     st='*' * 50))

            self.send_to_messagebar(
                "Please wait.. QGIS will be locked... See log panel for progress.",
                level=QgsMessageBar.WARNING,
                duration=0,
                addToLog=False,
                core_QGIS=False,
                showLogPanel=True)

            selectedIndices = [
                x.text() for x in self.chkgrpIndices.buttons()
                if x.isChecked()
            ]

            # Add settings to log
            settingsStr = 'Parameters:---------------------------------------'

            settingsStr += '\n    {:20}\t{}'.format(
                'Image layer:',
                self.mcboRasterLayer.currentLayer().name())
            settingsStr += '\n    {:20}\t{}'.format('Image nodata value:',
                                                    self.spnNoDataVal.value())

            if self.chkUsePoly.isChecked():
                if self.chkUseSelected.isChecked():
                    settingsStr += '\n    {:20}\t{} with {} selected features'.format(
                        'Layer:',
                        self.mcboPolygonLayer.currentLayer().name(),
                        len(self.mcboPolygonLayer.currentLayer().
                            selectedFeatures()))
                else:
                    settingsStr += '\n    {:20}\t{}'.format(
                        'Boundary layer:',
                        self.mcboPolygonLayer.currentLayer().name())

                if self.mFieldComboBox.currentField():
                    settingsStr += '\n    {:20}\t{}'.format(
                        'Block ID field:', self.mFieldComboBox.currentField())

            settingsStr += '\n    {:20}\t{}'.format('Resample pixel size: ',
                                                    self.dsbPixelSize.value())

            for k, v in self.band_mapping.iteritems():
                if v > 0:
                    settingsStr += '\n    {:20}\t{}'.format(
                        '{} Band:'.format(k.title()), v)

            settingsStr += '\n    {:20}\t{}'.format('Calculate Indices: ',
                                                    ', '.join(selectedIndices))
            settingsStr += '\n    {:30}\t{}'.format(
                'Output Coordinate System:', self.lblOutCRS.text())
            settingsStr += '\n    {:30}\t{}\n'.format(
                'Output Folder:', self.lneOutputFolder.text())

            LOGGER.info(settingsStr)

            lyrRaster = self.mcboRasterLayer.currentLayer()

            if self.chkUsePoly.isChecked():
                lyrBoundary = self.mcboPolygonLayer.currentLayer()

                if self.chkUseSelected.isChecked():
                    savePlyName = lyrBoundary.name() + '_poly.shp'
                    filePoly = os.path.join(TEMPDIR, savePlyName)
                    if os.path.exists(filePoly): removeFileFromQGIS(filePoly)

                    QgsVectorFileWriter.writeAsVectorFormat(lyrBoundary,
                                                            filePoly,
                                                            "utf-8",
                                                            lyrBoundary.crs(),
                                                            "ESRI Shapefile",
                                                            onlySelected=True)

                    if self.DISP_TEMP_LAYERS:
                        addVectorFileToQGIS(filePoly,
                                            layer_name=os.path.splitext(
                                                os.path.basename(filePoly))[0],
                                            group_layer_name='DEBUG',
                                            atTop=True)
                else:
                    filePoly = lyrBoundary.source()

            files = calc_indices_for_block(
                lyrRaster.source(),
                self.dsbPixelSize.value(),
                self.band_mapping,
                self.lneOutputFolder.text(),
                indices=selectedIndices,
                image_epsg=int(lyrRaster.crs().authid().replace('EPSG:', '')),
                image_nodata=self.spnNoDataVal.value(),
                polygon_shapefile=filePoly
                if self.chkUsePoly.isChecked() else None,
                groupby=self.mFieldComboBox.currentField()
                if self.mFieldComboBox.currentField() else None,
                out_epsg=int(self.outQgsCRS.authid().replace('EPSG:', '')))

            if self.chkAddToDisplay.isChecked():
                for ea_file in files:
                    raster_sym = RASTER_SYMBOLOGY[
                        'Image Indices (ie PCD, NDVI)']
                    raster_lyr = addRasterFileToQGIS(
                        ea_file,
                        atTop=False,
                        group_layer_name=os.path.basename(
                            os.path.dirname(ea_file)))
                    raster_apply_classified_renderer(
                        raster_lyr,
                        rend_type=raster_sym['type'],
                        num_classes=raster_sym['num_classes'],
                        color_ramp=raster_sym['colour_ramp'])

            self.cleanMessageBars(True)
            self.fraMain.setDisabled(False)

            self.iface.mainWindow().statusBar().clearMessage()
            self.iface.messageBar().popWidget()
            QtGui.qApp.restoreOverrideCursor()
            return super(CalculateImageIndicesDialog,
                         self).accept(*args, **kwargs)

        except Exception as err:

            QtGui.qApp.restoreOverrideCursor()
            self.iface.mainWindow().statusBar().clearMessage()
            self.cleanMessageBars(True)
            self.fraMain.setDisabled(False)

            self.send_to_messagebar(str(err),
                                    level=QgsMessageBar.CRITICAL,
                                    duration=0,
                                    addToLog=True,
                                    core_QGIS=False,
                                    showLogPanel=True,
                                    exc_info=sys.exc_info())

            return False  # leave dialog open