def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(self.plugin_dir, 'i18n', 'AcATaMa_{}.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) print("** INITIALIZING AcATaMa") self.menu_name_plugin = self.tr("Accuracy Assessment of Thematic Maps") self.pluginIsActive = False self.dockwidget = None self.about_dialog = AboutDialog()
class AcATaMa(object): """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(self.plugin_dir, 'i18n', 'AcATaMa_{}.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) print("** INITIALIZING AcATaMa") self.menu_name_plugin = self.tr("Accuracy Assessment of Thematic Maps") self.pluginIsActive = False self.dockwidget = None self.about_dialog = AboutDialog() # 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("AcATaMa", message) def initGui(self): ### Main dockwidget menu # Create action that will start plugin configuration icon_path = ':/plugins/AcATaMa/icons/acatama.svg' self.dockable_action = QAction(QIcon(icon_path), "AcATaMa", self.iface.mainWindow()) # connect the action to the run method self.dockable_action.triggered.connect(self.run) # Add toolbar button and menu item self.iface.addToolBarIcon(self.dockable_action) self.iface.addPluginToMenu(self.menu_name_plugin, self.dockable_action) # Plugin info # Create action that will start plugin configuration icon_path = ':/plugins/AcATaMa/icons/about.svg' self.about_action = QAction(QIcon(icon_path), self.tr('About'), self.iface.mainWindow()) # connect the action to the run method self.about_action.triggered.connect(self.about) # Add toolbar button and menu item self.iface.addPluginToMenu(self.menu_name_plugin, self.about_action) def about(self): self.about_dialog.show() #-------------------------------------------------------------------------- def run(self): """Run method that loads and starts the plugin""" if not self.pluginIsActive: self.pluginIsActive = True #print "** STARTING AcATaMa" # dockwidget may not exist if: # first run of plugin # removed on close (see self.onClosePlugin method) if self.dockwidget == None: # Create the dockwidget (after translation) and keep reference self.dockwidget = AcATaMaDockWidget() # connect to provide cleanup on closing of dockwidget self.dockwidget.closingPlugin.connect(self.onClosePlugin) # reload self.dockwidget.QPBtn_PluginClearReload.clicked.connect( self.clear_reload_plugin) # show the dockwidget # TODO: fix to allow choice of dock location self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dockwidget) self.dockwidget.show() #-------------------------------------------------------------------------- def onClosePlugin(self): """Cleanup necessary items here when plugin dockwidget is closed""" print("** CLOSING AcATaMa") if ClassificationDialog.is_opened: self.dockwidget.classification_dialog.closing() self.dockwidget.classification_dialog.reject(is_ok_to_close=True) if AccuracyAssessmentDialog.is_opened: self.dockwidget.accuracy_assessment_dialog.closing() self.dockwidget.accuracy_assessment_dialog.reject( is_ok_to_close=True) self.removes_temporary_files() # disconnects self.dockwidget.closingPlugin.disconnect(self.onClosePlugin) # remove this statement if dockwidget is to remain # for reuse if plugin is reopened # Commented next statement since it causes QGIS crashe # when closing the docked window: # self.dockwidget = None self.dockwidget.deleteLater() self.dockwidget = None self.pluginIsActive = False from qgis.utils import reloadPlugin reloadPlugin("AcATaMa") def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" print("** UNLOAD AcATaMa") self.removes_temporary_files() # Remove the plugin menu item and icon self.iface.removePluginMenu(self.menu_name_plugin, self.dockable_action) self.iface.removePluginMenu(self.menu_name_plugin, self.about_action) self.iface.removeToolBarIcon(self.dockable_action) if self.dockwidget: self.iface.removeDockWidget(self.dockwidget) def clear_reload_plugin(self): # first prompt quit_msg = "Are you sure you want to: clean tmp files, delete unsaved classification, " \ "clean all fields and reload plugin?" reply = QMessageBox.question( None, 'Clear all and reload the AcATaMa plugin.', quit_msg, QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.No: return self.onClosePlugin() from qgis.utils import plugins plugins["AcATaMa"].run() def removes_temporary_files(self): if not self.dockwidget: return # unload all layers instances from Qgis saved in tmp dir try: d = self.dockwidget.tmp_dir files_in_tmp_dir = [ os.path.join(d, f) for f in os.listdir(d) if os.path.isfile(os.path.join(d, f)) ] except: files_in_tmp_dir = [] for file_tmp in files_in_tmp_dir: unload_layer(file_tmp) # clear self.dockwidget.tmp_dir if self.dockwidget.tmp_dir and os.path.isdir(self.dockwidget.tmp_dir): shutil.rmtree(self.dockwidget.tmp_dir, ignore_errors=True) self.dockwidget.tmp_dir = None # clear qgis main canvas self.iface.mapCanvas().clearCache() self.iface.mapCanvas().refresh()
def setup_gui(self): # ######### plugin info ######### # self.about_dialog = AboutDialog() self.QPBtn_PluginInfo.setText("v{}".format(VERSION)) self.QPBtn_PluginInfo.clicked.connect(self.about_dialog.show) self.QPBtn_PluginDocs.clicked.connect( lambda: webbrowser.open("https://smbyc.github.io/AcATaMa")) # ######### load thematic raster image ######### # # set properties to QgsMapLayerComboBox self.QCBox_ThematicRaster.setCurrentIndex(-1) self.QCBox_ThematicRaster.setFilters(QgsMapLayerProxyModel.RasterLayer) # call to browse the thematic raster file self.QPBtn_browseThematicRaster.clicked.connect( lambda: self.browser_dialog_to_load_file( self.QCBox_ThematicRaster, dialog_title=self.tr( "Select the thematic raster image to evaluate"), file_filters=self.tr( "Raster files (*.tif *.img);;All files (*.*)"))) # select and check the thematic raster self.QCBox_ThematicRaster.layerChanged.connect( self.select_thematic_raster) # ######### shape area of interest ######### # self.widget_AreaOfInterest.setHidden(True) # set properties to QgsMapLayerComboBox self.QCBox_AreaOfInterest.setCurrentIndex(-1) self.QCBox_AreaOfInterest.setFilters( QgsMapLayerProxyModel.PolygonLayer) # call to browse the shape area self.QPBtn_browseAreaOfInterest.clicked.connect( lambda: self.browser_dialog_to_load_file( self.QCBox_AreaOfInterest, dialog_title=self.tr("Select the vector file"), file_filters=self.tr( "Vector files (*.gpkg *.shp);;All files (*.*)"))) # do clip self.QPBtn_ClippingThematic.clicked.connect( self.clipping_thematic_raster) # ######### create categorical ######### # TODO # self.widget_CategRaster.setHidden(True) # # handle connect when the list of layers changed # # call to browse the categorical raster # self.browseCategRaster.clicked.connect(lambda: self.fileDialog_browse( # self.selectCategRaster, # dialog_title=self.tr("Select the categorical raster file"), # dialog_types=self.tr("Raster files (*.tif *.img);;All files (*.*)"), # layer_type="raster")) # ######### simple random sampling ######### # self.widget_SimpRSwithCR.setHidden(True) # set properties to QgsMapLayerComboBox self.QCBox_CategRaster_SimpRS.setCurrentIndex(-1) self.QCBox_CategRaster_SimpRS.setFilters( QgsMapLayerProxyModel.RasterLayer) # call to browse the categorical raster self.QPBtn_browseCategRaster_SimpRS.clicked.connect( lambda: self.browser_dialog_to_load_file( self.QCBox_CategRaster_SimpRS, dialog_title=self.tr("Select the categorical raster file"), file_filters=self.tr( "Raster files (*.tif *.img);;All files (*.*)"))) # select and check the categorical raster self.QCBox_CategRaster_SimpRS.layerChanged.connect( self.select_categorical_raster_SimpRS) # generate and random sampling options self.widget_generate_SimpRS.generate_sampling_widget_options.setHidden( True) self.widget_generate_SimpRS.random_sampling_widget_options.setHidden( True) # save config self.widget_generate_SimpRS.widget_save_sampling_config.setHidden(True) iface.mapCanvas().layersChanged.connect( lambda: self.update_generated_sampling_list_in( self.widget_generate_SimpRS.QCBox_SamplingToSave)) self.widget_generate_SimpRS.QPBtn_SaveSamplingConf.clicked.connect( lambda: self.fileDialog_saveSamplingConf( self.widget_generate_SimpRS.QCBox_SamplingToSave)) # generate sampling self.widget_generate_SimpRS.QPBtn_GenerateSampling.clicked.connect( lambda: do_simple_random_sampling(self)) # update progress bar limits self.numberOfSamples_SimpRS.valueChanged.connect( lambda: self.widget_generate_SimpRS.QPBar_GenerateSampling. setValue(0)) self.numberOfSamples_SimpRS.valueChanged.connect( self.widget_generate_SimpRS.QPBar_GenerateSampling.setMaximum) # ######### stratified random sampling ######### # # set properties to QgsMapLayerComboBox self.QCBox_CategRaster_StraRS.setCurrentIndex(-1) self.QCBox_CategRaster_StraRS.setFilters( QgsMapLayerProxyModel.RasterLayer) # call to browse the categorical raster self.QPBtn_browseCategRaster_StraRS.clicked.connect( lambda: self.browser_dialog_to_load_file( self.QCBox_CategRaster_StraRS, dialog_title=self.tr("Select the categorical raster file"), file_filters=self.tr( "Raster files (*.tif *.img);;All files (*.*)"))) # select and check the categorical raster self.QCBox_CategRaster_StraRS.layerChanged.connect( self.select_categorical_raster_StraRS) self.QCBox_band_CategRaster_StraRS.currentIndexChanged.connect( self.reset_StraRS_method) self.nodata_CategRaster_StraRS.valueChanged.connect( self.reset_StraRS_method) # init variable for save tables content self.srs_tables = {} # fill table of categorical raster self.widget_TotalExpectedSE.setHidden(True) self.QCBox_StraRS_Method.currentIndexChanged.connect( lambda: fill_stratified_sampling_table(self)) # for each item changed in table, save and update it self.TotalExpectedSE.valueChanged.connect( lambda: update_stratified_sampling_table(self, "TotalExpectedSE")) self.QTableW_StraRS.itemChanged.connect( lambda: update_stratified_sampling_table(self, "TableContent")) # generate and random sampling options self.widget_generate_StraRS.generate_sampling_widget_options.setHidden( True) self.widget_generate_StraRS.random_sampling_widget_options.setHidden( True) # save config self.widget_generate_StraRS.widget_save_sampling_config.setHidden(True) iface.mapCanvas().layersChanged.connect( lambda: self.update_generated_sampling_list_in( self.widget_generate_StraRS.QCBox_SamplingToSave)) self.widget_generate_StraRS.QPBtn_SaveSamplingConf.clicked.connect( lambda: self.fileDialog_saveSamplingConf( self.widget_generate_StraRS.QCBox_SamplingToSave)) # generate sampling self.widget_generate_StraRS.QPBtn_GenerateSampling.clicked.connect( lambda: do_stratified_random_sampling(self)) # disable sampling tab at start self.scrollAreaWidgetContents_S.setDisabled(True) # ######### Classification sampling tab ######### # # set properties to QgsMapLayerComboBox self.QCBox_SamplingFile.setCurrentIndex(-1) self.QCBox_SamplingFile.setFilters(QgsMapLayerProxyModel.PointLayer) # show the classification file settings in plugin when it is selected self.QCBox_SamplingFile.layerChanged.connect( self.update_the_status_of_classification) # call to browse the sampling file self.QPBtn_browseSamplingFile.clicked.connect( lambda: self.browser_dialog_to_load_file( self.QCBox_SamplingFile, dialog_title=self.tr( "Select the Sampling points file to classify"), file_filters=self.tr( "Vector files (*.gpkg *.shp);;All files (*.*)"))) # call to reload sampling file self.QPBtn_reloadSamplingFile.clicked.connect( self.reload_sampling_file) # call to load and save classification config self.QPBtn_loadClassificationConfig.clicked.connect( self.fileDialog_loadClassificationConfig) self.QPBtn_saveClassificationConfig.clicked.connect( self.fileDialog_saveClassificationConfig) # save sampling + classification self.QPBtn_saveSamplingClassification.clicked.connect( self.fileDialog_saveSamplingClassification) # change grid config self.grid_columns.valueChanged.connect( lambda: self.set_grid_setting("column")) self.grid_rows.valueChanged.connect( lambda: self.set_grid_setting("row")) # disable group box that depends of sampling file self.QGBox_SamplingClassification.setDisabled(True) self.QGBox_saveSamplingClassified.setDisabled(True) # connect the action to the run method self.QPBtn_OpenClassificationDialog.clicked.connect( self.open_classification_dialog) # ######### Accuracy Assessment tab ######### # # set properties to QgsMapLayerComboBox self.QCBox_SamplingFile_AA.setCurrentIndex(-1) self.QCBox_SamplingFile_AA.setFilters(QgsMapLayerProxyModel.PointLayer) # set and show the classification file status in AA self.QCBox_SamplingFile_AA.layerChanged.connect( self.set_sampling_file_accuracy_assessment) # compute the AA and open the result dialog self.QPBtn_ComputeViewAccurasyAssessment.clicked.connect( self.open_accuracy_assessment_results)