def browser_dialog_to_load_file(self, combo_box, dialog_title, file_filters): file_path, _ = QFileDialog.getOpenFileName(self, dialog_title, "", file_filters) if file_path != '' and os.path.isfile(file_path): # load to qgis and update combobox list load_and_select_filepath_in(combo_box, file_path)
def clipping_thematic_raster(self): # first check input files requirements if not valid_file_selected_in(self.QCBox_ThematicRaster, "thematic raster"): return if not valid_file_selected_in(self.QCBox_AreaOfInterest, "area of interest shape"): return # first select the target dir for save the clipping file filename, ext = os.path.splitext(get_current_file_path_in(self.QCBox_ThematicRaster)) ext = ext if ext in [".tif", ".TIF", ".img", ".IMG"] else ".tif" suggested_filename = filename + "_clip" + ext file_out, _ = QFileDialog.getSaveFileName(self, self.tr("Select the output file to save the clipping file"), suggested_filename, self.tr("GeoTiff files (*.tif);;Img files (*.img);;All files (*.*)")) if file_out == '': return # clipping clip_file = do_clipping_with_shape( self.QCBox_ThematicRaster.currentLayer(), self.QCBox_AreaOfInterest.currentLayer(), file_out, get_nodata_value(self.QCBox_ThematicRaster.currentLayer())) # copy the style thematic_basename = os.path.splitext(get_current_file_path_in(self.QCBox_ThematicRaster))[0] if os.path.isfile(thematic_basename + ".qml"): copyfile(thematic_basename + ".qml", os.path.splitext(file_out)[0] + ".qml") # unload old thematic file unload_layer(get_current_file_path_in(self.QCBox_ThematicRaster)) # load to qgis and update combobox list load_and_select_filepath_in(self.QCBox_ThematicRaster, clip_file) self.select_thematic_raster(self.QCBox_ThematicRaster.currentLayer()) iface.messageBar().pushMessage("AcATaMa", "Clipping the thematic raster with shape, completed", level=Qgis.Success)
def fileDialog_loadClassificationConfig(self): file_path, _ = QFileDialog.getOpenFileName(self, self.tr("Restore the configuration and classification status"), "", self.tr("Yaml (*.yaml *.yml);;All files (*.*)")) if file_path != '' and os.path.isfile(file_path): # load classification status from yaml file import yaml with open(file_path, 'r') as yaml_file: try: yaml_config = yaml.load(yaml_file) except yaml.YAMLError as err: iface.messageBar().pushMessage("AcATaMa", "Error while read the yaml file classification config", level=Qgis.Critical) return # load the sampling file save in yaml config sampling_filepath = yaml_config["sampling_layer"] if not os.path.isfile(sampling_filepath): iface.messageBar().pushMessage("AcATaMa", "Error the sampling file saved in this config file, not exists", level=Qgis.Critical) # TODO: ask for new location of the sampling file return sampling_layer = load_and_select_filepath_in(self.QCBox_SamplingFile, sampling_filepath) # restore configuration and classification status classification = Classification(sampling_layer) classification.load_config(yaml_config) # reload sampling file status in accuracy assessment self.set_sampling_file_accuracy_assessment() iface.messageBar().pushMessage("AcATaMa", "File loaded successfully", level=Qgis.Success)
def load_config(self, yaml_config): from AcATaMa.gui.acatama_dockwidget import AcATaMaDockWidget as AcATaMa # restore the thematic raster if yaml_config["thematic_raster"]["path"]: # thematic raster load_and_select_filepath_in(AcATaMa.dockwidget.QCBox_ThematicRaster, yaml_config["thematic_raster"]["path"]) AcATaMa.dockwidget.select_thematic_raster(AcATaMa.dockwidget.QCBox_ThematicRaster.currentLayer()) # band number if "band" in yaml_config["thematic_raster"]: AcATaMa.dockwidget.QCBox_band_ThematicRaster.setCurrentIndex(yaml_config["thematic_raster"]["band"] - 1) # nodata AcATaMa.dockwidget.nodata_ThematicRaster.setValue(yaml_config["thematic_raster"]["nodata"]) # restore the classification settings AcATaMa.dockwidget.grid_columns.setValue(yaml_config["grid_view_widgets"]["columns"]) AcATaMa.dockwidget.grid_rows.setValue(yaml_config["grid_view_widgets"]["rows"]) self.dialog_size = yaml_config["dialog_size"] self.grid_columns = yaml_config["grid_view_widgets"]["columns"] self.grid_rows = yaml_config["grid_view_widgets"]["rows"] self.current_sample_idx = yaml_config["current_sample_idx"] self.fit_to_sample = yaml_config["fit_to_sample"] self.is_completed = yaml_config["is_completed"] # restore the buttons config self.buttons_config = yaml_config["classification_buttons"] # restore the view widget config self.view_widgets_config = yaml_config["view_widgets_config"] # support load the old format of config file TODO: delete for x in self.view_widgets_config.values(): if "render_file" in x: x["render_file_path"] = x["render_file"] del x["render_file"] if "name" in x: x["view_name"] = x["name"] del x["name"] if "layer_name" not in x: x["layer_name"] = None # restore the samples order points_ordered = [] for shape_id in yaml_config["points_order"]: # point saved exist in shape file if shape_id in [p.shape_id for p in self.points]: points_ordered.append([p for p in self.points if p.shape_id == shape_id][0]) # added new point inside shape file that not exists in yaml config for new_point_id in set([p.shape_id for p in self.points]) - set(yaml_config["points_order"]): points_ordered.append([p for p in self.points if p.shape_id == new_point_id][0]) # reassign points loaded and ordered self.points = points_ordered # restore point status classification for status in yaml_config["points"].values(): if status["shape_id"] in [p.shape_id for p in self.points]: point_to_restore = [p for p in self.points if p.shape_id == status["shape_id"]][0] point_to_restore.classif_id = status["classif_id"] if point_to_restore.classif_id is not None: point_to_restore.is_classified = True # update the status and labels plugin with the current sampling classification self.reload_classification_status() AcATaMa.dockwidget.update_the_status_of_classification() # define if this classification was made with thematic classes if self.buttons_config and yaml_config["thematic_raster"]["path"] and \ True in [bc["thematic_class"] is not None and bc["thematic_class"] != "" for bc in self.buttons_config.values()]: self.with_thematic_classes = True # restore accuracy assessment conf if "accuracy_assessment_sampling_file" in yaml_config and yaml_config["accuracy_assessment_sampling_file"]: load_and_select_filepath_in(AcATaMa.dockwidget.QCBox_SamplingFile_AA, yaml_config["accuracy_assessment_sampling_file"]) if "accuracy_assessment_dialog" in yaml_config: from AcATaMa.core.accuracy_assessment import AccuracyAssessment accuracy_assessment = AccuracyAssessment(self) area_unit, success = QgsUnitTypes.stringToAreaUnit(yaml_config["accuracy_assessment_dialog"]["area_unit"]) if success: accuracy_assessment.area_unit = area_unit accuracy_assessment.z_score = yaml_config["accuracy_assessment_dialog"]["z_score"] accuracy_assessment.csv_separator = yaml_config["accuracy_assessment_dialog"]["csv_separator"] accuracy_assessment.csv_decimal = yaml_config["accuracy_assessment_dialog"]["csv_decimal"] self.accuracy_assessment = accuracy_assessment
def __init__(self, sampling_layer, columns, rows): QDialog.__init__(self) self.sampling_layer = sampling_layer self.setupUi(self) ClassificationDialog.instance = self # flags self.setWindowFlags(self.windowFlags() | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint) # get classification or init new instance if sampling_layer in Classification.instances: self.classification = Classification.instances[sampling_layer] else: self.classification = Classification(sampling_layer) self.classification.grid_columns = columns self.classification.grid_rows = rows #### settings the classification dialog # set dialog title self.setWindowTitle("Classification of samples for " + sampling_layer.name()) # resize the classification dialog if self.classification.dialog_size: self.resize(*self.classification.dialog_size) # disable enter action self.QPBtn_SetClassification.setAutoDefault(False) self.QPBtn_unclassifySampleButton.setAutoDefault(False) # go to sample ID action self.GoTo_ID_Button.clicked.connect(self.go_to_sample_id) self.GoTo_ID.returnPressed.connect(self.go_to_sample_id) self.GoTo_ID.textChanged.connect( lambda: self.GoTo_ID.setStyleSheet("")) # open in Google Earth self.QPBtn_OpenInGE.clicked.connect( self.open_current_point_in_google_engine) # set properties and default value for the fit to sample spinBox based on sampling file layer_dist_unit = self.sampling_layer.crs().mapUnits() str_unit = QgsUnitTypes.toString(layer_dist_unit) abbr_unit = QgsUnitTypes.toAbbreviatedString(layer_dist_unit) self.radiusFitToSample.setSuffix(" {}".format(abbr_unit)) self.radiusFitToSample.setToolTip( "Units in {} for set the zoom radius to the current sample\n" "(units based on sampling file selected)".format(str_unit)) self.radiusFitToSample.setRange( 0, 360 if layer_dist_unit == QgsUnitTypes.DistanceDegrees else 10e6) self.radiusFitToSample.setDecimals(4 if layer_dist_unit in [ QgsUnitTypes.DistanceKilometers, QgsUnitTypes. DistanceNauticalMiles, QgsUnitTypes.DistanceMiles, QgsUnitTypes. DistanceDegrees ] else 1) self.radiusFitToSample.setSingleStep(0.0001 if layer_dist_unit in [ QgsUnitTypes.DistanceKilometers, QgsUnitTypes. DistanceNauticalMiles, QgsUnitTypes.DistanceMiles, QgsUnitTypes. DistanceDegrees ] else 1) self.radiusFitToSample.setValue(self.classification.fit_to_sample) # set total samples self.QPBar_SamplesNavigation.setMaximum(len( self.classification.points)) # actions for fit and go to current sample self.radiusFitToSample.valueChanged.connect( lambda: self.show_and_go_to_current_sample(highlight=False)) self.currentSample.clicked.connect( lambda: self.show_and_go_to_current_sample(highlight=True)) # move through samples self.nextSample.clicked.connect(self.next_sample) self.nextSampleNotClassified.clicked.connect( self.next_sample_not_classified) self.previousSample.clicked.connect(self.previous_sample) self.previousSampleNotClassified.clicked.connect( self.previous_sample_not_classified) # dialog buttons box self.closeButton.rejected.connect(self.closing) # disable enter action self.closeButton.button(QDialogButtonBox.Close).setAutoDefault(False) # init current point self.current_sample_idx = self.classification.current_sample_idx self.current_sample = None self.set_current_sample() # set classification buttons self.classification_btns_config = ClassificationButtonsConfig( self.classification.buttons_config) self.create_classification_buttons( buttons_config=self.classification.buttons_config) self.QPBtn_SetClassification.clicked.connect( self.open_set_classification_dialog) self.QPBtn_unclassifySampleButton.clicked.connect( self.unclassify_sample) # create dynamic size of the view render widgets windows # inside the grid with columns x rows divide by splitters h_splitters = [] view_widgets = [] for row in range(self.classification.grid_rows): splitter = QSplitter(Qt.Horizontal) for column in range(self.classification.grid_columns): new_view_widget = ClassificationViewWidget() splitter.addWidget(new_view_widget) h_splitters.append(splitter) view_widgets.append(new_view_widget) v_splitter = QSplitter(Qt.Vertical) for splitter in h_splitters: v_splitter.addWidget(splitter) # add to classification dialog self.widget_view_windows.layout().addWidget(v_splitter) # save instances ClassificationDialog.view_widgets = view_widgets # setup view widget [ view_widget.setup_view_widget(sampling_layer) for view_widget in ClassificationDialog.view_widgets ] for idx, view_widget in enumerate(ClassificationDialog.view_widgets): view_widget.id = idx # set the label names for each view for num_view, view_widget in enumerate( ClassificationDialog.view_widgets): view_widget.QLabel_ViewID.setText("View {}:".format(num_view + 1)) # restore view widgets status for config_id, view_config in self.classification.view_widgets_config.items( ): for view_widget in ClassificationDialog.view_widgets: if config_id == view_widget.id: view_widget = ClassificationDialog.view_widgets[config_id] # select the file for this view widget if exists and is loaded in Qgis layer_name = view_config["layer_name"] if not layer_name and view_config["render_file_path"]: layer_name = os.path.splitext( os.path.basename( view_config["render_file_path"]))[0] file_index = view_widget.QCBox_RenderFile.findText( layer_name, Qt.MatchFixedString) if file_index != -1: # select layer if exists in Qgis with block_signals_to(view_widget.QCBox_RenderFile): view_widget.QCBox_RenderFile.setCurrentIndex( file_index) elif view_config["render_file_path"] and os.path.isfile( view_config["render_file_path"]): # load file and select in view if this exists and not load in Qgis load_and_select_filepath_in( view_widget.QCBox_RenderFile, view_config["render_file_path"], layer_name=layer_name) elif view_config["render_file_path"] and not os.path.isfile( view_config["render_file_path"]): self.MsgBar.pushMessage( "Could not to load the layer '{}' in the view {}: no such file {}" .format( layer_name, "'{}'".format(view_config["view_name"]) if view_config["view_name"] else view_widget.id + 1, view_config["render_file_path"]), level=Qgis.Warning, duration=-1) else: self.MsgBar.pushMessage( "Could not to load the layer '{}' in the view {} (for network layers use save/load a Qgis project)" .format( layer_name, "'{}'".format(view_config["view_name"]) if view_config["view_name"] else view_widget.id + 1), level=Qgis.Warning, duration=-1) # TODO: restore size by view widget # view_widget.resize(*view_config["view_size"]) view_widget.QLabel_ViewName.setText( view_config["view_name"]) view_widget.scaleFactor.setValue( view_config["scale_factor"]) # active render layer in canvas view_widget.set_render_layer( view_widget.QCBox_RenderFile.currentLayer())
def restore(file_path): from AcATaMa.gui.acatama_dockwidget import AcATaMaDockWidget as AcATaMa # load the yaml file import yaml with open(file_path, 'r') as yaml_file: try: yaml_config = yaml.load(yaml_file, Loader=yaml.FullLoader) except yaml.YAMLError as err: iface.messageBar().pushMessage("AcATaMa", "Error while read the AcATaMa configuration file: {}".format(err), level=Qgis.Critical) return # ######### general configuration ######### # if "general" in yaml_config: AcATaMa.dockwidget.tabWidget.setCurrentIndex(yaml_config["general"]["tab_activated"]) # restore the thematic raster if yaml_config["thematic_raster"]["path"]: # thematic raster load_and_select_filepath_in(AcATaMa.dockwidget.QCBox_ThematicRaster, yaml_config["thematic_raster"]["path"]) AcATaMa.dockwidget.select_thematic_raster(AcATaMa.dockwidget.QCBox_ThematicRaster.currentLayer()) # band number if "band" in yaml_config["thematic_raster"]: AcATaMa.dockwidget.QCBox_band_ThematicRaster.setCurrentIndex(yaml_config["thematic_raster"]["band"] - 1) # nodata AcATaMa.dockwidget.nodata_ThematicRaster.setValue(yaml_config["thematic_raster"]["nodata"]) # ######### classification configuration ######### # # restore the classification settings # load the sampling file save in yaml config sampling_filepath = yaml_config["sampling_layer"] if os.path.isfile(sampling_filepath): sampling_layer = load_and_select_filepath_in(AcATaMa.dockwidget.QCBox_SamplingFile, sampling_filepath) classification = Classification(sampling_layer) AcATaMa.dockwidget.grid_columns.setValue(yaml_config["grid_view_widgets"]["columns"]) AcATaMa.dockwidget.grid_rows.setValue(yaml_config["grid_view_widgets"]["rows"]) classification.dialog_size = yaml_config["dialog_size"] classification.grid_columns = yaml_config["grid_view_widgets"]["columns"] classification.grid_rows = yaml_config["grid_view_widgets"]["rows"] classification.current_sample_idx = yaml_config["current_sample_idx"] classification.fit_to_sample = yaml_config["fit_to_sample"] classification.is_completed = yaml_config["is_completed"] # restore the buttons config classification.buttons_config = yaml_config["classification_buttons"] # restore the view widget config classification.view_widgets_config = yaml_config["view_widgets_config"] # support load the old format of config file TODO: delete for x in classification.view_widgets_config.values(): if "render_file" in x: x["render_file_path"] = x["render_file"] del x["render_file"] if "name" in x: x["view_name"] = x["name"] del x["name"] if "layer_name" not in x: x["layer_name"] = None # restore the samples order points_ordered = [] for shape_id in yaml_config["points_order"]: # point saved exist in shape file if shape_id in [p.shape_id for p in classification.points]: points_ordered.append([p for p in classification.points if p.shape_id == shape_id][0]) # added new point inside shape file that not exists in yaml config for new_point_id in set([p.shape_id for p in classification.points]) - set(yaml_config["points_order"]): points_ordered.append([p for p in classification.points if p.shape_id == new_point_id][0]) # reassign points loaded and ordered classification.points = points_ordered # restore point status classification for status in yaml_config["points"].values(): if status["shape_id"] in [p.shape_id for p in classification.points]: point_to_restore = [p for p in classification.points if p.shape_id == status["shape_id"]][0] point_to_restore.classif_id = status["classif_id"] if point_to_restore.classif_id is not None: point_to_restore.is_classified = True # update the status and labels plugin with the current sampling classification classification.reload_classification_status() AcATaMa.dockwidget.update_the_status_of_classification() # define if this classification was made with thematic classes if classification.buttons_config and yaml_config["thematic_raster"]["path"] and \ True in [bc["thematic_class"] is not None and bc["thematic_class"] != "" for bc in classification.buttons_config.values()]: classification.with_thematic_classes = True else: classification = None # ######### accuracy assessment ######### # # restore accuracy assessment settings # support load the old format of config file TODO: delete if "accuracy_assessment_sampling_file" in yaml_config and yaml_config["accuracy_assessment_sampling_file"]: load_and_select_filepath_in(AcATaMa.dockwidget.QCBox_SamplingFile_AA, yaml_config["accuracy_assessment_sampling_file"]) if "accuracy_assessment_sampling_type" in yaml_config: AcATaMa.dockwidget.QCBox_SamplingType_AA.setCurrentIndex(yaml_config["accuracy_assessment_sampling_type"]) if "accuracy_assessment_dialog" in yaml_config and classification: from AcATaMa.core.accuracy_assessment import AccuracyAssessment accuracy_assessment = AccuracyAssessment(classification) area_unit, success = QgsUnitTypes.stringToAreaUnit(yaml_config["accuracy_assessment_dialog"]["area_unit"]) if success: accuracy_assessment.area_unit = area_unit accuracy_assessment.z_score = yaml_config["accuracy_assessment_dialog"]["z_score"] accuracy_assessment.csv_separator = yaml_config["accuracy_assessment_dialog"]["csv_separator"] accuracy_assessment.csv_decimal = yaml_config["accuracy_assessment_dialog"]["csv_decimal"] classification.accuracy_assessment = accuracy_assessment # support load the old format end here if "accuracy_assessment" in yaml_config and "sampling_file" in yaml_config["accuracy_assessment"]: load_and_select_filepath_in(AcATaMa.dockwidget.QCBox_SamplingFile_AA, yaml_config["accuracy_assessment"]["sampling_file"]) if "accuracy_assessment" in yaml_config and "sampling_type" in yaml_config["accuracy_assessment"]: AcATaMa.dockwidget.QCBox_SamplingType_AA.setCurrentIndex(yaml_config["accuracy_assessment"]["sampling_type"]) if "accuracy_assessment" in yaml_config and "dialog" in yaml_config["accuracy_assessment"] and classification: from AcATaMa.core.accuracy_assessment import AccuracyAssessment accuracy_assessment = AccuracyAssessment(classification) area_unit, success = QgsUnitTypes.stringToAreaUnit(yaml_config["accuracy_assessment"]["dialog"]["area_unit"]) if success: accuracy_assessment.area_unit = area_unit accuracy_assessment.z_score = yaml_config["accuracy_assessment"]["dialog"]["z_score"] accuracy_assessment.csv_separator = yaml_config["accuracy_assessment"]["dialog"]["csv_separator"] accuracy_assessment.csv_decimal = yaml_config["accuracy_assessment"]["dialog"]["csv_decimal"] classification.accuracy_assessment = accuracy_assessment # reload sampling file status in accuracy assessment AcATaMa.dockwidget.set_sampling_file_accuracy_assessment()