def fileDialog_saveClassificationConfig(self): if not valid_file_selected_in(self.QCBox_SamplingFile): iface.messageBar().pushMessage( "AcATaMa", "Error, please select a sampling file to save configuration", level=Qgis.Warning) return # get file path to suggest to save but not in tmp directory file_path = get_file_path_of_layer( self.QCBox_SamplingFile.currentLayer()) path, filename = os.path.split(file_path) if self.tmp_dir in path: path = os.path.split( get_file_path_of_layer( self.QCBox_ThematicRaster.currentLayer()))[0] suggested_filename = os.path.splitext(os.path.join( path, filename))[0] + "_acatama.yml" if filename else "" file_out, _ = QFileDialog.getSaveFileName( self, self.tr("Save settings and classification status"), suggested_filename, self.tr("Yaml (*.yaml *.yml);;All files (*.*)")) if file_out != '': sampling_layer = self.QCBox_SamplingFile.currentLayer() if sampling_layer in Classification.instances: Classification.instances[sampling_layer].save_config(file_out) iface.messageBar().pushMessage("AcATaMa", "File saved successfully", level=Qgis.Success) else: iface.messageBar().pushMessage( "AcATaMa", "Failed to save, there isn't any configuration to save", level=Qgis.Warning)
def export_to_csv(self): # get file path to suggest to save but not in tmp directory from AcATaMa.gui.acatama_dockwidget import AcATaMaDockWidget as AcATaMa file_path = get_file_path_of_layer( AcATaMa.dockwidget.QCBox_SamplingFile_AA.currentLayer()) path, filename = os.path.split(file_path) if AcATaMa.dockwidget.tmp_dir in path: path = os.path.split( get_file_path_of_layer( AcATaMa.dockwidget.QCBox_ThematicRaster.currentLayer()))[0] suggested_filename = os.path.splitext(os.path.join( path, filename))[0] + "_results.csv" if filename else "" file_out, _ = QFileDialog.getSaveFileName( self, self.tr("Export accuracy assessment results to csv"), suggested_filename, self.tr("CSV files (*.csv);;All files (*.*)")) if file_out != '': try: accuracy_assessment_results.export_to_csv( self.accuracy_assessment, file_out, self.accuracy_assessment.csv_separator, self.accuracy_assessment.csv_decimal) self.MsgBar.pushMessage( "File saved successfully \"{}\"".format( os.path.basename(file_out)), level=Qgis.Success) except Exception as err: self.MsgBar.pushMessage( "Failed saving the csv file: {}".format(err), level=Qgis.Critical, duration=-1)
def get_pixel_count_by_pixel_values_parallel(layer, band, pixel_values=None): """Get the total pixel count for each pixel values""" if pixel_values is None: pixel_values = get_pixel_values(layer, band) # split the image in chunks, the 0,0 is left-upper corner gdal_file = gdal.Open(get_file_path_of_layer(layer), gdal.GA_ReadOnly) chunk_size = 1000 input_data = [] for y in chunks(range(gdal_file.RasterYSize), chunk_size): yoff = y[0] ysize = len(y) for x in chunks(range(gdal_file.RasterXSize), chunk_size): xoff = x[0] xsize = len(x) input_data.append((get_file_path_of_layer(layer), band, pixel_values, xoff, yoff, xsize, ysize)) # compute and merge all parallel process returns in one result with multiprocessing.Pool(multiprocessing.cpu_count()) as pool: imap_it = pool.imap(pixel_count_in_chunk, input_data) pixel_counts = np.sum([proc for proc in imap_it], axis=0).tolist() return dict(zip(pixel_values, pixel_counts))
def fileDialog_saveSamplingClassification(self): if not valid_file_selected_in(self.QCBox_SamplingFile): iface.messageBar().pushMessage("AcATaMa", "Error, please first select a sampling file", level=Qgis.Warning) return # get instance sampling_layer = self.QCBox_SamplingFile.currentLayer() if sampling_layer in Classification.instances: classification = Classification.instances[sampling_layer] if not classification.is_completed: quit_msg = "The classification for this sampling file is not completed, " \ "the result will have all sampling partially classified." \ "\nDo you want to continue?" reply = QMessageBox.question(None, 'The classification is not completed', quit_msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.No: return else: iface.messageBar().pushMessage("AcATaMa", "Error, the classification for the sampling selected has not been initiated", level=Qgis.Warning) return # get file path to suggest to save but not in tmp directory file_path = get_file_path_of_layer(self.QCBox_SamplingFile.currentLayer()) path, filename = os.path.split(file_path) if self.tmp_dir in path: path = os.path.split(get_file_path_of_layer(self.QCBox_ThematicRaster.currentLayer()))[0] suggested_filename = os.path.splitext(os.path.join(path, filename))[0] + "_classified.gpkg" if filename else "" file_out, _ = QFileDialog.getSaveFileName(self, self.tr("Save sampling file with the classification"), suggested_filename, self.tr("GeoPackage files (*.gpkg);;Shape files (*.shp);;All files (*.*)")) if file_out != '': classification.save_sampling_classification(file_out) iface.messageBar().pushMessage("AcATaMa", "File saved successfully", level=Qgis.Success)
def do_clipping_with_shape(target_layer, shape_layer, out_path, dst_nodata=None): target_file = get_file_path_of_layer(target_layer) filename, ext = os.path.splitext(out_path) tmp_file = filename + "_tmp" + ext # set the nodata dst_nodata = "-dstnodata {}".format(dst_nodata) if dst_nodata not in [None, -1] else "" # set the file path for the area of interest # check if the shape is a memory layer, then save and used it if not os.path.isfile(get_file_path_of_layer(shape_layer)): tmp_memory_fd, tmp_memory_file = tempfile.mkstemp(prefix='memory_layer_', suffix='.gpkg') QgsVectorFileWriter.writeAsVectorFormat(shape_layer, tmp_memory_file, "System", shape_layer.crs(), "GPKG") os.close(tmp_memory_fd) shape_file = tmp_memory_file else: shape_file = get_file_path_of_layer(shape_layer) # clipping in shape return_code = call('gdalwarp -multi -wo NUM_THREADS=ALL_CPUS --config GDALWARP_IGNORE_BAD_CUTLINE YES' ' -cutline "{}" {} "{}" "{}"'.format(shape_file, dst_nodata, target_file, tmp_file), shell=True) # create convert coordinates crsSrc = QgsCoordinateReferenceSystem(shape_layer.crs()) crsDest = QgsCoordinateReferenceSystem(target_layer.crs()) xform = QgsCoordinateTransform(crsSrc, crsDest, QgsProject.instance()) # trim the boundaries using the maximum extent for all features box = [] for f in shape_layer.getFeatures(): g = f.geometry() g.transform(xform) f.setGeometry(g) if box: box.combineExtentWith(f.geometry().boundingBox()) else: box = f.geometry().boundingBox() # intersect with the rater file extent box = box.intersect(target_layer.extent()) # trim gdal.Translate(out_path, tmp_file, projWin=[box.xMinimum(), box.yMaximum(), box.xMaximum(), box.yMinimum()]) # clean tmp file if not os.path.isfile(get_file_path_of_layer(shape_layer)) and os.path.isfile(tmp_memory_file): os.remove(tmp_memory_file) if os.path.isfile(tmp_file): os.remove(tmp_file) if return_code == 0: # successfully return out_path else: iface.messageBar().pushMessage("AcATaMa", "Error while clipping the raster file with shape.", level=Qgis.Warning)
def get_pixel_count_by_pixel_values(layer, band, pixel_values=None): if pixel_values is None: pixel_values = get_pixel_values(layer, band) raster_numpy = gdalnumeric.LoadFile(get_file_path_of_layer(layer)) pixel_counts = [] for pixel_value in pixel_values: pixel_counts.append((raster_numpy == int(pixel_value)).sum()) return dict(zip(pixel_values, pixel_counts))
def file_dialog_save_acatama_state(self): if self.suggested_yml_file: suggested_filename = self.suggested_yml_file elif valid_file_selected_in(self.QCBox_ThematicRaster) or valid_file_selected_in(self.QCBox_SamplingFile): # get file path to suggest where to save if valid_file_selected_in(self.QCBox_ThematicRaster): file_path = get_file_path_of_layer(self.QCBox_ThematicRaster.currentLayer()) else: file_path = get_file_path_of_layer(self.QCBox_SamplingFile.currentLayer()) path, filename = os.path.split(file_path) suggested_filename = os.path.splitext(os.path.join(path, filename))[0] + "_acatama.yml" if filename else "_acatama.yml" else: suggested_filename = "_acatama.yml" file_out, _ = QFileDialog.getSaveFileName(self, self.tr("Save AcATaMa configuration and state"), suggested_filename, self.tr("Yaml (*.yaml *.yml);;All files (*.*)")) if file_out != '': config.save(file_out) self.suggested_yml_file = file_out iface.messageBar().pushMessage("AcATaMa", "Configuration file saved successfully", level=Qgis.Success)
def save_config(self, file_out): import yaml from AcATaMa.gui.acatama_dockwidget import AcATaMaDockWidget as AcATaMa def setup_yaml(): """ Keep dump ordered with orderedDict """ represent_dict_order = lambda self, data: self.represent_mapping('tag:yaml.org,2002:map', list(data.items())) yaml.add_representer(OrderedDict, represent_dict_order) setup_yaml() data = OrderedDict() data["thematic_raster"] = \ {"path": get_current_file_path_in(AcATaMa.dockwidget.QCBox_ThematicRaster, show_message=False), "band": int(AcATaMa.dockwidget.QCBox_band_ThematicRaster.currentText()) if AcATaMa.dockwidget.QCBox_band_ThematicRaster.currentText() else None, "nodata": AcATaMa.dockwidget.nodata_ThematicRaster.value()} data["sampling_layer"] = get_file_path_of_layer(self.sampling_layer) data["dialog_size"] = self.dialog_size data["grid_view_widgets"] = {"columns": self.grid_columns, "rows": self.grid_rows} data["current_sample_idx"] = self.current_sample_idx data["fit_to_sample"] = self.fit_to_sample data["is_completed"] = self.is_completed data["view_widgets_config"] = self.view_widgets_config data["classification_buttons"] = self.buttons_config # save samples status points_config = {} for pnt_idx, point in enumerate(self.points): if point.is_classified: points_config[pnt_idx] = {"classif_id": point.classif_id, "shape_id": point.shape_id} data["points"] = points_config # save the samples order data["points_order"] = [p.shape_id for p in self.points] # save sampling selected in accuracy assessment data["accuracy_assessment_sampling_file"] = get_current_file_path_in(AcATaMa.dockwidget.QCBox_SamplingFile_AA, show_message=False) # save config of the accuracy assessment dialog if exists if self.accuracy_assessment: data["accuracy_assessment_dialog"] = { "area_unit": QgsUnitTypes.toString(self.accuracy_assessment.area_unit), "z_score": self.accuracy_assessment.z_score, "csv_separator": self.accuracy_assessment.csv_separator, "csv_decimal": self.accuracy_assessment.csv_decimal, } with open(file_out, 'w') as yaml_file: yaml.dump(data, yaml_file)
def export_to_csv(accu_asse, file_out, csv_separator, csv_decimal_separator): csv_rows = [] csv_rows.append(["Classification accuracy assessment results"]) csv_rows.append([]) csv_rows.append(["Thematic raster:"]) csv_rows.append([os.path.basename(accu_asse.ThematicR.file_path)]) csv_rows.append([]) csv_rows.append(["Sampling file:"]) csv_rows.append([ os.path.basename( get_file_path_of_layer(accu_asse.classification.sampling_layer)) ]) csv_rows.append([]) csv_rows.append(["Classification status:"]) csv_rows.append([ "{}/{} samples classified".format( accu_asse.classification.total_classified, accu_asse.classification.num_points) ]) ########################################################################### csv_rows.append([]) csv_rows.append(["1) Error matrix:"]) csv_rows.append(["", "", "Classified values"]) labels = [ "{} ({})".format( i, accu_asse.labels[str(i)] if str(i) in accu_asse.labels else "-") for i in accu_asse.values ] csv_rows.append(["", ""] + labels + [ "Total", "User accuracy", "Total class area ({area_unit})".format( area_unit=accu_asse.pixel_area_unit), "Wi" ]) for idx_row, value in enumerate(accu_asse.values): r = [] if idx_row == 0: r.append("Thematic raster classes") else: r.append("") r.append(value) r += accu_asse.error_matrix[idx_row] r.append(sum(accu_asse.error_matrix[idx_row])) r.append( rf(accu_asse.error_matrix[idx_row][idx_row] / sum(accu_asse.error_matrix[idx_row]) ) if sum(accu_asse.error_matrix[idx_row]) > 0 else "-") r.append( rf(accu_asse.thematic_pixels_count[value] * accu_asse.pixel_area_value)) r.append( rf(accu_asse.thematic_pixels_count[value] / sum( [accu_asse.thematic_pixels_count[v] for v in accu_asse.values]))) csv_rows.append(r) csv_rows.append(["", "total"] + [sum(t) for t in zip(*accu_asse.error_matrix)] + [total_samples] + [""] + [sum_total_class_area]) csv_rows.append(["", "Producer accuracy"] + [ rf(col[idx_col] / sum(col)) if sum(col) > 0 else "-" for idx_col, col in enumerate(zip(*accu_asse.error_matrix)) ] + [""] + [ rf( sum([ col[idx_col] for idx_col, col in enumerate(zip(*accu_asse.error_matrix)) ]) / total_samples) if total_samples != 0 else "-" ]) ########################################################################### csv_rows.append([]) csv_rows.append(["2) Error matrix of estimated area proportion:"]) csv_rows.append(["", "", "Classified values"]) labels = [ "{} ({})".format( i, accu_asse.labels[str(i)] if str(i) in accu_asse.labels else "-") for i in accu_asse.values ] csv_rows.append(["", ""] + labels + ["Wi"]) error_matrix_area_prop = copy.deepcopy(accu_asse.error_matrix) for idx_row, row in enumerate(accu_asse.error_matrix): wi = accu_asse.thematic_pixels_count[accu_asse.values[idx_row]] / \ total_pixels_classes for idx_col, value in enumerate(row): error_matrix_area_prop[idx_row][idx_col] = ( value / sum(row)) * wi if sum(row) > 0 else 0 for idx_row, value in enumerate(accu_asse.values): r = [] if idx_row == 0: r.append("Thematic raster classes") else: r.append("") r.append(value) r += [rf(t) if t > 0 else "-" for t in error_matrix_area_prop[idx_row]] r.append( rf(sum(error_matrix_area_prop[idx_row]) ) if sum(error_matrix_area_prop[idx_row]) > 0 else "-") csv_rows.append(r) csv_rows.append(["", "total"] + [rf(sum(t)) for t in zip(*error_matrix_area_prop)]) ########################################################################### csv_rows.append([]) csv_rows.append( ["3) Quadratic error matrix of estimated area proportion:"]) csv_rows.append(["", "", "Classified values"]) labels = [ "{} ({})".format( i, accu_asse.labels[str(i)] if str(i) in accu_asse.labels else "-") for i in accu_asse.values ] csv_rows.append(["", ""] + labels) quadratic_error_matrix = copy.deepcopy(accu_asse.error_matrix) for idx_row, row in enumerate(accu_asse.error_matrix): wi = accu_asse.thematic_pixels_count[accu_asse.values[idx_row]] / \ total_pixels_classes for idx_col, value in enumerate(row): quadratic_error_matrix[idx_row][idx_col] = \ (wi ** 2 * ((value / sum(row)) * (1 - (value / sum(row))) / (sum(row) - 1))) if sum(row) > 1 else 0 for idx_row, value in enumerate(accu_asse.values): r = [] if idx_row == 0: r.append("Thematic raster classes") else: r.append("") r.append(value) r += [rf(t) if t > 0 else "-" for t in quadratic_error_matrix[idx_row]] csv_rows.append(r) csv_rows.append(["", "total"] + [rf(sum(t)**0.5) for t in zip(*quadratic_error_matrix)]) ########################################################################### csv_rows.append([]) csv_rows.append(["4) Accuracy matrices:"]) csv_rows.append([]) csv_rows.append(["User's accuracy matrix of estimated area proportion:"]) csv_rows.append(["", "", "Classified values"]) labels = [ "{} ({})".format( i, accu_asse.labels[str(i)] if str(i) in accu_asse.labels else "-") for i in accu_asse.values ] csv_rows.append(["", ""] + labels) user_accuracy_matrix = copy.deepcopy(error_matrix_area_prop) for idx_row, row in enumerate(error_matrix_area_prop): for idx_col, value in enumerate(row): user_accuracy_matrix[idx_row][idx_col] = value / sum(row) if sum( row) > 0 else 0 for idx_row, value in enumerate(accu_asse.values): r = [] if idx_row == 0: r.append("Thematic raster classes") else: r.append("") r.append(value) r += [rf(t) if t > 0 else "-" for t in user_accuracy_matrix[idx_row]] csv_rows.append(r) csv_rows.append([]) csv_rows.append( ["Producer's accuracy matrix of estimated area proportion:"]) csv_rows.append(["", "", "Classified values"]) labels = [ "{} ({})".format( i, accu_asse.labels[str(i)] if str(i) in accu_asse.labels else "-") for i in accu_asse.values ] csv_rows.append(["", ""] + labels) producer_accuracy_matrix = copy.deepcopy(error_matrix_area_prop) for idx_col, col in enumerate(zip(*error_matrix_area_prop)): for idx_row, value in enumerate(col): producer_accuracy_matrix[idx_row][idx_col] = value / sum( col) if sum(col) > 0 else 0 for idx_row, value in enumerate(accu_asse.values): r = [] if idx_row == 0: r.append("Thematic raster classes") else: r.append("") r.append(value) r += [ rf(t) if t > 0 else "-" for t in producer_accuracy_matrix[idx_row] ] csv_rows.append(r) csv_rows.append([]) csv_rows.append(["Overall Accuracy:"]) overall_accuracy = sum( [row[idx_row] for idx_row, row in enumerate(error_matrix_area_prop)]) csv_rows.append([rf(overall_accuracy)]) ########################################################################### csv_rows.append([]) csv_rows.append(["5) Class area adjusted table:"]) csv_rows.append([ "", "Area ({area_unit})".format(area_unit=accu_asse.pixel_area_unit), "Error", "Lower limit", "Upper limit" ]) total_area = 0 for idx_row, value in enumerate(accu_asse.values): r = [] r.append("{} ({})".format( value, accu_asse.labels[str(value)] if str(value) in accu_asse.labels else "-")) # area area = sum(list( zip(*error_matrix_area_prop))[idx_row]) * sum_total_class_area r.append(rf(area)) total_area += area # error error = (sum(list(zip(*quadratic_error_matrix))[idx_row])** 0.5) * sum_total_class_area r.append(rf(error)) # lower limit r.append(rf(area - accu_asse.z_score * error)) # upper limit r.append(rf(area + accu_asse.z_score * error)) csv_rows.append(r) csv_rows.append(["total"] + [rf(total_area)]) # write CSV file with open(file_out, 'w') as csvfile: csv_w = csv.writer(csvfile, delimiter=str(csv_separator)) # replace with the user define decimal separator if csv_decimal_separator != ".": for idx, row in enumerate(csv_rows): csv_rows[idx] = [ str(item).replace('.', csv_decimal_separator) if isinstance(item, float) else item for item in row ] csv_w.writerows(csv_rows)
def get_html(accu_asse): ########################################################################### # #### preprocess common variables total_samples = sum([sum(r) for r in accu_asse.error_matrix]) total_pixels_classes = sum( [accu_asse.thematic_pixels_count[v] for v in accu_asse.values]) sum_total_class_area = total_pixels_classes * accu_asse.pixel_area_value if accu_asse.sampling_type in [ 'Simple random sampling post-stratified', 'Stratified random sampling' ]: error_matrix_area_prop = copy.deepcopy(accu_asse.error_matrix) for idx_row, row in enumerate(accu_asse.error_matrix): wi = accu_asse.thematic_pixels_count[accu_asse.values[idx_row]] / \ total_pixels_classes for idx_col, value in enumerate(row): error_matrix_area_prop[idx_row][idx_col] = ( value / sum(row)) * wi if sum(row) > 0 else 0 quadratic_error_matrix = copy.deepcopy(accu_asse.error_matrix) for idx_row, row in enumerate(accu_asse.error_matrix): wi = accu_asse.thematic_pixels_count[accu_asse.values[idx_row]] / \ total_pixels_classes for idx_col, value in enumerate(row): quadratic_error_matrix[idx_row][idx_col] = \ (wi ** 2 * ((value / sum(row)) * (1 - (value / sum(row))) / (sum(row) - 1))) if sum(row) > 1 else 0 accuracy_table = error_matrix_area_prop if accu_asse.sampling_type == 'Stratified random sampling' else accu_asse.error_matrix ########################################################################### # #### html init html = ''' <head> <style type="text/css"> table { table-layout: fixed; white-space: normal!important; background: #ffffff; margin: 4px; } th { word-wrap: break-word; padding: 4px 6px; background: #efefef; valign: middle; } td { word-wrap: break-word; padding: 4px 6px; background: #efefef; } .highlight { background: #dddddd; } .th-rows { max-width: 120px; } .empty { background: #f9f9f9; } </style> </head> <body> ''' html += "<h2>Classification accuracy assessment results</h2>" html += "<p><strong>Thematic raster:</strong> {}</p>".format( os.path.basename(accu_asse.ThematicR.file_path)) html += "<p><strong>Sampling file:</strong> {}</p>".format( os.path.basename( get_file_path_of_layer(accu_asse.classification.sampling_layer))) html += "<p><strong>Sampling type:</strong> {}</p>".format( accu_asse.sampling_type) html += "<p><strong>Classification status:</strong> {}/{} samples classified</p>".format( accu_asse.classification.total_classified, accu_asse.classification.num_points) # warning block if the thematic has a geographic units if accu_asse.base_area_unit == QgsUnitTypes.AreaSquareDegrees: html += "<p style='color:black;background-color:#ffc53a;white-space:pre;padding:4px'><strong>Warning!</strong><br/>" \ "The thematic raster has a geographic coordinate system, therefore all area values are not accurate.<br/>" \ "For fix that use the UTM coordinate system.</p>" # warning block for samples outside the thematic raster area or inside the no data values if accu_asse.samples_outside_the_thematic: html += "<p style='color:black;background-color:#ffc53a;white-space:pre;padding:4px'><strong>Warning!</strong><br/>" \ "There are {} samples classified that are outside the thematic raster area or inside the no data values:<br/>".format( len(accu_asse.samples_outside_the_thematic)) for idx, sample in enumerate(accu_asse.samples_outside_the_thematic): html += " {}) Sample ID: {}, Coordinate: {},{}<br/>".format( idx + 1, sample.shape_id, int(sample.QgsPnt.x()), int(sample.QgsPnt.y())) html += "These samples will be ignored for accuracy assessment results.</p>" ########################################################################### # #### 1) Error matrix html += "<p style='font-size:2px'><br/></p>" html += "<h3>1) Error matrix:</h3>" html += ''' <table> <tbody> <tr> <td class="empty"></td> <td class="empty"></td> <th colspan="{table_size}">Classified values</th> <td class="empty"></td> <td class="empty"></td> <td class="empty"></td> <td class="empty"></td> </tr> <tr> <td class="empty"></td> <td class="empty"></td> '''.format(table_size=len(accu_asse.values)) labels = [ "{} ({})".format( i, accu_asse.labels[str(i)] if str(i) in accu_asse.labels else "-") for i in accu_asse.values ] html += "".join(["<th >" + str(i) + "</th>" for i in labels]) html += ''' <th>Total</th> <th>User accuracy</th> <th>Total class area ({area_unit})</th> <th>Wi</th> </tr> '''.format(area_unit=accu_asse.pixel_area_unit) for idx_row, value in enumerate(accu_asse.values): html += "<tr>" if idx_row == 0: html += ''' <th class="th-rows" rowspan="{table_size}">Thematic raster<br />classes</th> '''.format(table_size=len(accu_asse.values)) html += "<th>{value}</th>".format(value=value) html += "".join([ ''' <td class="highlight">{table_field}</td> '''.format(table_field=t) for t in accu_asse.error_matrix[idx_row] ]) html += ''' <td>{total_row}</td> <td>{u_accuracy}</td> <td>{total_class_area}</td> <td>{wi}</td> </tr> '''.format( total_row=sum(accu_asse.error_matrix[idx_row]), u_accuracy=rf(accuracy_table[idx_row][idx_row] / sum(accuracy_table[idx_row])) if sum(accuracy_table[idx_row]) > 0 else "-", total_class_area=rf(accu_asse.thematic_pixels_count[value] * accu_asse.pixel_area_value), wi=rf(accu_asse.thematic_pixels_count[value] / total_pixels_classes)) html += ''' <tr> <td class="empty"></td> <th>Total</th> ''' html += "".join([ ''' <td>{total_col}</td> '''.format(total_col=sum(t)) for t in zip(*accu_asse.error_matrix) ]) html += ''' <td>{total_total}</td> '''.format(total_total=total_samples) html += ''' <td class="empty"></td> ''' html += ''' <td>{total_classes_area}</td> '''.format(total_classes_area=rf(sum_total_class_area)) html += ''' <td class="empty"></td> </tr> <tr> <td class="empty"></td> <th>Producer accuracy</th> ''' for idx_col, col in enumerate(zip(*accuracy_table)): html += ''' <td>{p_accuracy}</td> '''.format(p_accuracy=rf(col[idx_col] / sum(col)) if sum(col) > 0 else "-") html += ''' <td class="empty"></td> <td class="empty"></td> ''' html += ''' <td class="empty"></td> <td class="empty"></td> </tr> </tbody> </table> ''' ########################################################################### # #### 2) Accuracy if accu_asse.sampling_type in [ 'Simple random sampling', 'Simple random sampling post-stratified' ]: # overall overall_accuracy = sum([ row[idx_row] for idx_row, row in enumerate(accu_asse.error_matrix) ]) / total_samples standard_deviation = (overall_accuracy * (1 - overall_accuracy) / (total_samples - 1))**0.5 if accu_asse.sampling_type == 'Stratified random sampling': # overall overall_accuracy = sum([ row[idx_row] for idx_row, row in enumerate(error_matrix_area_prop) ]) overall_variance = sum([ ((accu_asse.thematic_pixels_count[value] / total_pixels_classes)** 2) * (accu_asse.error_matrix[idx_row][idx_row] / sum(accu_asse.error_matrix[idx_row])) * (1 - (accu_asse.error_matrix[idx_row][idx_row] / sum(accu_asse.error_matrix[idx_row]))) / (sum(accu_asse.error_matrix[idx_row]) - 1) for idx_row, value in enumerate(accu_asse.values) ]) standard_deviation = overall_variance**0.5 html += "<p style='font-size:2px'><br/></p>" html += "<h3>2) Accuracy:</h3>" html += "<h4>Overall:</h4>" # Overall Accuracy html += ''' <table> <tbody> <tr> <td><strong>Overall Accuracy</strong></td> <td><strong>Standard deviation</strong></td> </tr> <tr> <td class="highlight">{overall_accuracy}</td> <td>{standard_deviation}</td> </tr>'''.format(overall_accuracy=rf(overall_accuracy), standard_deviation=rf(standard_deviation)) html += ''' </tbody> </table> ''' # user's accuracy html += "<h4>User:</h4>" html += ''' <table> <tbody> <tr> <td class="empty"></td> '''.format(table_size=len(accu_asse.values)) headers = ["User's accuracy", "Standard deviation"] html += "".join(["<th>" + str(h) + "</th>" for h in headers]) html += "</tr>" for idx_row, value in enumerate(accu_asse.values): html += "<tr>" html += "<th >{} ({})</th>".format( value, accu_asse.labels[str(value)] if str(value) in accu_asse.labels else "-") # accuracy accuracy = accuracy_table[idx_row][idx_row] / sum( accuracy_table[idx_row]) html += '''<td class="highlight">{}</th>'''.format(rf(accuracy)) # standard error html += '''<td>{}</th>'''.format( rf((accuracy * (1 - accuracy) / (sum(accu_asse.error_matrix[idx_row]) - 1))**0.5)) html += "</tr>" html += ''' </tbody> </table> ''' # producer's accuracy html += "<h4>Producer:</h4>" html += ''' <table> <tbody> <tr> <td class="empty"></td> '''.format(table_size=len(accu_asse.values)) headers = ["Producer's accuracy", "Standard deviation"] html += "".join(["<th>" + str(h) + "</th>" for h in headers]) html += "</tr>" for idx_row, value in enumerate(accu_asse.values): html += "<tr>" html += "<th >{} ({})</th>".format( value, accu_asse.labels[str(value)] if str(value) in accu_asse.labels else "-") # accuracy accuracy = accuracy_table[idx_row][idx_row] / sum( list(zip(*accuracy_table))[idx_row]) html += '''<td class="highlight">{}</th>'''.format(rf(accuracy)) # standard error if accu_asse.sampling_type in [ 'Simple random sampling', 'Simple random sampling post-stratified' ]: producer_standard_error = ( accuracy * (1 - accuracy) / (sum(list(zip(*accu_asse.error_matrix))[idx_row]) - 1))**0.5 if accu_asse.sampling_type == 'Stratified random sampling': u_accuracy = accuracy_table[idx_row][idx_row] / sum( accuracy_table[idx_row]) producer_standard_error = \ (1/(sum([n*row[idx_row]/total_row for total_row, row, n in zip([sum(r) for r in accu_asse.error_matrix], accu_asse.error_matrix, [accu_asse.thematic_pixels_count[v] for v in accu_asse.values])])**2) * sum([n**2*(1-accuracy)**2*u_accuracy*(1-u_accuracy)/(total_row-1) if idx == idx_row else accuracy**2*n**2*row[idx_row]/total_row*(1-row[idx_row]/sum(list(zip(*accu_asse.error_matrix))[idx_row]))/(total_row-1) for idx, total_row, row, n in zip(range(len(accu_asse.error_matrix)), [sum(r) for r in accu_asse.error_matrix], accu_asse.error_matrix, [accu_asse.thematic_pixels_count[v] for v in accu_asse.values])]))**0.5 html += '''<td>{}</th>'''.format(rf(producer_standard_error)) html += "</tr>" html += ''' </tbody> </table> ''' # #### 2b) Accuracy matrix of estimated area proportion if accu_asse.sampling_type == 'Stratified random sampling': html += "<p style='font-size:2px'><br/></p>" html += "<h3>2b) Accuracy matrix of estimated area proportion:</h3>" ################################### html += "<h4>User:</h4>" html += ''' <table> <tbody> <tr> <td class="empty"></td> <td class="empty"></td> <th colspan="{table_size}">Classified values</th> </tr> <tr> <td class="empty"></td> <td class="empty"></td> '''.format(table_size=len(accu_asse.values)) labels = [ "{} ({})".format( i, accu_asse.labels[str(i)] if str(i) in accu_asse.labels else "-") for i in accu_asse.values ] html += "".join(["<th >" + str(i) + "</th>" for i in labels]) html += "</tr>" user_accuracy_matrix = copy.deepcopy(error_matrix_area_prop) for idx_row, row in enumerate(error_matrix_area_prop): for idx_col, value in enumerate(row): user_accuracy_matrix[idx_row][idx_col] = value / sum( row) if sum(row) > 0 else 0 for idx_row, value in enumerate(accu_asse.values): html += "<tr>" if idx_row == 0: html += ''' <th class="th-rows" rowspan="{table_size}">Thematic raster<br />classes</th> '''.format(table_size=len(accu_asse.values)) html += "<th>{value}</th>".format(value=value) html += "".join([ ''' <td class="highlight">{table_field}</td> '''.format(table_field=(rf(t)) if t > 0 else "-") for t in user_accuracy_matrix[idx_row] ]) html += "</tr>" html += ''' </tbody> </table> ''' ################################### html += "<h4>Producer:</h4>" html += ''' <table> <tbody> <tr> <td class="empty"></td> <td class="empty"></td> <th colspan="{table_size}">Classified values</th> </tr> <tr> <td class="empty"></td> <td class="empty"></td> '''.format(table_size=len(accu_asse.values)) labels = [ "{} ({})".format( i, accu_asse.labels[str(i)] if str(i) in accu_asse.labels else "-") for i in accu_asse.values ] html += "".join(["<th >" + str(i) + "</th>" for i in labels]) html += "</tr>" producer_accuracy_matrix = copy.deepcopy(error_matrix_area_prop) for idx_col, col in enumerate(zip(*error_matrix_area_prop)): for idx_row, value in enumerate(col): producer_accuracy_matrix[idx_row][idx_col] = value / sum( col) if sum(col) > 0 else 0 for idx_row, value in enumerate(accu_asse.values): html += "<tr>" if idx_row == 0: html += ''' <th class="th-rows" rowspan="{table_size}">Thematic raster<br />classes</th> '''.format(table_size=len(accu_asse.values)) html += "<th>{value}</th>".format(value=value) html += "".join([ ''' <td class="highlight">{table_field}</td> '''.format(table_field=(rf(t)) if t > 0 else "-") for t in producer_accuracy_matrix[idx_row] ]) html += "</tr>" html += ''' </tbody> </table> ''' ########################################################################### # #### 3) Error matrix of estimated area proportion if accu_asse.sampling_type in [ 'Simple random sampling post-stratified', 'Stratified random sampling' ]: html += "<p style='font-size:2px'><br/></p>" html += "<h3>3) Error matrix of estimated area proportion:</h3>" html += ''' <table> <tbody> <tr> <td class="empty"></td> <td class="empty"></td> <th colspan="{table_size}">Classified values</th> <td class="empty"></td> </tr> <tr> <td class="empty"></td> <td class="empty"></td> '''.format(table_size=len(accu_asse.values)) labels = [ "{} ({})".format( i, accu_asse.labels[str(i)] if str(i) in accu_asse.labels else "-") for i in accu_asse.values ] html += "".join(["<th >" + str(i) + "</th>" for i in labels]) html += ''' <th>Wi</th> </tr> ''' for idx_row, value in enumerate(accu_asse.values): html += "<tr>" if idx_row == 0: html += ''' <th class="th-rows" rowspan="{table_size}">Thematic raster<br />classes</th> '''.format(table_size=len(accu_asse.values)) html += "<th>{value}</th>".format(value=value) html += "".join([ ''' <td class="highlight">{table_field}</td> '''.format(table_field=(rf(t)) if t > 0 else "-") for t in error_matrix_area_prop[idx_row] ]) html += ''' <td>{wi}</td> </tr> '''.format(wi=rf(sum(error_matrix_area_prop[idx_row])) if sum(error_matrix_area_prop[idx_row]) > 0 else "-") html += ''' <tr> <td class="empty"></td> <th>total</th> ''' html += "".join([ ''' <td>{total_col}</td> '''.format(total_col=rf(sum(t))) for t in zip(*error_matrix_area_prop) ]) html += ''' <td></td> </tr> </tbody> </table> ''' ########################################################################### # #### 4) Quadratic error matrix of estimated area proportion if accu_asse.sampling_type in [ 'Simple random sampling post-stratified', 'Stratified random sampling' ]: html += "<p style='font-size:2px'><br/></p>" html += "<h3>4) Quadratic error matrix of estimated area proportion:</h3>" html += ''' <table> <tbody> <tr> <td class="empty"></td> <td class="empty"></td> <th colspan="{table_size}">Classified values</th> </tr> <tr> <td class="empty"></td> <td class="empty"></td> '''.format(table_size=len(accu_asse.values)) labels = [ "{} ({})".format( i, accu_asse.labels[str(i)] if str(i) in accu_asse.labels else "-") for i in accu_asse.values ] html += "".join(["<th >" + str(i) + "</th>" for i in labels]) html += "</tr>" for idx_row, value in enumerate(accu_asse.values): html += "<tr>" if idx_row == 0: html += ''' <th class="th-rows" rowspan="{table_size}">Thematic raster<br />classes</th> '''.format(table_size=len(accu_asse.values)) html += "<th>{value}</th>".format(value=value) html += "".join([ ''' <td class="highlight">{table_field}</td> '''.format(table_field=(rf(t)) if t > 0 else "-") for t in quadratic_error_matrix[idx_row] ]) html += "</tr>" html += ''' <tr> <td class="empty"></td> <th>total</th> ''' html += "".join([ ''' <td>{total_col}</td> '''.format(total_col=rf(sum(t)**0.5)) for t in zip(*quadratic_error_matrix) ]) html += ''' </tr> </tbody> </table> ''' ########################################################################### # #### 3/5) Class area adjusted html += "<p style='font-size:2px'><br/></p>" html += "<h3>{}) Class area adjusted:</h3>".format( "3" if accu_asse.sampling_type == 'Simple random sampling' else "5") html += ''' <table> <tbody> <tr> <td class="empty"></td> '''.format(table_size=len(accu_asse.values)) headers = [ "Area adjusted ({area_unit})".format( area_unit=accu_asse.pixel_area_unit), "Error", "Lower limit", "Upper limit" ] html += "".join(["<th >" + str(h) + "</th>" for h in headers]) html += "</tr>" total_area = 0 # the error for post-stratified if accu_asse.sampling_type == 'Simple random sampling post-stratified': std_dev_table = [[ (1 - i / total_row)**2 * i / (total_row - 1) for i in row ] for total_row, row in zip([sum(r) for r in accu_asse.error_matrix], accu_asse.error_matrix)] wi = [ accu_asse.thematic_pixels_count[value] / total_pixels_classes for value in accu_asse.values ] variance = [ ((1 - total_samples / total_pixels_classes) / total_samples) * sum([w * s for w, s in zip(wi, std_dev_col)]) + (1 / (total_samples**2)) * sum([(1 - w) * s for w, s in zip(wi, std_dev_col)]) for std_dev_col in zip(*std_dev_table) ] _error = [sum_total_class_area * v**0.5 for v in variance] for idx_row, value in enumerate(accu_asse.values): if accu_asse.sampling_type == 'Simple random sampling': p_k = sum(list( zip(*accu_asse.error_matrix))[idx_row]) / total_samples area = p_k * sum_total_class_area v_p_k = ((p_k) * (1 - p_k) / total_samples) * ( total_pixels_classes - total_samples) / (total_pixels_classes - 1) error = (v_p_k**0.5) * sum_total_class_area if accu_asse.sampling_type == 'Simple random sampling post-stratified': area = sum(list( zip(*error_matrix_area_prop))[idx_row]) * sum_total_class_area error = _error[idx_row] if accu_asse.sampling_type == 'Stratified random sampling': area = sum(list( zip(*error_matrix_area_prop))[idx_row]) * sum_total_class_area error = (sum(list(zip(*quadratic_error_matrix))[idx_row])** 0.5) * sum_total_class_area html += "<tr>" html += "<th >{} ({})</th>".format( value, accu_asse.labels[str(value)] if str(value) in accu_asse.labels else "-") # area html += '''<td>{area}</th>'''.format(area=rf(area)) total_area += area # error html += '''<td>{error}</th>'''.format(error=rf(error)) # lower limit lower_limit = area - accu_asse.z_score * error lower_limit = 0 if lower_limit < 0 else lower_limit html += '''<td>{lower_limit}</th>'''.format( lower_limit=rf(lower_limit)) # upper limit html += '''<td>{upper_limit}</th>'''.format( upper_limit=rf(area + accu_asse.z_score * error)) html += "</tr>" html += ''' <tr> <th>total</th> ''' html += '''<td>{total_area}</th>'''.format(total_area=rf(total_area)) html += ''' <td class="empty"></td> <td class="empty"></td> <td class="empty"></td> </tr>''' html += ''' </tbody> </table> ''' html += ''' </body> ''' return html
def get_html(accu_asse): html = ''' <head> <style type="text/css"> table { table-layout: fixed; white-space: normal!important; background: #ffffff; } th { word-wrap: break-word; padding: 2px; background: #efefef; valign: middle; } td { word-wrap: break-word; padding: 2px; background: #efefef; } .field-values { background: #dddddd; } .th-rows { max-width: 120px; } .empty { background: #f9f9f9; } </style> </head> <body> ''' html += "<h2>Classification accuracy assessment results</h2>" html += "<p><strong>Thematic raster:</strong> {}</p>".format( os.path.basename(accu_asse.ThematicR.file_path)) html += "<p><strong>Sampling file:</strong> {}</p>".format( os.path.basename( get_file_path_of_layer(accu_asse.classification.sampling_layer))) html += "<p><strong>Classification status:</strong> {}/{} samples classified</p>".format( accu_asse.classification.total_classified, accu_asse.classification.num_points) # warning block if the thematic has a geographic units if accu_asse.base_area_unit == QgsUnitTypes.AreaSquareDegrees: html += "<p style='color:black;background-color:#ffc53a;white-space:pre;padding:4px'><strong>Warning!</strong><br/>" \ "The thematic raster has a geographic coordinate system, therefore all area values are not accurate.<br/>" \ "For fix that use the UTM coordinate system.</p>" # warning block for samples outside the thematic raster area or inside the no data values if accu_asse.samples_outside_the_thematic: html += "<p style='color:black;background-color:#ffc53a;white-space:pre;padding:4px'><strong>Warning!</strong><br/>" \ "There are {} samples classified that are outside the thematic raster area or inside the no data values:<br/>".format( len(accu_asse.samples_outside_the_thematic)) for idx, sample in enumerate(accu_asse.samples_outside_the_thematic): html += " {}) Sample ID: {}, Coordinate: {},{}<br/>".format( idx + 1, sample.shape_id, int(sample.QgsPnt.x()), int(sample.QgsPnt.y())) html += "These samples will be ignored for accuracy assessment results.</p>" ########################################################################### html += "<p style='font-size:2px'><br/></p>" html += "<h3>1) Error matrix (confusion matrix):</h3>" html += ''' <table> <tbody> <tr> <td class="empty"></td> <td class="empty"></td> <th colspan="{table_size}">Classified values</th> <td class="empty"></td> <td class="empty"></td> <td class="empty"></td> <td class="empty"></td> </tr> <tr> <td class="empty"></td> <td class="empty"></td> '''.format(table_size=len(accu_asse.values)) labels = [ "{} ({})".format( i, accu_asse.labels[str(i)] if str(i) in accu_asse.labels else "-") for i in accu_asse.values ] html += "".join(["<th >" + str(i) + "</th>" for i in labels]) html += ''' <th>Total</th> <th>User accuracy</th> <th>Total class area ({area_unit})</th> <th>Wi</th> </tr> '''.format(area_unit=accu_asse.pixel_area_unit) for idx_row, value in enumerate(accu_asse.values): html += "<tr>" if idx_row == 0: html += ''' <th class="th-rows" rowspan="{table_size}">Thematic raster<br />classes</th> '''.format(table_size=len(accu_asse.values)) html += "<th>{value}</th>".format(value=value) html += "".join([ ''' <td class="field-values">{table_field}</td> '''.format(table_field=t) for t in accu_asse.error_matrix[idx_row] ]) html += ''' <td>{total_row}</td> <td>{u_accuracy}</td> <td>{total_class_area}</td> <td>{wi}</td> </tr> '''.format( total_row=sum(accu_asse.error_matrix[idx_row]), u_accuracy=rf(accu_asse.error_matrix[idx_row][idx_row] / sum(accu_asse.error_matrix[idx_row])) if sum(accu_asse.error_matrix[idx_row]) > 0 else "-", total_class_area=rf(accu_asse.thematic_pixels_count[value] * accu_asse.pixel_area_value), wi=rf(accu_asse.thematic_pixels_count[value] / sum( [accu_asse.thematic_pixels_count[v] for v in accu_asse.values]))) html += ''' <tr> <td class="empty"></td> <th>total</th> ''' html += "".join([ ''' <td>{total_col}</td> '''.format(total_col=sum(t)) for t in zip(*accu_asse.error_matrix) ]) html += ''' <td>{total_total}</td> '''.format(total_total=sum([sum(r) for r in accu_asse.error_matrix])) html += ''' <td></td> ''' html += ''' <td>{total_classes_area}</td> '''.format(total_classes_area=rf( sum([accu_asse.thematic_pixels_count[v] for v in accu_asse.values]) * accu_asse.pixel_area_value)) html += ''' <td></td> </tr> <tr> <td class="empty"></td> <th>Producer accuracy</th> ''' for idx_col, col in enumerate(zip(*accu_asse.error_matrix)): html += ''' <td>{p_accuracy}</td> '''.format(p_accuracy=rf(col[idx_col] / sum(col)) if sum(col) > 0 else "-") html += ''' <td></td> <td>{u_p_accuracy}</td> '''.format(u_p_accuracy=rf( sum([ col[idx_col] for idx_col, col in enumerate(zip(*accu_asse.error_matrix)) ]) / sum([sum(r) for r in accu_asse.error_matrix]) ) if sum([sum(r) for r in accu_asse.error_matrix]) != 0 else "-") html += ''' <td></td> <td></td> </tr> </tbody> </table> ''' ########################################################################### html += "<p style='font-size:2px'><br/></p>" html += "<h3>2) Error matrix of estimated area proportion:</h3>" html += ''' <table> <tbody> <tr> <td class="empty"></td> <td class="empty"></td> <th colspan="{table_size}">Classified values</th> <td class="empty"></td> </tr> <tr> <td class="empty"></td> <td class="empty"></td> '''.format(table_size=len(accu_asse.values)) labels = [ "{} ({})".format( i, accu_asse.labels[str(i)] if str(i) in accu_asse.labels else "-") for i in accu_asse.values ] html += "".join(["<th >" + str(i) + "</th>" for i in labels]) html += ''' <th>Wi</th> </tr> ''' error_matrix_area_prop = copy.deepcopy(accu_asse.error_matrix) for idx_row, row in enumerate(accu_asse.error_matrix): wi = accu_asse.thematic_pixels_count[accu_asse.values[idx_row]] / \ sum([accu_asse.thematic_pixels_count[v] for v in accu_asse.values]) for idx_col, value in enumerate(row): error_matrix_area_prop[idx_row][idx_col] = ( value / sum(row)) * wi if sum(row) > 0 else 0 for idx_row, value in enumerate(accu_asse.values): html += "<tr>" if idx_row == 0: html += ''' <th class="th-rows" rowspan="{table_size}">Thematic raster<br />classes</th> '''.format(table_size=len(accu_asse.values)) html += "<th>{value}</th>".format(value=value) html += "".join([ ''' <td class="field-values">{table_field}</td> '''.format(table_field=(rf(t)) if t > 0 else "-") for t in error_matrix_area_prop[idx_row] ]) html += ''' <td>{wi}</td> </tr> '''.format(wi=rf(sum(error_matrix_area_prop[idx_row])) if sum(error_matrix_area_prop[idx_row]) > 0 else "-") html += ''' <tr> <td class="empty"></td> <th>total</th> ''' html += "".join([ ''' <td>{total_col}</td> '''.format(total_col=rf(sum(t))) for t in zip(*error_matrix_area_prop) ]) html += ''' <td></td> </tr> </tbody> </table> ''' ########################################################################### html += "<p style='font-size:2px'><br/></p>" html += "<h3>3) Quadratic error matrix of estimated area proportion:</h3>" html += ''' <table> <tbody> <tr> <td class="empty"></td> <td class="empty"></td> <th colspan="{table_size}">Classified values</th> </tr> <tr> <td class="empty"></td> <td class="empty"></td> '''.format(table_size=len(accu_asse.values)) labels = [ "{} ({})".format( i, accu_asse.labels[str(i)] if str(i) in accu_asse.labels else "-") for i in accu_asse.values ] html += "".join(["<th >" + str(i) + "</th>" for i in labels]) html += "</tr>" quadratic_error_matrix = copy.deepcopy(accu_asse.error_matrix) for idx_row, row in enumerate(accu_asse.error_matrix): wi = accu_asse.thematic_pixels_count[accu_asse.values[idx_row]] / \ sum([accu_asse.thematic_pixels_count[v] for v in accu_asse.values]) for idx_col, value in enumerate(row): quadratic_error_matrix[idx_row][idx_col] = \ (wi**2*((value/sum(row))*(1-(value/sum(row)))/(sum(row)-1))) if sum(row) > 1 else 0 for idx_row, value in enumerate(accu_asse.values): html += "<tr>" if idx_row == 0: html += ''' <th class="th-rows" rowspan="{table_size}">Thematic raster<br />classes</th> '''.format(table_size=len(accu_asse.values)) html += "<th>{value}</th>".format(value=value) html += "".join([ ''' <td class="field-values">{table_field}</td> '''.format(table_field=(rf(t)) if t > 0 else "-") for t in quadratic_error_matrix[idx_row] ]) html += "</tr>" html += ''' <tr> <td class="empty"></td> <th>total</th> ''' html += "".join([ ''' <td>{total_col}</td> '''.format(total_col=rf(sum(t)**0.5)) for t in zip(*quadratic_error_matrix) ]) html += ''' </tr> </tbody> </table> ''' ########################################################################### html += "<p style='font-size:2px'><br/></p>" html += "<h3>4) Accuracy matrices:</h3>" ################################### html += "<h4>User's accuracy matrix of estimated area proportion:</h4>" html += ''' <table> <tbody> <tr> <td class="empty"></td> <td class="empty"></td> <th colspan="{table_size}">Classified values</th> </tr> <tr> <td class="empty"></td> <td class="empty"></td> '''.format(table_size=len(accu_asse.values)) labels = [ "{} ({})".format( i, accu_asse.labels[str(i)] if str(i) in accu_asse.labels else "-") for i in accu_asse.values ] html += "".join(["<th >" + str(i) + "</th>" for i in labels]) html += "</tr>" user_accuracy_matrix = copy.deepcopy(accu_asse.error_matrix) for idx_row, row in enumerate(accu_asse.error_matrix): for idx_col, value in enumerate(row): user_accuracy_matrix[idx_row][idx_col] = \ value/sum(row) if sum(row) > 0 else 0 for idx_row, value in enumerate(accu_asse.values): html += "<tr>" if idx_row == 0: html += ''' <th class="th-rows" rowspan="{table_size}">Thematic raster<br />classes</th> '''.format(table_size=len(accu_asse.values)) html += "<th>{value}</th>".format(value=value) html += "".join([ ''' <td class="field-values">{table_field}</td> '''.format(table_field=(rf(t)) if t > 0 else "-") for t in user_accuracy_matrix[idx_row] ]) html += "</tr>" html += ''' </tbody> </table> ''' ################################### html += "<h4>Producer's accuracy matrix of estimated area proportion:</h4>" html += ''' <table> <tbody> <tr> <td class="empty"></td> <td class="empty"></td> <th colspan="{table_size}">Classified values</th> </tr> <tr> <td class="empty"></td> <td class="empty"></td> '''.format(table_size=len(accu_asse.values)) labels = [ "{} ({})".format( i, accu_asse.labels[str(i)] if str(i) in accu_asse.labels else "-") for i in accu_asse.values ] html += "".join(["<th >" + str(i) + "</th>" for i in labels]) html += "</tr>" producer_accuracy_matrix = copy.deepcopy(error_matrix_area_prop) for idx_col, col in enumerate(zip(*error_matrix_area_prop)): for idx_row, value in enumerate(col): producer_accuracy_matrix[idx_row][idx_col] = value / sum( col) if sum(col) > 0 else 0 for idx_row, value in enumerate(accu_asse.values): html += "<tr>" if idx_row == 0: html += ''' <th class="th-rows" rowspan="{table_size}">Thematic raster<br />classes</th> '''.format(table_size=len(accu_asse.values)) html += "<th>{value}</th>".format(value=value) html += "".join([ ''' <td class="field-values">{table_field}</td> '''.format(table_field=(rf(t)) if t > 0 else "-") for t in producer_accuracy_matrix[idx_row] ]) html += "</tr>" html += ''' </tbody> </table> ''' ################################### html += "<h4>Overall Accuracy: </h4>" overall_accuracy = sum( [row[idx_row] for idx_row, row in enumerate(error_matrix_area_prop)]) html += ''' <table> <tbody> <tr> <td>{}</td> </tr>'''.format(rf(overall_accuracy)) html += ''' </tbody> </table> ''' ################################### html += "<p style='font-size:2px'><br/></p>" html += "<h3>5) Class area adjusted table:</h3>" html += ''' <table> <tbody> <tr> <td class="empty"></td> '''.format(table_size=len(accu_asse.values)) headers = [ "Area ({area_unit})".format(area_unit=accu_asse.pixel_area_unit), "Error", "Lower limit", "Upper limit" ] html += "".join(["<th >" + str(h) + "</th>" for h in headers]) html += "</tr>" total_area = 0 for idx_row, value in enumerate(accu_asse.values): html += "<tr>" html += "<th >{} ({})</th>".format( value, accu_asse.labels[str(value)] if str(value) in accu_asse.labels else "-") # area area = sum(list(zip(*error_matrix_area_prop))[idx_row]) * \ sum([accu_asse.thematic_pixels_count[v] for v in accu_asse.values]) * accu_asse.pixel_area_value html += '''<td>{area}</th>'''.format(area=rf(area)) total_area += area # error error = (sum(list(zip(*quadratic_error_matrix))[idx_row])**0.5) * \ sum([accu_asse.thematic_pixels_count[v] for v in accu_asse.values]) * accu_asse.pixel_area_value html += '''<td>{error}</th>'''.format(error=rf(error)) # lower limit html += '''<td>{lower_limit}</th>'''.format( lower_limit=rf(area - accu_asse.z_score * error)) # upper limit html += '''<td>{upper_limit}</th>'''.format( upper_limit=rf(area + accu_asse.z_score * error)) html += "</tr>" html += ''' <tr> <th>total</th> ''' html += '''<td>{total_area}</th>'''.format(total_area=rf(total_area)) html += ''' <td class="empty"></td> <td class="empty"></td> <td class="empty"></td> </tr>''' html += ''' </tbody> </table> ''' html += ''' </body> ''' return html
def get_pixel_count_by_pixel_values(layer, band, pixel_values=None, nodata=-1): app = QCoreApplication.instance() nodata = nodata if nodata != -1 else None if pixel_values is None: pixel_values = get_pixel_values(layer, band) # put nodata at the beginning, with the idea to include it for stopping # the counting when reaching the total pixels, delete it at the end if nodata is not None: if nodata in pixel_values: pixel_values.remove(nodata) pixel_values.insert(0, nodata) try: import xarray as xr import dask.array as da dataset = xr.open_rasterio(get_file_path_of_layer(layer), chunks={ 'band': 1, 'x': 2000, 'y': 2000 }) parallel = True except: dataset = gdal_array.LoadFile(get_file_path_of_layer(layer)) parallel = False if len(dataset.shape) == 3: if parallel: dataset = dataset.sel(band=band) else: dataset = dataset[band - 1] max_pixels = dataset.shape[-1] * dataset.shape[-2] progress = QProgressDialog( 'AcATaMa is counting the number of pixels for each thematic value.\n' 'Depending on the size of the image, it would take a few minutes.', None, 0, 100, iface.mainWindow()) progress.setWindowTitle("AcATaMa - Counting pixels by values... " + ("[parallel]" if parallel else "[not parallel!]")) progress.setWindowModality(Qt.WindowModal) progress.setValue(0) progress.show() app.processEvents() global total_count total_count = 0 def count_value(pixel_value): global total_count if total_count >= max_pixels: return 0 if parallel: count = da.count_nonzero(dataset == pixel_value).compute() else: count = np.count_nonzero(dataset == pixel_value) total_count += count progress.setValue(int(total_count * 100 / max_pixels)) return count pixel_counts = [count_value(pixel_value) for pixel_value in pixel_values] if nodata is not None: pixel_values.pop(0) pixel_counts.pop(0) progress.accept() return dict(zip(pixel_values, pixel_counts))