def generate_detection_layer(self): from pca4cd.pca4cd import PCA4CD as pca4cd detection_from = self.RangeChangeFrom.value() detection_to = self.RangeChangeTo.value() output_change_layer = Path(pca4cd.tmp_dir, self.pc_layer.name() + "_detection.tif") # compute the detection layer between range values da_pc = da.from_array(self.pc_data, chunks=(2000, 2000)) def calc(block, range_from, range_to): result = np.zeros_like(block) result[(block >= range_from) & (block <= range_to) & (block != 0)] = 1 return result # process map_blocks = da.map_blocks(calc, da_pc, range_from=detection_from, range_to=detection_to, dtype=np.int8) detection_layer_ds = map_blocks.compute(scheduler='threads', num_workers=cpu_count()) # save if self.driver_detection_layer is None: driver = gdal.GetDriverByName("GTiff") self.driver_detection_layer = driver.Create( str(output_change_layer), self.pc_gdal_ds.RasterXSize, self.pc_gdal_ds.RasterYSize, 1, gdal.GDT_Byte, ["NBITS=1", "COMPRESS=NONE"]) dl_band = self.driver_detection_layer.GetRasterBand(1) dl_band.SetNoDataValue(0) dl_band.WriteArray(detection_layer_ds) # set projection and geotransform if self.pc_gdal_ds.GetGeoTransform() is not None: self.driver_detection_layer.SetGeoTransform( self.pc_gdal_ds.GetGeoTransform()) if self.pc_gdal_ds.GetProjection() is not None: self.driver_detection_layer.SetProjection( self.pc_gdal_ds.GetProjection()) dl_band.FlushCache() dl_band = None self.driver_detection_layer.FlushCache() # necessary for fix flushing cache generating the detection layer the first time (Linux/Mac) # and not in Windows due to permission problems when the detection layer is overwritten if platform.system() != 'Windows': self.driver_detection_layer = None detection_layer = load_layer(output_change_layer, add_to_legend=False) apply_symbology(detection_layer, [("0", 0, (255, 255, 255, 0)), ("1", 1, (255, 255, 0, 255))]) self.render_widget.set_detection_layer(detection_layer) self.parent_view_widget.render_widget.set_detection_layer( detection_layer) self.parent_view_widget.EnableChangeDetection.setChecked(True) self.ShowHideChangeDetection.setEnabled(True) self.ShowHideChangeDetection.setChecked(True)
def do_merge_change_layers(self, merge_dialog): merged_change_layer = Path(merge_dialog.MergeFileWidget.filePath()) MergeChangeLayersDialog.merged_file_path = merged_change_layer # first unload layer from qgis if exists unload_layer(merged_change_layer) merge_method = merge_dialog.MergeMethod.currentText() if len(self.activated_change_layers) == 1: shutil.copy(get_file_path_of_layer(self.activated_change_layers[0]), merged_change_layer) if len(self.activated_change_layers) > 1 and merge_method == "Union": cmd = ['gdal_merge' if platform.system() == 'Windows' else 'gdal_merge.py', '-of', 'GTiff', '-o', str(merged_change_layer), '-n', '0', '-a_nodata', '0', '-ot', 'Byte'] + [str(get_file_path_of_layer(layer)) for layer in self.activated_change_layers] subprocess.run(" ".join(cmd), shell=True) if len(self.activated_change_layers) > 1 and merge_method == "Intersection": alpha_list = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"] input_files = {alpha_list[x]: str(get_file_path_of_layer(f)) for x, f in enumerate(self.activated_change_layers)} filter_ones = ",".join([alpha_list[x] + "==1" for x in range(len(self.activated_change_layers))]) filter_zeros = ",".join([alpha_list[x] + "==0" for x in range(len(self.activated_change_layers))]) cmd = ['gdal_calc' if platform.system() == 'Windows' else 'gdal_calc.py', '--overwrite', '--calc', '"0*(numpy.any([{filter_zeros}], axis=0)) + 1*(numpy.all([{filter_ones}], axis=0))"' .format(filter_zeros=filter_zeros, filter_ones=filter_ones), '--outfile', str(merged_change_layer), '--NoDataValue=0', '--type=Byte'] + \ [i for sl in [["-{}".format(letter), filepath] for letter, filepath in input_files.items()] for i in sl] subprocess.run(" ".join(cmd), shell=True) # unset nodata cmd = ['gdal_edit' if platform.system() == 'Windows' else 'gdal_edit.py', '"{}"'.format(merged_change_layer), '-unsetnodata'] subprocess.run(" ".join(cmd), shell=True) # apply style merged_layer = load_layer(merged_change_layer, add_to_legend=True if merge_dialog.LoadInQgis.isChecked() else False) apply_symbology(merged_layer, [("0", 0, (255, 255, 255, 0)), ("1", 1, (255, 255, 0, 255))]) # save named style in QML aux file merged_layer.saveNamedStyle(str(merged_change_layer.with_suffix(".qml"))) # add the merged layer to input and auxiliary view for view_widget in MainAnalysisDialog.view_widgets: if view_widget.pc_id is None: view_widget.WidgetDetectionLayer.setEnabled(True) view_widget.QPBtn_ComponentAnalysisDialog.setToolTip("") view_widget.render_widget.set_detection_layer(merged_layer) if view_widget.is_active: view_widget.EnableChangeDetection.setChecked(True) view_widget.QPBtn_ComponentAnalysisDialog.setEnabled(True) if len(self.activated_ids) == 1: self.MsgBar.pushMessage( "The change detection for {} was saved and loaded successfully".format( self.activated_ids[0]), level=Qgis.Success) else: self.MsgBar.pushMessage( "The change detection for {} were merged, saved and loaded successfully".format( ", ".join(self.activated_ids)), level=Qgis.Success)
def generate_principal_components(self): from pca4cd.pca4cd import PCA4CD as pca4cd # check if is valid the input raster layer if self.QCBox_InputData_A.currentLayer() is None: self.MsgBar.pushMessage("Select a valid input raster layer", level=Qgis.Warning) return # check both layers if not self.check_input_layers(self.QCBox_InputData_A.currentLayer(), self.QCBox_InputData_B.currentLayer()): return # check the nodata value nodata = self.NoData_ComputePCA.text() if self.NoData_ComputePCA.text( ) not in ["", "None", "nan"] else None if nodata is not None: try: nodata = float(nodata) except: self.MsgBar.pushMessage("The nodata value is not valid", level=Qgis.Warning) return path_layer_A = get_file_path_of_layer( self.QCBox_InputData_A.currentLayer()) path_layer_B = get_file_path_of_layer( self.QCBox_InputData_B.currentLayer()) n_pc = int(self.QCBox_nComponents.currentText()) estimator_matrix = self.QCBox_EstimatorMatrix.currentText() pca_files, pca_stats = pca(path_layer_A, path_layer_B, n_pc, estimator_matrix, pca4cd.tmp_dir, self.nThreads.value(), self.BlockSize.value(), nodata) pca_layers = [] if pca_files: for pca_file in pca_files: pca_layers.append(load_layer(pca_file, add_to_legend=False)) # then, open main analysis dialog self.open_main_analysis_dialog(pca_layers, pca_stats, nodata) else: self.MsgBar.pushMessage( "Error while generating the principal components, check the Qgis log", level=Qgis.Critical)
def load_external_pc_in_main_analysis_dialog(self): from pca4cd.pca4cd import PCA4CD as pca4cd stack_path = self.QgsFile_LoadStackPCA.filePath() if not os.path.isfile(stack_path): self.MsgBar.pushMessage("Select a valid stack for load", level=Qgis.Warning) return False # check the nodata value nodata = self.NoData_LoadPCA.text() if self.NoData_LoadPCA.text( ) not in ["", "None", "nan"] else None if nodata is not None: try: nodata = float(nodata) except: self.MsgBar.pushMessage("The nodata value is not valid", level=Qgis.Warning) return # extract each band as component in tmp file src_ds = gdal.Open(stack_path, gdal.GA_ReadOnly) num_bands = src_ds.RasterCount del src_ds pca_layers = [] for component in range(num_bands): tmp_pca_file = pca4cd.tmp_dir / 'pc_{}.tif'.format(component + 1) gdal.Translate(str(tmp_pca_file), stack_path, bandList=[component + 1], noData=nodata) pca_layers.append(load_layer(tmp_pca_file, add_to_legend=False)) # pca statistics pca_stats = {} pca_stats["eigenvals"] = None self.main_analysis_dialog = MainAnalysisDialog(None, None, pca_layers, pca_stats, nodata) # open dialog self.main_analysis_dialog.show() self.main_analysis_dialog.update_pc_style()